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_pixel.h" // for ocpnUSE_DIBSECTION
56#include "ocpn_platform.h"
57#include "pluginmanager.h" // for PlugInManager
58#include "s52plib.h"
59#include "s52s57.h"
60#include "s57chart.h"
61#include "top_frame.h"
62
63#ifdef ocpnUSE_GL
64#include "gl_chart_canvas.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[0] != ';') {
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[0] != ';') {
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() {
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() {
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() {
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 = top_frame::Get()->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 List_Of_M_COVR_Desc::compatibility_iterator node0 =
3843 m_CIB.m_cell_mcovr_list.GetFirst();
3844 if (node0) ret = node0->GetData();
3845 } else {
3846 List_Of_M_COVR_Desc::compatibility_iterator node =
3847 m_CIB.m_cell_mcovr_list.GetFirst();
3848 while (node) {
3849 M_COVR_Desc *pmcd = node->GetData();
3850
3851 if (G_PtInPolygon_FL(pmcd->pvertices, pmcd->m_nvertices, lon, lat)) {
3852 ret = pmcd;
3853 break;
3854 }
3855
3856 node = node->GetNext();
3857 }
3858 }
3859 return ret;
3860}
3861
3862// Find the proper M_COVR record within this current cell for this lat/lon
3863// And return the WGS84 offsets contained within
3864wxPoint2DDouble cm93chart::FindM_COVROffset(double lat, double lon) {
3865 wxPoint2DDouble ret(0., 0.);
3866
3867 // Default is to use the first M_COVR, the usual case
3868 List_Of_M_COVR_Desc::compatibility_iterator node0 =
3869 m_CIB.m_cell_mcovr_list.GetFirst();
3870 if (node0) {
3871 M_COVR_Desc *pmcd0 = node0->GetData();
3872 ret.m_x = pmcd0->transform_WGS84_offset_x;
3873 ret.m_y = pmcd0->transform_WGS84_offset_y;
3874 }
3875
3876 // If there are more than one M_COVR in this cell, need to search
3877 if (m_CIB.m_cell_mcovr_list.GetCount() > 1) {
3878 List_Of_M_COVR_Desc::compatibility_iterator node =
3879 m_CIB.m_cell_mcovr_list.GetFirst();
3880 while (node) {
3881 M_COVR_Desc *pmcd = node->GetData();
3882
3883 if (G_PtInPolygon_FL(pmcd->pvertices, pmcd->m_nvertices, lon, lat)) {
3884 ret.m_x = pmcd->transform_WGS84_offset_x;
3885 ret.m_y = pmcd->transform_WGS84_offset_y;
3886 break;
3887 }
3888
3889 node = node->GetNext();
3890 }
3891 }
3892 return ret;
3893}
3894
3895// Read the cm93 cell file header and create required Chartbase data
3896// structures
3897InitReturn cm93chart::CreateHeaderDataFromCM93Cell() {
3898 // Figure out the scale from the file name
3899 wxFileName fn(m_FullPath);
3900 wxString ext = fn.GetExt();
3901
3902 int scale;
3903 switch ((ext.mb_str())[(size_t)0]) {
3904 case 'Z':
3905 scale = 20000000;
3906 break;
3907 case 'A':
3908 scale = 3000000;
3909 break;
3910 case 'B':
3911 scale = 1000000;
3912 break;
3913 case 'C':
3914 scale = 200000;
3915 break;
3916 case 'D':
3917 scale = 100000;
3918 break;
3919 case 'E':
3920 scale = 50000;
3921 break;
3922 case 'F':
3923 scale = 20000;
3924 break;
3925 case 'G':
3926 scale = 7500;
3927 break;
3928 default:
3929 scale = 20000000;
3930 break;
3931 }
3932
3933 m_Chart_Scale = scale;
3934
3935 // Check with the manager to see if a chart of this scale has been
3936 // processed If there is no manager, punt and open the chart
3937 if (m_pManager) {
3938 bool bproc = false;
3939 switch (m_Chart_Scale) {
3940 case 20000000:
3941 bproc = m_pManager->m_bfoundZ;
3942 break;
3943 case 3000000:
3944 bproc = m_pManager->m_bfoundA;
3945 break;
3946 case 1000000:
3947 bproc = m_pManager->m_bfoundB;
3948 break;
3949 case 200000:
3950 bproc = m_pManager->m_bfoundC;
3951 break;
3952 case 100000:
3953 bproc = m_pManager->m_bfoundD;
3954 break;
3955 case 50000:
3956 bproc = m_pManager->m_bfoundE;
3957 break;
3958 case 20000:
3959 bproc = m_pManager->m_bfoundF;
3960 break;
3961 case 7500:
3962 bproc = m_pManager->m_bfoundG;
3963 break;
3964 }
3965
3966 if (bproc) return INIT_FAIL_NOERROR;
3967
3968 // Inform the manager that a chart of this scale has been processed
3969 switch (m_Chart_Scale) {
3970 case 20000000:
3971 m_pManager->m_bfoundZ = true;
3972 break;
3973 case 3000000:
3974 m_pManager->m_bfoundA = true;
3975 break;
3976 case 1000000:
3977 m_pManager->m_bfoundB = true;
3978 break;
3979 case 200000:
3980 m_pManager->m_bfoundC = true;
3981 break;
3982 case 100000:
3983 m_pManager->m_bfoundD = true;
3984 break;
3985 case 50000:
3986 m_pManager->m_bfoundE = true;
3987 break;
3988 case 20000:
3989 m_pManager->m_bfoundF = true;
3990 break;
3991 case 7500:
3992 m_pManager->m_bfoundG = true;
3993 break;
3994 }
3995 }
3996
3997 // Specify the whole world as chart coverage
3998 m_FullExtent.ELON = 179.0;
3999 m_FullExtent.WLON = -179.0;
4000 m_FullExtent.NLAT = 80.0;
4001 m_FullExtent.SLAT = -80.0;
4002 m_bExtentSet = true;
4003
4004 // Populate one (huge) M_COVR Entry
4005 m_nCOVREntries = 1;
4006 m_pCOVRTablePoints = (int *)malloc(sizeof(int));
4007 *m_pCOVRTablePoints = 4;
4008 m_pCOVRTable = (float **)malloc(sizeof(float *));
4009 float *pf = (float *)malloc(2 * 4 * sizeof(float));
4010 *m_pCOVRTable = pf;
4011 float *pfe = pf;
4012
4013 *pfe++ = m_FullExtent.NLAT; // LatMax;
4014 *pfe++ = m_FullExtent.WLON; // LonMin;
4015
4016 *pfe++ = m_FullExtent.NLAT; // LatMax;
4017 *pfe++ = m_FullExtent.ELON; // LonMax;
4018
4019 *pfe++ = m_FullExtent.SLAT; // LatMin;
4020 *pfe++ = m_FullExtent.ELON; // LonMax;
4021
4022 *pfe++ = m_FullExtent.SLAT; // LatMin;
4023 *pfe++ = m_FullExtent.WLON; // LonMin;
4024
4025 return INIT_OK;
4026}
4027
4028void cm93chart::ProcessMCOVRObjects(int cell_index, char subcell) {
4029 // Extract the m_covr structures inline
4030
4031 Object *pobject = m_CIB.pobject_block; // head of object array
4032
4033 int iObj = 0;
4034 while (iObj < m_CIB.m_nfeature_records) {
4035 if ((pobject != NULL)) {
4036 // Look for and process m_covr object(s)
4037 int iclass = pobject->otype;
4038
4039 wxString sclass = m_pDict->GetClassName(iclass);
4040
4041 if (sclass.IsSameAs("_m_sor")) {
4042 M_COVR_Desc *pmcd =
4043 m_pcovr_set->Find_MCD(cell_index, iObj, (int)subcell);
4044 if (NULL == pmcd) {
4045 Extended_Geometry *xgeom = BuildGeom(pobject, NULL, iObj);
4046
4047 // Decode the attributes, specifically looking for _wgsox, _wgsoy
4048
4049 double tmp_transform_x = 0.;
4050 double tmp_transform_y = 0.;
4051
4052 cm93_attr_block pab(pobject->attributes_block, m_pDict);
4053 for (int jattr = 0; jattr < pobject->n_attributes; jattr++) {
4054 unsigned char *curr_attr = pab.GetNextAttr();
4055 unsigned char iattr = *curr_attr;
4056 wxString sattr = m_pDict->GetAttrName(iattr);
4057 char vtype = m_pDict->GetAttrType(iattr);
4058 unsigned char *aval = curr_attr + 1;
4059
4060 if (vtype == 'R') {
4061 float *pf = (float *)aval;
4062#ifdef __ARM_ARCH
4063 float __attribute__((aligned(16))) tf1;
4064 unsigned char *pucf = (unsigned char *)pf;
4065 memcpy(&tf1, pucf, sizeof(float));
4066 if (sattr.IsSameAs("_wgsox"))
4067 tmp_transform_x = tf1;
4068 else if (sattr.IsSameAs("_wgsoy"))
4069 tmp_transform_y = tf1;
4070#else
4071 if (sattr.IsSameAs("_wgsox"))
4072 tmp_transform_x = *pf;
4073 else if (sattr.IsSameAs("_wgsoy"))
4074 tmp_transform_y = *pf;
4075#endif
4076 }
4077
4078 } // for all attributes
4079
4080 if (NULL != xgeom) {
4081 double lat, lon;
4082
4083 pmcd = new M_COVR_Desc;
4084
4085 // Record unique identifiers for this M_COVR object
4086 pmcd->m_cell_index = cell_index;
4087 pmcd->m_object_id = iObj;
4088 pmcd->m_subcell = (int)subcell;
4089
4090 // Get number of exterior ring points(vertices)
4091 int npta = xgeom->contour_array[0];
4092 float_2Dpt *geoPt = new float_2Dpt[npta + 2]; // vertex array
4093 float_2Dpt *ppt = geoPt;
4094
4095 // Transcribe exterior ring points to vertex array, in Lat/Lon
4096 // coordinates
4097 pmcd->m_covr_lon_max = -1000.;
4098 pmcd->m_covr_lon_min = 1000.;
4099 pmcd->m_covr_lat_max = -1000.;
4100 pmcd->m_covr_lat_min = 1000.;
4101
4102 for (int ip = 0; ip < npta; ip++) {
4103 cm93_point p;
4104 p.x = (int)xgeom->vertex_array[ip + 1].m_x;
4105 p.y = (int)xgeom->vertex_array[ip + 1].m_y;
4106
4107 Transform(&p, 0., 0., &lat, &lon);
4108 ppt->x = lon;
4109 ppt->y = lat;
4110
4111 pmcd->m_covr_lon_max = wxMax(pmcd->m_covr_lon_max, lon);
4112 pmcd->m_covr_lon_min = wxMin(pmcd->m_covr_lon_min, lon);
4113 pmcd->m_covr_lat_max = wxMax(pmcd->m_covr_lat_max, lat);
4114 pmcd->m_covr_lat_min = wxMin(pmcd->m_covr_lat_min, lat);
4115
4116 ppt++;
4117 }
4118 pmcd->m_nvertices = npta;
4119 pmcd->pvertices = geoPt;
4120
4121 pmcd->m_covr_bbox.Set(pmcd->m_covr_lat_min, pmcd->m_covr_lon_min,
4122 pmcd->m_covr_lat_max, pmcd->m_covr_lon_max);
4123
4124 // Capture and store the potential WGS transform offsets grabbed
4125 // during attribute decode
4126 pmcd->transform_WGS84_offset_x = tmp_transform_x;
4127 pmcd->transform_WGS84_offset_y = tmp_transform_y;
4128
4129 pmcd->m_centerlat_cos =
4130 cos(((pmcd->m_covr_lat_min + pmcd->m_covr_lat_max) / 2.) * PI /
4131 180.);
4132
4133 // Add this object to the covr_set
4134 m_pcovr_set->Add_Update_MCD(pmcd);
4135
4136 // Clean up the xgeom
4137 free(xgeom->pvector_index);
4138
4139 delete xgeom;
4140 }
4141 }
4142 }
4143 }
4144
4145 else // objectdef == NULL
4146 break;
4147
4148 pobject++;
4149 iObj++;
4150 }
4151}
4152
4153bool cm93chart::UpdateCovrSet(ViewPort *vpt) {
4154 // Create an array of CellIndexes covering the current viewport
4155 std::vector<int> vpcells = GetVPCellArray(*vpt);
4156
4157 // Check the member covr_set to see if all these viewport cells have had
4158 // their m_covr loaded
4159
4160 for (unsigned int i = 0; i < vpcells.size(); i++) {
4161 // If the cell is not already in the master coverset, go load enough of
4162 // it to get the offsets and outlines.....
4163 if (!m_pcovr_set->IsCovrLoaded(vpcells[i])) {
4164 if (loadcell_in_sequence(vpcells[i], '0')) {
4165 ProcessMCOVRObjects(vpcells[i], '0');
4166 Unload_CM93_Cell(); // all done with this (sub)cell
4167 }
4168 m_pcovr_set->m_cell_hash[vpcells[i]] = 1;
4169
4170 char loadcell_key = 'A'; // starting subcells
4171
4172 // Load the subcells in sequence
4173 // On successful load, add it to the covr set and process the cell
4174 while (loadcell_in_sequence(vpcells[i], loadcell_key)) {
4175 // Extract the m_covr structures inline
4176
4177 ProcessMCOVRObjects(vpcells[i], loadcell_key);
4178
4179 Unload_CM93_Cell(); // all done with this (sub)cell
4180
4181 loadcell_key++;
4182
4183 } // while
4184 } // cell is not in
4185 } // for cellindex array
4186
4187 return true;
4188}
4189
4190bool cm93chart::IsPointInLoadedM_COVR(double xc, double yc) {
4191 // Provisionally revert to older method pending investigation.
4192#if 1
4193 return m_region.Contains(yc, xc);
4194#else
4195 for (unsigned int im = 0; im < m_pcovr_array_loaded.GetCount(); im++) {
4196 if (G_PtInPolygon_FL(m_pcovr_array_loaded[im]->pvertices,
4197 m_pcovr_array_loaded[im]->m_nvertices, xc, yc))
4198 return true;
4199 }
4200 return false;
4201#endif
4202}
4203
4204LLRegion cm93chart::GetValidRegion() { return m_region; }
4205
4206int cm93chart::loadcell_in_sequence(int cellindex, char subcell) {
4207 int rv = loadsubcell(cellindex, subcell);
4208
4209 return rv;
4210}
4211
4212int cm93chart::loadsubcell(int cellindex, wxChar sub_char) {
4213 // Create the file name
4214
4215 int ilat = cellindex / 10000;
4216 int ilon = cellindex % 10000;
4217
4218 if (g_bDebugCM93) {
4219 double dlat = m_dval / 3.;
4220 double dlon = m_dval / 3.;
4221 double lat, lon;
4222 Get_CM93_Cell_Origin(cellindex, GetNativeScale(), &lat, &lon);
4223 printf(
4224 "\n Attempting loadcell %d scale %lc, sub_char %lc at lat: %g/%g "
4225 "lon:%g/%g\n",
4226 cellindex, wxChar(m_scalechar[0]), sub_char, lat, lat + dlat, lon,
4227 lon + dlon);
4228 }
4229
4230 int jlat = (int)(((ilat - 30) / m_dval) * m_dval) + 30; // normalize
4231 int jlon = (int)((ilon / m_dval) * m_dval);
4232
4233 int ilatroot = (((ilat - 30) / 60) * 60) + 30;
4234 int ilonroot = (ilon / 60) * 60;
4235
4236 wxString file;
4237 file.Printf("%04d%04d.", jlat, jlon);
4238 file += m_scalechar;
4239 file[0] = sub_char;
4240
4241 // We prefer to make use of the NoFind array to avoid file system access to
4242 // cells known not to exist. However, when the arra becomes "large", then
4243 // searching the array becomes slower than actually accessing the file system.
4244 // So, detect this case, and skip the NoFind array if the array size is larger
4245 // than nnn items. "nnn" determined by experimentation/intuition. Could also
4246 // be platform dependent.
4247 bool b_useNoFind = true;
4248 if (m_noFindArray.GetCount() > 500) b_useNoFind = false;
4249
4250 wxString fileroot;
4251 fileroot.Printf("%04d%04d", ilatroot, ilonroot);
4252 appendOSDirSep(&fileroot);
4253 fileroot.append(m_scalechar);
4254 appendOSDirSep(&fileroot);
4255 wxString key = fileroot;
4256 key.append(file);
4257 fileroot.Prepend(m_prefix);
4258
4259 file.Prepend(fileroot);
4260
4261 if (g_bDebugCM93) {
4262 char sfile[200];
4263 strncpy(sfile, file.mb_str(), 199);
4264 sfile[199] = 0;
4265 printf(" filename: %s\n", sfile);
4266 }
4267
4268 bool bfound = false;
4269 wxString compfile;
4270 if (b_useNoFind) {
4271 if (m_noFindArray.Index(key) == wxNOT_FOUND) {
4272 if (::wxFileExists(file))
4273 bfound = true;
4274 else
4275 m_noFindArray.Add(key);
4276 }
4277 } else {
4278 if (::wxFileExists(file)) bfound = true;
4279 ;
4280 }
4281
4282 if (!bfound) { // try compressed version
4283 if (b_useNoFind) {
4284 if (m_noFindArray.Index(key + ".xz") == wxNOT_FOUND) {
4285 if (::wxFileExists(file + ".xz")) {
4286 compfile = file + ".xz";
4287 }
4288 } else {
4289 m_noFindArray.Add(key + ".xz");
4290 }
4291 } else {
4292 if (::wxFileExists(file + ".xz")) compfile = file + ".xz";
4293 }
4294 }
4295
4296 // Try again with alternate scale character
4297 if (!bfound && !compfile.Length()) {
4298 // Try with alternate case of m_scalechar
4299 wxString new_scalechar = m_scalechar.Lower();
4300
4301 wxString file1;
4302 file1.Printf("%04d%04d.", jlat, jlon);
4303 file1 += new_scalechar;
4304 file1[0] = sub_char;
4305
4306 fileroot.Printf("%04d%04d", ilatroot, ilonroot);
4307 appendOSDirSep(&fileroot);
4308 fileroot.append(new_scalechar);
4309 appendOSDirSep(&fileroot);
4310 key = fileroot;
4311 key.append(file1);
4312
4313 fileroot.Prepend(m_prefix);
4314
4315 file1.Prepend(fileroot);
4316
4317 if (b_useNoFind) {
4318 if (m_noFindArray.Index(key) == wxNOT_FOUND) {
4319 if (::wxFileExists(file1)) {
4320 bfound = true;
4321 file = file1; // found the file as lowercase, substitute the name
4322 } else {
4323 m_noFindArray.Add(key);
4324 }
4325 }
4326 } else {
4327 if (::wxFileExists(file1)) {
4328 bfound = true;
4329 file = file1; // found the file as lowercase, substitute the name
4330 }
4331 }
4332
4333 if (!bfound) { // try compressed version
4334 if (b_useNoFind) {
4335 if (m_noFindArray.Index(key + ".xz") == wxNOT_FOUND) {
4336 if (::wxFileExists(file1 + ".xz"))
4337 compfile = file1 + ".xz";
4338 else
4339 m_noFindArray.Add(key + ".xz");
4340 }
4341 } else {
4342 if (::wxFileExists(file1 + ".xz")) compfile = file1 + ".xz";
4343 }
4344 }
4345 }
4346
4347 if (g_bDebugCM93) {
4348 printf("noFind count: %d\n", (int)m_noFindArray.GetCount());
4349 }
4350
4351 if (!bfound && !compfile.Length()) return 0;
4352
4353 // File is known to exist
4354
4355 wxString msg("Loading CM93 cell ");
4356 msg += file;
4357 wxLogMessage(msg);
4358
4359 // Set the member variable to be the actual file name for use in single
4360 // chart mode info display
4361 m_LastFileName = file;
4362
4363 // Decompress if needed
4364 if (compfile.Length()) {
4365 file = wxFileName::CreateTempFileName(wxFileName(compfile).GetFullName());
4366 if (!DecompressXZFile(compfile, file)) {
4367 wxRemoveFile(file);
4368 return 0;
4369 }
4370 }
4371
4372 if (g_bDebugCM93) {
4373 char str[256];
4374 strncpy(str, msg.mb_str(), 255);
4375 str[255] = 0;
4376 printf(" %s\n", str);
4377 }
4378
4379 // Ingest it
4380 if (!Ingest_CM93_Cell((const char *)file.mb_str(), &m_CIB)) {
4381 wxString msg(" cm93chart Error ingesting ");
4382 msg.Append(file);
4383 wxLogMessage(msg);
4384
4385 if (compfile.Length()) wxRemoveFile(file);
4386 return 0;
4387 }
4388
4389 if (compfile.Length()) wxRemoveFile(file);
4390
4391 return 1;
4392}
4393
4394void cm93chart::SetUserOffsets(int cell_index, int object_id, int subcell,
4395 int xoff, int yoff) {
4396 M_COVR_Desc *pmcd = GetCoverSet()->Find_MCD(cell_index, object_id, subcell);
4397 if (pmcd) {
4398 pmcd->user_xoff = xoff;
4399 pmcd->user_yoff = yoff;
4400 pmcd->m_buser_offsets = true;
4401 }
4402}
4403
4404wxPoint *cm93chart::GetDrawBuffer(int nSize) {
4405 // Reallocate the cm93chart DrawBuffer if it is currently too small
4406 if (nSize > m_nDrawBufferSize) {
4407 wxPoint *tmp = m_pDrawBuffer;
4408 m_pDrawBuffer =
4409 (wxPoint *)realloc(m_pDrawBuffer, sizeof(wxPoint) * (nSize + 1));
4410 if (NULL == m_pDrawBuffer) {
4411 free(tmp);
4412 tmp = NULL;
4413 } else
4414 m_nDrawBufferSize = nSize + 1;
4415 }
4416 return m_pDrawBuffer;
4417}
4418
4419//-----------------------------------------------------------------------------------------------
4420// cm93manager Implementation
4421//-----------------------------------------------------------------------------------------------
4422
4423cm93manager::cm93manager() {
4424 m_pcm93Dict = NULL;
4425
4426 m_bfoundA = false;
4427 m_bfoundB = false;
4428 m_bfoundC = false;
4429 m_bfoundD = false;
4430 m_bfoundE = false;
4431 m_bfoundF = false;
4432 m_bfoundG = false;
4433 m_bfoundZ = false;
4434}
4435
4436cm93manager::~cm93manager() { delete m_pcm93Dict; }
4437
4438bool cm93manager::Loadcm93Dictionary(const wxString &name) {
4439 // Find and load cm93_dictionary
4440 if (!m_pcm93Dict) {
4441 m_pcm93Dict = FindAndLoadDict(name);
4442
4443 if (!m_pcm93Dict) {
4444 wxLogMessage(" Cannot load CM93 Dictionary.");
4445 return false;
4446 }
4447
4448 if (!m_pcm93Dict->IsOk()) {
4449 wxLogMessage(" Error in loading CM93 Dictionary.");
4450 delete m_pcm93Dict;
4451 m_pcm93Dict = NULL;
4452 return false;
4453 ;
4454 }
4455 } else if (!m_pcm93Dict->IsOk()) {
4456 wxLogMessage(" CM93 Dictionary is not OK.");
4457 return false;
4458 }
4459
4460 return true;
4461}
4462
4463cm93_dictionary *cm93manager::FindAndLoadDict(const wxString &file) {
4464 cm93_dictionary *retval = NULL;
4465 cm93_dictionary *pdict = new cm93_dictionary();
4466
4467 // Search for the dictionary files all along the path of the passed
4468 // parameter filename
4469
4470 wxFileName fn(file);
4471 wxString path = fn.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
4472 wxString target;
4473 unsigned int i = 0;
4474
4475 while (i < path.Len()) {
4476 target.Append(path[i]);
4477 if (path[i] == fn.GetPathSeparator()) {
4478 if (pdict->LoadDictionary(target)) {
4479 retval = pdict;
4480 break;
4481 }
4482 if (pdict->LoadDictionary(target + "CM93ATTR")) {
4483 retval = pdict;
4484 break;
4485 }
4486 }
4487 i++;
4488 }
4489
4490 char t[100];
4491 strncpy(t, target.mb_str(), 99);
4492
4493 if (retval == NULL) delete pdict;
4494
4495 return retval;
4496}
4497
4498//----------------------------------------------------------------------------
4499// cm93 Composite Chart object class Implementation
4500//----------------------------------------------------------------------------
4501cm93compchart::cm93compchart() {
4502 m_ChartType = CHART_TYPE_CM93COMP;
4503 m_pDictComposite = NULL;
4504
4505 // Supply a default name for status bar field
4506 m_FullPath = "CM93";
4507
4508 // Set the "Description", so that it paints nice on the screen
4509 m_Description = "CM93Composite";
4510
4511 m_SE = "";
4512 m_datum_str = "WGS84";
4513 m_SoundingsDatum = "Unknown";
4514
4515 for (int i = 0; i < 8; i++) m_pcm93chart_array[i] = NULL;
4516
4517 m_pcm93chart_current = NULL;
4518
4519 m_cmscale = -1;
4520 m_Chart_Skew = 0.0;
4521
4522 m_pDummyBM = NULL;
4523
4524 SetSpecialOutlineCellIndex(0, 0, 0);
4525 m_last_cell_adjustvp = NULL;
4526
4527 m_pcm93mgr = new cm93manager();
4528}
4529
4530cm93compchart::~cm93compchart() {
4531 if (g_pCM93OffsetDialog) {
4532 g_pCM93OffsetDialog->Hide();
4533 }
4534
4535 for (int i = 0; i < 8; i++) delete m_pcm93chart_array[i];
4536
4537 delete m_pDictComposite;
4538 delete m_pDummyBM;
4539 delete m_pcm93mgr;
4540}
4541
4542InitReturn cm93compchart::Init(const wxString &name, ChartInitFlag flags) {
4543 m_FullPath = name;
4544
4545 wxFileName fn(name);
4546
4547 wxString target;
4548 wxString path;
4549
4550 // Verify that the passed file name exists
4551 if (!fn.FileExists()) {
4552 // It may be a directory
4553 if (wxDir::Exists(name)) {
4554 target = name;
4555 appendOSDirSep(&target);
4556 path = name;
4557 appendOSDirSep(&path);
4558 } else {
4559 wxString msg(" CM93Composite Chart Init cannot find ");
4560 msg.Append(name);
4561 wxLogMessage(msg);
4562 return INIT_FAIL_REMOVE;
4563 }
4564 } else // its a file that exists
4565 {
4566 // Get the cm93 cell database prefix
4567 path = fn.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
4568
4569 // Remove two subdirectories from the passed file name
4570 // This will give a normal CM93 root
4571 wxFileName file_path(path);
4572 file_path.RemoveLastDir();
4573 file_path.RemoveLastDir();
4574
4575 target = file_path.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
4576 }
4577
4578 m_prefixComposite = target;
4579
4580 wxString msg("CM93Composite Chart Root is ");
4581 msg.Append(m_prefixComposite);
4582 wxLogMessage(msg);
4583
4584 if (flags == THUMB_ONLY) {
4585 // SetColorScheme(cs, false);
4586
4587 return INIT_OK;
4588 }
4589
4590 if (flags == HEADER_ONLY) return CreateHeaderData();
4591
4592 // Load the cm93 dictionary if necessary
4593 if (!m_pDictComposite) {
4594 if (!m_pDictComposite) // second try from the file
4595 m_pDictComposite = FindAndLoadDictFromDir(path);
4596
4597 if (!m_pDictComposite) {
4598 wxLogMessage(
4599 " CM93Composite Chart Init cannot locate CM93 dictionary.");
4600 return INIT_FAIL_REMOVE;
4601 }
4602 }
4603
4604 // Set the color scheme
4605 SetColorScheme(m_global_color_scheme, false);
4606
4607 bReadyToRender = true;
4608
4609 return INIT_OK;
4610}
4611
4612void cm93compchart::Activate() {
4613 // if ( g_bShowCM93DetailSlider )
4614 // {
4615 // if ( !pPopupDetailSlider )
4616 // {
4617 // pPopupDetailSlider = new PopUpDSlide ( gFrame, -1 , 0,
4618 // -CM93_ZOOM_FACTOR_MAX_RANGE, CM93_ZOOM_FACTOR_MAX_RANGE,
4619 // wxPoint (
4620 // g_cm93detail_dialog_x,
4621 // g_cm93detail_dialog_y
4622 // ), wxDefaultSize,
4623 // wxSIMPLE_BORDER
4624 // , _T (
4625 // "cm93
4626 // Detail"
4627 // ) );
4628 // }
4629 //
4630 // // Here is an ugly piece of code which prevents the slider
4631 // from taking the keyboard focus
4632 // // Only seems to work for Windows.....
4633 // pPopupDetailSlider->Disable();
4634 // pPopupDetailSlider->Show();
4635 // pPopupDetailSlider->Enable();
4636 // }
4637}
4638
4639void cm93compchart::Deactivate() {
4640 if (pPopupDetailSlider) {
4641 pPopupDetailSlider->Destroy();
4642 pPopupDetailSlider = NULL;
4643 }
4644}
4645
4646double scale_breaks[] = {
4647 5000., // G
4648 15000., // F
4649 40000., // E
4650 150000., // D
4651 300000., // C
4652 1000000., // B
4653 5000000., // A
4654 20000000. // Z
4655};
4656
4657//-----------------------------------------------------------------------
4658// Calculate and Set ViewPoint Constants
4659//-----------------------------------------------------------------------
4660
4661int cm93compchart::GetCMScaleFromVP(const ViewPort &vpt) {
4662 double scale_mpp = 3000 / vpt.view_scale_ppm;
4663
4664 double scale_mpp_adj = scale_mpp;
4665
4666 double scale_breaks_adj[7];
4667
4668 for (int i = 0; i < 7; i++) scale_breaks_adj[i] = scale_breaks[i];
4669
4670 if (g_cm93_zoom_factor) {
4671#if 0
4672 // Completely intuitive exponential curve adjustment
4673 double efactor = ( double ) ( g_cm93_zoom_factor ) * ( .176 / 7. );
4674 for ( int i=0 ; i < 7 ; i++ )
4675 {
4676 double efr = efactor * ( 7 - i );
4677 scale_breaks_adj[i] = scale_breaks[i] * pow ( 10., efr );
4678 if ( g_bDebugCM93 )
4679 printf ( "g_cm93_zoom_factor: %2d efactor: %6g efr:%6g, scale_breaks[i]:%6g scale_breaks_adj[i]: %6g\n",
4680 g_cm93_zoom_factor, efactor, efr, scale_breaks[i], scale_breaks_adj[i] );
4681 }
4682#else
4683 // improved adjustment for small scales
4684 double efr = (double)g_cm93_zoom_factor * pow(scale_mpp, -.05);
4685 scale_mpp_adj *= pow(.6, efr);
4686#endif
4687 }
4688
4689 int cmscale_calc = 7;
4690 int brk_index = 0;
4691 while (cmscale_calc > 0) {
4692 if (scale_mpp_adj < scale_breaks_adj[brk_index]) break;
4693 cmscale_calc--;
4694 brk_index++;
4695 }
4696
4697 // Check for overzoom at the theoretically calcuolated chart scale
4698 // If overzoomed possible, switch to larger scale chart if available
4699 double zoom_factor = scale_breaks[7 - cmscale_calc] / vpt.chart_scale;
4700 if (zoom_factor > 4.0) {
4701 if (cmscale_calc < 7) cmscale_calc++;
4702 }
4703
4704 return cmscale_calc;
4705}
4706
4707void cm93compchart::SetVPParms(const ViewPort &vpt) {
4708 m_vpt = vpt; // save a copy
4709
4710 int cmscale = GetCMScaleFromVP(vpt); // First order calculation of cmscale
4711 m_cmscale = PrepareChartScale(vpt, cmscale, false);
4712
4713 // Continuoesly update the composite chart edition date to the latest cell
4714 // decoded
4715 if (m_pcm93chart_array[cmscale]) {
4716 if (!m_EdDate.IsValid() ||
4717 !m_pcm93chart_array[cmscale]->GetEditionDate().IsValid() ||
4718 m_pcm93chart_array[cmscale]->GetEditionDate().IsLaterThan(m_EdDate))
4719 m_EdDate = m_pcm93chart_array[cmscale]->GetEditionDate();
4720 }
4721}
4722
4723int cm93compchart::PrepareChartScale(const ViewPort &vpt, int cmscale,
4724 bool bOZ_protect) {
4725 if (g_bDebugCM93)
4726 printf("\non SetVPParms, cmscale:%d, %c\n", cmscale,
4727 (char)('A' + cmscale - 1));
4728
4729 wxChar ext;
4730 bool cellscale_is_useable = false;
4731 bool b_nochart = false;
4732
4733 while (!cellscale_is_useable) {
4734 // Open the proper scale chart, if not already open
4735 while (NULL == m_pcm93chart_array[cmscale]) {
4736 if (Is_CM93Cell_Present(m_prefixComposite, vpt.clat, vpt.clon, cmscale)) {
4737 if (g_bDebugCM93)
4738 printf(" chart %c at VP clat/clon is present\n",
4739 (char)('A' + cmscale - 1));
4740
4741 m_pcm93chart_array[cmscale] = new cm93chart();
4742
4743 ext = (wxChar)('A' + cmscale - 1);
4744 if (cmscale == 0) ext = 'Z';
4745
4746 wxString file_dummy = "CM93.";
4747 file_dummy << ext;
4748
4749 m_pcm93chart_array[cmscale]->SetCM93Dict(m_pDictComposite);
4750 m_pcm93chart_array[cmscale]->SetCM93Prefix(m_prefixComposite);
4751 m_pcm93chart_array[cmscale]->SetCM93Manager(m_pcm93mgr);
4752
4753 m_pcm93chart_array[cmscale]->SetColorScheme(m_global_color_scheme);
4754 m_pcm93chart_array[cmscale]->Init(file_dummy, FULL_INIT);
4755 } else if (cmscale == 0) {
4756 // wxString msg;
4757 // msg.Printf ( _T ( " CM93 finds no chart of
4758 // any scale present at Lat/Lon %g %g" ),
4759 // vpt.clat, vpt.clon ); wxLogMessage ( msg );
4760 if (g_bDebugCM93)
4761 printf(
4762 " CM93 finds no chart of any scale present at Lat/Lon %g %g\n",
4763 vpt.clat, vpt.clon);
4764
4765 b_nochart = true;
4766 break;
4767 }
4768
4769 else {
4770 cmscale--; // revert to smaller scale if selected is not present
4771 if (g_bDebugCM93)
4772 printf(" no %c scale chart present, adjusting cmscale to %c\n",
4773 (char)('A' + cmscale), (char)('A' + cmscale - 1));
4774 }
4775 }
4776
4777 m_pcm93chart_current = m_pcm93chart_array[cmscale];
4778
4779 if (b_nochart) {
4780 if (g_bDebugCM93) printf(" b_nochart return\n");
4781
4782 m_pcm93chart_current = NULL;
4783 for (int i = 0; i < 8; i++) {
4784 delete m_pcm93chart_array[i];
4785 m_pcm93chart_array[i] = NULL;
4786 }
4787
4788 return cmscale;
4789 }
4790
4791 if (m_pcm93chart_current) {
4792 // Pass the parameters to the proper scale chart
4793 // Which will also load the needed cell(s)
4794 m_pcm93chart_current->SetVPParms(vpt);
4795
4796 // Check to see if the viewpoint center is actually on the selected
4797 // chart
4798 float yc = vpt.clat;
4799 float xc = vpt.clon;
4800
4801 if (!m_pcm93chart_current->GetCoverSet()->GetCoverCount()) {
4802 if (g_bDebugCM93)
4803 printf(" chart %c has no M_COVR\n", (char)('A' + cmscale - 1));
4804 }
4805
4806 if (m_pcm93chart_current->IsPointInLoadedM_COVR(xc, yc)) {
4807 if (g_bDebugCM93)
4808 printf(" chart %c contains clat/clon\n", (char)('A' + cmscale - 1));
4809
4810 cellscale_is_useable = true;
4811 break;
4812 }
4813
4814 // This commented block assumed that scale 0 coverage is available
4815 // worlwide..... Might not be so with partial CM93 sets
4816 /*
4817 else if(cmscale == 0)
4818 {
4819 cellscale_is_useable = true;
4820 break;
4821 }
4822 */
4823
4824 else if (vpt.b_quilt && vpt.b_FullScreenQuilt) {
4825 ViewPort vp = vpt;
4826
4827 covr_set *pcover = m_pcm93chart_current->GetCoverSet();
4828 if (pcover) {
4829 bool boverlap = false;
4830 for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
4831 M_COVR_Desc *mcd = pcover->GetCover(im);
4832
4833 if (!(vp.GetBBox().IntersectOut(mcd->m_covr_bbox))) {
4834 boverlap = true;
4835 break;
4836 }
4837 }
4838 if (boverlap) cellscale_is_useable = true;
4839 }
4840 }
4841
4842 if (!cellscale_is_useable) {
4843 if (cmscale > 0)
4844 cmscale--; // revert to larger scale if the current scale cells do
4845 // not contain VP
4846 else
4847 b_nochart = true; // we have retired to scale 0, and still no chart
4848 // coverage, so stop already...
4849 if (g_bDebugCM93)
4850 printf(" VP is not in M_COVR, adjusting cmscale to %c\n",
4851 (char)('A' + cmscale - 1));
4852 }
4853 }
4854 }
4855
4856 // Final check the zoom factor
4857 if (bOZ_protect) {
4858 double zoom_factor = scale_breaks[7 - cmscale] / vpt.chart_scale;
4859
4860 if (zoom_factor > 4.0) {
4861 // See if there is a larger scale chart present that will avoid overzoom
4862
4863 float yc = vpt.clat;
4864 float xc = vpt.clon;
4865
4866 // Find out what the smallest available scale is that is not overzoomed
4867 FillScaleArray(vpt.clat, vpt.clon);
4868 int new_scale = cmscale;
4869 bool b_found = false;
4870 while (new_scale <= 7) {
4871 if (m_bScale_Array[new_scale]) {
4872 double new_zoom_factor =
4873 scale_breaks[7 - new_scale] / vpt.chart_scale;
4874
4875 // Do not allow excessive "under-zoom", for performance reasons
4876 if (new_zoom_factor < 1.0) {
4877 b_found = true;
4878 new_scale = cmscale;
4879 break;
4880 }
4881
4882 if (new_zoom_factor < 4.0) {
4883 if (NULL == m_pcm93chart_array[new_scale]) {
4884 m_pcm93chart_array[new_scale] = new cm93chart();
4885
4886 ext = (wxChar)('A' + new_scale - 1);
4887 if (new_scale == 0) ext = 'Z';
4888
4889 wxString file_dummy = "CM93.";
4890 file_dummy << ext;
4891
4892 m_pcm93chart_array[new_scale]->SetCM93Dict(m_pDictComposite);
4893 m_pcm93chart_array[new_scale]->SetCM93Prefix(m_prefixComposite);
4894 m_pcm93chart_array[new_scale]->SetCM93Manager(m_pcm93mgr);
4895
4896 m_pcm93chart_array[new_scale]->SetColorScheme(
4897 m_global_color_scheme);
4898 m_pcm93chart_array[new_scale]->Init(file_dummy, FULL_INIT);
4899 }
4900
4901 m_pcm93chart_array[new_scale]->SetVPParms(vpt);
4902 if (m_pcm93chart_array[new_scale]->IsPointInLoadedM_COVR(xc, yc)) {
4903 b_found = true;
4904 break;
4905 }
4906 }
4907 }
4908 new_scale++;
4909 }
4910 if (b_found) {
4911 cmscale = new_scale;
4912 m_pcm93chart_current = m_pcm93chart_array[cmscale];
4913 }
4914 }
4915 }
4916
4917 return cmscale;
4918}
4919
4920// Populate the member bool array describing which chart scales are available
4921// at any location
4922void cm93compchart::FillScaleArray(double lat, double lon) {
4923 for (int cmscale = 0; cmscale < 8; cmscale++)
4924 m_bScale_Array[cmscale] =
4925 Is_CM93Cell_Present(m_prefixComposite, lat, lon, cmscale);
4926}
4927
4928// These methods simply pass the called parameters to the currently active
4929// cm93chart
4930
4931wxString cm93compchart::GetPubDate() {
4932 wxString data;
4933
4934 if (NULL != m_pcm93chart_current)
4935
4936 data.Printf("%4d", m_current_cell_pub_date);
4937 else
4938 data = "????";
4939 return data;
4940}
4941
4942int cm93compchart::GetNativeScale() {
4943 if (m_pcm93chart_current)
4944 return m_pcm93chart_current->GetNativeScale();
4945 else
4946 return (int)1e8;
4947}
4948
4949double cm93compchart::GetNormalScaleMin(double canvas_scale_factor,
4950 bool b_allow_overzoom) {
4951 double oz_factor;
4952 oz_factor = 40.;
4953
4954 if (m_pcm93chart_current) {
4955 int cmscale = 0;
4956 if (m_pcm93chart_current->m_last_vp.IsValid()) {
4957 FillScaleArray(m_pcm93chart_current->m_last_vp.clat,
4958 m_pcm93chart_current->m_last_vp.clon);
4959
4960 // Find out what the smallest available scale is
4961 cmscale = 7;
4962 while (cmscale > 0) {
4963 if (m_bScale_Array[cmscale]) break;
4964 cmscale--;
4965 }
4966 }
4967
4968 // And return a sensible minimum scale, allowing selected overzoom.
4969 switch (cmscale) {
4970 case 0:
4971 return 20000000. / oz_factor; // Z
4972 case 1:
4973 return 3000000. / oz_factor; // A
4974 case 2:
4975 return 1000000. / oz_factor; // B
4976 case 3:
4977 return 200000. / oz_factor; // C
4978 case 4:
4979 return 100000. / oz_factor; // D
4980 case 5:
4981 return 50000. / oz_factor; // E
4982 case 6:
4983 return 20000. / oz_factor; // F
4984 case 7:
4985 return 500.; // G
4986 default:
4987 return 500. / oz_factor;
4988 }
4989 } else
4990 return 500.;
4991}
4992
4993double cm93compchart::GetNormalScaleMax(double canvas_scale_factor,
4994 int canvas_width) {
4995 return (180. / 360.) * PI * 2 *
4996 (WGS84_semimajor_axis_meters / (canvas_width / canvas_scale_factor));
4997 // return 1.0e8;
4998}
4999
5000wxPoint GetPixFromLLVP(double lat, double lon, const ViewPort &VPoint) {
5001 // Inline the Simple Mercator Transform for performance reasons
5002 double easting, northing;
5003
5004 double s, y3, s0, y30;
5005 double z = WGS84_semimajor_axis_meters * mercator_k0;
5006
5007 double xlon = lon;
5008
5009 /* Make sure lon and lon0 are same phase */
5010 if (lon * VPoint.clon < 0.) {
5011 if (lon < 0.)
5012 xlon += 360.;
5013 else
5014 xlon -= 360.;
5015 }
5016
5017 // And choose the closest direction
5018 if (fabs(xlon - VPoint.clon) > 180.) {
5019 if (xlon > VPoint.clon)
5020 xlon -= 360.;
5021 else
5022 xlon += 360.;
5023 }
5024 easting = (xlon - VPoint.clon) * DEGREE * z;
5025
5026 s = sin(lat * DEGREE);
5027 y3 = (.5 * log((1 + s) / (1 - s))) * z;
5028
5029 s0 = sin(VPoint.clat * DEGREE);
5030 y30 = (.5 * log((1 + s0) / (1 - s0))) * z;
5031 northing = y3 - y30;
5032
5033 wxPoint r;
5034
5035 double epix = easting * VPoint.view_scale_ppm;
5036 double npix = northing * VPoint.view_scale_ppm;
5037 r.x = (int)round((VPoint.pix_width / 2) + epix);
5038 r.y = (int)round((VPoint.pix_height / 2) - npix);
5039
5040 return r;
5041}
5042
5043// extern void catch_signals(int signo);
5044
5045void cm93compchart::GetValidCanvasRegion(const ViewPort &VPoint,
5046 OCPNRegion *pValidRegion) {
5047 OCPNRegion screen_region(0, 0, VPoint.pix_width, VPoint.pix_height);
5048 OCPNRegion ret = GetValidScreenCanvasRegion(
5049 VPoint, g_bopengl ? VPoint.rv_rect : screen_region);
5050 *pValidRegion = ret;
5051}
5052
5053OCPNRegion cm93compchart::GetValidScreenCanvasRegion(
5054 const ViewPort &VPoint, const OCPNRegion &ScreenRegion) {
5055 OCPNRegion ret_region;
5056
5057 ViewPort vp = VPoint;
5058
5059 vp.rotation = 0.;
5060
5061 if (m_pcm93chart_current) {
5062 int chart_native_scale = m_pcm93chart_current->GetNativeScale();
5063
5064 for (unsigned int im = 0;
5065 im < m_pcm93chart_current->m_pcovr_array_loaded.GetCount(); im++) {
5066 M_COVR_Desc *pmcd = (m_pcm93chart_current->m_pcovr_array_loaded[im]);
5067
5068 // We can make a quick test based on the bbox of the M_COVR and the
5069 // bbox of the ViewPort
5070
5071 if (vp.GetBBox().IntersectOut(pmcd->m_covr_bbox)) continue;
5072
5073 wxPoint *DrawBuf = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices);
5074
5075 OCPNRegion rgn_covr = vp.GetVPRegionIntersect(
5076 ScreenRegion, pmcd->m_nvertices, (float *)pmcd->pvertices,
5077 chart_native_scale, DrawBuf);
5078
5079 if (rgn_covr.IsOk()) // not empty
5080 ret_region.Union(rgn_covr);
5081 }
5082
5083 } else
5084 ret_region.Union(OCPNRegion(0, 0, 1, 1));
5085
5086 return ret_region;
5087}
5088
5089LLRegion cm93compchart::GetValidRegion() {
5090 if (m_pcm93chart_current) return m_pcm93chart_current->GetValidRegion();
5091
5092 return LLRegion(); // empty region
5093}
5094
5095bool cm93compchart::RenderRegionViewOnGL(const wxGLContext &glc,
5096 const ViewPort &VPoint,
5097 const OCPNRegion &RectRegion,
5098 const LLRegion &Region) {
5099 SetVPParms(VPoint);
5100
5101 if (g_pCM93OffsetDialog && g_pCM93OffsetDialog->IsShown())
5102 g_pCM93OffsetDialog->UpdateMCOVRList(VPoint);
5103
5104 return DoRenderRegionViewOnGL(glc, VPoint, RectRegion, Region);
5105}
5106
5107bool cm93compchart::DoRenderRegionViewOnGL(const wxGLContext &glc,
5108 const ViewPort &VPoint,
5109 const OCPNRegion &RectRegion,
5110 const LLRegion &Region) {
5111 // g_bDebugCM93 = true;
5112
5113 // CALLGRIND_START_INSTRUMENTATION
5114
5115 ViewPort vp = VPoint;
5116
5117 bool render_return = false;
5118 if (m_pcm93chart_current == 0) return render_return;
5119
5120 {
5121 // This will be done later, in s57chart base class render method
5123
5124 // Check the current chart scale to see if it covers the requested region
5125 // totally
5126 if (VPoint.b_quilt) {
5127 LLRegion vpr_empty = Region;
5128 LLRegion chart_region = GetValidRegion();
5129
5130 // old method which draws the regions from large to small scale, then
5131 // finishes with the largest scale. This is broken on systems with broken
5132 // clipping regions
5133
5134 // So we modify the algorithm as follows:
5135 // a. Calculate the region patches from large scale to small scale,
5136 // starting with the Reference scale, and
5137 // ending when the total region requested is full.
5138 // b. Save the calculated patches in an array as they are generated.
5139 // c. Render the regions/scales saved in the array in reverse order, from
5140 // small scale to large scale. d. Finally, render the Reference
5141 // region/scale.
5142 //
5143 // This logic has the advantage that only the minimum necessary Object
5144 // rendering is actually performed, and only within the minimum necessary
5145 // region.
5146
5147 if (!chart_region.Empty()) vpr_empty.Subtract(chart_region);
5148
5149 if (!vpr_empty.Empty() &&
5150 m_cmscale) // This chart scale does not fully cover the region
5151 {
5152 // Save the current cm93 chart scale for restoration later
5153 int cmscale_save = m_cmscale;
5154
5155 LLRegion region_vect[8];
5156
5157 // Render smaller scale cells the entire requested region is full
5158
5159 while (!vpr_empty.Empty() && m_cmscale) {
5160 // get the next smaller scale chart
5161 m_cmscale = PrepareChartScale(vp, m_cmscale - 1, false);
5162
5163 if (m_pcm93chart_current) {
5164 LLRegion sscale_region = GetValidRegion();
5165
5166 // Save the calculated per-scale region in the array
5167 region_vect[m_cmscale] = sscale_region;
5168 region_vect[m_cmscale].Intersect(vpr_empty);
5169 // Only need to render that part of the vp that is not yet full
5170 // Update the remaining empty region
5171 vpr_empty.Subtract(sscale_region);
5172 }
5173
5174 } // while
5175
5176 // Render all non-empty regions saved in the array, from small to large
5177 // scale.
5178 for (int i = 0; i < 8; i++) {
5179 if (!region_vect[i].Empty()) {
5180 m_cmscale = PrepareChartScale(vp, i, false);
5181 if (m_pcm93chart_current)
5182 render_return |= m_pcm93chart_current->RenderRegionViewOnGL(
5183 glc, vp, RectRegion, region_vect[i]);
5184 }
5185 }
5186
5187 // restore the base chart pointer
5188 m_cmscale = cmscale_save;
5189 m_pcm93chart_current = m_pcm93chart_array[m_cmscale];
5190 }
5191
5192 // Render the on-top Reference region/scale
5193 if (m_pcm93chart_current) {
5194 render_return |= m_pcm93chart_current->RenderRegionViewOnGL(
5195 glc, vp, RectRegion, Region);
5196 m_Name = m_pcm93chart_current->GetName();
5197 }
5198
5199 } else // Single chart mode
5200 {
5201 if (m_pcm93chart_current) {
5202 render_return = m_pcm93chart_current->RenderRegionViewOnGL(
5203 glc, vp, RectRegion, Region);
5204 m_Name = m_pcm93chart_current->GetLastFileName();
5205 }
5206 }
5207 }
5208
5209 if (VPoint.m_projection_type != PROJECTION_MERCATOR)
5210 return render_return; // TODO: fix below for non-mercator
5211
5212 if (!m_pcm93chart_current) return render_return;
5213
5214 // Render the cm93 cell's M_COVR outlines if called for
5215 if (m_cell_index_special_outline) {
5216#ifdef ocpnUSE_GL
5217 wxGLCanvas *glc = top_frame::Get()->GetWxGlCanvas();
5218 ocpnDC dc(*glc);
5219#else
5220 ocpnDC dc;
5221#endif
5222 dc.SetVP(VPoint);
5223
5224 covr_set *pcover = m_pcm93chart_current->GetCoverSet();
5225
5226 for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
5227 M_COVR_Desc *pmcd = pcover->GetCover(im);
5228 if ((pmcd->m_cell_index == m_cell_index_special_outline) &&
5229 (pmcd->m_object_id == m_object_id_special_outline) &&
5230 (pmcd->m_subcell == m_subcell_special_outline))
5231
5232 {
5233 // Draw this MCD's represented outline
5234
5235 // Case: vpBBox is completely inside the mcd box
5236 // if(!(
5237 // vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)) ||
5238 // !( vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)))
5239 {
5240 float_2Dpt *p = pmcd->pvertices;
5241 wxPoint *pwp = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices);
5242
5243 for (int ip = 0; ip < pmcd->m_nvertices; ip++) {
5244 double plon = p->x;
5245 if (fabs(plon - VPoint.clon) > 180.) {
5246 if (plon > VPoint.clon)
5247 plon -= 360.;
5248 else
5249 plon += 360.;
5250 }
5251
5252 double easting, northing, epix, npix;
5253 toSM(p->y, plon + 360., VPoint.clat, VPoint.clon + 360, &easting,
5254 &northing);
5255
5256 // Outlines stored in MCDs are not adjusted for offsets
5257 // easting -=
5258 // pmcd->transform_WGS84_offset_x;
5259 easting -= pmcd->user_xoff;
5260 // northing -=
5261 // pmcd->transform_WGS84_offset_y;
5262 northing -= pmcd->user_yoff;
5263
5264 epix = easting * VPoint.view_scale_ppm;
5265 npix = northing * VPoint.view_scale_ppm;
5266
5267 pwp[ip].x = (int)round((VPoint.pix_width / 2) + epix);
5268 pwp[ip].y = (int)round((VPoint.pix_height / 2) - npix);
5269
5270 p++;
5271 }
5272
5273 bool btest = true;
5274 if (btest) {
5275 wxPen pen(wxTheColourDatabase->Find("YELLOW"), 3);
5276 wxDash dash1[2];
5277 dash1[0] = 4; // Long dash
5278 dash1[1] = 4; // Short gap
5279 pen.SetStyle(wxPENSTYLE_USER_DASH);
5280 pen.SetDashes(2, dash1);
5281
5282 dc.SetPen(pen);
5283
5284 for (int iseg = 0; iseg < pmcd->m_nvertices - 1; iseg++) {
5285 int x0 = pwp[iseg].x;
5286 int y0 = pwp[iseg].y;
5287 int x1 = pwp[iseg + 1].x;
5288 int y1 = pwp[iseg + 1].y;
5289
5290 ClipResult res = cohen_sutherland_line_clip_i(
5291 &x0, &y0, &x1, &y1, 0, VPoint.pix_width, 0,
5292 VPoint.pix_height);
5293
5294 if (res ==
5295 Invisible) // Do not bother with segments that are invisible
5296 continue;
5297
5298 dc.DrawLine(x0, y0, x1, y1);
5299 }
5300 }
5301 }
5302 }
5303 }
5304 }
5305
5306 return render_return;
5307}
5308
5309bool cm93compchart::RenderRegionViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint,
5310 const OCPNRegion &Region) {
5311 SetVPParms(VPoint);
5312
5313 if (g_pCM93OffsetDialog && g_pCM93OffsetDialog->IsShown())
5314 g_pCM93OffsetDialog->UpdateMCOVRList(VPoint);
5315
5316 return DoRenderRegionViewOnDC(dc, VPoint, Region);
5317}
5318
5319bool cm93compchart::RenderViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint) {
5320 const OCPNRegion vpr(0, 0, VPoint.pix_width, VPoint.pix_height);
5321
5322 SetVPParms(VPoint);
5323
5324 return DoRenderRegionViewOnDC(dc, VPoint, vpr);
5325}
5326
5327bool cm93compchart::DoRenderRegionViewOnDC(wxMemoryDC &dc,
5328 const ViewPort &VPoint,
5329 const OCPNRegion &Region) {
5330 // g_bDebugCM93 = true;
5331
5332 // CALLGRIND_START_INSTRUMENTATION
5333 if (g_bDebugCM93) {
5334 printf("\nOn DoRenderRegionViewOnDC Ref scale is %d, %c\n", m_cmscale,
5335 (char)('A' + m_cmscale - 1));
5336 OCPNRegionIterator upd(Region);
5337 while (upd.HaveRects()) {
5338 wxRect rect = upd.GetRect();
5339 printf(" Region Rect: %d %d %d %d\n", rect.x, rect.y, rect.width,
5340 rect.height);
5341 upd.NextRect();
5342 ;
5343 }
5344 }
5345
5346 ViewPort vp = VPoint;
5347
5348 bool render_return = false;
5349 if (m_pcm93chart_current) {
5350 m_pcm93chart_current->SetVPParms(vp);
5351
5352 // Check the current chart scale to see if it covers the requested region
5353 // totally
5354 if (VPoint.b_quilt) {
5355 OCPNRegion vpr_empty = Region;
5356
5357 OCPNRegion chart_region = GetValidScreenCanvasRegion(vp, Region);
5358
5359 if (g_bDebugCM93) {
5360 printf(
5361 "On DoRenderRegionViewOnDC : Intersecting Ref region rectangles\n");
5362 OCPNRegionIterator upd(chart_region);
5363 while (upd.HaveRects()) {
5364 wxRect rect = upd.GetRect();
5365 printf(" Region Rect: %d %d %d %d\n", rect.x, rect.y, rect.width,
5366 rect.height);
5367 upd.NextRect();
5368 }
5369 }
5370
5371 if (!chart_region.IsEmpty()) vpr_empty.Subtract(chart_region);
5372
5373 if (!vpr_empty.Empty() &&
5374 m_cmscale) // This chart scale does not fully cover the region
5375 {
5376 // Render the target scale chart on a temp dc for safekeeping
5377#ifdef ocpnUSE_DIBSECTION
5378 ocpnMemDC temp_dc;
5379#else
5380 wxMemoryDC temp_dc;
5381#endif
5382 if (!chart_region.IsEmpty())
5383 render_return = m_pcm93chart_current->RenderRegionViewOnDC(
5384 temp_dc, vp, chart_region);
5385 else
5386 render_return = false;
5387
5388 // Save the current cm93 chart pointer for restoration later
5389 cm93chart *m_pcm93chart_save = m_pcm93chart_current;
5390
5391 // Prepare a blank quilt bitmap to build up the quilt upon
5392 // We need to do this in order to avoid polluting any of the
5393 // sub-chart cached bitmaps
5394 if (m_pDummyBM) {
5395 if ((m_pDummyBM->GetWidth() != VPoint.rv_rect.width) ||
5396 (m_pDummyBM->GetHeight() != VPoint.rv_rect.height)) {
5397 delete m_pDummyBM;
5398 m_pDummyBM = NULL;
5399 }
5400 }
5401 if (NULL == m_pDummyBM)
5402 m_pDummyBM =
5403 new wxBitmap(VPoint.rv_rect.width, VPoint.rv_rect.height, -1);
5404
5405#ifdef ocpnUSE_DIBSECTION // Clear the quilt
5406 ocpnMemDC dumm_dc;
5407#else
5408 wxMemoryDC dumm_dc;
5409#endif
5410 dumm_dc.SelectObject(*m_pDummyBM);
5411 dumm_dc.SetBackground(*wxBLACK_BRUSH);
5412 dumm_dc.Clear();
5413
5414 int cmscale_next = m_cmscale;
5415
5416 // Render smaller scale cells onto a temporary DC, blitting the valid
5417 // region onto the quilt dc until the region is full
5418 while (!vpr_empty.Empty() && cmscale_next) {
5419 // get the next smaller scale chart
5420 cmscale_next--;
5421 m_cmscale = PrepareChartScale(vp, cmscale_next, false);
5422#ifdef ocpnUSE_DIBSECTION
5423 ocpnMemDC build_dc;
5424#else
5425 wxMemoryDC build_dc;
5426#endif
5427
5428 if (m_pcm93chart_current) {
5429 if (g_bDebugCM93)
5430 printf(" In DRRVOD, add quilt patch at %d, %c\n", m_cmscale,
5431 (char)('A' + m_cmscale - 1));
5432
5433 m_pcm93chart_current->RenderRegionViewOnDC(build_dc, vp, Region);
5434
5435 OCPNRegion sscale_region = GetValidScreenCanvasRegion(vp, Region);
5436
5437 // Only need to render that part of the vp that is not yet full
5438 sscale_region.Intersect(vpr_empty);
5439
5440 // Blit the smaller scale chart patch onto the target DC
5441 OCPNRegionIterator upd(sscale_region);
5442 while (upd.HaveRects()) {
5443 wxRect rect = upd.GetRect();
5444 dumm_dc.Blit(rect.x, rect.y, rect.width, rect.height, &build_dc,
5445 rect.x, rect.y);
5446 upd.NextRect();
5447 }
5448 build_dc.SelectObject(wxNullBitmap); // safely unmap the bmp
5449
5450 // Update the remaining empty region
5451 if (!sscale_region.IsEmpty()) vpr_empty.Subtract(sscale_region);
5452 }
5453
5454 } // while
5455
5456 // Finally, Blit the target scale chart as saved on temp_dc to quilt
5457 // dc
5458 OCPNRegionIterator updt(chart_region);
5459 while (updt.HaveRects()) {
5460 wxRect rect = updt.GetRect();
5461 dumm_dc.Blit(rect.x, rect.y, rect.width, rect.height, &temp_dc,
5462 rect.x, rect.y);
5463 updt.NextRect();
5464 }
5465 temp_dc.SelectObject(wxNullBitmap); // safely unmap the base chart bmp
5466
5467 // restore the base chart pointer
5468 m_pcm93chart_current = m_pcm93chart_save;
5469
5470 // We can unselect the target from the dummy DC, to avoid having to
5471 // copy it.
5472 dumm_dc.SelectObject(wxNullBitmap);
5473
5474 // And the return dc is the quilt
5475 dc.SelectObject(*m_pDummyBM);
5476
5477 render_return = true;
5478 } else {
5479 m_pcm93chart_current->RenderRegionViewOnDC(dc, vp, Region);
5480 render_return = true;
5481 }
5482 m_Name = m_pcm93chart_current->GetName();
5483
5484 } else // Single chart mode
5485 {
5486 render_return =
5487 m_pcm93chart_current->RenderRegionViewOnDC(dc, vp, Region);
5488 m_Name = m_pcm93chart_current->GetLastFileName();
5489 }
5490
5491 } else {
5492 // one must always return a valid bitmap selected into the specified DC
5493 // Since the CM93 cell is not available at this location, select a dummy
5494 // placeholder
5495 if (m_pDummyBM) {
5496 if ((m_pDummyBM->GetWidth() != VPoint.pix_width) ||
5497 (m_pDummyBM->GetHeight() != VPoint.pix_height)) {
5498 delete m_pDummyBM;
5499 m_pDummyBM = NULL;
5500 }
5501 }
5502
5503 if (NULL == m_pDummyBM)
5504 m_pDummyBM = new wxBitmap(VPoint.pix_width, VPoint.pix_height, -1);
5505
5506 // Clear the bitmap
5507 wxMemoryDC mdc;
5508 mdc.SelectObject(*m_pDummyBM);
5509 mdc.SetBackground(*wxBLACK_BRUSH);
5510 mdc.Clear();
5511 mdc.SelectObject(wxNullBitmap);
5512
5513 dc.SelectObject(*m_pDummyBM);
5514 }
5515
5516 // CALLGRIND_STOP_INSTRUMENTATION
5517
5518 // Render the cm93 cell's M_COVR outlines if called for
5519 if (m_cell_index_special_outline && m_pcm93chart_current) {
5520 covr_set *pcover = m_pcm93chart_current->GetCoverSet();
5521
5522 for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
5523 M_COVR_Desc *pmcd = pcover->GetCover(im);
5524 if ((pmcd->m_cell_index == m_cell_index_special_outline) &&
5525 (pmcd->m_object_id == m_object_id_special_outline) &&
5526 (pmcd->m_subcell == m_subcell_special_outline))
5527
5528 {
5529 // Draw this MCD's represented outline
5530
5531 // Case: vpBBox is completely inside the mcd box
5532 // if(!(
5533 // vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)) ||
5534 // !( vp.vpBBox.IntersectOut(pmcd->m_covr_bbox)))
5535 {
5536 float_2Dpt *p = pmcd->pvertices;
5537 wxPoint *pwp = m_pcm93chart_current->GetDrawBuffer(pmcd->m_nvertices);
5538
5539 for (int ip = 0; ip < pmcd->m_nvertices; ip++) {
5540 double plon = p->x;
5541 if (fabs(plon - VPoint.clon) > 180.) {
5542 if (plon > VPoint.clon)
5543 plon -= 360.;
5544 else
5545 plon += 360.;
5546 }
5547
5548 double easting, northing, epix, npix;
5549 toSM(p->y, plon + 360., VPoint.clat, VPoint.clon + 360, &easting,
5550 &northing);
5551
5552 // Outlines stored in MCDs are not adjusted for offsets
5553 // easting -=
5554 // pmcd->transform_WGS84_offset_x;
5555 easting -= pmcd->user_xoff;
5556 // northing -=
5557 // pmcd->transform_WGS84_offset_y;
5558 northing -= pmcd->user_yoff;
5559
5560 epix = easting * VPoint.view_scale_ppm;
5561 npix = northing * VPoint.view_scale_ppm;
5562
5563 pwp[ip].x = (int)round((VPoint.pix_width / 2) + epix);
5564 pwp[ip].y = (int)round((VPoint.pix_height / 2) - npix);
5565
5566 p++;
5567 }
5568
5569 // Scrub the points
5570 // looking for segments for which the wrong longitude decision was
5571 // made
5572 // TODO all this mole needs to be rethought, again
5573 bool btest = true;
5574 /*
5575 wxPoint p0 = pwp[0];
5576 for(int ip = 1 ; ip < pmcd->m_nvertices
5577 ; ip++)
5578 {
5579 // if(((p0.x > VPoint.pix_width) &&
5580 (pwp[ip].x < 0)) || ((p0.x < 0) && (pwp[ip].x > VPoint.pix_width)))
5581 // btest = false;
5582
5583 p0 = pwp[ip];
5584 }
5585 */
5586 if (btest) {
5587 dc.SetPen(wxPen(wxTheColourDatabase->Find("YELLOW"), 4,
5588 wxPENSTYLE_LONG_DASH));
5589
5590 for (int iseg = 0; iseg < pmcd->m_nvertices - 1; iseg++) {
5591 int x0 = pwp[iseg].x;
5592 int y0 = pwp[iseg].y;
5593 int x1 = pwp[iseg + 1].x;
5594 int y1 = pwp[iseg + 1].y;
5595
5596 ClipResult res = cohen_sutherland_line_clip_i(
5597 &x0, &y0, &x1, &y1, 0, VPoint.pix_width, 0,
5598 VPoint.pix_height);
5599
5600 if (res ==
5601 Invisible) // Do not bother with segments that are invisible
5602 continue;
5603
5604 dc.DrawLine(x0, y0, x1, y1);
5605 }
5606 }
5607 }
5608 }
5609 }
5610 }
5611
5612 return render_return;
5613}
5614
5615void cm93compchart::UpdateRenderRegions(const ViewPort &VPoint) {
5616 OCPNRegion full_screen_region(0, 0, VPoint.rv_rect.width,
5617 VPoint.rv_rect.height);
5618
5619 ViewPort vp = VPoint;
5620
5621 SetVPParms(VPoint);
5622
5623 if (m_pcm93chart_current) {
5624 m_pcm93chart_current->SetVPParms(vp);
5625
5626 // Check the current chart scale to see if it covers the requested region
5627 // totally
5628 if (VPoint.b_quilt) {
5629 // Clear all the subchart regions
5630 for (int i = 0; i < 8; i++) {
5631 if (m_pcm93chart_array[i])
5632 m_pcm93chart_array[i]->m_render_region.Clear();
5633 }
5634
5635 OCPNRegion vpr_empty = full_screen_region;
5636
5637 OCPNRegion chart_region =
5638 GetValidScreenCanvasRegion(vp, full_screen_region);
5639 m_pcm93chart_current->m_render_region = chart_region; // update
5640
5641 if (!chart_region.IsEmpty()) vpr_empty.Subtract(chart_region);
5642
5643 if (!vpr_empty.Empty() &&
5644 m_cmscale) // This chart scale does not fully cover the region
5645 {
5646 // Save the current cm93 chart pointer for restoration later
5647 cm93chart *m_pcm93chart_save = m_pcm93chart_current;
5648
5649 int cmscale_next = m_cmscale;
5650
5651 while (!vpr_empty.Empty() && cmscale_next) {
5652 // get the next smaller scale chart
5653 cmscale_next--;
5654 m_cmscale = PrepareChartScale(vp, cmscale_next, false);
5655
5656 if (m_pcm93chart_current) {
5657 OCPNRegion sscale_region =
5658 GetValidScreenCanvasRegion(vp, full_screen_region);
5659 sscale_region.Intersect(vpr_empty);
5660 m_pcm93chart_current->m_render_region = sscale_region;
5661
5662 // Update the remaining empty region
5663 if (!sscale_region.IsEmpty()) vpr_empty.Subtract(sscale_region);
5664 }
5665
5666 } // while
5667
5668 // restore the base chart pointer
5669 m_pcm93chart_current = m_pcm93chart_save;
5670 }
5671 }
5672 }
5673}
5674
5675void cm93compchart::SetSpecialCellIndexOffset(int cell_index, int object_id,
5676 int subcell, int xoff, int yoff) {
5677 m_special_offset_x = xoff;
5678 m_special_offset_y = yoff;
5679
5680 if (m_pcm93chart_current)
5681 m_pcm93chart_current->SetUserOffsets(cell_index, object_id, subcell, xoff,
5682 yoff);
5683}
5684
5685bool cm93compchart::RenderNextSmallerCellOutlines(ocpnDC &dc, ViewPort &vp,
5686 ChartCanvas *cc) {
5687 if (m_cmscale >= 7) return false;
5688#ifdef ocpnUSE_GL
5689 glChartCanvas *glcc = cc->GetglCanvas();
5690 if (!glcc) return false;
5691#else
5692 return false;
5693#endif
5694
5695 int nss_max;
5696
5697 int nss = m_cmscale + 1;
5698
5699 // A little magic here.
5700 // Drawing all larger scale cell outlines is way too expensive.
5701 // So, stop the loop after we have rendered "something"
5702 // But don't stop at all if the viewport scale is less than 3 million.
5703 // This will have the effect of bringing in outlines of isolated large
5704 // scale cells embedded within small scale cells, like isolated islands in
5705 // the Pacific.
5706 bool bdrawn = false;
5707
5708 nss_max = 7;
5709
5710#if 0 /* only if chart outlines are rendered grounded to the charts */
5711 if(g_bopengl) { /* for opengl: lets keep this simple yet also functioning
5712 unlike the unbounded version (which is interesting)
5713 the small update rectangles normally encountered when panning
5714 can cause too many charts to load */
5715 if(nss_max > m_cmscale+3)
5716 nss_max = m_cmscale+3;
5717 }
5718#endif
5719 while (nss <= nss_max && (!bdrawn || (vp.chart_scale < 3e6))) {
5720 cm93chart *psc = m_pcm93chart_array[nss];
5721
5722 if (!psc) {
5723 m_pcm93chart_array[nss] = new cm93chart();
5724 psc = m_pcm93chart_array[nss];
5725
5726 wxChar ext = (wxChar)('A' + nss - 1);
5727 if (nss == 0) ext = 'Z';
5728
5729 wxString file_dummy = "CM93.";
5730 file_dummy << ext;
5731
5732 psc->SetCM93Dict(m_pDictComposite);
5733 psc->SetCM93Prefix(m_prefixComposite);
5734 psc->SetCM93Manager(m_pcm93mgr);
5735
5736 psc->SetColorScheme(m_global_color_scheme);
5737 psc->Init(file_dummy, FULL_INIT);
5738 }
5739
5740 if (nss != 1) { // skip rendering the A scale outlines
5741
5742 // Make sure the covr bounding box is complete
5743 psc->UpdateCovrSet(&vp);
5744
5745 // Render the chart outlines
5746 covr_set *pcover = psc->GetCoverSet();
5747
5748 for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
5749 M_COVR_Desc *mcd = pcover->GetCover(im);
5750
5751 if (vp.GetBBox().IntersectOut(mcd->m_covr_bbox)) continue;
5752
5753 wxPoint *pwp = psc->GetDrawBuffer(mcd->m_nvertices);
5754 bdrawn = RenderCellOutlines(dc, vp, pwp, mcd);
5755 }
5756 }
5757 nss++;
5758 }
5759
5760#ifdef ocpnUSE_GL
5761 if (g_bopengl) {
5762 glDisable(GL_LINE_STIPPLE);
5763 glDisable(GL_LINE_SMOOTH);
5764 glDisable(GL_BLEND);
5765 }
5766#endif
5767 return true;
5768}
5769
5770bool cm93compchart::RenderCellOutlines(ocpnDC &dc, ViewPort &vp, wxPoint *pwp,
5771 M_COVR_Desc *mcd) {
5772 float_2Dpt *p = mcd->pvertices;
5773 int np = mcd->m_nvertices;
5774
5775 for (int ip = 0; ip < np; ip++, p++) {
5776 pwp[ip] = vp.GetPixFromLL(p->y, p->x);
5777
5778 // Outlines stored in MCDs are not adjusted for offsets
5779 pwp[ip].x -= mcd->user_xoff * vp.view_scale_ppm;
5780 pwp[ip].y -= mcd->user_yoff * vp.view_scale_ppm;
5781 }
5782 // Scrub the points
5783 // looking for segments for which the wrong longitude decision was made
5784 // TODO all this mole needs to be rethought, again
5785 wxPoint p0 = pwp[0];
5786 for (int ip = 1; ip < np; ip++) {
5787 if (((p0.x > vp.pix_width) && (pwp[ip].x < 0)) ||
5788 ((p0.x < 0) && (pwp[ip].x > vp.pix_width)))
5789 return false;
5790
5791 p0 = pwp[ip];
5792 }
5793
5794 dc.DrawLines(mcd->m_nvertices, pwp, 0, 0, false);
5795 return true;
5796}
5797
5798void cm93compchart::GetPointPix(ObjRazRules *rzRules, float rlat, float rlon,
5799 wxPoint *r) {
5800 m_pcm93chart_current->GetPointPix(rzRules, rlat, rlon, r);
5801}
5802
5803void cm93compchart::GetPointPix(ObjRazRules *rzRules, wxPoint2DDouble *en,
5804 wxPoint *r, int nPoints) {
5805 m_pcm93chart_current->GetPointPix(rzRules, en, r, nPoints);
5806}
5807
5808void cm93compchart::GetPixPoint(int pixx, int pixy, double *plat, double *plon,
5809 ViewPort *vpt) {
5810 m_pcm93chart_current->GetPixPoint(pixx, pixy, plat, plon, vpt);
5811}
5812
5813void cm93compchart::UpdateLUPs(s57chart *pOwner) {
5814 for (int i = 0; i < 8; i++) {
5815 if (m_pcm93chart_array[i]) m_pcm93chart_array[i]->UpdateLUPs(pOwner);
5816 }
5817}
5818
5819std::list<S57Obj *> *cm93compchart::GetAssociatedObjects(S57Obj *obj) {
5820 if (m_pcm93chart_current)
5821 return m_pcm93chart_current->GetAssociatedObjects(obj);
5822 else
5823 return NULL;
5824}
5825
5826void cm93compchart::InvalidateCache() {
5827 for (int i = 0; i < 8; i++) {
5828 if (m_pcm93chart_array[i]) m_pcm93chart_array[i]->InvalidateCache();
5829 }
5830}
5831
5832void cm93compchart::ForceEdgePriorityEvaluate() {
5833 for (int i = 0; i < 8; i++) {
5834 if (m_pcm93chart_array[i])
5835 m_pcm93chart_array[i]->ForceEdgePriorityEvaluate();
5836 }
5837}
5838
5839void cm93compchart::SetColorScheme(ColorScheme cs, bool bApplyImmediate) {
5840 m_global_color_scheme = cs;
5841
5842 for (int i = 0; i < 8; i++) {
5843 if (m_pcm93chart_array[i])
5844 m_pcm93chart_array[i]->SetColorScheme(cs, bApplyImmediate);
5845 }
5846}
5847
5848ListOfObjRazRules *cm93compchart::GetObjRuleListAtLatLon(float lat, float lon,
5849 float select_radius,
5850 ViewPort *VPoint,
5851 int selection_mask) {
5852 float alon = lon;
5853
5854 ViewPort vp; // needs a new ViewPort also for ObjectRenderCheck()
5855 vp = *VPoint;
5856
5857 if (!VPoint->b_quilt)
5858 if (m_pcm93chart_current)
5859 return m_pcm93chart_current->GetObjRuleListAtLatLon(lat, alon,
5860 select_radius, &vp);
5861 else {
5862 // As default, return an empty list
5863 ListOfObjRazRules *ret_ptr = new ListOfObjRazRules;
5864 return ret_ptr;
5865 }
5866 else {
5867 UpdateRenderRegions(*VPoint);
5868
5869 // Search all of the subcharts, looking for the one whose render region
5870 // contains the requested point
5871 wxPoint p = VPoint->GetPixFromLL(lat, lon);
5872
5873 for (int i = 0; i < 8; i++) {
5874 if (m_pcm93chart_array[i]) {
5875 if (!m_pcm93chart_array[i]->m_render_region.IsEmpty()) {
5876 if (wxInRegion == m_pcm93chart_array[i]->m_render_region.Contains(p))
5877 return m_pcm93chart_array[i]->GetObjRuleListAtLatLon(
5878 lat, alon, select_radius, &vp, selection_mask);
5879 }
5880 }
5881 }
5882
5883 // As default, return an empty list
5884 ListOfObjRazRules *ret_ptr = new ListOfObjRazRules;
5885
5886 return ret_ptr;
5887 }
5888}
5889
5890std::unordered_map<unsigned, VE_Element *> &cm93compchart::Get_ve_hash() {
5891 return m_pcm93chart_current->Get_ve_hash();
5892}
5893
5894std::unordered_map<unsigned, VC_Element *> &cm93compchart::Get_vc_hash() {
5895 return m_pcm93chart_current->Get_vc_hash();
5896}
5897
5898bool cm93compchart::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) {
5899#ifdef ocpnUSE_GL
5900 if (g_bopengl) {
5901 /* need a full refresh if not in quilted mode, and the cell changed */
5902 // TODO re-add this for multicanvas
5903 // if ( !vp_last.b_quilt && m_last_cell_adjustvp != m_pcm93chart_current )
5904 // glChartCanvas::Invalidate();
5905
5906 m_last_cell_adjustvp = m_pcm93chart_current;
5907 }
5908#endif
5909
5910 // All the below logic is slow, and really redundant.
5911 // so, declare that cm93 charts do not require adjustment for optimum
5912 // performance.
5913
5914 if (m_pcm93chart_current) return false;
5915
5916 // This may be a partial screen render
5917 // If it is, the cmscale value on this render must match the same parameter
5918 // on the last render.
5919 // If it does not, the partial render will not quilt correctly with the
5920 // previous data Detect this case, and indicate that the entire screen must
5921 // be rendered.
5922
5923 int cmscale = GetCMScaleFromVP(
5924 vp_proposed); // This is the scale that should be used, based on the vp
5925
5926 int cmscale_actual = PrepareChartScale(
5927 vp_proposed, cmscale,
5928 false); // this is the scale that will be used, based on cell coverage
5929
5930 if (g_bDebugCM93)
5931 printf(" In AdjustVP, adjustment subchart scale is %c\n",
5932 (char)('A' + cmscale_actual - 1));
5933
5934 // We always need to do a VP adjustment, independent of this method's
5935 // return value. so, do an AdjustVP() based on the chart scale that WILL BE
5936 // USED And be sure to return false if that adjust method suggests so.
5937
5938 bool single_adjust = false;
5939 if (m_pcm93chart_array[cmscale_actual])
5940 single_adjust =
5941 m_pcm93chart_array[cmscale_actual]->AdjustVP(vp_last, vp_proposed);
5942
5943 if (m_cmscale != cmscale_actual) return false;
5944
5945 // In quilt mode, always indicate that the adjusted vp requires a full
5946 // repaint
5947 if (vp_last.b_quilt) return false;
5948
5949 return single_adjust;
5950}
5951
5952ThumbData *cm93compchart::GetThumbData(int tnx, int tny, float lat, float lon) {
5953 return (ThumbData *)NULL;
5954}
5955
5956InitReturn cm93compchart::CreateHeaderData() {
5957 m_Chart_Scale = 20000000;
5958
5959 // Read the root directory, getting subdirectories to build a small
5960 // scale coverage region
5961 wxRect extent_rect;
5962
5963 wxDir dirt(m_prefixComposite);
5964 wxString candidate;
5965 wxRegEx test("[0-9]+");
5966
5967 bool b_cont = dirt.GetFirst(&candidate);
5968
5969 while (b_cont) {
5970 if (test.Matches(candidate) && (candidate.Len() == 8)) {
5971 wxString dir = m_prefixComposite;
5972 dir += candidate;
5973 if (wxDir::Exists(dir)) {
5974 wxFileName name(dir);
5975 wxString num_name = name.GetName();
5976 long number;
5977 if (num_name.ToLong(&number)) {
5978 int ilat = number / 10000;
5979 int ilon = number % 10000;
5980
5981 int lat_base = (ilat - 270) / 3.;
5982 int lon_base = ilon / 3.;
5983 extent_rect.Union(wxRect(lon_base, lat_base, 20, 20));
5984 }
5985 }
5986 }
5987 b_cont = dirt.GetNext(&candidate);
5988 }
5989
5990 // Specify the chart coverage
5991 m_FullExtent.ELON = ((double)extent_rect.x + (double)extent_rect.width);
5992 m_FullExtent.WLON = ((double)extent_rect.x);
5993 m_FullExtent.NLAT = ((double)extent_rect.y + (double)extent_rect.height);
5994 m_FullExtent.SLAT = ((double)extent_rect.y);
5995 m_bExtentSet = true;
5996
5997 // Populate one M_COVR Entry
5998 m_nCOVREntries = 1;
5999 m_pCOVRTablePoints = (int *)malloc(sizeof(int));
6000 *m_pCOVRTablePoints = 4;
6001 m_pCOVRTable = (float **)malloc(sizeof(float *));
6002 float *pf = (float *)malloc(2 * 4 * sizeof(float));
6003 *m_pCOVRTable = pf;
6004 float *pfe = pf;
6005
6006 *pfe++ = m_FullExtent.NLAT; // LatMax;
6007 *pfe++ = m_FullExtent.WLON; // LonMin;
6008
6009 *pfe++ = m_FullExtent.NLAT; // LatMax;
6010 *pfe++ = m_FullExtent.ELON; // LonMax;
6011
6012 *pfe++ = m_FullExtent.SLAT; // LatMin;
6013 *pfe++ = m_FullExtent.ELON; // LonMax;
6014
6015 *pfe++ = m_FullExtent.SLAT; // LatMin;
6016 *pfe++ = m_FullExtent.WLON; // LonMin;
6017
6018 return INIT_OK;
6019}
6020
6021cm93_dictionary *cm93compchart::FindAndLoadDictFromDir(const wxString &dir) {
6022 cm93_dictionary *retval = NULL;
6023 cm93_dictionary *pdict = new cm93_dictionary();
6024
6025 // Quick look at the supplied directory...
6026 if (pdict->LoadDictionary(dir)) return pdict;
6027
6028 // Otherwise, search for the dictionary files all along the path of the
6029 // passed parameter
6030
6031 wxString path = dir;
6032 wxString target;
6033 unsigned int i = 0;
6034
6035 while (i < path.Len()) {
6036 target.Append(path[i]);
6037 if (path[i] == wxFileName::GetPathSeparator()) {
6038 // wxString msg = _T ( " Looking for CM93 dictionary in "
6039 // ); msg.Append ( target ); wxLogMessage ( msg );
6040
6041 if (pdict->LoadDictionary(target)) {
6042 retval = pdict;
6043 break;
6044 }
6045 }
6046 i++;
6047 }
6048
6049 if (NULL != retval) // Found it....
6050 return retval;
6051
6052 // Dictionary was not found in linear path of supplied dir.
6053 // Could be on branch, so, look at entire tree the hard way.
6054
6055 wxFileName fnc(dir);
6056 wxString found_dict_file_name;
6057
6058 bool bdone = false;
6059 while (!bdone) {
6060 path = fnc.GetPath(wxPATH_GET_VOLUME); // get path without sep
6061
6062 wxString msg = " Looking harder for CM93 dictionary in ";
6063 msg.Append(path);
6064 wxLogMessage(msg);
6065
6066 if ((path.Len() == 0) || path.IsSameAs(fnc.GetPathSeparator())) {
6067 bdone = true;
6068 wxLogMessage("Early break1");
6069 break;
6070 }
6071
6072 // Abort the search loop if the directory tree does not contain some
6073 // indication of CM93
6074 if ((wxNOT_FOUND == path.Lower().Find("cm93"))) {
6075 bdone = true;
6076 wxLogMessage("Early break2");
6077 break;
6078 }
6079
6080 // Search here
6081 // This takes a while to search a fully populated cm93 tree....
6082 wxDir dir(path);
6083
6084 if (dir.IsOpened()) {
6085 // Find the dictionary name, case insensitively
6086 FindCM93Dictionary cm93Dictionary(found_dict_file_name);
6087 dir.Traverse(cm93Dictionary);
6088 bdone = found_dict_file_name.Len() != 0;
6089 }
6090
6091 fnc.Assign(path); // convert the path to a filename for next loop
6092 }
6093
6094 if (found_dict_file_name.Len()) {
6095 wxFileName fnd(found_dict_file_name);
6096 wxString dpath =
6097 fnd.GetPath((int)(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME));
6098
6099 if (pdict->LoadDictionary(dpath)) retval = pdict;
6100 }
6101
6102 if (NULL == retval) delete pdict;
6103
6104 return retval;
6105}
6106
6107void cm93compchart::CloseandReopenCurrentSubchart() {
6108 delete m_pcm93chart_current;
6109 m_pcm93chart_current = NULL;
6110 m_pcm93chart_array[m_cmscale] = NULL;
6111
6112 SetVPParms(m_vpt);
6113 InvalidateCache();
6114}
6115
6116class CM93OffsetDialog;
6117
6118enum {
6119 tlCELL = 0,
6120 tlMCOVR,
6121 tlSCALE,
6122 tlXOFF,
6123 tlYOFF,
6124 tlUXOFF,
6125 tlUYOFF,
6126}; // OCPNOffsetListCtrl Columns;
6127
6128//---------------------------------------------------------------------------------------
6129// OCPNOffsetListCtrl Definition
6130//---------------------------------------------------------------------------------------
6131class OCPNOffsetListCtrl : public wxListCtrl {
6132public:
6133 OCPNOffsetListCtrl(CM93OffsetDialog *parent, wxWindowID id,
6134 const wxPoint &pos, const wxSize &size, long style);
6136
6137 wxString OnGetItemText(long item, long column) const;
6138 int OnGetItemColumnImage(long item, long column) const;
6139
6140 CM93OffsetDialog *m_parent;
6141};
6142
6143OCPNOffsetListCtrl::OCPNOffsetListCtrl(CM93OffsetDialog *parent, wxWindowID id,
6144 const wxPoint &pos, const wxSize &size,
6145 long style)
6146 : wxListCtrl(parent, id, pos, size, style) {
6147 m_parent = parent;
6148}
6149
6150OCPNOffsetListCtrl::~OCPNOffsetListCtrl() {}
6151
6152wxString OCPNOffsetListCtrl::OnGetItemText(long item, long column) const {
6153 wxString ret;
6154 M_COVR_Desc *pmcd = m_parent->m_pcovr_array[item];
6155
6156 switch (column) {
6157 case tlCELL: {
6158 ret.Printf("%d", pmcd->m_cell_index);
6159 if (((int)'0') == pmcd->m_subcell)
6160 ret.Prepend("0");
6161 else {
6162 char t = (char)pmcd->m_subcell;
6163 wxString p;
6164 p.Printf("%c", t);
6165 ret.Prepend(p);
6166 }
6167
6168 break;
6169 }
6170 case tlMCOVR:
6171 ret.Printf("%d", pmcd->m_object_id);
6172 break;
6173
6174 case tlSCALE:
6175 ret = m_parent->m_selected_chart_scale_char;
6176 break;
6177
6178 case tlXOFF:
6179 ret.Printf("%g", pmcd->transform_WGS84_offset_x);
6180 break;
6181
6182 case tlYOFF:
6183 ret.Printf("%g", pmcd->transform_WGS84_offset_y);
6184 break;
6185
6186 case tlUXOFF:
6187 ret.Printf("%6.0f", pmcd->user_xoff * pmcd->m_centerlat_cos);
6188 break;
6189
6190 case tlUYOFF:
6191 ret.Printf("%6.0f", pmcd->user_yoff * pmcd->m_centerlat_cos);
6192 break;
6193
6194 default:
6195 break;
6196 }
6197 return ret;
6198}
6199
6200int OCPNOffsetListCtrl::OnGetItemColumnImage(long item, long column) const {
6201 return -1;
6202}
6203
6204//---------------------------------------------------------------------------------------
6205// CM93OffsetDialog Implementation
6206//---------------------------------------------------------------------------------------
6207
6208IMPLEMENT_CLASS(CM93OffsetDialog, wxDialog)
6209
6210BEGIN_EVENT_TABLE(CM93OffsetDialog, wxDialog)
6211EVT_CLOSE(CM93OffsetDialog::OnClose)
6212END_EVENT_TABLE()
6213
6214CM93OffsetDialog::CM93OffsetDialog(wxWindow *parent) {
6215 m_pparent = parent;
6216 m_pcompchart = NULL;
6217
6218 m_xoff = 0;
6219 m_yoff = 0;
6220
6221 m_selected_list_index = -1;
6222 m_selected_cell_index = 0;
6223
6224 long wstyle = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER;
6225 wxDialog::Create(parent, -1, _("OpenCPN CM93 Cell Offset Adjustments"),
6226 wxPoint(0, 0), wxSize(800, 200), wstyle);
6227
6228 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
6229 SetFont(*qFont);
6230
6231 // A top-level sizer
6232 wxBoxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL);
6233 SetSizer(topSizer);
6234
6235 int width;
6236
6237 long flags = wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES |
6238 wxBORDER_SUNKEN;
6239#ifndef __WXQT__
6240 flags |= wxLC_VIRTUAL;
6241#endif
6242
6243 m_pListCtrlMCOVRs =
6244 new OCPNOffsetListCtrl(this, -1, wxDefaultPosition, wxDefaultSize, flags);
6245
6246 m_pListCtrlMCOVRs->Connect(
6247 wxEVT_COMMAND_LIST_ITEM_SELECTED,
6248 wxListEventHandler(CM93OffsetDialog::OnCellSelected), NULL, this);
6249
6250 int dx = GetCharWidth();
6251 int dy = GetCharHeight();
6252
6253 width = dx * 10;
6254 m_pListCtrlMCOVRs->InsertColumn(tlCELL, _("Cell"), wxLIST_FORMAT_LEFT, width);
6255
6256 // width = 80;
6257 m_pListCtrlMCOVRs->InsertColumn(tlMCOVR, _("M_COVR ID"), wxLIST_FORMAT_CENTER,
6258 width);
6259
6260 // width = 80;
6261 m_pListCtrlMCOVRs->InsertColumn(tlSCALE, _("Cell Scale"),
6262 wxLIST_FORMAT_CENTER, width);
6263
6264 // width = 90;
6265 m_pListCtrlMCOVRs->InsertColumn(tlXOFF, _("wgsox"), wxLIST_FORMAT_CENTER,
6266 width);
6267
6268 // width = 90;
6269 m_pListCtrlMCOVRs->InsertColumn(tlYOFF, _("wgsoy"), wxLIST_FORMAT_CENTER,
6270 width);
6271
6272 // width = 90;
6273 m_pListCtrlMCOVRs->InsertColumn(tlUXOFF, _("User X Offset"),
6274 wxLIST_FORMAT_CENTER, width);
6275
6276 // width = 90;
6277 m_pListCtrlMCOVRs->InsertColumn(tlUYOFF, _("User Y Offset"),
6278 wxLIST_FORMAT_CENTER, width);
6279
6280 topSizer->Add(m_pListCtrlMCOVRs, 1, wxEXPAND | wxALL, 0);
6281
6282 wxBoxSizer *boxSizer02 = new wxBoxSizer(wxVERTICAL);
6283 boxSizer02->AddSpacer(22);
6284
6285 wxStaticText *pStaticTextXoff = new wxStaticText(
6286 this, wxID_ANY,
6287 wxString::Format("%s (%s)", _("User X Offset"), _("meters")),
6288 wxDefaultPosition, wxDefaultSize, 0);
6289 boxSizer02->Add(pStaticTextXoff, 0, wxALL, 0);
6290
6291 m_pSpinCtrlXoff =
6292 new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
6293 wxSize(50, -1), wxSP_ARROW_KEYS, -10000, 10000, 0);
6294 m_pSpinCtrlXoff->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
6295 wxCommandEventHandler(CM93OffsetDialog::OnOffSetSet),
6296 NULL, this);
6297 boxSizer02->Add(m_pSpinCtrlXoff, 0, wxEXPAND | wxALL, 0);
6298
6299 wxStaticText *pStaticTextYoff = new wxStaticText(
6300 this, wxID_ANY,
6301 wxString::Format("%s (%s)", _("User Y Offset"), _("meters")),
6302 wxDefaultPosition, wxDefaultSize, 0);
6303 boxSizer02->Add(pStaticTextYoff, 0, wxALL, 0);
6304
6305 m_pSpinCtrlYoff =
6306 new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
6307 wxSize(50, -1), wxSP_ARROW_KEYS, -10000, 10000, 0);
6308 m_pSpinCtrlYoff->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED,
6309 wxCommandEventHandler(CM93OffsetDialog::OnOffSetSet),
6310 NULL, this);
6311 boxSizer02->Add(m_pSpinCtrlYoff, 0, wxEXPAND | wxALL, 0);
6312
6313 m_OKButton = new wxButton(this, wxID_ANY, _("OK"), wxDefaultPosition,
6314 wxDefaultSize, 0);
6315 m_OKButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
6316 wxCommandEventHandler(CM93OffsetDialog::OnOK), NULL,
6317 this);
6318 boxSizer02->Add(m_OKButton, 0, wxALL, 5);
6319 m_OKButton->SetDefault();
6320
6321 topSizer->Add(boxSizer02, 0, wxEXPAND | wxALL, 2);
6322
6323 wxSize sz(800, dy * 8);
6324#ifdef __WXQT__
6325 sz = wxGetDisplaySize();
6326 sz.y = dy * 8;
6327#endif
6328 SetSize(sz);
6329
6330 topSizer->Layout();
6331
6332 // This is silly, but seems to be required for __WXMSW__ build
6333 // If not done, the SECOND invocation of dialog fails to expand the list to
6334 // the full wxSizer size....
6335 SetSize(GetSize().x, GetSize().y - 1);
6336
6337 SetColorScheme();
6338
6339 // GetSizer()->SetSizeHints(this);
6340 Centre();
6341
6342#ifdef __WXQT__
6343 Move(-1, 100);
6344#endif
6345}
6346
6347CM93OffsetDialog::~CM93OffsetDialog() { g_pCM93OffsetDialog = NULL; }
6348
6349void CM93OffsetDialog::OnClose(wxCloseEvent &event) {
6350 if (m_pcompchart) {
6351 m_pcompchart->SetSpecialOutlineCellIndex(0, 0, 0);
6352
6353 m_pcompchart->InvalidateCache();
6354
6355 if (m_pparent) {
6356 m_pparent->Refresh(true);
6357 top_frame::Get()->InvalidateAllGL();
6358 }
6359 }
6360
6361 if (m_pListCtrlMCOVRs->GetItemCount() > m_selected_list_index)
6362 m_pListCtrlMCOVRs->SetItemState(m_selected_list_index, 0,
6363 wxLIST_STATE_SELECTED);
6364
6365 Hide();
6366}
6367
6368void CM93OffsetDialog::OnOK(wxCommandEvent &event) {
6369#ifdef __WXQT__
6370 UpdateOffsets();
6371#endif
6372 Close();
6373}
6374
6375void CM93OffsetDialog::SetCM93Chart(cm93compchart *pchart) {
6376 m_pcompchart = pchart;
6377}
6378
6379void CM93OffsetDialog::OnOffSetSet(wxCommandEvent &event) {
6380 m_xoff = m_pSpinCtrlXoff->GetValue() / m_centerlat_cos;
6381 m_yoff = m_pSpinCtrlYoff->GetValue() / m_centerlat_cos;
6382
6383#ifndef __WXQT__
6384 UpdateOffsets();
6385#endif
6386}
6387
6388void CM93OffsetDialog::UpdateOffsets() {
6389 if (m_pcompchart && m_selected_cell_index) {
6390 // Set the offsets of the selected cell/object
6391 m_pcompchart->SetSpecialCellIndexOffset(m_selected_cell_index,
6392 m_selected_object_id,
6393 m_selected_subcell, m_xoff, m_yoff);
6394
6395 // Closing the current cell will record the offsets in the M_COVR cache
6396 // file Re-opening will then refresh the M_COVRs in the cover set
6397 AbstractPlatform::ShowBusySpinner();
6398 m_pcompchart->CloseandReopenCurrentSubchart();
6399 AbstractPlatform::HideBusySpinner();
6400
6401 if (m_pparent) {
6402 m_pparent->Refresh(true);
6403 top_frame::Get()->InvalidateAllGL();
6404 }
6405 }
6406}
6407
6408void CM93OffsetDialog::SetColorScheme() { DimeControl(this); }
6409
6410void CM93OffsetDialog::OnCellSelected(wxListEvent &event) {
6411 if (m_pcompchart) {
6412 m_selected_list_index = event.GetIndex();
6413
6414 M_COVR_Desc *mcd = m_pcovr_array.Item(event.GetIndex());
6415
6416 if (m_selected_list_index > m_pListCtrlMCOVRs->GetItemCount())
6417 return; // error
6418
6419 cm93chart *pchart = m_pcompchart->GetCurrentSingleScaleChart();
6420 if (pchart) {
6421 M_COVR_Desc *cached_mcd = pchart->GetCoverSet()->Find_MCD(
6422 mcd->m_cell_index, mcd->m_object_id, mcd->m_subcell);
6423 if (cached_mcd) {
6424 m_pSpinCtrlXoff->SetValue(
6425 wxRound(cached_mcd->user_xoff * cached_mcd->m_centerlat_cos));
6426 m_pSpinCtrlYoff->SetValue(
6427 wxRound(cached_mcd->user_yoff * cached_mcd->m_centerlat_cos));
6428 }
6429 }
6430
6431 m_pcompchart->SetSpecialOutlineCellIndex(mcd->m_cell_index,
6432 mcd->m_object_id, mcd->m_subcell);
6433
6434 m_selected_cell_index = mcd->m_cell_index;
6435 m_selected_object_id = mcd->m_object_id;
6436 m_selected_subcell = mcd->m_subcell;
6437 m_centerlat_cos = mcd->m_centerlat_cos;
6438
6439 m_pcompchart->InvalidateCache();
6440
6441 if (m_pparent) {
6442 m_pparent->Refresh(true);
6443 top_frame::Get()->InvalidateAllGL();
6444 }
6445 }
6446}
6447
6448void CM93OffsetDialog::UpdateMCOVRList(const ViewPort &vpt) {
6449 if (m_pcompchart) {
6450 // In single chart mode, there is but one cm93chart (i.e. one "scale
6451 // value") shown at any one time
6452 cm93chart *pchart = m_pcompchart->GetCurrentSingleScaleChart();
6453
6454 if (pchart) {
6455 m_selected_chart_scale_char = pchart->GetScaleChar();
6456
6457 m_pcovr_array.Clear();
6458
6459 // Get an array of cell indicies at the current viewport
6460 std::vector<int> cell_array = pchart->GetVPCellArray(vpt);
6461
6462 ViewPort vp;
6463 vp = vpt;
6464
6465 // Get the cover set for the cm93chart
6466 // and walk the set looking for matches to the viewport referenced cell
6467 // array This will give us the covr descriptors of interest
6468 covr_set *pcover = pchart->GetCoverSet();
6469
6470 for (unsigned int im = 0; im < pcover->GetCoverCount(); im++) {
6471 M_COVR_Desc *mcd = pcover->GetCover(im);
6472
6473 for (unsigned int icell = 0; icell < cell_array.size(); icell++) {
6474 if (cell_array[icell] == mcd->m_cell_index) {
6475 wxPoint *pwp = pchart->GetDrawBuffer(mcd->m_nvertices);
6476 OCPNRegion rgn = mcd->GetRegion(vp, pwp);
6477
6478 // if(
6479 // !vp.GetBBox().IntersectOut(mcd->m_covr_bbox))
6480 if (rgn.Contains(0, 0, vpt.pix_width, vpt.pix_height) !=
6481 wxOutRegion)
6482 m_pcovr_array.Add(mcd);
6483 }
6484 }
6485 }
6486
6487 // Try to find and maintain the correct list selection, even though the
6488 // list contents may have changed
6489 int sel_index = -1;
6490 for (unsigned int im = 0; im < m_pcovr_array.size(); im++) {
6491 M_COVR_Desc *mcd = m_pcovr_array[im];
6492 if ((m_selected_cell_index == mcd->m_cell_index) &&
6493 (m_selected_object_id == mcd->m_object_id) &&
6494 (m_selected_subcell == mcd->m_subcell)) {
6495 sel_index = im;
6496 break;
6497 }
6498 }
6499
6500 if (!m_pListCtrlMCOVRs->IsVirtual()) {
6501 if (m_pListCtrlMCOVRs->GetItemCount())
6502 m_pListCtrlMCOVRs->DeleteAllItems();
6503
6504 for (unsigned int i = 0; i < m_pcovr_array.GetCount(); i++) {
6505 wxListItem item;
6506 item.SetId(i);
6507 m_pListCtrlMCOVRs->InsertItem(item);
6508 for (int j = 0; j < tlUYOFF + 1; j++) {
6509 item.SetColumn(j);
6510 item.SetText(m_pListCtrlMCOVRs->OnGetItemText(i, j));
6511 m_pListCtrlMCOVRs->SetItem(item);
6512 }
6513 }
6514 } else {
6515 m_pListCtrlMCOVRs->SetItemCount(m_pcovr_array.GetCount());
6516 }
6517
6518 if (-1 != sel_index)
6519 m_pListCtrlMCOVRs->SetItemState(sel_index, wxLIST_STATE_SELECTED,
6520 wxLIST_STATE_SELECTED);
6521 else
6522 m_pListCtrlMCOVRs->SetItemState(sel_index, 0,
6523 wxLIST_STATE_SELECTED); // deselect all
6524
6525 m_pListCtrlMCOVRs->Refresh(true);
6526 }
6527#ifdef __WXMSW__
6528 m_pListCtrlMCOVRs->Refresh(false);
6529#endif
6530 }
6531}
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:174
An iterator class for OCPNRegion.
A wrapper class for wxRegion with additional functionality.
Definition ocpn_region.h:37
ViewPort - Core geographic projection and coordinate transformation engine.
Definition viewport.h:56
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
Definition viewport.h:204
int pix_height
Height of the viewport in physical pixels.
Definition viewport.h:233
double rotation
Rotation angle of the viewport in radians.
Definition viewport.h:214
int pix_width
Width of the viewport in physical pixels.
Definition viewport.h:231
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
Definition viewport.cpp:133
double skew
Angular distortion (shear transform) applied to the viewport in radians.
Definition viewport.h:212
void GetLLFromPix(const wxPoint &p, double *lat, double *lon)
Convert physical pixel coordinates on the ViewPort to latitude and longitude.
Definition viewport.h:80
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:409
double clon
Center longitude of the viewport in degrees.
Definition viewport.h:199
double clat
Center latitude of the viewport in degrees.
Definition viewport.h:197
wxPoint GetPixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates.
Definition viewport.cpp:124
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition viewport.h:219
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:473
Represents an S57 format electronic navigational chart in OpenCPN.
Definition s57chart.h:90
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.
OpenCPN Georef utility.
OpenGL chart rendering canvas.
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
Definition gui_lib.cpp:61
General purpose GUI support.
Utility functions.
Optimized wxBitmap Object.
OpenCPN Platform specific support utilities.
@ PI_PRIO_NUM
Number of priority levels.
@ PI_LUPNAME_NUM
Total number of lookup table types.
Layer to use wxDC or opengl.
Tools to send data to plugins.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.
S57 Chart Object.
Definition cm93.h:169
Runtime representation of a plugin block.
Abstract gFrame/MyFrame interface.