OpenCPN Partial API docs
Loading...
Searching...
No Matches
Osenc.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: S57 SENC File Object
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2015 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26// For compilers that support precompilation, includes "wx.h".
27#include "wx/wxprec.h"
28
29#ifndef WX_PRECOMP
30#include "wx/wx.h"
31#endif // precompiled headers
32
33#include <setjmp.h>
34
35#include <wx/wfstream.h>
36#include <wx/filename.h>
37#include <wx/progdlg.h>
38
39#include "Osenc.h"
40#include "s52s57.h"
41#include "s57chart.h" // for one static method
42#include "cutil.h"
43#include "s57RegistrarMgr.h"
44#include "gdal/cpl_csv.h"
45#include "ogr_s57.h"
46#include "gdal/cpl_string.h"
47#include "LOD_reduce.h"
48
49#include "mygeom.h"
50#include "georef.h"
51#include "gui_lib.h"
52#include <mutex>
53
54extern s57RegistrarMgr *m_pRegistrarMan;
55extern wxString g_csv_locn;
56extern bool g_bGDAL_Debug;
57
58bool chain_broken_mssage_shown = false;
59
60using namespace std;
61
62#include <wx/arrimpl.cpp>
63WX_DEFINE_ARRAY(float *, MyFloatPtrArray);
64
65#define MAX_VECTOR_POINTS 1000
66
67#ifndef __WXMSW__
68sigjmp_buf env_osenc_ogrf; // the context saved by sigsetjmp();
69#endif
70
71std::mutex m;
72
73/************************************************************************/
74/* OpenCPN_OGRErrorHandler() */
75/* Use Global wxLog Class */
76/************************************************************************/
77bool g_OsencVerbose;
78
79void OpenCPN_OGR_OSENC_ErrorHandler(CPLErr eErrClass, int nError,
80 const char *pszErrorMsg) {
81#define ERR_BUF_LEN 2000
82
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(void) {
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 = _T("-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(_T("%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(_T("%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(_T("%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(
1056 _T("Preparing to apply ENC updates, target final update is %3d."),
1057 available_updates);
1058 wxLogMessage(msg1);
1059 }
1060
1061 wxString sobj;
1062
1063 // Here comes the actual ISO8211 file reading
1064
1065 // Set up the options
1066 char **papszReaderOptions = NULL;
1067 // papszReaderOptions = CSLSetNameValue(papszReaderOptions, S57O_LNAM_REFS,
1068 // "ON" );
1069 // papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES,
1070 // "ON" );
1071 papszReaderOptions =
1072 CSLSetNameValue(papszReaderOptions, S57O_RETURN_LINKAGES, "ON");
1073 papszReaderOptions =
1074 CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON");
1075 poS57DS->SetOptionList(papszReaderOptions);
1076
1077 // Open the OGRS57DataSource
1078 // This will ingest the .000 file from the working dir
1079
1080 bool b_current_debug = g_bGDAL_Debug;
1081 g_bGDAL_Debug = m_bVerbose;
1082
1083 // Form the .000 filename
1084 wxString s0_file = working_dir;
1085 if (s0_file.Last() != wxFileName::GetPathSeparator())
1086 s0_file.Append(wxFileName::GetPathSeparator());
1087 wxFileName f000(FullPath000);
1088
1089 s0_file.Append(f000.GetFullName());
1090
1091 if (poS57DS->Open(s0_file.mb_str(), TRUE, NULL)) return 1;
1092
1093 // Get a pointer to the reader
1094 S57Reader *poReader = poS57DS->GetModule(0);
1095
1096 m_last_applied_update = m_UPDN;
1097 wxString last_successful_update_file;
1098
1099 // Apply the updates...
1100 for (unsigned int i_up = 0; i_up < m_tmpup_array.GetCount(); i_up++) {
1101 wxFileName fn(m_tmpup_array[i_up]);
1102 wxString ext = fn.GetExt();
1103 long n_upd;
1104 ext.ToLong(&n_upd);
1105
1106 if (n_upd > 0) { // .000 is the base, not an update
1107 DDFModule oUpdateModule;
1108 if (!oUpdateModule.Open(m_tmpup_array[i_up].mb_str(), FALSE)) {
1109 break;
1110 }
1111 int upResult = poReader->ApplyUpdates(&oUpdateModule, n_upd);
1112 if (upResult) {
1113 break;
1114 }
1115 m_last_applied_update = n_upd;
1116 last_successful_update_file = m_tmpup_array[i_up];
1117 }
1118 }
1119
1120 // Check for bad/broken update chain....
1121 // It is a "warning" condition if an update fails.
1122 // We use the cell with all good updates applied so far, and so inform the
1123 // user. "Better a slightly out-of-date chart than no chart at all..."
1124
1125 // The logic will attempt to build a SENC on each instance of OCPN, so
1126 // eventually the
1127 // updates may be corrected, and the chart SENC is built correctly.
1128 // Or, the update files following the last good update may be manually
1129 // deleted.
1130
1131 if ((available_updates > 0) && (m_last_applied_update != available_updates)) {
1132 if (last_successful_update_file.Length()) {
1133 // Get the update date from the last good update module
1134 bool bSuccess;
1135 DDFModule oUpdateModule;
1136 wxString LastGoodUpdateDate;
1137 wxDateTime now = wxDateTime::Now();
1138 LastGoodUpdateDate = now.Format(_T("%Y%m%d"));
1139
1140 bSuccess = !(
1141 oUpdateModule.Open(last_successful_update_file.mb_str(), TRUE) == 0);
1142
1143 if (bSuccess) {
1144 // Get publish/update date
1145 oUpdateModule.Rewind();
1146 DDFRecord *pr = oUpdateModule.ReadRecord(); // Record 0
1147
1148 int nSuccess;
1149 char *u = NULL;
1150
1151 if (pr)
1152 u = (char *)(pr->GetStringSubfield("DSID", 0, "ISDT", 0, &nSuccess));
1153
1154 if (u) {
1155 if (strlen(u)) {
1156 LastGoodUpdateDate = wxString(u, wxConvUTF8);
1157 }
1158 }
1159 m_LastUpdateDate = LastGoodUpdateDate;
1160 }
1161
1162 // Inform the user
1163 wxString msg(
1164 _T("WARNING---ENC Update failed. Last valid update file is:"));
1165 msg += last_successful_update_file.mb_str();
1166 wxLogMessage(msg);
1167 wxLogMessage(
1168 _T(" This ENC exchange set should be updated and SENCs rebuilt."));
1169
1170 if (!m_NoErrDialog) {
1171 OCPNMessageBox(
1172 NULL,
1173 _("S57 Cell Update failed.\nENC features may be incomplete or "
1174 "inaccurate.\n\nCheck the logfile for details."),
1175 _("OpenCPN Create SENC Warning"), wxOK | wxICON_EXCLAMATION, 5);
1176 }
1177 } else { // no updates applied.
1178 if (!m_NoErrDialog)
1179 OCPNMessageBox(NULL,
1180 _("S57 Cell Update failed.\nNo updates could be "
1181 "applied.\nENC features may be incomplete or "
1182 "inaccurate.\n\nCheck the logfile for details."),
1183 _("OpenCPN Create SENC Warning"),
1184 wxOK | wxICON_EXCLAMATION, 5);
1185 }
1186 }
1187
1188 // Unset verbose debug option
1189 g_bGDAL_Debug = b_current_debug;
1190
1191 // Update the options, removing the RETURN_PRIMITIVES flags
1192 // This flag needed to be set on ingest() to create the proper field
1193 // defns, but cleared to fetch normal features
1194
1195 papszReaderOptions =
1196 CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF");
1197 poReader->SetOptions(papszReaderOptions);
1198 CSLDestroy(papszReaderOptions);
1199
1200 wxRemoveFile(s0_file);
1201
1202 return 0;
1203}
1204
1205int Osenc::ValidateAndCountUpdates(const wxFileName file000,
1206 const wxString CopyDir,
1207 wxString &LastUpdateDate, bool b_copyfiles) {
1208 int retval = 0;
1209 wxFileName last_up_added;
1210
1211 // wxString DirName000 = file000.GetPath((int)(wxPATH_GET_SEPARATOR |
1212 // wxPATH_GET_VOLUME)); wxDir dir(DirName000);
1213 m_UpFiles = new wxArrayString;
1214 retval =
1215 s57chart::GetUpdateFileArray(file000, m_UpFiles, m_date000, m_edtn000);
1216 int upmax = retval;
1217
1218 if (m_UpFiles->GetCount()) {
1219 // The s57reader of ogr requires that update set be sequentially
1220 // complete to perform all the updates. However, some NOAA ENC
1221 // distributions are not complete, as apparently some interim updates
1222 // have been withdrawn. Example: as of 20 Dec, 2005, the update set
1223 // for US5MD11M.000 includes US5MD11M.017, ...018, and ...019. Updates
1224 // 001 through 016 are missing.
1225 //
1226 // Workaround.
1227 // Create temporary dummy update files to fill out the set before
1228 // invoking ogr file open/ingest. Delete after SENC file create
1229 // finishes. Set starts with .000, which has the effect of copying the
1230 // base file to the working dir
1231
1232 // bool chain_broken_mssage_shown = false;
1233
1234 if (b_copyfiles) {
1235 unsigned int jup = 0;
1236 for (int iff = 0; iff < retval + 1; iff++) {
1237 wxString upFile;
1238 wxString targetFile;
1239
1240 if (jup < m_UpFiles->GetCount()) upFile = m_UpFiles->Item(jup);
1241 wxFileName upCheck(upFile);
1242 long tl = -1;
1243 wxString text = upCheck.GetExt();
1244 text.ToLong(&tl);
1245 if (tl == iff) {
1246 targetFile = upFile;
1247 jup++; // used this one
1248 } else {
1249 targetFile = file000.GetFullName(); // ext will be updated
1250 }
1251
1252 wxFileName ufile(targetFile);
1253 wxString sext;
1254 sext.Printf(_T("%03d"), iff);
1255 ufile.SetExt(sext);
1256
1257 // Create the target update file name
1258 wxString cp_ufile = CopyDir;
1259 if (cp_ufile.Last() != ufile.GetPathSeparator())
1260 cp_ufile.Append(ufile.GetPathSeparator());
1261
1262 cp_ufile.Append(ufile.GetFullName());
1263
1264 wxString tfile = ufile.GetFullPath();
1265
1266 // Explicit check for a short update file, possibly left over from
1267 // a crash...
1268 int flen = 0;
1269 if (ufile.FileExists()) {
1270 wxFile uf(ufile.GetFullPath());
1271 if (uf.IsOpened()) {
1272 flen = uf.Length();
1273 uf.Close();
1274 }
1275 }
1276
1277 if (ufile.FileExists() &&
1278 (flen > 25)) // a valid update file or base file
1279 {
1280 // Copy the valid file to the SENC directory
1281 bool cpok = wxCopyFile(ufile.GetFullPath(), cp_ufile);
1282 if (!cpok) {
1283 wxString msg(_T(" Cannot copy temporary working ENC file "));
1284 msg.Append(ufile.GetFullPath());
1285 msg.Append(_T(" to "));
1286 msg.Append(cp_ufile);
1287 wxLogMessage(msg);
1288 }
1289 }
1290
1291 else {
1292 // Create a dummy ISO8211 file with no real content
1293 // Correct this. We should break the walk, and notify the user See
1294 // FS#1406
1295
1296 // if( !chain_broken_mssage_shown ){
1297 // OCPNMessageBox(NULL,
1298 // _("S57 Cell Update
1299 // chain
1300 // incomplete.\nENC
1301 // features may be
1302 // incomplete or
1303 // inaccurate.\nCheck
1304 // the logfile for
1305 // details."),
1306 // _("OpenCPN Create
1307 // SENC Warning"), wxOK
1308 // |
1309 // wxICON_EXCLAMATION,
1310 // 30 );
1311 // chain_broken_mssage_shown
1312 // = true;
1313 // }
1314
1315 wxString msg(
1316 _T("WARNING---ENC Update chain incomplete. Substituting NULL ")
1317 _T("update file: "));
1318 msg += ufile.GetFullName();
1319 wxLogMessage(msg);
1320 wxLogMessage(_T(" Subsequent ENC updates may produce errors."));
1321 wxLogMessage(
1322 _T(" This ENC exchange set should be updated and SENCs ")
1323 _T("rebuilt."));
1324
1325 bool bstat;
1326 DDFModule dupdate;
1327 dupdate.Initialize('3', 'L', 'E', '1', '0', "!!!", 3, 4, 4);
1328 bstat = !(dupdate.Create(cp_ufile.mb_str()) == 0);
1329 dupdate.Close();
1330
1331 if (!bstat) {
1332 wxString msg(_T(" Error creating dummy update file: "));
1333 msg.Append(cp_ufile);
1334 wxLogMessage(msg);
1335 }
1336 }
1337
1338 m_tmpup_array.Add(cp_ufile);
1339 last_up_added = cp_ufile;
1340 }
1341 }
1342
1343 // Extract the date field from the last of the update files
1344 // which is by definition a valid, present update file....
1345
1346 wxFileName lastfile(last_up_added);
1347 wxString last_sext;
1348 last_sext.Printf(_T("%03d"), upmax);
1349 lastfile.SetExt(last_sext);
1350
1351 bool bSuccess;
1352 DDFModule oUpdateModule;
1353
1354 bSuccess =
1355 !(oUpdateModule.Open(lastfile.GetFullPath().mb_str(), TRUE) == 0);
1356
1357 if (bSuccess) {
1358 // Get publish/update date
1359 oUpdateModule.Rewind();
1360 DDFRecord *pr = oUpdateModule.ReadRecord(); // Record 0
1361
1362 int nSuccess;
1363 char *u = NULL;
1364
1365 if (pr)
1366 u = (char *)(pr->GetStringSubfield("DSID", 0, "ISDT", 0, &nSuccess));
1367
1368 if (u) {
1369 if (strlen(u)) {
1370 LastUpdateDate = wxString(u, wxConvUTF8);
1371 }
1372 } else {
1373 wxDateTime now = wxDateTime::Now();
1374 LastUpdateDate = now.Format(_T("%Y%m%d"));
1375 }
1376 }
1377 }
1378
1379 return retval;
1380}
1381
1382bool Osenc::GetBaseFileAttr(const wxString &FullPath000) {
1383 DDFModule oModule;
1384 if (!oModule.Open(FullPath000.mb_str())) {
1385 return false;
1386 }
1387
1388 oModule.Rewind();
1389
1390 // Read and parse DDFRecord 0 to get some interesting data
1391 // n.b. assumes that the required fields will be in Record 0.... Is this
1392 // always true?
1393
1394 DDFRecord *pr = oModule.ReadRecord(); // Record 0
1395 // pr->Dump(stdout);
1396
1397 // Fetch the Geo Feature Count, or something like it....
1398 m_nGeoRecords = pr->GetIntSubfield("DSSI", 0, "NOGR", 0);
1399 if (!m_nGeoRecords) {
1400 errorMessage =
1401 _T("GetBaseFileAttr: DDFRecord 0 does not contain DSSI:NOGR ");
1402
1403 m_nGeoRecords = 1; // backstop
1404 }
1405
1406 // Use ISDT(Issue Date) here, which is the same as UADT(Updates Applied) for
1407 // .000 files
1408 wxString date000;
1409 char *u = (char *)(pr->GetStringSubfield("DSID", 0, "ISDT", 0));
1410 if (u)
1411 date000 = wxString(u, wxConvUTF8);
1412 else {
1413 errorMessage =
1414 _T("GetBaseFileAttr: DDFRecord 0 does not contain DSID:ISDT ");
1415
1416 date000 =
1417 _T("20000101"); // backstop, very early, so any new files will update?
1418 }
1419 m_date000.ParseFormat(date000, _T("%Y%m%d"));
1420 if (!m_date000.IsValid()) m_date000.ParseFormat(_T("20000101"), _T("%Y%m%d"));
1421
1422 m_date000.ResetTime();
1423
1424 // Fetch the EDTN(Edition) field
1425 u = (char *)(pr->GetStringSubfield("DSID", 0, "EDTN", 0));
1426 if (u)
1427 m_edtn000 = wxString(u, wxConvUTF8);
1428 else {
1429 errorMessage =
1430 _T("GetBaseFileAttr: DDFRecord 0 does not contain DSID:EDTN ");
1431
1432 m_edtn000 = _T("1"); // backstop
1433 }
1434
1435 // m_SE = m_edtn000;
1436
1437 // Fetch the UPDN(Updates Applied) field
1438 u = (char *)(pr->GetStringSubfield("DSID", 0, "UPDN", 0));
1439 if (u) {
1440 long updn = 0;
1441 wxString tmp_updn = wxString(u, wxConvUTF8);
1442 if (tmp_updn.ToLong(&updn)) m_UPDN = updn;
1443
1444 } else {
1445 errorMessage =
1446 _T("GetBaseFileAttr: DDFRecord 0 does not contain DSID:UPDN ");
1447
1448 m_UPDN = 0; // backstop
1449 }
1450
1451 // Fetch the Native Scale by reading more records until DSPM is found
1452 m_native_scale = 0;
1453 for (; pr != NULL; pr = oModule.ReadRecord()) {
1454 if (pr->FindField("DSPM") != NULL) {
1455 m_native_scale = pr->GetIntSubfield("DSPM", 0, "CSCL", 0);
1456 break;
1457 }
1458 }
1459 if (!m_native_scale) {
1460 errorMessage = _T("GetBaseFileAttr: ENC not contain DSPM:CSCL ");
1461
1462 m_native_scale = 1000; // backstop
1463 }
1464
1465 return true;
1466}
1467
1468//---------------------------------------------------------------------------------------------------
1469/*
1470 * OpenCPN OSENC Version 2 Implementation
1471 */
1472//---------------------------------------------------------------------------------------------------
1473
1474int Osenc::createSenc200(const wxString &FullPath000,
1475 const wxString &SENCFileName, bool b_showProg) {
1476 lockCR.lock();
1477
1478 m_FullPath000 = FullPath000;
1479
1480 m_senc_file_create_version = 201;
1481
1482 if (!m_poRegistrar) {
1483 m_poRegistrar = new S57ClassRegistrar();
1484 m_poRegistrar->LoadInfo(g_csv_locn.mb_str(), FALSE);
1485 m_bPrivateRegistrar = true;
1486 // errorMessage = _T("S57 Registrar not set.");
1487 // return ERROR_REGISTRAR_NOT_SET;
1488 }
1489
1490 wxFileName SENCfile = wxFileName(SENCFileName);
1491 wxFileName file000 = wxFileName(FullPath000);
1492
1493 // Make the target directory if needed
1494 if (true != SENCfile.DirExists(SENCfile.GetPath())) {
1495 if (!SENCfile.Mkdir(SENCfile.GetPath())) {
1496 errorMessage =
1497 _T("Cannot create SENC file directory for ") + SENCfile.GetFullPath();
1498 lockCR.unlock();
1499 return ERROR_CANNOT_CREATE_SENC_DIR;
1500 }
1501 }
1502
1503 // Make a temp file to create the SENC in
1504 wxFileName tfn;
1505 wxString tmp_file = tfn.CreateTempFileName(_T(""));
1506
1507 // FILE *fps57;
1508 // const char *pp = "wb";
1509 // fps57 = fopen( tmp_file.mb_str(), pp );
1510 //
1511 // if( fps57 == NULL ) {
1512 // errorMessage = _T("Unable to create temp SENC file: ");
1513 // errorMessage.Append( tfn.GetFullPath() );
1514 // return ERROR_CANNOT_CREATE_TEMP_SENC_FILE;
1515 // }
1516
1517 if (m_pauxOutstream) {
1518 m_pOutstream = m_pauxOutstream;
1519 } else {
1520 m_pOutstream = new Osenc_outstreamFile();
1521 }
1522
1523 Osenc_outstream *stream = m_pOutstream;
1524
1525 if (!stream->Open(tmp_file)) {
1526 errorMessage = _T("Unable to create temp SENC file: ");
1527 errorMessage += tmp_file;
1528 lockCR.unlock();
1529 return ERROR_CANNOT_CREATE_TEMP_SENC_FILE;
1530 }
1531
1532 // Take a quick scan of the 000 file to get some basic attributes of the
1533 // exchange set.
1534 if (!GetBaseFileAttr(FullPath000)) {
1535 lockCR.unlock();
1536 return ERROR_BASEFILE_ATTRIBUTES;
1537 }
1538
1539 OGRS57DataSource S57DS;
1540 OGRS57DataSource *poS57DS = &S57DS;
1541 poS57DS->SetS57Registrar(m_poRegistrar);
1542
1543 // Ingest the .000 cell, with updates applied
1544
1545 if (ingestCell(poS57DS, FullPath000, SENCfile.GetPath())) {
1546 errorMessage = _T("Error ingesting: ") + FullPath000;
1547 lockCR.unlock();
1548 return ERROR_INGESTING000;
1549 }
1550
1551 S57Reader *poReader = poS57DS->GetModule(0);
1552
1553 // Create the Coverage table Records, which also calculates the chart extents
1554 if (!CreateCOVRTables(poReader, m_poRegistrar)) {
1555 lockCR.unlock();
1556 return ERROR_SENCFILE_ABORT;
1557 }
1558
1559 // Establish a common reference point for the chart, from the extent
1560 m_ref_lat = (m_extent.NLAT + m_extent.SLAT) / 2.;
1561 m_ref_lon = (m_extent.WLON + m_extent.ELON) / 2.;
1562
1563 bool bcont = true;
1564
1565 // Write the Header information
1566
1567 // char temp[201];
1568
1569 // fprintf( fps57, "SENC Version= %d\n", 200 );
1570
1571 // The chart cell "nice name"
1572 wxString nice_name;
1573 s57chart::GetChartNameFromTXT(FullPath000, nice_name);
1574
1575 string sname = "UTF8Error";
1576 wxCharBuffer buffer = nice_name.ToUTF8();
1577 if (buffer.data()) sname = buffer.data();
1578
1579 if (!WriteHeaderRecord200(stream, HEADER_SENC_VERSION,
1580 (uint16_t)m_senc_file_create_version)) {
1581 stream->Close();
1582 lockCR.unlock();
1583 return ERROR_SENCFILE_ABORT;
1584 }
1585
1586 if (!WriteHeaderRecord200(stream, HEADER_CELL_NAME, sname)) {
1587 stream->Close();
1588 lockCR.unlock();
1589 return ERROR_SENCFILE_ABORT;
1590 }
1591
1592 wxString date000 = m_date000.Format(_T("%Y%m%d"));
1593 string sdata = date000.ToStdString();
1594 if (!WriteHeaderRecord200(stream, HEADER_CELL_PUBLISHDATE, sdata)) {
1595 stream->Close();
1596 lockCR.unlock();
1597 return ERROR_SENCFILE_ABORT;
1598 }
1599
1600 long n000 = 0;
1601 m_edtn000.ToLong(&n000);
1602 if (!WriteHeaderRecord200(stream, HEADER_CELL_EDITION, (uint16_t)n000)) {
1603 stream->Close();
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 lockCR.unlock();
1612 return ERROR_SENCFILE_ABORT;
1613 }
1614
1615 if (!WriteHeaderRecord200(stream, HEADER_CELL_UPDATE,
1616 (uint16_t)m_last_applied_update)) {
1617 stream->Close();
1618 lockCR.unlock();
1619 return ERROR_SENCFILE_ABORT;
1620 }
1621
1622 if (!WriteHeaderRecord200(stream, HEADER_CELL_NATIVESCALE,
1623 (uint32_t)m_native_scale)) {
1624 stream->Close();
1625 lockCR.unlock();
1626 return ERROR_SENCFILE_ABORT;
1627 }
1628
1629 wxDateTime now = wxDateTime::Now();
1630 wxString dateNow = now.Format(_T("%Y%m%d"));
1631 sdata = dateNow.ToStdString();
1632 if (!WriteHeaderRecord200(stream, HEADER_CELL_SENCCREATEDATE, sdata)) {
1633 stream->Close();
1634 lockCR.unlock();
1635 return ERROR_SENCFILE_ABORT;
1636 }
1637
1638 // Write the Coverage table Records
1639 if (!CreateCovrRecords(stream)) {
1640 stream->Close();
1641 lockCR.unlock();
1642 return ERROR_SENCFILE_ABORT;
1643 }
1644
1645 poReader->Rewind();
1646
1647 // Prepare Vector Edge Helper table
1648 // And fill in the table
1649 int feid = 0;
1650 OGRFeature *pEdgeVectorRecordFeature = poReader->ReadVector(feid, RCNM_VE);
1651 while (NULL != pEdgeVectorRecordFeature) {
1652 int record_id = pEdgeVectorRecordFeature->GetFieldAsInteger("RCID");
1653
1654 m_vector_helper_hash[record_id] = feid;
1655
1656 feid++;
1657 delete pEdgeVectorRecordFeature;
1658 pEdgeVectorRecordFeature = poReader->ReadVector(feid, RCNM_VE);
1659 }
1660
1661 wxString Message = SENCfile.GetFullPath();
1662 Message.Append(_T("...Ingesting"));
1663
1664 wxString Title(_("OpenCPN S57 SENC File Create..."));
1665 Title.append(SENCfile.GetFullPath());
1666
1667#if wxUSE_PROGRESSDLG
1668
1669 wxStopWatch progsw;
1670 int nProg = poReader->GetFeatureCount();
1671
1672 if (wxThread::IsMain() && b_showProg) {
1673 m_ProgDialog = new wxGenericProgressDialog();
1674
1675 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
1676 m_ProgDialog->SetFont(*qFont);
1677
1678 m_ProgDialog->Create(Title, Message, nProg, NULL,
1679 wxPD_AUTO_HIDE | wxPD_SMOOTH);
1680 }
1681#endif
1682
1683 // Loop in the S57 reader, extracting Features one-by-one
1684 OGRFeature *objectDef;
1685
1686 int iObj = 0;
1687
1688 while (bcont) {
1689 objectDef = poReader->ReadNextFeature();
1690
1691 if (objectDef != NULL) {
1692 iObj++;
1693
1694#if wxUSE_PROGRESSDLG
1695
1696 // Update the progress dialog
1697 // We update only every 200 milliseconds to improve performance as
1698 // updating the dialog is very expensive...
1699 // WXGTK is measurably slower even with 100ms here
1700 if (m_ProgDialog && progsw.Time() > 200) {
1701 progsw.Start();
1702
1703 wxString sobj =
1704 wxString(objectDef->GetDefnRef()->GetName(), wxConvUTF8);
1705 sobj.Append(wxString::Format(_T(" %d/%d "), iObj, nProg));
1706
1707 bcont = m_ProgDialog->Update(iObj, sobj);
1708#if defined(__WXMSW__) || defined(__WXOSX__)
1709 wxSafeYield();
1710#endif
1711 }
1712#endif
1713
1714 OGRwkbGeometryType geoType = wkbUnknown;
1715 // This test should not be necessary for real (i.e not C_AGGR)
1716 // features However... some update files contain errors, and have
1717 // deleted some geometry without deleting the corresponding
1718 // feature(s). So, GeometryType becomes Unknown. e.g. US5MD11M.017 In
1719 // this case, all we can do is skip the feature....sigh.
1720
1721 if (objectDef->GetGeometryRef() != NULL)
1722 geoType = objectDef->GetGeometryRef()->getGeometryType();
1723
1724 // n.b This next line causes skip of C_AGGR features w/o geometry
1725 if (geoType != wkbUnknown) { // Write only if has wkbGeometry
1726 CreateSENCRecord200(objectDef, stream, 1, poReader);
1727 }
1728
1729 delete objectDef;
1730
1731 } else
1732 break;
1733 }
1734
1735 if (bcont) {
1736 // Create and write the Vector Edge Table
1737 CreateSENCVectorEdgeTableRecord200(stream, poReader);
1738
1739 // Create and write the Connected NodeTable
1740 CreateSENCVectorConnectedTableRecord200(stream, poReader);
1741 }
1742
1743 // All done, so clean up
1744 stream->Close();
1745
1746 // Delete any temporary (working) real and dummy update files,
1747 // as well as .000 file created by ValidateAndCountUpdates()
1748 for (unsigned int iff = 0; iff < m_tmpup_array.GetCount(); iff++)
1749 remove(m_tmpup_array[iff].mb_str());
1750
1751 int ret_code = 0;
1752
1753 if (!bcont) // aborted
1754 {
1755 wxRemoveFile(tmp_file); // kill the temp file
1756 ret_code = ERROR_SENCFILE_ABORT;
1757 }
1758
1759 if (bcont) {
1760 bool cpok = wxRenameFile(tmp_file, SENCfile.GetFullPath());
1761 if (!cpok) {
1762 errorMessage = _T(" Cannot rename temporary SENC file ");
1763 errorMessage.Append(tmp_file);
1764 errorMessage.Append(_T(" to "));
1765 errorMessage.Append(SENCfile.GetFullPath());
1766 ret_code = ERROR_SENCFILE_ABORT;
1767 } else
1768 ret_code = SENC_NO_ERROR;
1769 }
1770
1771#if wxUSE_PROGRESSDLG
1772 delete m_ProgDialog;
1773#endif
1774
1775 lockCR.unlock();
1776
1777 return ret_code;
1778}
1779
1780bool Osenc::CreateCovrRecords(Osenc_outstream *stream) {
1781 // First, create the Extent record
1782 _OSENC_EXTENT_Record record;
1783 record.record_type = CELL_EXTENT_RECORD;
1784 record.record_length = sizeof(_OSENC_EXTENT_Record);
1785 record.extent_sw_lat = m_extent.SLAT;
1786 record.extent_sw_lon = m_extent.WLON;
1787 record.extent_nw_lat = m_extent.NLAT;
1788 record.extent_nw_lon = m_extent.WLON;
1789 record.extent_ne_lat = m_extent.NLAT;
1790 record.extent_ne_lon = m_extent.ELON;
1791 record.extent_se_lat = m_extent.SLAT;
1792 record.extent_se_lon = m_extent.ELON;
1793
1794 size_t targetCount = sizeof(record);
1795 if (!stream->Write(&record, targetCount).IsOk()) return false;
1796
1797 for (int i = 0; i < m_nCOVREntries; i++) {
1798 int nPoints = m_pCOVRTablePoints[i];
1799
1800 float *fpbuf = m_pCOVRTable[i];
1801
1802 // Ready to write the record
1804 record.record_type = CELL_COVR_RECORD;
1805 record.record_length = sizeof(_OSENC_COVR_Record_Base) + sizeof(uint32_t) +
1806 (nPoints * 2 * sizeof(float));
1807
1808 // Write the base record
1809 size_t targetCount = sizeof(record);
1810 if (!stream->Write(&record, targetCount).IsOk()) return false;
1811
1812 // Write the point count
1813 targetCount = sizeof(uint32_t);
1814 if (!stream->Write(&nPoints, targetCount).IsOk()) return false;
1815
1816 // Write the point array
1817 targetCount = nPoints * 2 * sizeof(float);
1818 if (!stream->Write(fpbuf, targetCount).IsOk()) return false;
1819 }
1820
1821 for (int i = 0; i < m_nNoCOVREntries; i++) {
1822 int nPoints = m_pNoCOVRTablePoints[i];
1823
1824 float *fpbuf = m_pNoCOVRTable[i];
1825
1826 // Ready to write the record
1828 record.record_type = CELL_NOCOVR_RECORD;
1829 record.record_length = sizeof(_OSENC_NOCOVR_Record_Base) +
1830 sizeof(uint32_t) + (nPoints * 2 * sizeof(float));
1831
1832 // Write the base record
1833 size_t targetCount = sizeof(record);
1834 if (!stream->Write(&record, targetCount).IsOk()) return false;
1835
1836 // Write the point count
1837 targetCount = sizeof(uint32_t);
1838 if (!stream->Write(&nPoints, targetCount).IsOk()) return false;
1839
1840 // Write the point array
1841 targetCount = nPoints * 2 * sizeof(float);
1842 if (!stream->Write(fpbuf, targetCount).IsOk()) return false;
1843 }
1844
1845 return true;
1846}
1847
1848bool Osenc::WriteHeaderRecord200(Osenc_outstream *stream, int recordType,
1849 std::string payload) {
1850 int payloadLength = payload.length() + 1;
1851 int recordLength = payloadLength + sizeof(OSENC_Record_Base);
1852
1853 // Get a reference to the class persistent buffer
1854 unsigned char *pBuffer = getBuffer(recordLength);
1855
1856 OSENC_Record *pRecord = (OSENC_Record *)pBuffer;
1857 memset(pRecord, 0, recordLength);
1858 pRecord->record_type = recordType;
1859 pRecord->record_length = recordLength;
1860 memcpy(&pRecord->payload, payload.c_str(), payloadLength);
1861
1862 size_t targetCount = recordLength;
1863 if (!stream->Write(pBuffer, targetCount).IsOk())
1864 return false;
1865 else
1866 return true;
1867}
1868
1869bool Osenc::WriteHeaderRecord200(Osenc_outstream *stream, int recordType,
1870 uint16_t val) {
1871 int payloadLength = sizeof(uint16_t);
1872 int recordLength = payloadLength + sizeof(OSENC_Record_Base);
1873
1874 // Get a reference to the class persistent buffer
1875 unsigned char *pBuffer = getBuffer(recordLength);
1876
1877 OSENC_Record *pRecord = (OSENC_Record *)pBuffer;
1878 memset(pRecord, 0, recordLength);
1879 pRecord->record_type = recordType;
1880 pRecord->record_length = recordLength;
1881 memcpy(&pRecord->payload, &val, payloadLength);
1882
1883 size_t targetCount = recordLength;
1884 if (!stream->Write(pBuffer, targetCount).IsOk())
1885 return false;
1886 else
1887 return true;
1888}
1889
1890bool Osenc::WriteHeaderRecord200(Osenc_outstream *stream, int recordType,
1891 uint32_t val) {
1892 int payloadLength = sizeof(uint32_t);
1893 int recordLength = payloadLength + sizeof(OSENC_Record_Base);
1894
1895 // Get a reference to the class persistent buffer
1896 unsigned char *pBuffer = getBuffer(recordLength);
1897
1898 OSENC_Record *pRecord = (OSENC_Record *)pBuffer;
1899 memset(pRecord, 0, recordLength);
1900 pRecord->record_type = recordType;
1901 pRecord->record_length = recordLength;
1902 memcpy(&pRecord->payload, &val, payloadLength);
1903
1904 size_t targetCount = recordLength;
1905 if (!stream->Write(pBuffer, targetCount).IsOk())
1906 return false;
1907 else
1908 return true;
1909}
1910
1911bool Osenc::WriteFIDRecord200(Osenc_outstream *stream, int nOBJL, int featureID,
1912 int prim) {
1914 memset(&record, 0, sizeof(record));
1915
1916 record.record_type = FEATURE_ID_RECORD;
1917 record.record_length = sizeof(record);
1918
1919 record.feature_ID = featureID;
1920 record.feature_type_code = nOBJL;
1921 record.feature_primitive = prim;
1922
1923 size_t targetCount = sizeof(record);
1924 if (!stream->Write(&record, targetCount).IsOk())
1925 return false;
1926 else
1927 return true;
1928}
1929
1930bool Osenc::CreateMultiPointFeatureGeometryRecord200(OGRFeature *pFeature,
1931 Osenc_outstream *stream) {
1932 OGRGeometry *pGeo = pFeature->GetGeometryRef();
1933
1934 int wkb_len = pGeo->WkbSize();
1935 unsigned char *pwkb_buffer = (unsigned char *)malloc(wkb_len);
1936
1937 // Get the GDAL data representation
1938 pGeo->exportToWkb(wkbNDR, pwkb_buffer);
1939
1940 // Capture a buffer of the raw geometry
1941
1942 unsigned char *ps = pwkb_buffer;
1943 ps += 5;
1944 int nPoints = *((int *)ps); // point count
1945
1946 int sb_len = (nPoints * 3 * sizeof(float)); // points as floats
1947
1948 unsigned char *psb_buffer = (unsigned char *)malloc(sb_len);
1949 unsigned char *pd = psb_buffer;
1950
1951 ps = pwkb_buffer;
1952 ps += 9; // skip byte order, type, and count
1953
1954 float *pdf = (float *)pd;
1955
1956 // Set absurd bbox starting limits
1957 float lonmax = -1000;
1958 float lonmin = 1000;
1959 float latmax = -1000;
1960 float latmin = 1000;
1961
1962 for (int ip = 0; ip < nPoints; ip++) {
1963 // Workaround a bug?? in OGRGeometryCollection
1964 // While exporting point geometries serially, OGRPoint->exportToWkb assumes
1965 // that if Z is identically 0, then the point must be a 2D point only. So,
1966 // the collection Wkb is corrupted with some 3D, and some 2D points.
1967 // Workaround: Get reference to the points serially, and explicitly read
1968 // X,Y,Z Ignore the previously read Wkb buffer
1969
1970 OGRGeometryCollection *temp_geometry_collection =
1971 (OGRGeometryCollection *)pGeo;
1972 OGRGeometry *temp_geometry = temp_geometry_collection->getGeometryRef(ip);
1973 OGRPoint *pt_geom = (OGRPoint *)temp_geometry;
1974
1975 double lon = pt_geom->getX();
1976 double lat = pt_geom->getY();
1977 double depth = pt_geom->getZ();
1978
1979 // Calculate SM from chart common reference point
1980 double easting, northing;
1981 toSM(lat, lon, m_ref_lat, m_ref_lon, &easting, &northing);
1982
1983#ifdef __ARM_ARCH
1984 float __attribute__((aligned(16))) east = easting;
1985 float __attribute__((aligned(16))) north = northing;
1986 float __attribute__((aligned(16))) deep = depth;
1987 unsigned char *puceast = (unsigned char *)&east;
1988 unsigned char *pucnorth = (unsigned char *)&north;
1989 unsigned char *pucdeep = (unsigned char *)&deep;
1990
1991 memcpy(pdf++, puceast, sizeof(float));
1992 memcpy(pdf++, pucnorth, sizeof(float));
1993 memcpy(pdf++, pucdeep, sizeof(float));
1994
1995#else
1996 *pdf++ = easting;
1997 *pdf++ = northing;
1998 *pdf++ = (float)depth;
1999#endif
2000
2001 // Keep a running calculation of min/max
2002 lonmax = fmax(lon, lonmax);
2003 lonmin = fmin(lon, lonmin);
2004 latmax = fmax(lat, latmax);
2005 latmin = fmin(lat, latmin);
2006 }
2007
2008 // Ready to write the record
2010 record.record_type = FEATURE_GEOMETRY_RECORD_MULTIPOINT;
2011 record.record_length = sizeof(OSENC_MultipointGeometry_Record_Base) +
2012 (nPoints * 3 * sizeof(float));
2013 record.extent_e_lon = lonmax;
2014 record.extent_w_lon = lonmin;
2015 record.extent_n_lat = latmax;
2016 record.extent_s_lat = latmin;
2017 record.point_count = nPoints;
2018
2019 // Write the base record
2020 size_t targetCount = sizeof(record);
2021 if (!stream->Write(&record, targetCount).IsOk()) goto failure;
2022 // Write the 3D point array
2023 targetCount = nPoints * 3 * sizeof(float);
2024 if (!stream->Write(psb_buffer, targetCount).IsOk()) goto failure;
2025
2026 // Free the buffers
2027 free(psb_buffer);
2028 free(pwkb_buffer);
2029 return true;
2030failure:
2031 // Free the buffers
2032 free(psb_buffer);
2033 free(pwkb_buffer);
2034 return false;
2035}
2036
2037bool Osenc::CreateLineFeatureGeometryRecord200(S57Reader *poReader,
2038 OGRFeature *pFeature,
2039 Osenc_outstream *stream) {
2040 OGRGeometry *pGeo = pFeature->GetGeometryRef();
2041
2042 int wkb_len = pGeo->WkbSize();
2043 unsigned char *pwkb_buffer = (unsigned char *)malloc(wkb_len);
2044
2045 // Get the GDAL data representation
2046 pGeo->exportToWkb(wkbNDR, pwkb_buffer);
2047
2048 // Capture a buffer of the raw geometry
2049
2050 int sb_len =
2051 ((wkb_len - 9) / 2) + 9 + 16; // data will be 4 byte float, not double
2052
2053 unsigned char *psb_buffer = (unsigned char *)malloc(sb_len);
2054 unsigned char *pd = psb_buffer;
2055 unsigned char *ps = pwkb_buffer;
2056
2057 memcpy(pd, ps, 9); // byte order, type, and count
2058
2059 int ip = *((int *)(ps + 5)); // point count
2060
2061 pd += 9;
2062 ps += 9;
2063 double *psd = (double *)ps;
2064 float *pdf = (float *)pd;
2065
2066 // Set absurd bbox starting limits
2067 float lonmax = -1000;
2068 float lonmin = 1000;
2069 float latmax = -1000;
2070 float latmin = 1000;
2071
2072 for (int i = 0; i < ip; i++) { // convert doubles to floats
2073 // computing bbox as we go
2074 float lon, lat;
2075 double easting, northing;
2076#ifdef __ARM_ARCH
2077 double __attribute__((aligned(16))) east_d, north_d;
2078 unsigned char *pucd = (unsigned char *)psd;
2079
2080 memcpy(&east_d, pucd, sizeof(double));
2081 psd += 1;
2082 pucd += sizeof(double);
2083 memcpy(&north_d, pucd, sizeof(double));
2084 psd += 1;
2085 lon = east_d;
2086 lat = north_d;
2087
2088 // Calculate SM from chart common reference point
2089 toSM(lat, lon, m_ref_lat, m_ref_lon, &easting, &northing);
2090 unsigned char *puceasting = (unsigned char *)&easting;
2091 unsigned char *pucnorthing = (unsigned char *)&northing;
2092
2093 memcpy(pdf++, puceasting, sizeof(float));
2094 memcpy(pdf++, pucnorthing, sizeof(float));
2095
2096#else
2097 lon = (float)*psd++;
2098 lat = (float)*psd++;
2099
2100 // Calculate SM from chart common reference point
2101 toSM(lat, lon, m_ref_lat, m_ref_lon, &easting, &northing);
2102
2103 *pdf++ = easting;
2104 *pdf++ = northing;
2105#endif
2106
2107 lonmax = fmax(lon, lonmax);
2108 lonmin = fmin(lon, lonmin);
2109 latmax = fmax(lat, latmax);
2110 latmin = fmin(lat, latmin);
2111 }
2112
2113 int nEdgeVectorRecords = 0;
2114 unsigned char *pvec_buffer =
2115 getObjectVectorIndexTable(poReader, pFeature, nEdgeVectorRecords);
2116
2117#if 0
2118
2119 // Capture the Vector Table geometry indices into a memory buffer
2120 int *pNAME_RCID;
2121 int *pORNT;
2122 int nEdgeVectorRecords;
2123 OGRFeature *pEdgeVectorRecordFeature;
2124
2125 pNAME_RCID = (int *) pFeature->GetFieldAsIntegerList( "NAME_RCID", &nEdgeVectorRecords );
2126 pORNT = (int *) pFeature->GetFieldAsIntegerList( "ORNT", NULL );
2127
2128//fprintf( fpOut, "LSINDEXLIST %d\n", nEdgeVectorRecords );
2129// fwrite(pNAME_RCID, 1, nEdgeVectorRecords * sizeof(int), fpOut);
2130
2131
2132
2133 unsigned char *pvec_buffer = (unsigned char *)malloc(nEdgeVectorRecords * 3 * sizeof(int));
2134 unsigned char *pvRun = pvec_buffer;
2135
2136 // Set up the options, adding RETURN_PRIMITIVES
2137 char ** papszReaderOptions = NULL;
2138 papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
2139 papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES,"ON" );
2140 papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"ON" );
2141 poReader->SetOptions( papszReaderOptions );
2142
2143// Capture the beginning and end point connected nodes for each edge vector record
2144 for( int i = 0; i < nEdgeVectorRecords; i++ ) {
2145
2146 int *pI = (int *)pvRun;
2147
2148 int edge_rcid = pNAME_RCID[i];
2149
2150 int start_rcid, end_rcid;
2151 int target_record_feid = m_vector_helper_hash[pNAME_RCID[i]];
2152 pEdgeVectorRecordFeature = poReader->ReadVector( target_record_feid, RCNM_VE );
2153
2154 if( NULL != pEdgeVectorRecordFeature ) {
2155 start_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_0" );
2156 end_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_1" );
2157
2158 // Make sure the start and end points exist....
2159 // Note this poReader method was converted to Public access to
2160 // facilitate this test. There might be another clean way....
2161 // Problem first found on Holand ENC 1R5YM009.000
2162 if( !poReader->FetchPoint( RCNM_VC, start_rcid, NULL, NULL, NULL, NULL ) )
2163 start_rcid = -1;
2164 if( !poReader->FetchPoint( RCNM_VC, end_rcid, NULL, NULL, NULL, NULL ) )
2165 end_rcid = -2;
2166
2167 OGRLineString *poLS = (OGRLineString *)pEdgeVectorRecordFeature->GetGeometryRef();
2168
2169 int edge_ornt = 1;
2170
2171 if( edge_ornt == 1 ){ // forward
2172 *pI++ = start_rcid;
2173 *pI++ = edge_rcid;
2174 *pI++ = end_rcid;
2175 } else { // reverse
2176 *pI++ = end_rcid;
2177 *pI++ = edge_rcid;
2178 *pI++ = start_rcid;
2179 }
2180
2181 delete pEdgeVectorRecordFeature;
2182 } else {
2183 start_rcid = -1; // error indication
2184 end_rcid = -2;
2185
2186 *pI++ = start_rcid;
2187 *pI++ = edge_rcid;
2188 *pI++ = end_rcid;
2189 }
2190
2191 pvRun += 3 * sizeof(int);
2192 }
2193
2194
2195 // Reset the options
2196 papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"OFF" );
2197 poReader->SetOptions( papszReaderOptions );
2198 CSLDestroy( papszReaderOptions );
2199#endif
2200
2201 // Ready to write the record
2203 record.record_type = FEATURE_GEOMETRY_RECORD_LINE;
2204 record.record_length = sizeof(OSENC_LineGeometry_Record_Base) +
2205 (nEdgeVectorRecords * 3 * sizeof(int));
2206 record.extent_e_lon = lonmax;
2207 record.extent_w_lon = lonmin;
2208 record.extent_n_lat = latmax;
2209 record.extent_s_lat = latmin;
2210 record.edgeVector_count = nEdgeVectorRecords;
2211
2212 // Write the base record
2213 size_t targetCount = sizeof(record);
2214 if (!stream->Write(&record, targetCount).IsOk()) return false;
2215
2216 // Write the table index array
2217 targetCount = nEdgeVectorRecords * 3 * sizeof(int);
2218 if (!stream->Write(pvec_buffer, targetCount).IsOk()) return false;
2219
2220 // Free the buffers
2221 free(pvec_buffer);
2222 free(psb_buffer);
2223 free(pwkb_buffer);
2224
2225 return true;
2226}
2227
2228bool Osenc::CreateAreaFeatureGeometryRecord200(S57Reader *poReader,
2229 OGRFeature *pFeature,
2230 Osenc_outstream *stream) {
2231 int error_code;
2232
2233 PolyTessGeo *ppg = NULL;
2234
2235 OGRGeometry *pGeo = pFeature->GetGeometryRef();
2236 OGRPolygon *poly = (OGRPolygon *)(pGeo);
2237
2238 if (!poly->getExteriorRing()) return false;
2239
2240 lockCR.unlock();
2241 ppg = new PolyTessGeo(poly, true, m_ref_lat, m_ref_lon, m_LOD_meters);
2242 lockCR.lock();
2243
2244 error_code = ppg->ErrorCode;
2245
2246 if (error_code) {
2247 wxLogMessage(
2248 _T(" Warning: S57 SENC Geometry Error %d, Some Features ignored."),
2249 ppg->ErrorCode);
2250 delete ppg;
2251
2252 return false;
2253 }
2254
2255 // Ready to create the record
2256
2257 // The base record, with writing deferred until length is known
2259 memset(&baseRecord, 0, sizeof(baseRecord));
2260
2261 baseRecord.record_type = FEATURE_GEOMETRY_RECORD_AREA;
2262
2263 // Length calculation is deferred...
2264
2265 baseRecord.extent_s_lat = ppg->Get_ymin();
2266 baseRecord.extent_n_lat = ppg->Get_ymax();
2267 baseRecord.extent_e_lon = ppg->Get_xmax();
2268 baseRecord.extent_w_lon = ppg->Get_xmin();
2269
2270 baseRecord.contour_count = ppg->GetnContours();
2271
2272 // Create the array of contour point counts
2273
2274 int contourPointCountArraySize = ppg->GetnContours() * sizeof(uint32_t);
2275 uint32_t *contourPointCountArray =
2276 (uint32_t *)malloc(contourPointCountArraySize);
2277
2278 uint32_t *pr = contourPointCountArray;
2279
2280 for (int i = 0; i < ppg->GetnContours(); i++) {
2281 *pr++ = ppg->Get_PolyTriGroup_head()->pn_vertex[i];
2282 }
2283
2284 // All that is left is the tesselation result Triangle lists...
2285 // This could be a large array, and we don't want to use a buffer.
2286 // Rather, we want to write directly to the output file.
2287
2288 // So, we
2289 // a walk the TriPrim chain once to get it's length,
2290 // b update the total record length,
2291 // c write everything before the TriPrim chain,
2292 // d and then directly write the TriPrim chain.
2293
2294 // Walk the TriPrim chain
2295 int geoLength = 0;
2296
2297 TriPrim *pTP = ppg->Get_PolyTriGroup_head()
2298 ->tri_prim_head; // head of linked list of TriPrims
2299
2300 int n_TriPrims = 0;
2301 while (pTP) {
2302 geoLength += sizeof(uint8_t) + sizeof(uint32_t); // type, nvert
2303 geoLength += pTP->nVert * 2 * sizeof(float); // vertices
2304 geoLength += 4 * sizeof(double); // Primitive bounding box
2305 pTP = pTP->p_next;
2306
2307 n_TriPrims++;
2308 }
2309
2310 baseRecord.triprim_count = n_TriPrims; // Set the number of TriPrims
2311
2312 int nEdgeVectorRecords = 0;
2313 unsigned char *pvec_buffer =
2314 getObjectVectorIndexTable(poReader, pFeature, nEdgeVectorRecords);
2315
2316#if 0
2317 // Create the Vector Edge Index table into a memory buffer
2318 // This buffer will follow the triangle buffer in the output stream
2319 int *pNAME_RCID;
2320 int *pORNT;
2321 int nEdgeVectorRecords;
2322 OGRFeature *pEdgeVectorRecordFeature;
2323
2324 pNAME_RCID = (int *) pFeature->GetFieldAsIntegerList( "NAME_RCID", &nEdgeVectorRecords );
2325 pORNT = (int *) pFeature->GetFieldAsIntegerList( "ORNT", NULL );
2326
2327 baseRecord.edgeVector_count = nEdgeVectorRecords;
2328
2329 unsigned char *pvec_buffer = (unsigned char *)malloc(nEdgeVectorRecords * 3 * sizeof(int));
2330 unsigned char *pvRun = pvec_buffer;
2331
2332 // Set up the options, adding RETURN_PRIMITIVES
2333 char ** papszReaderOptions = NULL;
2334 papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
2335 papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES,"ON" );
2336 papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"ON" );
2337 poReader->SetOptions( papszReaderOptions );
2338
2339 // Capture the beginning and end point connected nodes for each edge vector record
2340 for( int i = 0; i < nEdgeVectorRecords; i++ ) {
2341
2342 int *pI = (int *)pvRun;
2343
2344 int start_rcid, end_rcid;
2345 int target_record_feid = m_vector_helper_hash[pNAME_RCID[i]];
2346 pEdgeVectorRecordFeature = poReader->ReadVector( target_record_feid, RCNM_VE );
2347
2348 int edge_rcid = pNAME_RCID[i];
2349
2350 if( NULL != pEdgeVectorRecordFeature ) {
2351 start_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_0" );
2352 end_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_1" );
2353 // Make sure the start and end points exist....
2354 // Note this poReader method was converted to Public access to
2355 // facilitate this test. There might be another clean way....
2356 // Problem first found on Holand ENC 1R5YM009.000
2357 if( !poReader->FetchPoint( RCNM_VC, start_rcid, NULL, NULL, NULL, NULL ) )
2358 start_rcid = -1;
2359 if( !poReader->FetchPoint( RCNM_VC, end_rcid, NULL, NULL, NULL, NULL ) )
2360 end_rcid = -2;
2361
2362 int edge_ornt = 1;
2363 // Allocate some storage for converted points
2364
2365 if( edge_ornt == 1 ){ // forward
2366 *pI++ = start_rcid;
2367 *pI++ = edge_rcid;
2368 *pI++ = end_rcid;
2369 } else { // reverse
2370 *pI++ = end_rcid;
2371 *pI++ = edge_rcid;
2372 *pI++ = start_rcid;
2373 }
2374
2375 delete pEdgeVectorRecordFeature;
2376 } else {
2377 start_rcid = -1; // error indication
2378 end_rcid = -2;
2379
2380 *pI++ = start_rcid;
2381 *pI++ = edge_rcid;
2382 *pI++ = end_rcid;
2383 }
2384
2385 pvRun += 3 * sizeof(int);
2386 }
2387
2388
2389 // Reset the options
2390 papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"OFF" );
2391 poReader->SetOptions( papszReaderOptions );
2392 CSLDestroy( papszReaderOptions );
2393
2394#endif
2395
2396 baseRecord.edgeVector_count = nEdgeVectorRecords;
2397
2398 // Calculate the total record length
2399 int recordLength = sizeof(OSENC_AreaGeometry_Record_Base);
2400 recordLength += contourPointCountArraySize;
2401 recordLength += geoLength;
2402 recordLength += nEdgeVectorRecords * 3 * sizeof(int);
2403 baseRecord.record_length = recordLength;
2404
2405 // Write the base record
2406 size_t targetCount = sizeof(baseRecord);
2407 if (!stream->Write(&baseRecord, targetCount).IsOk()) return false;
2408
2409 // Write the contour point count array
2410 targetCount = contourPointCountArraySize;
2411 if (!stream->Write(contourPointCountArray, targetCount).IsOk()) return false;
2412
2413 // Walk and transcribe the TriPrim chain
2414 pTP = ppg->Get_PolyTriGroup_head()
2415 ->tri_prim_head; // head of linked list of TriPrims
2416 while (pTP) {
2417 if (!stream->Write(&pTP->type, sizeof(uint8_t)).IsOk()) return false;
2418 if (!stream->Write(&pTP->nVert, sizeof(uint32_t)).IsOk()) return false;
2419
2420 // fwrite (&pTP->minxt , sizeof(double), 1, fpOut);
2421 // fwrite (&pTP->maxxt , sizeof(double), 1, fpOut);
2422 // fwrite (&pTP->minyt , sizeof(double), 1, fpOut);
2423 // fwrite (&pTP->maxyt , sizeof(double), 1, fpOut);
2424
2425 double minlat, minlon, maxlat, maxlon;
2426 minlat = pTP->tri_box.GetMinLat();
2427 minlon = pTP->tri_box.GetMinLon();
2428 maxlat = pTP->tri_box.GetMaxLat();
2429 maxlon = pTP->tri_box.GetMaxLon();
2430
2431 if (!stream->Write(&minlon, sizeof(double)).IsOk()) return false;
2432 if (!stream->Write(&maxlon, sizeof(double)).IsOk()) return false;
2433 if (!stream->Write(&minlat, sizeof(double)).IsOk()) return false;
2434 if (!stream->Write(&maxlat, sizeof(double)).IsOk()) return false;
2435
2436 // Testing TODO
2437 // float *pf = (float *)pTP->p_vertex;
2438 // float a = *pf++;
2439 // float b = *pf;
2440
2441 if (!stream->Write(pTP->p_vertex, pTP->nVert * 2 * sizeof(float)).IsOk())
2442 return false;
2443
2444 pTP = pTP->p_next;
2445 }
2446
2447 // Write the Edge Vector index table
2448 targetCount = nEdgeVectorRecords * 3 * sizeof(int);
2449 if (!stream->Write(pvec_buffer, targetCount).IsOk()) return false;
2450
2451 delete ppg;
2452 free(contourPointCountArray);
2453 free(pvec_buffer);
2454
2455 return true;
2456}
2457
2458unsigned char *Osenc::getObjectVectorIndexTable(S57Reader *poReader,
2459 OGRFeature *pFeature,
2460 int &nEntries) {
2461 // Create the Vector Edge Index table into a memory buffer
2462 // This buffer will follow the triangle buffer in the output stream
2463 int *pNAME_RCID;
2464 int *pORNT;
2465 int nEdgeVectorRecords;
2466 OGRFeature *pEdgeVectorRecordFeature;
2467
2468 pNAME_RCID =
2469 (int *)pFeature->GetFieldAsIntegerList("NAME_RCID", &nEdgeVectorRecords);
2470 pORNT = (int *)pFeature->GetFieldAsIntegerList("ORNT", NULL);
2471
2472 nEntries = nEdgeVectorRecords;
2473
2474 unsigned char *pvec_buffer =
2475 (unsigned char *)malloc(nEdgeVectorRecords * 3 * sizeof(int));
2476 unsigned char *pvRun = pvec_buffer;
2477
2478 // Set up the options, adding RETURN_PRIMITIVES
2479 char **papszReaderOptions = NULL;
2480 papszReaderOptions = CSLSetNameValue(papszReaderOptions, S57O_UPDATES, "ON");
2481 papszReaderOptions =
2482 CSLSetNameValue(papszReaderOptions, S57O_RETURN_LINKAGES, "ON");
2483 papszReaderOptions =
2484 CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON");
2485 poReader->SetOptions(papszReaderOptions);
2486
2487 // Capture the beginning and end point connected nodes for each edge vector
2488 // record
2489 for (int i = 0; i < nEdgeVectorRecords; i++) {
2490 int *pI = (int *)pvRun;
2491
2492 int edge_rcid = pNAME_RCID[i];
2493
2494 int start_rcid, end_rcid;
2495 int target_record_feid = m_vector_helper_hash[pNAME_RCID[i]];
2496 pEdgeVectorRecordFeature =
2497 poReader->ReadVector(target_record_feid, RCNM_VE);
2498
2499 if (NULL != pEdgeVectorRecordFeature) {
2500 start_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger("NAME_RCID_0");
2501 end_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger("NAME_RCID_1");
2502
2503 // Make sure the start and end points exist....
2504 // Note this poReader method was converted to Public access to
2505 // facilitate this test. There might be another clean way....
2506 // Problem first found on Holand ENC 1R5YM009.000
2507 if (!poReader->FetchPoint(RCNM_VC, start_rcid, NULL, NULL, NULL, NULL))
2508 start_rcid = -1;
2509 if (!poReader->FetchPoint(RCNM_VC, end_rcid, NULL, NULL, NULL, NULL))
2510 end_rcid = -2;
2511
2512 OGRLineString *poLS =
2513 (OGRLineString *)pEdgeVectorRecordFeature->GetGeometryRef();
2514 if (!poLS)
2515 edge_rcid = 0;
2516 else if (poLS->getNumPoints() < 1)
2517 edge_rcid = 0;
2518
2519 int edge_ornt = pORNT[i];
2520
2521 if (edge_ornt == 1) { // forward
2522 *pI++ = start_rcid;
2523 *pI++ = edge_rcid;
2524 *pI++ = end_rcid;
2525 } else { // reverse
2526 *pI++ = end_rcid;
2527 *pI++ = -edge_rcid;
2528 *pI++ = start_rcid;
2529 }
2530
2531 delete pEdgeVectorRecordFeature;
2532 } else {
2533 start_rcid = -1; // error indication
2534 end_rcid = -2;
2535
2536 *pI++ = start_rcid;
2537 *pI++ = edge_rcid;
2538 *pI++ = end_rcid;
2539 }
2540
2541 pvRun += 3 * sizeof(int);
2542 }
2543
2544 // Reset the options
2545 papszReaderOptions =
2546 CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF");
2547 poReader->SetOptions(papszReaderOptions);
2548 CSLDestroy(papszReaderOptions);
2549
2550 return pvec_buffer;
2551}
2552
2553void Osenc::CreateSENCVectorEdgeTableRecord200(Osenc_outstream *stream,
2554 S57Reader *poReader) {
2555 // We create the payload first, so we can calculate the total record length
2556 uint8_t *pPayload = NULL;
2557 int payloadSize = 0;
2558 uint8_t *pRun = pPayload;
2559
2560 // Set up the S57Reader options, adding RETURN_PRIMITIVES
2561 char **papszReaderOptions = NULL;
2562 papszReaderOptions = CSLSetNameValue(papszReaderOptions, S57O_UPDATES, "ON");
2563 papszReaderOptions =
2564 CSLSetNameValue(papszReaderOptions, S57O_RETURN_LINKAGES, "ON");
2565 papszReaderOptions =
2566 CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON");
2567 poReader->SetOptions(papszReaderOptions);
2568
2569 int feid = 0;
2570 OGRLineString *pLS = NULL;
2571 OGRGeometry *pGeo;
2572 OGRFeature *pEdgeVectorRecordFeature = poReader->ReadVector(feid, RCNM_VE);
2573
2574 int nFeatures = 0;
2575
2576 // Read all the EdgeVector Features
2577 while (NULL != pEdgeVectorRecordFeature) {
2578 // Check for a zero point count. Dunno why this should happen, other than
2579 // bad ENC encoding
2580
2581 int nPoints = 0;
2582 if (pEdgeVectorRecordFeature->GetGeometryRef() != NULL) {
2583 pGeo = pEdgeVectorRecordFeature->GetGeometryRef();
2584 if (pGeo->getGeometryType() == wkbLineString) {
2585 pLS = (OGRLineString *)pGeo;
2586 nPoints = pLS->getNumPoints();
2587 } else
2588 nPoints = 0;
2589 }
2590
2591 if (nPoints) {
2592 int new_size = payloadSize + (2 * sizeof(int));
2593 pPayload = (uint8_t *)realloc(pPayload, new_size);
2594 pRun = pPayload + payloadSize; // recalculate the running pointer,
2595 // since realloc may have moved memory
2596 payloadSize = new_size;
2597
2598 // Fetch and store the Record ID
2599 int record_id = pEdgeVectorRecordFeature->GetFieldAsInteger("RCID");
2600 *(int *)pRun = record_id;
2601 pRun += sizeof(int);
2602
2603 // Transcribe points to a buffer
2604 // We reduce the maximum number of points in the table to
2605 // MAX_VECTOR_POINTS using a naive algorithm skipping the proportional
2606 // part of them to avoid too deep a recursion and crash in DouglasPeucker
2607 // later
2608 int reduction_ratio = nPoints / MAX_VECTOR_POINTS + 1;
2609 int reduced_points = 0;
2610
2611 double *ppd = (double *)malloc((nPoints / reduction_ratio + 1) * 2 *
2612 sizeof(double));
2613 double *ppr = ppd;
2614
2615 for (int i = 0; i < nPoints; i++) {
2616 if (i % reduction_ratio == 0) {
2617 OGRPoint p;
2618 pLS->getPoint(i, &p);
2619
2620 // Calculate SM from chart common reference point
2621 double easting, northing;
2622 toSM(p.getY(), p.getX(), m_ref_lat, m_ref_lon, &easting, &northing);
2623
2624 *ppr++ = easting;
2625 *ppr++ = northing;
2626 reduced_points++;
2627 }
2628 }
2629 nPoints = reduced_points;
2630
2631 // Reduce the LOD of this linestring
2632 std::vector<int> index_keep;
2633 if (nPoints > 5 && (m_LOD_meters > .01)) {
2634 index_keep.push_back(0);
2635 index_keep.push_back(nPoints - 1);
2636
2637 DouglasPeucker(ppd, 0, nPoints - 1, m_LOD_meters, &index_keep);
2638 // printf("DP Reduction: %d/%d\n", index_keep.GetCount(),
2639 // nPoints);
2640
2641 } else {
2642 index_keep.resize(nPoints);
2643 for (int i = 0; i < nPoints; i++) index_keep[i] = i;
2644 }
2645
2646 // Store the point count in the payload
2647 int nPointReduced = index_keep.size();
2648 *(int *)pRun = nPointReduced;
2649 pRun += sizeof(int);
2650
2651 // transcribe the (possibly) reduced linestring to the payload
2652
2653 // Grow the payload buffer
2654 int new_size_red = payloadSize + (nPointReduced * 2 * sizeof(float));
2655 pPayload = (uint8_t *)realloc(pPayload, new_size_red);
2656 pRun = pPayload + payloadSize; // recalculate the running pointer,
2657 // since realloc may have moved memory
2658 payloadSize = new_size_red;
2659
2660 float *npp = (float *)pRun;
2661 float *npp_run = npp;
2662 ppr = ppd;
2663 for (int ip = 0; ip < nPoints; ip++) {
2664 double x = *ppr++;
2665 double y = *ppr++;
2666
2667 for (unsigned int j = 0; j < index_keep.size(); j++) {
2668 if (index_keep[j] == ip) {
2669 *npp_run++ = x;
2670 *npp_run++ = y;
2671 pRun += 2 * sizeof(float);
2672 break;
2673 }
2674 }
2675 }
2676
2677 nFeatures++;
2678
2679 free(ppd);
2680 }
2681
2682 // Next vector record
2683 feid++;
2684 delete pEdgeVectorRecordFeature;
2685 pEdgeVectorRecordFeature = poReader->ReadVector(feid, RCNM_VE);
2686
2687 } // while
2688
2689 // Now we know the payload length and the Feature count
2690 if (nFeatures) {
2691 // Now write the record out
2692 OSENC_VET_Record record;
2693
2694 record.record_type = VECTOR_EDGE_NODE_TABLE_RECORD;
2695 record.record_length =
2696 sizeof(OSENC_VET_Record_Base) + payloadSize + sizeof(uint32_t);
2697
2698 // Write out the record
2699 stream->Write(&record, sizeof(OSENC_VET_Record_Base));
2700
2701 // Write out the Feature(Object) count
2702 stream->Write(&nFeatures, sizeof(uint32_t));
2703
2704 // Write out the payload
2705 stream->Write(pPayload, payloadSize);
2706 }
2707 // All done with buffer
2708 free(pPayload);
2709
2710 // Reset the S57Reader options
2711 papszReaderOptions =
2712 CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF");
2713 poReader->SetOptions(papszReaderOptions);
2714 CSLDestroy(papszReaderOptions);
2715}
2716
2717void Osenc::CreateSENCVectorConnectedTableRecord200(Osenc_outstream *stream,
2718 S57Reader *poReader) {
2719 // We create the payload first, so we can calculate the total record length
2720 uint8_t *pPayload = NULL;
2721 int payloadSize = 0;
2722 uint8_t *pRun = pPayload;
2723
2724 // Set up the S57Reader options, adding RETURN_PRIMITIVES
2725 char **papszReaderOptions = NULL;
2726 papszReaderOptions = CSLSetNameValue(papszReaderOptions, S57O_UPDATES, "ON");
2727 papszReaderOptions =
2728 CSLSetNameValue(papszReaderOptions, S57O_RETURN_LINKAGES, "ON");
2729 papszReaderOptions =
2730 CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON");
2731 poReader->SetOptions(papszReaderOptions);
2732
2733 int feid = 0;
2734 OGRPoint *pP;
2735 OGRGeometry *pGeo;
2736 OGRFeature *pConnNodeRecordFeature = poReader->ReadVector(feid, RCNM_VC);
2737 int featureCount = 0;
2738
2739 // Read all the ConnectedVector Features
2740 while (NULL != pConnNodeRecordFeature) {
2741 if (pConnNodeRecordFeature->GetGeometryRef() != NULL) {
2742 pGeo = pConnNodeRecordFeature->GetGeometryRef();
2743 if (pGeo->getGeometryType() == wkbPoint) {
2744 int new_size = payloadSize + sizeof(int) + (2 * sizeof(float));
2745 pPayload = (uint8_t *)realloc(pPayload, new_size);
2746 pRun = pPayload + payloadSize; // recalculate the running pointer,
2747 // since realloc may have moved memory
2748 payloadSize = new_size;
2749
2750 // Fetch and store the Record ID
2751 int record_id = pConnNodeRecordFeature->GetFieldAsInteger("RCID");
2752 *(int *)pRun = record_id;
2753 pRun += sizeof(int);
2754
2755 pP = (OGRPoint *)pGeo;
2756
2757 // Calculate SM from chart common reference point
2758 double easting, northing;
2759 toSM(pP->getY(), pP->getX(), m_ref_lat, m_ref_lon, &easting, &northing);
2760
2761 // MyPoint pd;
2762 // pd.x = easting;
2763 // pd.y = northing;
2764 // memcpy(pRun, &pd, sizeof(MyPoint));
2765 float *ps = (float *)pRun;
2766 *ps++ = easting;
2767 *ps = northing;
2768
2769 featureCount++;
2770 }
2771 }
2772
2773 // Next vector record
2774 feid++;
2775 delete pConnNodeRecordFeature;
2776 pConnNodeRecordFeature = poReader->ReadVector(feid, RCNM_VC);
2777 } // while
2778
2779 // Now we know the payload length and the Feature count
2780
2781 // Now write the record out
2782 if (featureCount) {
2783 OSENC_VCT_Record record;
2784
2785 record.record_type = VECTOR_CONNECTED_NODE_TABLE_RECORD;
2786 record.record_length =
2787 sizeof(OSENC_VCT_Record_Base) + payloadSize + sizeof(int);
2788
2789 // Write out the record
2790 stream->Write(&record, sizeof(OSENC_VCT_Record_Base));
2791
2792 // Write out the Feature(Object) count
2793 stream->Write(&featureCount, sizeof(uint32_t));
2794
2795 // Write out the payload
2796 stream->Write(pPayload, payloadSize);
2797 }
2798
2799 // All done with buffer
2800 free(pPayload);
2801
2802 // Reset the S57Reader options
2803 papszReaderOptions =
2804 CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF");
2805 poReader->SetOptions(papszReaderOptions);
2806 CSLDestroy(papszReaderOptions);
2807}
2808
2809bool Osenc::CreateSENCRecord200(OGRFeature *pFeature, Osenc_outstream *stream,
2810 int mode, S57Reader *poReader) {
2811 // TODO
2812 // if(pFeature->GetFID() == 207)
2813 // int yyp = 4;
2814
2815 // Create the Feature Identification Record
2816
2817 // Fetch the S57 Standard Object Class identifier
2818 OGRFeatureDefn *pFD = pFeature->GetDefnRef();
2819 int nOBJL = pFD->GetOBJL();
2820
2821 OGRGeometry *pGeo = pFeature->GetGeometryRef();
2822 OGRwkbGeometryType gType = pGeo->getGeometryType();
2823
2824 int primitive = 0;
2825 switch (gType) {
2826 case wkbLineString:
2827 primitive = GEO_LINE;
2828 break;
2829 case wkbPoint:
2830 primitive = GEO_POINT;
2831 break;
2832 case wkbPolygon:
2833 primitive = GEO_AREA;
2834 break;
2835 default:
2836 primitive = 0;
2837 }
2838
2839 if (!WriteFIDRecord200(stream, nOBJL, pFeature->GetFID(), primitive))
2840 return false;
2841
2842#define MAX_HDR_LINE 400
2843
2844 // char line[MAX_HDR_LINE + 1];
2845 wxString sheader;
2846
2847 // fprintf( fpOut, "OGRFeature(%s):%ld\n",
2848 // pFeature->GetDefnRef()->GetName(), pFeature->GetFID() );
2849
2850 // In a loop, fetch attributes, and create OSENC Feature Attribute Records
2851 // In the interests of output file size, DO NOT report fields that are not
2852 // set.
2853
2854 // TODO Debugging
2855 // if(!strncmp(pFeature->GetDefnRef()->GetName(), "BOYLAT", 6))
2856 // int yyp = 4;
2857
2858 if (pFeature->GetFID() == 290) int yyp = 4;
2859
2860 int payloadLength = 0;
2861 void *payloadBuffer = NULL;
2862 unsigned int payloadBufferLength = 0;
2863
2864 for (int iField = 0; iField < pFeature->GetFieldCount(); iField++) {
2865 if (pFeature->IsFieldSet(iField)) {
2866 if ((iField == 1) || (iField > 7)) {
2867 OGRFieldDefn *poFDefn = pFeature->GetDefnRef()->GetFieldDefn(iField);
2868 // const char *pType = OGRFieldDefn::GetFieldTypeName(
2869 // poFDefn->GetType() );
2870 const char *pAttrName = poFDefn->GetNameRef();
2871 const char *pAttrVal = pFeature->GetFieldAsString(iField);
2872
2873 // Use the OCPN Registrar Manager to map attribute acronym to an
2874 // identifier. The mapping is defined by the file
2875 // {csv_dir}/s57attributes.csv
2876 int attributeID = m_pRegistrarMan->getAttributeID(pAttrName);
2877
2878 // Determine the {attribute_value_type} needed in the record
2879 int OGRvalueType = (int)poFDefn->GetType();
2880 int valueType = 0;
2881
2882 // Check for special cases
2883 if (-1 == attributeID) {
2884 if (!strncmp(pAttrName, "PRIM", 4)) {
2885 attributeID = ATTRIBUTE_ID_PRIM;
2886 }
2887 }
2888
2889#if 0 OFTInteger = 0, OFTIntegerList = 1, OFTReal = 2, OFTRealList = 3, OFTString = 4, OFTStringList = 5, OFTWideString = 6, OFTWideStringList = 7, OFTBinary = 8
2899#endif
2900 switch (OGRvalueType) {
2901 case 0: // Single integer
2902 {
2903 valueType = OGRvalueType;
2904
2905 if (payloadBufferLength < 4) {
2906 payloadBuffer = realloc(payloadBuffer, 4);
2907 payloadBufferLength = 4;
2908 }
2909
2910 int aValue = pFeature->GetFieldAsInteger(iField);
2911 memcpy(payloadBuffer, &aValue, sizeof(int));
2912 payloadLength = sizeof(int);
2913
2914 break;
2915 }
2916 case 1: // Integer list
2917 {
2918 valueType = OGRvalueType;
2919
2920 int nCount = 0;
2921 const int *aValueList =
2922 pFeature->GetFieldAsIntegerList(iField, &nCount);
2923
2924 if (payloadBufferLength < nCount * sizeof(int)) {
2925 payloadBuffer = realloc(payloadBuffer, nCount * sizeof(int));
2926 payloadBufferLength = nCount * sizeof(int);
2927 }
2928
2929 int *pBuffRun = (int *)payloadBuffer;
2930 for (int i = 0; i < nCount; i++) {
2931 *pBuffRun++ = aValueList[i];
2932 }
2933 payloadLength = nCount * sizeof(int);
2934
2935 break;
2936 }
2937 case 2: // Single double precision real
2938 {
2939 valueType = OGRvalueType;
2940
2941 if (payloadBufferLength < sizeof(double)) {
2942 payloadBuffer = realloc(payloadBuffer, sizeof(double));
2943 payloadBufferLength = sizeof(double);
2944 }
2945
2946 double aValue = pFeature->GetFieldAsDouble(iField);
2947 memcpy(payloadBuffer, &aValue, sizeof(double));
2948 payloadLength = sizeof(double);
2949
2950 break;
2951 }
2952
2953 case 3: // List of double precision real
2954 {
2955 valueType = OGRvalueType;
2956
2957 int nCount = 0;
2958 const double *aValueList =
2959 pFeature->GetFieldAsDoubleList(iField, &nCount);
2960
2961 if (payloadBufferLength < nCount * sizeof(double)) {
2962 payloadBuffer = realloc(payloadBuffer, nCount * sizeof(double));
2963 payloadBufferLength = nCount * sizeof(double);
2964 }
2965
2966 double *pBuffRun = (double *)payloadBuffer;
2967 for (int i = 0; i < nCount; i++) {
2968 *pBuffRun++ = aValueList[i];
2969 }
2970 payloadLength = nCount * sizeof(double);
2971
2972 break;
2973 }
2974
2975 case 4: // Ascii String
2976 {
2977 valueType = OGRvalueType;
2978 const char *pAttrVal = pFeature->GetFieldAsString(iField);
2979
2980 wxString wxAttrValue;
2981
2982 if ((0 == strncmp("NOBJNM", pAttrName, 6)) ||
2983 (0 == strncmp("NINFOM", pAttrName, 6)) ||
2984 (0 == strncmp("NPLDST", pAttrName, 6)) ||
2985 (0 == strncmp("NTXTDS", pAttrName, 6))) {
2986 if (poReader->GetNall() ==
2987 2) { // ENC is using UCS-2 / UTF-16 encoding
2988 wxMBConvUTF16 conv;
2989 wxString att_conv(pAttrVal, conv);
2990 att_conv.RemoveLast(); // Remove the \037 that terminates
2991 // UTF-16 strings in S57
2992 att_conv.Replace(_T("\n"),
2993 _T("|")); // Replace <new line> with special
2994 // break character
2995 wxAttrValue = att_conv;
2996 } else if (poReader->GetNall() ==
2997 1) { // ENC is using Lex level 1 (ISO 8859_1) encoding
2998 wxCSConv conv(_T("iso8859-1"));
2999 wxString att_conv(pAttrVal, conv);
3000 wxAttrValue = att_conv;
3001 }
3002 } else {
3003 if (poReader->GetAall() ==
3004 1) { // ENC is using Lex level 1 (ISO 8859_1) encoding for
3005 // "General Text"
3006 wxCSConv conv(_T("iso8859-1"));
3007 wxString att_conv(pAttrVal, conv);
3008 wxAttrValue = att_conv;
3009 } else
3010 wxAttrValue =
3011 wxString(pAttrVal); // ENC must be using Lex level 0
3012 // (ASCII) encoding for "General Text"
3013 }
3014
3015 unsigned int stringPayloadLength = 0;
3016
3017 wxCharBuffer buffer;
3018 if (wxAttrValue.Length()) { // need to explicitely encode as UTF8
3019 buffer = wxAttrValue.ToUTF8();
3020 pAttrVal = buffer.data();
3021 stringPayloadLength = strlen(buffer.data());
3022 }
3023
3024 if (stringPayloadLength) {
3025 if (payloadBufferLength < stringPayloadLength + 1) {
3026 payloadBuffer = realloc(payloadBuffer, stringPayloadLength + 1);
3027 payloadBufferLength = stringPayloadLength + 1;
3028 }
3029
3030 strcpy((char *)payloadBuffer, pAttrVal);
3031 payloadLength = stringPayloadLength + 1;
3032 } else
3033 attributeID = -1; // cancel this attribute record
3034
3035 break;
3036 }
3037
3038 default:
3039 valueType = -1;
3040 break;
3041 }
3042
3043 if (-1 != attributeID) {
3044 // Build the record
3045 int recordLength =
3046 sizeof(OSENC_Attribute_Record_Base) + payloadLength;
3047
3048 // Get a reference to the class persistent buffer
3049 unsigned char *pBuffer = getBuffer(recordLength);
3050
3051 OSENC_Attribute_Record *pRecord = (OSENC_Attribute_Record *)pBuffer;
3052 memset(pRecord, 0, sizeof(OSENC_Attribute_Record));
3053 pRecord->record_type = FEATURE_ATTRIBUTE_RECORD;
3054 pRecord->record_length = recordLength;
3055 pRecord->attribute_type = attributeID;
3056 pRecord->attribute_value_type = valueType;
3057 memcpy(&pRecord->payload, payloadBuffer, payloadLength);
3058
3059 // Write the record out....
3060 size_t targetCount = recordLength;
3061 if (!stream->Write(pBuffer, targetCount).IsOk()) {
3062 free(payloadBuffer);
3063 return false;
3064 }
3065 }
3066 }
3067 }
3068 }
3069 if (wkbPoint == pGeo->getGeometryType()) {
3070 OGRPoint *pp = (OGRPoint *)pGeo;
3071 int nqual = pp->getnQual();
3072 if (10 != nqual) // only add attribute if nQual is not "precisely known"
3073 {
3074 int attributeID = m_pRegistrarMan->getAttributeID("QUAPOS");
3075 int valueType = 0;
3076 if (-1 != attributeID) {
3077 if (payloadBufferLength < 4) {
3078 payloadBuffer = realloc(payloadBuffer, 4);
3079 payloadBufferLength = 4;
3080 }
3081
3082 memcpy(payloadBuffer, &nqual, sizeof(int));
3083 payloadLength = sizeof(int);
3084 // Build the record
3085 int recordLength = sizeof(OSENC_Attribute_Record_Base) + payloadLength;
3086
3087 // Get a reference to the class persistent buffer
3088 unsigned char *pBuffer = getBuffer(recordLength);
3089
3090 OSENC_Attribute_Record *pRecord = (OSENC_Attribute_Record *)pBuffer;
3091 memset(pRecord, 0, sizeof(OSENC_Attribute_Record));
3092 pRecord->record_type = FEATURE_ATTRIBUTE_RECORD;
3093 pRecord->record_length = recordLength;
3094 pRecord->attribute_type = attributeID;
3095 pRecord->attribute_value_type = valueType;
3096 memcpy(&pRecord->payload, payloadBuffer, payloadLength);
3097
3098 // Write the record out....
3099 size_t targetCount = recordLength;
3100 if (!stream->Write(pBuffer, targetCount).IsOk()) {
3101 free(payloadBuffer);
3102 return false;
3103 }
3104 }
3105 }
3106 }
3107
3108 free(payloadBuffer);
3109
3110#if 0
3111 // Special geometry cases
3113 if( wkbPoint == pGeo->getGeometryType() ) {
3114 OGRPoint *pp = (OGRPoint *) pGeo;
3115 int nqual = pp->getnQual();
3116 if( 10 != nqual ) // only add attribute if nQual is not "precisely known"
3117 {
3118 snprintf( line, MAX_HDR_LINE - 2, " %s (%c) = %d", "QUALTY", 'I', nqual );
3119 sheader += wxString( line, wxConvUTF8 );
3120 sheader += '\n';
3121 }
3122
3123 }
3124
3125 if( mode == 1 ) {
3126 sprintf( line, " %s %f %f\n", pGeo->getGeometryName(), m_ref_lat, m_ref_lon );
3127 sheader += wxString( line, wxConvUTF8 );
3128 }
3129
3130 wxCharBuffer buffer=sheader.ToUTF8();
3131 fprintf( fpOut, "HDRLEN=%lu\n", (unsigned long) strlen(buffer) );
3132 fwrite( buffer.data(), 1, strlen(buffer), fpOut );
3133
3134#endif
3135
3136 if ((pGeo != NULL)) {
3137 wxString msg;
3138
3139 OGRwkbGeometryType gType = pGeo->getGeometryType();
3140 switch (gType) {
3141 case wkbLineString: {
3142 if (!CreateLineFeatureGeometryRecord200(poReader, pFeature, stream))
3143 return false;
3144
3145 break;
3146 }
3147
3148 case wkbPoint: {
3150 record.record_type = FEATURE_GEOMETRY_RECORD_POINT;
3151 record.record_length = sizeof(record);
3152
3153 int wkb_len = pGeo->WkbSize();
3154 unsigned char *pwkb_buffer = (unsigned char *)malloc(wkb_len);
3155
3156 // Get the GDAL data representation
3157 pGeo->exportToWkb(wkbNDR, pwkb_buffer);
3158
3159 int nq_len = 4; // nQual length
3160 unsigned char *ps = pwkb_buffer;
3161
3162 ps += 5 + nq_len;
3163 double *psd = (double *)ps;
3164
3165 double lat, lon;
3166#ifdef __ARM_ARCH
3167 __attribute__((aligned(16))) double lata, lona;
3168 unsigned char *pucsd = (unsigned char *)psd;
3169
3170 memcpy(&lona, pucsd, sizeof(double));
3171 pucsd += sizeof(double);
3172 memcpy(&lata, pucsd, sizeof(double));
3173 lon = lona;
3174 lat = lata;
3175#else
3176 lon = *psd++; // fetch the point
3177 lat = *psd;
3178#endif
3179
3180 free(pwkb_buffer);
3181
3182 record.lat = lat;
3183 record.lon = lon;
3184
3185 // Write the record out....
3186 size_t targetCount = record.record_length;
3187 if (!stream->Write(&record, targetCount).IsOk()) return false;
3188
3189 break;
3190 }
3191
3192 case wkbMultiPoint:
3193 case wkbMultiPoint25D: {
3194 if (!CreateMultiPointFeatureGeometryRecord200(pFeature, stream))
3195 return false;
3196 break;
3197 }
3198
3199#if 1
3200 // Special case, polygons are handled separately
3201 case wkbPolygon: {
3202 if (!CreateAreaFeatureGeometryRecord200(poReader, pFeature, stream))
3203 return false;
3204
3205 break;
3206 }
3207#endif
3208 // All others
3209 default:
3210 msg = _T(" Warning: Unimplemented ogr geotype record ");
3211 wxLogMessage(msg);
3212
3213 break;
3214 } // switch
3215 }
3216 return true;
3217}
3218
3219// Build PolyGeo Object from OSENC200 file record
3220// Return an integer count of bytes consumed from the record in creating
3221// the PolyTessGeo
3222PolyTessGeo *Osenc::BuildPolyTessGeo(_OSENC_AreaGeometry_Record_Payload *record,
3223 unsigned char **next_byte) {
3224 PolyTessGeo *pPTG = new PolyTessGeo();
3225
3226 pPTG->SetExtents(record->extent_w_lon, record->extent_s_lat,
3227 record->extent_e_lon, record->extent_n_lat);
3228
3229 unsigned int n_TriPrim = record->triprim_count;
3230 int nContours = record->contour_count;
3231
3232 // Get a pointer to the payload
3233 void *payLoad = &record->payLoad;
3234
3235 // skip over the contour vertex count array, for now TODO
3236 // uint8_t *pTriPrims = (uint8_t *)payLoad + (nContours * sizeof(uint32_t));
3237
3238 // Create the head of the linked list of TriPrims
3239 PolyTriGroup *ppg = new PolyTriGroup;
3240 ppg->m_bSMSENC = true;
3241 ppg->data_type = DATA_TYPE_DOUBLE;
3242
3243 ppg->nContours = nContours;
3244
3245 ppg->pn_vertex = (int *)malloc(nContours * sizeof(int));
3246 int *pctr = ppg->pn_vertex;
3247
3248 // The point count array is the first element in the payload, length is known
3249 int *contour_pointcount_array_run = (int *)payLoad;
3250 for (int i = 0; i < nContours; i++) {
3251 *pctr++ = *contour_pointcount_array_run++;
3252 }
3253
3254 // Read Raw Geometry
3255 ppg->pgroup_geom = NULL;
3256
3257 // Now the triangle primitives
3258
3259 TriPrim **p_prev_triprim = &(ppg->tri_prim_head);
3260
3261 // Read the PTG_Triangle Geometry in a loop
3262 unsigned int tri_type;
3263 int nvert;
3264 int nvert_max = 0;
3265 int total_byte_size = 2 * sizeof(float);
3266
3267 uint8_t *pPayloadRun =
3268 (uint8_t *)contour_pointcount_array_run; // Points to the start of the
3269 // triangle primitives
3270
3271 for (unsigned int i = 0; i < n_TriPrim; i++) {
3272 tri_type = *pPayloadRun++;
3273 nvert = *(uint32_t *)pPayloadRun;
3274 pPayloadRun += sizeof(uint32_t);
3275
3276 TriPrim *tp = new TriPrim;
3277 *p_prev_triprim = tp; // make the link
3278 p_prev_triprim = &(tp->p_next);
3279 tp->p_next = NULL;
3280
3281 tp->type = tri_type;
3282 tp->nVert = nvert;
3283
3284 nvert_max =
3285 wxMax(nvert_max, nvert); // Keep a running tab of largest vertex count
3286
3287 // Read the triangle primitive bounding box as lat/lon
3288 double *pbb = (double *)pPayloadRun;
3289
3290 double minxt, minyt, maxxt, maxyt;
3291
3292#ifdef __ARM_ARCH
3293 double __attribute__((aligned(16))) abox[4];
3294 unsigned char *pucbb = (unsigned char *)pPayloadRun;
3295 memcpy(&abox[0], pucbb, 4 * sizeof(double));
3296
3297 minxt = abox[0];
3298 maxxt = abox[1];
3299 minyt = abox[2];
3300 maxyt = abox[3];
3301#else
3302 minxt = *pbb++;
3303 maxxt = *pbb++;
3304 minyt = *pbb++;
3305 maxyt = *pbb;
3306#endif
3307
3308 tp->tri_box.Set(minyt, minxt, maxyt, maxxt);
3309
3310 pPayloadRun += 4 * sizeof(double);
3311
3312 int byte_size = nvert * 2 * sizeof(float); // the vertices
3313 total_byte_size += byte_size;
3314
3315 tp->p_vertex = (double *)pPayloadRun;
3316
3317 pPayloadRun += byte_size;
3318 }
3319
3320 if (next_byte) *next_byte = pPayloadRun;
3321
3322 // Convert the vertex arrays into a single float memory allocation to enable
3323 // efficient access later
3324 unsigned char *vbuf = (unsigned char *)malloc(total_byte_size);
3325
3326 TriPrim *p_tp = ppg->tri_prim_head;
3327 unsigned char *p_run = vbuf;
3328 while (p_tp) {
3329 memcpy(p_run, p_tp->p_vertex, p_tp->nVert * 2 * sizeof(float));
3330 p_tp->p_vertex = (double *)p_run;
3331 p_run += p_tp->nVert * 2 * sizeof(float);
3332 p_tp = p_tp->p_next; // pick up the next in chain
3333 }
3334 ppg->bsingle_alloc = true;
3335 ppg->single_buffer = vbuf;
3336 ppg->single_buffer_size = total_byte_size;
3337 ppg->data_type = DATA_TYPE_FLOAT;
3338
3339 pPTG->SetPPGHead(ppg);
3340 pPTG->SetnVertexMax(nvert_max);
3341
3342 pPTG->Set_OK(true);
3343
3344 return pPTG;
3345}
3346
3347bool Osenc::CreateCOVRTables(S57Reader *poReader,
3348 S57ClassRegistrar *poRegistrar) {
3349 poReader->Rewind();
3350
3351 OGRFeature *pFeat;
3352 int catcov;
3353 float LatMax, LatMin, LonMax, LonMin;
3354 LatMax = -90.;
3355 LatMin = 90.;
3356 LonMax = -179.;
3357 LonMin = 179.;
3358
3359 m_pCOVRTablePoints = NULL;
3360 m_pCOVRTable = NULL;
3361
3362 // Create arrays to hold geometry objects temporarily
3363 MyFloatPtrArray *pAuxPtrArray = new MyFloatPtrArray;
3364 std::vector<int> auxCntArray, noCovrCntArray;
3365
3366 MyFloatPtrArray *pNoCovrPtrArray = new MyFloatPtrArray;
3367
3368 // Get the first M_COVR object
3369 pFeat = GetChartFirstM_COVR(catcov, poReader, poRegistrar);
3370
3371 while (pFeat) {
3372 // Get the next M_COVR feature, and create possible additional entries
3373 // for COVR
3374 OGRPolygon *poly = (OGRPolygon *)(pFeat->GetGeometryRef());
3375 OGRLinearRing *xring = poly->getExteriorRing();
3376
3377 int npt = xring->getNumPoints();
3378
3379 float *pf = NULL;
3380
3381 if (npt >= 3) {
3382 pf = (float *)malloc(2 * npt * sizeof(float));
3383 float *pfr = pf;
3384
3385 for (int i = 0; i < npt; i++) {
3386 OGRPoint p;
3387 xring->getPoint(i, &p);
3388
3389 if (catcov == 1) {
3390 LatMax = fmax(LatMax, p.getY());
3391 LatMin = fmin(LatMin, p.getY());
3392 LonMax = fmax(LonMax, p.getX());
3393 LonMin = fmin(LonMin, p.getX());
3394 }
3395
3396 pfr[0] = p.getY(); // lat
3397 pfr[1] = p.getX(); // lon
3398
3399 pfr += 2;
3400 }
3401
3402 if (catcov == 1) {
3403 pAuxPtrArray->Add(pf);
3404 auxCntArray.push_back(npt);
3405 } else if (catcov == 2) {
3406 pNoCovrPtrArray->Add(pf);
3407 noCovrCntArray.push_back(npt);
3408 } else
3409 free(pf);
3410 }
3411
3412 delete pFeat;
3413 pFeat = GetChartNextM_COVR(catcov, poReader);
3414 } // while
3415
3416 // Allocate the storage
3417
3418 m_nCOVREntries = auxCntArray.size();
3419
3420 // If only one M_COVR,CATCOV=1 object was found,
3421 // assign the geometry to the one and only COVR
3422
3423 if (m_nCOVREntries == 1) {
3424 m_pCOVRTablePoints = (int *)malloc(sizeof(int));
3425 *m_pCOVRTablePoints = auxCntArray[0];
3426 m_pCOVRTable = (float **)malloc(sizeof(float *));
3427 *m_pCOVRTable = (float *)malloc(auxCntArray[0] * 2 * sizeof(float));
3428 memcpy(*m_pCOVRTable, pAuxPtrArray->Item(0),
3429 auxCntArray[0] * 2 * sizeof(float));
3430 }
3431
3432 else if (m_nCOVREntries > 1) {
3433 // Create new COVR entries
3434 m_pCOVRTablePoints = (int *)malloc(m_nCOVREntries * sizeof(int));
3435 m_pCOVRTable = (float **)malloc(m_nCOVREntries * sizeof(float *));
3436
3437 for (unsigned int j = 0; j < (unsigned int)m_nCOVREntries; j++) {
3438 m_pCOVRTablePoints[j] = auxCntArray[j];
3439 m_pCOVRTable[j] = (float *)malloc(auxCntArray[j] * 2 * sizeof(float));
3440 memcpy(m_pCOVRTable[j], pAuxPtrArray->Item(j),
3441 auxCntArray[j] * 2 * sizeof(float));
3442 }
3443 }
3444
3445 else // strange case, found no CATCOV=1 M_COVR objects
3446 {
3447 wxString msg(_T(" ENC contains no useable M_COVR, CATCOV=1 features: "));
3448 msg.Append(m_FullPath000);
3449 wxLogMessage(msg);
3450 }
3451
3452 // And for the NoCovr regions
3453 m_nNoCOVREntries = noCovrCntArray.size();
3454
3455 if (m_nNoCOVREntries) {
3456 // Create new NoCOVR entries
3457 m_pNoCOVRTablePoints = (int *)malloc(m_nNoCOVREntries * sizeof(int));
3458 m_pNoCOVRTable = (float **)malloc(m_nNoCOVREntries * sizeof(float *));
3459
3460 for (unsigned int j = 0; j < (unsigned int)m_nNoCOVREntries; j++) {
3461 int npoints = noCovrCntArray[j];
3462 m_pNoCOVRTablePoints[j] = npoints;
3463 m_pNoCOVRTable[j] = (float *)malloc(npoints * 2 * sizeof(float));
3464 memcpy(m_pNoCOVRTable[j], pNoCovrPtrArray->Item(j),
3465 npoints * 2 * sizeof(float));
3466 }
3467 } else {
3468 m_pNoCOVRTablePoints = NULL;
3469 m_pNoCOVRTable = NULL;
3470 }
3471
3472 for (unsigned int j = 0; j < (unsigned int)m_nNoCOVREntries; j++)
3473 free(pNoCovrPtrArray->Item(j));
3474 for (unsigned int j = 0; j < (unsigned int)m_nCOVREntries; j++)
3475 free(pAuxPtrArray->Item(j));
3476
3477 delete pAuxPtrArray;
3478 delete pNoCovrPtrArray;
3479
3480 if (0 == m_nCOVREntries) { // fallback
3481 wxString msg(_T(" ENC contains no M_COVR features: "));
3482 msg.Append(m_FullPath000);
3483 wxLogMessage(msg);
3484
3485 msg = _T(" Calculating Chart Extents as fallback.");
3486 wxLogMessage(msg);
3487
3488 OGREnvelope Env;
3489
3490 if (poReader->GetExtent(&Env, true) == OGRERR_NONE) {
3491 LatMax = Env.MaxY;
3492 LonMax = Env.MaxX;
3493 LatMin = Env.MinY;
3494 LonMin = Env.MinX;
3495
3496 m_nCOVREntries = 1;
3497 m_pCOVRTablePoints = (int *)malloc(sizeof(int));
3498 *m_pCOVRTablePoints = 4;
3499 m_pCOVRTable = (float **)malloc(sizeof(float *));
3500 float *pf = (float *)malloc(2 * 4 * sizeof(float));
3501 *m_pCOVRTable = pf;
3502 float *pfe = pf;
3503
3504 *pfe++ = LatMax;
3505 *pfe++ = LonMin;
3506
3507 *pfe++ = LatMax;
3508 *pfe++ = LonMax;
3509
3510 *pfe++ = LatMin;
3511 *pfe++ = LonMax;
3512
3513 *pfe++ = LatMin;
3514 *pfe++ = LonMin;
3515
3516 } else {
3517 wxString msg(_T(" Cannot calculate Extents for ENC: "));
3518 msg.Append(m_FullPath000);
3519 wxLogMessage(msg);
3520
3521 return false; // chart is completely unusable
3522 }
3523 }
3524
3525 // Populate the oSENC clone of the chart's extent structure
3526 m_extent.NLAT = LatMax;
3527 m_extent.SLAT = LatMin;
3528 m_extent.ELON = LonMax;
3529 m_extent.WLON = LonMin;
3530
3531 return true;
3532}
3533
3534OGRFeature *Osenc::GetChartFirstM_COVR(int &catcov, S57Reader *pENCReader,
3535 S57ClassRegistrar *poRegistrar) {
3536 OGRFeature *rv = NULL;
3537
3538 if ((NULL != pENCReader) && (NULL != poRegistrar)) {
3539 // Select the proper class
3540 poRegistrar->SelectClass("M_COVR");
3541
3542 // OGRFeatureDefn *M_COVRdef = S57GenerateObjectClassDefn(
3543 // poRegistrar, 302, 0);
3544
3545 // find this feature
3546 bool bFound = false;
3547 OGRFeature *pobjectDef = pENCReader->ReadNextFeature(/*M_COVRdef*/);
3548 while (!bFound) {
3549 if (pobjectDef) {
3550 OGRFeatureDefn *poDefn = pobjectDef->GetDefnRef();
3551 if (poDefn && (poDefn->GetOBJL() == 302 /*poRegistrar->GetOBJL()*/)) {
3552 // Fetch the CATCOV attribute
3553 catcov = pobjectDef->GetFieldAsInteger("CATCOV");
3554 rv = pobjectDef;
3555 break;
3556 } else
3557 delete pobjectDef;
3558 } else {
3559 break;
3560 }
3561 pobjectDef = pENCReader->ReadNextFeature();
3562 }
3563 }
3564
3565 return rv;
3566}
3567
3568OGRFeature *Osenc::GetChartNextM_COVR(int &catcov, S57Reader *pENCReader) {
3569 catcov = -1;
3570
3571 if (pENCReader) {
3572 bool bFound = false;
3573 OGRFeature *pobjectDef = pENCReader->ReadNextFeature();
3574
3575 while (!bFound) {
3576 if (pobjectDef) {
3577 OGRFeatureDefn *poDefn = pobjectDef->GetDefnRef();
3578 if (poDefn && (poDefn->GetOBJL() == 302)) {
3579 // Fetch the CATCOV attribute
3580 catcov = pobjectDef->GetFieldAsInteger("CATCOV");
3581 return pobjectDef;
3582 } else
3583 delete pobjectDef;
3584
3585 } else
3586 return NULL;
3587
3588 pobjectDef = pENCReader->ReadNextFeature();
3589 }
3590 }
3591
3592 return NULL;
3593}
3594
3595int Osenc::GetBaseFileInfo(const wxString &FullPath000,
3596 const wxString &SENCFileName) {
3597 wxFileName SENCfile = wxFileName(SENCFileName);
3598
3599 // Take a quick scan of the 000 file to get some basic attributes of the
3600 // exchange set.
3601 if (!GetBaseFileAttr(FullPath000)) {
3602 return ERROR_BASEFILE_ATTRIBUTES;
3603 }
3604
3605 OGRS57DataSource oS57DS;
3606 oS57DS.SetS57Registrar(m_poRegistrar);
3607
3608 bool b_current_debug = g_bGDAL_Debug;
3609 g_bGDAL_Debug = false;
3610
3611 // Ingest the .000 cell, with updates applied
3612
3613 if (ingestCell(&oS57DS, FullPath000, SENCfile.GetPath())) {
3614 errorMessage = _T("Error ingesting: ") + FullPath000;
3615 return ERROR_INGESTING000;
3616 }
3617
3618 S57Reader *poReader = oS57DS.GetModule(0);
3619
3620 CalculateExtent(poReader, m_poRegistrar);
3621
3622 g_bGDAL_Debug = b_current_debug;
3623
3624 // delete poReader;
3625
3626 return SENC_NO_ERROR;
3627}
3628
3629bool Osenc::CalculateExtent(S57Reader *poReader,
3630 S57ClassRegistrar *poRegistrar) {
3631 poReader->Rewind();
3632
3633 OGRFeature *pFeat;
3634 int catcov;
3635 float LatMax, LatMin, LonMax, LonMin;
3636 LatMax = -90.;
3637 LatMin = 90.;
3638 LonMax = -179.;
3639 LonMin = 179.;
3640
3641 m_pCOVRTablePoints = NULL;
3642 m_pCOVRTable = NULL;
3643
3644 // // Create arrays to hold geometry objects temporarily
3645 // MyFloatPtrArray *pAuxPtrArray = new MyFloatPtrArray;
3646 // wxArrayInt *pAuxCntArray = new wxArrayInt;
3647 //
3648 // MyFloatPtrArray *pNoCovrPtrArray = new MyFloatPtrArray;
3649 // wxArrayInt *pNoCovrCntArray = new wxArrayInt;
3650
3651 // Get the first M_COVR object
3652 pFeat = GetChartFirstM_COVR(catcov, poReader, poRegistrar);
3653
3654 while (pFeat) {
3655 // Get the next M_COVR feature, and create possible additional entries
3656 // for COVR
3657 OGRPolygon *poly = (OGRPolygon *)(pFeat->GetGeometryRef());
3658 OGRLinearRing *xring = poly->getExteriorRing();
3659
3660 int npt = xring->getNumPoints();
3661
3662 if (npt >= 3) {
3663 for (int i = 0; i < npt; i++) {
3664 OGRPoint p;
3665 xring->getPoint(i, &p);
3666
3667 if (catcov == 1) {
3668 LatMax = fmax(LatMax, p.getY());
3669 LatMin = fmin(LatMin, p.getY());
3670 LonMax = fmax(LonMax, p.getX());
3671 LonMin = fmin(LonMin, p.getX());
3672 }
3673 }
3674 }
3675
3676 delete pFeat;
3677 pFeat = GetChartNextM_COVR(catcov, poReader);
3678 } // while
3679
3680 // Populate the oSENC clone of the chart's extent structure
3681 m_extent.NLAT = LatMax;
3682 m_extent.SLAT = LatMin;
3683 m_extent.ELON = LonMax;
3684 m_extent.WLON = LonMin;
3685
3686 return true;
3687}
3688
3689void Osenc::InitializePersistentBuffer(void) {
3690 pBuffer = (unsigned char *)malloc(1024);
3691 bufferSize = 1024;
3692}
3693
3694unsigned char *Osenc::getBuffer(size_t length) {
3695 if (length > bufferSize) {
3696 pBuffer = (unsigned char *)realloc(pBuffer, length * 2);
3697 bufferSize = length * 2;
3698 }
3699
3700 return pBuffer;
3701}
Definition: s57.h:144
s57RegistrarMgr Definition This is a class holding the ctor and dtor for the global registrar