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