1 /******************************************************************************
2  *
3  * Project:  OpenCPN
4  * Purpose:  S52 Conditional Symbology Library
5  * Author:   David Register, Sylvain Duclos
6  *
7  ***************************************************************************
8  *   Copyright (C) 2010 by David S. Register   *
9  *
10  *   Copyright (C) 2000-2001  Sylvain Duclos
11  *   sylvain_duclos@yahoo.com
12  *                                                                         *
13  *   This program is free software; you can redistribute it and/or modify  *
14  *   it under the terms of the GNU General Public License as published by  *
15  *   the Free Software Foundation; either version 2 of the License, or     *
16  *   (at your option) any later version.                                   *
17  *                                                                         *
18  *   This program is distributed in the hope that it will be useful,       *
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
21  *   GNU General Public License for more details.                          *
22  *                                                                         *
23  *   You should have received a copy of the GNU General Public License     *
24  *   along with this program; if not, write to the                         *
25  *   Free Software Foundation, Inc.,                                       *
26  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.             *
27  ***************************************************************************
28  *
29  */
30 
31 #include "wx/wxprec.h"
32 
33 #ifndef  WX_PRECOMP
34 #include "wx/wx.h"
35 #endif //precompiled headers
36 
37 #include "wx/tokenzr.h"
38 
39 #include "s57chart.h"
40 #include "s52plib.h"
41 #include "s52utils.h"
42 #include "dychart.h"
43 #include "cutil.h"
44 
45 bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val);
46 
47 #define UNKNOWN 1e6 //HUGE_VAL   // INFINITY/NAN
48 
49 #ifndef chk_snprintf
50 #define chk_snprintf(buf, len, fmt, ...) \
51 { \
52     int r = snprintf(buf, len, fmt, ##__VA_ARGS__); \
53     if (r == -1 || r >= len) wxLogWarning("snprint overrun"); \
54 }
55 #endif
56 
57 WX_DEFINE_ARRAY_DOUBLE(double, ArrayOfSortedDoubles);
58 
59 
60 // size of attributes value list buffer
61 #define LISTSIZE   32   // list size
62 
63 extern s52plib  *ps52plib;
64 
65 wxString *CSQUAPNT01(S57Obj *obj);
66 wxString *CSQUALIN01(S57Obj *obj);
67 
68 
69 
GetChartFloatingATONArray(ObjRazRules * rzRules)70 wxArrayPtrVoid *GetChartFloatingATONArray( ObjRazRules *rzRules )
71 {
72     S57Obj *obj = rzRules->obj;
73     if( obj->m_chart_context->chart )
74         return obj->m_chart_context->chart->pFloatingATONArray;
75     else
76         return obj->m_chart_context->pFloatingATONArray;
77 
78 }
79 
GetChartRigidATONArray(ObjRazRules * rzRules)80 wxArrayPtrVoid *GetChartRigidATONArray( ObjRazRules *rzRules )
81 {
82     S57Obj *obj = rzRules->obj;
83     if( obj->m_chart_context->chart )
84         return obj->m_chart_context->chart->pRigidATONArray;
85     else
86         return obj->m_chart_context->pRigidATONArray;
87 }
88 
CLRLIN01(void * param)89 static void *CLRLIN01(void *param)
90 {
91         ObjRazRules *rzRules = (ObjRazRules *)param;
92 //      S57Obj *obj = rzRules->obj;
93 
94         printf("s52csny : CLRLIN01 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
95    return NULL;
96 }
97 
DATCVR01(void * param)98 static void *DATCVR01(void *param)
99 {
100 
101 // Remarks: This conditional symbology procedure describes procedures for:
102 // - symbolizing the limit of ENC coverage;
103 // - symbolizing navigational purpose boundaries ("scale boundarie"); and
104 // - indicating overscale display.
105            //
106 // Note that the mandatory meta object CATQUA is symbolized by the look-up table.
107            //
108 // Because the methods adopted by an ECDIS to meet the IMO and IHO requirements
109 // listed on the next page will depend on the manufacturer's software, and cannot be
110 // described in terms of a flow chart in the same way as other conditional procedures,
111 // this procedure is in the form of written notes.
112 
113 //    ObjRazRules *rzRules = (ObjRazRules *)param;
114 //    S57Obj *obj = rzRules->obj;
115 
116     wxString rule_str;
117        ///////////////////////
118     // 1- REQUIREMENT
119     // (IMO/IHO specs. explenation)
120 
121        ///////////////////////
122     // 2- ENC COVERAGE
123        //
124     // 2.1- Limit of ENC coverage
125     //datcvr01 = g_string_new(";OP(3OD11060);LC(HODATA01)");
126     rule_str.Append(_T("LC(HODATA01)"));
127 //    rule_str.Append("AC(DEPDW)");
128     // FIXME: get cell extend
129 
130     // 2.2- No data areas
131     // This can be done outside of CS (ie when clearing the screen in Mesa)
132     // FIXME: ";OP(0---);AC(NODATA)"
133     // FIXME: set geo to cover earth (!)
134 
135        //////////////////////
136     // 3- SCALE BOUNDARIES
137        //
138     // 3.1- Chart scale boundaties
139     // FIXME;
140     //g_string_append(datcvr01, ";LS(SOLD,1,CHGRD)");
141     // -OR- LC(SCLBDYnn) (?)
142        //
143     // ;OP(3OS21030)
144 
145     // 3.2- Graphical index of navigational purpose
146     // FIXME: draw extent of available SENC in DB
147 
148        //////////////////////
149     // 4- OVERSCALE
150        //
151     // FIXME: get meta date CSCL of DSPM field
152     // FIXME: get object M_CSCL or CSCALE
153        //
154     // 4.1- Overscale indication
155        // FIXME: compute, scale = [denominator of the compilation scale] /
156     //                         [denominator of the display scale]
157     // FIXME: draw overscale indication (ie TX("X%3.1f",scale))
158        //
159     // 4.2- Ovescale area at a chart scale boundary
160     // FIXME: test if next chart is over scale (ie going from large scale chart
161     //        to a small scale chart)
162     // FIXME: draw AP(OVERSC01) on overscale part of display
163     //g_string(";OP(3OS21030)");
164 
165        //
166     // 4.3- Larger scale data available
167     // FIXME: display indication of better scale available (?)
168 
169 
170 
171 
172    wxString datcvr01;
173    datcvr01.Append(rule_str);
174    datcvr01.Append('\037');
175 
176    char *r = (char *)malloc(datcvr01.Len() + 1);
177    strcpy(r, datcvr01.mb_str());
178 
179    return r;
180 
181 }
182 
183 
GetIntAttr(S57Obj * obj,const char * AttrName,int & val)184 bool GetIntAttr(S57Obj *obj, const char *AttrName, int &val)
185 {
186     int idx = obj->GetAttributeIndex(AttrName);
187 
188     if(idx >= 0) {
189         //      using idx to get the attribute value
190         S57attVal *v = obj->attVal->Item(idx);
191 
192         assert(v->valType == OGR_INT);
193         val = *(int*)(v->value);
194 
195         return true;
196     }
197     else
198         return false;
199 
200 }
201 #if 0
202 bool GetIntAttr(S57Obj *obj, const char *AttrName, int &val)
203 {
204     char *attList = (char *)calloc(obj->attList->Len()+1, 1);
205     strncpy(attList, obj->attList->mb_str(), obj->attList->Len());
206 
207     char *patl = attList;
208     char *patr;
209     int idx = 0;
210     while(*patl)
211     {
212         patr = patl;
213         while(*patr != '\037')
214             patr++;
215 
216         if(!strncmp(patl, AttrName, 6))
217             break;
218 
219         patl = patr + 1;
220         idx++;
221     }
222 
223     if(!*patl)                                     // Requested Attribute not found
224         {
225             free(attList);
226             return false;                            // so don't return a value
227         }
228 
229         //      using idx to get the attribute value
230         wxArrayOfS57attVal      *pattrVal = obj->attVal;
231 
232     S57attVal *v = pattrVal->Item(idx);
233     val = *(int*)(v->value);
234 
235     free(attList);
236     return true;
237 }
238 #endif
239 /*
240 bool GetFloatAttr(S57Obj *obj, char *AttrName, float &val)
241 {
242         char *attList = (char *)(obj->attList->mb_str());        //attList is wxString
243 
244         char *patl = attList;
245         char *patr;
246         int idx = 0;
247         while(*patl)
248         {
249                 patr = patl;
250                 while(*patr != '\037')
251                         patr++;
252 
253                 if(!strncmp(patl, AttrName, 6))
254                         break;
255 
256                 patl = patr + 1;
257                 idx++;
258         }
259 
260         if(!*patl)                                        // Requested Attribute not found
261         {
262              free(attList);
263              return false;                                // so don't return a value
264         }
265 
266 //      using idx to get the attribute value
267         wxArrayOfS57attVal      *pattrVal = obj->attVal;
268 
269         S57attVal *v = pattrVal->Item(idx);
270         val = *(float*)(v->value);
271 
272         free(attList);
273         return true;
274 }
275 
276 */
GetDoubleAttr(S57Obj * obj,const char * AttrName,double & val)277 bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val)
278 {
279     int idx = obj->GetAttributeIndex(AttrName);
280 
281     if(idx >= 0) {
282 //      using idx to get the attribute value
283 
284         S57attVal *v = obj->attVal->Item(idx);
285         assert(v->valType == OGR_REAL);
286         val = *(double*)(v->value);
287 
288         return true;
289     }
290     else
291         return false;
292 }
293 
294 
GetStringAttr(S57Obj * obj,const char * AttrName,char * pval,int nc)295 bool GetStringAttr(S57Obj *obj, const char *AttrName, char *pval, int nc)
296 {
297     int idx = obj->GetAttributeIndex(AttrName);
298 
299     if(idx >= 0) {
300         //      using idx to get the attribute value
301         S57attVal *v = obj->attVal->Item(idx);
302 
303         assert(v->valType == OGR_STR);
304         char *val = (char *)(v->value);
305 
306         strncpy(pval, val, nc);
307 
308         return true;
309     }
310     else
311         return false;
312 }
313 
GetStringAttrWXS(S57Obj * obj,const char * AttrName)314 wxString *GetStringAttrWXS(S57Obj *obj, const char *AttrName)
315 {
316     int idx = obj->GetAttributeIndex(AttrName);
317 
318     if(idx >= 0) {
319         //      using idx to get the attribute value
320         S57attVal *v = obj->attVal->Item(idx);
321 
322         assert(v->valType == OGR_STR);
323         char *val = (char *)(v->value);
324 
325         return new wxString(val,  wxConvUTF8);
326     }
327     else
328         return NULL;
329 }
330 
_parseList(const char * str_in,char * buf,int buf_size)331 static int      _parseList(const char *str_in, char *buf, int buf_size)
332 // Put a string of comma delimited number in an array (buf).
333 // Return: the number of value in buf.
334 // Assume: - number < 256,
335 //         - list size less than buf_size .
336 // Note: buf is \0 terminated for strpbrk().
337 {
338     char *str = (char *)str_in;
339     int i = 0;
340 
341     if (NULL != str && *str != '\0') {
342         do {
343             if ( i>= LISTSIZE-1) {
344                 printf("OVERFLOW --value in list lost!!\n");
345                 break;
346             }
347 
348             /*
349             if (255 <  (unsigned char) atoi(str)) {
350                 PRINTF("value overflow (>255)\n");
351                 exit(0);
352             }
353             */
354 
355             buf[i++] = (unsigned char) atoi(str);
356 
357             while(isdigit(*str))
358                   str++;   // next
359 
360         } while(*str++ != '\0');      // skip ',' or exit
361     }
362 
363     buf[i] = '\0';
364 
365     return i;
366 }
367 
368 
_atPtPos(S57Obj * objNew,wxArrayPtrVoid * curntList,int bSectorCheck)369 static int      _atPtPos(S57Obj *objNew, wxArrayPtrVoid *curntList, int bSectorCheck)
370 // return TRUE if there is a light at this position
371 // or if its an extended arc radius else FALSE
372 {
373     unsigned int i;
374 
375     if(NULL == curntList)
376           return false;
377 
378     for (i=0; i<curntList->GetCount(); i++) {
379         S57Obj *objOld = (S57Obj *)curntList->Item(i);
380 
381         if ((objOld->x == objNew->x) && (objOld->y == objNew->y)) {
382 
383             if (!bSectorCheck)
384                 return TRUE;
385 /*
386             else {
387                 // check for extend arc radius
388                 GString *Asectr1str = S57_getAttVal(geoOld, "SECTR1");
389                 GString *Asectr2str = S57_getAttVal(geoOld, "SECTR2");
390                 GString *Bsectr1str = S57_getAttVal(geoNew, "SECTR1");
391                 GString *Bsectr2str = S57_getAttVal(geoNew, "SECTR2");
392 
393                 // check  present
394                 if (NULL == Asectr1str ||
395                     NULL == Asectr1str ||
396                     NULL == Asectr1str ||
397                     NULL == Asectr1str)
398                     return FALSE;
399 
400                 {
401                     double Asectr1 = atof(Asectr1str->str);
402                     double Asectr2 = atof(Asectr2str->str);
403                     double Bsectr1 = atof(Bsectr1str->str);
404                     double Bsectr2 = atof(Bsectr2str->str);
405                     double Asweep = (Asectr1 > Asectr2) ?
406                         Asectr2-Asectr1+360 : Asectr2-Asectr1;
407                     double Bsweep = (Bsectr1 > Bsectr2) ?
408                         Bsectr2-Bsectr1+360 : Bsectr2-Bsectr1;
409 
410                     // check sector overlap
411                     if (Asectr2<=Bsectr1 || Asectr1>=Bsectr2) {
412                         if (Asweep == Bsweep) {
413                             g_string_truncate(Bsectr2str, 0);
414                             g_string_sprintf(Bsectr2str, "%f",Bsectr2-1);
415                             S57_setAtt(geoNew, "SECTR2", Bsectr2str->str);
416                         }
417 
418                         return FALSE;
419                     }
420 
421                     // check if other sector larger
422                     if (Asweep >= Bsweep)
423                         return TRUE;
424                 }
425             }
426 */
427         }
428     }
429 
430     return FALSE;
431 }
432 
_selSYcol(char * buf,bool bsectr,double valnmr)433 wxString _selSYcol(char *buf, bool bsectr, double valnmr)
434 {
435     wxString sym;
436 
437     if(!bsectr)
438     {
439 
440       sym = _T(";SY(LITDEF11");                 // default
441 
442     // max 1 color
443       if ('\0' == buf[1])
444       {
445         if (strpbrk(buf, "\003"))
446               sym = _T(";SY(LIGHTS11");
447         else if (strpbrk(buf, "\004"))
448               sym = _T(";SY(LIGHTS12");
449         else if (strpbrk(buf, "\001\006\011"))
450               sym = _T(";SY(LIGHTS13");
451       }
452       else
453       {
454         // max 2 color
455         if ('\0' == buf[2]) {
456             if (strpbrk(buf, "\001") && strpbrk(buf, "\003"))
457                   sym = _T(";SY(LIGHTS11");
458             else if (strpbrk(buf, "\001") && strpbrk(buf, "\004"))
459                   sym = _T(";SY(LIGHTS12");
460         }
461       }
462     }
463 
464     else                // all-round fixed light symbolized as a circle, radius depends on color
465                         // This treatment is seen on SeeMyDenc by SevenCs
466                         // This may not be S-52 compliant....
467     {
468           //      Another non-standard extension....
469           //      All round light circle diameter is scaled if the light has a reasonable VALNMR attribute
470           int radius = 3;
471           if(valnmr > 0)
472           {
473                 if(valnmr < 7.0)
474                       radius = 3;
475                 else if(valnmr < 15.0)
476                       radius = 10;
477                 else if(valnmr < 30.0)
478                       radius = 15;
479                 else
480                       radius = 20;
481           }
482 
483        // max 1 color
484       if ('\0' == buf[1])
485       {
486           if (strpbrk(buf, "\003"))
487                 sym.Printf(_T(",LITRD, 2,0,360,%d,0"), radius + 1);
488           else if (strpbrk(buf, "\004"))
489                 sym.Printf(_T(",LITGN, 2,0,360,%d,0"), radius);
490           else if (strpbrk(buf, "\001\006\011"))
491                 sym.Printf(_T(",LITYW, 2,0,360,%d,0"), radius + 2);
492           else if (strpbrk(buf, "\014"))
493                 sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 3);
494           else
495                 sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 5);           // default
496 
497       }
498       else  if ('\0' == buf[2])       // or 2 color
499       {
500                 if (strpbrk(buf, "\001") && strpbrk(buf, "\003"))
501                       sym.Printf(_T(",LITRD, 2,0,360,%d,0"), radius + 1);
502                 else if (strpbrk(buf, "\001") && strpbrk(buf, "\004"))
503                       sym.Printf(_T(",LITGN, 2,0,360,%d,0"), radius);
504                 else
505                       sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 5);           // default
506 
507       }
508       else
509             sym.Printf(_T(",CHMGD, 2,0,360,%d,0"), radius + 5);
510 
511 
512       if(sym.Len())
513             sym.Prepend(_T(";CA(OUTLW, 4"));
514     }
515 
516 
517     return sym;
518 }
519 
_DEPVAL01(S57Obj * obj,double least_depth)520 static double   _DEPVAL01(S57Obj *obj, double least_depth)
521 // Remarks: S-57 Appendix B1 Annex A requires in Section 6 that areas of rocks be
522 // encoded as area obstruction, and that area OBSTRNs and area WRECKS
523 // be covered by either group 1 object DEPARE or group 1 object UNSARE.
524 // If the value of the attribute VALSOU for an area OBSTRN or WRECKS
525 // is missing, the DRVAL1 of an underlying DEPARE is the preferred default
526 // for establishing a depth value. This procedure either finds the shallowest
527 // DRVAL1 of the one or more underlying DEPAREs, or returns an
528 // "unknown"" depth value to the main procedure for the next default
529 // procedure.
530 
531 // NOTE: UNSARE test is useless since least_depth is already UNKNOWN
532 {
533     least_depth = UNKNOWN;
534 
535 /*
536     S57_geo *geoTmp = geo;
537 
538     // NOTE: the geo list is unchange (_UDWHAZ03 will unlink geo)
539     while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
540         GString *objlstr = S57_getAttVal(geoTmp, "OBJL");
541         int      objl    = (NULL == objlstr) ? 0 : atoi(objlstr->str);
542 
543         // get area DEPARE  that intersect this area
544         if (DEPARE==objl && LINES_T==S57_getObjtype(geo)) {
545             GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
546             double   drval1    = (NULL == drval1str) ? 9.0 : atof(drval1str->str);
547 
548             if (NULL != drval1str) {
549                 if (UNKNOWN==least_depth || least_depth<drval1)
550                     least_depth = drval1;
551             }
552 
553         }
554     }
555   */
556     return least_depth;
557 }
558 
_UDWHAZ03(S57Obj * obj,double depth_value,ObjRazRules * rzRules,bool * promote_return)559 static wxString *_UDWHAZ03(S57Obj *obj, double depth_value, ObjRazRules *rzRules, bool *promote_return)
560 // Remarks: Obstructions or isolated underwater dangers of depths less than the safety
561 // contour which lie within the safe waters defined by the safety contour are
562 // to be presented by a specific isolated danger symbol as hazardous objects
563 // and put in IMO category DISPLAYBASE (see (3), App.2, 1.3). This task
564 // is performed by this conditional symbology procedure.
565 {
566     wxString udwhaz03str;
567     int      danger         = FALSE;
568     int	     expsou = 0;
569     double   safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
570     bool     b_promote = false;
571 
572     if(depth_value == UNKNOWN) {
573           GetIntAttr(obj, "EXPSOU", expsou);
574           if (expsou != 1)
575               danger = TRUE;
576     }
577     if (danger == FALSE && (expsou == 1 || depth_value <= safety_contour)) {
578         // that intersect this point/line/area for OBSTRN04
579         // that intersect this point/area      for WRECKS02
580 
581         // get area DEPARE & DRGARE that intersect this point/line/area
582 
583         ListOfS57Obj *pobj_list = NULL;
584 
585 
586         if( obj->m_chart_context->chart )
587             pobj_list = obj->m_chart_context->chart->GetAssociatedObjects(obj);
588         else{
589             danger = false;
590 //            wxString *ret_str = new wxString(udwhaz03str);
591 //            return ret_str;
592         }
593 
594 
595         if( pobj_list ){
596             wxListOfS57ObjNode *node = pobj_list->GetFirst();
597             while(node)
598             {
599                 S57Obj *ptest_obj = node->GetData();
600                 if(GEO_LINE == ptest_obj->Primitive_type)
601                 {
602                     double drval2 = 0.0;
603                     GetDoubleAttr(ptest_obj, "DRVAL2", drval2);
604 
605                     if(drval2 < safety_contour)
606                     {
607                           danger = TRUE;
608                           break;
609                     }
610                 }
611                 else
612                 {
613                     double drval1 = 0.0;
614                     GetDoubleAttr(ptest_obj, "DRVAL1", drval1);
615 
616 #if 0
617                     double drval2 = 0.0;
618                     GetDoubleAttr(ptest_obj, "DRVAL2", drval2);
619 
620                     if(expsou == 1 || depth_value < drval2 )
621                         b_promote = true;
622 #endif
623 
624                     if(drval1 >= safety_contour && expsou != 1)
625                     {
626                           danger = TRUE;
627                           break;
628                     }
629                 }
630                 node = node->GetNext();
631             }
632 
633             delete pobj_list;
634         }
635     }
636 
637     if (TRUE == danger)
638     {
639               int watlev = 0; // Enum 0 invalid
640               GetIntAttr(obj, "WATLEV", watlev);
641 
642               if((1 == watlev) || (2 == watlev))
643               {
644                     // dry
645 //                  udwhaz03str = _T(";OP(--D14050)");
646               }
647               else
648               {
649                     udwhaz03str = _T(";SY(ISODGR51)");     //_T(";OP(8OD14010);SY(ISODGR51)");
650 //                  S57_setAtt(geo, "SCAMIN", "INFINITE");
651               }
652 
653               //  Move this object to DisplayBase category
654               rzRules->obj->m_DisplayCat = DISPLAYBASE;
655 
656 /*
657             GString *watlevstr = S57_getAttVal(geo, "WATLEV");
658             if (NULL != watlevstr && ('1' == *watlevstr->str || '2' == *watlevstr->str))
659                 udwhaz03str = g_string_new(";OP(--D14050");
660             else {
661                 udwhaz03str = g_string_new(";OP(8OD14010);SY(ISODGR01)");
662                 S57_setAtt(geo, "SCAMIN", "INFINITE");
663             }
664 */
665     }
666 
667 
668     if(promote_return)
669         *promote_return = b_promote;
670 
671     wxString *ret_str = new wxString(udwhaz03str);
672     return ret_str;
673 
674 }
675 
676 
677 
678 
679 
680 // Remarks: An object of the class "depth area" is coloured and covered with fill patterns
681 // according to the mariners selections of shallow contour, safety contour and
682 // deep contour. This requires a decision making process based  on DRVAL1 and DRVAL2.
683 // Objects of the class "dredged area" are handled by this routine as well to
684 // ensure a consistent symbolization of areas that represent the surface of the
685 // seabed.
686 
DEPARE01(void * param)687 static void *DEPARE01(void *param)
688 {
689 
690    ObjRazRules *rzRules = (ObjRazRules *)param;
691    S57Obj *obj = rzRules->obj;
692 
693 
694    double drval1, drval2;
695    bool drval1_found;
696 
697 //      Determine the color based on mariner selections
698 
699 
700    drval1 = -1.0;                                          // default values
701    drval1_found = GetDoubleAttr(obj, "DRVAL1", drval1);
702    drval2 = drval1 + 0.01;
703    GetDoubleAttr(obj, "DRVAL2", drval2);
704 
705 
706 
707 
708    //   Create a string of the proper color reference
709 
710     wxString rule_str =_T("AC(DEPIT)");
711 
712 
713     if (drval1 >= 0.0 && drval2 > 0.0)
714         rule_str  = _T("AC(DEPVS)");
715 
716     if (TRUE == S52_getMarinerParam(S52_MAR_TWO_SHADES))
717     {
718         if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)  &&
719             drval2 >  S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
720         {
721             rule_str  = _T("AC(DEPDW)");
722         }
723     }
724     else
725     {
726         if (drval1 >= S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR) &&
727             drval2 >  S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR))
728             rule_str  = _T("AC(DEPMS)");
729 
730         if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)  &&
731                 drval2 >  S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
732         {
733             rule_str  = _T("AC(DEPMD)");
734         }
735 
736         if (drval1 >= S52_getMarinerParam(S52_MAR_DEEP_CONTOUR)  &&
737                 drval2 >  S52_getMarinerParam(S52_MAR_DEEP_CONTOUR))
738         {
739             rule_str  = _T("AC(DEPDW)");
740         }
741 
742     }
743 
744 
745 //  If object is DRGARE....
746 
747     if(!strncmp(rzRules->LUP->OBCL, "DRGARE", 6))
748     {
749         if (!drval1_found) //If DRVAL1 was not defined...
750         {
751             rule_str  = _T("AC(DEPMD)");
752         }
753         rule_str.Append(_T(";AP(DRGARE01)"));
754         rule_str.Append(_T(";LS(DASH,1,CHGRF)"));
755 
756 // Todo Restrictions
757 /*
758         char pval[30];
759         if(true == GetStringAttr(obj, "RESTRN", pval, 20))
760         {
761             GString *rescsp01 = _RESCSP01(geo);
762             if (NULL != rescsp01)
763             {
764                 g_string_append(depare01, rescsp01->str);
765                 g_string_free(rescsp01, TRUE);
766             }
767         }
768 */
769     }
770 
771 
772     rule_str.Append('\037');
773 
774     char *r = (char *)malloc(rule_str.Len() + 1);
775     strcpy(r, rule_str.mb_str());
776     return r;
777 
778 }
779 /*
780 static void *DEPCNT02A(void *param)
781 {
782  //    ObjRazRules *rzRules = (ObjRazRules *)param;
783  //    S57Obj *obj = rzRules->obj;
784 
785         //      Add another rule onto the ruleList
786 
787     Rules *r = NULL;
788 
789     r = (Rules*)calloc(1, sizeof(Rules));
790     r->ruleType = RUL_SIM_LN;
791     r->INSTstr = "SOLD,1,DEPCN";                                // points into the plib data space
792 
793         return NULL;
794 
795 }
796 */
797 
DEPCNT02(void * param)798 static void *DEPCNT02 (void *param)
799 // Remarks: An object of the class "depth contour" or "line depth area" is highlighted and must
800 // be shown under all circumstances if it matches the safety contour depth value
801 // entered by the mariner (see IMO PS 3.6). But, while the mariner is free to enter any
802 // safety contour depth value that he thinks is suitable for the safety of his ship, the
803 // SENC only contains a limited choice of depth contours. This symbology procedure
804 // determines whether a contour matches the selected safety contour. If the selected
805 // safety contour does not exist in the data, the procedure will default to the next deeper
806 // contour. The contour selected is highlighted as the safety contour and put in
807 // DISPLAYBASE. The procedure also identifies any line segment of the spatial
808 // component of the object that has a "QUAPOS" value indicating unreliable
809 // positioning, and symbolizes it with a double dashed line.
810             //
811 // Note: Depth contours are not normally labeled. The ECDIS may provide labels, on demand
812 // only as with other text, or provide the depth value on cursor picking
813 {
814 //      GString *depcnt02  = NULL;
815 //      int      safe      = FALSE;     // initialy not a safety contour
816 //      GString *objlstr   = NULL;
817 //      int      objl      = 0;
818 //      GString *quaposstr = NULL;
819 //      int      quapos    = 0;
820       double drval1, drval2;
821       bool safe = FALSE;
822       wxString rule_str;
823       double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
824 
825 //      objlstr = S57_getAttVal(geo, "OBJL");
826 //      objl    = (NULL == objlstr) ? 0 : atoi(objlstr->str);
827 
828       ObjRazRules *rzRules = (ObjRazRules *)param;
829       S57Obj *obj = rzRules->obj;
830 // Debug
831 //      if(obj->Index == 812)
832 //            int tty = 5;
833 
834       if ((!strncmp(obj->FeatureName, "DEPARE", 6)) && GEO_LINE==obj->Primitive_type)
835       {
836             drval1 = 0.0;                                          // default values
837             GetDoubleAttr(obj, "DRVAL1", drval1);
838             drval2 = drval1;
839             GetDoubleAttr(obj, "DRVAL2", drval2);
840 
841 //            GString *drval1str = S57_getAttVal(geo, "DRVAL1");
842 //            double   drval1    = (NULL == drval1str) ? 0.0    : atof(drval1str->str);
843 //            GString *drval2str = S57_getAttVal(geo, "DRVAL2");
844 //            double   drval2    = (NULL == drval2str) ? drval1 : atof(drval2str->str);
845 
846             if (drval1 <= safety_contour)
847             {
848                   if (drval2 >= safety_contour)
849                         safe = TRUE;
850             }
851             else
852             {
853                   double next_safe_contour = 1e6;
854                   if( obj->m_chart_context->chart ){
855                       next_safe_contour = obj->m_chart_context->chart->GetCalculatedSafetyContour();
856                       if (drval1 == next_safe_contour)
857                               safe = TRUE;
858                   }
859                   else {
860                       next_safe_contour = obj->m_chart_context->safety_contour;
861 
862                       if (fabs(drval1 - next_safe_contour) < 1e-4)
863                           safe = true;
864                   }
865 
866 //                  safe = FALSE;            //for debug
867                               /*
868                   if (1 == S52_state)
869                         return NULL;
870                   else
871                   {
872                         S57_geo *geoTmp = geo;
873 
874                 // get area DEPARE & DRGARE that intersect this line
875                         while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
876                               drval1str = S57_getAttVal(geoTmp, "DRVAL1");
877                               drval1    = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
878 
879                               if (NULL == drval1str) {
880                                     safe = TRUE;
881                                     break;
882                               }
883 
884                               if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
885                                     safe = TRUE;
886                                     break;
887                               }
888                         }
889                 // debug trace
890                 //if (safe) PRINTF("** DEPARE: SAFE FOUND**\n");
891                   }
892                               */
893             }
894 
895       }
896       else
897       {
898         // continuation A (DEPCNT)
899             double valdco = 0;
900             GetDoubleAttr(obj, "VALDCO", valdco);
901 //            GString *valdcostr = S57_getAttVal(geo, "VALDCO");
902 //            double   valdco    = (NULL == valdcostr) ? 0.0 : atof(valdcostr->str);
903 
904             if (valdco == safety_contour)
905                   safe = TRUE;   // this is useless !?!?
906             else
907             {
908                   double next_safe_contour = 1e6;
909                   if( obj->m_chart_context->chart ){
910                       next_safe_contour = obj->m_chart_context->chart->GetCalculatedSafetyContour();
911                       if (valdco == next_safe_contour)
912                               safe = TRUE;
913                   }
914                   else{
915                     next_safe_contour = obj->m_chart_context->safety_contour;
916 
917                     if (fabs(valdco - next_safe_contour) < 1e-4)
918                       safe = true;
919                   }
920 
921 
922 /*
923                   if (valdco > safety_contour)
924                   {
925                         safe = FALSE;
926                         if (1 == S52_state)
927                               return NULL;
928                         else {
929                               S57_geo *geoTmp = geo;
930 
931                     // get area DEPARE & DRGARE that intersect this line
932                               while (NULL != (geoTmp = S57_nextObj(geoTmp))){
933                                     GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
934                                     double   drval1    = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
935 
936                                     if (NULL == drval1str) {
937                                           safe = TRUE;
938                                           break;
939                                     }
940 
941                                     if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
942                                           safe = TRUE;
943                                           break;
944                                     }
945                               }
946                     // debug trace
947                     //if (safe) PRINTF("** DEPCN: SAFE FOUND**\n");
948                         }
949 
950 */
951             }
952       }
953 
954     // Continuation B
955       int quapos = 0;
956       GetIntAttr(obj, "QUAPOS", quapos);        // QUAPOS is an E (Enumerated) type attribute
957 
958       if (0 != quapos) {
959             if ( 2 <= quapos && quapos < 10) {
960                   if (safe) {
961                       wxString safeCntr = _T("LS(DASH,2,DEPSC)");
962                       S57Obj tempObj;
963                       LUPrec* safelup = ps52plib->S52_LUPLookup( PLAIN_BOUNDARIES, "SAFECD", &tempObj, false );
964                       if( safelup )
965                           safeCntr = *safelup->INST;
966                       rule_str = _T(";") + safeCntr;
967                   }
968                   else
969                         rule_str = _T(";LS(DASH,1,DEPCN)");
970             }
971       } else {
972             if (safe) {
973                 wxString safeCntr = _T("LS(SOLD,2,DEPSC)");
974                 S57Obj tempObj;
975                 LUPrec* safelup = ps52plib->S52_LUPLookup( PLAIN_BOUNDARIES, "SAFECN", &tempObj, false );
976                 if( safelup )
977                     safeCntr = *safelup->INST;
978                 rule_str = _T(";") + safeCntr;
979             }
980             else
981                   rule_str = _T(";LS(SOLD,1,DEPCN)");
982       }
983 
984       if (safe) {
985 //            S57_setAtt(geo, "SCAMIN", "INFINITE");
986 //            rule_str.Prepend(_T(";OP(8OD13010)"));       //depcnt02 = g_string_prepend(depcnt02, ";OP(8OD13010)");
987            //  Move this object to DisplayBase category
988             rzRules->obj->m_DisplayCat = DISPLAYBASE;
989             rzRules->obj->Scamin = 1e8;                 // effectively no SCAMIN
990 //            rzRules->LUP->DPRI = PRIO_HAZARDS;
991 
992       } else {
993 //            rule_str.Prepend(_T(";OP(---33020)"));       //depcnt02 = g_string_prepend(depcnt02, ";OP(---33020)");
994       }
995     // facultative in S-52
996     //if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
997     //    GString *sndfrm02 = _SNDFRM02(geo, depth_value);
998     //    depcnt02 = g_string_append(depcnt02, sndfrm02->str);
999     //    g_string_free(sndfrm02, TRUE);
1000     //}
1001 
1002     // debug
1003     //PRINTF("depth= %f\n", depth_value);
1004 
1005  //           S57_unlinkObj(geo);
1006 
1007             rule_str.Append('\037');
1008 
1009             char *r = (char *)malloc(rule_str.Len() + 1);
1010             strcpy(r, rule_str.mb_str());
1011             return r;
1012 
1013 //            return depcnt02;
1014 }
1015 
1016 
1017 
1018 
1019 
1020 
1021 
1022 
1023 
1024 
1025 
DEPVAL01(void * param)1026 static void *DEPVAL01(void *param)
1027 {
1028         ObjRazRules *rzRules = (ObjRazRules *)param;
1029 //      S57Obj *obj = rzRules->obj;
1030 
1031         printf("s52csny : DEPVAL01 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
1032    return NULL;
1033 }
1034 
LEGLIN02(void * param)1035 static void *LEGLIN02(void *param)
1036 {
1037         ObjRazRules *rzRules = (ObjRazRules *)param;
1038 //      S57Obj *obj = rzRules->obj;
1039 
1040         printf("s52csny : LEGLIN02 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
1041    return NULL;
1042 }
1043 
1044 /*
1045 static void *LIGHTS04A(void *param)
1046 {
1047         ObjRazRules *rzRules = (ObjRazRules *)param;
1048         S57Obj *obj = rzRules->obj;
1049 
1050         wxString rule_str;
1051 
1052         char col_str[2];
1053         GetStringAttr(obj, "COLOUR", col_str, 1);
1054 
1055         double height_val = 0;
1056         GetDoubleAttr(obj, "HEIGHT", height_val);
1057 
1058 //      if(obj->attList->Contains(wxString("HEIGHT")))
1059 //              int uupr = 5;
1060 
1061 //      Different symbology depending upon Paper or Simplified Mariner Selection
1062 
1063 
1064     if(ps52plib->m_nSymbolStyle == PAPER_CHART)
1065     {
1066             if(col_str[0] == '3')
1067             {                                                     // red
1068                     if(height_val)
1069                         rule_str = _T("SY(LIGHTS93)");            // all round
1070                     else
1071                         rule_str = _T("SY(LIGHTS01)");            // flare
1072             }
1073 
1074             else if(col_str[0] == '4')                            // green
1075             {
1076                     if(height_val)
1077                         rule_str = _T("SY(LIGHTS92)");
1078                     else
1079                         rule_str = _T("SY(LIGHTS02)");
1080             }
1081 
1082             else                                                  // Generic, shows as yellow
1083             {
1084                     if(height_val)
1085                         rule_str = _T("SY(LIGHTS91)");
1086                     else
1087                         rule_str = _T("SY(LIGHTS03)");
1088             }
1089     }
1090 
1091     else                                                     // must be Simplified
1092     {
1093             if(col_str[0] == '3')
1094             {                                                     // red
1095                     if(height_val)
1096                         rule_str = _T("SY(LIGHTS93)");            // all round
1097                     else
1098                         rule_str = _T("SY(LIGHTS01)");            // flare
1099             }
1100 
1101             else if(col_str[0] == '4')                            // green
1102             {
1103                     if(height_val)
1104                         rule_str = _T("SY(LIGHTS92)");
1105                     else
1106                         rule_str = _T("SY(LIGHTS02)");
1107             }
1108 
1109             else                                                  // Generic, shows as yellow
1110             {
1111                     if(height_val)
1112                         rule_str = _T("SY(LIGHTS91)");
1113                     else
1114                         rule_str = _T("SY(LIGHTS03)");
1115             }
1116     }
1117 
1118 
1119 
1120     rule_str.Append('\037');
1121 
1122     char *r = (char *)malloc(rule_str.Len() + 1);
1123     strcpy(r, rule_str.mb_str());
1124     return r;
1125 }
1126 
1127 //    A simple placeholder for Conditional Symbology method LIGHTS05
1128 static void *LIGHTS05A(void *param)
1129 {
1130       ObjRazRules *rzRules = (ObjRazRules *)param;
1131       S57Obj *obj = rzRules->obj;
1132 
1133       wxString rule_str;
1134 
1135       char col_str[2];
1136       GetStringAttr(obj, "COLOUR", col_str, 1);
1137 
1138       double height_val = 0;
1139       GetDoubleAttr(obj, "HEIGHT", height_val);
1140 
1141       if(col_str[0] == '3')
1142       {                                                     // red
1143                   rule_str = _T("SY(LIGHTS11)");            // flare
1144       }
1145 
1146       else if(col_str[0] == '4')                            // green
1147                   rule_str = _T("SY(LIGHTS12)");
1148 
1149       else                                                  // Generic, shows as yellow
1150                   rule_str = _T("SY(LIGHTS13)");
1151 
1152 
1153 
1154       rule_str.Append('\037');
1155 
1156       char *r = (char *)malloc(rule_str.Len() + 1);
1157       strcpy(r, rule_str.mb_str());
1158       return r;
1159 }
1160 */
1161 
1162 static wxString _LITDSN01(S57Obj *obj);
1163 static void *LIGHTS06 (void *param);
1164 
LIGHTS05(void * param)1165 static void *LIGHTS05 (void *param)
1166 // Remarks: A light is one of the most complex S-57 objects. Its presentation depends on
1167 // whether it is a light on a floating or fixed platform, its range, it's colour and
1168 // so on. This conditional symbology procedure derives the correct
1169 // presentation from these parameters and also generates an area that shows the
1170 // coverage of the light.
1171 //
1172 // Notes on light sectors:
1173 // 1.) The radial leg-lines defining the light sectors are normally drawn to only 25mm
1174 // from the light to avoid clutter (see Part C). However, the mariner should be able to
1175 // select "full light-sector lines" and have the leg-lines extended to the nominal range
1176 // of the light (VALMAR).
1177 //
1178 // 2.) Part C of this procedure symbolizes the sectors at the light itself. In addition,
1179 // it should be possible, upon request, for the mariner to be capable of identifying
1180 // the colour and sector limit lines of the sectors affecting the ship even if the light
1181 // itself is off the display.
1182 // [ed. last sentence in bold]
1183 
1184 
1185 {
1186     // As transition, we use the PLIB 4.0 LIGHTS06 Procedure
1187     return LIGHTS06(param);
1188 
1189 
1190 #define UNKNOWN_DOUBLE -9;
1191     wxString lights05;
1192 
1193     ObjRazRules *rzRules = (ObjRazRules *)param;
1194     S57Obj *obj = rzRules->obj;
1195 
1196     double valnmr = UNKNOWN_DOUBLE;
1197     GetDoubleAttr(obj, "VALNMR", valnmr);
1198 
1199 
1200     char catlitstr[20] = {'\0'};
1201     GetStringAttr(obj, "CATLIT", catlitstr, 19);
1202 
1203     char litvisstr[20] = {'\0'};;
1204     GetStringAttr(obj, "LITVIS", litvisstr, 19);
1205 
1206 
1207     char     catlit[LISTSIZE]  = {'\0'};
1208     char     litvis[LISTSIZE]  = {'\0'};
1209     char     col_str[20] = {'\0'};
1210 
1211     bool     flare_at_45       = false;
1212     double   sectr1            = UNKNOWN_DOUBLE;
1213     double   sectr2            = UNKNOWN_DOUBLE;
1214     double   sweep = 0.;
1215     char     colist[LISTSIZE]  = {'\0'};   // colour list
1216     bool     b_isflare = false;
1217 
1218     wxString orientstr;
1219 
1220     if ( strlen(catlitstr))
1221     {
1222         _parseList(catlitstr, catlit, sizeof(colist));
1223 
1224         // FIXME: OR vs AND/OR
1225         if (strpbrk(catlit, "\010\013")) {
1226             lights05.Append(_T(";SY(LIGHTS82)"));
1227             goto l05_end;
1228         }
1229 
1230         if (strpbrk(catlit, "\011")) {
1231             lights05.Append(_T(";SY(LIGHTS81)"));
1232             goto l05_end;
1233         }
1234 
1235 /*
1236         if (strpbrk(catlit, "\001\020")) {
1237             orientstr = S57_getAttVal(geo, "ORIENT");
1238             if (NULL != orientstr) {
1239                 // FIXME: create a geo object (!?) LINE of lenght VALNMR
1240                 // using ORIENT (from seaward) & POINT_T position
1241                 g_string_append(lights05, ";LS(DASH,1,CHBLK)");
1242             }
1243         }
1244 */
1245     }
1246 
1247     // Continuation A
1248 
1249     GetStringAttr(obj, "COLOUR", col_str, 19);
1250 
1251     if (strlen(col_str))
1252           _parseList(col_str, colist, sizeof(colist));
1253     else
1254     {
1255         colist[0] = '\014';  // magenta (12)
1256         colist[1] = '\000';
1257     }
1258 
1259     GetDoubleAttr(obj, "SECTR1", sectr1);
1260     GetDoubleAttr(obj, "SECTR2", sectr2);
1261 
1262 
1263     if ((-9 == sectr1) || (-9 == sectr2))
1264     {
1265         // This is not a sector light
1266 
1267           //      What follows is one interpretation of the modern (3_3 +)
1268           //      Presentation Library CS flow chart, which I(dsr) have never seen verbatim.
1269           //      We will use flare light symbols for floating aids, and
1270           //      all round sector lights for fixed aids.
1271 
1272         wxString ssym;
1273 
1274         if(_atPtPos(obj, GetChartFloatingATONArray( rzRules ), false))          // Is this LIGHTS feature colocated with ...ANY... floating aid?
1275         {
1276             flare_at_45 = false;
1277 
1278             //TODO create LightArray in s57chart.
1279             //  Then, if another LIGHT object is colocated here, set flare_at_45
1280 /*            if(_atPtPos(obj, rzRules->chart->pLIGHTSArray, false))          // Is this LIGHTS feature colocated with another LIGHTS?
1281 
1282 
1283             //    If the light is white, yellow, or orange, make it a flare at 45 degrees
1284                   if(strpbrk(colist, "\001\005\011"))
1285                     flare_at_45 = true;
1286 */
1287             ssym = _selSYcol(colist, 0, valnmr);              // flare
1288             b_isflare = true;
1289         }
1290         else
1291         {
1292             ssym = _selSYcol(colist, 1, valnmr);              // all round light
1293             b_isflare = false;
1294         }
1295 
1296 
1297         //  Is the light a directional or moire?
1298         if (strpbrk(catlit, "\001\016"))
1299         {
1300             if (orientstr.Len())
1301             {
1302                 lights05.Append(ssym);
1303                 lights05.Append(orientstr);
1304                 lights05.Append(_T(";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" ));
1305             }
1306             else
1307                 lights05.Append(_T(";SY(QUESMRK1)"));
1308         }
1309         else
1310         {
1311             lights05.Append(ssym);
1312             if(b_isflare)
1313             {
1314                 if (flare_at_45)
1315                       lights05.Append(_T(",45)"));
1316                 else
1317                       lights05.Append(_T(",135)"));
1318             }
1319         }
1320 
1321 
1322         goto l05_end;
1323     }
1324 
1325     // Continuation B --sector light
1326     if (-9 == sectr1)
1327     {
1328         sectr1 = 0.0;
1329         sectr2 = 0.0;
1330     }
1331     else
1332         sweep = (sectr1 > sectr2) ? sectr2-sectr1+360 : sectr2-sectr1;
1333 
1334 
1335     if (sweep<1.0 || sweep==360.0)
1336     {
1337         // handle all round light
1338       wxString ssym = _selSYcol(colist, 1, valnmr);           // all round light
1339       lights05.Append(ssym);
1340 
1341 
1342 /*
1343         if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
1344             GString *litdsn01 = _LITDSN01(geo);
1345             if (NULL != litdsn01) {
1346                 g_string_append(lights05, ";TX('");
1347                 g_string_append(lights05, litdsn01->str);
1348                 g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" );
1349                 g_string_free(litdsn01, TRUE);
1350             }
1351 
1352         }
1353 */
1354       goto l05_end;
1355     }
1356 
1357 /*
1358     // scan for other lights with sector overlap at this position
1359     // compute light sector radius according to other sector
1360     if (1 == S52_state)
1361     {
1362         _setPtPos(geo, SECTRLIST);
1363         g_string_free(lights05, TRUE);
1364         return NULL;
1365     }
1366     else
1367     {
1368         extend_arc_radius = _atPtPos(geo, SECTRLIST);
1369 
1370         // passe value via attribs to _renderAC
1371         if (extend_arc_radius)
1372             // FIXME: draw radius 25 mm
1373             S57_setAtt(geo, "extend_arc_radius", "Y");
1374         else
1375             // FIXME: draw radius 20 mm
1376             S57_setAtt(geo, "extend_arc_radius", "N");
1377     }
1378 */
1379     // setup sector
1380     {
1381              //        Build the (opencpn private) command string like this:
1382             //        e.g.  ";CA(OUTLW, 4,LITRD, 2, sectr1, sectr2, radius)"
1383 
1384 
1385           double arc_radius = 20.;                // mm
1386           double sector_radius = 25.;
1387 
1388           //      Another non-standard extension....
1389           //      Sector light arc radius is scaled if the light has a reasonable VALNMR attribute
1390           if(valnmr > 0)
1391           {
1392                 if(valnmr < 15.0)
1393                       arc_radius = 10.;
1394                 else if(valnmr < 30.0)
1395                       arc_radius = 15.;
1396                 else
1397                       arc_radius = 20.;
1398           }
1399 
1400           char sym[80];
1401           strcpy(sym,";CA(OUTLW, 4");
1402 
1403 
1404             // max 1 color
1405             if ('\0' == colist[1])
1406             {
1407                 if (strpbrk(colist, "\003"))
1408                     strcat(sym, ",LITRD, 2");
1409                 else if (strpbrk(colist, "\004"))
1410                       strcat(sym, ",LITGN, 2");
1411                 else if (strpbrk(colist, "\001\006\013"))
1412                       strcat(sym, ",LITYW, 2");
1413                 else
1414                       strcat(sym, ",CHMGD, 2");                 // default is magenta
1415 
1416             }
1417             else if ('\0' == colist[2])
1418             {
1419                     if (strpbrk(colist, "\001") && strpbrk(colist, "\003"))
1420                           strcat(sym, ",LITRD, 2");
1421                     else if (strpbrk(colist, "\001") && strpbrk(colist, "\004"))
1422                           strcat(sym, ",LITGN, 2");
1423                     else
1424                           strcat(sym, ",CHMGD, 2");                 // default is magenta
1425             }
1426             else
1427                   strcat(sym, ",CHMGD, 2");                 // default is magenta
1428 
1429 
1430             if ( strlen(litvisstr))               // Obscured/faint sector?
1431             {
1432                   _parseList(litvisstr, litvis, sizeof(litvis));
1433 
1434                 if (strpbrk(litvis, "\003\007\010"))
1435                      strcpy(sym, ";CA(CHBLK, 4,CHBRN, 1");
1436             }
1437 
1438             if(sectr2 <= sectr1)
1439                   sectr2 += 360;
1440 
1441             //    Sectors are defined from seaward
1442             if(sectr1 > 180)
1443                   sectr1 -= 180;
1444             else
1445                   sectr1 += 180;
1446 
1447             if(sectr2 > 180)
1448                   sectr2 -= 180;
1449             else
1450                   sectr2 += 180;
1451 
1452             char arc_data[80];
1453             sprintf(arc_data, ",%5.1f, %5.1f, %5.1f, %5.1f", sectr1, sectr2, arc_radius, sector_radius);
1454 
1455             strcat(sym, arc_data);
1456 
1457             wxString ssym(sym, wxConvUTF8);
1458             lights05 = ssym;
1459 
1460             goto l05_end;
1461 
1462 
1463     }
1464 
1465 
1466 l05_end:
1467 
1468 //      if( ps52plib->m_bShowLdisText )
1469       {
1470             // Only show Light in certain position once. Otherwise there will be clutter.
1471             static double lastLat, lastLon;
1472             static wxString lastDescription;
1473             bool isFirstSector = true;
1474 
1475             if( lastLat == obj->m_lat && lastLon == obj->m_lon ) isFirstSector = false;
1476             lastLat = obj->m_lat;
1477             lastLon = obj->m_lon;
1478 
1479             wxString litdsn01 = _LITDSN01( obj );
1480 
1481             if( litdsn01.Len() && isFirstSector ) {
1482                   lastDescription = litdsn01;
1483                   lights05.Append( _T(";TX('") );
1484                   lights05.Append( litdsn01 );
1485 
1486                   if( flare_at_45 )
1487                         lights05.Append( _T("',3,3,3,'15110',2,-1,CHBLK,23)" ) );
1488                   else
1489                         lights05.Append( _T("',3,2,3,'15110',2,0,CHBLK,23)" ) );
1490             }
1491 
1492             if( !isFirstSector && lastDescription != litdsn01 ) {
1493                   lastDescription = litdsn01;
1494                   lights05.Append( _T(";TX('") );
1495                   lights05.Append( litdsn01 );
1496                   lights05.Append( _T("',3,2,3,'15110',2,1,CHBLK,23)" ) );
1497             }
1498       }
1499 
1500       lights05.Append( '\037' );
1501 
1502       char *r = (char *) malloc( lights05.Len() + 1 );
1503       strcpy( r, lights05.mb_str() );
1504 
1505       return r;
1506 }
1507 
1508 
1509 
LIGHTS06(void * param)1510 static void *LIGHTS06 (void *param)
1511 // Remarks: A light is one of the most complex S-57 objects. Its presentation depends on
1512 // whether it is a light on a floating or fixed platform, its range, it's colour and
1513 // so on. This conditional symbology procedure derives the correct
1514 // presentation from these parameters and also generates an area that shows the
1515 // coverage of the light.
1516 //
1517 // Notes on light sectors:
1518 // 1.) The radial leg-lines defining the light sectors are normally drawn to only 25mm
1519 // from the light to avoid clutter (see Part C). However, the mariner should be able to
1520 // select "full light-sector lines" and have the leg-lines extended to the nominal range
1521 // of the light (VALMAR).
1522 //
1523 // 2.) Part C of this procedure symbolizes the sectors at the light itself. In addition,
1524 // it should be possible, upon request, for the mariner to be capable of identifying
1525 // the colour and sector limit lines of the sectors affecting the ship even if the light
1526 // itself is off the display.
1527 // [ed. last sentence in bold]
1528 
1529 
1530 {
1531     #define UNKNOWN_DOUBLE -9;
1532     wxString lights06;
1533 
1534     ObjRazRules *rzRules = (ObjRazRules *)param;
1535     S57Obj *obj = rzRules->obj;
1536 
1537     double valnmr = 9.0;
1538     GetDoubleAttr(obj, "VALNMR", valnmr);
1539 
1540 
1541     char catlitstr[20] = {'\0'};
1542     GetStringAttr(obj, "CATLIT", catlitstr, 19);
1543 
1544     char litvisstr[20] = {'\0'};;
1545     GetStringAttr(obj, "LITVIS", litvisstr, 19);
1546 
1547 
1548     char     catlit[LISTSIZE]  = {'\0'};
1549     char     litvis[LISTSIZE]  = {'\0'};
1550     char     col_str[20] = {'\0'};
1551 
1552     bool     flare_at_45       = false;
1553     double   sectr1            = UNKNOWN_DOUBLE;
1554     double   sectr2            = UNKNOWN_DOUBLE;
1555     double   sweep = 0.;
1556     char     colist[LISTSIZE]  = {'\0'};   // colour list
1557     bool     b_isflare = false;
1558 
1559     wxString orientstr;
1560 
1561     if ( strlen(catlitstr))
1562     {
1563         _parseList(catlitstr, catlit, sizeof(colist));
1564 
1565         if (strpbrk(catlit, "\010\013")) {
1566             lights06.Append(_T(";SY(LIGHTS82)"));
1567             goto l06_end;
1568         }
1569 
1570         if (strpbrk(catlit, "\011")) {
1571             lights06.Append(_T(";SY(LIGHTS81)"));
1572             goto l06_end;
1573         }
1574 
1575         /*
1576          *        if (strpbrk(catlit, "\001\020")) {
1577          *            orientstr = S57_getAttVal(geo, "ORIENT");
1578          *            if (NULL != orientstr) {
1579          *                // FIXME: create a geo object (!?) LINE of lenght VALNMR
1580          *                // using ORIENT (from seaward) & POINT_T position
1581          *                g_string_append(lights05, ";LS(DASH,1,CHBLK)");
1582          }
1583          }
1584          */
1585          }
1586 
1587          // Continuation A
1588 
1589          GetStringAttr(obj, "COLOUR", col_str, 19);
1590 
1591          if (strlen(col_str))
1592              _parseList(col_str, colist, sizeof(colist));
1593          else
1594          {
1595              colist[0] = '\014';  // magenta (12)
1596              colist[1] = '\000';
1597          }
1598 
1599          GetDoubleAttr(obj, "SECTR1", sectr1);
1600          GetDoubleAttr(obj, "SECTR2", sectr2);
1601 
1602 
1603          if ((-9 == sectr1) || (-9 == sectr2))
1604          {
1605              // This is not a sector light
1606 
1607 
1608              wxString ssym;
1609 
1610              if(valnmr < 10.0){
1611 
1612             //TODO create LightArray in s57chart.
1613             //  Then, if another LIGHT object is colocated here, set flare_at_45
1614             /*            if(_atPtPos(obj, rzRules->chart->pLIGHTSArray, false))          // Is this LIGHTS feature colocated with another LIGHTS?
1615              *
1616              *
1617              *            //    If the light is white, yellow, or orange, make it a flare at 45 degrees
1618              *                  if(strpbrk(colist, "\001\005\011"))
1619              *                    flare_at_45 = true;
1620              */
1621                 ssym = _selSYcol(colist, 0, valnmr);              // flare
1622                 b_isflare = true;
1623                 flare_at_45 = false;
1624             }
1625             else
1626             {
1627                 ssym = _selSYcol(colist, 1, valnmr);              // all round light
1628                 b_isflare = false;
1629             }
1630 
1631 
1632             //  Is the light a directional or moire?
1633             if (strpbrk(catlit, "\001\016"))
1634             {
1635                 if (orientstr.Len())
1636                 {
1637                     lights06.Append(ssym);
1638                     lights06.Append(orientstr);
1639                     lights06.Append(_T(";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" ));
1640                 }
1641                 else
1642                     lights06.Append(_T(";SY(QUESMRK1)"));
1643             }
1644             else
1645             {
1646                 lights06.Append(ssym);
1647 
1648                 if(b_isflare)
1649                 {
1650                     if (flare_at_45)
1651                         lights06.Append(_T(",45)"));
1652                     else
1653                         lights06.Append(_T(",135)"));
1654                 }
1655             }
1656 
1657 
1658             goto l06_end;
1659          }
1660 
1661          // Continuation B --sector light
1662          if (-9 == sectr1)
1663          {
1664              sectr1 = 0.0;
1665              sectr2 = 0.0;
1666          }
1667          else
1668              sweep = (sectr1 > sectr2) ? sectr2-sectr1+360 : sectr2-sectr1;
1669 
1670 
1671          if (sweep<1.0 || sweep==360.0)
1672          {
1673              // handle all round light
1674              wxString ssym = _selSYcol(colist, 1, valnmr);           // all round light
1675              lights06.Append(ssym);
1676 
1677 
1678              /*
1679               *        if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
1680               *            GString *litdsn01 = _LITDSN01(geo);
1681               *            if (NULL != litdsn01) {
1682               *                g_string_append(lights05, ";TX('");
1683               *                g_string_append(lights05, litdsn01->str);
1684               *                g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" );
1685               *                g_string_free(litdsn01, TRUE);
1686               }
1687 
1688               }
1689               */
1690              goto l06_end;
1691          }
1692 
1693          /*
1694           *    // scan for other lights with sector overlap at this position
1695           *    // compute light sector radius according to other sector
1696           *    if (1 == S52_state)
1697           *    {
1698           *        _setPtPos(geo, SECTRLIST);
1699           *        g_string_free(lights05, TRUE);
1700           *        return NULL;
1701           }
1702           else
1703           {
1704               extend_arc_radius = _atPtPos(geo, SECTRLIST);
1705 
1706               // passe value via attribs to _renderAC
1707               if (extend_arc_radius)
1708                   // FIXME: draw radius 25 mm
1709                   S57_setAtt(geo, "extend_arc_radius", "Y");
1710               else
1711                   // FIXME: draw radius 20 mm
1712                   S57_setAtt(geo, "extend_arc_radius", "N");
1713           }
1714           */
1715          // setup sector
1716          {
1717              //        Build the (opencpn private) command string like this:
1718              //        e.g.  ";CA(OUTLW, 4,LITRD, 2, sectr1, sectr2, radius)"
1719 
1720 
1721              double arc_radius = 20.;                // mm
1722              double sector_radius = 25.;
1723 
1724              //      Another non-standard extension....
1725              //      Sector light arc radius is scaled if the light has a reasonable VALNMR attribute
1726              if(valnmr > 0)
1727              {
1728                  if(valnmr < 15.0)
1729                      arc_radius = 10.;
1730                  else if(valnmr < 30.0)
1731                      arc_radius = 15.;
1732                  else
1733                      arc_radius = 20.;
1734              }
1735 
1736              char sym[80];
1737              strcpy(sym,";CA(OUTLW, 4");
1738 
1739 
1740              // max 1 color
1741              if ('\0' == colist[1])
1742              {
1743                  if (strpbrk(colist, "\003"))
1744                      strcat(sym, ",LITRD, 2");
1745                  else if (strpbrk(colist, "\004"))
1746                      strcat(sym, ",LITGN, 2");
1747                  else if (strpbrk(colist, "\001\006\013"))
1748                      strcat(sym, ",LITYW, 2");
1749                  else
1750                      strcat(sym, ",CHMGD, 2");                 // default is magenta
1751 
1752              }
1753              else if ('\0' == colist[2])
1754                      {
1755                          if (strpbrk(colist, "\001") && strpbrk(colist, "\003"))
1756                              strcat(sym, ",LITRD, 2");
1757                          else if (strpbrk(colist, "\001") && strpbrk(colist, "\004"))
1758                              strcat(sym, ",LITGN, 2");
1759                          else
1760                              strcat(sym, ",CHMGD, 2");                 // default is magenta
1761                      }
1762                      else
1763                          strcat(sym, ",CHMGD, 2");                 // default is magenta
1764 
1765 
1766                      if ( strlen(litvisstr))               // Obscured/faint sector?
1767             {
1768                 _parseList(litvisstr, litvis, sizeof(litvis));
1769 
1770                 if (strpbrk(litvis, "\003\007\010"))
1771                     strcpy(sym, ";CA(CHBLK, 4,CHBRN, 1");
1772             }
1773 
1774             if(sectr2 <= sectr1)
1775                 sectr2 += 360;
1776 
1777             //    Sectors are defined from seaward
1778                 if(sectr1 > 180)
1779                     sectr1 -= 180;
1780                 else
1781                     sectr1 += 180;
1782 
1783                 if(sectr2 > 180)
1784                     sectr2 -= 180;
1785                 else
1786                     sectr2 += 180;
1787 
1788                 char arc_data[80];
1789                 sprintf(arc_data, ",%5.1f, %5.1f, %5.1f, %5.1f", sectr1, sectr2, arc_radius, sector_radius);
1790 
1791                 strcat(sym, arc_data);
1792 
1793                 wxString ssym(sym, wxConvUTF8);
1794                 lights06 = ssym;
1795 
1796                 goto l06_end;
1797 
1798 
1799          }
1800 
1801 
1802 l06_end:
1803 
1804          //      if( ps52plib->m_bShowLdisText )
1805          {
1806              // Only show Light in certain position once. Otherwise there will be clutter.
1807              static double lastLat, lastLon;
1808              static wxString lastDescription;
1809              bool isFirstSector = true;
1810 
1811              if( lastLat == obj->m_lat && lastLon == obj->m_lon ) isFirstSector = false;
1812                             lastLat = obj->m_lat;
1813              lastLon = obj->m_lon;
1814 
1815              wxString litdsn01 = _LITDSN01( obj );
1816 
1817              if( litdsn01.Len() && isFirstSector ) {
1818                  lastDescription = litdsn01;
1819                  lights06.Append( _T(";TX('") );
1820                  lights06.Append( litdsn01 );
1821 
1822                  if( flare_at_45 )
1823                      lights06.Append( _T("',3,3,3,'15110',2,-1,CHBLK,23)" ) );
1824                  else
1825                      lights06.Append( _T("',3,2,3,'15110',2,0,CHBLK,23)" ) );
1826              }
1827 
1828              if( !isFirstSector && lastDescription != litdsn01 ) {
1829                  lastDescription = litdsn01;
1830                  lights06.Append( _T(";TX('") );
1831                  lights06.Append( litdsn01 );
1832                  lights06.Append( _T("',3,2,3,'15110',2,1,CHBLK,23)" ) );
1833              }
1834          }
1835 
1836          lights06.Append( '\037' );
1837 
1838          char *r = (char *) malloc( lights06.Len() + 1 );
1839          strcpy( r, lights06.mb_str() );
1840 
1841           return r;
1842     }
1843 
1844 
1845 
1846 
LITDSN01(void * param)1847 static void *LITDSN01(void *param)
1848 {
1849         ObjRazRules *rzRules = (ObjRazRules *)param;
1850 //      S57Obj *obj = rzRules->obj;
1851 
1852         printf("s52csny : LITDSN01 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
1853    return NULL;
1854 }
1855 
1856 /*
1857 static void *OBSTRN04a(void *param)
1858 {
1859         ObjRazRules *rzRules = (ObjRazRules *)param;
1860 //      S57Obj *obj = rzRules->obj;
1861 
1862         static int f03;
1863         if(!f03)
1864             printf("s52csny : OBSTRN04 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
1865         f03++;
1866    return NULL;
1867 }
1868 */
1869 
1870 wxString SNDFRM02(S57Obj *obj, double depth_value);
1871 
OBSTRN04(void * param)1872 static void *OBSTRN04 (void *param)
1873 // Remarks: Obstructions or isolated underwater dangers of depths less than the safety
1874 // contour which lie within the safe waters defined by the safety contour are
1875 // to be presented by a specific isolated danger symbol and put in IMO
1876 // category DISPLAYBASE (see (3), App.2, 1.3). This task is performed
1877 // by the sub-procedure "UDWHAZ03" which is called by this symbology
1878 // procedure. Objects of the class "under water rock" are handled by this
1879 // routine as well to ensure a consistent symbolization of isolated dangers on
1880 // the seabed.
1881 {
1882       wxString obstrn04str;
1883 //      GString *sndfrm02str = NULL;
1884       wxString *udwhaz03str = NULL;
1885 //      GString *valsoustr   = S57_getAttVal(geo, "VALSOU");
1886       bool b_promote = false;
1887 
1888       ObjRazRules *rzRules = (ObjRazRules *)param;
1889       S57Obj *obj = rzRules->obj;
1890 
1891       //TODO    Debug Hook
1892 //       if(obj->Index == 534)
1893 //             int yyp = 5;
1894 
1895       double   valsou      = UNKNOWN;
1896       double   depth_value = UNKNOWN;
1897       double   least_depth = UNKNOWN;
1898 
1899 
1900       wxString sndfrm02str;
1901       wxString *quapnt01str = NULL;
1902 
1903       GetDoubleAttr(obj, "VALSOU", valsou);
1904       wxString *objName = GetStringAttrWXS(obj, "OBJNAM");
1905 
1906       if (valsou != UNKNOWN)
1907       {
1908             depth_value = valsou;
1909             sndfrm02str = SNDFRM02(obj, valsou);
1910       }
1911       else
1912       {
1913             if (GEO_AREA == obj->Primitive_type)
1914                   least_depth = _DEPVAL01(obj, least_depth);
1915 
1916             if (UNKNOWN == least_depth)
1917             {
1918                   int catobs = 0;
1919                   GetIntAttr(obj, "CATOBS", catobs);
1920                   int watlev = 0;
1921                   GetIntAttr(obj, "WATLEV", watlev);
1922                   int expsou = 0;
1923                   GetIntAttr(obj, "EXPSOU", expsou);
1924                   if (expsou != 1) {
1925                      if (6 == catobs)
1926                         depth_value = 0.01;
1927                      else if (0 == watlev) // default
1928                         depth_value = -15.0;
1929                      else
1930                      {
1931                         switch (watlev){
1932                               case 5: depth_value =   0.0 ; break;
1933                               case 3: depth_value =   0.01; break;
1934                               case 4:
1935                               case 1:
1936                               case 2:
1937                               default : depth_value = -15.0 ; break;
1938                         }
1939                      }
1940                   }
1941             }
1942             else
1943                   depth_value = least_depth;
1944       }
1945 
1946       udwhaz03str = _UDWHAZ03(obj, depth_value, rzRules, &b_promote);
1947 
1948 
1949       if (GEO_POINT == obj->Primitive_type)
1950       {
1951         // Continuation A
1952             int      sounding    = FALSE;
1953             quapnt01str = CSQUAPNT01(obj);
1954 
1955             if (0 != udwhaz03str->Len())
1956             {
1957                   obstrn04str.Append(*udwhaz03str);
1958                   obstrn04str.Append(*quapnt01str);
1959 
1960                   goto end;
1961             }
1962 
1963             if (UNKNOWN != valsou)
1964             {
1965                    if (valsou <= 20.0)
1966                   {
1967                         int watlev = -9;
1968                         GetIntAttr(obj, "WATLEV", watlev);
1969 
1970                         if (!strncmp(obj->FeatureName, "UWTROC", 6))
1971                         {
1972                               if (-9 == watlev) {  // default
1973                                     obstrn04str.Append(_T(";SY(DANGER51)"));
1974                                     sounding = TRUE;
1975                               } else {
1976                                     switch (watlev){
1977                                           case 3: obstrn04str.Append(_T(";SY(DANGER51)")); sounding = TRUE ; break;
1978                                           case 4:
1979                                           case 5: obstrn04str.Append(_T(";SY(UWTROC04)")); sounding = FALSE; break;
1980                                           default : obstrn04str.Append(_T(";SY(DANGER51)")); sounding = TRUE ; break;
1981                                     }
1982                               }
1983                               if(b_promote){
1984                                   //  Move this UWTROC object to DisplayBase category
1985                                   rzRules->obj->m_DisplayCat = DISPLAYBASE;
1986                               }
1987                         }
1988                         else
1989                         { // OBSTRN
1990                               if (-9 == watlev) { // default
1991                                     obstrn04str.Append(_T(";SY(DANGER01)"));
1992                                     sounding = TRUE;
1993                               } else {
1994                                     switch (watlev) {
1995                                           case 1:
1996                                           case 2: obstrn04str.Append(_T(";SY(LNDARE01)")); sounding = FALSE; break;
1997                                           case 3: obstrn04str.Append(_T(";SY(DANGER52)")); sounding = TRUE;  break;
1998                                           case 4:
1999                                           case 5: obstrn04str.Append(_T(";SY(DANGER53)")); sounding = TRUE; break;
2000                                           default : obstrn04str.Append(_T(";SY(DANGER51)")); sounding = TRUE; break;
2001                                     }
2002                               }
2003                         }
2004                   }
2005                   else
2006                   {  // valsou > 20.0
2007                         obstrn04str.Append(_T(";SY(DANGER52)"));
2008                         sounding = TRUE;
2009                   }
2010             }
2011             else
2012             {  // NO valsou
2013 //                  GString *objlstr   = S57_getAttVal(geo, "OBJL");
2014 //                  int     objl       = (NULL == objlstr)? 0 : atoi(objlstr->str);
2015                   int watlev = -9;
2016                   GetIntAttr(obj, "WATLEV", watlev);
2017 //                  GString *watlevstr = S57_getAttVal(geo, "WATLEV");
2018 
2019                   if (!strncmp(obj->FeatureName, "UWTROC", 6))
2020                   {
2021                         if (watlev == -9)  // default
2022                               obstrn04str.Append(_T(";SY(UWTROC04)"));
2023                         else {
2024                               switch (watlev) {
2025                                     case 2: obstrn04str.Append(_T(";SY(LNDARE01)")); break;
2026                                     case 3: obstrn04str.Append(_T(";SY(UWTROC03)")); break;
2027                                     default: obstrn04str.Append(_T(";SY(UWTROC04)")); break;
2028                               }
2029                         }
2030 
2031                         if(b_promote){
2032                             //  Move this UWTROC object to DisplayBase category
2033                             rzRules->obj->m_DisplayCat = DISPLAYBASE;
2034                         }
2035                   }
2036                   else
2037                   { // OBSTRN
2038                         if ( -9 == watlev) // default
2039                               obstrn04str = _T(";SY(OBSTRN01)");
2040                         else
2041                         {
2042                               switch (watlev) {
2043                                     case 1: obstrn04str.Append(_T(";SY(OBSTRN11)")); break;
2044                                     case 2: obstrn04str.Append(_T(";SY(OBSTRN11)")); break;
2045                                     case 3: obstrn04str.Append(_T(";SY(OBSTRN01)")); break;
2046                                     case 4: obstrn04str.Append(_T(";SY(OBSTRN03)")); break;
2047                                     case 5: obstrn04str.Append(_T(";SY(OBSTRN03)")); break;
2048                                     default : obstrn04str.Append(_T(";SY(OBSTRN01)")); break;
2049                               }
2050                         }
2051                   }
2052              }
2053 
2054              if (sounding)
2055                   obstrn04str.Append(sndfrm02str);
2056 
2057              obstrn04str.Append(*quapnt01str);
2058 
2059             goto end;
2060 
2061       }     // if geopoint
2062       else
2063       {
2064              if (GEO_LINE == obj->Primitive_type)
2065              {
2066                  // Continuation B
2067 
2068                  quapnt01str = CSQUAPNT01(obj);
2069 
2070                  if( quapnt01str->Len() > 1 ) {
2071                      long quapos;
2072                      quapnt01str->ToLong(&quapos);
2073                      if ( 2 <= quapos && quapos < 10){
2074                          if (udwhaz03str->Len())
2075                              obstrn04str.Append(_T(";LC(LOWACC41)"));
2076                          else
2077                              obstrn04str.Append(_T(";LC(LOWACC31)"));
2078                      }
2079                      goto end;
2080                  }
2081 
2082                  if ( udwhaz03str->Len() )
2083                  {
2084                      obstrn04str.Append( _T("LS(DOTT,2,CHBLK)") );
2085                      goto end;
2086                  }
2087 
2088                  if (UNKNOWN != valsou){
2089                      if (valsou <= 20.0)
2090                          obstrn04str.Append( _T(";LS(DOTT,2,CHBLK)") );
2091                      else
2092                          obstrn04str.Append( _T(";LS(DASH,2,CHBLK)") );
2093                  }
2094                  else
2095                      obstrn04str.Append( _T(";LS(DOTT,2,CHBLK)") );
2096 
2097 
2098                  if (udwhaz03str->Len()){
2099                         //  Show the isolated danger symbol at the midpoint of the line
2100                     }
2101                  else {
2102                     if (UNKNOWN != valsou)
2103                         if (valsou <= 20.0)
2104                             obstrn04str.Append(sndfrm02str);
2105                  }
2106                }
2107 
2108             else                // Area feature
2109             {
2110                   quapnt01str = CSQUAPNT01(obj);
2111 
2112                   if (0 != udwhaz03str->Len())
2113                   {
2114                        obstrn04str.Append(_T(";AC(DEPVS);AP(FOULAR01)"));
2115                        obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)"));
2116                        obstrn04str.Append(*udwhaz03str);
2117                        obstrn04str.Append(*quapnt01str);
2118 
2119                         goto end;
2120                   }
2121 
2122                   if (UNKNOWN != valsou) {
2123                 // BUG in CA49995B.000 if we get here because there is no color
2124                 // beside NODATA (ie there is a hole in group 1 area!)
2125                 //g_string_append(obstrn04, ";AC(UINFR)");
2126 
2127                         if (valsou <= 20.0)
2128                               obstrn04str.Append(_T(";LS(DOTT,2,CHBLK)"));
2129                         else
2130                               obstrn04str.Append(_T(";LS(DASH,2,CHBLK)"));
2131 
2132                         obstrn04str.Append(sndfrm02str);
2133 
2134                   } else {
2135                         int watlev = -9;
2136                         GetIntAttr(obj, "WATLEV", watlev);
2137 //                        GString *watlevstr = S57_getAttVal(geo, "WATLEV");
2138 
2139                         if (watlev == -9)   // default
2140                             obstrn04str.Append(_T(";AC(DEPVS);LS(DOTT,2,CHBLK)"));
2141                         else {
2142                             switch (watlev) {
2143                                 case 1:
2144                                 case 2: obstrn04str.Append(_T(";AC(CHBRN);LS(SOLD,2,CSTLN)")); break;
2145                                 case 4: obstrn04str.Append(_T(";AC(DEPIT);LS(DASH,2,CSTLN)")); break;
2146                                 case 5:
2147                                 case 3:
2148                                     {
2149                                         int catobs = -9;
2150                                         GetIntAttr(obj, "CATOBS", catobs);
2151                                         if (6 == catobs)
2152                                             obstrn04str.Append(_T(";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)"));
2153                                         else
2154                                             obstrn04str.Append(_T(";AC(DEPVS);LS(DOTT,2,CHBLK)"));
2155                                     }
2156                                     break;
2157                                 default: obstrn04str.Append(_T(";AC(DEPVS);LS(DOTT,2,CHBLK)"));  break;
2158                             }
2159                         }
2160                   }
2161                   obstrn04str.Append(*quapnt01str);
2162                   goto end;
2163 
2164 /*
2165             // Continuation C (AREAS_T)
2166                   GString *quapnt01str = CSQUAPNT01(geo);
2167                   if (NULL != udwhaz03str) {
2168                         g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01)");
2169                         g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
2170                         g_string_append(obstrn04str, udwhaz03str->str);
2171                         if (NULL != quapnt01str)
2172                               g_string_append(obstrn04str, quapnt01str->str);
2173 
2174                         return obstrn04str;
2175                   }
2176 
2177                   if (UNKNOWN != valsou) {
2178                 // BUG in CA49995B.000 if we get here because there is no color
2179                 // beside NODATA (ie there is a hole in group 1 area!)
2180                 //g_string_append(obstrn04, ";AC(UINFR)");
2181 
2182                         if (valsou <= 20.0)
2183                               g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
2184                         else
2185                               g_string_append(obstrn04str, ";LS(DASH,2,CHBLK)");
2186 
2187                         g_string_append(obstrn04str, sndfrm02str->str);
2188 
2189                   } else {
2190                         GString *watlevstr = S57_getAttVal(geo, "WATLEV");
2191 
2192                         if (NULL == watlevstr)   // default
2193                               g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)");
2194                         else {
2195                               if ('3' == *watlevstr->str) {
2196                                     GString *catobsstr = S57_getAttVal(geo, "CATOBS");
2197                                     if (NULL != catobsstr && '6' == *catobsstr->str)
2198                                           g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)");
2199                               } else {
2200                                     switch (*watlevstr->str) {
2201                                           case '1':
2202                                                 case '2': g_string_append(obstrn04str, ";AC(CHBRN);LS(SOLD,2,CSTLN)"); break;
2203                                                 case '4': g_string_append(obstrn04str, ";AC(DEPIT);LS(DASH,2,CSTLN)"); break;
2204                                           case '5':
2205                                           case '3':
2206                                                 default : g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)");  break;
2207                                     }
2208                               }
2209                         }
2210                   }
2211 
2212                   g_string_append(obstrn04str, quapnt01str->str);
2213 
2214                   return obstrn04str;
2215 */
2216             }     // area
2217       }
2218 
2219 end:
2220 
2221     // This is a specialization, to print OBJNAM for obstructions, if available
2222     // Seen in NZ ENCs, e.g. "Horn Rock"
2223     if(objName)
2224         obstrn04str.Append(_T(";TX(OBJNAM,1,2,3,'15118',-1,-1,CHBLK,26)"));
2225 
2226     obstrn04str.Append('\037');
2227 
2228     char *r = (char *)malloc(obstrn04str.Len() + 1);
2229     strcpy(r, obstrn04str.mb_str());
2230 
2231     delete udwhaz03str;
2232     delete quapnt01str;
2233 
2234     return r;
2235 }
2236 
2237 
2238 
OWNSHP02(void * param)2239 static void *OWNSHP02(void *param)
2240 {
2241         ObjRazRules *rzRules = (ObjRazRules *)param;
2242 //      S57Obj *obj = rzRules->obj;
2243 
2244         printf("s52csny : OWNSHP02 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
2245    return NULL;
2246 }
2247 
PASTRK01(void * param)2248 static void *PASTRK01(void *param)
2249 {
2250         ObjRazRules *rzRules = (ObjRazRules *)param;
2251 //      S57Obj *obj = rzRules->obj;
2252 
2253         printf("s52csny : PASTRK01 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
2254    return NULL;
2255 }
2256 
2257 static void *QUALIN01(void *param);
2258 static void *QUAPNT01(void *param);
2259 
QUAPOS01(void * param)2260 static void *QUAPOS01(void *param)
2261 // Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
2262 // to the spatial object, not the feature object.
2263 // In OpenCPN implementation, QUAPOS of Point Objects has been converted to
2264 // QUALTY attribute of object.
2265 //
2266 // This procedure passes the object to procedure QUALIN01 or QUAPNT01,
2267 // which traces back to the spatial object, retrieves any QUAPOS attributes,
2268 // and returns the appropriate symbolization to QUAPOS01.
2269 {
2270     ObjRazRules *rzRules = (ObjRazRules *)param;
2271     S57Obj *obj = rzRules->obj;
2272 
2273     wxString *q = NULL;
2274 
2275     if (GEO_LINE == obj->Primitive_type)
2276           q = CSQUALIN01(obj);
2277 
2278     else
2279           q = CSQUAPNT01(obj);
2280 
2281     char *r = (char *)malloc(q->Len() + 1);
2282     strcpy(r, q->mb_str());
2283 
2284     delete q;
2285 
2286     return r;
2287 
2288 }
2289 
QUALIN01(void * param)2290 static void *QUALIN01(void *param)
2291 // Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
2292 // only to the spatial component(s) of an object.
2293 //
2294 // A line object may be composed of more than one spatial object.
2295 //
2296 // This procedure looks at each of the spatial
2297 // objects, and symbolizes the line according to the positional accuracy.
2298 {
2299     ObjRazRules *rzRules = (ObjRazRules *)param;
2300     S57Obj *obj = rzRules->obj;
2301 
2302     wxString *q = CSQUALIN01(obj);
2303     char *r = (char *)malloc(q->Len() + 1);
2304     strcpy(r, q->mb_str());
2305 
2306     delete q;
2307     return r;
2308 }
2309 
CSQUALIN01(S57Obj * obj)2310 wxString *CSQUALIN01(S57Obj *obj)
2311 // Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
2312 // only to the spatial component(s) of an object.
2313 //
2314 // A line object may be composed of more than one spatial object.
2315 //
2316 // This procedure looks at each of the spatial
2317 // objects, and symbolizes the line according to the positional accuracy.
2318 {
2319     wxString qualino1;
2320     int quapos = 0;
2321     bool bquapos = GetIntAttr(obj, "QUAPOS", quapos);
2322     const char *line = NULL;
2323 
2324     if (bquapos) {
2325         if ( 2 <= quapos && quapos < 10)
2326             line = "LC(LOWACC21)";
2327     } else {
2328         if (!strncmp("COALNE", obj->FeatureName, 6)) {
2329             int conrad;
2330             bool bconrad = GetIntAttr(obj, "CONRAD", conrad);
2331 
2332             if (bconrad) {
2333                 if (1 == conrad)
2334                     line = "LS(SOLD,3,CHMGF);LS(SOLD,1,CSTLN)";
2335                 else
2336                     line = "LS(SOLD,1,CSTLN)";
2337             } else
2338                 line = "LS(SOLD,1,CSTLN)";
2339 
2340         } else  //LNDARE
2341             line = "LS(SOLD,1,CSTLN)";
2342     }
2343 
2344     if (NULL != line)
2345         qualino1.Append(wxString(line,  wxConvUTF8));
2346 
2347     qualino1.Append('\037');
2348 
2349     wxString *r = new wxString(qualino1);
2350 
2351 /*    char *r = (char *)malloc(qualino1.Len() + 1);
2352     strcpy(r, qualino1.mb_str());
2353 */
2354     return r;
2355 }
2356 
2357 
2358 
QUAPNT01(void * param)2359 static void *QUAPNT01(void *param)
2360 // Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
2361 // only to the spatial component(s) of an object.
2362 //
2363 // This procedure retrieves any QUALTY (ne QUAPOS) attributes, and returns the
2364 // appropriate symbols to the calling procedure.
2365 
2366 {
2367     ObjRazRules *rzRules = (ObjRazRules *)param;
2368     S57Obj *obj = rzRules->obj;
2369 
2370     wxString *q = CSQUAPNT01(obj);
2371 
2372     char *r = (char *)malloc(q->Len() + 1);
2373     strcpy(r, q->mb_str());
2374 
2375     return r;
2376 }
2377 
CSQUAPNT01(S57Obj * obj)2378 wxString *CSQUAPNT01(S57Obj *obj)
2379 // Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
2380 // only to the spatial component(s) of an object.
2381 //
2382 // This procedure retrieves any QUALTY (ne QUAPOS) attributes, and returns the
2383 // appropriate symbols to the calling procedure.
2384 
2385 {
2386     wxString quapnt01;
2387     int accurate  = TRUE;
2388     int qualty = 10;
2389     bool bquapos = GetIntAttr(obj, "QUAPOS", qualty);
2390 
2391     if (bquapos) {
2392         if ( 2 <= qualty && qualty < 10)
2393             accurate = FALSE;
2394     }
2395 
2396     if (!accurate)
2397     {
2398           switch(qualty)
2399           {
2400           case 4:
2401                 quapnt01.Append(_T(";SY(QUAPOS01)")); break;      // "PA"
2402           case 5:
2403                 quapnt01.Append(_T(";SY(QUAPOS02)")); break;      // "PD"
2404           case 7:
2405           case 8:
2406                 quapnt01.Append(_T(";SY(QUAPOS03)")); break;      // "REP"
2407           default:
2408                 quapnt01.Append(_T(";SY(LOWACC03)")); break;      // "?"
2409           }
2410     }
2411 
2412     quapnt01.Append('\037');
2413 
2414     wxString *r = new wxString;
2415 
2416     *r = quapnt01;
2417 
2418 /*    char *r = (char *)malloc(quapnt01.Len() + 1);
2419     strcpy(r, quapnt01.mb_str());
2420 */
2421     return r;
2422 }
2423 
SLCONS03(void * param)2424 static void *SLCONS03(void *param)
2425 
2426     // Remarks: Shoreline construction objects which have a QUAPOS attribute on their
2427 // spatial component indicating that their position is unreliable are symbolized
2428 // by a special linestyle in the place of the varied linestyles normally used.
2429 // Otherwise this procedure applies the normal symbolization.
2430 {
2431     ObjRazRules *rzRules = (ObjRazRules *)param;
2432     S57Obj *obj = rzRules->obj;
2433 
2434 
2435     wxString slcons03;
2436 
2437     bool bvalstr;
2438     int ival;
2439 
2440     const char    *cmdw      = NULL;   // command word
2441 
2442     int quapos;
2443     bool bquapos = GetIntAttr(obj, "QUAPOS", quapos);
2444 
2445 
2446     if (GEO_POINT == obj->Primitive_type) {
2447         if (bquapos) {
2448             if (2 <= quapos && quapos < 10)
2449                 cmdw ="SY(LOWACC01)";
2450         }
2451     } else {
2452 
2453         // This instruction not found in PLIB 3.4, but seems to appear in later PLIB implementations
2454         // by commercial ECDIS providers, so.....
2455         if (GEO_AREA == obj->Primitive_type) {
2456             slcons03 = _T("AP(CROSSX01);");
2457         }
2458 
2459         // GEO_LINE and GEO_AREA are the same
2460         if (bquapos) {
2461             if (2 <= quapos && quapos < 10)
2462                 cmdw ="LC(LOWACC01)";
2463         } else {
2464             bvalstr = GetIntAttr(obj, "CONDTN", ival);
2465 
2466             if (bvalstr && ( 1 == ival || 2 == ival))
2467                 cmdw = "LS(DASH,1,CSTLN)";
2468             else {
2469                 ival = 0;
2470                 bvalstr  = GetIntAttr(obj, "CATSLC", ival);
2471 
2472                 if (bvalstr && ( 6  == ival || 15 == ival || 16 == ival ))      // Some sort of wharf
2473                     cmdw = "LS(SOLD,4,CSTLN)";
2474                 else {
2475                     bvalstr = GetIntAttr(obj, "WATLEV", ival);
2476 
2477                     if (bvalstr && 2 == ival)
2478                         cmdw = "LS(SOLD,2,CSTLN)";
2479                     else
2480                         if (bvalstr && (3 == ival || 4 == ival))
2481                             cmdw = "LS(DASH,2,CSTLN)";
2482                         else
2483                             cmdw = "LS(SOLD,2,CSTLN)";  // default
2484 
2485                 }
2486             }
2487         }
2488     }
2489 
2490 
2491 
2492     // WARNING: not explicitly specified in S-52 !!
2493     // FIXME:this is to put AC(DEPIT) --intertidal area
2494     // Could this be bug in OGR ?
2495     /*
2496     if (AREAS_T == S57_getObjtype(geo)) {
2497         GString    *seabed01  = NULL;
2498         GString    *drval1str = S57_getAttVal(geo, "DRVAL1");
2499         double      drval1    = (NULL == drval1str)? -UNKNOWN : atof(drval1str->str);
2500         GString    *drval2str = S57_getAttVal(geo, "DRVAL2");
2501         double      drval2    = (NULL == drval2str)? -UNKNOWN : atof(drval2str->str);
2502         // NOTE: change sign of infinity (minus) to get out of bound in seabed01
2503 
2504 
2505         PRINTF("***********drval1=%f drval2=%f \n", drval1, drval2);
2506         seabed01 = _SEABED01(drval1, drval2);
2507         slcons03 = g_string_new(seabed01->str);
2508         g_string_free(seabed01, TRUE);
2509 
2510     }
2511     */
2512 
2513     if (NULL != cmdw)
2514         slcons03.Append(wxString(cmdw,  wxConvUTF8));
2515 
2516     //      Match CM93 CMAPECS presentation?
2517 /*
2518     if (GEO_AREA == obj->Primitive_type)
2519           slcons03.Append(_T(";AC(LANDA)"));
2520 */
2521 
2522     slcons03.Append('\037');
2523 
2524     char *r = (char *)malloc(slcons03.Len() + 1);
2525     strcpy(r, slcons03.mb_str());
2526 
2527 
2528     return r;
2529 
2530 
2531 }
2532 
RESARE02(void * param)2533 static void *RESARE02(void *param)
2534 // Remarks: A list-type attribute is used because an area of the object class RESARE may
2535 // have more than one category (CATREA). For example an inshore traffic
2536 // zone might also have fishing and anchoring prohibition and a prohibited
2537 // area might also be a bird sanctuary or a mine field.
2538 //
2539 // This conditional procedure is set up to ensure that the categories of most
2540 // importance to safe navigation are prominently symbolized, and to pass on
2541 // all given information with minimum clutter. Only the most significant
2542 // restriction is symbolized, and an indication of further limitations is given by
2543 // a subscript "!" or "I". Further details are given under conditional
2544 // symbology procedure RESTRN01
2545 //
2546 // Other object classes affected by attribute RESTRN are handled by
2547 // conditional symbology procedure RESTRN01.
2548 {
2549     ObjRazRules *rzRules = (ObjRazRules *)param;
2550     S57Obj *obj = rzRules->obj;
2551 
2552 
2553     wxString resare02;
2554 
2555     wxString *restrnstr = GetStringAttrWXS(obj, "RESTRN");
2556 //    GString *restrnstr        = S57_getAttVal(geo, "RESTRN");
2557 
2558 
2559     char     restrn[LISTSIZE] = {'\0'};
2560 //    GString *catreastr        = S57_getAttVal(geo, "CATREA");
2561     wxString *catreastr = GetStringAttrWXS(obj, "CATREA");
2562 
2563     char     catrea[LISTSIZE] = {'\0'};
2564     wxString symb;
2565     wxString line;
2566     wxString prio;
2567 
2568     if (NULL != catreastr)
2569           _parseList(catreastr->mb_str(), catrea, sizeof(catrea));
2570 
2571     if ( NULL != restrnstr) {
2572           _parseList(restrnstr->mb_str(), restrn, sizeof(restrn));
2573 
2574 
2575         if (strpbrk(restrn, "\007\010\016")) {                          // entry restrictions
2576             // Continuation A
2577             if (strpbrk(restrn, "\001\002\003\004\005\006"))            // anchoring, fishing, trawling
2578                 symb = _T(";SY(ENTRES61)");
2579             else {
2580                 if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
2581                     symb = _T(";SY(ENTRES61)");
2582                 else {
2583                     if (strpbrk(restrn, "\011\012\013\014\015"))
2584                         symb = _T(";SY(ENTRES71)");
2585                     else {
2586                         if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
2587                             symb = _T(";SY(ENTRES71)");
2588                         else
2589                             symb = _T(";SY(ENTRES51)");
2590                     }
2591                 }
2592             }
2593 
2594             if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
2595                 line = _T(";LC(RESARE51)");
2596             else
2597                 line =_T( ";LS(DASH,2,CHMGD)");
2598 
2599             prio = _T(";OP(6---)");  // display prio set to 6
2600 
2601         } else {
2602             if (strpbrk(restrn, "\001\002")) {                          // anchoring
2603                 // Continuation B
2604                 if (strpbrk(restrn, "\003\004\005\006"))
2605                     symb = _T(";SY(ACHRES61)");
2606                 else {
2607                     if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
2608                         symb = _T(";SY(ACHRES61)");
2609                     else {
2610                         if (strpbrk(restrn, "\011\012\013\014\015"))
2611                             symb = _T(";SY(ACHRES71)");
2612                         else {
2613                             if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
2614                                 symb =_T( ";SY(ACHRES71)");
2615                             else
2616                                 symb = _T(";SY(RESTRN51)");
2617                         }
2618                     }
2619                 }
2620 
2621                 if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
2622                     line = _T(";LC(RESARE51)");                // could be ACHRES51 when _drawLC is implemented fully
2623                 else
2624                     line = _T(";LS(DASH,2,CHMGD)");
2625 
2626                 prio = _T(";OP(6---)");  // display prio set to 6
2627 
2628             } else {
2629                 if (strpbrk(restrn, "\003\004\005\006")) {              // fishing/trawling
2630                     // Continuation C
2631                     if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
2632                         symb = _T(";SY(FSHRES51)");
2633                     else {
2634                         if (strpbrk(restrn, "\011\012\013\014\015"))
2635                             symb = _T(";SY(FSHRES71)");
2636                         else{
2637                             if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
2638                                 symb = _T(";SY(FSHRES71)");
2639                             else
2640                                 symb = _T(";SY(FSHRES51)");
2641                         }
2642                     }
2643 
2644                     if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
2645                         line = _T(";LC(FSHRES51)");
2646                     else
2647                         line = _T(";LS(DASH,2,CHMGD)");
2648 
2649                     prio = _T(";OP(6---)");  // display prio set to 6
2650 
2651                 } else {
2652                     if (strpbrk(restrn, "\011\012\013\014\015"))        // diving, dredging, waking...
2653                         symb = _T(";SY(INFARE51)");
2654                     else
2655                         symb = _T(";SY(RSRDEF51)");
2656 
2657                     if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
2658                         line = _T(";LC(CTYARE51)");
2659                     else
2660                         line = _T(";LS(DASH,2,CHMGD)");
2661 
2662                 }
2663                 //  Todo more for s57 3.1  Look at caris catalog ATTR::RESARE
2664 
2665             }
2666         }
2667 
2668     } else {
2669         // Continuation D
2670         if (NULL != catreastr) {
2671             if (strpbrk(catrea, "\001\010\011\014\016\023\025\031")) {
2672                 if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
2673                     symb = _T(";SY(CTYARE71)");
2674                 else
2675                     symb = _T(";SY(CTYARE51)");
2676             } else {
2677                 if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
2678                     symb = _T(";SY(INFARE51)");
2679                 else
2680                     symb = _T(";SY(RSRDEF51)");
2681             }
2682         } else
2683             symb = _T(";SY(RSRDEF51)");
2684 
2685         if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
2686             line = _T(";LC(CTYARE51)");
2687         else
2688             line = _T(";LS(DASH,2,CHMGD)");
2689     }
2690 
2691     // create command word
2692     if (prio.Len())
2693         resare02.Append(prio);
2694     resare02.Append(line);
2695     resare02.Append(symb);
2696 
2697     resare02.Append('\037');
2698 
2699     char *r = (char *)malloc(resare02.Len() + 1);
2700     strcpy(r, resare02.mb_str());
2701 
2702     delete restrnstr;
2703     delete catreastr;
2704 
2705     return r;
2706 }
2707 
2708 
2709 
2710 
2711 
2712 
2713 
2714 
2715 static void *_RESCSP01(void *param);
RESTRN01(void * param)2716 static void *RESTRN01 (void *param)
2717 // Remarks: Objects subject to RESTRN01 are actually symbolised in sub-process
2718 // RESCSP01, since the latter can also be accessed from other conditional
2719 // symbology procedures. RESTRN01 merely acts as a "signpost" for
2720 // RESCSP01.
2721 //
2722 // Object class RESARE is symbolised for the effect of attribute RESTRN in a separate
2723 // conditional symbology procedure called RESARE02.
2724 //
2725 // Since many of the areas concerned cover shipping channels, the number of symbols used
2726 // is minimised to reduce clutter. To do this, values of RESTRN are ranked for significance
2727 // as follows:
2728 // "Traffic Restriction" values of RESTRN:
2729 // (1) RESTRN 7,8: entry prohibited or restricted
2730 //     RESTRN 14: IMO designated "area to be avoided" part of a TSS
2731 // (2) RESTRN 1,2: anchoring prohibited or restricted
2732 // (3) RESTRN 3,4,5,6: fishing or trawling prohibited or restricted
2733 // (4) "Other Restriction" values of RESTRN are:
2734 //     RESTRN 9, 10: dredging prohibited or restricted,
2735 //     RESTRN 11,12: diving prohibited or restricted,
2736 //     RESTRN 13   : no wake area.
2737 {
2738     ObjRazRules *rzRules = (ObjRazRules *)param;
2739     S57Obj *obj = rzRules->obj;
2740 
2741     wxString *restrnstr = GetStringAttrWXS(obj, "RESTRN");
2742 
2743 //    GString *restrn01str = S57_getAttVal(geo, "RESTRN");
2744     char *restrn01    = NULL;
2745 
2746     if (NULL != restrnstr)
2747         restrn01 = (char *)_RESCSP01(param);
2748     else
2749         restrn01 = NULL;
2750 
2751     delete restrnstr;
2752     return restrn01;
2753 }
2754 
_RESCSP01(void * param)2755 static void *_RESCSP01(void *param)
2756 // Remarks: See procedure RESTRN01
2757 {
2758     ObjRazRules *rzRules = (ObjRazRules *)param;
2759     S57Obj *obj = rzRules->obj;
2760 
2761     wxString rescsp01;
2762 //    char *rescsp01         = NULL;
2763     wxString *restrnstr = GetStringAttrWXS(obj, "RESTRN");
2764 //    GString *restrnstr        = S57_getAttVal(geo, "RESTRN");
2765     char     restrn[LISTSIZE] = {'\0'};   // restriction list
2766     wxString symb;
2767     char    *r = NULL;
2768 
2769     if ( restrnstr->Len()) {
2770           _parseList(restrnstr->mb_str(), restrn, sizeof(restrn));
2771 
2772         if (strpbrk(restrn, "\007\010\016")) {
2773             // continuation A
2774             if (strpbrk(restrn, "\001\002\003\004\005\006"))
2775                 symb = _T(";SY(ENTRES61)");
2776             else {
2777                 if (strpbrk(restrn, "\011\012\013\014\015"))
2778                     symb = _T(";SY(ENTRES71)");
2779                 else
2780                     symb = _T(";SY(ENTRES51)");
2781 
2782             }
2783         } else {
2784             if (strpbrk(restrn, "\001\002")) {
2785                 // continuation B
2786                 if (strpbrk(restrn, "\003\004\005\006"))
2787                     symb = _T(";SY(ACHRES61)");
2788                 else {
2789                     if (strpbrk(restrn, "\011\012\013\014\015"))
2790                         symb =_T( ";SY(ACHRES71)");
2791                     else
2792                         symb = _T(";SY(ACHRES51)");
2793                 }
2794 
2795 
2796             } else {
2797                 if (strpbrk(restrn, "\003\004\005\006")) {
2798                     // continuation C
2799                     if (strpbrk(restrn, "\011\012\013\014\015"))
2800                         symb = _T(";SY(FSHRES71)");
2801                     else
2802                         symb = _T(";SY(FSHRES51)");
2803 
2804 
2805                 } else {
2806                     if (strpbrk(restrn, "\011\012\013\014\015"))
2807                         symb = _T(";SY(INFARE51)");
2808                     else
2809                         symb = _T(";SY(RSRDEF51)");
2810 
2811                 }
2812             }
2813         }
2814 
2815         rescsp01.Append(symb);
2816         rescsp01.Append('\037');
2817 
2818         r = (char *)malloc(rescsp01.Len() + 1);
2819         strcpy(r, rescsp01.mb_str());
2820 
2821         delete restrnstr;
2822     }
2823 
2824     return r;
2825 }
2826 
SEABED01(void * param)2827 static void *SEABED01(void *param)
2828 {
2829         ObjRazRules *rzRules = (ObjRazRules *)param;
2830 //      S57Obj *obj = rzRules->obj;
2831 
2832         CPLError((CPLErr)0, 0,"s52csny : SEABED01 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
2833    return NULL;
2834 }
2835 
2836 /*
2837 static void *SNDFRM02(void *param)
2838 {
2839         ObjRazRules *rzRules = (ObjRazRules *)param;
2840 //      S57Obj *obj = rzRules->obj;
2841 
2842         CPLError((CPLErr)0, 0,"s52csny : SNDFRM02 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
2843    return NULL;
2844 }
2845 */
2846 
2847 wxString SNDFRM02(S57Obj *obj, double depth_value);
2848 
SOUNDG02(void * param)2849 static void *SOUNDG02(void *param)
2850 // Remarks: In S-57 soundings are elements of sounding arrays rather than individual
2851 // objects. Thus the conditional symbology methodology must examine each
2852 // sounding of a sounding array one by one. To symbolize the depth values it
2853 // calls the procedure SNDFRM02 which in turn translates the depth values
2854 // into a set of symbols to be shown at the soundings position.
2855 {
2856     // Shortcut.  This CS method causes a branch to an S52plib method
2857     // which splits multi-point soundings into separate point objects,
2858     // and then calls CS(SOUNDG03) on successive points below.
2859       char *r = (char *)malloc(6);
2860       strcpy(r, "MP();");
2861 
2862       return r;
2863 
2864 }
2865 
2866 
SOUNDG03(void * param)2867 static void *SOUNDG03(void *param)
2868 // Remarks:  SOUNDG03 is a private conditional symbology,
2869 // called to render individual points of a multi-point sounding set.
2870 {
2871     ObjRazRules *rzRules = (ObjRazRules *)param;
2872     S57Obj *obj = rzRules->obj;
2873 
2874     wxString s = SNDFRM02(obj, obj->z);
2875 
2876     char *r = (char *)malloc(s.Len() + 1);
2877     strcpy(r, s.mb_str());
2878 
2879     return r;
2880 }
2881 
SNDFRM02(S57Obj * obj,double depth_value_in)2882 wxString SNDFRM02(S57Obj *obj, double depth_value_in)
2883 // Remarks: Soundings differ from plain text because they have to be readable under all
2884 // circumstances and their digits are placed according to special rules. This
2885 // conditional symbology procedure accesses a set of carefully designed
2886 // sounding symbols provided by the symbol library and composes them to
2887 // sounding labels. It symbolizes swept depth and it also symbolizes for low
2888 // reliability as indicated by attributes QUASOU and QUAPOS.
2889 {
2890     wxString sndfrm02;
2891     char     temp_str[LISTSIZE] = {'\0'};
2892     wxString symbol_prefix;
2893 
2894     char symbol_prefix_a[200];
2895 
2896     wxString *tecsoustr = GetStringAttrWXS(obj, "TECSOU");
2897     char     tecsou[LISTSIZE] = {'\0'};
2898 
2899     wxString *quasoustr = GetStringAttrWXS(obj, "QUASOU");
2900     char     quasou[LISTSIZE] = {'\0'};
2901 
2902 
2903     wxString *statusstr = GetStringAttrWXS(obj, "STATUS");
2904     char     status[LISTSIZE] = {'\0'};
2905 
2906     double   leading_digit    = 0.0;
2907 
2908     double safety_depth = S52_getMarinerParam(S52_MAR_SAFETY_DEPTH);
2909 
2910     //      Do the math to convert soundings to ft/metres/fathoms on request
2911     double depth_value = depth_value_in;
2912 
2913     //      If the sounding value from the ENC (or SENC) is bogus, so state
2914     if(depth_value_in > 40000.)
2915         depth_value = 99999.;
2916     if(depth_value_in < -1000.)
2917         depth_value = 0.;
2918 
2919     switch(ps52plib->m_nDepthUnitDisplay)
2920     {
2921         case 0:
2922             depth_value = depth_value   * 3 * 39.37 / 36;              // feet
2923             safety_depth = safety_depth * 3 * 39.37 / 36;
2924             break;
2925         case 2:
2926             depth_value = depth_value   * 3 * 39.37 / (36 * 6);        // fathoms
2927             safety_depth = safety_depth * 3 * 39.37 / (36 * 6);
2928             break;
2929         default:
2930             break;
2931     }
2932 
2933 
2934     // FIXME: test to fix the rounding error (!?)
2935     depth_value  += (depth_value > 0.0)? 0.01: -0.01;
2936     leading_digit = (int) fabs(depth_value);
2937 
2938     if (depth_value <= safety_depth)            //S52_getMarinerParam(S52_MAR_SAFETY_DEPTH)
2939         symbol_prefix = _T("SOUNDS");
2940     else
2941         symbol_prefix = _T("SOUNDG");
2942 
2943     strcpy(symbol_prefix_a,symbol_prefix.mb_str());
2944 
2945     if (NULL != tecsoustr)
2946     {
2947         _parseList(tecsoustr->mb_str(), tecsou, sizeof(tecsou));
2948         if (strpbrk(tecsou, "\006"))
2949         {
2950             chk_snprintf(temp_str, LISTSIZE, ";SY(%sB1)", symbol_prefix_a);
2951             sndfrm02.Append(wxString(temp_str, wxConvUTF8));
2952         }
2953     }
2954 
2955     if (NULL != quasoustr) _parseList(quasoustr->mb_str(), quasou, sizeof(quasou));
2956     if (NULL != statusstr) _parseList(statusstr->mb_str(), status, sizeof(status));
2957 
2958     if (strpbrk(quasou, "\003\004\005\010\011") || strpbrk(status, "\022"))
2959     {
2960         chk_snprintf(temp_str, LISTSIZE, ";SY(%sC2)", symbol_prefix_a);
2961         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
2962     }
2963     else
2964     {
2965         int quapos = 0;
2966         GetIntAttr(obj, "QUAPOS", quapos);
2967         if (0 != quapos)
2968         {
2969             if (2 <= quapos && quapos < 10)
2970             {
2971                 chk_snprintf(temp_str, LISTSIZE,
2972                              ";SY(%sC2)", symbol_prefix_a);
2973                 sndfrm02.Append(wxString(temp_str, wxConvUTF8));
2974             }
2975         }
2976     }
2977 
2978     // Continuation A
2979     if (fabs(depth_value) < 10.0) {
2980 
2981         //      If showing as "feet", round off to one digit only
2982         if( (ps52plib->m_nDepthUnitDisplay == 0) && (depth_value > 0) ){
2983             double r1 = depth_value ;
2984             depth_value = wxRound( r1 ) ;
2985             leading_digit = (int) depth_value;
2986         }
2987 
2988         if (depth_value < 10.0) {
2989             // can be above water (negative)
2990             int fraction = (int)ABS((fabs(depth_value) - leading_digit)*10);
2991 
2992 
2993             chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)",
2994                          symbol_prefix_a, (int)ABS(leading_digit));
2995             sndfrm02.Append(wxString(temp_str, wxConvUTF8));
2996             if(fraction > 0) {
2997                 chk_snprintf(temp_str, LISTSIZE,
2998                              ";SY(%s5%1i)", symbol_prefix_a, fraction);
2999                 sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3000             }
3001 
3002             // above sea level (negative)
3003             if (depth_value < 0.0)
3004             {
3005                 chk_snprintf(temp_str, LISTSIZE,
3006                              ";SY(%sA1)", symbol_prefix_a);
3007                 sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3008             }
3009             goto return_point;
3010         }
3011     }
3012 
3013     if (fabs(depth_value) < 31.0) {
3014         bool b_2digit = false;
3015         double depth_value_pos = fabs(depth_value);
3016 
3017         //      If showing as "feet", round off to two digits only
3018         if( (ps52plib->m_nDepthUnitDisplay == 0) && (depth_value_pos > 0) ){
3019             double r1 = depth_value ;
3020             depth_value = wxRound( r1 ) ;
3021             leading_digit = (int) depth_value_pos;
3022             b_2digit = true;
3023         }
3024 
3025 
3026         double fraction = fabs(depth_value_pos - floor(leading_digit));
3027 
3028         if (fraction != 0.0) {
3029             fraction = fraction * 10;
3030             if (leading_digit >= 10.0)
3031             {
3032                 chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)",
3033                              symbol_prefix_a, (int)leading_digit/10);
3034                 sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3035             }
3036 
3037             double first_digit = floor(leading_digit / 10);
3038             int secnd_digit = (int)(floor(leading_digit - (first_digit * 10)));
3039             chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)",
3040                          symbol_prefix_a, secnd_digit/*(int)leading_digit*/);
3041             sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3042 
3043             if(!b_2digit){
3044                 if((int)fraction > 0) {
3045                     chk_snprintf(temp_str, LISTSIZE, ";SY(%s5%1i)",
3046                                  symbol_prefix_a, (int)fraction);
3047                     sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3048                 }
3049             }
3050 
3051             if (depth_value < 0.0)
3052             {
3053                 chk_snprintf(temp_str, LISTSIZE, ";SY(%sA1)", symbol_prefix_a);
3054                 sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3055             }
3056 
3057             goto return_point;
3058         }
3059     }
3060 
3061     // Continuation B
3062     if (fabs(depth_value) < 100.0)
3063     {
3064         leading_digit = fabs(leading_digit);
3065 
3066         double first_digit = floor(leading_digit / 10);
3067         double secnd_digit = floor(leading_digit - (first_digit * 10));
3068 
3069         if (depth_value < 0.0)
3070         {
3071             chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)",
3072                          symbol_prefix_a, (int)first_digit);
3073             sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3074             chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)",
3075                          symbol_prefix_a, (int)secnd_digit);
3076             sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3077             chk_snprintf(temp_str, LISTSIZE, ";SY(%sA1)", symbol_prefix_a);
3078             sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3079         }
3080         else{
3081             chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)",
3082                          symbol_prefix_a, (int)first_digit);
3083             sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3084             chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)",
3085                          symbol_prefix_a, (int)secnd_digit);
3086             sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3087         }
3088         goto return_point;
3089     }
3090 
3091     if (depth_value < 1000.0)
3092     {
3093         double first_digit = floor(leading_digit / 100);
3094         double secnd_digit = floor((leading_digit - (first_digit * 100)) / 10);
3095         double third_digit = floor(leading_digit - (first_digit * 100) - (secnd_digit * 10));
3096 
3097         chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)",
3098                      symbol_prefix_a, (int)first_digit);
3099         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3100         chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)",
3101                      symbol_prefix_a, (int)secnd_digit);
3102         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3103         chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)",
3104                      symbol_prefix_a, (int)third_digit);
3105         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3106 
3107         goto return_point;
3108     }
3109 
3110     if (depth_value < 10000.0)
3111     {
3112         double first_digit = floor(leading_digit / 1000);
3113         double secnd_digit = floor((leading_digit - (first_digit * 1000)) / 100);
3114         double third_digit = floor((leading_digit - (first_digit * 1000) - (secnd_digit * 100)) / 10);
3115         double last_digit  = floor(leading_digit - (first_digit * 1000) - (secnd_digit * 100) - (third_digit * 10)) ;
3116 
3117         chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)",
3118                      symbol_prefix_a, (int)first_digit);
3119         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3120         chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)",
3121                      symbol_prefix_a, (int)secnd_digit);
3122         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3123         chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)",
3124                      symbol_prefix_a, (int)third_digit);
3125         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3126         chk_snprintf(temp_str, LISTSIZE, ";SY(%s4%1i)",
3127                      symbol_prefix_a, (int)last_digit);
3128         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3129 
3130         goto return_point;
3131     }
3132 
3133     // Continuation C
3134     {
3135         double first_digit  = floor(leading_digit / 10000);
3136         double secnd_digit  = floor((leading_digit - (first_digit * 10000)) / 1000);
3137         double third_digit  = floor((leading_digit - (first_digit * 10000) - (secnd_digit * 1000)) / 100 );
3138         double fourth_digit = floor((leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100)) / 10 ) ;
3139         double last_digit   = floor(leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100) - (fourth_digit * 10)) ;
3140 
3141         chk_snprintf(temp_str, LISTSIZE, ";SY(%s3%1i)",
3142                      symbol_prefix_a, (int)first_digit);
3143         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3144         chk_snprintf(temp_str, LISTSIZE, ";SY(%s2%1i)",
3145                      symbol_prefix_a, (int)secnd_digit);
3146         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3147         chk_snprintf(temp_str, LISTSIZE, ";SY(%s1%1i)",
3148                      symbol_prefix_a, (int)third_digit);
3149         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3150         chk_snprintf(temp_str, LISTSIZE, ";SY(%s0%1i)",
3151                      symbol_prefix_a, (int)fourth_digit);
3152         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3153         chk_snprintf(temp_str, LISTSIZE, ";SY(%s4%1i)",
3154                      symbol_prefix_a, (int)last_digit);
3155         sndfrm02.Append(wxString(temp_str, wxConvUTF8));
3156 
3157         goto return_point;
3158     }
3159 
3160     return_point:
3161     sndfrm02.Append('\037');
3162 
3163     delete tecsoustr;
3164     delete quasoustr;
3165     delete statusstr;
3166 
3167     return sndfrm02;
3168 }
3169 
3170 
TOPMAR01(void * param)3171 static void *TOPMAR01 (void *param)
3172 // Remarks: Topmark objects are to be symbolized through consideration of their
3173 // platforms e.g. a buoy. Therefore this conditional symbology procedure
3174 // searches for platforms by looking for other objects that are located at the
3175 // same position.. Based on the finding whether the platform is rigid or
3176 // floating, the respective upright or sloping symbol is selected and presented
3177 // at the objects location. Buoyf symbols and topmark symbols have been
3178 // carefully designed to fit to each other when combined at the same position.
3179 // The result is a composed symbol that looks like the traditional symbols the
3180 // mariner is used to.
3181 {
3182     ObjRazRules *rzRules = (ObjRazRules *)param;
3183     S57Obj *obj = rzRules->obj;
3184 
3185     int top_int = 0;
3186     bool battr = GetIntAttr(obj, "TOPSHP", top_int);
3187 
3188     wxString sy;
3189 
3190     if (!battr)
3191         sy = _T(";SY(QUESMRK1)");
3192     else {
3193         int floating    = FALSE; // not a floating platform
3194         int topshp      = (!battr) ? 0 : top_int;
3195 
3196 
3197         if (TRUE == _atPtPos(obj, GetChartFloatingATONArray( rzRules ), false))
3198             floating = TRUE;
3199         else
3200             // FIXME: this test is wierd since it doesn't affect 'floating'
3201             if (TRUE == _atPtPos(obj, GetChartRigidATONArray( rzRules ), false))
3202                 floating = FALSE;
3203 
3204 
3205         if (floating) {
3206             // floating platform
3207             switch (topshp) {
3208                 case 1 : sy = _T(";SY(TOPMAR02)"); break;
3209                 case 2 : sy = _T(";SY(TOPMAR04)"); break;
3210                 case 3 : sy = _T(";SY(TOPMAR10)"); break;
3211                 case 4 : sy = _T(";SY(TOPMAR12)"); break;
3212 
3213                 case 5 : sy = _T(";SY(TOPMAR13)"); break;
3214                 case 6 : sy = _T(";SY(TOPMAR14)"); break;
3215                 case 7 : sy = _T(";SY(TOPMAR65)"); break;
3216                 case 8 : sy = _T(";SY(TOPMAR17)"); break;
3217 
3218                 case 9 : sy = _T(";SY(TOPMAR16)"); break;
3219                 case 10: sy = _T(";SY(TOPMAR08)"); break;
3220                 case 11: sy = _T(";SY(TOPMAR07)"); break;
3221                 case 12: sy = _T(";SY(TOPMAR14)"); break;
3222 
3223                 case 13: sy = _T(";SY(TOPMAR05)"); break;
3224                 case 14: sy = _T(";SY(TOPMAR06)"); break;
3225                 case 17: sy = _T(";SY(TMARDEF2)"); break;
3226                 case 18: sy = _T(";SY(TOPMAR10)"); break;
3227 
3228                 case 19: sy = _T(";SY(TOPMAR13)"); break;
3229                 case 20: sy = _T(";SY(TOPMAR14)"); break;
3230                 case 21: sy = _T(";SY(TOPMAR13)"); break;
3231                 case 22: sy = _T(";SY(TOPMAR14)"); break;
3232 
3233                 case 23: sy = _T(";SY(TOPMAR14)"); break;
3234                 case 24: sy = _T(";SY(TOPMAR02)"); break;
3235                 case 25: sy = _T(";SY(TOPMAR04)"); break;
3236                 case 26: sy = _T(";SY(TOPMAR10)"); break;
3237 
3238                 case 27: sy = _T(";SY(TOPMAR17)"); break;
3239                 case 28: sy = _T(";SY(TOPMAR18)"); break;
3240                 case 29: sy = _T(";SY(TOPMAR02)"); break;
3241                 case 30: sy = _T(";SY(TOPMAR17)"); break;
3242 
3243                 case 31: sy = _T(";SY(TOPMAR14)"); break;
3244                 case 32: sy = _T(";SY(TOPMAR10)"); break;
3245                 case 33: sy = _T(";SY(TMARDEF2)"); break;
3246                 default: sy = _T(";SY(TMARDEF2)"); break;
3247             }
3248         } else {
3249             // not a floating platform
3250             switch (topshp) {
3251                 case 1 : sy = _T(";SY(TOPMAR22)"); break;
3252                 case 2 : sy = _T(";SY(TOPMAR24)"); break;
3253                 case 3 : sy = _T(";SY(TOPMAR30)"); break;
3254                 case 4 : sy = _T(";SY(TOPMAR32)"); break;
3255 
3256                 case 5 : sy = _T(";SY(TOPMAR33)"); break;
3257                 case 6 : sy = _T(";SY(TOPMAR34)"); break;
3258                 case 7 : sy = _T(";SY(TOPMAR85)"); break;
3259                 case 8 : sy = _T(";SY(TOPMAR86)"); break;
3260 
3261                 case 9 : sy = _T(";SY(TOPMAR36)"); break;
3262                 case 10: sy = _T(";SY(TOPMAR28)"); break;
3263                 case 11: sy = _T(";SY(TOPMAR27)"); break;
3264                 case 12: sy = _T(";SY(TOPMAR14)"); break;
3265 
3266                 case 13: sy = _T(";SY(TOPMAR25)"); break;
3267                 case 14: sy = _T(";SY(TOPMAR26)"); break;
3268                 case 15: sy = _T(";SY(TOPMAR88)"); break;
3269                 case 16: sy = _T(";SY(TOPMAR87)"); break;
3270 
3271                 case 17: sy = _T(";SY(TMARDEF1)"); break;
3272                 case 18: sy = _T(";SY(TOPMAR30)"); break;
3273                 case 19: sy = _T(";SY(TOPMAR33)"); break;
3274                 case 20: sy = _T(";SY(TOPMAR34)"); break;
3275 
3276                 case 21: sy = _T(";SY(TOPMAR33)"); break;
3277                 case 22: sy = _T(";SY(TOPMAR34)"); break;
3278                 case 23: sy = _T(";SY(TOPMAR34)"); break;
3279                 case 24: sy = _T(";SY(TOPMAR22)"); break;
3280 
3281                 case 25: sy = _T(";SY(TOPMAR24)"); break;
3282                 case 26: sy = _T(";SY(TOPMAR30)"); break;
3283                 case 27: sy = _T(";SY(TOPMAR86)"); break;
3284                 case 28: sy = _T(";SY(TOPMAR89)"); break;
3285 
3286                 case 29: sy = _T(";SY(TOPMAR22)"); break;
3287                 case 30: sy = _T(";SY(TOPMAR86)"); break;
3288                 case 31: sy = _T(";SY(TOPMAR14)"); break;
3289                 case 32: sy = _T(";SY(TOPMAR30)"); break;
3290                 case 33: sy = _T(";SY(TMARDEF1)"); break;
3291                 default: sy = _T(";SY(TMARDEF1)"); break;
3292             }
3293         }
3294 
3295     }
3296 
3297     wxString topmar;
3298     topmar.Append(sy);
3299     topmar.Append('\037');
3300 
3301     char *r = (char *)malloc(topmar.Len() + 1);
3302     strcpy(r, topmar.mb_str());
3303 
3304     return r;
3305 }
3306 
3307 
UDWHAZ03(void * param)3308 static void *UDWHAZ03(void *param)
3309 {
3310         ObjRazRules *rzRules = (ObjRazRules *)param;
3311 //      S57Obj *obj = rzRules->obj;
3312 
3313         CPLError((CPLErr)0, 0,"s52csny : UDWHAZ03 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
3314    return NULL;
3315 }
3316 
VESSEL01(void * param)3317 static void *VESSEL01(void *param)
3318 {
3319         ObjRazRules *rzRules = (ObjRazRules *)param;
3320 //      S57Obj *obj = rzRules->obj;
3321 
3322         CPLError((CPLErr)0, 0,"s52csny : VESSEL01 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
3323    return NULL;
3324 }
3325 
VRMEBL01(void * param)3326 static void *VRMEBL01(void *param)
3327 {
3328         ObjRazRules *rzRules = (ObjRazRules *)param;
3329 //      S57Obj *obj = rzRules->obj;
3330 
3331         CPLError((CPLErr)0, 0,"s52csny : VRMEBL01 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
3332    return NULL;
3333 }
3334 
3335 /*
3336 static void *WRECKS02a(void *param)
3337 {
3338         ObjRazRules *rzRules = (ObjRazRules *)param;
3339 //      S57Obj *obj = rzRules->obj;
3340 
3341         static int f07;
3342         if(!f07)
3343                 CPLError((CPLErr)0, 0,"s52csny : WRECKS02 ERROR no conditional symbology for: %s\n",rzRules->LUP->OBCL);
3344         f07++;
3345    return NULL;
3346 }
3347 */
3348 
WRECKS02(void * param)3349 static void *WRECKS02 (void *param)
3350 // Remarks: Wrecks of depths less than the safety contour which lie within the safe waters
3351 // defined by the safety contour are to be presented by a specific isolated
3352 // danger symbol and put in IMO category DISPLAYBASE (see (3), App.2,
3353 // 1.3). This task is performed by the sub-procedure "UDWHAZ03" which is
3354 // called by this symbology procedure.
3355 {
3356     wxString wrecks02str;
3357     wxString sndfrm02str;
3358     wxString *udwhaz03str = NULL;
3359     wxString *quapnt01str = NULL;
3360     double   least_depth = UNKNOWN;
3361     double   depth_value = UNKNOWN;
3362 //    GString *valsoustr   = S57_getAttVal(geo, "VALSOU");
3363     double   valsou      = UNKNOWN;
3364     bool b_promote = false;
3365 
3366     ObjRazRules *rzRules = (ObjRazRules *)param;
3367     S57Obj *obj = rzRules->obj;
3368 
3369     GetDoubleAttr(obj, "VALSOU", valsou);
3370 
3371     int watlev = -9;
3372     GetIntAttr(obj, "WATLEV", watlev);
3373     int catwrk = -9;
3374     GetIntAttr(obj, "CATWRK", catwrk);
3375 
3376     int quasou = -9;
3377     // QUASOU is a list ie a string for us
3378     wxString *quasoustr = GetStringAttrWXS(obj, "QUASOU");
3379     char     quasouchar[LISTSIZE] = {'\0'};
3380 
3381     double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
3382 
3383     if (UNKNOWN != valsou)
3384     {
3385         depth_value = valsou;
3386         sndfrm02str = SNDFRM02(obj, depth_value);
3387     }
3388     else
3389     {
3390         if (GEO_AREA == obj->Primitive_type)
3391             least_depth = _DEPVAL01(obj, least_depth);
3392 
3393         if (least_depth == UNKNOWN)
3394 /*
3395         {
3396             // WARNING: ambiguity removed in WRECKS03 (see update)
3397 
3398             if (-9 == watlev) // default
3399                 depth_value = -15.0;
3400             else
3401                 switch (watlev)
3402                   { // ambiguous
3403                     case 1:
3404                     case 2: depth_value = -15.0 ; break;
3405                     case 3: depth_value =   0.01; break;
3406                     case 4: depth_value = -15.0 ; break;
3407                     case 5: depth_value =   0.0 ; break;
3408                     case 6: depth_value = -15.0 ; break;
3409                     default :
3410                           {
3411                               if (-9 != catwrk)
3412                               {
3413                                     switch (catwrk)
3414                                     {
3415                                           case 1: depth_value =  20.0; break;
3416                                           case 2: depth_value =   0.0; break;
3417                                           case 4:
3418                                           case 5: depth_value = -15.0; break;
3419                                     }
3420                                }
3421                           }
3422                   }
3423         }
3424 */
3425 ////////////////////////////////////////////////
3426 //    DSR New Logic Here  (FIXME)
3427         {
3428            if (-9 != catwrk)
3429            {
3430                   switch (catwrk)
3431                   {
3432                         case 1: depth_value =  20.0; break;       // safe
3433                         case 2: depth_value =   0.0; break;       // dangerous
3434                         case 4:
3435                         case 5: depth_value = -15.0; break;
3436                   }
3437             }
3438             else
3439             {
3440                 if (-9 == watlev) // default
3441                       depth_value = -15.0;
3442                 else
3443                   switch (watlev)
3444                   {
3445                     case 1:
3446                     case 2: depth_value = -15.0 ; break;
3447                     case 3: depth_value =   0.01; break;
3448                     case 4: depth_value = -15.0 ; break;
3449                     case 5: depth_value =   0.0 ; break;
3450                     case 6: depth_value = -15.0 ; break;
3451                   }
3452             }
3453 
3454         }
3455 
3456         else
3457             depth_value = least_depth;
3458 
3459 
3460     }
3461     if (NULL != quasoustr) _parseList(quasoustr->mb_str(), quasouchar, sizeof(quasouchar));
3462 
3463     if (quasouchar[0] == 0 || NULL == strpbrk(quasouchar, "\07"))
3464     {
3465 	    //Fixes FS 165   XXX where it is?
3466 	    // 7 is 'least depth unknown, safe clearance at value shown'
3467 		udwhaz03str = _UDWHAZ03(obj, depth_value, rzRules, &b_promote);
3468 
3469     }
3470 	else
3471 	{
3472         quasou = 7;
3473 		udwhaz03str = new wxString();
3474     }
3475     quapnt01str = CSQUAPNT01(obj);
3476 
3477     if (GEO_POINT == obj->Primitive_type) {
3478           if (0 != udwhaz03str->Len()) {
3479             wrecks02str = wxString(*udwhaz03str);
3480 
3481           wrecks02str.Append(*quapnt01str);
3482 
3483         } else {
3484             // Continuation A (POINT_T)
3485             if (UNKNOWN != valsou) {
3486 ///////////////////////////////////////////
3487 //    DSR New logic here, FIXME check s52 specs
3488 
3489 /*
3490                 if (valsou <= 20.0)
3491                 {
3492                     wrecks02str = wxString(";SY(DANGER51)");
3493                     if (NULL != sndfrm02str)
3494                         wrecks02str.Append(sndfrm02str);
3495                 }
3496                 else
3497                     wrecks02str = wxString(";SY(DANGER52)");
3498 */
3499                 if((valsou < safety_contour)/* || (2 == catwrk)*/)    // maybe redundant, seems like wrecks with valsou < 20
3500                                                                   // are always coded as "dangerous wrecks"
3501                                                                   // Excluding (2 == catwrk) matches Caris logic
3502                       wrecks02str = wxString(_T(";SY(DANGER51)"));
3503                 else
3504                       wrecks02str = wxString(_T(";SY(DANGER52)"));
3505 				wrecks02str.Append(_T(";TX('Wk',2,1,2,'15110',1,0,CHBLK,21)"));
3506 				if ( 7 == quasou ) //Fixes FS 165
3507 					wrecks02str.Append(_T(";SY(WRECKS07)"));
3508 
3509                 wrecks02str.Append(sndfrm02str);       // always show valsou depth
3510 ///////////////////////////////////////////
3511 
3512                 wrecks02str.Append(*udwhaz03str);
3513                 wrecks02str.Append(*quapnt01str);
3514 
3515             } else {
3516                 wxString sym;
3517 
3518                 if (-9 != catwrk && -9 != watlev) {
3519                     if (1 == catwrk && 3 == watlev)
3520                           sym =_T(";SY(WRECKS04)");
3521                     else {
3522                         if (2 == catwrk && 3 == watlev)
3523                               sym = _T(";SY(WRECKS05)");
3524                         else {
3525                             if (4 == catwrk || 5 == catwrk)
3526                                   sym = _T(";SY(WRECKS01)");
3527                             else {
3528                                 if (1 == watlev ||
3529                                     2 == watlev ||
3530                                     5 == watlev ||
3531                                     4 == watlev ){
3532                                       sym = _T(";SY(WRECKS01)");
3533                                 } else
3534                                       sym = _T(";SY(WRECKS05)"); // default
3535 
3536                             }
3537                         }
3538                     }
3539                 }
3540 
3541                 wrecks02str = sym;
3542                 if (NULL != quapnt01str)
3543                     wrecks02str.Append(*quapnt01str);
3544 
3545             }
3546 
3547         }
3548 
3549 
3550     } else {
3551         // Continuation B (AREAS_T)
3552         int quapos = 0;
3553         GetIntAttr(obj, "QUAPOS", quapos);
3554 
3555         wxString line;
3556 
3557         if (2 <= quapos && quapos < 10)
3558               line = _T(";LC(LOWACC41)");
3559         else {
3560               if ( 0 != udwhaz03str->Len())
3561                   line = _T(";LS(DOTT,2,CHBLK)");
3562             else {
3563                  if (UNKNOWN != valsou){
3564                      if (valsou <= 20)
3565                            line = _T(";LS(DOTT,2,CHBLK)");
3566                      else
3567                            line = _T(";LS(DASH,2,CHBLK)");
3568                  } else {
3569 
3570                      if (-9 == watlev)
3571                            line = _T(";LS(DOTT,2,CSTLN)");
3572                      else {
3573                          switch (watlev){
3574                              case 1:
3575                              case 2: line = _T(";LS(SOLD,2,CSTLN)"); break;
3576                              case 4: line = _T(";LS(DASH,2,CSTLN)"); break;
3577                              case 3:
3578                              case 5:
3579 
3580                              default : line = _T(";LS(DOTT,2,CSTLN)"); break;
3581                          }
3582                      }
3583 
3584                  }
3585             }
3586         }
3587         wrecks02str = wxString(line);
3588 
3589         if (UNKNOWN != valsou) {
3590             if (valsou <= 20) {
3591                     wrecks02str.Append(*udwhaz03str);
3592                     wrecks02str.Append(*quapnt01str);
3593                     wrecks02str.Append(sndfrm02str);
3594 
3595             } else {
3596                 // NOTE: ??? same as above ???
3597                     wrecks02str.Append(*udwhaz03str);
3598                     wrecks02str.Append(*quapnt01str);
3599             }
3600         } else {
3601             wxString ac;
3602 
3603             if (-9 == watlev)
3604                   ac = _T(";AC(DEPVS)");
3605             else
3606                 switch (watlev) {
3607                           case 1:
3608                           case 2: ac = _T(";AC(CHBRN)"); break;
3609                           case 4: ac = _T(";AC(DEPIT)"); break;
3610                           case 5:
3611                           case 3:
3612                           default : ac = _T(";AC(DEPVS)"); break;
3613                 }
3614 
3615             wrecks02str.Append(ac);
3616 
3617             wrecks02str.Append(*udwhaz03str);
3618             wrecks02str.Append(*quapnt01str);
3619         }
3620     }
3621 
3622     wrecks02str.Append('\037');
3623 
3624     char *r = (char *)malloc(wrecks02str.Len() + 1);
3625     strcpy(r, wrecks02str.mb_str());
3626 
3627     delete udwhaz03str;
3628     delete quapnt01str;
3629     delete quasoustr;
3630     return r;
3631 }
3632 
3633 
_LITDSN01(S57Obj * obj)3634 static wxString _LITDSN01(S57Obj *obj)
3635 // Remarks: In S-57 the light characteristics are held as a series of attributes values. The
3636 // mariner may wish to see a light description text string displayed on the
3637 // screen similar to the string commonly found on a paper chart. This
3638 // conditional procedure, reads the attribute values from the above list of
3639 // attributes and composes a light description string which can be displayed.
3640 // This procedure is provided as a C function which has as input, the above
3641 // listed attribute values and as output, the light description.
3642 {
3643       // CATLIT, LITCHR, COLOUR, HEIGHT, LITCHR, SIGGRP, SIGPER, STATUS, VALNMR
3644 
3645       char colist[20];
3646       wxString return_value;
3647 #if 0
3648       // XXX CATLIT
3649       int catlit = -9;
3650       GetIntAttr(obj, "CATLIT", catlit);
3651 
3652       if(-9 != catlit)
3653       {
3654       }
3655 #endif
3656 
3657     /*
3658       1: directional function  IP 30.1-3;  475.7;
3659       2: rear/upper light
3660       3: front/lower light
3661       4: leading light           IP 20.1-3;      475.6;
3662       5: aero light                  IP 60;      476.1;
3663       6: air obstruction light IP 61;      476.2;
3664       7: fog detector light        IP 62;  477;
3665       8: flood light                 IP 63;      478.2;
3666       9: strip light                 IP 64;      478.5;
3667       10: subsidiary light          IP 42;  471.8;
3668       11: spotlight
3669       12: front
3670       13: rear
3671       14: lower
3672       15: upper
3673       16: moire' effect           IP 31;    475.8;
3674       17: emergency
3675       18: bearing light                   478.1;
3676       19: horizontally disposed
3677       20: vertically disposed
3678     */
3679 
3680     // LITCHR
3681       int litchr = -9;
3682       wxString spost(_T(""));
3683       GetIntAttr(obj, "LITCHR", litchr);
3684 
3685       bool b_grp2 = false;                      // 2 GRP attributes expected
3686       if(-9 != litchr)
3687       {
3688             switch (litchr)
3689             {
3690 /*
3691                   case 1:   return_value.Append(_T("F"));    break;
3692                   case 2:   return_value.Append(_T("Fl"));   break;
3693                   case 3:   return_value.Append(_T("Fl"));   break;
3694                   case 4:   return_value.Append(_T("Q"));    break;
3695                   case 7:   return_value.Append(_T("Iso"));  break;
3696                   case 8:   return_value.Append(_T("Occ"));  break;
3697                   case 12:  return_value.Append(_T("Mo"));   break;
3698 */
3699 
3700                   case 1: return_value.Append(_T("F"));    break;                   //fixed     IP 10.1;
3701                   case 2: return_value.Append(_T("Fl"));   break;                   //flashing  IP 10.4;
3702                   case 3: return_value.Append(_T("LFl"));  break;                   //long-flashing   IP 10.5;
3703                   case 4: return_value.Append(_T("Q"));    break;                   //quick-flashing  IP 10.6;
3704                   case 5: return_value.Append(_T("VQ"));   break;                   //very quick-flashing   IP 10.7;
3705                   case 6: return_value.Append(_T("UQ"));   break;                   //ultra quick-flashing  IP 10.8;
3706                   case 7: return_value.Append(_T("Iso"));  break;                   //isophased IP 10.3;
3707                   case 8: return_value.Append(_T("Occ"));  break;                   //occulting IP 10.2;
3708                   case 9: return_value.Append(_T("IQ"));   break;                   //interrupted quick-flashing  IP 10.6;
3709                   case 10: return_value.Append(_T("IVQ")); break;                   //interrupted very quick-flashing   IP 10.7;
3710                   case 11: return_value.Append(_T("IUQ")); break;                   //interrupted ultra quick-flashing  IP 10.8;
3711                   case 12: return_value.Append(_T("Mo"));  break;                   //morse     IP 10.9;
3712                   case 13: return_value.Append(_T("F + Fl"));   b_grp2 = true; break;                   //fixed/flash     IP 10.10;
3713                   case 14: return_value.Append(_T("Fl + LFl")); b_grp2 = true; break;                   //flash/long-flash
3714                   case 15: return_value.Append(_T("Occ + Fl")); b_grp2 = true; break;                   //occulting/flash
3715                   case 16: return_value.Append(_T("F + LFl"));  b_grp2 = true;  break;                   //fixed/long-flash
3716                   case 17: return_value.Append(_T("Al Occ"));    break;                   //occulting alternating
3717                   case 18: return_value.Append(_T("Al LFl"));    break;                   //long-flash alternating
3718                   case 19: return_value.Append(_T("Al Fl"));    break;                   //flash alternating
3719                   case 20: return_value.Append(_T("Al Grp"));    break;                   //group alternating
3720                   case 21: return_value.Append(_T("F")); spost = _T(" (vert)");    break;                   //2 fixed (vertical)
3721                   case 22: return_value.Append(_T("F")); spost = _T(" (horz)");    break;                   //2 fixed (horizontal)
3722                   case 23: return_value.Append(_T("F")); spost = _T(" (vert)");    break;                   //3 fixed (vertical)
3723                   case 24: return_value.Append(_T("F")); spost = _T(" (horz)");    break;                   //3 fixed (horizontal)
3724                   case 25: return_value.Append(_T("Q + LFl"));  b_grp2 = true;    break;                   //quick-flash plus long-flash
3725                   case 26: return_value.Append(_T("VQ + LFl")); b_grp2 = true;    break;                   //very quick-flash plus long-flash
3726                   case 27: return_value.Append(_T("UQ + LFl")); b_grp2 = true;    break;                   //ultra quick-flash plus long-flash
3727                   case 28: return_value.Append(_T("Alt"));                        break;                   //alternating
3728                   case 29: return_value.Append(_T("F + Alt")); b_grp2 = true;     break;                   //fixed and alternating flashing
3729 
3730                   default: break;
3731             }
3732       }
3733 
3734       int nfirst_grp = -1;
3735       if(b_grp2)
3736       {
3737             wxString ret_new;
3738             nfirst_grp = return_value.Find(_T(" "));
3739             if( wxNOT_FOUND != nfirst_grp)
3740             {
3741                   ret_new = return_value.Mid(0, nfirst_grp);
3742                   ret_new.Append(_T("(?)"));
3743                   ret_new.Append(return_value.Mid(nfirst_grp));
3744                   return_value = ret_new;
3745                   nfirst_grp += 1;
3746             }
3747       }
3748 
3749 
3750 
3751      // SIGGRP, (c)(c) ...
3752       char grp_str[20] = {'\0'};
3753       GetStringAttr(obj, "SIGGRP", grp_str, 19);
3754       if(strlen(grp_str))
3755       {
3756             wxString ss(grp_str, wxConvUTF8);
3757 
3758             if(b_grp2)
3759             {
3760                   wxStringTokenizer tkz(ss, _T("()"));
3761 
3762                   int n_tok = 0;
3763                   while ( tkz.HasMoreTokens() && (n_tok <2))
3764                   {
3765                         wxString s = tkz.GetNextToken();
3766                         if(s.Len())
3767                         {
3768                               if((n_tok == 0) && (nfirst_grp > 0))
3769                               {
3770                                     return_value[nfirst_grp] = s[0];
3771                               }
3772                               else
3773                               {
3774                                     if(s != _T("1"))
3775                                     {
3776                                           return_value.Append(_T("("));
3777                                           return_value.Append(s);
3778                                           return_value.Append(_T(")"));
3779                                     }
3780                               }
3781 
3782                               n_tok++;
3783                         }
3784                   }
3785             }
3786             else
3787             {
3788                   if(ss != _T("(1)"))
3789                         return_value.Append(ss);
3790             }
3791       }
3792 
3793       // COLOUR,
3794       char col_str[20] = {'\0'};
3795 
3796       // Don't show for sectored lights since we are only showing one of the sectors.
3797       double sectrTest;
3798       bool hasSectors = GetDoubleAttr( obj, "SECTR1", sectrTest );
3799 
3800       if( ! hasSectors ) {
3801             GetStringAttr(obj, "COLOUR", col_str, 19);
3802 
3803             int n_cols = 0;
3804             if (strlen(col_str))
3805                   n_cols = _parseList(col_str, colist, sizeof(colist));
3806 
3807             if(n_cols)
3808                   return_value.Append(_T(" "));
3809 
3810             for(int i=0 ; i < n_cols ; i++)
3811             {
3812                   switch (colist[i])
3813                   {
3814                         case 1:  return_value.Append(_T("W")); break;
3815                         case 3:  return_value.Append(_T("R")); break;
3816                         case 4:  return_value.Append(_T("G")); break;
3817                         case 6:  return_value.Append(_T("Y")); break;
3818                         default:  break;
3819                   }
3820             }
3821       }
3822 
3823 
3824 
3825     /*
3826       1: white     IP 11.1;    450.2-3;
3827       2: black
3828       3: red IP 11.2;    450.2-3;
3829       4: green     IP 11.3;    450.2-3;
3830       5: blue      IP 11.4;    450.2-3;
3831       6: yellow    IP 11.6;    450.2-3;
3832       7: grey
3833       8: brown
3834       9: amber     IP 11.8;    450.2-3;
3835       10: violet    IP 11.5;    450.2-3;
3836       11: orange    IP 11.7;    450.2-3;
3837       12: magenta
3838       13: pink
3839     */
3840 
3841     // SIGPER, xx.xx
3842       double   sigper      = UNKNOWN;
3843       GetDoubleAttr(obj, "SIGPER", sigper);
3844 
3845       if(UNKNOWN != sigper)
3846       {
3847             wxString s;
3848             if(fabs(wxRound(sigper) - sigper) > 0.01)
3849                   s.Printf(_T("%4.1fs"), sigper);
3850             else
3851                   s.Printf(_T("%2.0fs"), sigper);
3852 
3853             s.Trim(false);          // remove leading spaces
3854             s.Prepend(_T(" "));
3855             return_value.Append(s);
3856       }
3857 
3858 
3859     // HEIGHT, xxx.x
3860       double   height      = UNKNOWN;
3861       GetDoubleAttr(obj, "HEIGHT", height);
3862 
3863       if(UNKNOWN != height)
3864       {
3865             wxString s;
3866             switch(ps52plib->m_nDepthUnitDisplay)
3867             {
3868                   case 0:                       // feet
3869                   case 2:                       // fathoms
3870                         s.Printf(_T("%3.0fft"), height* 3 * 39.37 / 36);
3871                         break;
3872                   default:
3873                         s.Printf(_T("%3.0fm"), height);
3874                         break;
3875             }
3876 
3877             s.Trim(false);          // remove leading spaces
3878             s.Prepend(_T(" "));
3879             return_value.Append(s);
3880       }
3881 
3882 
3883     // VALNMR, xx.x
3884       double   valnmr      = UNKNOWN;
3885       GetDoubleAttr(obj, "VALNMR", valnmr);
3886 
3887       if( UNKNOWN != valnmr && ! hasSectors )
3888       {
3889             wxString s;
3890             s.Printf(_T("%2.0fNm"), valnmr);
3891             s.Trim(false);          // remove leading spaces
3892             s.Prepend(_T(" "));
3893             return_value.Append(s);
3894       }
3895 
3896 #if 0
3897 
3898     // STATUS,
3899       gstr = S57_getAttVal(geo, "STATUS");
3900       if (NULL != gstr)
3901             g_string_append(litdsn01, gstr->str);
3902 
3903     /*
3904       1: permanent
3905       2: occasional      IP 50;      473.2;
3906       3: recommended     IN 10;      431.1;
3907       4: not in use      IL 14, 44;  444.7;
3908       5: periodic/intermittent IC 21; IQ 71;     353.3; 460.5;
3909       6: reserved  IN 12.9;
3910       7: temporary IP 54;
3911       8: private   IQ 70;
3912       9: mandatory
3913       10: destroyed/ruined
3914       11: extinguished
3915       12: illuminated
3916       13: historic
3917       14: public
3918       15: synchronized
3919       16: watched
3920       17: un-watched
3921       18: existence doubtful
3922     */
3923 
3924 
3925 #endif
3926 
3927       return_value.Append(spost);                     // add any final modifiers
3928 
3929       return return_value;
3930 }
3931 
3932 
SYMINS01(void * param)3933 static void *SYMINS01(void *param)
3934 {
3935     ObjRazRules *rzRules = (ObjRazRules *)param;
3936     S57Obj *obj = rzRules->obj;
3937     char symins[80] = {'\0'};
3938     GetStringAttr(obj, "SYMINS", symins, 79);
3939 
3940     strcat(symins, "\037");
3941     char *r = (char *)malloc(strlen(symins) + 1);
3942     strcpy(r, symins);
3943 
3944    return r;
3945 }
3946 
3947 //--------------------------------
3948 //
3949 // JUMP TABLE SECTION
3950 //
3951 //--------------------------------
3952 Cond condTable[] = {
3953    {"CLRLIN01",CLRLIN01},
3954    {"DATCVR01",DATCVR01},
3955    {"DATCVR01",DATCVR01},
3956    {"DEPARE01",DEPARE01},
3957    {"DEPARE02",DEPARE01},                 // new in PLIB 3_3, opencpn defaults to DEPARE01
3958    {"DEPCNT02",DEPCNT02},
3959    {"DEPVAL01",DEPVAL01},
3960    {"LEGLIN02",LEGLIN02},
3961    {"LIGHTS05",LIGHTS05},                 // new in PLIB 3_3, replaces LIGHTS04
3962    {"LITDSN01",LITDSN01},
3963    {"OBSTRN04",OBSTRN04},
3964    {"OWNSHP02",OWNSHP02},
3965    {"PASTRK01",PASTRK01},
3966    {"QUAPOS01",QUAPOS01},
3967    {"QUALIN01",QUALIN01},
3968    {"QUAPNT01",QUAPNT01},
3969    {"SLCONS03",SLCONS03},
3970    {"RESARE02",RESARE02},
3971    {"RESTRN01",RESTRN01},
3972 //   {"RESCSP01",RESCSP01},
3973    {"SEABED01",SEABED01},
3974 //   {"SNDFRM02",SNDFRM02},
3975    {"SOUNDG02",SOUNDG02},
3976    {"TOPMAR01",TOPMAR01},
3977    {"UDWHAZ03",UDWHAZ03},
3978    {"VESSEL01",VESSEL01},
3979    {"VRMEBL01",VRMEBL01},
3980    {"WRECKS02",WRECKS02},
3981    {"SOUNDG03",SOUNDG03},                   // special case for MPS
3982    {"SYMINS01",SYMINS01},                   //  Container for Virtual AIS ATONS, special case
3983    {"########",NULL}
3984 };
3985 
3986 #if 0
3987 // S52CS.c : Conditional Symbologie procedure 3.2 (CS)
3988 //
3989 // Project:  OpENCview
3990 
3991 /*
3992     This file is part of the OpENCview project, a viewer of ENC
3993     Copyright (C) 2000-2004  Sylvain Duclos sduclos@users.sourceforgue.net
3994 
3995     This program is free software; you can redistribute it and/or modify
3996     it under the terms of the GNU General Public License as published by
3997     the Free Software Foundation; either version 2 of the License, or
3998     (at your option) any later version.
3999 
4000     This program is distributed in the hope that it will be useful,
4001     but WITHOUT ANY WARRANTY; without even the implied warranty of
4002     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4003     GNU General Public License for more details.
4004 
4005     You should have received a copy of the GNU General Public License
4006     along with this program; if not, write to the Free Software
4007     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
4008 */
4009 
4010 // NOTE: remarks commenting each CS are extracted from pslb03_2.pdf (sec. 12)
4011 
4012 // FIXME: DEPCNT02: call DB for area DEPARE & DRGARE that intersect this line
4013 // FIXME:_DEPVAL01: call DB for area DEPARE & UNSARE that intersect this area
4014 // FIXME:_UDWHAZ03: call DB for area DRGARE & DEPARE that intersect this point/area
4015 
4016 #include "S52CS.h"
4017 
4018 #include "S52utils.h"   // PRINTF()
4019 
4020 #include <stdlib.h>     // atof()
4021 #include <math.h>       // fabsf(), HUGE_VAL
4022 #include <ctype.h>      // isdigit()
4023 
4024 #define UNKNOWN 1e6     //HUGE_VAL   // INFINITY/NAN
4025 
4026 #define COALNE   30   // Coastline
4027 #define DEPARE   42   // Depth area
4028 #define DEPCNT   43   // Depth contour
4029 #define DRGARE   46   // Dredged area
4030 #define UWTROC  153   // Underwater rock / awash rock
4031 #define WRECKS  159   // Wreck
4032 
4033 // point list name
4034 #define LIGHTLIST 0
4035 #define SECTRLIST 1
4036 #define FLOATLIST 2   // floating platform
4037 #define RIGIDLIST 3   // rigid platform
4038 //static GPtrArray   *_lightList = NULL;
4039 //static GPtrArray   *_sectrList = NULL;
4040 //static GPtrArray   *_flaotList = NULL;
4041 static GPtrArray *_ptList[] = {NULL, NULL, NULL, NULL};
4042 
4043 int S52_state = 1;
4044 
4045 // size of attributes value list buffer
4046 #define LISTSIZE   16   // list size
4047 
4048 #define version "3.2.0"
4049 char     *S52_CS_version()
4050 {
4051     return version;
4052 }
4053 
4054 int       S52_CS_init()
4055 {
4056     _ptList[LIGHTLIST] = g_ptr_array_new();
4057     _ptList[SECTRLIST] = g_ptr_array_new();
4058     _ptList[FLOATLIST] = g_ptr_array_new();
4059     _ptList[RIGIDLIST] = g_ptr_array_new();
4060 
4061     return 1;
4062 }
4063 
4064 int       S52_CS_done()
4065 {
4066     g_ptr_array_free(_ptList[LIGHTLIST], TRUE);
4067     g_ptr_array_free(_ptList[SECTRLIST], TRUE);
4068     g_ptr_array_free(_ptList[FLOATLIST], TRUE);
4069     g_ptr_array_free(_ptList[RIGIDLIST], TRUE);
4070 
4071     return 1;
4072 }
4073 
4074 int       S52_CS_setPtPos(S57_geo *geoData, char *name)
4075 {
4076     printf("name = %s\n", name);
4077 
4078     if (POINT_T == S57_getObjtype(geoData)) {
4079 
4080         // set floating platform
4081         if ((0==strncmp(name, "LITFLT", 6)) ||
4082             (0==strncmp(name, "LITVES", 6)) ||
4083             (0==strncmp(name, "BOY",    3)))
4084             g_ptr_array_add(_ptList[FLOATLIST], (gpointer) geoData);
4085 
4086         // set rigid platform
4087         if (0==strncmp(name, "BCN",    3))
4088             g_ptr_array_add(_ptList[RIGIDLIST], (gpointer) geoData);
4089     }
4090 
4091     return 1;
4092 }
4093 
4094 static int      _atPtPos(S57_geo *geoNew, int listNm)
4095 // return TRUE if there is a light at this position
4096 // or if its an extended arc radius else FALSE
4097 {
4098     int i;
4099     GPtrArray *curntList = _ptList[listNm];
4100 
4101     for (i=0; i<curntList->len; i++) {
4102         S57_geo *geoOld = g_ptr_array_index(curntList, i);
4103 
4104         if (S57_samePtPos(geoNew, geoOld)) {
4105 
4106             if (SECTRLIST != listNm)
4107                 return TRUE;
4108             else {
4109                 // check for extend arc radius
4110                 GString *Asectr1str = S57_getAttVal(geoOld, "SECTR1");
4111                 GString *Asectr2str = S57_getAttVal(geoOld, "SECTR2");
4112                 GString *Bsectr1str = S57_getAttVal(geoNew, "SECTR1");
4113                 GString *Bsectr2str = S57_getAttVal(geoNew, "SECTR2");
4114 
4115                 // check  present
4116                 if (NULL == Asectr1str ||
4117                     NULL == Asectr1str ||
4118                     NULL == Asectr1str ||
4119                     NULL == Asectr1str)
4120                     return FALSE;
4121 
4122                 {
4123                     double Asectr1 = atof(Asectr1str->str);
4124                     double Asectr2 = atof(Asectr2str->str);
4125                     double Bsectr1 = atof(Bsectr1str->str);
4126                     double Bsectr2 = atof(Bsectr2str->str);
4127                     double Asweep = (Asectr1 > Asectr2) ?
4128                         Asectr2-Asectr1+360 : Asectr2-Asectr1;
4129                     double Bsweep = (Bsectr1 > Bsectr2) ?
4130                         Bsectr2-Bsectr1+360 : Bsectr2-Bsectr1;
4131 
4132                     // check sector overlap
4133                     if (Asectr2<=Bsectr1 || Asectr1>=Bsectr2) {
4134                         if (Asweep == Bsweep) {
4135                             g_string_truncate(Bsectr2str, 0);
4136                             g_string_sprintf(Bsectr2str, "%f",Bsectr2-1);
4137                             S57_setAtt(geoNew, "SECTR2", Bsectr2str->str);
4138                         }
4139 
4140                         return FALSE;
4141                     }
4142 
4143                     // check if other sector larger
4144                     if (Asweep >= Bsweep)
4145                         return TRUE;
4146                 }
4147             }
4148         }
4149     }
4150 
4151     return FALSE;
4152 }
4153 
4154 static int      _setPtPos(S57_geo *geo, int listNm)
4155 // TRUE if set new position of a light
4156 // else FALSE (ie there is a light at this position)
4157 {
4158     GPtrArray *curntList = _ptList[listNm];
4159 
4160     if (_atPtPos(geo, listNm))
4161         return 1;
4162     else
4163         g_ptr_array_add(curntList, (gpointer) geo);
4164 
4165     return 0;
4166 }
4167 
4168 static int      _parseList(const char *str, char *buf)
4169 // Put a string of comma delimited number in an array (buf).
4170 // Return: the number of value in buf.
4171 // Assume: - number < 256,
4172 //         - list size less then LISTSIZE-1 .
4173 // Note: buf is \0 terminated for strpbrk().
4174 {
4175     int i = 0;
4176 
4177     if (NULL != str && *str != '\0') {
4178         do {
4179             if ( i>= LISTSIZE-1) {
4180                 PRINTF("OVERFLOW --value in list lost!!\n");
4181                 break;
4182             }
4183 
4184             /*
4185             if (255 <  (unsigned char) atoi(str)) {
4186                 PRINTF("value overflow (>255)\n");
4187                 exit(0);
4188             }
4189             */
4190 
4191             buf[i++] = (unsigned char) atoi(str);
4192 
4193             while(isdigit(*str++));   // next
4194             //while( g_ascii_isdigit(c));   // next
4195 
4196         } while(*str++ != '\0');      // skip ',' or exit
4197     }
4198 
4199     buf[i] = '\0';
4200 
4201     return i;
4202 }
4203 
4204 
4205 static GString *CLRLIN01 (S57_geo *geo)
4206 // Remarks: A clearing line shows a single arrow head at one of its ends. The direction
4207 // of the clearing line must be calculated from its line object in order to rotate
4208 // the arrow head symbol and place it at the correct end. This cannot be
4209 // achieved with a complex linestyle since linestyle symbols cannot be sized
4210 // to the length of the clearing line. Instead a linestyle with a repeating pattern
4211 // of arrow symbols had to be used which does not comply with the required
4212 // symbolization.
4213 {
4214 
4215     PRINTF("Mariner's object not drawn\n");
4216 
4217     return NULL;
4218 }
4219 
4220 static GString *DATCVR01 (S57_geo *geo)
4221 // Remarks: This conditional symbology procedure describes procedures for:
4222 // - symbolizing the limit of ENC coverage;
4223 // - symbolizing navigational purpose boundaries ("scale boundarie"); and
4224 // - indicating overscale display.
4225 //
4226 // Note that the mandatory meta object CATQUA is symbolized by the look-up table.
4227 //
4228 // Because the methods adopted by an ECDIS to meet the IMO and IHO requirements
4229 // listed on the next page will depend on the manufacturer's software, and cannot be
4230 // described in terms of a flow chart in the same way as other conditional procedures,
4231 // this procedure is in the form of written notes.
4232 {
4233     GString *datcvr01 = NULL;
4234 
4235     ///////////////////////
4236     // 1- REQUIREMENT
4237     // (IMO/IHO specs. explenation)
4238 
4239     ///////////////////////
4240     // 2- ENC COVERAGE
4241     //
4242     // 2.1- Limit of ENC coverage
4243     //datcvr01 = g_string_new(";OP(3OD11060);LC(HODATA01)");
4244     // FIXME: get cell extend
4245 
4246     // 2.2- No data areas
4247     // This can be done outside of CS (ie when clearing the screen in Mesa)
4248     // FIXME: ";OP(0---);AC(NODATA)"
4249     // FIXME: set geo to cover earth (!)
4250 
4251     //////////////////////
4252     // 3- SCALE BOUNDARIES
4253     //
4254     // 3.1- Chart scale boundaties
4255     // FIXME;
4256     //g_string_append(datcvr01, ";LS(SOLD,1,CHGRD)");
4257     // -OR- LC(SCLBDYnn) (?)
4258     //
4259     // ;OP(3OS21030)
4260 
4261     // 3.2- Graphical index of navigational purpose
4262     // FIXME: draw extent of available SENC in DB
4263 
4264     //////////////////////
4265     // 4- OVERSCALE
4266     //
4267     // FIXME: get meta date CSCL of DSPM field
4268     // FIXME: get object M_CSCL or CSCALE
4269     //
4270     // 4.1- Overscale indication
4271     // FIXME: compute, scale = [denominator of the compilation scale] /
4272     //                         [denominator of the display scale]
4273     // FIXME: draw overscale indication (ie TX("X%3.1f",scale))
4274     //
4275     // 4.2- Ovescale area at a chart scale boundary
4276     // FIXME: test if next chart is over scale (ie going from large scale chart
4277     //        to a small scale chart)
4278     // FIXME: draw AP(OVERSC01) on overscale part of display
4279     //g_string(";OP(3OS21030)");
4280 
4281     //
4282     // 4.3- Larger scale data available
4283     // FIXME: display indication of better scale available (?)
4284 
4285 
4286     PRINTF("not computed\n");
4287 
4288     return datcvr01;
4289 }
4290 
4291 static GString *_SEABED01(double drval1, double drval2);
4292 static GString *_RESCSP01(S57_geo *geo);
4293 static GString *DEPARE01 (S57_geo *geo)
4294 // Remarks: An object of the class "depth area" is coloured and covered with fill patterns
4295 // according to the mariners selections of shallow contour, safety contour and
4296 // deep contour. This requires a decision making process provided by the sub-procedure
4297 // "SEABED01" which is called by this symbology procedure.
4298 // Objects of the class "dredged area" are handled by this routine as well to
4299 // ensure a consistent symbolization of areas that represent the surface of the
4300 // seabed.
4301 {
4302     GString *depare01  = NULL;
4303     int      objl      = 0;
4304     GString *objlstr   = NULL;
4305     GString *drval1str = S57_getAttVal(geo, "DRVAL1");
4306     double   drval1    = UNKNOWN;
4307     GString *drval2str = S57_getAttVal(geo, "DRVAL2");
4308     double   drval2    = UNKNOWN;
4309 
4310     if (NULL == drval1str || NULL == drval2str) {
4311         PRINTF("ERROR: drval1 or drval2 should have the value UNKNOWN\n");
4312         return NULL;
4313     }
4314 
4315     drval1 = (NULL == drval1str) ? -1.0 : atof(drval1str->str);
4316     drval2 = (NULL == drval2str) ? drval1+0.01 : atof(drval2str->str);
4317 
4318     depare01 = _SEABED01(drval1, drval2);
4319 
4320     objlstr = S57_getAttVal(geo, "OBJL");
4321     objl    = (NULL == objlstr) ? 0 : atoi(objlstr->str);
4322 
4323     if (DRGARE == objl) {
4324         g_string_append(depare01, ";AP(DRGARE01)");
4325         g_string_append(depare01, ";LS(DASH,1,CHGRF)");
4326 
4327         if (NULL != S57_getAttVal(geo, "RESTRN")) {
4328             GString *rescsp01 = _RESCSP01(geo);
4329             if (NULL != rescsp01) {
4330                 g_string_append(depare01, rescsp01->str);
4331                 g_string_free(rescsp01, TRUE);
4332             }
4333         }
4334 
4335     }
4336 
4337     return depare01;
4338 }
4339 
4340 static GString *_SNDFRM02(S57_geo *geo, double depth_value);
4341 static GString *DEPCNT02 (S57_geo *geo)
4342 // Remarks: An object of the class "depth contour" or "line depth area" is highlighted and must
4343 // be shown under all circumstances if it matches the safety contour depth value
4344 // entered by the mariner (see IMO PS 3.6). But, while the mariner is free to enter any
4345 // safety contour depth value that he thinks is suitable for the safety of his ship, the
4346 // SENC only contains a limited choice of depth contours. This symbology procedure
4347 // determines whether a contour matches the selected safety contour. If the selected
4348 // safety contour does not exist in the data, the procedure will default to the next deeper
4349 // contour. The contour selected is highlighted as the safety contour and put in
4350 // DISPLAYBASE. The procedure also identifies any line segment of the spatial
4351 // component of the object that has a "QUAPOS" value indicating unreliable
4352 // positioning, and symbolizes it with a double dashed line.
4353 //
4354 // Note: Depth contours are not normally labeled. The ECDIS may provide labels, on demand
4355 // only as with other text, or provide the depth value on cursor picking
4356 {
4357     GString *depcnt02  = NULL;
4358     int      safe      = FALSE;     // initialy not a safety contour
4359     GString *objlstr   = NULL;
4360     int      objl      = 0;
4361     GString *quaposstr = NULL;
4362     int      quapos    = 0;
4363     double   depth_value;
4364 
4365     objlstr = S57_getAttVal(geo, "OBJL");
4366     objl    = (NULL == objlstr) ? 0 : atoi(objlstr->str);
4367 
4368     if (DEPARE==objl && LINES_T==S57_getObjtype(geo))
4369     {
4370         GString *drval1str = S57_getAttVal(geo, "DRVAL1");
4371         double   drval1    = (NULL == drval1str) ? 0.0    : atof(drval1str->str);
4372         GString *drval2str = S57_getAttVal(geo, "DRVAL2");
4373         double   drval2    = (NULL == drval2str) ? drval1 : atof(drval2str->str);
4374 
4375         if (drval1 <= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
4376         {
4377             if (drval2 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
4378                 safe = TRUE;
4379         }
4380         else
4381         {
4382             if (1 == S52_state)
4383                 return NULL;
4384             else
4385             {
4386                 S57_geo *geoTmp = geo;
4387 
4388                 // get area DEPARE & DRGARE that intersect this line
4389                 while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
4390                     drval1str = S57_getAttVal(geoTmp, "DRVAL1");
4391                     drval1    = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
4392 
4393                     if (NULL == drval1str) {
4394                         safe = TRUE;
4395                         break;
4396                     }
4397 
4398                     if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
4399                         safe = TRUE;
4400                         break;
4401                     }
4402                 }
4403                 // debug trace
4404                 //if (safe) PRINTF("** DEPARE: SAFE FOUND**\n");
4405             }
4406         }
4407 
4408         depth_value = drval1;
4409 
4410     }
4411     else
4412     {
4413         // continuation A (DEPCNT)
4414         GString *valdcostr = S57_getAttVal(geo, "VALDCO");
4415         double   valdco    = (NULL == valdcostr) ? 0.0 : atof(valdcostr->str);
4416 
4417         depth_value = valdco;
4418 
4419         if (valdco == S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR))
4420             safe = TRUE;   // this is useless !?!?
4421         else
4422         {
4423             if (valdco > S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
4424                 if (1 == S52_state)
4425                     return NULL;
4426                 else {
4427                     S57_geo *geoTmp = geo;
4428 
4429                     // get area DEPARE & DRGARE that intersect this line
4430                     while (NULL != (geoTmp = S57_nextObj(geoTmp))){
4431                         GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
4432                         double   drval1    = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
4433 
4434                         if (NULL == drval1str) {
4435                             safe = TRUE;
4436                             break;
4437                         }
4438 
4439                         if (drval1 < S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
4440                             safe = TRUE;
4441                             break;
4442                         }
4443                     }
4444                     // debug trace
4445                     //if (safe) PRINTF("** DEPCN: SAFE FOUND**\n");
4446                 }
4447             }
4448         }
4449     }
4450 
4451     // Continuation B
4452     quaposstr = S57_getAttVal(geo, "QUAPOS");
4453     if (NULL != quaposstr) {
4454         quapos = atoi(quaposstr->str);
4455         if ( 2 <= quapos && quapos < 10) {
4456             if (safe)
4457                 depcnt02 = g_string_new(";LS(DASH,2,DEPSC)");
4458             else
4459                 depcnt02 = g_string_new(";LS(DASH,1,DEPCN)");
4460         }
4461     } else {
4462         if (safe)
4463             depcnt02 = g_string_new(";LS(SOLD,2,DEPSC)");
4464         else
4465             depcnt02 = g_string_new(";LS(SOLD,1,DEPCN)");
4466     }
4467 
4468     if (safe) {
4469         S57_setAtt(geo, "SCAMIN", "INFINITE");
4470         depcnt02 = g_string_prepend(depcnt02, ";OP(8OD13010)");
4471     } else
4472         depcnt02 = g_string_prepend(depcnt02, ";OP(---33020)");
4473 
4474     // facultative in S-52
4475     //if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
4476     //    GString *sndfrm02 = _SNDFRM02(geo, depth_value);
4477     //    depcnt02 = g_string_append(depcnt02, sndfrm02->str);
4478     //    g_string_free(sndfrm02, TRUE);
4479     //}
4480 
4481     // debug
4482     //PRINTF("depth= %f\n", depth_value);
4483 
4484     S57_unlinkObj(geo);
4485 
4486     return depcnt02;
4487 }
4488 
4489 static double   _DEPVAL01(S57_geo *geo, double least_depth)
4490 // Remarks: S-57 Appendix B1 Annex A requires in Section 6 that areas of rocks be
4491 // encoded as area obstruction, and that area OBSTRNs and area WRECKS
4492 // be covered by either group 1 object DEPARE or group 1 object UNSARE.
4493 // If the value of the attribute VALSOU for an area OBSTRN or WRECKS
4494 // is missing, the DRVAL1 of an underlying DEPARE is the preferred default
4495 // for establishing a depth value. This procedure either finds the shallowest
4496 // DRVAL1 of the one or more underlying DEPAREs, or returns an
4497 // "unknown"" depth value to the main procedure for the next default
4498 // procedure.
4499 
4500 // NOTE: UNSARE test is useless since least_depth is already UNKNOWN
4501 {
4502     least_depth = UNKNOWN;
4503 
4504 
4505     S57_geo *geoTmp = geo;
4506 
4507     // NOTE: the geo list is unchange (_UDWHAZ03 will unlink geo)
4508     while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
4509         GString *objlstr = S57_getAttVal(geoTmp, "OBJL");
4510         int      objl    = (NULL == objlstr) ? 0 : atoi(objlstr->str);
4511 
4512         // get area DEPARE  that intersect this area
4513         if (DEPARE==objl && LINES_T==S57_getObjtype(geo)) {
4514             GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
4515             double   drval1    = (NULL == drval1str) ? 9.0 : atof(drval1str->str);
4516 
4517             if (NULL != drval1str) {
4518                 if (UNKNOWN==least_depth || least_depth<drval1)
4519                     least_depth = drval1;
4520             }
4521 
4522         }
4523     }
4524 
4525     return least_depth;
4526 }
4527 
4528 static GString *LEGLIN02 (S57_geo *geo)
4529 
4530 // Remarks: The course of a leg is given by its start and end point. Therefore this
4531 // conditional symbology procedure calculates the course and shows it
4532 // alongside the leg. It also places the "distance to run" labels and cares for the
4533 // different presentation of planned & alternate legs.
4534 {
4535     PRINTF("Mariner's object not drawn\n");
4536     return NULL;
4537 }
4538 
4539 static GString *_LITDSN01(S57_geo *geo);
4540 static GString *LIGHTS05 (S57_geo *geo)
4541 // Remarks: A light is one of the most complex S-57 objects. Its presentation depends on
4542 // whether it is a light on a floating or fixed platform, its range, it's colour and
4543 // so on. This conditional symbology procedure derives the correct
4544 // presentation from these parameters and also generates an area that shows the
4545 // coverage of the light.
4546 //
4547 // Notes on light sectors:
4548 // 1.) The radial leg-lines defining the light sectors are normally drawn to only 25mm
4549 // from the light to avoid clutter (see Part C). However, the mariner should be able to
4550 // select "full light-sector lines" and have the leg-lines extended to the nominal range
4551 // of the light (VALMAR).
4552 //
4553 // 2.) Part C of this procedure symbolizes the sectors at the light itself. In addition,
4554 // it should be possible, upon request, for the mariner to be capable of identifying
4555 // the colour and sector limit lines of the sectors affecting the ship even if the light
4556 // itself is off the display.
4557 // [ed. last sentence in bold]
4558 
4559 // NOTE: why is this relationship not already encoded in S57 (ei. C_AGGR or C_STAC) ?
4560 
4561 {
4562     GString *lights05          = NULL;
4563     GString *valnmrstr         = S57_getAttVal(geo, "VALNMR");
4564     double   valnmr            = 0.0;
4565     GString *catlitstr         = S57_getAttVal(geo, "CATLIT");
4566     char     catlit[LISTSIZE]  = {'\0'};
4567     int      flare_at_45       = FALSE;
4568     int      extend_arc_radius = TRUE;
4569     GString *sectr1str         = NULL;
4570     GString *sectr2str         = NULL;
4571     double   sectr1            = 0.0;
4572     double   sectr2            = 0.0;
4573     GString *colourstr         = NULL;
4574     char     colist[LISTSIZE]  = {'\0'};   // colour list
4575     GString *orientstr         = NULL;
4576     double   sweep             = 0.0;
4577 
4578 
4579     lights05 = g_string_new("");
4580 
4581     valnmr = (NULL == valnmrstr) ? 9.0 : atof(valnmrstr->str);
4582 
4583     if ( NULL != catlitstr) {
4584         _parseList(catlitstr->str, catlit);
4585 
4586         // FIXME: OR vs AND/OR
4587         if (strpbrk(catlit, "\010\013")) {
4588             g_string_append(lights05, ";SY(LIGHTS82)");
4589             return lights05;
4590         }
4591 
4592         if (strpbrk(catlit, "\011")) {
4593             g_string_append(lights05, ";SY(LIGHTS81)");
4594             return lights05;
4595         }
4596 
4597         if (strpbrk(catlit, "\001\020")) {
4598             orientstr = S57_getAttVal(geo, "ORIENT");
4599             if (NULL != orientstr) {
4600                 // FIXME: create a geo object (!?) LINE of lenght VALNMR
4601                 // using ORIENT (from seaward) & POINT_T position
4602                 g_string_append(lights05, ";LS(DASH,1,CHBLK)");
4603             }
4604         }
4605     }
4606 
4607     // Continuation A
4608     colourstr = S57_getAttVal(geo, "COLOUR");
4609     if (NULL != colourstr)
4610         _parseList(colourstr->str, colist);
4611     else {
4612         colist[0] = '\014';  // maganta (12)
4613         colist[1] = '\000';
4614     }
4615 
4616     sectr1str = S57_getAttVal(geo, "SECTR1");
4617     sectr1    = (NULL == sectr1str) ? 0.0 : atof(sectr1str->str);
4618     sectr2str = S57_getAttVal(geo, "SECTR2");
4619     sectr2    = (NULL == sectr2str) ? 0.0 : atof(sectr2str->str);
4620 
4621     if (NULL==sectr1str || NULL==sectr2str) {
4622         // not a sector light
4623         char *sym;
4624 
4625         if (1==S52_state) {
4626             _setPtPos(geo, LIGHTLIST);
4627             g_string_free(lights05, TRUE);
4628             return NULL;
4629         } else
4630             flare_at_45 = _atPtPos(geo, LIGHTLIST);
4631 
4632         sym = _selSYcol(colist);
4633 
4634         if (strpbrk(catlit, "\001\020")) {
4635             if (NULL != orientstr){
4636                 g_string_append(lights05, sym);
4637                 g_string_sprintfa(lights05, ",%s)", orientstr->str);
4638                 g_string_append(lights05, ";TE('%03.0lf deg','ORIENT',3,3,3,'15110',3,1,CHBLK,23)" );
4639             } else
4640                 g_string_append(lights05, ";SY(QUSMRK1)");
4641         } else {
4642             g_string_append(lights05, sym);
4643             if (flare_at_45)
4644                 g_string_append(lights05, ",145)");
4645             else
4646                 g_string_append(lights05, ",135)");
4647         }
4648 
4649         if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
4650             GString *litdsn01 = _LITDSN01(geo);
4651             if (NULL != litdsn01){
4652                 g_string_append(lights05, ";TX('");
4653                 g_string_append(lights05, litdsn01->str);
4654                 g_string_free(litdsn01, TRUE);
4655 
4656                 if (flare_at_45)
4657                     g_string_append(lights05, "',3,3,3,'15110',2,-1,CHBLK,23)" );
4658                 else
4659                     g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" );
4660             }
4661         }
4662 
4663         return lights05;
4664     }
4665 
4666     // Continuation B --sector light
4667     if (NULL == sectr1str) {
4668         sectr1 = 0.0;
4669         sectr2 = 0.0;
4670     } else
4671         sweep = (sectr1 > sectr2) ? sectr2-sectr1+360 : sectr2-sectr1;
4672 
4673 
4674     if (sweep<1.0 || sweep==360.0) {
4675         // handle all round light
4676         char *sym = _selSYcol(colist);;
4677 
4678         g_string_append(lights05, sym);
4679         g_string_append(lights05, ",135)");
4680 
4681         if (TRUE == S52_getMarinerParam(S52_MAR_SHOW_TEXT)) {
4682             GString *litdsn01 = _LITDSN01(geo);
4683             if (NULL != litdsn01) {
4684                 g_string_append(lights05, ";TX('");
4685                 g_string_append(lights05, litdsn01->str);
4686                 g_string_append(lights05, "',3,2,3,'15110',2,0,CHBLK,23)" );
4687                 g_string_free(litdsn01, TRUE);
4688             }
4689 
4690         }
4691 
4692         return lights05;
4693     }
4694 
4695     // scan for other lights with sector overlap at this position
4696     // compute light sector radius acording to other sector
4697     if (1 == S52_state) {
4698         _setPtPos(geo, SECTRLIST);
4699         g_string_free(lights05, TRUE);
4700         return NULL;
4701     } else {
4702         extend_arc_radius = _atPtPos(geo, SECTRLIST);
4703 
4704         // passe value via attribs to _renderAC
4705         if (extend_arc_radius)
4706             // FIXME: draw radius 25 mm
4707             S57_setAtt(geo, "extend_arc_radius", "Y");
4708         else
4709             // FIXME: draw radius 20 mm
4710             S57_setAtt(geo, "extend_arc_radius", "N");
4711     }
4712 
4713     // setup sector
4714     {
4715         char litvis[LISTSIZE] = {'\0'};  // list visibility
4716         GString *litvisstr = S57_getAttVal(geo, "LITVIS");
4717 
4718         // sector leg --logic is _renderLS()
4719         g_string_append(lights05, ";LS(DASH,1,CHBLK)");
4720 
4721         // get light vis.
4722         if (NULL != litvisstr) _parseList(litvisstr->str, litvis);
4723 
4724         // faint light
4725         // FIXME: spec say OR (ie 1 number) the code is AND/OR
4726         if (strpbrk(litvis, "\003\007\010")) {
4727             // NOTE: LS(DASH,1,CHBLK)
4728             // pass flag to _renderAC()
4729             g_string_append(lights05, ";AC(CHBLK)");
4730             S57_setAtt(geo, "faint_light", "Y");
4731 
4732         } else {
4733             // set arc colour
4734             char *sym = ";AC(CHMGD)";  // other
4735 
4736             // max 1 color
4737             if ('\0' == colist[1]) {
4738                 if (strpbrk(colist, "\003"))
4739                     sym = ";AC(LITRD)";
4740                 else if (strpbrk(colist, "\004"))
4741                     sym = ";AC(LITGN)";
4742                 else if (strpbrk(colist, "\001\006\013"))
4743                     sym = ";AC(LITYW)";
4744             } else {
4745                 // max 2 color
4746                 if ('\0' == colist[2]) {
4747                     if (strpbrk(colist, "\001") && strpbrk(colist, "\003"))
4748                         sym = ";AC(LITRD)";
4749                     else if (strpbrk(colist, "\001") && strpbrk(colist, "\004"))
4750                         sym = ";AC(LITGN)";
4751                 }
4752             }
4753 
4754             g_string_append(lights05, sym);
4755         }
4756     }
4757 
4758     return lights05;
4759 }
4760 
4761 static GString *_LITDSN01(S57_geo *geo)
4762 // Remarks: In S-57 the light characteristics are held as a series of attributes values. The
4763 // mariner may wish to see a light description text string displayed on the
4764 // screen similar to the string commonly found on a paper chart. This
4765 // conditional procedure, reads the attribute values from the above list of
4766 // attributes and composes a light description string which can be displayed.
4767 // This procedure is provided as a C function which has as input, the above
4768 // listed attribute values and as output, the light description.
4769 {
4770     GString *litdsn01 = g_string_new("");
4771     GString *gstr     = NULL;  // tmp
4772 
4773     // FIXME: need grammar to create light's text
4774 
4775     // CATLIT, LITCHR, COLOUR, HEIGHT, LITCHR, SIGGRP, SIGPER, STATUS, VALNMR
4776 
4777     // CATLIT
4778     gstr = S57_getAttVal(geo, "CATLIT");
4779     if (NULL != gstr)
4780         g_string_append(litdsn01, gstr->str);
4781 
4782     /*
4783      1: directional function  IP 30.1-3;  475.7;
4784      2: rear/upper light
4785      3: front/lower light
4786      4: leading light           IP 20.1-3;      475.6;
4787      5: aero light                  IP 60;      476.1;
4788      6: air obstruction light IP 61;      476.2;
4789      7: fog detector light        IP 62;  477;
4790      8: flood light                 IP 63;      478.2;
4791      9: strip light                 IP 64;      478.5;
4792     10: subsidiary light          IP 42;  471.8;
4793     11: spotlight
4794     12: front
4795     13: rear
4796     14: lower
4797     15: upper
4798     16: moire' effect           IP 31;    475.8;
4799     17: emergency
4800     18: bearing light                   478.1;
4801     19: horizontally disposed
4802     20: vertically disposed
4803      */
4804 
4805     // LITCHR
4806     gstr = S57_getAttVal(geo, "LITCHR");
4807     if (NULL != gstr)
4808         g_string_append(litdsn01, gstr->str);
4809 
4810     /*
4811      1: fixed     IP 10.1;
4812      2: flashing  IP 10.4;
4813      3: long-flashing   IP 10.5;
4814      4: quick-flashing  IP 10.6;
4815      5: very quick-flashing   IP 10.7;
4816      6: ultra quick-flashing  IP 10.8;
4817      7: isophased IP 10.3;
4818      8: occulting IP 10.2;
4819      9: interrupted quick-flashing  IP 10.6;
4820     10: interrupted very quick-flashing   IP 10.7;
4821     11: interrupted ultra quick-flashing  IP 10.8;
4822     12: morse     IP 10.9;
4823     13: fixed/flash     IP 10.10;
4824     14: flash/long-flash
4825     15: occulting/flash
4826     16: fixed/long-flash
4827     17: occulting alternating
4828     18: long-flash alternating
4829     19: flash alternating
4830     20: group alternating
4831     21: 2 fixed (vertical)
4832     22: 2 fixed (horizontal)
4833     23: 3 fixed (vertical)
4834     24: 3 fixed (horizontal)
4835     25: quick-flash plus long-flash
4836     26: very quick-flash plus long-flash
4837     27: ultra quick-flash plus long-flash
4838     28: alternating
4839     29: fixed and alternating flashing
4840     */
4841 
4842     // COLOUR,
4843     gstr = S57_getAttVal(geo, "COLOUR");
4844     if (NULL != gstr)
4845         g_string_append(litdsn01, gstr->str);
4846 
4847     /*
4848      1: white     IP 11.1;    450.2-3;
4849      2: black
4850      3: red IP 11.2;    450.2-3;
4851      4: green     IP 11.3;    450.2-3;
4852      5: blue      IP 11.4;    450.2-3;
4853      6: yellow    IP 11.6;    450.2-3;
4854      7: grey
4855      8: brown
4856      9: amber     IP 11.8;    450.2-3;
4857     10: violet    IP 11.5;    450.2-3;
4858     11: orange    IP 11.7;    450.2-3;
4859     12: magenta
4860     13: pink
4861     */
4862 
4863     // HEIGHT, xxx.x
4864     gstr = S57_getAttVal(geo, "HEIGHT");
4865     if (NULL != gstr)
4866         g_string_append(litdsn01, gstr->str);
4867 
4868 
4869     // SIGGRP, (c)(c) ...
4870     gstr = S57_getAttVal(geo, "SIGGRP");
4871     if (NULL != gstr)
4872         g_string_append(litdsn01, gstr->str);
4873 
4874 
4875     // SIGPER, xx.xx
4876     gstr = S57_getAttVal(geo, "SIGPER");
4877     if (NULL != gstr)
4878         g_string_append(litdsn01, gstr->str);
4879 
4880 
4881     // STATUS,
4882     gstr = S57_getAttVal(geo, "STATUS");
4883     if (NULL != gstr)
4884         g_string_append(litdsn01, gstr->str);
4885 
4886     /*
4887      1: permanent
4888      2: occasional      IP 50;      473.2;
4889      3: recommended     IN 10;      431.1;
4890      4: not in use      IL 14, 44;  444.7;
4891      5: periodic/intermittent IC 21; IQ 71;     353.3; 460.5;
4892      6: reserved  IN 12.9;
4893      7: temporary IP 54;
4894      8: private   IQ 70;
4895      9: mandatory
4896     10: destroyed/ruined
4897     11: extinguished
4898     12: illuminated
4899     13: historic
4900     14: public
4901     15: synchronized
4902     16: watched
4903     17: un-watched
4904     18: existence doubtful
4905     */
4906 
4907     // VALNMR, xx.x
4908     gstr = S57_getAttVal(geo, "VALNMR");
4909     if (NULL != gstr)
4910         g_string_append(litdsn01, gstr->str);
4911 
4912 
4913     PRINTF("FIXME: lights description not translated into text\n");
4914 
4915     return litdsn01;
4916 }
4917 
4918 static GString *_UDWHAZ03(S57_geo *geo, double depth_value);
4919 static GString *_QUAPNT01(S57_geo *geo);
4920 
4921 static GString *OBSTRN04 (S57_geo *geo)
4922 // Remarks: Obstructions or isolated underwater dangers of depths less than the safety
4923 // contour which lie within the safe waters defined by the safety contour are
4924 // to be presented by a specific isolated danger symbol and put in IMO
4925 // category DISPLAYBASE (see (3), App.2, 1.3). This task is performed
4926 // by the sub-procedure "UDWHAZ03" which is called by this symbology
4927 // procedure. Objects of the class "under water rock" are handled by this
4928 // routine as well to ensure a consistent symbolization of isolated dangers on
4929 // the seabed.
4930 {
4931     GString *obstrn04str = g_string_new("");
4932     GString *sndfrm02str = NULL;
4933     GString *udwhaz03str = NULL;
4934     GString *valsoustr   = S57_getAttVal(geo, "VALSOU");
4935     double   valsou      = UNKNOWN;
4936     double   depth_value = UNKNOWN;
4937     double   least_depth = UNKNOWN;
4938 
4939     // exit if not in drawing state
4940     if (1 == S52_state)
4941         return NULL;
4942 
4943     if (NULL != valsoustr) {
4944         valsou      = atof(valsoustr->str);
4945         depth_value = valsou;
4946         sndfrm02str = _SNDFRM02(geo, depth_value);
4947     } else {
4948         if (AREAS_T == S57_getObjtype(geo))
4949             least_depth = _DEPVAL01(geo, least_depth);
4950 
4951         if (UNKNOWN != least_depth) {
4952             GString *catobsstr = S57_getAttVal(geo, "CATOBS");
4953             GString *watlevstr = S57_getAttVal(geo, "WATLEV");
4954 
4955             if (NULL != catobsstr && '6' == *catobsstr->str)
4956                 depth_value = 0.01;
4957             else
4958                 if (NULL == watlevstr) // default
4959                     depth_value = -15.0;
4960                 else {
4961                     switch (*watlevstr->str){
4962                         case '5': depth_value =   0.0 ; break;
4963                         case '3': depth_value =   0.01; break;
4964                         case '4':
4965                         case '1':
4966                         case '2':
4967                         default : depth_value = -15.0 ; break;
4968                     }
4969                 }
4970         } else
4971             depth_value = least_depth;
4972     }
4973 
4974     udwhaz03str = _UDWHAZ03(geo, depth_value);
4975 
4976     if (POINT_T == S57_getObjtype(geo)) {
4977         // Continuation A
4978         int      sounding    = FALSE;
4979         GString *quapnt01str = _QUAPNT01(geo);
4980 
4981         if (NULL != udwhaz03str){
4982             g_string_append(obstrn04str, udwhaz03str->str);
4983             if (NULL != quapnt01str)
4984                 g_string_append(obstrn04str, quapnt01str->str);
4985 
4986             if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
4987             if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
4988             if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
4989 
4990             return obstrn04str;
4991         }
4992 
4993         if (UNKNOWN != valsou) {
4994             if (valsou <= 20.0) {
4995                 GString *objlstr   = S57_getAttVal(geo, "OBJL");
4996                 int      objl      = (NULL == objlstr)? 0 : atoi(objlstr->str);
4997                 GString *watlevstr = S57_getAttVal(geo, "WATLEV");
4998 
4999                 if (UWTROC == objl) {
5000                     if (NULL == watlevstr) {  // default
5001                         g_string_append(obstrn04str, ";SY(DANGER01)");
5002                         sounding = TRUE;
5003                     } else {
5004                         switch (*watlevstr->str){
5005                             case '3': g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE ; break;
5006                             case '4':
5007                             case '5': g_string_append(obstrn04str, ";SY(UWTROC04)"); sounding = FALSE; break;
5008                             default : g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE ; break;
5009                         }
5010                     }
5011                 } else { // OBSTRN
5012                     if (NULL == watlevstr) { // default
5013                         g_string_append(obstrn04str, ";SY(DANGER01)");
5014                         sounding = TRUE;
5015                     } else {
5016                         switch (*watlevstr->str) {
5017                             case '1':
5018                             case '2': g_string_append(obstrn04str, ";SY(OBSTRN11)"); sounding = FALSE; break;
5019                             case '3': g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE;  break;
5020                             case '4':
5021                             case '5': g_string_append(obstrn04str, ";SY(DANGER03)"); sounding = TRUE; break;
5022                             default : g_string_append(obstrn04str, ";SY(DANGER01)"); sounding = TRUE; break;
5023                         }
5024                     }
5025                 }
5026             } else {  // valsou > 20.0
5027                 g_string_append(obstrn04str, ";SY(DANGER02)");
5028                 sounding = FALSE;
5029             }
5030 
5031         } else {  // NO valsou
5032                 GString *objlstr   = S57_getAttVal(geo, "OBJL");
5033                 int     objl       = (NULL == objlstr)? 0 : atoi(objlstr->str);
5034                 GString *watlevstr = S57_getAttVal(geo, "WATLEV");
5035 
5036                 if (UWTROC == objl) {
5037                     if (NULL == watlevstr)  // default
5038                        g_string_append(obstrn04str, ";SY(UWTROC04)");
5039                     else {
5040                         if ('3' == *watlevstr->str)
5041                             g_string_append(obstrn04str, ";SY(UWTROC03)");
5042                         else
5043                             g_string_append(obstrn04str, ";SY(UWTROC04)");
5044                     }
5045 
5046                 } else { // OBSTRN
5047                     if ( NULL == watlevstr) // default
5048                         g_string_append(obstrn04str, ";SY(OBSTRN01)");
5049                     else {
5050                         switch (*watlevstr->str) {
5051                             case '1':
5052                             case '2': g_string_append(obstrn04str, ";SY(OBSTRN11)"); break;
5053                             case '3': g_string_append(obstrn04str, ";SY(OBSTRN01)"); break;
5054                             case '4':
5055                             case '5':
5056                             default : g_string_append(obstrn04str, ";SY(OBSTRN01)"); break;
5057                         }
5058                     }
5059                 }
5060 
5061         }
5062 
5063         if (sounding && NULL != sndfrm02str)
5064             g_string_append(obstrn04str, sndfrm02str->str);
5065 
5066         if (NULL != quapnt01str)
5067             g_string_append(obstrn04str, quapnt01str->str);
5068 
5069         if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
5070         if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
5071         if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
5072 
5073         return obstrn04str;
5074 
5075     } else {
5076         if (LINES_T == S57_getObjtype(geo)) {
5077             // Continuation B
5078             GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
5079             int      quapos    = 0;
5080 
5081             if (NULL != quaposstr) {
5082                 quapos = atoi(quaposstr->str);
5083                 if ( 2 <= quapos && quapos < 10){
5084                     if (NULL != udwhaz03str)
5085                         g_string_append(obstrn04str, ";LC(LOWACC41)");
5086                     else
5087                         g_string_append(obstrn04str, ";LC(LOWACC31)");
5088                 }
5089             }
5090 
5091             if (NULL != udwhaz03str)
5092                 g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
5093 
5094             if (UNKNOWN != valsou)
5095                 if (valsou <= 20.0)
5096                     g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
5097                 else
5098                     g_string_append(obstrn04str, ";LS(DASH,2,CHBLK)");
5099             else
5100                 g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
5101 
5102 
5103             if (NULL != udwhaz03str)
5104                 g_string_append(obstrn04str, udwhaz03str->str);
5105             else {
5106                 if (UNKNOWN != valsou)
5107                     if (valsou <= 20.0)
5108                         g_string_append(obstrn04str, sndfrm02str->str);
5109             }
5110 
5111             if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
5112             if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
5113 
5114             return obstrn04str;
5115 
5116         } else {
5117             // Continuation C (AREAS_T)
5118             GString *quapnt01str = _QUAPNT01(geo);
5119             if (NULL != udwhaz03str) {
5120                 g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01)");
5121                 g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
5122                 g_string_append(obstrn04str, udwhaz03str->str);
5123                 if (NULL != quapnt01str)
5124                     g_string_append(obstrn04str, quapnt01str->str);
5125 
5126                 if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
5127                 if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
5128                 if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
5129 
5130                 return obstrn04str;
5131             }
5132 
5133             if (UNKNOWN != valsou) {
5134                 // BUG in CA49995B.000 if we get here because there is no color
5135                 // beside NODATA (ie there is a hole in group 1 area!)
5136                 //g_string_append(obstrn04, ";AC(UINFR)");
5137 
5138                 if (valsou <= 20.0)
5139                     g_string_append(obstrn04str, ";LS(DOTT,2,CHBLK)");
5140                 else
5141                     g_string_append(obstrn04str, ";LS(DASH,2,CHBLK)");
5142 
5143                 g_string_append(obstrn04str, sndfrm02str->str);
5144 
5145             } else {
5146                 GString *watlevstr = S57_getAttVal(geo, "WATLEV");
5147 
5148                 if (NULL == watlevstr)   // default
5149                     g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)");
5150                 else {
5151                     if ('3' == *watlevstr->str) {
5152                         GString *catobsstr = S57_getAttVal(geo, "CATOBS");
5153                         if (NULL != catobsstr && '6' == *catobsstr->str)
5154                             g_string_append(obstrn04str, ";AC(DEPVS);AP(FOULAR01);LS(DOTT,2,CHBLK)");
5155                     } else {
5156                         switch (*watlevstr->str) {
5157                             case '1':
5158                             case '2': g_string_append(obstrn04str, ";AC(CHBRN);LS(SOLD,2,CSTLN)"); break;
5159                             case '4': g_string_append(obstrn04str, ";AC(DEPIT);LS(DASH,2,CSTLN)"); break;
5160                             case '5':
5161                             case '3':
5162                             default : g_string_append(obstrn04str, ";AC(DEPVS);LS(DOTT,2,CHBLK)");  break;
5163                         }
5164                     }
5165                 }
5166             }
5167 
5168             g_string_append(obstrn04str, quapnt01str->str);
5169 
5170             if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
5171             if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
5172             if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
5173 
5174 
5175             return obstrn04str;
5176         }
5177 
5178     }
5179 
5180 
5181     // check if one exit point could do!!!
5182     return NULL;
5183 }
5184 
5185 static GString *OWNSHP02 (S57_geo *geo)
5186 // Remarks:
5187 // 1. CONNING POSITION
5188 //    1.1 When own-ship is drawn to scale, the conning position must be correctly located in
5189 //        relation to the ship's outline. The conning position then serves as the pivot point for
5190 //        the own-ship symbol, to be located by the ECDIS at the correct latitude, longitude
5191 //        for the conning point, as computed from the positioning system, correcting for
5192 //        antenna offset.
5193 //    1.2 In this procedure it is assumed that the heading line, beam bearing line and course
5194 //        and speed vector originate at the conning point. If another point of origin is used,
5195 //        for example to account for the varying position of the ships turning centre, this must
5196 //        be made clear to the mariner.
5197 //
5198 // 2. DISPLAY OPTIONS
5199 //    2.1 Only the ship symbol is mandatory for an ECDIS. The mariner should be prompted
5200 //        to select from the following additional optional features:
5201 //    - display own-ship as:
5202 //        1. symbol, or
5203 //        2. scaled outline
5204 //    - select time period determining vector length for own-ship and other vessel course and speed
5205 //      vectors, (all vectors must be for the same time period),
5206 //    - display own-ship vector,
5207 //    - select ground or water stabilization for all vectors, and select whether to display the type of
5208 //      stabilization, (by arrowhead),
5209 //    - select one-minute or six-minute vector time marks,
5210 //    - select whether to show a heading line, to the edge of the display window,
5211 //    - select whether to show a beam bearing line, and if so what length (default: 10mm total
5212 //      length).
5213 {
5214     PRINTF("Mariner's object not drawn\n");
5215     return NULL;
5216 }
5217 
5218 static GString *PASTRK01 (S57_geo *geo)
5219 // Remarks: This conditional symbology procedure was designed to allow the mariner
5220 // to select time labels at the pasttrack (see (3) 10.5.11.1). The procedure also
5221 // cares for the presentation of primary and secondary pasttrack.
5222 //
5223 // The manufacturer should define his own data class (spatial primitive) in xyt
5224 // (position and time) in order to represent Pastrk.
5225 {
5226 
5227     PRINTF("Mariner's object not drawn\n");
5228     return NULL;
5229 }
5230 
5231 static GString *_QUALIN01(S57_geo *geo);
5232 static GString *QUAPOS01 (S57_geo *geo)
5233 // Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
5234 // to the spatial object, not the feature object.
5235 //
5236 // This procedure passes the object to procedure QUALIN01 or QUAPNT01,
5237 // which traces back to the spatial object, retrieves any QUAPOS attributes,
5238 // and returns the appropriate symbolization to QUAPOS01.
5239 {
5240     GString *quapos01 = NULL;
5241 
5242     if (LINES_T == S57_getObjtype(geo))
5243         quapos01 = _QUALIN01(geo);
5244     else
5245         quapos01 = _QUAPNT01(geo);
5246 
5247     return quapos01;
5248 }
5249 
5250 static GString *_QUALIN01(S57_geo *geo)
5251 // Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
5252 // only to the spatial component(s) of an object.
5253 //
5254 // A line object may be composed of more than one spatial object.
5255 //
5256 // This procedure looks at each of the spatial
5257 // objects, and symbolizes the line according to the positional accuracy.
5258 {
5259     GString *qualino1  = NULL;
5260     GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
5261     int      quapos    = 0;
5262     char    *line      = NULL;
5263 
5264     if (NULL != quaposstr) {
5265         quapos = atoi(quaposstr->str);
5266         if ( 2 <= quapos && quapos < 10)
5267             line = ";LC(LOWACC21)";
5268     } else {
5269         GString *objlstr = S57_getAttVal(geo, "OBJL");
5270         int      objl    = (NULL == objlstr)? 0 : atoi(objlstr->str);
5271 
5272         if (COALNE == objl) {
5273             GString *conradstr = S57_getAttVal(geo, "CONRAD");
5274 
5275             if (NULL != conradstr) {
5276                 if ('1' == *conradstr->str)
5277                     line = ";LS(SOLD,3,CHMGF);LS(SOLD,1,CSTLN)";
5278                 else
5279                     line = ";LS(SOLD,1,CSTLN)";
5280             } else
5281                 line = ";LS(SOLD,1,CSTLN)";
5282 
5283         } else  //LNDARE
5284             line = ";LS(SOLD,1,CSTLN)";
5285     }
5286 
5287     if (NULL != line)
5288         qualino1 = g_string_new(line);
5289 
5290     return qualino1;
5291 }
5292 
5293 static GString *_QUAPNT01(S57_geo *geo)
5294 // Remarks: The attribute QUAPOS, which identifies low positional accuracy, is attached
5295 // only to the spatial component(s) of an object.
5296 //
5297 // This procedure retrieves any QUAPOS attributes, and returns the
5298 // appropriate symbols to the calling procedure.
5299 {
5300     GString *quapnt01  = NULL;
5301     int      accurate  = TRUE;
5302     GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
5303     int      quapos    = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
5304 
5305     if (NULL != quaposstr) {
5306         if ( 2 <= quapos && quapos < 10)
5307             accurate = FALSE;
5308     }
5309 
5310     if (accurate)
5311         quapnt01 = g_string_new(";SY(LOWACC01)");
5312 
5313     return quapnt01;
5314 }
5315 
5316 static GString *SLCONS03 (S57_geo *geo)
5317 // Remarks: Shoreline construction objects which have a QUAPOS attribute on their
5318 // spatial component indicating that their position is unreliable are symbolized
5319 // by a special linestyle in the place of the varied linestyles normally used.
5320 // Otherwise this procedure applies the normal symbolization.
5321 {
5322     GString *slcons03  = NULL;
5323     GString *valstr    = NULL;
5324     char    *cmdw      = NULL;   // command word
5325     GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
5326     int      quapos    = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
5327 
5328     if (POINT_T == S57_getObjtype(geo)) {
5329         if (NULL != quaposstr) {
5330             if (2 <= quapos && quapos < 10)
5331                 cmdw =";SY(LOWACC01)";
5332         }
5333     } else {
5334         // LINE_T and AREA_T are the same
5335         if (NULL != quaposstr) {
5336             if (2 <= quapos && quapos < 10)
5337                 cmdw =";LC(LOWACC01)";
5338         } else {
5339             valstr = S57_getAttVal(geo, "CONDTN");
5340 
5341             if (NULL != valstr && ( '1' == *valstr->str || '2' == *valstr->str))
5342                     cmdw = ";LS(DASH,1,CSTLN)";
5343             else {
5344                 int val = 0;
5345                 valstr  = S57_getAttVal(geo, "CATSLC");
5346                 val     = (NULL == valstr)? 0 : atoi(valstr->str);
5347 
5348                 if (NULL != valstr && ( 6  == val || 15 == val || 16 == val ))
5349                         cmdw = ";LS(SOLD,4,CSTLN)";
5350                 else {
5351                     valstr = S57_getAttVal(geo, "WATLEV");
5352 
5353                     if (NULL != valstr && '2' == *valstr->str)
5354                             cmdw = ";LS(SOLD,2,CSTLN)";
5355                     else
5356                         if (NULL != valstr && ('3' == *valstr->str || '4' == *valstr->str))
5357                             cmdw = ";LS(DASH,2,CSTLN)";
5358                         else
5359                             cmdw = ";LS(SOLD,2,CSTLN)";  // default
5360 
5361                 }
5362             }
5363         }
5364     }
5365 
5366     // WARNING: not explicitly specified in S-52 !!
5367     // FIXME:this is to put AC(DEPIT) --intertidal area
5368     // Could this be bug in OGR ?
5369     /*
5370     if (AREAS_T == S57_getObjtype(geo)) {
5371         GString    *seabed01  = NULL;
5372         GString    *drval1str = S57_getAttVal(geo, "DRVAL1");
5373         double      drval1    = (NULL == drval1str)? -UNKNOWN : atof(drval1str->str);
5374         GString    *drval2str = S57_getAttVal(geo, "DRVAL2");
5375         double      drval2    = (NULL == drval2str)? -UNKNOWN : atof(drval2str->str);
5376         // NOTE: change sign of infinity (minus) to get out of bound in seabed01
5377 
5378 
5379         PRINTF("***********drval1=%f drval2=%f \n", drval1, drval2);
5380         seabed01 = _SEABED01(drval1, drval2);
5381         slcons03 = g_string_new(seabed01->str);
5382         g_string_free(seabed01, TRUE);
5383 
5384     }
5385     */
5386 
5387     if (NULL != cmdw) {
5388         if (NULL == slcons03)
5389             slcons03 = g_string_new(cmdw);
5390         else
5391             g_string_append(slcons03, cmdw);
5392     }
5393 
5394     return slcons03;
5395 }
5396 
5397 static GString *RESARE02 (S57_geo *geo)
5398 // Remarks: A list-type attribute is used because an area of the object class RESARE may
5399 // have more than one category (CATREA). For example an inshore traffic
5400 // zone might also have fishing and anchoring prohibition and a prohibited
5401 // area might also be a bird sanctuary or a mine field.
5402 //
5403 // This conditional procedure is set up to ensure that the categories of most
5404 // importance to safe navigation are prominently symbolized, and to pass on
5405 // all given information with minimum clutter. Only the most significant
5406 // restriction is symbolized, and an indication of further limitations is given by
5407 // a subscript "!" or "I". Further details are given under conditional
5408 // symbology procedure RESTRN01
5409 //
5410 // Other object classes affected by attribute RESTRN are handled by
5411 // conditional symbology procedure RESTRN01.
5412 {
5413     GString *resare02         = g_string_new("");
5414     GString *restrnstr        = S57_getAttVal(geo, "RESTRN");
5415     char     restrn[LISTSIZE] = {'\0'};
5416     GString *catreastr        = S57_getAttVal(geo, "CATREA");
5417     char     catrea[LISTSIZE] = {'\0'};
5418     char    *symb             = NULL;
5419     char    *line             = NULL;
5420     char    *prio             = NULL;
5421 
5422     if ( NULL != restrnstr) {
5423         _parseList(restrnstr->str, restrn);
5424 
5425         if (NULL != catreastr) _parseList(catreastr->str, catrea);
5426 
5427         if (strpbrk(restrn, "\007\010\016")) {
5428             // Continuation A
5429             if (strpbrk(restrn, "\001\002\003\004\005\006"))
5430                 symb = ";SY(ENTRES61)";
5431             else {
5432                 if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
5433                         symb = ";SY(ENTRES61)";
5434                 else {
5435                     if (strpbrk(restrn, "\011\012\013\014\015"))
5436                         symb = ";SY(ENTRES71)";
5437                     else {
5438                         if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
5439                             symb = ";SY(ENTRES71)";
5440                         else
5441                             symb = ";SY(ENTRES51)";
5442                     }
5443                 }
5444             }
5445 
5446             if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
5447                 line = ";LC(CTYARE51)";
5448             else
5449                 line = ";LS(DASH,2,CHMGD)";
5450 
5451             prio = ";OP(6---)";  // display prio set to 6
5452 
5453         } else {
5454             if (strpbrk(restrn, "\001\002")) {
5455                 // Continuation B
5456                 if (strpbrk(restrn, "\003\004\005\006"))
5457                     symb = ";SY(ACHRES61)";
5458                 else {
5459                     if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
5460                             symb = ";SY(ACHRES61)";
5461                     else {
5462                         if (strpbrk(restrn, "\011\012\013\014\015"))
5463                             symb = ";SY(ACHRES71)";
5464                         else {
5465                             if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
5466                                 symb = ";SY(ACHRES71)";
5467                             else
5468                                 symb = ";SY(ACHRES51)";
5469                         }
5470                     }
5471                 }
5472 
5473                 if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
5474                     line = ";LC(ACHRE51)";
5475                 else
5476                     line = ";LS(DASH,2,CHMGD)";
5477 
5478                 prio = ";OP(6---)";  // display prio set to 6
5479 
5480             } else {
5481                 if (strpbrk(restrn, "\003\004\005\006")) {
5482                     // Continuation C
5483                     if (NULL != catreastr && strpbrk(catrea, "\001\010\011\014\016\023\025\031"))
5484                             symb = ";SY(FSHRES51)";
5485                     else {
5486                         if (strpbrk(restrn, "\011\012\013\014\015"))
5487                             symb = ";SY(FSHRES71)";
5488                         else{
5489                             if (NULL != catreastr && strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
5490                                 symb = ";SY(FSHRES71)";
5491                             else
5492                                 symb = ";SY(FSHRES51)";
5493                         }
5494                     }
5495 
5496                     if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
5497                         line = ";LC(FSHRES51)";
5498                     else
5499                         line = ";LS(DASH,2,CHMGD)";
5500 
5501                     prio = ";OP(6---)";  // display prio set to 6
5502 
5503                 } else {
5504                     if (strpbrk(restrn, "\011\012\013\014\015"))
5505                         symb = ";SY(INFARE51)";
5506                     else
5507                         symb = ";SY(RSRDEF51)";
5508 
5509                     if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
5510                         line = ";LC(CTYARE51)";
5511                     else
5512                         line = ";LS(DASH,2,CHMGD)";
5513 
5514                 }
5515             }
5516         }
5517 
5518     } else {
5519         // Continuation D
5520         if (NULL != catreastr) {
5521             if (strpbrk(catrea, "\001\010\011\014\016\023\025\031")) {
5522                 if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
5523                     symb = ";SY(CTYARE71)";
5524                 else
5525                     symb = ";SY(CTYARE51)";
5526             } else {
5527                 if (strpbrk(catrea, "\004\005\006\007\012\022\024\026\027\030"))
5528                     symb = ";SY(INFARE71)";
5529                 else
5530                     symb = ";SY(RSRDEF51)";
5531             }
5532         } else
5533             symb = ";SY(RSRDEF51)";
5534 
5535         if (TRUE == S52_getMarinerParam(S52_MAR_SYMBOLIZED_BND))
5536             line = ";LC(CTYARE51)";
5537         else
5538             line = ";LS(DASH,2,CHMGD)";
5539     }
5540 
5541     // create command word
5542     if (NULL != prio)
5543         g_string_append(resare02, prio);
5544     g_string_append(resare02, line);
5545     g_string_append(resare02, symb);
5546 
5547     return resare02;
5548 }
5549 
5550 static GString *RESTRN01 (S57_geo *geo)
5551 // Remarks: Objects subject to RESTRN01 are actually symbolised in sub-process
5552 // RESCSP01, since the latter can also be accessed from other conditional
5553 // symbology procedures. RESTRN01 merely acts as a "signpost" for
5554 // RESCSP01.
5555 //
5556 // Object class RESARE is symbolised for the effect of attribute RESTRN in a separate
5557 // conditional symbology procedure called RESARE02.
5558 //
5559 // Since many of the areas concerned cover shipping channels, the number of symbols used
5560 // is minimised to reduce clutter. To do this, values of RESTRN are ranked for significance
5561 // as follows:
5562 // "Traffic Restriction" values of RESTRN:
5563 // (1) RESTRN 7,8: entry prohibited or restricted
5564 //     RESTRN 14: IMO designated "area to be avoided" part of a TSS
5565 // (2) RESTRN 1,2: anchoring prohibited or restricted
5566 // (3) RESTRN 3,4,5,6: fishing or trawling prohibited or restricted
5567 // (4) "Other Restriction" values of RESTRN are:
5568 //     RESTRN 9, 10: dredging prohibited or restricted,
5569 //     RESTRN 11,12: diving prohibited or restricted,
5570 //     RESTRN 13   : no wake area.
5571 {
5572     GString *restrn01str = S57_getAttVal(geo, "RESTRN");
5573     GString *restrn01    = NULL;
5574 
5575     if (NULL != restrn01str)
5576         restrn01 = _RESCSP01(geo);
5577     else
5578         restrn01 = g_string_new(";OP(----)");  // return NOOP to silence error msg
5579 
5580     return restrn01;
5581 }
5582 
5583 static GString *_RESCSP01(S57_geo *geo)
5584 // Remarks: See procedure RESTRN01
5585 {
5586     GString *rescsp01         = NULL;
5587     GString *restrnstr        = S57_getAttVal(geo, "RESTRN");
5588     char     restrn[LISTSIZE] = {'\0'};   // restriction list
5589     char    *symb             = NULL;
5590 
5591     if ( NULL != restrnstr) {
5592         _parseList(restrnstr->str, restrn);
5593 
5594         if (strpbrk(restrn, "\007\010\016")) {
5595             // continuation A
5596             if (strpbrk(restrn, "\001\002\003\004\005\006"))
5597                 symb = ";SY(ENTRES61)";
5598             else {
5599                 if (strpbrk(restrn, "\011\012\013\014\015"))
5600                     symb = ";SY(ENTRES71)";
5601                 else
5602                     symb = ";SY(ENTRES51)";
5603 
5604             }
5605         } else {
5606             if (strpbrk(restrn, "\001\002")) {
5607                 // continuation B
5608                 if (strpbrk(restrn, "\003\004\005\006"))
5609                     symb = ";SY(ACHRES61)";
5610                 else {
5611                     if (strpbrk(restrn, "\011\012\013\014\015"))
5612                         symb = ";SY(ACHRES71)";
5613                     else
5614                         symb = ";SY(ACHRES51)";
5615                 }
5616 
5617 
5618             } else {
5619                 if (strpbrk(restrn, "\003\004\005\006")) {
5620                     // continuation C
5621                     if (strpbrk(restrn, "\011\012\013\014\015"))
5622                         symb = ";SY(FSHRES71)";
5623                     else
5624                         symb = ";SY(FSHRES51)";
5625 
5626 
5627                 } else {
5628                     if (strpbrk(restrn, "\011\012\013\014\015"))
5629                         symb = ";SY(INFARE51)";
5630                     else
5631                         symb = ";SY(RSRDEF51)";
5632 
5633                 }
5634             }
5635         }
5636 
5637         rescsp01 = g_string_new(symb);
5638     }
5639 
5640     return rescsp01;
5641 }
5642 
5643 static GString *_SEABED01(double drval1, double drval2)
5644 // Remarks: An area object that is part of the seabed is coloured as necessary according
5645 // to the mariners selection of two shades, (shallow contour, safety contour,
5646 // deep contour), or four shades (safety contour only). This requires a decision
5647 // making process provided by this conditional symbology procedure. Note
5648 // that this procedure is called as a sub-procedure by other conditional
5649 // symbology procedures.
5650 //
5651 // Note: The requirement to show four depth shades is not mandatory. Also,
5652 // the requirement to show the shallow pattern is not mandatory. However,
5653 // both these features are strongly recommended.
5654 
5655 // return: is never NULL
5656 
5657 {
5658     GString *seabed01 = NULL;
5659     gboolean shallow  = TRUE;
5660     char    *arecol   = ";AC(DEPIT)";
5661 
5662     if (drval1 >= 0.0 && drval2 > 0.0)
5663         arecol  = ";AC(DEPVS)";
5664 
5665     if (TRUE == S52_getMarinerParam(S52_MAR_TWO_SHADES)){
5666         if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)  &&
5667             drval2 >  S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
5668             arecol  = ";AC(DEPDW)";
5669             shallow = FALSE;
5670         }
5671     } else {
5672         if (drval1 >= S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR) &&
5673             drval2 >  S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR))
5674             arecol  = ";AC(DEPMS)";
5675 
5676             if (drval1 >= S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)  &&
5677                 drval2 >  S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR)) {
5678             arecol  = ";AC(DEPMD)";
5679             shallow = FALSE;
5680         }
5681 
5682             if (drval1 >= S52_getMarinerParam(S52_MAR_DEEP_CONTOUR)  &&
5683                 drval2 >  S52_getMarinerParam(S52_MAR_DEEP_CONTOUR)) {
5684             arecol  = ";AC(DEPDW)";
5685             shallow = FALSE;
5686         }
5687 
5688     }
5689 
5690     seabed01 = g_string_new(arecol);
5691 
5692     if (TRUE==S52_getMarinerParam(S52_MAR_SHALLOW_PATTERN) && TRUE==shallow)
5693         g_string_append(seabed01, ";AP(DIAMOND1)");
5694 
5695     return seabed01;
5696 }
5697 
5698 static GString *SOUNDG02 (S57_geo *geo)
5699 // Remarks: In S-57 soundings are elements of sounding arrays rather than individual
5700 // objects. Thus this conditional symbology procedure examines each
5701 // sounding of a sounding array one by one. To symbolize the depth values it
5702 // calls the procedure SNDFRM02 which in turn translates the depth values
5703 // into a set of symbols to be shown at the soundings position.
5704 {
5705     guint   npt = 0;
5706     double *ppt = NULL;
5707 
5708     if (POINT_T != S57_getObjtype(geo)) {
5709         PRINTF("invalid object type (not M_PNT_T)\n");
5710         //return NULL;
5711         exit(0);
5712     }
5713 
5714     S57_getGeoData(geo, 0, &npt, &ppt);
5715 
5716     return _SNDFRM02(geo, ppt[2]);
5717 }
5718 
5719 static GString *_SNDFRM02(S57_geo *geo, double depth_value)
5720 // Remarks: Soundings differ from plain text because they have to be readable under all
5721 // circumstances and their digits are placed according to special rules. This
5722 // conditional symbology procedure accesses a set of carefully designed
5723 // sounding symbols provided by the symbol library and composes them to
5724 // sounding labels. It symbolizes swept depth and it also symbolizes for low
5725 // reliability as indicated by attributes QUASOU and QUAPOS.
5726 {
5727     GString *sndfrm02         = g_string_new("");
5728     char    *symbol_prefix    = NULL;
5729     GString *tecsoustr        = S57_getAttVal(geo, "TECSOU");
5730     char     tecsou[LISTSIZE] = {'\0'};
5731     GString *quasoustr        = S57_getAttVal(geo, "QUASOU");
5732     char     quasou[LISTSIZE] = {'\0'};
5733     GString *statusstr        = S57_getAttVal(geo, "STATUS");
5734     char     status[LISTSIZE] = {'\0'};
5735     double   leading_digit    = 0.0;
5736 
5737     // FIXME: test to fix the rounding error (!?)
5738     depth_value  += (depth_value > 0.0)? 0.01: -0.01;
5739     leading_digit = (int) depth_value;
5740 
5741     if (depth_value <= S52_getMarinerParam(S52_MAR_SAFETY_DEPTH))
5742         symbol_prefix = "SOUNDS";
5743     else
5744         symbol_prefix = "SOUNDG";
5745 
5746     if (NULL != tecsoustr) {
5747         _parseList(tecsoustr->str, tecsou);
5748         if (strpbrk(tecsou, "\006"))
5749             g_string_sprintfa(sndfrm02, ";SY(%sB1)", symbol_prefix);
5750     }
5751 
5752     if (NULL != quasoustr) _parseList(quasoustr->str, quasou);
5753     if (NULL != statusstr) _parseList(statusstr->str, status);
5754 
5755     if (strpbrk(quasou, "\003\004\005\010\011") || strpbrk(status, "\022"))
5756             g_string_sprintfa(sndfrm02, ";SY(%sC2)", symbol_prefix);
5757     else {
5758         GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
5759         int      quapos    = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
5760 
5761         if (NULL != quaposstr) {
5762             if (2 <= quapos && quapos < 10)
5763                 g_string_sprintfa(sndfrm02, ";SY(%sC2)", symbol_prefix);
5764         }
5765     }
5766 
5767     // Continuation A
5768     if (depth_value < 10.0) {
5769         // can be above water (negative)
5770         int fraction = (int)ABS((depth_value - leading_digit)*10);
5771 
5772         g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)ABS(leading_digit));
5773         g_string_sprintfa(sndfrm02, ";SY(%s5%1i)", symbol_prefix, fraction);
5774 
5775         // above sea level (negative)
5776         if (depth_value < 0.0)
5777             g_string_sprintfa(sndfrm02, ";SY(%sA1)", symbol_prefix);
5778 
5779         return sndfrm02;
5780     }
5781 
5782     if (depth_value < 31.0) {
5783         double fraction = depth_value - leading_digit;
5784 
5785         if (fraction != 0.0) {
5786             fraction = fraction * 10;
5787             if (leading_digit >= 10.0)
5788                 g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)leading_digit/10);
5789 
5790             g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)leading_digit);
5791             g_string_sprintfa(sndfrm02, ";SY(%s5%1i)", symbol_prefix, (int)fraction);
5792 
5793             return sndfrm02;
5794         }
5795     }
5796 
5797     // Continuation B
5798     depth_value = leading_digit;    // truncate to integer
5799     if (depth_value < 100.0) {
5800         double first_digit = leading_digit / 10;
5801         double secnd_digit = leading_digit - (first_digit * 10);
5802 
5803         g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)first_digit);
5804         g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)secnd_digit);
5805 
5806         return sndfrm02;
5807     }
5808 
5809     if (depth_value < 1000.0) {
5810         double first_digit = leading_digit / 100;
5811         double secnd_digit = leading_digit - (first_digit * 100);
5812         double third_digit = leading_digit - (first_digit * 100) - (secnd_digit * 10);
5813 
5814         g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)first_digit);
5815         g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)secnd_digit);
5816         g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)third_digit);
5817 
5818         return sndfrm02;
5819     }
5820 
5821     if (depth_value < 10000.0) {
5822         double first_digit = leading_digit / 1000;
5823         double secnd_digit = leading_digit - (first_digit * 1000);
5824         double third_digit = leading_digit - (first_digit * 1000) - (secnd_digit * 100);
5825         double last_digit  = leading_digit - (first_digit * 1000) - (secnd_digit * 100) - (third_digit * 100) ;
5826 
5827         g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)first_digit);
5828         g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)secnd_digit);
5829         g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)third_digit);
5830         g_string_sprintfa(sndfrm02, ";SY(%s4%1i)", symbol_prefix, (int)last_digit);
5831 
5832         return sndfrm02;
5833     }
5834 
5835     // Continuation C
5836     {
5837         double first_digit  = leading_digit / 10000;
5838         double secnd_digit  = leading_digit - (first_digit * 10000);
5839         double third_digit  = leading_digit - (first_digit * 10000) - (secnd_digit * 1000);
5840         double fourth_digit = leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100) ;
5841         double last_digit   = leading_digit - (first_digit * 10000) - (secnd_digit * 1000) - (third_digit * 100) - (fourth_digit * 10) ;
5842 
5843         g_string_sprintfa(sndfrm02, ";SY(%s3%1i)", symbol_prefix, (int)first_digit);
5844         g_string_sprintfa(sndfrm02, ";SY(%s2%1i)", symbol_prefix, (int)secnd_digit);
5845         g_string_sprintfa(sndfrm02, ";SY(%s1%1i)", symbol_prefix, (int)third_digit);
5846         g_string_sprintfa(sndfrm02, ";SY(%s0%1i)", symbol_prefix, (int)fourth_digit);
5847         g_string_sprintfa(sndfrm02, ";SY(%s4%1i)", symbol_prefix, (int)last_digit);
5848 
5849         return sndfrm02;
5850     }
5851 
5852     return sndfrm02;
5853 }
5854 
5855 static GString *TOPMAR01 (S57_geo *geo)
5856 // Remarks: Topmark objects are to be symbolized through consideration of their
5857 // platforms e.g. a buoy. Therefore this conditional symbology procedure
5858 // searches for platforms by looking for other objects that are located at the
5859 // same position.. Based on the finding whether the platform is rigid or
5860 // floating, the respective upright or sloping symbol is selected and presented
5861 // at the objects location. Buoy symbols and topmark symbols have been
5862 // carefully designed to fit to each other when combined at the same position.
5863 // The result is a composed symbol that looks like the traditional symbols the
5864 // mariner is used to.
5865 {
5866     GString *topshpstr = S57_getAttVal(geo, "TOPSHP");
5867     GString *topmar    = NULL;
5868     char    *sy        = NULL;
5869 
5870     if (NULL == topshpstr)
5871         sy = ";SY(QUESMRK1)";
5872     else {
5873         int floating    = FALSE; // not a floating platform
5874         int topshp      = (NULL==topshpstr) ? 0 : atoi(topshpstr->str);
5875 
5876         if (TRUE == _atPtPos(geo, FLOATLIST))
5877             floating = TRUE;
5878         else
5879             // FIXME: this test is wierd since it doesn't affect 'floating'
5880             if (TRUE == _atPtPos(geo, RIGIDLIST))
5881                 floating = FALSE;
5882 
5883 
5884         if (floating) {
5885             // floating platform
5886             switch (topshp) {
5887                 case 1 : sy = ";SY(TOPMAR02)"; break;
5888                 case 2 : sy = ";SY(TOPMAR04)"; break;
5889                 case 3 : sy = ";SY(TOPMAR10)"; break;
5890                 case 4 : sy = ";SY(TOPMAR12)"; break;
5891 
5892                 case 5 : sy = ";SY(TOPMAR13)"; break;
5893                 case 6 : sy = ";SY(TOPMAR14)"; break;
5894                 case 7 : sy = ";SY(TOPMAR65)"; break;
5895                 case 8 : sy = ";SY(TOPMAR17)"; break;
5896 
5897                 case 9 : sy = ";SY(TOPMAR16)"; break;
5898                 case 10: sy = ";SY(TOPMAR08)"; break;
5899                 case 11: sy = ";SY(TOPMAR07)"; break;
5900                 case 12: sy = ";SY(TOPMAR14)"; break;
5901 
5902                 case 13: sy = ";SY(TOPMAR05)"; break;
5903                 case 14: sy = ";SY(TOPMAR06)"; break;
5904                 case 17: sy = ";SY(TMARDEF2)"; break;
5905                 case 18: sy = ";SY(TOPMAR10)"; break;
5906 
5907                 case 19: sy = ";SY(TOPMAR13)"; break;
5908                 case 20: sy = ";SY(TOPMAR14)"; break;
5909                 case 21: sy = ";SY(TOPMAR13)"; break;
5910                 case 22: sy = ";SY(TOPMAR14)"; break;
5911 
5912                 case 23: sy = ";SY(TOPMAR14)"; break;
5913                 case 24: sy = ";SY(TOPMAR02)"; break;
5914                 case 25: sy = ";SY(TOPMAR04)"; break;
5915                 case 26: sy = ";SY(TOPMAR10)"; break;
5916 
5917                 case 27: sy = ";SY(TOPMAR17)"; break;
5918                 case 28: sy = ";SY(TOPMAR18)"; break;
5919                 case 29: sy = ";SY(TOPMAR02)"; break;
5920                 case 30: sy = ";SY(TOPMAR17)"; break;
5921 
5922                 case 31: sy = ";SY(TOPMAR14)"; break;
5923                 case 32: sy = ";SY(TOPMAR10)"; break;
5924                 case 33: sy = ";SY(TMARDEF2)"; break;
5925                 default: sy = ";SY(TMARDEF2)"; break;
5926             }
5927         } else {
5928             // not a floating platform
5929             switch (topshp) {
5930                 case 1 : sy = ";SY(TOPMAR22)"; break;
5931                 case 2 : sy = ";SY(TOPMAR24)"; break;
5932                 case 3 : sy = ";SY(TOPMAR30)"; break;
5933                 case 4 : sy = ";SY(TOPMAR32)"; break;
5934 
5935                 case 5 : sy = ";SY(TOPMAR33)"; break;
5936                 case 6 : sy = ";SY(TOPMAR34)"; break;
5937                 case 7 : sy = ";SY(TOPMAR85)"; break;
5938                 case 8 : sy = ";SY(TOPMAR86)"; break;
5939 
5940                 case 9 : sy = ";SY(TOPMAR36)"; break;
5941                 case 10: sy = ";SY(TOPMAR28)"; break;
5942                 case 11: sy = ";SY(TOPMAR27)"; break;
5943                 case 12: sy = ";SY(TOPMAR14)"; break;
5944 
5945                 case 13: sy = ";SY(TOPMAR25)"; break;
5946                 case 14: sy = ";SY(TOPMAR26)"; break;
5947                 case 15: sy = ";SY(TOPMAR88)"; break;
5948                 case 16: sy = ";SY(TOPMAR87)"; break;
5949 
5950                 case 17: sy = ";SY(TMARDEF1)"; break;
5951                 case 18: sy = ";SY(TOPMAR30)"; break;
5952                 case 19: sy = ";SY(TOPMAR33)"; break;
5953                 case 20: sy = ";SY(TOPMAR34)"; break;
5954 
5955                 case 21: sy = ";SY(TOPMAR33)"; break;
5956                 case 22: sy = ";SY(TOPMAR34)"; break;
5957                 case 23: sy = ";SY(TOPMAR34)"; break;
5958                 case 24: sy = ";SY(TOPMAR22)"; break;
5959 
5960                 case 25: sy = ";SY(TOPMAR24)"; break;
5961                 case 26: sy = ";SY(TOPMAR30)"; break;
5962                 case 27: sy = ";SY(TOPMAR86)"; break;
5963                 case 28: sy = ";SY(TOPMAR89)"; break;
5964 
5965                 case 29: sy = ";SY(TOPMAR22)"; break;
5966                 case 30: sy = ";SY(TOPMAR86)"; break;
5967                 case 31: sy = ";SY(TOPMAR14)"; break;
5968                 case 32: sy = ";SY(TOPMAR30)"; break;
5969                 case 33: sy = ";SY(TMARDEF1)"; break;
5970                 default: sy = ";SY(TMARDEF1)"; break;
5971             }
5972         }
5973 
5974     }
5975 
5976     topmar = g_string_new(sy);
5977 
5978     return topmar;
5979 }
5980 
5981 /*
5982 static GString *_UDWHAZ03(S57_geo *geo, double depth_value)
5983 // Remarks: Obstructions or isolated underwater dangers of depths less than the safety
5984 // contour which lie within the safe waters defined by the safety contour are
5985 // to be presented by a specific isolated danger symbol as hazardous objects
5986 // and put in IMO category DISPLAYBASE (see (3), App.2, 1.3). This task
5987 // is performed by this conditional symbology procedure.
5988 {
5989     GString *udwhaz03str    = NULL;
5990     int      danger         = FALSE;
5991     double   safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
5992     S57_geo *geoTmp         = geo;
5993 
5994     if (depth_value <= safety_contour) {
5995         // that intersect this point/line/area for OBSTRN04
5996         // that intersect this point/area      for WRECKS02
5997 
5998         // get area DEPARE & DRGARE that intersect this point/line/area
5999         while (NULL != (geoTmp = S57_nextObj(geoTmp))) {
6000 
6001             if (LINES_T  == S57_getObjtype(geoTmp)) {
6002                 GString *drval2str = S57_getAttVal(geoTmp, "DRVAL2");
6003                 double   drval2    = (NULL == drval2str) ? 0.0 : atof(drval2str->str);
6004 
6005                 if (drval2 < safety_contour) {
6006                     danger = TRUE;
6007                     break;
6008                 }
6009 
6010             } else {
6011                 //
6012                 GString *drval1str = S57_getAttVal(geoTmp, "DRVAL1");
6013                 double   drval1    = (NULL == drval1str) ? 0.0 : atof(drval1str->str);
6014 
6015                 if (drval1 >= safety_contour) {
6016                     danger = TRUE;
6017                     break;
6018                 }
6019             }
6020 
6021         }
6022 
6023 
6024         //danger = TRUE;   // true
6025         if (TRUE == danger) {
6026             GString *watlevstr = S57_getAttVal(geo, "WATLEV");
6027             if (NULL != watlevstr && ('1' == *watlevstr->str || '2' == *watlevstr->str))
6028                 udwhaz03str = g_string_new(";OP(--D14050");
6029             else {
6030                 udwhaz03str = g_string_new(";OP(8OD14010);SY(ISODGR51)");
6031                 S57_setAtt(geo, "SCAMIN", "INFINITE");
6032             }
6033         }
6034     }
6035 
6036     S57_unlinkObj(geo);
6037 
6038     return udwhaz03str;
6039 }
6040 
6041 static GString *VESSEL01 (S57_geo *geo)
6042 // Remarks: The mariner should be prompted to select from the following options:
6043 // - ARPA target or AIS report (overall decision or vessel by vessel) (vesrce)
6044 // - *time-period determining vector-length for all vectors (vecper)
6045 // - whether to show a vector (overall or vessel by vessel) (vestat)
6046 // - *whether to symbolize vector stabilization (vecstb)
6047 // - *whether to show one-minute or six-minute vector time marks (vecmrk)
6048 // - whether to show heading line on AIS vessel reports (headng)
6049 // * Note that the same vector parameters should be used for own-ship and all vessel
6050 // vectors.
6051 {
6052     PRINTF("Mariner's object not drawn\n");
6053     return NULL;
6054 }
6055 
6056 static GString *VRMEBL01 (S57_geo *geo)
6057 // Remarks: This conditional symbology procedure symbolizes the three cases of range
6058 // circle, bearing line and range/bearing line. VRM's and EBL's can be ship-centred
6059 // or freely movable, and two line-styles are available
6060 {
6061     PRINTF("Mariner's object not drawn\n");
6062     return NULL;
6063 }
6064 
6065 static GString *WRECKS02 (S57_geo *geo)
6066 // Remarks: Wrecks of depths less than the safety contour which lie within the safe waters
6067 // defined by the safety contour are to be presented by a specific isolated
6068 // danger symbol and put in IMO category DISPLAYBASE (see (3), App.2,
6069 // 1.3). This task is performed by the sub-procedure "UDWHAZ03" which is
6070 // called by this symbology procedure.
6071 {
6072     GString *wrecks02str = NULL;
6073     GString *sndfrm02str = NULL;
6074     GString *udwhaz03str = NULL;
6075     GString *quapnt01str = NULL;
6076     double   least_depth = UNKNOWN;
6077     double   depth_value = UNKNOWN;
6078     GString *valsoustr   = S57_getAttVal(geo, "VALSOU");
6079     double   valsou      = UNKNOWN;
6080 
6081     // exit if not in drawing state
6082     if (1 == S52_state)
6083         return NULL;
6084 
6085     if (NULL != valsoustr) {
6086         valsou      = atof(valsoustr->str);
6087         depth_value = valsou;
6088         sndfrm02str = _SNDFRM02(geo, depth_value);
6089     } else {
6090         if (AREAS_T == S57_getObjtype(geo))
6091             least_depth = _DEPVAL01(geo, least_depth);
6092 
6093         if (least_depth == UNKNOWN) {
6094             // WARNING: ambiguity removed in WRECKS03 (see update)
6095             GString *watlevstr = S57_getAttVal(geo, "WATLEV");
6096             GString *catwrkstr = S57_getAttVal(geo, "CATWRK");
6097 
6098             if (NULL == watlevstr) // default
6099                 depth_value = -15.0;
6100             else
6101                 switch (*watlevstr->str) { // ambiguous
6102                     case '1':
6103                     case '2': depth_value = -15.0 ; break;
6104                     case '3': depth_value =   0.01; break;
6105                     case '4': depth_value = -15.0 ; break;
6106                     case '5': depth_value =   0.0 ; break;
6107                     case '6': depth_value = -15.0 ; break;
6108                     default :{
6109                         if (NULL != catwrkstr) {
6110                             switch (*catwrkstr->str) {
6111                                 case '1': depth_value =  20.0; break;
6112                                 case '2': depth_value =   0.0; break;
6113                                 case '4':
6114                                 case '5': depth_value = -15.0; break;
6115                             }
6116                         }
6117                     }
6118                 }
6119         } else
6120             depth_value = least_depth;
6121 
6122 
6123     }
6124 
6125     udwhaz03str = _UDWHAZ03(geo, depth_value);
6126     quapnt01str = _QUAPNT01(geo);
6127 
6128     if (POINT_T == S57_getObjtype(geo)) {
6129         if (NULL != udwhaz03str) {
6130             wrecks02str = g_string_new(udwhaz03str->str);
6131 
6132             if (NULL != quapnt01str)
6133                 g_string_append(wrecks02str, quapnt01str->str);
6134 
6135         } else {
6136             // Continuation A (POINT_T)
6137             if (UNKNOWN != valsou) {
6138 
6139                 if (valsou <= 20.0) {
6140                     wrecks02str = g_string_new(";SY(DANGER01)");
6141                     if (NULL != sndfrm02str)
6142                         g_string_append(wrecks02str, sndfrm02str->str);
6143 
6144                 } else
6145                     wrecks02str = g_string_new(";SY(DANGER02)");
6146 
6147                 if (NULL != udwhaz03str)
6148                     g_string_append(wrecks02str, udwhaz03str->str);
6149                 if (NULL != quapnt01str)
6150                     g_string_append(wrecks02str, quapnt01str->str);
6151 
6152             } else {
6153                 char    *sym       = NULL;
6154                 GString *catwrkstr = S57_getAttVal(geo, "CATWRK");
6155                 GString *watlevstr = S57_getAttVal(geo, "WATLEV");
6156 
6157                 if (NULL != catwrkstr && NULL != watlevstr) {
6158                     if ('1' == *catwrkstr->str && '3' == *watlevstr->str)
6159                         sym =";SY(WRECKS04)";
6160                     else {
6161                         if ('2' == *catwrkstr->str && '3' == *watlevstr->str)
6162                             sym = ";SY(WRECKS05)";
6163                         else {
6164                             if ('4' == *catwrkstr->str || '5' == *catwrkstr->str)
6165                                 sym = ";SY(WRECKS01)";
6166                             else {
6167                                 if ('1' == *watlevstr->str ||
6168                                     '2' == *watlevstr->str ||
6169                                     '5' == *watlevstr->str ||
6170                                     '4' == *watlevstr->str ){
6171                                     sym = ";SY(WRECKS01)";
6172                                 } else
6173                                     sym = ";SY(WRECKS05)"; // default
6174 
6175                             }
6176                         }
6177                     }
6178                 }
6179 
6180                 wrecks02str = g_string_new(sym);
6181                 if (NULL != quapnt01str)
6182                     g_string_append(wrecks02str, quapnt01str->str);
6183 
6184             }
6185 
6186         }
6187 
6188 
6189     } else {
6190         // Continuation B (AREAS_T)
6191         GString *quaposstr = S57_getAttVal(geo, "QUAPOS");
6192         int      quapos    = (NULL == quaposstr)? 0 : atoi(quaposstr->str);
6193         char    *line      = NULL;
6194 
6195         if (2 <= quapos && quapos < 10)
6196             line = ";LC(LOWACC41)";
6197         else {
6198             if ( NULL != udwhaz03str)
6199                 line = ";LS(DOTT,2,CHBLK)";
6200             else {
6201                  if (UNKNOWN != valsou){
6202                      if (valsou <= 20)
6203                          line = ";LS(DOTT,2,CHBLK)";
6204                      else
6205                          line = ";LS(DASH,2,CHBLK)";
6206                  } else {
6207                      GString  *watlevstr = S57_getAttVal(geo, "WATLEV");
6208 
6209                      if (NULL == watlevstr)
6210                          line = ";LS(DOTT,2,CSTLN)";
6211                      else {
6212                          switch (*watlevstr->str){
6213                              case '1':
6214                              case '2': line = ";LS(SOLD,2,CSTLN)"; break;
6215                              case '4': line = ";LS(DASH,2,CSTLN)"; break;
6216                              case '3':
6217                              case '5':
6218 
6219                              default : line = ";LS(DOTT,2,CSTLN)"; break;
6220                          }
6221                      }
6222 
6223                  }
6224             }
6225         }
6226         wrecks02str = g_string_new(line);
6227 
6228         if (UNKNOWN != valsou) {
6229             if (valsou <= 20) {
6230                 if (NULL != udwhaz03str)
6231                     g_string_append(wrecks02str, udwhaz03str->str);
6232 
6233                 if (NULL != quapnt01str)
6234                     g_string_append(wrecks02str, quapnt01str->str);
6235 
6236                 if (NULL != sndfrm02str)
6237                     g_string_append(wrecks02str, sndfrm02str->str);
6238 
6239             } else {
6240                 // NOTE: ??? same as above ???
6241                 if (NULL != udwhaz03str)
6242                     g_string_append(wrecks02str, udwhaz03str->str);
6243 
6244                 if (NULL != quapnt01str)
6245                     g_string_append(wrecks02str, quapnt01str->str);
6246             }
6247         } else {
6248             char    *ac        = NULL;
6249             GString *watlevstr = S57_getAttVal(geo, "WATLEV");
6250 
6251             if (NULL == watlevstr)
6252                 ac = ";AC(DEPVS)";
6253             else
6254                 switch (*watlevstr->str) {
6255                     case '1':
6256                     case '2': ac = ";AC(CHBRN)"; break;
6257                     case '4': ac = ";AC(DEPIT)"; break;
6258                     case '5':
6259                     case '3':
6260                     default : ac = ";AC(DEPVS)"; break;
6261                 }
6262 
6263             g_string_append(wrecks02str, ac);
6264 
6265             if (NULL != udwhaz03str)
6266                 g_string_append(wrecks02str, udwhaz03str->str);
6267 
6268             if (NULL != quapnt01str)
6269                 g_string_append(wrecks02str, quapnt01str->str);
6270         }
6271     }
6272 
6273     if (NULL != sndfrm02str) g_string_free(sndfrm02str, TRUE);
6274     if (NULL != udwhaz03str) g_string_free(udwhaz03str, TRUE);
6275     if (NULL != quapnt01str) g_string_free(quapnt01str, TRUE);
6276 
6277     return wrecks02str;
6278 }
6279 
6280 
6281 //--------------------------------
6282 //
6283 // JUMP TABLE SECTION
6284 //
6285 //--------------------------------
6286 
6287 
6288 CondSymb condTable[] = {
6289    // name      call            Sub-Procedure
6290    {"CLRLIN01", CLRLIN01},   //
6291    {"DATCVR01", DATCVR01},   //
6292    {"DEPARE01", DEPARE01},   // _RESCSP01, _SEABED01
6293    {"DEPCNT02", DEPCNT02},   //
6294    {"LEGLIN02", LEGLIN02},   //                          str
6295    {"LIGHTS05", LIGHTS05},   // _LITDSN01
6296    {"OBSTRN04", OBSTRN04},   // _DEPVAL01, _QUAPNT01, _SNDFRM02, _UDWHAZ03
6297    {"OWNSHP02", OWNSHP02},   //
6298    {"PASTRK01", PASTRK01},   //
6299    {"QUAPOS01", QUAPOS01},   // _QUALIN01, _QUAPNT01
6300    {"SLCONS03", SLCONS03},   //
6301    {"RESARE02", RESARE02},   //
6302    {"RESTRN01", RESTRN01},   // _RESCSP01
6303    {"SOUNDG02", SOUNDG02},   // _SNDFRM02
6304    {"TOPMAR01", TOPMAR01},   //
6305    {"VESSEL01", VESSEL01},   //
6306    {"VRMEBL01", VRMEBL01},   //
6307    {"WRECKS02", WRECKS02},   // _DEPVAL01, _QUAPNT01, _SNDFRM02, _UDWHAZ03
6308 
6309    {"########",NULL}
6310 };
6311 */
6312 #endif
6313