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