OpenCPN Partial API docs
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 "model/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 "model/georef.h"
51 #include "gui_lib.h"
52 #include <mutex>
53 
54 extern s57RegistrarMgr *m_pRegistrarMan;
55 extern wxString g_csv_locn;
56 extern bool g_bGDAL_Debug;
57 
58 bool chain_broken_mssage_shown = false;
59 
60 using namespace std;
61 
62 #include <wx/arrimpl.cpp>
63 WX_DEFINE_ARRAY(float *, MyFloatPtrArray);
64 
65 #define MAX_VECTOR_POINTS 1000
66 
67 #ifndef __WXMSW__
68 sigjmp_buf env_osenc_ogrf; // the context saved by sigsetjmp();
69 #endif
70 
71 std::mutex m;
72 
73 /************************************************************************/
74 /* OpenCPN_OGRErrorHandler() */
75 /* Use Global wxLog Class */
76 /************************************************************************/
77 bool g_OsencVerbose;
78 
79 void 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 //--------------------------------------------------------------------------
110 Osenc_instreamFile::Osenc_instreamFile() { Init(); }
111 
112 Osenc_instreamFile::~Osenc_instreamFile() { delete m_instream; }
113 
114 bool Osenc_instreamFile::Open(const wxString &senc_file_name) {
115  m_instream = new wxFFileInputStream(senc_file_name);
116  return m_instream->IsOk();
117 }
118 
119 void Osenc_instreamFile::Close() {}
120 
121 Osenc_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 
127 bool Osenc_instreamFile::IsOk() {
128  if (m_instream) m_ok = m_instream->IsOk();
129 
130  return m_ok;
131 }
132 
133 bool Osenc_instreamFile::isAvailable() { return true; }
134 
135 void Osenc_instreamFile::Shutdown() {}
136 
137 void 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 //--------------------------------------------------------------------------
146 Osenc_outstreamFile::Osenc_outstreamFile() { Init(); }
147 
148 Osenc_outstreamFile::~Osenc_outstreamFile() { delete m_outstream; }
149 
150 bool 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 
158 void Osenc_outstreamFile::Close() {
159  if (m_outstream) m_ok = m_outstream->Close();
160 }
161 
162 Osenc_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 
168 bool Osenc_outstreamFile::IsOk() {
169  if (m_outstream) m_ok = m_outstream->IsOk();
170 
171  return m_ok;
172 }
173 
174 void Osenc_outstreamFile::Init() {
175  m_outstream = NULL;
176  m_ok = false;
177 }
178 
179 //--------------------------------------------------------------------------
180 // Osenc implementation
181 //--------------------------------------------------------------------------
182 
183 Osenc::Osenc() { init(); }
184 
185 Osenc::~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 
219 void 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 
257 void Osenc::setVerbose(bool verbose) {
258  m_bVerbose = verbose;
259  g_OsencVerbose = verbose;
260 }
261 
262 int 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
283  Osenc_instreamFile fpx;
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  }
434  _OSENC_EXTENT_Record_Payload *pPayload =
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 
453  _OSENC_COVR_Record_Payload *pPayload =
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 
475  _OSENC_NOCOVR_Record_Payload *pPayload =
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 
505 std::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 
513 std::string Osenc::GetAttributeAcronymFromTypecode(int typeCode) {
514  if (m_pRegistrarMan)
515  return m_pRegistrarMan->getAttributeAcronym(typeCode);
516  else
517  return "";
518 }
519 
520 int 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
540  Osenc_instreamFile fpx;
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  }
670  _OSENC_EXTENT_Record_Payload *pPayload =
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 
1038 int 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 
1205 int 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 
1382 bool 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 
1474 int 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  delete m_pOutstream;
1529  lockCR.unlock();
1530  return ERROR_CANNOT_CREATE_TEMP_SENC_FILE;
1531  }
1532 
1533  // Take a quick scan of the 000 file to get some basic attributes of the
1534  // exchange set.
1535  if (!GetBaseFileAttr(FullPath000)) {
1536  delete m_pOutstream;
1537  lockCR.unlock();
1538  return ERROR_BASEFILE_ATTRIBUTES;
1539  }
1540 
1541  OGRS57DataSource S57DS;
1542  OGRS57DataSource *poS57DS = &S57DS;
1543  poS57DS->SetS57Registrar(m_poRegistrar);
1544 
1545  // Ingest the .000 cell, with updates applied
1546 
1547  if (ingestCell(poS57DS, FullPath000, SENCfile.GetPath())) {
1548  errorMessage = _T("Error ingesting: ") + FullPath000;
1549  delete m_pOutstream;
1550  lockCR.unlock();
1551  return ERROR_INGESTING000;
1552  }
1553 
1554  S57Reader *poReader = poS57DS->GetModule(0);
1555 
1556  // Create the Coverage table Records, which also calculates the chart extents
1557  if (!CreateCOVRTables(poReader, m_poRegistrar)) {
1558  delete m_pOutstream;
1559  lockCR.unlock();
1560  return ERROR_SENCFILE_ABORT;
1561  }
1562 
1563  // Establish a common reference point for the chart, from the extent
1564  m_ref_lat = (m_extent.NLAT + m_extent.SLAT) / 2.;
1565  m_ref_lon = (m_extent.WLON + m_extent.ELON) / 2.;
1566 
1567  bool bcont = true;
1568 
1569  // Write the Header information
1570 
1571  // char temp[201];
1572 
1573  // fprintf( fps57, "SENC Version= %d\n", 200 );
1574 
1575  // The chart cell "nice name"
1576  wxString nice_name;
1577  s57chart::GetChartNameFromTXT(FullPath000, nice_name);
1578 
1579  string sname = "UTF8Error";
1580  wxCharBuffer buffer = nice_name.ToUTF8();
1581  if (buffer.data()) sname = buffer.data();
1582 
1583  if (!WriteHeaderRecord200(stream, HEADER_SENC_VERSION,
1584  (uint16_t)m_senc_file_create_version)) {
1585  stream->Close();
1586  delete m_pOutstream;
1587  lockCR.unlock();
1588  return ERROR_SENCFILE_ABORT;
1589  }
1590 
1591  if (!WriteHeaderRecord200(stream, HEADER_CELL_NAME, sname)) {
1592  stream->Close();
1593  delete m_pOutstream;
1594  lockCR.unlock();
1595  return ERROR_SENCFILE_ABORT;
1596  }
1597 
1598  wxString date000 = m_date000.Format(_T("%Y%m%d"));
1599  string sdata = date000.ToStdString();
1600  if (!WriteHeaderRecord200(stream, HEADER_CELL_PUBLISHDATE, sdata)) {
1601  stream->Close();
1602  delete m_pOutstream;
1603  lockCR.unlock();
1604  return ERROR_SENCFILE_ABORT;
1605  }
1606 
1607  long n000 = 0;
1608  m_edtn000.ToLong(&n000);
1609  if (!WriteHeaderRecord200(stream, HEADER_CELL_EDITION, (uint16_t)n000)) {
1610  stream->Close();
1611  delete m_pOutstream;
1612  lockCR.unlock();
1613  return ERROR_SENCFILE_ABORT;
1614  }
1615 
1616  sdata = m_LastUpdateDate.ToStdString();
1617  if (!WriteHeaderRecord200(stream, HEADER_CELL_UPDATEDATE, sdata)) {
1618  stream->Close();
1619  delete m_pOutstream;
1620  lockCR.unlock();
1621  return ERROR_SENCFILE_ABORT;
1622  }
1623 
1624  if (!WriteHeaderRecord200(stream, HEADER_CELL_UPDATE,
1625  (uint16_t)m_last_applied_update)) {
1626  stream->Close();
1627  delete m_pOutstream;
1628  lockCR.unlock();
1629  return ERROR_SENCFILE_ABORT;
1630  }
1631 
1632  if (!WriteHeaderRecord200(stream, HEADER_CELL_NATIVESCALE,
1633  (uint32_t)m_native_scale)) {
1634  stream->Close();
1635  delete m_pOutstream;
1636  lockCR.unlock();
1637  return ERROR_SENCFILE_ABORT;
1638  }
1639 
1640  wxDateTime now = wxDateTime::Now();
1641  wxString dateNow = now.Format(_T("%Y%m%d"));
1642  sdata = dateNow.ToStdString();
1643  if (!WriteHeaderRecord200(stream, HEADER_CELL_SENCCREATEDATE, sdata)) {
1644  stream->Close();
1645  delete m_pOutstream;
1646  lockCR.unlock();
1647  return ERROR_SENCFILE_ABORT;
1648  }
1649 
1650  // Write the Coverage table Records
1651  if (!CreateCovrRecords(stream)) {
1652  stream->Close();
1653  delete m_pOutstream;
1654  lockCR.unlock();
1655  return ERROR_SENCFILE_ABORT;
1656  }
1657 
1658  poReader->Rewind();
1659 
1660  // Prepare Vector Edge Helper table
1661  // And fill in the table
1662  int feid = 0;
1663  OGRFeature *pEdgeVectorRecordFeature = poReader->ReadVector(feid, RCNM_VE);
1664  while (NULL != pEdgeVectorRecordFeature) {
1665  int record_id = pEdgeVectorRecordFeature->GetFieldAsInteger("RCID");
1666 
1667  m_vector_helper_hash[record_id] = feid;
1668 
1669  feid++;
1670  delete pEdgeVectorRecordFeature;
1671  pEdgeVectorRecordFeature = poReader->ReadVector(feid, RCNM_VE);
1672  }
1673 
1674  wxString Message = SENCfile.GetFullPath();
1675  Message.Append(_T("...Ingesting"));
1676 
1677  wxString Title(_("OpenCPN S57 SENC File Create..."));
1678  Title.append(SENCfile.GetFullPath());
1679 
1680 #if wxUSE_PROGRESSDLG
1681 
1682  wxStopWatch progsw;
1683  int nProg = poReader->GetFeatureCount();
1684 
1685  if (wxThread::IsMain() && b_showProg) {
1686  m_ProgDialog = new wxGenericProgressDialog();
1687 
1688  wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
1689  m_ProgDialog->SetFont(*qFont);
1690 
1691  m_ProgDialog->Create(Title, Message, nProg, NULL,
1692  wxPD_AUTO_HIDE | wxPD_SMOOTH);
1693  }
1694 #endif
1695 
1696  // Loop in the S57 reader, extracting Features one-by-one
1697  OGRFeature *objectDef;
1698 
1699  int iObj = 0;
1700 
1701  while (bcont) {
1702  objectDef = poReader->ReadNextFeature();
1703 
1704  if (objectDef != NULL) {
1705  iObj++;
1706 
1707 #if wxUSE_PROGRESSDLG
1708 
1709  // Update the progress dialog
1710  // We update only every 200 milliseconds to improve performance as
1711  // updating the dialog is very expensive...
1712  // WXGTK is measurably slower even with 100ms here
1713  if (m_ProgDialog && progsw.Time() > 200) {
1714  progsw.Start();
1715 
1716  wxString sobj =
1717  wxString(objectDef->GetDefnRef()->GetName(), wxConvUTF8);
1718  sobj.Append(wxString::Format(_T(" %d/%d "), iObj, nProg));
1719 
1720  bcont = m_ProgDialog->Update(iObj, sobj);
1721 #if defined(__WXMSW__) || defined(__WXOSX__)
1722  wxSafeYield();
1723 #endif
1724  }
1725 #endif
1726 
1727  OGRwkbGeometryType geoType = wkbUnknown;
1728  // This test should not be necessary for real (i.e not C_AGGR)
1729  // features However... some update files contain errors, and have
1730  // deleted some geometry without deleting the corresponding
1731  // feature(s). So, GeometryType becomes Unknown. e.g. US5MD11M.017 In
1732  // this case, all we can do is skip the feature....sigh.
1733 
1734  if (objectDef->GetGeometryRef() != NULL)
1735  geoType = objectDef->GetGeometryRef()->getGeometryType();
1736 
1737  // n.b This next line causes skip of C_AGGR features w/o geometry
1738  if (geoType != wkbUnknown) { // Write only if has wkbGeometry
1739  CreateSENCRecord200(objectDef, stream, 1, poReader);
1740  }
1741 
1742  delete objectDef;
1743 
1744  } else
1745  break;
1746  }
1747 
1748  if (bcont) {
1749  // Create and write the Vector Edge Table
1750  CreateSENCVectorEdgeTableRecord200(stream, poReader);
1751 
1752  // Create and write the Connected NodeTable
1753  CreateSENCVectorConnectedTableRecord200(stream, poReader);
1754  }
1755 
1756  // All done, so clean up
1757  stream->Close();
1758  delete m_pOutstream;
1759 
1760  // Delete any temporary (working) real and dummy update files,
1761  // as well as .000 file created by ValidateAndCountUpdates()
1762  for (unsigned int iff = 0; iff < m_tmpup_array.GetCount(); iff++)
1763  remove(m_tmpup_array[iff].mb_str());
1764 
1765  int ret_code = 0;
1766 
1767  if (!bcont) // aborted
1768  {
1769  wxRemoveFile(tmp_file); // kill the temp file
1770  ret_code = ERROR_SENCFILE_ABORT;
1771  }
1772 
1773  if (bcont) {
1774  bool cpok = wxRenameFile(tmp_file, SENCfile.GetFullPath());
1775  if (!cpok) {
1776  errorMessage = _T(" Cannot rename temporary SENC file ");
1777  errorMessage.Append(tmp_file);
1778  errorMessage.Append(_T(" to "));
1779  errorMessage.Append(SENCfile.GetFullPath());
1780  ret_code = ERROR_SENCFILE_ABORT;
1781  } else
1782  ret_code = SENC_NO_ERROR;
1783  }
1784 
1785 #if wxUSE_PROGRESSDLG
1786  delete m_ProgDialog;
1787 #endif
1788 
1789  lockCR.unlock();
1790 
1791  return ret_code;
1792 }
1793 
1794 bool Osenc::CreateCovrRecords(Osenc_outstream *stream) {
1795  // First, create the Extent record
1796  _OSENC_EXTENT_Record record;
1797  record.record_type = CELL_EXTENT_RECORD;
1798  record.record_length = sizeof(_OSENC_EXTENT_Record);
1799  record.extent_sw_lat = m_extent.SLAT;
1800  record.extent_sw_lon = m_extent.WLON;
1801  record.extent_nw_lat = m_extent.NLAT;
1802  record.extent_nw_lon = m_extent.WLON;
1803  record.extent_ne_lat = m_extent.NLAT;
1804  record.extent_ne_lon = m_extent.ELON;
1805  record.extent_se_lat = m_extent.SLAT;
1806  record.extent_se_lon = m_extent.ELON;
1807 
1808  size_t targetCount = sizeof(record);
1809  if (!stream->Write(&record, targetCount).IsOk()) return false;
1810 
1811  for (int i = 0; i < m_nCOVREntries; i++) {
1812  int nPoints = m_pCOVRTablePoints[i];
1813 
1814  float *fpbuf = m_pCOVRTable[i];
1815 
1816  // Ready to write the record
1817  _OSENC_COVR_Record_Base record;
1818  record.record_type = CELL_COVR_RECORD;
1819  record.record_length = sizeof(_OSENC_COVR_Record_Base) + sizeof(uint32_t) +
1820  (nPoints * 2 * sizeof(float));
1821 
1822  // Write the base record
1823  size_t targetCount = sizeof(record);
1824  if (!stream->Write(&record, targetCount).IsOk()) return false;
1825 
1826  // Write the point count
1827  targetCount = sizeof(uint32_t);
1828  if (!stream->Write(&nPoints, targetCount).IsOk()) return false;
1829 
1830  // Write the point array
1831  targetCount = nPoints * 2 * sizeof(float);
1832  if (!stream->Write(fpbuf, targetCount).IsOk()) return false;
1833  }
1834 
1835  for (int i = 0; i < m_nNoCOVREntries; i++) {
1836  int nPoints = m_pNoCOVRTablePoints[i];
1837 
1838  float *fpbuf = m_pNoCOVRTable[i];
1839 
1840  // Ready to write the record
1842  record.record_type = CELL_NOCOVR_RECORD;
1843  record.record_length = sizeof(_OSENC_NOCOVR_Record_Base) +
1844  sizeof(uint32_t) + (nPoints * 2 * sizeof(float));
1845 
1846  // Write the base record
1847  size_t targetCount = sizeof(record);
1848  if (!stream->Write(&record, targetCount).IsOk()) return false;
1849 
1850  // Write the point count
1851  targetCount = sizeof(uint32_t);
1852  if (!stream->Write(&nPoints, targetCount).IsOk()) return false;
1853 
1854  // Write the point array
1855  targetCount = nPoints * 2 * sizeof(float);
1856  if (!stream->Write(fpbuf, targetCount).IsOk()) return false;
1857  }
1858 
1859  return true;
1860 }
1861 
1862 bool Osenc::WriteHeaderRecord200(Osenc_outstream *stream, int recordType,
1863  std::string payload) {
1864  int payloadLength = payload.length() + 1;
1865  int recordLength = payloadLength + sizeof(OSENC_Record_Base);
1866 
1867  // Get a reference to the class persistent buffer
1868  unsigned char *pBuffer = getBuffer(recordLength);
1869 
1870  OSENC_Record *pRecord = (OSENC_Record *)pBuffer;
1871  memset(pRecord, 0, recordLength);
1872  pRecord->record_type = recordType;
1873  pRecord->record_length = recordLength;
1874  memcpy(&pRecord->payload, payload.c_str(), payloadLength);
1875 
1876  size_t targetCount = recordLength;
1877  if (!stream->Write(pBuffer, targetCount).IsOk())
1878  return false;
1879  else
1880  return true;
1881 }
1882 
1883 bool Osenc::WriteHeaderRecord200(Osenc_outstream *stream, int recordType,
1884  uint16_t val) {
1885  int payloadLength = sizeof(uint16_t);
1886  int recordLength = payloadLength + sizeof(OSENC_Record_Base);
1887 
1888  // Get a reference to the class persistent buffer
1889  unsigned char *pBuffer = getBuffer(recordLength);
1890 
1891  OSENC_Record *pRecord = (OSENC_Record *)pBuffer;
1892  memset(pRecord, 0, recordLength);
1893  pRecord->record_type = recordType;
1894  pRecord->record_length = recordLength;
1895  memcpy(&pRecord->payload, &val, payloadLength);
1896 
1897  size_t targetCount = recordLength;
1898  if (!stream->Write(pBuffer, targetCount).IsOk())
1899  return false;
1900  else
1901  return true;
1902 }
1903 
1904 bool Osenc::WriteHeaderRecord200(Osenc_outstream *stream, int recordType,
1905  uint32_t val) {
1906  int payloadLength = sizeof(uint32_t);
1907  int recordLength = payloadLength + sizeof(OSENC_Record_Base);
1908 
1909  // Get a reference to the class persistent buffer
1910  unsigned char *pBuffer = getBuffer(recordLength);
1911 
1912  OSENC_Record *pRecord = (OSENC_Record *)pBuffer;
1913  memset(pRecord, 0, recordLength);
1914  pRecord->record_type = recordType;
1915  pRecord->record_length = recordLength;
1916  memcpy(&pRecord->payload, &val, payloadLength);
1917 
1918  size_t targetCount = recordLength;
1919  if (!stream->Write(pBuffer, targetCount).IsOk())
1920  return false;
1921  else
1922  return true;
1923 }
1924 
1925 bool Osenc::WriteFIDRecord200(Osenc_outstream *stream, int nOBJL, int featureID,
1926  int prim) {
1928  memset(&record, 0, sizeof(record));
1929 
1930  record.record_type = FEATURE_ID_RECORD;
1931  record.record_length = sizeof(record);
1932 
1933  record.feature_ID = featureID;
1934  record.feature_type_code = nOBJL;
1935  record.feature_primitive = prim;
1936 
1937  size_t targetCount = sizeof(record);
1938  if (!stream->Write(&record, targetCount).IsOk())
1939  return false;
1940  else
1941  return true;
1942 }
1943 
1944 bool Osenc::CreateMultiPointFeatureGeometryRecord200(OGRFeature *pFeature,
1945  Osenc_outstream *stream) {
1946  OGRGeometry *pGeo = pFeature->GetGeometryRef();
1947 
1948  int wkb_len = pGeo->WkbSize();
1949  unsigned char *pwkb_buffer = (unsigned char *)malloc(wkb_len);
1950 
1951  // Get the GDAL data representation
1952  pGeo->exportToWkb(wkbNDR, pwkb_buffer);
1953 
1954  // Capture a buffer of the raw geometry
1955 
1956  unsigned char *ps = pwkb_buffer;
1957  ps += 5;
1958  int nPoints = *((int *)ps); // point count
1959 
1960  int sb_len = (nPoints * 3 * sizeof(float)); // points as floats
1961 
1962  unsigned char *psb_buffer = (unsigned char *)malloc(sb_len);
1963  unsigned char *pd = psb_buffer;
1964 
1965  ps = pwkb_buffer;
1966  ps += 9; // skip byte order, type, and count
1967 
1968  float *pdf = (float *)pd;
1969 
1970  // Set absurd bbox starting limits
1971  float lonmax = -1000;
1972  float lonmin = 1000;
1973  float latmax = -1000;
1974  float latmin = 1000;
1975 
1976  for (int ip = 0; ip < nPoints; ip++) {
1977  // Workaround a bug?? in OGRGeometryCollection
1978  // While exporting point geometries serially, OGRPoint->exportToWkb assumes
1979  // that if Z is identically 0, then the point must be a 2D point only. So,
1980  // the collection Wkb is corrupted with some 3D, and some 2D points.
1981  // Workaround: Get reference to the points serially, and explicitly read
1982  // X,Y,Z Ignore the previously read Wkb buffer
1983 
1984  OGRGeometryCollection *temp_geometry_collection =
1985  (OGRGeometryCollection *)pGeo;
1986  OGRGeometry *temp_geometry = temp_geometry_collection->getGeometryRef(ip);
1987  OGRPoint *pt_geom = (OGRPoint *)temp_geometry;
1988 
1989  double lon = pt_geom->getX();
1990  double lat = pt_geom->getY();
1991  double depth = pt_geom->getZ();
1992 
1993  // Calculate SM from chart common reference point
1994  double easting, northing;
1995  toSM(lat, lon, m_ref_lat, m_ref_lon, &easting, &northing);
1996 
1997 #ifdef __ARM_ARCH
1998  float __attribute__((aligned(16))) east = easting;
1999  float __attribute__((aligned(16))) north = northing;
2000  float __attribute__((aligned(16))) deep = depth;
2001  unsigned char *puceast = (unsigned char *)&east;
2002  unsigned char *pucnorth = (unsigned char *)&north;
2003  unsigned char *pucdeep = (unsigned char *)&deep;
2004 
2005  memcpy(pdf++, puceast, sizeof(float));
2006  memcpy(pdf++, pucnorth, sizeof(float));
2007  memcpy(pdf++, pucdeep, sizeof(float));
2008 
2009 #else
2010  *pdf++ = easting;
2011  *pdf++ = northing;
2012  *pdf++ = (float)depth;
2013 #endif
2014 
2015  // Keep a running calculation of min/max
2016  lonmax = fmax(lon, lonmax);
2017  lonmin = fmin(lon, lonmin);
2018  latmax = fmax(lat, latmax);
2019  latmin = fmin(lat, latmin);
2020  }
2021 
2022  // Ready to write the record
2024  record.record_type = FEATURE_GEOMETRY_RECORD_MULTIPOINT;
2025  record.record_length = sizeof(OSENC_MultipointGeometry_Record_Base) +
2026  (nPoints * 3 * sizeof(float));
2027  record.extent_e_lon = lonmax;
2028  record.extent_w_lon = lonmin;
2029  record.extent_n_lat = latmax;
2030  record.extent_s_lat = latmin;
2031  record.point_count = nPoints;
2032 
2033  // Write the base record
2034  size_t targetCount = sizeof(record);
2035  if (!stream->Write(&record, targetCount).IsOk()) goto failure;
2036  // Write the 3D point array
2037  targetCount = nPoints * 3 * sizeof(float);
2038  if (!stream->Write(psb_buffer, targetCount).IsOk()) goto failure;
2039 
2040  // Free the buffers
2041  free(psb_buffer);
2042  free(pwkb_buffer);
2043  return true;
2044 failure:
2045  // Free the buffers
2046  free(psb_buffer);
2047  free(pwkb_buffer);
2048  return false;
2049 }
2050 
2051 bool Osenc::CreateLineFeatureGeometryRecord200(S57Reader *poReader,
2052  OGRFeature *pFeature,
2053  Osenc_outstream *stream) {
2054  OGRGeometry *pGeo = pFeature->GetGeometryRef();
2055 
2056  int wkb_len = pGeo->WkbSize();
2057  unsigned char *pwkb_buffer = (unsigned char *)malloc(wkb_len);
2058 
2059  // Get the GDAL data representation
2060  pGeo->exportToWkb(wkbNDR, pwkb_buffer);
2061 
2062  // Capture a buffer of the raw geometry
2063 
2064  int sb_len =
2065  ((wkb_len - 9) / 2) + 9 + 16; // data will be 4 byte float, not double
2066 
2067  unsigned char *psb_buffer = (unsigned char *)malloc(sb_len);
2068  unsigned char *pd = psb_buffer;
2069  unsigned char *ps = pwkb_buffer;
2070 
2071  memcpy(pd, ps, 9); // byte order, type, and count
2072 
2073  int ip = *((int *)(ps + 5)); // point count
2074 
2075  pd += 9;
2076  ps += 9;
2077  double *psd = (double *)ps;
2078  float *pdf = (float *)pd;
2079 
2080  // Set absurd bbox starting limits
2081  float lonmax = -1000;
2082  float lonmin = 1000;
2083  float latmax = -1000;
2084  float latmin = 1000;
2085 
2086  for (int i = 0; i < ip; i++) { // convert doubles to floats
2087  // computing bbox as we go
2088  float lon, lat;
2089  double easting, northing;
2090 #ifdef __ARM_ARCH
2091  double __attribute__((aligned(16))) east_d, north_d;
2092  unsigned char *pucd = (unsigned char *)psd;
2093 
2094  memcpy(&east_d, pucd, sizeof(double));
2095  psd += 1;
2096  pucd += sizeof(double);
2097  memcpy(&north_d, pucd, sizeof(double));
2098  psd += 1;
2099  lon = east_d;
2100  lat = north_d;
2101 
2102  // Calculate SM from chart common reference point
2103  toSM(lat, lon, m_ref_lat, m_ref_lon, &easting, &northing);
2104  unsigned char *puceasting = (unsigned char *)&easting;
2105  unsigned char *pucnorthing = (unsigned char *)&northing;
2106 
2107  memcpy(pdf++, puceasting, sizeof(float));
2108  memcpy(pdf++, pucnorthing, sizeof(float));
2109 
2110 #else
2111  lon = (float)*psd++;
2112  lat = (float)*psd++;
2113 
2114  // Calculate SM from chart common reference point
2115  toSM(lat, lon, m_ref_lat, m_ref_lon, &easting, &northing);
2116 
2117  *pdf++ = easting;
2118  *pdf++ = northing;
2119 #endif
2120 
2121  lonmax = fmax(lon, lonmax);
2122  lonmin = fmin(lon, lonmin);
2123  latmax = fmax(lat, latmax);
2124  latmin = fmin(lat, latmin);
2125  }
2126 
2127  int nEdgeVectorRecords = 0;
2128  unsigned char *pvec_buffer =
2129  getObjectVectorIndexTable(poReader, pFeature, nEdgeVectorRecords);
2130 
2131 #if 0
2132 
2133  // Capture the Vector Table geometry indices into a memory buffer
2134  int *pNAME_RCID;
2135  int *pORNT;
2136  int nEdgeVectorRecords;
2137  OGRFeature *pEdgeVectorRecordFeature;
2138 
2139  pNAME_RCID = (int *) pFeature->GetFieldAsIntegerList( "NAME_RCID", &nEdgeVectorRecords );
2140  pORNT = (int *) pFeature->GetFieldAsIntegerList( "ORNT", NULL );
2141 
2142 //fprintf( fpOut, "LSINDEXLIST %d\n", nEdgeVectorRecords );
2143 // fwrite(pNAME_RCID, 1, nEdgeVectorRecords * sizeof(int), fpOut);
2144 
2145 
2146 
2147  unsigned char *pvec_buffer = (unsigned char *)malloc(nEdgeVectorRecords * 3 * sizeof(int));
2148  unsigned char *pvRun = pvec_buffer;
2149 
2150  // Set up the options, adding RETURN_PRIMITIVES
2151  char ** papszReaderOptions = NULL;
2152  papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
2153  papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES,"ON" );
2154  papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"ON" );
2155  poReader->SetOptions( papszReaderOptions );
2156 
2157 // Capture the beginning and end point connected nodes for each edge vector record
2158  for( int i = 0; i < nEdgeVectorRecords; i++ ) {
2159 
2160  int *pI = (int *)pvRun;
2161 
2162  int edge_rcid = pNAME_RCID[i];
2163 
2164  int start_rcid, end_rcid;
2165  int target_record_feid = m_vector_helper_hash[pNAME_RCID[i]];
2166  pEdgeVectorRecordFeature = poReader->ReadVector( target_record_feid, RCNM_VE );
2167 
2168  if( NULL != pEdgeVectorRecordFeature ) {
2169  start_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_0" );
2170  end_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_1" );
2171 
2172  // Make sure the start and end points exist....
2173  // Note this poReader method was converted to Public access to
2174  // facilitate this test. There might be another clean way....
2175  // Problem first found on Holand ENC 1R5YM009.000
2176  if( !poReader->FetchPoint( RCNM_VC, start_rcid, NULL, NULL, NULL, NULL ) )
2177  start_rcid = -1;
2178  if( !poReader->FetchPoint( RCNM_VC, end_rcid, NULL, NULL, NULL, NULL ) )
2179  end_rcid = -2;
2180 
2181  OGRLineString *poLS = (OGRLineString *)pEdgeVectorRecordFeature->GetGeometryRef();
2182 
2183  int edge_ornt = 1;
2184 
2185  if( edge_ornt == 1 ){ // forward
2186  *pI++ = start_rcid;
2187  *pI++ = edge_rcid;
2188  *pI++ = end_rcid;
2189  } else { // reverse
2190  *pI++ = end_rcid;
2191  *pI++ = edge_rcid;
2192  *pI++ = start_rcid;
2193  }
2194 
2195  delete pEdgeVectorRecordFeature;
2196  } else {
2197  start_rcid = -1; // error indication
2198  end_rcid = -2;
2199 
2200  *pI++ = start_rcid;
2201  *pI++ = edge_rcid;
2202  *pI++ = end_rcid;
2203  }
2204 
2205  pvRun += 3 * sizeof(int);
2206  }
2207 
2208 
2209  // Reset the options
2210  papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"OFF" );
2211  poReader->SetOptions( papszReaderOptions );
2212  CSLDestroy( papszReaderOptions );
2213 #endif
2214 
2215  // Ready to write the record
2217  record.record_type = FEATURE_GEOMETRY_RECORD_LINE;
2218  record.record_length = sizeof(OSENC_LineGeometry_Record_Base) +
2219  (nEdgeVectorRecords * 3 * sizeof(int));
2220  record.extent_e_lon = lonmax;
2221  record.extent_w_lon = lonmin;
2222  record.extent_n_lat = latmax;
2223  record.extent_s_lat = latmin;
2224  record.edgeVector_count = nEdgeVectorRecords;
2225 
2226  // Write the base record
2227  size_t targetCount = sizeof(record);
2228  if (!stream->Write(&record, targetCount).IsOk()) return false;
2229 
2230  // Write the table index array
2231  targetCount = nEdgeVectorRecords * 3 * sizeof(int);
2232  if (!stream->Write(pvec_buffer, targetCount).IsOk()) return false;
2233 
2234  // Free the buffers
2235  free(pvec_buffer);
2236  free(psb_buffer);
2237  free(pwkb_buffer);
2238 
2239  return true;
2240 }
2241 
2242 bool Osenc::CreateAreaFeatureGeometryRecord200(S57Reader *poReader,
2243  OGRFeature *pFeature,
2244  Osenc_outstream *stream) {
2245  int error_code;
2246 
2247  PolyTessGeo *ppg = NULL;
2248 
2249  OGRGeometry *pGeo = pFeature->GetGeometryRef();
2250  OGRPolygon *poly = (OGRPolygon *)(pGeo);
2251 
2252  if (!poly->getExteriorRing()) return false;
2253 
2254  lockCR.unlock();
2255  ppg = new PolyTessGeo(poly, true, m_ref_lat, m_ref_lon, m_LOD_meters);
2256  lockCR.lock();
2257 
2258  error_code = ppg->ErrorCode;
2259 
2260  if (error_code) {
2261  wxLogMessage(
2262  _T(" Warning: S57 SENC Geometry Error %d, Some Features ignored."),
2263  ppg->ErrorCode);
2264  delete ppg;
2265 
2266  return false;
2267  }
2268 
2269  // Ready to create the record
2270 
2271  // The base record, with writing deferred until length is known
2272  OSENC_AreaGeometry_Record_Base baseRecord;
2273  memset(&baseRecord, 0, sizeof(baseRecord));
2274 
2275  baseRecord.record_type = FEATURE_GEOMETRY_RECORD_AREA;
2276 
2277  // Length calculation is deferred...
2278 
2279  baseRecord.extent_s_lat = ppg->Get_ymin();
2280  baseRecord.extent_n_lat = ppg->Get_ymax();
2281  baseRecord.extent_e_lon = ppg->Get_xmax();
2282  baseRecord.extent_w_lon = ppg->Get_xmin();
2283 
2284  baseRecord.contour_count = ppg->GetnContours();
2285 
2286  // Create the array of contour point counts
2287 
2288  int contourPointCountArraySize = ppg->GetnContours() * sizeof(uint32_t);
2289  uint32_t *contourPointCountArray =
2290  (uint32_t *)malloc(contourPointCountArraySize);
2291 
2292  uint32_t *pr = contourPointCountArray;
2293 
2294  for (int i = 0; i < ppg->GetnContours(); i++) {
2295  *pr++ = ppg->Get_PolyTriGroup_head()->pn_vertex[i];
2296  }
2297 
2298  // All that is left is the tesselation result Triangle lists...
2299  // This could be a large array, and we don't want to use a buffer.
2300  // Rather, we want to write directly to the output file.
2301 
2302  // So, we
2303  // a walk the TriPrim chain once to get it's length,
2304  // b update the total record length,
2305  // c write everything before the TriPrim chain,
2306  // d and then directly write the TriPrim chain.
2307 
2308  // Walk the TriPrim chain
2309  int geoLength = 0;
2310 
2311  TriPrim *pTP = ppg->Get_PolyTriGroup_head()
2312  ->tri_prim_head; // head of linked list of TriPrims
2313 
2314  int n_TriPrims = 0;
2315  while (pTP) {
2316  geoLength += sizeof(uint8_t) + sizeof(uint32_t); // type, nvert
2317  geoLength += pTP->nVert * 2 * sizeof(float); // vertices
2318  geoLength += 4 * sizeof(double); // Primitive bounding box
2319  pTP = pTP->p_next;
2320 
2321  n_TriPrims++;
2322  }
2323 
2324  baseRecord.triprim_count = n_TriPrims; // Set the number of TriPrims
2325 
2326  int nEdgeVectorRecords = 0;
2327  unsigned char *pvec_buffer =
2328  getObjectVectorIndexTable(poReader, pFeature, nEdgeVectorRecords);
2329 
2330 #if 0
2331  // Create the Vector Edge Index table into a memory buffer
2332  // This buffer will follow the triangle buffer in the output stream
2333  int *pNAME_RCID;
2334  int *pORNT;
2335  int nEdgeVectorRecords;
2336  OGRFeature *pEdgeVectorRecordFeature;
2337 
2338  pNAME_RCID = (int *) pFeature->GetFieldAsIntegerList( "NAME_RCID", &nEdgeVectorRecords );
2339  pORNT = (int *) pFeature->GetFieldAsIntegerList( "ORNT", NULL );
2340 
2341  baseRecord.edgeVector_count = nEdgeVectorRecords;
2342 
2343  unsigned char *pvec_buffer = (unsigned char *)malloc(nEdgeVectorRecords * 3 * sizeof(int));
2344  unsigned char *pvRun = pvec_buffer;
2345 
2346  // Set up the options, adding RETURN_PRIMITIVES
2347  char ** papszReaderOptions = NULL;
2348  papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_UPDATES, "ON" );
2349  papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES,"ON" );
2350  papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"ON" );
2351  poReader->SetOptions( papszReaderOptions );
2352 
2353  // Capture the beginning and end point connected nodes for each edge vector record
2354  for( int i = 0; i < nEdgeVectorRecords; i++ ) {
2355 
2356  int *pI = (int *)pvRun;
2357 
2358  int start_rcid, end_rcid;
2359  int target_record_feid = m_vector_helper_hash[pNAME_RCID[i]];
2360  pEdgeVectorRecordFeature = poReader->ReadVector( target_record_feid, RCNM_VE );
2361 
2362  int edge_rcid = pNAME_RCID[i];
2363 
2364  if( NULL != pEdgeVectorRecordFeature ) {
2365  start_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_0" );
2366  end_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger( "NAME_RCID_1" );
2367  // Make sure the start and end points exist....
2368  // Note this poReader method was converted to Public access to
2369  // facilitate this test. There might be another clean way....
2370  // Problem first found on Holand ENC 1R5YM009.000
2371  if( !poReader->FetchPoint( RCNM_VC, start_rcid, NULL, NULL, NULL, NULL ) )
2372  start_rcid = -1;
2373  if( !poReader->FetchPoint( RCNM_VC, end_rcid, NULL, NULL, NULL, NULL ) )
2374  end_rcid = -2;
2375 
2376  int edge_ornt = 1;
2377  // Allocate some storage for converted points
2378 
2379  if( edge_ornt == 1 ){ // forward
2380  *pI++ = start_rcid;
2381  *pI++ = edge_rcid;
2382  *pI++ = end_rcid;
2383  } else { // reverse
2384  *pI++ = end_rcid;
2385  *pI++ = edge_rcid;
2386  *pI++ = start_rcid;
2387  }
2388 
2389  delete pEdgeVectorRecordFeature;
2390  } else {
2391  start_rcid = -1; // error indication
2392  end_rcid = -2;
2393 
2394  *pI++ = start_rcid;
2395  *pI++ = edge_rcid;
2396  *pI++ = end_rcid;
2397  }
2398 
2399  pvRun += 3 * sizeof(int);
2400  }
2401 
2402 
2403  // Reset the options
2404  papszReaderOptions = CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,"OFF" );
2405  poReader->SetOptions( papszReaderOptions );
2406  CSLDestroy( papszReaderOptions );
2407 
2408 #endif
2409 
2410  baseRecord.edgeVector_count = nEdgeVectorRecords;
2411 
2412  // Calculate the total record length
2413  int recordLength = sizeof(OSENC_AreaGeometry_Record_Base);
2414  recordLength += contourPointCountArraySize;
2415  recordLength += geoLength;
2416  recordLength += nEdgeVectorRecords * 3 * sizeof(int);
2417  baseRecord.record_length = recordLength;
2418 
2419  // Write the base record
2420  size_t targetCount = sizeof(baseRecord);
2421  if (!stream->Write(&baseRecord, targetCount).IsOk()) return false;
2422 
2423  // Write the contour point count array
2424  targetCount = contourPointCountArraySize;
2425  if (!stream->Write(contourPointCountArray, targetCount).IsOk()) return false;
2426 
2427  // Walk and transcribe the TriPrim chain
2428  pTP = ppg->Get_PolyTriGroup_head()
2429  ->tri_prim_head; // head of linked list of TriPrims
2430  while (pTP) {
2431  if (!stream->Write(&pTP->type, sizeof(uint8_t)).IsOk()) return false;
2432  if (!stream->Write(&pTP->nVert, sizeof(uint32_t)).IsOk()) return false;
2433 
2434  // fwrite (&pTP->minxt , sizeof(double), 1, fpOut);
2435  // fwrite (&pTP->maxxt , sizeof(double), 1, fpOut);
2436  // fwrite (&pTP->minyt , sizeof(double), 1, fpOut);
2437  // fwrite (&pTP->maxyt , sizeof(double), 1, fpOut);
2438 
2439  double minlat, minlon, maxlat, maxlon;
2440  minlat = pTP->tri_box.GetMinLat();
2441  minlon = pTP->tri_box.GetMinLon();
2442  maxlat = pTP->tri_box.GetMaxLat();
2443  maxlon = pTP->tri_box.GetMaxLon();
2444 
2445  if (!stream->Write(&minlon, sizeof(double)).IsOk()) return false;
2446  if (!stream->Write(&maxlon, sizeof(double)).IsOk()) return false;
2447  if (!stream->Write(&minlat, sizeof(double)).IsOk()) return false;
2448  if (!stream->Write(&maxlat, sizeof(double)).IsOk()) return false;
2449 
2450  // Testing TODO
2451  // float *pf = (float *)pTP->p_vertex;
2452  // float a = *pf++;
2453  // float b = *pf;
2454 
2455  if (!stream->Write(pTP->p_vertex, pTP->nVert * 2 * sizeof(float)).IsOk())
2456  return false;
2457 
2458  pTP = pTP->p_next;
2459  }
2460 
2461  // Write the Edge Vector index table
2462  targetCount = nEdgeVectorRecords * 3 * sizeof(int);
2463  if (!stream->Write(pvec_buffer, targetCount).IsOk()) return false;
2464 
2465  delete ppg;
2466  free(contourPointCountArray);
2467  free(pvec_buffer);
2468 
2469  return true;
2470 }
2471 
2472 unsigned char *Osenc::getObjectVectorIndexTable(S57Reader *poReader,
2473  OGRFeature *pFeature,
2474  int &nEntries) {
2475  // Create the Vector Edge Index table into a memory buffer
2476  // This buffer will follow the triangle buffer in the output stream
2477  int *pNAME_RCID;
2478  int *pORNT;
2479  int nEdgeVectorRecords;
2480  OGRFeature *pEdgeVectorRecordFeature;
2481 
2482  pNAME_RCID =
2483  (int *)pFeature->GetFieldAsIntegerList("NAME_RCID", &nEdgeVectorRecords);
2484  pORNT = (int *)pFeature->GetFieldAsIntegerList("ORNT", NULL);
2485 
2486  nEntries = nEdgeVectorRecords;
2487 
2488  unsigned char *pvec_buffer =
2489  (unsigned char *)malloc(nEdgeVectorRecords * 3 * sizeof(int));
2490  unsigned char *pvRun = pvec_buffer;
2491 
2492  // Set up the options, adding RETURN_PRIMITIVES
2493  char **papszReaderOptions = NULL;
2494  papszReaderOptions = CSLSetNameValue(papszReaderOptions, S57O_UPDATES, "ON");
2495  papszReaderOptions =
2496  CSLSetNameValue(papszReaderOptions, S57O_RETURN_LINKAGES, "ON");
2497  papszReaderOptions =
2498  CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON");
2499  poReader->SetOptions(papszReaderOptions);
2500 
2501  // Capture the beginning and end point connected nodes for each edge vector
2502  // record
2503  for (int i = 0; i < nEdgeVectorRecords; i++) {
2504  int *pI = (int *)pvRun;
2505 
2506  int edge_rcid = pNAME_RCID[i];
2507 
2508  int start_rcid, end_rcid;
2509  int target_record_feid = m_vector_helper_hash[pNAME_RCID[i]];
2510  pEdgeVectorRecordFeature =
2511  poReader->ReadVector(target_record_feid, RCNM_VE);
2512 
2513  if (NULL != pEdgeVectorRecordFeature) {
2514  start_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger("NAME_RCID_0");
2515  end_rcid = pEdgeVectorRecordFeature->GetFieldAsInteger("NAME_RCID_1");
2516 
2517  // Make sure the start and end points exist....
2518  // Note this poReader method was converted to Public access to
2519  // facilitate this test. There might be another clean way....
2520  // Problem first found on Holand ENC 1R5YM009.000
2521  if (!poReader->FetchPoint(RCNM_VC, start_rcid, NULL, NULL, NULL, NULL))
2522  start_rcid = -1;
2523  if (!poReader->FetchPoint(RCNM_VC, end_rcid, NULL, NULL, NULL, NULL))
2524  end_rcid = -2;
2525 
2526  OGRLineString *poLS =
2527  (OGRLineString *)pEdgeVectorRecordFeature->GetGeometryRef();
2528  if (!poLS)
2529  edge_rcid = 0;
2530  else if (poLS->getNumPoints() < 1)
2531  edge_rcid = 0;
2532 
2533  int edge_ornt = pORNT[i];
2534 
2535  if (edge_ornt == 1) { // forward
2536  *pI++ = start_rcid;
2537  *pI++ = edge_rcid;
2538  *pI++ = end_rcid;
2539  } else { // reverse
2540  *pI++ = end_rcid;
2541  *pI++ = -edge_rcid;
2542  *pI++ = start_rcid;
2543  }
2544 
2545  delete pEdgeVectorRecordFeature;
2546  } else {
2547  start_rcid = -1; // error indication
2548  end_rcid = -2;
2549 
2550  *pI++ = start_rcid;
2551  *pI++ = edge_rcid;
2552  *pI++ = end_rcid;
2553  }
2554 
2555  pvRun += 3 * sizeof(int);
2556  }
2557 
2558  // Reset the options
2559  papszReaderOptions =
2560  CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF");
2561  poReader->SetOptions(papszReaderOptions);
2562  CSLDestroy(papszReaderOptions);
2563 
2564  return pvec_buffer;
2565 }
2566 
2567 void Osenc::CreateSENCVectorEdgeTableRecord200(Osenc_outstream *stream,
2568  S57Reader *poReader) {
2569  // We create the payload first, so we can calculate the total record length
2570  uint8_t *pPayload = NULL;
2571  int payloadSize = 0;
2572  uint8_t *pRun = pPayload;
2573 
2574  // Set up the S57Reader options, adding RETURN_PRIMITIVES
2575  char **papszReaderOptions = NULL;
2576  papszReaderOptions = CSLSetNameValue(papszReaderOptions, S57O_UPDATES, "ON");
2577  papszReaderOptions =
2578  CSLSetNameValue(papszReaderOptions, S57O_RETURN_LINKAGES, "ON");
2579  papszReaderOptions =
2580  CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON");
2581  poReader->SetOptions(papszReaderOptions);
2582 
2583  int feid = 0;
2584  OGRLineString *pLS = NULL;
2585  OGRGeometry *pGeo;
2586  OGRFeature *pEdgeVectorRecordFeature = poReader->ReadVector(feid, RCNM_VE);
2587 
2588  int nFeatures = 0;
2589 
2590  // Read all the EdgeVector Features
2591  while (NULL != pEdgeVectorRecordFeature) {
2592  // Check for a zero point count. Dunno why this should happen, other than
2593  // bad ENC encoding
2594 
2595  int nPoints = 0;
2596  if (pEdgeVectorRecordFeature->GetGeometryRef() != NULL) {
2597  pGeo = pEdgeVectorRecordFeature->GetGeometryRef();
2598  if (pGeo->getGeometryType() == wkbLineString) {
2599  pLS = (OGRLineString *)pGeo;
2600  nPoints = pLS->getNumPoints();
2601  } else
2602  nPoints = 0;
2603  }
2604 
2605  if (nPoints) {
2606  int new_size = payloadSize + (2 * sizeof(int));
2607  pPayload = (uint8_t *)realloc(pPayload, new_size);
2608  pRun = pPayload + payloadSize; // recalculate the running pointer,
2609  // since realloc may have moved memory
2610  payloadSize = new_size;
2611 
2612  // Fetch and store the Record ID
2613  int record_id = pEdgeVectorRecordFeature->GetFieldAsInteger("RCID");
2614  *(int *)pRun = record_id;
2615  pRun += sizeof(int);
2616 
2617  // Transcribe points to a buffer
2618  // We reduce the maximum number of points in the table to
2619  // MAX_VECTOR_POINTS using a naive algorithm skipping the proportional
2620  // part of them to avoid too deep a recursion and crash in DouglasPeucker
2621  // later
2622  int reduction_ratio = nPoints / MAX_VECTOR_POINTS + 1;
2623  int reduced_points = 0;
2624 
2625  double *ppd = (double *)malloc((nPoints / reduction_ratio + 1) * 2 *
2626  sizeof(double));
2627  double *ppr = ppd;
2628 
2629  for (int i = 0; i < nPoints; i++) {
2630  if (i % reduction_ratio == 0) {
2631  OGRPoint p;
2632  pLS->getPoint(i, &p);
2633 
2634  // Calculate SM from chart common reference point
2635  double easting, northing;
2636  toSM(p.getY(), p.getX(), m_ref_lat, m_ref_lon, &easting, &northing);
2637 
2638  *ppr++ = easting;
2639  *ppr++ = northing;
2640  reduced_points++;
2641  }
2642  }
2643  nPoints = reduced_points;
2644 
2645  // Reduce the LOD of this linestring
2646  std::vector<int> index_keep;
2647  if (nPoints > 5 && (m_LOD_meters > .01)) {
2648  index_keep.push_back(0);
2649  index_keep.push_back(nPoints - 1);
2650 
2651  DouglasPeucker(ppd, 0, nPoints - 1, m_LOD_meters, &index_keep);
2652  // printf("DP Reduction: %d/%d\n", index_keep.GetCount(),
2653  // nPoints);
2654 
2655  } else {
2656  index_keep.resize(nPoints);
2657  for (int i = 0; i < nPoints; i++) index_keep[i] = i;
2658  }
2659 
2660  // Store the point count in the payload
2661  int nPointReduced = index_keep.size();
2662  *(int *)pRun = nPointReduced;
2663  pRun += sizeof(int);
2664 
2665  // transcribe the (possibly) reduced linestring to the payload
2666 
2667  // Grow the payload buffer
2668  int new_size_red = payloadSize + (nPointReduced * 2 * sizeof(float));
2669  pPayload = (uint8_t *)realloc(pPayload, new_size_red);
2670  pRun = pPayload + payloadSize; // recalculate the running pointer,
2671  // since realloc may have moved memory
2672  payloadSize = new_size_red;
2673 
2674  float *npp = (float *)pRun;
2675  float *npp_run = npp;
2676  ppr = ppd;
2677  for (int ip = 0; ip < nPoints; ip++) {
2678  double x = *ppr++;
2679  double y = *ppr++;
2680 
2681  for (unsigned int j = 0; j < index_keep.size(); j++) {
2682  if (index_keep[j] == ip) {
2683  *npp_run++ = x;
2684  *npp_run++ = y;
2685  pRun += 2 * sizeof(float);
2686  break;
2687  }
2688  }
2689  }
2690 
2691  nFeatures++;
2692 
2693  free(ppd);
2694  }
2695 
2696  // Next vector record
2697  feid++;
2698  delete pEdgeVectorRecordFeature;
2699  pEdgeVectorRecordFeature = poReader->ReadVector(feid, RCNM_VE);
2700 
2701  } // while
2702 
2703  // Now we know the payload length and the Feature count
2704  if (nFeatures) {
2705  // Now write the record out
2706  OSENC_VET_Record record;
2707 
2708  record.record_type = VECTOR_EDGE_NODE_TABLE_RECORD;
2709  record.record_length =
2710  sizeof(OSENC_VET_Record_Base) + payloadSize + sizeof(uint32_t);
2711 
2712  // Write out the record
2713  stream->Write(&record, sizeof(OSENC_VET_Record_Base));
2714 
2715  // Write out the Feature(Object) count
2716  stream->Write(&nFeatures, sizeof(uint32_t));
2717 
2718  // Write out the payload
2719  stream->Write(pPayload, payloadSize);
2720  }
2721  // All done with buffer
2722  free(pPayload);
2723 
2724  // Reset the S57Reader options
2725  papszReaderOptions =
2726  CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF");
2727  poReader->SetOptions(papszReaderOptions);
2728  CSLDestroy(papszReaderOptions);
2729 }
2730 
2731 void Osenc::CreateSENCVectorConnectedTableRecord200(Osenc_outstream *stream,
2732  S57Reader *poReader) {
2733  // We create the payload first, so we can calculate the total record length
2734  uint8_t *pPayload = NULL;
2735  int payloadSize = 0;
2736  uint8_t *pRun = pPayload;
2737 
2738  // Set up the S57Reader options, adding RETURN_PRIMITIVES
2739  char **papszReaderOptions = NULL;
2740  papszReaderOptions = CSLSetNameValue(papszReaderOptions, S57O_UPDATES, "ON");
2741  papszReaderOptions =
2742  CSLSetNameValue(papszReaderOptions, S57O_RETURN_LINKAGES, "ON");
2743  papszReaderOptions =
2744  CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "ON");
2745  poReader->SetOptions(papszReaderOptions);
2746 
2747  int feid = 0;
2748  OGRPoint *pP;
2749  OGRGeometry *pGeo;
2750  OGRFeature *pConnNodeRecordFeature = poReader->ReadVector(feid, RCNM_VC);
2751  int featureCount = 0;
2752 
2753  // Read all the ConnectedVector Features
2754  while (NULL != pConnNodeRecordFeature) {
2755  if (pConnNodeRecordFeature->GetGeometryRef() != NULL) {
2756  pGeo = pConnNodeRecordFeature->GetGeometryRef();
2757  if (pGeo->getGeometryType() == wkbPoint) {
2758  int new_size = payloadSize + sizeof(int) + (2 * sizeof(float));
2759  pPayload = (uint8_t *)realloc(pPayload, new_size);
2760  pRun = pPayload + payloadSize; // recalculate the running pointer,
2761  // since realloc may have moved memory
2762  payloadSize = new_size;
2763 
2764  // Fetch and store the Record ID
2765  int record_id = pConnNodeRecordFeature->GetFieldAsInteger("RCID");
2766  *(int *)pRun = record_id;
2767  pRun += sizeof(int);
2768 
2769  pP = (OGRPoint *)pGeo;
2770 
2771  // Calculate SM from chart common reference point
2772  double easting, northing;
2773  toSM(pP->getY(), pP->getX(), m_ref_lat, m_ref_lon, &easting, &northing);
2774 
2775  // MyPoint pd;
2776  // pd.x = easting;
2777  // pd.y = northing;
2778  // memcpy(pRun, &pd, sizeof(MyPoint));
2779  float *ps = (float *)pRun;
2780  *ps++ = easting;
2781  *ps = northing;
2782 
2783  featureCount++;
2784  }
2785  }
2786 
2787  // Next vector record
2788  feid++;
2789  delete pConnNodeRecordFeature;
2790  pConnNodeRecordFeature = poReader->ReadVector(feid, RCNM_VC);
2791  } // while
2792 
2793  // Now we know the payload length and the Feature count
2794 
2795  // Now write the record out
2796  if (featureCount) {
2797  OSENC_VCT_Record record;
2798 
2799  record.record_type = VECTOR_CONNECTED_NODE_TABLE_RECORD;
2800  record.record_length =
2801  sizeof(OSENC_VCT_Record_Base) + payloadSize + sizeof(int);
2802 
2803  // Write out the record
2804  stream->Write(&record, sizeof(OSENC_VCT_Record_Base));
2805 
2806  // Write out the Feature(Object) count
2807  stream->Write(&featureCount, sizeof(uint32_t));
2808 
2809  // Write out the payload
2810  stream->Write(pPayload, payloadSize);
2811  }
2812 
2813  // All done with buffer
2814  free(pPayload);
2815 
2816  // Reset the S57Reader options
2817  papszReaderOptions =
2818  CSLSetNameValue(papszReaderOptions, S57O_RETURN_PRIMITIVES, "OFF");
2819  poReader->SetOptions(papszReaderOptions);
2820  CSLDestroy(papszReaderOptions);
2821 }
2822 
2823 bool Osenc::CreateSENCRecord200(OGRFeature *pFeature, Osenc_outstream *stream,
2824  int mode, S57Reader *poReader) {
2825  // TODO
2826  // if(pFeature->GetFID() == 207)
2827  // int yyp = 4;
2828 
2829  // Create the Feature Identification Record
2830 
2831  // Fetch the S57 Standard Object Class identifier
2832  OGRFeatureDefn *pFD = pFeature->GetDefnRef();
2833  int nOBJL = pFD->GetOBJL();
2834 
2835  OGRGeometry *pGeo = pFeature->GetGeometryRef();
2836  OGRwkbGeometryType gType = pGeo->getGeometryType();
2837 
2838  int primitive = 0;
2839  switch (gType) {
2840  case wkbLineString:
2841  primitive = GEO_LINE;
2842  break;
2843  case wkbPoint:
2844  primitive = GEO_POINT;
2845  break;
2846  case wkbPolygon:
2847  primitive = GEO_AREA;
2848  break;
2849  default:
2850  primitive = 0;
2851  }
2852 
2853  if (!WriteFIDRecord200(stream, nOBJL, pFeature->GetFID(), primitive))
2854  return false;
2855 
2856 #define MAX_HDR_LINE 400
2857 
2858  // char line[MAX_HDR_LINE + 1];
2859  wxString sheader;
2860 
2861  // fprintf( fpOut, "OGRFeature(%s):%ld\n",
2862  // pFeature->GetDefnRef()->GetName(), pFeature->GetFID() );
2863 
2864  // In a loop, fetch attributes, and create OSENC Feature Attribute Records
2865  // In the interests of output file size, DO NOT report fields that are not
2866  // set.
2867 
2868  // TODO Debugging
2869  // if(!strncmp(pFeature->GetDefnRef()->GetName(), "BOYLAT", 6))
2870  // int yyp = 4;
2871 
2872  if (pFeature->GetFID() == 290) int yyp = 4;
2873 
2874  int payloadLength = 0;
2875  void *payloadBuffer = NULL;
2876  unsigned int payloadBufferLength = 0;
2877 
2878  for (int iField = 0; iField < pFeature->GetFieldCount(); iField++) {
2879  if (pFeature->IsFieldSet(iField)) {
2880  if ((iField == 1) || (iField > 7)) {
2881  OGRFieldDefn *poFDefn = pFeature->GetDefnRef()->GetFieldDefn(iField);
2882  // const char *pType = OGRFieldDefn::GetFieldTypeName(
2883  // poFDefn->GetType() );
2884  const char *pAttrName = poFDefn->GetNameRef();
2885  const char *pAttrVal = pFeature->GetFieldAsString(iField);
2886 
2887  // Use the OCPN Registrar Manager to map attribute acronym to an
2888  // identifier. The mapping is defined by the file
2889  // {csv_dir}/s57attributes.csv
2890  int attributeID = m_pRegistrarMan->getAttributeID(pAttrName);
2891 
2892  // Determine the {attribute_value_type} needed in the record
2893  int OGRvalueType = (int)poFDefn->GetType();
2894  int valueType = 0;
2895 
2896  // Check for special cases
2897  if (-1 == attributeID) {
2898  if (!strncmp(pAttrName, "PRIM", 4)) {
2899  attributeID = ATTRIBUTE_ID_PRIM;
2900  }
2901  }
2902 
2903 #if 0 OFTInteger = 0, OFTIntegerList = 1, OFTReal = 2, OFTRealList = 3, OFTString = 4, OFTStringList = 5, OFTWideString = 6, OFTWideStringList = 7, OFTBinary = 8
2913 #endif
2914  switch (OGRvalueType) {
2915  case 0: // Single integer
2916  {
2917  valueType = OGRvalueType;
2918 
2919  if (payloadBufferLength < 4) {
2920  payloadBuffer = realloc(payloadBuffer, 4);
2921  payloadBufferLength = 4;
2922  }
2923 
2924  int aValue = pFeature->GetFieldAsInteger(iField);
2925  memcpy(payloadBuffer, &aValue, sizeof(int));
2926  payloadLength = sizeof(int);
2927 
2928  break;
2929  }
2930  case 1: // Integer list
2931  {
2932  valueType = OGRvalueType;
2933 
2934  int nCount = 0;
2935  const int *aValueList =
2936  pFeature->GetFieldAsIntegerList(iField, &nCount);
2937 
2938  if (payloadBufferLength < nCount * sizeof(int)) {
2939  payloadBuffer = realloc(payloadBuffer, nCount * sizeof(int));
2940  payloadBufferLength = nCount * sizeof(int);
2941  }
2942 
2943  int *pBuffRun = (int *)payloadBuffer;
2944  for (int i = 0; i < nCount; i++) {
2945  *pBuffRun++ = aValueList[i];
2946  }
2947  payloadLength = nCount * sizeof(int);
2948 
2949  break;
2950  }
2951  case 2: // Single double precision real
2952  {
2953  valueType = OGRvalueType;
2954 
2955  if (payloadBufferLength < sizeof(double)) {
2956  payloadBuffer = realloc(payloadBuffer, sizeof(double));
2957  payloadBufferLength = sizeof(double);
2958  }
2959 
2960  double aValue = pFeature->GetFieldAsDouble(iField);
2961  memcpy(payloadBuffer, &aValue, sizeof(double));
2962  payloadLength = sizeof(double);
2963 
2964  break;
2965  }
2966 
2967  case 3: // List of double precision real
2968  {
2969  valueType = OGRvalueType;
2970 
2971  int nCount = 0;
2972  const double *aValueList =
2973  pFeature->GetFieldAsDoubleList(iField, &nCount);
2974 
2975  if (payloadBufferLength < nCount * sizeof(double)) {
2976  payloadBuffer = realloc(payloadBuffer, nCount * sizeof(double));
2977  payloadBufferLength = nCount * sizeof(double);
2978  }
2979 
2980  double *pBuffRun = (double *)payloadBuffer;
2981  for (int i = 0; i < nCount; i++) {
2982  *pBuffRun++ = aValueList[i];
2983  }
2984  payloadLength = nCount * sizeof(double);
2985 
2986  break;
2987  }
2988 
2989  case 4: // Ascii String
2990  {
2991  valueType = OGRvalueType;
2992  const char *pAttrVal = pFeature->GetFieldAsString(iField);
2993 
2994  wxString wxAttrValue;
2995 
2996  if ((0 == strncmp("NOBJNM", pAttrName, 6)) ||
2997  (0 == strncmp("NINFOM", pAttrName, 6)) ||
2998  (0 == strncmp("NPLDST", pAttrName, 6)) ||
2999  (0 == strncmp("NTXTDS", pAttrName, 6))) {
3000  if (poReader->GetNall() ==
3001  2) { // ENC is using UCS-2 / UTF-16 encoding
3002  wxMBConvUTF16 conv;
3003  wxString att_conv(pAttrVal, conv);
3004  att_conv.RemoveLast(); // Remove the \037 that terminates
3005  // UTF-16 strings in S57
3006  att_conv.Replace(_T("\n"),
3007  _T("|")); // Replace <new line> with special
3008  // break character
3009  wxAttrValue = att_conv;
3010  } else if (poReader->GetNall() ==
3011  1) { // ENC is using Lex level 1 (ISO 8859_1) encoding
3012  wxCSConv conv(_T("iso8859-1"));
3013  wxString att_conv(pAttrVal, conv);
3014  wxAttrValue = att_conv;
3015  }
3016  } else {
3017  if (poReader->GetAall() ==
3018  1) { // ENC is using Lex level 1 (ISO 8859_1) encoding for
3019  // "General Text"
3020  wxCSConv conv(_T("iso8859-1"));
3021  wxString att_conv(pAttrVal, conv);
3022  wxAttrValue = att_conv;
3023  } else
3024  wxAttrValue =
3025  wxString(pAttrVal); // ENC must be using Lex level 0
3026  // (ASCII) encoding for "General Text"
3027  }
3028 
3029  unsigned int stringPayloadLength = 0;
3030 
3031  wxCharBuffer buffer;
3032  if (wxAttrValue.Length()) { // need to explicitely encode as UTF8
3033  buffer = wxAttrValue.ToUTF8();
3034  pAttrVal = buffer.data();
3035  stringPayloadLength = strlen(buffer.data());
3036  }
3037 
3038  if (stringPayloadLength) {
3039  if (payloadBufferLength < stringPayloadLength + 1) {
3040  payloadBuffer = realloc(payloadBuffer, stringPayloadLength + 1);
3041  payloadBufferLength = stringPayloadLength + 1;
3042  }
3043 
3044  strcpy((char *)payloadBuffer, pAttrVal);
3045  payloadLength = stringPayloadLength + 1;
3046  } else
3047  attributeID = -1; // cancel this attribute record
3048 
3049  break;
3050  }
3051 
3052  default:
3053  valueType = -1;
3054  break;
3055  }
3056 
3057  if (-1 != attributeID) {
3058  // Build the record
3059  int recordLength =
3060  sizeof(OSENC_Attribute_Record_Base) + payloadLength;
3061 
3062  // Get a reference to the class persistent buffer
3063  unsigned char *pBuffer = getBuffer(recordLength);
3064 
3065  OSENC_Attribute_Record *pRecord = (OSENC_Attribute_Record *)pBuffer;
3066  memset(pRecord, 0, sizeof(OSENC_Attribute_Record));
3067  pRecord->record_type = FEATURE_ATTRIBUTE_RECORD;
3068  pRecord->record_length = recordLength;
3069  pRecord->attribute_type = attributeID;
3070  pRecord->attribute_value_type = valueType;
3071  memcpy(&pRecord->payload, payloadBuffer, payloadLength);
3072 
3073  // Write the record out....
3074  size_t targetCount = recordLength;
3075  if (!stream->Write(pBuffer, targetCount).IsOk()) {
3076  free(payloadBuffer);
3077  return false;
3078  }
3079  }
3080  }
3081  }
3082  }
3083  if (wkbPoint == pGeo->getGeometryType()) {
3084  OGRPoint *pp = (OGRPoint *)pGeo;
3085  int nqual = pp->getnQual();
3086  if (10 != nqual) // only add attribute if nQual is not "precisely known"
3087  {
3088  int attributeID = m_pRegistrarMan->getAttributeID("QUAPOS");
3089  int valueType = 0;
3090  if (-1 != attributeID) {
3091  if (payloadBufferLength < 4) {
3092  payloadBuffer = realloc(payloadBuffer, 4);
3093  payloadBufferLength = 4;
3094  }
3095 
3096  memcpy(payloadBuffer, &nqual, sizeof(int));
3097  payloadLength = sizeof(int);
3098  // Build the record
3099  int recordLength = sizeof(OSENC_Attribute_Record_Base) + payloadLength;
3100 
3101  // Get a reference to the class persistent buffer
3102  unsigned char *pBuffer = getBuffer(recordLength);
3103 
3104  OSENC_Attribute_Record *pRecord = (OSENC_Attribute_Record *)pBuffer;
3105  memset(pRecord, 0, sizeof(OSENC_Attribute_Record));
3106  pRecord->record_type = FEATURE_ATTRIBUTE_RECORD;
3107  pRecord->record_length = recordLength;
3108  pRecord->attribute_type = attributeID;
3109  pRecord->attribute_value_type = valueType;
3110  memcpy(&pRecord->payload, payloadBuffer, payloadLength);
3111 
3112  // Write the record out....
3113  size_t targetCount = recordLength;
3114  if (!stream->Write(pBuffer, targetCount).IsOk()) {
3115  free(payloadBuffer);
3116  return false;
3117  }
3118  }
3119  }
3120  }
3121 
3122  free(payloadBuffer);
3123 
3124 #if 0
3125  // Special geometry cases
3127  if( wkbPoint == pGeo->getGeometryType() ) {
3128  OGRPoint *pp = (OGRPoint *) pGeo;
3129  int nqual = pp->getnQual();
3130  if( 10 != nqual ) // only add attribute if nQual is not "precisely known"
3131  {
3132  snprintf( line, MAX_HDR_LINE - 2, " %s (%c) = %d", "QUALTY", 'I', nqual );
3133  sheader += wxString( line, wxConvUTF8 );
3134  sheader += '\n';
3135  }
3136 
3137  }
3138 
3139  if( mode == 1 ) {
3140  sprintf( line, " %s %f %f\n", pGeo->getGeometryName(), m_ref_lat, m_ref_lon );
3141  sheader += wxString( line, wxConvUTF8 );
3142  }
3143 
3144  wxCharBuffer buffer=sheader.ToUTF8();
3145  fprintf( fpOut, "HDRLEN=%lu\n", (unsigned long) strlen(buffer) );
3146  fwrite( buffer.data(), 1, strlen(buffer), fpOut );
3147 
3148 #endif
3149 
3150  if ((pGeo != NULL)) {
3151  wxString msg;
3152 
3153  OGRwkbGeometryType gType = pGeo->getGeometryType();
3154  switch (gType) {
3155  case wkbLineString: {
3156  if (!CreateLineFeatureGeometryRecord200(poReader, pFeature, stream))
3157  return false;
3158 
3159  break;
3160  }
3161 
3162  case wkbPoint: {
3164  record.record_type = FEATURE_GEOMETRY_RECORD_POINT;
3165  record.record_length = sizeof(record);
3166 
3167  int wkb_len = pGeo->WkbSize();
3168  unsigned char *pwkb_buffer = (unsigned char *)malloc(wkb_len);
3169 
3170  // Get the GDAL data representation
3171  pGeo->exportToWkb(wkbNDR, pwkb_buffer);
3172 
3173  int nq_len = 4; // nQual length
3174  unsigned char *ps = pwkb_buffer;
3175 
3176  ps += 5 + nq_len;
3177  double *psd = (double *)ps;
3178 
3179  double lat, lon;
3180 #ifdef __ARM_ARCH
3181  __attribute__((aligned(16))) double lata, lona;
3182  unsigned char *pucsd = (unsigned char *)psd;
3183 
3184  memcpy(&lona, pucsd, sizeof(double));
3185  pucsd += sizeof(double);
3186  memcpy(&lata, pucsd, sizeof(double));
3187  lon = lona;
3188  lat = lata;
3189 #else
3190  lon = *psd++; // fetch the point
3191  lat = *psd;
3192 #endif
3193 
3194  free(pwkb_buffer);
3195 
3196  record.lat = lat;
3197  record.lon = lon;
3198 
3199  // Write the record out....
3200  size_t targetCount = record.record_length;
3201  if (!stream->Write(&record, targetCount).IsOk()) return false;
3202 
3203  break;
3204  }
3205 
3206  case wkbMultiPoint:
3207  case wkbMultiPoint25D: {
3208  if (!CreateMultiPointFeatureGeometryRecord200(pFeature, stream))
3209  return false;
3210  break;
3211  }
3212 
3213 #if 1
3214  // Special case, polygons are handled separately
3215  case wkbPolygon: {
3216  if (!CreateAreaFeatureGeometryRecord200(poReader, pFeature, stream))
3217  return false;
3218 
3219  break;
3220  }
3221 #endif
3222  // All others
3223  default:
3224  msg = _T(" Warning: Unimplemented ogr geotype record ");
3225  wxLogMessage(msg);
3226 
3227  break;
3228  } // switch
3229  }
3230  return true;
3231 }
3232 
3233 // Build PolyGeo Object from OSENC200 file record
3234 // Return an integer count of bytes consumed from the record in creating
3235 // the PolyTessGeo
3236 PolyTessGeo *Osenc::BuildPolyTessGeo(_OSENC_AreaGeometry_Record_Payload *record,
3237  unsigned char **next_byte) {
3238  PolyTessGeo *pPTG = new PolyTessGeo();
3239 
3240  pPTG->SetExtents(record->extent_w_lon, record->extent_s_lat,
3241  record->extent_e_lon, record->extent_n_lat);
3242 
3243  unsigned int n_TriPrim = record->triprim_count;
3244  int nContours = record->contour_count;
3245 
3246  // Get a pointer to the payload
3247  void *payLoad = &record->payLoad;
3248 
3249  // skip over the contour vertex count array, for now TODO
3250  // uint8_t *pTriPrims = (uint8_t *)payLoad + (nContours * sizeof(uint32_t));
3251 
3252  // Create the head of the linked list of TriPrims
3253  PolyTriGroup *ppg = new PolyTriGroup;
3254  ppg->m_bSMSENC = true;
3255  ppg->data_type = DATA_TYPE_DOUBLE;
3256 
3257  ppg->nContours = nContours;
3258 
3259  ppg->pn_vertex = (int *)malloc(nContours * sizeof(int));
3260  int *pctr = ppg->pn_vertex;
3261 
3262  // The point count array is the first element in the payload, length is known
3263  int *contour_pointcount_array_run = (int *)payLoad;
3264  for (int i = 0; i < nContours; i++) {
3265  *pctr++ = *contour_pointcount_array_run++;
3266  }
3267 
3268  // Read Raw Geometry
3269  ppg->pgroup_geom = NULL;
3270 
3271  // Now the triangle primitives
3272 
3273  TriPrim **p_prev_triprim = &(ppg->tri_prim_head);
3274 
3275  // Read the PTG_Triangle Geometry in a loop
3276  unsigned int tri_type;
3277  int nvert;
3278  int nvert_max = 0;
3279  int total_byte_size = 2 * sizeof(float);
3280 
3281  uint8_t *pPayloadRun =
3282  (uint8_t *)contour_pointcount_array_run; // Points to the start of the
3283  // triangle primitives
3284 
3285  for (unsigned int i = 0; i < n_TriPrim; i++) {
3286  tri_type = *pPayloadRun++;
3287  nvert = *(uint32_t *)pPayloadRun;
3288  pPayloadRun += sizeof(uint32_t);
3289 
3290  TriPrim *tp = new TriPrim;
3291  *p_prev_triprim = tp; // make the link
3292  p_prev_triprim = &(tp->p_next);
3293  tp->p_next = NULL;
3294 
3295  tp->type = tri_type;
3296  tp->nVert = nvert;
3297 
3298  nvert_max =
3299  wxMax(nvert_max, nvert); // Keep a running tab of largest vertex count
3300 
3301  // Read the triangle primitive bounding box as lat/lon
3302  double *pbb = (double *)pPayloadRun;
3303 
3304  double minxt, minyt, maxxt, maxyt;
3305 
3306 #ifdef __ARM_ARCH
3307  double __attribute__((aligned(16))) abox[4];
3308  unsigned char *pucbb = (unsigned char *)pPayloadRun;
3309  memcpy(&abox[0], pucbb, 4 * sizeof(double));
3310 
3311  minxt = abox[0];
3312  maxxt = abox[1];
3313  minyt = abox[2];
3314  maxyt = abox[3];
3315 #else
3316  minxt = *pbb++;
3317  maxxt = *pbb++;
3318  minyt = *pbb++;
3319  maxyt = *pbb;
3320 #endif
3321 
3322  tp->tri_box.Set(minyt, minxt, maxyt, maxxt);
3323 
3324  pPayloadRun += 4 * sizeof(double);
3325 
3326  int byte_size = nvert * 2 * sizeof(float); // the vertices
3327  total_byte_size += byte_size;
3328 
3329  tp->p_vertex = (double *)pPayloadRun;
3330 
3331  pPayloadRun += byte_size;
3332  }
3333 
3334  if (next_byte) *next_byte = pPayloadRun;
3335 
3336  // Convert the vertex arrays into a single float memory allocation to enable
3337  // efficient access later
3338  unsigned char *vbuf = (unsigned char *)malloc(total_byte_size);
3339 
3340  TriPrim *p_tp = ppg->tri_prim_head;
3341  unsigned char *p_run = vbuf;
3342  while (p_tp) {
3343  memcpy(p_run, p_tp->p_vertex, p_tp->nVert * 2 * sizeof(float));
3344  p_tp->p_vertex = (double *)p_run;
3345  p_run += p_tp->nVert * 2 * sizeof(float);
3346  p_tp = p_tp->p_next; // pick up the next in chain
3347  }
3348  ppg->bsingle_alloc = true;
3349  ppg->single_buffer = vbuf;
3350  ppg->single_buffer_size = total_byte_size;
3351  ppg->data_type = DATA_TYPE_FLOAT;
3352 
3353  pPTG->SetPPGHead(ppg);
3354  pPTG->SetnVertexMax(nvert_max);
3355 
3356  pPTG->Set_OK(true);
3357 
3358  return pPTG;
3359 }
3360 
3361 bool Osenc::CreateCOVRTables(S57Reader *poReader,
3362  S57ClassRegistrar *poRegistrar) {
3363  poReader->Rewind();
3364 
3365  OGRFeature *pFeat;
3366  int catcov;
3367  float LatMax, LatMin, LonMax, LonMin;
3368  LatMax = -90.;
3369  LatMin = 90.;
3370  LonMax = -179.;
3371  LonMin = 179.;
3372 
3373  m_pCOVRTablePoints = NULL;
3374  m_pCOVRTable = NULL;
3375 
3376  // Create arrays to hold geometry objects temporarily
3377  MyFloatPtrArray *pAuxPtrArray = new MyFloatPtrArray;
3378  std::vector<int> auxCntArray, noCovrCntArray;
3379 
3380  MyFloatPtrArray *pNoCovrPtrArray = new MyFloatPtrArray;
3381 
3382  // Get the first M_COVR object
3383  pFeat = GetChartFirstM_COVR(catcov, poReader, poRegistrar);
3384 
3385  while (pFeat) {
3386  // Get the next M_COVR feature, and create possible additional entries
3387  // for COVR
3388  OGRPolygon *poly = (OGRPolygon *)(pFeat->GetGeometryRef());
3389  OGRLinearRing *xring = poly->getExteriorRing();
3390 
3391  int npt = xring->getNumPoints();
3392 
3393  float *pf = NULL;
3394 
3395  if (npt >= 3) {
3396  pf = (float *)malloc(2 * npt * sizeof(float));
3397  float *pfr = pf;
3398 
3399  for (int i = 0; i < npt; i++) {
3400  OGRPoint p;
3401  xring->getPoint(i, &p);
3402 
3403  if (catcov == 1) {
3404  LatMax = fmax(LatMax, p.getY());
3405  LatMin = fmin(LatMin, p.getY());
3406  LonMax = fmax(LonMax, p.getX());
3407  LonMin = fmin(LonMin, p.getX());
3408  }
3409 
3410  pfr[0] = p.getY(); // lat
3411  pfr[1] = p.getX(); // lon
3412 
3413  pfr += 2;
3414  }
3415 
3416  if (catcov == 1) {
3417  pAuxPtrArray->Add(pf);
3418  auxCntArray.push_back(npt);
3419  } else if (catcov == 2) {
3420  pNoCovrPtrArray->Add(pf);
3421  noCovrCntArray.push_back(npt);
3422  } else
3423  free(pf);
3424  }
3425 
3426  delete pFeat;
3427  pFeat = GetChartNextM_COVR(catcov, poReader);
3428  } // while
3429 
3430  // Allocate the storage
3431 
3432  m_nCOVREntries = auxCntArray.size();
3433 
3434  // If only one M_COVR,CATCOV=1 object was found,
3435  // assign the geometry to the one and only COVR
3436 
3437  if (m_nCOVREntries == 1) {
3438  m_pCOVRTablePoints = (int *)malloc(sizeof(int));
3439  *m_pCOVRTablePoints = auxCntArray[0];
3440  m_pCOVRTable = (float **)malloc(sizeof(float *));
3441  *m_pCOVRTable = (float *)malloc(auxCntArray[0] * 2 * sizeof(float));
3442  memcpy(*m_pCOVRTable, pAuxPtrArray->Item(0),
3443  auxCntArray[0] * 2 * sizeof(float));
3444  }
3445 
3446  else if (m_nCOVREntries > 1) {
3447  // Create new COVR entries
3448  m_pCOVRTablePoints = (int *)malloc(m_nCOVREntries * sizeof(int));
3449  m_pCOVRTable = (float **)malloc(m_nCOVREntries * sizeof(float *));
3450 
3451  for (unsigned int j = 0; j < (unsigned int)m_nCOVREntries; j++) {
3452  m_pCOVRTablePoints[j] = auxCntArray[j];
3453  m_pCOVRTable[j] = (float *)malloc(auxCntArray[j] * 2 * sizeof(float));
3454  memcpy(m_pCOVRTable[j], pAuxPtrArray->Item(j),
3455  auxCntArray[j] * 2 * sizeof(float));
3456  }
3457  }
3458 
3459  else // strange case, found no CATCOV=1 M_COVR objects
3460  {
3461  wxString msg(_T(" ENC contains no useable M_COVR, CATCOV=1 features: "));
3462  msg.Append(m_FullPath000);
3463  wxLogMessage(msg);
3464  }
3465 
3466  // And for the NoCovr regions
3467  m_nNoCOVREntries = noCovrCntArray.size();
3468 
3469  if (m_nNoCOVREntries) {
3470  // Create new NoCOVR entries
3471  m_pNoCOVRTablePoints = (int *)malloc(m_nNoCOVREntries * sizeof(int));
3472  m_pNoCOVRTable = (float **)malloc(m_nNoCOVREntries * sizeof(float *));
3473 
3474  for (unsigned int j = 0; j < (unsigned int)m_nNoCOVREntries; j++) {
3475  int npoints = noCovrCntArray[j];
3476  m_pNoCOVRTablePoints[j] = npoints;
3477  m_pNoCOVRTable[j] = (float *)malloc(npoints * 2 * sizeof(float));
3478  memcpy(m_pNoCOVRTable[j], pNoCovrPtrArray->Item(j),
3479  npoints * 2 * sizeof(float));
3480  }
3481  } else {
3482  m_pNoCOVRTablePoints = NULL;
3483  m_pNoCOVRTable = NULL;
3484  }
3485 
3486  for (unsigned int j = 0; j < (unsigned int)m_nNoCOVREntries; j++)
3487  free(pNoCovrPtrArray->Item(j));
3488  for (unsigned int j = 0; j < (unsigned int)m_nCOVREntries; j++)
3489  free(pAuxPtrArray->Item(j));
3490 
3491  delete pAuxPtrArray;
3492  delete pNoCovrPtrArray;
3493 
3494  if (0 == m_nCOVREntries) { // fallback
3495  wxString msg(_T(" ENC contains no M_COVR features: "));
3496  msg.Append(m_FullPath000);
3497  wxLogMessage(msg);
3498 
3499  msg = _T(" Calculating Chart Extents as fallback.");
3500  wxLogMessage(msg);
3501 
3502  OGREnvelope Env;
3503 
3504  if (poReader->GetExtent(&Env, true) == OGRERR_NONE) {
3505  LatMax = Env.MaxY;
3506  LonMax = Env.MaxX;
3507  LatMin = Env.MinY;
3508  LonMin = Env.MinX;
3509 
3510  m_nCOVREntries = 1;
3511  m_pCOVRTablePoints = (int *)malloc(sizeof(int));
3512  *m_pCOVRTablePoints = 4;
3513  m_pCOVRTable = (float **)malloc(sizeof(float *));
3514  float *pf = (float *)malloc(2 * 4 * sizeof(float));
3515  *m_pCOVRTable = pf;
3516  float *pfe = pf;
3517 
3518  *pfe++ = LatMax;
3519  *pfe++ = LonMin;
3520 
3521  *pfe++ = LatMax;
3522  *pfe++ = LonMax;
3523 
3524  *pfe++ = LatMin;
3525  *pfe++ = LonMax;
3526 
3527  *pfe++ = LatMin;
3528  *pfe++ = LonMin;
3529 
3530  } else {
3531  wxString msg(_T(" Cannot calculate Extents for ENC: "));
3532  msg.Append(m_FullPath000);
3533  wxLogMessage(msg);
3534 
3535  return false; // chart is completely unusable
3536  }
3537  }
3538 
3539  // Populate the oSENC clone of the chart's extent structure
3540  m_extent.NLAT = LatMax;
3541  m_extent.SLAT = LatMin;
3542  m_extent.ELON = LonMax;
3543  m_extent.WLON = LonMin;
3544 
3545  return true;
3546 }
3547 
3548 OGRFeature *Osenc::GetChartFirstM_COVR(int &catcov, S57Reader *pENCReader,
3549  S57ClassRegistrar *poRegistrar) {
3550  OGRFeature *rv = NULL;
3551 
3552  if ((NULL != pENCReader) && (NULL != poRegistrar)) {
3553  // Select the proper class
3554  poRegistrar->SelectClass("M_COVR");
3555 
3556  // OGRFeatureDefn *M_COVRdef = S57GenerateObjectClassDefn(
3557  // poRegistrar, 302, 0);
3558 
3559  // find this feature
3560  bool bFound = false;
3561  OGRFeature *pobjectDef = pENCReader->ReadNextFeature(/*M_COVRdef*/);
3562  while (!bFound) {
3563  if (pobjectDef) {
3564  OGRFeatureDefn *poDefn = pobjectDef->GetDefnRef();
3565  if (poDefn && (poDefn->GetOBJL() == 302 /*poRegistrar->GetOBJL()*/)) {
3566  // Fetch the CATCOV attribute
3567  catcov = pobjectDef->GetFieldAsInteger("CATCOV");
3568  rv = pobjectDef;
3569  break;
3570  } else
3571  delete pobjectDef;
3572  } else {
3573  break;
3574  }
3575  pobjectDef = pENCReader->ReadNextFeature();
3576  }
3577  }
3578 
3579  return rv;
3580 }
3581 
3582 OGRFeature *Osenc::GetChartNextM_COVR(int &catcov, S57Reader *pENCReader) {
3583  catcov = -1;
3584 
3585  if (pENCReader) {
3586  bool bFound = false;
3587  OGRFeature *pobjectDef = pENCReader->ReadNextFeature();
3588 
3589  while (!bFound) {
3590  if (pobjectDef) {
3591  OGRFeatureDefn *poDefn = pobjectDef->GetDefnRef();
3592  if (poDefn && (poDefn->GetOBJL() == 302)) {
3593  // Fetch the CATCOV attribute
3594  catcov = pobjectDef->GetFieldAsInteger("CATCOV");
3595  return pobjectDef;
3596  } else
3597  delete pobjectDef;
3598 
3599  } else
3600  return NULL;
3601 
3602  pobjectDef = pENCReader->ReadNextFeature();
3603  }
3604  }
3605 
3606  return NULL;
3607 }
3608 
3609 int Osenc::GetBaseFileInfo(const wxString &FullPath000,
3610  const wxString &SENCFileName) {
3611  wxFileName SENCfile = wxFileName(SENCFileName);
3612 
3613  // Take a quick scan of the 000 file to get some basic attributes of the
3614  // exchange set.
3615  if (!GetBaseFileAttr(FullPath000)) {
3616  return ERROR_BASEFILE_ATTRIBUTES;
3617  }
3618 
3619  OGRS57DataSource oS57DS;
3620  oS57DS.SetS57Registrar(m_poRegistrar);
3621 
3622  bool b_current_debug = g_bGDAL_Debug;
3623  g_bGDAL_Debug = false;
3624 
3625  // Ingest the .000 cell, with updates applied
3626 
3627  if (ingestCell(&oS57DS, FullPath000, SENCfile.GetPath())) {
3628  errorMessage = _T("Error ingesting: ") + FullPath000;
3629  return ERROR_INGESTING000;
3630  }
3631 
3632  S57Reader *poReader = oS57DS.GetModule(0);
3633 
3634  CalculateExtent(poReader, m_poRegistrar);
3635 
3636  g_bGDAL_Debug = b_current_debug;
3637 
3638  // delete poReader;
3639 
3640  return SENC_NO_ERROR;
3641 }
3642 
3643 bool Osenc::CalculateExtent(S57Reader *poReader,
3644  S57ClassRegistrar *poRegistrar) {
3645  poReader->Rewind();
3646 
3647  OGRFeature *pFeat;
3648  int catcov;
3649  float LatMax, LatMin, LonMax, LonMin;
3650  LatMax = -90.;
3651  LatMin = 90.;
3652  LonMax = -179.;
3653  LonMin = 179.;
3654 
3655  m_pCOVRTablePoints = NULL;
3656  m_pCOVRTable = NULL;
3657 
3658  // // Create arrays to hold geometry objects temporarily
3659  // MyFloatPtrArray *pAuxPtrArray = new MyFloatPtrArray;
3660  // wxArrayInt *pAuxCntArray = new wxArrayInt;
3661  //
3662  // MyFloatPtrArray *pNoCovrPtrArray = new MyFloatPtrArray;
3663  // wxArrayInt *pNoCovrCntArray = new wxArrayInt;
3664 
3665  // Get the first M_COVR object
3666  pFeat = GetChartFirstM_COVR(catcov, poReader, poRegistrar);
3667 
3668  while (pFeat) {
3669  // Get the next M_COVR feature, and create possible additional entries
3670  // for COVR
3671  OGRPolygon *poly = (OGRPolygon *)(pFeat->GetGeometryRef());
3672  OGRLinearRing *xring = poly->getExteriorRing();
3673 
3674  int npt = xring->getNumPoints();
3675 
3676  if (npt >= 3) {
3677  for (int i = 0; i < npt; i++) {
3678  OGRPoint p;
3679  xring->getPoint(i, &p);
3680 
3681  if (catcov == 1) {
3682  LatMax = fmax(LatMax, p.getY());
3683  LatMin = fmin(LatMin, p.getY());
3684  LonMax = fmax(LonMax, p.getX());
3685  LonMin = fmin(LonMin, p.getX());
3686  }
3687  }
3688  }
3689 
3690  delete pFeat;
3691  pFeat = GetChartNextM_COVR(catcov, poReader);
3692  } // while
3693 
3694  // Populate the oSENC clone of the chart's extent structure
3695  m_extent.NLAT = LatMax;
3696  m_extent.SLAT = LatMin;
3697  m_extent.ELON = LonMax;
3698  m_extent.WLON = LonMin;
3699 
3700  return true;
3701 }
3702 
3703 void Osenc::InitializePersistentBuffer(void) {
3704  pBuffer = (unsigned char *)malloc(1024);
3705  bufferSize = 1024;
3706 }
3707 
3708 unsigned char *Osenc::getBuffer(size_t length) {
3709  if (length > bufferSize) {
3710  pBuffer = (unsigned char *)realloc(pBuffer, length * 2);
3711  bufferSize = length * 2;
3712  }
3713 
3714  return pBuffer;
3715 }
General purpose GUI support.