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