1 /***************************************************************************
2  *
3  * Project:  OpenCPN
4  * Purpose:  S57 SENC File Object
5  * Author:   David Register
6  *
7  ***************************************************************************
8  *   Copyright (C) 2015 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 <setjmp.h>
34 
35 #include <wx/wfstream.h>
36 #include <wx/filename.h>
37 #include <wx/progdlg.h>
38 
39 #include "Osenc.h"
40 #include "s52s57.h"
41 #include "s57chart.h"           // for one static method
42 #include "cutil.h"
43 #include "s57RegistrarMgr.h"
44 #include "gdal/cpl_csv.h"
45 #include "chart1.h"             // for fonts
46 #include "ogr_s57.h"
47 #include "gdal/cpl_string.h"
48 
49 #include "mygeom.h"
50 #include "georef.h"
51 #include <mutex>
52 
53 extern s57RegistrarMgr          *m_pRegistrarMan;
54 extern wxString                 g_csv_locn;
55 extern bool                     g_bGDAL_Debug;
56 
57 bool chain_broken_mssage_shown = false;
58 
59 using namespace std;
60 
61 #include <wx/arrimpl.cpp>
62 WX_DEFINE_ARRAY( float*, MyFloatPtrArray );
63 
64 #ifndef __WXMSW__
65 sigjmp_buf env_osenc_ogrf;                    // the context saved by sigsetjmp();
66 #endif
67 
68 std::mutex m;
69 
70 
71 
72 /************************************************************************/
73 /*                       OpenCPN_OGRErrorHandler()                      */
74 /*                       Use Global wxLog Class                         */
75 /************************************************************************/
76 bool g_OsencVerbose;
77 
OpenCPN_OGR_OSENC_ErrorHandler(CPLErr eErrClass,int nError,const char * pszErrorMsg)78 void OpenCPN_OGR_OSENC_ErrorHandler( CPLErr eErrClass, int nError, const char * pszErrorMsg )
79 {
80 
81     #define ERR_BUF_LEN 2000
82 
83     char buf[ERR_BUF_LEN + 1];
84 
85     if( eErrClass == CE_Debug ){
86         if( g_OsencVerbose )
87             sprintf( buf, " %s", pszErrorMsg );
88     }
89     else if( eErrClass == CE_Warning ) sprintf( buf, "   Warning %d: %s\n", nError, pszErrorMsg );
90     else
91         sprintf( buf, "   ERROR %d: %s\n", nError, pszErrorMsg );
92 
93     if( g_bGDAL_Debug  || ( CE_Debug != eErrClass) ) {          // log every warning or error
94         wxString msg( buf, wxConvUTF8 );
95         wxLogMessage( msg );
96     }
97 
98     //      Do not simply return on CE_Fatal errors, as we don't want to abort()
99 
100 #ifndef __WXMSW__
101     if( eErrClass == CE_Fatal ) {
102         siglongjmp( env_osenc_ogrf, 1 );                  // jump back to the setjmp() point
103     }
104 #endif
105 
106 }
107 
108 
109 //--------------------------------------------------------------------------
110 //      Osenc_instreamFile implementation
111 //      A simple file stream implementation based on wxFFileInputStream
112 //--------------------------------------------------------------------------
Osenc_instreamFile()113 Osenc_instreamFile::Osenc_instreamFile()
114 {
115     Init();
116 }
117 
~Osenc_instreamFile()118 Osenc_instreamFile::~Osenc_instreamFile()
119 {
120     delete m_instream;
121 }
122 
Open(const wxString & senc_file_name)123 bool Osenc_instreamFile::Open( const wxString &senc_file_name )
124 {
125     m_instream = new wxFFileInputStream(senc_file_name);
126     return m_instream->IsOk();
127 }
128 
Close()129 void Osenc_instreamFile::Close()
130 {
131 }
132 
Read(void * buffer,size_t size)133 Osenc_instream &Osenc_instreamFile::Read(void *buffer, size_t size)
134 {
135     if(m_instream)
136         m_ok = m_instream->Read(buffer, size).IsOk();
137 
138     return *this;
139 }
140 
141 
IsOk()142 bool Osenc_instreamFile::IsOk()
143 {
144     if(m_instream)
145         m_ok =m_instream->IsOk();
146 
147     return m_ok;
148 }
149 
isAvailable()150 bool Osenc_instreamFile::isAvailable()
151 {
152     return true;
153 }
154 
Shutdown()155 void Osenc_instreamFile::Shutdown()
156 {
157 }
158 
Init()159 void Osenc_instreamFile::Init()
160 {
161     m_instream = NULL;
162     m_ok = false;
163 }
164 
165 
166 //--------------------------------------------------------------------------
167 //      Osenc_outstreamFile implementation
168 //      A simple file stream implementation based on wxFFileOutStream
169 //--------------------------------------------------------------------------
Osenc_outstreamFile()170 Osenc_outstreamFile::Osenc_outstreamFile()
171 {
172     Init();
173 }
174 
~Osenc_outstreamFile()175 Osenc_outstreamFile::~Osenc_outstreamFile()
176 {
177     delete m_outstream;
178 }
179 
Open(const wxString & file)180 bool Osenc_outstreamFile::Open(const wxString &file)
181 {
182     Init();
183     m_outstream = new wxFFileOutputStream(file);
184     if(m_outstream)
185         m_ok= m_outstream->IsOk();
186 
187     return m_ok;
188 }
189 
190 
Close()191 void Osenc_outstreamFile::Close()
192 {
193     if(m_outstream)
194         m_ok = m_outstream->Close();
195 }
196 
Write(const void * buffer,size_t size)197 Osenc_outstream &Osenc_outstreamFile::Write(const void *buffer, size_t size)
198 {
199     if(m_outstream)
200         m_ok = m_outstream->Write(buffer, size).IsOk();
201 
202     return *this;
203 }
204 
205 
IsOk()206 bool Osenc_outstreamFile::IsOk()
207 {
208     if(m_outstream)
209         m_ok =m_outstream->IsOk();
210 
211     return m_ok;
212 }
213 
Init()214 void Osenc_outstreamFile::Init()
215 {
216     m_outstream = NULL;
217     m_ok = false;
218 }
219 
220 
221 //--------------------------------------------------------------------------
222 //      Osenc implementation
223 //--------------------------------------------------------------------------
224 
Osenc()225 Osenc::Osenc()
226 {
227     init();
228 }
229 
~Osenc()230 Osenc::~Osenc()
231 {
232     if(m_bPrivateRegistrar)
233         delete m_poRegistrar;
234 
235     // Free the coverage arrays, if they exist
236     SENCFloatPtrArray &AuxPtrArray = getSENCReadAuxPointArray();
237     std::vector<int> &AuxCntArray = getSENCReadAuxPointCountArray();
238     int nCOVREntries = AuxCntArray.size();
239         for( unsigned int j = 0; j < (unsigned int) nCOVREntries; j++ ) {
240         free(AuxPtrArray[j]);
241     }
242 
243     SENCFloatPtrArray &AuxNoPtrArray = getSENCReadNOCOVRPointArray();
244     std::vector<int> &AuxNoCntArray = getSENCReadNOCOVRPointCountArray();
245     int nNoCOVREntries = AuxNoCntArray.size();
246     for( unsigned int j = 0; j < (unsigned int) nNoCOVREntries; j++ ) {
247         free(AuxNoPtrArray[j]);
248     }
249 
250     free(pBuffer);
251 
252 
253     for( unsigned int j = 0; j < (unsigned int) m_nNoCOVREntries; j++ )
254         free (m_pNoCOVRTable[j]);
255 
256     for( unsigned int j = 0; j < (unsigned int) m_nCOVREntries; j++ )
257         free (m_pCOVRTable[j]);
258 
259     free( m_pCOVRTablePoints );
260     free( m_pCOVRTable );
261     free( m_pNoCOVRTablePoints );
262     free( m_pNoCOVRTable );
263     delete m_UpFiles;
264     CPLPopErrorHandler();
265 
266 
267 }
268 
init(void)269 void Osenc::init( void )
270 {
271     m_LOD_meters = 0;
272     m_poRegistrar = NULL;
273     m_bPrivateRegistrar = false;
274     m_senc_file_read_version = 0;
275     m_ProgDialog = NULL;
276     InitializePersistentBuffer();
277 
278     m_ref_lat = 0;
279     m_ref_lon = 0;
280 
281     m_read_base_edtn = _T("-1");
282 
283     m_nNoCOVREntries = 0;
284     m_nCOVREntries = 0;
285     m_pCOVRTablePoints = NULL;
286     m_pCOVRTable = NULL;
287     m_pNoCOVRTablePoints = NULL;
288     m_pNoCOVRTable = NULL;
289 
290     m_pauxOutstream = NULL;
291     m_pauxInstream = NULL;
292     m_pOutstream = NULL;
293     m_pInstream = NULL;
294     m_UpFiles = nullptr;
295 
296     m_bVerbose = true;
297     g_OsencVerbose = true;
298     m_NoErrDialog = false;
299 
300     //      Insert my local error handler to catch OGR errors,
301     //      Especially CE_Fatal type errors
302     //      Discovered/debugged on US5MD11M.017.  VI 548 geometry deleted
303     CPLPushErrorHandler( OpenCPN_OGR_OSENC_ErrorHandler );
304 
305     lockCR = std::unique_lock<std::mutex>(m, std::defer_lock);
306 
307 }
308 
setVerbose(bool verbose)309 void Osenc::setVerbose(bool verbose )
310 {
311     m_bVerbose = verbose;
312     g_OsencVerbose = verbose;
313 }
314 
ingestHeader(const wxString & senc_file_name)315 int Osenc::ingestHeader(const wxString &senc_file_name)
316 {
317     //  Read oSENC header records, stopping at the first Feature_ID record
318     //  Then check to see if everything is defined as required.
319 
320 
321     int ret_val = SENC_NO_ERROR;                    // default is OK
322 
323     wxFileName fn(senc_file_name);
324 
325     //    Sanity check for existence of file
326 
327 //    int nProg = 0;
328 
329 //     wxString ifs( senc_file_name );
330 //
331 //     wxFFileInputStream fpx_u( ifs );
332 //     if (!fpx_u.IsOk()) {
333 //         return ERROR_SENCFILE_NOT_FOUND;
334 //     }
335 //     wxBufferedInputStream fpx( fpx_u );
336 
337     //    Sanity check for existence of file
338     Osenc_instreamFile fpx;
339     fpx.Open( senc_file_name );
340     if (!fpx.IsOk())
341         return ERROR_SENCFILE_NOT_FOUND;
342 
343 
344     //  For identification purposes, the very first record must be the OSENC Version Number Record
345     OSENC_Record_Base record;
346 
347     fpx.Read(&record, sizeof(OSENC_Record_Base));
348     if(!fpx.IsOk()){
349         return ERROR_SENCFILE_NOT_FOUND;
350     }
351 
352     // Check Record
353     if(HEADER_SENC_VERSION != record.record_type){
354         return ERROR_SENCFILE_NOT_FOUND;
355     }
356 
357     //  This is the correct record type (OSENC Version Number Record), so read it
358     unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
359     if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
360         return ERROR_SENCFILE_NOT_FOUND;
361     }
362     uint16_t *pint = (uint16_t*)buf;
363     m_senc_file_read_version = *pint;
364 
365 
366     //  Read the rest of the records in the header
367     int dun = 0;
368 
369     while( !dun ) {
370         //      Read a record Header
371         OSENC_Record_Base record;
372 
373 //        off = fpx.TellI();
374 
375         fpx.Read(&record, sizeof(OSENC_Record_Base));
376         if(!fpx.IsOk()){
377             dun = 1;
378             break;
379         }
380 
381         // Process Records
382         switch( record.record_type){
383             case HEADER_SENC_VERSION:
384             {
385                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
386                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
387                     dun = 1; break;
388                 }
389                 uint16_t *pint = (uint16_t*)buf;
390                 m_senc_file_read_version = *pint;
391                 break;
392             }
393             case HEADER_CELL_NAME:
394             {
395                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
396                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
397                     dun = 1; break;
398                 }
399                 m_Name = wxString( buf, wxConvUTF8 );
400                 break;
401             }
402             case HEADER_CELL_PUBLISHDATE:
403             {
404                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
405                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
406                     dun = 1; break;
407                 }
408                 break;
409             }
410 
411             case HEADER_CELL_EDITION:
412             {
413                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
414                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
415                     dun = 1; break;
416                 }
417                 uint16_t *pint = (uint16_t*)buf;
418                 m_read_base_edtn.Printf(_T("%d"), *pint);
419                 break;
420             }
421 
422             case HEADER_CELL_UPDATEDATE:
423             {
424                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
425                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
426                     dun = 1; break;
427                 }
428                 break;
429             }
430 
431             case HEADER_CELL_UPDATE:
432             {
433                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
434                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
435                     dun = 1; break;
436                 }
437 
438                 uint16_t *pint = (uint16_t*)buf;
439                 m_read_last_applied_update = *pint;
440                 break;
441             }
442 
443             case HEADER_CELL_NATIVESCALE:
444             {
445                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
446                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
447                     dun = 1; break;
448                 }
449                 uint32_t *pint = (uint32_t*)buf;
450                 m_Chart_Scale = *pint;
451 
452                 break;
453             }
454 
455             case HEADER_CELL_SENCCREATEDATE:
456             {
457                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
458                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
459                     dun = 1; break;
460                 }
461                 m_readFileCreateDate = wxString( buf, wxConvUTF8 );
462 
463                 break;
464             }
465 
466             case CELL_EXTENT_RECORD:
467             {
468                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
469                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
470                     dun = 1; break;
471                 }
472                 _OSENC_EXTENT_Record_Payload *pPayload = (_OSENC_EXTENT_Record_Payload *)buf;
473                 m_extent.NLAT = pPayload->extent_nw_lat;
474                 m_extent.SLAT = pPayload->extent_se_lat;
475                 m_extent.WLON = pPayload->extent_nw_lon;
476                 m_extent.ELON = pPayload->extent_se_lon;
477 
478                 break;
479             }
480 
481             case CELL_COVR_RECORD:
482             {
483                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
484                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
485                     dun = 1; break;
486                 }
487 
488                 _OSENC_COVR_Record_Payload *pPayload = (_OSENC_COVR_Record_Payload*)buf;
489 
490                 int point_count = pPayload->point_count;
491                 m_AuxCntArray.push_back(point_count);
492 
493                 float *pf = (float *)malloc(point_count * 2 * sizeof(float));
494                 memcpy(pf, &pPayload->point_array, point_count * 2 * sizeof(float));
495                 m_AuxPtrArray.Add(pf);
496 
497                 break;
498             }
499 
500             case CELL_NOCOVR_RECORD:
501             {
502                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
503                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
504                     dun = 1; break;
505                 }
506 
507                 _OSENC_NOCOVR_Record_Payload *pPayload = (_OSENC_NOCOVR_Record_Payload*)buf;
508 
509                 int point_count = pPayload->point_count;
510                 m_NoCovrCntArray.push_back(point_count);
511 
512                 float *pf = (float *)malloc(point_count * 2 * sizeof(float));
513                 memcpy(pf, &pPayload->point_array, point_count * 2 * sizeof(float));
514                 m_NoCovrPtrArray.Add(pf);
515 
516 
517                 break;
518             }
519 
520             case FEATURE_ID_RECORD:
521             {
522                 dun = 1;
523                 break;
524             }
525 
526             default:
527             {
528                 dun = 1;
529                 break;
530             }
531 
532         } // switch
533 
534     }   // while
535 
536     return ret_val;
537 }
538 
GetFeatureAcronymFromTypecode(int typeCode)539 std::string Osenc::GetFeatureAcronymFromTypecode( int typeCode)
540 {
541     if(m_pRegistrarMan){
542         std::string  acronym = m_pRegistrarMan->getFeatureAcronym(typeCode);
543         return acronym.c_str();
544     }
545     else
546         return "";
547 }
548 
GetAttributeAcronymFromTypecode(int typeCode)549 std::string Osenc::GetAttributeAcronymFromTypecode( int typeCode)
550 {
551     if(m_pRegistrarMan)
552         return m_pRegistrarMan->getAttributeAcronym(typeCode);
553     else
554         return "";
555 }
556 
557 
ingest200(const wxString & senc_file_name,S57ObjVector * pObjectVector,VE_ElementVector * pVEArray,VC_ElementVector * pVCArray)558 int Osenc::ingest200(const wxString &senc_file_name,
559                   S57ObjVector *pObjectVector,
560                   VE_ElementVector *pVEArray,
561                   VC_ElementVector *pVCArray)
562 {
563 
564     int ret_val = SENC_NO_ERROR;                    // default is OK
565 
566 //    wxFileName fn(senc_file_name);
567 //    m_ID = fn.GetName();                          // This will be the NOAA File name, usually
568 
569 
570 //    int nProg = 0;
571 
572 //     wxString ifs( senc_file_name );
573 //
574 //     wxFFileInputStream fpx_u( ifs );
575 //     if (!fpx_u.IsOk()) {
576 //         return ERROR_SENCFILE_NOT_FOUND;
577 //     }
578 //     wxBufferedInputStream fpx( fpx_u );
579 
580     //    Sanity check for existence of file
581     Osenc_instreamFile fpx;
582     fpx.Open( senc_file_name );
583     if (!fpx.IsOk())
584         return ERROR_SENCFILE_NOT_FOUND;
585 
586     S57Obj *obj = 0;
587     int featureID;
588 
589     int dun = 0;
590 
591     while( !dun ) {
592 
593         //      Read a record Header
594         OSENC_Record_Base record;
595 
596 //        long off = fpx.TellI();
597 
598         fpx.Read(&record, sizeof(OSENC_Record_Base));
599         if(!fpx.IsOk()){
600             dun = 1;
601             break;
602         }
603 
604         // Process Records
605         switch( record.record_type){
606             case HEADER_SENC_VERSION:
607             {
608                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
609                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
610                     dun = 1; break;
611                 }
612                 uint16_t *pint = (uint16_t*)buf;
613                 m_senc_file_read_version = *pint;
614                 break;
615             }
616             case HEADER_CELL_NAME:
617             {
618                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
619                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
620                     dun = 1; break;
621                 }
622                 m_Name = wxString( buf, wxConvUTF8 );
623                 break;
624             }
625             case HEADER_CELL_PUBLISHDATE:
626             {
627                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
628                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
629                     dun = 1; break;
630                 }
631                 m_sdate000 = wxString( buf, wxConvUTF8 );
632                 break;
633             }
634 
635             case HEADER_CELL_EDITION:
636             {
637                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
638                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
639                     dun = 1; break;
640                 }
641                 uint16_t *pint = (uint16_t*)buf;
642                 m_read_base_edtn.Printf(_T("%d"), *pint);
643 
644                 break;
645             }
646 
647             case HEADER_CELL_UPDATEDATE:
648             {
649                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
650                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
651                     dun = 1; break;
652                 }
653                 m_LastUpdateDate = wxString( buf, wxConvUTF8 );
654                 break;
655             }
656 
657             case HEADER_CELL_UPDATE:
658             {
659                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
660                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
661                     dun = 1; break;
662                 }
663                 uint16_t *pint = (uint16_t*)buf;
664                 m_read_last_applied_update = *pint;
665 
666                 break;
667             }
668 
669             case HEADER_CELL_NATIVESCALE:
670             {
671                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
672                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
673                     dun = 1; break;
674                 }
675                 uint32_t *pint = (uint32_t*)buf;
676                 m_Chart_Scale = *pint;
677                 break;
678             }
679 
680             case HEADER_CELL_SENCCREATEDATE:
681             {
682                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
683                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
684                     dun = 1; break;
685                 }
686                 break;
687             }
688 
689             case CELL_EXTENT_RECORD:
690             {
691                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
692                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
693                     dun = 1; break;
694                 }
695                 _OSENC_EXTENT_Record_Payload *pPayload = (_OSENC_EXTENT_Record_Payload *)buf;
696                 m_extent.NLAT = pPayload->extent_nw_lat;
697                 m_extent.SLAT = pPayload->extent_se_lat;
698                 m_extent.WLON = pPayload->extent_nw_lon;
699                 m_extent.ELON = pPayload->extent_se_lon;
700 
701                 //  We declare the ref_lat/ref_lon to be the centroid of the extents
702                 //  This is how the SENC was created....
703                 m_ref_lat = (m_extent.NLAT + m_extent.SLAT) / 2.;
704                 m_ref_lon = (m_extent.ELON + m_extent.WLON) / 2.;
705 
706                 break;
707             }
708 
709             case CELL_COVR_RECORD:
710             {
711                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
712                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
713                     dun = 1; break;
714                 }
715 
716                 break;
717             }
718 
719             case CELL_NOCOVR_RECORD:
720             {
721                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
722                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
723                     dun = 1; break;
724                 }
725 
726                 break;
727             }
728 
729             case FEATURE_ID_RECORD:
730             {
731                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
732                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
733                     dun = 1; break;
734                 }
735 
736                 // Starting definition of a new feature
737                 _OSENC_Feature_Identification_Record_Payload *pPayload = (_OSENC_Feature_Identification_Record_Payload *)buf;
738 
739                 // Get the Feature type code and ID
740                 int featureTypeCode = pPayload->feature_type_code;
741                 featureID = pPayload->feature_ID;
742 
743                 //TODO
744 //                if(207 == featureID)
745 //                    int yyp = 4;
746 
747                 //  Look up the FeatureName from the Registrar
748 
749                 std::string acronym = GetFeatureAcronymFromTypecode( featureTypeCode );
750 
751                 //TODO debugging
752 //                 printf("%s\n", acronym.c_str());
753 //                 if(!strncmp(acronym.c_str(), "BOYLAT", 6))
754 //                     int yyp = 4;
755 
756                 if(acronym.length()){
757                     obj = new S57Obj(acronym.c_str());
758                     obj->Index = featureID;
759 
760                     pObjectVector->push_back(obj);
761                 }
762 
763                 break;
764             }
765 
766             case FEATURE_ATTRIBUTE_RECORD:
767             {
768                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
769                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
770                     dun = 1; break;
771                 }
772 
773                 // Get the payload
774                 OSENC_Attribute_Record_Payload *pPayload = (OSENC_Attribute_Record_Payload *)buf;
775 
776                 int attributeTypeCode = pPayload->attribute_type_code;
777 
778                 //  Handle Special cases...
779 
780                 //  The primitive type of the Feature is encoded in the SENC as an attribute of defined type.
781 //                if( ATTRIBUTE_ID_PRIM == attributeTypeCode ){
782 //                    primitiveType = pPayload->attribute_value_int;
783 //                }
784 
785 
786                 //  Get the standard attribute acronym
787                 std::string acronym = GetAttributeAcronymFromTypecode( attributeTypeCode );
788 
789                 int attributeValueType = pPayload->attribute_value_type;
790 
791                 if( acronym.length() ){
792                     switch(attributeValueType){
793                         case 0:
794                         {
795                             uint32_t val = pPayload->attribute_value_int;
796                             if(obj){
797                                 obj->AddIntegerAttribute( acronym.c_str(), val );
798                             }
799                             break;
800                         }
801 
802                         case 1:             // Integer list
803                         {
804                             // Calculate the number of elements from the record size
805                             //int nCount = (record.record_length - sizeof(_OSENC_Attribute_Record)) ;
806 
807                             break;
808 
809                         }
810                         case 2:             // Single double precision real
811                         {
812                             double val = pPayload->attribute_value_double;
813                             if(obj)
814                                 obj->AddDoubleAttribute( acronym.c_str(), val );
815                             break;
816                         }
817 
818                         case 3:             // List of double precision real
819                         {
820                             //TODO
821                             break;
822                         }
823 
824                         case 4:             // Ascii String
825                         {
826                             char *val = (char *)&pPayload->attribute_value_char_ptr;
827                             if(obj)
828                                 obj->AddStringAttribute( acronym.c_str(), val );
829 
830                             break;
831                         }
832 
833                         default:
834                             break;
835                     }
836                 }
837 
838                 break;
839             }
840 
841             case FEATURE_GEOMETRY_RECORD_POINT:
842             {
843                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
844                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
845                     dun = 1; break;
846                 }
847 
848                 // Get the payload
849                 _OSENC_PointGeometry_Record_Payload *pPayload = (_OSENC_PointGeometry_Record_Payload *)buf;
850 
851                 if(obj){
852                     obj->SetPointGeometry( pPayload->lat, pPayload->lon, m_ref_lat, m_ref_lon);
853                 }
854 
855                 break;
856             }
857 
858             case FEATURE_GEOMETRY_RECORD_AREA:
859             {
860                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
861                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
862                     dun = 1; break;
863                 }
864 
865                 // Get the payload
866                 _OSENC_AreaGeometry_Record_Payload *pPayload = (_OSENC_AreaGeometry_Record_Payload *)buf;
867 
868                 if(obj){
869                     unsigned char *next_byte;
870                     PolyTessGeo *pPTG = BuildPolyTessGeo( pPayload, &next_byte);
871 
872                     obj->SetAreaGeometry(pPTG, m_ref_lat, m_ref_lon ) ;
873 
874                     //  Set the Line geometry for the Feature
875                     LineGeometryDescriptor Descriptor;
876 
877                     //  Copy some simple stuff
878                     Descriptor.extent_e_lon = pPayload->extent_e_lon;
879                     Descriptor.extent_w_lon = pPayload->extent_w_lon;
880                     Descriptor.extent_s_lat = pPayload->extent_s_lat;
881                     Descriptor.extent_n_lat = pPayload->extent_n_lat;
882 
883                     Descriptor.indexCount = pPayload->edgeVector_count;
884 
885                     // Copy the line index table, which in this case is offset in the payload
886                     Descriptor.indexTable = (int *)malloc(pPayload->edgeVector_count * 3 * sizeof(int));
887                     memcpy( Descriptor.indexTable, next_byte,
888                             pPayload->edgeVector_count * 3 * sizeof(int) );
889 
890 
891                     obj->SetLineGeometry( &Descriptor, GEO_AREA, m_ref_lat, m_ref_lon ) ;
892 
893                 }
894 
895                 break;
896 
897             }
898 
899             case FEATURE_GEOMETRY_RECORD_LINE:
900             {
901                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
902                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
903                     dun = 1; break;
904                 }
905 
906                 // Get the payload & parse it
907                 _OSENC_LineGeometry_Record_Payload *pPayload = (_OSENC_LineGeometry_Record_Payload *)buf;
908                 LineGeometryDescriptor lD;
909 
910                 //  Copy some simple stuff
911                 lD.extent_e_lon = pPayload->extent_e_lon;
912                 lD.extent_w_lon = pPayload->extent_w_lon;
913                 lD.extent_s_lat = pPayload->extent_s_lat;
914                 lD.extent_n_lat = pPayload->extent_n_lat;
915 
916                 lD.indexCount = pPayload->edgeVector_count;
917 
918                 // Copy the payload tables
919                 lD.indexTable = (int *)malloc(pPayload->edgeVector_count * 3 * sizeof(int));
920                 memcpy( lD.indexTable, &pPayload->payLoad, pPayload->edgeVector_count * 3 * sizeof(int) );
921 
922                 if(obj)
923                     obj->SetLineGeometry( &lD, GEO_LINE, m_ref_lat, m_ref_lon ) ;
924 
925                 break;
926 
927             }
928 
929             case FEATURE_GEOMETRY_RECORD_MULTIPOINT:
930             {
931                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
932                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
933                     dun = 1; break;
934                 }
935 
936                 // Get the payload & parse it
937                 OSENC_MultipointGeometry_Record_Payload *pPayload = (OSENC_MultipointGeometry_Record_Payload *)buf;
938 
939                 //  Set the Multipoint geometry for the Feature
940                 MultipointGeometryDescriptor Descriptor;
941 
942                 //  Copy some simple stuff
943                 Descriptor.extent_e_lon = pPayload->extent_e_lon;
944                 Descriptor.extent_w_lon = pPayload->extent_w_lon;
945                 Descriptor.extent_s_lat = pPayload->extent_s_lat;
946                 Descriptor.extent_n_lat = pPayload->extent_n_lat;
947 
948                 Descriptor.pointCount = pPayload->point_count;
949                 Descriptor.pointTable = &pPayload->payLoad;
950 
951                 if (obj)
952                     obj->SetMultipointGeometry( &Descriptor, m_ref_lat, m_ref_lon);
953 
954                 break;
955             }
956 
957 
958             case VECTOR_EDGE_NODE_TABLE_RECORD:
959             {
960                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
961                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
962                     dun = 1; break;
963                 }
964 
965                 //  Parse the buffer
966                 uint8_t *pRun = (uint8_t *)buf;
967 
968                 // The Feature(Object) count
969                 int nCount = *(int *)pRun;
970 
971                 pRun += sizeof(int);
972 
973                 for(int i=0 ; i < nCount ; i++ ) {
974                     int featureIndex = *(int*)pRun;
975                     pRun += sizeof(int);
976 
977                     int pointCount = *(int*)pRun;
978                     pRun += sizeof(int);
979 
980                     float *pPoints = NULL;
981                     if( pointCount ) {
982                         pPoints = (float *) malloc( pointCount * 2 * sizeof(float) );
983                         memcpy(pPoints, pRun, pointCount * 2 * sizeof(float));
984                     }
985                     pRun += pointCount * 2 * sizeof(float);
986 
987                     VE_Element *pvee = new VE_Element;
988                     pvee->index = featureIndex;
989                     pvee->nCount = pointCount;
990                     pvee->pPoints = pPoints;
991                     pvee->max_priority = 0;            // Default
992 
993                     pVEArray->push_back(pvee);
994 
995                 }
996 
997 
998                 break;
999             }
1000 
1001             case VECTOR_CONNECTED_NODE_TABLE_RECORD:
1002             {
1003                 unsigned char *buf = getBuffer( record.record_length - sizeof(OSENC_Record_Base));
1004                 if(!fpx.Read(buf, record.record_length - sizeof(OSENC_Record_Base)).IsOk()){
1005                     dun = 1; break;
1006                 }
1007 
1008                 //  Parse the buffer
1009                 uint8_t *pRun = (uint8_t *)buf;
1010 
1011                 // The Feature(Object) count
1012                 int nCount = *(int *)pRun;
1013                 pRun += sizeof(int);
1014 
1015                 for(int i=0 ; i < nCount ; i++ ) {
1016                     int featureIndex = *(int*)pRun;
1017                     pRun += sizeof(int);
1018 
1019                     float *pPoint = (float *) malloc( 2 * sizeof(float) );
1020                     memcpy(pPoint, pRun, 2 * sizeof(float));
1021                     pRun += 2 * sizeof(float);
1022 
1023                     VC_Element *pvce = new VC_Element;
1024                     pvce->index = featureIndex;
1025                     pvce->pPoint = pPoint;
1026 
1027                     pVCArray->push_back(pvce);
1028                 }
1029 
1030 
1031                 break;
1032             }
1033 
1034             default:
1035                 break;
1036 
1037         }       // switch
1038 
1039     }
1040 
1041 
1042     return ret_val;
1043 
1044 }
1045 
1046 
1047 
ingestCell(OGRS57DataSource * poS57DS,const wxString & FullPath000,const wxString & working_dir)1048 int Osenc::ingestCell( OGRS57DataSource *poS57DS, const wxString &FullPath000, const wxString &working_dir )
1049 {
1050     //      Analyze Updates
1051     //      The OGR library will apply updates automatically, if enabled.
1052     //      Alternatively, we can explicitely find and apply updates from any source directory.
1053     //      We need to keep track of the last sequential update applied, to look out for new updates
1054 
1055     wxString LastUpdateDate = m_date000.Format( _T("%Y%m%d") );
1056 
1057     int available_updates = ValidateAndCountUpdates( FullPath000, working_dir, LastUpdateDate, true );
1058     m_LastUpdateDate = LastUpdateDate;          // tentative, adjusted later on failure of update
1059 
1060     if(m_bVerbose && ( available_updates > m_UPDN ) ){
1061         wxString msg1;
1062         msg1.Printf( _T("Preparing to apply ENC updates, target final update is %3d."), available_updates );
1063         wxLogMessage( msg1 );
1064     }
1065 
1066     wxString sobj;
1067 
1068     //  Here comes the actual ISO8211 file reading
1069 
1070     //  Set up the options
1071     char ** papszReaderOptions = NULL;
1072     //    papszReaderOptions = CSLSetNameValue(papszReaderOptions, S57O_LNAM_REFS, "ON" );
1073 //    papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
1074     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES, "ON" );
1075     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON" );
1076     poS57DS->SetOptionList( papszReaderOptions );
1077 
1078     //      Open the OGRS57DataSource
1079     //      This will ingest the .000 file from the working dir
1080 
1081     bool b_current_debug = g_bGDAL_Debug;
1082     g_bGDAL_Debug = m_bVerbose;
1083 
1084     // Form the .000 filename
1085     wxString s0_file = working_dir;
1086     if( s0_file.Last() != wxFileName::GetPathSeparator() )
1087         s0_file.Append( wxFileName::GetPathSeparator() );
1088     wxFileName f000(FullPath000);
1089 
1090     s0_file.Append( f000.GetFullName() );
1091 
1092     if(poS57DS->Open( s0_file.mb_str(), TRUE, NULL))
1093         return 1;
1094 
1095     //      Get a pointer to the reader
1096     S57Reader *poReader = poS57DS->GetModule( 0 );
1097 
1098     m_last_applied_update = m_UPDN;
1099     wxString last_successful_update_file;
1100 
1101     // Apply the updates...
1102     for(unsigned int i_up = 0 ; i_up < m_tmpup_array.GetCount() ; i_up++){
1103         wxFileName fn(m_tmpup_array[i_up]);
1104         wxString ext = fn.GetExt();
1105         long n_upd;
1106         ext.ToLong(&n_upd);
1107 
1108         if(n_upd > 0){                  // .000 is the base, not an update
1109             DDFModule oUpdateModule;
1110             if(!oUpdateModule.Open( m_tmpup_array[i_up].mb_str(), FALSE )){
1111                 break;
1112             }
1113             int upResult = poReader->ApplyUpdates( &oUpdateModule, n_upd );
1114             if(upResult){
1115                 break;
1116             }
1117             m_last_applied_update = n_upd;
1118             last_successful_update_file = m_tmpup_array[i_up];
1119         }
1120     }
1121 
1122 
1123     //  Check for bad/broken update chain....
1124     //  It is a "warning" condition if an update fails.
1125     //  We use the cell with all good updates applied so far, and so inform the user.
1126     //  "Better a slightly out-of-date chart than no chart at all..."
1127 
1128     // The logic will attempt to build a SENC on each instance of OCPN, so eventually the
1129     //  updates may be corrected, and the chart SENC is built correctly.
1130     //  Or, the update files following the last good update may be manually deleted.
1131 
1132     if( (available_updates > 0) && (m_last_applied_update != available_updates) ){
1133 
1134         if(last_successful_update_file.Length()){
1135             //  Get the update date from the last good update module
1136             bool bSuccess;
1137             DDFModule oUpdateModule;
1138             wxString LastGoodUpdateDate;
1139             wxDateTime now = wxDateTime::Now();
1140             LastGoodUpdateDate = now.Format( _T("%Y%m%d") );
1141 
1142             bSuccess = !( oUpdateModule.Open( last_successful_update_file.mb_str(), TRUE ) == 0 );
1143 
1144             if( bSuccess ) {
1145                 //      Get publish/update date
1146                 oUpdateModule.Rewind();
1147                 DDFRecord *pr = oUpdateModule.ReadRecord();                     // Record 0
1148 
1149                 int nSuccess;
1150                 char *u = NULL;
1151 
1152                 if( pr ) u = (char *) ( pr->GetStringSubfield( "DSID", 0, "ISDT", 0, &nSuccess ) );
1153 
1154                 if( u ) {
1155                     if( strlen( u ) ) {
1156                         LastGoodUpdateDate = wxString( u, wxConvUTF8 );
1157                     }
1158                 }
1159                 m_LastUpdateDate = LastGoodUpdateDate;
1160             }
1161 
1162             // Inform the user
1163             wxString msg( _T("WARNING---ENC Update failed.  Last valid update file is:"));
1164             msg +=  last_successful_update_file.mb_str();
1165             wxLogMessage(msg);
1166             wxLogMessage(_T("   This ENC exchange set should be updated and SENCs rebuilt.") );
1167 
1168 
1169             if( !m_NoErrDialog ){
1170                 OCPNMessageBox(NULL,
1171                     _("S57 Cell Update failed.\nENC features may be incomplete or inaccurate.\n\nCheck the logfile for details."),
1172                     _("OpenCPN Create SENC Warning"), wxOK | wxICON_EXCLAMATION, 5 );
1173             }
1174         }
1175         else{            // no updates applied.
1176                 if( !m_NoErrDialog )
1177                     OCPNMessageBox(NULL,
1178                                _("S57 Cell Update failed.\nNo updates could be applied.\nENC features may be incomplete or inaccurate.\n\nCheck the logfile for details."),
1179                                _("OpenCPN Create SENC Warning"), wxOK | wxICON_EXCLAMATION, 5 );
1180         }
1181     }
1182 
1183 
1184     //  Unset verbose debug option
1185     g_bGDAL_Debug = b_current_debug;
1186 
1187 
1188     //      Update the options, removing the RETURN_PRIMITIVES flags
1189     //      This flag needed to be set on ingest() to create the proper field defns,
1190     //      but cleared to fetch normal features
1191 
1192     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF" );
1193     poReader->SetOptions( papszReaderOptions );
1194     CSLDestroy( papszReaderOptions );
1195 
1196     wxRemoveFile(s0_file);
1197 
1198     return 0;
1199 }
1200 
1201 
ValidateAndCountUpdates(const wxFileName file000,const wxString CopyDir,wxString & LastUpdateDate,bool b_copyfiles)1202 int Osenc::ValidateAndCountUpdates( const wxFileName file000, const wxString CopyDir,
1203                                     wxString &LastUpdateDate, bool b_copyfiles )
1204 {
1205 
1206     int retval = 0;
1207     wxFileName last_up_added;
1208 
1209     //       wxString DirName000 = file000.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
1210     //       wxDir dir(DirName000);
1211     m_UpFiles = new wxArrayString;
1212     retval = s57chart::GetUpdateFileArray( file000, m_UpFiles, m_date000, m_edtn000);
1213     int upmax = retval;
1214 
1215     if( m_UpFiles->GetCount() ) {
1216         //      The s57reader of ogr requires that update set be sequentially complete
1217         //      to perform all the updates.  However, some NOAA ENC distributions are
1218         //      not complete, as apparently some interim updates have been  withdrawn.
1219         //      Example:  as of 20 Dec, 2005, the update set for US5MD11M.000 includes
1220         //      US5MD11M.017, ...018, and ...019.  Updates 001 through 016 are missing.
1221         //
1222         //      Workaround.
1223         //      Create temporary dummy update files to fill out the set before invoking
1224         //      ogr file open/ingest.  Delete after SENC file create finishes.
1225         //      Set starts with .000, which has the effect of copying the base file to the working dir
1226 
1227         //        bool chain_broken_mssage_shown = false;
1228 
1229         if( b_copyfiles ) {
1230 
1231             unsigned int jup = 0;
1232             for( int iff = 0; iff < retval + 1; iff++ ) {
1233                 wxString upFile;
1234                 wxString targetFile;
1235 
1236                 if(jup < m_UpFiles->GetCount())
1237                     upFile = m_UpFiles->Item(jup);
1238                 wxFileName upCheck(upFile);
1239                 long tl = -1;
1240                 wxString text = upCheck.GetExt();
1241                 text.ToLong(&tl);
1242                 if(tl == iff){
1243                     targetFile = upFile;
1244                     jup++;              // used this one
1245                 }
1246                 else{
1247                     targetFile = file000.GetFullName();         // ext will be updated
1248                 }
1249 
1250                 wxFileName ufile( targetFile );
1251                 wxString sext;
1252                 sext.Printf( _T("%03d"), iff );
1253                 ufile.SetExt( sext );
1254 
1255                 //      Create the target update file name
1256                 wxString cp_ufile = CopyDir;
1257                 if( cp_ufile.Last() != ufile.GetPathSeparator() ) cp_ufile.Append(
1258                     ufile.GetPathSeparator() );
1259 
1260                 cp_ufile.Append( ufile.GetFullName() );
1261 
1262                 wxString tfile = ufile.GetFullPath();
1263 
1264                 //      Explicit check for a short update file, possibly left over from a crash...
1265                 int flen = 0;
1266                 if( ufile.FileExists() ) {
1267                     wxFile uf( ufile.GetFullPath() );
1268                     if( uf.IsOpened() ) {
1269                         flen = uf.Length();
1270                         uf.Close();
1271                     }
1272                 }
1273 
1274                 if( ufile.FileExists() && ( flen > 25 ) )        // a valid update file or base file
1275                         {
1276                             //      Copy the valid file to the SENC directory
1277                             bool cpok = wxCopyFile( ufile.GetFullPath(), cp_ufile );
1278                             if( !cpok ) {
1279                                 wxString msg( _T("   Cannot copy temporary working ENC file ") );
1280                                 msg.Append( ufile.GetFullPath() );
1281                                 msg.Append( _T(" to ") );
1282                                 msg.Append( cp_ufile );
1283                                 wxLogMessage( msg );
1284                             }
1285                         }
1286 
1287                         else {
1288                             // Create a dummy ISO8211 file with no real content
1289                             // Correct this.  We should break the walk, and notify the user  See FS#1406
1290 
1291                             //                             if( !chain_broken_mssage_shown ){
1292                                 //                                 OCPNMessageBox(NULL,
1293                                 //                                                _("S57 Cell Update chain incomplete.\nENC features may be incomplete or inaccurate.\nCheck the logfile for details."),
1294                                 //                                                _("OpenCPN Create SENC Warning"), wxOK | wxICON_EXCLAMATION, 30 );
1295                                 //                                                chain_broken_mssage_shown = true;
1296                                 //                             }
1297 
1298                                 wxString msg( _T("WARNING---ENC Update chain incomplete. Substituting NULL update file: "));
1299                                 msg += ufile.GetFullName();
1300                                 wxLogMessage(msg);
1301                                 wxLogMessage(_T("   Subsequent ENC updates may produce errors.") );
1302                                 wxLogMessage(_T("   This ENC exchange set should be updated and SENCs rebuilt.") );
1303 
1304                                 bool bstat;
1305                                 DDFModule dupdate;
1306                                 dupdate.Initialize( '3', 'L', 'E', '1', '0', "!!!", 3, 4, 4 );
1307                                 bstat = !( dupdate.Create( cp_ufile.mb_str() ) == 0 );
1308                                 dupdate.Close();
1309 
1310                                 if( !bstat ) {
1311                                     wxString msg( _T("   Error creating dummy update file: ") );
1312                                     msg.Append( cp_ufile );
1313                                     wxLogMessage( msg );
1314                                 }
1315                         }
1316 
1317                         m_tmpup_array.Add( cp_ufile );
1318                         last_up_added = cp_ufile;
1319             }
1320         }
1321 
1322         //      Extract the date field from the last of the update files
1323         //      which is by definition a valid, present update file....
1324 
1325         wxFileName lastfile( last_up_added );
1326         wxString last_sext;
1327         last_sext.Printf( _T("%03d"), upmax );
1328         lastfile.SetExt( last_sext );
1329 
1330         bool bSuccess;
1331         DDFModule oUpdateModule;
1332 
1333         bSuccess = !( oUpdateModule.Open( lastfile.GetFullPath().mb_str(), TRUE ) == 0 );
1334 
1335         if( bSuccess ) {
1336             //      Get publish/update date
1337             oUpdateModule.Rewind();
1338             DDFRecord *pr = oUpdateModule.ReadRecord();                     // Record 0
1339 
1340             int nSuccess;
1341             char *u = NULL;
1342 
1343             if( pr ) u = (char *) ( pr->GetStringSubfield( "DSID", 0, "ISDT", 0, &nSuccess ) );
1344 
1345             if( u ) {
1346                 if( strlen( u ) ) {
1347                     LastUpdateDate = wxString( u, wxConvUTF8 );
1348                 }
1349             } else {
1350                 wxDateTime now = wxDateTime::Now();
1351                 LastUpdateDate = now.Format( _T("%Y%m%d") );
1352             }
1353         }
1354     }
1355 
1356     return retval;
1357 }
1358 
GetBaseFileAttr(const wxString & FullPath000)1359 bool Osenc::GetBaseFileAttr( const wxString &FullPath000 )
1360 {
1361 
1362     DDFModule oModule;
1363     if( !oModule.Open( FullPath000.mb_str() ) ) {
1364         return false;
1365     }
1366 
1367     oModule.Rewind();
1368 
1369     //    Read and parse DDFRecord 0 to get some interesting data
1370     //    n.b. assumes that the required fields will be in Record 0....  Is this always true?
1371 
1372     DDFRecord *pr = oModule.ReadRecord();                               // Record 0
1373     //    pr->Dump(stdout);
1374 
1375     //    Fetch the Geo Feature Count, or something like it....
1376     m_nGeoRecords = pr->GetIntSubfield( "DSSI", 0, "NOGR", 0 );
1377     if( !m_nGeoRecords ) {
1378         errorMessage = _T("GetBaseFileAttr:  DDFRecord 0 does not contain DSSI:NOGR ") ;
1379 
1380         m_nGeoRecords = 1;                // backstop
1381     }
1382 
1383     //  Use ISDT(Issue Date) here, which is the same as UADT(Updates Applied) for .000 files
1384     wxString date000;
1385     char *u = (char *) ( pr->GetStringSubfield( "DSID", 0, "ISDT", 0 ) );
1386     if( u ) date000 = wxString( u, wxConvUTF8 );
1387     else {
1388         errorMessage =  _T("GetBaseFileAttr:  DDFRecord 0 does not contain DSID:ISDT ");
1389 
1390         date000 = _T("20000101");             // backstop, very early, so any new files will update?
1391     }
1392     m_date000.ParseFormat( date000, _T("%Y%m%d") );
1393     if( !m_date000.IsValid() ) m_date000.ParseFormat( _T("20000101"), _T("%Y%m%d") );
1394 
1395     m_date000.ResetTime();
1396 
1397     //    Fetch the EDTN(Edition) field
1398     u = (char *) ( pr->GetStringSubfield( "DSID", 0, "EDTN", 0 ) );
1399     if( u ) m_edtn000 = wxString( u, wxConvUTF8 );
1400     else {
1401         errorMessage =  _T("GetBaseFileAttr:  DDFRecord 0 does not contain DSID:EDTN ");
1402 
1403         m_edtn000 = _T("1");                // backstop
1404     }
1405 
1406     //m_SE = m_edtn000;
1407 
1408     //    Fetch the UPDN(Updates Applied) field
1409     u = (char *) ( pr->GetStringSubfield( "DSID", 0, "UPDN", 0 ) );
1410     if( u ){
1411         long updn = 0;
1412         wxString tmp_updn = wxString( u, wxConvUTF8 );
1413         if(tmp_updn.ToLong(&updn))
1414             m_UPDN = updn;
1415 
1416     }
1417     else {
1418         errorMessage =  _T("GetBaseFileAttr:  DDFRecord 0 does not contain DSID:UPDN ");
1419 
1420         m_UPDN = 0;                // backstop
1421     }
1422 
1423     //      Fetch the Native Scale by reading more records until DSPM is found
1424     m_native_scale = 0;
1425     for( ; pr != NULL; pr = oModule.ReadRecord() ) {
1426         if( pr->FindField( "DSPM" ) != NULL ) {
1427             m_native_scale = pr->GetIntSubfield( "DSPM", 0, "CSCL", 0 );
1428             break;
1429         }
1430     }
1431     if( !m_native_scale ) {
1432         errorMessage = _T("GetBaseFileAttr:  ENC not contain DSPM:CSCL ");
1433 
1434         m_native_scale = 1000;                // backstop
1435     }
1436 
1437     return true;
1438 }
1439 
1440 
1441 
1442 
1443 //---------------------------------------------------------------------------------------------------
1444 /*
1445  *      OpenCPN OSENC Version 2 Implementation
1446  */
1447 //---------------------------------------------------------------------------------------------------
1448 
1449 
1450 
1451 
1452 
1453 
createSenc200(const wxString & FullPath000,const wxString & SENCFileName,bool b_showProg)1454 int Osenc::createSenc200(const wxString& FullPath000, const wxString& SENCFileName, bool b_showProg)
1455 {
1456     lockCR.lock();
1457 
1458     m_FullPath000 = FullPath000;
1459 
1460     m_senc_file_create_version = 201;
1461 
1462     if(!m_poRegistrar){
1463         m_poRegistrar = new S57ClassRegistrar();
1464         m_poRegistrar->LoadInfo( g_csv_locn.mb_str(), FALSE );
1465         m_bPrivateRegistrar = true;
1466         //errorMessage = _T("S57 Registrar not set.");
1467         //return ERROR_REGISTRAR_NOT_SET;
1468     }
1469 
1470     wxFileName SENCfile = wxFileName( SENCFileName );
1471     wxFileName file000 = wxFileName( FullPath000 );
1472 
1473 
1474     //      Make the target directory if needed
1475     if( true != SENCfile.DirExists( SENCfile.GetPath() ) ) {
1476         if( !SENCfile.Mkdir( SENCfile.GetPath() ) ) {
1477             errorMessage = _T("Cannot create SENC file directory for ") + SENCfile.GetFullPath();
1478             lockCR.unlock();
1479             return ERROR_CANNOT_CREATE_SENC_DIR;
1480         }
1481     }
1482 
1483 
1484     //          Make a temp file to create the SENC in
1485     wxFileName tfn;
1486     wxString tmp_file = tfn.CreateTempFileName( _T("") );
1487 
1488 //     FILE *fps57;
1489 //     const char *pp = "wb";
1490 //     fps57 = fopen( tmp_file.mb_str(), pp );
1491 //
1492 //     if( fps57 == NULL ) {
1493 //         errorMessage = _T("Unable to create temp SENC file: ");
1494 //         errorMessage.Append( tfn.GetFullPath() );
1495 //         return ERROR_CANNOT_CREATE_TEMP_SENC_FILE;
1496 //     }
1497 
1498     if(m_pauxOutstream){
1499         m_pOutstream = m_pauxOutstream;
1500     }
1501     else{
1502         m_pOutstream = new Osenc_outstreamFile();
1503     }
1504 
1505     Osenc_outstream *stream = m_pOutstream;
1506 
1507     if( !stream->Open( tmp_file) ) {
1508         errorMessage = _T("Unable to create temp SENC file: ");
1509         errorMessage += tmp_file;
1510         lockCR.unlock();
1511         return ERROR_CANNOT_CREATE_TEMP_SENC_FILE;
1512     }
1513 
1514     //  Take a quick scan of the 000 file to get some basic attributes of the exchange set.
1515     if(!GetBaseFileAttr( FullPath000 ) ){
1516         lockCR.unlock();
1517         return ERROR_BASEFILE_ATTRIBUTES;
1518     }
1519 
1520     OGRS57DataSource S57DS;
1521     OGRS57DataSource *poS57DS = &S57DS;
1522     poS57DS->SetS57Registrar( m_poRegistrar );
1523 
1524 
1525 
1526     //  Ingest the .000 cell, with updates applied
1527 
1528     if(ingestCell( poS57DS, FullPath000, SENCfile.GetPath())){
1529         errorMessage = _T("Error ingesting: ") + FullPath000;
1530         lockCR.unlock();
1531         return ERROR_INGESTING000;
1532     }
1533 
1534     S57Reader *poReader = poS57DS->GetModule( 0 );
1535 
1536     //  Create the Coverage table Records, which also calculates the chart extents
1537     if(!CreateCOVRTables( poReader, m_poRegistrar )){
1538         lockCR.unlock();
1539         return ERROR_SENCFILE_ABORT;
1540     }
1541 
1542 
1543     //  Establish a common reference point for the chart, from the extent
1544     m_ref_lat = ( m_extent.NLAT + m_extent.SLAT ) / 2.;
1545     m_ref_lon = ( m_extent.WLON + m_extent.ELON ) / 2.;
1546 
1547     bool bcont = true;
1548 
1549     //          Write the Header information
1550 
1551     //char temp[201];
1552 
1553     //fprintf( fps57, "SENC Version= %d\n", 200 );
1554 
1555     // The chart cell "nice name"
1556     wxString nice_name;
1557     s57chart::GetChartNameFromTXT( FullPath000, nice_name );
1558 
1559     string sname = "UTF8Error";
1560     wxCharBuffer buffer=nice_name.ToUTF8();
1561     if(buffer.data())
1562         sname = buffer.data();
1563 
1564     if( !WriteHeaderRecord200( stream, HEADER_SENC_VERSION, (uint16_t)m_senc_file_create_version) ){
1565         stream->Close();
1566         lockCR.unlock();
1567         return ERROR_SENCFILE_ABORT;
1568     }
1569 
1570     if( !WriteHeaderRecord200( stream, HEADER_CELL_NAME, sname) ){
1571         stream->Close();
1572         lockCR.unlock();
1573         return ERROR_SENCFILE_ABORT;
1574     }
1575 
1576     wxString date000 = m_date000.Format( _T("%Y%m%d") );
1577     string sdata = date000.ToStdString();
1578     if( !WriteHeaderRecord200( stream, HEADER_CELL_PUBLISHDATE, sdata) ){
1579         stream->Close();
1580         lockCR.unlock();
1581         return ERROR_SENCFILE_ABORT;
1582     }
1583 
1584 
1585     long n000 = 0;
1586     m_edtn000.ToLong( &n000 );
1587     if( !WriteHeaderRecord200( stream, HEADER_CELL_EDITION, (uint16_t)n000) ){
1588         stream->Close();
1589         lockCR.unlock();
1590         return ERROR_SENCFILE_ABORT;
1591     }
1592 
1593     sdata = m_LastUpdateDate.ToStdString();
1594     if( !WriteHeaderRecord200( stream, HEADER_CELL_UPDATEDATE, sdata) ){
1595         stream->Close();
1596         lockCR.unlock();
1597         return ERROR_SENCFILE_ABORT;
1598     }
1599 
1600 
1601     if( !WriteHeaderRecord200( stream, HEADER_CELL_UPDATE, (uint16_t)m_last_applied_update) ){
1602         stream->Close();
1603         lockCR.unlock();
1604         return ERROR_SENCFILE_ABORT;
1605     }
1606 
1607     if( !WriteHeaderRecord200( stream, HEADER_CELL_NATIVESCALE, (uint32_t)m_native_scale) ){
1608         stream->Close();
1609         lockCR.unlock();
1610         return ERROR_SENCFILE_ABORT;
1611     }
1612 
1613     wxDateTime now = wxDateTime::Now();
1614     wxString dateNow = now.Format( _T("%Y%m%d") );
1615     sdata = dateNow.ToStdString();
1616     if( !WriteHeaderRecord200( stream, HEADER_CELL_SENCCREATEDATE, sdata) ){
1617         stream->Close();
1618         lockCR.unlock();
1619         return ERROR_SENCFILE_ABORT;
1620     }
1621 
1622 
1623     //  Write the Coverage table Records
1624     if(!CreateCovrRecords(stream)){
1625         stream->Close();
1626         lockCR.unlock();
1627         return ERROR_SENCFILE_ABORT;
1628     }
1629 
1630 
1631 
1632     poReader->Rewind();
1633 
1634 
1635     //        Prepare Vector Edge Helper table
1636     //        And fill in the table
1637     int feid = 0;
1638     OGRFeature *pEdgeVectorRecordFeature = poReader->ReadVector( feid, RCNM_VE );
1639     while( NULL != pEdgeVectorRecordFeature ) {
1640         int record_id = pEdgeVectorRecordFeature->GetFieldAsInteger( "RCID" );
1641 
1642         m_vector_helper_hash[record_id] = feid;
1643 
1644         feid++;
1645         delete pEdgeVectorRecordFeature;
1646         pEdgeVectorRecordFeature = poReader->ReadVector( feid, RCNM_VE );
1647     }
1648 
1649     wxString Message = SENCfile.GetFullPath();
1650     Message.Append( _T("...Ingesting") );
1651 
1652     wxString Title( _("OpenCPN S57 SENC File Create...") );
1653     Title.append( SENCfile.GetFullPath() );
1654 
1655 #if wxUSE_PROGRESSDLG
1656 
1657     wxStopWatch progsw;
1658     int nProg = poReader->GetFeatureCount();
1659 
1660     if(wxThread::IsMain() && b_showProg){
1661         m_ProgDialog = new wxGenericProgressDialog();
1662 
1663         wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
1664         m_ProgDialog->SetFont( *qFont );
1665 
1666         m_ProgDialog->Create( Title, Message, nProg, NULL, wxPD_AUTO_HIDE | wxPD_SMOOTH );
1667     }
1668 #endif
1669 
1670 
1671     //  Loop in the S57 reader, extracting Features one-by-one
1672     OGRFeature *objectDef;
1673 
1674     int iObj = 0;
1675 
1676     while( bcont ) {
1677         objectDef = poReader->ReadNextFeature();
1678 
1679         if( objectDef != NULL ) {
1680 
1681             iObj++;
1682 
1683 #if wxUSE_PROGRESSDLG
1684 
1685             //  Update the progress dialog
1686             //We update only every 200 milliseconds to improve performance as updating the dialog is very expensive...
1687             // WXGTK is measurably slower even with 100ms here
1688             if( m_ProgDialog && progsw.Time() > 200 )
1689             {
1690                 progsw.Start();
1691 
1692                 wxString sobj = wxString( objectDef->GetDefnRef()->GetName(), wxConvUTF8 );
1693                 sobj.Append( wxString::Format( _T("  %d/%d       "), iObj, nProg ) );
1694 
1695                 bcont = m_ProgDialog->Update( iObj, sobj );
1696 #if defined(__WXMSW__) || defined(__WXOSX__)
1697                 wxSafeYield();
1698 #endif
1699             }
1700 #endif
1701 
1702             OGRwkbGeometryType geoType = wkbUnknown;
1703             //      This test should not be necessary for real (i.e not C_AGGR) features
1704             //      However... some update files contain errors, and have deleted some
1705             //      geometry without deleting the corresponding feature(s).
1706             //      So, GeometryType becomes Unknown.
1707             //      e.g. US5MD11M.017
1708             //      In this case, all we can do is skip the feature....sigh.
1709 
1710             if( objectDef->GetGeometryRef() != NULL )
1711                 geoType = objectDef->GetGeometryRef()->getGeometryType();
1712 
1713             //      n.b  This next line causes skip of C_AGGR features w/o geometry
1714                 if( geoType != wkbUnknown ){                             // Write only if has wkbGeometry
1715                     CreateSENCRecord200( objectDef, stream, 1, poReader );
1716                 }
1717 
1718                 delete objectDef;
1719 
1720         } else
1721             break;
1722 
1723     }
1724 
1725     if( bcont ) {
1726         //      Create and write the Vector Edge Table
1727         CreateSENCVectorEdgeTableRecord200( stream, poReader );
1728 
1729         //      Create and write the Connected NodeTable
1730         CreateSENCVectorConnectedTableRecord200( stream, poReader );
1731     }
1732 
1733 
1734     //          All done, so clean up
1735     stream->Close();
1736 
1737     //  Delete any temporary (working) real and dummy update files,
1738     //  as well as .000 file created by ValidateAndCountUpdates()
1739     for( unsigned int iff = 0; iff < m_tmpup_array.GetCount(); iff++ )
1740         remove( m_tmpup_array[iff].mb_str() );
1741 
1742     int ret_code = 0;
1743 
1744     if( !bcont )                // aborted
1745     {
1746         wxRemoveFile( tmp_file );                     // kill the temp file
1747         ret_code = ERROR_SENCFILE_ABORT;
1748     }
1749 
1750     if( bcont ) {
1751         bool cpok = wxRenameFile( tmp_file, SENCfile.GetFullPath() );
1752         if( !cpok ) {
1753             errorMessage =  _T("   Cannot rename temporary SENC file ");
1754             errorMessage.Append( tmp_file );
1755             errorMessage.Append( _T(" to ") );
1756             errorMessage.Append( SENCfile.GetFullPath() );
1757             ret_code = ERROR_SENCFILE_ABORT;
1758         } else
1759             ret_code = SENC_NO_ERROR;
1760     }
1761 
1762 #if wxUSE_PROGRESSDLG
1763     delete m_ProgDialog;
1764 #endif
1765 
1766     lockCR.unlock();
1767 
1768     return ret_code;
1769 
1770 
1771 }
1772 
CreateCovrRecords(Osenc_outstream * stream)1773 bool Osenc::CreateCovrRecords(Osenc_outstream *stream)
1774 {
1775     // First, create the Extent record
1776     _OSENC_EXTENT_Record record;
1777     record.record_type = CELL_EXTENT_RECORD;
1778     record.record_length = sizeof(_OSENC_EXTENT_Record);
1779     record.extent_sw_lat = m_extent.SLAT;
1780     record.extent_sw_lon = m_extent.WLON;
1781     record.extent_nw_lat = m_extent.NLAT;
1782     record.extent_nw_lon = m_extent.WLON;
1783     record.extent_ne_lat = m_extent.NLAT;
1784     record.extent_ne_lon = m_extent.ELON;
1785     record.extent_se_lat = m_extent.SLAT;
1786     record.extent_se_lon = m_extent.ELON;
1787 
1788     size_t targetCount = sizeof(record);
1789     if(!stream->Write(&record , targetCount).IsOk())
1790         return false;
1791 
1792 
1793     for(int i=0 ; i < m_nCOVREntries ; i++){
1794 
1795         int nPoints = m_pCOVRTablePoints[i];
1796 
1797         float *fpbuf = m_pCOVRTable[i];
1798 
1799         //  Ready to write the record
1800         _OSENC_COVR_Record_Base record;
1801         record.record_type = CELL_COVR_RECORD;
1802         record.record_length = sizeof(_OSENC_COVR_Record_Base) + sizeof(uint32_t) + (nPoints * 2 * sizeof(float) );
1803 
1804         //  Write the base record
1805         size_t targetCount = sizeof(record);
1806         if(!stream->Write(&record , targetCount).IsOk())
1807             return false;
1808 
1809         //Write the point count
1810         targetCount = sizeof(uint32_t);
1811         if(!stream->Write(&nPoints , targetCount).IsOk())
1812             return false;
1813 
1814         //  Write the point array
1815         targetCount = nPoints * 2 * sizeof(float);
1816         if(!stream->Write(fpbuf , targetCount).IsOk())
1817             return false;
1818 
1819     }
1820 
1821     for(int i=0 ; i < m_nNoCOVREntries ; i++){
1822 
1823         int nPoints = m_pNoCOVRTablePoints[i];
1824 
1825         float *fpbuf = m_pNoCOVRTable[i];
1826 
1827         //  Ready to write the record
1828         _OSENC_NOCOVR_Record_Base record;
1829         record.record_type = CELL_NOCOVR_RECORD;
1830         record.record_length = sizeof(_OSENC_NOCOVR_Record_Base) + sizeof(uint32_t) + (nPoints * 2 * sizeof(float) );
1831 
1832         //  Write the base record
1833         size_t targetCount = sizeof(record);
1834         if(!stream->Write(&record , targetCount).IsOk())
1835             return false;
1836 
1837         //Write the point count
1838         targetCount = sizeof(uint32_t);
1839         if(!stream->Write(&nPoints , targetCount).IsOk())
1840             return false;
1841 
1842         //  Write the point array
1843         targetCount = nPoints * 2 * sizeof(float);
1844         if(!stream->Write(fpbuf, targetCount).IsOk())
1845             return false;
1846 
1847     }
1848 
1849     return true;
1850 }
1851 
1852 
1853 
WriteHeaderRecord200(Osenc_outstream * stream,int recordType,std::string payload)1854 bool Osenc::WriteHeaderRecord200( Osenc_outstream *stream, int recordType, std::string payload){
1855 
1856     int payloadLength = payload.length() + 1;
1857     int recordLength = payloadLength + sizeof(OSENC_Record_Base);
1858 
1859     //  Get a reference to the class persistent buffer
1860     unsigned char *pBuffer = getBuffer( recordLength );
1861 
1862     OSENC_Record *pRecord = (OSENC_Record *)pBuffer;
1863     memset(pRecord, 0, recordLength);
1864     pRecord->record_type = recordType;
1865     pRecord->record_length = recordLength;
1866     memcpy(&pRecord->payload, payload.c_str(), payloadLength);
1867 
1868     size_t targetCount = recordLength;
1869     if(!stream->Write(pBuffer, targetCount).IsOk())
1870         return false;
1871     else
1872         return true;
1873 }
1874 
WriteHeaderRecord200(Osenc_outstream * stream,int recordType,uint16_t val)1875 bool Osenc::WriteHeaderRecord200( Osenc_outstream *stream, int recordType, uint16_t val){
1876 
1877     int payloadLength = sizeof(uint16_t);
1878     int recordLength = payloadLength + sizeof(OSENC_Record_Base);
1879 
1880     //  Get a reference to the class persistent buffer
1881     unsigned char *pBuffer = getBuffer( recordLength );
1882 
1883     OSENC_Record *pRecord = (OSENC_Record *)pBuffer;
1884     memset(pRecord, 0, recordLength);
1885     pRecord->record_type = recordType;
1886     pRecord->record_length = recordLength;
1887     memcpy(&pRecord->payload, &val, payloadLength);
1888 
1889     size_t targetCount = recordLength;
1890     if(!stream->Write(pBuffer, targetCount).IsOk())
1891         return false;
1892     else
1893         return true;
1894 }
1895 
WriteHeaderRecord200(Osenc_outstream * stream,int recordType,uint32_t val)1896 bool Osenc::WriteHeaderRecord200( Osenc_outstream *stream, int recordType, uint32_t val){
1897 
1898     int payloadLength = sizeof(uint32_t);
1899     int recordLength = payloadLength + sizeof(OSENC_Record_Base);
1900 
1901     //  Get a reference to the class persistent buffer
1902     unsigned char *pBuffer = getBuffer( recordLength );
1903 
1904     OSENC_Record *pRecord = (OSENC_Record *)pBuffer;
1905     memset(pRecord, 0, recordLength);
1906     pRecord->record_type = recordType;
1907     pRecord->record_length = recordLength;
1908     memcpy(&pRecord->payload, &val, payloadLength);
1909 
1910     size_t targetCount = recordLength;
1911     if(!stream->Write(pBuffer, targetCount).IsOk())
1912         return false;
1913     else
1914         return true;
1915 }
1916 
WriteFIDRecord200(Osenc_outstream * stream,int nOBJL,int featureID,int prim)1917 bool Osenc::WriteFIDRecord200( Osenc_outstream *stream, int nOBJL, int featureID, int prim){
1918 
1919 
1920     OSENC_Feature_Identification_Record_Base record;
1921     memset(&record, 0, sizeof(record));
1922 
1923 
1924     record.record_type = FEATURE_ID_RECORD;
1925     record.record_length = sizeof(record);
1926 
1927     record.feature_ID = featureID;
1928     record.feature_type_code = nOBJL;
1929     record.feature_primitive = prim;
1930 
1931     size_t targetCount = sizeof(record);
1932     if(!stream->Write(&record, targetCount).IsOk())
1933         return false;
1934     else
1935         return true;
1936 }
1937 
CreateMultiPointFeatureGeometryRecord200(OGRFeature * pFeature,Osenc_outstream * stream)1938 bool Osenc::CreateMultiPointFeatureGeometryRecord200( OGRFeature *pFeature, Osenc_outstream *stream)
1939 {
1940     OGRGeometry *pGeo = pFeature->GetGeometryRef();
1941 
1942     int wkb_len = pGeo->WkbSize();
1943     unsigned char *pwkb_buffer = (unsigned char *) malloc( wkb_len );
1944 
1945     //  Get the GDAL data representation
1946     pGeo->exportToWkb( wkbNDR, pwkb_buffer );
1947 
1948 
1949     //  Capture a buffer of the raw geometry
1950 
1951     unsigned char *ps = pwkb_buffer;
1952     ps += 5;
1953     int nPoints = *( (int *) ps );                     // point count
1954 
1955     int sb_len = (  nPoints * 3 * sizeof(float) ) ;    //points as floats
1956 
1957     unsigned char *psb_buffer = (unsigned char *) malloc( sb_len );
1958     unsigned char *pd = psb_buffer;
1959 
1960     ps = pwkb_buffer;
1961     ps += 9;            // skip byte order, type, and count
1962 
1963     float *pdf = (float *) pd;
1964 
1965     //  Set absurd bbox starting limits
1966     float lonmax = -1000;
1967     float lonmin = 1000;
1968     float latmax = -1000;
1969     float latmin = 1000;
1970 
1971 
1972     for( int ip = 0; ip < nPoints; ip++ ) {
1973 
1974         // Workaround a bug?? in OGRGeometryCollection
1975         // While exporting point geometries serially, OGRPoint->exportToWkb assumes that
1976         // if Z is identically 0, then the point must be a 2D point only.
1977         // So, the collection Wkb is corrupted with some 3D, and some 2D points.
1978         // Workaround:  Get reference to the points serially, and explicitly read X,Y,Z
1979         // Ignore the previously read Wkb buffer
1980 
1981         OGRGeometryCollection *temp_geometry_collection = (OGRGeometryCollection *) pGeo;
1982         OGRGeometry *temp_geometry = temp_geometry_collection->getGeometryRef( ip );
1983         OGRPoint *pt_geom = (OGRPoint *) temp_geometry;
1984 
1985         double lon = pt_geom->getX();
1986         double lat = pt_geom->getY();
1987         double depth = pt_geom->getZ();
1988 
1989         //  Calculate SM from chart common reference point
1990         double easting, northing;
1991         toSM( lat, lon, m_ref_lat, m_ref_lon, &easting, &northing );
1992 
1993         #ifdef __ARM_ARCH
1994         float __attribute__((aligned(16))) east = easting;
1995         float __attribute__((aligned(16))) north = northing;
1996         float __attribute__((aligned(16))) deep = depth;
1997         unsigned char *puceast = (unsigned char *)&east;
1998         unsigned char *pucnorth = (unsigned char *)&north;
1999         unsigned char *pucdeep = (unsigned char *)&deep;
2000 
2001         memcpy(pdf++, puceast, sizeof(float));
2002         memcpy(pdf++, pucnorth, sizeof(float));
2003         memcpy(pdf++, pucdeep, sizeof(float));
2004 
2005         #else
2006         *pdf++ = easting;
2007         *pdf++ = northing;
2008         *pdf++ = (float) depth;
2009         #endif
2010 
2011         //  Keep a running calculation of min/max
2012         lonmax = fmax(lon, lonmax);
2013         lonmin = fmin(lon, lonmin);
2014         latmax = fmax(lat, latmax);
2015         latmin = fmin(lat, latmin);
2016     }
2017 
2018 
2019 
2020     //  Ready to write the record
2021     OSENC_MultipointGeometry_Record_Base record;
2022     record.record_type = FEATURE_GEOMETRY_RECORD_MULTIPOINT;
2023     record.record_length = sizeof(OSENC_MultipointGeometry_Record_Base) +
2024                             (nPoints * 3 * sizeof(float) );
2025     record.extent_e_lon = lonmax;
2026     record.extent_w_lon = lonmin;
2027     record.extent_n_lat = latmax;
2028     record.extent_s_lat = latmin;
2029     record.point_count = nPoints;
2030 
2031     //  Write the base record
2032     size_t targetCount = sizeof(record);
2033     if(!stream->Write(&record, targetCount).IsOk())
2034         goto failure;
2035     //  Write the 3D point array
2036     targetCount = nPoints * 3 * sizeof(float);
2037     if(!stream->Write(psb_buffer, targetCount).IsOk())
2038         goto failure;
2039 
2040     //  Free the buffers
2041     free( psb_buffer );
2042     free( pwkb_buffer );
2043     return true;
2044 failure:
2045     //  Free the buffers
2046     free( psb_buffer );
2047     free( pwkb_buffer );
2048     return false;
2049 }
2050 
2051 
2052 
CreateLineFeatureGeometryRecord200(S57Reader * poReader,OGRFeature * pFeature,Osenc_outstream * stream)2053 bool Osenc::CreateLineFeatureGeometryRecord200( S57Reader *poReader, OGRFeature *pFeature, Osenc_outstream *stream)
2054 {
2055     OGRGeometry *pGeo = pFeature->GetGeometryRef();
2056 
2057     int wkb_len = pGeo->WkbSize();
2058     unsigned char *pwkb_buffer = (unsigned char *) malloc( wkb_len );
2059 
2060     //  Get the GDAL data representation
2061     pGeo->exportToWkb( wkbNDR, pwkb_buffer );
2062 
2063 
2064     //  Capture a buffer of the raw geometry
2065 
2066     int sb_len = ( ( wkb_len - 9 ) / 2 ) + 9 + 16;  // data will be 4 byte float, not double
2067 
2068     unsigned char *psb_buffer = (unsigned char *) malloc( sb_len );
2069     unsigned char *pd = psb_buffer;
2070     unsigned char *ps = pwkb_buffer;
2071 
2072     memcpy( pd, ps, 9 );                                  // byte order, type, and count
2073 
2074     int ip = *( (int *) ( ps + 5 ) );                              // point count
2075 
2076     pd += 9;
2077     ps += 9;
2078     double *psd = (double *) ps;
2079     float *pdf = (float *) pd;
2080 
2081     //  Set absurd bbox starting limits
2082     float lonmax = -1000;
2083     float lonmin = 1000;
2084     float latmax = -1000;
2085     float latmin = 1000;
2086 
2087     for( int i = 0; i < ip; i++ ){                           // convert doubles to floats
2088                                                          // computing bbox as we go
2089         float lon, lat;
2090         double easting, northing;
2091     #ifdef __ARM_ARCH
2092         double __attribute__((aligned(16))) east_d, north_d;
2093         unsigned char *pucd = (unsigned char *)psd;
2094 
2095         memcpy(&east_d, pucd, sizeof(double));
2096         psd+= 1;
2097         pucd += sizeof(double);
2098         memcpy(&north_d, pucd, sizeof(double));
2099         psd += 1;
2100         lon = east_d;
2101         lat = north_d;
2102 
2103     //  Calculate SM from chart common reference point
2104         toSM( lat, lon, m_ref_lat, m_ref_lon, &easting, &northing );
2105         unsigned char *puceasting = (unsigned char *)&easting;
2106         unsigned char *pucnorthing = (unsigned char *)&northing;
2107 
2108         memcpy(pdf++, puceasting, sizeof(float));
2109         memcpy(pdf++, pucnorthing, sizeof(float));
2110 
2111     #else
2112         lon = (float) *psd++;
2113         lat = (float) *psd++;
2114 
2115     //  Calculate SM from chart common reference point
2116         toSM( lat, lon, m_ref_lat, m_ref_lon, &easting, &northing );
2117 
2118         *pdf++ = easting;
2119         *pdf++ = northing;
2120     #endif
2121 
2122         lonmax = fmax(lon, lonmax);
2123         lonmin = fmin(lon, lonmin);
2124         latmax = fmax(lat, latmax);
2125         latmin = fmin(lat, latmin);
2126 
2127     }
2128 
2129     int nEdgeVectorRecords = 0;
2130     unsigned char *pvec_buffer = getObjectVectorIndexTable( poReader, pFeature, nEdgeVectorRecords );
2131 
2132 #if 0
2133 
2134     //    Capture the Vector Table geometry indices into a memory buffer
2135     int *pNAME_RCID;
2136     int *pORNT;
2137     int nEdgeVectorRecords;
2138     OGRFeature *pEdgeVectorRecordFeature;
2139 
2140     pNAME_RCID = (int *) pFeature->GetFieldAsIntegerList( "NAME_RCID", &nEdgeVectorRecords );
2141     pORNT = (int *) pFeature->GetFieldAsIntegerList( "ORNT", NULL );
2142 
2143 //fprintf( fpOut, "LSINDEXLIST %d\n", nEdgeVectorRecords );
2144 //                    fwrite(pNAME_RCID, 1, nEdgeVectorRecords * sizeof(int), fpOut);
2145 
2146 
2147 
2148     unsigned char *pvec_buffer = (unsigned char *)malloc(nEdgeVectorRecords * 3 * sizeof(int));
2149     unsigned char *pvRun = pvec_buffer;
2150 
2151     //  Set up the options, adding RETURN_PRIMITIVES
2152     char ** papszReaderOptions = NULL;
2153     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
2154     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES,"ON" );
2155     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"ON" );
2156     poReader->SetOptions( papszReaderOptions );
2157 
2158 //    Capture the beginning and end point connected nodes for each edge vector record
2159     for( int i = 0; i < nEdgeVectorRecords; i++ ) {
2160 
2161         int *pI = (int *)pvRun;
2162 
2163         int edge_rcid = pNAME_RCID[i];
2164 
2165         int start_rcid, end_rcid;
2166         int target_record_feid = m_vector_helper_hash[pNAME_RCID[i]];
2167         pEdgeVectorRecordFeature = poReader->ReadVector( target_record_feid, RCNM_VE );
2168 
2169         if( NULL != pEdgeVectorRecordFeature ) {
2170             start_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_0" );
2171             end_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_1" );
2172 
2173         //    Make sure the start and end points exist....
2174         //    Note this poReader method was converted to Public access to
2175         //     facilitate this test.  There might be another clean way....
2176         //    Problem first found on Holand ENC 1R5YM009.000
2177             if( !poReader->FetchPoint( RCNM_VC, start_rcid, NULL, NULL, NULL, NULL ) )
2178                 start_rcid = -1;
2179             if( !poReader->FetchPoint( RCNM_VC, end_rcid, NULL, NULL, NULL, NULL ) )
2180                 end_rcid = -2;
2181 
2182             OGRLineString *poLS = (OGRLineString *)pEdgeVectorRecordFeature->GetGeometryRef();
2183 
2184             int edge_ornt = 1;
2185 
2186              if( edge_ornt == 1 ){                                    // forward
2187                 *pI++ = start_rcid;
2188                 *pI++ = edge_rcid;
2189                 *pI++ = end_rcid;
2190             } else {                                                  // reverse
2191                 *pI++ = end_rcid;
2192                 *pI++ = edge_rcid;
2193                 *pI++ = start_rcid;
2194             }
2195 
2196             delete pEdgeVectorRecordFeature;
2197         } else {
2198             start_rcid = -1;                                    // error indication
2199             end_rcid = -2;
2200 
2201             *pI++ = start_rcid;
2202             *pI++ = edge_rcid;
2203             *pI++ = end_rcid;
2204         }
2205 
2206         pvRun += 3 * sizeof(int);
2207     }
2208 
2209 
2210         //  Reset the options
2211     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"OFF" );
2212     poReader->SetOptions( papszReaderOptions );
2213     CSLDestroy( papszReaderOptions );
2214 #endif
2215 
2216 
2217     //  Ready to write the record
2218     OSENC_LineGeometry_Record_Base record;
2219     record.record_type = FEATURE_GEOMETRY_RECORD_LINE;
2220     record.record_length = sizeof(OSENC_LineGeometry_Record_Base) +
2221                             (nEdgeVectorRecords * 3 * sizeof(int) );
2222     record.extent_e_lon = lonmax;
2223     record.extent_w_lon = lonmin;
2224     record.extent_n_lat = latmax;
2225     record.extent_s_lat = latmin;
2226     record.edgeVector_count = nEdgeVectorRecords;
2227 
2228     //  Write the base record
2229     size_t targetCount = sizeof(record);
2230     if(!stream->Write(&record, targetCount).IsOk())
2231         return false;
2232 
2233     //  Write the table index array
2234     targetCount = nEdgeVectorRecords * 3 * sizeof(int);
2235     if(!stream->Write(pvec_buffer, targetCount).IsOk())
2236         return false;
2237 
2238     //  Free the buffers
2239     free(pvec_buffer);
2240     free( psb_buffer );
2241     free( pwkb_buffer );
2242 
2243     return true;
2244 }
2245 
2246 
2247 
2248 
2249 
CreateAreaFeatureGeometryRecord200(S57Reader * poReader,OGRFeature * pFeature,Osenc_outstream * stream)2250 bool Osenc::CreateAreaFeatureGeometryRecord200(S57Reader *poReader, OGRFeature *pFeature, Osenc_outstream *stream)
2251 {
2252     int error_code;
2253 
2254     PolyTessGeo *ppg = NULL;
2255 
2256     OGRGeometry *pGeo = pFeature->GetGeometryRef();
2257     OGRPolygon *poly = (OGRPolygon *) ( pGeo );
2258 
2259     if( !poly->getExteriorRing() )
2260         return false;
2261 
2262     lockCR.unlock();
2263     ppg = new PolyTessGeo( poly, true, m_ref_lat, m_ref_lon, m_LOD_meters );
2264     lockCR.lock();
2265 
2266     error_code = ppg->ErrorCode;
2267 
2268     if( error_code ){
2269         wxLogMessage( _T("   Warning: S57 SENC Geometry Error %d, Some Features ignored."), ppg->ErrorCode );
2270         delete ppg;
2271 
2272         return false;
2273     }
2274 
2275 
2276 
2277     //  Ready to create the record
2278 
2279     //  The base record, with writing deferred until length is known
2280     OSENC_AreaGeometry_Record_Base baseRecord;
2281     memset(&baseRecord, 0, sizeof(baseRecord));
2282 
2283     baseRecord.record_type = FEATURE_GEOMETRY_RECORD_AREA;
2284 
2285     //  Length calculation is deferred...
2286 
2287     baseRecord.extent_s_lat = ppg->Get_ymin();
2288     baseRecord.extent_n_lat = ppg->Get_ymax();
2289     baseRecord.extent_e_lon = ppg->Get_xmax();
2290     baseRecord.extent_w_lon = ppg->Get_xmin();
2291 
2292     baseRecord.contour_count = ppg->GetnContours();
2293 
2294     //  Create the array of contour point counts
2295 
2296     int contourPointCountArraySize = ppg->GetnContours() * sizeof(uint32_t);
2297     uint32_t *contourPointCountArray = (uint32_t *)malloc(contourPointCountArraySize );
2298 
2299     uint32_t *pr = contourPointCountArray;
2300 
2301     for(int i=0 ; i<ppg->GetnContours() ; i++){
2302         *pr++ = ppg->Get_PolyTriGroup_head()->pn_vertex[i];
2303     }
2304 
2305     //  All that is left is the tesselation result Triangle lists...
2306     //  This could be a large array, and we don't want to use a buffer.
2307     //  Rather, we want to write directly to the output file.
2308 
2309     //  So, we
2310     //  a  walk the TriPrim chain once to get it's length,
2311     //  b update the total record length,
2312     //  c write everything before the TriPrim chain,
2313     //  d and then directly write the TriPrim chain.
2314 
2315     //  Walk the TriPrim chain
2316     int geoLength = 0;
2317 
2318     TriPrim *pTP = ppg->Get_PolyTriGroup_head()->tri_prim_head;         // head of linked list of TriPrims
2319 
2320     int n_TriPrims = 0;
2321     while(pTP)
2322     {
2323         geoLength += sizeof(uint8_t) + sizeof(uint32_t);              // type, nvert
2324         geoLength += pTP->nVert * 2 * sizeof(float);                  // vertices
2325         geoLength += 4 * sizeof(double);                              // Primitive bounding box
2326         pTP = pTP->p_next;
2327 
2328         n_TriPrims++;
2329     }
2330 
2331     baseRecord.triprim_count = n_TriPrims;                            //  Set the number of TriPrims
2332 
2333 
2334     int nEdgeVectorRecords = 0;
2335     unsigned char *pvec_buffer = getObjectVectorIndexTable( poReader, pFeature, nEdgeVectorRecords );
2336 
2337 #if 0
2338      //  Create the Vector Edge Index table into a memory buffer
2339      //  This buffer will follow the triangle buffer in the output stream
2340     int *pNAME_RCID;
2341     int *pORNT;
2342     int nEdgeVectorRecords;
2343     OGRFeature *pEdgeVectorRecordFeature;
2344 
2345     pNAME_RCID = (int *) pFeature->GetFieldAsIntegerList( "NAME_RCID", &nEdgeVectorRecords );
2346     pORNT = (int *) pFeature->GetFieldAsIntegerList( "ORNT", NULL );
2347 
2348     baseRecord.edgeVector_count = nEdgeVectorRecords;
2349 
2350     unsigned char *pvec_buffer = (unsigned char *)malloc(nEdgeVectorRecords * 3 * sizeof(int));
2351     unsigned char *pvRun = pvec_buffer;
2352 
2353     //  Set up the options, adding RETURN_PRIMITIVES
2354     char ** papszReaderOptions = NULL;
2355     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
2356     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES,"ON" );
2357     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"ON" );
2358     poReader->SetOptions( papszReaderOptions );
2359 
2360     //    Capture the beginning and end point connected nodes for each edge vector record
2361     for( int i = 0; i < nEdgeVectorRecords; i++ ) {
2362 
2363         int *pI = (int *)pvRun;
2364 
2365         int start_rcid, end_rcid;
2366         int target_record_feid = m_vector_helper_hash[pNAME_RCID[i]];
2367         pEdgeVectorRecordFeature = poReader->ReadVector( target_record_feid, RCNM_VE );
2368 
2369         int edge_rcid = pNAME_RCID[i];
2370 
2371         if( NULL != pEdgeVectorRecordFeature ) {
2372             start_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_0" );
2373             end_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_1" );
2374             //    Make sure the start and end points exist....
2375             //    Note this poReader method was converted to Public access to
2376             //     facilitate this test.  There might be another clean way....
2377             //    Problem first found on Holand ENC 1R5YM009.000
2378             if( !poReader->FetchPoint( RCNM_VC, start_rcid, NULL, NULL, NULL, NULL ) )
2379                 start_rcid = -1;
2380             if( !poReader->FetchPoint( RCNM_VC, end_rcid, NULL, NULL, NULL, NULL ) )
2381                 end_rcid = -2;
2382 
2383             int edge_ornt = 1;
2384             //  Allocate some storage for converted points
2385 
2386             if( edge_ornt == 1 ){                                    // forward
2387                 *pI++ = start_rcid;
2388                 *pI++ = edge_rcid;
2389                 *pI++ = end_rcid;
2390             } else {                                                  // reverse
2391                 *pI++ = end_rcid;
2392                 *pI++ = edge_rcid;
2393                 *pI++ = start_rcid;
2394             }
2395 
2396             delete pEdgeVectorRecordFeature;
2397         } else {
2398             start_rcid = -1;                                    // error indication
2399             end_rcid = -2;
2400 
2401             *pI++ = start_rcid;
2402             *pI++ = edge_rcid;
2403             *pI++ = end_rcid;
2404         }
2405 
2406         pvRun += 3 * sizeof(int);
2407     }
2408 
2409 
2410     //  Reset the options
2411     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"OFF" );
2412     poReader->SetOptions( papszReaderOptions );
2413     CSLDestroy( papszReaderOptions );
2414 
2415 #endif
2416 
2417     baseRecord.edgeVector_count = nEdgeVectorRecords;
2418 
2419     //  Calculate the total record length
2420     int recordLength = sizeof(OSENC_AreaGeometry_Record_Base);
2421     recordLength += contourPointCountArraySize;
2422     recordLength += geoLength;
2423     recordLength += nEdgeVectorRecords * 3 * sizeof(int);
2424     baseRecord.record_length = recordLength;
2425 
2426     //  Write the base record
2427     size_t targetCount = sizeof(baseRecord);
2428     if(!stream->Write(&baseRecord, targetCount).IsOk())
2429         return false;
2430 
2431     //  Write the contour point count array
2432     targetCount = contourPointCountArraySize;
2433     if(!stream->Write(contourPointCountArray, targetCount).IsOk())
2434         return false;
2435 
2436     //  Walk and transcribe the TriPrim chain
2437     pTP = ppg->Get_PolyTriGroup_head()->tri_prim_head;         // head of linked list of TriPrims
2438     while(pTP)
2439     {
2440 
2441         if(!stream->Write(&pTP->type, sizeof(uint8_t)).IsOk())
2442             return false;
2443         if(!stream->Write(&pTP->nVert, sizeof(uint32_t)).IsOk())
2444             return false;
2445 
2446 //         fwrite (&pTP->minxt , sizeof(double), 1, fpOut);
2447 //         fwrite (&pTP->maxxt , sizeof(double), 1, fpOut);
2448 //         fwrite (&pTP->minyt , sizeof(double), 1, fpOut);
2449 //         fwrite (&pTP->maxyt , sizeof(double), 1, fpOut);
2450 
2451         double minlat, minlon, maxlat, maxlon;
2452         minlat = pTP->tri_box.GetMinLat();
2453         minlon = pTP->tri_box.GetMinLon();
2454         maxlat = pTP->tri_box.GetMaxLat();
2455         maxlon = pTP->tri_box.GetMaxLon();
2456 
2457 
2458         if(!stream->Write(&minlon, sizeof(double)).IsOk())
2459             return false;
2460         if(!stream->Write(&maxlon, sizeof(double)).IsOk())
2461             return false;
2462         if(!stream->Write(&minlat, sizeof(double)).IsOk())
2463             return false;
2464         if(!stream->Write(&maxlat, sizeof(double)).IsOk())
2465             return false;
2466 
2467         // Testing TODO
2468 //         float *pf = (float *)pTP->p_vertex;
2469 //         float a = *pf++;
2470 //         float b = *pf;
2471 
2472 
2473             if(!stream->Write(pTP->p_vertex, pTP->nVert * 2 * sizeof(float)).IsOk())
2474             return false;
2475 
2476 
2477         pTP = pTP->p_next;
2478     }
2479 
2480     //  Write the Edge Vector index table
2481     targetCount = nEdgeVectorRecords * 3 * sizeof(int);
2482     if(!stream->Write(pvec_buffer, targetCount).IsOk())
2483         return false;
2484 
2485 
2486     delete ppg;
2487     free(contourPointCountArray);
2488     free( pvec_buffer );
2489 
2490     return true;
2491 }
2492 
getObjectVectorIndexTable(S57Reader * poReader,OGRFeature * pFeature,int & nEntries)2493 unsigned char *Osenc::getObjectVectorIndexTable( S57Reader *poReader, OGRFeature *pFeature, int &nEntries )
2494 {
2495     //  Create the Vector Edge Index table into a memory buffer
2496     //  This buffer will follow the triangle buffer in the output stream
2497     int *pNAME_RCID;
2498     int *pORNT;
2499     int nEdgeVectorRecords;
2500     OGRFeature *pEdgeVectorRecordFeature;
2501 
2502     pNAME_RCID = (int *) pFeature->GetFieldAsIntegerList( "NAME_RCID", &nEdgeVectorRecords );
2503     pORNT = (int *) pFeature->GetFieldAsIntegerList( "ORNT", NULL );
2504 
2505     nEntries = nEdgeVectorRecords;
2506 
2507     unsigned char *pvec_buffer = (unsigned char *)malloc(nEdgeVectorRecords * 3 * sizeof(int));
2508     unsigned char *pvRun = pvec_buffer;
2509 
2510     //  Set up the options, adding RETURN_PRIMITIVES
2511     char ** papszReaderOptions = NULL;
2512     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
2513     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES,"ON" );
2514     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"ON" );
2515     poReader->SetOptions( papszReaderOptions );
2516 
2517     //    Capture the beginning and end point connected nodes for each edge vector record
2518     for( int i = 0; i < nEdgeVectorRecords; i++ ) {
2519 
2520         int *pI = (int *)pvRun;
2521 
2522         int edge_rcid = pNAME_RCID[i];
2523 
2524         int start_rcid, end_rcid;
2525         int target_record_feid = m_vector_helper_hash[pNAME_RCID[i]];
2526         pEdgeVectorRecordFeature = poReader->ReadVector( target_record_feid, RCNM_VE );
2527 
2528         if( NULL != pEdgeVectorRecordFeature ) {
2529             start_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_0" );
2530             end_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_1" );
2531 
2532             //    Make sure the start and end points exist....
2533             //    Note this poReader method was converted to Public access to
2534             //     facilitate this test.  There might be another clean way....
2535             //    Problem first found on Holand ENC 1R5YM009.000
2536             if( !poReader->FetchPoint( RCNM_VC, start_rcid, NULL, NULL, NULL, NULL ) )
2537                 start_rcid = -1;
2538             if( !poReader->FetchPoint( RCNM_VC, end_rcid, NULL, NULL, NULL, NULL ) )
2539                 end_rcid = -2;
2540 
2541             OGRLineString *poLS = (OGRLineString *)pEdgeVectorRecordFeature->GetGeometryRef();
2542             if(!poLS)
2543                 edge_rcid = 0;
2544             else if(poLS->getNumPoints() < 1)
2545                 edge_rcid = 0;
2546 
2547             int edge_ornt = pORNT[i];
2548 
2549             if( edge_ornt == 1 ){                                    // forward
2550                 *pI++ = start_rcid;
2551                 *pI++ = edge_rcid;
2552                 *pI++ = end_rcid;
2553             } else {                                                  // reverse
2554                 *pI++ = end_rcid;
2555                 *pI++ = -edge_rcid;
2556                 *pI++ = start_rcid;
2557             }
2558 
2559             delete pEdgeVectorRecordFeature;
2560         } else {
2561             start_rcid = -1;                                    // error indication
2562             end_rcid = -2;
2563 
2564             *pI++ = start_rcid;
2565             *pI++ = edge_rcid;
2566             *pI++ = end_rcid;
2567         }
2568 
2569         pvRun += 3 * sizeof(int);
2570     }
2571 
2572 
2573     //  Reset the options
2574     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"OFF" );
2575     poReader->SetOptions( papszReaderOptions );
2576     CSLDestroy( papszReaderOptions );
2577 
2578     return pvec_buffer;
2579 
2580 }
2581 
CreateSENCVectorEdgeTableRecord200(Osenc_outstream * stream,S57Reader * poReader)2582 void Osenc::CreateSENCVectorEdgeTableRecord200( Osenc_outstream *stream, S57Reader *poReader )
2583 {
2584     //  We create the payload first, so we can calculate the total record length
2585     uint8_t *pPayload = NULL;
2586     int payloadSize = 0;
2587     uint8_t *pRun = pPayload;
2588 
2589     //  Set up the S57Reader options, adding RETURN_PRIMITIVES
2590     char ** papszReaderOptions = NULL;
2591     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
2592     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES, "ON" );
2593     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON" );
2594     poReader->SetOptions( papszReaderOptions );
2595 
2596     int feid = 0;
2597     OGRLineString *pLS = NULL;
2598     OGRGeometry *pGeo;
2599     OGRFeature *pEdgeVectorRecordFeature = poReader->ReadVector( feid, RCNM_VE );
2600 
2601     int nFeatures = 0;
2602 
2603     //  Read all the EdgeVector Features
2604     while( NULL != pEdgeVectorRecordFeature ) {
2605 
2606         //  Check for a zero point count.  Dunno why this should happen, other than bad ENC encoding
2607 
2608         int nPoints = 0;
2609         if( pEdgeVectorRecordFeature->GetGeometryRef() != NULL ) {
2610             pGeo = pEdgeVectorRecordFeature->GetGeometryRef();
2611             if( pGeo->getGeometryType() == wkbLineString ) {
2612                 pLS = (OGRLineString *) pGeo;
2613                 nPoints = pLS->getNumPoints();
2614             } else
2615                 nPoints = 0;
2616         }
2617 
2618         if(nPoints){
2619             int new_size = payloadSize + (2 * sizeof(int));
2620             pPayload = (uint8_t *)realloc(pPayload, new_size );
2621             pRun = pPayload + payloadSize;                   //  recalculate the running pointer,
2622                                                         //  since realloc may have moved memory
2623             payloadSize = new_size;
2624 
2625         //  Fetch and store the Record ID
2626             int record_id = pEdgeVectorRecordFeature->GetFieldAsInteger( "RCID" );
2627             *(int*)pRun = record_id;
2628             pRun += sizeof(int);
2629 
2630 
2631         //  Transcribe points to a buffer
2632 
2633             double *ppd = (double *)malloc(nPoints * 2 *sizeof(double));
2634             double *ppr = ppd;
2635 
2636             for( int i = 0; i < nPoints; i++ ) {
2637                 OGRPoint p;
2638                 pLS->getPoint( i, &p );
2639 
2640                 //  Calculate SM from chart common reference point
2641                 double easting, northing;
2642                 toSM( p.getY(), p.getX(), m_ref_lat, m_ref_lon, &easting, &northing );
2643 
2644                 *ppr++ = easting;
2645                 *ppr++ = northing;
2646             }
2647 
2648             //      Reduce the LOD of this linestring
2649             std::vector<int> index_keep;
2650             if(nPoints > 5 && (m_LOD_meters > .01)){
2651                 index_keep.push_back(0);
2652                 index_keep.push_back(nPoints-1);
2653 
2654                 DouglasPeucker(ppd, 0, nPoints-1, m_LOD_meters, &index_keep);
2655                 //               printf("DP Reduction: %d/%d\n", index_keep.GetCount(), nPoints);
2656 
2657             }
2658             else {
2659                 index_keep.resize(nPoints);
2660                 for(int i = 0 ; i < nPoints ; i++)
2661                     index_keep[i] = i;
2662             }
2663 
2664 
2665             //  Store the point count in the payload
2666             int nPointReduced = index_keep.size();
2667             *(int *)pRun = nPointReduced;
2668             pRun += sizeof(int);
2669 
2670             //  transcribe the (possibly) reduced linestring to the payload
2671 
2672             //  Grow the payload buffer
2673             int new_size_red = payloadSize + (nPointReduced* 2 * sizeof(float)) ;
2674             pPayload = (uint8_t *)realloc(pPayload, new_size_red );
2675             pRun = pPayload + payloadSize;              //  recalculate the running pointer,
2676                                                         //  since realloc may have moved memory
2677             payloadSize = new_size_red;
2678 
2679             float *npp = (float *)pRun;
2680             float *npp_run = npp;
2681             ppr = ppd;
2682             for(int ip = 0 ; ip < nPoints ; ip++)
2683             {
2684                 double x = *ppr++;
2685                 double y = *ppr++;
2686 
2687                 for(unsigned int j=0 ; j < index_keep.size() ; j++){
2688                     if(index_keep[j] == ip){
2689                         *npp_run++ = x;
2690                         *npp_run++ = y;
2691                         pRun += 2 * sizeof(float);
2692                         break;
2693                     }
2694                 }
2695             }
2696 
2697             nFeatures++;
2698 
2699             free( ppd );
2700         }
2701 
2702         //    Next vector record
2703         feid++;
2704         delete pEdgeVectorRecordFeature;
2705         pEdgeVectorRecordFeature = poReader->ReadVector( feid, RCNM_VE );
2706 
2707     }   // while
2708 
2709     // Now we know the payload length and the Feature count
2710     if(nFeatures){
2711         //  Now write the record out
2712         OSENC_VET_Record record;
2713 
2714         record.record_type = VECTOR_EDGE_NODE_TABLE_RECORD;
2715         record.record_length = sizeof(OSENC_VET_Record_Base) + payloadSize + sizeof(uint32_t);
2716 
2717         // Write out the record
2718         stream->Write(&record , sizeof(OSENC_VET_Record_Base));
2719 
2720         //  Write out the Feature(Object) count
2721         stream->Write(&nFeatures , sizeof(uint32_t));
2722 
2723         //  Write out the payload
2724         stream->Write(pPayload, payloadSize);
2725 
2726     }
2727     //  All done with buffer
2728     free(pPayload);
2729 
2730     //  Reset the S57Reader options
2731     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF" );
2732     poReader->SetOptions( papszReaderOptions );
2733     CSLDestroy( papszReaderOptions );
2734 
2735 
2736 }
2737 
2738 
CreateSENCVectorConnectedTableRecord200(Osenc_outstream * stream,S57Reader * poReader)2739 void Osenc::CreateSENCVectorConnectedTableRecord200( Osenc_outstream *stream, S57Reader *poReader )
2740 {
2741     //  We create the payload first, so we can calculate the total record length
2742     uint8_t *pPayload = NULL;
2743     int payloadSize = 0;
2744     uint8_t *pRun = pPayload;
2745 
2746     //  Set up the S57Reader options, adding RETURN_PRIMITIVES
2747     char ** papszReaderOptions = NULL;
2748     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
2749     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES, "ON" );
2750     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON" );
2751     poReader->SetOptions( papszReaderOptions );
2752 
2753 
2754     int feid = 0;
2755     OGRPoint *pP;
2756     OGRGeometry *pGeo;
2757     OGRFeature *pConnNodeRecordFeature = poReader->ReadVector( feid, RCNM_VC );
2758     int featureCount = 0;
2759 
2760     //  Read all the ConnectedVector Features
2761     while( NULL != pConnNodeRecordFeature ) {
2762 
2763 
2764         if( pConnNodeRecordFeature->GetGeometryRef() != NULL ) {
2765             pGeo = pConnNodeRecordFeature->GetGeometryRef();
2766             if( pGeo->getGeometryType() == wkbPoint ) {
2767 
2768 
2769                 int new_size = payloadSize + sizeof(int) + (2 * sizeof(float));
2770                 pPayload = (uint8_t *)realloc(pPayload, new_size );
2771                 pRun = pPayload + payloadSize;                   //  recalculate the running pointer,
2772                                                                  //  since realloc may have moved memory
2773                 payloadSize = new_size;
2774 
2775                 //  Fetch and store the Record ID
2776                 int record_id = pConnNodeRecordFeature->GetFieldAsInteger( "RCID" );
2777                 *(int*)pRun = record_id;
2778                 pRun += sizeof(int);
2779 
2780 
2781 
2782                 pP = (OGRPoint *) pGeo;
2783 
2784                 //  Calculate SM from chart common reference point
2785                 double easting, northing;
2786                 toSM( pP->getY(), pP->getX(), m_ref_lat, m_ref_lon, &easting, &northing );
2787 
2788 //                 MyPoint pd;
2789 //                 pd.x = easting;
2790 //                 pd.y = northing;
2791 //                 memcpy(pRun, &pd, sizeof(MyPoint));
2792                 float *ps = (float *)pRun;
2793                 *ps++ = easting;
2794                 *ps = northing;
2795 
2796                 featureCount++;
2797             }
2798         }
2799 
2800         //    Next vector record
2801         feid++;
2802         delete pConnNodeRecordFeature;
2803         pConnNodeRecordFeature = poReader->ReadVector( feid, RCNM_VC );
2804     }   // while
2805 
2806     // Now we know the payload length and the Feature count
2807 
2808     //  Now write the record out
2809     if(featureCount){
2810         OSENC_VCT_Record record;
2811 
2812         record.record_type = VECTOR_CONNECTED_NODE_TABLE_RECORD;
2813         record.record_length = sizeof(OSENC_VCT_Record_Base) + payloadSize + sizeof(int);
2814 
2815         // Write out the record
2816         stream->Write(&record , sizeof(OSENC_VCT_Record_Base));
2817 
2818         //  Write out the Feature(Object) count
2819         stream->Write(&featureCount , sizeof(uint32_t));
2820 
2821         //  Write out the payload
2822         stream->Write(pPayload, payloadSize);
2823     }
2824 
2825     //  All done with buffer
2826     free(pPayload);
2827 
2828     //  Reset the S57Reader options
2829     papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF" );
2830     poReader->SetOptions( papszReaderOptions );
2831     CSLDestroy( papszReaderOptions );
2832 
2833 }
2834 
2835 
2836 
2837 
CreateSENCRecord200(OGRFeature * pFeature,Osenc_outstream * stream,int mode,S57Reader * poReader)2838 bool Osenc::CreateSENCRecord200( OGRFeature *pFeature, Osenc_outstream *stream, int mode, S57Reader *poReader )
2839 {
2840     //TODO
2841 //    if(pFeature->GetFID() == 207)
2842 //        int yyp = 4;
2843 
2844     //  Create the Feature Identification Record
2845 
2846     // Fetch the S57 Standard Object Class identifier
2847     OGRFeatureDefn *pFD = pFeature->GetDefnRef();
2848     int nOBJL = pFD->GetOBJL();
2849 
2850     OGRGeometry *pGeo = pFeature->GetGeometryRef();
2851     OGRwkbGeometryType gType = pGeo->getGeometryType();
2852 
2853     int primitive = 0;
2854     switch(gType){
2855 
2856         case wkbLineString:
2857             primitive = GEO_LINE;
2858             break;
2859         case wkbPoint:
2860             primitive = GEO_POINT;
2861             break;
2862         case wkbPolygon:
2863             primitive = GEO_AREA;
2864             break;
2865         default:
2866             primitive = 0;
2867     }
2868 
2869 
2870     if(!WriteFIDRecord200( stream, nOBJL, pFeature->GetFID(), primitive) )
2871         return false;
2872 
2873 
2874     #define MAX_HDR_LINE    400
2875 
2876 //    char line[MAX_HDR_LINE + 1];
2877     wxString sheader;
2878 
2879 //    fprintf( fpOut, "OGRFeature(%s):%ld\n", pFeature->GetDefnRef()->GetName(), pFeature->GetFID() );
2880 
2881     //  In a loop, fetch attributes, and create OSENC Feature Attribute Records
2882     //  In the interests of output file size, DO NOT report fields that are not set.
2883 
2884     //TODO Debugging
2885 //     if(!strncmp(pFeature->GetDefnRef()->GetName(), "BOYLAT", 6))
2886 //         int yyp = 4;
2887 
2888      if(pFeature->GetFID() == 290)
2889          int yyp = 4;
2890 
2891     int payloadLength = 0;
2892     void *payloadBuffer = NULL;
2893     unsigned int payloadBufferLength = 0;
2894 
2895     for( int iField = 0; iField < pFeature->GetFieldCount(); iField++ ) {
2896         if( pFeature->IsFieldSet( iField ) ) {
2897             if( ( iField == 1 ) || ( iField > 7 ) ) {
2898                 OGRFieldDefn *poFDefn = pFeature->GetDefnRef()->GetFieldDefn( iField );
2899                 //const char *pType = OGRFieldDefn::GetFieldTypeName( poFDefn->GetType() );
2900                 const char *pAttrName = poFDefn->GetNameRef();
2901                 const char *pAttrVal = pFeature->GetFieldAsString( iField );
2902 
2903                 //  Use the OCPN Registrar Manager to map attribute acronym to an identifier.
2904                 //  The mapping is defined by the file {csv_dir}/s57attributes.csv
2905                 int attributeID = m_pRegistrarMan->getAttributeID(pAttrName);
2906 
2907                 //  Determine the {attribute_value_type} needed in the record
2908                 int OGRvalueType = (int)poFDefn->GetType();
2909                 int valueType = 0;
2910 
2911                 //Check for special cases
2912                 if( -1 == attributeID){
2913                     if(!strncmp(pAttrName, "PRIM", 4) ){
2914                         attributeID = ATTRIBUTE_ID_PRIM;
2915                     }
2916                 }
2917 
2918 #if 0
2919                 /** Simple 32bit integer */                   OFTInteger = 0,
2920                 /** List of 32bit integers */                 OFTIntegerList = 1,
2921                 /** Double Precision floating point */        OFTReal = 2,
2922                 /** List of doubles */                        OFTRealList = 3,
2923                 /** String of ASCII chars */                  OFTString = 4,
2924                 /** Array of strings */                       OFTStringList = 5,
2925                 /** Double byte string (unsupported) */       OFTWideString = 6,
2926                 /** List of wide strings (unsupported) */     OFTWideStringList = 7,
2927                 /** Raw Binary data (unsupported) */          OFTBinary = 8
2928 #endif
2929                 switch(OGRvalueType){
2930                     case 0:             // Single integer
2931                     {
2932                         valueType = OGRvalueType;
2933 
2934                         if(payloadBufferLength < 4){
2935                             payloadBuffer = realloc(payloadBuffer, 4);
2936                             payloadBufferLength = 4;
2937                         }
2938 
2939                         int aValue = pFeature->GetFieldAsInteger( iField );
2940                         memcpy(payloadBuffer, &aValue, sizeof(int) );
2941                         payloadLength = sizeof(int);
2942 
2943                         break;
2944                     }
2945                     case 1:             // Integer list
2946                     {
2947                         valueType = OGRvalueType;
2948 
2949                         int nCount = 0;
2950                         const int *aValueList = pFeature->GetFieldAsIntegerList( iField, &nCount );
2951 
2952                         if(payloadBufferLength < nCount * sizeof(int)){
2953                             payloadBuffer = realloc(payloadBuffer, nCount * sizeof(int));
2954                             payloadBufferLength = nCount * sizeof(int);
2955                         }
2956 
2957                         int *pBuffRun = (int *)payloadBuffer;
2958                         for(int i=0 ; i < nCount ; i++){
2959                             *pBuffRun++ = aValueList[i];
2960                         }
2961                         payloadLength = nCount * sizeof(int);
2962 
2963                         break;
2964                     }
2965                     case 2:             // Single double precision real
2966                     {
2967                         valueType = OGRvalueType;
2968 
2969                         if(payloadBufferLength < sizeof(double)){
2970                             payloadBuffer = realloc(payloadBuffer, sizeof(double));
2971                             payloadBufferLength = sizeof(double);
2972                         }
2973 
2974                         double aValue = pFeature->GetFieldAsDouble( iField );
2975                         memcpy(payloadBuffer, &aValue, sizeof(double) );
2976                         payloadLength = sizeof(double);
2977 
2978                         break;
2979                     }
2980 
2981                     case 3:             // List of double precision real
2982                     {
2983                         valueType = OGRvalueType;
2984 
2985                         int nCount = 0;
2986                         const double *aValueList = pFeature->GetFieldAsDoubleList( iField, &nCount );
2987 
2988                         if(payloadBufferLength < nCount * sizeof(double)){
2989                             payloadBuffer = realloc(payloadBuffer, nCount * sizeof(double));
2990                             payloadBufferLength = nCount * sizeof(double);
2991                         }
2992 
2993                         double *pBuffRun = (double *)payloadBuffer;
2994                         for(int i=0 ; i < nCount ; i++){
2995                             *pBuffRun++ = aValueList[i];
2996                         }
2997                         payloadLength = nCount * sizeof(double);
2998 
2999                         break;
3000                     }
3001 
3002                     case 4:             // Ascii String
3003                     {
3004                         valueType = OGRvalueType;
3005                         const char *pAttrVal = pFeature->GetFieldAsString( iField );
3006 
3007                         wxString wxAttrValue;
3008 
3009                         if( (0 == strncmp("NOBJNM",pAttrName, 6) ) ||
3010                             (0 == strncmp("NINFOM",pAttrName, 6) ) ||
3011                             (0 == strncmp("NPLDST",pAttrName, 6) ) ||
3012                             (0 == strncmp("NTXTDS",pAttrName, 6) ) )
3013                             {
3014                                 if( poReader->GetNall() == 2) {     // ENC is using UCS-2 / UTF-16 encoding
3015                                     wxMBConvUTF16 conv;
3016                                     wxString att_conv(pAttrVal, conv);
3017                                     att_conv.RemoveLast();      // Remove the \037 that terminates UTF-16 strings in S57
3018                                     att_conv.Replace(_T("\n"), _T("|") );  //Replace  <new line> with special break character
3019                                     wxAttrValue = att_conv;
3020                                     }
3021                                  else if( poReader->GetNall() == 1) {     // ENC is using Lex level 1 (ISO 8859_1) encoding
3022                                     wxCSConv conv(_T("iso8859-1") );
3023                                     wxString att_conv(pAttrVal, conv);
3024                                     wxAttrValue = att_conv;
3025                                 }
3026                             }
3027                          else{
3028                             if( poReader->GetAall() == 1) {     // ENC is using Lex level 1 (ISO 8859_1) encoding for "General Text"
3029                                 wxCSConv conv(_T("iso8859-1") );
3030                                 wxString att_conv(pAttrVal, conv);
3031                                 wxAttrValue = att_conv;
3032                                 }
3033                             else
3034                                 wxAttrValue = wxString(pAttrVal);  // ENC must be using Lex level 0 (ASCII) encoding for "General Text"
3035                         }
3036 
3037                         unsigned int stringPayloadLength = 0;
3038 
3039                         wxCharBuffer buffer;
3040                         if(wxAttrValue.Length()){               // need to explicitely encode as UTF8
3041                             buffer=wxAttrValue.ToUTF8();
3042                             pAttrVal = buffer.data();
3043                             stringPayloadLength = strlen(buffer.data());
3044                         }
3045 
3046                         if(stringPayloadLength){
3047                             if(payloadBufferLength < stringPayloadLength + 1){
3048                                 payloadBuffer = realloc(payloadBuffer, stringPayloadLength + 1);
3049                                 payloadBufferLength = stringPayloadLength + 1;
3050                             }
3051 
3052                             strcpy((char *)payloadBuffer, pAttrVal );
3053                             payloadLength = stringPayloadLength + 1;
3054                         }
3055                         else
3056                             attributeID = -1;                   // cancel this attribute record
3057 
3058 
3059                         break;
3060                     }
3061 
3062                     default:
3063                         valueType = -1;
3064                         break;
3065                 }
3066 
3067                 if( -1 != attributeID){
3068                     // Build the record
3069                     int recordLength = sizeof( OSENC_Attribute_Record_Base ) + payloadLength;
3070 
3071                     //  Get a reference to the class persistent buffer
3072                     unsigned char *pBuffer = getBuffer( recordLength );
3073 
3074                     OSENC_Attribute_Record *pRecord = (OSENC_Attribute_Record *)pBuffer;
3075                     memset(pRecord, 0, sizeof(OSENC_Attribute_Record));
3076                     pRecord->record_type = FEATURE_ATTRIBUTE_RECORD;
3077                     pRecord->record_length = recordLength;
3078                     pRecord->attribute_type = attributeID;
3079                     pRecord->attribute_value_type = valueType;
3080                     memcpy( &pRecord->payload, payloadBuffer, payloadLength );
3081 
3082                     // Write the record out....
3083                     size_t targetCount = recordLength;
3084                     if(!stream->Write(pBuffer, targetCount).IsOk()) {
3085                         free( payloadBuffer );
3086                         return false;
3087                     }
3088 
3089                 }
3090 
3091             }
3092         }
3093     }
3094     if( wkbPoint == pGeo->getGeometryType() ) {
3095         OGRPoint *pp = (OGRPoint *) pGeo;
3096         int nqual = pp->getnQual();
3097         if( 10 != nqual )                    // only add attribute if nQual is not "precisely known"
3098         {
3099             int attributeID = m_pRegistrarMan->getAttributeID("QUAPOS");
3100             int valueType = 0;
3101             if( -1 != attributeID){
3102                 if(payloadBufferLength < 4){
3103                     payloadBuffer = realloc(payloadBuffer, 4);
3104                     payloadBufferLength = 4;
3105                 }
3106 
3107                 memcpy(payloadBuffer, &nqual, sizeof(int) );
3108                 payloadLength = sizeof(int);
3109                 // Build the record
3110                 int recordLength = sizeof( OSENC_Attribute_Record_Base ) + payloadLength;
3111 
3112                 //  Get a reference to the class persistent buffer
3113                 unsigned char *pBuffer = getBuffer( recordLength );
3114 
3115                 OSENC_Attribute_Record *pRecord = (OSENC_Attribute_Record *)pBuffer;
3116                 memset(pRecord, 0, sizeof(OSENC_Attribute_Record));
3117                 pRecord->record_type = FEATURE_ATTRIBUTE_RECORD;
3118                 pRecord->record_length = recordLength;
3119                 pRecord->attribute_type = attributeID;
3120                 pRecord->attribute_value_type = valueType;
3121                 memcpy( &pRecord->payload, payloadBuffer, payloadLength );
3122 
3123                 // Write the record out....
3124                 size_t targetCount = recordLength;
3125                 if(!stream->Write(pBuffer, targetCount).IsOk()) {
3126                         free( payloadBuffer );
3127                         return false;
3128                 }
3129             }
3130         }
3131     }
3132 
3133     free( payloadBuffer );
3134 
3135 #if 0
3136     //    Special geometry cases
3137     ///172
3138     if( wkbPoint == pGeo->getGeometryType() ) {
3139         OGRPoint *pp = (OGRPoint *) pGeo;
3140         int nqual = pp->getnQual();
3141         if( 10 != nqual )                    // only add attribute if nQual is not "precisely known"
3142                 {
3143                     snprintf( line, MAX_HDR_LINE - 2, "  %s (%c) = %d", "QUALTY", 'I', nqual );
3144                     sheader += wxString( line, wxConvUTF8 );
3145                     sheader += '\n';
3146                 }
3147 
3148     }
3149 
3150     if( mode == 1 ) {
3151         sprintf( line, "  %s %f %f\n", pGeo->getGeometryName(), m_ref_lat, m_ref_lon );
3152         sheader += wxString( line, wxConvUTF8 );
3153     }
3154 
3155     wxCharBuffer buffer=sheader.ToUTF8();
3156     fprintf( fpOut, "HDRLEN=%lu\n", (unsigned long) strlen(buffer) );
3157     fwrite( buffer.data(), 1, strlen(buffer), fpOut );
3158 
3159 #endif
3160 
3161     if( ( pGeo != NULL ) ) {
3162 
3163         wxString msg;
3164 
3165         OGRwkbGeometryType gType = pGeo->getGeometryType();
3166         switch( gType ){
3167 
3168             case wkbLineString: {
3169 
3170                 if( !CreateLineFeatureGeometryRecord200(poReader, pFeature, stream) )
3171                     return false;
3172 
3173                 break;
3174             }
3175 
3176             case wkbPoint: {
3177 
3178                 OSENC_PointGeometry_Record record;
3179                 record.record_type = FEATURE_GEOMETRY_RECORD_POINT;
3180                 record.record_length = sizeof( record );
3181 
3182                 int wkb_len = pGeo->WkbSize();
3183                 unsigned char *pwkb_buffer = (unsigned char *) malloc( wkb_len );
3184 
3185                 //  Get the GDAL data representation
3186                 pGeo->exportToWkb( wkbNDR, pwkb_buffer );
3187 
3188                 int nq_len = 4;                                     // nQual length
3189                 unsigned char *ps = pwkb_buffer;
3190 
3191                 ps += 5 + nq_len;
3192                 double *psd = (double *) ps;
3193 
3194                 double lat, lon;
3195                 #ifdef __ARM_ARCH
3196                 __attribute__((aligned(16))) double lata, lona;
3197                 unsigned char *pucsd = (unsigned char *)psd;
3198 
3199                 memcpy(&lona, pucsd, sizeof(double));
3200                 pucsd += sizeof(double);
3201                 memcpy(&lata, pucsd, sizeof(double));
3202                 lon = lona;
3203                 lat = lata;
3204                 #else
3205                 lon = *psd++;                                      // fetch the point
3206                 lat = *psd;
3207                 #endif
3208 
3209                 free( pwkb_buffer );
3210 
3211                 record.lat = lat;
3212                 record.lon = lon;
3213 
3214                 // Write the record out....
3215                 size_t targetCount = record.record_length;
3216                 if(!stream->Write(&record, targetCount).IsOk())
3217                     return false;
3218 
3219                 break;
3220             }
3221 
3222             case wkbMultiPoint:
3223             case wkbMultiPoint25D:{
3224 
3225                 if(!CreateMultiPointFeatureGeometryRecord200( pFeature, stream))
3226                     return false;
3227                 break;
3228             }
3229 
3230 #if 1
3231                 //      Special case, polygons are handled separately
3232                 case wkbPolygon: {
3233 
3234                      if( !CreateAreaFeatureGeometryRecord200(poReader, pFeature, stream) )
3235                          return false;
3236 
3237                     break;
3238                 }
3239 #endif
3240                 //      All others
3241                 default:
3242                     msg = _T("   Warning: Unimplemented ogr geotype record ");
3243                     wxLogMessage( msg );
3244 
3245                     break;
3246         }       // switch
3247 
3248     }
3249     return true;
3250 }
3251 
3252 
3253 //      Build PolyGeo Object from OSENC200 file record
3254 //      Return an integer count of bytes consumed from the record in creating the PolyTessGeo
BuildPolyTessGeo(_OSENC_AreaGeometry_Record_Payload * record,unsigned char ** next_byte)3255 PolyTessGeo *Osenc::BuildPolyTessGeo(_OSENC_AreaGeometry_Record_Payload *record, unsigned char **next_byte )
3256 {
3257 
3258     PolyTessGeo *pPTG = new PolyTessGeo();
3259 
3260     pPTG->SetExtents(record->extent_w_lon, record->extent_s_lat, record->extent_e_lon, record->extent_n_lat);
3261 
3262     unsigned int n_TriPrim = record->triprim_count;
3263     int nContours = record->contour_count;
3264 
3265     //  Get a pointer to the payload
3266     void *payLoad = &record->payLoad;
3267 
3268     //  skip over the contour vertex count array, for now  TODO
3269     //uint8_t *pTriPrims = (uint8_t *)payLoad + (nContours * sizeof(uint32_t));
3270 
3271 
3272     //  Create the head of the linked list of TriPrims
3273     PolyTriGroup *ppg = new PolyTriGroup;
3274     ppg->m_bSMSENC = true;
3275     ppg->data_type = DATA_TYPE_DOUBLE;
3276 
3277 
3278     ppg->nContours = nContours;
3279 
3280     ppg->pn_vertex = (int *)malloc(nContours * sizeof(int));
3281     int *pctr = ppg->pn_vertex;
3282 
3283     //  The point count array is the first element in the payload, length is known
3284     int *contour_pointcount_array_run = (int*)payLoad;
3285     for(int i=0 ; i < nContours ; i++){
3286         *pctr++ = *contour_pointcount_array_run++;
3287     }
3288 
3289 
3290     //  Read Raw Geometry
3291     ppg->pgroup_geom = NULL;
3292 
3293 
3294     //  Now the triangle primitives
3295 
3296     TriPrim **p_prev_triprim = &(ppg->tri_prim_head);
3297 
3298     //  Read the PTG_Triangle Geometry in a loop
3299     unsigned int tri_type;
3300     int nvert;
3301     int nvert_max = 0;
3302     int total_byte_size = 2 * sizeof(float);
3303 
3304     uint8_t *pPayloadRun = (uint8_t *)contour_pointcount_array_run; //Points to the start of the triangle primitives
3305 
3306     for(unsigned int i=0 ; i < n_TriPrim ; i++){
3307         tri_type = *pPayloadRun++;
3308         nvert = *(uint32_t *)pPayloadRun;
3309         pPayloadRun += sizeof(uint32_t);
3310 
3311 
3312         TriPrim *tp = new TriPrim;
3313         *p_prev_triprim = tp;                               // make the link
3314         p_prev_triprim = &(tp->p_next);
3315         tp->p_next = NULL;
3316 
3317         tp->type = tri_type;
3318         tp->nVert = nvert;
3319 
3320         nvert_max = wxMax(nvert_max, nvert);       // Keep a running tab of largest vertex count
3321 
3322         //  Read the triangle primitive bounding box as lat/lon
3323         double *pbb = (double *)pPayloadRun;
3324 
3325         double      minxt, minyt, maxxt, maxyt;
3326 
3327         #ifdef __ARM_ARCH
3328         double __attribute__((aligned(16))) abox[4];
3329         unsigned char *pucbb = (unsigned char *)pPayloadRun;
3330         memcpy(&abox[0], pucbb, 4 * sizeof(double));
3331 
3332         minxt = abox[0];
3333         maxxt = abox[1];
3334         minyt = abox[2];
3335         maxyt = abox[3];
3336         #else
3337         minxt = *pbb++;
3338         maxxt = *pbb++;
3339         minyt = *pbb++;
3340         maxyt = *pbb;
3341         #endif
3342 
3343         tp->tri_box.Set(minyt, minxt, maxyt, maxxt);
3344 
3345         pPayloadRun += 4 * sizeof(double);
3346 
3347 
3348         int byte_size = nvert * 2 * sizeof(float);              // the vertices
3349         total_byte_size += byte_size;
3350 
3351         tp->p_vertex = (double *)pPayloadRun;
3352 
3353 
3354         pPayloadRun += byte_size;
3355 
3356     }
3357 
3358     if(next_byte)
3359         *next_byte = pPayloadRun;
3360 
3361     //  Convert the vertex arrays into a single float memory allocation to enable efficient access later
3362     unsigned char *vbuf = (unsigned char *)malloc(total_byte_size);
3363 
3364     TriPrim *p_tp = ppg->tri_prim_head;
3365     unsigned char *p_run = vbuf;
3366     while( p_tp ) {
3367             memcpy(p_run, p_tp->p_vertex, p_tp->nVert * 2 * sizeof(float));
3368             p_tp->p_vertex = (double  *)p_run;
3369             p_run += p_tp->nVert * 2 * sizeof(float);
3370             p_tp = p_tp->p_next; // pick up the next in chain
3371     }
3372     ppg->bsingle_alloc = true;
3373     ppg->single_buffer = vbuf;
3374     ppg->single_buffer_size = total_byte_size;
3375     ppg->data_type = DATA_TYPE_FLOAT;
3376 
3377     pPTG->SetPPGHead(ppg);
3378     pPTG->SetnVertexMax( nvert_max );
3379 
3380 
3381     pPTG->Set_OK( true );
3382 
3383 
3384     return pPTG;
3385 
3386 }
3387 
3388 
3389 
CreateCOVRTables(S57Reader * poReader,S57ClassRegistrar * poRegistrar)3390 bool Osenc::CreateCOVRTables( S57Reader *poReader, S57ClassRegistrar *poRegistrar )
3391 {
3392     poReader->Rewind();
3393 
3394     OGRFeature *pFeat;
3395     int catcov;
3396     float LatMax, LatMin, LonMax, LonMin;
3397     LatMax = -90.;
3398     LatMin = 90.;
3399     LonMax = -179.;
3400     LonMin = 179.;
3401 
3402     m_pCOVRTablePoints = NULL;
3403     m_pCOVRTable = NULL;
3404 
3405     //  Create arrays to hold geometry objects temporarily
3406     MyFloatPtrArray *pAuxPtrArray = new MyFloatPtrArray;
3407     std::vector<int> auxCntArray, noCovrCntArray;
3408 
3409     MyFloatPtrArray *pNoCovrPtrArray = new MyFloatPtrArray;
3410 
3411     //Get the first M_COVR object
3412     pFeat = GetChartFirstM_COVR( catcov, poReader, poRegistrar );
3413 
3414     while( pFeat ) {
3415         //    Get the next M_COVR feature, and create possible additional entries for COVR
3416         OGRPolygon *poly = (OGRPolygon *) ( pFeat->GetGeometryRef() );
3417         OGRLinearRing *xring = poly->getExteriorRing();
3418 
3419         int npt = xring->getNumPoints();
3420 
3421         float *pf = NULL;
3422 
3423         if( npt >= 3 ) {
3424             pf = (float *) malloc( 2 * npt * sizeof(float) );
3425             float *pfr = pf;
3426 
3427             for( int i = 0; i < npt; i++ ) {
3428                 OGRPoint p;
3429                 xring->getPoint( i, &p );
3430 
3431                 if( catcov == 1 ) {
3432                     LatMax = fmax(LatMax, p.getY());
3433                     LatMin = fmin(LatMin, p.getY());
3434                     LonMax = fmax(LonMax, p.getX());
3435                     LonMin = fmin(LonMin, p.getX());
3436                 }
3437 
3438                 pfr[0] = p.getY();             // lat
3439                 pfr[1] = p.getX();             // lon
3440 
3441                 pfr += 2;
3442             }
3443 
3444             if( catcov == 1 ) {
3445                 pAuxPtrArray->Add( pf );
3446                 auxCntArray.push_back( npt );
3447             }
3448             else if( catcov == 2 ){
3449                 pNoCovrPtrArray->Add( pf );
3450                 noCovrCntArray.push_back( npt );
3451             }
3452             else
3453                 free( pf );
3454         }
3455 
3456 
3457         delete pFeat;
3458         pFeat = GetChartNextM_COVR( catcov, poReader );
3459     }         // while
3460 
3461     //    Allocate the storage
3462 
3463     m_nCOVREntries = auxCntArray.size();
3464 
3465     //    If only one M_COVR,CATCOV=1 object was found,
3466     //    assign the geometry to the one and only COVR
3467 
3468     if( m_nCOVREntries == 1 ) {
3469         m_pCOVRTablePoints = (int *) malloc( sizeof(int) );
3470         *m_pCOVRTablePoints = auxCntArray[0];
3471         m_pCOVRTable = (float **) malloc( sizeof(float *) );
3472         *m_pCOVRTable = (float *) malloc( auxCntArray[0] * 2 * sizeof(float) );
3473         memcpy( *m_pCOVRTable, pAuxPtrArray->Item( 0 ),
3474                 auxCntArray[0] * 2 * sizeof(float) );
3475     }
3476 
3477     else if( m_nCOVREntries > 1 ) {
3478         //    Create new COVR entries
3479         m_pCOVRTablePoints = (int *) malloc( m_nCOVREntries * sizeof(int) );
3480         m_pCOVRTable = (float **) malloc( m_nCOVREntries * sizeof(float *) );
3481 
3482         for( unsigned int j = 0; j < (unsigned int) m_nCOVREntries; j++ ) {
3483             m_pCOVRTablePoints[j] = auxCntArray[j];
3484             m_pCOVRTable[j] = (float *) malloc( auxCntArray[j] * 2 * sizeof(float) );
3485             memcpy( m_pCOVRTable[j], pAuxPtrArray->Item(j),
3486                     auxCntArray[j] * 2 * sizeof(float) );
3487         }
3488     }
3489 
3490     else                                     // strange case, found no CATCOV=1 M_COVR objects
3491         {
3492             wxString msg( _T("   ENC contains no useable M_COVR, CATCOV=1 features:  ") );
3493             msg.Append( m_FullPath000 );
3494             wxLogMessage( msg );
3495         }
3496 
3497 
3498         //      And for the NoCovr regions
3499         m_nNoCOVREntries = noCovrCntArray.size();
3500 
3501     if( m_nNoCOVREntries ) {
3502         //    Create new NoCOVR entries
3503         m_pNoCOVRTablePoints = (int *) malloc( m_nNoCOVREntries * sizeof(int) );
3504         m_pNoCOVRTable = (float **) malloc( m_nNoCOVREntries * sizeof(float *) );
3505 
3506         for( unsigned int j = 0; j < (unsigned int) m_nNoCOVREntries; j++ ) {
3507             int npoints = noCovrCntArray[j];
3508             m_pNoCOVRTablePoints[j] = npoints;
3509             m_pNoCOVRTable[j] = (float *) malloc( npoints * 2 * sizeof(float) );
3510             memcpy( m_pNoCOVRTable[j], pNoCovrPtrArray->Item( j ),
3511                     npoints * 2 * sizeof(float) );
3512         }
3513     }
3514     else {
3515         m_pNoCOVRTablePoints = NULL;
3516         m_pNoCOVRTable = NULL;
3517     }
3518 
3519      for( unsigned int j = 0; j < (unsigned int) m_nNoCOVREntries; j++ )
3520          free(pNoCovrPtrArray->Item(j));
3521      for( unsigned int j = 0; j < (unsigned int) m_nCOVREntries; j++ )
3522          free(pAuxPtrArray->Item(j));
3523 
3524 
3525     delete pAuxPtrArray;
3526     delete pNoCovrPtrArray;
3527 
3528 
3529     if( 0 == m_nCOVREntries ) {                        // fallback
3530         wxString msg( _T("   ENC contains no M_COVR features:  ") );
3531         msg.Append( m_FullPath000 );
3532         wxLogMessage( msg );
3533 
3534         msg =  _T("   Calculating Chart Extents as fallback.");
3535         wxLogMessage( msg );
3536 
3537         OGREnvelope Env;
3538 
3539         if( poReader->GetExtent( &Env, true ) == OGRERR_NONE ) {
3540 
3541             LatMax = Env.MaxY;
3542             LonMax = Env.MaxX;
3543             LatMin = Env.MinY;
3544             LonMin = Env.MinX;
3545 
3546             m_nCOVREntries = 1;
3547             m_pCOVRTablePoints = (int *) malloc( sizeof(int) );
3548             *m_pCOVRTablePoints = 4;
3549             m_pCOVRTable = (float **) malloc( sizeof(float *) );
3550             float *pf = (float *) malloc( 2 * 4 * sizeof(float) );
3551             *m_pCOVRTable = pf;
3552             float *pfe = pf;
3553 
3554             *pfe++ = LatMax;
3555             *pfe++ = LonMin;
3556 
3557             *pfe++ = LatMax;
3558             *pfe++ = LonMax;
3559 
3560             *pfe++ = LatMin;
3561             *pfe++ = LonMax;
3562 
3563             *pfe++ = LatMin;
3564             *pfe++ = LonMin;
3565 
3566         } else {
3567             wxString msg( _T("   Cannot calculate Extents for ENC:  ") );
3568             msg.Append( m_FullPath000 );
3569             wxLogMessage( msg );
3570 
3571             return false;                     // chart is completely unusable
3572         }
3573     }
3574 
3575     //    Populate the oSENC clone of the chart's extent structure
3576      m_extent.NLAT = LatMax;
3577      m_extent.SLAT = LatMin;
3578      m_extent.ELON = LonMax;
3579      m_extent.WLON = LonMin;
3580 
3581 
3582     return true;
3583 }
3584 
3585 
GetChartFirstM_COVR(int & catcov,S57Reader * pENCReader,S57ClassRegistrar * poRegistrar)3586 OGRFeature *Osenc::GetChartFirstM_COVR( int &catcov, S57Reader *pENCReader, S57ClassRegistrar *poRegistrar )
3587 {
3588   OGRFeature * rv = NULL;
3589 
3590     if( ( NULL != pENCReader ) && ( NULL != poRegistrar ) ) {
3591 
3592         //      Select the proper class
3593         poRegistrar->SelectClass( "M_COVR" );
3594 
3595 //        OGRFeatureDefn *M_COVRdef = S57GenerateObjectClassDefn( poRegistrar, 302, 0);
3596 
3597         //      find this feature
3598         bool bFound = false;
3599         OGRFeature *pobjectDef = pENCReader->ReadNextFeature(/*M_COVRdef*/ );
3600         while(!bFound){
3601             if( pobjectDef ) {
3602 
3603                 OGRFeatureDefn *poDefn = pobjectDef->GetDefnRef();
3604                 if(poDefn && (poDefn->GetOBJL() == 302/*poRegistrar->GetOBJL()*/)){
3605                 //  Fetch the CATCOV attribute
3606                     catcov = pobjectDef->GetFieldAsInteger( "CATCOV" );
3607                     rv = pobjectDef;
3608                     break;
3609                 }
3610                 else
3611                     delete pobjectDef;
3612             }
3613             else{
3614                 break;
3615             }
3616             pobjectDef = pENCReader->ReadNextFeature( );
3617         }
3618     }
3619 
3620     return rv;
3621 }
3622 
GetChartNextM_COVR(int & catcov,S57Reader * pENCReader)3623 OGRFeature *Osenc::GetChartNextM_COVR( int &catcov, S57Reader *pENCReader )
3624 {
3625     catcov = -1;
3626 
3627 
3628 
3629     if( pENCReader ) {
3630         bool bFound = false;
3631         OGRFeature *pobjectDef = pENCReader->ReadNextFeature( );
3632 
3633         while(!bFound){
3634             if( pobjectDef ) {
3635                 OGRFeatureDefn *poDefn = pobjectDef->GetDefnRef();
3636                 if(poDefn && (poDefn->GetOBJL() == 302)){
3637                 //  Fetch the CATCOV attribute
3638                     catcov = pobjectDef->GetFieldAsInteger( "CATCOV" );
3639                     return pobjectDef;
3640                 }
3641                 else
3642                     delete pobjectDef;
3643 
3644             }
3645             else
3646                 return NULL;
3647 
3648             pobjectDef = pENCReader->ReadNextFeature( );
3649 
3650         }
3651     }
3652 
3653     return NULL;
3654 }
3655 
3656 
GetBaseFileInfo(const wxString & FullPath000,const wxString & SENCFileName)3657 int Osenc::GetBaseFileInfo(const wxString& FullPath000, const wxString& SENCFileName)
3658 {
3659     wxFileName SENCfile = wxFileName( SENCFileName );
3660 
3661     //  Take a quick scan of the 000 file to get some basic attributes of the exchange set.
3662     if(!GetBaseFileAttr( FullPath000 ) ){
3663         return ERROR_BASEFILE_ATTRIBUTES;
3664     }
3665 
3666     OGRS57DataSource oS57DS;
3667     oS57DS.SetS57Registrar( m_poRegistrar );
3668 
3669     bool b_current_debug = g_bGDAL_Debug;
3670     g_bGDAL_Debug = false;
3671 
3672 
3673     //  Ingest the .000 cell, with updates applied
3674 
3675     if(ingestCell( &oS57DS, FullPath000, SENCfile.GetPath())){
3676         errorMessage = _T("Error ingesting: ") + FullPath000;
3677         return ERROR_INGESTING000;
3678     }
3679 
3680 
3681     S57Reader *poReader = oS57DS.GetModule( 0 );
3682 
3683     CalculateExtent( poReader, m_poRegistrar );
3684 
3685 
3686     g_bGDAL_Debug = b_current_debug;
3687 
3688     //    delete poReader;
3689 
3690     return SENC_NO_ERROR;
3691 
3692 }
3693 
CalculateExtent(S57Reader * poReader,S57ClassRegistrar * poRegistrar)3694 bool Osenc::CalculateExtent( S57Reader *poReader, S57ClassRegistrar *poRegistrar )
3695 {
3696     poReader->Rewind();
3697 
3698     OGRFeature *pFeat;
3699     int catcov;
3700     float LatMax, LatMin, LonMax, LonMin;
3701     LatMax = -90.;
3702     LatMin = 90.;
3703     LonMax = -179.;
3704     LonMin = 179.;
3705 
3706     m_pCOVRTablePoints = NULL;
3707     m_pCOVRTable = NULL;
3708 
3709     //     //  Create arrays to hold geometry objects temporarily
3710     //     MyFloatPtrArray *pAuxPtrArray = new MyFloatPtrArray;
3711     //     wxArrayInt *pAuxCntArray = new wxArrayInt;
3712     //
3713     //     MyFloatPtrArray *pNoCovrPtrArray = new MyFloatPtrArray;
3714     //     wxArrayInt *pNoCovrCntArray = new wxArrayInt;
3715 
3716     //Get the first M_COVR object
3717     pFeat = GetChartFirstM_COVR( catcov, poReader, poRegistrar );
3718 
3719     while( pFeat ) {
3720         //    Get the next M_COVR feature, and create possible additional entries for COVR
3721         OGRPolygon *poly = (OGRPolygon *) ( pFeat->GetGeometryRef() );
3722         OGRLinearRing *xring = poly->getExteriorRing();
3723 
3724         int npt = xring->getNumPoints();
3725 
3726         if( npt >= 3 ) {
3727             for( int i = 0; i < npt; i++ ) {
3728                 OGRPoint p;
3729                 xring->getPoint( i, &p );
3730 
3731                 if( catcov == 1 ) {
3732                     LatMax = fmax(LatMax, p.getY());
3733                     LatMin = fmin(LatMin, p.getY());
3734                     LonMax = fmax(LonMax, p.getX());
3735                     LonMin = fmin(LonMin, p.getX());
3736                 }
3737             }
3738         }
3739 
3740 
3741         delete pFeat;
3742         pFeat = GetChartNextM_COVR( catcov, poReader );
3743     }         // while
3744 
3745     //    Populate the oSENC clone of the chart's extent structure
3746     m_extent.NLAT = LatMax;
3747     m_extent.SLAT = LatMin;
3748     m_extent.ELON = LonMax;
3749     m_extent.WLON = LonMin;
3750 
3751     return true;
3752 }
3753 
3754 
3755 
3756 
3757 
3758 
3759 
3760 
3761 
3762 
3763 
InitializePersistentBuffer(void)3764 void Osenc::InitializePersistentBuffer( void ){
3765 
3766     pBuffer = (unsigned char *)malloc(1024);
3767     bufferSize = 1024;
3768 }
3769 
getBuffer(size_t length)3770 unsigned char *Osenc::getBuffer( size_t length){
3771 
3772     if(length > bufferSize){
3773         pBuffer = (unsigned char *)realloc(pBuffer, length * 2);
3774         bufferSize = length * 2;
3775     }
3776 
3777     return pBuffer;
3778 
3779 }
3780 
3781