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