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