1 /***************************************************************************
2  *
3  * Project:  OpenCPN
4  * Purpose:  S57 Chart Object
5  * Author:   David Register
6  *
7  ***************************************************************************
8  *   Copyright (C) 2010 by David S. Register                               *
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  *   This program is distributed in the hope that it will be useful,       *
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
18  *   GNU General Public License for more details.                          *
19  *                                                                         *
20  *   You should have received a copy of the GNU General Public License     *
21  *   along with this program; if not, write to the                         *
22  *   Free Software Foundation, Inc.,                                       *
23  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
24  **************************************************************************/
25 
26 // For compilers that support precompilation, includes "wx.h".
27 #include "wx/wxprec.h"
28 
29 #ifndef  WX_PRECOMP
30   #include "wx/wx.h"
31 #endif //precompiled headers
32 
33 #include "wx/image.h"                           // for some reason, needed for msvc???
34 #include "wx/tokenzr.h"
35 #include <wx/textfile.h>
36 #include <wx/filename.h>
37 
38 #include "dychart.h"
39 #include "OCPNPlatform.h"
40 
41 #include "s52s57.h"
42 #include "s52plib.h"
43 
44 #include "s57chart.h"
45 
46 #include "mygeom.h"
47 #include "cutil.h"
48 #include "georef.h"
49 #include "navutil.h"                            // for LogMessageOnce
50 #include "ocpn_pixel.h"
51 #include "ocpndc.h"
52 #include "s52utils.h"
53 #include "wx28compat.h"
54 #include "ChartDataInputStream.h"
55 
56 #include "gdal/cpl_csv.h"
57 #include "setjmp.h"
58 
59 #include "ogr_s57.h"
60 
61 #include "pluginmanager.h"                      // for S57 lights overlay
62 
63 #include "Osenc.h"
64 #include "chcanv.h"
65 #include "SencManager.h"
66 
67 #ifdef __MSVC__
68 #define _CRTDBG_MAP_ALLOC
69 #include <stdlib.h>
70 #include <crtdbg.h>
71 #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__ )
72 #define new DEBUG_NEW
73 #endif
74 
75 #ifdef ocpnUSE_GL
76 #include "glChartCanvas.h"
77 #include "linmath.h"
78 #endif
79 
80 #include <algorithm>          // for std::sort
81 #include <map>
82 
83 #include "ssl/sha1.h"
84 
85 #ifdef __MSVC__
86 #define strncasecmp(x,y,z) _strnicmp(x,y,z)
87 #endif
88 
89 
90 extern bool GetDoubleAttr(S57Obj *obj, const char *AttrName, double &val);      // found in s52cnsy
91 
92 void OpenCPN_OGRErrorHandler( CPLErr eErrClass, int nError,
93                               const char * pszErrorMsg );               // installed GDAL OGR library error handler
94 
95 
96 #ifdef ocpnUSE_GL
97 extern PFNGLGENBUFFERSPROC                 s_glGenBuffers;
98 extern PFNGLBINDBUFFERPROC                 s_glBindBuffer;
99 extern PFNGLBUFFERDATAPROC                 s_glBufferData;
100 extern PFNGLDELETEBUFFERSPROC              s_glDeleteBuffers;
101 
102 #ifndef USE_ANDROID_GLES2
103 #define glGenBuffers(a,b) (s_glGenBuffers)(a,b);
104 #define glBindBuffer(a,b) (s_glBindBuffer)(a,b);
105 #define glBufferData(a,b,c,d) (s_glBufferData)(a,b,c,d);
106 #define glDeleteBuffers(a,b) (s_glDeleteBuffers)(a,b);
107 #endif
108 
109 #endif
110 
111 
112 extern s52plib           *ps52plib;
113 extern S57ClassRegistrar *g_poRegistrar;
114 extern wxString          g_csv_locn;
115 extern wxString          g_SENCPrefix;
116 extern bool              g_bGDAL_Debug;
117 extern bool              g_bDebugS57;
118 extern MyFrame*          gFrame;
119 extern PlugInManager     *g_pi_manager;
120 extern bool              g_b_overzoom_x;
121 extern bool              g_b_EnableVBO;
122 extern OCPNPlatform     *g_Platform;
123 extern SENCThreadManager *g_SencThreadManager;
124 
125 int                      g_SENC_LOD_pixels;
126 
127 
128 static jmp_buf env_ogrf;                    // the context saved by setjmp();
129 
130 #include <wx/arrimpl.cpp>                   // Implement an array of S57 Objects
131 WX_DEFINE_OBJARRAY(ArrayOfS57Obj);
132 
133 #include <wx/listimpl.cpp>
134 WX_DEFINE_LIST(ListOfS57Obj);                // Implement a list of S57 Objects
135 
136 WX_DEFINE_LIST(ListOfObjRazRules);   // Implement a list ofObjRazRules
137 
138 #define S57_THUMB_SIZE  200
139 
140 static int              s_bInS57;         // Exclusion flag to prvent recursion in this class init call.
141                                           // Init() is not reentrant due to static wxProgressDialog callback....
142 int s_cnt;
143 
hash_fast64(const void * buf,size_t len,uint64_t seed)144 static uint64_t hash_fast64(const void *buf, size_t len, uint64_t seed)
145 {
146     const uint64_t    m = 0x880355f21e6d1965ULL;
147     const uint64_t *pos = (const uint64_t *)buf;
148     const uint64_t *end = pos + (len >> 3);
149     const unsigned char *pc;
150     uint64_t h = len * m ^ seed;
151     uint64_t v;
152     while (pos != end) {
153         v = *pos++;
154         v ^= v >> 23;
155         v *= 0x2127599bf4325c37ULL;
156         h ^= v ^ (v >> 47);
157         h *= m;
158     }
159     pc = (const unsigned char*)pos;
160     v = 0;
161     switch (len & 7) {
162         case 7: v ^= (uint64_t)pc[6] << 48;    // FALL THROUGH
163         case 6: v ^= (uint64_t)pc[5] << 40;    // FALL THROUGH
164         case 5: v ^= (uint64_t)pc[4] << 32;    // FALL THROUGH
165         case 4: v ^= (uint64_t)pc[3] << 24;    // FALL THROUGH
166         case 3: v ^= (uint64_t)pc[2] << 16;    // FALL THROUGH
167         case 2: v ^= (uint64_t)pc[1] << 8;     // FALL THROUGH
168         case 1: v ^= (uint64_t)pc[0];
169             v ^= v >> 23;
170             v *= 0x2127599bf4325c37ULL;
171             h ^= v ^ (v >> 47);
172             h *= m;
173     }
174 
175     h ^= h >> 23;
176     h *= 0x2127599bf4325c37ULL;
177     h ^= h >> 47;
178     return h;
179 }
180 
hash_fast32(const void * buf,size_t len,unsigned int seed)181 static unsigned int hash_fast32(const void *buf, size_t len, unsigned int seed)
182 {
183     uint64_t h = hash_fast64(buf, len, seed);
184     /* The following trick converts the 64-bit hashcode to a
185      * residue over a Fermat Number, in which information from
186      * both the higher and lower parts of hashcode shall be
187      * retained. */
188     return h - (h >> 32);
189 }
190 
hash() const191 unsigned long connector_key::hash() const
192 {
193     return hash_fast32(k, sizeof k, 0);
194 }
195 
196 
197 //----------------------------------------------------------------------------------
198 //      render_canvas_parms Implementation
199 //----------------------------------------------------------------------------------
200 
render_canvas_parms()201 render_canvas_parms::render_canvas_parms()
202 {
203     pix_buff = NULL;
204 }
205 
206 
~render_canvas_parms(void)207 render_canvas_parms::~render_canvas_parms( void )
208 {
209 }
210 
211 //----------------------------------------------------------------------------------
212 //      s57chart Implementation
213 //----------------------------------------------------------------------------------
214 
s57chart()215 s57chart::s57chart()
216 {
217 
218     m_ChartType = CHART_TYPE_S57;
219     m_ChartFamily = CHART_FAMILY_VECTOR;
220 
221     for( int i = 0; i < PRIO_NUM; i++ )
222         for( int j = 0; j < LUPNAME_NUM; j++ )
223             razRules[i][j] = NULL;
224 
225     m_Chart_Scale = 1;                              // Will be fetched during Init()
226     m_Chart_Skew = 0.0;
227 
228     pDIB = NULL;
229     m_pCloneBM = NULL;
230 
231 // Create ATON arrays, needed by S52PLIB
232     pFloatingATONArray = new wxArrayPtrVoid;
233     pRigidATONArray = new wxArrayPtrVoid;
234 
235     m_tmpup_array = NULL;
236 
237     m_DepthUnits = _T("METERS");
238     m_depth_unit_id = DEPTH_UNIT_METERS;
239 
240     bGLUWarningSent = false;
241 
242     m_pENCDS = NULL;
243 
244     m_nvaldco = 0;
245     m_nvaldco_alloc = 0;
246     m_pvaldco_array = NULL;
247 
248     m_bExtentSet = false;
249 
250     m_pDIBThumbDay = NULL;
251     m_pDIBThumbDim = NULL;
252     m_pDIBThumbOrphan = NULL;
253     m_bbase_file_attr_known = false;
254 
255     m_bLinePrioritySet = false;
256     m_plib_state_hash = 0;
257 
258     m_btex_mem = false;
259 
260     ref_lat = 0.0;
261     ref_lon = 0.0;
262 
263     m_b2pointLUPS = false;
264     m_b2lineLUPS = false;
265 
266     m_next_safe_cnt = 1e6;
267     m_LineVBO_name = -1;
268     m_line_vertex_buffer = 0;
269     m_this_chart_context =  0;
270     m_Chart_Skew = 0;
271     m_vbo_byte_length = 0;
272     m_SENCthreadStatus = THREAD_INACTIVE;
273     bReadyToRender = false;
274     m_RAZBuilt = false;
275     m_disableBackgroundSENC = false;
276 }
277 
~s57chart()278 s57chart::~s57chart()
279 {
280 
281     FreeObjectsAndRules();
282 
283     delete pDIB;
284 
285     delete m_pCloneBM;
286 //    delete pFullPath;
287 
288     delete pFloatingATONArray;
289     delete pRigidATONArray;
290 
291     delete m_pENCDS;
292 
293     free( m_pvaldco_array );
294 
295     free( m_line_vertex_buffer );
296 
297     delete m_pDIBThumbOrphan;
298 
299     for (unsigned i=0; i<m_pcs_vector.size(); i++)
300         delete m_pcs_vector.at(i);
301 
302     for (unsigned i=0; i<m_pve_vector.size(); i++)
303         delete m_pve_vector.at(i);
304 
305     m_pcs_vector.clear();
306     m_pve_vector.clear();
307 
308     for( VE_Hash::iterator it = m_ve_hash.begin(); it != m_ve_hash.end(); ++it ) {
309         VE_Element *pedge = it->second;
310         if(pedge){
311             free(pedge->pPoints);
312             delete pedge;
313         }
314     }
315     m_ve_hash.clear();
316 
317     for( VC_Hash::iterator itc = m_vc_hash.begin(); itc != m_vc_hash.end(); ++itc ) {
318         VC_Element *pcs = itc->second;
319         if(pcs) {
320             free(pcs->pPoint);
321             delete pcs;
322         }
323     }
324     m_vc_hash.clear();
325 
326 #ifdef ocpnUSE_GL
327     if(s_glDeleteBuffers && (m_LineVBO_name > 0))
328         glDeleteBuffers(1, (GLuint *)&m_LineVBO_name);
329 #endif
330     free (m_this_chart_context);
331 
332     if(m_TempFilePath.Length() && (m_FullPath != m_TempFilePath)){
333         if( ::wxFileExists(m_TempFilePath) )
334             wxRemoveFile(m_TempFilePath);
335     }
336 
337     //  Check the SENCThreadManager to see if this chart is queued or active
338     if(g_SencThreadManager){
339         if(g_SencThreadManager->IsChartInTicketlist(this)){
340             g_SencThreadManager->SetChartPointer(this, NULL);
341         }
342     }
343 
344 }
345 
GetValidCanvasRegion(const ViewPort & VPoint,OCPNRegion * pValidRegion)346 void s57chart::GetValidCanvasRegion( const ViewPort& VPoint, OCPNRegion *pValidRegion )
347 {
348     int rxl, rxr;
349     int ryb, ryt;
350     double easting, northing;
351     double epix, npix;
352 
353     toSM( m_FullExtent.SLAT, m_FullExtent.WLON, VPoint.clat, VPoint.clon, &easting, &northing );
354     epix = easting * VPoint.view_scale_ppm;
355     npix = northing * VPoint.view_scale_ppm;
356 
357     rxl = (int) round((VPoint.pix_width / 2) + epix);
358     ryb = (int) round((VPoint.pix_height / 2) - npix);
359 
360     toSM( m_FullExtent.NLAT, m_FullExtent.ELON, VPoint.clat, VPoint.clon, &easting, &northing );
361     epix = easting * VPoint.view_scale_ppm;
362     npix = northing * VPoint.view_scale_ppm;
363 
364     rxr = (int) round((VPoint.pix_width / 2) + epix);
365     ryt = (int) round((VPoint.pix_height / 2) - npix);
366 
367     pValidRegion->Clear();
368     pValidRegion->Union( rxl, ryt, rxr - rxl, ryb - ryt );
369 }
370 
GetValidRegion()371 LLRegion s57chart::GetValidRegion()
372 {
373     double ll[8] = {m_FullExtent.SLAT, m_FullExtent.WLON,
374                     m_FullExtent.SLAT, m_FullExtent.ELON,
375                     m_FullExtent.NLAT, m_FullExtent.ELON,
376                     m_FullExtent.NLAT, m_FullExtent.WLON};
377     return LLRegion(4, ll);
378 }
379 
SetColorScheme(ColorScheme cs,bool bApplyImmediate)380 void s57chart::SetColorScheme( ColorScheme cs, bool bApplyImmediate )
381 {
382     if( !ps52plib ) return;
383     //  Here we convert (subjectively) the Global ColorScheme
384     //  to an appropriate S52 Color scheme, by name.
385 
386     switch( cs ){
387         case GLOBAL_COLOR_SCHEME_DAY:
388             ps52plib->SetPLIBColorScheme( _T("DAY") );
389             break;
390         case GLOBAL_COLOR_SCHEME_DUSK:
391             ps52plib->SetPLIBColorScheme( _T("DUSK") );
392             break;
393         case GLOBAL_COLOR_SCHEME_NIGHT:
394             ps52plib->SetPLIBColorScheme( _T("NIGHT") );
395             break;
396         default:
397             ps52plib->SetPLIBColorScheme( _T("DAY") );
398             break;
399     }
400 
401     m_global_color_scheme = cs;
402 
403     if( bApplyImmediate ) {
404         delete pDIB;        // Toss any current cache
405         pDIB = NULL;
406     }
407 
408     //      Clear out any cached bitmaps in the text cache
409     ClearRenderedTextCache();
410 
411     //      Setup the proper thumbnail bitmap pointer
412     ChangeThumbColor(cs);
413 
414 }
415 
ChangeThumbColor(ColorScheme cs)416 void s57chart::ChangeThumbColor(ColorScheme cs)
417 {
418     if( 0 == m_pDIBThumbDay )
419         return;
420 
421     switch( cs ){
422         default:
423         case GLOBAL_COLOR_SCHEME_DAY:
424             pThumbData->pDIBThumb = m_pDIBThumbDay;
425             m_pDIBThumbOrphan = m_pDIBThumbDim;
426             break;
427         case GLOBAL_COLOR_SCHEME_DUSK:
428         case GLOBAL_COLOR_SCHEME_NIGHT: {
429             if( NULL == m_pDIBThumbDim ) {
430                 wxImage img = m_pDIBThumbDay->ConvertToImage();
431 
432 #if wxCHECK_VERSION(2, 8, 0)
433                 wxImage gimg = img.ConvertToGreyscale( 0.1, 0.1, 0.1 ); // factors are completely subjective
434 #else
435                 wxImage gimg = img;
436 #endif
437 
438 //#ifdef ocpnUSE_ocpnBitmap
439 //                      ocpnBitmap *pBMP =  new ocpnBitmap(gimg, m_pDIBThumbDay->GetDepth());
440 //#else
441                 wxBitmap *pBMP = new wxBitmap( gimg );
442 //#endif
443                 m_pDIBThumbDim = pBMP;
444                 m_pDIBThumbOrphan = m_pDIBThumbDay;
445             }
446 
447             pThumbData->pDIBThumb = m_pDIBThumbDim;
448             break;
449         }
450     }
451 }
452 
GetChartExtent(Extent * pext)453 bool s57chart::GetChartExtent( Extent *pext )
454 {
455     if( m_bExtentSet ) {
456         *pext = m_FullExtent;
457         return true;
458     } else
459         return false;
460 }
461 
free_mps(mps_container * mps)462 static void free_mps(mps_container *mps)
463 {
464 
465     if ( mps == 0)
466         return;
467     if( ps52plib && mps->cs_rules ){
468         for(unsigned int i=0 ; i < mps->cs_rules->GetCount() ; i++){
469             Rules *rule_chain_top = mps->cs_rules->Item(i);
470             ps52plib->DestroyRulesChain( rule_chain_top );
471         }
472         delete mps->cs_rules;
473     }
474     free( mps );
475 }
476 
FreeObjectsAndRules()477 void s57chart::FreeObjectsAndRules()
478 {
479 //      Delete the created ObjRazRules, including the S57Objs
480 //      and any child lists
481 //      The LUPs of base elements are deleted elsewhere ( void s52plib::DestroyLUPArray ( wxArrayOfLUPrec *pLUPArray ))
482 //      But we need to manually destroy any LUPS related to children
483 
484     ObjRazRules *top;
485     ObjRazRules *nxx;
486     for( int i = 0; i < PRIO_NUM; ++i ) {
487         for( int j = 0; j < LUPNAME_NUM; j++ ) {
488 
489             top = razRules[i][j];
490             while( top != NULL ) {
491                 top->obj->nRef--;
492                 if( 0 == top->obj->nRef )
493                     delete top->obj;
494 
495                 if( top->child ) {
496                     ObjRazRules *ctop = top->child;
497                     while( ctop ) {
498                         delete ctop->obj;
499 
500                         if( ps52plib ) ps52plib->DestroyLUP( ctop->LUP );
501                         delete ctop->LUP;
502 
503                         ObjRazRules *cnxx = ctop->next;
504                         delete ctop;
505                         ctop = cnxx;
506                     }
507                 }
508                 free_mps( top->mps );
509 
510                 nxx = top->next;
511                 free( top );
512                 top = nxx;
513             }
514         }
515     }
516 }
517 
ClearRenderedTextCache()518 void s57chart::ClearRenderedTextCache()
519 {
520     ObjRazRules *top;
521     for( int i = 0; i < PRIO_NUM; ++i ) {
522         for( int j = 0; j < LUPNAME_NUM; j++ ) {
523             top = razRules[i][j];
524             while( top != NULL ) {
525                 if( top->obj->bFText_Added ) {
526                     top->obj->bFText_Added = false;
527                     delete top->obj->FText;
528                     top->obj->FText = NULL;
529                 }
530 
531                 if( top->child ) {
532                     ObjRazRules *ctop = top->child;
533                     while( ctop ) {
534                         if( ctop->obj->bFText_Added ) {
535                             ctop->obj->bFText_Added = false;
536                             delete ctop->obj->FText;
537                             ctop->obj->FText = NULL;
538                         }
539                         ctop = ctop->next;
540                     }
541                 }
542 
543                 top = top->next;
544             }
545         }
546     }
547 }
548 
GetNormalScaleMin(double canvas_scale_factor,bool b_allow_overzoom)549 double s57chart::GetNormalScaleMin( double canvas_scale_factor, bool b_allow_overzoom )
550 {
551 //    if( b_allow_overzoom )
552         return m_Chart_Scale * 0.125;
553 //    else
554 //        return m_Chart_Scale * 0.25;
555 }
GetNormalScaleMax(double canvas_scale_factor,int canvas_width)556 double s57chart::GetNormalScaleMax( double canvas_scale_factor, int canvas_width )
557 {
558     return m_Chart_Scale * 4.0;
559 
560 }
561 
562 //-----------------------------------------------------------------------
563 //              Pixel to Lat/Long Conversion helpers
564 //-----------------------------------------------------------------------
565 
GetPointPix(ObjRazRules * rzRules,float north,float east,wxPoint * r)566 void s57chart::GetPointPix( ObjRazRules *rzRules, float north, float east, wxPoint *r )
567 {
568     r->x = roundint(((east - m_easting_vp_center) * m_view_scale_ppm) + m_pixx_vp_center);
569     r->y = roundint(m_pixy_vp_center - ((north - m_northing_vp_center) * m_view_scale_ppm));
570 }
571 
GetPointPix(ObjRazRules * rzRules,wxPoint2DDouble * en,wxPoint * r,int nPoints)572 void s57chart::GetPointPix( ObjRazRules *rzRules, wxPoint2DDouble *en, wxPoint *r, int nPoints )
573 {
574     for( int i = 0; i < nPoints; i++ ) {
575         r[i].x =  roundint(((en[i].m_x - m_easting_vp_center) * m_view_scale_ppm) + m_pixx_vp_center);
576         r[i].y =  roundint(m_pixy_vp_center - ((en[i].m_y - m_northing_vp_center) * m_view_scale_ppm));
577     }
578 }
579 
GetPixPoint(int pixx,int pixy,double * plat,double * plon,ViewPort * vpt)580 void s57chart::GetPixPoint( int pixx, int pixy, double *plat, double *plon, ViewPort *vpt )
581 {
582     if(vpt->m_projection_type != PROJECTION_MERCATOR)
583         printf("s57chart unhandled projection\n");
584 
585     //    Use Mercator estimator
586     int dx = pixx - ( vpt->pix_width / 2 );
587     int dy = ( vpt->pix_height / 2 ) - pixy;
588 
589     double xp = ( dx * cos( vpt->skew ) ) - ( dy * sin( vpt->skew ) );
590     double yp = ( dy * cos( vpt->skew ) ) + ( dx * sin( vpt->skew ) );
591 
592     double d_east = xp / vpt->view_scale_ppm;
593     double d_north = yp / vpt->view_scale_ppm;
594 
595     double slat, slon;
596     fromSM( d_east, d_north, vpt->clat, vpt->clon, &slat, &slon );
597 
598     *plat = slat;
599     *plon = slon;
600 
601 }
602 
603 //-----------------------------------------------------------------------
604 //              Calculate and Set ViewPoint Constants
605 //-----------------------------------------------------------------------
606 
SetVPParms(const ViewPort & vpt)607 void s57chart::SetVPParms( const ViewPort &vpt )
608 {
609     //  Set up local SM rendering constants
610     m_pixx_vp_center = vpt.pix_width / 2.0;
611     m_pixy_vp_center = vpt.pix_height / 2.0;
612     m_view_scale_ppm = vpt.view_scale_ppm;
613 
614     toSM( vpt.clat, vpt.clon, ref_lat, ref_lon, &m_easting_vp_center, &m_northing_vp_center );
615 
616     vp_transform.easting_vp_center = m_easting_vp_center;
617     vp_transform.northing_vp_center = m_northing_vp_center;
618 }
619 
AdjustVP(ViewPort & vp_last,ViewPort & vp_proposed)620 bool s57chart::AdjustVP( ViewPort &vp_last, ViewPort &vp_proposed )
621 {
622     if( IsCacheValid() ) {
623 
624         //      If this viewpoint is same scale as last...
625         if( vp_last.view_scale_ppm == vp_proposed.view_scale_ppm ) {
626 
627             double prev_easting_c, prev_northing_c;
628             toSM( vp_last.clat, vp_last.clon, ref_lat, ref_lon, &prev_easting_c, &prev_northing_c );
629 
630             double easting_c, northing_c;
631             toSM( vp_proposed.clat, vp_proposed.clon, ref_lat, ref_lon, &easting_c, &northing_c );
632 
633             //  then require this viewport to be exact integral pixel difference from last
634             //  adjusting clat/clat and SM accordingly
635 
636             double delta_pix_x = ( easting_c - prev_easting_c ) * vp_proposed.view_scale_ppm;
637             int dpix_x = (int) round ( delta_pix_x );
638             double dpx = dpix_x;
639 
640             double delta_pix_y = ( northing_c - prev_northing_c ) * vp_proposed.view_scale_ppm;
641             int dpix_y = (int) round ( delta_pix_y );
642             double dpy = dpix_y;
643 
644             double c_east_d = ( dpx / vp_proposed.view_scale_ppm ) + prev_easting_c;
645             double c_north_d = ( dpy / vp_proposed.view_scale_ppm ) + prev_northing_c;
646 
647             double xlat, xlon;
648             fromSM( c_east_d, c_north_d, ref_lat, ref_lon, &xlat, &xlon );
649 
650             vp_proposed.clon = xlon;
651             vp_proposed.clat = xlat;
652 
653             return true;
654         }
655     }
656 
657     return false;
658 }
659 
660 /*
661  bool s57chart::IsRenderDelta(ViewPort &vp_last, ViewPort &vp_proposed)
662  {
663  double last_center_easting, last_center_northing, this_center_easting, this_center_northing;
664  toSM ( vp_proposed.clat, vp_proposed.clon, ref_lat, ref_lon, &this_center_easting, &this_center_northing );
665  toSM ( vp_last.clat,     vp_last.clon,     ref_lat, ref_lon, &last_center_easting, &last_center_northing );
666 
667  int dx = (int)round((last_center_easting  - this_center_easting)  * vp_proposed.view_scale_ppm);
668  int dy = (int)round((last_center_northing - this_center_northing) * vp_proposed.view_scale_ppm);
669 
670  return((dx !=  0) || (dy != 0) || !(IsCacheValid()) || (vp_proposed.view_scale_ppm != vp_last.view_scale_ppm));
671  }
672  */
673 
LoadThumb()674 void  s57chart::LoadThumb()
675 {
676     wxFileName fn( m_FullPath );
677     wxString SENCdir = g_SENCPrefix;
678 
679     if( SENCdir.Last() != fn.GetPathSeparator() ) SENCdir.Append( fn.GetPathSeparator() );
680 
681     wxFileName tsfn( SENCdir );
682     tsfn.SetFullName( fn.GetFullName() );
683 
684     wxFileName ThumbFileNameLook( tsfn );
685     ThumbFileNameLook.SetExt( _T("BMP") );
686 
687     wxBitmap *pBMP;
688     if( ThumbFileNameLook.FileExists() ) {
689         pBMP = new wxBitmap;
690 
691         pBMP->LoadFile( ThumbFileNameLook.GetFullPath(), wxBITMAP_TYPE_BMP );
692         m_pDIBThumbDay = pBMP;
693         m_pDIBThumbOrphan = 0;
694         m_pDIBThumbDim = 0;
695 
696     }
697 }
698 
699 
GetThumbData(int tnx,int tny,float lat,float lon)700 ThumbData *s57chart::GetThumbData( int tnx, int tny, float lat, float lon )
701 {
702     //  Plot the passed lat/lon at the thumbnail bitmap scale
703     //  Using simple linear algorithm.
704     if( pThumbData->pDIBThumb == 0) {
705         LoadThumb();
706         ChangeThumbColor(m_global_color_scheme);
707     }
708 
709     UpdateThumbData( lat, lon );
710 
711     return pThumbData;
712 }
713 
UpdateThumbData(double lat,double lon)714 bool s57chart::UpdateThumbData( double lat, double lon )
715 {
716     //  Plot the passed lat/lon at the thumbnail bitmap scale
717     //  Using simple linear algorithm.
718     int test_x, test_y;
719     if( pThumbData->pDIBThumb ) {
720         double lat_top = m_FullExtent.NLAT;
721         double lat_bot = m_FullExtent.SLAT;
722         double lon_left = m_FullExtent.WLON;
723         double lon_right = m_FullExtent.ELON;
724 
725         // Build the scale factors just as the thumbnail was built
726         double ext_max = fmax((lat_top - lat_bot), (lon_right - lon_left));
727 
728         double thumb_view_scale_ppm = ( S57_THUMB_SIZE / ext_max ) / ( 1852 * 60 );
729         double east, north;
730         toSM( lat, lon, ( lat_top + lat_bot ) / 2., ( lon_left + lon_right ) / 2., &east, &north );
731 
732         test_x = pThumbData->pDIBThumb->GetWidth() / 2 + (int) ( east * thumb_view_scale_ppm );
733         test_y = pThumbData->pDIBThumb->GetHeight() / 2 - (int) ( north * thumb_view_scale_ppm );
734 
735     } else {
736         test_x = 0;
737         test_y = 0;
738     }
739 
740     if( ( test_x != pThumbData->ShipX ) || ( test_y != pThumbData->ShipY ) ) {
741         pThumbData->ShipX = test_x;
742         pThumbData->ShipY = test_y;
743         return TRUE;
744     } else
745         return FALSE;
746 }
747 
SetFullExtent(Extent & ext)748 void s57chart::SetFullExtent( Extent& ext )
749 {
750     m_FullExtent.NLAT = ext.NLAT;
751     m_FullExtent.SLAT = ext.SLAT;
752     m_FullExtent.WLON = ext.WLON;
753     m_FullExtent.ELON = ext.ELON;
754 
755     m_bExtentSet = true;
756 }
757 
ForceEdgePriorityEvaluate(void)758 void s57chart::ForceEdgePriorityEvaluate( void )
759 {
760     m_bLinePrioritySet = false;
761 }
762 
SetLinePriorities(void)763 void s57chart::SetLinePriorities( void )
764 {
765     if( !ps52plib ) return;
766 
767     //      If necessary.....
768     //      Establish line feature rendering priorities
769 
770     if( !m_bLinePrioritySet ) {
771         ObjRazRules *top;
772         ObjRazRules *crnt;
773 
774         for( int i = 0; i < PRIO_NUM; ++i ) {
775 
776             top = razRules[i][2];           //LINES
777             while( top != NULL ) {
778                 ObjRazRules *crnt = top;
779                 top = top->next;
780                 ps52plib->SetLineFeaturePriority( crnt, i );
781             }
782 
783             //    In the interest of speed, choose only the one necessary area boundary style index
784             int j;
785             if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
786                 j = 4;
787             else
788                 j = 3;
789 
790             top = razRules[i][j];
791             while( top != NULL ) {
792                 crnt = top;
793                 top = top->next;               // next object
794                 ps52plib->SetLineFeaturePriority( crnt, i );
795             }
796 
797         }
798 
799 
800         // Traverse the entire object list again, setting the priority of each line_segment_element
801         // to the maximum priority seen for that segment
802         for( int i = 0; i < PRIO_NUM; ++i ) {
803             for( int j = 0; j < LUPNAME_NUM; j++ ) {
804                 ObjRazRules *top = razRules[i][j];
805                 while( top != NULL ) {
806                     S57Obj *obj = top->obj;
807 
808                     VE_Element *pedge;
809                     connector_segment *pcs;
810                     line_segment_element *list = obj->m_ls_list;
811                     while( list ){
812                         switch (list->ls_type){
813                             case TYPE_EE:
814                             case TYPE_EE_REV:
815                                 pedge = list->pedge;// (VE_Element *)list->private0;
816                                 if(pedge)
817                                     list->priority = pedge->max_priority;
818                                 break;
819 
820                             default:
821                                 pcs = list->pcs; //(connector_segment *)list->private0;
822                                 if(pcs)
823                                     list->priority = pcs->max_priority_cs;
824                                 break;
825                         }
826 
827                         list = list->next;
828                     }
829 
830                     top = top->next;
831                 }
832             }
833         }
834     }
835 
836     //      Mark the priority as set.
837     //      Generally only reset by Options Dialog post processing
838     m_bLinePrioritySet = true;
839 }
840 
841 #if 0
842 void s57chart::SetLinePriorities( void )
843 {
844     if( !ps52plib ) return;
845 
846     //      If necessary.....
847     //      Establish line feature rendering priorities
848 
849     if( !m_bLinePrioritySet ) {
850         ObjRazRules *top;
851         ObjRazRules *crnt;
852 
853         for( int i = 0; i < PRIO_NUM; ++i ) {
854 
855             top = razRules[i][2];           //LINES
856             while( top != NULL ) {
857                 ObjRazRules *crnt = top;
858                 top = top->next;
859                 ps52plib->SetLineFeaturePriority( crnt, i );
860             }
861 
862             //    In the interest of speed, choose only the one necessary area boundary style index
863             int j;
864             if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
865                 j = 4;
866             else
867                 j = 3;
868 
869             top = razRules[i][j];
870             while( top != NULL ) {
871                 crnt = top;
872                 top = top->next;               // next object
873                 ps52plib->SetLineFeaturePriority( crnt, i );
874             }
875 
876         }
877 
878 
879         // Traverse the entire object list again, setting the priority of each line_segment_element
880         // to the maximum priority seen for that segment
881         for( int i = 0; i < PRIO_NUM; ++i ) {
882             for( int j = 0; j < LUPNAME_NUM; j++ ) {
883                 ObjRazRules *top = razRules[i][j];
884                 while( top != NULL ) {
885                     S57Obj *obj = top->obj;
886 
887                     VE_Element *pedge;
888                     connector_segment *pcs;
889                     line_segment_element *list = obj->m_ls_list;
890                     while( list ){
891                         switch (list->type){
892                             case TYPE_EE:
893 
894                                 pedge = (VE_Element *)list->private0;
895                                 if(pedge)
896                                     list->priority = pedge->max_priority;
897                                 break;
898 
899                             default:
900                                 pcs = (connector_segment *)list->private0;
901                                 if(pcs)
902                                     list->priority = pcs->max_priority;
903                                 break;
904                         }
905 
906                         list = list->next;
907                     }
908 
909                     top = top->next;
910                 }
911             }
912         }
913     }
914 
915     //      Mark the priority as set.
916     //      Generally only reset by Options Dialog post processing
917     m_bLinePrioritySet = true;
918 }
919 #endif
920 
GetLineFeaturePointArray(S57Obj * obj,void ** ret_array)921 int s57chart::GetLineFeaturePointArray(S57Obj *obj, void **ret_array)
922 {
923     //  Walk the line segment list once to get the required array size
924 
925     int nPoints = 0;
926     line_segment_element *ls_list = obj->m_ls_list;
927     while( ls_list){
928         if( (ls_list->ls_type == TYPE_EE) || (ls_list->ls_type == TYPE_EE_REV) )
929             nPoints += ls_list->pedge->nCount;
930         else
931             nPoints += 2;
932         ls_list = ls_list->next;
933     }
934 
935     if(!nPoints){
936         *ret_array = 0;
937         return 0;
938     }
939 
940     //  Allocate the buffer
941     float *br = (float *)malloc(nPoints * 2 * sizeof(float));
942     *ret_array = br;
943 
944     // populate the buffer
945     unsigned char *source_buffer = (unsigned char *)GetLineVertexBuffer();
946     ls_list = obj->m_ls_list;
947     while( ls_list){
948         size_t vbo_offset = 0;
949         size_t count = 0;
950         if( (ls_list->ls_type == TYPE_EE) || (ls_list->ls_type == TYPE_EE_REV) ){
951             vbo_offset = ls_list->pedge->vbo_offset;
952             count = ls_list->pedge->nCount;
953         }
954         else{
955             vbo_offset = ls_list->pcs->vbo_offset;
956             count = 2;
957         }
958 
959         memcpy(br, source_buffer + vbo_offset, count * 2 * sizeof(float));
960         br += count * 2;
961         ls_list = ls_list->next;
962     }
963 
964     return nPoints;
965 
966 }
967 
968 
969 #if 0
970 int s57chart::GetLineFeaturePointArray(S57Obj *obj, void **ret_array)
971 {
972     //  Walk the line segment list once to get the required array size
973 
974     int nPoints = 0;
975     line_segment_element *ls_list = obj->m_ls_list;
976     while( ls_list){
977         nPoints += ls_list->n_points;
978         ls_list = ls_list->next;
979     }
980 
981     if(!nPoints){
982         *ret_array = 0;
983         return 0;
984     }
985 
986     //  Allocate the buffer
987     float *br = (float *)malloc(nPoints * 2 * sizeof(float));
988     *ret_array = br;
989 
990     // populate the buffer
991     unsigned char *source_buffer = (unsigned char *)GetLineVertexBuffer();
992     ls_list = obj->m_ls_list;
993     while( ls_list){
994         memcpy(br, source_buffer + ls_list->vbo_offset, ls_list->n_points * 2 * sizeof(float));
995         br += ls_list->n_points * 2;
996         ls_list = ls_list->next;
997     }
998 
999     return nPoints;
1000 
1001 }
1002 #endif
1003 
1004 typedef struct segment_pair{
1005     float e0, n0, e1, n1;
1006 }_segment_pair;
1007 
1008 
AssembleLineGeometry(void)1009 void s57chart::AssembleLineGeometry( void )
1010 {
1011     // Walk the hash tables to get the required buffer size
1012 
1013     //  Start with the edge hash table
1014     size_t nPoints = 0;
1015     VE_Hash::iterator it;
1016     for( it = m_ve_hash.begin(); it != m_ve_hash.end(); ++it ) {
1017         VE_Element *pedge = it->second;
1018         if( pedge ) {
1019             nPoints += pedge->nCount;
1020         }
1021     }
1022 
1023     //    printf("time0 %f\n", sw.GetTime());
1024 
1025 
1026 
1027     std::map<long long, connector_segment *> ce_connector_hash;
1028     std::map<long long, connector_segment *> ec_connector_hash;
1029     std::map<long long, connector_segment *> cc_connector_hash;
1030 
1031     std::map<long long, connector_segment *>::iterator csit;
1032 
1033     int ndelta = 0;
1034 
1035     //  Define a vector to temporarily hold the geometry for the created pcs elements
1036 
1037     std::vector<segment_pair> connector_segment_vector;
1038     size_t seg_pair_index = 0;
1039 
1040     //  Get the end node connected segments.  To do this, we
1041     //  walk the Feature array and process each feature that potentially has a LINE type element
1042     for( int i = 0; i < PRIO_NUM; ++i ) {
1043         for( int j = 0; j < LUPNAME_NUM; j++ ) {
1044             ObjRazRules *top = razRules[i][j];
1045             while( top != NULL ) {
1046                 S57Obj *obj = top->obj;
1047 
1048                 if( (!obj->m_ls_list) && (obj->m_n_lsindex) )     // object has not been processed yet
1049                 {
1050                     line_segment_element list_top;
1051                     list_top.next = 0;
1052 
1053                     line_segment_element *le_current = &list_top;
1054 
1055                     for( int iseg = 0; iseg < obj->m_n_lsindex; iseg++ ) {
1056 
1057                         if(!obj->m_lsindex_array)
1058                             continue;
1059 
1060                         int seg_index = iseg * 3;
1061                         int *index_run = &obj->m_lsindex_array[seg_index];
1062 
1063                         //  Get first connected node
1064                         unsigned int inode = *index_run++;
1065 
1066                         //  Get the edge
1067                         bool edge_dir = true;
1068                         int venode = *index_run++;
1069                         if(venode < 0){
1070                             venode = -venode;
1071                             edge_dir = false;
1072                         }
1073 
1074                         VE_Element *pedge = 0;
1075                         if(venode){
1076                             if(m_ve_hash.find(venode) != m_ve_hash.end())
1077                                 pedge = m_ve_hash[venode];
1078                         }
1079 
1080                         //  Get end connected node
1081                         unsigned int enode = *index_run++;
1082 
1083                         //  Get first connected node
1084                         VC_Element *ipnode = 0;
1085                         ipnode = m_vc_hash[inode];
1086 
1087                         //  Get end connected node
1088                         VC_Element *epnode = 0;
1089                         epnode = m_vc_hash[enode];
1090 
1091 
1092                         if( ipnode ) {
1093                             if(pedge && pedge->nCount)
1094                             {
1095 
1096                                 //      The initial node exists and connects to the start of an edge
1097 
1098                                 long long key = ((unsigned long long)inode << 32) + venode;
1099 
1100                                 connector_segment *pcs = NULL;
1101                                 csit = ce_connector_hash.find( key );
1102                                 if( csit == ce_connector_hash.end() ){
1103                                     ndelta += 2;
1104                                     pcs = new connector_segment;
1105                                     ce_connector_hash[key] = pcs;
1106 
1107                                     // capture and store geometry
1108                                     segment_pair pair;
1109                                     float *ppt = ipnode->pPoint;
1110                                     pair.e0 = *ppt++;
1111                                     pair.n0 = *ppt;
1112 
1113                                     if(edge_dir){
1114                                         pair.e1 = pedge->pPoints[ 0 ];
1115                                         pair.n1 = pedge->pPoints[ 1 ];
1116                                     }
1117                                     else{
1118                                         int last_point_index = (pedge->nCount -1) * 2;
1119                                         pair.e1 = pedge->pPoints[ last_point_index ];
1120                                         pair.n1 = pedge->pPoints[ last_point_index + 1 ];
1121                                     }
1122 
1123                                     connector_segment_vector.push_back(pair);
1124                                     pcs->vbo_offset = seg_pair_index;               // use temporarily
1125                                     seg_pair_index ++;
1126 
1127                                     // calculate the centroid of this connector segment, used for viz testing
1128                                     double lat, lon;
1129                                     fromSM_Plugin( (pair.e0 + pair.e1)/2, (pair.n0 + pair.n1)/2, ref_lat, ref_lon, &lat, &lon );
1130                                     pcs->cs_lat_avg = lat;
1131                                     pcs->cs_lon_avg = lon;
1132 
1133                                 }
1134                                 else
1135                                     pcs = csit->second;
1136 
1137 
1138                                 line_segment_element *pls = new line_segment_element;
1139                                 pls->next = 0;
1140                                 //                            pls->n_points = 2;
1141                                 pls->priority = 0;
1142                                 pls->pcs = pcs;
1143                                 pls->ls_type = TYPE_CE;
1144 
1145                                 le_current->next = pls;             // hook it up
1146                                 le_current = pls;
1147 
1148                             }
1149                         }
1150 
1151                         if(pedge && pedge->nCount){
1152                             line_segment_element *pls = new line_segment_element;
1153                             pls->next = 0;
1154                             //                        pls->n_points = pedge->nCount;
1155                             pls->priority = 0;
1156                             pls->pedge = pedge;
1157                             pls->ls_type = TYPE_EE;
1158                             if( !edge_dir )
1159                                 pls->ls_type = TYPE_EE_REV;
1160 
1161 
1162                             le_current->next = pls;             // hook it up
1163                             le_current = pls;
1164 
1165                         }   //pedge
1166 
1167                         // end node
1168                         if( epnode ) {
1169 
1170                             if(ipnode){
1171                                 if(pedge && pedge->nCount){
1172 
1173                                     long long key = ((unsigned long long)venode << 32) + enode;
1174 
1175                                     connector_segment *pcs = NULL;
1176                                     csit = ec_connector_hash.find( key );
1177                                     if( csit == ec_connector_hash.end() ){
1178                                         ndelta += 2;
1179                                         pcs = new connector_segment;
1180                                         ec_connector_hash[key] = pcs;
1181 
1182                                         // capture and store geometry
1183                                         segment_pair pair;
1184 
1185                                         if(!edge_dir){
1186                                             pair.e0 = pedge->pPoints[ 0 ];
1187                                             pair.n0 = pedge->pPoints[ 1 ];
1188                                         }
1189                                         else{
1190                                             int last_point_index = (pedge->nCount -1) * 2;
1191                                             pair.e0 = pedge->pPoints[ last_point_index ];
1192                                             pair.n0 = pedge->pPoints[ last_point_index + 1 ];
1193                                         }
1194 
1195 
1196                                         float *ppt = epnode->pPoint;
1197                                         pair.e1 = *ppt++;
1198                                         pair.n1 = *ppt;
1199 
1200                                         connector_segment_vector.push_back(pair);
1201                                         pcs->vbo_offset = seg_pair_index;               // use temporarily
1202                                         seg_pair_index ++;
1203 
1204                                         // calculate the centroid of this connector segment, used for viz testing
1205                                         double lat, lon;
1206                                         fromSM_Plugin( (pair.e0 + pair.e1)/2, (pair.n0 + pair.n1)/2, ref_lat, ref_lon, &lat, &lon );
1207                                         pcs->cs_lat_avg = lat;
1208                                         pcs->cs_lon_avg = lon;
1209 
1210                                     }
1211                                     else
1212                                         pcs = csit->second;
1213 
1214                                     line_segment_element *pls = new line_segment_element;
1215                                     pls->next = 0;
1216                                     pls->priority = 0;
1217                                     pls->pcs = pcs;
1218                                     pls->ls_type = TYPE_EC;
1219 
1220                                     le_current->next = pls;             // hook it up
1221                                     le_current = pls;
1222 
1223 
1224                                 }
1225                                 else {
1226                                     long long key = ((unsigned long long)inode << 32) + enode;
1227 
1228                                     connector_segment *pcs = NULL;
1229                                     csit = cc_connector_hash.find( key );
1230                                     if( csit == cc_connector_hash.end() ){
1231                                         ndelta += 2;
1232                                         pcs = new connector_segment;
1233                                         cc_connector_hash[key] = pcs;
1234 
1235                                         // capture and store geometry
1236                                         segment_pair pair;
1237 
1238                                         float *ppt = ipnode->pPoint;
1239                                         pair.e0 = *ppt++;
1240                                         pair.n0 = *ppt;
1241 
1242                                         ppt = epnode->pPoint;
1243                                         pair.e1 = *ppt++;
1244                                         pair.n1 = *ppt;
1245 
1246                                         connector_segment_vector.push_back(pair);
1247                                         pcs->vbo_offset = seg_pair_index;               // use temporarily
1248                                         seg_pair_index ++;
1249 
1250                                         // calculate the centroid of this connector segment, used for viz testing
1251                                         double lat, lon;
1252                                         fromSM_Plugin( (pair.e0 + pair.e1)/2, (pair.n0 + pair.n1)/2, ref_lat, ref_lon, &lat, &lon );
1253                                         pcs->cs_lat_avg = lat;
1254                                         pcs->cs_lon_avg = lon;
1255 
1256                                     }
1257                                     else
1258                                         pcs = csit->second;
1259 
1260                                     line_segment_element *pls = new line_segment_element;
1261                                     pls->next = 0;
1262                                     pls->priority = 0;
1263                                     pls->pcs = pcs;
1264                                     pls->ls_type = TYPE_CC;
1265 
1266                                     le_current->next = pls;             // hook it up
1267                                     le_current = pls;
1268 
1269 
1270                                 }
1271                             }
1272                         }
1273 
1274 
1275                     }  // for
1276 
1277                     //  All done, so assign the list to the object
1278                     obj->m_ls_list = list_top.next;    // skipping the empty first placeholder element
1279 
1280                     //  Rarely, some objects are improperly coded, e.g. cm93
1281                     //  If found, signal this downstream for NIL processing
1282                     if(obj->m_ls_list == NULL){
1283                         obj->m_n_lsindex = 0;
1284                    }
1285 
1286                     // we are all finished with the line segment index array, per object
1287                     free(obj->m_lsindex_array);
1288                     obj->m_lsindex_array = NULL;
1289                 }
1290 
1291                 top = top->next;
1292             }
1293         }
1294     }
1295     //    printf("time1 %f\n", sw.GetTime());
1296 
1297     //  We have the total VBO point count, and a nice hashmap of the connector segments
1298     nPoints += ndelta;          // allow for the connector segments
1299 
1300     size_t vbo_byte_length = 2 * nPoints * sizeof(float);
1301 
1302     unsigned char *buffer_offset;
1303     size_t offset;
1304 
1305     if(0 == m_vbo_byte_length){
1306         m_line_vertex_buffer = (float *)malloc( vbo_byte_length);
1307         m_vbo_byte_length = vbo_byte_length;
1308         buffer_offset = (unsigned char *)m_line_vertex_buffer;
1309         offset = 0;
1310     }
1311     else{
1312         m_line_vertex_buffer = (float *)realloc( m_line_vertex_buffer, m_vbo_byte_length + vbo_byte_length );
1313         buffer_offset = (unsigned char *)m_line_vertex_buffer + m_vbo_byte_length;
1314         offset = m_vbo_byte_length;
1315         m_vbo_byte_length = m_vbo_byte_length + vbo_byte_length;
1316     }
1317 
1318     float *lvr = (float *)buffer_offset;
1319 
1320     //      Copy and edge points as floats,
1321     //      and recording each segment's offset in the array
1322     for( it = m_ve_hash.begin(); it != m_ve_hash.end(); ++it ) {
1323         VE_Element *pedge = it->second;
1324         if( pedge ) {
1325             memcpy(lvr, pedge->pPoints, pedge->nCount * 2 * sizeof(float));
1326             lvr += pedge->nCount * 2;
1327 
1328             pedge->vbo_offset = offset;
1329             offset += pedge->nCount * 2 * sizeof(float);
1330         }
1331 //         else
1332 //             int yyp = 4;        //TODO Why are zero elements being inserted into m_ve_hash?
1333     }
1334 
1335     //      Now iterate on the hashmaps, adding the connector segments in the temporary vector to the VBO buffer
1336     //      At the  same time, populate a vector, storing the pcs pointers to allow destruction at this class dtor.
1337     //      This will allow us to destroy (automatically) the pcs hashmaps, and save some storage
1338 
1339 
1340     for( csit = ce_connector_hash.begin(); csit != ce_connector_hash.end(); ++csit )
1341     {
1342         connector_segment *pcs = csit->second;
1343         m_pcs_vector.push_back(pcs);
1344 
1345         segment_pair pair = connector_segment_vector.at(pcs->vbo_offset);
1346         *lvr++ = pair.e0;
1347         *lvr++ = pair.n0;
1348         *lvr++ = pair.e1;
1349         *lvr++ = pair.n1;
1350 
1351         pcs->vbo_offset = offset;
1352         offset += 4 * sizeof(float);
1353     }
1354 
1355     for( csit = ec_connector_hash.begin(); csit != ec_connector_hash.end(); ++csit )
1356     {
1357         connector_segment *pcs = csit->second;
1358         m_pcs_vector.push_back(pcs);
1359 
1360         segment_pair pair = connector_segment_vector.at(pcs->vbo_offset);
1361         *lvr++ = pair.e0;
1362         *lvr++ = pair.n0;
1363         *lvr++ = pair.e1;
1364         *lvr++ = pair.n1;
1365 
1366         pcs->vbo_offset = offset;
1367         offset += 4 * sizeof(float);
1368     }
1369 
1370     for( csit = cc_connector_hash.begin(); csit != cc_connector_hash.end(); ++csit )
1371     {
1372         connector_segment *pcs = csit->second;
1373         m_pcs_vector.push_back(pcs);
1374 
1375         segment_pair pair = connector_segment_vector.at(pcs->vbo_offset);
1376         *lvr++ = pair.e0;
1377         *lvr++ = pair.n0;
1378         *lvr++ = pair.e1;
1379         *lvr++ = pair.n1;
1380 
1381         pcs->vbo_offset = offset;
1382         offset += 4 * sizeof(float);
1383     }
1384 
1385     // And so we can empty the temp buffer
1386     connector_segment_vector.clear();
1387 
1388     // We can convert the edge hashmap to a vector, to allow  us to destroy the hashmap
1389     // and at the same time free up the point storage in the VE_Elements, since all the points
1390     // are now in the VBO buffer
1391     for( it = m_ve_hash.begin(); it != m_ve_hash.end(); ++it ) {
1392         VE_Element *pedge = it->second;
1393         if(pedge){
1394             m_pve_vector.push_back(pedge);
1395             free(pedge->pPoints);
1396         }
1397     }
1398     m_ve_hash.clear();
1399 
1400 
1401     // and we can empty the connector hashmap,
1402     // and at the same time free up the point storage in the VC_Elements, since all the points
1403     // are now in the VBO buffer
1404     for( VC_Hash::iterator itc = m_vc_hash.begin(); itc != m_vc_hash.end(); ++itc ) {
1405         VC_Element *pcs = itc->second;
1406         if(pcs)
1407             free(pcs->pPoint);
1408         delete pcs;
1409     }
1410     m_vc_hash.clear();
1411 
1412 
1413 
1414 
1415 }
1416 
1417 
1418 
1419 
BuildLineVBO(void)1420 void s57chart::BuildLineVBO( void )
1421 {
1422 #ifdef ocpnUSE_GL
1423     // cm93 cannot efficiently use VBO, since the edge list is discovered incrementally,
1424     // and this would require rebuilding the VBO for each new cell that is loaded.
1425 
1426     if(CHART_TYPE_CM93 == GetChartType())
1427         return;
1428 
1429     if(!g_b_EnableVBO)
1430         return;
1431 
1432     if(m_LineVBO_name == -1){
1433 
1434         //      Create the VBO
1435         GLuint vboId;
1436         glGenBuffers(1, &vboId);
1437 
1438          // bind VBO in order to use
1439         glBindBuffer(GL_ARRAY_BUFFER, vboId);
1440 
1441         // upload data to VBO
1442 #ifndef USE_ANDROID_GLES2
1443         glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
1444 #endif
1445         glBufferData(GL_ARRAY_BUFFER, m_vbo_byte_length, m_line_vertex_buffer, GL_STATIC_DRAW);
1446 
1447 #ifndef USE_ANDROID_GLES2
1448         glDisableClientState(GL_VERTEX_ARRAY);            // deactivate vertex array
1449 #endif
1450         glBindBuffer(GL_ARRAY_BUFFER, 0);
1451 
1452         //  Loop and populate all the objects
1453         for( int i = 0; i < PRIO_NUM; ++i ) {
1454             for( int j = 0; j < LUPNAME_NUM; j++ ) {
1455                 ObjRazRules *top = razRules[i][j];
1456                 while( top != NULL ) {
1457                     S57Obj *obj = top->obj;
1458                     obj->auxParm2 = vboId;
1459                     top = top->next;
1460                 }
1461             }
1462         }
1463 
1464         m_LineVBO_name = vboId;
1465 
1466     }
1467 #endif
1468 }
1469 
1470 
1471 /*              RectRegion:
1472  *                      This is the Screen region desired to be updated.  Will be either 1 rectangle(full screen)
1473  *                      or two rectangles (panning with FBO accelerated pan logic)
1474  *
1475  *              Region:
1476  *                      This is the LLRegion describing the quilt active region for this chart.
1477  *
1478  *              So, Actual rendering area onscreen should be clipped to the intersection of the two regions.
1479  */
1480 
RenderRegionViewOnGL(const wxGLContext & glc,const ViewPort & VPoint,const OCPNRegion & RectRegion,const LLRegion & Region)1481 bool s57chart::RenderRegionViewOnGL( const wxGLContext &glc, const ViewPort& VPoint,
1482                                      const OCPNRegion &RectRegion, const LLRegion &Region )
1483 {
1484     if(!m_RAZBuilt) return false;
1485 
1486     return DoRenderRegionViewOnGL( glc, VPoint, RectRegion, Region, false );
1487 }
1488 
RenderOverlayRegionViewOnGL(const wxGLContext & glc,const ViewPort & VPoint,const OCPNRegion & RectRegion,const LLRegion & Region)1489 bool s57chart::RenderOverlayRegionViewOnGL( const wxGLContext &glc, const ViewPort& VPoint,
1490                                             const OCPNRegion &RectRegion, const LLRegion &Region )
1491 {
1492     if(!m_RAZBuilt) return false;
1493 
1494     return DoRenderRegionViewOnGL( glc, VPoint, RectRegion, Region, true );
1495 }
1496 
RenderRegionViewOnGLNoText(const wxGLContext & glc,const ViewPort & VPoint,const OCPNRegion & RectRegion,const LLRegion & Region)1497 bool s57chart::RenderRegionViewOnGLNoText( const wxGLContext &glc, const ViewPort& VPoint,
1498                                      const OCPNRegion &RectRegion, const LLRegion &Region )
1499 {
1500     if(!m_RAZBuilt) return false;
1501 
1502     bool b_text = ps52plib->GetShowS57Text();
1503     ps52plib->m_bShowS57Text = false;
1504     bool b_ret =  DoRenderRegionViewOnGL( glc, VPoint, RectRegion, Region, false );
1505     ps52plib->m_bShowS57Text = b_text;
1506 
1507     return b_ret;
1508 }
1509 
RenderViewOnGLTextOnly(const wxGLContext & glc,const ViewPort & VPoint)1510 bool s57chart::RenderViewOnGLTextOnly( const wxGLContext &glc, const ViewPort& VPoint)
1511 {
1512     if(!m_RAZBuilt) return false;
1513 
1514 #ifdef ocpnUSE_GL
1515 
1516     if( !ps52plib ) return false;
1517 
1518     SetVPParms( VPoint );
1519 
1520 #ifndef USE_ANDROID_GLES2
1521     glPushMatrix(); //    Adjust for rotation
1522 #endif
1523     glChartCanvas::RotateToViewPort(VPoint);
1524 
1525     glChartCanvas::DisableClipRegion();
1526     DoRenderOnGLText( glc, VPoint );
1527 
1528 #ifndef USE_ANDROID_GLES2
1529     glPopMatrix();
1530 #endif
1531 
1532 #endif
1533     return true;
1534 }
1535 
DoRenderRegionViewOnGL(const wxGLContext & glc,const ViewPort & VPoint,const OCPNRegion & RectRegion,const LLRegion & Region,bool b_overlay)1536 bool s57chart::DoRenderRegionViewOnGL( const wxGLContext &glc, const ViewPort& VPoint,
1537                                        const OCPNRegion &RectRegion, const LLRegion &Region, bool b_overlay )
1538 {
1539     if(!m_RAZBuilt) return false;
1540 
1541 #ifdef ocpnUSE_GL
1542 
1543     if( !ps52plib ) return false;
1544 
1545     if( g_bDebugS57 ) printf( "\n" );
1546 
1547     SetVPParms( VPoint );
1548 
1549     ps52plib->PrepareForRender((ViewPort *)&VPoint);
1550 
1551     if( m_plib_state_hash != ps52plib->GetStateHash() ) {
1552         m_bLinePrioritySet = false;                     // need to reset line priorities
1553         UpdateLUPs( this );                               // and update the LUPs
1554         ClearRenderedTextCache();                       // and reset the text renderer,
1555                                                         //for the case where depth(height) units change
1556         ResetPointBBoxes( m_last_vp, VPoint );
1557         SetSafetyContour();
1558 
1559         m_plib_state_hash = ps52plib->GetStateHash();
1560 
1561     }
1562 
1563     if( VPoint.view_scale_ppm != m_last_vp.view_scale_ppm ) {
1564         ResetPointBBoxes( m_last_vp, VPoint );
1565     }
1566 
1567     BuildLineVBO();
1568     SetLinePriorities();
1569 
1570     //        Clear the text declutter list
1571     ps52plib->ClearTextList();
1572 
1573     ViewPort vp = VPoint;
1574 
1575     // region always has either 1 or 2 rectangles (full screen or panning rectangles)
1576     for(OCPNRegionIterator upd ( RectRegion ); upd.HaveRects(); upd.NextRect()) {
1577         LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
1578         chart_region.Intersect(Region);
1579 
1580         if(!chart_region.Empty()) {
1581 
1582             //TODO  I think this needs nore work for alternate Projections...
1583             //  cm93 vpoint crossing Greenwich, panning east, was rendering areas incorrectly.
1584             ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
1585 
1586             if(CHART_TYPE_CM93 == GetChartType()){
1587                 // for now I will revert to the faster rectangle clipping now that rendering order is resolved
1588 //                glChartCanvas::SetClipRegion(cvp, chart_region);
1589                 glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
1590                 ps52plib->m_last_clip_rect = upd.GetRect();
1591             }
1592             else
1593             {
1594 #ifdef OPT_USE_ANDROID_GLES2
1595 
1596                 // GLES2 will be faster if we setup and use a smaller viewport for each rectangle render.
1597                 // This is because when using shaders, clip operations (e.g. scissor, stencil) happen after the fragment shader executes.
1598                 // However, with a smaller viewport, the fragment shader will not be invoked if the vertices are all outside the vieport.
1599 
1600                 wxRect r = upd.GetRect();
1601                 ViewPort *vp = &cvp;
1602                 glViewport( r.x, vp->pix_height - (r.y + r.height), r.width, r.height );
1603 
1604                 //mat4x4 m;
1605                  //mat4x4_identity(m);
1606 
1607                 mat4x4 I, Q;
1608                 mat4x4_identity(I);
1609 
1610                 float yp = vp->pix_height - (r.y + r.height);
1611                 // Translate
1612                 I[3][0]  = (-r.x - (float)r.width/2) *  (  2.0 / (float)r.width);
1613                 I[3][1]  = (r.y + (float)r.height/2) * (  2.0 / (float)r.height);
1614 
1615                 // Scale
1616                 I[0][0] *= 2.0 / (float)r.width;
1617                 I[1][1] *= -2.0 / (float)r.height;
1618 
1619                 //Rotate
1620                 float angle = 0;
1621                 mat4x4_rotate_Z(Q, I, angle);
1622 
1623                 mat4x4_dup((float (*)[4])vp->vp_transform, Q);
1624 
1625 #else
1626                 //glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
1627                 glChartCanvas::SetClipRegion(cvp, chart_region);
1628 
1629             ps52plib->m_last_clip_rect = upd.GetRect();
1630 
1631 
1632 #endif
1633 
1634             }
1635 
1636 //            ps52plib->m_last_clip_rect = upd.GetRect();
1637 #ifndef USE_ANDROID_GLES2
1638             glPushMatrix(); //    Adjust for rotation
1639 #endif
1640             glChartCanvas::RotateToViewPort(VPoint);
1641 
1642             wxRect r = upd.GetRect();
1643             //qDebug() << "Rect" << r.x << r.y << r.width << r.height;
1644 
1645             //qDebug() << "Start DoRender" << sw.GetTime();
1646             DoRenderOnGL( glc, cvp );
1647             //qDebug() << "End DoRender" << sw.GetTime();
1648 
1649 #ifndef USE_ANDROID_GLES2
1650             glPopMatrix();
1651 #endif
1652             glChartCanvas::DisableClipRegion();
1653         }
1654     }
1655 
1656 //      Update last_vp to reflect current state
1657     m_last_vp = VPoint;
1658 
1659 
1660 //      CALLGRIND_STOP_INSTRUMENTATION
1661 
1662 #endif
1663     return true;
1664 }
1665 
DoRenderOnGL(const wxGLContext & glc,const ViewPort & VPoint)1666 bool s57chart::DoRenderOnGL( const wxGLContext &glc, const ViewPort& VPoint )
1667 {
1668 #ifdef ocpnUSE_GL
1669 
1670     int i;
1671     ObjRazRules *top;
1672     ObjRazRules *crnt;
1673     ViewPort tvp = VPoint;                    // undo const  TODO fix this in PLIB
1674 
1675 #if 1
1676     //      Render the areas quickly
1677     for( i = 0; i < PRIO_NUM; ++i ) {
1678         if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
1679             top = razRules[i][4]; // Area Symbolized Boundaries
1680         else
1681             top = razRules[i][3]; // Area Plain Boundaries
1682 
1683         while( top != NULL ) {
1684             crnt = top;
1685             top = top->next;               // next object
1686             crnt->sm_transform_parms = &vp_transform;
1687             ps52plib->RenderAreaToGL( glc, crnt, &tvp );
1688         }
1689     }
1690 
1691 #else
1692     //      Render the areas quickly
1693     for( i = 0; i < PRIO_NUM; ++i ) {
1694         if( PI_GetPLIBBoundaryStyle() == SYMBOLIZED_BOUNDARIES )
1695             top = razRules[i][4]; // Area Symbolized Boundaries
1696             else
1697                 top = razRules[i][3];           // Area Plain Boundaries
1698 
1699                 while( top != NULL ) {
1700                     crnt = top;
1701                     top = top->next;               // next object
1702                     crnt->sm_transform_parms = &vp_transform;
1703 
1704                     // This may be a deferred tesselation
1705                     // Don't pre-process the geometry unless the object is to be actually rendered
1706                     if(!crnt->obj->pPolyTessGeo->IsOk() ){
1707                         if(ps52plib->ObjectRenderCheckRules( crnt, &tvp, true )){
1708                             if(!crnt->obj->pPolyTessGeo->m_pxgeom)
1709                                 crnt->obj->pPolyTessGeo->m_pxgeom = buildExtendedGeom( crnt->obj );
1710                         }
1711                     }
1712                     ps52plib->RenderAreaToGL( glc, crnt, &tvp );
1713                 }
1714     }
1715 #endif
1716 //qDebug() << "Done areas" << sw.GetTime();
1717 
1718     //    Render the lines and points
1719     for( i = 0; i < PRIO_NUM; ++i ) {
1720         if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
1721             top = razRules[i][4]; // Area Symbolized Boundaries
1722         else
1723             top = razRules[i][3]; // Area Plain Boundaries
1724         while( top != NULL ) {
1725             crnt = top;
1726             top = top->next;               // next object
1727             crnt->sm_transform_parms = &vp_transform;
1728             ps52plib->RenderObjectToGL( glc, crnt, &tvp );
1729         }
1730     }
1731     //qDebug() << "Done Boundaries" << sw.GetTime();
1732 
1733     for( i = 0; i < PRIO_NUM; ++i ) {
1734         top = razRules[i][2];           //LINES
1735         while( top != NULL ) {
1736             crnt = top;
1737             top = top->next;
1738             crnt->sm_transform_parms = &vp_transform;
1739             ps52plib->RenderObjectToGL( glc, crnt, &tvp );
1740         }
1741     }
1742 
1743  //qDebug() << "Done Lines" << sw.GetTime();
1744 
1745     for( i = 0; i < PRIO_NUM; ++i ) {
1746         if( ps52plib->m_nSymbolStyle == SIMPLIFIED )
1747             top = razRules[i][0];       //SIMPLIFIED Points
1748         else
1749             top = razRules[i][1];           //Paper Chart Points Points
1750 
1751         while( top != NULL ) {
1752             crnt = top;
1753             top = top->next;
1754             crnt->sm_transform_parms = &vp_transform;
1755             ps52plib->RenderObjectToGL( glc, crnt, &tvp );
1756         }
1757     }
1758     //qDebug() << "Done Points" << sw.GetTime();
1759 
1760 
1761 #endif          //#ifdef ocpnUSE_GL
1762 
1763     return true;
1764 }
1765 
DoRenderOnGLText(const wxGLContext & glc,const ViewPort & VPoint)1766 bool s57chart::DoRenderOnGLText( const wxGLContext &glc, const ViewPort& VPoint )
1767 {
1768 #ifdef ocpnUSE_GL
1769 
1770     int i;
1771     ObjRazRules *top;
1772     ObjRazRules *crnt;
1773     ViewPort tvp = VPoint;                    // undo const  TODO fix this in PLIB
1774 
1775 #if 0
1776     //      Render the areas quickly
1777     for( i = 0; i < PRIO_NUM; ++i ) {
1778         if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
1779             top = razRules[i][4]; // Area Symbolized Boundaries
1780         else
1781             top = razRules[i][3];           // Area Plain Boundaries
1782 
1783             while( top != NULL ) {
1784                 crnt = top;
1785                 top = top->next;               // next object
1786                 crnt->sm_transform_parms = &vp_transform;
1787 ///                ps52plib->RenderAreaToGL( glc, crnt, &tvp );
1788             }
1789     }
1790 #endif
1791 
1792     //    Render the lines and points
1793     for( i = 0; i < PRIO_NUM; ++i ) {
1794         if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
1795             top = razRules[i][4]; // Area Symbolized Boundaries
1796         else
1797             top = razRules[i][3]; // Area Plain Boundaries
1798 
1799         while( top != NULL ) {
1800                 crnt = top;
1801                 top = top->next;               // next object
1802                 crnt->sm_transform_parms = &vp_transform;
1803                 ps52plib->RenderObjectToGLText( glc, crnt, &tvp );
1804         }
1805 
1806         top = razRules[i][2];           //LINES
1807         while( top != NULL ) {
1808                 crnt = top;
1809                 top = top->next;
1810                 crnt->sm_transform_parms = &vp_transform;
1811                 ps52plib->RenderObjectToGLText( glc, crnt, &tvp );
1812         }
1813 
1814         if( ps52plib->m_nSymbolStyle == SIMPLIFIED )
1815             top = razRules[i][0];       //SIMPLIFIED Points
1816         else
1817             top = razRules[i][1];           //Paper Chart Points Points
1818 
1819         while( top != NULL ) {
1820                 crnt = top;
1821                 top = top->next;
1822                 crnt->sm_transform_parms = &vp_transform;
1823                 ps52plib->RenderObjectToGLText( glc, crnt, &tvp );
1824         }
1825 
1826     }
1827 
1828 #endif          //#ifdef ocpnUSE_GL
1829 
1830     return true;
1831 }
1832 
1833 
RenderRegionViewOnDCNoText(wxMemoryDC & dc,const ViewPort & VPoint,const OCPNRegion & Region)1834 bool s57chart::RenderRegionViewOnDCNoText( wxMemoryDC& dc, const ViewPort& VPoint,
1835                                      const OCPNRegion &Region )
1836 {
1837     if(!m_RAZBuilt)
1838         return false;
1839 
1840     bool b_text = ps52plib->GetShowS57Text();
1841     ps52plib->m_bShowS57Text = false;
1842     bool b_ret = DoRenderRegionViewOnDC( dc, VPoint, Region, false );
1843     ps52plib->m_bShowS57Text = b_text;
1844 
1845     return true;
1846 }
1847 
RenderRegionViewOnDCTextOnly(wxMemoryDC & dc,const ViewPort & VPoint,const OCPNRegion & Region)1848 bool s57chart::RenderRegionViewOnDCTextOnly( wxMemoryDC& dc, const ViewPort& VPoint,
1849                                            const OCPNRegion &Region )
1850 {
1851     if(!dc.IsOk())
1852         return false;
1853 
1854     SetVPParms( VPoint );
1855 
1856     //  If the viewport is rotated, there will only be one rectangle in the region
1857     //  so we can take a shortcut...
1858     if(fabs(VPoint.rotation) > .01){
1859         DCRenderText( dc, VPoint );
1860     }
1861     else{
1862         ViewPort temp_vp = VPoint;
1863         double temp_lon_left, temp_lat_bot, temp_lon_right, temp_lat_top;
1864 
1865         //    Decompose the region into rectangles,
1866         OCPNRegionIterator upd( Region ); // get the requested rect list
1867         while( upd.HaveRects() ) {
1868             wxRect rect = upd.GetRect();
1869 
1870             wxPoint p;
1871             p.x = rect.x;
1872             p.y = rect.y;
1873 
1874             temp_vp.GetLLFromPix( p, &temp_lat_top, &temp_lon_left);
1875 
1876             p.x += rect.width;
1877             p.y += rect.height;
1878             temp_vp.GetLLFromPix( p, &temp_lat_bot, &temp_lon_right);
1879 
1880             if( temp_lon_right < temp_lon_left )        // presumably crossing Greenwich
1881                     temp_lon_right += 360.;
1882 
1883 
1884             temp_vp.GetBBox().Set(temp_lat_bot, temp_lon_left, temp_lat_top, temp_lon_right);
1885 
1886             wxDCClipper clip(dc, rect);
1887             DCRenderText( dc, temp_vp );
1888 
1889             upd.NextRect();
1890         }
1891     }
1892 
1893     return true;
1894 }
1895 
RenderRegionViewOnDC(wxMemoryDC & dc,const ViewPort & VPoint,const OCPNRegion & Region)1896 bool s57chart::RenderRegionViewOnDC( wxMemoryDC& dc, const ViewPort& VPoint,
1897         const OCPNRegion &Region )
1898 {
1899     if(!m_RAZBuilt)
1900         return false;
1901 
1902     return DoRenderRegionViewOnDC( dc, VPoint, Region, false );
1903 }
1904 
RenderOverlayRegionViewOnDC(wxMemoryDC & dc,const ViewPort & VPoint,const OCPNRegion & Region)1905 bool s57chart::RenderOverlayRegionViewOnDC( wxMemoryDC& dc, const ViewPort& VPoint,
1906         const OCPNRegion &Region )
1907 {
1908     if(!m_RAZBuilt)
1909         return false;
1910     return DoRenderRegionViewOnDC( dc, VPoint, Region, true );
1911 }
1912 
DoRenderRegionViewOnDC(wxMemoryDC & dc,const ViewPort & VPoint,const OCPNRegion & Region,bool b_overlay)1913 bool s57chart::DoRenderRegionViewOnDC( wxMemoryDC& dc, const ViewPort& VPoint,
1914         const OCPNRegion &Region, bool b_overlay )
1915 {
1916     SetVPParms( VPoint );
1917 
1918     bool force_new_view = false;
1919 
1920     if( Region != m_last_Region ) force_new_view = true;
1921 
1922     ps52plib->PrepareForRender((ViewPort *)&VPoint);
1923 
1924     if( m_plib_state_hash != ps52plib->GetStateHash() ) {
1925         m_bLinePrioritySet = false;                     // need to reset line priorities
1926         UpdateLUPs( this );                               // and update the LUPs
1927         ClearRenderedTextCache();                       // and reset the text renderer,
1928                                                         //for the case where depth(height) units change
1929         ResetPointBBoxes( m_last_vp, VPoint );
1930         SetSafetyContour();
1931     }
1932 
1933     if( VPoint.view_scale_ppm != m_last_vp.view_scale_ppm ) {
1934         ResetPointBBoxes( m_last_vp, VPoint );
1935     }
1936 
1937     SetLinePriorities();
1938 
1939     bool bnew_view = DoRenderViewOnDC( dc, VPoint, DC_RENDER_ONLY, force_new_view );
1940 
1941     //    If quilting, we need to return a cloned bitmap instead of the original golden item
1942     if( VPoint.b_quilt ) {
1943         if( m_pCloneBM ) {
1944             if( ( m_pCloneBM->GetWidth() != VPoint.pix_width )
1945                     || ( m_pCloneBM->GetHeight() != VPoint.pix_height ) ) {
1946                 delete m_pCloneBM;
1947                 m_pCloneBM = NULL;
1948             }
1949         }
1950         if( NULL == m_pCloneBM ) m_pCloneBM = new wxBitmap( VPoint.pix_width, VPoint.pix_height,
1951                 -1 );
1952 
1953         wxMemoryDC dc_clone;
1954         dc_clone.SelectObject( *m_pCloneBM );
1955 
1956 #ifdef ocpnUSE_DIBSECTION
1957         ocpnMemDC memdc, dc_org;
1958 #else
1959         wxMemoryDC memdc, dc_org;
1960 #endif
1961 
1962         pDIB->SelectIntoDC( dc_org );
1963 
1964         //    Decompose the region into rectangles, and fetch them into the target dc
1965         OCPNRegionIterator upd( Region ); // get the requested rect list
1966         while( upd.HaveRects() ) {
1967             wxRect rect = upd.GetRect();
1968             dc_clone.Blit( rect.x, rect.y, rect.width, rect.height, &dc_org, rect.x, rect.y );
1969             upd.NextRect();
1970         }
1971 
1972         dc_clone.SelectObject( wxNullBitmap );
1973         dc_org.SelectObject( wxNullBitmap );
1974 
1975         //    Create a mask
1976         if( b_overlay ) {
1977             wxColour nodat = GetGlobalColor( _T ( "NODTA" ) );
1978             wxColour nodat_sub = nodat;
1979 
1980 #ifdef ocpnUSE_ocpnBitmap
1981             nodat_sub = wxColour( nodat.Blue(), nodat.Green(), nodat.Red() );
1982 #endif
1983             m_pMask = new wxMask( *m_pCloneBM, nodat_sub );
1984             m_pCloneBM->SetMask( m_pMask );
1985         }
1986 
1987         dc.SelectObject( *m_pCloneBM );
1988     } else
1989         pDIB->SelectIntoDC( dc );
1990 
1991     m_last_Region = Region;
1992 
1993     return true;
1994 
1995 }
1996 
RenderViewOnDC(wxMemoryDC & dc,const ViewPort & VPoint)1997 bool s57chart::RenderViewOnDC( wxMemoryDC& dc, const ViewPort& VPoint )
1998 {
1999 //    CALLGRIND_START_INSTRUMENTATION
2000 
2001     SetVPParms( VPoint );
2002 
2003     ps52plib->PrepareForRender((ViewPort *)&VPoint);
2004 
2005     if( m_plib_state_hash != ps52plib->GetStateHash() ) {
2006         m_bLinePrioritySet = false;                     // need to reset line priorities
2007         UpdateLUPs( this );                               // and update the LUPs
2008         ClearRenderedTextCache();                       // and reset the text renderer
2009         SetSafetyContour();
2010     }
2011 
2012     SetLinePriorities();
2013 
2014     bool bnew_view = DoRenderViewOnDC( dc, VPoint, DC_RENDER_ONLY, false );
2015 
2016     pDIB->SelectIntoDC( dc );
2017 
2018     return bnew_view;
2019 
2020 //    CALLGRIND_STOP_INSTRUMENTATION
2021 
2022 }
2023 
DoRenderViewOnDC(wxMemoryDC & dc,const ViewPort & VPoint,RenderTypeEnum option,bool force_new_view)2024 bool s57chart::DoRenderViewOnDC( wxMemoryDC& dc, const ViewPort& VPoint, RenderTypeEnum option,
2025         bool force_new_view )
2026 {
2027     bool bnewview = false;
2028     wxPoint rul, rlr;
2029     bool bNewVP = false;
2030 
2031     bool bReallyNew = false;
2032 
2033     double easting_ul, northing_ul;
2034     double easting_lr, northing_lr;
2035     double prev_easting_ul = 0., prev_northing_ul = 0.;
2036 
2037     if( ps52plib->GetPLIBColorScheme() != m_lastColorScheme ) bReallyNew = true;
2038     m_lastColorScheme = ps52plib->GetPLIBColorScheme();
2039 
2040     if( VPoint.view_scale_ppm != m_last_vp.view_scale_ppm ) bReallyNew = true;
2041 
2042     //      If the scale is very small, do not use the cache to avoid harmonic difficulties...
2043     if( VPoint.chart_scale > 1e8 ) bReallyNew = true;
2044 
2045     wxRect dest( 0, 0, VPoint.pix_width, VPoint.pix_height );
2046     if( m_last_vprect != dest ) bReallyNew = true;
2047     m_last_vprect = dest;
2048 
2049     if( m_plib_state_hash != ps52plib->GetStateHash() ) {
2050         bReallyNew = true;
2051         m_plib_state_hash = ps52plib->GetStateHash();
2052     }
2053 
2054     if( bReallyNew ) {
2055         bNewVP = true;
2056         delete pDIB;
2057         pDIB = NULL;
2058         bnewview = true;
2059     }
2060 
2061 //      Calculate the desired rectangle in the last cached image space
2062     if( m_last_vp.IsValid() ) {
2063         easting_ul = m_easting_vp_center - ( ( VPoint.pix_width / 2 ) / m_view_scale_ppm );
2064         northing_ul = m_northing_vp_center + ( ( VPoint.pix_height / 2 ) / m_view_scale_ppm );
2065         easting_lr = easting_ul + ( VPoint.pix_width / m_view_scale_ppm );
2066         northing_lr = northing_ul - ( VPoint.pix_height / m_view_scale_ppm );
2067 
2068         double last_easting_vp_center, last_northing_vp_center;
2069         toSM( m_last_vp.clat, m_last_vp.clon, ref_lat, ref_lon, &last_easting_vp_center,
2070                 &last_northing_vp_center );
2071 
2072         prev_easting_ul = last_easting_vp_center
2073                 - ( ( m_last_vp.pix_width / 2 ) / m_view_scale_ppm );
2074         prev_northing_ul = last_northing_vp_center
2075                 + ( ( m_last_vp.pix_height / 2 ) / m_view_scale_ppm );
2076 
2077         double dx = ( easting_ul - prev_easting_ul ) * m_view_scale_ppm;
2078         double dy = ( prev_northing_ul - northing_ul ) * m_view_scale_ppm;
2079 
2080         rul.x = (int) round((easting_ul - prev_easting_ul) * m_view_scale_ppm);
2081         rul.y = (int) round((prev_northing_ul - northing_ul) * m_view_scale_ppm);
2082 
2083         rlr.x = (int) round((easting_lr - prev_easting_ul) * m_view_scale_ppm);
2084         rlr.y = (int) round((prev_northing_ul - northing_lr) * m_view_scale_ppm);
2085 
2086         if( ( fabs( dx - wxRound( dx ) ) > 1e-5 ) || ( fabs( dy - wxRound( dy ) ) > 1e-5 ) ) {
2087             if( g_bDebugS57 ) printf(
2088                     "s57chart::DoRender  Cache miss on non-integer pixel delta %g %g\n", dx, dy );
2089             rul.x = 0;
2090             rul.y = 0;
2091             rlr.x = 0;
2092             rlr.y = 0;
2093             bNewVP = true;
2094         }
2095 
2096         else if( ( rul.x != 0 ) || ( rul.y != 0 ) ) {
2097             if( g_bDebugS57 ) printf( "newvp due to rul\n" );
2098             bNewVP = true;
2099         }
2100     } else {
2101         rul.x = 0;
2102         rul.y = 0;
2103         rlr.x = 0;
2104         rlr.y = 0;
2105         bNewVP = true;
2106     }
2107 
2108     if( force_new_view ) bNewVP = true;
2109 
2110     //      Using regions, calculate re-usable area of pDIB
2111 
2112     OCPNRegion rgn_last( 0, 0, VPoint.pix_width, VPoint.pix_height );
2113     OCPNRegion rgn_new( rul.x, rul.y, rlr.x - rul.x, rlr.y - rul.y );
2114     rgn_last.Intersect( rgn_new );            // intersection is reusable portion
2115 
2116     if( bNewVP && ( NULL != pDIB ) && !rgn_last.IsEmpty() ) {
2117         int xu, yu, wu, hu;
2118         rgn_last.GetBox( xu, yu, wu, hu );
2119 
2120         int desx = 0;
2121         int desy = 0;
2122         int srcx = xu;
2123         int srcy = yu;
2124 
2125         if( rul.x < 0 ) {
2126             srcx = 0;
2127             desx = -rul.x;
2128         }
2129         if( rul.y < 0 ) {
2130             srcy = 0;
2131             desy = -rul.y;
2132         }
2133 
2134         ocpnMemDC dc_last;
2135         pDIB->SelectIntoDC( dc_last );
2136 
2137         ocpnMemDC dc_new;
2138         PixelCache *pDIBNew = new PixelCache( VPoint.pix_width, VPoint.pix_height, BPP );
2139         pDIBNew->SelectIntoDC( dc_new );
2140 
2141 //        printf("reuse blit %d %d %d %d %d %d\n",desx, desy, wu, hu,  srcx, srcy);
2142         dc_new.Blit( desx, desy, wu, hu, (wxDC *) &dc_last, srcx, srcy, wxCOPY );
2143 
2144         //        Ask the plib to adjust the persistent text rectangle list for this canvas shift
2145         //        This ensures that, on pans, the list stays in registration with the new text renders to come
2146         ps52plib->AdjustTextList( desx - srcx, desy - srcy, VPoint.pix_width, VPoint.pix_height );
2147 
2148         dc_new.SelectObject( wxNullBitmap );
2149         dc_last.SelectObject( wxNullBitmap );
2150 
2151         delete pDIB;
2152         pDIB = pDIBNew;
2153 
2154 //              OK, now have the re-useable section in place
2155 //              Next, build the new sections
2156 
2157         pDIB->SelectIntoDC( dc );
2158 
2159         OCPNRegion rgn_delta( 0, 0, VPoint.pix_width, VPoint.pix_height );
2160         OCPNRegion rgn_reused( desx, desy, wu, hu );
2161         rgn_delta.Subtract( rgn_reused );
2162 
2163         OCPNRegionIterator upd( rgn_delta ); // get the update rect list
2164         while( upd.HaveRects() ) {
2165             wxRect rect = upd.GetRect();
2166 
2167 //      Build temp ViewPort on this region
2168 
2169             ViewPort temp_vp = VPoint;
2170             double temp_lon_left, temp_lat_bot, temp_lon_right, temp_lat_top;
2171 
2172             double temp_northing_ul = prev_northing_ul - ( rul.y / m_view_scale_ppm )
2173                     - ( rect.y / m_view_scale_ppm );
2174             double temp_easting_ul = prev_easting_ul + ( rul.x / m_view_scale_ppm )
2175                     + ( rect.x / m_view_scale_ppm );
2176             fromSM( temp_easting_ul, temp_northing_ul, ref_lat, ref_lon, &temp_lat_top,
2177                     &temp_lon_left );
2178 
2179             double temp_northing_lr = temp_northing_ul - ( rect.height / m_view_scale_ppm );
2180             double temp_easting_lr = temp_easting_ul + ( rect.width / m_view_scale_ppm );
2181             fromSM( temp_easting_lr, temp_northing_lr, ref_lat, ref_lon, &temp_lat_bot,
2182                     &temp_lon_right );
2183 
2184             temp_vp.GetBBox().Set( temp_lat_bot, temp_lon_left,
2185                                    temp_lat_top, temp_lon_right );
2186 
2187             //      Allow some slop in the viewport
2188             //    TODO Investigate why this fails if greater than 5 percent
2189             double margin = wxMin(temp_vp.GetBBox().GetLonRange(), temp_vp.GetBBox().GetLatRange())
2190                     * 0.05;
2191             temp_vp.GetBBox().EnLarge( margin );
2192 
2193 //      And Render it new piece on the target dc
2194 //     printf("New Render, rendering %d %d %d %d \n", rect.x, rect.y, rect.width, rect.height);
2195 
2196             DCRenderRect( dc, temp_vp, &rect );
2197 
2198             upd.NextRect();
2199         }
2200 
2201         dc.SelectObject( wxNullBitmap );
2202 
2203         bnewview = true;
2204 
2205 //      Update last_vp to reflect the current cached bitmap
2206         m_last_vp = VPoint;
2207 
2208     }
2209 
2210     else if( bNewVP || ( NULL == pDIB ) ) {
2211         delete pDIB;
2212         pDIB = new PixelCache( VPoint.pix_width, VPoint.pix_height, BPP );     // destination
2213 
2214         wxRect full_rect( 0, 0, VPoint.pix_width, VPoint.pix_height );
2215         pDIB->SelectIntoDC( dc );
2216 
2217         //        Clear the text declutter list
2218         ps52plib->ClearTextList();
2219 
2220         DCRenderRect( dc, VPoint, &full_rect );
2221 
2222         dc.SelectObject( wxNullBitmap );
2223 
2224         bnewview = true;
2225 
2226 //      Update last_vp to reflect the current cached bitmap
2227         m_last_vp = VPoint;
2228 
2229     }
2230 
2231     return bnewview;
2232 
2233 }
2234 
DCRenderRect(wxMemoryDC & dcinput,const ViewPort & vp,wxRect * rect)2235 int s57chart::DCRenderRect( wxMemoryDC& dcinput, const ViewPort& vp, wxRect* rect )
2236 {
2237 
2238     int i;
2239     ObjRazRules *top;
2240     ObjRazRules *crnt;
2241 
2242     wxASSERT(rect);
2243     ViewPort tvp = vp;                    // undo const  TODO fix this in PLIB
2244 
2245 //    This does not work due to some issue with ref data of allocated buffer.....
2246 //    render_canvas_parms pb_spec( rect->x, rect->y, rect->width, rect->height,  GetGlobalColor ( _T ( "NODTA" ) ));
2247 
2248     render_canvas_parms pb_spec;
2249 
2250     pb_spec.depth = BPP;
2251     pb_spec.pb_pitch = ( ( rect->width * pb_spec.depth / 8 ) );
2252     pb_spec.lclip = rect->x;
2253     pb_spec.rclip = rect->x + rect->width - 1;
2254     pb_spec.pix_buff = (unsigned char *) malloc( rect->height * pb_spec.pb_pitch );
2255     pb_spec.width = rect->width;
2256     pb_spec.height = rect->height;
2257     pb_spec.x = rect->x;
2258     pb_spec.y = rect->y;
2259 
2260 #ifdef ocpnUSE_ocpnBitmap
2261     pb_spec.b_revrgb = true;
2262 #else
2263     pb_spec.b_revrgb = false;
2264 #endif
2265 
2266     // Preset background
2267     wxColour color = GetGlobalColor( _T ( "NODTA" ) );
2268     unsigned char r, g, b;
2269     if( color.IsOk() ) {
2270         r = color.Red();
2271         g = color.Green();
2272         b = color.Blue();
2273     } else
2274         r = g = b = 0;
2275 
2276     if( pb_spec.depth == 24 ) {
2277         for( int i = 0; i < pb_spec.height; i++ ) {
2278             unsigned char *p = pb_spec.pix_buff + ( i * pb_spec.pb_pitch );
2279             for( int j = 0; j < pb_spec.width; j++ ) {
2280                 *p++ = r;
2281                 *p++ = g;
2282                 *p++ = b;
2283             }
2284         }
2285     } else {
2286         int color_int = ( ( r ) << 16 ) + ( ( g ) << 8 ) + ( b );
2287 
2288         for( int i = 0; i < pb_spec.height; i++ ) {
2289             int *p = (int *) ( pb_spec.pix_buff + ( i * pb_spec.pb_pitch ) );
2290             for( int j = 0; j < pb_spec.width; j++ ) {
2291                 *p++ = color_int;
2292             }
2293         }
2294     }
2295 
2296 //      Render the areas quickly
2297     for( i = 0; i < PRIO_NUM; ++i ) {
2298         if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
2299             top = razRules[i][4]; // Area Symbolized Boundaries
2300         else
2301             top = razRules[i][3]; // Area Plain Boundaries
2302 
2303         while( top != NULL ) {
2304             crnt = top;
2305             top = top->next;               // next object
2306             crnt->sm_transform_parms = &vp_transform;
2307             ps52plib->RenderAreaToDC( &dcinput, crnt, &tvp, &pb_spec );
2308         }
2309     }
2310 
2311 //      Convert the Private render canvas into a bitmap
2312 #ifdef ocpnUSE_ocpnBitmap
2313     ocpnBitmap *pREN = new ocpnBitmap( pb_spec.pix_buff, pb_spec.width, pb_spec.height,
2314             pb_spec.depth );
2315 #else
2316     wxImage *prender_image = new wxImage(pb_spec.width, pb_spec.height, false);
2317     prender_image->SetData((unsigned char*)pb_spec.pix_buff);
2318     wxBitmap *pREN = new wxBitmap(*prender_image);
2319 
2320 #endif
2321 
2322 //      Map it into a temporary DC
2323     wxMemoryDC dc_ren;
2324     dc_ren.SelectObject( *pREN );
2325 
2326 //      Blit it onto the target dc
2327     dcinput.Blit( pb_spec.x, pb_spec.y, pb_spec.width, pb_spec.height, (wxDC *) &dc_ren, 0, 0 );
2328 
2329 //      And clean up the mess
2330     dc_ren.SelectObject( wxNullBitmap );
2331 
2332 #ifdef ocpnUSE_ocpnBitmap
2333     free( pb_spec.pix_buff );
2334 #else
2335     delete prender_image;           // the image owns the data
2336                                     // and so will free it in due course
2337 #endif
2338 
2339     delete pREN;
2340 
2341 //      Render the rest of the objects/primitives
2342     DCRenderLPB( dcinput, vp, rect );
2343 
2344     return 1;
2345 }
2346 
DCRenderLPB(wxMemoryDC & dcinput,const ViewPort & vp,wxRect * rect)2347 bool s57chart::DCRenderLPB( wxMemoryDC& dcinput, const ViewPort& vp, wxRect* rect )
2348 {
2349     int i;
2350     ObjRazRules *top;
2351     ObjRazRules *crnt;
2352     ViewPort tvp = vp;                    // undo const  TODO fix this in PLIB
2353 
2354     for( i = 0; i < PRIO_NUM; ++i ) {
2355 //      Set up a Clipper for Lines
2356         wxDCClipper *pdcc = NULL;
2357 //      if( rect ) {
2358 //         wxRect nr = *rect;
2359 //         pdcc = new wxDCClipper(dcinput, nr);
2360 //      }
2361 
2362         if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
2363             top = razRules[i][4]; // Area Symbolized Boundaries
2364         else
2365             top = razRules[i][3];           // Area Plain Boundaries
2366         while( top != NULL ) {
2367             crnt = top;
2368             top = top->next;               // next object
2369             crnt->sm_transform_parms = &vp_transform;
2370             ps52plib->RenderObjectToDC( &dcinput, crnt, &tvp );
2371         }
2372 
2373         top = razRules[i][2];           //LINES
2374         while( top != NULL ) {
2375             crnt = top;
2376             top = top->next;
2377             crnt->sm_transform_parms = &vp_transform;
2378             ps52plib->RenderObjectToDC( &dcinput, crnt, &tvp );
2379         }
2380 
2381         if( ps52plib->m_nSymbolStyle == SIMPLIFIED )
2382             top = razRules[i][0];       //SIMPLIFIED Points
2383         else
2384             top = razRules[i][1];           //Paper Chart Points Points
2385 
2386         while( top != NULL ) {
2387             crnt = top;
2388             top = top->next;
2389             crnt->sm_transform_parms = &vp_transform;
2390             ps52plib->RenderObjectToDC( &dcinput, crnt, &tvp );
2391         }
2392 
2393         //      Destroy Clipper
2394         if( pdcc ) delete pdcc;
2395     }
2396 
2397     /*
2398      printf("Render Lines                  %ldms\n", stlines.Time());
2399      printf("Render Simple Points          %ldms\n", stsim_pt.Time());
2400      printf("Render Paper Points           %ldms\n", stpap_pt.Time());
2401      printf("Render Symbolized Boundaries  %ldms\n", stasb.Time());
2402      printf("Render Plain Boundaries       %ldms\n\n", stapb.Time());
2403      */
2404     return true;
2405 }
2406 
DCRenderText(wxMemoryDC & dcinput,const ViewPort & vp)2407 bool s57chart::DCRenderText( wxMemoryDC& dcinput, const ViewPort& vp )
2408 {
2409     int i;
2410     ObjRazRules *top;
2411     ObjRazRules *crnt;
2412     ViewPort tvp = vp;                    // undo const  TODO fix this in PLIB
2413 
2414     for( i = 0; i < PRIO_NUM; ++i ) {
2415 
2416         if( ps52plib->m_nBoundaryStyle == SYMBOLIZED_BOUNDARIES )
2417             top = razRules[i][4]; // Area Symbolized Boundaries
2418         else
2419             top = razRules[i][3]; // Area Plain Boundaries
2420 
2421         while( top != NULL ) {
2422                 crnt = top;
2423                 top = top->next;               // next object
2424                 crnt->sm_transform_parms = &vp_transform;
2425                 ps52plib->RenderObjectToDCText( &dcinput, crnt, &tvp );
2426         }
2427 
2428         top = razRules[i][2];           //LINES
2429         while( top != NULL ) {
2430                 crnt = top;
2431                 top = top->next;
2432                 crnt->sm_transform_parms = &vp_transform;
2433                 ps52plib->RenderObjectToDCText( &dcinput, crnt, &tvp );
2434         }
2435 
2436         if( ps52plib->m_nSymbolStyle == SIMPLIFIED )
2437             top = razRules[i][0];       //SIMPLIFIED Points
2438         else
2439             top = razRules[i][1];           //Paper Chart Points Points
2440 
2441         while( top != NULL ) {
2442                 crnt = top;
2443                 top = top->next;
2444                 crnt->sm_transform_parms = &vp_transform;
2445                 ps52plib->RenderObjectToDCText( &dcinput, crnt, &tvp );
2446         }
2447     }
2448 
2449     return true;
2450 }
2451 
2452 
2453 
2454 
IsCellOverlayType(const wxString & FullPath)2455 bool s57chart::IsCellOverlayType( const wxString &FullPath )
2456 {
2457     wxFileName fn( FullPath );
2458     //      Get the "Usage" character
2459     wxString cname = fn.GetName();
2460     if(cname.Length() >= 3)
2461         return ( (cname[2] == 'L') || (cname[2] == 'A'));
2462     else
2463         return false;
2464 }
2465 
Init(const wxString & name,ChartInitFlag flags)2466 InitReturn s57chart::Init( const wxString& name, ChartInitFlag flags )
2467 {
2468     // Really can only Init and use S57 chart if the S52 Presentation Library is present and OK
2469     if( (NULL ==ps52plib) || !(ps52plib->m_bOK) )
2470         return INIT_FAIL_REMOVE;
2471 
2472     wxString ext;
2473     if(name.Upper().EndsWith(".XZ")) {
2474         ext = wxFileName(name.Left(name.Length()-3)).GetExt();
2475 
2476         // decompress to temp file to allow seeking
2477         m_TempFilePath = wxFileName::GetTempDir() + wxFileName::GetPathSeparator() +
2478             wxFileName(name).GetName();
2479 
2480         if(!wxFileExists(m_TempFilePath) && !DecompressXZFile(name, m_TempFilePath)) {
2481             wxRemoveFile(m_TempFilePath);
2482             return INIT_FAIL_REMOVE;
2483         }
2484     } else {
2485         m_TempFilePath = name;
2486         ext = wxFileName(name).GetExt();
2487     }
2488     m_FullPath = name;
2489 
2490     //    Use a static semaphore flag to prevent recursion
2491     if( s_bInS57 ) {
2492 //          printf("s57chart::Init() recursion..., retry\n");
2493 //          wxLogMessage(_T("Recursion"));
2494         return INIT_FAIL_NOERROR;
2495     }
2496 
2497     s_bInS57++;
2498 
2499     InitReturn ret_value = INIT_OK;
2500 
2501     m_Description = name;
2502 
2503     wxFileName fn( m_TempFilePath );
2504 
2505     //      Get the "Usage" character
2506     wxString cname = fn.GetName();
2507     m_usage_char = cname[2];
2508 
2509     //  Establish a common reference point for the chart
2510     ref_lat = ( m_FullExtent.NLAT + m_FullExtent.SLAT ) / 2.;
2511     ref_lon = ( m_FullExtent.WLON + m_FullExtent.ELON ) / 2.;
2512 
2513     if( flags == THUMB_ONLY ) {
2514 
2515         // Look for Thumbnail
2516         // LoadThumb();
2517 
2518         s_bInS57--;
2519         return INIT_OK;
2520     }
2521 
2522     if( flags == HEADER_ONLY ) {
2523         if( ext == _T("000") ) {
2524             if( !GetBaseFileAttr( fn.GetFullPath() ) )
2525                 ret_value = INIT_FAIL_REMOVE;
2526             else {
2527                 if( !CreateHeaderDataFromENC() )
2528                     ret_value = INIT_FAIL_REMOVE;
2529                 else
2530                     ret_value = INIT_OK;
2531             }
2532         } else if( ext == _T("S57") ) {
2533             m_SENCFileName = m_TempFilePath;
2534             if( !CreateHeaderDataFromSENC() ) ret_value = INIT_FAIL_REMOVE;
2535             else
2536                 ret_value = INIT_OK;
2537         }
2538 
2539         s_bInS57--;
2540         return ret_value;
2541 
2542     }
2543 
2544     //      Full initialization from here
2545 
2546     if( !m_bbase_file_attr_known ) {
2547         if( !GetBaseFileAttr( m_TempFilePath ) )
2548             ret_value = INIT_FAIL_REMOVE;
2549         else
2550             m_bbase_file_attr_known = true;
2551     }
2552 
2553     if( ext == _T("000") ) {
2554         if( m_bbase_file_attr_known ) {
2555 
2556             int sret = FindOrCreateSenc( m_FullPath );
2557             if(sret == BUILD_SENC_PENDING){
2558                     s_bInS57--;
2559                     return INIT_OK;
2560             }
2561 
2562             if( sret != BUILD_SENC_OK ) {
2563                 if( sret == BUILD_SENC_NOK_RETRY ) ret_value = INIT_FAIL_RETRY;
2564                 else
2565                     ret_value = INIT_FAIL_REMOVE;
2566             } else
2567                 ret_value = PostInit( flags, m_global_color_scheme );
2568 
2569         }
2570 
2571     }
2572 
2573     else if( ext == _T("S57") ) {
2574 
2575         m_SENCFileName = m_TempFilePath;
2576         ret_value = PostInit( flags, m_global_color_scheme );
2577 
2578     }
2579 
2580 
2581     s_bInS57--;
2582     return ret_value;
2583 
2584 }
2585 
buildSENCName(const wxString & name)2586 wxString s57chart::buildSENCName( const wxString& name)
2587 {
2588     wxFileName fn(name);
2589     fn.SetExt( _T("S57") );
2590     wxString file_name = fn.GetFullName();
2591 
2592     //      Set the proper directory for the SENC files
2593     wxString SENCdir = g_SENCPrefix;
2594 
2595     if( SENCdir.Last() != wxFileName::GetPathSeparator() )
2596         SENCdir.Append( wxFileName::GetPathSeparator() );
2597 
2598 #if 1
2599     wxString source_dir = fn.GetPath(wxPATH_GET_SEPARATOR);
2600     wxCharBuffer buf = source_dir.ToUTF8();
2601     unsigned char sha1_out[20];
2602     sha1( (unsigned char *) buf.data(), strlen(buf.data()), sha1_out );
2603 
2604     wxString sha1;
2605     for (unsigned int i=0 ; i < 6 ; i++){
2606         wxString s;
2607         s.Printf(_T("%02X"), sha1_out[i]);
2608         sha1 += s;
2609     }
2610     sha1 += _T("_");
2611     file_name.Prepend(sha1);
2612 #endif
2613 
2614     wxFileName tsfn( SENCdir );
2615     tsfn.SetFullName( file_name );
2616 
2617     return tsfn.GetFullPath();
2618 }
2619 
2620 //-----------------------------------------------------------------------------------------------
2621 //    Find or Create a relevent SENC file from a given .000 ENC file
2622 //    Returns with error code, and associated SENC file name in m_S57FileName
2623 //-----------------------------------------------------------------------------------------------
FindOrCreateSenc(const wxString & name,bool b_progress)2624 int s57chart::FindOrCreateSenc( const wxString& name, bool b_progress )
2625 {
2626     //  This method may be called for a compressed .000 cell, so check and decompress if necessary
2627     wxString ext;
2628     if(name.Upper().EndsWith(".XZ")) {
2629         ext = wxFileName(name.Left(name.Length()-3)).GetExt();
2630 
2631         // decompress to temp file to allow seeking
2632         m_TempFilePath = wxFileName::GetTempDir() + wxFileName::GetPathSeparator() +
2633         wxFileName(name).GetName();
2634 
2635         if(!wxFileExists(m_TempFilePath) && !DecompressXZFile(name, m_TempFilePath)) {
2636             wxRemoveFile(m_TempFilePath);
2637             return INIT_FAIL_REMOVE;
2638         }
2639     } else {
2640         m_TempFilePath = name;
2641         ext = wxFileName(name).GetExt();
2642     }
2643     m_FullPath = name;
2644 
2645     if( !m_bbase_file_attr_known ) {
2646         if( !GetBaseFileAttr( m_TempFilePath ) )
2647             return INIT_FAIL_REMOVE;
2648         else
2649             m_bbase_file_attr_known = true;
2650     }
2651 
2652     //      Establish location for SENC files
2653     m_SENCFileName = buildSENCName( name );
2654 
2655     int build_ret_val = 1;
2656 
2657     bool bbuild_new_senc = false;
2658     m_bneed_new_thumbnail = false;
2659 
2660     wxFileName FileName000( m_TempFilePath );
2661 
2662 //      Look for SENC file in the target directory
2663 
2664     wxString msg( _T("S57chart::Checking SENC file: ") );
2665     msg.Append( m_SENCFileName );
2666     wxLogMessage( msg );
2667 
2668     {
2669         int force_make_senc = 0;
2670 
2671         if( ::wxFileExists(m_SENCFileName) ){                    // SENC file exists
2672 
2673             Osenc senc;
2674             if(senc.ingestHeader( m_SENCFileName ) ){
2675                 bbuild_new_senc = true;
2676                 wxLogMessage(_T("    Rebuilding SENC due to ingestHeader failure."));
2677             }
2678             else{
2679 
2680                 int senc_file_version = senc.getSencReadVersion();
2681 
2682                 int last_update = senc.getSENCReadLastUpdate();
2683 
2684                 wxString str = senc.getSENCFileCreateDate();
2685                 wxDateTime SENCCreateDate;
2686                 SENCCreateDate.ParseFormat( str, _T("%Y%m%d"));
2687 
2688                 if( SENCCreateDate.IsValid() )
2689                     SENCCreateDate.ResetTime();                   // to midnight
2690 
2691 //                wxULongLong size000 = senc.getFileSize000();
2692 //                wxString ssize000 = senc.getsFileSize000();
2693 
2694                 wxString senc_base_edtn = senc.getSENCReadBaseEdition();
2695                 long isenc_edition;
2696                 senc_base_edtn.ToLong(&isenc_edition);
2697                 long ifile_edition;
2698                 m_edtn000.ToLong(&ifile_edition);
2699 
2700 //              Anything to do?
2701 //force_make_senc = 1;
2702                 //  SENC file version has to be correct for other tests to make sense
2703                 if( senc_file_version != CURRENT_SENC_FORMAT_VERSION ){
2704                     bbuild_new_senc = true;
2705                     wxLogMessage(_T("    Rebuilding SENC due to SENC format update."));
2706                 }
2707 
2708                 //  Senc EDTN must be the same as .000 file EDTN.
2709                 //  This test catches the usual case where the .000 file is updated from the web,
2710                 //  and all updates (.001, .002, etc.)  are subsumed.
2711 
2712                 else if( ifile_edition > isenc_edition ){
2713                     bbuild_new_senc = true;
2714                     wxLogMessage(_T("    Rebuilding SENC due to cell edition update."));
2715                     wxString msg;
2716                     msg = _T("    Last edition recorded in SENC: ");
2717                     msg += senc_base_edtn;
2718                     msg += _T("  most recent edition cell file: ");
2719                     msg += m_edtn000;
2720                     wxLogMessage(msg);
2721                 }
2722                 else {
2723                     //    See if there are any new update files  in the ENC directory
2724                     int most_recent_update_file = GetUpdateFileArray( FileName000, NULL, m_date000, m_edtn000 );
2725 
2726                     if( ifile_edition == isenc_edition ){
2727                         if( most_recent_update_file > last_update ){
2728                             bbuild_new_senc = true;
2729                             wxLogMessage(_T("    Rebuilding SENC due to incremental cell update."));
2730                             wxString msg;
2731                             msg.Printf(_T("    Last update recorded in SENC: %d   most recent update file: %d"), last_update, most_recent_update_file);
2732                             wxLogMessage(msg);
2733                         }
2734                     }
2735 
2736 //          Make simple tests to see if the .000 file is "newer" than the SENC file representation
2737 //          These tests may be redundant, since the DSID:EDTN test above should catch new base files
2738                     wxDateTime OModTime000;
2739                     FileName000.GetTimes( NULL, &OModTime000, NULL );
2740                     OModTime000.ResetTime();                      // to midnight
2741                     if( SENCCreateDate.IsValid() ){
2742                         if( OModTime000.IsLaterThan( SENCCreateDate ) ){
2743                             wxLogMessage(_T("    Rebuilding SENC due to Senc vs cell file time check."));
2744                             bbuild_new_senc = true;
2745                         }
2746                     }
2747                     else{
2748                         bbuild_new_senc = true;
2749                         wxLogMessage(_T("    Rebuilding SENC due to SENC create time invalid."));
2750                     }
2751 
2752 
2753 //                     int Osize000l = FileName000.GetSize().GetLo();
2754 //                     int Osize000h = FileName000.GetSize().GetHi();
2755 //                     wxString t;
2756 //                     t.Printf(_T("%d%d"), Osize000h, Osize000l);
2757 //                     if( !t.IsSameAs( ssize000) )
2758 //                         bbuild_new_senc = true;
2759 
2760                 }
2761 
2762                 if( force_make_senc )
2763                     bbuild_new_senc = true;
2764 
2765             }
2766         }
2767         else if( !::wxFileExists(m_SENCFileName ) )                    // SENC file does not exist
2768         {
2769             wxLogMessage(_T("    Rebuilding SENC due to missing SENC file."));
2770             bbuild_new_senc = true;
2771         }
2772     }
2773 
2774     if( bbuild_new_senc ) {
2775         m_bneed_new_thumbnail = true; // force a new thumbnail to be built in PostInit()
2776         build_ret_val = BuildSENCFile( m_TempFilePath, m_SENCFileName, b_progress );
2777 
2778         if(BUILD_SENC_PENDING == build_ret_val)
2779             return BUILD_SENC_PENDING;
2780         if( BUILD_SENC_NOK_PERMANENT == build_ret_val )
2781             return INIT_FAIL_REMOVE;
2782         if( BUILD_SENC_NOK_RETRY == build_ret_val )
2783             return INIT_FAIL_RETRY;
2784     }
2785 
2786     return INIT_OK;
2787 }
2788 
PostInit(ChartInitFlag flags,ColorScheme cs)2789 InitReturn s57chart::PostInit( ChartInitFlag flags, ColorScheme cs )
2790 {
2791 
2792 //    SENC file is ready, so build the RAZ structure
2793     if( 0 != BuildRAZFromSENCFile( m_SENCFileName ) ) {
2794         wxString msg( _T("   Cannot load SENC file ") );
2795         msg.Append( m_SENCFileName );
2796         wxLogMessage( msg );
2797 
2798         return INIT_FAIL_RETRY;
2799     }
2800 
2801 //      Check for and if necessary rebuild Thumbnail
2802 //      Going to be in the global (user) SENC file directory
2803 #if 1
2804     wxString SENCdir = g_SENCPrefix;
2805     if( SENCdir.Last() != wxFileName::GetPathSeparator() )
2806         SENCdir.Append( wxFileName::GetPathSeparator() );
2807 
2808     wxFileName s57File(m_SENCFileName);
2809     wxFileName ThumbFileName( SENCdir, s57File.GetName().Mid( 13 ), _T("BMP") );
2810 
2811     if( !ThumbFileName.FileExists() || m_bneed_new_thumbnail )
2812     {
2813         BuildThumbnail( ThumbFileName.GetFullPath() );
2814 
2815         //  Update the member thumbdata structure
2816         if( ThumbFileName.FileExists() ) {
2817             wxBitmap *pBMP_NEW;
2818 #ifdef ocpnUSE_ocpnBitmap
2819             pBMP_NEW = new ocpnBitmap;
2820 #else
2821             pBMP_NEW = new wxBitmap;
2822 #endif
2823             if( pBMP_NEW->LoadFile( ThumbFileName.GetFullPath(), wxBITMAP_TYPE_BMP ) ) {
2824                 delete pThumbData;
2825                 pThumbData = new ThumbData;
2826                 m_pDIBThumbDay = pBMP_NEW;
2827 //                    pThumbData->pDIBThumb = pBMP_NEW;
2828             }
2829         }
2830     }
2831 #endif
2832 
2833 //    Set the color scheme
2834     m_global_color_scheme = cs;
2835     SetColorScheme( cs, false );
2836 
2837 //    Build array of contour values for later use by conditional symbology
2838 
2839     BuildDepthContourArray();
2840     m_RAZBuilt = true;
2841     bReadyToRender = true;
2842 
2843     return INIT_OK;
2844 }
2845 
ClearDepthContourArray(void)2846 void s57chart::ClearDepthContourArray( void )
2847 {
2848 
2849     if( m_nvaldco_alloc ) {
2850         free (m_pvaldco_array);
2851     }
2852     m_nvaldco_alloc = 5;
2853     m_nvaldco = 0;
2854     m_pvaldco_array = (double *) calloc( m_nvaldco_alloc, sizeof(double) );
2855 }
2856 
BuildDepthContourArray(void)2857 void s57chart::BuildDepthContourArray( void )
2858 {
2859     //    Build array of contour values for later use by conditional symbology
2860 
2861     if( 0 == m_nvaldco_alloc ) {
2862         m_nvaldco_alloc = 5;
2863         m_pvaldco_array = (double *) calloc( m_nvaldco_alloc, sizeof(double) );
2864     }
2865 
2866     ObjRazRules *top;
2867     // some ENC have a lot of DEPCNT objects but they seem to store them
2868     // in VALDCO order, try to take advantage of that.
2869     double prev_valdco = 0.0;
2870 
2871     for( int i = 0; i < PRIO_NUM; ++i ) {
2872         for( int j = 0; j < LUPNAME_NUM; j++ ) {
2873 
2874             top = razRules[i][j];
2875             while( top != NULL ) {
2876                 if( !strncmp( top->obj->FeatureName, "DEPCNT", 6 ) ) {
2877                     double valdco = 0.0;
2878                     if( GetDoubleAttr( top->obj, "VALDCO", valdco ) ) {
2879                         if (valdco != prev_valdco) {
2880                             prev_valdco = valdco;
2881                             m_nvaldco++;
2882                             if( m_nvaldco > m_nvaldco_alloc ) {
2883                                 void *tr = realloc( (void *) m_pvaldco_array,
2884                                         m_nvaldco_alloc * 2 * sizeof(double) );
2885                                 m_pvaldco_array = (double *) tr;
2886                                 m_nvaldco_alloc *= 2;
2887                             }
2888                             m_pvaldco_array[m_nvaldco - 1] = valdco;
2889                         }
2890                     }
2891                 }
2892                 ObjRazRules *nxx = top->next;
2893                 top = nxx;
2894             }
2895         }
2896     }
2897     std::sort( m_pvaldco_array, m_pvaldco_array + m_nvaldco );
2898     SetSafetyContour();
2899 }
2900 
2901 
SetSafetyContour(void)2902 void s57chart::SetSafetyContour(void)
2903 {
2904     // Iterate through the array of contours in this cell, choosing the best one to
2905     // render as a bold "safety contour" in the PLIB.
2906 
2907     //    This method computes the smallest chart DEPCNT:VALDCO value which
2908     //    is greater than or equal to the current PLIB mariner parameter S52_MAR_SAFETY_CONTOUR
2909 
2910     double mar_safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
2911 
2912     int i = 0;
2913     if( NULL != m_pvaldco_array ) {
2914         for( i = 0; i < m_nvaldco; i++ ) {
2915             if( m_pvaldco_array[i] >= mar_safety_contour )
2916                 break;
2917         }
2918 
2919         if( i < m_nvaldco )
2920             m_next_safe_cnt = m_pvaldco_array[i];
2921         else
2922             m_next_safe_cnt = (double) 1e6;
2923      } else {
2924          m_next_safe_cnt = (double) 1e6;
2925      }
2926 
2927      // A safety contour greater than "Deep Depth" makes no sense...
2928      // So, declare "no suitable safety depth contour"
2929      if(m_next_safe_cnt > S52_getMarinerParam(S52_MAR_DEEP_CONTOUR))
2930          m_next_safe_cnt = (double) 1e6;
2931 
2932 }
2933 
InvalidateCache()2934 void s57chart::InvalidateCache()
2935 {
2936     delete pDIB;
2937     pDIB = NULL;
2938 }
2939 
BuildThumbnail(const wxString & bmpname)2940 bool s57chart::BuildThumbnail( const wxString &bmpname )
2941 {
2942     bool ret_code;
2943 
2944     wxFileName ThumbFileName( bmpname );
2945 
2946     //      Make the target directory if needed
2947     if( true != ThumbFileName.DirExists( ThumbFileName.GetPath() ) ) {
2948         if( !ThumbFileName.Mkdir( ThumbFileName.GetPath() ) ) {
2949             wxLogMessage(
2950                     _T("   Cannot create BMP file directory for ")
2951                             + ThumbFileName.GetFullPath() );
2952             return false;
2953         }
2954     }
2955 
2956     //      Set up a private ViewPort
2957     ViewPort vp;
2958 
2959     vp.clon = ( m_FullExtent.ELON + m_FullExtent.WLON ) / 2.;
2960     vp.clat = ( m_FullExtent.NLAT + m_FullExtent.SLAT ) / 2.;
2961 
2962     float ext_max =
2963             fmax((m_FullExtent.NLAT - m_FullExtent.SLAT), (m_FullExtent.ELON - m_FullExtent.WLON));
2964 
2965     vp.view_scale_ppm = ( S57_THUMB_SIZE / ext_max ) / ( 1852 * 60 );
2966 
2967     vp.pix_height = S57_THUMB_SIZE;
2968     vp.pix_width = S57_THUMB_SIZE;
2969 
2970     vp.m_projection_type = PROJECTION_MERCATOR;
2971 
2972     vp.GetBBox().Set( m_FullExtent.SLAT, m_FullExtent.WLON,
2973                       m_FullExtent.NLAT, m_FullExtent.ELON );
2974 
2975     vp.chart_scale = 10000000 - 1;
2976     vp.ref_scale = vp.chart_scale;
2977     vp.Validate();
2978 
2979     // cause a clean new render
2980     delete pDIB;
2981     pDIB = NULL;
2982 
2983     SetVPParms( vp );
2984 
2985 //      Borrow the OBJLArray temporarily to set the object type visibility for this render
2986 //      First, make a copy for the curent OBJLArray viz settings, setting current value to invisible
2987 
2988     unsigned int OBJLCount = ps52plib->pOBJLArray->GetCount();
2989 //      int *psave_viz = new int[OBJLCount];
2990     int *psave_viz = (int *) malloc( OBJLCount * sizeof(int) );
2991 
2992     int *psvr = psave_viz;
2993     OBJLElement *pOLE;
2994     unsigned int iPtr;
2995 
2996     for( iPtr = 0; iPtr < OBJLCount; iPtr++ ) {
2997         pOLE = (OBJLElement *) ( ps52plib->pOBJLArray->Item( iPtr ) );
2998         *psvr++ = pOLE->nViz;
2999         pOLE->nViz = 0;
3000     }
3001 
3002 //      Also, save some other settings
3003     bool bsavem_bShowSoundgp = ps52plib->m_bShowSoundg;
3004     bool bsave_text = ps52plib->m_bShowS57Text;
3005 
3006     // SetDisplayCategory may clear Noshow array
3007     ps52plib->SaveObjNoshow();
3008 
3009 //      Now, set up what I want for this render
3010     for( iPtr = 0; iPtr < OBJLCount; iPtr++ ) {
3011         pOLE = (OBJLElement *) ( ps52plib->pOBJLArray->Item( iPtr ) );
3012         if( !strncmp( pOLE->OBJLName, "LNDARE", 6 ) ) pOLE->nViz = 1;
3013         if( !strncmp( pOLE->OBJLName, "DEPARE", 6 ) ) pOLE->nViz = 1;
3014     }
3015 
3016 
3017     ps52plib->m_bShowSoundg = false;
3018     ps52plib->m_bShowS57Text = false;
3019 
3020 //      Use display category MARINERS_STANDARD to force use of OBJLArray
3021     DisCat dsave = ps52plib->GetDisplayCategory();
3022     ps52plib->SetDisplayCategory( MARINERS_STANDARD );
3023 
3024     ps52plib->AddObjNoshow( "BRIDGE" );
3025     ps52plib->AddObjNoshow( "GATCON" );
3026 
3027     double safety_depth = S52_getMarinerParam(S52_MAR_SAFETY_DEPTH);
3028     S52_setMarinerParam(S52_MAR_SAFETY_DEPTH, -100);
3029     double safety_contour = S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
3030     S52_setMarinerParam(S52_MAR_SAFETY_CONTOUR, -100);
3031 
3032 
3033 #ifdef ocpnUSE_DIBSECTION
3034     ocpnMemDC memdc, dc_org;
3035 #else
3036     wxMemoryDC memdc, dc_org;
3037 #endif
3038 
3039 //      set the color scheme
3040     ps52plib->SaveColorScheme();
3041     ps52plib->SetPLIBColorScheme( _T("DAY") );
3042 //      Do the render
3043     DoRenderViewOnDC( memdc, vp, DC_RENDER_ONLY, true );
3044 
3045 //      Release the DIB
3046     memdc.SelectObject( wxNullBitmap );
3047 
3048 //      Restore the plib to previous state
3049     psvr = psave_viz;
3050     for( iPtr = 0; iPtr < OBJLCount; iPtr++ ) {
3051         pOLE = (OBJLElement *) ( ps52plib->pOBJLArray->Item( iPtr ) );
3052         pOLE->nViz = *psvr++;
3053     }
3054 
3055     ps52plib->SetDisplayCategory(dsave);
3056     ps52plib->RestoreObjNoshow();
3057 
3058     ps52plib->RemoveObjNoshow( "BRIDGE" );
3059     ps52plib->RemoveObjNoshow( "GATCON" );
3060 
3061     ps52plib->m_bShowSoundg = bsavem_bShowSoundgp;
3062     ps52plib->m_bShowS57Text = bsave_text;
3063 
3064     S52_setMarinerParam(S52_MAR_SAFETY_DEPTH, safety_depth);
3065     S52_setMarinerParam(S52_MAR_SAFETY_CONTOUR, safety_contour);
3066 
3067 //      Reset the color scheme
3068     ps52plib->RestoreColorScheme();
3069 
3070 //       delete psave_viz;
3071     free( psave_viz );
3072 
3073 //      Clone pDIB into pThumbData;
3074     wxBitmap *pBMP;
3075 
3076     pBMP = new wxBitmap( vp.pix_width, vp.pix_height/*,  BPP*/);
3077 
3078     wxMemoryDC dc_clone;
3079     dc_clone.SelectObject( *pBMP );
3080 
3081     pDIB->SelectIntoDC( dc_org );
3082 
3083     dc_clone.Blit( 0, 0, vp.pix_width, vp.pix_height, (wxDC *) &dc_org, 0, 0 );
3084 
3085     dc_clone.SelectObject( wxNullBitmap );
3086     dc_org.SelectObject( wxNullBitmap );
3087 
3088     //   Save the file
3089     ret_code = pBMP->SaveFile( ThumbFileName.GetFullPath(), wxBITMAP_TYPE_BMP );
3090 
3091     delete pBMP;
3092 
3093     return ret_code;
3094 }
3095 
3096 #include <wx/arrimpl.cpp>
3097 WX_DEFINE_ARRAY_PTR( float*, MyFloatPtrArray );
3098 
3099 //    Read the .000 ENC file and create required Chartbase data structures
CreateHeaderDataFromENC(void)3100 bool s57chart::CreateHeaderDataFromENC( void )
3101 {
3102     if( !InitENCMinimal( m_TempFilePath ) ) {
3103         wxString msg( _T("   Cannot initialize ENC file ") );
3104         msg.Append( m_TempFilePath );
3105         wxLogMessage( msg );
3106 
3107         return false;
3108     }
3109 
3110     OGRFeature *pFeat;
3111     int catcov;
3112     float LatMax, LatMin, LonMax, LonMin;
3113     LatMax = -90.;
3114     LatMin = 90.;
3115     LonMax = -179.;
3116     LonMin = 179.;
3117 
3118     m_pCOVRTablePoints = NULL;
3119     m_pCOVRTable = NULL;
3120 
3121     //  Create arrays to hold geometry objects temporarily
3122     MyFloatPtrArray *pAuxPtrArray = new MyFloatPtrArray;
3123     std::vector<int> auxCntArray, noCovrCntArray;
3124 
3125     MyFloatPtrArray *pNoCovrPtrArray = new MyFloatPtrArray;
3126 
3127     //Get the first M_COVR object
3128     pFeat = GetChartFirstM_COVR( catcov );
3129 
3130     while( pFeat ) {
3131                 //    Get the next M_COVR feature, and create possible additional entries for COVR
3132                 OGRPolygon *poly = (OGRPolygon *) ( pFeat->GetGeometryRef() );
3133                 OGRLinearRing *xring = poly->getExteriorRing();
3134 
3135                 int npt = xring->getNumPoints();
3136 
3137                 float *pf = NULL;
3138 
3139                 if( npt >= 3 ) {
3140                     pf = (float *) malloc( 2 * npt * sizeof(float) );
3141                     float *pfr = pf;
3142 
3143                     for( int i = 0; i < npt; i++ ) {
3144                         OGRPoint p;
3145                         xring->getPoint( i, &p );
3146 
3147                         if( catcov == 1 ) {
3148                             LatMax = fmax(LatMax, p.getY());
3149                             LatMin = fmin(LatMin, p.getY());
3150                             LonMax = fmax(LonMax, p.getX());
3151                             LonMin = fmin(LonMin, p.getX());
3152                         }
3153 
3154                         pfr[0] = p.getY();             // lat
3155                         pfr[1] = p.getX();             // lon
3156 
3157                         pfr += 2;
3158                     }
3159 
3160                     if( catcov == 1 ) {
3161                         pAuxPtrArray->Add( pf );
3162                         auxCntArray.push_back( npt );
3163                     }
3164                     else if( catcov == 2 ){
3165                         pNoCovrPtrArray->Add( pf );
3166                         noCovrCntArray.push_back( npt );
3167                     }
3168                 }
3169 
3170 
3171             delete pFeat;
3172             pFeat = GetChartNextM_COVR( catcov );
3173     }         // while
3174 
3175     //    Allocate the storage
3176 
3177     m_nCOVREntries = auxCntArray.size();
3178 
3179     //    Create new COVR entries
3180 
3181     if( m_nCOVREntries >= 1 ) {
3182         m_pCOVRTablePoints = (int *) malloc( m_nCOVREntries * sizeof(int) );
3183         m_pCOVRTable = (float **) malloc( m_nCOVREntries * sizeof(float *) );
3184 
3185         for( unsigned int j = 0; j < (unsigned int) m_nCOVREntries; j++ ) {
3186             m_pCOVRTablePoints[j] = auxCntArray[j];
3187             m_pCOVRTable[j] = pAuxPtrArray->Item( j );
3188         }
3189     }
3190 
3191     else                                     // strange case, found no CATCOV=1 M_COVR objects
3192     {
3193         wxString msg( _T("   ENC contains no useable M_COVR, CATCOV=1 features:  ") );
3194         msg.Append( m_TempFilePath );
3195         wxLogMessage( msg );
3196     }
3197 
3198 
3199     //      And for the NoCovr regions
3200     m_nNoCOVREntries = noCovrCntArray.size();
3201 
3202     if( m_nNoCOVREntries ) {
3203         //    Create new NoCOVR entries
3204         m_pNoCOVRTablePoints = (int *) malloc( m_nNoCOVREntries * sizeof(int) );
3205         m_pNoCOVRTable = (float **) malloc( m_nNoCOVREntries * sizeof(float *) );
3206 
3207         for( unsigned int j = 0; j < (unsigned int) m_nNoCOVREntries; j++ ) {
3208             m_pNoCOVRTablePoints[j] = noCovrCntArray[j];
3209             m_pNoCOVRTable[j] = pNoCovrPtrArray->Item( j );
3210         }
3211     }
3212     else {
3213         m_pNoCOVRTablePoints = NULL;
3214         m_pNoCOVRTable = NULL;
3215     }
3216 
3217     delete pAuxPtrArray;
3218     delete pNoCovrPtrArray;
3219 
3220 
3221     if( 0 == m_nCOVREntries ) {                        // fallback
3222         wxString msg( _T("   ENC contains no M_COVR features:  ") );
3223         msg.Append( m_TempFilePath );
3224         wxLogMessage( msg );
3225 
3226         msg =  _T("   Calculating Chart Extents as fallback.");
3227         wxLogMessage( msg );
3228 
3229         OGREnvelope Env;
3230 
3231         //    Get the reader
3232         S57Reader *pENCReader = m_pENCDS->GetModule( 0 );
3233 
3234         if( pENCReader->GetExtent( &Env, true ) == OGRERR_NONE ) {
3235 
3236             LatMax = Env.MaxY;
3237             LonMax = Env.MaxX;
3238             LatMin = Env.MinY;
3239             LonMin = Env.MinX;
3240 
3241             m_nCOVREntries = 1;
3242             m_pCOVRTablePoints = (int *) malloc( sizeof(int) );
3243             *m_pCOVRTablePoints = 4;
3244             m_pCOVRTable = (float **) malloc( sizeof(float *) );
3245             float *pf = (float *) malloc( 2 * 4 * sizeof(float) );
3246             *m_pCOVRTable = pf;
3247             float *pfe = pf;
3248 
3249             *pfe++ = LatMax;
3250             *pfe++ = LonMin;
3251 
3252             *pfe++ = LatMax;
3253             *pfe++ = LonMax;
3254 
3255             *pfe++ = LatMin;
3256             *pfe++ = LonMax;
3257 
3258             *pfe++ = LatMin;
3259             *pfe++ = LonMin;
3260 
3261         } else {
3262             wxString msg( _T("   Cannot calculate Extents for ENC:  ") );
3263             msg.Append( m_TempFilePath );
3264             wxLogMessage( msg );
3265 
3266             return false;                     // chart is completely unusable
3267         }
3268     }
3269 
3270     //    Populate the chart's extent structure
3271     m_FullExtent.NLAT = LatMax;
3272     m_FullExtent.SLAT = LatMin;
3273     m_FullExtent.ELON = LonMax;
3274     m_FullExtent.WLON = LonMin;
3275     m_bExtentSet = true;
3276 
3277     //    Set the chart scale
3278     m_Chart_Scale = GetENCScale();
3279 
3280     wxString nice_name;
3281     GetChartNameFromTXT( m_TempFilePath, nice_name );
3282     m_Name = nice_name;
3283 
3284 
3285     return true;
3286 }
3287 
3288 //    Read the .S57 oSENC file (CURRENT_SENC_FORMAT_VERSION >= 200) and create required Chartbase data structures
CreateHeaderDataFromoSENC(void)3289 bool s57chart::CreateHeaderDataFromoSENC( void )
3290 {
3291     bool ret_val = true;
3292 
3293     wxFFileInputStream fpx( m_SENCFileName );
3294     if (!fpx.IsOk()) {
3295         if( !::wxFileExists(m_SENCFileName) ) {
3296             wxString msg( _T("   Cannot open SENC file ") );
3297             msg.Append( m_SENCFileName );
3298             wxLogMessage( msg );
3299 
3300         }
3301         return false;
3302     }
3303 
3304     Osenc senc;
3305     if(senc.ingestHeader( m_SENCFileName ) ){
3306         return false;
3307     }
3308     else{
3309 
3310         // Get Chartbase member elements from the oSENC file records in the header
3311 
3312         // Scale
3313         m_Chart_Scale = senc.getSENCReadScale();
3314 
3315         // Nice Name
3316         m_Name = senc.getReadName();
3317 
3318         // ID
3319         m_ID = senc.getReadID();
3320 
3321         // Extents
3322         Extent &ext = senc.getReadExtent();
3323 
3324         m_FullExtent.ELON = ext.ELON;
3325         m_FullExtent.WLON = ext.WLON;
3326         m_FullExtent.NLAT = ext.NLAT;
3327         m_FullExtent.SLAT = ext.SLAT;
3328         m_bExtentSet = true;
3329 
3330 
3331         //Coverage areas
3332         SENCFloatPtrArray &AuxPtrArray = senc.getSENCReadAuxPointArray();
3333         std::vector<int> &AuxCntArray = senc.getSENCReadAuxPointCountArray();
3334 
3335         m_nCOVREntries = AuxCntArray.size();
3336 
3337         m_pCOVRTablePoints = (int *) malloc( m_nCOVREntries * sizeof(int) );
3338         m_pCOVRTable = (float **) malloc( m_nCOVREntries * sizeof(float *) );
3339 
3340         for( unsigned int j = 0; j < (unsigned int) m_nCOVREntries; j++ ) {
3341             m_pCOVRTablePoints[j] = AuxCntArray[j];
3342             m_pCOVRTable[j] = (float *) malloc( AuxCntArray[j] * 2 * sizeof(float) );
3343             memcpy( m_pCOVRTable[j], AuxPtrArray[j],
3344                     AuxCntArray[j] * 2 * sizeof(float) );
3345         }
3346 
3347         // NoCoverage areas
3348         SENCFloatPtrArray &NoCovrPtrArray = senc.getSENCReadNOCOVRPointArray();
3349         std::vector<int> &NoCovrCntArray = senc.getSENCReadNOCOVRPointCountArray();
3350 
3351         m_nNoCOVREntries = NoCovrCntArray.size();
3352 
3353         if( m_nNoCOVREntries ) {
3354             //    Create new NoCOVR entries
3355             m_pNoCOVRTablePoints = (int *) malloc( m_nNoCOVREntries * sizeof(int) );
3356             m_pNoCOVRTable = (float **) malloc( m_nNoCOVREntries * sizeof(float *) );
3357 
3358             for( unsigned int j = 0; j < (unsigned int) m_nNoCOVREntries; j++ ) {
3359                 int npoints = NoCovrCntArray[j];
3360                 m_pNoCOVRTablePoints[j] = npoints;
3361                 m_pNoCOVRTable[j] = (float *) malloc( npoints * 2 * sizeof(float) );
3362                 memcpy( m_pNoCOVRTable[j], NoCovrPtrArray[j],
3363                         npoints * 2 * sizeof(float) );
3364             }
3365         }
3366 
3367 
3368         //  Misc
3369         m_SE = m_edtn000;
3370         m_datum_str = _T("WGS84");
3371         m_SoundingsDatum = _T("MEAN LOWER LOW WATER");
3372 
3373 
3374         int senc_file_version = senc.getSencReadVersion();
3375 
3376         int last_update = senc.getSENCReadLastUpdate();
3377 
3378         wxString str = senc.getSENCFileCreateDate();
3379         wxDateTime SENCCreateDate;
3380         SENCCreateDate.ParseFormat( str, _T("%Y%m%d"));
3381 
3382         if( SENCCreateDate.IsValid() )
3383             SENCCreateDate.ResetTime();                   // to midnight
3384 
3385          wxString senc_base_edtn = senc.getSENCReadBaseEdition();
3386     }
3387 
3388     return ret_val;
3389 }
3390 
3391 
3392 
3393 //    Read the .S57 SENC file and create required Chartbase data structures
CreateHeaderDataFromSENC(void)3394 bool s57chart::CreateHeaderDataFromSENC( void )
3395 {
3396     if(CURRENT_SENC_FORMAT_VERSION >= 200)
3397         return CreateHeaderDataFromoSENC();
3398 
3399     return false;
3400 
3401  }
3402 
3403 /*    This method returns the smallest chart DEPCNT:VALDCO value which
3404  is greater than or equal to the specified value
3405  */
GetNearestSafeContour(double safe_cnt,double & next_safe_cnt)3406 bool s57chart::GetNearestSafeContour( double safe_cnt, double &next_safe_cnt )
3407 {
3408     int i = 0;
3409     if( NULL != m_pvaldco_array ) {
3410         for( i = 0; i < m_nvaldco; i++ ) {
3411             if( m_pvaldco_array[i] >= safe_cnt ) break;
3412         }
3413 
3414         if( i < m_nvaldco ) next_safe_cnt = m_pvaldco_array[i];
3415         else
3416             next_safe_cnt = (double) 1e6;
3417         return true;
3418     } else {
3419         next_safe_cnt = (double) 1e6;
3420         return false;
3421     }
3422 }
3423 
3424 /*
3425  --------------------------------------------------------------------------
3426  Build a list of "associated" DEPARE and DRGARE objects from a given
3427  object. to be "associated" means to be physically intersecting,
3428  overlapping, or contained within, depending upon the geometry type
3429  of the given object.
3430  --------------------------------------------------------------------------
3431  */
3432 
GetAssociatedObjects(S57Obj * obj)3433 ListOfS57Obj *s57chart::GetAssociatedObjects( S57Obj *obj )
3434 {
3435     int disPrioIdx;
3436     bool gotit;
3437 
3438     ListOfS57Obj *pobj_list = new ListOfS57Obj;
3439     pobj_list->Clear();
3440 
3441     double lat, lon;
3442     fromSM( ( obj->x * obj->x_rate ) + obj->x_origin, ( obj->y * obj->y_rate ) + obj->y_origin,
3443             ref_lat, ref_lon, &lat, &lon );
3444     //    What is the entry object geometry type?
3445 
3446     switch( obj->Primitive_type ){
3447         case GEO_POINT:
3448             //  n.b.  This logic not perfectly right for LINE and AREA features
3449             //  It uses the object reference point for testing, instead of the decomposed
3450             //  line or boundary geometry.  Thus, it may fail on some intersecting relationships.
3451             //  Judged acceptable, in favor of performance implications.
3452             //  DSR
3453         case GEO_LINE:
3454         case GEO_AREA:
3455             ObjRazRules *top;
3456             disPrioIdx = 1;         // PRIO_GROUP1:S57 group 1 filled areas
3457 
3458             gotit = false;
3459             top = razRules[disPrioIdx][3];     // PLAIN_BOUNDARIES
3460             while( top != NULL ) {
3461                 if( top->obj->bIsAssociable ) {
3462                     if( top->obj->BBObj.Contains( lat, lon ) ) {
3463                         if( IsPointInObjArea( lat, lon, 0.0, top->obj ) ) {
3464                             pobj_list->Append( top->obj );
3465                             gotit = true;
3466                             break;
3467                         }
3468                     }
3469                 }
3470 
3471                 ObjRazRules *nxx = top->next;
3472                 top = nxx;
3473             }
3474 
3475             if( !gotit ) {
3476                 top = razRules[disPrioIdx][4];     // SYMBOLIZED_BOUNDARIES
3477                 while( top != NULL ) {
3478                     if( top->obj->bIsAssociable ) {
3479                         if( top->obj->BBObj.Contains( lat, lon ) ) {
3480                             if( IsPointInObjArea( lat, lon, 0.0, top->obj ) ) {
3481                                 pobj_list->Append( top->obj );
3482                                 break;
3483                             }
3484                         }
3485                     }
3486 
3487                     ObjRazRules *nxx = top->next;
3488                     top = nxx;
3489                 }
3490             }
3491 
3492             break;
3493 
3494         default:
3495             break;
3496     }
3497 
3498     return pobj_list;
3499 }
3500 
GetChartNameFromTXT(const wxString & FullPath,wxString & Name)3501 void s57chart::GetChartNameFromTXT( const wxString& FullPath, wxString &Name )
3502 {
3503 
3504     wxFileName fn( FullPath );
3505 
3506     wxString target_name = fn.GetName();
3507     target_name.RemoveLast();
3508 
3509     wxString dir_name = fn.GetPath();
3510 
3511     wxDir dir( dir_name );                                  // The directory containing the file
3512 
3513     wxArrayString FileList;
3514 
3515     dir.GetAllFiles( fn.GetPath(), &FileList );             // list all the files
3516 
3517     //    Iterate on the file list...
3518 
3519     bool found_name = false;
3520     wxString name;
3521     name.Clear();
3522 
3523     for( unsigned int j = 0; j < FileList.GetCount(); j++ ) {
3524         wxFileName file( FileList[j] );
3525         if( ( ( file.GetExt() ).MakeUpper() ) == _T("TXT") ) {
3526             //  Look for the line beginning with the name of the .000 file
3527             wxTextFile text_file( file.GetFullPath() );
3528 
3529             bool file_ok = true;
3530             //  Suppress log messages on bad file reads
3531             {
3532                 wxLogNull logNo;
3533                 if( !text_file.Open() ) {
3534                     if( !text_file.Open(wxConvISO8859_1) )
3535                         file_ok = false;
3536                 }
3537             }
3538 
3539             if( file_ok ) {
3540                 wxString str = text_file.GetFirstLine();
3541                 while( !text_file.Eof() ) {
3542                     if( 0 == target_name.CmpNoCase( str.Mid( 0, target_name.Len() ) ) ) { // found it
3543                         wxString tname = str.AfterFirst( '-' );
3544                         name = tname.AfterFirst( ' ' );
3545                         found_name = true;
3546                         break;
3547                     } else {
3548                         str = text_file.GetNextLine();
3549                     }
3550                 }
3551             } else {
3552                 wxString msg( _T("   Error Reading ENC .TXT file: ") );
3553                 msg.Append( file.GetFullPath() );
3554                 wxLogMessage( msg );
3555             }
3556 
3557             text_file.Close();
3558 
3559             if( found_name ) break;
3560         }
3561     }
3562 
3563     Name = name;
3564 
3565 }
3566 
3567 //---------------------------------------------------------------------------------
3568 //      S57 Database methods
3569 //---------------------------------------------------------------------------------
3570 
3571 //-------------------------------
3572 //
3573 // S57 OBJECT ACCESSOR SECTION
3574 //
3575 //-------------------------------
3576 
getName(OGRFeature * feature)3577 const char *s57chart::getName( OGRFeature *feature )
3578 {
3579     return feature->GetDefnRef()->GetName();
3580 }
3581 
ExtensionCompare(const wxString & first,const wxString & second)3582 static int ExtensionCompare( const wxString& first, const wxString& second )
3583 {
3584     wxFileName fn1( first );
3585     wxFileName fn2( second );
3586     wxString ext1( fn1.GetExt() );
3587     wxString ext2( fn2.GetExt() );
3588 
3589     return ext1.Cmp( ext2 );
3590 }
3591 
3592 
GetUpdateFileArray(const wxFileName file000,wxArrayString * UpFiles,wxDateTime date000,wxString edtn000)3593 int s57chart::GetUpdateFileArray( const wxFileName file000, wxArrayString *UpFiles,
3594                                   wxDateTime date000, wxString edtn000)
3595 {
3596     wxString DirName000 = file000.GetPath( (int) ( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME ) );
3597     wxDir dir( DirName000 );
3598     if(!dir.IsOpened()){
3599         DirName000.Prepend(wxFileName::GetPathSeparator());
3600         DirName000.Prepend(_T("."));
3601         dir.Open(DirName000);
3602         if(!dir.IsOpened()){
3603             return 0;
3604         }
3605     }
3606 
3607     int flags = wxDIR_DEFAULT;
3608 
3609     // Check dir structure
3610     //  We look to see if the directory one level above where the .000 file is located happens to be "perfectly numeric" in name.
3611     //  If so, the dataset is presumed to be organized with each update in its own directory.
3612     //  So, we search for updates from this level, recursing into subdirs.
3613     wxFileName fnDir( DirName000 );
3614     fnDir.RemoveLastDir();
3615     wxString sdir = fnDir.GetPath();
3616     wxFileName fnTest(sdir);
3617     wxString sname = fnTest.GetName();
3618     long tmps;
3619     if(sname.ToLong( &tmps )){
3620         dir.Open(sdir);
3621         DirName000 = sdir;
3622         flags |= wxDIR_DIRS;
3623     }
3624 
3625     wxString ext;
3626     wxArrayString *dummy_array;
3627     int retval = 0;
3628 
3629     if( UpFiles == NULL )
3630         dummy_array = new wxArrayString;
3631     else
3632         dummy_array = UpFiles;
3633 
3634     wxArrayString possibleFiles;
3635     wxDir::GetAllFiles( DirName000, &possibleFiles, wxEmptyString, flags );
3636 
3637     for(unsigned int i=0 ; i < possibleFiles.GetCount() ; i++){
3638         wxString filename(possibleFiles[i]);
3639 
3640         wxFileName file( filename );
3641         ext = file.GetExt();
3642 
3643         long tmp;
3644         //  Files of interest have the same base name is the target .000 cell,
3645         //  and have numeric extension
3646         if( ext.ToLong( &tmp ) && ( file.GetName() == file000.GetName() ) ) {
3647             wxString FileToAdd = filename;
3648 
3649             wxCharBuffer buffer=FileToAdd.ToUTF8();             // Check file namme for convertability
3650 
3651             if( buffer.data() && !filename.IsSameAs( _T("CATALOG.031"), false ) )           // don't process catalogs
3652             {
3653                 //          We must check the update file for validity
3654                 //          1.  Is update field DSID:EDTN  equal to base .000 file DSID:EDTN?
3655                 //          2.  Is update file DSID.ISDT greater than or equal to base .000 file DSID:ISDT
3656 
3657                 wxDateTime umdate;
3658                 wxString sumdate;
3659                 wxString umedtn;
3660                 DDFModule *poModule = new DDFModule();
3661                 if( !poModule->Open( FileToAdd.mb_str() ) ) {
3662                     wxString msg( _T("   s57chart::BuildS57File  Unable to open update file ") );
3663                     msg.Append( FileToAdd );
3664                     wxLogMessage( msg );
3665                 } else {
3666                     poModule->Rewind();
3667 
3668                     //    Read and parse DDFRecord 0 to get some interesting data
3669                     //    n.b. assumes that the required fields will be in Record 0....  Is this always true?
3670 
3671                     DDFRecord *pr = poModule->ReadRecord();                              // Record 0
3672                     //    pr->Dump(stdout);
3673 
3674                     //  Fetch ISDT(Issue Date)
3675                     char *u = NULL;
3676                     if( pr ) {
3677                         u = (char *) ( pr->GetStringSubfield( "DSID", 0, "ISDT", 0 ) );
3678 
3679                         if( u ) {
3680                             if( strlen( u ) ) sumdate = wxString( u, wxConvUTF8 );
3681                         }
3682                     } else {
3683                         wxString msg(
3684                             _T("   s57chart::BuildS57File  DDFRecord 0 does not contain DSID:ISDT in update file ") );
3685                         msg.Append( FileToAdd );
3686                         wxLogMessage( msg );
3687 
3688                         sumdate = _T("20000101");           // backstop, very early, so wont be used
3689                     }
3690 
3691                     umdate.ParseFormat( sumdate, _T("%Y%m%d") );
3692                     if( !umdate.IsValid() ) umdate.ParseFormat( _T("20000101"), _T("%Y%m%d") );
3693 
3694                                      umdate.ResetTime();
3695 
3696                     //    Fetch the EDTN(Edition) field
3697                                      if( pr ) {
3698                                          u = NULL;
3699                                          u = (char *) ( pr->GetStringSubfield( "DSID", 0, "EDTN", 0 ) );
3700                                          if( u ) {
3701                                              if( strlen( u ) ) umedtn = wxString( u, wxConvUTF8 );
3702                                          }
3703                                      } else {
3704                                          wxString msg(
3705                                              _T("   s57chart::BuildS57File  DDFRecord 0 does not contain DSID:EDTN in update file ") );
3706                                          msg.Append( FileToAdd );
3707                                          wxLogMessage( msg );
3708 
3709                                          umedtn = _T("1");                // backstop
3710                                      }
3711                 }
3712 
3713                 delete poModule;
3714 
3715                 if( ( !umdate.IsEarlierThan( date000 ) ) && ( umedtn.IsSameAs( edtn000 ) ) ) // Note polarity on Date compare....
3716                 dummy_array->Add( FileToAdd );                    // Looking for umdate >= m_date000
3717             }
3718         }
3719     }
3720 
3721     //      Sort the candidates
3722     dummy_array->Sort( ExtensionCompare );
3723 
3724     //      Get the update number of the last in the list
3725     if( dummy_array->GetCount() ) {
3726         wxString Last = dummy_array->Last();
3727         wxFileName fnl( Last );
3728         ext = fnl.GetExt();
3729         wxCharBuffer buffer=ext.ToUTF8();
3730         if(buffer.data())
3731             retval = atoi( buffer.data() );
3732     }
3733 
3734     if( UpFiles == NULL ) delete dummy_array;
3735 
3736     return retval;
3737 }
3738 
3739 
ValidateAndCountUpdates(const wxFileName file000,const wxString CopyDir,wxString & LastUpdateDate,bool b_copyfiles)3740 int s57chart::ValidateAndCountUpdates( const wxFileName file000, const wxString CopyDir,
3741         wxString &LastUpdateDate, bool b_copyfiles )
3742 {
3743 
3744     int retval = 0;
3745 
3746     //       wxString DirName000 = file000.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
3747     //       wxDir dir(DirName000);
3748     wxArrayString *UpFiles = new wxArrayString;
3749     retval = GetUpdateFileArray( file000, UpFiles, m_date000, m_edtn000);
3750 
3751     if( UpFiles->GetCount() ) {
3752         //      The s57reader of ogr requires that update set be sequentially complete
3753         //      to perform all the updates.  However, some NOAA ENC distributions are
3754         //      not complete, as apparently some interim updates have been  withdrawn.
3755         //      Example:  as of 20 Dec, 2005, the update set for US5MD11M.000 includes
3756         //      US5MD11M.017, ...018, and ...019.  Updates 001 through 016 are missing.
3757         //
3758         //      Workaround.
3759         //      Create temporary dummy update files to fill out the set before invoking
3760         //      ogr file open/ingest.  Delete after SENC file create finishes.
3761         //      Set starts with .000, which has the effect of copying the base file to the working dir
3762 
3763         bool chain_broken_mssage_shown = false;
3764 
3765         if( b_copyfiles ) {
3766             m_tmpup_array = new wxArrayString;       // save a list of created files for later erase
3767 
3768             for( int iff = 0; iff < retval + 1; iff++ ) {
3769                 wxFileName ufile( m_TempFilePath );
3770                 wxString sext;
3771                 sext.Printf( _T("%03d"), iff );
3772                 ufile.SetExt( sext );
3773 
3774                 //      Create the target update file name
3775                 wxString cp_ufile = CopyDir;
3776                 if( cp_ufile.Last() != ufile.GetPathSeparator() ) cp_ufile.Append(
3777                         ufile.GetPathSeparator() );
3778 
3779                 cp_ufile.Append( ufile.GetFullName() );
3780 
3781                 //      Explicit check for a short update file, possibly left over from a crash...
3782                 int flen = 0;
3783                 if( ufile.FileExists() ) {
3784                     wxFile uf( ufile.GetFullPath() );
3785                     if( uf.IsOpened() ) {
3786                         flen = uf.Length();
3787                         uf.Close();
3788                     }
3789                 }
3790 
3791                 if( ufile.FileExists() && ( flen > 25 ) )        // a valid update file or base file
3792                         {
3793                     //      Copy the valid file to the SENC directory
3794                     bool cpok = wxCopyFile( ufile.GetFullPath(), cp_ufile );
3795                     if( !cpok ) {
3796                         wxString msg( _T("   Cannot copy temporary working ENC file ") );
3797                         msg.Append( ufile.GetFullPath() );
3798                         msg.Append( _T(" to ") );
3799                         msg.Append( cp_ufile );
3800                         wxLogMessage( msg );
3801                     }
3802                 }
3803 
3804                 else {
3805                     // Create a dummy ISO8211 file with no real content
3806                     // Correct this.  We should break the walk, and notify the user  See FS#1406
3807 
3808                     if( !chain_broken_mssage_shown ){
3809                         OCPNMessageBox(NULL,
3810                         _("S57 Cell Update chain incomplete.\nENC features may be incomplete or inaccurate.\nCheck the logfile for details."),
3811                         _("OpenCPN Create SENC Warning"), wxOK | wxICON_EXCLAMATION, 30 );
3812                         chain_broken_mssage_shown = true;
3813                     }
3814 
3815                     wxString msg( _T("WARNING---ENC Update chain incomplete. Substituting NULL update file: "));
3816                     msg += ufile.GetFullName();
3817                     wxLogMessage(msg);
3818                     wxLogMessage(_T("   Subsequent ENC updates may produce errors.") );
3819                     wxLogMessage(_T("   This ENC exchange set should be updated and SENCs rebuilt.") );
3820 
3821                     bool bstat;
3822                     DDFModule *dupdate = new DDFModule;
3823                     dupdate->Initialize( '3', 'L', 'E', '1', '0', "!!!", 3, 4, 4 );
3824                     bstat = !( dupdate->Create( cp_ufile.mb_str() ) == 0 );
3825                     delete dupdate;
3826 
3827                     if( !bstat ) {
3828                         wxString msg( _T("   Error creating dummy update file: ") );
3829                         msg.Append( cp_ufile );
3830                         wxLogMessage( msg );
3831                     }
3832                 }
3833 
3834                 m_tmpup_array->Add( cp_ufile );
3835             }
3836         }
3837 
3838         //      Extract the date field from the last of the update files
3839         //      which is by definition a valid, present update file....
3840 
3841         wxFileName lastfile( m_TempFilePath );
3842         wxString last_sext;
3843         last_sext.Printf( _T("%03d"), retval );
3844         lastfile.SetExt( last_sext );
3845 
3846         bool bSuccess;
3847         DDFModule oUpdateModule;
3848 
3849 //            bSuccess = !(oUpdateModule.Open( m_tmpup_array->Last().mb_str(), TRUE ) == 0);
3850         bSuccess = !( oUpdateModule.Open( lastfile.GetFullPath().mb_str(), TRUE ) == 0 );
3851 
3852         if( bSuccess ) {
3853 //      Get publish/update date
3854             oUpdateModule.Rewind();
3855             DDFRecord *pr = oUpdateModule.ReadRecord();                     // Record 0
3856 
3857             int nSuccess;
3858             char *u = NULL;
3859 
3860             if( pr ) u = (char *) ( pr->GetStringSubfield( "DSID", 0, "ISDT", 0, &nSuccess ) );
3861 
3862             if( u ) {
3863                 if( strlen( u ) ) {
3864                     LastUpdateDate = wxString( u, wxConvUTF8 );
3865                 }
3866             } else {
3867                 wxDateTime now = wxDateTime::Now();
3868                 LastUpdateDate = now.Format( _T("%Y%m%d") );
3869             }
3870         }
3871     }
3872 
3873     delete UpFiles;
3874     return retval;
3875 }
3876 
3877 
GetISDT(void)3878 wxString s57chart::GetISDT( void )
3879 {
3880     if( m_date000.IsValid() ) return m_date000.Format( _T("%Y%m%d") );
3881     else
3882         return _T("Unknown");
3883 }
3884 
GetBaseFileAttr(const wxString & file000)3885 bool s57chart::GetBaseFileAttr( const wxString& file000 )
3886 {
3887     if( !wxFileName::FileExists( file000 ) ) return false;
3888 
3889     wxString FullPath000 = file000;
3890     DDFModule *poModule = new DDFModule();
3891     if( !poModule->Open( FullPath000.mb_str() ) ) {
3892         wxString msg( _T("   s57chart::BuildS57File  Unable to open ") );
3893         msg.Append( FullPath000 );
3894         wxLogMessage( msg );
3895         delete poModule;
3896         return false;
3897     }
3898 
3899     poModule->Rewind();
3900 
3901 //    Read and parse DDFRecord 0 to get some interesting data
3902 //    n.b. assumes that the required fields will be in Record 0....  Is this always true?
3903 
3904     DDFRecord *pr = poModule->ReadRecord();                               // Record 0
3905 //    pr->Dump(stdout);
3906 
3907 //    Fetch the Geo Feature Count, or something like it....
3908     m_nGeoRecords = pr->GetIntSubfield( "DSSI", 0, "NOGR", 0 );
3909     if( !m_nGeoRecords ) {
3910         wxString msg( _T("   s57chart::BuildS57File  DDFRecord 0 does not contain DSSI:NOGR ") );
3911         wxLogMessage( msg );
3912 
3913         m_nGeoRecords = 1;                // backstop
3914     }
3915 
3916 //  Use ISDT(Issue Date) here, which is the same as UADT(Updates Applied) for .000 files
3917     wxString date000;
3918     char *u = (char *) ( pr->GetStringSubfield( "DSID", 0, "ISDT", 0 ) );
3919     if( u ) date000 = wxString( u, wxConvUTF8 );
3920     else {
3921         wxString msg( _T("   s57chart::BuildS57File  DDFRecord 0 does not contain DSID:ISDT ") );
3922         wxLogMessage( msg );
3923 
3924         date000 = _T("20000101");             // backstop, very early, so any new files will update?
3925     }
3926     m_date000.ParseFormat( date000, _T("%Y%m%d") );
3927     if( !m_date000.IsValid() ) m_date000.ParseFormat( _T("20000101"), _T("%Y%m%d") );
3928 
3929     m_date000.ResetTime();
3930 
3931 //    Fetch the EDTN(Edition) field
3932     u = (char *) ( pr->GetStringSubfield( "DSID", 0, "EDTN", 0 ) );
3933     if( u ) m_edtn000 = wxString( u, wxConvUTF8 );
3934     else {
3935         wxString msg( _T("   s57chart::BuildS57File  DDFRecord 0 does not contain DSID:EDTN ") );
3936         wxLogMessage( msg );
3937 
3938         m_edtn000 = _T("1");                // backstop
3939     }
3940 
3941     m_SE = m_edtn000;
3942 
3943 //      Fetch the Native Scale by reading more records until DSPM is found
3944     m_native_scale = 0;
3945     for( ; pr != NULL; pr = poModule->ReadRecord() ) {
3946         if( pr->FindField( "DSPM" ) != NULL ) {
3947             m_native_scale = pr->GetIntSubfield( "DSPM", 0, "CSCL", 0 );
3948             break;
3949         }
3950     }
3951     if( !m_native_scale ) {
3952         wxString msg( _T("   s57chart::BuildS57File  ENC not contain DSPM:CSCL ") );
3953         wxLogMessage( msg );
3954 
3955         m_native_scale = 1000;                // backstop
3956     }
3957 
3958     delete poModule;
3959 
3960     return true;
3961 }
3962 
BuildSENCFile(const wxString & FullPath000,const wxString & SENCFileName,bool b_progress)3963 int s57chart::BuildSENCFile( const wxString& FullPath000, const wxString& SENCFileName, bool b_progress )
3964 {
3965 
3966     //  LOD calculation
3967     double display_pix_per_meter  = g_Platform->GetDisplayDPmm() * 1000;
3968     double meters_per_pixel_max_scale = GetNormalScaleMin(0,g_b_overzoom_x)/display_pix_per_meter;
3969     m_LOD_meters = meters_per_pixel_max_scale * g_SENC_LOD_pixels;
3970 
3971     //  Establish a common reference point for the chart
3972     ref_lat = ( m_FullExtent.NLAT + m_FullExtent.SLAT ) / 2.;
3973     ref_lon = ( m_FullExtent.WLON + m_FullExtent.ELON ) / 2.;
3974 
3975     if(!m_disableBackgroundSENC){
3976         if(g_SencThreadManager){
3977             SENCJobTicket *ticket = new SENCJobTicket();
3978             ticket->m_LOD_meters = m_LOD_meters;
3979             ticket->ref_lat = ref_lat;
3980             ticket->ref_lon = ref_lon;
3981             ticket->m_FullPath000 = FullPath000;
3982             ticket->m_SENCFileName = SENCFileName;
3983             ticket->m_chart = this;
3984 
3985             m_SENCthreadStatus = g_SencThreadManager->ScheduleJob(ticket);
3986             bReadyToRender = true;
3987             return BUILD_SENC_PENDING;
3988 
3989         }
3990         else
3991             return BUILD_SENC_NOK_RETRY;
3992 
3993     }
3994     else{
3995         Osenc senc;
3996 
3997         senc.setRegistrar( g_poRegistrar );
3998         senc.setRefLocn(ref_lat, ref_lon);
3999         senc.SetLODMeters(m_LOD_meters);
4000 
4001         OCPNPlatform::ShowBusySpinner();
4002 
4003         int ret = senc.createSenc200( FullPath000, SENCFileName, b_progress );
4004 
4005         OCPNPlatform::HideBusySpinner();
4006 
4007         if(ret == ERROR_INGESTING000)
4008             return BUILD_SENC_NOK_PERMANENT;
4009         else
4010             return ret;
4011     }
4012 }
4013 
4014 
4015 
4016 
BuildRAZFromSENCFile(const wxString & FullPath)4017 int s57chart::BuildRAZFromSENCFile( const wxString& FullPath )
4018 {
4019     int ret_val = 0;                    // default is OK
4020 
4021     Osenc sencfile;
4022 
4023     // Set up the containers for ingestion results.
4024     // These will be populated by Osenc, and owned by the caller (this).
4025     S57ObjVector Objects;
4026     VE_ElementVector VEs;
4027     VC_ElementVector VCs;
4028 
4029     sencfile.setRefLocn(ref_lat, ref_lon);
4030 
4031     int srv = sencfile.ingest200(FullPath, &Objects, &VEs, &VCs);
4032 
4033     if(srv != SENC_NO_ERROR){
4034         wxLogMessage( sencfile.getLastError() );
4035         //TODO  Clean up here, or massive leaks result
4036         return 1;
4037     }
4038 
4039     //  Get the cell Ref point as recorded in the SENC
4040     Extent ext = sencfile.getReadExtent();
4041 
4042     m_FullExtent.ELON = ext.ELON;
4043     m_FullExtent.WLON = ext.WLON;
4044     m_FullExtent.NLAT = ext.NLAT;
4045     m_FullExtent.SLAT = ext.SLAT;
4046     m_bExtentSet = true;
4047 
4048     ref_lat = (ext.NLAT + ext.SLAT) / 2.;
4049     ref_lon = (ext.ELON + ext.WLON) / 2.;
4050 
4051     //  Process the Edge feature arrays.
4052 
4053     //    Create a hash map of VE_Element pointers as a chart class member
4054     int n_ve_elements = VEs.size();
4055 
4056     double scale = gFrame->GetBestVPScale(this);
4057     int nativescale = GetNativeScale();
4058 
4059     for( int i = 0; i < n_ve_elements; i++ ) {
4060 
4061         VE_Element *vep = VEs.at( i );
4062         if(vep && vep->nCount){
4063             //  Get a bounding box for the edge
4064             double east_max = -1e7; double east_min = 1e7;
4065             double north_max = -1e7; double north_min = 1e7;
4066 
4067             float *vrun = vep->pPoints;
4068             for(size_t i=0 ; i < vep->nCount; i++){
4069                 east_max = wxMax(east_max, *vrun);
4070                 east_min = wxMin(east_min, *vrun);
4071                 vrun++;
4072 
4073                 north_max = wxMax(north_max, *vrun);
4074                 north_min = wxMin(north_min, *vrun);
4075                 vrun++;
4076             }
4077 
4078             double lat1, lon1, lat2, lon2;
4079             fromSM( east_min, north_min, ref_lat, ref_lon, &lat1, &lon1 );
4080             fromSM( east_max, north_max, ref_lat, ref_lon, &lat2, &lon2 );
4081             vep->edgeBBox.Set( lat1, lon1, lat2, lon2);
4082         }
4083 
4084         m_ve_hash[vep->index] = vep;
4085     }
4086 
4087 
4088     //    Create a hash map VC_Element pointers as a chart class member
4089     int n_vc_elements = VCs.size();
4090 
4091     for( int i = 0; i < n_vc_elements; i++ ) {
4092         VC_Element *vcp = VCs.at( i );
4093         m_vc_hash[vcp->index] = vcp;
4094     }
4095 
4096 
4097     VEs.clear();        // destroy contents, no longer needed
4098     VCs.clear();
4099 
4100     //Walk the vector of S57Objs, associating LUPS, instructions, etc...
4101 
4102     for(unsigned int i=0 ; i < Objects.size() ; i++){
4103 
4104         S57Obj *obj = Objects[i];
4105 
4106         //      This is where Simplified or Paper-Type point features are selected
4107         LUPrec *LUP;
4108         LUPname LUP_Name = PAPER_CHART;
4109 
4110         const wxString objnam  = obj->GetAttrValueAsString("OBJNAM");
4111         if (objnam.Len() > 0) {
4112             const wxString fe_name = wxString(obj->FeatureName, wxConvUTF8);
4113             g_pi_manager->SendVectorChartObjectInfo( FullPath, fe_name, objnam, obj->m_lat, obj->m_lon, scale, nativescale );
4114         }
4115         //If there is a localized object name and it actually is different from the object name, send it as well...
4116         const wxString nobjnam  = obj->GetAttrValueAsString("NOBJNM");
4117         if (nobjnam.Len() > 0 && nobjnam != objnam) {
4118             const wxString fe_name = wxString(obj->FeatureName, wxConvUTF8);
4119             g_pi_manager->SendVectorChartObjectInfo( FullPath, fe_name, nobjnam, obj->m_lat, obj->m_lon, scale, nativescale );
4120         }
4121 
4122         switch( obj->Primitive_type ){
4123             case GEO_POINT:
4124             case GEO_META:
4125             case GEO_PRIM:
4126 
4127                 if( PAPER_CHART == ps52plib->m_nSymbolStyle )
4128                     LUP_Name = PAPER_CHART;
4129                 else
4130                     LUP_Name = SIMPLIFIED;
4131 
4132                 break;
4133 
4134             case GEO_LINE:
4135                 LUP_Name = LINES;
4136                 break;
4137 
4138             case GEO_AREA:
4139                 if( PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle )
4140                     LUP_Name = PLAIN_BOUNDARIES;
4141                 else
4142                     LUP_Name = SYMBOLIZED_BOUNDARIES;
4143 
4144                 break;
4145         }
4146 
4147         LUP = ps52plib->S52_LUPLookup( LUP_Name, obj->FeatureName, obj );
4148 
4149         if( NULL == LUP ) {
4150             if( g_bDebugS57 ) {
4151                 wxString msg( obj->FeatureName, wxConvUTF8 );
4152                 msg.Prepend( _T("   Could not find LUP for ") );
4153                 LogMessageOnce( msg );
4154             }
4155             delete obj;
4156             obj = NULL;
4157             Objects[i] = NULL;
4158         } else {
4159             //              Convert LUP to rules set
4160             ps52plib->_LUP2rules( LUP, obj );
4161 
4162             //              Add linked object/LUP to the working set
4163             _insertRules( obj, LUP, this );
4164 
4165             //              Establish Object's Display Category
4166             obj->m_DisplayCat = LUP->DISC;
4167 
4168             //              Establish objects base display priority
4169             obj->m_DPRI = LUP->DPRI - '0';
4170 
4171                 //  Is this a category-movable object?
4172             if( !strncmp(obj->FeatureName, "OBSTRN", 6) ||
4173                     !strncmp(obj->FeatureName, "WRECKS", 6) ||
4174                     !strncmp(obj->FeatureName, "DEPCNT", 6) ||
4175                     !strncmp(obj->FeatureName, "UWTROC", 6) )
4176                 {
4177                     obj->m_bcategory_mutable = true;
4178                 }
4179             else{
4180                 obj->m_bcategory_mutable = false;
4181             }
4182         }
4183 
4184         //      Build/Maintain the ATON floating/rigid arrays
4185         if( obj && (GEO_POINT == obj->Primitive_type) ) {
4186 
4187             // set floating platform
4188             if( ( !strncmp( obj->FeatureName, "LITFLT", 6 ) )
4189                 || ( !strncmp( obj->FeatureName, "LITVES", 6 ) )
4190                 || ( !strncasecmp( obj->FeatureName, "BOY", 3 ) ) ) {
4191                     pFloatingATONArray->Add( obj );
4192             }
4193 
4194             // set rigid platform
4195             if( !strncasecmp( obj->FeatureName, "BCN", 3 ) ) {
4196                 pRigidATONArray->Add( obj );
4197             }
4198 
4199 
4200             //    Mark the object as an ATON
4201             if( ( !strncmp( obj->FeatureName, "LIT", 3 ) )
4202                     || ( !strncmp( obj->FeatureName, "LIGHTS", 6 ) )
4203                     || ( !strncasecmp( obj->FeatureName, "BCN", 3 ) )
4204                     || ( !strncasecmp( obj->FeatureName, "BOY", 3 ) ) ) {
4205                 obj->bIsAton = true;
4206             }
4207         }
4208 
4209     }   // Objects iterator
4210 
4211 
4212     //   Decide on pub date to show
4213 
4214     wxDateTime d000;
4215     d000.ParseFormat( sencfile.getBaseDate(), _T("%Y%m%d") );
4216     if( !d000.IsValid() )
4217         d000.ParseFormat( _T("20000101"), _T("%Y%m%d") );
4218 
4219     wxDateTime updt;
4220     updt.ParseFormat( sencfile.getUpdateDate(), _T("%Y%m%d") );
4221     if( !updt.IsValid() )
4222         updt.ParseFormat( _T("20000101"), _T("%Y%m%d") );
4223 
4224     if(updt.IsLaterThan(d000))
4225         m_PubYear.Printf(_T("%4d"), updt.GetYear());
4226     else
4227         m_PubYear.Printf(_T("%4d"), d000.GetYear());
4228 
4229 
4230 
4231 //    Set some base class values
4232      wxDateTime upd = updt;
4233      if( !upd.IsValid() )
4234          upd.ParseFormat( _T("20000101"), _T("%Y%m%d") );
4235 
4236      upd.ResetTime();
4237      m_EdDate = upd;
4238 
4239      m_SE = sencfile.getSENCReadBaseEdition();
4240 
4241     wxString supdate;
4242     supdate.Printf(_T(" / %d"), sencfile.getSENCReadLastUpdate());
4243     m_SE += supdate;
4244 
4245 
4246     m_datum_str = _T("WGS84");
4247 
4248     m_SoundingsDatum = _T("MEAN LOWER LOW WATER");
4249     m_ID = sencfile.getReadID();
4250     m_Name = sencfile.getReadName();
4251 
4252     ObjRazRules *top;
4253 
4254     AssembleLineGeometry();
4255 
4256     //  Set up the chart context
4257     m_this_chart_context = (chart_context *)calloc( sizeof(chart_context), 1);
4258     m_this_chart_context->chart = this;
4259     m_this_chart_context->vertex_buffer = GetLineVertexBuffer();
4260 
4261     //  Loop and populate all the objects
4262     for( int i = 0; i < PRIO_NUM; ++i ) {
4263         for( int j = 0; j < LUPNAME_NUM; j++ ) {
4264             top = razRules[i][j];
4265             while( top != NULL ) {
4266                 S57Obj *obj = top->obj;
4267                 obj->m_chart_context = m_this_chart_context;
4268                 top = top->next;
4269             }
4270         }
4271     }
4272 
4273 
4274     return ret_val;
4275 }
4276 
4277 
4278 
_insertRules(S57Obj * obj,LUPrec * LUP,s57chart * pOwner)4279 int s57chart::_insertRules( S57Obj *obj, LUPrec *LUP, s57chart *pOwner )
4280 {
4281     ObjRazRules *rzRules = NULL;
4282     int disPrioIdx = 0;
4283     int LUPtypeIdx = 0;
4284 
4285     if( LUP == NULL ) {
4286 //      printf("SEQuencer:_insertRules(): ERROR no rules to insert!!\n");
4287         return 0;
4288     }
4289 
4290     // find display priority index       --talky version
4291     switch( LUP->DPRI ){
4292         case PRIO_NODATA:
4293             disPrioIdx = 0;
4294             break;  // no data fill area pattern
4295         case PRIO_GROUP1:
4296             disPrioIdx = 1;
4297             break;  // S57 group 1 filled areas
4298         case PRIO_AREA_1:
4299             disPrioIdx = 2;
4300             break;  // superimposed areas
4301         case PRIO_AREA_2:
4302             disPrioIdx = 3;
4303             break;  // superimposed areas also water features
4304         case PRIO_SYMB_POINT:
4305             disPrioIdx = 4;
4306             break;  // point symbol also land features
4307         case PRIO_SYMB_LINE:
4308             disPrioIdx = 5;
4309             break;  // line symbol also restricted areas
4310         case PRIO_SYMB_AREA:
4311             disPrioIdx = 6;
4312             break;  // area symbol also traffic areas
4313         case PRIO_ROUTEING:
4314             disPrioIdx = 7;
4315             break;  // routeing lines
4316         case PRIO_HAZARDS:
4317             disPrioIdx = 8;
4318             break;  // hazards
4319         case PRIO_MARINERS:
4320             disPrioIdx = 9;
4321             break;  // VRM & EBL, own ship
4322         default:
4323             printf( "SEQuencer:_insertRules():ERROR no display priority!!!\n" );
4324     }
4325 
4326     // find look up type index
4327     switch( LUP->TNAM ){
4328         case SIMPLIFIED:
4329             LUPtypeIdx = 0;
4330             break; // points
4331         case PAPER_CHART:
4332             LUPtypeIdx = 1;
4333             break; // points
4334         case LINES:
4335             LUPtypeIdx = 2;
4336             break; // lines
4337         case PLAIN_BOUNDARIES:
4338             LUPtypeIdx = 3;
4339             break; // areas
4340         case SYMBOLIZED_BOUNDARIES:
4341             LUPtypeIdx = 4;
4342             break; // areas
4343         default:
4344             printf( "SEQuencer:_insertRules():ERROR no look up type !!!\n" );
4345     }
4346 
4347     // insert rules
4348     rzRules = (ObjRazRules *) malloc( sizeof(ObjRazRules) );
4349     rzRules->obj = obj;
4350     obj->nRef++;                         // Increment reference counter for delete check;
4351     rzRules->LUP = LUP;
4352     rzRules->child = NULL;
4353     rzRules->mps = NULL;
4354 
4355 #if 0
4356     rzRules->next = razRules[disPrioIdx][LUPtypeIdx];
4357     razRules[disPrioIdx][LUPtypeIdx] = rzRules;
4358 #else
4359     // Find the end of the list, and append the object
4360     // This is required to honor the "natural order" priority rules for objects of same Display Priority
4361     ObjRazRules *rNext = NULL;
4362     ObjRazRules *rPrevious = NULL;
4363     if(razRules[disPrioIdx][LUPtypeIdx]){
4364         rPrevious = razRules[disPrioIdx][LUPtypeIdx];
4365         rNext = rPrevious->next;
4366     }
4367     while(rNext){
4368         rPrevious = rNext;
4369         rNext = rPrevious->next;
4370     }
4371 
4372     rzRules->next = NULL;
4373     if(rPrevious)
4374         rPrevious->next = rzRules;
4375     else
4376         razRules[disPrioIdx][LUPtypeIdx] = rzRules;
4377 
4378 #endif
4379 
4380     return 1;
4381 }
4382 
ResetPointBBoxes(const ViewPort & vp_last,const ViewPort & vp_this)4383 void s57chart::ResetPointBBoxes( const ViewPort &vp_last, const ViewPort &vp_this )
4384 {
4385     ObjRazRules *top;
4386     ObjRazRules *nxx;
4387 
4388     double d = vp_last.view_scale_ppm / vp_this.view_scale_ppm;
4389 
4390     for( int i = 0; i < PRIO_NUM; ++i ) {
4391         for( int j = 0; j < 2; ++j ) {
4392             top = razRules[i][j];
4393 
4394             while( top != NULL ) {
4395                 if( !top->obj->geoPtMulti )                      // do not reset multipoints
4396                 {
4397                     if(top->obj->BBObj.GetValid()) { // scale bbobj
4398                         double lat = top->obj->m_lat, lon = top->obj->m_lon;
4399 
4400                         double lat1 = (lat - top->obj->BBObj.GetMinLat()) * d;
4401                         double lat2 = (lat - top->obj->BBObj.GetMaxLat()) * d;
4402 
4403                         double minlon = top->obj->BBObj.GetMinLon();
4404                         double maxlon = top->obj->BBObj.GetMaxLon();
4405 
4406                         double lon1 = (lon - minlon) * d;
4407                         double lon2 = (lon - maxlon) * d;
4408 
4409                         top->obj->BBObj.Set( lat - lat1, lon - lon1,
4410                                              lat - lat2, lon - lon2 );
4411 
4412                         // this method is very close, but errors accumulate
4413                         top->obj->BBObj.Invalidate();
4414                     }
4415                 }
4416 
4417                 nxx = top->next;
4418                 top = nxx;
4419             }
4420         }
4421     }
4422 }
4423 
4424 //      Traverse the ObjRazRules tree, and fill in
4425 //      any Lups/rules not linked on initial chart load.
4426 //      For example, if chart was loaded with PAPER_CHART symbols,
4427 //      locate and load the equivalent SIMPLIFIED symbology.
4428 //      Likewise for PLAIN/SYMBOLIZED boundaries.
4429 //
4430 //      This method is usually called after a chart display style
4431 //      change via the "Options" dialog, to ensure all symbology is
4432 //      present iff needed.
4433 
UpdateLUPs(s57chart * pOwner)4434 void s57chart::UpdateLUPs( s57chart *pOwner )
4435 {
4436     ObjRazRules *top;
4437     ObjRazRules *nxx;
4438     LUPrec *LUP;
4439     for( int i = 0; i < PRIO_NUM; ++i ) {
4440         //  SIMPLIFIED is set, PAPER_CHART is bare
4441         if( ( razRules[i][0] ) && ( NULL == razRules[i][1] ) ) {
4442             m_b2pointLUPS = true;
4443             top = razRules[i][0];
4444 
4445             while( top != NULL ) {
4446                 LUP = ps52plib->S52_LUPLookup( PAPER_CHART, top->obj->FeatureName, top->obj );
4447                 if( LUP ) {
4448                     //  A POINT object can only appear in two places in the table, SIMPLIFIED or PAPER_CHART
4449                     //  although it is allowed for the Display priority to be different for each
4450                     if(top->obj->nRef < 2) {
4451                         ps52plib->_LUP2rules( LUP, top->obj );
4452                         _insertRules( top->obj, LUP, pOwner );
4453                         top->obj->m_DisplayCat = LUP->DISC;
4454                     }
4455                 }
4456 
4457                 nxx = top->next;
4458                 top = nxx;
4459             }
4460         }
4461 
4462         //  PAPER_CHART is set, SIMPLIFIED is bare
4463         if( ( razRules[i][1] ) && ( NULL == razRules[i][0] ) ) {
4464             m_b2pointLUPS = true;
4465             top = razRules[i][1];
4466 
4467             while( top != NULL ) {
4468                 LUP = ps52plib->S52_LUPLookup( SIMPLIFIED, top->obj->FeatureName, top->obj );
4469                 if( LUP ) {
4470                     if(top->obj->nRef < 2) {
4471                         ps52plib->_LUP2rules( LUP, top->obj );
4472                         _insertRules( top->obj, LUP, pOwner );
4473                         top->obj->m_DisplayCat = LUP->DISC;
4474                     }
4475                 }
4476 
4477                 nxx = top->next;
4478                 top = nxx;
4479             }
4480         }
4481 
4482         //  PLAIN_BOUNDARIES is set, SYMBOLIZED_BOUNDARIES is bare
4483         if( ( razRules[i][3] ) && ( NULL == razRules[i][4] ) ) {
4484             m_b2lineLUPS = true;
4485             top = razRules[i][3];
4486 
4487             while( top != NULL ) {
4488                 LUP = ps52plib->S52_LUPLookup( SYMBOLIZED_BOUNDARIES, top->obj->FeatureName,
4489                         top->obj );
4490                 if( LUP ) {
4491                     ps52plib->_LUP2rules( LUP, top->obj );
4492                     _insertRules( top->obj, LUP, pOwner );
4493                     top->obj->m_DisplayCat = LUP->DISC;
4494                 }
4495 
4496                 nxx = top->next;
4497                 top = nxx;
4498             }
4499         }
4500 
4501         //  SYMBOLIZED_BOUNDARIES is set, PLAIN_BOUNDARIES is bare
4502         if( ( razRules[i][4] ) && ( NULL == razRules[i][3] ) ) {
4503             m_b2lineLUPS = true;
4504             top = razRules[i][4];
4505 
4506             while( top != NULL ) {
4507                 LUP = ps52plib->S52_LUPLookup( PLAIN_BOUNDARIES, top->obj->FeatureName, top->obj );
4508                 if( LUP ) {
4509                     ps52plib->_LUP2rules( LUP, top->obj );
4510                     _insertRules( top->obj, LUP, pOwner );
4511                     top->obj->m_DisplayCat = LUP->DISC;
4512                 }
4513 
4514                 nxx = top->next;
4515                 top = nxx;
4516             }
4517         }
4518 
4519         //  Traverse this priority level again,
4520         //  clearing any object CS rules and flags,
4521         //  so that the next render operation will re-evaluate the CS
4522 
4523         for( int j = 0; j < LUPNAME_NUM; j++ ) {
4524             top = razRules[i][j];
4525             while( top != NULL ) {
4526                 top->obj->bCS_Added = 0;
4527                 free_mps( top->mps );
4528                 top->mps = 0;
4529                 if (top->LUP)
4530                     top->obj->m_DisplayCat = top->LUP->DISC;
4531 
4532                 nxx = top->next;
4533                 top = nxx;
4534             }
4535         }
4536 
4537         //  Traverse this priority level again,
4538         //  clearing any object CS rules and flags of any child list,
4539         //  so that the next render operation will re-evaluate the CS
4540 
4541         for( int j = 0; j < LUPNAME_NUM; j++ ) {
4542             top = razRules[i][j];
4543             while( top != NULL ) {
4544                 if( top->child ) {
4545                     ObjRazRules *ctop = top->child;
4546                     while( NULL != ctop ) {
4547                         ctop->obj->bCS_Added = 0;
4548                         free_mps( ctop->mps );
4549                         ctop->mps = 0;
4550 
4551                         if (ctop->LUP)
4552                             ctop->obj->m_DisplayCat = ctop->LUP->DISC;
4553                         ctop = ctop->next;
4554                     }
4555                 }
4556                 nxx = top->next;
4557                 top = nxx;
4558             }
4559         }
4560 
4561     }
4562 
4563     //    Clear the dynamically created Conditional Symbology LUP Array
4564     // This can not be done on a per-chart basis, since the plib services all charts
4565     // TODO really should make the dynamic LUPs belong to the chart class that created them
4566 }
4567 
4568 
GetObjRuleListAtLatLon(float lat,float lon,float select_radius,ViewPort * VPoint,int selection_mask)4569 ListOfObjRazRules *s57chart::GetObjRuleListAtLatLon( float lat, float lon, float select_radius,
4570         ViewPort *VPoint, int selection_mask )
4571 {
4572 
4573     ListOfObjRazRules *ret_ptr = new ListOfObjRazRules;
4574 
4575 //    Iterate thru the razRules array, by object/rule type
4576 
4577     ObjRazRules *top;
4578 
4579     for( int i = 0; i < PRIO_NUM; ++i ) {
4580 
4581         if(selection_mask & MASK_POINT){
4582             // Points by type, array indices [0..1]
4583 
4584             int point_type = ( ps52plib->m_nSymbolStyle == SIMPLIFIED ) ? 0 : 1;
4585             top = razRules[i][point_type];
4586 
4587             while( top != NULL ) {
4588                 if( top->obj->npt == 1 )       // Do not select Multipoint objects (SOUNDG) yet.
4589                         {
4590                     if( ps52plib->ObjectRenderCheck( top, VPoint ) ) {
4591                         if( DoesLatLonSelectObject( lat, lon, select_radius, top->obj ) )
4592                             ret_ptr->Append( top );
4593                     }
4594                 }
4595 
4596                 //    Check the child branch, if any.
4597                 //    This is where Multipoint soundings are captured individually
4598                 if( top->child ) {
4599                     ObjRazRules *child_item = top->child;
4600                     while( child_item != NULL ) {
4601                         if( ps52plib->ObjectRenderCheck( child_item, VPoint ) ) {
4602                             if( DoesLatLonSelectObject( lat, lon, select_radius, child_item->obj ) )
4603                                 ret_ptr->Append( child_item );
4604                         }
4605 
4606                         child_item = child_item->next;
4607                     }
4608                 }
4609 
4610                 top = top->next;
4611             }
4612         }
4613 
4614         if(selection_mask & MASK_AREA){
4615                 // Areas by boundary type, array indices [3..4]
4616 
4617             int area_boundary_type = ( ps52plib->m_nBoundaryStyle == PLAIN_BOUNDARIES ) ? 3 : 4;
4618             top = razRules[i][area_boundary_type];           // Area nnn Boundaries
4619             while( top != NULL ) {
4620                 if( ps52plib->ObjectRenderCheck( top, VPoint ) ) {
4621                     if( DoesLatLonSelectObject( lat, lon, select_radius, top->obj ) ) ret_ptr->Append(
4622                             top );
4623                 }
4624 
4625                 top = top->next;
4626             }         // while
4627         }
4628 
4629         if(selection_mask & MASK_LINE){
4630                 // Finally, lines
4631             top = razRules[i][2];           // Lines
4632 
4633             while( top != NULL ) {
4634                 if( ps52plib->ObjectRenderCheck( top, VPoint ) ) {
4635                     if( DoesLatLonSelectObject( lat, lon, select_radius, top->obj ) ) ret_ptr->Append(
4636                             top );
4637                 }
4638 
4639                 top = top->next;
4640             }
4641         }
4642     }
4643 
4644     return ret_ptr;
4645 }
4646 
DoesLatLonSelectObject(float lat,float lon,float select_radius,S57Obj * obj)4647 bool s57chart::DoesLatLonSelectObject( float lat, float lon, float select_radius, S57Obj *obj )
4648 {
4649     switch( obj->Primitive_type ){
4650         //  For single Point objects, the integral object bounding box contains the lat/lon of the object,
4651         //  possibly expanded by text or symbol rendering
4652         case GEO_POINT: {
4653             if( !obj->BBObj.GetValid() ) return false;
4654 
4655             if( 1 == obj->npt ) {
4656                 //  Special case for LIGHTS
4657                 //  Sector lights have had their BBObj expanded to include the entire drawn sector
4658                 //  This is too big for pick area, can be confusing....
4659                 //  So make a temporary box at the light's lat/lon, with select_radius size
4660                 if( !strncmp( obj->FeatureName, "LIGHTS", 6 ) ) {
4661 
4662                     double sectrTest;
4663                     bool hasSectors = GetDoubleAttr( obj, "SECTR1", sectrTest );
4664                     if(hasSectors){
4665                     double olon, olat;
4666                     fromSM( ( obj->x * obj->x_rate ) + obj->x_origin,
4667                             ( obj->y * obj->y_rate ) + obj->y_origin, ref_lat, ref_lon, &olat,
4668                             &olon );
4669 
4670                     // Double the select radius to adjust for the fact that LIGHTS has
4671                     // a 0x0 BBox to start with, which makes it smaller than all other
4672                     // rendered objects.
4673                     LLBBox sbox;
4674                     sbox.Set(olat, olon, olat, olon);
4675 
4676                         if( sbox.ContainsMarge( lat, lon, select_radius ) )
4677                             return true;
4678                     }
4679                     else if( obj->BBObj.ContainsMarge( lat, lon, select_radius ) )
4680                         return true;
4681 
4682                 }
4683 
4684                 else if( obj->BBObj.ContainsMarge( lat, lon, select_radius ) )
4685                     return true;
4686             }
4687 
4688             //  For MultiPoint objects, make a bounding box from each point's lat/lon
4689             //  and check it
4690             else {
4691                 if( !obj->BBObj.GetValid() ) return false;
4692 
4693                 //  Coarse test first
4694                 if( !obj->BBObj.ContainsMarge( lat, lon, select_radius ) ) return false;
4695                 //  Now decomposed soundings, one by one
4696                 double *pdl = obj->geoPtMulti;
4697                 for( int ip = 0; ip < obj->npt; ip++ ) {
4698                     double lon_point = *pdl++;
4699                     double lat_point = *pdl++;
4700                     LLBBox BB_point;
4701                     BB_point.Set( lat_point, lon_point, lat_point, lon_point );
4702                     if( BB_point.ContainsMarge( lat, lon, select_radius ) ) {
4703 //                                  index = ip;
4704                         return true;
4705                     }
4706                 }
4707             }
4708 
4709             break;
4710         }
4711         case GEO_AREA: {
4712             //  Coarse test first
4713             if( !obj->BBObj.ContainsMarge( lat, lon, select_radius ) ) return false;
4714             else
4715                 return IsPointInObjArea( lat, lon, select_radius, obj );
4716         }
4717 
4718         case GEO_LINE: {
4719             //  Coarse test first
4720             if( !obj->BBObj.ContainsMarge( lat, lon, select_radius ) )
4721                 return false;
4722 
4723             float sel_rad_meters = select_radius * 1852 * 60;       // approximately
4724             double easting, northing;
4725             toSM( lat, lon, ref_lat, ref_lon, &easting, &northing );
4726 
4727             if( obj->geoPt ) {
4728 
4729                 //  Line geometry is carried in SM or CM93 coordinates, so...
4730                 //  make the hit test using SM coordinates, converting from object points to SM using per-object conversion factors.
4731 
4732                 pt *ppt = obj->geoPt;
4733                 int npt = obj->npt;
4734 
4735                 double xr = obj->x_rate;
4736                 double xo = obj->x_origin;
4737                 double yr = obj->y_rate;
4738                 double yo = obj->y_origin;
4739 
4740                 double north0 = ( ppt->y * yr ) + yo;
4741                 double east0 = ( ppt->x * xr ) + xo;
4742                 ppt++;
4743 
4744                 for( int ip = 1; ip < npt; ip++ ) {
4745 
4746                     double north = ( ppt->y * yr ) + yo;
4747                     double east = ( ppt->x * xr ) + xo;
4748 
4749                     //    A slightly less coarse segment bounding box check
4750                     if( northing >= ( fmin(north, north0) - sel_rad_meters ) ) if( northing
4751                         <= ( fmax(north, north0) + sel_rad_meters ) ) if( easting
4752                         >= ( fmin(east, east0) - sel_rad_meters ) ) if( easting
4753                         <= ( fmax(east, east0) + sel_rad_meters ) ) {
4754                         return true;
4755                     }
4756 
4757                     north0 = north;
4758                     east0 = east;
4759                     ppt++;
4760                 }
4761             }
4762             else{                       // in oSENC V2, Array of points is stored in prearranged VBO array.
4763                 if( obj->m_ls_list ){
4764 
4765                     float *ppt;
4766                     unsigned char *vbo_point = (unsigned char *)obj->m_chart_context->chart->GetLineVertexBuffer();
4767                     line_segment_element *ls = obj->m_ls_list;
4768 
4769                     while(ls && vbo_point){
4770                         int nPoints;
4771                         if( (ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV) ){
4772                             ppt = (float *)(vbo_point + ls->pedge->vbo_offset);
4773                             nPoints = ls->pedge->nCount;
4774                         }
4775                         else{
4776                             ppt = (float *)(vbo_point + ls->pcs->vbo_offset);
4777                             nPoints = 2;
4778                         }
4779 
4780                         float north0 = ppt[1];
4781                         float east0 = ppt[0];
4782 
4783                         ppt += 2;
4784 
4785                         for(int ip=0 ; ip < nPoints - 1 ; ip++){
4786 
4787                             float north = ppt[1];
4788                             float east = ppt[0];
4789 
4790                             if( northing >= ( fmin(north, north0) - sel_rad_meters ) )
4791                                 if( northing <= ( fmax(north, north0) + sel_rad_meters ) )
4792                                     if( easting >= ( fmin(east, east0) - sel_rad_meters ) )
4793                                         if( easting <= ( fmax(east, east0) + sel_rad_meters ) ) {
4794                                  return true;
4795                              }
4796 
4797                              north0 = north;
4798                              east0 = east;
4799 
4800                              ppt += 2;
4801                         }
4802 
4803                         ls = ls->next;
4804                     }
4805                 }
4806             }
4807 
4808             break;
4809         }
4810 
4811         case GEO_META:
4812         case GEO_PRIM:
4813 
4814             break;
4815     }
4816 
4817     return false;
4818 }
4819 
GetAttributeDecode(wxString & att,int ival)4820 wxString s57chart::GetAttributeDecode( wxString& att, int ival )
4821 {
4822     wxString ret_val = _T("");
4823 
4824     //  Get the attribute code from the acronym
4825     const char *att_code;
4826 
4827     wxString file( g_csv_locn );
4828     file.Append( _T("/s57attributes.csv") );
4829 
4830     if( !wxFileName::FileExists( file ) ) {
4831         wxString msg( _T("   Could not open ") );
4832         msg.Append( file );
4833         wxLogMessage( msg );
4834 
4835         return ret_val;
4836     }
4837 
4838     att_code = MyCSVGetField( file.mb_str(), "Acronym",                  // match field
4839             att.mb_str(),               // match value
4840             CC_ExactString, "Code" );             // return field
4841 
4842     // Now, get a nice description from s57expectedinput.csv
4843     //  This will have to be a 2-d search, using ID field and Code field
4844 
4845     // Ingest, and get a pointer to the ingested table for "Expected Input" file
4846     wxString ei_file( g_csv_locn );
4847     ei_file.Append( _T("/s57expectedinput.csv") );
4848 
4849     if( !wxFileName::FileExists( ei_file ) ) {
4850         wxString msg( _T("   Could not open ") );
4851         msg.Append( ei_file );
4852         wxLogMessage( msg );
4853 
4854         return ret_val;
4855     }
4856 
4857     CSVTable *psTable = CSVAccess( ei_file.mb_str() );
4858     CSVIngest( ei_file.mb_str() );
4859 
4860     char **papszFields = NULL;
4861     int bSelected = FALSE;
4862 
4863     /* -------------------------------------------------------------------- */
4864     /*      Scan from in-core lines.                                        */
4865     /* -------------------------------------------------------------------- */
4866     int iline = 0;
4867     while( !bSelected && iline + 1 < psTable->nLineCount ) {
4868         iline++;
4869         papszFields = CSVSplitLine( psTable->papszLines[iline] );
4870 
4871         if( !strcmp( papszFields[0], att_code ) ) {
4872             if( atoi( papszFields[1] ) == ival ) {
4873                 ret_val = wxString( papszFields[2], wxConvUTF8 );
4874                 bSelected = TRUE;
4875             }
4876         }
4877 
4878         CSLDestroy( papszFields );
4879     }
4880 
4881     return ret_val;
4882 
4883 }
4884 
4885 //----------------------------------------------------------------------------------
4886 
IsPointInObjArea(float lat,float lon,float select_radius,S57Obj * obj)4887 bool s57chart::IsPointInObjArea( float lat, float lon, float select_radius, S57Obj *obj )
4888 {
4889     bool ret = false;
4890 
4891     if( obj->pPolyTessGeo ) {
4892         if( !obj->pPolyTessGeo->IsOk() )
4893             obj->pPolyTessGeo->BuildDeferredTess();
4894 
4895         PolyTriGroup *ppg = obj->pPolyTessGeo->Get_PolyTriGroup_head();
4896 
4897         TriPrim *pTP = ppg->tri_prim_head;
4898 
4899         MyPoint pvert_list[3];
4900 
4901         //  Polygon geometry is carried in SM coordinates, so...
4902         //  make the hit test thus.
4903         double easting, northing;
4904         toSM( lat, lon, ref_lat, ref_lon, &easting, &northing );
4905 
4906         //  On some chart types (e.g. cm93), the tesseleated coordinates are stored differently.
4907         //  Adjust the pick point (easting/northing) to correspond.
4908         if( !ppg->m_bSMSENC ) {
4909             double y_rate = obj->y_rate;
4910             double y_origin = obj->y_origin;
4911             double x_rate = obj->x_rate;
4912             double x_origin = obj->x_origin;
4913 
4914             double northing_scaled = ( northing - y_origin ) / y_rate;
4915             double easting_scaled = ( easting - x_origin ) / x_rate;
4916             northing = northing_scaled;
4917             easting = easting_scaled;
4918         }
4919 
4920         while( pTP ) {
4921 //  Coarse test
4922             if( pTP->tri_box.Contains( lat, lon ) ) {
4923 
4924                 if(ppg->data_type == DATA_TYPE_DOUBLE) {
4925                     double *p_vertex = pTP->p_vertex;
4926 
4927                     switch( pTP->type ){
4928                         case PTG_TRIANGLE_FAN: {
4929                             for( int it = 0; it < pTP->nVert - 2; it++ ) {
4930                                 pvert_list[0].x = p_vertex[0];
4931                                 pvert_list[0].y = p_vertex[1];
4932 
4933                                 pvert_list[1].x = p_vertex[( it * 2 ) + 2];
4934                                 pvert_list[1].y = p_vertex[( it * 2 ) + 3];
4935 
4936                                 pvert_list[2].x = p_vertex[( it * 2 ) + 4];
4937                                 pvert_list[2].y = p_vertex[( it * 2 ) + 5];
4938 
4939                                 if( G_PtInPolygon( (MyPoint *) pvert_list, 3, easting, northing ) ) {
4940                                     ret = true;
4941                                     break;
4942                                 }
4943                             }
4944                             break;
4945                         }
4946                         case PTG_TRIANGLE_STRIP: {
4947                             for( int it = 0; it < pTP->nVert - 2; it++ ) {
4948                                 pvert_list[0].x = p_vertex[( it * 2 )];
4949                                 pvert_list[0].y = p_vertex[( it * 2 ) + 1];
4950 
4951                                 pvert_list[1].x = p_vertex[( it * 2 ) + 2];
4952                                 pvert_list[1].y = p_vertex[( it * 2 ) + 3];
4953 
4954                                 pvert_list[2].x = p_vertex[( it * 2 ) + 4];
4955                                 pvert_list[2].y = p_vertex[( it * 2 ) + 5];
4956 
4957                                 if( G_PtInPolygon( (MyPoint *) pvert_list, 3, easting, northing ) ) {
4958                                     ret = true;
4959                                     break;
4960                                 }
4961                             }
4962                             break;
4963                         }
4964                         case PTG_TRIANGLES: {
4965                             for( int it = 0; it < pTP->nVert; it += 3 ) {
4966                                 pvert_list[0].x = p_vertex[( it * 2 )];
4967                                 pvert_list[0].y = p_vertex[( it * 2 ) + 1];
4968 
4969                                 pvert_list[1].x = p_vertex[( it * 2 ) + 2];
4970                                 pvert_list[1].y = p_vertex[( it * 2 ) + 3];
4971 
4972                                 pvert_list[2].x = p_vertex[( it * 2 ) + 4];
4973                                 pvert_list[2].y = p_vertex[( it * 2 ) + 5];
4974 
4975                                 if( G_PtInPolygon( (MyPoint *) pvert_list, 3, easting, northing ) ) {
4976                                     ret = true;
4977                                     break;
4978                                 }
4979                             }
4980                             break;
4981                         }
4982                     }
4983                 }
4984                 else if(ppg->data_type == DATA_TYPE_FLOAT) {
4985                     float *p_vertex = (float *)pTP->p_vertex;
4986 
4987                     switch( pTP->type ){
4988                         case PTG_TRIANGLE_FAN: {
4989                             for( int it = 0; it < pTP->nVert - 2; it++ ) {
4990                                 pvert_list[0].x = p_vertex[0];
4991                                 pvert_list[0].y = p_vertex[1];
4992 
4993                                 pvert_list[1].x = p_vertex[( it * 2 ) + 2];
4994                                 pvert_list[1].y = p_vertex[( it * 2 ) + 3];
4995 
4996                                 pvert_list[2].x = p_vertex[( it * 2 ) + 4];
4997                                 pvert_list[2].y = p_vertex[( it * 2 ) + 5];
4998 
4999                                 if( G_PtInPolygon( (MyPoint *) pvert_list, 3, easting, northing ) ) {
5000                                     ret = true;
5001                                     break;
5002                                 }
5003                             }
5004                             break;
5005                         }
5006                         case PTG_TRIANGLE_STRIP: {
5007                             for( int it = 0; it < pTP->nVert - 2; it++ ) {
5008                                 pvert_list[0].x = p_vertex[( it * 2 )];
5009                                 pvert_list[0].y = p_vertex[( it * 2 ) + 1];
5010 
5011                                 pvert_list[1].x = p_vertex[( it * 2 ) + 2];
5012                                 pvert_list[1].y = p_vertex[( it * 2 ) + 3];
5013 
5014                                 pvert_list[2].x = p_vertex[( it * 2 ) + 4];
5015                                 pvert_list[2].y = p_vertex[( it * 2 ) + 5];
5016 
5017                                 if( G_PtInPolygon( (MyPoint *) pvert_list, 3, easting, northing ) ) {
5018                                     ret = true;
5019                                     break;
5020                                 }
5021                             }
5022                             break;
5023                         }
5024                         case PTG_TRIANGLES: {
5025                             for( int it = 0; it < pTP->nVert; it += 3 ) {
5026                                 pvert_list[0].x = p_vertex[( it * 2 )];
5027                                 pvert_list[0].y = p_vertex[( it * 2 ) + 1];
5028 
5029                                 pvert_list[1].x = p_vertex[( it * 2 ) + 2];
5030                                 pvert_list[1].y = p_vertex[( it * 2 ) + 3];
5031 
5032                                 pvert_list[2].x = p_vertex[( it * 2 ) + 4];
5033                                 pvert_list[2].y = p_vertex[( it * 2 ) + 5];
5034 
5035                                 if( G_PtInPolygon( (MyPoint *) pvert_list, 3, easting, northing ) ) {
5036                                     ret = true;
5037                                     break;
5038                                 }
5039                             }
5040                             break;
5041                         }
5042                     }
5043                 }
5044                 else{
5045                     ret = true;         // Unknown data type, accept the entire TriPrim via coarse test.
5046                     break;
5047                 }
5048             }
5049             pTP = pTP->p_next;
5050         }
5051 
5052     }           // if pPolyTessGeo
5053 
5054     return ret;
5055 }
5056 
5057 
GetObjectAttributeValueAsString(S57Obj * obj,int iatt,wxString curAttrName)5058 wxString s57chart::GetObjectAttributeValueAsString( S57Obj *obj, int iatt, wxString curAttrName )
5059 {
5060     wxString value;
5061     S57attVal *pval;
5062 
5063     pval = obj->attVal->Item( iatt );
5064     switch( pval->valType ){
5065         case OGR_STR: {
5066             if( pval->value ) {
5067                 wxString val_str( (char *) ( pval->value ), wxConvUTF8 );
5068                 long ival;
5069                 if( val_str.ToLong( &ival ) ) {
5070                     if( 0 == ival )
5071                         value = _T("Unknown");
5072                     else {
5073                         wxString decode_val = GetAttributeDecode( curAttrName, ival );
5074                         if( !decode_val.IsEmpty() ) {
5075                             value = decode_val;
5076                             wxString iv;
5077                             iv.Printf( _T(" (%d)"), (int) ival );
5078                             value.Append( iv );
5079                         } else
5080                             value.Printf( _T("%d"), (int) ival );
5081                     }
5082                 }
5083 
5084                 else if( val_str.IsEmpty() )
5085                     value = _T("Unknown");
5086 
5087                 else {
5088                     value.Clear();
5089                     wxString value_increment;
5090                     wxStringTokenizer tk( val_str, wxT(",") );
5091                     int iv = 0;
5092                     if( tk.HasMoreTokens() ) {
5093                         while( tk.HasMoreTokens() ) {
5094                             wxString token = tk.GetNextToken();
5095                             long ival;
5096                             if( token.ToLong( &ival ) ) {
5097                                 wxString decode_val = GetAttributeDecode( curAttrName, ival );
5098 
5099                                 value_increment.Printf( _T(" (%d)"), (int) ival );
5100 
5101                                 if( !decode_val.IsEmpty() )
5102                                     value_increment.Prepend(decode_val);
5103 
5104                                 if( iv ) value_increment.Prepend( wxT(", ") );
5105                                 value.Append( value_increment );
5106 
5107                             }
5108                             else{
5109                                 if(iv) value.Append(_T(","));
5110                                 value.Append( token );
5111                             }
5112 
5113                             iv++;
5114                         }
5115                     }
5116                     else
5117                         value.Append( val_str );
5118                 }
5119             } else
5120                 value = _T("[NULL VALUE]");
5121 
5122             break;
5123         }
5124 
5125         case OGR_INT: {
5126             int ival = *( (int *) pval->value );
5127             wxString decode_val = GetAttributeDecode( curAttrName, ival );
5128 
5129             if( !decode_val.IsEmpty() ) {
5130                 value = decode_val;
5131                 wxString iv;
5132                 iv.Printf( _T("(%d)"), ival );
5133                 value.Append( iv );
5134             } else
5135                 value.Printf( _T("(%d)"), ival );
5136 
5137             break;
5138         }
5139         case OGR_INT_LST:
5140             break;
5141 
5142         case OGR_REAL: {
5143             double dval = *( (double *) pval->value );
5144             wxString val_suffix = _T(" m");
5145 
5146             //    As a special case, convert some attribute values to feet.....
5147             if( ( curAttrName == _T("VERCLR") ) || ( curAttrName == _T("VERCCL") ) || ( curAttrName == _T("VERCOP") )
5148                     || ( curAttrName == _T("HEIGHT") ) || ( curAttrName == _T("HORCLR") ) ) {
5149                 switch( ps52plib->m_nDepthUnitDisplay ){
5150                     case 0:                       // feet
5151                     case 2:                       // fathoms
5152                         dval = dval * 3 * 39.37 / 36;              // feet
5153                         val_suffix = _T(" ft");
5154                         break;
5155                     default:
5156                         break;
5157                 }
5158             }
5159 
5160             else if( ( curAttrName == _T("VALSOU") ) || ( curAttrName == _T("DRVAL1") )
5161                     || ( curAttrName == _T("DRVAL2") ) || ( curAttrName == _T("VALDCO") ) ) {
5162                 switch( ps52plib->m_nDepthUnitDisplay ){
5163                     case 0:                       // feet
5164                         dval = dval * 3 * 39.37 / 36;              // feet
5165                         val_suffix = _T(" ft");
5166                         break;
5167                     case 2:                       // fathoms
5168                         dval = dval * 3 * 39.37 / 36;              // fathoms
5169                         dval /= 6.0;
5170                         val_suffix = _T(" fathoms");
5171                         break;
5172                     default:
5173                         break;
5174                 }
5175             }
5176 
5177             else if( curAttrName == _T("SECTR1") ) val_suffix = _T("&deg;");
5178             else if( curAttrName == _T("SECTR2") ) val_suffix = _T("&deg;");
5179             else if( curAttrName == _T("ORIENT") ) val_suffix = _T("&deg;");
5180             else if( curAttrName == _T("VALNMR") ) val_suffix = _T(" Nm");
5181             else if( curAttrName == _T("SIGPER") ) val_suffix = _T("s");
5182             else if( curAttrName == _T("VALACM") ) val_suffix = _T(" Minutes/year");
5183             else if( curAttrName == _T("VALMAG") ) val_suffix = _T("&deg;");
5184             else if( curAttrName == _T("CURVEL") ) val_suffix = _T(" kt");
5185 
5186             if( dval - floor( dval ) < 0.01 ) value.Printf( _T("%2.0f"), dval );
5187             else
5188                 value.Printf( _T("%4.1f"), dval );
5189 
5190             value << val_suffix;
5191 
5192             break;
5193         }
5194 
5195         case OGR_REAL_LST: {
5196             break;
5197         }
5198     }
5199     return value;
5200 }
5201 
GetAttributeValueAsString(S57attVal * pAttrVal,wxString AttrName)5202 wxString s57chart::GetAttributeValueAsString( S57attVal *pAttrVal, wxString AttrName )
5203 {
5204     if(NULL == pAttrVal)
5205         return _T("");
5206 
5207     wxString value;
5208     switch( pAttrVal->valType ){
5209         case OGR_STR: {
5210             if( pAttrVal->value ) {
5211                 wxString val_str( (char *) ( pAttrVal->value ), wxConvUTF8 );
5212                 long ival;
5213                 if( val_str.ToLong( &ival ) ) {
5214                     if( 0 == ival )
5215                         value = _T("Unknown");
5216                     else {
5217                         wxString decode_val = GetAttributeDecode( AttrName, ival );
5218                         if( !decode_val.IsEmpty() ) {
5219                             value = decode_val;
5220                             wxString iv;
5221                             iv.Printf( _T("(%d)"), (int) ival );
5222                             value.Append( iv );
5223                         } else
5224                             value.Printf( _T("%d"), (int) ival );
5225                     }
5226                 }
5227 
5228                 else if( val_str.IsEmpty() ) value = _T("Unknown");
5229 
5230                 else {
5231                     value.Clear();
5232                     wxString value_increment;
5233                     wxStringTokenizer tk( val_str, wxT(",") );
5234                     int iv = 0;
5235                     while( tk.HasMoreTokens() ) {
5236                         wxString token = tk.GetNextToken();
5237                         long ival;
5238                         if( token.ToLong( &ival ) ) {
5239                             wxString decode_val = GetAttributeDecode( AttrName, ival );
5240                             if( !decode_val.IsEmpty() ) value_increment = decode_val;
5241                             else
5242                                 value_increment.Printf( _T(" %d"), (int) ival );
5243 
5244                             if( iv ) value_increment.Prepend( wxT(", ") );
5245                         }
5246                         value.Append( value_increment );
5247 
5248                         iv++;
5249                     }
5250                     value.Append( val_str );
5251                 }
5252             } else
5253                 value = _T("[NULL VALUE]");
5254 
5255             break;
5256         }
5257 
5258         case OGR_INT: {
5259             int ival = *( (int *) pAttrVal->value );
5260             wxString decode_val = GetAttributeDecode( AttrName, ival );
5261 
5262             if( !decode_val.IsEmpty() ) {
5263                 value = decode_val;
5264                 wxString iv;
5265                 iv.Printf( _T("(%d)"), ival );
5266                 value.Append( iv );
5267             } else
5268                 value.Printf( _T("(%d)"), ival );
5269 
5270             break;
5271         }
5272         case OGR_INT_LST:
5273             break;
5274 
5275         case OGR_REAL: {
5276             double dval = *( (double *) pAttrVal->value );
5277             wxString val_suffix = _T(" m");
5278 
5279             //    As a special case, convert some attribute values to feet.....
5280             if( ( AttrName == _T("VERCLR") ) || ( AttrName == _T("VERCCL") ) || ( AttrName == _T("VERCOP") )
5281                 || ( AttrName == _T("HEIGHT") ) || ( AttrName == _T("HORCLR") ) ) {
5282                     switch( ps52plib->m_nDepthUnitDisplay ){
5283                         case 0:                       // feet
5284                         case 2:                       // fathoms
5285                         dval = dval * 3 * 39.37 / 36;              // feet
5286                         val_suffix = _T(" ft");
5287                         break;
5288                         default:
5289                             break;
5290                     }
5291                 }
5292 
5293                 else if( ( AttrName == _T("VALSOU") ) || ( AttrName == _T("DRVAL1") )
5294                     || ( AttrName == _T("DRVAL2") ) ) {
5295                     switch( ps52plib->m_nDepthUnitDisplay ){
5296                         case 0:                       // feet
5297                         dval = dval * 3 * 39.37 / 36;              // feet
5298                         val_suffix = _T(" ft");
5299                         break;
5300                         case 2:                       // fathoms
5301                         dval = dval * 3 * 39.37 / 36;              // fathoms
5302                         dval /= 6.0;
5303                         val_suffix = _T(" fathoms");
5304                         break;
5305                         default:
5306                             break;
5307                     }
5308                 }
5309 
5310                 else if( AttrName == _T("SECTR1") ) val_suffix = _T("&deg;");
5311                 else if( AttrName == _T("SECTR2") ) val_suffix = _T("&deg;");
5312                 else if( AttrName == _T("ORIENT") ) val_suffix = _T("&deg;");
5313                 else if( AttrName == _T("VALNMR") ) val_suffix = _T(" Nm");
5314                 else if( AttrName == _T("SIGPER") ) val_suffix = _T("s");
5315                 else if( AttrName == _T("VALACM") ) val_suffix = _T(" Minutes/year");
5316                 else if( AttrName == _T("VALMAG") ) val_suffix = _T("&deg;");
5317                 else if( AttrName == _T("CURVEL") ) val_suffix = _T(" kt");
5318 
5319                if( dval - floor( dval ) < 0.01 ) value.Printf( _T("%2.0f"), dval );
5320                else
5321                    value.Printf( _T("%4.1f"), dval );
5322 
5323                value << val_suffix;
5324 
5325                break;
5326         }
5327 
5328                         case OGR_REAL_LST: {
5329                             break;
5330                         }
5331     }
5332     return value;
5333 }
5334 
CompareLights(const S57Light * l1,const S57Light * l2)5335 bool s57chart::CompareLights( const S57Light* l1, const S57Light* l2 )
5336 {
5337     int positionDiff = l1->position.Cmp( l2->position );
5338     if( positionDiff != 0 ) return true;
5339 
5340 
5341     int attrIndex1 = l1->attributeNames.Index( _T("SECTR1") );
5342     int attrIndex2 = l2->attributeNames.Index( _T("SECTR1") );
5343 
5344     // This should put Lights without sectors last in the list.
5345     if( attrIndex1 == wxNOT_FOUND && attrIndex2 == wxNOT_FOUND ) return false;
5346     if( attrIndex1 != wxNOT_FOUND && attrIndex2 == wxNOT_FOUND ) return true;
5347     if( attrIndex1 == wxNOT_FOUND && attrIndex2 != wxNOT_FOUND ) return false;
5348 
5349     double angle1, angle2;
5350     l1->attributeValues.Item( attrIndex1 ).ToDouble( &angle1 );
5351     l2->attributeValues.Item( attrIndex2 ).ToDouble( &angle2 );
5352 
5353     return angle1 < angle2;
5354 }
5355 
type2str(GeoPrim_t type)5356 static const char *type2str( GeoPrim_t type)
5357 {
5358     const char *r = "Unknown";
5359     switch(type) {
5360     case GEO_POINT:
5361         return "Point";
5362         break;
5363     case GEO_LINE:
5364         return "Line";
5365         break;
5366     case GEO_AREA:
5367         return "Area";
5368         break;
5369     case GEO_META:
5370         return "Meta";
5371         break;
5372     case GEO_PRIM:
5373         return "Prim";
5374         break;
5375     }
5376     return r;
5377 }
5378 
CreateObjDescriptions(ListOfObjRazRules * rule_list)5379 wxString s57chart::CreateObjDescriptions( ListOfObjRazRules* rule_list )
5380 {
5381     wxString ret_val;
5382     int attrCounter;
5383     wxString curAttrName, value;
5384     bool isLight = false;
5385     wxString className;
5386     wxString classDesc;
5387     wxString classAttributes;
5388     wxString objText;
5389     wxString lightsHtml;
5390     wxString positionString;
5391     std::vector<S57Light*> lights;
5392     S57Light* curLight = nullptr;
5393     wxFileName file ;
5394 
5395     for( ListOfObjRazRules::Node *node = rule_list->GetLast(); node; node = node->GetPrevious() ) {
5396         ObjRazRules *current = node->GetData();
5397         positionString.Clear();
5398         objText.Clear();
5399 
5400         // Soundings have no information, so don't show them
5401         if( 0 == strncmp( current->LUP->OBCL, "SOUND", 5 ) ) continue;
5402 
5403         if( current->obj->Primitive_type == GEO_META ) continue;
5404         if( current->obj->Primitive_type == GEO_PRIM ) continue;
5405 
5406         className = wxString( current->obj->FeatureName, wxConvUTF8 );
5407 
5408         // Lights get grouped together to make display look nicer.
5409         isLight = !strcmp( current->obj->FeatureName, "LIGHTS" );
5410 
5411         //    Get the object's nice description from s57objectclasses.csv
5412         //    using cpl_csv from the gdal library
5413 
5414         const char *name_desc;
5415         if( g_csv_locn.Len() ) {
5416             wxString oc_file( g_csv_locn );
5417             oc_file.Append( _T("/s57objectclasses.csv") );
5418             name_desc = MyCSVGetField( oc_file.mb_str(), "Acronym",                  // match field
5419                     current->obj->FeatureName,            // match value
5420                     CC_ExactString, "ObjectClass" );             // return field
5421         } else
5422             name_desc = "";
5423 
5424         // In case there is no nice description for this object class, use the 6 char class name
5425         if( 0 == strlen( name_desc ) ) {
5426             name_desc = current->obj->FeatureName;
5427             classDesc = wxString( name_desc, wxConvUTF8, 1 );
5428             classDesc << wxString( name_desc + 1, wxConvUTF8 ).MakeLower();
5429         } else {
5430             classDesc = wxString( name_desc, wxConvUTF8 );
5431         }
5432 
5433         //    Show LUP
5434         if( g_bDebugS57 ) {
5435             wxString index;
5436 
5437             classAttributes = _T("");
5438             index.Printf( _T("Feature Index: %d<br>"), current->obj->Index );
5439             classAttributes << index;
5440 
5441             wxString LUPstring;
5442             LUPstring.Printf( _T("LUP RCID:  %d<br>"), current->LUP->RCID );
5443             classAttributes << LUPstring;
5444 
5445             wxString Bbox;
5446             LLBBox bbox = current->obj->BBObj;
5447             Bbox.Printf( _T("Lat/Lon box:  %g %g %g %g<br>"), bbox.GetMinLat(), bbox.GetMaxLat() , bbox.GetMinLon(), bbox.GetMaxLon() );
5448             classAttributes << Bbox;
5449 
5450             wxString Type;
5451             Type.Printf( _T(" Type:  %s<br>"), type2str(current->obj->Primitive_type));
5452             classAttributes << Type;
5453 
5454             LUPstring = _T("    LUP ATTC: ");
5455             if( current->LUP->ATTArray.size() ) LUPstring += wxString(current->LUP->ATTArray[0], wxConvUTF8);
5456             LUPstring += _T("<br>");
5457             classAttributes << LUPstring;
5458 
5459             LUPstring = _T("    LUP INST: ");
5460             if( current->LUP->INST ) LUPstring += *( current->LUP->INST );
5461             LUPstring += _T("<br><br>");
5462             classAttributes << LUPstring;
5463 
5464         }
5465 
5466         if( GEO_POINT == current->obj->Primitive_type ) {
5467             double lon, lat;
5468             fromSM( ( current->obj->x * current->obj->x_rate ) + current->obj->x_origin,
5469                     ( current->obj->y * current->obj->y_rate ) + current->obj->y_origin, ref_lat,
5470                     ref_lon, &lat, &lon );
5471 
5472             if( lon > 180.0 ) lon -= 360.;
5473 
5474             positionString.Clear();
5475             positionString += toSDMM( 1, lat );
5476             positionString << _T(" ");
5477             positionString += toSDMM( 2, lon );
5478 
5479             if( isLight ) {
5480                 curLight = new S57Light;
5481                 curLight->position = positionString;
5482                 curLight->hasSectors = false;
5483                 lights.push_back( curLight );
5484             }
5485         }
5486 
5487         //    Get the Attributes and values, making sure they can be converted from UTF8
5488         if(current->obj->att_array) {
5489             char *curr_att = current->obj->att_array;
5490 
5491             attrCounter = 0;
5492 
5493             wxString attribStr;
5494             int noAttr = 0;
5495             attribStr << _T("<table border=0 cellspacing=0 cellpadding=0>");
5496 
5497             if( g_bDebugS57 ) {
5498                 ret_val << _T("<p>") << classAttributes;
5499             }
5500 
5501             bool inDepthRange = false;
5502 
5503             while( attrCounter < current->obj->n_attr ) {
5504                 //    Attribute name
5505                 curAttrName = wxString(curr_att, wxConvUTF8, 6);
5506                 noAttr++;
5507 
5508                 // Sort out how some kinds of attibutes are displayed to get a more readable look.
5509                 // DEPARE gets just its range. Lights are grouped.
5510 
5511                 if( isLight ) {
5512                     assert( curLight != nullptr);
5513                     curLight->attributeNames.Add( curAttrName );
5514                     if( curAttrName.StartsWith( _T("SECTR") ) ) curLight->hasSectors = true;
5515                 } else {
5516                     if( curAttrName == _T("DRVAL1") ) {
5517                         attribStr << _T("<tr><td><font size=-1>");
5518                         inDepthRange = true;
5519                     } else if( curAttrName == _T("DRVAL2") ) {
5520                         attribStr << _T(" - ");
5521                         inDepthRange = false;
5522                     } else {
5523                         if( inDepthRange ) {
5524                             attribStr << _T("</font></td></tr>\n");
5525                             inDepthRange = false;
5526                         }
5527                         attribStr << _T("<tr><td valign=top><font size=-2>");
5528                         if(curAttrName == _T("catgeo"))
5529                             attribStr << _T("CATGEO");
5530                         else
5531                             attribStr << curAttrName;
5532                         attribStr << _T("</font></td><td>&nbsp;&nbsp;</td><td valign=top><font size=-1>");
5533                     }
5534                 }
5535 
5536                 // What we need to do...
5537                 // Change senc format, instead of (S), (I), etc, use the attribute types fetched from the S57attri...csv file
5538                 // This will be like (E), (L), (I), (F)
5539                 //  will affect lots of other stuff.  look for S57attVal.valType
5540                 // need to do this in creatsencrecord above, and update the senc format.
5541 
5542                 value = GetObjectAttributeValueAsString( current->obj, attrCounter, curAttrName );
5543 
5544                 // If the atribute value is a filename, change the value into a link to that file
5545                 wxString AttrNamesFiles = _T("PICREP,TXTDSC,NTXTDS"); //AttrNames that might have a filename as value
5546                 if ( AttrNamesFiles.Find( curAttrName) != wxNOT_FOUND )
5547                     if ( value.Find(_T(".XML")) == wxNOT_FOUND ){ // Don't show xml files
5548                         file.Assign( GetFullPath() );
5549                         file.Assign( file.GetPath(), value );
5550                         file.Normalize();
5551                         if( file.IsOk() ){
5552                             if( file.Exists() )
5553                                 value = wxString::Format( _T("<a href=\"%s\">%s</a>"), file.GetFullPath(), file.GetFullName() );
5554                             else
5555                                 value = value + _T("&nbsp;&nbsp;<font color=\"red\">[ ") + _("this file is not available") + _T(" ]</font>");
5556                         }
5557                     }
5558                 AttrNamesFiles = _T("DATEND,DATSTA,PEREND,PERSTA"); //AttrNames with date info
5559                 if ( AttrNamesFiles.Find( curAttrName) != wxNOT_FOUND ) {
5560                     bool d = true;
5561                     bool m = true;
5562                     wxString ts = value;
5563 
5564                     ts.Replace(wxT("--"),wxT("0000"));//make a valid year entry if not available
5565                     if( ts.Length() < 5){ //(no month set)
5566                         m = false;
5567                         ts.Append(wxT("01") ); // so we add a fictive month to get a valid date
5568                     }
5569                     if( ts.Length() < 7){ //(no day set)
5570                         d=false;
5571                         ts.Append(wxT("01") ); // so we add a fictive day to get a valid date
5572                     }
5573                     wxString::const_iterator end;
5574                     wxDateTime dt;
5575                     if( dt.ParseFormat( ts, "%Y%m%d", &end ) ){
5576                         ts.Empty();
5577                         if ( m ) ts =  wxDateTime::GetMonthName(dt.GetMonth());
5578                         if ( d ) ts.Append( wxString::Format(wxT(" %d"), dt.GetDay()) );
5579                         if( dt.GetYear()>0 ) ts.Append( wxString::Format(wxT(",  %i"), dt.GetYear() ) );
5580                         if ( curAttrName == _T("PEREND")) ts = _("Period ends: ") + ts + wxT("  (")+ value + wxT(")");
5581                         if ( curAttrName == _T("PERSTA")) ts = _("Period starts: ") + ts + wxT("  (")+ value + wxT(")");
5582                         if ( curAttrName == _T("DATEND")) ts = _("Date ending: ") + ts + wxT("  (")+ value + wxT(")");
5583                         if ( curAttrName == _T("DATSTA")) ts = _("Date starting: ") + ts + wxT("  (")+ value + wxT(")");
5584                         value= ts;
5585                     }
5586                 }
5587                 if ( curAttrName == _T("TS_TSP")){ //Tidal current applet
5588                     wxArrayString as;
5589                     wxString ts, ts1;
5590                     wxStringTokenizer tk(value,  wxT(","));
5591                     ts = tk.GetNextToken(); //we don't show this part (the TT entry number)'
5592                     ts1 = tk.GetNextToken(); //Now has the tidal reference port name'
5593                     ts =  _T("Tidal Streams referred to<br><b>");
5594                     ts.Append(tk.GetNextToken()).Append(_T("</b> at <b>")).Append(ts1);
5595                     ts.Append(/*tk.GetNextToken()).Append(*/_T("</b><br><table >"))  ;
5596                     int i = -6;
5597                     while ( tk.HasMoreTokens() ){ // fill the current table
5598                         ts.Append(_T("<tr><td>")).Append( wxString::Format(wxT("%i"),i)).Append(_T("</td><td>"))
5599                             .Append(tk.GetNextToken()).Append(_T("&#176</td><td>")).Append(tk.GetNextToken()).Append(_T("</td></tr>"));
5600                         i++;
5601                     }
5602                         ts.Append(_T("</table>"));
5603                         value = ts;
5604                 }
5605 
5606                 if( isLight ) {
5607                     assert( curLight != nullptr);
5608                     curLight->attributeValues.Add( value );
5609                 } else {
5610                     if( curAttrName == _T("INFORM") || curAttrName == _T("NINFOM") )
5611                         value.Replace(_T("|"), _T("<br>") );
5612 
5613                     if(curAttrName == _T("catgeo"))
5614                         attribStr << type2str(current->obj->Primitive_type);
5615                     else
5616                         attribStr << value;
5617 
5618                     if( !( curAttrName == _T("DRVAL1") ) ) {
5619                         attribStr << _T("</font></td></tr>\n");
5620                     }
5621                 }
5622 
5623                 attrCounter++;
5624                 curr_att += 6;
5625 
5626             }             //while attrCounter < current->obj->n_attr
5627 
5628             if( !isLight ) {
5629                 attribStr << _T("</table>\n");
5630 
5631                 objText += _T("<b>") + classDesc + _T("</b> <font size=-2>(") + className
5632                         + _T(")</font>") + _T("<br>");
5633 
5634                 if( positionString.Length() ) objText << _T("<font size=-2>") << positionString
5635                         << _T("</font><br>\n");
5636 
5637                 if( noAttr > 0 ) objText << attribStr;
5638 
5639                 if( node != rule_list->GetFirst() ) objText += _T("<hr noshade>");
5640                 objText += _T("<br>");
5641                 ret_val << objText;
5642             }
5643 
5644         }
5645     } // Object for loop
5646 
5647     // Add the additional info files
5648     wxArrayString files;
5649     file.Assign( GetFullPath() );
5650     wxString AddFiles = wxString::Format(_T("<hr noshade><br><b>Additional info files attached to: </b> <font size=-2>%s</font><br><table border=0 cellspacing=0 cellpadding=3>"), file.GetFullName() );
5651     file.Normalize();
5652     file.Assign( file.GetPath(), wxT("") );
5653     wxDir::GetAllFiles( file.GetFullPath(), &files,  wxT("*.TXT"), wxDIR_FILES  );
5654     wxDir::GetAllFiles( file.GetFullPath(), &files,  wxT("*.txt"), wxDIR_FILES  );
5655     if ( files.Count() > 0 )
5656     {
5657         for ( size_t i=0; i < files.Count(); i++){
5658             file.Assign( files.Item(i) );
5659             AddFiles << wxString::Format( _T("<tr><td valign=top><font size=-2><a href=\"%s\">%s</a></font></td><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp</td>"), file.GetFullPath(), file.GetFullName() );
5660             if ( files.Count() > ++i){
5661                 file.Assign( files.Item(i) );
5662                 AddFiles << wxString::Format( _T("<td valign=top><font size=-2><a href=\"%s\">%s</a></font></td>"), file.GetFullPath(), file.GetFullName() );
5663             }
5664         }
5665         ret_val << AddFiles <<_T("</table>");
5666     }
5667 
5668     if( !lights.empty() ) {
5669         assert( curLight != nullptr);
5670 
5671         // For lights we now have all the info gathered but no HTML output yet, now
5672         // run through the data and build a merged table for all lights.
5673 
5674         std::sort(lights.begin(), lights.end(), s57chart::CompareLights);
5675 
5676         wxString lastPos;
5677 
5678         for(auto const& thisLight: lights) {
5679             int attrIndex;
5680 
5681             if( thisLight->position != lastPos ) {
5682 
5683                 lastPos = thisLight->position;
5684 
5685                 if( thisLight != *lights.begin() )
5686                     lightsHtml << _T("</table>\n<hr noshade>\n");
5687 
5688                 lightsHtml << _T("<b>Light</b> <font size=-2>(LIGHTS)</font><br>");
5689                 lightsHtml << _T("<font size=-2>") << thisLight->position << _T("</font><br>\n");
5690 
5691                 if( curLight->hasSectors )
5692                     lightsHtml <<_("<font size=-2>(Sector angles are True Bearings from Seaward)</font><br>");
5693 
5694                 lightsHtml << _T("<table>");
5695             }
5696 
5697             lightsHtml << _T("<tr>");
5698             lightsHtml << _T("<td><font size=-1>");
5699 
5700             wxString colorStr;
5701             attrIndex = thisLight->attributeNames.Index( _T("COLOUR") );
5702             if( attrIndex != wxNOT_FOUND ) {
5703                 wxString color = thisLight->attributeValues.Item( attrIndex );
5704                 if( color == _T("red (3)") )
5705                     colorStr = _T("<table border=0><tr><td bgcolor=red>&nbsp;&nbsp;&nbsp;</td></tr></table> ");
5706                 else if( color == _T("green (4)") )
5707                     colorStr = _T("<table border=0><tr><td bgcolor=green>&nbsp;&nbsp;&nbsp;</td></tr></table> ");
5708                 else if( color == _T("white (1)") )
5709                     colorStr = _T("<table border=0><tr><td bgcolor=white>&nbsp;&nbsp;&nbsp;</td></tr></table> ");
5710                 else if( color == _T("yellow (6)") )
5711                     colorStr = _T("<table border=0><tr><td bgcolor=yellow>&nbsp;&nbsp;&nbsp;</td></tr></table> ");
5712                 else if( color == _T("blue (5)") )
5713                     colorStr = _T("<table border=0><tr><td bgcolor=blue>&nbsp;&nbsp;&nbsp;</td></tr></table> ");
5714                 else
5715                     colorStr = _T("<table border=0><tr><td bgcolor=grey>&nbsp;?&nbsp;</td></tr></table> ");
5716             }
5717 
5718             int visIndex = thisLight->attributeNames.Index( _T("LITVIS") );
5719             if(visIndex != wxNOT_FOUND){
5720                 wxString vis = thisLight->attributeValues.Item( visIndex );
5721                 if(vis.Contains( _T("8"))){
5722                     if( attrIndex != wxNOT_FOUND ) {
5723                         wxString color = thisLight->attributeValues.Item( attrIndex );
5724                         if( color == _T("red (3)") )
5725                             colorStr = _T("<table border=0><tr><td bgcolor=DarkRed>&nbsp;&nbsp;&nbsp;</td></tr></table> ");
5726                         if( color == _T("green (4)") )
5727                             colorStr = _T("<table border=0><tr><td bgcolor=DarkGreen>&nbsp;&nbsp;&nbsp;</td></tr></table> ");
5728                         if( color == _T("white (1)") )
5729                             colorStr = _T("<table border=0><tr><td bgcolor=GoldenRod>&nbsp;&nbsp;&nbsp;</td></tr></table> ");
5730                     }
5731                 }
5732             }
5733 
5734             lightsHtml << colorStr;
5735 
5736             lightsHtml << _T("</font></td><td><font size=-1><nobr><b>");
5737 
5738             attrIndex = thisLight->attributeNames.Index( _T("LITCHR") );
5739             if( attrIndex != wxNOT_FOUND ) {
5740                 wxString character = thisLight->attributeValues[attrIndex];
5741                 lightsHtml << character.BeforeFirst( wxChar( '(' ) ) << _T(" ");
5742             }
5743 
5744             attrIndex = thisLight->attributeNames.Index( _T("SIGGRP") );
5745             if( attrIndex != wxNOT_FOUND ) {
5746                 lightsHtml << thisLight->attributeValues[attrIndex];
5747                 lightsHtml << _T(" ");
5748             }
5749 
5750             attrIndex = thisLight->attributeNames.Index( _T("SIGPER") );
5751             if( attrIndex != wxNOT_FOUND ) {
5752                 lightsHtml << thisLight->attributeValues[attrIndex];
5753                 lightsHtml << _T(" ");
5754             }
5755 
5756             attrIndex = thisLight->attributeNames.Index( _T("HEIGHT") );
5757             if( attrIndex != wxNOT_FOUND ) {
5758                 lightsHtml << thisLight->attributeValues[attrIndex];
5759                 lightsHtml << _T(" ");
5760             }
5761 
5762             attrIndex = thisLight->attributeNames.Index( _T("VALNMR") );
5763             if( attrIndex != wxNOT_FOUND ) {
5764                 lightsHtml << thisLight->attributeValues[attrIndex];
5765                 lightsHtml << _T(" ");
5766             }
5767 
5768             lightsHtml << _T("</b>");
5769 
5770             attrIndex = thisLight->attributeNames.Index( _T("SECTR1") );
5771             if( attrIndex != wxNOT_FOUND ) {
5772                 lightsHtml << _T("(") <<thisLight->attributeValues[attrIndex];
5773                 lightsHtml << _T(" - ");
5774                 attrIndex = thisLight->attributeNames.Index( _T("SECTR2") );
5775                 lightsHtml << thisLight->attributeValues[attrIndex] << _T(") ");
5776             }
5777 
5778             lightsHtml << _T("</nobr>");
5779 
5780             attrIndex = thisLight->attributeNames.Index( _T("CATLIT") );
5781             if( attrIndex != wxNOT_FOUND ) {
5782                 lightsHtml << _T("<nobr>");
5783                 lightsHtml
5784                         << thisLight->attributeValues[attrIndex].BeforeFirst(
5785                                 wxChar( '(' ) );
5786                 lightsHtml << _T("</nobr> ");
5787             }
5788 
5789             attrIndex = thisLight->attributeNames.Index( _T("EXCLIT") );
5790             if( attrIndex != wxNOT_FOUND ) {
5791                 lightsHtml << _T("<nobr>");
5792                 lightsHtml
5793                         << thisLight->attributeValues[attrIndex].BeforeFirst(
5794                                 wxChar( '(' ) );
5795                 lightsHtml << _T("</nobr> ");
5796             }
5797 
5798             attrIndex = thisLight->attributeNames.Index( _T("OBJNAM") );
5799             if( attrIndex != wxNOT_FOUND ) {
5800                 lightsHtml << _T("<br><nobr>");
5801                 lightsHtml << thisLight->attributeValues[attrIndex].Left( 1 ).Upper();
5802                 lightsHtml << thisLight->attributeValues[attrIndex].Mid( 1 );
5803                 lightsHtml << _T("</nobr> ");
5804             }
5805 
5806             lightsHtml << _T("</font></td>");
5807             lightsHtml << _T("</tr>");
5808 
5809             thisLight->attributeNames.Clear();
5810             thisLight->attributeValues.Clear();
5811             delete thisLight;
5812         }
5813         lightsHtml << _T("</table><hr noshade>\n");
5814         ret_val = lightsHtml << ret_val;
5815 
5816         lights.clear();
5817     }
5818 
5819     return ret_val;
5820 }
5821 
5822 //------------------------------------------------------------------------
5823 //
5824 //          S57 ENC (i.e. "raw") DataSet support functions
5825 //          Not bulletproof, so call carefully
5826 //
5827 //------------------------------------------------------------------------
InitENCMinimal(const wxString & FullPath)5828 bool s57chart::InitENCMinimal( const wxString &FullPath )
5829 {
5830     if( NULL == g_poRegistrar ) {
5831         wxLogMessage( _T("   Error: No ClassRegistrar in InitENCMinimal.") );
5832         return false;
5833     }
5834 
5835     m_pENCDS = new OGRS57DataSource;
5836 
5837     m_pENCDS->SetS57Registrar( g_poRegistrar );             ///172
5838 
5839     if( !m_pENCDS->OpenMin( FullPath.mb_str(), TRUE ) )       ///172
5840     return false;
5841 
5842     S57Reader *pENCReader = m_pENCDS->GetModule( 0 );
5843     pENCReader->SetClassBased( g_poRegistrar );
5844 
5845     pENCReader->Ingest();
5846 
5847     return true;
5848 }
5849 
GetChartFirstM_COVR(int & catcov)5850 OGRFeature *s57chart::GetChartFirstM_COVR( int &catcov )
5851 {
5852 //    Get the reader
5853     S57Reader *pENCReader = m_pENCDS->GetModule( 0 );
5854 
5855     if( ( NULL != pENCReader ) && ( NULL != g_poRegistrar ) ) {
5856 
5857 //      Select the proper class
5858         g_poRegistrar->SelectClass( "M_COVR" );
5859 
5860 //      Build a new feature definition for this class
5861         OGRFeatureDefn *poDefn = S57GenerateObjectClassDefn( g_poRegistrar,
5862                 g_poRegistrar->GetOBJL(), pENCReader->GetOptionFlags() );
5863 
5864 //      Add this feature definition to the reader
5865         pENCReader->AddFeatureDefn( poDefn );
5866 
5867 //    Also, add as a Layer to Datasource to ensure proper deletion
5868         m_pENCDS->AddLayer( new OGRS57Layer( m_pENCDS, poDefn, 1 ) );
5869 
5870 //      find this feature
5871         OGRFeature *pobjectDef = pENCReader->ReadNextFeature( poDefn );
5872         if( pobjectDef ) {
5873             //  Fetch the CATCOV attribute
5874             catcov = pobjectDef->GetFieldAsInteger( "CATCOV" );
5875             return pobjectDef;
5876         }
5877 
5878         else {
5879             return NULL;
5880         }
5881     } else
5882         return NULL;
5883 }
5884 
GetChartNextM_COVR(int & catcov)5885 OGRFeature *s57chart::GetChartNextM_COVR( int &catcov )
5886 {
5887     catcov = -1;
5888 
5889 //    Get the reader
5890     S57Reader *pENCReader = m_pENCDS->GetModule( 0 );
5891 
5892 //    Get the Feature Definition, stored in Layer 0
5893     OGRFeatureDefn *poDefn = m_pENCDS->GetLayer( 0 )->GetLayerDefn();
5894 
5895     if( pENCReader ) {
5896         OGRFeature *pobjectDef = pENCReader->ReadNextFeature( poDefn );
5897 
5898         if( pobjectDef ) {
5899             catcov = pobjectDef->GetFieldAsInteger( "CATCOV" );
5900             return pobjectDef;
5901         }
5902 
5903         return NULL;
5904     } else
5905         return NULL;
5906 
5907 }
5908 
GetENCScale(void)5909 int s57chart::GetENCScale( void )
5910 {
5911     if( NULL == m_pENCDS ) return 0;
5912 
5913     //    Assume that chart has been initialized for minimal ENC access
5914     //    which implies that the ENC has been fully ingested, and some
5915     //    interesting values have been extracted thereby.
5916 
5917 //    Get the reader
5918     S57Reader *pENCReader = m_pENCDS->GetModule( 0 );
5919 
5920     if( pENCReader ) return pENCReader->GetCSCL();       ///172
5921     else
5922         return 1;
5923 }
5924 
5925 /************************************************************************/
5926 /*                       OpenCPN_OGRErrorHandler()                      */
5927 /*                       Use Global wxLog Class                         */
5928 /************************************************************************/
5929 
OpenCPN_OGRErrorHandler(CPLErr eErrClass,int nError,const char * pszErrorMsg)5930 void OpenCPN_OGRErrorHandler( CPLErr eErrClass, int nError, const char * pszErrorMsg )
5931 {
5932 
5933 #define ERR_BUF_LEN 2000
5934 
5935     char buf[ERR_BUF_LEN + 1];
5936 
5937     if( eErrClass == CE_Debug ) sprintf( buf, " %s", pszErrorMsg );
5938     else if( eErrClass == CE_Warning ) sprintf( buf, "   Warning %d: %s\n", nError, pszErrorMsg );
5939     else
5940         sprintf( buf, "   ERROR %d: %s\n", nError, pszErrorMsg );
5941 
5942     if( g_bGDAL_Debug  || ( CE_Debug != eErrClass) ) {          // log every warning or error
5943         wxString msg( buf, wxConvUTF8 );
5944         wxLogMessage( msg );
5945     }
5946 
5947     //      Do not simply return on CE_Fatal errors, as we don't want to abort()
5948 
5949     if( eErrClass == CE_Fatal ) {
5950         longjmp( env_ogrf, 1 );                  // jump back to the setjmp() point
5951     }
5952 
5953 }
5954 
5955 //      In GDAL-1.2.0, CSVGetField is not exported.......
5956 //      So, make my own simplified copy
5957 /************************************************************************/
5958 /*                           MyCSVGetField()                            */
5959 /*                                                                      */
5960 /************************************************************************/
5961 
MyCSVGetField(const char * pszFilename,const char * pszKeyFieldName,const char * pszKeyFieldValue,CSVCompareCriteria eCriteria,const char * pszTargetField)5962 const char *MyCSVGetField( const char * pszFilename, const char * pszKeyFieldName,
5963         const char * pszKeyFieldValue, CSVCompareCriteria eCriteria, const char * pszTargetField )
5964 
5965 {
5966     char **papszRecord;
5967     int iTargetField;
5968 
5969     /* -------------------------------------------------------------------- */
5970     /*      Find the correct record.                                        */
5971     /* -------------------------------------------------------------------- */
5972     papszRecord = CSVScanFileByName( pszFilename, pszKeyFieldName, pszKeyFieldValue, eCriteria );
5973 
5974     if( papszRecord == NULL ) return "";
5975 
5976     /* -------------------------------------------------------------------- */
5977     /*      Figure out which field we want out of this.                     */
5978     /* -------------------------------------------------------------------- */
5979     iTargetField = CSVGetFileFieldId( pszFilename, pszTargetField );
5980     if( iTargetField < 0 ) return "";
5981 
5982     if( iTargetField >= CSLCount( papszRecord ) ) return "";
5983 
5984     return ( papszRecord[iTargetField] );
5985 }
5986 
5987 //------------------------------------------------------------------------
5988 //
5989 //          Some s57 Utilities
5990 //          Meant to be called "bare", usually with no class instance.
5991 //
5992 //------------------------------------------------------------------------
5993 
5994 //----------------------------------------------------------------------------------
5995 // Get Chart Extents
5996 //----------------------------------------------------------------------------------
5997 
s57_GetChartExtent(const wxString & FullPath,Extent * pext)5998 bool s57_GetChartExtent( const wxString& FullPath, Extent *pext )
5999 {
6000     //   Fix this  find extents of which?? layer??
6001     /*
6002      OGRS57DataSource *poDS = new OGRS57DataSource;
6003      poDS->Open(pFullPath, TRUE);
6004 
6005      if( poDS == NULL )
6006      return false;
6007 
6008      OGREnvelope Env;
6009      S57Reader   *poReader = poDS->GetModule(0);
6010      poReader->GetExtent(&Env, true);
6011 
6012      pext->NLAT = Env.MaxY;
6013      pext->ELON = Env.MaxX;
6014      pext->SLAT = Env.MinY;
6015      pext->WLON = Env.MinX;
6016 
6017      delete poDS;
6018      */
6019     return false;
6020 }
6021 
s57_DrawExtendedLightSectors(ocpnDC & dc,ViewPort & viewport,std::vector<s57Sector_t> & sectorlegs)6022 void s57_DrawExtendedLightSectors( ocpnDC& dc, ViewPort& viewport, std::vector<s57Sector_t>& sectorlegs ) {
6023     float rangeScale = 0.0;
6024 
6025     if( sectorlegs.size() > 0 ) {
6026         std::vector<int> sectorangles;
6027         for( unsigned int i=0; i<sectorlegs.size(); i++ ) {
6028             if( fabs( sectorlegs[i].sector1 - sectorlegs[i].sector2 ) < 0.3 )
6029                 continue;
6030 
6031             double endx, endy;
6032             ll_gc_ll( sectorlegs[i].pos.m_y, sectorlegs[i].pos.m_x,
6033                     sectorlegs[i].sector1 + 180.0, sectorlegs[i].range,
6034                     &endy, &endx );
6035 
6036             wxPoint end1 = viewport.GetPixFromLL( endy, endx );
6037 
6038             ll_gc_ll( sectorlegs[i].pos.m_y, sectorlegs[i].pos.m_x,
6039                     sectorlegs[i].sector2 + 180.0, sectorlegs[i].range,
6040                     &endy, &endx );
6041 
6042             wxPoint end2 = viewport.GetPixFromLL( endy, endx );
6043 
6044             wxPoint lightPos = viewport.GetPixFromLL( sectorlegs[i].pos.m_y, sectorlegs[i].pos.m_x );
6045 
6046             // Make sure arcs are well inside viewport.
6047             float rangePx = sqrtf( powf( (float) (lightPos.x - end1.x), 2) +
6048                                    powf( (float) (lightPos.y - end1.y), 2) );
6049             rangePx /= 3.0;
6050             if( rangeScale == 0.0 ) {
6051                 rangeScale = 1.0;
6052                 if( rangePx > viewport.pix_height / 3 ) {
6053                     rangeScale *= (viewport.pix_height / 3) / rangePx;
6054                 }
6055             }
6056 
6057             rangePx = rangePx * rangeScale;
6058 
6059             int legOpacity;
6060             wxPen *arcpen = wxThePenList->FindOrCreatePen( sectorlegs[i].color, 12, wxPENSTYLE_SOLID );
6061             arcpen->SetCap( wxCAP_BUTT );
6062             dc.SetPen( *arcpen );
6063 
6064             float angle1, angle2;
6065             angle1 = -(sectorlegs[i].sector2 + 90.0) - viewport.rotation * 180.0 / PI;
6066             angle2 = -(sectorlegs[i].sector1 + 90.0) - viewport.rotation * 180.0 / PI;
6067             if( angle1 > angle2 ) {
6068                 angle2 += 360.0;
6069             }
6070             int lpx = lightPos.x;
6071             int lpy = lightPos.y;
6072             int npoints = 0;
6073             wxPoint arcpoints[150]; // Size relates to "step" below.
6074 
6075             float step = 3.0;
6076             while( (step < 15) && ((rangePx * sin(step * PI / 180.)) < 10) ) step += 2.0; // less points on small arcs
6077 
6078             // Make sure we start and stop exactly on the leg lines.
6079             int narc = ( angle2 - angle1 ) / step;
6080             narc++;
6081             step = ( angle2 - angle1 ) / (float)narc;
6082 
6083             if( sectorlegs[i].isleading && (angle2 - angle1 < 60)  ) {
6084                 wxPoint yellowCone[3];
6085                 yellowCone[0] = lightPos;
6086                 yellowCone[1] = end1;
6087                 yellowCone[2] = end2;
6088                 arcpen = wxThePenList->FindOrCreatePen( wxColor( 0,0,0,0 ), 1, wxPENSTYLE_SOLID );
6089                 dc.SetPen( *arcpen );
6090                 wxColor c = sectorlegs[i].color;
6091                 c.Set( c.Red(), c.Green(), c.Blue(), 0.6*c.Alpha() );
6092                 dc.SetBrush( wxBrush( c ) );
6093                 dc.StrokePolygon( 3, yellowCone, 0, 0 );
6094                 legOpacity = 50;
6095             } else {
6096                 for( float a = angle1; a <= angle2 + 0.1; a += step ) {
6097                     int x = lpx + (int) ( rangePx * cos( a * PI / 180. ) );
6098                     int y = lpy - (int) ( rangePx * sin( a * PI / 180. ) );
6099                     arcpoints[npoints].x = x;
6100                     arcpoints[npoints].y = y;
6101                     npoints++;
6102                 }
6103                 dc.StrokeLines( npoints, arcpoints );
6104                 legOpacity = 128;
6105             }
6106 
6107             arcpen = wxThePenList->FindOrCreatePen( wxColor( 0,0,0,legOpacity ), 1, wxPENSTYLE_SOLID );
6108             dc.SetPen( *arcpen );
6109 
6110             // Only draw each leg line once.
6111 
6112             bool haveAngle1 = false;
6113             bool haveAngle2 = false;
6114             int sec1 = (int)sectorlegs[i].sector1;
6115             int sec2 = (int)sectorlegs[i].sector2;
6116             if(sec1 > 360) sec1 -= 360;
6117             if(sec2 > 360) sec2 -= 360;
6118 
6119             if((sec2 == 360) && (sec1 == 0))  //FS#1437
6120                 continue;
6121 
6122             for( unsigned int j=0; j<sectorangles.size(); j++ ) {
6123 
6124                 if( sectorangles[j] == sec1 ) haveAngle1 = true;
6125                 if( sectorangles[j] == sec2 ) haveAngle2 = true;
6126             }
6127 
6128             if( ! haveAngle1 ) {
6129                 dc.StrokeLine( lightPos, end1 );
6130                 sectorangles.push_back( sec1 );
6131             }
6132 
6133             if( ! haveAngle2 ) {
6134                 dc.StrokeLine( lightPos, end2 );
6135                 sectorangles.push_back( sec2 );
6136             }
6137         }
6138     }
6139 }
6140 
s57_CheckExtendedLightSectors(ChartCanvas * cc,int mx,int my,ViewPort & viewport,std::vector<s57Sector_t> & sectorlegs)6141 bool s57_CheckExtendedLightSectors( ChartCanvas *cc, int mx, int my, ViewPort& viewport, std::vector<s57Sector_t>& sectorlegs )
6142 {
6143     if( !cc )
6144         return false;
6145 
6146     double cursor_lat, cursor_lon;
6147     static float lastLat, lastLon;
6148 
6149     if( !ps52plib || !ps52plib->m_bExtendLightSectors )
6150         return false;
6151 
6152     ChartPlugInWrapper *target_plugin_chart = NULL;
6153     s57chart *Chs57 = NULL;
6154 
6155     ChartBase *target_chart = cc->GetChartAtCursor();
6156     if( target_chart ){
6157         if( (target_chart->GetChartType() == CHART_TYPE_PLUGIN) && (target_chart->GetChartFamily() == CHART_FAMILY_VECTOR) )
6158             target_plugin_chart = dynamic_cast<ChartPlugInWrapper *>(target_chart);
6159         else
6160             Chs57 = dynamic_cast<s57chart*>( target_chart );
6161     }
6162 
6163 
6164     cc->GetCanvasPixPoint ( mx, my, cursor_lat, cursor_lon );
6165 
6166     if( lastLat == cursor_lat && lastLon == cursor_lon ) return false;
6167 
6168     lastLat = cursor_lat;
6169     lastLon = cursor_lon;
6170     bool newSectorsNeedDrawing = false;
6171 
6172     bool bhas_red_green = false;
6173     bool bleading_attribute = false;
6174 
6175     int opacity = 100;
6176     if( cc->GetColorScheme() == GLOBAL_COLOR_SCHEME_DUSK ) opacity = 50;
6177     if( cc->GetColorScheme() == GLOBAL_COLOR_SCHEME_NIGHT) opacity = 20;
6178 
6179     int yOpacity = (float)opacity*1.3; // Matched perception of white/yellow with red/green
6180 
6181     if( target_plugin_chart || Chs57  ) {
6182         // Go get the array of all objects at the cursor lat/lon
6183         float selectRadius = 16 / ( viewport.view_scale_ppm * 1852 * 60 );
6184 
6185         ListOfObjRazRules* rule_list = NULL;
6186         ListOfPI_S57Obj* pi_rule_list = NULL;
6187         if( Chs57 )
6188             rule_list = Chs57->GetObjRuleListAtLatLon( cursor_lat, cursor_lon, selectRadius, &viewport, MASK_POINT );
6189         else if( target_plugin_chart )
6190             pi_rule_list = g_pi_manager->GetPlugInObjRuleListAtLatLon( target_plugin_chart,
6191                                                                        cursor_lat, cursor_lon, selectRadius, viewport );
6192 
6193 
6194         sectorlegs.clear();
6195 
6196         wxPoint2DDouble lightPosD(0,0);
6197         wxPoint2DDouble objPos;
6198 
6199         char *curr_att = NULL;
6200         int n_attr = 0;
6201         wxArrayOfS57attVal *attValArray = NULL;
6202 
6203         ListOfObjRazRules::Node *snode = NULL;
6204         ListOfPI_S57Obj::Node *pnode = NULL;
6205 
6206         if(Chs57 && rule_list)
6207             snode = rule_list->GetLast();
6208         else if( target_plugin_chart && pi_rule_list)
6209             pnode = pi_rule_list->GetLast();
6210 
6211 
6212         while(1) {
6213 
6214             bool is_light = false;
6215             if(Chs57) {
6216                 if(!snode)
6217                     break;
6218 
6219                 ObjRazRules *current = snode->GetData();
6220                 S57Obj* light = current->obj;
6221                 if( !strcmp( light->FeatureName, "LIGHTS" ) ) {
6222                     objPos = wxPoint2DDouble( light->m_lat, light->m_lon );
6223                     curr_att = light->att_array;
6224                     n_attr = light->n_attr;
6225                     attValArray = light->attVal;
6226                     is_light = true;
6227                 }
6228             }
6229             else if( target_plugin_chart ) {
6230                 if(!pnode)
6231                     break;
6232                 PI_S57Obj* light = pnode->GetData();
6233                 if( !strcmp( light->FeatureName, "LIGHTS" ) ) {
6234                     objPos = wxPoint2DDouble( light->m_lat, light->m_lon );
6235                     curr_att = light->att_array;
6236                     n_attr = light->n_attr;
6237                     attValArray = light->attVal;
6238                     is_light = true;
6239                 }
6240             }
6241 
6242 
6243             //  Ready to go
6244             int attrCounter;
6245             double sectr1 = -1;
6246             double sectr2 = -1;
6247             double valnmr = -1;
6248             wxString curAttrName;
6249             wxColor color;
6250 
6251             if( lightPosD.m_x == 0 && lightPosD.m_y == 0.0 )
6252                 lightPosD = objPos;
6253 
6254             if( is_light && (lightPosD == objPos) ) {
6255 
6256                 if( curr_att ) {
6257                     bool bviz = true;
6258 
6259                     attrCounter = 0;
6260                     int noAttr = 0;
6261                     s57Sector_t sector;
6262 
6263                     bleading_attribute = false;
6264 
6265                     while( attrCounter < n_attr ) {
6266                         curAttrName = wxString(curr_att, wxConvUTF8, 6 );
6267                         noAttr++;
6268 
6269                         S57attVal *pAttrVal = NULL;
6270                         if( attValArray ){
6271                             if(Chs57)
6272                                 pAttrVal = attValArray->Item(attrCounter);
6273                             else if( target_plugin_chart )
6274                                 pAttrVal = attValArray->Item(attrCounter);
6275                         }
6276 
6277                         wxString value = s57chart::GetAttributeValueAsString( pAttrVal, curAttrName );
6278 
6279                         if( curAttrName == _T("LITVIS") ){
6280                             if(value.StartsWith(_T("obsc")) )
6281                                 bviz = false;
6282                         }
6283                         if( curAttrName == _T("SECTR1") )value.ToDouble( &sectr1 );
6284                         if( curAttrName == _T("SECTR2") ) value.ToDouble( &sectr2 );
6285                         if( curAttrName == _T("VALNMR") ) value.ToDouble( &valnmr );
6286                         if( curAttrName == _T("COLOUR") ) {
6287                             if( value == _T("red(3)") ) {
6288                                 color = wxColor( 255, 0, 0, opacity );
6289                                 sector.iswhite = false;
6290                                 bhas_red_green = true;
6291                             }
6292 
6293                             if( value == _T("green(4)") ) {
6294                                 color = wxColor( 0, 255, 0, opacity );
6295                                 sector.iswhite = false;
6296                                 bhas_red_green = true;
6297                             }
6298                         }
6299 
6300                         if( curAttrName == _T("EXCLIT") ) {
6301                             if( value.Find( _T("(3)") ) ) valnmr = 1.0;  // Fog lights.
6302                         }
6303 
6304                         if( curAttrName == _T("CATLIT") ){
6305                             if( value.Upper().StartsWith( _T("DIRECT")) || value.Upper().StartsWith(_T("LEAD")) )
6306                                 bleading_attribute = true;
6307                         }
6308 
6309                         attrCounter++;
6310                         curr_att += 6;
6311                     }
6312 
6313                     if( ( sectr1 >= 0 ) && ( sectr2 >= 0 ) ) {
6314                             if( sectr1 > sectr2 ) {             // normalize
6315                                     sectr2 += 360.0;
6316                             }
6317 
6318                         sector.pos.m_x = objPos.m_y;              // lon
6319                         sector.pos.m_y = objPos.m_x;
6320 
6321                         sector.range = (valnmr > 0.0) ? valnmr : 2.5; // Short default range.
6322                         sector.sector1 = sectr1;
6323                         sector.sector2 = sectr2;
6324 
6325                         if(!color.IsOk()){
6326                             color = wxColor( 255, 255, 0, yOpacity );
6327                             sector.iswhite = true;
6328                         }
6329                         sector.color = color;
6330                         sector.isleading = false;           // tentative judgment, check below
6331 
6332                         if( bleading_attribute )
6333                             sector.isleading = true;
6334 
6335                         bool newsector = true;
6336                         for( unsigned int i=0; i<sectorlegs.size(); i++ ) {
6337                             if( sectorlegs[i].pos == sector.pos &&
6338                                 sectorlegs[i].sector1 == sector.sector1 &&
6339                                 sectorlegs[i].sector2 == sector.sector2 ) {
6340                                 newsector = false;
6341                         //  In the case of duplicate sectors, choose the instance with largest range.
6342                             //  This applies to the case where day and night VALNMR are different, and so
6343                             //  makes the vector result independent of the order of day/night light features.
6344                                 sectorlegs[i].range = wxMax(sectorlegs[i].range, sector.range);
6345                             }
6346                         }
6347 
6348                         if(!bviz)
6349                             newsector = false;
6350 
6351                         if((sector.sector2 == 360) && ( sector.sector1 == 0))  //FS#1437
6352                             newsector = false;
6353 
6354                         if( newsector ) {
6355                             sectorlegs.push_back( sector );
6356                             newSectorsNeedDrawing = true;
6357                         }
6358                     }
6359                 }
6360             }
6361 
6362 
6363 
6364 
6365             if(Chs57)
6366                 snode = snode->GetPrevious();
6367             else if( target_plugin_chart )
6368                 pnode = pnode->GetPrevious();
6369 
6370         }               // end of while
6371 
6372         if(rule_list) {
6373             rule_list->Clear();
6374             delete rule_list;
6375         }
6376 
6377         if(pi_rule_list) {
6378             pi_rule_list->Clear();
6379             delete pi_rule_list;
6380         }
6381     }
6382 
6383 #if 0
6384     //  Work with the sector legs vector to identify  and mark "Leading Lights"
6385     int ns = sectorlegs.size();
6386     if( sectorlegs.size() > 0 ) {
6387         for( unsigned int i=0; i<sectorlegs.size(); i++ ) {
6388             if( fabs( sectorlegs[i].sector1 - sectorlegs[i].sector2 ) < 0.5 )
6389                 continue;
6390 
6391             if(((sectorlegs[i].sector2 - sectorlegs[i].sector1) < 15)  && sectorlegs[i].iswhite ) {
6392                 //      Check to see if this sector has a visible range greater than any other white light
6393 
6394                 if( sectorlegs.size() > 1 ) {
6395                     bool bleading = true;
6396                     for( unsigned int j=0; j<sectorlegs.size(); j++ ) {
6397                         if(i == j)
6398                             continue;
6399                         if((sectorlegs[j].iswhite) && (sectorlegs[i].range <= sectorlegs[j].range) ){
6400                             if((sectorlegs[j].sector2 - sectorlegs[j].sector1) >= 15){  // test sector should not be a leading light
6401                                 bleading = false;    // cannot be a sector, since its range is <= another white light
6402                                 break;
6403                             }
6404                         }
6405                     }
6406 
6407                     if(bleading)
6408                         sectorlegs[i].isleading = true;
6409                 }
6410             }
6411             else
6412                 sectorlegs[i].isleading = false;
6413 
6414         }
6415     }
6416 #endif
6417 
6418 //  Work with the sector legs vector to identify  and mark "Leading Lights"
6419 //  Sectors with CATLIT "Leading" or "Directional" attribute set have already been marked
6420     for( unsigned int i=0; i<sectorlegs.size(); i++ ) {
6421 
6422         if(((sectorlegs[i].sector2 - sectorlegs[i].sector1) < 15) ) {
6423             if( sectorlegs[i].iswhite && bhas_red_green )
6424                 sectorlegs[i].isleading = true;
6425         }
6426     }
6427 
6428 
6429     return newSectorsNeedDrawing;
6430 }
6431