OpenCPN Partial API docs
cm93.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: cm93 Chart Object
5  * Author: David Register
6  *
7  ***************************************************************************
8  * Copyright (C) 2010 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 <wx/textfile.h>
34 #include <wx/tokenzr.h>
35 #include <wx/arrstr.h>
36 #include <wx/mstream.h>
37 #include <wx/spinctrl.h>
38 #include <wx/listctrl.h>
39 #include <wx/regex.h>
40 
41 #include <algorithm>
42 #include <unordered_map>
43 
44 #include "gdal/ogr_api.h"
45 #include "s52s57.h"
46 #include "s57chart.h"
47 #include "cm93.h"
48 #include "s52plib.h"
49 #include "model/georef.h"
50 #include "mygeom.h"
51 #include "model/cutil.h"
52 #include "navutil.h" // for LogMessageOnce
53 #include "ocpn_pixel.h" // for ocpnUSE_DIBSECTION
54 #include "ocpndc.h"
55 #include "pluginmanager.h" // for PlugInManager
56 #include "OCPNPlatform.h"
57 #include "model/wx28compat.h"
58 #include "model/chartdata_input_stream.h"
59 #include "DetailSlider.h"
60 #include "chcanv.h"
61 #include "gui_lib.h"
62 #include "ocpn_frame.h"
63 #include "line_clip.h"
64 
65 #include <stdio.h>
66 
67 #ifdef ocpnUSE_GL
68 #include "glChartCanvas.h"
69 extern ocpnGLOptions g_GLOptions;
70 #endif
71 
72 #ifdef __MSVC__
73 #define _CRTDBG_MAP_ALLOC
74 #include <stdlib.h>
75 #include <crtdbg.h>
76 #define DEBUG_NEW new (_NORMAL_BLOCK, __FILE__, __LINE__)
77 #define new DEBUG_NEW
78 #endif
79 
80 extern CM93OffsetDialog *g_pCM93OffsetDialog;
81 extern OCPNPlatform *g_Platform;
82 extern s52plib *ps52plib;
83 extern bool g_bDebugCM93;
84 extern int g_cm93_zoom_factor;
85 extern PopUpDSlide *pPopupDetailSlider;
86 extern int g_detailslider_dialog_x, g_detailslider_dialog_y;
87 
88 extern bool g_bopengl;
89 extern PlugInManager *g_pi_manager;
90 
91 extern bool g_b_EnableVBO;
92 
93 // TODO These should be gotten from the ctor
94 extern MyFrame *gFrame;
95 
96 #include <wx/arrimpl.cpp>
97 WX_DEFINE_OBJARRAY(Array_Of_M_COVR_Desc);
98 
99 #include <wx/listimpl.cpp>
100 WX_DEFINE_LIST(List_Of_M_COVR_Desc);
101 
102 void appendOSDirSep(wxString *pString) {
103  wxChar sep = wxFileName::GetPathSeparator();
104  if (pString->Last() != sep) pString->Append(sep);
105 }
106 
107 //----------------------------------------------------------------------------
108 // M_COVR_Desc Implementation
109 //----------------------------------------------------------------------------
110 
111 M_COVR_Desc::M_COVR_Desc() {
112  pvertices = NULL;
113  gl_screen_vertices = NULL;
114  gl_screen_projection_type = PROJECTION_UNKNOWN;
115 
116  user_xoff = 0.;
117  user_yoff = 0.;
118  m_centerlat_cos = 1.0;
119  m_buser_offsets = false;
120 
121  m_ngl_vertices = 0;
122  gl_screen_vertices = NULL;
123 }
124 
125 M_COVR_Desc::~M_COVR_Desc() {
126  delete[] pvertices;
127  delete[] gl_screen_vertices;
128 }
129 
130 int M_COVR_Desc::GetWKBSize() {
131  int size = 0;
132 
133  size = sizeof(int); // size itself
134  size += sizeof(int); // m_cell_index;
135  size += sizeof(int); // m_object_id;
136  size += sizeof(int); // m_subcell
137  size += sizeof(int); // m_nvertices;
138  size += m_nvertices * sizeof(float_2Dpt); // pvertices;
139 
140  size += sizeof(int); // m_npub_year;
141  size += 8 * sizeof(double); // all the rest
142 
143  return size;
144 }
145 
146 bool M_COVR_Desc::WriteWKB(void *p) {
147  if (p) {
148  int *pr = (int *)p;
149  *pr++ = GetWKBSize();
150 
151  *pr++ = m_cell_index;
152  *pr++ = m_object_id;
153  *pr++ = m_subcell;
154 
155  *pr++ = m_nvertices;
156 
157  float_2Dpt *pfo = (float_2Dpt *)pr;
158  float_2Dpt *pfi = pvertices;
159  for (int i = 0; i < m_nvertices; i++) *pfo++ = *pfi++;
160 
161  int *pi = (int *)pfo;
162  *pi++ = m_npub_year;
163 
164  double *pd = (double *)pi;
165  *pd++ = transform_WGS84_offset_x;
166  *pd++ = transform_WGS84_offset_y;
167  *pd++ = m_covr_lat_min;
168  *pd++ = m_covr_lat_max;
169  *pd++ = m_covr_lon_min;
170  *pd++ = m_covr_lon_max;
171 
172  double centerlat_cos =
173  cos(((m_covr_lat_min + m_covr_lat_max) / 2.) * PI / 180.);
174 
175  *pd++ = user_xoff * centerlat_cos;
176  *pd++ = user_yoff * centerlat_cos;
177  }
178 
179  return true;
180 }
181 
182 int M_COVR_Desc::ReadWKB(wxFFileInputStream &ifs) {
183  // Read the length of the WKB
184  int length = 0;
185  if (!ifs.Read(&length, sizeof(int)).Eof()) {
186  ifs.Read(&m_cell_index, sizeof(int));
187  ifs.Read(&m_object_id, sizeof(int));
188  ifs.Read(&m_subcell, sizeof(int));
189 
190  ifs.Read(&m_nvertices, sizeof(int));
191 
192  pvertices = new float_2Dpt[m_nvertices];
193 
194  ifs.Read(pvertices, m_nvertices * sizeof(float_2Dpt));
195 
196  ifs.Read(&m_npub_year, sizeof(int));
197 
198  ifs.Read(&transform_WGS84_offset_x, sizeof(double));
199  ifs.Read(&transform_WGS84_offset_y, sizeof(double));
200  ifs.Read(&m_covr_lat_min, sizeof(double));
201  ifs.Read(&m_covr_lat_max, sizeof(double));
202  ifs.Read(&m_covr_lon_min, sizeof(double));
203  ifs.Read(&m_covr_lon_max, sizeof(double));
204 
205  m_centerlat_cos = cos(((m_covr_lat_min + m_covr_lat_max) / 2.) * PI / 180.);
206 
207  ifs.Read(&user_xoff, sizeof(double));
208  ifs.Read(&user_yoff, sizeof(double));
209 
210  user_xoff /= m_centerlat_cos;
211  user_yoff /= m_centerlat_cos;
212 
213  if ((fabs(user_xoff) > 1.) || (fabs(user_yoff) > 1.))
214  m_buser_offsets = true;
215  else
216  m_buser_offsets = false;
217 
218  m_covr_bbox.Set(m_covr_lat_min, m_covr_lon_min, m_covr_lat_max,
219  m_covr_lon_max);
220  }
221  return length;
222 }
223 
224 OCPNRegion M_COVR_Desc::GetRegion(const ViewPort &vp, wxPoint *pwp) {
225  float_2Dpt *p = pvertices;
226 
227  for (int ip = 0; ip < m_nvertices; ip++) {
228  double plon = p->x;
229  if (fabs(plon - vp.clon) > 180.) {
230  if (plon > vp.clon)
231  plon -= 360.;
232  else
233  plon += 360.;
234  }
235 
236  double easting, northing, epix, npix;
237  toSM(p->y, plon + 360., vp.clat, vp.clon + 360, &easting, &northing);
238 
239  // easting -= transform_WGS84_offset_x;
240  easting -= user_xoff;
241  // northing -= transform_WGS84_offset_y;
242  northing -= user_yoff;
243 
244  epix = easting * vp.view_scale_ppm;
245  npix = northing * vp.view_scale_ppm;
246 
247  pwp[ip].x = (int)round((vp.pix_width / 2) + epix);
248  pwp[ip].y = (int)round((vp.pix_height / 2) - npix);
249 
250  p++;
251  }
252 
253  return OCPNRegion(m_nvertices, pwp);
254 }
255 
256 //----------------------------------------------------------------------------
257 // cm93 covr_set object class
258 // This is a helper class which holds all the known information
259 // relating to cm93 cell MCOVR objects of a particular scale
260 //----------------------------------------------------------------------------
261 
262 char sig_version[] = "COVR1002";
263 
264 class covr_set {
265 public:
266  covr_set(cm93chart *parent);
267  ~covr_set();
268 
269  bool Init(wxChar scale_char, wxString &prefix);
270  unsigned int GetCoverCount() { return m_covr_array_outlines.GetCount(); }
271  M_COVR_Desc *GetCover(unsigned int im) { return &m_covr_array_outlines[im]; }
272  void Add_MCD(M_COVR_Desc *pmcd);
273  bool Add_Update_MCD(M_COVR_Desc *pmcd);
274  bool IsCovrLoaded(int cell_index);
275  int Find_MCD(M_COVR_Desc *pmcd);
276  M_COVR_Desc *Find_MCD(int cell_index, int object_id, int sbcell);
277 
278  cm93chart *m_pParent;
279  wxChar m_scale_char;
280  int m_scale;
281 
282  wxString m_cachefile;
283 
284  // array, for chart outline rendering
285  Array_Of_M_COVR_Desc m_covr_array_outlines;
286 
287  // This is a hash, indexed by cell index, elements
288  // contain the number of M_COVRs found on this particular cell
289  std::unordered_map<int, int> m_cell_hash;
290 };
291 
292 covr_set::covr_set(cm93chart *parent) { m_pParent = parent; }
293 
294 covr_set::~covr_set() {
295  // Create/Update the cache
296  if (m_cachefile.IsEmpty())
297  return; // presumably for Z scale charts
298  // for which we create no cache
299 
300  if (m_covr_array_outlines.GetCount()) {
301  wxFFileOutputStream ofs(m_cachefile);
302  if (ofs.IsOk()) {
303  ofs.Write(sig_version, 8); // write signature
304 
305  for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) {
306  int wkbsize = m_covr_array_outlines[i].GetWKBSize();
307  if (wkbsize) {
308  char *p = (char *)malloc(wkbsize * sizeof(char));
309  m_covr_array_outlines[i].WriteWKB(p);
310  ofs.Write(p, wkbsize);
311  free(p);
312  }
313  }
314  ofs.Close();
315  }
316  }
317 }
318 
319 bool covr_set::Init(wxChar scale_char, wxString &prefix) {
320  m_scale_char = scale_char;
321 
322  switch (m_scale_char) {
323  case 'Z':
324  m_scale = 20000000;
325  break;
326  case 'A':
327  m_scale = 3000000;
328  break;
329  case 'B':
330  m_scale = 1000000;
331  break;
332  case 'C':
333  m_scale = 200000;
334  break;
335  case 'D':
336  m_scale = 100000;
337  break;
338  case 'E':
339  m_scale = 50000;
340  break;
341  case 'F':
342  m_scale = 20000;
343  break;
344  case 'G':
345  m_scale = 7500;
346  break;
347  default:
348  m_scale = 20000000;
349  break;
350  }
351 
352  // Create the cache file name
353  wxString prefix_string = prefix;
354  wxString sep(wxFileName::GetPathSeparator());
355  prefix_string.Replace(sep, _T ( "_" ));
356  prefix_string.Replace(_T ( ":" ), _T ( "_" )); // for Windows
357 
358  m_cachefile = g_Platform->GetPrivateDataDir();
359  appendOSDirSep(&m_cachefile);
360 
361  m_cachefile += _T ( "cm93" );
362  appendOSDirSep(&m_cachefile);
363 
364  m_cachefile +=
365  prefix_string; // include the cm93 prefix string in the cache file name
366  m_cachefile += _T ( "_" ); // to support multiple cm93 data sets
367 
368  wxString cache_old_old_name = m_cachefile;
369  cache_old_old_name += _T ( "coverset." );
370  cache_old_old_name += m_scale_char;
371 
372  wxString cache_old_name = m_cachefile;
373  cache_old_name += _T ( "coverset_sig." );
374  cache_old_name += m_scale_char;
375 
376  m_cachefile += _T ( "coverset_sigp." );
377  m_cachefile += m_scale_char;
378 
379  wxFileName fn(m_cachefile);
380  if (!fn.DirExists()) wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
381 
382  // Preload the cache
383  if (!wxFileName::FileExists(m_cachefile)) {
384  // The signed file does not exist
385  // Check for an old style file, and delete if found.
386  if (wxFileName::FileExists(cache_old_name)) ::wxRemoveFile(cache_old_name);
387  if (wxFileName::FileExists(cache_old_old_name))
388  ::wxRemoveFile(cache_old_old_name);
389  return false;
390  }
391 
392  wxFFileInputStream ifs(m_cachefile);
393  if (ifs.IsOk()) {
394  char sig_bytes[9];
395  // Validate the file signature
396  if (!ifs.Read(&sig_bytes, 8).Eof()) {
397  if (strncmp(sig_bytes, sig_version, 8)) {
398  return false; // bad signature match
399  }
400  } else
401  return false; // short file
402 
403  bool b_cont = true;
404  while (b_cont) {
405  M_COVR_Desc *pmcd = new M_COVR_Desc;
406  int length = pmcd->ReadWKB(ifs);
407 
408  if (length) {
409  m_covr_array_outlines.Add(pmcd);
410 
411  if (m_cell_hash.find(pmcd->m_cell_index) == m_cell_hash.end())
412  m_cell_hash[pmcd->m_cell_index] = 0; // initialize the element
413 
414  m_cell_hash[pmcd->m_cell_index]++; // add this M_COVR to the hash map
415 
416  } else {
417  delete pmcd;
418  b_cont = false;
419  }
420  }
421  }
422 
423  return true;
424 }
425 
426 void covr_set::Add_MCD(M_COVR_Desc *pmcd) {
427  m_covr_array_outlines.Add(pmcd);
428 
429  if (m_cell_hash.find(pmcd->m_cell_index) ==
430  m_cell_hash.end()) // not present yet?
431  m_cell_hash[pmcd->m_cell_index] = 0; // initialize
432 
433  m_cell_hash[pmcd->m_cell_index]++; // add this M_COVR to the hash map
434 }
435 
436 bool covr_set::IsCovrLoaded(int cell_index) {
437  return (m_cell_hash.find(cell_index) != m_cell_hash.end());
438 }
439 
440 bool covr_set::Add_Update_MCD(M_COVR_Desc *pmcd) {
441  if (m_cell_hash.find(pmcd->m_cell_index) ==
442  m_cell_hash.end()) // not present yet?
443  {
444  m_covr_array_outlines.Add(pmcd);
445  m_cell_hash[pmcd->m_cell_index] = 1; // initialize
446  return true;
447  }
448  // There is at least one MCD already in place for this cell index
449  // We need to search the entire table to see if any of those MCD's
450  // correspond to this MCD's object identifier and subcell, as well as cell
451  // index
452  else {
453  bool b_found = false;
454  for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) {
455  M_COVR_Desc *pmcd_candidate = &m_covr_array_outlines[i];
456  if ((pmcd_candidate->m_cell_index == pmcd->m_cell_index) &&
457  (pmcd_candidate->m_object_id == pmcd->m_object_id) &&
458  (pmcd_candidate->m_subcell == pmcd->m_subcell))
459 
460  {
461  b_found = true;
462  break;
463  }
464  }
465 
466  if (!b_found) {
467  m_covr_array_outlines.Add(pmcd);
468  m_cell_hash[pmcd->m_cell_index]++; // add this M_COVR to the hash map
469  return true;
470  } else
471  return false;
472  }
473 }
474 
475 int covr_set::Find_MCD(M_COVR_Desc *pmcd) {
476  if (m_cell_hash.find(pmcd->m_cell_index) ==
477  m_cell_hash.end()) // not present?
478  return -1;
479  else {
480  // There is at least one MCD already in place for this cell index
481  // We need to search the entire table to see if any of those MCD's
482  // correspond to this MCD's object identifier as well as cell index
483 
484  for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) {
485  M_COVR_Desc *pmcd_candidate = &m_covr_array_outlines[i];
486  if ((pmcd_candidate->m_cell_index == pmcd->m_cell_index) &&
487  (pmcd_candidate->m_object_id == pmcd->m_object_id) &&
488  (pmcd_candidate->m_subcell == pmcd->m_subcell)) {
489  return (int)i;
490  }
491  }
492  }
493  return -1;
494 }
495 
496 M_COVR_Desc *covr_set::Find_MCD(int cell_index, int object_id, int subcell) {
497  if (m_cell_hash.find(cell_index) == m_cell_hash.end()) // not present?
498  return NULL;
499 
500  for (unsigned int i = 0; i < m_covr_array_outlines.GetCount(); i++) {
501  M_COVR_Desc *pmcd_candidate = &m_covr_array_outlines[i];
502  if ((pmcd_candidate->m_cell_index == cell_index) &&
503  (pmcd_candidate->m_object_id == object_id) &&
504  (pmcd_candidate->m_subcell == subcell))
505 
506  return pmcd_candidate;
507  }
508 
509  return NULL;
510 }
511 
512 // CM93 Encode/Decode support tables
513 
514 static unsigned char Table_0[] = {
515  0x0CD, 0x0EA, 0x0DC, 0x048, 0x03E, 0x06D, 0x0CA, 0x07B, 0x052, 0x0E1, 0x0A4,
516  0x08E, 0x0AB, 0x005, 0x0A7, 0x097, 0x0B9, 0x060, 0x039, 0x085, 0x07C, 0x056,
517  0x07A, 0x0BA, 0x068, 0x06E, 0x0F5, 0x05D, 0x002, 0x04E, 0x00F, 0x0A1, 0x027,
518  0x024, 0x041, 0x034, 0x000, 0x05A, 0x0FE, 0x0CB, 0x0D0, 0x0FA, 0x0F8, 0x06C,
519  0x074, 0x096, 0x09E, 0x00E, 0x0C2, 0x049, 0x0E3, 0x0E5, 0x0C0, 0x03B, 0x059,
520  0x018, 0x0A9, 0x086, 0x08F, 0x030, 0x0C3, 0x0A8, 0x022, 0x00A, 0x014, 0x01A,
521  0x0B2, 0x0C9, 0x0C7, 0x0ED, 0x0AA, 0x029, 0x094, 0x075, 0x00D, 0x0AC, 0x00C,
522  0x0F4, 0x0BB, 0x0C5, 0x03F, 0x0FD, 0x0D9, 0x09C, 0x04F, 0x0D5, 0x084, 0x01E,
523  0x0B1, 0x081, 0x069, 0x0B4, 0x009, 0x0B8, 0x03C, 0x0AF, 0x0A3, 0x008, 0x0BF,
524  0x0E0, 0x09A, 0x0D7, 0x0F7, 0x08C, 0x067, 0x066, 0x0AE, 0x0D4, 0x04C, 0x0A5,
525  0x0EC, 0x0F9, 0x0B6, 0x064, 0x078, 0x006, 0x05B, 0x09B, 0x0F2, 0x099, 0x0CE,
526  0x0DB, 0x053, 0x055, 0x065, 0x08D, 0x007, 0x033, 0x004, 0x037, 0x092, 0x026,
527  0x023, 0x0B5, 0x058, 0x0DA, 0x02F, 0x0B3, 0x040, 0x05E, 0x07F, 0x04B, 0x062,
528  0x080, 0x0E4, 0x06F, 0x073, 0x01D, 0x0DF, 0x017, 0x0CC, 0x028, 0x025, 0x02D,
529  0x0EE, 0x03A, 0x098, 0x0E2, 0x001, 0x0EB, 0x0DD, 0x0BC, 0x090, 0x0B0, 0x0FC,
530  0x095, 0x076, 0x093, 0x046, 0x057, 0x02C, 0x02B, 0x050, 0x011, 0x00B, 0x0C1,
531  0x0F0, 0x0E7, 0x0D6, 0x021, 0x031, 0x0DE, 0x0FF, 0x0D8, 0x012, 0x0A6, 0x04D,
532  0x08A, 0x013, 0x043, 0x045, 0x038, 0x0D2, 0x087, 0x0A0, 0x0EF, 0x082, 0x0F1,
533  0x047, 0x089, 0x06A, 0x0C8, 0x054, 0x01B, 0x016, 0x07E, 0x079, 0x0BD, 0x06B,
534  0x091, 0x0A2, 0x071, 0x036, 0x0B7, 0x003, 0x03D, 0x072, 0x0C6, 0x044, 0x08B,
535  0x0CF, 0x015, 0x09F, 0x032, 0x0C4, 0x077, 0x083, 0x063, 0x020, 0x088, 0x0F6,
536  0x0AD, 0x0F3, 0x0E8, 0x04A, 0x0E9, 0x035, 0x01C, 0x05F, 0x019, 0x01F, 0x07D,
537  0x070, 0x0FB, 0x0D1, 0x051, 0x010, 0x0D3, 0x02E, 0x061, 0x09D, 0x05C, 0x02A,
538  0x042, 0x0BE, 0x0E6};
539 
540 static unsigned char Encode_table[256];
541 static unsigned char Decode_table[256];
542 
543 static bool cm93_decode_table_created;
544 
545 // Case-insensitive cm93 directory tree depth-first traversal to find the
546 // dictionary... This could be made simpler, but matches the old code better as
547 // is
548 class FindCM93Dictionary : public wxDirTraverser {
549 public:
550  FindCM93Dictionary(wxString &path) : m_path(path) {}
551 
552  virtual wxDirTraverseResult OnFile(const wxString &filename) {
553  wxString name = filename.AfterLast(wxFileName::GetPathSeparator()).Lower();
554  if (name == wxT("cm93obj.dic")) {
555  m_path = filename;
556  return wxDIR_STOP;
557  }
558 
559  return wxDIR_CONTINUE;
560  }
561 
562  virtual wxDirTraverseResult OnDir(const wxString &WXUNUSED(dirname)) {
563  return wxDIR_CONTINUE;
564  }
565 
566 private:
567  wxString &m_path;
568 };
569 
570 cm93_dictionary::cm93_dictionary() {
571  m_S57ClassArray = NULL;
572  m_AttrArray = NULL;
573  m_GeomTypeArray = NULL;
574  ;
575  m_ValTypeArray = NULL;
576  m_max_class = 0;
577  m_ok = false;
578 }
579 
580 bool cm93_dictionary::LoadDictionary(const wxString &dictionary_dir) {
581  int i, nline;
582  wxString line;
583  wxString dir(dictionary_dir); // a copy
584  bool ret_val = false;
585 
586  wxChar sep = wxFileName::GetPathSeparator();
587  if (dir.Last() != sep) dir.Append(sep);
588 
589  m_dict_dir = dir;
590 
591  // Build some array strings for Feature decoding
592 
593  wxString sf(dir);
594  sf.Append(_T ( "CM93OBJ.DIC" ));
595 
596  if (!wxFileName::FileExists(sf)) {
597  sf = dir;
598  sf.Append(_T ( "cm93obj.dic" ));
599  if (!wxFileName::FileExists(sf)) return false;
600  }
601 
602  wxTextFile file;
603  if (!file.Open(sf)) return false;
604 
605  nline = file.GetLineCount();
606 
607  if (!nline) return false;
608 
609  // Read the file once to get the max class number
610  int iclass_max = 0;
611 
612  for (i = 0; i < nline; i++) {
613  line = file.GetLine(i);
614 
615  wxStringTokenizer tkz(line, wxT("|"));
616  // while ( tkz.HasMoreTokens() )
617  {
618  // 6 char class name
619  wxString class_name = tkz.GetNextToken();
620 
621  // class number, ascii
622  wxString token = tkz.GetNextToken();
623  long liclass;
624  token.ToLong(&liclass);
625  int iclass = liclass;
626  if (iclass > iclass_max) iclass_max = iclass;
627 
628  // geom type, ascii
629  wxString geo_type = tkz.GetNextToken();
630  }
631  }
632 
633  m_max_class = iclass_max;
634 
635  // Create the class name array
636  m_S57ClassArray = new wxArrayString;
637  m_S57ClassArray->Add(_T ( "NULLNM" ), iclass_max + 1);
638 
639  // And an array of ints describing the geometry type per class
640  m_GeomTypeArray = (int *)malloc((iclass_max + 1) * sizeof(int));
641 
642  // Iterate over the file, filling in the values
643  for (i = 0; i < nline; i++) {
644  line = file.GetLine(i);
645 
646  wxStringTokenizer tkz(line, wxT("|"));
647  // while ( tkz.HasMoreTokens() )
648  {
649  // 6 char class name
650  wxString class_name = tkz.GetNextToken();
651 
652  // class number, ascii
653  wxString token = tkz.GetNextToken();
654  long liclass;
655  token.ToLong(&liclass);
656  int iclass = liclass;
657 
658  // geom type, ascii
659  wxString geo_type = tkz.GetNextToken();
660 
661  m_S57ClassArray->Insert(class_name, iclass);
662  m_S57ClassArray->RemoveAt(iclass + 1);
663 
664  int igeom_type = -1; // default unknown
665  wxChar geo_type_primary = geo_type[0];
666 
667  if (geo_type_primary == 'A')
668  igeom_type = 3;
669  else if (geo_type_primary == 'L')
670  igeom_type = 2;
671  else if (geo_type_primary == 'P')
672  igeom_type = 1;
673 
674  // Note: there are other types in the file, e.g. 'C'. Dunno what this
675  // is Also, some object classes want multiple geometries, like PA, PLA,
676  // etc. Take only primary, ignore the rest
677 
678  m_GeomTypeArray[iclass] = igeom_type;
679  }
680  }
681  file.Close();
682 
683  // Build some array strings for Attribute decoding
684 
685  wxString sfa(dir);
686  sfa.Append(_T ( "ATTRLUT.DIC" ));
687 
688  if (!wxFileName::FileExists(sfa)) {
689  sfa = dir;
690  sfa.Append(_T ( "attrlut.dic" ));
691  }
692 
693  if (wxFileName::FileExists(sfa)) {
694  wxFFileInputStream filea(sfa);
695 
696  if (filea.IsOk()) {
697  // Read the file once to get the max attr number
698  int iattr_max = 0;
699 
700  while (!filea.Eof()) {
701  // read a line
702  line.Empty();
703  while (1) {
704  char a = filea.GetC();
705  if (filea.Eof()) break;
706  line.Append(a);
707  if (a == 0x0a) break;
708  }
709 
710  if (!line.StartsWith((const wxChar *)wxT(";"))) {
711  wxStringTokenizer tkz(line, wxT("|"));
712  {
713  // 6 attribute label
714  wxString class_name = tkz.GetNextToken();
715 
716  // attribute number, ascii
717  wxString token = tkz.GetNextToken();
718  long liattr;
719  token.ToLong(&liattr);
720  int iattr = liattr;
721  if (iattr > iattr_max) iattr_max = iattr;
722  }
723  }
724  }
725 
726  m_max_attr = iattr_max;
727 
728  filea.SeekI(0);
729 
730  // Create the attribute label array
731 
732  m_AttrArray = new wxArrayString;
733  m_AttrArray->Add(_T ( "NULLNM" ), iattr_max + 1);
734 
735  // And an array of chars describing the attribute value type
736  m_ValTypeArray = (char *)malloc((iattr_max + 1) * sizeof(char));
737 
738  // Iterate over the file, filling in the values
739  while (!filea.Eof()) {
740  // read a line
741  line.Empty();
742  while (1) {
743  char a = filea.GetC();
744  if (filea.Eof()) break;
745  line.Append(a);
746  if (a == 0x0a) break;
747  }
748 
749  if (!line.StartsWith((const wxChar *)wxT(";"))) {
750  wxStringTokenizer tkz(line, wxT("|"));
751  {
752  // 6 char class name
753  wxString attr_name = tkz.GetNextToken();
754 
755  // class number, ascii
756  wxString token = tkz.GetNextToken();
757  long liattr;
758  token.ToLong(&liattr);
759  int iattr = liattr;
760 
761  m_AttrArray->Insert(attr_name, iattr);
762  m_AttrArray->RemoveAt(iattr + 1);
763 
764  // Skip some
765  token = tkz.GetNextToken();
766  token = tkz.GetNextToken();
767  token = tkz.GetNextToken();
768  token = tkz.GetNextToken().Trim();
769 
770  char atype = '?';
771  if (token.IsSameAs(_T ( "aFLOAT" )))
772  atype = 'R';
773  else if (token.IsSameAs(_T ( "aBYTE" )))
774  atype = 'B';
775  else if (token.IsSameAs(_T ( "aSTRING" )))
776  atype = 'S';
777  else if (token.IsSameAs(_T ( "aCMPLX" )))
778  atype = 'C';
779  else if (token.IsSameAs(_T ( "aLIST" )))
780  atype = 'L';
781  else if (token.IsSameAs(_T ( "aWORD10" )))
782  atype = 'W';
783  else if (token.IsSameAs(_T ( "aLONG" )))
784  atype = 'G';
785 
786  m_ValTypeArray[iattr] = atype;
787  }
788  }
789  }
790  ret_val = true;
791  } else // stream IsOK
792  {
793  ret_val = false;
794  }
795  }
796 
797  else // Look for alternate file
798  {
799  sfa = dir;
800  sfa.Append(_T ( "CM93ATTR.DIC" ));
801 
802  if (!wxFileName::FileExists(sfa)) {
803  sfa = dir;
804  sfa.Append(_T ( "cm93attr.dic" ));
805  }
806 
807  if (wxFileName::FileExists(sfa)) {
808  wxFFileInputStream filea(sfa);
809 
810  if (filea.IsOk()) {
811  // Read the file once to get the max attr number
812  int iattr_max = 0;
813 
814  while (!filea.Eof()) {
815  // read a line
816  line.Empty();
817  while (1) {
818  char a = filea.GetC();
819  if (filea.Eof()) break;
820  line.Append(a);
821  if (a == 0x0a) break;
822  }
823 
824  if (!line.StartsWith((const wxChar *)wxT(";"))) {
825  wxStringTokenizer tkz(line, wxT("|"));
826  if (tkz.CountTokens()) {
827  // 6 attribute label
828  wxString class_name = tkz.GetNextToken();
829 
830  // attribute number, ascii
831  wxString token = tkz.GetNextToken();
832  long liattr;
833  token.ToLong(&liattr);
834  int iattr = liattr;
835  if (iattr > iattr_max) iattr_max = iattr;
836  }
837  }
838  }
839 
840  m_max_attr = iattr_max;
841 
842  filea.SeekI(0);
843 
844  // Create the attribute label array
845 
846  m_AttrArray = new wxArrayString;
847  m_AttrArray->Add(_T ( "NULLNM" ), iattr_max + 1);
848 
849  // And an array of chars describing the attribute value type
850  m_ValTypeArray = (char *)malloc((iattr_max + 1) * sizeof(char));
851  for (int iat = 0; iat < iattr_max + 1; iat++) m_ValTypeArray[iat] = '?';
852 
853  // Iterate over the file, filling in the values
854  while (!filea.Eof()) {
855  // read a line
856  line.Empty();
857  while (1) {
858  char a = filea.GetC();
859  if (filea.Eof()) break;
860  line.Append(a);
861  if (a == 0x0a) break;
862  }
863 
864  if (!line.StartsWith((const wxChar *)wxT(";"))) {
865  wxStringTokenizer tkz(line, wxT("|\r\n"));
866  if (tkz.CountTokens() >= 3) {
867  // 6 char class name
868  wxString attr_name = tkz.GetNextToken();
869 
870  // class number, ascii
871  wxString token = tkz.GetNextToken();
872  long liattr;
873  token.ToLong(&liattr);
874  int iattr = liattr;
875 
876  m_AttrArray->Insert(attr_name, iattr);
877  m_AttrArray->RemoveAt(iattr + 1);
878 
879  token = tkz.GetNextToken().Trim();
880 
881  char atype = '?';
882  if (token.IsSameAs(_T ( "aFLOAT" )))
883  atype = 'R';
884  else if (token.IsSameAs(_T ( "aBYTE" )))
885  atype = 'B';
886  else if (token.IsSameAs(_T ( "aSTRING" )))
887  atype = 'S';
888  else if (token.IsSameAs(_T ( "aCMPLX" )))
889  atype = 'C';
890  else if (token.IsSameAs(_T ( "aLIST" )))
891  atype = 'L';
892  else if (token.IsSameAs(_T ( "aWORD10" )))
893  atype = 'W';
894  else if (token.IsSameAs(_T ( "aLONG" )))
895  atype = 'G';
896 
897  m_ValTypeArray[iattr] = atype;
898  }
899  }
900  }
901  ret_val = true;
902  } else // stream IsOK
903  ret_val = false;
904  }
905  }
906 
907  if (ret_val) {
908  m_ok = true;
909 
910  wxString msg(_T ( "Loaded CM93 Dictionary from " ));
911  msg.Append(dir);
912  wxLogMessage(msg);
913  }
914 
915  return ret_val;
916 }
917 
918 wxString cm93_dictionary::GetClassName(int iclass) {
919  if ((iclass > m_max_class) || (iclass < 0))
920  return (_T ( "Unknown" ));
921  else
922  return (m_S57ClassArray->Item(iclass));
923 }
924 
925 wxString cm93_dictionary::GetAttrName(int iattr) {
926  if ((iattr > m_max_attr) || (iattr < 0))
927  return (_T ( "UnknownAttr" ));
928  else
929  return (m_AttrArray->Item(iattr));
930 }
931 
932 // char vtype = m_pDict->m_ValTypeArray[iattr];
933 char cm93_dictionary::GetAttrType(int iattr) {
934  if ((iattr > m_max_attr) || (iattr < 0))
935  return ('?');
936  else
937  return (m_ValTypeArray[iattr]);
938 }
939 
940 cm93_dictionary::~cm93_dictionary() {
941  delete m_S57ClassArray;
942  free(m_GeomTypeArray);
943  delete m_AttrArray;
944  free(m_ValTypeArray);
945 }
946 
947 // CM93 Decode support routines
948 
949 void CreateDecodeTable(void) {
950  int i;
951  for (i = 0; i < 256; i++) {
952  Encode_table[i] = Table_0[i] ^ 8;
953  }
954 
955  for (i = 0; i < 256; i++) {
956  unsigned char a = Encode_table[i];
957  Decode_table[(int)a] = (unsigned char)i;
958  }
959 }
960 
961 static int read_and_decode_bytes(FILE *stream, void *p, int nbytes) {
962  if (0 == nbytes) // declare victory if no bytes requested
963  return 1;
964 
965  // read into callers buffer
966  if (fread(p, nbytes, 1, stream) != 1) return 0;
967 
968  // decode inplace
969  unsigned char *q = (unsigned char *)p;
970 
971  for (int i = 0; i < nbytes; i++) {
972  unsigned char a = *q;
973  int b = a;
974  unsigned char c = Decode_table[b];
975  *q = c;
976 
977  q++;
978  }
979  return 1;
980 }
981 
982 static int read_and_decode_double(FILE *stream, double *p) {
983  double t;
984  // read into temp buffer
985  if (fread(&t, sizeof(double), 1, stream) != 1) return 0;
986 
987  // decode inplace
988  unsigned char *q = (unsigned char *)&t;
989 
990  for (unsigned int i = 0; i < sizeof(double); i++) {
991  unsigned char a = *q;
992  int b = a;
993  unsigned char c = Decode_table[b];
994  *q = c;
995 
996  q++;
997  }
998 
999  // copy to target
1000  *p = t;
1001 
1002  return 1;
1003 }
1004 
1005 static int read_and_decode_int(FILE *stream, int *p) {
1006  int t;
1007  // read into temp buffer
1008  if (fread(&t, sizeof(int), 1, stream) != 1) return 0;
1009 
1010  // decode inplace
1011  unsigned char *q = (unsigned char *)&t;
1012 
1013  for (unsigned int i = 0; i < sizeof(int); i++) {
1014  unsigned char a = *q;
1015  int b = a;
1016  unsigned char c = Decode_table[b];
1017  *q = c;
1018 
1019  q++;
1020  }
1021 
1022  // copy to target
1023  *p = t;
1024 
1025  return 1;
1026 }
1027 
1028 static int read_and_decode_ushort(FILE *stream, unsigned short *p) {
1029  unsigned short t;
1030  // read into temp buffer
1031  if (fread(&t, sizeof(unsigned short), 1, stream) != 1) return 0;
1032 
1033  // decode inplace
1034  unsigned char *q = (unsigned char *)&t;
1035 
1036  for (unsigned int i = 0; i < sizeof(unsigned short); i++) {
1037  unsigned char a = *q;
1038  int b = a;
1039  unsigned char c = Decode_table[b];
1040  *q = c;
1041 
1042  q++;
1043  }
1044 
1045  // copy to target
1046  *p = t;
1047 
1048  return 1;
1049 }
1050 
1051 // Calculate the CM93 CellIndex integer for a given Lat/Lon, at a given scale
1052 
1053 int Get_CM93_CellIndex(double lat, double lon, int scale) {
1054  int retval = 0;
1055 
1056  int dval;
1057  switch (scale) {
1058  case 20000000:
1059  dval = 120;
1060  break; // Z
1061  case 3000000:
1062  dval = 60;
1063  break; // A
1064  case 1000000:
1065  dval = 30;
1066  break; // B
1067  case 200000:
1068  dval = 12;
1069  break; // C
1070  case 100000:
1071  dval = 3;
1072  break; // D
1073  case 50000:
1074  dval = 1;
1075  break; // E
1076  case 20000:
1077  dval = 1;
1078  break; // F
1079  case 7500:
1080  dval = 1;
1081  break; // G
1082  default:
1083  dval = 1;
1084  break;
1085  }
1086 
1087  // Longitude
1088  double lon1 = (lon + 360.) * 3.; // basic cell size is 20 minutes
1089  while (lon1 >= 1080.0) lon1 -= 1080.0;
1090  unsigned short lon2 = (unsigned short)floor(lon1 / dval); // normalize
1091  unsigned short lon3 = lon2 * dval;
1092 
1093  retval = lon3;
1094 
1095  // Latitude
1096  double lat1 = (lat * 3.) + 270. - 30;
1097  unsigned short lat2 = (unsigned short)floor(lat1 / dval); // normalize
1098  unsigned short lat3 = lat2 * dval;
1099 
1100  retval += (lat3 + 30) * 10000;
1101 
1102  return retval;
1103 }
1104 
1105 // Calculate the Lat/Lon of the lower left corner of a CM93 cell,
1106 // given a CM93 CellIndex and scale
1107 // Returned longitude value is always > 0
1108 void Get_CM93_Cell_Origin(int cellindex, int scale, double *lat, double *lon) {
1109  // Longitude
1110  double idx1 = cellindex % 10000;
1111  double lont = (idx1 / 3.);
1112 
1113  *lon = lont;
1114 
1115  // Latitude
1116  int idx2 = cellindex / 10000;
1117  double lat1 = idx2 - 270.;
1118  *lat = lat1 / 3.;
1119 }
1120 
1121 // Answer the query: "Is there a cm93 cell at the specified scale which
1122 // contains a given lat/lon?"
1123 bool Is_CM93Cell_Present(wxString &fileprefix, double lat, double lon,
1124  int scale_index) {
1125  int scale;
1126  int dval;
1127  wxChar scale_char;
1128 
1129  switch (scale_index) {
1130  case 0:
1131  scale = 20000000;
1132  dval = 120;
1133  scale_char = 'Z';
1134  break; // Z
1135  case 1:
1136  scale = 3000000;
1137  dval = 60;
1138  scale_char = 'A';
1139  break; // A
1140  case 2:
1141  scale = 1000000;
1142  dval = 30;
1143  scale_char = 'B';
1144  break; // B
1145  case 3:
1146  scale = 200000;
1147  dval = 12;
1148  scale_char = 'C';
1149  break; // C
1150  case 4:
1151  scale = 100000;
1152  dval = 3;
1153  scale_char = 'D';
1154  break; // D
1155  case 5:
1156  scale = 50000;
1157  dval = 1;
1158  scale_char = 'E';
1159  break; // E
1160  case 6:
1161  scale = 20000;
1162  dval = 1;
1163  scale_char = 'F';
1164  break; // F
1165  case 7:
1166  scale = 7500;
1167  dval = 1;
1168  scale_char = 'G';
1169  break; // G
1170  default:
1171  scale = 20000000;
1172  dval = 120;
1173  scale_char = ' ';
1174  break;
1175  }
1176 
1177  int cellindex = Get_CM93_CellIndex(lat, lon, scale);
1178 
1179  // Create the file name
1180  wxString file;
1181 
1182  int ilat = cellindex / 10000;
1183  int ilon = cellindex % 10000;
1184 
1185  int jlat = (((ilat - 30) / dval) * dval) + 30; // normalize
1186  int jlon = (ilon / dval) * dval;
1187 
1188  int ilatroot = (((ilat - 30) / 60) * 60) + 30;
1189  int ilonroot = (ilon / 60) * 60;
1190 
1191  wxString fileroot;
1192  fileroot.Printf(_T ( "%04d%04d" ), ilatroot, ilonroot);
1193  appendOSDirSep(&fileroot);
1194 
1195  wxString sdir(fileprefix);
1196  sdir += fileroot;
1197  sdir += scale_char;
1198 
1199  wxString tfile;
1200  tfile.Printf(_T ( "?%03d%04d." ), jlat, jlon);
1201  tfile += scale_char;
1202 
1203  // Validate that the directory exists, adjusting case if necessary
1204  if (!::wxDirExists(sdir)) {
1205  wxString old_scalechar(scale_char);
1206  wxString new_scalechar = old_scalechar.Lower();
1207 
1208  sdir = fileprefix;
1209  sdir += fileroot;
1210  sdir += new_scalechar;
1211  }
1212 
1213  if (::wxDirExists(sdir)) {
1214  wxDir dir(sdir);
1215 
1216  wxArrayString file_array;
1217  int n_files = dir.GetAllFiles(sdir, &file_array, tfile, wxDIR_FILES);
1218 
1219  if (n_files) return true;
1220 
1221  // Try with alternate case of m_scalechar
1222  wxString old_scalechar(scale_char);
1223  wxString new_scalechar = old_scalechar.Lower();
1224 
1225  wxString tfile1;
1226  tfile1.Printf(_T ( "?%03d%04d." ), jlat, jlon);
1227  tfile1 += new_scalechar;
1228 
1229  int n_files1 = dir.GetAllFiles(sdir, &file_array, tfile1, wxDIR_FILES);
1230 
1231  if (n_files1) return true;
1232 
1233  // try compressed
1234  n_files =
1235  dir.GetAllFiles(sdir, &file_array, tfile + _T(".xz"), wxDIR_FILES);
1236 
1237  if (n_files) return true;
1238  }
1239 
1240  return false;
1241 }
1242 
1243 static bool read_header_and_populate_cib(FILE *stream, Cell_Info_Block *pCIB) {
1244  // Read header, populate Cell_Info_Block
1245 
1246  // This 128 byte block is read element-by-element, to allow for
1247  // endian-ness correction by element.
1248  // Unused elements are read and, well, unused.
1249 
1250  header_struct header;
1251 
1252  memset((void *)&header, 0, sizeof(header));
1253 
1254  read_and_decode_double(stream, &header.lon_min);
1255  read_and_decode_double(stream, &header.lat_min);
1256  read_and_decode_double(stream, &header.lon_max);
1257  read_and_decode_double(stream, &header.lat_max);
1258 
1259  read_and_decode_double(stream, &header.easting_min);
1260  read_and_decode_double(stream, &header.northing_min);
1261  read_and_decode_double(stream, &header.easting_max);
1262  read_and_decode_double(stream, &header.northing_max);
1263 
1264  read_and_decode_ushort(stream, &header.usn_vector_records);
1265  read_and_decode_int(stream, &header.n_vector_record_points);
1266  read_and_decode_int(stream, &header.m_46);
1267  read_and_decode_int(stream, &header.m_4a);
1268  read_and_decode_ushort(stream, &header.usn_point3d_records);
1269  read_and_decode_int(stream, &header.m_50);
1270  read_and_decode_int(stream, &header.m_54);
1271  read_and_decode_ushort(stream, &header.usn_point2d_records);
1272  read_and_decode_ushort(stream, &header.m_5a);
1273  read_and_decode_ushort(stream, &header.m_5c);
1274  read_and_decode_ushort(stream, &header.usn_feature_records);
1275 
1276  read_and_decode_int(stream, &header.m_60);
1277  read_and_decode_int(stream, &header.m_64);
1278  read_and_decode_ushort(stream, &header.m_68);
1279  read_and_decode_ushort(stream, &header.m_6a);
1280  read_and_decode_ushort(stream, &header.m_6c);
1281  read_and_decode_int(stream, &header.m_nrelated_object_pointers);
1282 
1283  read_and_decode_int(stream, &header.m_72);
1284  read_and_decode_ushort(stream, &header.m_76);
1285 
1286  read_and_decode_int(stream, &header.m_78);
1287  read_and_decode_int(stream, &header.m_7c);
1288 
1289  // Calculate and record the cell coordinate transform coefficients
1290 
1291  double delta_x = header.easting_max - header.easting_min;
1292  if (delta_x < 0)
1293  delta_x += CM93_semimajor_axis_meters * 2.0 * PI; // add one trip around
1294 
1295  pCIB->transform_x_rate = delta_x / 65535;
1296  pCIB->transform_y_rate = (header.northing_max - header.northing_min) / 65535;
1297 
1298  pCIB->transform_x_origin = header.easting_min;
1299  pCIB->transform_y_origin = header.northing_min;
1300 
1301  pCIB->min_lat = header.lat_min;
1302  pCIB->min_lon = header.lon_min;
1303 
1304  // pCIB->m_cell_mcovr_array.Empty();
1305 
1306  // Extract some table sizes from the header, and pre-allocate the tables
1307  // We do it this way to avoid incremental realloc() calls, which are
1308  // expensive
1309 
1310  pCIB->m_nfeature_records = header.usn_feature_records;
1311  pCIB->pobject_block =
1312  (Object *)calloc(pCIB->m_nfeature_records * sizeof(Object), 1);
1313 
1314  pCIB->m_n_point2d_records = header.usn_point2d_records;
1315  pCIB->p2dpoint_array =
1316  (cm93_point *)malloc(pCIB->m_n_point2d_records * sizeof(cm93_point));
1317 
1318  pCIB->pprelated_object_block =
1319  (Object **)malloc(header.m_nrelated_object_pointers * sizeof(Object *));
1320 
1321  pCIB->object_vector_record_descriptor_block =
1322  (vector_record_descriptor *)malloc((header.m_4a + header.m_46) *
1323  sizeof(vector_record_descriptor));
1324 
1325  pCIB->attribute_block_top = (unsigned char *)calloc(header.m_78, 1);
1326 
1327  pCIB->m_nvector_records = header.usn_vector_records;
1328  pCIB->edge_vector_descriptor_block = (geometry_descriptor *)malloc(
1329  header.usn_vector_records * sizeof(geometry_descriptor));
1330 
1331  pCIB->pvector_record_block_top =
1332  (cm93_point *)malloc(header.n_vector_record_points * sizeof(cm93_point));
1333 
1334  pCIB->m_n_point3d_records = header.usn_point3d_records;
1335  pCIB->point3d_descriptor_block = (geometry_descriptor *)malloc(
1336  pCIB->m_n_point3d_records * sizeof(geometry_descriptor));
1337 
1338  pCIB->p3dpoint_array =
1339  (cm93_point_3d *)malloc(header.m_50 * sizeof(cm93_point_3d));
1340 
1341  return true;
1342 }
1343 
1344 static bool read_vector_record_table(FILE *stream, int count,
1345  Cell_Info_Block *pCIB) {
1346  bool brv;
1347 
1348  geometry_descriptor *p = pCIB->edge_vector_descriptor_block;
1349  cm93_point *q = pCIB->pvector_record_block_top;
1350 
1351  for (int iedge = 0; iedge < count; iedge++) {
1352  p->index = iedge;
1353 
1354  unsigned short npoints;
1355  brv = !(read_and_decode_ushort(stream, &npoints) == 0);
1356  if (!brv) return false;
1357 
1358  p->n_points = npoints;
1359  p->p_points = q;
1360 
1361  // brv = read_and_decode_bytes(stream, q, p->n_points *
1362  // sizeof(cm93_point));
1363  // if(!brv)
1364  // return false;
1365 
1366  unsigned short x, y;
1367  for (int index = 0; index < p->n_points; index++) {
1368  if (!read_and_decode_ushort(stream, &x)) return false;
1369  if (!read_and_decode_ushort(stream, &y)) return false;
1370 
1371  q[index].x = x;
1372  q[index].y = y;
1373  }
1374 
1375  // Compute and store the min/max of this block of n_points
1376  cm93_point *t = p->p_points;
1377 
1378  p->x_max = t->x;
1379  p->x_min = t->x;
1380  p->y_max = t->y;
1381  p->y_min = t->y;
1382 
1383  t++;
1384 
1385  for (int j = 0; j < p->n_points - 1; j++) {
1386  if (t->x >= p->x_max) p->x_max = t->x;
1387 
1388  if (t->x <= p->x_min) p->x_min = t->x;
1389 
1390  if (t->y >= p->y_max) p->y_max = t->y;
1391 
1392  if (t->y <= p->y_max) p->y_min = t->y;
1393 
1394  t++;
1395  }
1396 
1397  // Advance the block pointer
1398  q += p->n_points;
1399 
1400  // Advance the geometry descriptor pointer
1401  p++;
1402  }
1403 
1404  return true;
1405 }
1406 
1407 static bool read_3dpoint_table(FILE *stream, int count, Cell_Info_Block *pCIB) {
1408  geometry_descriptor *p = pCIB->point3d_descriptor_block;
1409  cm93_point_3d *q = pCIB->p3dpoint_array;
1410 
1411  for (int i = 0; i < count; i++) {
1412  unsigned short npoints;
1413  if (!read_and_decode_ushort(stream, &npoints)) return false;
1414 
1415  p->n_points = npoints;
1416  p->p_points = (cm93_point *)q; // might not be the right cast
1417 
1418  // unsigned short t = p->n_points;
1419 
1420  // if(!read_and_decode_bytes(stream, q, t*6))
1421  // return false;
1422 
1423  unsigned short x, y, z;
1424  for (int index = 0; index < p->n_points; index++) {
1425  if (!read_and_decode_ushort(stream, &x)) return false;
1426  if (!read_and_decode_ushort(stream, &y)) return false;
1427  if (!read_and_decode_ushort(stream, &z)) return false;
1428 
1429  q[index].x = x;
1430  q[index].y = y;
1431  q[index].z = z;
1432  }
1433 
1434  p++;
1435  q++;
1436  }
1437 
1438  return true;
1439 }
1440 
1441 static bool read_2dpoint_table(FILE *stream, int count, Cell_Info_Block *pCIB) {
1442  // int rv = read_and_decode_bytes(stream, pCIB->p2dpoint_array, count *
1443  // 4);
1444 
1445  unsigned short x, y;
1446  for (int index = 0; index < count; index++) {
1447  if (!read_and_decode_ushort(stream, &x)) return false;
1448  if (!read_and_decode_ushort(stream, &y)) return false;
1449 
1450  pCIB->p2dpoint_array[index].x = x;
1451  pCIB->p2dpoint_array[index].y = y;
1452  }
1453 
1454  return true;
1455 }
1456 
1457 static bool read_feature_record_table(FILE *stream, int n_features,
1458  Cell_Info_Block *pCIB) {
1459  try {
1460  Object *pobj = pCIB->pobject_block; // head of object array
1461 
1462  vector_record_descriptor *pobject_vector_collection =
1463  pCIB->object_vector_record_descriptor_block;
1464 
1465  Object **p_relob =
1466  pCIB->pprelated_object_block; // head of previously allocated related
1467  // object pointer block
1468 
1469  unsigned char *puc_var10 = pCIB->attribute_block_top; // m_3a;
1470  int puc10count = 0; // should be same as header.m_78
1471 
1472  unsigned char object_type;
1473  unsigned char geom_prim;
1474  unsigned short obj_desc_bytes = 0;
1475 
1476  unsigned int t;
1477  unsigned short index;
1478  unsigned short n_elements;
1479 
1480  for (int iobject = 0; iobject < n_features; iobject++) {
1481  // read the object definition
1482  read_and_decode_bytes(stream, &object_type, 1); // read the object type
1483  read_and_decode_bytes(stream, &geom_prim,
1484  1); // read the object geometry primitive type
1485  read_and_decode_ushort(stream,
1486  &obj_desc_bytes); // read the object byte count
1487 
1488  pobj->otype = object_type;
1489  pobj->geotype = geom_prim;
1490 
1491  switch (pobj->geotype & 0x0f) {
1492  case 4: // AREA
1493  {
1494  if (!read_and_decode_ushort(stream, &n_elements)) return false;
1495 
1496  pobj->n_geom_elements = n_elements;
1497  t = (pobj->n_geom_elements * 2) + 2;
1498  obj_desc_bytes -= t;
1499 
1500  pobj->pGeometry =
1501  pobject_vector_collection; // save pointer to created
1502  // vector_record_descriptor in the
1503  // object
1504 
1505  for (unsigned short i = 0; i < pobj->n_geom_elements; i++) {
1506  if (!read_and_decode_ushort(stream, &index)) return false;
1507 
1508  if ((index & 0x1fff) > pCIB->m_nvector_records)
1509  return false; // error in this cell, ignore all of it
1510 
1511  geometry_descriptor *u = &pCIB->edge_vector_descriptor_block[(
1512  index & 0x1fff)]; // point to the vector descriptor
1513 
1514  pobject_vector_collection->pGeom_Description = u;
1515  pobject_vector_collection->segment_usage =
1516  (unsigned char)(index >> 13);
1517 
1518  pobject_vector_collection++;
1519  }
1520 
1521  break;
1522  } // AREA geom
1523 
1524  case 2: // LINE geometry
1525  {
1526  if (!read_and_decode_ushort(
1527  stream, &n_elements)) // read geometry element count
1528  return false;
1529 
1530  pobj->n_geom_elements = n_elements;
1531  t = (pobj->n_geom_elements * 2) + 2;
1532  obj_desc_bytes -= t;
1533 
1534  pobj->pGeometry =
1535  pobject_vector_collection; // save pointer to created
1536  // vector_record_descriptor in the
1537  // object
1538 
1539  for (unsigned short i = 0; i < pobj->n_geom_elements; i++) {
1540  unsigned short geometry_index;
1541 
1542  if (!read_and_decode_ushort(stream, &geometry_index)) return false;
1543 
1544  if ((geometry_index & 0x1fff) > pCIB->m_nvector_records)
1545  // *(int *)(0) = 0; // error
1546  return 0; // error, bad pointer
1547 
1548  geometry_descriptor *u = &pCIB->edge_vector_descriptor_block[(
1549  geometry_index & 0x1fff)]; // point to the vector descriptor
1550 
1551  pobject_vector_collection->pGeom_Description = u;
1552  pobject_vector_collection->segment_usage =
1553  (unsigned char)(geometry_index >> 13);
1554 
1555  pobject_vector_collection++;
1556  }
1557 
1558  break;
1559  }
1560 
1561  case 1: {
1562  if (!read_and_decode_ushort(stream, &index)) return false;
1563 
1564  obj_desc_bytes -= 2;
1565 
1566  pobj->n_geom_elements = 1; // one point
1567 
1568  pobj->pGeometry = &pCIB->p2dpoint_array[index]; // cm93_point *
1569 
1570  break;
1571  }
1572 
1573  case 8: {
1574  if (!read_and_decode_ushort(stream, &index)) return false;
1575  obj_desc_bytes -= 2;
1576 
1577  pobj->n_geom_elements = 1; // one point
1578 
1579  pobj->pGeometry =
1580  &pCIB->point3d_descriptor_block[index]; // geometry_descriptor *
1581 
1582  break;
1583  }
1584 
1585  } // switch
1586 
1587  if ((pobj->geotype & 0x10) == 0x10) // children/related
1588  {
1589  unsigned char nrelated;
1590  if (!read_and_decode_bytes(stream, &nrelated, 1)) return false;
1591 
1592  pobj->n_related_objects = nrelated;
1593  t = (pobj->n_related_objects * 2) + 1;
1594  obj_desc_bytes -= t;
1595 
1596  pobj->p_related_object_pointer_array = p_relob;
1597  p_relob += pobj->n_related_objects;
1598 
1599  Object **w = (Object **)pobj->p_related_object_pointer_array;
1600  for (unsigned char j = 0; j < pobj->n_related_objects; j++) {
1601  if (!read_and_decode_ushort(stream, &index)) return false;
1602 
1603  if (index > pCIB->m_nfeature_records)
1604  // *(int *)(0) = 0; // error
1605  return false;
1606 
1607  Object *prelated_object = &pCIB->pobject_block[index];
1608  *w = prelated_object; // fwd link
1609 
1610  prelated_object->p_related_object_pointer_array =
1611  pobj; // back link, array of 1 element
1612  w++;
1613  }
1614  }
1615 
1616  if ((pobj->geotype & 0x20) == 0x20) {
1617  unsigned short nrelated;
1618  if (!read_and_decode_ushort(stream, &nrelated)) return false;
1619 
1620  pobj->n_related_objects = (unsigned char)(nrelated & 0xFF);
1621  obj_desc_bytes -= 2;
1622  }
1623 
1624  if ((pobj->geotype & 0x40) == 0x40) {
1625  }
1626 
1627  if ((pobj->geotype & 0x80) == 0x80) // attributes
1628  {
1629  unsigned char nattr;
1630  if (!read_and_decode_bytes(stream, &nattr, 1)) return false; // m_od
1631 
1632  pobj->n_attributes = nattr;
1633  obj_desc_bytes -= 5;
1634 
1635  pobj->attributes_block = puc_var10;
1636  puc_var10 += obj_desc_bytes;
1637 
1638  puc10count += obj_desc_bytes;
1639 
1640  if (!read_and_decode_bytes(stream, pobj->attributes_block,
1641  obj_desc_bytes))
1642  return false; // the attributes....
1643 
1644  if ((pobj->geotype & 0x0f) == 1) {
1645  }
1646  }
1647 
1648  pobj++; // next object
1649  }
1650 
1651  // wxASSERT(puc10count == pCIB->m_22->m_78);
1652  }
1653 
1654  catch (...) {
1655  printf("catch on read_feature_record_table\n");
1656  }
1657 
1658  return true;
1659 }
1660 
1661 bool Ingest_CM93_Cell(const char *cell_file_name, Cell_Info_Block *pCIB) {
1662  try {
1663  int file_length;
1664 
1665  // Get the file length
1666  FILE *flstream = fopen(cell_file_name, "rb");
1667  if (!flstream) return false;
1668 
1669  fseek(flstream, 0, SEEK_END);
1670  file_length = ftell(flstream);
1671  fclose(flstream);
1672 
1673  // Open the file
1674  FILE *stream = fopen(cell_file_name, "rb");
1675  if (!stream) return false;
1676 
1677  // Validate the integrity of the cell file
1678 
1679  unsigned short word0 = 0;
1680  ;
1681  int int0 = 0;
1682  int int1 = 0;
1683  ;
1684 
1685  read_and_decode_ushort(stream,
1686  &word0); // length of prolog + header (10 + 128)
1687  read_and_decode_int(stream, &int0); // length of table 1
1688  read_and_decode_int(stream, &int1); // length of table 2
1689 
1690  int test = word0 + int0 + int1;
1691  if (test != file_length) {
1692  fclose(stream);
1693  return false; // file is corrupt
1694  }
1695 
1696  // Cell is OK, proceed to ingest
1697 
1698  if (!read_header_and_populate_cib(stream, pCIB)) {
1699  fclose(stream);
1700  return false;
1701  }
1702 
1703  if (!read_vector_record_table(stream, pCIB->m_nvector_records, pCIB)) {
1704  fclose(stream);
1705  return false;
1706  }
1707 
1708  if (!read_3dpoint_table(stream, pCIB->m_n_point3d_records, pCIB)) {
1709  fclose(stream);
1710  return false;
1711  }
1712 
1713  if (!read_2dpoint_table(stream, pCIB->m_n_point2d_records, pCIB)) {
1714  fclose(stream);
1715  return false;
1716  }
1717 
1718  if (!read_feature_record_table(stream, pCIB->m_nfeature_records, pCIB)) {
1719  fclose(stream);
1720  return false;
1721  }
1722 
1723  // int file_end = ftell(stream);
1724 
1725  // wxASSERT(file_end == file_length);
1726 
1727  fclose(stream);
1728 
1729  return true;
1730  }
1731 
1732  catch (...) {
1733  return false;
1734  }
1735 }
1736 
1737 //----------------------------------------------------------------------------------
1738 // cm93chart Implementation
1739 //----------------------------------------------------------------------------------
1740 
1741 cm93chart::cm93chart() {
1742  m_ChartType = CHART_TYPE_CM93;
1743 
1744  // Create the decode table once, if needed
1745  if (!cm93_decode_table_created) {
1746  CreateDecodeTable();
1747  cm93_decode_table_created = true;
1748  }
1749 
1750  m_pDict = NULL;
1751  m_pManager = NULL;
1752 
1753  m_current_cell_vearray_offset = 0;
1754 
1755  m_ncontour_alloc = 100; // allocate inital vertex count container array
1756  m_pcontour_array = (int *)malloc(m_ncontour_alloc * sizeof(int));
1757 
1758  // Establish a common reference point for the cell
1759  ref_lat = 0.;
1760  ref_lon = 0.;
1761 
1762  // Need a covr_set
1763  m_pcovr_set = new covr_set(this);
1764 
1765  // Make initial allocation of shared outline drawing buffer
1766  m_pDrawBuffer = (wxPoint *)malloc(4 * sizeof(wxPoint));
1767  m_nDrawBufferSize = 1;
1768 
1769  // Set up the chart context
1770  m_this_chart_context = (chart_context *)calloc(sizeof(chart_context), 1);
1771  m_this_chart_context->chart = this;
1772  m_RAZBuilt = true;
1773 }
1774 
1775 cm93chart::~cm93chart() {
1776  free(m_pcontour_array);
1777 
1778  delete m_pcovr_set;
1779 
1780  free(m_pDrawBuffer);
1781 }
1782 
1783 void cm93chart::Unload_CM93_Cell(void) {
1784  free(m_CIB.pobject_block);
1785  // free(m_CIB.m_2a);
1786  free(m_CIB.p2dpoint_array);
1787  free(m_CIB.pprelated_object_block);
1788  free(m_CIB.object_vector_record_descriptor_block);
1789  free(m_CIB.attribute_block_top);
1790  free(m_CIB.edge_vector_descriptor_block);
1791  free(m_CIB.pvector_record_block_top);
1792  free(m_CIB.point3d_descriptor_block);
1793  free(m_CIB.p3dpoint_array);
1794 }
1795 
1796 // The idea here is to suggest to upper layers the appropriate scale values
1797 // to be used with this chart If max is too large, performance suffers, and
1798 // the charts are very cluttered onscreen. If the min is too small, then the
1799 // chart rendereding will be over-scaled, and accuracy suffers. In some ways,
1800 // this is subjective.....
1801 
1802 double cm93chart::GetNormalScaleMin(double canvas_scale_factor,
1803  bool b_allow_overzoom) {
1804  switch (GetNativeScale()) {
1805  case 20000000:
1806  return 3000000.; // Z
1807  case 3000000:
1808  return 1000000.; // A
1809  case 1000000:
1810  return 200000.; // B
1811  case 200000:
1812  return 40000.; // C
1813  case 100000:
1814  return 20000.; // D
1815  case 50000:
1816  return 10000.; // E
1817  case 20000:
1818  return 5000.; // F
1819  case 7500:
1820  return 3500.; // G
1821  }
1822 
1823  return 1.0;
1824 }
1825 
1826 double cm93chart::GetNormalScaleMax(double canvas_scale_factor,
1827  int canvas_width) {
1828  /*
1829  XXX previous declaration hides overloaded virtual function
1830  and it was calling:
1831  s57chart::GetNormalScaleMax( canvas_scale_factor, canvas_width )
1832  should we restore this behavior?
1833  */
1834  switch (GetNativeScale()) {
1835  case 20000000:
1836  return 50000000.; // Z
1837  case 3000000:
1838  return 6000000.; // A
1839  case 1000000:
1840  return 2000000.; // B
1841  case 200000:
1842  return 400000.; // C
1843  case 100000:
1844  return 200000.; // D
1845  case 50000:
1846  return 100000.; // E
1847  case 20000:
1848  return 40000.; // F
1849  case 7500:
1850  return 15000.; // G
1851  }
1852 
1853  return 1.0e7;
1854 }
1855 
1856 void cm93chart::GetPointPix(ObjRazRules *rzRules, float north, float east,
1857  wxPoint *r) {
1858  wxPoint2DDouble en(east, north);
1859  GetPointPix(rzRules, &en, r, 1);
1860 }
1861 
1862 void cm93chart::GetPointPix(ObjRazRules *rzRules, wxPoint2DDouble *en,
1863  wxPoint *r, int nPoints) {
1864  S57Obj *obj = rzRules->obj;
1865 
1866  double xr = obj->x_rate;
1867  double xo = obj->x_origin;
1868  double yr = obj->y_rate;
1869  double yo = obj->y_origin;
1870 
1871  if (m_vp_current.m_projection_type == PROJECTION_MERCATOR) {
1872  if (m_vp_current.GetBBox().GetMaxLon() >= 180. &&
1873  rzRules->obj->BBObj.GetMaxLon() < m_vp_current.GetBBox().GetMinLon())
1874  xo += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
1875  else if ((m_vp_current.GetBBox().GetMinLon() <= -180. &&
1876  rzRules->obj->BBObj.GetMinLon() >
1877  m_vp_current.GetBBox().GetMaxLon()) ||
1878  (rzRules->obj->BBObj.GetMaxLon() >= 180 &&
1879  m_vp_current.GetBBox().GetMinLon() <= 0.))
1880  xo -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
1881 
1882  for (int i = 0; i < nPoints; i++) {
1883  double valx = (en[i].m_x * xr) + xo;
1884  double valy = (en[i].m_y * yr) + yo;
1885 
1886  r[i].x = ((valx - m_easting_vp_center) * m_view_scale_ppm) +
1887  m_pixx_vp_center + 0.5;
1888  r[i].y = m_pixy_vp_center -
1889  ((valy - m_northing_vp_center) * m_view_scale_ppm) + 0.5;
1890  }
1891  } else {
1892  for (int i = 0; i < nPoints; i++) {
1893  double valx = (en[i].m_x * xr) + xo;
1894  double valy = (en[i].m_y * yr) + yo;
1895 
1896  double lat, lon;
1897  fromSM(valx - m_easting_vp_center, valy - m_northing_vp_center,
1898  m_vp_current.clat, m_vp_current.clon, &lat, &lon);
1899 
1900  double rotation = m_vp_current.rotation;
1901  m_vp_current.SetRotationAngle(0);
1902  r[i] = m_vp_current.GetPixFromLL(lat, lon);
1903  m_vp_current.SetRotationAngle(rotation);
1904  }
1905  }
1906 }
1907 
1908 void cm93chart::GetPixPoint(int pixx, int pixy, double *plat, double *plon,
1909  ViewPort *vpt) {
1910 #if 1
1911  vpt->GetLLFromPix(wxPoint(pixx, pixy), plat, plon);
1912 
1913  // if ( *plon < 0. )
1914  // *plon += 360.;
1915 
1916 #else
1917  // Use Mercator estimator
1918  int dx = pixx - (vpt->pix_width / 2);
1919  int dy = (vpt->pix_height / 2) - pixy;
1920 
1921  double xp = (dx * cos(vpt->skew)) - (dy * sin(vpt->skew));
1922  double yp = (dy * cos(vpt->skew)) + (dx * sin(vpt->skew));
1923 
1924  double d_east = xp / vpt->view_scale_ppm;
1925  double d_north = yp / vpt->view_scale_ppm;
1926 
1927  double slat, slon;
1928  fromSM(d_east, d_north, vpt->clat, vpt->clon, &slat, &slon);
1929 
1930  if (slon > 360.) slon -= 360.;
1931 
1932  *plat = slat;
1933  *plon = slon;
1934 #endif
1935 }
1936 
1937 bool cm93chart::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) {
1938  if (IsCacheValid()) {
1939  // If this viewpoint is same scale as last...
1940  if (vp_last.view_scale_ppm == vp_proposed.view_scale_ppm) {
1941  // then require this viewport to be exact integral pixel difference from
1942  // last adjusting clat/clat and SM accordingly
1943 #if 1
1944  wxPoint2DDouble p = vp_proposed.GetDoublePixFromLL(ref_lat, ref_lon) -
1945  vp_last.GetDoublePixFromLL(ref_lat, ref_lon);
1946 
1947  double xlat, xlon;
1948  vp_last.GetLLFromPix(wxPoint(round(p.m_x), round(p.m_y)), &xlat, &xlon);
1949 #else
1950  double prev_easting_c, prev_northing_c;
1951  toSM(vp_last.clat, vp_last.clon, ref_lat, ref_lon, &prev_easting_c,
1952  &prev_northing_c);
1953 
1954  double easting_c, northing_c;
1955  toSM(vp_proposed.clat, vp_proposed.clon, ref_lat, ref_lon, &easting_c,
1956  &northing_c);
1957 
1958  double delta_pix_x =
1959  (easting_c - prev_easting_c) * vp_proposed.view_scale_ppm;
1960  int dpix_x = (int)round(delta_pix_x);
1961  double dpx = dpix_x;
1962 
1963  double delta_pix_y =
1964  (northing_c - prev_northing_c) * vp_proposed.view_scale_ppm;
1965  int dpix_y = (int)round(delta_pix_y);
1966  double dpy = dpix_y;
1967 
1968  double c_east_d = (dpx / vp_proposed.view_scale_ppm) + prev_easting_c;
1969  double c_north_d = (dpy / vp_proposed.view_scale_ppm) + prev_northing_c;
1970 
1971  double xlat, xlon;
1972  fromSM(c_east_d, c_north_d, ref_lat, ref_lon, &xlat, &xlon);
1973 #endif
1974  vp_proposed.clon = xlon;
1975  vp_proposed.clat = xlat;
1976 
1977  return true;
1978  }
1979  }
1980 
1981  return false;
1982 }
1983 
1984 //-----------------------------------------------------------------------
1985 // Calculate and Set ViewPoint Constants
1986 //-----------------------------------------------------------------------
1987 
1988 void cm93chart::SetVPParms(const ViewPort &vpt) {
1989  if (m_vp_current == vpt) {
1990  return;
1991  }
1992  // Save a copy for later reference
1993  m_vp_current = vpt;
1994 
1995  // Set up local SM rendering constants
1996  m_pixx_vp_center = vpt.pix_width / 2;
1997  m_pixy_vp_center = vpt.pix_height / 2;
1998  m_view_scale_ppm = vpt.view_scale_ppm;
1999 
2000  toSM(vpt.clat, vpt.clon, ref_lat, ref_lon, &m_easting_vp_center,
2001  &m_northing_vp_center);
2002 
2003  vp_transform.easting_vp_center = m_easting_vp_center;
2004  vp_transform.northing_vp_center = m_northing_vp_center;
2005 
2006  if (g_bDebugCM93) {
2007  // Fetch the lat/lon of the screen corner points
2008  ViewPort vptl = vpt;
2009  LLBBox box = vptl.GetBBox();
2010  double ll_lon = box.GetMinLon();
2011  double ll_lat = box.GetMinLat();
2012 
2013  double ur_lon = box.GetMaxLon();
2014  double ur_lat = box.GetMaxLat();
2015 
2016  printf(
2017  "cm93chart::SetVPParms ll_lon: %g ll_lat: %g ur_lon: %g ur_lat: "
2018  " %g m_dval: %g\n",
2019  ll_lon, ll_lat, ur_lon, ur_lat, m_dval);
2020  }
2021 
2022  // Create an array of CellIndexes covering the current viewport
2023  std::vector<int> vpcells = GetVPCellArray(vpt);
2024 
2025  // Check the member array to see if all these viewport cells have been
2026  // loaded
2027  bool bcell_is_in;
2028 
2029  for (unsigned int i = 0; i < vpcells.size(); i++) {
2030  bcell_is_in = false;
2031  for (unsigned int j = 0; j < m_cells_loaded_array.size(); j++) {
2032  if (vpcells[i] == m_cells_loaded_array[j]) {
2033  bcell_is_in = true;
2034  break;
2035  }
2036  }
2037 
2038  // The cell is not in place, so go load it
2039  if (!bcell_is_in) {
2040 #ifndef __OCPN__ANDROID__
2041  AbstractPlatform::ShowBusySpinner();
2042 #endif
2043  int cell_index = vpcells[i];
2044 
2045  if (loadcell_in_sequence(cell_index, '0')) // Base cell
2046  {
2047  ProcessVectorEdges();
2048  CreateObjChain(cell_index, (int)'0', vpt.view_scale_ppm);
2049 
2050  ForceEdgePriorityEvaluate(); // need to re-evaluate priorities
2051 
2052  m_cells_loaded_array.push_back(cell_index);
2053 
2054  Unload_CM93_Cell();
2055  }
2056 
2057  char loadcell_key = 'A'; // starting
2058 
2059  // Load any subcells in sequence
2060  // On successful load, add it to the member list and process the cell
2061  while (loadcell_in_sequence(cell_index, loadcell_key)) {
2062  ProcessVectorEdges();
2063  CreateObjChain(cell_index, (int)loadcell_key, vpt.view_scale_ppm);
2064 
2065  ForceEdgePriorityEvaluate(); // need to re-evaluate priorities
2066 
2067  if (std::find(m_cells_loaded_array.begin(), m_cells_loaded_array.end(),
2068  cell_index) == m_cells_loaded_array.end())
2069  m_cells_loaded_array.push_back(cell_index);
2070 
2071  Unload_CM93_Cell();
2072 
2073  loadcell_key++;
2074  }
2075 
2076  AssembleLineGeometry();
2077 
2078  ClearDepthContourArray();
2079  BuildDepthContourArray();
2080 
2081  // Set up the chart context
2082  m_this_chart_context->m_pvc_hash = &Get_vc_hash();
2083  m_this_chart_context->m_pve_hash = &Get_ve_hash();
2084 
2085  m_this_chart_context->pFloatingATONArray = pFloatingATONArray;
2086  m_this_chart_context->pRigidATONArray = pRigidATONArray;
2087  m_this_chart_context->chart = this;
2088  m_this_chart_context->chart_type = GetChartType();
2089 
2090  m_this_chart_context->safety_contour = m_next_safe_cnt;
2091  m_this_chart_context->vertex_buffer = GetLineVertexBuffer();
2092  m_this_chart_context->pt2GetAssociatedObjects = &s57chart::GetAssociatedObjects;
2093 
2094  // Loop and populate all the objects
2095  for (int i = 0; i < PI_PRIO_NUM; ++i) {
2096  for (int j = 0; j < PI_LUPNAME_NUM; j++) {
2097  ObjRazRules *top = razRules[i][j];
2098  while (top) {
2099  if (top->obj) top->obj->m_chart_context = m_this_chart_context;
2100  top = top->next;
2101  }
2102  }
2103  }
2104 
2105  AbstractPlatform::HideBusySpinner();
2106  }
2107  }
2108 }
2109 
2110 std::vector<int> cm93chart::GetVPCellArray(const ViewPort &vpt) {
2111  // Fetch the lat/lon of the screen corner points
2112  ViewPort vptl = vpt;
2113  LLBBox box = vptl.GetBBox();
2114  double ll_lon = box.GetMinLon();
2115  double ll_lat = box.GetMinLat();
2116 
2117  double ur_lon = box.GetMaxLon();
2118  double ur_lat = box.GetMaxLat();
2119 
2120  // CLip upper latitude to avoid trying to fetch non-existent cells.
2121  ur_lat = wxMin(ur_lat, 79.99999);
2122 
2123  // CLip upper latitude to avoid trying to fetch non-existent cells above N80.
2124  ur_lat = wxMin(ur_lat, 79.99999);
2125 
2126  // Adjust to always positive for easier cell calculations
2127  if (ll_lon < 0) {
2128  ll_lon += 360;
2129  ur_lon += 360;
2130  }
2131 
2132  // Create an array of CellIndexes covering the current viewport
2133  std::vector<int> vpcells;
2134 
2135  int lower_left_cell = Get_CM93_CellIndex(ll_lat, ll_lon, GetNativeScale());
2136  vpcells.push_back(lower_left_cell); // always add the lower left cell
2137 
2138  if (g_bDebugCM93)
2139  printf("cm93chart::GetVPCellArray Adding %d\n", lower_left_cell);
2140 
2141  double rlat, rlon;
2142  Get_CM93_Cell_Origin(lower_left_cell, GetNativeScale(), &rlat, &rlon);
2143 
2144  // Use exact integer math here
2145  // It is more obtuse, but it removes dependency on FP rounding policy
2146 
2147  int loni_0 = (int)wxRound(rlon * 3);
2148  int loni_20 = loni_0 + (int)m_dval; // already added the lower left cell
2149  int lati_20 = (int)wxRound(rlat * 3);
2150 
2151  while (lati_20 < (ur_lat * 3.)) {
2152  while (loni_20 < (ur_lon * 3.)) {
2153  unsigned int next_lon = loni_20 + 1080;
2154  while (next_lon >= 1080) next_lon -= 1080;
2155 
2156  unsigned int next_cell = next_lon;
2157 
2158  next_cell += (lati_20 + 270) * 10000;
2159 
2160  vpcells.push_back((int)next_cell);
2161  if (g_bDebugCM93)
2162  printf("cm93chart::GetVPCellArray Adding %d\n", next_cell);
2163 
2164  loni_20 += (int)m_dval;
2165  }
2166  lati_20 += (int)m_dval;
2167  loni_20 = loni_0;
2168  }
2169 
2170  return vpcells;
2171 }
2172 
2173 void cm93chart::ProcessVectorEdges(void) {
2174  // Create the vector(edge) map for this cell, appending to the existing
2175  // member hash map
2176  auto &vehash = Get_ve_hash();
2177 
2178  m_current_cell_vearray_offset =
2179  vehash.size(); // keys start at the current size
2180  geometry_descriptor *pgd = m_CIB.edge_vector_descriptor_block;
2181 
2182  for (int iedge = 0; iedge < m_CIB.m_nvector_records; iedge++) {
2183  VE_Element *vep = new VE_Element;
2184  vep->index = iedge + m_current_cell_vearray_offset;
2185  vep->nCount = pgd->n_points;
2186  vep->pPoints = NULL;
2187  vep->max_priority = -99; // Default
2188 
2189  if (pgd->n_points) {
2190  float *pPoints = (float *)malloc(pgd->n_points * 2 * sizeof(float));
2191  vep->pPoints = pPoints;
2192 
2193  cm93_point *ppt = pgd->p_points;
2194 
2195  // Get a bounding box for the edge
2196  double east_max = -1e7;
2197  double east_min = 1e7;
2198  double north_max = -1e7;
2199  double north_min = 1e7;
2200 
2201  for (int ip = 0; ip < pgd->n_points; ip++) {
2202  *pPoints++ = ppt->x;
2203  *pPoints++ = ppt->y;
2204 
2205  east_max = wxMax(east_max, ppt->x);
2206  east_min = wxMin(east_min, ppt->x);
2207  north_max = wxMax(north_max, ppt->y);
2208  north_min = wxMin(north_min, ppt->y);
2209 
2210  ppt++;
2211  }
2212 
2213  cm93_point p;
2214  double lat1, lon1, lat2, lon2;
2215 
2216  // TODO Not precisely correct, transform should account for
2217  // "trans_WGS84_offset_x"
2218  p.x = east_min;
2219  p.y = north_min;
2220  Transform(&p, 0, 0, &lat1, &lon1);
2221 
2222  p.x = east_max;
2223  p.y = north_max;
2224  Transform(&p, 0, 0, &lat2, &lon2);
2225 
2226  // if(lon1 > lon2)
2227  // lon2 += 360;
2228 
2229  vep->edgeBBox.Set(lat1, lon1, lat2, lon2);
2230  }
2231 
2232  vehash[vep->index] = vep;
2233 
2234  pgd++; // next geometry descriptor
2235  }
2236 }
2237 
2238 int cm93chart::CreateObjChain(int cell_index, int subcell,
2239  double view_scale_ppm) {
2240  LUPrec *LUP;
2241  LUPname LUP_Name = PAPER_CHART;
2242 
2243  m_CIB.m_cell_mcovr_list.Clear();
2244 
2245  // CALLGRIND_START_INSTRUMENTATION
2246 
2247  Object *pobjectDef = m_CIB.pobject_block; // head of object array
2248  m_CIB.b_have_offsets = false; // will be set if any M_COVRs in this cell have
2249  // defined, non-zero WGS84 offsets
2250  m_CIB.b_have_user_offsets = false; // will be set if any M_COVRs in this cell
2251  // have user defined offsets
2252 
2253  int iObj = 0;
2254  S57Obj *obj;
2255 
2256  double scale = gFrame->GetBestVPScale(this);
2257  int nativescale = GetNativeScale();
2258 
2259  while (iObj < m_CIB.m_nfeature_records) {
2260  if ((pobjectDef != NULL)) {
2261  Extended_Geometry *xgeom = BuildGeom(pobjectDef, NULL, iObj);
2262 
2263  obj = NULL;
2264  if (NULL != xgeom)
2265  obj =
2266  CreateS57Obj(cell_index, iObj, subcell, pobjectDef, m_pDict, xgeom,
2267  ref_lat, ref_lon, GetNativeScale(), view_scale_ppm);
2268 
2269  if (obj) {
2270  wxString objnam = obj->GetAttrValueAsString("OBJNAM");
2271  wxString fe_name = wxString(obj->FeatureName, wxConvUTF8);
2272  if (fe_name == _T("_texto"))
2273  objnam = obj->GetAttrValueAsString("_texta");
2274  if (objnam.Len() > 0) {
2275  wxString cellname =
2276  wxString::Format(_T("%i_%i"), cell_index, subcell);
2277  g_pi_manager->SendVectorChartObjectInfo(cellname, fe_name, objnam,
2278  obj->m_lat, obj->m_lon, scale,
2279  nativescale);
2280  }
2281  // Build/Maintain the ATON floating/rigid arrays
2282  if (GEO_POINT == obj->Primitive_type) {
2283  // set floating platform
2284  if ((!strncmp(obj->FeatureName, "LITFLT", 6)) ||
2285  (!strncmp(obj->FeatureName, "LITVES", 6)) ||
2286  (!strncmp(obj->FeatureName, "BOY", 3))) {
2287  pFloatingATONArray->Add(obj);
2288  }
2289 
2290  // set rigid platform
2291  if (!strncmp(obj->FeatureName, "BCN", 3)) pRigidATONArray->Add(obj);
2292 
2293  // Mark the object as an ATON
2294  if ((!strncmp(obj->FeatureName, "LIT", 3)) ||
2295  (!strncmp(obj->FeatureName, "LIGHTS", 6)) ||
2296  (!strncmp(obj->FeatureName, "BCN", 3)) ||
2297  (!strncmp(obj->FeatureName, "_slgto", 6)) ||
2298  (!strncmp(obj->FeatureName, "_boygn", 6)) ||
2299  (!strncmp(obj->FeatureName, "_bcngn", 6)) ||
2300  (!strncmp(obj->FeatureName, "_extgn", 6)) ||
2301  (!strncmp(obj->FeatureName, "TOWERS", 6)) ||
2302  (!strncmp(obj->FeatureName, "BOY", 3))) {
2303  obj->bIsAton = true;
2304  }
2305  }
2306 
2307  // Mark th object as an "associable depth area"
2308  // Flag is used by conditional symbology
2309  if (GEO_AREA == obj->Primitive_type) {
2310  if (!strncmp(obj->FeatureName, "DEPARE", 6) ||
2311  !strncmp(obj->FeatureName, "DRGARE", 6))
2312  obj->bIsAssociable = true;
2313  }
2314 
2315  // This is where Simplified or Paper-Type point features are
2316  // selected In the case where the chart needs alternate LUPS
2317  // loaded, do so. This case is triggered when the UpdateLUP()
2318  // method has been called on a partially loaded chart.
2319 
2320  switch (obj->Primitive_type) {
2321  case GEO_POINT:
2322  case GEO_META:
2323  case GEO_PRIM:
2324  if (PAPER_CHART == ps52plib->m_nSymbolStyle)
2325  LUP_Name = PAPER_CHART;
2326  else
2327  LUP_Name = SIMPLIFIED;
2328 
2329  if (m_b2pointLUPS) {
2330  LUPname LUPO_Name;
2331  if (PAPER_CHART == ps52plib->m_nSymbolStyle)
2332  LUPO_Name = SIMPLIFIED;
2333  else
2334  LUPO_Name = PAPER_CHART;
2335 
2336  // Load the alternate LUP
2337  LUPrec *LUPO =
2338  ps52plib->S52_LUPLookup(LUPO_Name, obj->FeatureName, obj);
2339  if (LUPO) {
2340  ps52plib->_LUP2rules(LUPO, obj);
2341  _insertRules(obj, LUPO, this);
2342  }
2343  }
2344  break;
2345 
2346  case GEO_LINE:
2347  LUP_Name = LINES;
2348  break;
2349 
2350  case GEO_AREA:
2351  if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle)
2352  LUP_Name = PLAIN_BOUNDARIES;
2353  else
2354  LUP_Name = SYMBOLIZED_BOUNDARIES;
2355 
2356  if (m_b2lineLUPS) {
2357  LUPname LUPO_Name;
2358  if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle)
2359  LUPO_Name = SYMBOLIZED_BOUNDARIES;
2360  else
2361  LUPO_Name = PLAIN_BOUNDARIES;
2362 
2363  // Load the alternate LUP
2364  LUPrec *LUPO =
2365  ps52plib->S52_LUPLookup(LUPO_Name, obj->FeatureName, obj);
2366  if (LUPO) {
2367  ps52plib->_LUP2rules(LUPO, obj);
2368  _insertRules(obj, LUPO, this);
2369  }
2370  }
2371  break;
2372  }
2373 
2374  LUP = ps52plib->S52_LUPLookup(LUP_Name, obj->FeatureName, obj);
2375 
2376  if (NULL == LUP) {
2377  if (g_bDebugCM93) {
2378  wxString msg(obj->FeatureName, wxConvUTF8);
2379  msg.Prepend(_T ( " CM93 could not find LUP for " ));
2380  LogMessageOnce(msg);
2381  }
2382  if (0 == obj->nRef) delete obj;
2383  } else {
2384  // Convert LUP to rules set
2385  ps52plib->_LUP2rules(LUP, obj);
2386 
2387  // Add linked object/LUP to the working set
2388  _insertRules(obj, LUP, this);
2389 
2390  // Establish Object's Display Category
2391  obj->m_DisplayCat = LUP->DISC;
2392 
2393  // Establish objects base display priority
2394  obj->m_DPRI = LUP->DPRI - '0';
2395 
2396  // Populate the chart context
2397  obj->m_chart_context = m_this_chart_context;
2398  }
2399  }
2400 
2401  }
2402 
2403  else // objectdef == NULL
2404  break;
2405 
2406  pobjectDef++;
2407 
2408  iObj++;
2409  }
2410 
2411  // CALLGRIND_STOP_INSTRUMENTATION
2412 
2413  return 1;
2414 }
2415 
2416 InitReturn cm93chart::Init(const wxString &name, ChartInitFlag flags) {
2417  m_FullPath = name;
2418  m_Description = m_FullPath;
2419 
2420  wxFileName fn(name);
2421 
2422  if (!m_prefix.Len())
2423  m_prefix = fn.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
2424 
2425  m_scalechar = fn.GetExt();
2426 
2427  // Figure out the scale from the file name
2428 
2429  int scale;
2430  switch ((m_scalechar.mb_str())[(size_t)0]) {
2431  case 'Z':
2432  scale = 20000000;
2433  break;
2434  case 'A':
2435  scale = 3000000;
2436  break;
2437  case 'B':
2438  scale = 1000000;
2439  break;
2440  case 'C':
2441  scale = 200000;
2442  break;
2443  case 'D':
2444  scale = 100000;
2445  break;
2446  case 'E':
2447  scale = 50000;
2448  break;
2449  case 'F':
2450  scale = 20000;
2451  break;
2452  case 'G':
2453  scale = 7500;
2454  break;
2455  default:
2456  scale = 20000000;
2457  break;
2458  }
2459 
2460  m_Chart_Scale = scale;
2461 
2462  switch (GetNativeScale()) {
2463  case 20000000:
2464  m_dval = 120;
2465  break; // Z
2466  case 3000000:
2467  m_dval = 60;
2468  break; // A
2469  case 1000000:
2470  m_dval = 30;
2471  break; // B
2472  case 200000:
2473  m_dval = 12;
2474  break; // C
2475  case 100000:
2476  m_dval = 3;
2477  break; // D
2478  case 50000:
2479  m_dval = 1;
2480  break; // E
2481  case 20000:
2482  m_dval = 1;
2483  break; // F
2484  case 7500:
2485  m_dval = 1;
2486  break; // G
2487  default:
2488  m_dval = 1;
2489  break;
2490  }
2491 
2492  // Set the nice name
2493  wxString data = _T ( "CM93Chart " );
2494  data.Append(m_scalechar);
2495  wxString s;
2496  s.Printf(_T ( " 1/%d" ), m_Chart_Scale);
2497  data.Append(s);
2498  m_Name = data;
2499 
2500  // Initialize the covr_set
2501  if (scale != 20000000)
2502  m_pcovr_set->Init(m_scalechar.mb_str()[(size_t)0], m_prefix);
2503 
2504  if (flags == THUMB_ONLY) {
2505  // SetColorScheme(cs, false);
2506 
2507  return INIT_OK;
2508  }
2509 
2510  if (!m_pManager) m_pManager = new cm93manager;
2511 
2512  if (flags == HEADER_ONLY) return CreateHeaderDataFromCM93Cell();
2513 
2514  // Load the cm93 dictionary if necessary
2515  if (!m_pDict) {
2516  if (m_pManager) {
2517  if (m_pManager->Loadcm93Dictionary(name))
2518  m_pDict = m_pManager->m_pcm93Dict;
2519  else {
2520  wxLogMessage(_T ( " CM93Chart Init cannot locate CM93 dictionary." ));
2521  return INIT_FAIL_REMOVE;
2522  }
2523  }
2524  }
2525 
2526  bReadyToRender = true;
2527 
2528  return INIT_OK;
2529 }
2530 
2531 Extended_Geometry *cm93chart::BuildGeom(Object *pobject,
2532  wxFileOutputStream *postream,
2533  int iobject)
2534 
2535 {
2536  wxString s;
2537  int geomtype;
2538 
2539  int geom_type_maybe = pobject->geotype;
2540 
2541  switch (geom_type_maybe) {
2542  case 1:
2543  geomtype = 1;
2544  break;
2545  case 2:
2546  geomtype = 2;
2547  break;
2548  case 4:
2549  geomtype = 3;
2550  break;
2551  case 129:
2552  geomtype = 1;
2553  break;
2554  case 130:
2555  geomtype = 2;
2556  break;
2557  case 132:
2558  geomtype = 3;
2559  break;
2560  case 8:
2561  geomtype = 8;
2562  break;
2563  case 16:
2564  geomtype = 16;
2565  break;
2566  case 161:
2567  geomtype = 1;
2568  break; // lighthouse first child
2569  case 33:
2570  geomtype = 1;
2571  break;
2572  default:
2573  geomtype = -1;
2574  break;
2575  }
2576 
2577  int iseg;
2578 
2579  Extended_Geometry *ret_ptr = new Extended_Geometry;
2580 
2581  int lon_max, lat_max, lon_min, lat_min;
2582  lon_max = 0;
2583  lon_min = 65536;
2584  lat_max = 0;
2585  lat_min = 65536;
2586 
2587  switch (geomtype) {
2588  case 3: // Areas
2589  {
2590  vector_record_descriptor *psegs =
2591  (vector_record_descriptor *)pobject->pGeometry;
2592 
2593  int nsegs = pobject->n_geom_elements;
2594 
2595  ret_ptr->n_vector_indices = nsegs;
2596  ret_ptr->pvector_index = (int *)malloc(nsegs * 3 * sizeof(int));
2597 
2598  // Traverse the object once to get a maximum polygon vertex count
2599  int n_maxvertex = 0;
2600  for (int i = 0; i < nsegs; i++) {
2601  geometry_descriptor *pgd =
2602  (geometry_descriptor *)(psegs[i].pGeom_Description);
2603  n_maxvertex += pgd->n_points;
2604  }
2605 
2606  // TODO May not need this fluff adder....
2607  n_maxvertex += 1; // fluff
2608 
2609  wxPoint2DDouble *pPoints =
2610  (wxPoint2DDouble *)calloc((n_maxvertex) * sizeof(wxPoint2DDouble), 1);
2611 
2612  int ip = 1;
2613  int n_prev_vertex_index = 1;
2614  bool bnew_ring = true;
2615  int ncontours = 0;
2616  iseg = 0;
2617 
2618  cm93_point start_point;
2619  start_point.x = 0;
2620  start_point.y = 0;
2621 
2622  cm93_point cur_end_point;
2623  cur_end_point.x = 1;
2624  cur_end_point.y = 1;
2625 
2626  int n_max_points = -1;
2627  while (iseg < nsegs) {
2628  int type_seg = psegs[iseg].segment_usage;
2629 
2630  geometry_descriptor *pgd =
2631  (geometry_descriptor *)(psegs[iseg].pGeom_Description);
2632 
2633  int npoints = pgd->n_points;
2634  cm93_point *rseg = pgd->p_points;
2635 
2636  n_max_points = wxMax(n_max_points, npoints);
2637 
2638  // Establish ring starting conditions
2639  if (bnew_ring) {
2640  bnew_ring = false;
2641 
2642  if ((type_seg & 4) == 0)
2643  start_point = rseg[0];
2644  else
2645  start_point = rseg[npoints - 1];
2646  }
2647 
2648  if (((type_seg & 4) == 0)) {
2649  cur_end_point = rseg[npoints - 1];
2650  for (int j = 0; j < npoints; j++) {
2651  // if(ncontours == 0) // outer
2652  // ring describes envelope
2653  {
2654  lon_max = wxMax(lon_max, rseg[j].x);
2655  lon_min = wxMin(lon_min, rseg[j].x);
2656  lat_max = wxMax(lat_max, rseg[j].y);
2657  lat_min = wxMin(lat_min, rseg[j].y);
2658  }
2659 
2660  pPoints[ip].m_x = rseg[j].x;
2661  pPoints[ip].m_y = rseg[j].y;
2662  ip++;
2663  }
2664  } else if ((type_seg & 4) == 4) // backwards
2665  {
2666  cur_end_point = rseg[0];
2667  for (int j = npoints - 1; j >= 0; j--) {
2668  // if(ncontours == 0) // outer
2669  // ring describes envelope
2670  {
2671  lon_max = wxMax(lon_max, rseg[j].x);
2672  lon_min = wxMin(lon_min, rseg[j].x);
2673  lat_max = wxMax(lat_max, rseg[j].y);
2674  lat_min = wxMin(lat_min, rseg[j].y);
2675  }
2676 
2677  pPoints[ip].m_x = rseg[j].x;
2678  pPoints[ip].m_y = rseg[j].y;
2679  ip++;
2680  }
2681  }
2682 
2683  ip--; // skip the last point in each segment
2684 
2685  ret_ptr->pvector_index[iseg * 3 + 0] =
2686  0; //-1; // first connected node
2687  ret_ptr->pvector_index[iseg * 3 + 1] =
2688  pgd->index + m_current_cell_vearray_offset; // edge index
2689  ret_ptr->pvector_index[iseg * 3 + 2] =
2690  0; //-2; // last connected node
2691 
2692  if ((cur_end_point.x == start_point.x) &&
2693  (cur_end_point.y == start_point.y)) {
2694  // done with a ring
2695 
2696  ip++; // leave in ring closure point
2697 
2698  int nRingVertex = ip - n_prev_vertex_index;
2699 
2700  // possibly increase contour array size
2701  if (ncontours > m_ncontour_alloc - 1) {
2702  m_ncontour_alloc *= 2;
2703  int *tmp = m_pcontour_array;
2704  m_pcontour_array = (int *)realloc(m_pcontour_array,
2705  m_ncontour_alloc * sizeof(int));
2706  if (NULL == tmp) {
2707  free(tmp);
2708  tmp = NULL;
2709  }
2710  }
2711  m_pcontour_array[ncontours] = nRingVertex; // store the vertex count
2712 
2713  bnew_ring = true; // set for next ring
2714  n_prev_vertex_index = ip;
2715  ncontours++;
2716  }
2717  iseg++;
2718  } // while iseg
2719 
2720  ret_ptr->n_max_edge_points = n_max_points;
2721 
2722  ret_ptr->n_contours =
2723  ncontours; // parameters passed to trapezoid tesselator
2724 
2725  if (0 == ncontours) ncontours = 1; // avoid 0 alloc
2726  ret_ptr->contour_array = (int *)malloc(ncontours * sizeof(int));
2727  memcpy(ret_ptr->contour_array, m_pcontour_array, ncontours * sizeof(int));
2728 
2729  ret_ptr->vertex_array = pPoints;
2730  ret_ptr->n_max_vertex = n_maxvertex;
2731 
2732  ret_ptr->pogrGeom = NULL;
2733 
2734  ret_ptr->xmin = lon_min;
2735  ret_ptr->xmax = lon_max;
2736  ret_ptr->ymin = lat_min;
2737  ret_ptr->ymax = lat_max;
2738 
2739  break;
2740  } // case 3
2741 
2742  case 1: // single points
2743  {
2744  cm93_point *pt = (cm93_point *)pobject->pGeometry;
2745  ret_ptr->pogrGeom = NULL; // t;
2746 
2747  ret_ptr->pointx = pt->x;
2748  ret_ptr->pointy = pt->y;
2749  break;
2750  }
2751 
2752  case 2: // LINE geometry
2753  {
2754  vector_record_descriptor *psegs =
2755  (vector_record_descriptor *)pobject->pGeometry;
2756 
2757  int nsegs = pobject->n_geom_elements;
2758 
2759  ret_ptr->n_vector_indices = nsegs;
2760  ret_ptr->pvector_index = (int *)malloc(nsegs * 3 * sizeof(int));
2761 
2762  // Calculate the number of points
2763  int n_maxvertex = 0;
2764  for (int imseg = 0; imseg < nsegs; imseg++) {
2765  geometry_descriptor *pgd =
2766  (geometry_descriptor *)psegs->pGeom_Description;
2767 
2768  n_maxvertex += pgd->n_points;
2769  psegs++;
2770  }
2771 
2772  wxPoint2DDouble *pPoints =
2773  (wxPoint2DDouble *)malloc(n_maxvertex * sizeof(wxPoint2DDouble));
2774 
2775  psegs = (vector_record_descriptor *)pobject->pGeometry;
2776 
2777  int ip = 0;
2778  int lon_max, lat_max, lon_min, lat_min;
2779  lon_max = 0;
2780  lon_min = 65536;
2781  lat_max = 0;
2782  lat_min = 65536;
2783  int n_max_points = -1;
2784 
2785  for (int iseg = 0; iseg < nsegs; iseg++) {
2786  int type_seg = psegs->segment_usage;
2787 
2788  geometry_descriptor *pgd =
2789  (geometry_descriptor *)psegs->pGeom_Description;
2790 
2791  psegs++; // next segment
2792 
2793  int npoints = pgd->n_points;
2794  cm93_point *rseg = pgd->p_points;
2795 
2796  n_max_points = wxMax(n_max_points, npoints);
2797 
2798  if (((type_seg & 4) != 4)) {
2799  for (int j = 0; j < npoints; j++) {
2800  lon_max = wxMax(lon_max, rseg[j].x);
2801  lon_min = wxMin(lon_min, rseg[j].x);
2802  lat_max = wxMax(lat_max, rseg[j].y);
2803  lat_min = wxMin(lat_min, rseg[j].y);
2804 
2805  pPoints[ip].m_x = rseg[j].x;
2806  pPoints[ip].m_y = rseg[j].y;
2807  ip++;
2808  }
2809  }
2810 
2811  else if ((type_seg & 4) == 4) // backwards
2812  {
2813  for (int j = npoints - 1; j >= 0; j--) {
2814  lon_max = wxMax(lon_max, rseg[j].x);
2815  lon_min = wxMin(lon_min, rseg[j].x);
2816  lat_max = wxMax(lat_max, rseg[j].y);
2817  lat_min = wxMin(lat_min, rseg[j].y);
2818 
2819  pPoints[ip].m_x = rseg[j].x;
2820  pPoints[ip].m_y = rseg[j].y;
2821  ip++;
2822  }
2823  }
2824 
2825  ret_ptr->pvector_index[iseg * 3 + 0] =
2826  0; //-1; // first connected node
2827  ret_ptr->pvector_index[iseg * 3 + 1] =
2828  pgd->index + m_current_cell_vearray_offset; // edge index
2829  ret_ptr->pvector_index[iseg * 3 + 2] =
2830  0; //-2; // last connected node
2831 
2832  } // for
2833 
2834  ret_ptr->n_max_edge_points = n_max_points;
2835 
2836  ret_ptr->vertex_array = pPoints;
2837  ret_ptr->n_max_vertex = n_maxvertex;
2838 
2839  ret_ptr->pogrGeom = NULL;
2840 
2841  ret_ptr->xmin = lon_min;
2842  ret_ptr->xmax = lon_max;
2843  ret_ptr->ymin = lat_min;
2844  ret_ptr->ymax = lat_max;
2845 
2846  break;
2847  } // case 2 (lines)
2848 
2849  case 8: {
2850  geometry_descriptor *pgd = (geometry_descriptor *)pobject->pGeometry;
2851 
2852  int npoints = pgd->n_points;
2853  cm93_point_3d *rseg = (cm93_point_3d *)pgd->p_points;
2854 
2855  OGRMultiPoint *pSMP = new OGRMultiPoint;
2856 
2857  int z;
2858  double zp;
2859  for (int ip = 0; ip < npoints; ip++) {
2860  z = rseg[ip].z;
2861 
2862  // This is a magic number if there ever was one.....
2863  if (z >= 12000)
2864  zp = double(z - 12000);
2865  else
2866  zp = z / 10.;
2867 
2868  OGRPoint *ppoint = new OGRPoint(rseg[ip].x, rseg[ip].y, zp);
2869  pSMP->addGeometryDirectly(ppoint);
2870 
2871  lon_max = wxMax(lon_max, rseg[ip].x);
2872  lon_min = wxMin(lon_min, rseg[ip].x);
2873  lat_max = wxMax(lat_max, rseg[ip].y);
2874  lat_min = wxMin(lat_min, rseg[ip].y);
2875  }
2876 
2877  ret_ptr->pogrGeom = pSMP;
2878 
2879  ret_ptr->xmin = lon_min;
2880  ret_ptr->xmax = lon_max;
2881  ret_ptr->ymin = lat_min;
2882  ret_ptr->ymax = lat_max;
2883 
2884  break;
2885  }
2886 
2887  case 16:
2888  break; // this is the case of objects with children
2889  // the parent has no geometry.....
2890 
2891  default: {
2892  wxPrintf(_T ( "Unexpected geomtype %d for Feature %d\n" ), geomtype,
2893  iobject);
2894  break;
2895  }
2896 
2897  } // switch
2898 
2899  return ret_ptr;
2900 }
2901 
2902 void cm93chart::Transform(cm93_point *s, double trans_x, double trans_y,
2903  double *lat, double *lon) {
2904  // Simple linear transform
2905  double valx = (s->x * m_CIB.transform_x_rate) + m_CIB.transform_x_origin;
2906  double valy = (s->y * m_CIB.transform_y_rate) + m_CIB.transform_y_origin;
2907 
2908  // Add in the WGS84 offset corrections
2909  valx -= trans_x;
2910  valy -= trans_y;
2911 
2912  // Convert to lat/lon
2913  *lat =
2914  (2.0 * atan(exp(valy / CM93_semimajor_axis_meters)) - PI / 2.) / DEGREE;
2915  *lon = (valx / (DEGREE * CM93_semimajor_axis_meters));
2916 }
2917 
2918 cm93_attr_block::cm93_attr_block(void *block, cm93_dictionary *pdict) {
2919  m_cptr = 0;
2920  m_block = (unsigned char *)block;
2921  m_pDict = pdict;
2922 }
2923 
2924 unsigned char *cm93_attr_block::GetNextAttr() {
2925  // return current pointer
2926  unsigned char *ret_val = m_block + m_cptr;
2927 
2928  // Advance the pointer
2929 
2930  unsigned char iattr = *(m_block + m_cptr);
2931  m_cptr++;
2932 
2933  // char vtype = m_pDict->m_ValTypeArray[iattr];
2934  char vtype = m_pDict->GetAttrType(iattr);
2935 
2936  switch (vtype) {
2937  case 'I': // never seen?
2938  m_cptr += 2;
2939  break;
2940  case 'B':
2941  m_cptr += 1;
2942  // pb = (unsigned char *)aval;
2943  // sprintf(val, "%d", *pb);
2944  // pvtype = 'I'; // override
2945  break;
2946  case 'S':
2947  while (*(m_block + m_cptr)) m_cptr++;
2948  m_cptr++; // skip terminator
2949  // sprintf(val, "%s", aval);
2950  break;
2951  case 'R':
2952  m_cptr += 4;
2953  // pf = (float *)aval;
2954  // sprintf(val, "%g", *pf);
2955  break;
2956  case 'W':
2957  m_cptr += 2;
2958  break;
2959  case 'G':
2960  m_cptr += 4;
2961  break;
2962  case 'C':
2963  m_cptr += 3;
2964  while (*(m_block + m_cptr)) m_cptr++;
2965  m_cptr++; // skip terminator
2966  // sprintf(val, "%s", &aval[3]);
2967  // pvtype = 'S'; // override
2968  break;
2969  case 'L': {
2970  unsigned char nl = *(m_block + m_cptr);
2971  m_cptr++;
2972  m_cptr += nl;
2973 
2974  // pb = (unsigned char *)aval;
2975  // unsigned char nl = *pb++;
2976  // char vi[20];
2977  // val[0] = 0;
2978  // for(int i=0 ; i<nl ; i++)
2979  // {
2980  // sprintf(vi, "%d,", *pb++);
2981  // strcat(val, vi);
2982  // }
2983  // if(strlen(val))
2984  // val[strlen(val)-1] = 0; // strip last
2985  // ","
2986  // pvtype = 'S'; // override
2987  break;
2988  }
2989  default:
2990  // sprintf(val, "Unknown Value Type");
2991  break;
2992  }
2993 
2994  return ret_val;
2995 }
2996 
2997 wxString ParseSLGTA(wxString &val) {
2998  wxString result;
2999  char line[30];
3000 
3001  wxString s;
3002  wxStringTokenizer tkz(val, wxT("|"));
3003 
3004  s = tkz.GetNextToken();
3005  s = tkz.GetNextToken();
3006  s = tkz.GetNextToken(); // Mark attributes
3007 
3008  // Defaults, black can
3009  wxString sc, st, sp;
3010  int color = 0;
3011  sc = _T ( "" );
3012  int type = 0;
3013  st = _T ( "" );
3014  int colpat = 0;
3015  sp = _T ( "" );
3016 
3017  if (s[0] == 'R') {
3018  color = 3;
3019  sc = _T ( "3" );
3020  }
3021 
3022  else if (s[0] == 'G') {
3023  color = 4;
3024  sc = _T ( "4" );
3025  } else if (s.Mid(0, 3) == _T ( "W/O" )) {
3026  color = 1;
3027  sc = _T ( "1,11" );
3028 
3029  colpat = 1;
3030  sp = _T ( "1" );
3031  } else if (s.Mid(0, 5) == _T ( "LIGHT" )) {
3032  color = 0;
3033  type = 0;
3034  }
3035 
3036  if (val.Find(_T ( "Spar" )) != wxNOT_FOUND) {
3037  type = 5;
3038  st = _T ( "5" );
3039  }
3040  if (val.Find(_T ( "SPAR" )) != wxNOT_FOUND) {
3041  type = 5;
3042  st = _T ( "5" );
3043  }
3044 
3045  if ((type == 2) && (color == 3)) // red can?
3046  {
3047  type = 1; // change to nun
3048  st = _T ( "1" );
3049  }
3050 
3051  if (color) {
3052  sprintf(line, " %s (%c) = %s", "COLOUR", 'I', (const char *)sc.mb_str());
3053  result += wxString(line, wxConvUTF8);
3054  result += '\n';
3055  if (!type) {
3056  sprintf(line, " %s (%c) = %s", "BOYSHP", 'I', "4");
3057  result += wxString(line, wxConvUTF8);
3058  result += '\n';
3059  }
3060  }
3061 
3062  if (type) {
3063  sprintf(line, " %s (%c) = %s", "BOYSHP", 'I', (const char *)st.mb_str());
3064  result += wxString(line, wxConvUTF8);
3065  result += '\n';
3066  if (!color) {
3067  sprintf(line, " %s (%c) = %s", "COLOUR", 'I', "2");
3068  result += wxString(line, wxConvUTF8);
3069  result += '\n';
3070  }
3071  }
3072 
3073  if (colpat) {
3074  sprintf(line, " %s (%c) = %s", "COLPAT", 'I', (const char *)sp.mb_str());
3075  result += wxString(line, wxConvUTF8);
3076  result += '\n';
3077  }
3078 
3079  return result;
3080 }
3081 
3082 wxString ParseTEXTA(wxString &val) {
3083  wxString result;
3084  char line[30];
3085 
3086  if (val.Contains(_T ( "WK S" ))) {
3087  sprintf(line, " %s (%c) = %s", "WRKATT", 'I', "1");
3088  result += wxString(line, wxConvUTF8);
3089  result += '\n';
3090  }
3091 
3092  return result;
3093 }
3094 
3095 void cm93chart::translate_colmar(const wxString &sclass,
3096  S57attVal *pattValTmp) {
3097  int *pcur_attr = (int *)pattValTmp->value;
3098  int cur_attr = *pcur_attr;
3099 
3100  wxString lstring;
3101 
3102  switch (cur_attr) {
3103  case 1:
3104  lstring = _T ( "4" );
3105  break; // green
3106  case 2:
3107  lstring = _T ( "2" );
3108  break; // black
3109  case 3:
3110  lstring = _T ( "3" );
3111  break; // red
3112  case 4:
3113  lstring = _T ( "6" );
3114  break; // yellow
3115  case 5:
3116  lstring = _T ( "1" );
3117  break; // white
3118  case 6:
3119  lstring = _T ( "11" );
3120  break; // orange
3121  case 7:
3122  lstring = _T ( "2,6" );
3123  break; // black/yellow
3124  case 8:
3125  lstring = _T ( "2,6,2" );
3126  break; // black/yellow/black
3127  case 9:
3128  lstring = _T ( "6,2" );
3129  break; // yellow/black
3130  case 10:
3131  lstring = _T ( "6,2,6" );
3132  break; // yellow/black/yellow
3133  case 11:
3134  lstring = _T ( "3,1" );
3135  break; // red/white
3136  case 12:
3137  lstring = _T ( "4,3,4" );
3138  break; // green/red/green
3139  case 13:
3140  lstring = _T ( "3,4,3" );
3141  break; // red/green/red
3142  case 14:
3143  lstring = _T ( "2,3,2" );
3144  break; // black/red/black
3145  case 15:
3146  lstring = _T ( "6,3,6" );
3147  break; // yellow/red/yellow
3148  case 16:
3149  lstring = _T ( "4,3" );
3150  break; // green/red
3151  case 17:
3152  lstring = _T ( "3,4" );
3153  break; // red/green
3154  case 18:
3155  lstring = _T ( "4,1" );
3156  break; // green/white
3157  default:
3158  break;
3159  }
3160 
3161  if (lstring.Len()) {
3162  free(pattValTmp->value); // free the old int pointer
3163 
3164  pattValTmp->valType = OGR_STR;
3165  pattValTmp->value = strdup(lstring.mb_str());
3166  }
3167 }
3168 
3169 S57Obj *cm93chart::CreateS57Obj(int cell_index, int iobject, int subcell,
3170  Object *pobject, cm93_dictionary *pDict,
3171  Extended_Geometry *xgeom, double ref_lat,
3172  double ref_lon, double scale,
3173  double view_scale_ppm) {
3174 #define MAX_HDR_LINE 4000
3175 
3176  // printf("%d\n", iobject);
3177 
3178  int npub_year = 1993; // silly default
3179 
3180  int iclass = pobject->otype;
3181  int geomtype = pobject->geotype & 0x0f;
3182 
3183  double tmp_transform_x = 0.;
3184  double tmp_transform_y = 0.;
3185 
3186  // Per object transfor offsets,
3187  double trans_WGS84_offset_x = 0.;
3188  double trans_WGS84_offset_y = 0.;
3189 
3190  wxString sclass = pDict->GetClassName(iclass);
3191  if (sclass == _T ( "Unknown" )) {
3192  wxString msg;
3193  msg.Printf(_T ( " CM93 Error...object type %d not found in CM93OBJ.DIC" ),
3194  iclass);
3195  wxLogMessage(msg);
3196  delete xgeom;
3197  return NULL;
3198  }
3199 
3200  wxString sclass_sub = sclass;
3201 
3202  // Going to make some substitutions here
3203  if (sclass.IsSameAs(_T ( "ITDARE" ))) sclass_sub = _T ( "DEPARE" );
3204 
3205  if (sclass.IsSameAs(_T ( "_m_sor" ))) sclass_sub = _T ( "M_COVR" );
3206 
3207  if (sclass.IsSameAs(_T ( "SPOGRD" ))) sclass_sub = _T ( "DMPGRD" );
3208 
3209  if (sclass.IsSameAs(_T ( "FSHHAV" ))) sclass_sub = _T ( "FSHFAC" );
3210 
3211  if (sclass.IsSameAs(_T ( "OFSPRD" ))) sclass_sub = _T ( "CTNARE" );
3212 
3213  // Create the S57 Object
3214  S57Obj *pobj = new S57Obj();
3215 
3216  pobj->Index = iobject;
3217 
3218  char u[201];
3219  strncpy(u, sclass_sub.mb_str(), 199);
3220  u[200] = '\0';
3221  memcpy(pobj->FeatureName, u, 7);
3222 
3223  pobj->attVal = new wxArrayOfS57attVal();
3224 
3225  cm93_attr_block pab(pobject->attributes_block, pDict);
3226 
3227  for (int jattr = 0; jattr < pobject->n_attributes; jattr++) {
3228  unsigned char *curr_attr = pab.GetNextAttr();
3229 
3230  unsigned char iattr = *curr_attr;
3231 
3232  wxString sattr = pDict->GetAttrName(iattr);
3233 
3234  char vtype = pDict->GetAttrType(iattr);
3235 
3236  unsigned char *aval = curr_attr + 1;
3237 
3238  char val[4000];
3239  int *pi;
3240  float *pf;
3241  unsigned short *pw;
3242  unsigned char *pb;
3243  int *pAVI;
3244  char *pAVS;
3245  double *pAVR;
3246  double dival;
3247  int ival;
3248 
3249  S57attVal *pattValTmp = new S57attVal;
3250 
3251  switch (vtype) {
3252  case 'I': // never seen?
3253  pi = (int *)aval;
3254  pAVI = (int *)malloc(sizeof(int)); // new int;
3255  *pAVI = *pi;
3256  pattValTmp->valType = OGR_INT;
3257  pattValTmp->value = pAVI;
3258  break;
3259  case 'B':
3260  pb = (unsigned char *)aval;
3261  pAVI = (int *)malloc(sizeof(int)); // new int;
3262  *pAVI = (int)(*pb);
3263  pattValTmp->valType = OGR_INT;
3264  pattValTmp->value = pAVI;
3265  break;
3266  case 'W': // aWORD10
3267  pw = (unsigned short *)aval;
3268  ival = (int)(*pw);
3269  dival = ival;
3270 
3271  pAVR = (double *)malloc(sizeof(double)); // new double;
3272  *pAVR = dival / 10.;
3273  pattValTmp->valType = OGR_REAL;
3274  pattValTmp->value = pAVR;
3275  break;
3276  case 'G':
3277  pi = (int *)aval;
3278  pAVI = (int *)malloc(sizeof(int)); // new int;
3279  *pAVI = (int)(*pi);
3280  pattValTmp->valType = OGR_INT;
3281  pattValTmp->value = pAVI;
3282  break;
3283 
3284  case 'S':
3285  pAVS = strdup((char*)aval);
3286  pattValTmp->valType = OGR_STR;
3287  pattValTmp->value = pAVS;
3288  break;
3289 
3290  case 'C':
3291  pAVS = strdup((const char*)&aval[3]);
3292  pattValTmp->valType = OGR_STR;
3293  pattValTmp->value = pAVS;
3294  break;
3295  case 'L': {
3296  pb = (unsigned char *)aval;
3297  unsigned char nl = *pb++;
3298  char vi[20];
3299  val[0] = 0;
3300  for (int i = 0; i < nl; i++) {
3301  sprintf(vi, "%d,", *pb++);
3302  strcat(val, vi);
3303  }
3304  if (strlen(val)) val[strlen(val) - 1] = 0; // strip last ","
3305 
3306  pAVS = strdup(val);
3307  pattValTmp->valType = OGR_STR;
3308  pattValTmp->value = pAVS;
3309  break;
3310  }
3311  case 'R': {
3312  pAVR = (double *)malloc(sizeof(double)); // new double;
3313  pf = (float *)aval;
3314 #ifdef __ARM_ARCH
3315  {
3316  float __attribute__((aligned(16))) tf1;
3317  unsigned char *pucf = (unsigned char *)pf;
3318 
3319  memcpy(&tf1, pucf, sizeof(float));
3320  *pAVR = tf1;
3321  }
3322 #else
3323  *pAVR = *pf;
3324 #endif
3325  pattValTmp->valType = OGR_REAL;
3326  pattValTmp->value = pAVR;
3327  break;
3328  }
3329  default:
3330  sattr.Clear(); // Unknown, TODO track occasional case '?'
3331  break;
3332  } // switch
3333 
3334  if (sattr.IsSameAs(_T ( "COLMAR" ))) {
3335  translate_colmar(sclass, pattValTmp);
3336  sattr = _T ( "COLOUR" );
3337  }
3338  // XXX should be done from s57 list ans cm93 list for any mismatch
3339  // ie cm93 QUASOU is an enum s57 is a list
3340  if (pattValTmp->valType == OGR_INT &&
3341  (sattr.IsSameAs(_T ( "QUASOU" )) || sattr.IsSameAs(_T ( "CATLIT" )))) {
3342  int v = *(int *)pattValTmp->value;
3343  free(pattValTmp->value);
3344  sprintf(val, "%d", v);
3345  pAVS = strdup(val);
3346  pattValTmp->valType = OGR_STR;
3347  pattValTmp->value = pAVS;
3348  }
3349 
3350  // Do CM93 $SCODE attribute substitutions
3351  if (sclass.IsSameAs(_T ( "$AREAS" )) && (vtype == 'S') &&
3352  sattr.IsSameAs(_T ( "$SCODE" ))) {
3353  if (!strcmp((char *)pattValTmp->value, "II25")) {
3354  free(pattValTmp->value);
3355  pattValTmp->value = strdup("BACKGROUND");
3356  }
3357  }
3358 
3359  // Capture some attributes on the fly as needed
3360  if (sattr.IsSameAs(_T ( "RECDAT" )) || sattr.IsSameAs(_T ( "_dgdat" ))) {
3361  if (sclass_sub.IsSameAs(_T ( "M_COVR" )) && (vtype == 'S')) {
3362  wxString pub_date((char *)pattValTmp->value, wxConvUTF8);
3363 
3364  wxDateTime upd;
3365  upd.ParseFormat(pub_date, _T ( "%Y%m%d" ));
3366  if (!upd.IsValid()) upd.ParseFormat(_T ( "20000101" ), _T ( "%Y%m%d" ));
3367  m_EdDate = upd;
3368 
3369  pub_date.Truncate(4);
3370 
3371  long nyear = 0;
3372  pub_date.ToLong(&nyear);
3373  npub_year = nyear;
3374  }
3375  }
3376 
3377  // Capture the potential WGS84 transform offset for later use
3378  if (sclass_sub.IsSameAs(_T ( "M_COVR" )) && (vtype == 'R')) {
3379  if (sattr.IsSameAs(_T ( "_wgsox" ))) {
3380  tmp_transform_x = *(double *)pattValTmp->value;
3381  if (fabs(tmp_transform_x) > 1.0) // metres
3382  m_CIB.b_have_offsets = true;
3383  } else if (sattr.IsSameAs(_T ( "_wgsoy" ))) {
3384  tmp_transform_y = *(double *)pattValTmp->value;
3385  if (fabs(tmp_transform_y) > 1.0) m_CIB.b_have_offsets = true;
3386  }
3387  }
3388 
3389  if (sattr.Len()) {
3390  wxASSERT(sattr.Len() == 6);
3391  wxCharBuffer dbuffer = sattr.ToUTF8();
3392  if (dbuffer.data()) {
3393  pobj->att_array =
3394  (char *)realloc(pobj->att_array, 6 * (pobj->n_attr + 1));
3395 
3396  strncpy(pobj->att_array + (6 * sizeof(char) * pobj->n_attr),
3397  dbuffer.data(), 6);
3398  pobj->n_attr++;
3399 
3400  pobj->attVal->Add(pattValTmp);
3401  } else
3402  delete pattValTmp;
3403  } else
3404  delete pattValTmp;
3405 
3406  } // for
3407 
3408  // ATON label optimization:
3409  // Some CM93 ATON objects do not contain OBJNAM attribute, which means that
3410  // no label is shown for these objects when ATON labals are requested Look
3411  // for these cases, and change the INFORM attribute label to OBJNAM, if
3412  // present.
3413 
3414  if (1 == geomtype) {
3415  if ((!strncmp(pobj->FeatureName, "LIT", 3)) ||
3416  (!strncmp(pobj->FeatureName, "LIGHTS", 6)) ||
3417  (!strncmp(pobj->FeatureName, "BCN", 3)) ||
3418  (!strncmp(pobj->FeatureName, "_slgto", 6)) ||
3419  (!strncmp(pobj->FeatureName, "_boygn", 6)) ||
3420  (!strncmp(pobj->FeatureName, "_bcngn", 6)) ||
3421  (!strncmp(pobj->FeatureName, "_extgn", 6)) ||
3422  (!strncmp(pobj->FeatureName, "TOWERS", 6)) ||
3423  (!strncmp(pobj->FeatureName, "BOY", 3))) {
3424  bool bfound_OBJNAM = (pobj->GetAttributeIndex("OBJNAM") != -1);
3425  bool bfound_INFORM = (pobj->GetAttributeIndex("INFORM") != -1);
3426 
3427  if ((!bfound_OBJNAM) && (bfound_INFORM)) // can make substitution
3428  {
3429  char *patl = pobj->att_array;
3430  for (int i = 0; i < pobj->n_attr; i++) { // find "INFORM"
3431  if (!strncmp(patl, "INFORM", 6)) {
3432  memcpy(patl, "OBJNAM", 6); // change to "OBJNAM"
3433  break;
3434  }
3435 
3436  patl += 6;
3437  }
3438  }
3439  }
3440  }
3441 
3442  switch (geomtype) {
3443  case 4: {
3444  pobj->Primitive_type = GEO_AREA;
3445 
3446  // Check for and maintain the class array of M_COVR objects
3447  if (sclass_sub.IsSameAs(_T ( "M_COVR" ))) {
3448  M_COVR_Desc *pmcd;
3449 
3450  M_COVR_Desc *pmcd_look =
3451  GetCoverSet()->Find_MCD(cell_index, iobject, subcell);
3452  if (NULL == pmcd_look) // not found
3453  {
3454  double lat, lon;
3455 
3456  pmcd = new M_COVR_Desc;
3457 
3458  // Record unique identifiers for this M_COVR object
3459  pmcd->m_cell_index = cell_index;
3460  pmcd->m_object_id = iobject;
3461  pmcd->m_subcell = subcell;
3462 
3463  // User offsets start empty
3464  pmcd->user_xoff = 0;
3465  pmcd->user_yoff = 0;
3466  pmcd->m_buser_offsets = false;
3467 
3468  // Record the Publication Year of this cell
3469  pmcd->m_npub_year = npub_year;
3470 
3471  // Get number of exterior ring points(vertices)
3472  int npta = xgeom->contour_array[0];
3473  float_2Dpt *geoPt = new float_2Dpt[npta + 2]; // vertex array
3474  float_2Dpt *ppt = geoPt;
3475 
3476  pmcd->m_covr_lon_max = -1000.;
3477  pmcd->m_covr_lon_min = 1000.;
3478  pmcd->m_covr_lat_max = -1000.;
3479  pmcd->m_covr_lat_min = 1000.;
3480 
3481  // Transcribe exterior ring points to vertex array, in Lat/Lon
3482  // coordinates
3483  for (int ip = 0; ip < npta; ip++) {
3484  cm93_point p;
3485  p.x = (int)xgeom->vertex_array[ip + 1].m_x;
3486  p.y = (int)xgeom->vertex_array[ip + 1].m_y;
3487 
3488  Transform(&p, 0, 0, /*tmp_transform_x, tmp_transform_y,*/ &lat,
3489  &lon);
3490  ppt->x = lon;
3491  ppt->y = lat;
3492 
3493  pmcd->m_covr_lon_max = wxMax(pmcd->m_covr_lon_max, lon);
3494  pmcd->m_covr_lon_min = wxMin(pmcd->m_covr_lon_min, lon);
3495  pmcd->m_covr_lat_max = wxMax(pmcd->m_covr_lat_max, lat);
3496  pmcd->m_covr_lat_min = wxMin(pmcd->m_covr_lat_min, lat);
3497 
3498  ppt++;
3499  }
3500  pmcd->m_nvertices = npta;
3501  pmcd->pvertices = geoPt;
3502 
3503  pmcd->m_covr_bbox.Set(pmcd->m_covr_lat_min, pmcd->m_covr_lon_min,
3504  pmcd->m_covr_lat_max, pmcd->m_covr_lon_max);
3505 
3506  // Capture and store the potential WGS transform offsets grabbed
3507  // during attribute decode
3508  pmcd->transform_WGS84_offset_x = tmp_transform_x;
3509  pmcd->transform_WGS84_offset_y = tmp_transform_y;
3510 
3511  pmcd->m_centerlat_cos = cos(
3512  ((pmcd->m_covr_lat_min + pmcd->m_covr_lat_max) / 2.) * PI / 180.);
3513 
3514  // Add this MCD to the persistent class covr_set
3515  GetCoverSet()->Add_Update_MCD(pmcd);
3516 
3517  } else {
3518  // If already in the coverset, are there user offsets applied to this
3519  // MCD?
3520  if (pmcd_look->m_buser_offsets) {
3521  m_CIB.b_have_user_offsets = true;
3522 
3523  m_CIB.user_xoff = pmcd_look->user_xoff;
3524  m_CIB.user_yoff = pmcd_look->user_yoff;
3525  }
3526 
3527  pmcd = pmcd_look;
3528  }
3529 
3530  // Add this geometry to the currently loaded class M_COVR array
3531  m_pcovr_array_loaded.Add(pmcd);
3532 
3533  // Update the covr region
3534  unsigned int n = pmcd->m_nvertices;
3535  double *pts = new double[2 * n];
3536 
3537  // copy into array of doubles
3538  for (size_t i = 0; i < 2 * n; i++)
3539  pts[i] = ((float *)pmcd->pvertices)[i];
3540 
3541  // normalize to 0-360 coords for areas that cross 180 (will be adjusted
3542  // in LLRegion)
3543  if (LLRegion::PointsCCW(n, pts))
3544  for (size_t i = 0; i < n; i++)
3545  if (pts[2 * i + 1] < 0) pts[2 * i + 1] += 360;
3546 
3547  // perform region union logic
3548  LLRegion rgn_covr(n, pts);
3549  m_region.Union(rgn_covr);
3550  delete[] pts;
3551 
3552  // Add the MCD it to the current (temporary) per cell list
3553  // This array is used only to quickly find the M_COVR object
3554  // parameters which apply to other objects loaded from this cell. We
3555  // do this so we don't have to search the entire (worldwide) coverset
3556  // for this chart scale
3557  m_CIB.m_cell_mcovr_list.Append(pmcd);
3558  }
3559 
3560  // Declare x/y of the object to be average of all cm93points
3561  pobj->x = (xgeom->xmin + xgeom->xmax) / 2.;
3562  pobj->y = (xgeom->ymin + xgeom->ymax) / 2.;
3563 
3564  // associate the vector(edge) index table
3565  pobj->m_n_lsindex = xgeom->n_vector_indices;
3566  pobj->m_lsindex_array =
3567  xgeom->pvector_index; // object now owns the array
3568  pobj->m_n_edge_max_points = 0; // xgeom->n_max_edge_points;
3569 
3570  // Find the proper WGS offset for this object
3571  if (m_CIB.b_have_offsets || m_CIB.b_have_user_offsets) {
3572  double latc, lonc;
3573  cm93_point pc;
3574  pc.x = (short unsigned int)pobj->x;
3575  pc.y = (short unsigned int)pobj->y;
3576  Transform(&pc, 0., 0., &latc, &lonc);
3577 
3578  M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(latc, lonc);
3579  if (pmcd) {
3580  trans_WGS84_offset_x = pmcd->user_xoff;
3581  trans_WGS84_offset_y = pmcd->user_yoff;
3582  }
3583  }
3584 
3585  // Set the s57obj bounding box as lat/lon
3586  double lat1, lon1, lat2, lon2;
3587  cm93_point p;
3588 
3589  p.x = (int)xgeom->xmin;
3590  p.y = (int)xgeom->ymin;
3591  Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
3592  xgeom->ref_lat = lat1;
3593  xgeom->ref_lon = lon1;
3594 
3595  p.x = (int)xgeom->xmax;
3596  p.y = (int)xgeom->ymax;
3597  Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat2, &lon2);
3598  pobj->BBObj.Set(lat1, lon1, lat2, lon2);
3599 
3600  // Set the object base point
3601  p.x = (int)pobj->x;
3602  p.y = (int)pobj->y;
3603  Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
3604  pobj->m_lon = lon1;
3605  pobj->m_lat = lat1;
3606 
3607  if (1) {
3608  // This will be a deferred tesselation.....
3609 
3610  // Set up the conversion factors for use in the tesselator
3611  xgeom->x_rate = m_CIB.transform_x_rate;
3612  xgeom->x_offset = m_CIB.transform_x_origin - trans_WGS84_offset_x;
3613  xgeom->y_rate = m_CIB.transform_y_rate;
3614  xgeom->y_offset = m_CIB.transform_y_origin - trans_WGS84_offset_y;
3615 
3616  pobj->pPolyTessGeo = new PolyTessGeo(xgeom);
3617  }
3618 
3619  break;
3620  }
3621 
3622  case 1: {
3623  pobj->Primitive_type = GEO_POINT;
3624  pobj->npt = 1;
3625 
3626  pobj->x = xgeom->pointx;
3627  pobj->y = xgeom->pointy;
3628 
3629  double lat, lon;
3630  cm93_point p;
3631  p.x = xgeom->pointx;
3632  p.y = xgeom->pointy;
3633  Transform(&p, 0., 0., &lat, &lon);
3634 
3635  // Find the proper WGS offset for this object
3636  if (m_CIB.b_have_offsets || m_CIB.b_have_user_offsets) {
3637  M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(lat, lon);
3638  if (pmcd) {
3639  trans_WGS84_offset_x = pmcd->user_xoff;
3640  trans_WGS84_offset_y = pmcd->user_yoff;
3641  }
3642  }
3643 
3644  // Transform again to pick up offsets
3645  Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat, &lon);
3646 
3647  pobj->m_lat = lat;
3648  pobj->m_lon = lon;
3649 
3650  // make initial bounding box large enough for worst possible case
3651  // it's not possible to know unless we knew the font, but this works
3652  // except for huge font sizes
3653  // this is not very good or accurate or efficient and hopefully we can
3654  // replace the current bounding box logic with calculating logic
3655  double llsize = 1e-3 / view_scale_ppm;
3656 
3657  pobj->BBObj.Set(lat, lon, lat, lon);
3658  pobj->BBObj.EnLarge(llsize);
3659 
3660  break;
3661  }
3662 
3663  case 8: // wkbMultiPoint25D:
3664  {
3665  pobj->Primitive_type = GEO_POINT;
3666 
3667  // Set the s57obj bounding box as lat/lon
3668  double lat1, lon1, lat2, lon2;
3669  cm93_point p;
3670 
3671  p.x = (int)xgeom->xmin;
3672  p.y = (int)xgeom->ymin;
3673  Transform(&p, 0., 0., &lat1, &lon1);
3674 
3675  p.x = (int)xgeom->xmax;
3676  p.y = (int)xgeom->ymax;
3677  Transform(&p, 0., 0., &lat2, &lon2);
3678  pobj->BBObj.Set(lat1, lon1, lat2, lon2);
3679 
3680  // and declare x/y of the object to be average of all cm93points
3681  pobj->x = (xgeom->xmin + xgeom->xmax) / 2.;
3682  pobj->y = (xgeom->ymin + xgeom->ymax) / 2.;
3683 
3684  OGRMultiPoint *pGeo = (OGRMultiPoint *)xgeom->pogrGeom;
3685  pobj->npt = pGeo->getNumGeometries();
3686 
3687  pobj->geoPtz = (double *)malloc(pobj->npt * 3 * sizeof(double));
3688  pobj->geoPtMulti = (double *)malloc(pobj->npt * 2 * sizeof(double));
3689 
3690  double *pdd = pobj->geoPtz;
3691  double *pdl = pobj->geoPtMulti;
3692 
3693  for (int ip = 0; ip < pobj->npt; ip++) {
3694  OGRPoint *ppt = (OGRPoint *)(pGeo->getGeometryRef(ip));
3695 
3696  cm93_point p;
3697  p.x = (int)ppt->getX();
3698  p.y = (int)ppt->getY();
3699  double depth = ppt->getZ();
3700 
3701  double east = p.x;
3702  double north = p.y;
3703 
3704  double snd_trans_x = 0.;
3705  double snd_trans_y = 0.;
3706 
3707  // Find the proper offset for this individual sounding
3708  if (m_CIB.b_have_user_offsets) {
3709  double lats, lons;
3710  Transform(&p, 0., 0., &lats, &lons);
3711 
3712  M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(lats, lons);
3713  if (pmcd) {
3714  // For lat/lon calculation below
3715  snd_trans_x = pmcd->user_xoff;
3716  snd_trans_y = pmcd->user_yoff;
3717 
3718  // Actual cm93 point of this sounding, back-converted from metres
3719  // e/n
3720  east -= pmcd->user_xoff / m_CIB.transform_x_rate;
3721  north -= pmcd->user_yoff / m_CIB.transform_y_rate;
3722  }
3723  }
3724 
3725  *pdd++ = east;
3726  *pdd++ = north;
3727  *pdd++ = depth;
3728 
3729  // Save offset lat/lon of point in obj->geoPtMulti for later use in
3730  // decomposed bboxes
3731  Transform(&p, snd_trans_x, snd_trans_y, &lat1, &lon1);
3732  *pdl++ = lon1;
3733  *pdl++ = lat1;
3734  }
3735 
3736  // Set the object base point
3737  p.x = (int)pobj->x;
3738  p.y = (int)pobj->y;
3739  Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
3740  pobj->m_lon = lon1;
3741  pobj->m_lat = lat1;
3742 
3743  delete pGeo;
3744 
3745  break;
3746  } // case 8
3747 
3748  case 2: {
3749  pobj->Primitive_type = GEO_LINE;
3750 
3751  pobj->npt = xgeom->n_max_vertex;
3752  pobj->geoPt = (pt *)xgeom->vertex_array;
3753  xgeom->vertex_array = NULL; // object now owns the array
3754 
3755  // Declare x/y of the object to be average of all cm93points
3756  pobj->x = (xgeom->xmin + xgeom->xmax) / 2.;
3757  pobj->y = (xgeom->ymin + xgeom->ymax) / 2.;
3758 
3759  // associate the vector(edge) index table
3760  pobj->m_n_lsindex = xgeom->n_vector_indices;
3761  pobj->m_lsindex_array =
3762  xgeom->pvector_index; // object now owns the array
3763  pobj->m_n_edge_max_points = 0; // xgeom->n_max_edge_points;
3764 
3765  // Find the proper WGS offset for this object
3766  if (m_CIB.b_have_offsets || m_CIB.b_have_user_offsets) {
3767  double latc, lonc;
3768  cm93_point pc;
3769  pc.x = (short unsigned int)pobj->x;
3770  pc.y = (short unsigned int)pobj->y;
3771  Transform(&pc, 0., 0., &latc, &lonc);
3772 
3773  M_COVR_Desc *pmcd = FindM_COVR_InWorkingSet(latc, lonc);
3774  if (pmcd) {
3775  trans_WGS84_offset_x = pmcd->user_xoff;
3776  trans_WGS84_offset_y = pmcd->user_yoff;
3777  }
3778  }
3779 
3780  // Set the s57obj bounding box as lat/lon
3781  double lat1, lon1, lat2, lon2;
3782  cm93_point p;
3783 
3784  p.x = (int)xgeom->xmin;
3785  p.y = (int)xgeom->ymin;
3786  Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
3787 
3788  p.x = (int)xgeom->xmax;
3789  p.y = (int)xgeom->ymax;
3790  Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat2, &lon2);
3791  pobj->BBObj.Set(lat1, lon1, lat2, lon2);
3792 
3793  // Set the object base point
3794  p.x = (int)pobj->x;
3795  p.y = (int)pobj->y;
3796  Transform(&p, trans_WGS84_offset_x, trans_WGS84_offset_y, &lat1, &lon1);
3797  pobj->m_lon = lon1;
3798  pobj->m_lat = lat1;
3799 
3800  break;
3801 
3802  } // case 2
3803  default: {
3804  // TODO GEO_PRIM here is a placeholder. Trace this code....
3805  pobj->Primitive_type = GEO_PRIM;
3806  break;
3807  }
3808 
3809  } // geomtype switch
3810 
3811  // Is this a catagory-movable object?
3812  if (!strncmp(pobj->FeatureName, "OBSTRN", 6) ||
3813  !strncmp(pobj->FeatureName, "WRECKS", 6) ||
3814  !strncmp(pobj->FeatureName, "DEPCNT", 6) ||
3815  !strncmp(pobj->FeatureName, "UWTROC", 6)) {
3816  pobj->m_bcategory_mutable = true;
3817  } else {
3818  pobj->m_bcategory_mutable = false;
3819  }
3820 
3821  // Build/Maintain a list of found OBJL types for later use
3822  // And back-reference the appropriate list index in S57Obj for Display
3823  // Filtering
3824 
3825  pobj->iOBJL = -1; // deferred, done by OBJL filtering in the PLIB as needed
3826 
3827  // Everything in Xgeom that is needed later has been given to the object
3828  // So, the xgeom object can be deleted
3829  // Except for area features, which will get deferred tesselation, and so need
3830  // the Extended geometry point Those features will own the xgeom...
3831  if (geomtype != 4) delete xgeom;
3832 
3833  // Set the per-object transform coefficients
3834  pobj->x_rate =
3835  m_CIB.transform_x_rate *
3836  (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters);
3837  pobj->y_rate =
3838  m_CIB.transform_y_rate *
3839  (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters);
3840  pobj->x_origin =
3841  m_CIB.transform_x_origin *
3842  (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters);
3843  pobj->y_origin =
3844  m_CIB.transform_y_origin *
3845  (mercator_k0 * WGS84_semimajor_axis_meters / CM93_semimajor_axis_meters);
3846 
3847  // Add in the possible offsets to WGS84 which come from the proper M_COVR
3848  // containing this feature
3849  pobj->x_origin -= trans_WGS84_offset_x;
3850  pobj->y_origin -= trans_WGS84_offset_y;
3851 
3852  // Mark the object chart type, for the convenience of S52PLIB
3853  pobj->auxParm3 = CHART_TYPE_CM93;
3854 
3855  return pobj;
3856 }
3857 
3858 // Find the proper M_COVR record within this current cell for this lat/lon
3859 M_COVR_Desc *cm93chart::FindM_COVR_InWorkingSet(double lat, double lon) {
3860  M_COVR_Desc *ret = NULL;
3861  // Default is to use the first M_COVR, the usual case
3862  if (m_CIB.m_cell_mcovr_list.GetCount() == 1) {
3863  wxList_Of_M_COVR_DescNode *node0 = m_CIB.m_cell_mcovr_list.GetFirst();
3864  if (node0) ret = node0->GetData();
3865  } else {
3866  wxList_Of_M_COVR_DescNode *node = m_CIB.m_cell_mcovr_list.GetFirst();
3867  while (node) {
3868  M_COVR_Desc *pmcd = node->GetData();
3869 
3870  if (G_PtInPolygon_FL(pmcd->pvertices, pmcd->m_nvertices, lon, lat)) {
3871  ret = pmcd;
3872  break;
3873  }
3874 
3875  node = node->GetNext();
3876  }
3877  }
3878  return ret;
3879 }
3880 
3881 // Find the proper M_COVR record within this current cell for this lat/lon
3882 // And return the WGS84 offsets contained within
3883 wxPoint2DDouble cm93chart::FindM_COVROffset(double lat, double lon) {
3884  wxPoint2DDouble ret(0., 0.);
3885 
3886  // Default is to use the first M_COVR, the usual case
3887  wxList_Of_M_COVR_DescNode *node0 = m_CIB.m_cell_mcovr_list.GetFirst();
3888  if (node0) {
3889  M_COVR_Desc *pmcd0 = node0->GetData();
3890  ret.m_x = pmcd0->transform_WGS84_offset_x;
3891  ret.m_y = pmcd0->transform_WGS84_offset_y;
3892  }
3893 
3894  // If there are more than one M_COVR in this cell, need to search
3895  if (m_CIB.m_cell_mcovr_list.GetCount() > 1) {
3896  wxList_Of_M_COVR_DescNode *node = m_CIB.m_cell_mcovr_list.GetFirst();
3897  while (node) {
3898  M_COVR_Desc *pmcd = node->GetData();
3899 
3900  if (G_PtInPolygon_FL(pmcd->pvertices, pmcd->m_nvertices, lon, lat)) {
3901  ret.m_x = pmcd->transform_WGS84_offset_x;
3902  ret.m_y = pmcd->transform_WGS84_offset_y;
3903  break;
3904  }
3905 
3906  node = node->GetNext();
3907  }
3908  }
3909  return ret;
3910 }
3911 
3912 // Read the cm93 cell file header and create required Chartbase data
3913 // structures
3914 InitReturn cm93chart::CreateHeaderDataFromCM93Cell(void) {
3915  // Figure out the scale from the file name
3916  wxFileName fn(m_FullPath);
3917  wxString ext = fn.GetExt();
3918 
3919  int scale;
3920  switch ((ext.mb_str())[(size_t)0]) {
3921  case 'Z':
3922  scale = 20000000;
3923  break;
3924  case 'A':
3925  scale = 3000000;
3926  break;
3927  case 'B':
3928  scale = 1000000;
3929  break;
3930  case 'C':
3931  scale = 200000;
3932  break;
3933  case 'D':
3934  scale = 100000;
3935  break;
3936  case 'E':
3937  scale = 50000;
3938  break;
3939  case 'F':
3940  scale = 20000;
3941  break;
3942  case 'G':
3943  scale = 7500;
3944  break;
3945  default:
3946  scale = 20000000;
3947  break;
3948  }
3949 
3950  m_Chart_Scale = scale;
3951 
3952  // Check with the manager to see if a chart of this scale has been
3953  // processed If there is no manager, punt and open the chart
3954  if (m_pManager) {
3955  bool bproc = false;
3956  switch (m_Chart_Scale) {
3957  case 20000000:
3958  bproc = m_pManager->m_bfoundZ;
3959  break;
3960  case 3000000:
3961  bproc = m_pManager->m_bfoundA;
3962  break;
3963  case 1000000:
3964  bproc = m_pManager->m_bfoundB;
3965  break;
3966  case 200000:
3967  bproc = m_pManager->m_bfoundC;
3968  break;
3969  case 100000:
3970  bproc = m_pManager->m_bfoundD;
3971  break;
3972  case 50000:
3973  bproc = m_pManager->m_bfoundE;
3974  break;
3975  case 20000:
3976  bproc = m_pManager->m_bfoundF;
3977  break;
3978  case 7500:
3979  bproc = m_pManager->m_bfoundG;
3980  break;
3981  }
3982 
3983  if (bproc) return INIT_FAIL_NOERROR;
3984 
3985  // Inform the manager that a chart of this scale has been processed
3986  switch (m_Chart_Scale) {
3987  case 20000000:
3988  m_pManager->m_bfoundZ = true;
3989  break;
3990  case 3000000:
3991  m_pManager->m_bfoundA = true;
3992  break;
3993  case 1000000:
3994  m_pManager->m_bfoundB = true;
3995  break;
3996  case 200000:
3997  m_pManager->m_bfoundC = true;
3998  break;
3999  case 100000:
4000  m_pManager->m_bfoundD = true;
4001  break;
4002  case 50000:
4003  m_pManager->m_bfoundE = true;
4004  break;
4005  case 20000:
4006  m_pManager->m_bfoundF = true;
4007  break;
4008  case 7500:
4009  m_pManager->m_bfoundG = true;
4010  break;
4011  }
4012  }
4013 
4014  // Specify the whole world as chart coverage
4015  m_FullExtent.ELON = 179.0;
4016  m_FullExtent.WLON = -179.0;
4017  m_FullExtent.NLAT = 80.0;
4018  m_FullExtent.SLAT = -80.0;
4019  m_bExtentSet = true;
4020 
4021  // Populate one (huge) M_COVR Entry
4022  m_nCOVREntries = 1;
4023  m_pCOVRTablePoints = (int *)malloc(sizeof(int));
4024  *m_pCOVRTablePoints = 4;
4025  m_pCOVRTable = (float **)malloc(sizeof(float *));
4026  float *pf = (float *)malloc(2 * 4 * sizeof(float));
4027  *m_pCOVRTable = pf;
4028  float *pfe = pf;
4029 
4030  *pfe++ = m_FullExtent.NLAT; // LatMax;
4031  *pfe++ = m_FullExtent.WLON; // LonMin;
4032 
4033  *pfe++ = m_FullExtent.NLAT; // LatMax;
4034  *pfe++ = m_FullExtent.ELON; // LonMax;
4035 
4036  *pfe++ = m_FullExtent.SLAT; // LatMin;
4037  *pfe++ = m_FullExtent.ELON; // LonMax;
4038 
4039  *pfe++ = m_FullExtent.SLAT; // LatMin;
4040  *pfe++ = m_FullExtent.WLON; // LonMin;
4041 
4042  return INIT_OK;
4043 }
4044 
4045 void cm93chart::ProcessMCOVRObjects(int cell_index, char subcell) {
4046  // Extract the m_covr structures inline
4047 
4048  Object *pobject = m_CIB.pobject_block; // head of object array
4049 
4050  int iObj = 0;
4051  while (iObj < m_CIB.m_nfeature_records) {
4052  if ((pobject != NULL)) {
4053  // Look for and process m_covr object(s)
4054  int iclass = pobject->otype;
4055 
4056  wxString sclass = m_pDict->GetClassName(iclass);
4057 
4058  if (sclass.IsSameAs(_T ( "_m_sor" ))) {
4059  M_COVR_Desc *pmcd =
4060  m_pcovr_set->Find_MCD(cell_index, iObj, (int)subcell);
4061  if (NULL == pmcd) {
4062  Extended_Geometry *xgeom = BuildGeom(pobject, NULL, iObj);
4063 
4064  // Decode the attributes, specifically looking for _wgsox, _wgsoy
4065 
4066  double tmp_transform_x = 0.;
4067  double tmp_transform_y = 0.;
4068 
4069  cm93_attr_block pab(pobject->attributes_block, m_pDict);
4070  for (int jattr = 0; jattr < pobject->n_attributes; jattr++) {
4071  unsigned char *curr_attr = pab.GetNextAttr();
4072  unsigned char iattr = *curr_attr;
4073  wxString sattr = m_pDict->GetAttrName(iattr);
4074  char vtype = m_pDict->GetAttrType(iattr);
4075  unsigned char *aval = curr_attr + 1;
4076 
4077  if (vtype == 'R') {
4078  float *pf = (float *)aval;
4079 #ifdef __ARM_ARCH
4080  float __attribute__((aligned(16))) tf1;
4081  unsigned char *pucf = (unsigned char *)pf;
4082  memcpy(&tf1, pucf, sizeof(float));
4083  if (sattr.IsSameAs(_T ( "_wgsox" )))
4084  tmp_transform_x = tf1;
4085  else if (sattr.IsSameAs(_T ( "_wgsoy" )))
4086  tmp_transform_y = tf1;
4087 #else
4088  if (sattr.IsSameAs(_T ( "_wgsox" )))
4089  tmp_transform_x = *pf;
4090  else if (sattr.IsSameAs(_T ( "_wgsoy" )))
4091  tmp_transform_y = *pf;
4092 #endif
4093  }
4094 
4095  } // for all attributes
4096 
4097  if (NULL != xgeom) {
4098  double lat, lon;
4099 
4100  pmcd = new M_COVR_Desc;
4101 
4102  // Record unique identifiers for this M_COVR object
4103  pmcd->m_cell_index = cell_index;
4104  pmcd->m_object_id = iObj;
4105  pmcd->m_subcell = (int)subcell;
4106 
4107  // Get number of exterior ring points(vertices)
4108  int npta = xgeom->contour_array[0];
4109  float_2Dpt *geoPt = new float_2Dpt[npta + 2]; // vertex array
4110  float_2Dpt *ppt = geoPt;
4111 
4112  // Transcribe exterior ring points to vertex array, in Lat/Lon
4113  // coordinates
4114  pmcd->m_covr_lon_max = -1000.;
4115  pmcd->m_covr_lon_min = 1000.;
4116  pmcd->m_covr_lat_max = -1000.;
4117  pmcd->m_covr_lat_min = 1000.;
4118 
4119  for (int ip = 0; ip < npta; ip++) {
4120  cm93_point p;
4121  p.x = (int)xgeom->vertex_array[ip + 1].m_x;
4122  p.y = (int)xgeom->vertex_array[ip + 1].m_y;
4123 
4124  Transform(&p, 0., 0., &lat, &lon);
4125  ppt->x = lon;
4126  ppt->y = lat;
4127 
4128  pmcd->m_covr_lon_max = wxMax(pmcd->m_covr_lon_max, lon);
4129  pmcd->m_covr_lon_min = wxMin(pmcd->m_covr_lon_min, lon);
4130  pmcd->m_covr_lat_max = wxMax(pmcd->m_covr_lat_max, lat);
4131  pmcd->m_covr_lat_min = wxMin(pmcd->m_covr_lat_min, lat);
4132 
4133  ppt++;
4134  }
4135  pmcd->m_nvertices = npta;
4136  pmcd->pvertices = geoPt;
4137 
4138  pmcd->m_covr_bbox.Set(pmcd->m_covr_lat_min, pmcd->m_covr_lon_min,
4139  pmcd->m_covr_lat_max, pmcd->m_covr_lon_max);
4140 
4141  // Capture and store the potential WGS transform offsets grabbed
4142  // during attribute decode
4143  pmcd->transform_WGS84_offset_x = tmp_transform_x;
4144  pmcd->transform_WGS84_offset_y = tmp_transform_y;
4145 
4146  pmcd->m_centerlat_cos =
4147  cos(((pmcd->m_covr_lat_min + pmcd->m_covr_lat_max) / 2.) * PI /
4148  180.);
4149 
4150  // Add this object to the covr_set
4151  m_pcovr_set->Add_Update_MCD(pmcd);
4152 
4153  // Clean up the xgeom
4154  free(xgeom->pvector_index);
4155 
4156  delete xgeom;
4157  }
4158  }
4159  }
4160  }
4161 
4162  else // objectdef == NULL
4163  break;
4164 
4165  pobject++;
4166  iObj++;
4167  }
4168 }
4169 
4170 bool cm93chart::UpdateCovrSet(ViewPort *vpt) {
4171  // Create an array of CellIndexes covering the current viewport
4172  std::vector<int> vpcells = GetVPCellArray(*vpt);
4173 
4174  // Check the member covr_set to see if all these viewport cells have had
4175  // their m_covr loaded
4176 
4177  for (unsigned int i = 0; i < vpcells.size(); i++) {
4178  // If the cell is not already in the master coverset, go load enough of
4179  // it to get the offsets and outlines.....
4180  if (!m_pcovr_set->IsCovrLoaded(vpcells[i])) {
4181  if (loadcell_in_sequence(vpcells[i], '0')) {
4182  ProcessMCOVRObjects(vpcells[i], '0');
4183  Unload_CM93_Cell(); // all done with this (sub)cell
4184  }
4185  m_pcovr_set->m_cell_hash[vpcells[i]] = 1;
4186 
4187  char loadcell_key = 'A'; // starting subcells
4188 
4189  // Load the subcells in sequence
4190  // On successful load, add it to the covr set and process the cell
4191  while (loadcell_in_sequence(vpcells[i], loadcell_key)) {
4192  // Extract the m_covr structures inline
4193 
4194  ProcessMCOVRObjects(vpcells[i], loadcell_key);
4195 
4196  Unload_CM93_Cell(); // all done with this (sub)cell
4197 
4198  loadcell_key++;
4199 
4200  } // while
4201  } // cell is not in
4202  } // for cellindex array
4203 
4204  return true;
4205 }
4206 
4207 bool cm93chart::IsPointInLoadedM_COVR(double xc, double yc) {
4208  // Provisionally revert to older method pending investigation.
4209 #if 1
4210  return m_region.Contains(yc, xc);
4211 #else
4212  for (unsigned int im = 0; im < m_pcovr_array_loaded.GetCount(); im++) {
4213  if (G_PtInPolygon_FL(m_pcovr_array_loaded[im]->pvertices,
4214  m_pcovr_array_loaded[im]->m_nvertices, xc, yc))
4215  return true;
4216  }
4217  return false;
4218 #endif
4219 }
4220 
4221 LLRegion cm93chart::GetValidRegion() { return m_region; }
4222 
4223 int cm93chart::loadcell_in_sequence(int cellindex, char subcell) {
4224  int rv = loadsubcell(cellindex, subcell);
4225 
4226  return rv;
4227 }
4228 
4229 int cm93chart::loadsubcell(int cellindex, wxChar sub_char) {
4230  // Create the file name
4231 
4232  int ilat = cellindex / 10000;
4233  int ilon = cellindex % 10000;
4234 
4235  if (g_bDebugCM93) {
4236  double dlat = m_dval / 3.;
4237  double dlon = m_dval / 3.;
4238  double lat, lon;
4239  Get_CM93_Cell_Origin(cellindex, GetNativeScale(), &lat, &lon);
4240  printf(
4241  "\n Attempting loadcell %d scale %lc, sub_char %lc at lat: %g/%g "
4242  "lon:%g/%g\n",
4243  cellindex, wxChar(m_scalechar[0]), sub_char, lat, lat + dlat, lon,
4244  lon + dlon);
4245  }
4246 
4247  int jlat = (int)(((ilat - 30) / m_dval) * m_dval) + 30; // normalize
4248  int jlon = (int)((ilon / m_dval) * m_dval);
4249 
4250  int ilatroot = (((ilat - 30) / 60) * 60) + 30;
4251  int ilonroot = (ilon / 60) * 60;
4252 
4253  wxString file;
4254  file.Printf(_T ( "%04d%04d." ), jlat, jlon);
4255  file += m_scalechar;
4256  file[0] = sub_char;
4257 
4258  // We prefer to make use of the NoFind array to avoid file system access to
4259  // cells known not to exist. However, when the arra becomes "large", then
4260  // searching the array becomes slower than actually accessing the file system.
4261  // So, detect this case, and skip the NoFind array if the array size is larger
4262  // than nnn items. "nnn" determined by experimentation/intuition. Could also
4263  // be platform dependent.
4264  bool b_useNoFind = true;
4265  if (m_noFindArray.GetCount() > 500) b_useNoFind = false;
4266 
4267  wxString fileroot;
4268  fileroot.Printf(_T ( "%04d%04d" ), ilatroot, ilonroot);
4269  appendOSDirSep(&fileroot);
4270  fileroot.append(m_scalechar);
4271  appendOSDirSep(&fileroot);
4272  wxString key = fileroot;
4273  key.append(file);
4274  fileroot.Prepend(m_prefix);
4275 
4276  file.Prepend(fileroot);
4277 
4278  if (g_bDebugCM93) {
4279  char sfile[200];
4280  strncpy(sfile, file.mb_str(), 199);
4281  sfile[199] = 0;
4282  printf(" filename: %s\n", sfile);
4283  }
4284 
4285  bool bfound = false;
4286  wxString compfile;
4287  if (b_useNoFind) {
4288  if (m_noFindArray.Index(key) == wxNOT_FOUND) {
4289  if (::wxFileExists(file))
4290  bfound = true;
4291  else
4292  m_noFindArray.Add(key);
4293  }
4294  } else {
4295  if (::wxFileExists(file)) bfound = true;
4296  ;
4297  }
4298 
4299  if (!bfound) { // try compressed version
4300  if (b_useNoFind) {
4301  if (m_noFindArray.Index(key + _T(".xz")) == wxNOT_FOUND) {
4302  if (::wxFileExists(file + _T(".xz"))) {
4303  compfile = file + _T(".xz");
4304  }
4305  } else {
4306  m_noFindArray.Add(key + _T(".xz"));
4307  }
4308  } else {
4309  if (::wxFileExists(file + _T(".xz"))) compfile = file + _T(".xz");
4310  }
4311  }
4312 
4313  // Try again with alternate scale character
4314  if (!bfound && !compfile.Length()) {
4315  // Try with alternate case of m_scalechar
4316  wxString new_scalechar = m_scalechar.Lower();
4317 
4318  wxString file1;
4319  file1.Printf(_T ( "%04d%04d." ), jlat, jlon);
4320  file1 += new_scalechar;
4321  file1[0] = sub_char;
4322 
4323  fileroot.Printf(_T ( "%04d%04d" ), ilatroot, ilonroot);
4324  appendOSDirSep(&fileroot);
4325  fileroot.append(new_scalechar);
4326  appendOSDirSep(&fileroot);
4327  key = fileroot;
4328  key.append(file1);
4329 
4330  fileroot.Prepend(m_prefix);
4331 
4332  file1.Prepend(fileroot);
4333 
4334  if (b_useNoFind) {
4335  if (m_noFindArray.Index(key) == wxNOT_FOUND) {
4336  if (::wxFileExists(file1)) {
4337  bfound = true;
4338  file = file1; // found the file as lowercase, substitute the name
4339  } else {
4340  m_noFindArray.Add(key);
4341  }
4342  }
4343  } else {
4344  if (::wxFileExists(file1)) {
4345  bfound = true;
4346  file = file1; // found the file as lowercase, substitute the name
4347  }
4348  }
4349 
4350  if (!bfound) { // try compressed version
4351  if (b_useNoFind) {
4352  if (m_noFindArray.Index(key + _T(".xz")) == wxNOT_FOUND) {
4353  if (::wxFileExists(file1 + _T(".xz")))
4354  compfile = file1 + _T(".xz");
4355  else
4356  m_noFindArray.Add(key + _T(".xz"));
4357  }
4358  } else {
4359  if (::wxFileExists(file1 + _T(".xz"))) compfile = file1 + _T(".xz");
4360  }
4361  }
4362  }
4363 
4364  if (g_bDebugCM93) {
4365  printf("noFind count: %d\n", (int)m_noFindArray.GetCount());
4366  }
4367 
4368  if (!bfound && !compfile.Length()) return 0;
4369 
4370  // File is known to exist
4371 
4372  wxString msg(_T ( "Loading CM93 cell " ));
4373  msg += file;
4374  wxLogMessage(msg);
4375 
4376  // Set the member variable to be the actual file name for use in single
4377  // chart mode info display
4378  m_LastFileName = file;
4379 
4380  // Decompress if needed
4381  if (compfile.Length()) {
4382  file = wxFileName::CreateTempFileName(wxFileName(compfile).GetFullName());
4383  if (!DecompressXZFile(compfile, file)) {
4384  wxRemoveFile(file);
4385  return 0;
4386  }
4387  }
4388 
4389  if (g_bDebugCM93) {
4390  char str[256];
4391  strncpy(str, msg.mb_str(), 255);
4392  str[255] = 0;
4393  printf(" %s\n", str);
4394  }
4395 
4396  // Ingest it
4397  if (!Ingest_CM93_Cell((const char *)file.mb_str(), &m_CIB)) {
4398  wxString msg(_T ( " cm93chart Error ingesting " ));
4399  msg.Append(file);
4400  wxLogMessage(msg);
4401 
4402  if (compfile.Length()) wxRemoveFile(file);
4403  return 0;
4404  }
4405 
4406  if (compfile.Length()) wxRemoveFile(file);
4407 
4408  return 1;
4409 }
4410 
4411 void cm93chart::SetUserOffsets(int cell_index, int object_id, int subcell,
4412  int xoff, int yoff) {
4413  M_COVR_Desc *pmcd = GetCoverSet()->Find_MCD(cell_index, object_id, subcell);
4414  if (pmcd) {
4415  pmcd->user_xoff = xoff;
4416  pmcd->user_yoff = yoff;
4417  pmcd->m_buser_offsets = true;
4418  }
4419 }
4420 
4421 wxPoint *cm93chart::GetDrawBuffer(int nSize) {
4422  // Reallocate the cm93chart DrawBuffer if it is currently too small
4423  if (nSize > m_nDrawBufferSize) {
4424  wxPoint *tmp = m_pDrawBuffer;
4425  m_pDrawBuffer =
4426  (wxPoint *)realloc(m_pDrawBuffer, sizeof(wxPoint) * (nSize + 1));
4427  if (NULL == m_pDrawBuffer) {
4428  free(tmp);
4429  tmp = NULL;
4430  } else
4431  m_nDrawBufferSize = nSize + 1;
4432  }
4433  return m_pDrawBuffer;
4434 }
4435 
4436 //-----------------------------------------------------------------------------------------------
4437 // cm93manager Implementation
4438 //-----------------------------------------------------------------------------------------------
4439 
4440 cm93manager::cm93manager(void) {
4441  m_pcm93Dict = NULL;
4442 
4443  m_bfoundA = false;
4444  m_bfoundB = false;
4445  m_bfoundC = false;
4446  m_bfoundD = false;
4447  m_bfoundE = false;
4448  m_bfoundF = false;
4449  m_bfoundG = false;
4450  m_bfoundZ = false;
4451 }
4452 
4453 cm93manager::~cm93manager(void) { delete m_pcm93Dict; }
4454 
4455 bool cm93manager::Loadcm93Dictionary(const wxString &name) {
4456  // Find and load cm93_dictionary
4457  if (!m_pcm93Dict) {
4458  m_pcm93Dict = FindAndLoadDict(name);
4459 
4460  if (!m_pcm93Dict) {
4461  wxLogMessage(_T ( " Cannot load CM93 Dictionary." ));
4462  return false;
4463  }
4464 
4465  if (!m_pcm93Dict->IsOk()) {
4466  wxLogMessage(_T ( " Error in loading CM93 Dictionary." ));
4467  delete m_pcm93Dict;
4468  m_pcm93Dict = NULL;
4469  return false;
4470  ;
4471  }
4472  } else if (!m_pcm93Dict->IsOk()) {
4473  wxLogMessage(_T ( " CM93 Dictionary is not OK." ));
4474  return false;
4475  }
4476 
4477  return true;
4478 }
4479 
4480 cm93_dictionary *cm93manager::FindAndLoadDict(const wxString &file) {
4481  cm93_dictionary *retval = NULL;
4482  cm93_dictionary *pdict = new cm93_dictionary();
4483 
4484  // Search for the dictionary files all along the path of the passed
4485  // parameter filename
4486 
4487  wxFileName fn(file);
4488  wxString path = fn.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
4489  wxString target;
4490  unsigned int i = 0;
4491 
4492  while (i < path.Len()) {
4493  target.Append(path[i]);
4494  if (path[i] == fn.GetPathSeparator()) {
4495  if (pdict->LoadDictionary(target)) {
4496  retval = pdict;
4497  break;
4498  }
4499  if (pdict->LoadDictionary(target + _T ( "CM93ATTR" ))) {
4500  retval = pdict;
4501  break;
4502  }
4503  }
4504  i++;
4505  }
4506 
4507  char t[100];
4508  strncpy(t, target.mb_str(), 99);
4509 
4510  if (retval == NULL) delete pdict;
4511 
4512  return retval;
4513 }
4514 
4515 //----------------------------------------------------------------------------
4516 // cm93 Composite Chart object class Implementation
4517 //----------------------------------------------------------------------------
4518 cm93compchart::cm93compchart() {
4519  m_ChartType = CHART_TYPE_CM93COMP;
4520  m_pDictComposite = NULL;
4521 
4522  // Supply a default name for status bar field
4523  m_FullPath = _T ( "CM93" );
4524 
4525  // Set the "Description", so that it paints nice on the screen
4526  m_Description = _T ( "CM93Composite" );
4527 
4528  m_SE = _T ( "" );
4529  m_datum_str = _T ( "WGS84" );
4530  m_SoundingsDatum = _T ( "Unknown" );
4531 
4532  for (int i = 0; i < 8; i++) m_pcm93chart_array[i] = NULL;
4533 
4534  m_pcm93chart_current = NULL;
4535 
4536  m_cmscale = -1;
4537  m_Chart_Skew = 0.0;
4538 
4539  m_pDummyBM = NULL;
4540 
4541  SetSpecialOutlineCellIndex(0, 0, 0);
4542  m_last_cell_adjustvp = NULL;
4543 
4544  m_pcm93mgr = new cm93manager();
4545 }
4546 
4547 cm93compchart::~cm93compchart() {
4548  if (g_pCM93OffsetDialog) {
4549  g_pCM93OffsetDialog->Hide();
4550  }
4551 
4552  for (int i = 0; i < 8; i++) delete m_pcm93chart_array[i];
4553 
4554  delete m_pDictComposite;
4555  delete m_pDummyBM;
4556  delete m_pcm93mgr;
4557 }
4558 
4559 InitReturn cm93compchart::Init(const wxString &name, ChartInitFlag flags) {
4560  m_FullPath = name;
4561 
4562  wxFileName fn(name);
4563 
4564  wxString target;
4565  wxString path;
4566 
4567  // Verify that the passed file name exists
4568  if (!fn.FileExists()) {
4569  // It may be a directory
4570  if (wxDir::Exists(name)) {
4571  target = name;
4572  appendOSDirSep(&target);
4573  path = name;
4574  appendOSDirSep(&path);
4575  } else {
4576  wxString msg(_T ( " CM93Composite Chart Init cannot find " ));
4577  msg.Append(name);
4578  wxLogMessage(msg);
4579  return INIT_FAIL_REMOVE;
4580  }
4581  } else // its a file that exists
4582  {
4583  // Get the cm93 cell database prefix
4584  path = fn.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
4585 
4586  // Remove two subdirectories from the passed file name
4587  // This will give a normal CM93 root
4588  wxFileName file_path(path);
4589  file_path.RemoveLastDir();
4590  file_path.RemoveLastDir();
4591 
4592  target = file_path.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
4593  }
4594 
4595  m_prefixComposite = target;
4596 
4597  wxString msg(_T ( "CM93Composite Chart Root is " ));
4598  msg.Append(m_prefixComposite);
4599  wxLogMessage(msg);
4600 
4601  if (flags == THUMB_ONLY) {
4602  // SetColorScheme(cs, false);
4603 
4604  return INIT_OK;
4605  }
4606 
4607  if (flags == HEADER_ONLY) return CreateHeaderData();
4608 
4609  // Load the cm93 dictionary if necessary
4610  if (!m_pDictComposite) {
4611  if (!m_pDictComposite) // second try from the file
4612  m_pDictComposite = FindAndLoadDictFromDir(path);
4613 
4614  if (!m_pDictComposite) {
4615  wxLogMessage(
4616  _T ( " CM93Composite Chart Init cannot locate CM93 dictionary." ));
4617  return INIT_FAIL_REMOVE;
4618  }
4619  }
4620 
4621  // Set the color scheme
4622  SetColorScheme(m_global_color_scheme, false);
4623 
4624  bReadyToRender = true;
4625 
4626  return INIT_OK;
4627 }
4628 
4629 void cm93compchart::Activate(void) {
4630  // if ( g_bShowCM93DetailSlider )
4631  // {
4632  // if ( !pPopupDetailSlider )
4633  // {
4634  // pPopupDetailSlider = new PopUpDSlide ( gFrame, -1 , 0,
4635  // -CM93_ZOOM_FACTOR_MAX_RANGE, CM93_ZOOM_FACTOR_MAX_RANGE,
4636  // wxPoint (
4637  // g_cm93detail_dialog_x,
4638  // g_cm93detail_dialog_y
4639  // ), wxDefaultSize,
4640  // wxSIMPLE_BORDER
4641  // , _T (
4642  // "cm93
4643  // Detail"
4644  // ) );
4645  // }
4646  //
4647  // // Here is an ugly piece of code which prevents the slider
4648  // from taking the keyboard focus
4649  // // Only seems to work for Windows.....
4650  // pPopupDetailSlider->Disable();
4651  // pPopupDetailSlider->Show();
4652  // pPopupDetailSlider->Enable();
4653  // }
4654 }
4655 
4656 void cm93compchart::Deactivate(void) {
4657  if (pPopupDetailSlider) {
4658  pPopupDetailSlider->Destroy();
4659  pPopupDetailSlider = NULL;
4660  }
4661 }
4662 
4663 double scale_breaks[] = {
4664  5000., // G
4665  15000., // F
4666  40000., // E
4667  150000., // D
4668  300000., // C
4669  1000000., // B
4670  5000000., // A
4671  20000000. // Z
4672 };
4673 
4674 //-----------------------------------------------------------------------
4675 // Calculate and Set ViewPoint Constants
4676 //-----------------------------------------------------------------------
4677 
4678 int cm93compchart::GetCMScaleFromVP(const ViewPort &vpt) {
4679  double scale_mpp = 3000 / vpt.view_scale_ppm;
4680 
4681  double scale_mpp_adj = scale_mpp;
4682 
4683  double scale_breaks_adj[7];
4684 
4685  for (int i = 0; i < 7; i++) scale_breaks_adj[i] = scale_breaks[i];
4686 
4687  if (g_cm93_zoom_factor) {
4688 #if 0
4689  // Completely intuitive exponential curve adjustment
4690  double efactor = ( double ) ( g_cm93_zoom_factor ) * ( .176 / 7. );
4691  for ( int i=0 ; i < 7 ; i++ )
4692  {
4693  double efr = efactor * ( 7 - i );
4694  scale_breaks_adj[i] = scale_breaks[i] * pow ( 10., efr );
4695  if ( g_bDebugCM93 )
4696  printf ( "g_cm93_zoom_factor: %2d efactor: %6g efr:%6g, scale_breaks[i]:%6g scale_breaks_adj[i]: %6g\n",
4697  g_cm93_zoom_factor, efactor, efr, scale_breaks[i], scale_breaks_adj[i] );
4698  }
4699 #else
4700  // improved adjustment for small scales
4701  double efr = (double)g_cm93_zoom_factor * pow(scale_mpp, -.05);
4702  scale_mpp_adj *= pow(.6, efr);
4703 #endif
4704  }
4705 
4706  int cmscale_calc = 7;
4707  int brk_index = 0;
4708  while (cmscale_calc > 0) {
4709  if (scale_mpp_adj < scale_breaks_adj[brk_index]) break;
4710  cmscale_calc--;
4711  brk_index++;
4712  }
4713 
4714  // Check for overzoom at the theoretically calcuolated chart scale
4715  // If overzoomed possible, switch to larger scale chart if available
4716  double zoom_factor = scale_breaks[7 - cmscale_calc] / vpt.chart_scale;
4717  if (zoom_factor > 4.0) {
4718  if (cmscale_calc < 7) cmscale_calc++;
4719  }
4720 
4721  return cmscale_calc;
4722 }
4723 
4724 void cm93compchart::SetVPParms(const ViewPort &vpt) {
4725  m_vpt = vpt; // save a copy
4726 
4727  int cmscale = GetCMScaleFromVP(vpt); // First order calculation of cmscale
4728  m_cmscale = PrepareChartScale(vpt, cmscale, false);
4729 
4730  // Continuoesly update the composite chart edition date to the latest cell
4731  // decoded
4732  if (m_pcm93chart_array[cmscale]) {
4733  if (!m_EdDate.IsValid() || !m_pcm93chart_array[cmscale]->GetEditionDate().IsValid() || m_pcm93chart_array[cmscale]->GetEditionDate().IsLaterThan(m_EdDate))
4734  m_EdDate = m_pcm93chart_array[cmscale]->GetEditionDate();
4735  }
4736 }
4737 
4738 int cm93compchart::PrepareChartScale(const ViewPort &vpt, int cmscale,
4739  bool bOZ_protect) {
4740  if (g_bDebugCM93)
4741  printf("\non SetVPParms, cmscale:%d, %c\n", cmscale,
4742  (char)('A' + cmscale - 1));
4743 
4744  wxChar ext;
4745  bool cellscale_is_useable = false;
4746  bool b_nochart = false;
4747 
4748  while (!cellscale_is_useable) {
4749  // Open the proper scale chart, if not already open
4750  while (NULL == m_pcm93chart_array[cmscale]) {
4751  if (Is_CM93Cell_Present(m_prefixComposite, vpt.clat, vpt.clon, cmscale)) {
4752  if (g_bDebugCM93)
4753  printf(" chart %c at VP clat/clon is present\n",
4754  (char)('A' + cmscale - 1));
4755 
4756  m_pcm93chart_array[cmscale] = new cm93chart();
4757 
4758  ext = (wxChar)('A' + cmscale - 1);
4759  if (cmscale == 0) ext = 'Z';
4760 
4761  wxString file_dummy = _T ( "CM93." );
4762  file_dummy << ext;
4763 
4764  m_pcm93chart_array[cmscale]->SetCM93Dict(m_pDictComposite);
4765  m_pcm93chart_array[cmscale]->SetCM93Prefix(m_prefixComposite);
4766  m_pcm93chart_array[cmscale]->SetCM93Manager(m_pcm93mgr);
4767 
4768  m_pcm93chart_array[cmscale]->SetColorScheme(m_global_color_scheme);
4769  m_pcm93chart_array[cmscale]->Init(file_dummy, FULL_INIT);
4770  } else if (cmscale == 0) {
4771  // wxString msg;
4772  // msg.Printf ( _T ( " CM93 finds no chart of
4773  // any scale present at Lat/Lon %g %g" ),
4774  // vpt.clat, vpt.clon ); wxLogMessage ( msg );
4775  if (g_bDebugCM93)
4776  printf(
4777  " CM93 finds no chart of any scale present at Lat/Lon %g %g\n",
4778  vpt.clat, vpt.clon);
4779 
4780  b_nochart = true;
4781  break;
4782  }
4783 
4784  else {
4785  cmscale--; // revert to smaller scale if selected is not present
4786  if (g_bDebugCM93)
4787  printf(" no %c scale chart present, adjusting cmscale to %c\n",
4788  (char)('A' + cmscale), (char)('A' + cmscale - 1));
4789  }
4790  }
4791 
4792  m_pcm93chart_current = m_pcm93chart_array[cmscale];
4793 
4794  if (b_nochart) {
4795  if (g_bDebugCM93) printf(" b_nochart return\n");
4796 
4797  m_pcm93chart_current = NULL;
4798  for (int i = 0; i < 8; i++) {
4799  delete m_pcm93chart_array[i];
4800  m_pcm93chart_array[i] = NULL;
4801  }
4802 
4803  return cmscale;
4804  }
4805 
4806  if (m_pcm93chart_current) {
4807  // Pass the parameters to the proper scale chart
4808  // Which will also load the needed cell(s)
4809  m_pcm93chart_current->SetVPParms(vpt);
4810 
4811  // Check to see if the viewpoint center is actually on the selected
4812  // chart
4813  float yc = vpt.clat;
4814  float xc = vpt.clon;
4815 
4816  if (!m_pcm93chart_current->GetCoverSet()->GetCoverCount()) {
4817  if (g_bDebugCM93)
4818  printf(" chart %c has no M_COVR\n", (char)('A' + cmscale - 1));
4819  }
4820 
4821  if (m_pcm93chart_current->IsPointInLoadedM_COVR(xc, yc)) {
4822  if (g_bDebugCM93)
4823  printf(" chart %c contains clat/clon\n", (char)('A' + cmscale - 1));
4824 
4825  cellscale_is_useable = true;
4826  break;
4827  }
4828 
4829  // This commented block assumed that scale 0 coverage is available
4830  // worlwide..... Might not be so with partial CM93 sets
4831  /*
4832  else if(cmscale == 0)
4833  {
4834  cellscale_is_useable = true;
4835  break;
4836  }
4837  */
4838 
4839  else if (vpt.b_quilt && vpt.b_FullScreenQuilt) {
4840  ViewPort vp = vpt;
4841 
4842  covr_set *pcover = m_pcm93chart_current->GetCoverSet();
4843  if (pcover) {
4844  bool boverlap = false;
4845  for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
4846  M_COVR_Desc *mcd = pcover->GetCover(im);
4847 
4848  if (!(vp.GetBBox().IntersectOut(mcd->m_covr_bbox))) {
4849  boverlap = true;
4850  break;
4851  }
4852  }
4853  if (boverlap) cellscale_is_useable = true;
4854  }
4855  }
4856 
4857  if (!cellscale_is_useable) {
4858  if (cmscale > 0)
4859  cmscale--; // revert to larger scale if the current scale cells do
4860  // not contain VP
4861  else
4862  b_nochart = true; // we have retired to scale 0, and still no chart
4863  // coverage, so stop already...
4864  if (g_bDebugCM93)
4865  printf(" VP is not in M_COVR, adjusting cmscale to %c\n",
4866  (char)('A' + cmscale - 1));
4867  }
4868  }
4869  }
4870 
4871  // Final check the zoom factor
4872  if (bOZ_protect) {
4873  double zoom_factor = scale_breaks[7 - cmscale] / vpt.chart_scale;
4874 
4875  if (zoom_factor > 4.0) {
4876  // See if there is a larger scale chart present that will avoid overzoom
4877 
4878  float yc = vpt.clat;
4879  float xc = vpt.clon;
4880 
4881  // Find out what the smallest available scale is that is not overzoomed
4882  FillScaleArray(vpt.clat, vpt.clon);
4883  int new_scale = cmscale;
4884  bool b_found = false;
4885  while (new_scale <= 7) {
4886  if (m_bScale_Array[new_scale]) {
4887  double new_zoom_factor =
4888  scale_breaks[7 - new_scale] / vpt.chart_scale;
4889 
4890  // Do not allow excessive "under-zoom", for performance reasons
4891  if (new_zoom_factor < 1.0) {
4892  b_found = true;
4893  new_scale = cmscale;
4894  break;
4895  }
4896 
4897  if (new_zoom_factor < 4.0) {
4898  if (NULL == m_pcm93chart_array[new_scale]) {
4899  m_pcm93chart_array[new_scale] = new cm93chart();
4900 
4901  ext = (wxChar)('A' + new_scale - 1);
4902  if (new_scale == 0) ext = 'Z';
4903 
4904  wxString file_dummy = _T ( "CM93." );
4905  file_dummy << ext;
4906 
4907  m_pcm93chart_array[new_scale]->SetCM93Dict(m_pDictComposite);
4908  m_pcm93chart_array[new_scale]->SetCM93Prefix(m_prefixComposite);
4909  m_pcm93chart_array[new_scale]->SetCM93Manager(m_pcm93mgr);
4910 
4911  m_pcm93chart_array[new_scale]->SetColorScheme(
4912  m_global_color_scheme);
4913  m_pcm93chart_array[new_scale]->Init(file_dummy, FULL_INIT);
4914  }
4915 
4916  m_pcm93chart_array[new_scale]->SetVPParms(vpt);
4917  if (m_pcm93chart_array[new_scale]->IsPointInLoadedM_COVR(xc, yc)) {
4918  b_found = true;
4919  break;
4920  }
4921  }
4922  }
4923  new_scale++;
4924  }
4925  if (b_found) {
4926  cmscale = new_scale;
4927  m_pcm93chart_current = m_pcm93chart_array[cmscale];
4928  }
4929  }
4930  }
4931 
4932  return cmscale;
4933 }
4934 
4935 // Populate the member bool array describing which chart scales are available
4936 // at any location
4937 void cm93compchart::FillScaleArray(double lat, double lon) {
4938  for (int cmscale = 0; cmscale < 8; cmscale++)
4939  m_bScale_Array[cmscale] =
4940  Is_CM93Cell_Present(m_prefixComposite, lat, lon, cmscale);
4941 }
4942 
4943 // These methods simply pass the called parameters to the currently active
4944 // cm93chart
4945 
4946 wxString cm93compchart::GetPubDate() {
4947  wxString data;
4948 
4949  if (NULL != m_pcm93chart_current)
4950 
4951  data.Printf(_T ( "%4d" ), m_current_cell_pub_date);
4952  else
4953  data = _T ( "????" );
4954  return data;
4955 }
4956 
4957 int cm93compchart::GetNativeScale() {
4958  if (m_pcm93chart_current)
4959  return m_pcm93chart_current->GetNativeScale();
4960  else
4961  return (int)1e8;
4962 }
4963 
4964 double cm93compchart::GetNormalScaleMin(double canvas_scale_factor,
4965  bool b_allow_overzoom) {
4966  double oz_factor;
4967  oz_factor = 40.;
4968 
4969  if (m_pcm93chart_current) {
4970  int cmscale = 0;
4971  if (m_pcm93chart_current->m_last_vp.IsValid()) {
4972  FillScaleArray(m_pcm93chart_current->m_last_vp.clat,
4973  m_pcm93chart_current->m_last_vp.clon);
4974 
4975  // Find out what the smallest available scale is
4976  cmscale = 7;
4977  while (cmscale > 0) {
4978  if (m_bScale_Array[cmscale]) break;
4979  cmscale--;
4980  }
4981  }
4982 
4983  // And return a sensible minimum scale, allowing selected overzoom.
4984  switch (cmscale) {
4985  case 0:
4986  return 20000000. / oz_factor; // Z
4987  case 1:
4988  return 3000000. / oz_factor; // A
4989  case 2:
4990  return 1000000. / oz_factor; // B
4991  case 3:
4992  return 200000. / oz_factor; // C
4993  case 4:
4994  return 100000. / oz_factor; // D
4995  case 5:
4996  return 50000. / oz_factor; // E
4997  case 6:
4998  return 20000. / oz_factor; // F
4999  case 7:
5000  return 500.; // G
5001  default:
5002  return 500. / oz_factor;
5003  }
5004  } else
5005  return 500.;
5006 }
5007 
5008 double cm93compchart::GetNormalScaleMax(double canvas_scale_factor,
5009  int canvas_width) {
5010  return (180. / 360.) * PI * 2 *
5011  (WGS84_semimajor_axis_meters / (canvas_width / canvas_scale_factor));
5012  // return 1.0e8;
5013 }
5014 
5015 wxPoint GetPixFromLLVP(double lat, double lon, const ViewPort &VPoint) {
5016  // Inline the Simple Mercator Transform for performance reasons
5017  double easting, northing;
5018 
5019  double s, y3, s0, y30;
5020  double z = WGS84_semimajor_axis_meters * mercator_k0;
5021 
5022  double xlon = lon;
5023 
5024  /* Make sure lon and lon0 are same phase */
5025  if (lon * VPoint.clon < 0.) {
5026  if (lon < 0.)
5027  xlon += 360.;
5028  else
5029  xlon -= 360.;
5030  }
5031 
5032  // And choose the closest direction
5033  if (fabs(xlon - VPoint.clon) > 180.) {
5034  if (xlon > VPoint.clon)
5035  xlon -= 360.;
5036  else
5037  xlon += 360.;
5038  }
5039  easting = (xlon - VPoint.clon) * DEGREE * z;
5040 
5041  s = sin(lat * DEGREE);
5042  y3 = (.5 * log((1 + s) / (1 - s))) * z;
5043 
5044  s0 = sin(VPoint.clat * DEGREE);
5045  y30 = (.5 * log((1 + s0) / (1 - s0))) * z;
5046  northing = y3 - y30;
5047 
5048  wxPoint r;
5049 
5050  double epix = easting * VPoint.view_scale_ppm;
5051  double npix = northing * VPoint.view_scale_ppm;
5052  r.x = (int)round((VPoint.pix_width / 2) + epix);
5053  r.y = (int)round((VPoint.pix_height / 2) - npix);
5054 
5055  return r;
5056 }
5057 
5058 // extern void catch_signals(int signo);
5059 
5060 void cm93compchart::GetValidCanvasRegion(const ViewPort &VPoint,
5061  OCPNRegion *pValidRegion) {
5062  OCPNRegion screen_region(0, 0, VPoint.pix_width, VPoint.pix_height);
5063  OCPNRegion ret = GetValidScreenCanvasRegion(
5064  VPoint, g_bopengl ? VPoint.rv_rect : screen_region);
5065  *pValidRegion = ret;
5066 }
5067 
5068 OCPNRegion cm93compchart::GetValidScreenCanvasRegion(
5069  const ViewPort &VPoint, const OCPNRegion &ScreenRegion) {
5070  OCPNRegion ret_region;
5071 
5072  ViewPort vp = VPoint;
5073 
5074  vp.rotation = 0.;
5075 
5076  if (m_pcm93chart_current) {
5077  int chart_native_scale = m_pcm93chart_current->GetNativeScale();
5078 
5079  for (unsigned int im = 0;
5080  im < m_pcm93chart_current->m_pcovr_array_loaded.GetCount(); im++) {
5081  M_COVR_Desc *pmcd = (m_pcm93chart_current->m_pcovr_array_loaded[im]);
5082 
5083  // We can make a quick test based on the bbox of the M_COVR and the
5084  // bbox of the ViewPort
5085 
5086  if (vp.GetBBox().IntersectOut(pmcd->m_covr_bbox)) continue;
5087 
5088  wxPoint *DrawBuf = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices);
5089 
5090  OCPNRegion rgn_covr = vp.GetVPRegionIntersect(
5091  ScreenRegion, pmcd->m_nvertices, (float *)pmcd->pvertices,
5092  chart_native_scale, DrawBuf);
5093 
5094  if (rgn_covr.IsOk()) // not empty
5095  ret_region.Union(rgn_covr);
5096  }
5097 
5098  } else
5099  ret_region.Union(OCPNRegion(0, 0, 1, 1));
5100 
5101  return ret_region;
5102 }
5103 
5104 LLRegion cm93compchart::GetValidRegion() {
5105  if (m_pcm93chart_current) return m_pcm93chart_current->GetValidRegion();
5106 
5107  return LLRegion(); // empty region
5108 }
5109 
5110 bool cm93compchart::RenderRegionViewOnGL(const wxGLContext &glc,
5111  const ViewPort &VPoint,
5112  const OCPNRegion &RectRegion,
5113  const LLRegion &Region) {
5114  SetVPParms(VPoint);
5115 
5116  if (g_pCM93OffsetDialog && g_pCM93OffsetDialog->IsShown())
5117  g_pCM93OffsetDialog->UpdateMCOVRList(VPoint);
5118 
5119  return DoRenderRegionViewOnGL(glc, VPoint, RectRegion, Region);
5120 }
5121 
5122 bool cm93compchart::DoRenderRegionViewOnGL(const wxGLContext &glc,
5123  const ViewPort &VPoint,
5124  const OCPNRegion &RectRegion,
5125  const LLRegion &Region) {
5126  // g_bDebugCM93 = true;
5127 
5128  // CALLGRIND_START_INSTRUMENTATION
5129 
5130  ViewPort vp = VPoint;
5131 
5132  bool render_return = false;
5133  if (m_pcm93chart_current == 0) return render_return;
5134 
5135  {
5136  // This will be done later, in s57chart base class render method
5138 
5139  // Check the current chart scale to see if it covers the requested region
5140  // totally
5141  if (VPoint.b_quilt) {
5142  LLRegion vpr_empty = Region;
5143  LLRegion chart_region = GetValidRegion();
5144 
5145  // old method which draws the regions from large to small scale, then
5146  // finishes with the largest scale. This is broken on systems with broken
5147  // clipping regions
5148 
5149  // So we modify the algorithm as follows:
5150  // a. Calculate the region patches from large scale to small scale,
5151  // starting with the Reference scale, and
5152  // ending when the total region requested is full.
5153  // b. Save the calculated patches in an array as they are generated.
5154  // c. Render the regions/scales saved in the array in reverse order, from
5155  // small scale to large scale. d. Finally, render the Reference
5156  // region/scale.
5157  //
5158  // This logic has the advantage that only the minimum necessary Object
5159  // rendering is actually performed, and only within the minimum necessary
5160  // region.
5161 
5162  if (!chart_region.Empty()) vpr_empty.Subtract(chart_region);
5163 
5164  if (!vpr_empty.Empty() &&
5165  m_cmscale) // This chart scale does not fully cover the region
5166  {
5167  // Save the current cm93 chart scale for restoration later
5168  int cmscale_save = m_cmscale;
5169 
5170  LLRegion region_vect[8];
5171 
5172  // Render smaller scale cells the entire requested region is full
5173 
5174  while (!vpr_empty.Empty() && m_cmscale) {
5175  // get the next smaller scale chart
5176  m_cmscale = PrepareChartScale(vp, m_cmscale - 1, false);
5177 
5178  if (m_pcm93chart_current) {
5179  LLRegion sscale_region = GetValidRegion();
5180 
5181  // Save the calculated per-scale region in the array
5182  region_vect[m_cmscale] = sscale_region;
5183  region_vect[m_cmscale].Intersect(vpr_empty);
5184  // Only need to render that part of the vp that is not yet full
5185  // Update the remaining empty region
5186  vpr_empty.Subtract(sscale_region);
5187  }
5188 
5189  } // while
5190 
5191  // Render all non-empty regions saved in the array, from small to large
5192  // scale.
5193  for (int i = 0; i < 8; i++) {
5194  if (!region_vect[i].Empty()) {
5195  m_cmscale = PrepareChartScale(vp, i, false);
5196  if (m_pcm93chart_current)
5197  render_return |= m_pcm93chart_current->RenderRegionViewOnGL(
5198  glc, vp, RectRegion, region_vect[i]);
5199  }
5200  }
5201 
5202  // restore the base chart pointer
5203  m_cmscale = cmscale_save;
5204  m_pcm93chart_current = m_pcm93chart_array[m_cmscale];
5205  }
5206 
5207  // Render the on-top Reference region/scale
5208  if (m_pcm93chart_current) {
5209  render_return |= m_pcm93chart_current->RenderRegionViewOnGL(
5210  glc, vp, RectRegion, Region);
5211  m_Name = m_pcm93chart_current->GetName();
5212  }
5213 
5214  } else // Single chart mode
5215  {
5216  if (m_pcm93chart_current) {
5217  render_return = m_pcm93chart_current->RenderRegionViewOnGL(
5218  glc, vp, RectRegion, Region);
5219  m_Name = m_pcm93chart_current->GetLastFileName();
5220  }
5221  }
5222  }
5223 
5224  if (VPoint.m_projection_type != PROJECTION_MERCATOR)
5225  return render_return; // TODO: fix below for non-mercator
5226 
5227  if (!m_pcm93chart_current) return render_return;
5228 
5229  // Render the cm93 cell's M_COVR outlines if called for
5230  if (m_cell_index_special_outline) {
5231  ocpnDC dc;
5232  dc.SetVP(VPoint);
5233 
5234  covr_set *pcover = m_pcm93chart_current->GetCoverSet();
5235 
5236  for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
5237  M_COVR_Desc *pmcd = pcover->GetCover(im);
5238  if ((pmcd->m_cell_index == m_cell_index_special_outline) &&
5239  (pmcd->m_object_id == m_object_id_special_outline) &&
5240  (pmcd->m_subcell == m_subcell_special_outline))
5241 
5242  {
5243  // Draw this MCD's represented outline
5244 
5245  // Case: vpBBox is completely inside the mcd box
5246  // if(!(
5247  // vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)) ||
5248  // !( vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)))
5249  {
5250  float_2Dpt *p = pmcd->pvertices;
5251  wxPoint *pwp = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices);
5252 
5253  for (int ip = 0; ip < pmcd->m_nvertices; ip++) {
5254  double plon = p->x;
5255  if (fabs(plon - VPoint.clon) > 180.) {
5256  if (plon > VPoint.clon)
5257  plon -= 360.;
5258  else
5259  plon += 360.;
5260  }
5261 
5262  double easting, northing, epix, npix;
5263  toSM(p->y, plon + 360., VPoint.clat, VPoint.clon + 360, &easting,
5264  &northing);
5265 
5266  // Outlines stored in MCDs are not adjusted for offsets
5267  // easting -=
5268  // pmcd->transform_WGS84_offset_x;
5269  easting -= pmcd->user_xoff;
5270  // northing -=
5271  // pmcd->transform_WGS84_offset_y;
5272  northing -= pmcd->user_yoff;
5273 
5274  epix = easting * VPoint.view_scale_ppm;
5275  npix = northing * VPoint.view_scale_ppm;
5276 
5277  pwp[ip].x = (int)round((VPoint.pix_width / 2) + epix);
5278  pwp[ip].y = (int)round((VPoint.pix_height / 2) - npix);
5279 
5280  p++;
5281  }
5282 
5283  bool btest = true;
5284  if (btest) {
5285  wxPen pen(wxTheColourDatabase->Find(_T ( "YELLOW" )), 3);
5286  wxDash dash1[2];
5287  dash1[0] = 4; // Long dash
5288  dash1[1] = 4; // Short gap
5289  pen.SetStyle(wxPENSTYLE_USER_DASH);
5290  pen.SetDashes(2, dash1);
5291 
5292  dc.SetPen(pen);
5293 
5294  for (int iseg = 0; iseg < pmcd->m_nvertices - 1; iseg++) {
5295  int x0 = pwp[iseg].x;
5296  int y0 = pwp[iseg].y;
5297  int x1 = pwp[iseg + 1].x;
5298  int y1 = pwp[iseg + 1].y;
5299 
5300  ClipResult res = cohen_sutherland_line_clip_i(
5301  &x0, &y0, &x1, &y1, 0, VPoint.pix_width, 0,
5302  VPoint.pix_height);
5303 
5304  if (res ==
5305  Invisible) // Do not bother with segments that are invisible
5306  continue;
5307 
5308  dc.DrawLine(x0, y0, x1, y1);
5309  }
5310  }
5311  }
5312  }
5313  }
5314  }
5315 
5316  return render_return;
5317 }
5318 
5319 bool cm93compchart::RenderRegionViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint,
5320  const OCPNRegion &Region) {
5321  SetVPParms(VPoint);
5322 
5323  if (g_pCM93OffsetDialog && g_pCM93OffsetDialog->IsShown())
5324  g_pCM93OffsetDialog->UpdateMCOVRList(VPoint);
5325 
5326  return DoRenderRegionViewOnDC(dc, VPoint, Region);
5327 }
5328 
5329 bool cm93compchart::RenderViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint) {
5330  const OCPNRegion vpr(0, 0, VPoint.pix_width, VPoint.pix_height);
5331 
5332  SetVPParms(VPoint);
5333 
5334  return DoRenderRegionViewOnDC(dc, VPoint, vpr);
5335 }
5336 
5337 bool cm93compchart::DoRenderRegionViewOnDC(wxMemoryDC &dc,
5338  const ViewPort &VPoint,
5339  const OCPNRegion &Region) {
5340  // g_bDebugCM93 = true;
5341 
5342  // CALLGRIND_START_INSTRUMENTATION
5343  if (g_bDebugCM93) {
5344  printf("\nOn DoRenderRegionViewOnDC Ref scale is %d, %c\n", m_cmscale,
5345  (char)('A' + m_cmscale - 1));
5346  OCPNRegionIterator upd(Region);
5347  while (upd.HaveRects()) {
5348  wxRect rect = upd.GetRect();
5349  printf(" Region Rect: %d %d %d %d\n", rect.x, rect.y, rect.width,
5350  rect.height);
5351  upd.NextRect();
5352  ;
5353  }
5354  }
5355 
5356  ViewPort vp = VPoint;
5357 
5358  bool render_return = false;
5359  if (m_pcm93chart_current) {
5360  m_pcm93chart_current->SetVPParms(vp);
5361 
5362  // Check the current chart scale to see if it covers the requested region
5363  // totally
5364  if (VPoint.b_quilt) {
5365  OCPNRegion vpr_empty = Region;
5366 
5367  OCPNRegion chart_region = GetValidScreenCanvasRegion(vp, Region);
5368 
5369  if (g_bDebugCM93) {
5370  printf(
5371  "On DoRenderRegionViewOnDC : Intersecting Ref region rectangles\n");
5372  OCPNRegionIterator upd(chart_region);
5373  while (upd.HaveRects()) {
5374  wxRect rect = upd.GetRect();
5375  printf(" Region Rect: %d %d %d %d\n", rect.x, rect.y, rect.width,
5376  rect.height);
5377  upd.NextRect();
5378  }
5379  }
5380 
5381  if (!chart_region.IsEmpty()) vpr_empty.Subtract(chart_region);
5382 
5383  if (!vpr_empty.Empty() &&
5384  m_cmscale) // This chart scale does not fully cover the region
5385  {
5386  // Render the target scale chart on a temp dc for safekeeping
5387 #ifdef ocpnUSE_DIBSECTION
5388  ocpnMemDC temp_dc;
5389 #else
5390  wxMemoryDC temp_dc;
5391 #endif
5392  if (!chart_region.IsEmpty())
5393  render_return = m_pcm93chart_current->RenderRegionViewOnDC(
5394  temp_dc, vp, chart_region);
5395  else
5396  render_return = false;
5397 
5398  // Save the current cm93 chart pointer for restoration later
5399  cm93chart *m_pcm93chart_save = m_pcm93chart_current;
5400 
5401  // Prepare a blank quilt bitmap to build up the quilt upon
5402  // We need to do this in order to avoid polluting any of the
5403  // sub-chart cached bitmaps
5404  if (m_pDummyBM) {
5405  if ((m_pDummyBM->GetWidth() != VPoint.rv_rect.width) ||
5406  (m_pDummyBM->GetHeight() != VPoint.rv_rect.height)) {
5407  delete m_pDummyBM;
5408  m_pDummyBM = NULL;
5409  }
5410  }
5411  if (NULL == m_pDummyBM)
5412  m_pDummyBM =
5413  new wxBitmap(VPoint.rv_rect.width, VPoint.rv_rect.height, -1);
5414 
5415  // Clear the quilt
5416 #ifdef ocpnUSE_DIBSECTION
5417  ocpnMemDC dumm_dc;
5418 #else
5419  wxMemoryDC dumm_dc;
5420 #endif
5421  dumm_dc.SelectObject(*m_pDummyBM);
5422  dumm_dc.SetBackground(*wxBLACK_BRUSH);
5423  dumm_dc.Clear();
5424 
5425  int cmscale_next = m_cmscale;
5426 
5427  // Render smaller scale cells onto a temporary DC, blitting the valid
5428  // region onto the quilt dc until the region is full
5429  while (!vpr_empty.Empty() && cmscale_next) {
5430  // get the next smaller scale chart
5431  cmscale_next--;
5432  m_cmscale = PrepareChartScale(vp, cmscale_next, false);
5433 #ifdef ocpnUSE_DIBSECTION
5434  ocpnMemDC build_dc;
5435 #else
5436  wxMemoryDC build_dc;
5437 #endif
5438 
5439  if (m_pcm93chart_current) {
5440  if (g_bDebugCM93)
5441  printf(" In DRRVOD, add quilt patch at %d, %c\n", m_cmscale,
5442  (char)('A' + m_cmscale - 1));
5443 
5444  m_pcm93chart_current->RenderRegionViewOnDC(build_dc, vp, Region);
5445 
5446  OCPNRegion sscale_region = GetValidScreenCanvasRegion(vp, Region);
5447 
5448  // Only need to render that part of the vp that is not yet full
5449  sscale_region.Intersect(vpr_empty);
5450 
5451  // Blit the smaller scale chart patch onto the target DC
5452  OCPNRegionIterator upd(sscale_region);
5453  while (upd.HaveRects()) {
5454  wxRect rect = upd.GetRect();
5455  dumm_dc.Blit(rect.x, rect.y, rect.width, rect.height, &build_dc,
5456  rect.x, rect.y);
5457  upd.NextRect();
5458  }
5459  build_dc.SelectObject(wxNullBitmap); // safely unmap the bmp
5460 
5461  // Update the remaining empty region
5462  if (!sscale_region.IsEmpty()) vpr_empty.Subtract(sscale_region);
5463  }
5464 
5465  } // while
5466 
5467  // Finally, Blit the target scale chart as saved on temp_dc to quilt
5468  // dc
5469  OCPNRegionIterator updt(chart_region);
5470  while (updt.HaveRects()) {
5471  wxRect rect = updt.GetRect();
5472  dumm_dc.Blit(rect.x, rect.y, rect.width, rect.height, &temp_dc,
5473  rect.x, rect.y);
5474  updt.NextRect();
5475  }
5476  temp_dc.SelectObject(wxNullBitmap); // safely unmap the base chart bmp
5477 
5478  // restore the base chart pointer
5479  m_pcm93chart_current = m_pcm93chart_save;
5480 
5481  // We can unselect the target from the dummy DC, to avoid having to
5482  // copy it.
5483  dumm_dc.SelectObject(wxNullBitmap);
5484 
5485  // And the return dc is the quilt
5486  dc.SelectObject(*m_pDummyBM);
5487 
5488  render_return = true;
5489  } else {
5490  m_pcm93chart_current->RenderRegionViewOnDC(dc, vp, Region);
5491  render_return = true;
5492  }
5493  m_Name = m_pcm93chart_current->GetName();
5494 
5495  } else // Single chart mode
5496  {
5497  render_return =
5498  m_pcm93chart_current->RenderRegionViewOnDC(dc, vp, Region);
5499  m_Name = m_pcm93chart_current->GetLastFileName();
5500  }
5501 
5502  } else {
5503  // one must always return a valid bitmap selected into the specified DC
5504  // Since the CM93 cell is not available at this location, select a dummy
5505  // placeholder
5506  if (m_pDummyBM) {
5507  if ((m_pDummyBM->GetWidth() != VPoint.pix_width) ||
5508  (m_pDummyBM->GetHeight() != VPoint.pix_height)) {
5509  delete m_pDummyBM;
5510  m_pDummyBM = NULL;
5511  }
5512  }
5513 
5514  if (NULL == m_pDummyBM)
5515  m_pDummyBM = new wxBitmap(VPoint.pix_width, VPoint.pix_height, -1);
5516 
5517  // Clear the bitmap
5518  wxMemoryDC mdc;
5519  mdc.SelectObject(*m_pDummyBM);
5520  mdc.SetBackground(*wxBLACK_BRUSH);
5521  mdc.Clear();
5522  mdc.SelectObject(wxNullBitmap);
5523 
5524  dc.SelectObject(*m_pDummyBM);
5525  }
5526 
5527  // CALLGRIND_STOP_INSTRUMENTATION
5528 
5529  // Render the cm93 cell's M_COVR outlines if called for
5530  if (m_cell_index_special_outline && m_pcm93chart_current) {
5531  covr_set *pcover = m_pcm93chart_current->GetCoverSet();
5532 
5533  for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
5534  M_COVR_Desc *pmcd = pcover->GetCover(im);
5535  if ((pmcd->m_cell_index == m_cell_index_special_outline) &&
5536  (pmcd->m_object_id == m_object_id_special_outline) &&
5537  (pmcd->m_subcell == m_subcell_special_outline))
5538 
5539  {
5540  // Draw this MCD's represented outline
5541 
5542  // Case: vpBBox is completely inside the mcd box
5543  // if(!(
5544  // vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)) ||
5545  // !( vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)))
5546  {
5547  float_2Dpt *p = pmcd->pvertices;
5548  wxPoint *pwp = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices);
5549 
5550  for (int ip = 0; ip < pmcd->m_nvertices; ip++) {
5551  double plon = p->x;
5552  if (fabs(plon - VPoint.clon) > 180.) {
5553  if (plon > VPoint.clon)
5554  plon -= 360.;
5555  else
5556  plon += 360.;
5557  }
5558 
5559  double easting, northing, epix, npix;
5560  toSM(p->y, plon + 360., VPoint.clat, VPoint.clon + 360, &easting,
5561  &northing);
5562 
5563  // Outlines stored in MCDs are not adjusted for offsets
5564  // easting -=
5565  // pmcd->transform_WGS84_offset_x;
5566  easting -= pmcd->user_xoff;
5567  // northing -=
5568  // pmcd->transform_WGS84_offset_y;
5569  northing -= pmcd->user_yoff;
5570 
5571  epix = easting * VPoint.view_scale_ppm;
5572  npix = northing * VPoint.view_scale_ppm;
5573 
5574  pwp[ip].x = (int)round((VPoint.pix_width / 2) + epix);
5575  pwp[ip].y = (int)round((VPoint.pix_height / 2) - npix);
5576 
5577  p++;
5578  }
5579 
5580  // Scrub the points
5581  // looking for segments for which the wrong longitude decision was
5582  // made
5583  // TODO all this mole needs to be rethought, again
5584  bool btest = true;
5585  /*
5586  wxPoint p0 = pwp[0];
5587  for(int ip = 1 ; ip < pmcd->m_nvertices
5588  ; ip++)
5589  {
5590  // if(((p0.x > VPoint.pix_width) &&
5591  (pwp[ip].x < 0)) || ((p0.x < 0) && (pwp[ip].x > VPoint.pix_width)))
5592  // btest = false;
5593 
5594  p0 = pwp[ip];
5595  }
5596  */
5597  if (btest) {
5598  dc.SetPen(wxPen(wxTheColourDatabase->Find(_T ( "YELLOW" )), 4,
5599  wxPENSTYLE_LONG_DASH));
5600 
5601  for (int iseg = 0; iseg < pmcd->m_nvertices - 1; iseg++) {
5602  int x0 = pwp[iseg].x;
5603  int y0 = pwp[iseg].y;
5604  int x1 = pwp[iseg + 1].x;
5605  int y1 = pwp[iseg + 1].y;
5606 
5607  ClipResult res = cohen_sutherland_line_clip_i(
5608  &x0, &y0, &x1, &y1, 0, VPoint.pix_width, 0,
5609  VPoint.pix_height);
5610 
5611  if (res ==
5612  Invisible) // Do not bother with segments that are invisible
5613  continue;
5614 
5615  dc.DrawLine(x0, y0, x1, y1);
5616  }
5617  }
5618  }
5619  }
5620  }
5621  }
5622 
5623  return render_return;
5624 }
5625 
5626 void cm93compchart::UpdateRenderRegions(const ViewPort &VPoint) {
5627  OCPNRegion full_screen_region(0, 0, VPoint.rv_rect.width,
5628  VPoint.rv_rect.height);
5629 
5630  ViewPort vp = VPoint;
5631 
5632  SetVPParms(VPoint);
5633 
5634  if (m_pcm93chart_current) {
5635  m_pcm93chart_current->SetVPParms(vp);
5636 
5637  // Check the current chart scale to see if it covers the requested region
5638  // totally
5639  if (VPoint.b_quilt) {
5640  // Clear all the subchart regions
5641  for (int i = 0; i < 8; i++) {
5642  if (m_pcm93chart_array[i])
5643  m_pcm93chart_array[i]->m_render_region.Clear();
5644  }
5645 
5646  OCPNRegion vpr_empty = full_screen_region;
5647 
5648  OCPNRegion chart_region =
5649  GetValidScreenCanvasRegion(vp, full_screen_region);
5650  m_pcm93chart_current->m_render_region = chart_region; // update
5651 
5652  if (!chart_region.IsEmpty()) vpr_empty.Subtract(chart_region);
5653 
5654  if (!vpr_empty.Empty() &&
5655  m_cmscale) // This chart scale does not fully cover the region
5656  {
5657  // Save the current cm93 chart pointer for restoration later
5658  cm93chart *m_pcm93chart_save = m_pcm93chart_current;
5659 
5660  int cmscale_next = m_cmscale;
5661 
5662  while (!vpr_empty.Empty() && cmscale_next) {
5663  // get the next smaller scale chart
5664  cmscale_next--;
5665  m_cmscale = PrepareChartScale(vp, cmscale_next, false);
5666 
5667  if (m_pcm93chart_current) {
5668  OCPNRegion sscale_region =
5669  GetValidScreenCanvasRegion(vp, full_screen_region);
5670  sscale_region.Intersect(vpr_empty);
5671  m_pcm93chart_current->m_render_region = sscale_region;
5672 
5673  // Update the remaining empty region
5674  if (!sscale_region.IsEmpty()) vpr_empty.Subtract(sscale_region);
5675  }
5676 
5677  } // while
5678 
5679  // restore the base chart pointer
5680  m_pcm93chart_current = m_pcm93chart_save;
5681  }
5682  }
5683  }
5684 }
5685 
5686 void cm93compchart::SetSpecialCellIndexOffset(int cell_index, int object_id,
5687  int subcell, int xoff, int yoff) {
5688  m_special_offset_x = xoff;
5689  m_special_offset_y = yoff;
5690 
5691  if (m_pcm93chart_current)
5692  m_pcm93chart_current->SetUserOffsets(cell_index, object_id, subcell, xoff,
5693  yoff);
5694 }
5695 
5696 bool cm93compchart::RenderNextSmallerCellOutlines(ocpnDC &dc, ViewPort &vp,
5697  ChartCanvas *cc) {
5698  if (m_cmscale >= 7) return false;
5699 #ifdef ocpnUSE_GL
5700  glChartCanvas *glcc = cc->GetglCanvas();
5701  if (!glcc) return false;
5702 #else
5703  return false;
5704 #endif
5705 
5706  int nss_max;
5707 
5708  int nss = m_cmscale + 1;
5709 
5710  // A little magic here.
5711  // Drawing all larger scale cell outlines is way too expensive.
5712  // So, stop the loop after we have rendered "something"
5713  // But don't stop at all if the viewport scale is less than 3 million.
5714  // This will have the effect of bringing in outlines of isolated large
5715  // scale cells embedded within small scale cells, like isolated islands in
5716  // the Pacific.
5717  bool bdrawn = false;
5718 
5719  nss_max = 7;
5720 
5721 #if 0 /* only if chart outlines are rendered grounded to the charts */
5722  if(g_bopengl) { /* for opengl: lets keep this simple yet also functioning
5723  unlike the unbounded version (which is interesting)
5724  the small update rectangles normally encountered when panning
5725  can cause too many charts to load */
5726  if(nss_max > m_cmscale+3)
5727  nss_max = m_cmscale+3;
5728  }
5729 #endif
5730  while (nss <= nss_max && (!bdrawn || (vp.chart_scale < 3e6))) {
5731  cm93chart *psc = m_pcm93chart_array[nss];
5732 
5733  if (!psc) {
5734  m_pcm93chart_array[nss] = new cm93chart();
5735  psc = m_pcm93chart_array[nss];
5736 
5737  wxChar ext = (wxChar)('A' + nss - 1);
5738  if (nss == 0) ext = 'Z';
5739 
5740  wxString file_dummy = _T ( "CM93." );
5741  file_dummy << ext;
5742 
5743  psc->SetCM93Dict(m_pDictComposite);
5744  psc->SetCM93Prefix(m_prefixComposite);
5745  psc->SetCM93Manager(m_pcm93mgr);
5746 
5747  psc->SetColorScheme(m_global_color_scheme);
5748  psc->Init(file_dummy, FULL_INIT);
5749  }
5750 
5751  if (nss != 1) { // skip rendering the A scale outlines
5752 
5753  // Make sure the covr bounding box is complete
5754  psc->UpdateCovrSet(&vp);
5755 
5756  // Render the chart outlines
5757  covr_set *pcover = psc->GetCoverSet();
5758 
5759  for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
5760  M_COVR_Desc *mcd = pcover->GetCover(im);
5761 
5762  if (vp.GetBBox().IntersectOut(mcd->m_covr_bbox)) continue;
5763 
5764  wxPoint *pwp = psc->GetDrawBuffer(mcd->m_nvertices);
5765  bdrawn = RenderCellOutlines(dc, vp, pwp, mcd);
5766  }
5767  }
5768  nss++;
5769  }
5770 
5771 #ifdef ocpnUSE_GL
5772  if (g_bopengl) {
5773  glDisable(GL_LINE_STIPPLE);
5774  glDisable(GL_LINE_SMOOTH);
5775  glDisable(GL_BLEND);
5776  }
5777 #endif
5778  return true;
5779 }
5780 
5781 bool cm93compchart::RenderCellOutlines(ocpnDC &dc, ViewPort &vp,
5782  wxPoint *pwp, M_COVR_Desc *mcd) {
5783  float_2Dpt *p = mcd->pvertices;
5784  int np = mcd->m_nvertices;
5785 
5786  for (int ip = 0; ip < np; ip++, p++) {
5787  pwp[ip] = vp.GetPixFromLL(p->y, p->x);
5788 
5789  // Outlines stored in MCDs are not adjusted for offsets
5790  pwp[ip].x -= mcd->user_xoff * vp.view_scale_ppm;
5791  pwp[ip].y -= mcd->user_yoff * vp.view_scale_ppm;
5792  }
5793  // Scrub the points
5794  // looking for segments for which the wrong longitude decision was made
5795  // TODO all this mole needs to be rethought, again
5796  wxPoint p0 = pwp[0];
5797  for (int ip = 1; ip < np; ip++) {
5798  if (((p0.x > vp.pix_width) && (pwp[ip].x < 0)) ||
5799  ((p0.x < 0) && (pwp[ip].x > vp.pix_width)))
5800  return false;
5801 
5802  p0 = pwp[ip];
5803  }
5804 
5805  dc.DrawLines(mcd->m_nvertices, pwp, 0, 0, false);
5806  return true;
5807 }
5808 
5809 void cm93compchart::GetPointPix(ObjRazRules *rzRules, float rlat, float rlon,
5810  wxPoint *r) {
5811  m_pcm93chart_current->GetPointPix(rzRules, rlat, rlon, r);
5812 }
5813 
5814 void cm93compchart::GetPointPix(ObjRazRules *rzRules, wxPoint2DDouble *en,
5815  wxPoint *r, int nPoints) {
5816  m_pcm93chart_current->GetPointPix(rzRules, en, r, nPoints);
5817 }
5818 
5819 void cm93compchart::GetPixPoint(int pixx, int pixy, double *plat, double *plon,
5820  ViewPort *vpt) {
5821  m_pcm93chart_current->GetPixPoint(pixx, pixy, plat, plon, vpt);
5822 }
5823 
5824 void cm93compchart::UpdateLUPs(s57chart *pOwner) {
5825  for (int i = 0; i < 8; i++) {
5826  if (m_pcm93chart_array[i]) m_pcm93chart_array[i]->UpdateLUPs(pOwner);
5827  }
5828 }
5829 
5830 std::list<S57Obj*> *cm93compchart::GetAssociatedObjects(S57Obj *obj) {
5831  if (m_pcm93chart_current)
5832  return m_pcm93chart_current->GetAssociatedObjects(obj);
5833  else
5834  return NULL;
5835 }
5836 
5837 void cm93compchart::InvalidateCache() {
5838  for (int i = 0; i < 8; i++) {
5839  if (m_pcm93chart_array[i]) m_pcm93chart_array[i]->InvalidateCache();
5840  }
5841 }
5842 
5843 void cm93compchart::ForceEdgePriorityEvaluate(void) {
5844  for (int i = 0; i < 8; i++) {
5845  if (m_pcm93chart_array[i])
5846  m_pcm93chart_array[i]->ForceEdgePriorityEvaluate();
5847  }
5848 }
5849 
5850 void cm93compchart::SetColorScheme(ColorScheme cs, bool bApplyImmediate) {
5851  m_global_color_scheme = cs;
5852 
5853  for (int i = 0; i < 8; i++) {
5854  if (m_pcm93chart_array[i])
5855  m_pcm93chart_array[i]->SetColorScheme(cs, bApplyImmediate);
5856  }
5857 }
5858 
5859 ListOfObjRazRules *cm93compchart::GetObjRuleListAtLatLon(float lat, float lon,
5860  float select_radius,
5861  ViewPort *VPoint,
5862  int selection_mask) {
5863  float alon = lon;
5864 
5865  ViewPort vp; // needs a new ViewPort also for ObjectRenderCheck()
5866  vp = *VPoint;
5867 
5868  if (!VPoint->b_quilt)
5869  if (m_pcm93chart_current)
5870  return m_pcm93chart_current->GetObjRuleListAtLatLon(lat, alon,
5871  select_radius, &vp);
5872  else {
5873  // As default, return an empty list
5874  ListOfObjRazRules *ret_ptr = new ListOfObjRazRules;
5875  return ret_ptr;
5876  }
5877  else {
5878  UpdateRenderRegions(*VPoint);
5879 
5880  // Search all of the subcharts, looking for the one whose render region
5881  // contains the requested point
5882  wxPoint p = VPoint->GetPixFromLL(lat, lon);
5883 
5884  for (int i = 0; i < 8; i++) {
5885  if (m_pcm93chart_array[i]) {
5886  if (!m_pcm93chart_array[i]->m_render_region.IsEmpty()) {
5887  if (wxInRegion == m_pcm93chart_array[i]->m_render_region.Contains(p))
5888  return m_pcm93chart_array[i]->GetObjRuleListAtLatLon(
5889  lat, alon, select_radius, &vp, selection_mask);
5890  }
5891  }
5892  }
5893 
5894  // As default, return an empty list
5895  ListOfObjRazRules *ret_ptr = new ListOfObjRazRules;
5896 
5897  return ret_ptr;
5898  }
5899 }
5900 
5901 std::unordered_map<unsigned, VE_Element *> &cm93compchart::Get_ve_hash(void) {
5902  return m_pcm93chart_current->Get_ve_hash();
5903 }
5904 
5905 std::unordered_map<unsigned, VC_Element *> &cm93compchart::Get_vc_hash(void) {
5906  return m_pcm93chart_current->Get_vc_hash();
5907 }
5908 
5909 bool cm93compchart::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) {
5910 #ifdef ocpnUSE_GL
5911  if (g_bopengl) {
5912  /* need a full refresh if not in quilted mode, and the cell changed */
5913  // TODO re-add this for multicanvas
5914  // if ( !vp_last.b_quilt && m_last_cell_adjustvp != m_pcm93chart_current )
5915  // glChartCanvas::Invalidate();
5916 
5917  m_last_cell_adjustvp = m_pcm93chart_current;
5918  }
5919 #endif
5920 
5921  // All the below logic is slow, and really redundant.
5922  // so, declare that cm93 charts do not require adjustment for optimum
5923  // performance.
5924 
5925  if (m_pcm93chart_current) return false;
5926 
5927  // This may be a partial screen render
5928  // If it is, the cmscale value on this render must match the same parameter
5929  // on the last render.
5930  // If it does not, the partial render will not quilt correctly with the
5931  // previous data Detect this case, and indicate that the entire screen must
5932  // be rendered.
5933 
5934  int cmscale = GetCMScaleFromVP(
5935  vp_proposed); // This is the scale that should be used, based on the vp
5936 
5937  int cmscale_actual = PrepareChartScale(
5938  vp_proposed, cmscale,
5939  false); // this is the scale that will be used, based on cell coverage
5940 
5941  if (g_bDebugCM93)
5942  printf(" In AdjustVP, adjustment subchart scale is %c\n",
5943  (char)('A' + cmscale_actual - 1));
5944 
5945  // We always need to do a VP adjustment, independent of this method's
5946  // return value. so, do an AdjustVP() based on the chart scale that WILL BE
5947  // USED And be sure to return false if that adjust method suggests so.
5948 
5949  bool single_adjust = false;
5950  if (m_pcm93chart_array[cmscale_actual])
5951  single_adjust =
5952  m_pcm93chart_array[cmscale_actual]->AdjustVP(vp_last, vp_proposed);
5953 
5954  if (m_cmscale != cmscale_actual) return false;
5955 
5956  // In quilt mode, always indicate that the adjusted vp requires a full
5957  // repaint
5958  if (vp_last.b_quilt) return false;
5959 
5960  return single_adjust;
5961 }
5962 
5963 ThumbData *cm93compchart::GetThumbData(int tnx, int tny, float lat, float lon) {
5964  return (ThumbData *)NULL;
5965 }
5966 
5967 InitReturn cm93compchart::CreateHeaderData() {
5968  m_Chart_Scale = 20000000;
5969 
5970  // Read the root directory, getting subdirectories to build a small
5971  // scale coverage region
5972  wxRect extent_rect;
5973 
5974  wxDir dirt(m_prefixComposite);
5975  wxString candidate;
5976  wxRegEx test(_T("[0-9]+"));
5977 
5978  bool b_cont = dirt.GetFirst(&candidate);
5979 
5980  while (b_cont) {
5981  if (test.Matches(candidate) && (candidate.Len() == 8)) {
5982  wxString dir = m_prefixComposite;
5983  dir += candidate;
5984  if (wxDir::Exists(dir)) {
5985  wxFileName name(dir);
5986  wxString num_name = name.GetName();
5987  long number;
5988  if (num_name.ToLong(&number)) {
5989  int ilat = number / 10000;
5990  int ilon = number % 10000;
5991 
5992  int lat_base = (ilat - 270) / 3.;
5993  int lon_base = ilon / 3.;
5994  extent_rect.Union(wxRect(lon_base, lat_base, 20, 20));
5995  }
5996  }
5997  }
5998  b_cont = dirt.GetNext(&candidate);
5999  }
6000 
6001  // Specify the chart coverage
6002  m_FullExtent.ELON = ((double)extent_rect.x + (double)extent_rect.width);
6003  m_FullExtent.WLON = ((double)extent_rect.x);
6004  m_FullExtent.NLAT = ((double)extent_rect.y + (double)extent_rect.height);
6005  m_FullExtent.SLAT = ((double)extent_rect.y);
6006  m_bExtentSet = true;
6007 
6008  // Populate one M_COVR Entry
6009  m_nCOVREntries = 1;
6010  m_pCOVRTablePoints = (int *)malloc(sizeof(int));
6011  *m_pCOVRTablePoints = 4;
6012  m_pCOVRTable = (float **)malloc(sizeof(float *));
6013  float *pf = (float *)malloc(2 * 4 * sizeof(float));
6014  *m_pCOVRTable = pf;
6015  float *pfe = pf;
6016 
6017  *pfe++ = m_FullExtent.NLAT; // LatMax;
6018  *pfe++ = m_FullExtent.WLON; // LonMin;
6019 
6020  *pfe++ = m_FullExtent.NLAT; // LatMax;
6021  *pfe++ = m_FullExtent.ELON; // LonMax;
6022 
6023  *pfe++ = m_FullExtent.SLAT; // LatMin;
6024  *pfe++ = m_FullExtent.ELON; // LonMax;
6025 
6026  *pfe++ = m_FullExtent.SLAT; // LatMin;
6027  *pfe++ = m_FullExtent.WLON; // LonMin;
6028 
6029  return INIT_OK;
6030 }
6031 
6032 cm93_dictionary *cm93compchart::FindAndLoadDictFromDir(const wxString &dir) {
6033  cm93_dictionary *retval = NULL;
6034  cm93_dictionary *pdict = new cm93_dictionary();
6035 
6036  // Quick look at the supplied directory...
6037  if (pdict->LoadDictionary(dir)) return pdict;
6038 
6039  // Otherwise, search for the dictionary files all along the path of the
6040  // passed parameter
6041 
6042  wxString path = dir;
6043  wxString target;
6044  unsigned int i = 0;
6045 
6046  while (i < path.Len()) {
6047  target.Append(path[i]);
6048  if (path[i] == wxFileName::GetPathSeparator()) {
6049  // wxString msg = _T ( " Looking for CM93 dictionary in "
6050  // ); msg.Append ( target ); wxLogMessage ( msg );
6051 
6052  if (pdict->LoadDictionary(target)) {
6053  retval = pdict;
6054  break;
6055  }
6056  }
6057  i++;
6058  }
6059 
6060  if (NULL != retval) // Found it....
6061  return retval;
6062 
6063  // Dictionary was not found in linear path of supplied dir.
6064  // Could be on branch, so, look at entire tree the hard way.
6065 
6066  wxFileName fnc(dir);
6067  wxString found_dict_file_name;
6068 
6069  bool bdone = false;
6070  while (!bdone) {
6071  path = fnc.GetPath(wxPATH_GET_VOLUME); // get path without sep
6072 
6073  wxString msg = _T ( " Looking harder for CM93 dictionary in " );
6074  msg.Append(path);
6075  wxLogMessage(msg);
6076 
6077  if ((path.Len() == 0) || path.IsSameAs(fnc.GetPathSeparator())) {
6078  bdone = true;
6079  wxLogMessage(_T ( "Early break1" ));
6080  break;
6081  }
6082 
6083  // Abort the search loop if the directory tree does not contain some
6084  // indication of CM93
6085  if ((wxNOT_FOUND == path.Lower().Find(_T ( "cm93" )))) {
6086  bdone = true;
6087  wxLogMessage(_T ( "Early break2" ));
6088  break;
6089  }
6090 
6091  // Search here
6092  // This takes a while to search a fully populated cm93 tree....
6093  wxDir dir(path);
6094 
6095  if (dir.IsOpened()) {
6096  // Find the dictionary name, case insensitively
6097  FindCM93Dictionary cm93Dictionary(found_dict_file_name);
6098  dir.Traverse(cm93Dictionary);
6099  bdone = found_dict_file_name.Len() != 0;
6100  }
6101 
6102  fnc.Assign(path); // convert the path to a filename for next loop
6103  }
6104 
6105  if (found_dict_file_name.Len()) {
6106  wxFileName fnd(found_dict_file_name);
6107  wxString dpath =
6108  fnd.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
6109 
6110  if (pdict->LoadDictionary(dpath)) retval = pdict;
6111  }
6112 
6113  if (NULL == retval) delete pdict;
6114 
6115  return retval;
6116 }
6117 
6118 void cm93compchart::CloseandReopenCurrentSubchart(void) {
6119  delete m_pcm93chart_current;
6120  m_pcm93chart_current = NULL;
6121  m_pcm93chart_array[m_cmscale] = NULL;
6122 
6123  SetVPParms(m_vpt);
6124  InvalidateCache();
6125 }
6126 
6127 class CM93OffsetDialog;
6128 
6129 enum {
6130  tlCELL = 0,
6131  tlMCOVR,
6132  tlSCALE,
6133  tlXOFF,
6134  tlYOFF,
6135  tlUXOFF,
6136  tlUYOFF,
6137 }; // OCPNOffsetListCtrl Columns;
6138 
6139 //---------------------------------------------------------------------------------------
6140 // OCPNOffsetListCtrl Definition
6141 //---------------------------------------------------------------------------------------
6142 class OCPNOffsetListCtrl : public wxListCtrl {
6143 public:
6144  OCPNOffsetListCtrl(CM93OffsetDialog *parent, wxWindowID id,
6145  const wxPoint &pos, const wxSize &size, long style);
6146  ~OCPNOffsetListCtrl();
6147 
6148  wxString OnGetItemText(long item, long column) const;
6149  int OnGetItemColumnImage(long item, long column) const;
6150 
6151  CM93OffsetDialog *m_parent;
6152 };
6153 
6154 OCPNOffsetListCtrl::OCPNOffsetListCtrl(CM93OffsetDialog *parent, wxWindowID id,
6155  const wxPoint &pos, const wxSize &size,
6156  long style)
6157  : wxListCtrl(parent, id, pos, size, style) {
6158  m_parent = parent;
6159 }
6160 
6161 OCPNOffsetListCtrl::~OCPNOffsetListCtrl() {}
6162 
6163 wxString OCPNOffsetListCtrl::OnGetItemText(long item, long column) const {
6164  wxString ret;
6165  M_COVR_Desc *pmcd = m_parent->m_pcovr_array[item];
6166 
6167  switch (column) {
6168  case tlCELL: {
6169  ret.Printf(_T ( "%d" ), pmcd->m_cell_index);
6170  if (((int)'0') == pmcd->m_subcell)
6171  ret.Prepend(_T ( "0" ));
6172  else {
6173  char t = (char)pmcd->m_subcell;
6174  wxString p;
6175  p.Printf(_T ( "%c" ), t);
6176  ret.Prepend(p);
6177  }
6178 
6179  break;
6180  }
6181  case tlMCOVR:
6182  ret.Printf(_T ( "%d" ), pmcd->m_object_id);
6183  break;
6184 
6185  case tlSCALE:
6186  ret = m_parent->m_selected_chart_scale_char;
6187  break;
6188 
6189  case tlXOFF:
6190  ret.Printf(_T ( "%g" ), pmcd->transform_WGS84_offset_x);
6191  break;
6192 
6193  case tlYOFF:
6194  ret.Printf(_T ( "%g" ), pmcd->transform_WGS84_offset_y);
6195  break;
6196 
6197  case tlUXOFF:
6198  ret.Printf(_T ( "%6.0f" ), pmcd->user_xoff * pmcd->m_centerlat_cos);
6199  break;
6200 
6201  case tlUYOFF:
6202  ret.Printf(_T ( "%6.0f" ), pmcd->user_yoff * pmcd->m_centerlat_cos);
6203  break;
6204 
6205  default:
6206  break;
6207  }
6208  return ret;
6209 }
6210 
6211 int OCPNOffsetListCtrl::OnGetItemColumnImage(long item, long column) const {
6212  return -1;
6213 }
6214 
6215 //---------------------------------------------------------------------------------------
6216 // CM93OffsetDialog Implementation
6217 //---------------------------------------------------------------------------------------
6218 
6219 IMPLEMENT_CLASS(CM93OffsetDialog, wxDialog)
6220 
6221 BEGIN_EVENT_TABLE(CM93OffsetDialog, wxDialog)
6222 EVT_CLOSE(CM93OffsetDialog::OnClose)
6223 END_EVENT_TABLE()
6224 
6225 CM93OffsetDialog::CM93OffsetDialog(wxWindow *parent) {
6226  m_pparent = parent;
6227  m_pcompchart = NULL;
6228 
6229  m_xoff = 0;
6230  m_yoff = 0;
6231 
6232  m_selected_list_index = -1;
6233  m_selected_cell_index = 0;
6234 
6235  long wstyle = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER;
6236  wxDialog::Create(parent, -1, _("OpenCPN CM93 Cell Offset Adjustments"),
6237  wxPoint(0, 0), wxSize(800, 200), wstyle);
6238 
6239  wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
6240  SetFont(*qFont);
6241 
6242  // A top-level sizer
6243  wxBoxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL);
6244  SetSizer(topSizer);
6245 
6246  int width;
6247 
6248  long flags = wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES |
6249  wxBORDER_SUNKEN;
6250 #ifndef __WXQT__
6251  flags |= wxLC_VIRTUAL;
6252 #endif
6253 
6254  m_pListCtrlMCOVRs =
6255  new OCPNOffsetListCtrl(this, -1, wxDefaultPosition, wxDefaultSize, flags);
6256 
6257  m_pListCtrlMCOVRs->Connect(
6258  wxEVT_COMMAND_LIST_ITEM_SELECTED,
6259  wxListEventHandler(CM93OffsetDialog::OnCellSelected), NULL, this);
6260 
6261  int dx = GetCharWidth();
6262  int dy = GetCharHeight();
6263 
6264  width = dx * 10;
6265  m_pListCtrlMCOVRs->InsertColumn(tlCELL, _("Cell"), wxLIST_FORMAT_LEFT, width);
6266 
6267  // width = 80;
6268  m_pListCtrlMCOVRs->InsertColumn(tlMCOVR, _("M_COVR ID"), wxLIST_FORMAT_CENTER,
6269  width);
6270 
6271  // width = 80;
6272  m_pListCtrlMCOVRs->InsertColumn(tlSCALE, _("Cell Scale"),
6273  wxLIST_FORMAT_CENTER, width);
6274 
6275  // width = 90;
6276  m_pListCtrlMCOVRs->InsertColumn(tlXOFF, _("wgsox"), wxLIST_FORMAT_CENTER,
6277  width);
6278 
6279  // width = 90;
6280  m_pListCtrlMCOVRs->InsertColumn(tlYOFF, _("wgsoy"), wxLIST_FORMAT_CENTER,
6281  width);
6282 
6283  // width = 90;
6284  m_pListCtrlMCOVRs->InsertColumn(tlUXOFF, _("User X Offset"),
6285  wxLIST_FORMAT_CENTER, width);
6286 
6287  // width = 90;
6288  m_pListCtrlMCOVRs->InsertColumn(tlUYOFF, _("User Y Offset"),
6289  wxLIST_FORMAT_CENTER, width);
6290 
6291  topSizer->Add(m_pListCtrlMCOVRs, 1, wxEXPAND | wxALL, 0);
6292 
6293  wxBoxSizer *boxSizer02 = new wxBoxSizer(wxVERTICAL);
6294  boxSizer02->AddSpacer(22);
6295 
6296  wxStaticText *pStaticTextXoff = new wxStaticText(
6297  this, wxID_ANY,
6298  wxString::Format(_T( "%s (%s)" ), _("User X Offset"), _("meters")),
6299  wxDefaultPosition, wxDefaultSize, 0);
6300  boxSizer02->Add(pStaticTextXoff, 0, wxALL, 0);
6301 
6302  m_pSpinCtrlXoff =
6303  new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
6304  wxSize(50, -1), wxSP_ARROW_KEYS, -10000, 10000, 0);
6305  m_pSpinCtrlXoff->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
6306  wxCommandEventHandler(CM93OffsetDialog::OnOffSetSet),
6307  NULL, this);
6308  boxSizer02->Add(m_pSpinCtrlXoff, 0, wxEXPAND | wxALL, 0);
6309 
6310  wxStaticText *pStaticTextYoff = new wxStaticText(
6311  this, wxID_ANY,
6312  wxString::Format(_T( "%s (%s)" ), _("User Y Offset"), _("meters")),
6313  wxDefaultPosition, wxDefaultSize, 0);
6314  boxSizer02->Add(pStaticTextYoff, 0, wxALL, 0);
6315 
6316  m_pSpinCtrlYoff =
6317  new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
6318  wxSize(50, -1), wxSP_ARROW_KEYS, -10000, 10000, 0);
6319  m_pSpinCtrlYoff->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
6320  wxCommandEventHandler(CM93OffsetDialog::OnOffSetSet),
6321  NULL, this);
6322  boxSizer02->Add(m_pSpinCtrlYoff, 0, wxEXPAND | wxALL, 0);
6323 
6324  m_OKButton = new wxButton(this, wxID_ANY, _("OK"), wxDefaultPosition,
6325  wxDefaultSize, 0);
6326  m_OKButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
6327  wxCommandEventHandler(CM93OffsetDialog::OnOK), NULL,
6328  this);
6329  boxSizer02->Add(m_OKButton, 0, wxALL, 5);
6330  m_OKButton->SetDefault();
6331 
6332  topSizer->Add(boxSizer02, 0, wxEXPAND | wxALL, 2);
6333 
6334  wxSize sz(800, dy * 8);
6335 #ifdef __WXQT__
6336  sz = wxGetDisplaySize();
6337  sz.y = dy * 8;
6338 #endif
6339  SetSize(sz);
6340 
6341  topSizer->Layout();
6342 
6343  // This is silly, but seems to be required for __WXMSW__ build
6344  // If not done, the SECOND invocation of dialog fails to expand the list to
6345  // the full wxSizer size....
6346  SetSize(GetSize().x, GetSize().y - 1);
6347 
6348  SetColorScheme();
6349 
6350  // GetSizer()->SetSizeHints(this);
6351  Centre();
6352 
6353 #ifdef __WXQT__
6354  Move(-1, 100);
6355 #endif
6356 }
6357 
6358 CM93OffsetDialog::~CM93OffsetDialog() { g_pCM93OffsetDialog = NULL; }
6359 
6360 void CM93OffsetDialog::OnClose(wxCloseEvent &event) {
6361  if (m_pcompchart) {
6362  m_pcompchart->SetSpecialOutlineCellIndex(0, 0, 0);
6363 
6364  m_pcompchart->InvalidateCache();
6365 
6366  if (m_pparent) {
6367  m_pparent->Refresh(true);
6368  gFrame->InvalidateAllGL();
6369  }
6370  }
6371 
6372  if (m_pListCtrlMCOVRs->GetItemCount() > m_selected_list_index)
6373  m_pListCtrlMCOVRs->SetItemState(m_selected_list_index, 0,
6374  wxLIST_STATE_SELECTED);
6375 
6376  Hide();
6377 }
6378 
6379 void CM93OffsetDialog::OnOK(wxCommandEvent &event) {
6380 #ifdef __WXQT__
6381  UpdateOffsets();
6382 #endif
6383  Close();
6384 }
6385 
6386 void CM93OffsetDialog::SetCM93Chart(cm93compchart *pchart) {
6387  m_pcompchart = pchart;
6388 }
6389 
6390 void CM93OffsetDialog::OnOffSetSet(wxCommandEvent &event) {
6391  m_xoff = m_pSpinCtrlXoff->GetValue() / m_centerlat_cos;
6392  m_yoff = m_pSpinCtrlYoff->GetValue() / m_centerlat_cos;
6393 
6394 #ifndef __WXQT__
6395  UpdateOffsets();
6396 #endif
6397 }
6398 
6399 void CM93OffsetDialog::UpdateOffsets(void) {
6400  if (m_pcompchart && m_selected_cell_index) {
6401  // Set the offsets of the selected cell/object
6402  m_pcompchart->SetSpecialCellIndexOffset(m_selected_cell_index,
6403  m_selected_object_id,
6404  m_selected_subcell, m_xoff, m_yoff);
6405 
6406  // Closing the current cell will record the offsets in the M_COVR cache
6407  // file Re-opening will then refresh the M_COVRs in the cover set
6408  AbstractPlatform::ShowBusySpinner();
6409  m_pcompchart->CloseandReopenCurrentSubchart();
6410  AbstractPlatform::HideBusySpinner();
6411 
6412  if (m_pparent) {
6413  m_pparent->Refresh(true);
6414  gFrame->InvalidateAllGL();
6415  }
6416  }
6417 }
6418 
6419 void CM93OffsetDialog::SetColorScheme() { DimeControl(this); }
6420 
6421 void CM93OffsetDialog::OnCellSelected(wxListEvent &event) {
6422  if (m_pcompchart) {
6423  m_selected_list_index = event.GetIndex();
6424 
6425  M_COVR_Desc *mcd = m_pcovr_array.Item(event.GetIndex());
6426 
6427  if (m_selected_list_index > m_pListCtrlMCOVRs->GetItemCount())
6428  return; // error
6429 
6430  cm93chart *pchart = m_pcompchart->GetCurrentSingleScaleChart();
6431  if (pchart) {
6432  M_COVR_Desc *cached_mcd = pchart->GetCoverSet()->Find_MCD(
6433  mcd->m_cell_index, mcd->m_object_id, mcd->m_subcell);
6434  if (cached_mcd) {
6435  m_pSpinCtrlXoff->SetValue(
6436  wxRound(cached_mcd->user_xoff * cached_mcd->m_centerlat_cos));
6437  m_pSpinCtrlYoff->SetValue(
6438  wxRound(cached_mcd->user_yoff * cached_mcd->m_centerlat_cos));
6439  }
6440  }
6441 
6442  m_pcompchart->SetSpecialOutlineCellIndex(mcd->m_cell_index,
6443  mcd->m_object_id, mcd->m_subcell);
6444 
6445  m_selected_cell_index = mcd->m_cell_index;
6446  m_selected_object_id = mcd->m_object_id;
6447  m_selected_subcell = mcd->m_subcell;
6448  m_centerlat_cos = mcd->m_centerlat_cos;
6449 
6450  m_pcompchart->InvalidateCache();
6451 
6452  if (m_pparent) {
6453  m_pparent->Refresh(true);
6454  gFrame->InvalidateAllGL();
6455  }
6456  }
6457 }
6458 
6459 void CM93OffsetDialog::UpdateMCOVRList(const ViewPort &vpt) {
6460  if (m_pcompchart) {
6461  // In single chart mode, there is but one cm93chart (i.e. one "scale
6462  // value") shown at any one time
6463  cm93chart *pchart = m_pcompchart->GetCurrentSingleScaleChart();
6464 
6465  if (pchart) {
6466  m_selected_chart_scale_char = pchart->GetScaleChar();
6467 
6468  m_pcovr_array.Clear();
6469 
6470  // Get an array of cell indicies at the current viewport
6471  std::vector<int> cell_array = pchart->GetVPCellArray(vpt);
6472 
6473  ViewPort vp;
6474  vp = vpt;
6475 
6476  // Get the cover set for the cm93chart
6477  // and walk the set looking for matches to the viewport referenced cell
6478  // array This will give us the covr descriptors of interest
6479  covr_set *pcover = pchart->GetCoverSet();
6480 
6481  for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
6482  M_COVR_Desc *mcd = pcover->GetCover(im);
6483 
6484  for (unsigned int icell = 0; icell < cell_array.size(); icell++) {
6485  if (cell_array[icell] == mcd->m_cell_index) {
6486  wxPoint *pwp = pchart->GetDrawBuffer(mcd->m_nvertices);
6487  OCPNRegion rgn = mcd->GetRegion(vp, pwp);
6488 
6489  // if(
6490  // !vp.GetBBox().IntersectOut(mcd->m_covr_bbox))
6491  if (rgn.Contains(0, 0, vpt.pix_width, vpt.pix_height) !=
6492  wxOutRegion)
6493  m_pcovr_array.Add(mcd);
6494  }
6495  }
6496  }
6497 
6498  // Try to find and maintain the correct list selection, even though the
6499  // list contents may have changed
6500  int sel_index = -1;
6501  for (unsigned int im = 0; im < m_pcovr_array.size(); im++) {
6502  M_COVR_Desc *mcd = m_pcovr_array[im];
6503  if ((m_selected_cell_index == mcd->m_cell_index) &&
6504  (m_selected_object_id == mcd->m_object_id) &&
6505  (m_selected_subcell == mcd->m_subcell)) {
6506  sel_index = im;
6507  break;
6508  }
6509  }
6510 
6511  if (!m_pListCtrlMCOVRs->IsVirtual()) {
6512  if (m_pListCtrlMCOVRs->GetItemCount())
6513  m_pListCtrlMCOVRs->DeleteAllItems();
6514 
6515  for (unsigned int i = 0; i < m_pcovr_array.GetCount(); i++) {
6516  wxListItem item;
6517  item.SetId(i);
6518  m_pListCtrlMCOVRs->InsertItem(item);
6519  for (int j = 0; j < tlUYOFF + 1; j++) {
6520  item.SetColumn(j);
6521  item.SetText(m_pListCtrlMCOVRs->OnGetItemText(i, j));
6522  m_pListCtrlMCOVRs->SetItem(item);
6523  }
6524  }
6525  } else {
6526  m_pListCtrlMCOVRs->SetItemCount(m_pcovr_array.GetCount());
6527  }
6528 
6529  if (-1 != sel_index)
6530  m_pListCtrlMCOVRs->SetItemState(sel_index, wxLIST_STATE_SELECTED,
6531  wxLIST_STATE_SELECTED);
6532  else
6533  m_pListCtrlMCOVRs->SetItemState(sel_index, 0,
6534  wxLIST_STATE_SELECTED); // deselect all
6535 
6536  m_pListCtrlMCOVRs->Refresh(true);
6537  }
6538 #ifdef __WXMSW__
6539  m_pListCtrlMCOVRs->Refresh(false);
6540 #endif
6541  }
6542 }
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
Definition: ocpndc.h:58
General purpose GUI support.
Definition: cm93.h:174
Runtime representation of a plugin block.
Definition: Quilt.cpp:867