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