OpenCPN Partial API docs
Loading...
Searching...
No Matches
chartimg.cpp
1/**************************************************************************
2 * Copyright (C) 2015 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 <assert.h>
25
26// For compilers that support precompilation, includes "wx.h".
27#include <wx/wxprec.h>
28
29#ifndef WX_PRECOMP
30#include <wx/wx.h>
31#endif
32
33// Why are these not in wx/prec.h?
34#include <wx/dir.h>
35#include <wx/stream.h>
36#include <wx/wfstream.h>
37#include <wx/tokenzr.h>
38#include <wx/filename.h>
39#include <wx/image.h>
40#include <wx/fileconf.h>
41
43
44#include "config.h"
45#include "chartimg.h"
46#include "ocpn_pixel.h"
47
48#ifndef _MSC_VER
49#include <signal.h>
50#include <setjmp.h>
51#include "navutil.h"
52
53#define OCPN_USE_CONFIG 1
54#endif
55
56#ifndef _MSC_VER
57struct sigaction sa_all_chart;
58struct sigaction sa_all_previous;
59
60sigjmp_buf env_chart; // the context saved by sigsetjmp();
61
62void catch_signals_chart(int signo) {
63 switch (signo) {
64 case SIGSEGV:
65 siglongjmp(env_chart, 1); // jump back to the setjmp() point
66 break;
67
68 default:
69 break;
70 }
71}
72
73#endif
74
75// Missing from MSW include files
76#ifdef _MSC_VER
77typedef __int32 int32_t;
78typedef unsigned __int32 uint32_t;
79typedef __int64 int64_t;
80typedef unsigned __int64 uint64_t;
81#endif
82
83// ----------------------------------------------------------------------------
84// Random Prototypes
85// ----------------------------------------------------------------------------
86
87bool G_FloatPtInPolygon(MyFlPoint *rgpts, int wnumpts, float x, float y);
88
89// ----------------------------------------------------------------------------
90// private classes
91// ----------------------------------------------------------------------------
92
93// ============================================================================
94// ThumbData implementation
95// ============================================================================
96
97ThumbData::ThumbData() { pDIBThumb = NULL; }
98
99ThumbData::~ThumbData() { delete pDIBThumb; }
100
101// ============================================================================
102// Palette implementation
103// ============================================================================
104opncpnPalette::opncpnPalette() {
105 // Index into palette is 1-based, so predefine the first entry as null
106 nFwd = 1;
107 nRev = 1;
108 FwdPalette = (int *)malloc(sizeof(int));
109 RevPalette = (int *)malloc(sizeof(int));
110 FwdPalette[0] = 0;
111 RevPalette[0] = 0;
112}
113
114opncpnPalette::~opncpnPalette() {
115 if (NULL != FwdPalette) free(FwdPalette);
116 if (NULL != RevPalette) free(RevPalette);
117}
118
119// ============================================================================
120// ChartBase implementation
121// ============================================================================
122ChartBase::ChartBase() {
123 m_depth_unit_id = DEPTH_UNIT_UNKNOWN;
124
125 pThumbData = new ThumbData;
126
127 m_global_color_scheme = GLOBAL_COLOR_SCHEME_RGB;
128
129 bReadyToRender = false;
130
131 Chart_Error_Factor = 0;
132
133 m_Chart_Scale = 10000; // a benign value
134 m_Chart_Skew = 0.0;
135
136 m_nCOVREntries = 0;
137 m_pCOVRTable = NULL;
138 m_pCOVRTablePoints = NULL;
139
140 m_nNoCOVREntries = 0;
141 m_pNoCOVRTable = NULL;
142 m_pNoCOVRTablePoints = NULL;
143
144 m_EdDate = wxInvalidDateTime;
145
146 m_lon_datum_adjust = 0.;
147 m_lat_datum_adjust = 0.;
148
149 m_projection = PROJECTION_MERCATOR; // default
150}
151
152ChartBase::~ChartBase() {
153 delete pThumbData;
154
155 // Free the COVR tables
156
157 for (unsigned int j = 0; j < (unsigned int)m_nCOVREntries; j++)
158 free(m_pCOVRTable[j]);
159
160 free(m_pCOVRTable);
161 free(m_pCOVRTablePoints);
162
163 // Free the No COVR tables
164
165 for (unsigned int j = 0; j < (unsigned int)m_nNoCOVREntries; j++)
166 free(m_pNoCOVRTable[j]);
167
168 free(m_pNoCOVRTable);
169 free(m_pNoCOVRTablePoints);
170}
171
172wxString ChartBase::GetHashKey() const {
173 wxString key = GetFullPath();
174 wxChar separator = wxFileName::GetPathSeparator();
175 for (unsigned int pos = 0; pos < key.size(); pos = key.find(separator, pos))
176 key.replace(pos, 1, "!");
177 return key;
178}
179
180/*
181int ChartBase::Continue_BackgroundHiDefRender()
182{
183 return BR_DONE_NOP; // signal "done, no refresh"
184}
185*/
186
187// ============================================================================
188// ChartDummy implementation
189// ============================================================================
190
191ChartDummy::ChartDummy() {
192 m_pBM = NULL;
193 m_ChartType = CHART_TYPE_DUMMY;
194 m_ChartFamily = CHART_FAMILY_UNKNOWN;
195 m_Chart_Scale = 22000000;
196
197 m_FullPath = "No Chart Available";
198 m_Description = m_FullPath;
199}
200
201ChartDummy::~ChartDummy() { delete m_pBM; }
202
203InitReturn ChartDummy::Init(const wxString &name, ChartInitFlag init_flags) {
204 return INIT_OK;
205}
206
207void ChartDummy::SetColorScheme(ColorScheme cs, bool bApplyImmediate) {}
208
209ThumbData *ChartDummy::GetThumbData(int tnx, int tny, float lat, float lon) {
210 return (ThumbData *)NULL;
211}
212
213bool ChartDummy::UpdateThumbData(double lat, double lon) { return FALSE; }
214
215bool ChartDummy::GetChartExtent(Extent *pext) {
216 pext->NLAT = 80;
217 pext->SLAT = -80;
218 pext->ELON = 179;
219 pext->WLON = -179;
220
221 return true;
222}
223
224bool ChartDummy::RenderRegionViewOnGL(const wxGLContext &glc,
225 const ViewPort &VPoint,
226 const OCPNRegion &RectRegion,
227 const LLRegion &Region) {
228 return true;
229}
230
231bool ChartDummy::RenderRegionViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint,
232 const OCPNRegion &Region) {
233 return RenderViewOnDC(dc, VPoint);
234}
235
236bool ChartDummy::RenderViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint) {
237 if (m_pBM && m_pBM->IsOk()) {
238 if ((m_pBM->GetWidth() != VPoint.pix_width) ||
239 (m_pBM->GetHeight() != VPoint.pix_height)) {
240 delete m_pBM;
241 m_pBM = NULL;
242 }
243 } else {
244 delete m_pBM;
245 m_pBM = NULL;
246 }
247
248 if (VPoint.pix_width && VPoint.pix_height) {
249 if (NULL == m_pBM)
250 m_pBM = new wxBitmap(VPoint.pix_width, VPoint.pix_height, -1);
251
252 dc.SelectObject(*m_pBM);
253
254 dc.SetBackground(*wxBLACK_BRUSH);
255 dc.Clear();
256 }
257
258 return true;
259}
260
261bool ChartDummy::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) {
262 return false;
263}
264
265void ChartDummy::GetValidCanvasRegion(const ViewPort &VPoint,
266 OCPNRegion *pValidRegion) {
267 pValidRegion->Clear();
268 pValidRegion->Union(0, 0, 1, 1);
269}
270
271LLRegion ChartDummy::GetValidRegion() { return LLRegion(); }
272
273// ============================================================================
274// ChartGEO implementation
275// ============================================================================
276ChartGEO::ChartGEO() { m_ChartType = CHART_TYPE_GEO; }
277
278ChartGEO::~ChartGEO() {}
279
280InitReturn ChartGEO::Init(const wxString &name, ChartInitFlag init_flags) {
281#define BUF_LEN_MAX 4096
282
283 PreInit(name, init_flags, GLOBAL_COLOR_SCHEME_DAY);
284
285 char buffer[BUF_LEN_MAX];
286
287 ifs_hdr =
288 new wxFFileInputStream(name); // open the file as a read-only stream
289
290 m_filesize = wxFileName::GetSize(name);
291
292 if (!ifs_hdr->IsOk()) return INIT_FAIL_REMOVE;
293
294 int nPlypoint = 0;
295 Plypoint *pPlyTable = (Plypoint *)malloc(sizeof(Plypoint));
296
297 m_FullPath = name;
298 m_Description = m_FullPath;
299
300 wxFileName GEOFile(m_FullPath);
301
302 wxString Path;
303 Path = GEOFile.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME);
304
305 // Read the GEO file, extracting useful information
306
307 ifs_hdr->SeekI(0, wxFromStart); // rewind
308
309 Size_X = Size_Y = 0;
310
311 while ((ReadBSBHdrLine(ifs_hdr, &buffer[0], BUF_LEN_MAX)) != 0) {
312 wxString str_buf(buffer, wxConvUTF8);
313 if (!strncmp(buffer, "Bitmap", 6)) {
314 wxStringTokenizer tkz(str_buf, "=");
315 wxString token = tkz.GetNextToken();
316 if (token.IsSameAs("Bitmap", TRUE)) {
317 pBitmapFilePath = new wxString();
318
319 int i;
320 i = tkz.GetPosition();
321 pBitmapFilePath->Clear();
322 while (buffer[i]) {
323 pBitmapFilePath->Append(buffer[i]);
324 i++;
325 }
326 }
327 }
328
329 else if (!strncmp(buffer, "Scale", 5)) {
330 wxStringTokenizer tkz(str_buf, "=");
331 wxString token = tkz.GetNextToken();
332 if (token.IsSameAs("Scale", TRUE)) // extract Scale
333 {
334 int i;
335 i = tkz.GetPosition();
336 m_Chart_Scale = atoi(&buffer[i]);
337 }
338 }
339
340 else if (!strncmp(buffer, "Depth", 5)) {
341 wxStringTokenizer tkz(str_buf, "=");
342 wxString token = tkz.GetNextToken();
343 if (token.IsSameAs("Depth Units", FALSE)) // extract Depth Units
344 {
345 int i;
346 i = tkz.GetPosition();
347 wxString str(&buffer[i], wxConvUTF8);
348 m_DepthUnits = str.Trim();
349 }
350 }
351
352 else if (!strncmp(buffer, "Point", 5)) // Extract RefPoints
353 {
354 int i, xr, yr;
355 float ltr, lnr;
356 sscanf(&buffer[0], "Point%d=%f %f %d %d", &i, &lnr, &ltr, &yr, &xr);
357 pRefTable =
358 (Refpoint *)realloc(pRefTable, sizeof(Refpoint) * (nRefpoint + 1));
359 pRefTable[nRefpoint].xr = xr;
360 pRefTable[nRefpoint].yr = yr;
361 pRefTable[nRefpoint].latr = ltr;
362 pRefTable[nRefpoint].lonr = lnr;
363 pRefTable[nRefpoint].bXValid = 1;
364 pRefTable[nRefpoint].bYValid = 1;
365
366 nRefpoint++;
367
368 }
369
370 else if (!strncmp(buffer, "Vertex", 6)) {
371 int i;
372 float ltp, lnp;
373 sscanf(buffer, "Vertex%d=%f %f", &i, &ltp, &lnp);
374 Plypoint *tmp = pPlyTable;
375 pPlyTable =
376 (Plypoint *)realloc(pPlyTable, sizeof(Plypoint) * (nPlypoint + 1));
377 if (NULL == pPlyTable) {
378 free(tmp);
379 tmp = NULL;
380 } else {
381 pPlyTable[nPlypoint].ltp = ltp;
382 pPlyTable[nPlypoint].lnp = lnp;
383 nPlypoint++;
384 }
385 }
386
387 else if (!strncmp(buffer, "Date Pub", 8)) {
388 char date_string[40];
389 char date_buf[10];
390 sscanf(buffer, "Date Published=%s\r\n", &date_string[0]);
391 wxString date_wxstr(date_string, wxConvUTF8);
392 wxDateTime dt;
393 if (dt.ParseDate(date_wxstr)) // successful parse?
394 {
395 sprintf(date_buf, "%d", dt.GetYear());
396 } else {
397 sscanf(date_string, "%s", date_buf);
398 }
399 m_PubYear = wxString(date_buf, wxConvUTF8);
400 }
401
402 else if (!strncmp(buffer, "Skew", 4)) {
403 wxStringTokenizer tkz(str_buf, "=");
404 wxString token = tkz.GetNextToken();
405 if (token.IsSameAs("Skew Angle", FALSE)) // extract Skew Angle
406 {
407 int i;
408 i = tkz.GetPosition();
409 float fcs;
410 sscanf(&buffer[i], "%f,", &fcs);
411 m_Chart_Skew = fcs;
412 }
413 }
414
415 else if (!strncmp(buffer, "Latitude Offset", 15)) {
416 wxStringTokenizer tkz(str_buf, "=");
417 wxString token = tkz.GetNextToken();
418 if (token.IsSameAs("Latitude Offset", FALSE)) {
419 int i;
420 i = tkz.GetPosition();
421 float lto;
422 sscanf(&buffer[i], "%f,", &lto);
423 m_dtm_lat = lto;
424 }
425 }
426
427 else if (!strncmp(buffer, "Longitude Offset", 16)) {
428 wxStringTokenizer tkz(str_buf, "=");
429 wxString token = tkz.GetNextToken();
430 if (token.IsSameAs("Longitude Offset", FALSE)) {
431 int i;
432 i = tkz.GetPosition();
433 float lno;
434 sscanf(&buffer[i], "%f,", &lno);
435 m_dtm_lon = lno;
436 }
437 }
438
439 else if (!strncmp(buffer, "Datum", 5)) {
440 wxStringTokenizer tkz(str_buf, "=");
441 wxString token = tkz.GetNextToken();
442 if (token.IsSameAs("Datum", FALSE)) {
443 token = tkz.GetNextToken();
444 m_datum_str = token;
445 }
446 }
447
448 else if (!strncmp(buffer, "Name", 4)) {
449 wxStringTokenizer tkz(str_buf, "=");
450 wxString token = tkz.GetNextToken();
451 if (token.IsSameAs("Name", FALSE)) // Name
452 {
453 int i;
454 i = tkz.GetPosition();
455 m_Name.Clear();
456 while (isprint(buffer[i]) && (i < 80)) m_Name.Append(buffer[i++]);
457 }
458 }
459 } // while
460
461 // Extract the remaining data from .NOS Bitmap file
462 ifs_bitmap = NULL;
463
464 // Something wrong with the .geo file, there is no Bitmap reference
465 // This is where the arbitrarily bad file is caught, such as
466 // a file with.GEO extension that is not really a chart
467
468 if (pBitmapFilePath == NULL) {
469 free(pPlyTable);
470 return INIT_FAIL_REMOVE;
471 }
472
473 wxString NOS_Name(*pBitmapFilePath); // take a copy
474
475 wxDir target_dir(Path);
476 wxArrayString file_array;
477 int nfiles = wxDir::GetAllFiles(Path, &file_array);
478 int ifile;
479
480 pBitmapFilePath->Prepend(Path);
481
482 wxFileName NOS_filename(*pBitmapFilePath);
483 if (!NOS_filename.FileExists()) {
484 // File as fetched verbatim from the .geo file doesn't exist.
485 // Try all possible upper/lower cases
486 // Extract the filename and extension
487 wxString fname(NOS_filename.GetName());
488 wxString fext(NOS_filename.GetExt());
489
490 // Try all four combinations, the hard way
491 // case 1
492 fname.MakeLower();
493 fext.MakeLower();
494 NOS_filename.SetName(fname);
495 NOS_filename.SetExt(fext);
496
497 if (NOS_filename.FileExists()) goto found_uclc_file;
498
499 // case 2
500 fname.MakeLower();
501 fext.MakeUpper();
502 NOS_filename.SetName(fname);
503 NOS_filename.SetExt(fext);
504
505 if (NOS_filename.FileExists()) goto found_uclc_file;
506
507 // case 3
508 fname.MakeUpper();
509 fext.MakeLower();
510 NOS_filename.SetName(fname);
511 NOS_filename.SetExt(fext);
512
513 if (NOS_filename.FileExists()) goto found_uclc_file;
514
515 // case 4
516 fname.MakeUpper();
517 fext.MakeUpper();
518 NOS_filename.SetName(fname);
519 NOS_filename.SetExt(fext);
520
521 if (NOS_filename.FileExists()) goto found_uclc_file;
522
523 // Search harder
524
525 for (ifile = 0; ifile < nfiles; ifile++) {
526 wxString file_up = file_array[ifile];
527 file_up.MakeUpper();
528
529 wxString target_up = *pBitmapFilePath;
530 target_up.MakeUpper();
531
532 if (file_up.IsSameAs(target_up)) {
533 NOS_filename.Clear();
534 NOS_filename.Assign(file_array[ifile]);
535 goto found_uclc_file;
536 }
537 }
538
539 free(pPlyTable);
540 return INIT_FAIL_REMOVE; // not found at all
541
542 found_uclc_file:
543
544 delete pBitmapFilePath; // fix up the member element
545 pBitmapFilePath = new wxString(NOS_filename.GetFullPath());
546 }
547 ifss_bitmap =
548 new wxFFileInputStream(*pBitmapFilePath); // open the bitmap file
549 ifs_bitmap = new wxBufferedInputStream(*ifss_bitmap);
550
551 if (!ifss_bitmap->IsOk()) {
552 free(pPlyTable);
553 return INIT_FAIL_REMOVE;
554 }
555
556 while ((ReadBSBHdrLine(ifss_bitmap, &buffer[0], BUF_LEN_MAX)) != 0) {
557 wxString str_buf(buffer, wxConvUTF8);
558
559 if (!strncmp(buffer, "NOS", 3)) {
560 wxStringTokenizer tkz(str_buf, ",=");
561 while (tkz.HasMoreTokens()) {
562 wxString token = tkz.GetNextToken();
563 if (token.IsSameAs("RA", TRUE)) // extract RA=x,y
564 {
565 int i;
566 tkz.GetNextToken();
567 tkz.GetNextToken();
568 i = tkz.GetPosition();
569 Size_X = atoi(&buffer[i]);
570 wxString token = tkz.GetNextToken();
571 i = tkz.GetPosition();
572 Size_Y = atoi(&buffer[i]);
573 } else if (token.IsSameAs("DU", TRUE)) // extract DU=n
574 {
575 token = tkz.GetNextToken();
576 long temp_du;
577 if (token.ToLong(&temp_du)) m_Chart_DU = temp_du;
578 }
579 }
580
581 }
582
583 else if (!strncmp(buffer, "RGB", 3))
584 CreatePaletteEntry(buffer, COLOR_RGB_DEFAULT);
585
586 else if (!strncmp(buffer, "DAY", 3))
587 CreatePaletteEntry(buffer, DAY);
588
589 else if (!strncmp(buffer, "DSK", 3))
590 CreatePaletteEntry(buffer, DUSK);
591
592 else if (!strncmp(buffer, "NGT", 3))
593 CreatePaletteEntry(buffer, NIGHT);
594
595 else if (!strncmp(buffer, "NGR", 3))
596 CreatePaletteEntry(buffer, NIGHTRED);
597
598 else if (!strncmp(buffer, "GRY", 3))
599 CreatePaletteEntry(buffer, GRAY);
600
601 else if (!strncmp(buffer, "PRC", 3))
602 CreatePaletteEntry(buffer, PRC);
603
604 else if (!strncmp(buffer, "PRG", 3))
605 CreatePaletteEntry(buffer, PRG);
606 }
607
608 // Validate some of the header data
609 if (Size_X <= 0 || Size_Y <= 0) {
610 free(pPlyTable);
611 return INIT_FAIL_REMOVE;
612 }
613
614 if (nPlypoint < 3) {
615 wxString msg(" Chart File contains less than 3 PLY points: ");
616 msg.Append(m_FullPath);
617 wxLogMessage(msg);
618 free(pPlyTable);
619
620 return INIT_FAIL_REMOVE;
621 }
622
623 if (m_datum_str.IsEmpty()) {
624 wxString msg(" Chart datum not specified on chart ");
625 msg.Append(m_FullPath);
626 wxLogMessage(msg);
627 wxLogMessage(" Default datum (WGS84) substituted.");
628
629 // return INIT_FAIL_REMOVE;
630 } else {
631 char d_str[100];
632 strncpy(d_str, m_datum_str.mb_str(), 99);
633 d_str[99] = 0;
634
635 int datum_index = GetDatumIndex(d_str);
636
637 if (datum_index < 0) {
638 wxString msg(" Chart datum {");
639 msg += m_datum_str;
640 msg += "} invalid on chart ";
641 msg.Append(m_FullPath);
642 wxLogMessage(msg);
643 wxLogMessage(" Default datum (WGS84) substituted.");
644
645 datum_index = DATUM_INDEX_WGS84;
646 }
647 m_datum_index = datum_index;
648 }
649
650 // Convert captured plypoint information into chart COVR structures
651 m_nCOVREntries = 1;
652 m_pCOVRTablePoints = (int *)malloc(sizeof(int));
653 *m_pCOVRTablePoints = nPlypoint;
654 m_pCOVRTable = (float **)malloc(sizeof(float *));
655 *m_pCOVRTable = (float *)malloc(nPlypoint * 2 * sizeof(float));
656 memcpy(*m_pCOVRTable, pPlyTable, nPlypoint * 2 * sizeof(float));
657
658 free(pPlyTable);
659
660 if (!SetMinMax()) return INIT_FAIL_REMOVE; // have to bail here
661
662 AnalyzeSkew();
663
664 if (init_flags == HEADER_ONLY) return INIT_OK;
665
666 // Advance to the data
667 char c;
668 if ((c = ifs_bitmap->GetC()) != 0x1a) {
669 return INIT_FAIL_REMOVE;
670 }
671 if ((c = ifs_bitmap->GetC()) == 0x0d) {
672 if ((c = ifs_bitmap->GetC()) != 0x0a) {
673 return INIT_FAIL_REMOVE;
674 }
675 if ((c = ifs_bitmap->GetC()) != 0x1a) {
676 return INIT_FAIL_REMOVE;
677 }
678 if ((c = ifs_bitmap->GetC()) != 0x00) {
679 return INIT_FAIL_REMOVE;
680 }
681 }
682
683 else if (c != 0x00) {
684 return INIT_FAIL_REMOVE;
685 }
686
687 // Read the Color table bit size
688 nColorSize = ifs_bitmap->GetC();
689 if (nColorSize == wxEOF || nColorSize <= 0 || nColorSize > 7) {
690 wxString msg(" Invalid nColorSize data, corrupt on chart ");
691 msg.Append(m_FullPath);
692 wxLogMessage(msg);
693 return INIT_FAIL_REMOVE;
694 }
695
696 // Perform common post-init actions in ChartBaseBSB
697 InitReturn pi_ret = PostInit();
698 if (pi_ret != INIT_OK)
699 return pi_ret;
700 else
701 return INIT_OK;
702}
703
704// ============================================================================
705// ChartKAP implementation
706// ============================================================================
707
708ChartKAP::ChartKAP() { m_ChartType = CHART_TYPE_KAP; }
709
710ChartKAP::~ChartKAP() {}
711
712InitReturn ChartKAP::Init(const wxString &name, ChartInitFlag init_flags) {
713#define BUF_LEN_MAX 4096
714
715 ifs_hdr = new ChartDataNonSeekableInputStream(
716 name); // open the Header file as a read-only stream
717
718 if (!ifs_hdr->IsOk()) return INIT_FAIL_REMOVE;
719
720 int nPlypoint = 0;
721 Plypoint *pPlyTable = (Plypoint *)malloc(sizeof(Plypoint));
722
723 PreInit(name, init_flags, GLOBAL_COLOR_SCHEME_DAY);
724
725 char buffer[BUF_LEN_MAX];
726
727 m_FullPath = name;
728 m_Description = m_FullPath;
729
730 // Clear georeferencing coefficients
731 for (int icl = 0; icl < 12; icl++) {
732 wpx[icl] = 0;
733 wpy[icl] = 0;
734 pwx[icl] = 0;
735 pwy[icl] = 0;
736 }
737
738 // Validate the BSB header
739 // by reading some characters into a buffer and looking for BSB\ keyword
740
741 unsigned int TestBlockSize = 1999;
742 ifs_hdr->Read(buffer, TestBlockSize);
743
744 if (ifs_hdr->LastRead() != TestBlockSize) {
745 wxString msg;
746 msg.Printf(" Could not read first %d bytes of header for chart file: ",
747 TestBlockSize);
748 msg.Append(name);
749 wxLogMessage(msg);
750 free(pPlyTable);
751 return INIT_FAIL_REMOVE;
752 }
753
754 unsigned int i;
755 for (i = 0; i < TestBlockSize - 4; i++) {
756 // Test for "BSB/"
757 if (buffer[i + 0] == 'B' && buffer[i + 1] == 'S' && buffer[i + 2] == 'B' &&
758 buffer[i + 3] == '/')
759 break;
760
761 // Test for "NOS/"
762 if (buffer[i + 0] == 'N' && buffer[i + 1] == 'O' && buffer[i + 2] == 'S' &&
763 buffer[i + 3] == '/')
764 break;
765 }
766 if (i == TestBlockSize - 4) {
767 wxString msg(" Chart file has no BSB header, cannot Init.");
768 msg.Append(name);
769 wxLogMessage(msg);
770 free(pPlyTable);
771 return INIT_FAIL_REMOVE;
772 }
773
774 // Read and Parse Chart Header, line by line
775 ifs_hdr->SeekI(0, wxFromStart); // rewind
776
777 Size_X = Size_Y = 0;
778
779 int done_header_parse = 0;
780 wxCSConv iso_conv("ISO-8859-1"); // we will need a converter
781
782 while (done_header_parse == 0) {
783 if (ReadBSBHdrLine(ifs_hdr, buffer, BUF_LEN_MAX) == 0) {
784 unsigned char c;
785 c = ifs_hdr->GetC();
786 ifs_hdr->Ungetch(c);
787
788 if (0x1a == c)
789 done_header_parse = 1;
790 else {
791 free(pPlyTable);
792 return INIT_FAIL_REMOVE;
793 }
794
795 continue;
796 }
797
798 wxString str_buf(buffer, wxConvUTF8);
799 if (!str_buf.Len()) // failed conversion
800 str_buf = wxString(buffer, iso_conv);
801
802 if (str_buf.Find("SHOM") != wxNOT_FOUND) m_b_SHOM = true;
803
804 if (!strncmp(buffer, "BSB", 3)) {
805 wxString clip_str_buf(
806 &buffer[0],
807 iso_conv); // for single byte French encodings of NAme field
808 wxStringTokenizer tkz(clip_str_buf, "/,=");
809 while (tkz.HasMoreTokens()) {
810 wxString token = tkz.GetNextToken();
811 if (token.IsSameAs("RA", TRUE)) // extract RA=x,y
812 {
813 int i;
814 i = tkz.GetPosition();
815 Size_X = atoi(&buffer[i]);
816 wxString token = tkz.GetNextToken();
817 i = tkz.GetPosition();
818 Size_Y = atoi(&buffer[i]);
819 } else if (token.IsSameAs("NA", TRUE)) // extract NA=str
820 {
821 int i = tkz.GetPosition();
822 char nbuf[81];
823 int j = 0;
824 while ((buffer[i] != ',') && (i < 80)) nbuf[j++] = buffer[i++];
825 nbuf[j] = 0;
826 wxString n_str(nbuf, iso_conv);
827 m_Name = n_str;
828 } else if (token.IsSameAs("NU", TRUE)) // extract NU=str
829 {
830 int i = tkz.GetPosition();
831 char nbuf[81];
832 int j = 0;
833 while ((buffer[i] != ',') && (i < 80)) nbuf[j++] = buffer[i++];
834 nbuf[j] = 0;
835 wxString n_str(nbuf, iso_conv);
836 m_ID = n_str;
837 } else if (token.IsSameAs("DU", TRUE)) // extract DU=n
838 {
839 token = tkz.GetNextToken();
840 long temp_du;
841 if (token.ToLong(&temp_du)) m_Chart_DU = temp_du;
842 }
843 }
844 }
845
846 else if (!strncmp(buffer, "KNP", 3)) {
847 wxString conv_buf(buffer, iso_conv);
848 wxStringTokenizer tkz(conv_buf, "/,=");
849 while (tkz.HasMoreTokens()) {
850 wxString token = tkz.GetNextToken();
851 if (token.IsSameAs("SC", TRUE)) // extract Scale
852 {
853 int i;
854 i = tkz.GetPosition();
855 m_Chart_Scale = atoi(&buffer[i]);
856 if (0 == m_Chart_Scale) m_Chart_Scale = 100000000;
857 } else if (token.IsSameAs("SK", TRUE)) // extract Skew
858 {
859 int i;
860 i = tkz.GetPosition();
861 float fcs;
862 sscanf(&buffer[i], "%f,", &fcs);
863 m_Chart_Skew = fcs;
864 } else if (token.IsSameAs("UN", TRUE)) // extract Depth Units
865 {
866 int i;
867 i = tkz.GetPosition();
868 wxString str(&buffer[i], iso_conv);
869 m_DepthUnits = str.BeforeFirst(',');
870 } else if (token.IsSameAs("GD", TRUE)) // extract Datum
871 {
872 int i;
873 i = tkz.GetPosition();
874 wxString str(&buffer[i], iso_conv);
875 m_datum_str = str.BeforeFirst(',').Trim();
876 } else if (token.IsSameAs("SD", TRUE)) // extract Soundings Datum
877 {
878 int i;
879 i = tkz.GetPosition();
880 wxString str(&buffer[i], iso_conv);
881 m_SoundingsDatum = str.BeforeFirst(',').Trim();
882 } else if (token.IsSameAs("PP",
883 TRUE)) // extract Projection Parameter
884 {
885 int i;
886 i = tkz.GetPosition();
887 double fcs;
888 wxString str(&buffer[i], iso_conv);
889 wxString str1 = str.BeforeFirst(',').Trim();
890 if (str1.ToDouble(&fcs)) m_proj_parameter = fcs;
891 } else if (token.IsSameAs("PR", TRUE)) // extract Projection Type
892 {
893 int i;
894 i = tkz.GetPosition();
895 wxString str(&buffer[i], iso_conv);
896 wxString stru = str.MakeUpper();
897 bool bp_set = false;
898 ;
899
900 if (stru.Matches("*MERCATOR*")) {
901 m_projection = PROJECTION_MERCATOR;
902 bp_set = true;
903 }
904
905 if (stru.Matches("*TRANSVERSE*")) {
906 m_projection = PROJECTION_TRANSVERSE_MERCATOR;
907 bp_set = true;
908 }
909
910 if (stru.Matches("*CONIC*")) {
911 m_projection = PROJECTION_POLYCONIC;
912 bp_set = true;
913 }
914
915 if (stru.Matches("*TM*")) {
916 m_projection = PROJECTION_TRANSVERSE_MERCATOR;
917 bp_set = true;
918 }
919
920 if (stru.Matches("*GAUSS CONFORMAL*")) {
921 m_projection = PROJECTION_TRANSVERSE_MERCATOR;
922 bp_set = true;
923 }
924
925 if (!bp_set) {
926 m_projection = PROJECTION_UNKNOWN;
927 wxString msg(" Chart projection is ");
928 msg += tkz.GetNextToken();
929 msg += " which is unsupported. Disabling chart ";
930 msg += m_FullPath;
931 wxLogMessage(msg);
932 free(pPlyTable);
933 return INIT_FAIL_REMOVE;
934 }
935 } else if (token.IsSameAs(
936 "DX",
937 TRUE)) // extract Pixel scale parameter, if present
938 {
939 int i;
940 i = tkz.GetPosition();
941 float x;
942 sscanf(&buffer[i], "%f,", &x);
943 m_dx = x;
944 } else if (token.IsSameAs(
945 "DY",
946 TRUE)) // extract Pixel scale parameter, if present
947 {
948 int i;
949 i = tkz.GetPosition();
950 float x;
951 sscanf(&buffer[i], "%f,", &x);
952 m_dy = x;
953 }
954 }
955 }
956
957 else if (!strncmp(buffer, "RGB", 3))
958 CreatePaletteEntry(buffer, COLOR_RGB_DEFAULT);
959
960 else if (!strncmp(buffer, "DAY", 3))
961 CreatePaletteEntry(buffer, DAY);
962
963 else if (!strncmp(buffer, "DSK", 3))
964 CreatePaletteEntry(buffer, DUSK);
965
966 else if (!strncmp(buffer, "NGT", 3))
967 CreatePaletteEntry(buffer, NIGHT);
968
969 else if (!strncmp(buffer, "NGR", 3))
970 CreatePaletteEntry(buffer, NIGHTRED);
971
972 else if (!strncmp(buffer, "GRY", 3))
973 CreatePaletteEntry(buffer, GRAY);
974
975 else if (!strncmp(buffer, "PRC", 3))
976 CreatePaletteEntry(buffer, PRC);
977
978 else if (!strncmp(buffer, "PRG", 3))
979 CreatePaletteEntry(buffer, PRG);
980
981 else if (!strncmp(buffer, "REF", 3)) {
982 int i, xr, yr;
983 float ltr, lnr;
984 sscanf(&buffer[4], "%d,%d,%d,%f,%f", &i, &xr, &yr, &ltr, &lnr);
985 pRefTable =
986 (Refpoint *)realloc(pRefTable, sizeof(Refpoint) * (nRefpoint + 1));
987 pRefTable[nRefpoint].xr = xr;
988 pRefTable[nRefpoint].yr = yr;
989 pRefTable[nRefpoint].latr = ltr;
990 pRefTable[nRefpoint].lonr = lnr;
991 pRefTable[nRefpoint].bXValid = 1;
992 pRefTable[nRefpoint].bYValid = 1;
993
994 nRefpoint++;
995
996 }
997
998 else if (!strncmp(buffer, "WPX", 3)) {
999 int idx = 0;
1000 double d;
1001 wxStringTokenizer tkz(str_buf.Mid(4), ",");
1002 wxString token = tkz.GetNextToken();
1003
1004 if (token.ToLong((long int *)&wpx_type)) {
1005 while (tkz.HasMoreTokens() && (idx < 12)) {
1006 token = tkz.GetNextToken();
1007 if (token.ToDouble(&d)) {
1008 wpx[idx] = d;
1009 idx++;
1010 }
1011 }
1012 }
1013 n_wpx = idx;
1014 }
1015
1016 else if (!strncmp(buffer, "WPY", 3)) {
1017 int idx = 0;
1018 double d;
1019 wxStringTokenizer tkz(str_buf.Mid(4), ",");
1020 wxString token = tkz.GetNextToken();
1021
1022 if (token.ToLong((long int *)&wpy_type)) {
1023 while (tkz.HasMoreTokens() && (idx < 12)) {
1024 token = tkz.GetNextToken();
1025 if (token.ToDouble(&d)) {
1026 wpy[idx] = d;
1027 idx++;
1028 }
1029 }
1030 }
1031 n_wpy = idx;
1032 }
1033
1034 else if (!strncmp(buffer, "PWX", 3)) {
1035 int idx = 0;
1036 double d;
1037 wxStringTokenizer tkz(str_buf.Mid(4), ",");
1038 wxString token = tkz.GetNextToken();
1039
1040 if (token.ToLong((long int *)&pwx_type)) {
1041 while (tkz.HasMoreTokens() && (idx < 12)) {
1042 token = tkz.GetNextToken();
1043 if (token.ToDouble(&d)) {
1044 pwx[idx] = d;
1045 idx++;
1046 }
1047 }
1048 }
1049 n_pwx = idx;
1050 }
1051
1052 else if (!strncmp(buffer, "PWY", 3)) {
1053 int idx = 0;
1054 double d;
1055 wxStringTokenizer tkz(str_buf.Mid(4), ",");
1056 wxString token = tkz.GetNextToken();
1057
1058 if (token.ToLong((long int *)&pwy_type)) {
1059 while (tkz.HasMoreTokens() && (idx < 12)) {
1060 token = tkz.GetNextToken();
1061 if (token.ToDouble(&d)) {
1062 pwy[idx] = d;
1063 idx++;
1064 }
1065 }
1066 }
1067 n_pwy = idx;
1068 }
1069
1070 else if (!strncmp(buffer, "CPH", 3)) {
1071 float float_cph;
1072 sscanf(&buffer[4], "%f", &float_cph);
1073 m_cph = float_cph;
1074 }
1075
1076 else if (!strncmp(buffer, "VER", 3)) {
1077 wxStringTokenizer tkz(str_buf, "/,=");
1078 wxString token = tkz.GetNextToken();
1079
1080 m_bsb_ver = tkz.GetNextToken();
1081 }
1082
1083 else if (!strncmp(buffer, "DTM", 3)) {
1084 double val;
1085 wxStringTokenizer tkz(str_buf, "/,=");
1086 wxString token = tkz.GetNextToken();
1087
1088 token = tkz.GetNextToken();
1089 if (token.ToDouble(&val)) m_dtm_lat = val;
1090
1091 token = tkz.GetNextToken();
1092 if (token.ToDouble(&val)) m_dtm_lon = val;
1093
1094 // float fdtmlat, fdtmlon;
1095 // sscanf(&buffer[4], "%f,%f", &fdtmlat, &fdtmlon);
1096 // m_dtm_lat = fdtmlat;
1097 // m_dtm_lon = fdtmlon;
1098 }
1099
1100 else if (!strncmp(buffer, "PLY", 3)) {
1101 int i;
1102 float ltp, lnp;
1103 if (sscanf(&buffer[4], "%d,%f,%f", &i, &ltp, &lnp) != 3) {
1104 free(pPlyTable);
1105 return INIT_FAIL_REMOVE;
1106 }
1107 Plypoint *tmp = pPlyTable;
1108 pPlyTable =
1109 (Plypoint *)realloc(pPlyTable, sizeof(Plypoint) * (nPlypoint + 1));
1110 if (NULL == pPlyTable) {
1111 free(tmp);
1112 tmp = NULL;
1113 } else {
1114 pPlyTable[nPlypoint].ltp = ltp;
1115 pPlyTable[nPlypoint].lnp = lnp;
1116 nPlypoint++;
1117 }
1118 if (NULL == pPlyTable || nPlypoint > 1000000) {
1119 // arbitrary 8MB for pPlyTable
1120 nPlypoint = 0;
1121 break;
1122 }
1123 }
1124
1125 else if (!strncmp(buffer, "CED", 3)) {
1126 wxStringTokenizer tkz(str_buf, "/,=");
1127 while (tkz.HasMoreTokens()) {
1128 wxString token = tkz.GetNextToken();
1129 if (token.IsSameAs("ED", TRUE)) // extract Edition Date
1130 {
1131 int i;
1132 i = tkz.GetPosition();
1133
1134 char date_string[40];
1135 char date_buf[16];
1136 date_string[0] = 0;
1137 date_buf[0] = 0;
1138 sscanf(&buffer[i], "%s\r\n", date_string);
1139 wxString date_wxstr(date_string, wxConvUTF8);
1140
1141 wxDateTime dt;
1142 if (dt.ParseDate(date_wxstr)) // successful parse?
1143 {
1144 int iyear =
1145 dt.GetYear(); // GetYear() fails on W98, DMC compiler, wx2.8.3
1146 // BSB charts typically list publish date as xx/yy/zz
1147 // This our own little version of the Y2K problem.
1148 // Just apply some sensible logic
1149
1150 if (iyear < 50) {
1151 iyear += 2000;
1152 dt.SetYear(iyear);
1153 } else if ((iyear >= 50) && (iyear < 100)) {
1154 iyear += 1900;
1155 dt.SetYear(iyear);
1156 }
1157 assert(iyear <= 9999);
1158 sprintf(date_buf, "%d", iyear);
1159
1160 // Initialize the wxDateTime menber for Edition Date
1161 m_EdDate = dt;
1162 } else {
1163 sscanf(date_string, "%s", date_buf);
1164 m_EdDate.Set(1, wxDateTime::Jan,
1165 2000); // Todo this could be smarter
1166 }
1167
1168 m_PubYear = wxString(date_buf, wxConvUTF8);
1169 } else if (token.IsSameAs("SE", TRUE)) // extract Source Edition
1170 {
1171 int i;
1172 i = tkz.GetPosition();
1173 wxString str(&buffer[i], iso_conv);
1174 m_SE = str.BeforeFirst(',');
1175 }
1176 }
1177 }
1178 } // while
1179
1180 // Some charts improperly encode the DTM parameters.
1181 // Identify them as necessary, for further processing
1182 if (m_b_SHOM && (m_bsb_ver == "1.1")) m_b_apply_dtm = false;
1183
1184 // If imbedded coefficients are found,
1185 // then use the polynomial georeferencing algorithms
1186 if (n_pwx && n_pwy && n_pwx && n_pwy) bHaveEmbeddedGeoref = true;
1187
1188 // Set up the projection point according to the projection parameter
1189 if (m_projection == PROJECTION_MERCATOR)
1190 m_proj_lat = m_proj_parameter;
1191 else if (m_projection == PROJECTION_TRANSVERSE_MERCATOR)
1192 m_proj_lon = m_proj_parameter;
1193 else if (m_projection == PROJECTION_POLYCONIC)
1194 m_proj_lon = m_proj_parameter;
1195
1196 // We have seen improperly coded charts, with non-sense value of PP
1197 // parameter FS#1251 Check and override if necessary
1198 if (m_proj_lat > 82.0 || m_proj_lat < -82.0) m_proj_lat = 0.0;
1199
1200 // Validate some of the header data
1201 if (Size_X <= 0 || Size_Y <= 0) {
1202 free(pPlyTable);
1203 return INIT_FAIL_REMOVE;
1204 }
1205
1206 if (nPlypoint < 3) {
1207 wxString msg(" Chart File contains less than 3 or too many PLY points: ");
1208 msg.Append(m_FullPath);
1209 wxLogMessage(msg);
1210 free(pPlyTable);
1211 return INIT_FAIL_REMOVE;
1212 }
1213
1214 if (m_datum_str.IsEmpty()) {
1215 wxString msg(" Chart datum not specified on chart ");
1216 msg.Append(m_FullPath);
1217 wxLogMessage(msg);
1218 wxLogMessage(" Default datum (WGS84) substituted.");
1219
1220 // return INIT_FAIL_REMOVE;
1221 } else {
1222 char d_str[100];
1223 strncpy(d_str, m_datum_str.mb_str(), 99);
1224 d_str[99] = 0;
1225
1226 int datum_index = GetDatumIndex(d_str);
1227
1228 if (datum_index < 0) {
1229 wxString msg(" Chart datum {");
1230 msg += m_datum_str;
1231 msg += "} invalid on chart ";
1232 msg.Append(m_FullPath);
1233 wxLogMessage(msg);
1234 wxLogMessage(" Default datum (WGS84) substituted.");
1235
1236 // return INIT_FAIL_REMOVE;
1237 }
1238 }
1239
1240 /* Augment ply points
1241 This is needed for example on polyconic charts or skewed charts because
1242 straight lines in the chart coordinates can not use simple
1243 interpolation in lat/lon or mercator coordinate space to draw the
1244 borders or be used for quilting operation.
1245 TODO: should this be added as a subroutine for GEO chartso? */
1246 if ((m_projection != PROJECTION_MERCATOR &&
1247 m_projection != PROJECTION_TRANSVERSE_MERCATOR) ||
1248 m_Chart_Skew > 2) {
1249 // Analyze Refpoints early because we need georef coefficient here.
1250 AnalyzeRefpoints(false); // no post test needed
1251
1252 // We need to compute a tentative min/max lat/lon to perform georefs
1253 // These lat/lon extents will be more accurately updated later.
1254 m_LonMax = -360.0;
1255 m_LonMin = 360.0;
1256 m_LatMax = -90.0;
1257 m_LatMin = 90.0;
1258
1259 for (int i = 0; i < nPlypoint; i++) {
1260 m_LatMax = wxMax(m_LatMax, pPlyTable[i].ltp);
1261 m_LatMin = wxMin(m_LatMin, pPlyTable[i].ltp);
1262 m_LonMax = wxMax(m_LonMax, pPlyTable[i].lnp);
1263 m_LonMin = wxMin(m_LonMin, pPlyTable[i].lnp);
1264 }
1265
1266 int count = nPlypoint;
1267 nPlypoint = 0;
1268 Plypoint *pOldPlyTable = pPlyTable;
1269 pPlyTable = NULL;
1270 double lastplylat = 0.0, lastplylon = 0.0, x1 = 0.0, y1 = 0.0, x2, y2;
1271 double plylat, plylon;
1272 for (int i = 0; i < count + 1; i++) {
1273 plylat = pOldPlyTable[i % count].ltp;
1274 plylon = pOldPlyTable[i % count].lnp;
1275 latlong_to_chartpix(plylat, plylon, x2, y2);
1276 if (i > 0) {
1277 if (lastplylon - plylon > 180.)
1278 lastplylon -= 360.;
1279 else if (lastplylon - plylon < -180.)
1280 lastplylon += 360.;
1281
1282 // use 2 degree steps
1283 double steps =
1284 ceil((fabs(lastplylat - plylat) + fabs(lastplylon - plylon)) / 2);
1285 for (double c = 0; c < steps; c++) {
1286 double d = c / steps, lat, lon;
1287 wxPoint2DDouble s;
1288 double x = (1 - d) * x1 + d * x2, y = (1 - d) * y1 + d * y2;
1289 chartpix_to_latlong(x, y, &lat, &lon);
1290 pPlyTable = (Plypoint *)realloc(pPlyTable,
1291 sizeof(Plypoint) * (nPlypoint + 1));
1292 pPlyTable[nPlypoint].ltp = lat;
1293 pPlyTable[nPlypoint].lnp = lon;
1294 nPlypoint++;
1295 }
1296 }
1297 x1 = x2, y1 = y2;
1298 lastplylat = plylat, lastplylon = plylon;
1299 }
1300 free(pOldPlyTable);
1301 }
1302
1303 // Convert captured plypoint information into chart COVR structures
1304
1305 // A special-case test for poorly formatted charts
1306 // We look for cases where the declared PlyPoints are far outside of the
1307 // chart raster bitmap If found, we change the COVR region to the valid
1308 // bitmap region, instead of the default PlyPoints region
1309 // Set a tentative lat/lon range.
1310 m_LonMax = -360.;
1311 m_LonMin = 360.;
1312 for (int i = 0; i < nPlypoint; i++) {
1313 m_LonMin = wxMin(m_LonMin, pPlyTable[i].lnp);
1314 m_LonMax = wxMax(m_LonMax, pPlyTable[i].lnp);
1315 }
1316 // This test does not really work for charts that cross IDL
1317 bool b_test = true;
1318 bool b_adjusted = false;
1319 if (m_LonMax * m_LonMin < 0) {
1320 if ((m_LonMax - m_LonMin) > 180.) b_test = false;
1321 }
1322
1323 if (b_test) {
1324 if (!bHaveEmbeddedGeoref) {
1325 // Analyze Refpoints early because we might need georef coefficient
1326 // here.
1327 AnalyzeRefpoints(false); // no post test needed
1328 }
1329
1330 bool bAdjustPly = false;
1331 wxRect bitRect(0, 0, Size_X, Size_Y);
1332 bitRect.Inflate(5); // allow for a little roundoff error
1333 for (int i = 0; i < nPlypoint; i++) {
1334 double pix_x, pix_y;
1335 latlong_to_chartpix(pPlyTable[i].ltp, pPlyTable[i].lnp, pix_x, pix_y);
1336 if (!bitRect.Contains(pix_x, pix_y)) {
1337 bAdjustPly = true;
1338 if (m_b_cdebug)
1339 printf("Adjusting COVR region on: %s\n", name.ToUTF8().data());
1340 break;
1341 }
1342 }
1343
1344 if (bAdjustPly) {
1345 float *points = new float[2 * nPlypoint];
1346 for (int i = 0; i < nPlypoint; i++)
1347 points[2 * i + 0] = pPlyTable[i].ltp,
1348 points[2 * i + 1] = pPlyTable[i].lnp;
1349 LLRegion covrRegion(nPlypoint, points);
1350 delete[] points;
1351 covrRegion.Intersect(GetValidRegion());
1352
1353 if (covrRegion.contours.size()) { // Check for no intersection caused by
1354 // bogus georef....
1355 m_nCOVREntries = covrRegion.contours.size();
1356 m_pCOVRTablePoints = (int *)malloc(m_nCOVREntries * sizeof(int));
1357 m_pCOVRTable = (float **)malloc(m_nCOVREntries * sizeof(float *));
1358 auto it = covrRegion.contours.begin();
1359 for (int i = 0; i < m_nCOVREntries; i++) {
1360 m_pCOVRTablePoints[i] = it->size();
1361 m_pCOVRTable[i] =
1362 (float *)malloc(m_pCOVRTablePoints[i] * 2 * sizeof(float));
1363 auto jt = it->begin();
1364 for (int j = 0; j < m_pCOVRTablePoints[i]; j++) {
1365 m_pCOVRTable[i][2 * j + 0] = jt->y;
1366 m_pCOVRTable[i][2 * j + 1] = jt->x;
1367 jt++;
1368 }
1369 it++;
1370 }
1371 b_adjusted = true;
1372 }
1373 }
1374 }
1375
1376 if (!b_adjusted) {
1377 m_nCOVREntries = 1;
1378 m_pCOVRTablePoints = (int *)malloc(sizeof(int));
1379 *m_pCOVRTablePoints = nPlypoint;
1380 m_pCOVRTable = (float **)malloc(sizeof(float *));
1381 *m_pCOVRTable = (float *)malloc(nPlypoint * 2 * sizeof(float));
1382 memcpy(*m_pCOVRTable, pPlyTable, nPlypoint * 2 * sizeof(float));
1383 }
1384
1385 free(pPlyTable);
1386
1387 // Setup the datum transform parameters
1388 char d_str[100];
1389 strncpy(d_str, m_datum_str.mb_str(), 99);
1390 d_str[99] = 0;
1391
1392 int datum_index = GetDatumIndex(d_str);
1393 m_datum_index = datum_index;
1394
1395 if (datum_index < 0)
1396 m_ExtraInfo = "---<<< Warning: Chart Datum may be incorrect. >>>---";
1397
1398 // Establish defaults, may be overridden later
1399 m_lon_datum_adjust = (-m_dtm_lon) / 3600.;
1400 m_lat_datum_adjust = (-m_dtm_lat) / 3600.;
1401
1402 // Adjust the PLY points to WGS84 datum
1403 Plypoint *ppp = (Plypoint *)GetCOVRTableHead(0);
1404 int cnPlypoint = GetCOVRTablenPoints(0);
1405
1406 for (int u = 0; u < cnPlypoint; u++) {
1407 double dlat = 0;
1408 double dlon = 0;
1409
1410 if (m_datum_index == DATUM_INDEX_WGS84 ||
1411 m_datum_index == DATUM_INDEX_UNKNOWN) {
1412 dlon = m_dtm_lon / 3600.;
1413 dlat = m_dtm_lat / 3600.;
1414 }
1415
1416 else {
1417 double to_lat, to_lon;
1418 MolodenskyTransform(ppp->ltp, ppp->lnp, &to_lat, &to_lon, m_datum_index,
1419 DATUM_INDEX_WGS84);
1420 dlon = (to_lon - ppp->lnp);
1421 dlat = (to_lat - ppp->ltp);
1422 if (m_b_apply_dtm) {
1423 dlon += m_dtm_lon / 3600.;
1424 dlat += m_dtm_lat / 3600.;
1425 }
1426 }
1427
1428 ppp->lnp += dlon;
1429 ppp->ltp += dlat;
1430 ppp++;
1431 }
1432
1433 if (!SetMinMax()) return INIT_FAIL_REMOVE; // have to bail here
1434
1435 AnalyzeSkew();
1436
1437 if (init_flags == HEADER_ONLY) return INIT_OK;
1438
1439 // Advance to the data
1440 unsigned char c;
1441 bool bcorrupt = false;
1442
1443 if ((c = ifs_hdr->GetC()) != 0x1a) {
1444 bcorrupt = true;
1445 }
1446 if ((c = ifs_hdr->GetC()) == 0x0d) {
1447 if ((c = ifs_hdr->GetC()) != 0x0a) {
1448 bcorrupt = true;
1449 }
1450 if ((c = ifs_hdr->GetC()) != 0x1a) {
1451 bcorrupt = true;
1452 }
1453 if ((c = ifs_hdr->GetC()) != 0x00) {
1454 bcorrupt = true;
1455 }
1456 }
1457
1458 else if (c != 0x00) {
1459 bcorrupt = true;
1460 }
1461
1462 if (bcorrupt) {
1463 wxString msg(" Chart File RLL data corrupt on chart ");
1464 msg.Append(m_FullPath);
1465 wxLogMessage(msg);
1466
1467 return INIT_FAIL_REMOVE;
1468 }
1469
1470 // Read the Color table bit size
1471 nColorSize = ifs_hdr->GetC();
1472 if (nColorSize == wxEOF || nColorSize <= 0 || nColorSize > 7) {
1473 wxString msg(" Invalid nColorSize data, corrupt on chart ");
1474 msg.Append(m_FullPath);
1475 wxLogMessage(msg);
1476 return INIT_FAIL_REMOVE;
1477 }
1478
1479 nFileOffsetDataStart = ifs_hdr->TellI();
1480 delete ifs_hdr;
1481 ifs_hdr = NULL;
1482
1483 ChartDataInputStream *stream =
1484 new ChartDataInputStream(name); // Open again, as the bitmap
1485 wxString tempfile;
1486#ifdef OCPN_USE_LZMA
1487 tempfile = stream->TempFileName();
1488#endif
1489 m_filesize = wxFileName::GetSize(tempfile.empty() ? name : tempfile);
1490
1491 ifss_bitmap = stream;
1492 ifs_bitmap = new wxBufferedInputStream(*ifss_bitmap);
1493
1494 // Perform common post-init actions in ChartBaseBSB
1495 InitReturn pi_ret = PostInit();
1496 if (pi_ret != INIT_OK) return pi_ret;
1497 return INIT_OK;
1498}
1499
1500// ============================================================================
1501// ChartBaseBSB implementation
1502// ============================================================================
1503
1504ChartBaseBSB::ChartBaseBSB() {
1505 // Init some private data
1506 m_ChartFamily = CHART_FAMILY_RASTER;
1507
1508 pBitmapFilePath = NULL;
1509
1510 pline_table = NULL;
1511 ifs_buf = NULL;
1512
1513 cached_image_ok = 0;
1514
1515 pRefTable = (Refpoint *)malloc(sizeof(Refpoint));
1516 nRefpoint = 0;
1517 cPoints.status = 0;
1518 bHaveEmbeddedGeoref = false;
1519 n_wpx = 0;
1520 n_wpy = 0;
1521 n_pwx = 0;
1522 n_pwy = 0;
1523
1524#ifdef __ANDROID__
1525 bUseLineCache = false;
1526#else
1527 bUseLineCache = true;
1528#endif
1529
1530 m_Chart_Skew = 0.0;
1531
1532 pPixCache = NULL;
1533
1534 pLineCache = NULL;
1535
1536 m_bilinear_limit = 8; // bilinear scaling only up to n
1537
1538 ifs_bitmap = NULL;
1539 ifss_bitmap = NULL;
1540 ifs_hdr = NULL;
1541
1542 for (int i = 0; i < N_BSB_COLORS; i++) pPalettes[i] = NULL;
1543
1544 bGeoErrorSent = false;
1545 m_Chart_DU = 0;
1546 m_cph = 0.;
1547
1548 m_mapped_color_index = COLOR_RGB_DEFAULT;
1549
1550 m_datum_str = "WGS84"; // assume until proven otherwise
1551
1552 m_dtm_lat = 0.;
1553 m_dtm_lon = 0.;
1554
1555 m_dx = 0.;
1556 m_dy = 0.;
1557 m_proj_lat = 0.;
1558 m_proj_lon = 0.;
1559 m_proj_parameter = 0.;
1560 m_b_SHOM = false;
1561 m_b_apply_dtm = true;
1562
1563 m_b_cdebug = 0;
1564
1565#ifdef OCPN_USE_CONFIG
1566 wxFileConfig *pfc = (wxFileConfig *)pConfig;
1567 pfc->SetPath("/Settings");
1568 pfc->Read("DebugBSBImg", &m_b_cdebug, 0);
1569#endif
1570}
1571
1572ChartBaseBSB::~ChartBaseBSB() {
1573 if (pBitmapFilePath) delete pBitmapFilePath;
1574
1575 if (pline_table) free(pline_table);
1576
1577 if (ifs_buf) free(ifs_buf);
1578
1579 free(pRefTable);
1580 // free(pPlyTable);
1581
1582 delete ifs_bitmap;
1583 delete ifs_hdr;
1584 delete ifss_bitmap;
1585
1586 if (cPoints.status) {
1587 free(cPoints.tx);
1588 free(cPoints.ty);
1589 free(cPoints.lon);
1590 free(cPoints.lat);
1591
1592 free(cPoints.pwx);
1593 free(cPoints.wpx);
1594 free(cPoints.pwy);
1595 free(cPoints.wpy);
1596 }
1597
1598 // Free the line cache
1599 FreeLineCacheRows();
1600 free(pLineCache);
1601
1602 delete pPixCache;
1603
1604 for (int i = 0; i < N_BSB_COLORS; i++) delete pPalettes[i];
1605}
1606
1607void ChartBaseBSB::FreeLineCacheRows(int start, int end) {
1608 if (pLineCache) {
1609 if (end < 0)
1610 end = Size_Y;
1611 else
1612 end = wxMin(end, Size_Y);
1613 for (int ylc = start; ylc < end; ylc++) {
1614 CachedLine *pt = &pLineCache[ylc];
1615 if (pt->bValid) {
1616 free(pt->pTileOffset);
1617 free(pt->pPix);
1618 pt->bValid = false;
1619 }
1620 }
1621 }
1622}
1623
1624bool ChartBaseBSB::HaveLineCacheRow(int row) {
1625 if (pLineCache) {
1626 CachedLine *pt = &pLineCache[row];
1627 return pt->bValid;
1628 }
1629 return false;
1630}
1631
1632// Report recommended minimum and maximum scale values for which use of this
1633// chart is valid
1634
1635double ChartBaseBSB::GetNormalScaleMin(double canvas_scale_factor,
1636 bool b_allow_overzoom) {
1637 // if(b_allow_overzoom)
1638 return (canvas_scale_factor / m_ppm_avg) /
1639 32; // allow wide range overzoom overscale
1640 // else
1641 // return (canvas_scale_factor / m_ppm_avg) / 2; // don't
1642 // suggest too much overscale
1643}
1644
1645double ChartBaseBSB::GetNormalScaleMax(double canvas_scale_factor,
1646 int canvas_width) {
1647 return (canvas_scale_factor / m_ppm_avg) *
1648 4.0; // excessive underscale is slow, and unreadable
1649}
1650
1651double ChartBaseBSB::GetNearestPreferredScalePPM(double target_scale_ppm) {
1653 target_scale_ppm, .01, 64.); // changed from 32 to 64 to allow super
1654 // small scale BSB charts as quilt base
1655}
1656
1658 double scale_factor_min,
1659 double scale_factor_max) {
1660 double chart_1x_scale = GetPPM();
1661
1662 double binary_scale_factor = 1.;
1663
1664 // Overzoom....
1665 if (chart_1x_scale > target_scale) {
1666 double binary_scale_factor_max = 1 / scale_factor_min;
1667
1668 while (binary_scale_factor < binary_scale_factor_max) {
1669 if (fabs((chart_1x_scale / binary_scale_factor) - target_scale) <
1670 (target_scale * 0.05))
1671 break;
1672 if ((chart_1x_scale / binary_scale_factor) < target_scale)
1673 break;
1674 else
1675 binary_scale_factor *= 2.;
1676 }
1677 }
1678
1679 // Underzoom.....
1680 else {
1681 int ibsf = 1;
1682 int isf_max = (int)scale_factor_max;
1683 while (ibsf < isf_max) {
1684 if (fabs((chart_1x_scale * ibsf) - target_scale) < (target_scale * 0.05))
1685 break;
1686
1687 else if ((chart_1x_scale * ibsf) > target_scale) {
1688 if (ibsf > 1) ibsf /= 2;
1689 break;
1690 } else
1691 ibsf *= 2;
1692 }
1693
1694 binary_scale_factor = 1. / ibsf;
1695 }
1696
1697 return chart_1x_scale / binary_scale_factor;
1698}
1699
1700InitReturn ChartBaseBSB::Init(const wxString &name, ChartInitFlag init_flags) {
1701 m_global_color_scheme = GLOBAL_COLOR_SCHEME_RGB;
1702 return INIT_OK;
1703}
1704
1705InitReturn ChartBaseBSB::PreInit(const wxString &name, ChartInitFlag init_flags,
1706 ColorScheme cs) {
1707 m_global_color_scheme = cs;
1708 return INIT_OK;
1709}
1710
1711void ChartBaseBSB::CreatePaletteEntry(char *buffer, int palette_index) {
1712 if (palette_index < N_BSB_COLORS) {
1713 if (!pPalettes[palette_index]) pPalettes[palette_index] = new opncpnPalette;
1714 opncpnPalette *pp = pPalettes[palette_index];
1715
1716 pp->FwdPalette =
1717 (int *)realloc(pp->FwdPalette, (pp->nFwd + 1) * sizeof(int));
1718 pp->RevPalette =
1719 (int *)realloc(pp->RevPalette, (pp->nRev + 1) * sizeof(int));
1720 pp->nFwd++;
1721 pp->nRev++;
1722
1723 int i;
1724 int n, r, g, b;
1725 sscanf(&buffer[4], "%d,%d,%d,%d", &n, &r, &g, &b);
1726
1727 i = n;
1728
1729 int fcolor, rcolor;
1730 fcolor = (b << 16) + (g << 8) + r;
1731 rcolor = (r << 16) + (g << 8) + b;
1732
1733 pp->RevPalette[i] = rcolor;
1734 pp->FwdPalette[i] = fcolor;
1735 }
1736}
1737
1738InitReturn ChartBaseBSB::PostInit() {
1739 // catch undefined shift if not already done in derived classes
1740 if (nColorSize == wxEOF || nColorSize <= 0 || nColorSize > 7) {
1741 wxString msg(" Invalid nColorSize data, corrupt in PostInit() on chart ");
1742 msg.Append(m_FullPath);
1743 wxLogMessage(msg);
1744 return INIT_FAIL_REMOVE;
1745 }
1746
1747 if (Size_X <= 0 || Size_X > INT_MAX / 4 || Size_Y <= 0 ||
1748 Size_Y - 1 > INT_MAX / 4) {
1749 wxString msg(
1750 " Invalid Size_X/Size_Y data, corrupt in PostInit() on chart ");
1751 msg.Append(m_FullPath);
1752 wxLogMessage(msg);
1753 return INIT_FAIL_REMOVE;
1754 }
1755
1756 // Validate the palette array, substituting DEFAULT for missing entries
1757 int nfwd_def = 1;
1758 int nrev_def = 1;
1759 if (pPalettes[COLOR_RGB_DEFAULT]) {
1760 nrev_def = pPalettes[COLOR_RGB_DEFAULT]->nRev;
1761 nfwd_def = pPalettes[COLOR_RGB_DEFAULT]->nFwd;
1762 }
1763
1764 for (int i = 0; i < N_BSB_COLORS; i++) {
1765 if (pPalettes[i] == NULL) {
1766 opncpnPalette *pNullSubPal = new opncpnPalette;
1767
1768 pNullSubPal->nFwd = nfwd_def; // copy the palette count
1769 pNullSubPal->nRev = nrev_def; // copy the palette count
1770 // Deep copy the palette rgb tables
1771 free(pNullSubPal->FwdPalette);
1772 pNullSubPal->FwdPalette = (int *)malloc(pNullSubPal->nFwd * sizeof(int));
1773 if (pPalettes[COLOR_RGB_DEFAULT])
1774 memcpy(pNullSubPal->FwdPalette,
1775 pPalettes[COLOR_RGB_DEFAULT]->FwdPalette,
1776 pNullSubPal->nFwd * sizeof(int));
1777
1778 free(pNullSubPal->RevPalette);
1779 pNullSubPal->RevPalette = (int *)malloc(pNullSubPal->nRev * sizeof(int));
1780 if (pPalettes[COLOR_RGB_DEFAULT])
1781 memcpy(pNullSubPal->RevPalette,
1782 pPalettes[COLOR_RGB_DEFAULT]->RevPalette,
1783 pNullSubPal->nRev * sizeof(int));
1784
1785 pPalettes[i] = pNullSubPal;
1786 }
1787 }
1788
1789 // Establish the palette type and default palette
1790 palette_direction = GetPaletteDir();
1791
1792 SetColorScheme(m_global_color_scheme, false);
1793
1794 // Allocate memory for ifs file buffering
1795 ifs_bufsize = Size_X * 4;
1796 ifs_buf = (unsigned char *)malloc(ifs_bufsize);
1797 if (!ifs_buf) return INIT_FAIL_REMOVE;
1798
1799 ifs_bufend = ifs_buf + ifs_bufsize;
1800 ifs_lp = ifs_bufend;
1801 ifs_file_offset = -ifs_bufsize;
1802
1803 // Create and load the line offset index table
1804 pline_table = NULL;
1805 pline_table = (int *)malloc((Size_Y + 1) * sizeof(int)); // Ugly....
1806 if (!pline_table) return INIT_FAIL_REMOVE;
1807
1808 ifs_bitmap->SeekI((Size_Y + 1) * -4,
1809 wxFromEnd); // go to Beginning of offset table
1810 pline_table[Size_Y] = ifs_bitmap->TellI(); // fill in useful last table entry
1811
1812 unsigned char *tmp = (unsigned char *)malloc(Size_Y * sizeof(int));
1813 ifs_bitmap->Read(tmp, Size_Y * sizeof(int));
1814 if (ifs_bitmap->LastRead() != Size_Y * sizeof(int)) {
1815 wxString msg(" Chart File corrupt in PostInit() on chart ");
1816 msg.Append(m_FullPath);
1817 wxLogMessage(msg);
1818 free(tmp);
1819
1820 return INIT_FAIL_REMOVE;
1821 }
1822
1823 int offset;
1824 unsigned char *b = tmp;
1825 for (int ifplt = 0; ifplt < Size_Y; ifplt++) {
1826 offset = 0;
1827 offset += *b++ * 256 * 256 * 256;
1828 offset += *b++ * 256 * 256;
1829 offset += *b++ * 256;
1830 offset += *b++;
1831
1832 pline_table[ifplt] = offset;
1833 }
1834 free(tmp);
1835 // Try to validate the line index
1836
1837 bool bline_index_ok = true;
1838 m_nLineOffset = 0;
1839
1840 wxULongLong bitmap_filesize = m_filesize;
1841 if ((m_ChartType == CHART_TYPE_GEO) && pBitmapFilePath)
1842 bitmap_filesize = wxFileName::GetSize(*pBitmapFilePath);
1843
1844 // look logically at the line offset table
1845 for (int iplt = 0; iplt < Size_Y - 1; iplt++) {
1846 if (pline_table[iplt] > bitmap_filesize) {
1847 wxString msg(" Chart File corrupt in PostInit() on chart ");
1848 msg.Append(m_FullPath);
1849 wxLogMessage(msg);
1850
1851 return INIT_FAIL_REMOVE;
1852 }
1853
1854 int thisline_size = pline_table[iplt + 1] - pline_table[iplt];
1855 if (thisline_size < 0) {
1856 wxString msg(" Chart File corrupt in PostInit() on chart ");
1857 msg.Append(m_FullPath);
1858 wxLogMessage(msg);
1859
1860 return INIT_FAIL_REMOVE;
1861 }
1862 }
1863
1864 // For older charts, say Version 1.x, we will try to read the chart and check
1865 // the lines for coherence These older charts are more likely to have index
1866 // troubles.... We only need to check a few lines. Errors are quickly
1867 // apparent.
1868 double ver;
1869 m_bsb_ver.ToDouble(&ver);
1870 if (ver < 2.0) {
1871 for (int iplt = 0; iplt < 10; iplt++) {
1872 if (wxInvalidOffset ==
1873 ifs_bitmap->SeekI(pline_table[iplt], wxFromStart)) {
1874 wxString msg(" Chart File corrupt in PostInit() on chart ");
1875 msg.Append(m_FullPath);
1876 wxLogMessage(msg);
1877
1878 return INIT_FAIL_REMOVE;
1879 }
1880
1881 int thisline_size = pline_table[iplt + 1] - pline_table[iplt];
1882 ifs_bitmap->Read(ifs_buf, thisline_size);
1883
1884 unsigned char *lp = ifs_buf;
1885
1886 unsigned char byNext;
1887 int nLineMarker = 0;
1888 do {
1889 byNext = *lp++;
1890 nLineMarker = nLineMarker * 128 + (byNext & 0x7f);
1891 } while ((byNext & 0x80) != 0);
1892
1893 // Linemarker Correction factor needed here
1894 // Some charts start with LineMarker = 0, some with LineMarker = 1
1895 // Assume the first LineMarker found is the index base, and use
1896 // as a correction offset
1897
1898 if (iplt == 0) m_nLineOffset = nLineMarker;
1899
1900 if (nLineMarker != iplt + m_nLineOffset) {
1901 bline_index_ok = false;
1902 break;
1903 }
1904 }
1905 }
1906
1907 // Recreate the scan line index if the embedded version seems corrupt
1908 if (!bline_index_ok) {
1909 wxString msg(" Line Index corrupt, recreating Index for chart ");
1910 msg.Append(m_FullPath);
1911 wxLogMessage(msg);
1912 if (!CreateLineIndex()) {
1913 wxString msg(" Error creating Line Index for chart ");
1914 msg.Append(m_FullPath);
1915 wxLogMessage(msg);
1916 return INIT_FAIL_REMOVE;
1917 }
1918 }
1919
1920 // Allocate the Line Cache
1921 if (bUseLineCache) {
1922 pLineCache = (CachedLine *)malloc(Size_Y * sizeof(CachedLine));
1923 CachedLine *pt;
1924
1925 for (int ylc = 0; ylc < Size_Y; ylc++) {
1926 pt = &pLineCache[ylc];
1927 pt->bValid = false;
1928 pt->pPix = NULL; //(unsigned char *)malloc(1);
1929 pt->pTileOffset = NULL;
1930 }
1931 } else
1932 pLineCache = NULL;
1933
1934 // Validate/Set Depth Unit Type
1935 wxString test_str = m_DepthUnits.Upper();
1936 if (test_str.IsSameAs("FEET", FALSE))
1937 m_depth_unit_id = DEPTH_UNIT_FEET;
1938 else if (test_str.IsSameAs("METERS", FALSE))
1939 m_depth_unit_id = DEPTH_UNIT_METERS;
1940 else if (test_str.IsSameAs("METRES",
1941 FALSE)) // Special case for alternate spelling
1942 m_depth_unit_id = DEPTH_UNIT_METERS;
1943 else if (test_str.IsSameAs("METRIC", FALSE))
1944 m_depth_unit_id = DEPTH_UNIT_METERS;
1945 else if (test_str.IsSameAs("FATHOMS", FALSE))
1946 m_depth_unit_id = DEPTH_UNIT_FATHOMS;
1947 else if (test_str.Find("FATHOMS") !=
1948 wxNOT_FOUND) // Special case for "Fathoms and Feet"
1949 m_depth_unit_id = DEPTH_UNIT_FATHOMS;
1950 else if (test_str.Find("METERS") !=
1951 wxNOT_FOUND) // Special case for "Meters and decimeters"
1952 m_depth_unit_id = DEPTH_UNIT_METERS;
1953
1954 // Analyze Refpoints
1955 int analyze_ret_val = AnalyzeRefpoints();
1956 if (0 != analyze_ret_val) return INIT_FAIL_REMOVE;
1957
1958 bReadyToRender = true;
1959 return INIT_OK;
1960}
1961
1962bool ChartBaseBSB::CreateLineIndex() {
1963 // Assumes file stream ifs_bitmap is currently open
1964
1965 // wxBufferedInputStream *pbis = new wxBufferedInputStream(*ifss_bitmap);
1966
1967 // Seek to start of data
1968 ifs_bitmap->SeekI(nFileOffsetDataStart); // go to Beginning of data
1969
1970 for (int iplt = 0; iplt < Size_Y; iplt++) {
1971 int offset = ifs_bitmap->TellI();
1972
1973 int iscan = BSBScanScanline(ifs_bitmap);
1974
1975 // There is no sense reporting an error here, since we are recreating after
1976 // an error
1977 /*
1978 if(iscan > Size_Y)
1979 {
1980
1981 wxString msg("CreateLineIndex() failed on chart ");
1982 msg.Append(m_FullPath);
1983 wxLogMessage(msg);
1984 return false;
1985 }
1986
1987 // Skipped lines?
1988 if(iscan != iplt)
1989 {
1990 while((iplt < iscan) && (iplt < Size_Y))
1991 {
1992 pline_table[iplt] = 0;
1993 iplt++;
1994 }
1995 }
1996 */
1997 pline_table[iplt] = offset;
1998 }
1999
2000 return true;
2001}
2002
2003// Invalidate and Free the line cache contents
2004void ChartBaseBSB::InvalidateLineCache() {
2005 if (pLineCache) {
2006 CachedLine *pt;
2007 for (int ylc = 0; ylc < Size_Y; ylc++) {
2008 pt = &pLineCache[ylc];
2009 if (pt) {
2010 free(pt->pPix);
2011 pt->pPix = NULL;
2012 free(pt->pTileOffset);
2013 pt->pTileOffset = NULL;
2014 pt->bValid = false;
2015 }
2016 }
2017 }
2018}
2019
2020bool ChartBaseBSB::GetChartExtent(Extent *pext) {
2021 pext->NLAT = m_LatMax;
2022 pext->SLAT = m_LatMin;
2023 pext->ELON = m_LonMax;
2024 pext->WLON = m_LonMin;
2025
2026 return true;
2027}
2028
2029bool ChartBaseBSB::SetMinMax() {
2030 // Calculate the Chart Extents(M_LatMin, M_LonMin, etc.)
2031 // from the COVR data, for fast database search
2032 m_LonMax = -360.0;
2033 m_LonMin = 360.0;
2034 m_LatMax = -90.0;
2035 m_LatMin = 90.0;
2036
2037 Plypoint *ppp = (Plypoint *)GetCOVRTableHead(0);
2038 int cnPlypoint = GetCOVRTablenPoints(0);
2039
2040 for (int u = 0; u < cnPlypoint; u++) {
2041 if (ppp->lnp > m_LonMax) m_LonMax = ppp->lnp;
2042 if (ppp->lnp < m_LonMin) m_LonMin = ppp->lnp;
2043
2044 if (ppp->ltp > m_LatMax) m_LatMax = ppp->ltp;
2045 if (ppp->ltp < m_LatMin) m_LatMin = ppp->ltp;
2046
2047 ppp++;
2048 }
2049
2050 // Check for special cases
2051
2052 // Case 1: Chart spans International Date Line or Greenwich, Longitude
2053 // min/max is non-obvious.
2054 if ((m_LonMax * m_LonMin) < 0) // min/max are opposite signs
2055 {
2056 // Georeferencing is not yet available, so find the reference points
2057 // closest to min/max ply points
2058
2059 if (0 == nRefpoint) return false; // have to bail here
2060
2061 // for m_LonMax
2062 double min_dist_x = 360;
2063 int imaxclose = 0;
2064 for (int ic = 0; ic < nRefpoint; ic++) {
2065 double dist = sqrt(
2066 ((m_LatMax - pRefTable[ic].latr) * (m_LatMax - pRefTable[ic].latr)) +
2067 ((m_LonMax - pRefTable[ic].lonr) * (m_LonMax - pRefTable[ic].lonr)));
2068
2069 if (dist < min_dist_x) {
2070 min_dist_x = dist;
2071 imaxclose = ic;
2072 }
2073 }
2074
2075 // for m_LonMin
2076 double min_dist_n = 360;
2077 int iminclose = 0;
2078 for (int id = 0; id < nRefpoint; id++) {
2079 double dist = sqrt(
2080 ((m_LatMin - pRefTable[id].latr) * (m_LatMin - pRefTable[id].latr)) +
2081 ((m_LonMin - pRefTable[id].lonr) * (m_LonMin - pRefTable[id].lonr)));
2082
2083 if (dist < min_dist_n) {
2084 min_dist_n = dist;
2085 iminclose = id;
2086 }
2087 }
2088
2089 // Is this chart crossing IDL or Greenwich?
2090 // Make the check
2091 if (pRefTable[imaxclose].xr < pRefTable[iminclose].xr) {
2092 // This chart crosses IDL and needs a flip, meaning that all negative
2093 // longitudes need to be normalized and the min/max relcalculated This
2094 // code added to correct non-rectangular charts crossing IDL, such as
2095 // nz14605.kap
2096
2097 m_LonMax = -360.0;
2098 m_LonMin = 360.0;
2099 m_LatMax = -90.0;
2100 m_LatMin = 90.0;
2101
2102 Plypoint *ppp =
2103 (Plypoint *)GetCOVRTableHead(0); // Normalize the plypoints
2104 int cnPlypoint = GetCOVRTablenPoints(0);
2105
2106 for (int u = 0; u < cnPlypoint; u++) {
2107 if (ppp->lnp < 0.) ppp->lnp += 360.;
2108
2109 if (ppp->lnp > m_LonMax) m_LonMax = ppp->lnp;
2110 if (ppp->lnp < m_LonMin) m_LonMin = ppp->lnp;
2111
2112 if (ppp->ltp > m_LatMax) m_LatMax = ppp->ltp;
2113 if (ppp->ltp < m_LatMin) m_LatMin = ppp->ltp;
2114
2115 ppp++;
2116 }
2117 }
2118 }
2119
2120 // Case 2 Lons are both < -180, which means the extent will be reported
2121 // incorrectly and the plypoint structure will be wrong This case is seen
2122 // first on 81004_1.KAP, (Mariannas)
2123
2124 if ((m_LonMax < -180.) && (m_LonMin < -180.)) {
2125 m_LonMin += 360.; // Normalize the extents
2126 m_LonMax += 360.;
2127
2128 Plypoint *ppp = (Plypoint *)GetCOVRTableHead(0); // Normalize the plypoints
2129 int cnPlypoint = GetCOVRTablenPoints(0);
2130
2131 for (int u = 0; u < cnPlypoint; u++) {
2132 ppp->lnp += 360.;
2133 ppp++;
2134 }
2135 }
2136
2137 return true;
2138}
2139
2140void ChartBaseBSB::SetColorScheme(ColorScheme cs, bool bApplyImmediate) {
2141 // Here we convert (subjectively) the Global ColorScheme
2142 // to an appropriate BSB_Color_Capability index.
2143
2144 switch (cs) {
2145 case GLOBAL_COLOR_SCHEME_RGB:
2146 m_mapped_color_index = COLOR_RGB_DEFAULT;
2147 break;
2148 case GLOBAL_COLOR_SCHEME_DAY:
2149 case GLOBAL_COLOR_SCHEME_DAY_HICON:
2150 m_mapped_color_index = DAY;
2151 break;
2152 case GLOBAL_COLOR_SCHEME_DUSK:
2153 case GLOBAL_COLOR_SCHEME_NIGHT_HICON:
2154 m_mapped_color_index = DUSK;
2155 break;
2156 case GLOBAL_COLOR_SCHEME_NIGHT:
2157 m_mapped_color_index = NIGHT;
2158 break;
2159 default:
2160 m_mapped_color_index = DAY;
2161 break;
2162 }
2163
2164 pPalette = GetPalettePtr(m_mapped_color_index);
2165
2166 m_global_color_scheme = cs;
2167
2168 // Force a cache dump in a simple sideways manner
2169 if (bApplyImmediate) {
2170 m_cached_scale_ppm = 1.0;
2171 }
2172
2173 // Force a new thumbnail
2174 if (pThumbData) pThumbData->pDIBThumb = NULL;
2175}
2176
2177wxBitmap *ChartBaseBSB::CreateThumbnail(int tnx, int tny, ColorScheme cs) {
2178 // Calculate the size and divisors
2179
2180 int divx = wxMax(1, Size_X / (4 * tnx));
2181 int divy = wxMax(1, Size_Y / (4 * tny));
2182
2183 int div_factor = std::min(divx, divy);
2184
2185 int des_width = Size_X / div_factor;
2186 int des_height = Size_Y / div_factor;
2187
2188 wxRect gts;
2189 gts.x = 0; // full chart
2190 gts.y = 0;
2191 gts.width = Size_X;
2192 gts.height = Size_Y;
2193
2194 int this_bpp = 24; // for wxImage
2195 // Allocate the pixel storage needed for one line of chart bits
2196 unsigned char *pLineT = (unsigned char *)malloc((Size_X + 1) * BPP / 8);
2197
2198 // Scale the data quickly
2199 unsigned char *pPixTN =
2200 (unsigned char *)malloc(des_width * des_height * this_bpp / 8);
2201
2202 int ix = 0;
2203 int iy = 0;
2204 int iyd = 0;
2205 int ixd = 0;
2206 int yoffd;
2207 unsigned char *pxs;
2208 unsigned char *pxd;
2209
2210 // Temporarily set the color scheme
2211 ColorScheme cs_tmp = m_global_color_scheme;
2212 SetColorScheme(cs, false);
2213
2214 while (iyd < des_height) {
2215 if (0 == BSBGetScanline(pLineT, iy, 0, Size_X, 1)) // get a line
2216 {
2217 free(pLineT);
2218 free(pPixTN);
2219 return NULL;
2220 }
2221
2222 yoffd = iyd * des_width * this_bpp / 8; // destination y
2223
2224 ix = 0;
2225 ixd = 0;
2226 while (ixd < des_width) {
2227 pxs = pLineT + (ix * BPP / 8);
2228 pxd = pPixTN + (yoffd + (ixd * this_bpp / 8));
2229 *pxd++ = *pxs++;
2230 *pxd++ = *pxs++;
2231 *pxd = *pxs;
2232
2233 ix += div_factor;
2234 ixd++;
2235 }
2236
2237 iy += div_factor;
2238 iyd++;
2239 }
2240
2241 free(pLineT);
2242
2243 // Reset ColorScheme
2244 SetColorScheme(cs_tmp, false);
2245
2246 wxBitmap *retBMP;
2247
2248#ifdef ocpnUSE_ocpnBitmap
2249 wxBitmap *bmx2 = new ocpnBitmap(pPixTN, des_width, des_height, -1);
2250 wxImage imgx2 = bmx2->ConvertToImage();
2251 imgx2.Rescale(des_width / 4, des_height / 4, wxIMAGE_QUALITY_HIGH);
2252 retBMP = new wxBitmap(imgx2);
2253 delete bmx2;
2254#else
2255 wxImage thumb_image(des_width, des_height, pPixTN, true);
2256 thumb_image.Rescale(des_width / 4, des_height / 4, wxIMAGE_QUALITY_HIGH);
2257 retBMP = new wxBitmap(thumb_image);
2258#endif
2259
2260 free(pPixTN);
2261
2262 return retBMP;
2263}
2264
2265//-------------------------------------------------------------------------------------------------
2266// Get the Chart thumbnail data structure
2267// Creating the thumbnail bitmap as required
2268//-------------------------------------------------------------------------------------------------
2269
2270ThumbData *ChartBaseBSB::GetThumbData(int tnx, int tny, float lat, float lon) {
2271 // Create the bitmap if needed
2272 if (!pThumbData->pDIBThumb)
2273 pThumbData->pDIBThumb = CreateThumbnail(tnx, tny, m_global_color_scheme);
2274
2275 pThumbData->Thumb_Size_X = tnx;
2276 pThumbData->Thumb_Size_Y = tny;
2277
2278 // Plot the supplied Lat/Lon on the thumbnail
2279 int divx = Size_X / tnx;
2280 int divy = Size_Y / tny;
2281
2282 int div_factor = std::min(divx, divy);
2283
2284 double pixx, pixy;
2285
2286 // Using a temporary synthetic ViewPort and source rectangle,
2287 // calculate the ships position on the thumbnail
2288 ViewPort tvp;
2289 tvp.pix_width = tnx;
2290 tvp.pix_height = tny;
2291 tvp.view_scale_ppm = GetPPM() / div_factor;
2292 wxRect trex = Rsrc;
2293 Rsrc.x = 0;
2294 Rsrc.y = 0;
2295 latlong_to_pix_vp(lat, lon, pixx, pixy, tvp);
2296 Rsrc = trex;
2297
2298 pThumbData->ShipX = pixx; // / div_factor;
2299 pThumbData->ShipY = pixy; // / div_factor;
2300
2301 return pThumbData;
2302}
2303
2304bool ChartBaseBSB::UpdateThumbData(double lat, double lon) {
2305 // Plot the supplied Lat/Lon on the thumbnail
2306 // Return TRUE if the pixel location of ownship has changed
2307
2308 int divx = Size_X / pThumbData->Thumb_Size_X;
2309 int divy = Size_Y / pThumbData->Thumb_Size_Y;
2310
2311 int div_factor = std::min(divx, divy);
2312
2313 double pixx_test, pixy_test;
2314
2315 // Using a temporary synthetic ViewPort and source rectangle,
2316 // calculate the ships position on the thumbnail
2317 ViewPort tvp;
2318 tvp.pix_width = pThumbData->Thumb_Size_X;
2319 tvp.pix_height = pThumbData->Thumb_Size_Y;
2320 tvp.view_scale_ppm = GetPPM() / div_factor;
2321 wxRect trex = Rsrc;
2322 Rsrc.x = 0;
2323 Rsrc.y = 0;
2324 latlong_to_pix_vp(lat, lon, pixx_test, pixy_test, tvp);
2325 Rsrc = trex;
2326
2327 if ((pixx_test != pThumbData->ShipX) || (pixy_test != pThumbData->ShipY)) {
2328 pThumbData->ShipX = pixx_test;
2329 pThumbData->ShipY = pixy_test;
2330 return TRUE;
2331 } else
2332 return FALSE;
2333}
2334
2335//-----------------------------------------------------------------------
2336// Pixel to Lat/Long Conversion helpers
2337//-----------------------------------------------------------------------
2338static double polytrans(double *coeff, double lon, double lat);
2339
2340int ChartBaseBSB::vp_pix_to_latlong(ViewPort &vp, double pixx, double pixy,
2341 double *plat, double *plon) {
2342 if (bHaveEmbeddedGeoref) {
2343 double raster_scale = GetPPM() / vp.view_scale_ppm;
2344
2345 double px = pixx * raster_scale + Rsrc.x;
2346 double py = pixy * raster_scale + Rsrc.y;
2347 // pix_to_latlong(px, py, plat, plon);
2348
2349 if (1) {
2350 double lon = polytrans(pwx, px, py);
2351 lon = (lon < 0) ? lon + m_cph : lon - m_cph;
2352 *plon = lon - m_lon_datum_adjust;
2353 *plat = polytrans(pwy, px, py) - m_lat_datum_adjust;
2354 }
2355
2356 return 0;
2357 } else {
2358 double slat, slon;
2359 double xp, yp;
2360
2361 if (m_projection == PROJECTION_TRANSVERSE_MERCATOR) {
2362 // Use Projected Polynomial algorithm
2363
2364 double raster_scale = GetPPM() / vp.view_scale_ppm;
2365
2366 // Apply poly solution to vp center point
2367 double easting, northing;
2368 toTM(vp.clat + m_lat_datum_adjust, vp.clon + m_lon_datum_adjust,
2369 m_proj_lat, m_proj_lon, &easting, &northing);
2370 double xc = polytrans(cPoints.wpx, easting, northing);
2371 double yc = polytrans(cPoints.wpy, easting, northing);
2372
2373 // convert screen pixels to chart pixmap relative
2374 double px = xc + (pixx - (vp.pix_width / 2)) * raster_scale;
2375 double py = yc + (pixy - (vp.pix_height / 2)) * raster_scale;
2376
2377 // Apply polynomial solution to chart relative pixels to get e/n
2378 double east = polytrans(cPoints.pwx, px, py);
2379 double north = polytrans(cPoints.pwy, px, py);
2380
2381 // Apply inverse Projection to get lat/lon
2382 double lat, lon;
2383 fromTM(east, north, m_proj_lat, m_proj_lon, &lat, &lon);
2384
2385 // Datum adjustments.....
2386 //?? lon = (lon < 0) ? lon + m_cph : lon - m_cph;
2387 double slon_p = lon - m_lon_datum_adjust;
2388 double slat_p = lat - m_lat_datum_adjust;
2389
2390 // printf("%8g %8g %8g %8g %g\n", slat, slat_p, slon,
2391 // slon_p, slon - slon_p);
2392 slon = slon_p;
2393 slat = slat_p;
2394
2395 } else if (m_projection == PROJECTION_MERCATOR) {
2396 // Use Projected Polynomial algorithm
2397
2398 double raster_scale = GetPPM() / vp.view_scale_ppm;
2399
2400 // Apply poly solution to vp center point
2401 double easting, northing;
2402 toSM_ECC(vp.clat + m_lat_datum_adjust, vp.clon + m_lon_datum_adjust,
2403 m_proj_lat, m_proj_lon, &easting, &northing);
2404 double xc = polytrans(cPoints.wpx, easting, northing);
2405 double yc = polytrans(cPoints.wpy, easting, northing);
2406
2407 // convert screen pixels to chart pixmap relative
2408 double px = xc + (pixx - (vp.pix_width / 2)) * raster_scale;
2409 double py = yc + (pixy - (vp.pix_height / 2)) * raster_scale;
2410
2411 // Apply polynomial solution to chart relative pixels to get e/n
2412 double east = polytrans(cPoints.pwx, px, py);
2413 double north = polytrans(cPoints.pwy, px, py);
2414
2415 // Apply inverse Projection to get lat/lon
2416 double lat, lon;
2417 fromSM_ECC(east, north, m_proj_lat, m_proj_lon, &lat, &lon);
2418
2419 // Make Datum adjustments.....
2420 double slon_p = lon - m_lon_datum_adjust;
2421 double slat_p = lat - m_lat_datum_adjust;
2422
2423 slon = slon_p;
2424 slat = slat_p;
2425
2426 // printf("vp.clon %g xc %g px %g east %g
2427 // \n", vp.clon, xc, px, east);
2428
2429 } else if (m_projection == PROJECTION_POLYCONIC) {
2430 // Use Projected Polynomial algorithm
2431
2432 double raster_scale = GetPPM() / vp.view_scale_ppm;
2433
2434 // Apply poly solution to vp center point
2435 double easting, northing;
2436 toPOLY(vp.clat + m_lat_datum_adjust, vp.clon + m_lon_datum_adjust,
2437 m_proj_lat, m_proj_lon, &easting, &northing);
2438 double xc = polytrans(cPoints.wpx, easting, northing);
2439 double yc = polytrans(cPoints.wpy, easting, northing);
2440
2441 // convert screen pixels to chart pixmap relative
2442 double px = xc + (pixx - (vp.pix_width / 2)) * raster_scale;
2443 double py = yc + (pixy - (vp.pix_height / 2)) * raster_scale;
2444
2445 // Apply polynomial solution to chart relative pixels to get e/n
2446 double east = polytrans(cPoints.pwx, px, py);
2447 double north = polytrans(cPoints.pwy, px, py);
2448
2449 // Apply inverse Projection to get lat/lon
2450 double lat, lon;
2451 fromPOLY(east, north, m_proj_lat, m_proj_lon, &lat, &lon);
2452
2453 // Make Datum adjustments.....
2454 double slon_p = lon - m_lon_datum_adjust;
2455 double slat_p = lat - m_lat_datum_adjust;
2456
2457 slon = slon_p;
2458 slat = slat_p;
2459
2460 } else {
2461 // Use a Mercator estimator, with Eccentricity corrrection applied
2462 int dx = pixx - (vp.pix_width / 2);
2463 int dy = (vp.pix_height / 2) - pixy;
2464
2465 xp = (dx * cos(vp.skew)) - (dy * sin(vp.skew));
2466 yp = (dy * cos(vp.skew)) + (dx * sin(vp.skew));
2467
2468 double d_east = xp / vp.view_scale_ppm;
2469 double d_north = yp / vp.view_scale_ppm;
2470
2471 fromSM_ECC(d_east, d_north, vp.clat, vp.clon, &slat, &slon);
2472 }
2473
2474 *plat = slat;
2475
2476 if (slon < -180.)
2477 slon += 360.;
2478 else if (slon > 180.)
2479 slon -= 360.;
2480 *plon = slon;
2481
2482 return 0;
2483 }
2484}
2485
2486int ChartBaseBSB::latlong_to_pix_vp(double lat, double lon, double &pixx,
2487 double &pixy, ViewPort &vp) {
2488 double alat, alon;
2489
2490 if (bHaveEmbeddedGeoref) {
2491 double alat, alon;
2492
2493 alon = lon + m_lon_datum_adjust;
2494 alat = lat + m_lat_datum_adjust;
2495
2496 AdjustLongitude(alon);
2497
2498 if (1) {
2499 /* change longitude phase (CPH) */
2500 double lonp = (alon < 0) ? alon + m_cph : alon - m_cph;
2501 double xd = polytrans(wpx, lonp, alat);
2502 double yd = polytrans(wpy, lonp, alat);
2503
2504 double raster_scale = GetPPM() / vp.view_scale_ppm;
2505
2506 pixx = (xd - Rsrc.x) / raster_scale;
2507 pixy = (yd - Rsrc.y) / raster_scale;
2508
2509 return 0;
2510 }
2511 } else {
2512 double easting, northing;
2513 double xlon = lon;
2514
2515 // Make sure lon and lon0 are same phase
2516 /*
2517 if((xlon * vp.clon) < 0.)
2518 {
2519 if(xlon < 0.)
2520 xlon += 360.;
2521 else
2522 xlon -= 360.;
2523 }
2524
2525 if(fabs(xlon - vp.clon) > 180.)
2526 {
2527 if(xlon > vp.clon)
2528 xlon -= 360.;
2529 else
2530 xlon += 360.;
2531 }
2532 */
2533
2534 if (m_projection == PROJECTION_TRANSVERSE_MERCATOR) {
2535 // Use Projected Polynomial algorithm
2536
2537 alon = lon + m_lon_datum_adjust;
2538 alat = lat + m_lat_datum_adjust;
2539
2540 // Get e/n from TM Projection
2541 toTM(alat, alon, m_proj_lat, m_proj_lon, &easting, &northing);
2542
2543 // Apply poly solution to target point
2544 double xd = polytrans(cPoints.wpx, easting, northing);
2545 double yd = polytrans(cPoints.wpy, easting, northing);
2546
2547 // Apply poly solution to vp center point
2548 toTM(vp.clat + m_lat_datum_adjust, vp.clon + m_lon_datum_adjust,
2549 m_proj_lat, m_proj_lon, &easting, &northing);
2550 double xc = polytrans(cPoints.wpx, easting, northing);
2551 double yc = polytrans(cPoints.wpy, easting, northing);
2552
2553 // Calculate target point relative to vp center
2554 double raster_scale = GetPPM() / vp.view_scale_ppm;
2555
2556 double xs = xc - vp.pix_width * raster_scale / 2;
2557 double ys = yc - vp.pix_height * raster_scale / 2;
2558
2559 pixx = (xd - xs) / raster_scale;
2560 pixy = (yd - ys) / raster_scale;
2561
2562 } else if (m_projection == PROJECTION_MERCATOR) {
2563 // Use Projected Polynomial algorithm
2564
2565 alon = lon + m_lon_datum_adjust;
2566 alat = lat + m_lat_datum_adjust;
2567
2568 // Get e/n from Projection
2569 xlon = alon;
2570 AdjustLongitude(xlon);
2571 toSM_ECC(alat, xlon, m_proj_lat, m_proj_lon, &easting, &northing);
2572
2573 // Apply poly solution to target point
2574 double xd = polytrans(cPoints.wpx, easting, northing);
2575 double yd = polytrans(cPoints.wpy, easting, northing);
2576
2577 // Apply poly solution to vp center point
2578 double xlonc = vp.clon;
2579 AdjustLongitude(xlonc);
2580
2581 toSM_ECC(vp.clat + m_lat_datum_adjust, xlonc + m_lon_datum_adjust,
2582 m_proj_lat, m_proj_lon, &easting, &northing);
2583 double xc = polytrans(cPoints.wpx, easting, northing);
2584 double yc = polytrans(cPoints.wpy, easting, northing);
2585
2586 // Calculate target point relative to vp center
2587 double raster_scale = GetPPM() / vp.view_scale_ppm;
2588
2589 double xs = xc - vp.pix_width * raster_scale / 2;
2590 double ys = yc - vp.pix_height * raster_scale / 2;
2591
2592 pixx = (xd - xs) / raster_scale;
2593 pixy = (yd - ys) / raster_scale;
2594
2595 } else if (m_projection == PROJECTION_POLYCONIC) {
2596 // Use Projected Polynomial algorithm
2597
2598 alon = lon + m_lon_datum_adjust;
2599 alat = lat + m_lat_datum_adjust;
2600
2601 // Get e/n from Projection
2602 xlon = AdjustLongitude(alon);
2603 toPOLY(alat, xlon, m_proj_lat, m_proj_lon, &easting, &northing);
2604
2605 // Apply poly solution to target point
2606 double xd = polytrans(cPoints.wpx, easting, northing);
2607 double yd = polytrans(cPoints.wpy, easting, northing);
2608
2609 // Apply poly solution to vp center point
2610 double xlonc = AdjustLongitude(vp.clon);
2611
2612 toPOLY(vp.clat + m_lat_datum_adjust, xlonc + m_lon_datum_adjust,
2613 m_proj_lat, m_proj_lon, &easting, &northing);
2614 double xc = polytrans(cPoints.wpx, easting, northing);
2615 double yc = polytrans(cPoints.wpy, easting, northing);
2616
2617 // Calculate target point relative to vp center
2618 double raster_scale = GetPPM() / vp.view_scale_ppm;
2619
2620 double xs = xc - vp.pix_width * raster_scale / 2;
2621 double ys = yc - vp.pix_height * raster_scale / 2;
2622
2623 pixx = (xd - xs) / raster_scale;
2624 pixy = (yd - ys) / raster_scale;
2625
2626 } else {
2627 toSM_ECC(lat, xlon, vp.clat, vp.clon, &easting, &northing);
2628
2629 double epix = easting * vp.view_scale_ppm;
2630 double npix = northing * vp.view_scale_ppm;
2631
2632 double dx = epix * cos(vp.skew) + npix * sin(vp.skew);
2633 double dy = npix * cos(vp.skew) - epix * sin(vp.skew);
2634
2635 pixx = ((double)vp.pix_width / 2) + dx;
2636 pixy = ((double)vp.pix_height / 2) - dy;
2637 }
2638 return 0;
2639 }
2640
2641 return 1;
2642}
2643
2644void ChartBaseBSB::latlong_to_chartpix(double lat, double lon, double &pixx,
2645 double &pixy) {
2646 double alat, alon;
2647 pixx = 0.0;
2648 pixy = 0.0;
2649
2650 if (bHaveEmbeddedGeoref) {
2651 double alat, alon;
2652
2653 alon = lon + m_lon_datum_adjust;
2654 alat = lat + m_lat_datum_adjust;
2655
2656 alon = AdjustLongitude(alon);
2657
2658 /* change longitude phase (CPH) */
2659 double lonp = (alon < 0) ? alon + m_cph : alon - m_cph;
2660 pixx = polytrans(wpx, lonp, alat);
2661 pixy = polytrans(wpy, lonp, alat);
2662 } else {
2663 double easting, northing;
2664 double xlon = lon;
2665
2666 if (m_projection == PROJECTION_TRANSVERSE_MERCATOR) {
2667 // Use Projected Polynomial algorithm
2668
2669 alon = lon + m_lon_datum_adjust;
2670 alat = lat + m_lat_datum_adjust;
2671
2672 // Get e/n from TM Projection
2673 toTM(alat, alon, m_proj_lat, m_proj_lon, &easting, &northing);
2674
2675 // Apply poly solution to target point
2676 pixx = polytrans(cPoints.wpx, easting, northing);
2677 pixy = polytrans(cPoints.wpy, easting, northing);
2678
2679 } else if (m_projection == PROJECTION_MERCATOR) {
2680 // Use Projected Polynomial algorithm
2681
2682 alon = lon + m_lon_datum_adjust;
2683 alat = lat + m_lat_datum_adjust;
2684
2685 // Get e/n from Projection
2686 xlon = AdjustLongitude(alon);
2687
2688 toSM_ECC(alat, xlon, m_proj_lat, m_proj_lon, &easting, &northing);
2689
2690 // Apply poly solution to target point
2691 pixx = polytrans(cPoints.wpx, easting, northing);
2692 pixy = polytrans(cPoints.wpy, easting, northing);
2693
2694 } else if (m_projection == PROJECTION_POLYCONIC) {
2695 // Use Projected Polynomial algorithm
2696
2697 alon = lon + m_lon_datum_adjust;
2698 alat = lat + m_lat_datum_adjust;
2699
2700 // Get e/n from Projection
2701 xlon = AdjustLongitude(alon);
2702 toPOLY(alat, xlon, m_proj_lat, m_proj_lon, &easting, &northing);
2703
2704 // Apply poly solution to target point
2705 pixx = polytrans(cPoints.wpx, easting, northing);
2706 pixy = polytrans(cPoints.wpy, easting, northing);
2707 }
2708 }
2709}
2710
2711void ChartBaseBSB::chartpix_to_latlong(double pixx, double pixy, double *plat,
2712 double *plon) {
2713 if (bHaveEmbeddedGeoref) {
2714 double lon = polytrans(pwx, pixx, pixy);
2715 lon = (lon < 0) ? lon + m_cph : lon - m_cph;
2716 *plon = lon - m_lon_datum_adjust;
2717 *plat = polytrans(pwy, pixx, pixy) - m_lat_datum_adjust;
2718 } else {
2719 double slat, slon;
2720 if (m_projection == PROJECTION_TRANSVERSE_MERCATOR) {
2721 // Use Projected Polynomial algorithm
2722
2723 // Apply polynomial solution to chart relative pixels to get e/n
2724 double east = polytrans(cPoints.pwx, pixx, pixy);
2725 double north = polytrans(cPoints.pwy, pixx, pixy);
2726
2727 // Apply inverse Projection to get lat/lon
2728 double lat, lon;
2729 fromTM(east, north, m_proj_lat, m_proj_lon, &lat, &lon);
2730
2731 // Datum adjustments.....
2732 //?? lon = (lon < 0) ? lon + m_cph : lon - m_cph;
2733 slon = lon - m_lon_datum_adjust;
2734 slat = lat - m_lat_datum_adjust;
2735
2736 } else if (m_projection == PROJECTION_MERCATOR) {
2737 // Use Projected Polynomial algorithm
2738 // Apply polynomial solution to chart relative pixels to get e/n
2739 double east = polytrans(cPoints.pwx, pixx, pixy);
2740 double north = polytrans(cPoints.pwy, pixx, pixy);
2741
2742 // Apply inverse Projection to get lat/lon
2743 double lat, lon;
2744 fromSM_ECC(east, north, m_proj_lat, m_proj_lon, &lat, &lon);
2745
2746 // Make Datum adjustments.....
2747 slon = lon - m_lon_datum_adjust;
2748 slat = lat - m_lat_datum_adjust;
2749 } else if (m_projection == PROJECTION_POLYCONIC) {
2750 // Use Projected Polynomial algorithm
2751 // Apply polynomial solution to chart relative pixels to get e/n
2752 double east = polytrans(cPoints.pwx, pixx, pixy);
2753 double north = polytrans(cPoints.pwy, pixx, pixy);
2754
2755 // Apply inverse Projection to get lat/lon
2756 double lat, lon;
2757 fromPOLY(east, north, m_proj_lat, m_proj_lon, &lat, &lon);
2758
2759 // Make Datum adjustments.....
2760 slon = lon - m_lon_datum_adjust;
2761 slat = lat - m_lat_datum_adjust;
2762
2763 } else {
2764 slon = 0.;
2765 slat = 0.;
2766 }
2767
2768 *plat = slat;
2769
2770 if (slon < -180.)
2771 slon += 360.;
2772 else if (slon > 180.)
2773 slon -= 360.;
2774 *plon = slon;
2775 }
2776}
2777
2778void ChartBaseBSB::ComputeSourceRectangle(const ViewPort &vp,
2779 wxRect *pSourceRect) {
2780 m_raster_scale_factor = GetRasterScaleFactor(vp);
2781 double xd, yd;
2782 latlong_to_chartpix(vp.clat, vp.clon, xd, yd);
2783
2784 wxRealPoint pos, size;
2785
2786 pos.x = xd - (vp.pix_width * m_raster_scale_factor / 2);
2787 pos.y = yd - (vp.pix_height * m_raster_scale_factor / 2);
2788
2789 size.x = vp.pix_width * m_raster_scale_factor;
2790 size.y = vp.pix_height * m_raster_scale_factor;
2791
2792 *pSourceRect =
2793 wxRect(wxRound(pos.x), wxRound(pos.y), wxRound(size.x), wxRound(size.y));
2794}
2795
2796double ChartBaseBSB::GetRasterScaleFactor(const ViewPort &vp) {
2797 // This funny contortion is necessary to allow scale factors < 1, i.e.
2798 // overzoom
2799 return (wxRound(100000 * GetPPM() / vp.view_scale_ppm)) / 100000.;
2800}
2801
2802void ChartBaseBSB::SetVPRasterParms(const ViewPort &vpt) {
2803 // Calculate the potential datum offset parameters for this viewport, if
2804 // not WGS84
2805
2806 if (m_datum_index == DATUM_INDEX_WGS84 ||
2807 m_datum_index == DATUM_INDEX_UNKNOWN) {
2808 m_lon_datum_adjust = (-m_dtm_lon) / 3600.;
2809 m_lat_datum_adjust = (-m_dtm_lat) / 3600.;
2810 }
2811
2812 else {
2813 double to_lat, to_lon;
2814 MolodenskyTransform(vpt.clat, vpt.clon, &to_lat, &to_lon, m_datum_index,
2815 DATUM_INDEX_WGS84);
2816 m_lon_datum_adjust = -(to_lon - vpt.clon);
2817 m_lat_datum_adjust = -(to_lat - vpt.clat);
2818 if (m_b_apply_dtm) {
2819 m_lon_datum_adjust -= m_dtm_lon / 3600.;
2820 m_lat_datum_adjust -= m_dtm_lat / 3600.;
2821 }
2822 }
2823
2824 ComputeSourceRectangle(vpt, &Rsrc);
2825
2826 if (vpt.IsValid()) m_vp_render_last = vpt;
2827}
2828
2829bool ChartBaseBSB::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) {
2830 bool bInside = G_FloatPtInPolygon((MyFlPoint *)GetCOVRTableHead(0),
2831 GetCOVRTablenPoints(0), vp_proposed.clon,
2832 vp_proposed.clat);
2833 if (!bInside) return false;
2834
2835 int ret_val = 0;
2836 double binary_scale_factor = GetPPM() / vp_proposed.view_scale_ppm;
2837
2838 if (vp_last.IsValid()) {
2839 // We only need to adjust the VP if the cache is valid and potentially
2840 // usable, i.e. the scale factor is integer... The objective here is to
2841 // ensure that the VP center falls on an exact pixel boundary within the
2842 // cache
2843
2844 if (cached_image_ok && (binary_scale_factor > 1.0) &&
2845 (fabs(binary_scale_factor - wxRound(binary_scale_factor)) < 1e-5)) {
2846 if (m_b_cdebug)
2847 printf(" Possible Adjust VP for integer scale: %g\n",
2848 binary_scale_factor);
2849
2850 wxRect rprop;
2851 ComputeSourceRectangle(vp_proposed, &rprop);
2852
2853 double pixx, pixy;
2854 double lon_adj, lat_adj;
2855 latlong_to_pix_vp(vp_proposed.clat, vp_proposed.clon, pixx, pixy,
2856 vp_proposed);
2857 vp_pix_to_latlong(vp_proposed, pixx, pixy, &lat_adj, &lon_adj);
2858
2859 vp_proposed.clat = lat_adj;
2860 vp_proposed.clon = lon_adj;
2861 ret_val = 1;
2862 }
2863 }
2864
2865 return (ret_val > 0);
2866}
2867
2868bool ChartBaseBSB::IsRenderCacheable(wxRect &source, wxRect &dest) {
2869 double scale_x = (double)source.width / (double)dest.width;
2870
2871 if (scale_x <= 1.0) // overzoom
2872 {
2873 // if(m_b_cdebug)printf(" MISS<<<>>>GVUC: Overzoom\n");
2874 return false;
2875 }
2876
2877 // Using the cache only works for pure binary scale factors......
2878 if ((fabs(scale_x - wxRound(scale_x))) > .0001) {
2879 // if(m_b_cdebug)printf(" MISS<<<>>>GVUC: Not digital scale
2880 // test 1\n");
2881 return false;
2882 }
2883
2884 // Scale must be exactly digital...
2885 if ((int)(source.width / dest.width) != (int)wxRound(scale_x)) {
2886 // if(m_b_cdebug)printf(" MISS<<<>>>GVUC: Not digital scale
2887 // test 2\n");
2888 return false;
2889 }
2890
2891 return true;
2892}
2893
2894void ChartBaseBSB::GetValidCanvasRegion(const ViewPort &VPoint,
2895 OCPNRegion *pValidRegion) {
2896 SetVPRasterParms(VPoint);
2897
2898 double raster_scale = VPoint.view_scale_ppm / GetPPM();
2899
2900 int rxl, rxr;
2901 int ryb, ryt;
2902
2903 rxl = wxMax(-Rsrc.x * raster_scale, VPoint.rv_rect.x);
2904 rxr = wxMin((Size_X - Rsrc.x) * raster_scale,
2905 VPoint.rv_rect.width + VPoint.rv_rect.x);
2906
2907 ryt = wxMax(-Rsrc.y * raster_scale, VPoint.rv_rect.y);
2908 ryb = wxMin((Size_Y - Rsrc.y) * raster_scale,
2909 VPoint.rv_rect.height + VPoint.rv_rect.y);
2910
2911 pValidRegion->Clear();
2912 pValidRegion->Union(rxl, ryt, rxr - rxl, ryb - ryt);
2913}
2914
2915LLRegion ChartBaseBSB::GetValidRegion() {
2916 // should we cache this?
2917 double ll[8];
2918 chartpix_to_latlong(0, 0, ll + 0, ll + 1);
2919 chartpix_to_latlong(0, Size_Y, ll + 2, ll + 3);
2920 chartpix_to_latlong(Size_X, Size_Y, ll + 4, ll + 5);
2921 chartpix_to_latlong(Size_X, 0, ll + 6, ll + 7);
2922
2923 // for now don't allow raster charts to cross both 0 meridian and IDL
2924 // (complicated to deal with)
2925 for (int i = 1; i < 6; i += 2)
2926 if (fabs(ll[i] - ll[i + 2]) > 180) {
2927 // we detect crossing idl here, make all longitudes positive
2928 for (int i = 1; i < 8; i += 2)
2929 if (ll[i] < 0) ll[i] += 360;
2930 break;
2931 }
2932
2933 return LLRegion(4, ll);
2934}
2935
2936bool ChartBaseBSB::GetViewUsingCache(wxRect &source, wxRect &dest,
2937 const OCPNRegion &Region,
2938 ScaleTypeEnum scale_type) {
2939 wxRect s1;
2940 ScaleTypeEnum scale_type_corrected;
2941
2942 if (m_b_cdebug) printf(" source: %d %d\n", source.x, source.y);
2943 if (m_b_cdebug) printf(" cache: %d %d\n", cache_rect.x, cache_rect.y);
2944
2945 // Anything to do?
2946 if ((source == cache_rect) /*&& (cache_scale_method == scale_type)*/ &&
2947 (cached_image_ok)) {
2948 if (m_b_cdebug) printf(" GVUC: Cache is good, nothing to do\n");
2949 return false;
2950 }
2951
2952 double scale_x = (double)source.width / (double)dest.width;
2953
2954 if (m_b_cdebug) printf("GVUC: scale_x: %g\n", scale_x);
2955
2956 // Enforce a limit on bilinear scaling, for performance reasons
2957 scale_type_corrected = scale_type; // RENDER_LODEF; //scale_type;
2958 if (scale_x > m_bilinear_limit) scale_type_corrected = RENDER_LODEF;
2959
2960 {
2961 // if(b_cdebug)printf(" MISS<<<>>>GVUC: Intentional out\n");
2962 // return GetView( source, dest, scale_type_corrected );
2963 }
2964
2965 // Using the cache only works for pure binary scale factors......
2966 if ((fabs(scale_x - wxRound(scale_x))) > .0001) {
2967 if (m_b_cdebug) printf(" MISS<<<>>>GVUC: Not digital scale test 1\n");
2968 return GetView(source, dest, scale_type_corrected);
2969 }
2970
2971 // scale_type_corrected = RENDER_LODEF;
2972
2973 if (!cached_image_ok) {
2974 if (m_b_cdebug) printf(" MISS<<<>>>GVUC: Cache NOk\n");
2975 return GetView(source, dest, scale_type_corrected);
2976 }
2977
2978 if (scale_x < 1.0) // overzoom
2979 {
2980 if (m_b_cdebug) printf(" MISS<<<>>>GVUC: Overzoom\n");
2981 return GetView(source, dest, scale_type_corrected);
2982 }
2983
2984 // Scale must be exactly digital...
2985 if ((int)(source.width / dest.width) != (int)wxRound(scale_x)) {
2986 if (m_b_cdebug) printf(" MISS<<<>>>GVUC: Not digital scale test 2\n");
2987 return GetView(source, dest, scale_type_corrected);
2988 }
2989
2990 // Calculate the digital scale, e.g. 1,2,4,8,,,
2991 int cs1d = source.width / dest.width;
2992 if (abs(source.x - cache_rect.x) % cs1d) {
2993 if (m_b_cdebug)
2994 printf(" source.x: %d cache_rect.x: %d cs1d: %d\n", source.x,
2995 cache_rect.x, cs1d);
2996 if (m_b_cdebug) printf(" MISS<<<>>>GVUC: x mismatch\n");
2997 return GetView(source, dest, scale_type_corrected);
2998 }
2999 if (abs(source.y - cache_rect.y) % cs1d) {
3000 if (m_b_cdebug) printf(" MISS<<<>>>GVUC: y mismatch\n");
3001 return GetView(source, dest, scale_type_corrected);
3002 }
3003
3004 if (pPixCache && ((pPixCache->GetWidth() != dest.width) ||
3005 (pPixCache->GetHeight() != dest.height))) {
3006 if (m_b_cdebug) printf(" MISS<<<>>>GVUC: dest size mismatch\n");
3007 return GetView(source, dest, scale_type_corrected);
3008 }
3009
3010 int stride_rows =
3011 (source.y + source.height) - (cache_rect.y + cache_rect.height);
3012 int scaled_stride_rows = (int)(stride_rows / scale_x);
3013
3014 if (abs(stride_rows) >= source.height) // Pan more than one screen
3015 return GetView(source, dest, scale_type_corrected);
3016
3017 int stride_pixels =
3018 (source.x + source.width) - (cache_rect.x + cache_rect.width);
3019 int scaled_stride_pixels = (int)(stride_pixels / scale_x);
3020
3021 if (abs(stride_pixels) >= source.width) // Pan more than one screen
3022 return GetView(source, dest, scale_type_corrected);
3023
3024 if (m_b_cdebug) printf(" GVUC Using raster data cache\n");
3025
3026 ScaleTypeEnum pan_scale_type_x = scale_type_corrected;
3027 ScaleTypeEnum pan_scale_type_y = scale_type_corrected;
3028
3029 // "Blit" the valid pixels out of the way
3030 if (pPixCache) {
3031 int height = pPixCache->GetHeight();
3032 int width = pPixCache->GetWidth();
3033 int buffer_stride_bytes = pPixCache->GetLinePitch();
3034
3035 unsigned char *ps;
3036 unsigned char *pd;
3037
3038 if (stride_rows > 0) // pan down
3039 {
3040 ps = pPixCache->GetpData() +
3041 (abs(scaled_stride_rows) * buffer_stride_bytes);
3042 if (stride_pixels > 0) ps += scaled_stride_pixels * BPP / 8;
3043
3044 pd = pPixCache->GetpData();
3045 if (stride_pixels <= 0) pd += abs(scaled_stride_pixels) * BPP / 8;
3046
3047 for (int iy = 0; iy < (height - abs(scaled_stride_rows)); iy++) {
3048 memmove(pd, ps, (width - abs(scaled_stride_pixels)) * BPP / 8);
3049 ps += buffer_stride_bytes;
3050 pd += buffer_stride_bytes;
3051 }
3052
3053 } else {
3054 ps = pPixCache->GetpData() +
3055 ((height - abs(scaled_stride_rows) - 1) * buffer_stride_bytes);
3056 if (stride_pixels > 0) // make a hole on right
3057 ps += scaled_stride_pixels * BPP / 8;
3058
3059 pd = pPixCache->GetpData() + ((height - 1) * buffer_stride_bytes);
3060 if (stride_pixels <= 0) // make a hole on the left
3061 pd += abs(scaled_stride_pixels) * BPP / 8;
3062
3063 for (int iy = 0; iy < (height - abs(scaled_stride_rows)); iy++) {
3064 memmove(pd, ps, (width - abs(scaled_stride_pixels)) * BPP / 8);
3065 ps -= buffer_stride_bytes;
3066 pd -= buffer_stride_bytes;
3067 }
3068 }
3069
3070 // Y Pan
3071 if (source.y != cache_rect.y) {
3072 wxRect sub_dest = dest;
3073 sub_dest.height = abs(scaled_stride_rows);
3074
3075 if (stride_rows > 0) // pan down
3076 {
3077 sub_dest.y = height - scaled_stride_rows;
3078
3079 } else {
3080 sub_dest.y = 0;
3081 }
3082
3083 // Get the new bits needed
3084
3085 // A little optimization...
3086 // No sense in fetching bits that are not part of the ultimate render
3087 // region
3088 wxRegionContain rc = Region.Contains(sub_dest);
3089 if ((wxPartRegion == rc) || (wxInRegion == rc)) {
3090 GetAndScaleData(pPixCache->GetpData(), pPixCache->GetLength(), source,
3091 source.width, sub_dest, width, cs1d, pan_scale_type_y);
3092 }
3093 pPixCache->Update();
3094
3095 // Update the cached parameters, Y only
3096
3097 cache_rect.y = source.y;
3098 // cache_rect = source;
3099 cache_rect_scaled = dest;
3100 cached_image_ok = 1;
3101
3102 } // Y Pan
3103
3104 // X Pan
3105 if (source.x != cache_rect.x) {
3106 wxRect sub_dest = dest;
3107 sub_dest.width = abs(scaled_stride_pixels);
3108
3109 if (stride_pixels > 0) // pan right
3110 {
3111 sub_dest.x = width - scaled_stride_pixels;
3112 } else // pan left
3113 {
3114 sub_dest.x = 0;
3115 }
3116
3117 // Get the new bits needed
3118
3119 // A little optimization...
3120 // No sense in fetching bits that are not part of the ultimate render
3121 // region
3122 wxRegionContain rc = Region.Contains(sub_dest);
3123 if ((wxPartRegion == rc) || (wxInRegion == rc)) {
3124 GetAndScaleData(pPixCache->GetpData(), pPixCache->GetLength(), source,
3125 source.width, sub_dest, width, cs1d, pan_scale_type_x);
3126 }
3127
3128 pPixCache->Update();
3129
3130 // Update the cached parameters
3131 cache_rect = source;
3132 cache_rect_scaled = dest;
3133 cached_image_ok = 1;
3134
3135 } // X pan
3136
3137 return true;
3138 }
3139 return false;
3140}
3141
3142int s_dc;
3143
3144bool ChartBaseBSB::RenderViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint) {
3145 SetVPRasterParms(VPoint);
3146
3147 OCPNRegion rgn(0, 0, VPoint.pix_width, VPoint.pix_height);
3148
3149 bool bsame_region = (rgn == m_last_region); // only want to do this once
3150
3151 if (!bsame_region) cached_image_ok = false;
3152
3153 m_last_region = rgn;
3154
3155 return RenderRegionViewOnDC(dc, VPoint, rgn);
3156}
3157
3158bool ChartBaseBSB::RenderRegionViewOnGL(const wxGLContext &glc,
3159 const ViewPort &VPoint,
3160 const OCPNRegion &RectRegion,
3161 const LLRegion &Region) {
3162 return true;
3163}
3164
3165bool ChartBaseBSB::RenderRegionViewOnDC(wxMemoryDC &dc, const ViewPort &VPoint,
3166 const OCPNRegion &Region) {
3167 SetVPRasterParms(VPoint);
3168
3169 wxRect dest(0, 0, VPoint.pix_width, VPoint.pix_height);
3170 // double factor = ((double)Rsrc.width)/((double)dest.width);
3171 double factor = GetRasterScaleFactor(VPoint);
3172 if (m_b_cdebug) {
3173 printf("%d RenderRegion ScaleType: %d factor: %g\n", s_dc++,
3174 RENDER_HIDEF, factor);
3175 printf("Rect list:\n");
3176 OCPNRegionIterator upd(Region); // get the requested rect list
3177 while (upd.HaveRects()) {
3178 wxRect rect = upd.GetRect();
3179 printf(" %d %d %d %d\n", rect.x, rect.y, rect.width, rect.height);
3180 upd.NextRect();
3181 }
3182 }
3183
3184 // Invalidate the cache if the scale has changed or the viewport size has
3185 // changed....
3186 if ((fabs(m_cached_scale_ppm - VPoint.view_scale_ppm) > 1e-9) ||
3187 (m_last_vprect != dest)) {
3188 cached_image_ok = false;
3189 m_vp_render_last.Invalidate();
3190 }
3191 /*
3192 if(pPixCache)
3193 {
3194 if((pPixCache->GetWidth() != dest.width) ||
3195 (pPixCache->GetHeight() != dest.height))
3196 {
3197 delete pPixCache;
3198 pPixCache = new PixelCache(dest.width, dest.height, BPP);
3199 }
3200 }
3201 else
3202 pPixCache = new PixelCache(dest.width, dest.height, BPP);
3203 */
3204
3205 m_cached_scale_ppm = VPoint.view_scale_ppm;
3206 m_last_vprect = dest;
3207
3208 if (cached_image_ok) {
3209 // Anything to do?
3210 bool bsame_region = (Region == m_last_region); // only want to do this once
3211
3212 if ((bsame_region) && (Rsrc == cache_rect)) {
3213 pPixCache->SelectIntoDC(dc);
3214 if (m_b_cdebug) printf(" Using Current PixelCache\n");
3215 return false;
3216 }
3217 }
3218
3219 m_last_region = Region;
3220
3221 // Analyze the region requested
3222 // When rendering complex regions, (more than say 4 rectangles)
3223 // .OR. small proportions, then rectangle rendering may be faster
3224 // Also true if the scale is less than near unity, or overzoom.
3225 // This will be the case for backgrounds of the quilt.
3226
3227 /* Update for Version 2.4.0
3228 This logic seems flawed, at least for quilts which contain charts having
3229 non-rectangular coverage areas. These quilt regions decompose to ...LOTS... of
3230 rectangles, most of which are 1 pixel in height. This is very slow, due to the
3231 overhead of GetAndScaleData(). However, remember that overzoom never uses the
3232 cache, nor does non-binary scale factors.. So, we check to see if this is a
3233 cacheable render, and that the number of rectangles is "reasonable"
3234 */
3235
3236 // Get the region rectangle count
3237
3238 int n_rect = 0;
3239 OCPNRegionIterator upd(Region); // get the requested rect list
3240 while (upd.HaveRects()) {
3241 n_rect++;
3242 upd.NextRect();
3243 }
3244
3245 if ((!IsRenderCacheable(Rsrc, dest) && (n_rect > 4) && (n_rect < 20)) ||
3246 (factor < 1)) {
3247 if (m_b_cdebug)
3248 printf(" RenderRegion by rect iterator n_rect: %d\n", n_rect);
3249
3250 // Verify that the persistent pixel cache is at least as large as the
3251 // largest rectangle in the region
3252 wxRect dest_check_rect = dest;
3253 OCPNRegionIterator upd_check(Region); // get the requested rect list
3254 while (upd_check.HaveRects()) {
3255 wxRect rect = upd_check.GetRect();
3256 dest_check_rect.Union(rect);
3257 upd_check.NextRect();
3258 }
3259
3260 if (pPixCache) {
3261 if ((pPixCache->GetWidth() != dest_check_rect.width) ||
3262 (pPixCache->GetHeight() != dest_check_rect.height)) {
3263 delete pPixCache;
3264 pPixCache =
3265 new PixelCache(dest_check_rect.width, dest_check_rect.height, BPP);
3266 }
3267 } else
3268 pPixCache =
3269 new PixelCache(dest_check_rect.width, dest_check_rect.height, BPP);
3270
3271 ScaleTypeEnum ren_type = RENDER_LODEF;
3272
3273 // Decompose the region into rectangles, and fetch them into the target
3274 // dc
3275 OCPNRegionIterator upd(Region); // get the requested rect list
3276 int ir = 0;
3277 while (upd.HaveRects()) {
3278 wxRect rect = upd.GetRect();
3279
3280 // Floating point math can lead to negative rectangle origin.
3281 // If this happens, we arbitrarily shift the rectangle to be positive
3282 // semidefinite. This will cause at most a 1 pixlel error onscreen.
3283 if (rect.y < 0) rect.Offset(0, -rect.y);
3284 if (rect.x < 0) rect.Offset(-rect.x, 0);
3285
3286 GetAndScaleData(pPixCache->GetpData(), pPixCache->GetLength(), Rsrc,
3287 Rsrc.width, rect, pPixCache->GetWidth(), factor,
3288 ren_type);
3289
3290 ir++;
3291 upd.NextRect();
3292 ;
3293 }
3294
3295 pPixCache->Update();
3296
3297 // Update cache parameters
3298 cache_rect = Rsrc;
3299 cache_scale_method = ren_type;
3300 cached_image_ok = false; // Never cache this type of render
3301
3302 // Select the data into the dc
3303 pPixCache->SelectIntoDC(dc);
3304
3305 return true;
3306 }
3307
3308 // Default is to try using the cache
3309
3310 if (pPixCache) {
3311 if ((pPixCache->GetWidth() != dest.width) ||
3312 (pPixCache->GetHeight() != dest.height)) {
3313 delete pPixCache;
3314 pPixCache = new PixelCache(dest.width, dest.height, BPP);
3315 }
3316 } else
3317 pPixCache = new PixelCache(dest.width, dest.height, BPP);
3318
3319 if (m_b_cdebug) printf(" Render Region By GVUC\n");
3320
3321 // A performance enhancement.....
3322 ScaleTypeEnum scale_type_zoom = RENDER_HIDEF;
3323 double binary_scale_factor = VPoint.view_scale_ppm / GetPPM();
3324 if (binary_scale_factor < .20) scale_type_zoom = RENDER_LODEF;
3325
3326 bool bnewview = GetViewUsingCache(Rsrc, dest, Region, scale_type_zoom);
3327
3328 // Select the data into the dc
3329 pPixCache->SelectIntoDC(dc);
3330
3331 return bnewview;
3332}
3333
3334wxImage *ChartBaseBSB::GetImage() {
3335 int img_size_x = ((Size_X >> 2) * 4) + 4;
3336 wxImage *img = new wxImage(img_size_x, Size_Y, false);
3337
3338 unsigned char *ppnx = img->GetData();
3339
3340 for (int i = 0; i < Size_Y; i++) {
3341 wxRect source_rect(0, i, Size_X, 1);
3342 wxRect dest_rect(0, 0, Size_X, 1);
3343
3344 GetAndScaleData(img->GetData(), img_size_x * Size_Y * 3, source_rect,
3345 Size_X, dest_rect, Size_X, 1.0, RENDER_HIDEF);
3346
3347 ppnx += img_size_x * 3;
3348 }
3349
3350 return img;
3351}
3352
3353bool ChartBaseBSB::GetView(wxRect &source, wxRect &dest,
3354 ScaleTypeEnum scale_type) {
3355 assert(pPixCache != 0);
3356 // PixelCache *pPixCacheTemp = new PixelCache(dest.width, dest.height,
3357 // BPP);
3358
3359 // Get and Rescale the data directly into the temporary PixelCache data
3360 // buffer
3361 double factor = ((double)source.width) / ((double)dest.width);
3362
3363 /*
3364 if(!GetAndScaleData(&ppnx, source, source.width, dest, dest.width,
3365 factor, scale_type))
3366 {
3367 delete pPixCacheTemp; // Some error, retain
3368 old cache return false;
3369 }
3370 else
3371 {
3372 delete pPixCache; // new cache is OK
3373 pPixCache = pPixCacheTemp;
3374 }
3375 */
3376 GetAndScaleData(pPixCache->GetpData(), pPixCache->GetLength(), source,
3377 source.width, dest, dest.width, factor, scale_type);
3378 pPixCache->Update();
3379
3380 // Update cache parameters
3381
3382 cache_rect = source;
3383 cache_rect_scaled = dest;
3384 cache_scale_method = scale_type;
3385
3386 cached_image_ok = 1;
3387
3388 return TRUE;
3389}
3390
3391bool ChartBaseBSB::GetAndScaleData(unsigned char *ppn, size_t data_size,
3392 wxRect &source, int source_stride,
3393 wxRect &dest, int dest_stride,
3394 double scale_factor,
3395 ScaleTypeEnum scale_type) {
3396 unsigned char *s_data = NULL;
3397
3398 double factor = scale_factor;
3399 int Factor = (int)factor;
3400
3401 int target_width = (int)wxRound((double)source.width / factor);
3402 int target_height = (int)wxRound((double)source.height / factor);
3403
3404 int dest_line_length = dest_stride * BPP / 8;
3405
3406 // On MSW, if using DibSections, each scan line starts on a DWORD boundary.
3407 // The DibSection has been allocated to conform with this requirement.
3408#ifdef __PIX_CACHE_DIBSECTION__
3409 dest_line_length = (((dest_stride * 24) + 31) & ~31) >> 3;
3410#endif
3411
3412 if ((target_height == 0) || (target_width == 0)) return false;
3413
3414 // `volatile` may be needed with regard to `sigsetjmp()` below;
3415 // at least it prevents compiler warning about clobbering the variable.
3416 unsigned char *volatile target_data = ppn;
3417 unsigned char *data = ppn;
3418
3419 if (factor > 1) // downsampling
3420 {
3421 if (scale_type == RENDER_HIDEF) {
3422 // Allocate a working buffer based on scale factor
3423 int blur_factor = wxMax(2, Factor);
3424 int wb_size = (source.width) * (blur_factor * 2) * BPP / 8;
3425 s_data = (unsigned char *)malloc(wb_size); // work buffer
3426 unsigned char *pixel;
3427 int y_offset;
3428
3429 for (int y = dest.y; y < (dest.y + dest.height); y++) {
3430 // Read "blur_factor" lines
3431
3432 wxRect s1;
3433 s1.x = source.x;
3434 s1.y = source.y + (int)(y * factor);
3435 s1.width = source.width;
3436 s1.height = blur_factor;
3437 GetChartBits(s1, s_data, 1);
3438
3439 target_data = data + (y * dest_line_length /*dest_stride * BPP/8*/);
3440
3441 for (int x = 0; x < target_width; x++) {
3442 unsigned int avgRed = 0;
3443 unsigned int avgGreen = 0;
3444 unsigned int avgBlue = 0;
3445 unsigned int pixel_count = 0;
3446 unsigned char *pix0 = s_data + BPP / 8 * ((int)(x * factor));
3447 y_offset = 0;
3448
3449 if ((x * Factor) < (Size_X - source.x)) {
3450 // determine average
3451 for (int y1 = 0; y1 < blur_factor; ++y1) {
3452 pixel = pix0 + (BPP / 8 * y_offset);
3453 for (int x1 = 0; x1 < blur_factor; ++x1) {
3454 avgRed += pixel[0];
3455 avgGreen += pixel[1];
3456 avgBlue += pixel[2];
3457
3458 pixel += BPP / 8;
3459
3460 pixel_count++;
3461 }
3462 y_offset += source.width;
3463 }
3464
3465 if (0 == pixel_count) // Protect
3466 pixel_count = 1;
3467
3468 target_data[0] = avgRed / pixel_count; // >> scounter;
3469 target_data[1] = avgGreen / pixel_count; // >> scounter;
3470 target_data[2] = avgBlue / pixel_count; // >> scounter;
3471 target_data += BPP / 8;
3472 } else {
3473 target_data[0] = 0;
3474 target_data[1] = 0;
3475 target_data[2] = 0;
3476 target_data += BPP / 8;
3477 }
3478
3479 } // for x
3480
3481 } // for y
3482
3483 } // SCALE_BILINEAR
3484
3485 else if (scale_type == RENDER_LODEF) {
3486 int get_bits_submap = 1;
3487
3488 int scaler = 16;
3489
3490 if (source.width > 32767) // High underscale can exceed signed math bits
3491 scaler = 8;
3492
3493 int wb_size = (Size_X) * ((/*Factor +*/ 1) * 2) * BPP / 8;
3494 s_data = (unsigned char *)malloc(wb_size); // work buffer
3495
3496 long x_delta = (source.width << scaler) / target_width;
3497 long y_delta = (source.height << scaler) / target_height;
3498
3499 int y = dest.y; // starting here
3500 long ys = dest.y * y_delta;
3501
3502 while (y < dest.y + dest.height) {
3503 // Read 1 line at the right place from the source
3504
3505 wxRect s1;
3506 s1.x = 0;
3507 s1.y = source.y + (ys >> scaler);
3508 s1.width = Size_X;
3509 s1.height = 1;
3510 GetChartBits(s1, s_data, get_bits_submap);
3511
3512 target_data = data + (y * dest_line_length /*dest_stride * BPP/8*/) +
3513 (dest.x * BPP / 8);
3514
3515 long x = (source.x << scaler) + (dest.x * x_delta);
3516 long sizex16 = Size_X << scaler;
3517 int xt = dest.x;
3518
3519 while ((xt < dest.x + dest.width) && (x < 0)) {
3520 target_data[0] = 0;
3521 target_data[1] = 0;
3522 target_data[2] = 0;
3523
3524 target_data += BPP / 8;
3525 x += x_delta;
3526 xt++;
3527 }
3528
3529 while ((xt < dest.x + dest.width) && (x < sizex16)) {
3530 unsigned char *src_pixel = &s_data[(x >> scaler) * BPP / 8];
3531
3532 target_data[0] = src_pixel[0];
3533 target_data[1] = src_pixel[1];
3534 target_data[2] = src_pixel[2];
3535
3536 target_data += BPP / 8;
3537 x += x_delta;
3538 xt++;
3539 }
3540
3541 while (xt < dest.x + dest.width) {
3542 target_data[0] = 0;
3543 target_data[1] = 0;
3544 target_data[2] = 0;
3545
3546 target_data += BPP / 8;
3547 xt++;
3548 }
3549
3550 y++;
3551 ys += y_delta;
3552 }
3553
3554 } // SCALE_SUBSAMP
3555
3556 } else // factor < 1, overzoom
3557 {
3558 int i = 0;
3559 int j = 0;
3560 unsigned char *target_line_start = NULL;
3561 unsigned char *target_data_x = NULL;
3562 int y_offset = 0;
3563
3564#ifdef __WXGTK__
3565 sigaction(SIGSEGV, NULL,
3566 &sa_all_previous); // save existing action for this signal
3567
3568 struct sigaction temp;
3569 sigaction(SIGSEGV, NULL, &temp); // inspect existing action for this signal
3570
3571 temp.sa_handler = catch_signals_chart; // point to my handler
3572 sigemptyset(&temp.sa_mask); // make the blocking set
3573 // empty, so that all
3574 // other signals will be
3575 // unblocked during my handler
3576 temp.sa_flags = 0;
3577 sigaction(SIGSEGV, &temp, NULL);
3578
3579 if (sigsetjmp(env_chart,
3580 1)) // Something in the below code block faulted....
3581 {
3582 sigaction(SIGSEGV, &sa_all_previous, NULL); // reset signal handler
3583
3584 wxString msg;
3585 msg.Printf(" Caught SIGSEGV on GetandScaleData, Factor < 1");
3586 wxLogMessage(msg);
3587
3588 msg.Printf(
3589 " m_raster_scale_factor: %g source.width: %d dest.y: %d "
3590 "dest.x: %d dest.width: %d dest.height: %d ",
3591 m_raster_scale_factor, source.width, dest.y, dest.x, dest.width,
3592 dest.height);
3593 wxLogMessage(msg);
3594
3595 msg.Printf(
3596 " i: %d j: %d dest_stride: %d target_line_start: %p "
3597 "target_data_x: %p y_offset: %d",
3598 i, j, dest_stride, target_line_start, target_data_x, y_offset);
3599 wxLogMessage(msg);
3600
3601 free(s_data);
3602 return true;
3603
3604 }
3605
3606 else
3607#endif
3608 {
3609
3610 double xd, yd;
3611 latlong_to_chartpix(m_vp_render_last.clat, m_vp_render_last.clon, xd, yd);
3612 double xrd =
3613 xd - (m_vp_render_last.pix_width * m_raster_scale_factor / 2);
3614 double yrd =
3615 yd - (m_vp_render_last.pix_height * m_raster_scale_factor / 2);
3616 double x_vernier = (xrd - wxRound(xrd));
3617 double y_vernier = (yrd - wxRound(yrd));
3618 int x_vernier_i = (int)wxRound(x_vernier / m_raster_scale_factor);
3619 int y_vernier_i = (int)wxRound(y_vernier / m_raster_scale_factor);
3620
3621 // Seems safe enough to read all the required data
3622 // Although we must adjust (increase) temporary allocation for negative
3623 // source.x and for vernier
3624 int sx = wxMax(source.x, 0);
3625 s_data = (unsigned char *)malloc((sx + source.width + 2) *
3626 (source.height + 2) * BPP / 8);
3627
3628 wxRect vsource = source;
3629 vsource.height += 2; // get more bits to allow for vernier
3630 vsource.width += 2;
3631 vsource.x -= 1;
3632 vsource.y -= 1;
3633
3634 GetChartBits(vsource, s_data, 1);
3635 unsigned char *source_data = s_data;
3636
3637 j = dest.y;
3638
3639 while (j < dest.y + dest.height) {
3640 y_offset = (int)((j - y_vernier_i) * m_raster_scale_factor) *
3641 vsource.width; // into the source data
3642
3643 target_line_start =
3644 target_data + (j * dest_line_length /*dest_stride * BPP / 8*/);
3645 target_data_x = target_line_start + ((dest.x) * BPP / 8);
3646
3647 i = dest.x;
3648
3649 // Check data bounds to be sure of not overrunning the upstream buffer
3650 if ((target_data_x + (dest.width * BPP / 8)) >
3651 (target_data + data_size)) {
3652 j = dest.y + dest.height;
3653 } else {
3654 while (i < dest.x + dest.width) {
3655 memcpy(target_data_x,
3656 source_data + BPP / 8 *
3657 (y_offset + (int)((i + x_vernier_i) *
3658 m_raster_scale_factor)),
3659 BPP / 8);
3660
3661 target_data_x += BPP / 8;
3662
3663 i++;
3664 }
3665 }
3666
3667 j++;
3668 }
3669 }
3670#ifdef __WXGTK__
3671 sigaction(SIGSEGV, &sa_all_previous, NULL); // reset signal handler
3672#endif
3673 }
3674
3675 free(s_data);
3676
3677 return true;
3678}
3679
3680bool ChartBaseBSB::GetChartBits(wxRect &source, unsigned char *pPix,
3681 int sub_samp) {
3682 wxCriticalSectionLocker locker(m_critSect);
3683
3684 int iy;
3685#define FILL_BYTE 0
3686
3687 // Decode the KAP file RLL stream into image pPix
3688
3689 unsigned char *pCP;
3690 pCP = pPix;
3691
3692 iy = source.y;
3693
3694 while (iy < source.y + source.height) {
3695 if ((iy >= 0) && (iy < Size_Y)) {
3696 if (source.x >= 0) {
3697 if ((source.x + source.width) > Size_X) {
3698 if ((Size_X - source.x) < 0)
3699 memset(pCP, FILL_BYTE, source.width * BPP / 8);
3700 else {
3701 BSBGetScanline(pCP, iy, source.x, Size_X, sub_samp);
3702 memset(pCP + (Size_X - source.x) * BPP / 8, FILL_BYTE,
3703 (source.x + source.width - Size_X) * BPP / 8);
3704 }
3705 } else
3706 BSBGetScanline(pCP, iy, source.x, source.x + source.width, sub_samp);
3707 } else {
3708 if ((source.width + source.x) >= 0) {
3709 // Special case, black on left side
3710 // must ensure that (black fill length % sub_samp) == 0
3711
3712 int xfill_corrected = -source.x + (source.x % sub_samp); //+ve
3713 memset(pCP, FILL_BYTE, (xfill_corrected * BPP / 8));
3714 BSBGetScanline(pCP + (xfill_corrected * BPP / 8), iy, 0,
3715 source.width + source.x, sub_samp);
3716
3717 } else {
3718 memset(pCP, FILL_BYTE, source.width * BPP / 8);
3719 }
3720 }
3721 }
3722
3723 else // requested y is off chart
3724 {
3725 memset(pCP, FILL_BYTE, source.width * BPP / 8);
3726 }
3727
3728 pCP += source.width * BPP / 8 * sub_samp;
3729
3730 iy += sub_samp;
3731 } // while iy
3732
3733 return true;
3734}
3735
3736//-----------------------------------------------------------------------------------------------
3737// BSB File Read Support
3738//-----------------------------------------------------------------------------------------------
3739
3740//-----------------------------------------------------------------------------------------------
3741// ReadBSBHdrLine
3742//
3743// Read and return count of a line of BSB header file
3744//-----------------------------------------------------------------------------------------------
3745
3746int ChartBaseBSB::ReadBSBHdrLine(wxInputStream *ifs, char *buf, int buf_len_max)
3747
3748{
3749 char read_char;
3750 char cr_test;
3751 int line_length = 0;
3752 char *lbuf;
3753
3754 lbuf = buf;
3755
3756 while (!ifs->Eof() && line_length < buf_len_max) {
3757 int c = ifs->GetC();
3758 if (c < 0) break;
3759 read_char = c;
3760 if (0x1A == read_char) {
3761 ifs->Ungetch(read_char);
3762 return (0);
3763 }
3764
3765 if (0 == read_char) // embedded erroneous unicode character?
3766 read_char = 0x20;
3767
3768 // Manage continued lines
3769 if (read_char == 10 || read_char == 13) {
3770 // Check to see if there is an extra CR
3771 cr_test = ifs->GetC();
3772 if (cr_test == 13) cr_test = ifs->GetC(); // skip any extra CR
3773
3774 if (cr_test != 10 && cr_test != 13) ifs->Ungetch(cr_test);
3775 read_char = '\n';
3776 }
3777
3778 // Look for continued lines, indicated by ' ' in first position
3779 if (read_char == '\n') {
3780 cr_test = 0;
3781 cr_test = ifs->GetC();
3782
3783 if (cr_test != ' ') {
3784 ifs->Ungetch(cr_test);
3785 *lbuf = '\0';
3786 return line_length;
3787 }
3788
3789 // Merge out leading spaces
3790 while (cr_test == ' ') cr_test = ifs->GetC();
3791 ifs->Ungetch(cr_test);
3792
3793 // Add a comma
3794 *lbuf = ',';
3795 lbuf++;
3796 }
3797
3798 else {
3799 *lbuf = read_char;
3800 lbuf++;
3801 line_length++;
3802 }
3803
3804 } // while
3805
3806 // Terminate line
3807 if (line_length) *(lbuf - 1) = '\0';
3808
3809 return line_length;
3810}
3811
3812//-----------------------------------------------------------------------
3813// Scan a BSB Scan Line from raw data
3814// Leaving stream pointer at start of next line
3815//-----------------------------------------------------------------------
3816int ChartBaseBSB::BSBScanScanline(wxInputStream *pinStream) {
3817 int nLineMarker, nValueShift, iPixel = 0;
3818 unsigned char byValueMask, byCountMask;
3819 unsigned char byNext;
3820 int coffset;
3821
3822 // if(1)
3823 {
3824 // Read the line number.
3825 nLineMarker = 0;
3826 do {
3827 byNext = pinStream->GetC();
3828 nLineMarker = nLineMarker * 128 + (byNext & 0x7f);
3829 } while ((byNext & 0x80) != 0);
3830
3831 // Setup masking values.
3832 nValueShift = 7 - nColorSize;
3833 byValueMask = (((1 << nColorSize)) - 1) << nValueShift;
3834 byCountMask = (1 << (7 - nColorSize)) - 1;
3835
3836 // Read and simulate expansion of runs.
3837
3838 while (((byNext = pinStream->GetC()) != 0) && (iPixel < Size_X)) {
3839 // int nPixValue;
3840 int nRunCount;
3841 // nPixValue = (byNext & byValueMask) >> nValueShift;
3842
3843 nRunCount = byNext & byCountMask;
3844
3845 while ((byNext & 0x80) != 0) {
3846 byNext = pinStream->GetC();
3847 nRunCount = nRunCount * 128 + (byNext & 0x7f);
3848 }
3849
3850 if (iPixel + nRunCount + 1 > Size_X) nRunCount = Size_X - iPixel - 1;
3851
3852 // Store nPixValue in the destination
3853 // memset(pCL, nPixValue, nRunCount+1);
3854 // pCL += nRunCount+1;
3855 iPixel += nRunCount + 1;
3856 }
3857 coffset = pinStream->TellI();
3858 }
3859
3860 return nLineMarker;
3861}
3862// MSVC compiler makes a bad decision about when to inline (or not) some
3863// intrinsics, like memset(). So,... Here is a little hand-crafted memset()
3864// substitue for known short strings. It will be inlined by MSVC compiler
3865// using /02 settings
3866
3867inline void memset_short(unsigned char *dst, unsigned char cbyte, int count) {
3868#if 0 // def __MSVC__
3869 __asm {
3870 pushf // save Direction flag
3871 cld // set direction "up"
3872 mov edi, dst
3873 mov ecx, count
3874 mov al, cbyte
3875 rep stosb
3876 popf
3877 }
3878#else
3879 memset(dst, cbyte, count);
3880#endif
3881}
3882// could use a larger value for slightly less ram but slower random access,
3883// this is chosen as it is also the opengl tile size so should work well
3884#define TILE_SIZE 512
3885
3886// #define USE_OLD_CACHE // removed this (and simplify code below) once the new
3887// method is verified #define PRINT_TIMINGS // enable for profiling
3888
3889#ifdef PRINT_TIMINGS
3890class OCPNStopWatch {
3891public:
3892 OCPNStopWatch() { Reset(); }
3893 void Reset() { clock_gettime(CLOCK_REALTIME, &tp); }
3894
3895 double Time() {
3896 timespec tp_end;
3897 clock_gettime(CLOCK_REALTIME, &tp_end);
3898 return (tp_end.tv_sec - tp.tv_sec) * 1.e3 +
3899 (tp_end.tv_nsec - tp.tv_nsec) / 1.e6;
3900 }
3901
3902private:
3903 timespec tp;
3904};
3905#endif
3906
3907#define FAIL \
3908 do { \
3909 free(pt->pTileOffset); \
3910 pt->pTileOffset = NULL; \
3911 free(pt->pPix); \
3912 pt->pPix = NULL; \
3913 pt->bValid = false; \
3914 return 0; \
3915 } while (0)
3916
3917//-----------------------------------------------------------------------
3918// Get a BSB Scan Line Using Cache and scan line index if available
3919//-----------------------------------------------------------------------
3920int ChartBaseBSB::BSBGetScanline(unsigned char *pLineBuf, int y, int xs, int xl,
3921 int sub_samp)
3922
3923{
3924 unsigned char *prgb = pLineBuf;
3925 int nValueShift;
3926 unsigned char byValueMask, byCountMask;
3927 unsigned char byNext;
3928 CachedLine *pt = NULL, cached_line;
3929 unsigned char *pCL;
3930 int rgbval;
3931 unsigned char *lp;
3932 int ix = xs;
3933 int pos = 0;
3934
3935 if (bUseLineCache && pLineCache) {
3936 // Is the requested line in the cache, and valid?
3937 pt = &pLineCache[y];
3938 } else {
3939 pt = &cached_line;
3940 pt->bValid = false;
3941 }
3942
3943#ifdef PRINT_TIMINGS
3944 OCPNStopWatch sw;
3945 static double ttime;
3946 static int cnt;
3947 cnt++;
3948#endif
3949
3950 if (!pt->bValid) // not valid, allocate
3951 {
3952 pt->size = pline_table[y + 1] - pline_table[y];
3953
3954#ifdef USE_OLD_CACHE
3955 pt->pPix = (unsigned char *)malloc(Size_X);
3956#else
3957 pt->pTileOffset = (TileOffsetCache *)calloc(
3958 sizeof(TileOffsetCache) * (Size_X / TILE_SIZE + 1), 1);
3959 pt->pPix = (unsigned char *)malloc(pt->size);
3960#endif
3961 if (pline_table[y] == 0 || pline_table[y + 1] == 0) FAIL;
3962
3963 // as of 2015, in wxWidgets buffered streams don't test for a zero seek
3964 // so we check here to possibly avoid this seek with a measured performance
3965 // gain
3966 if (ifs_bitmap->TellI() != pline_table[y] &&
3967 wxInvalidOffset == ifs_bitmap->SeekI(pline_table[y], wxFromStart))
3968 FAIL;
3969
3970#ifdef USE_OLD_CACHE
3971 if (pt->size > ifs_bufsize) {
3972 unsigned char *tmp = ifs_buf;
3973 if (!(ifs_buf = (unsigned char *)realloc(ifs_buf, pt->size))) {
3974 free(tmp);
3975 FAIL;
3976 }
3977 ifs_bufsize = pt->size;
3978 }
3979
3980 lp = ifs_buf;
3981#else
3982 lp = pt->pPix;
3983#endif
3984 ifs_bitmap->Read(lp, pt->size);
3985
3986#ifdef USE_OLD_CACHE
3987 pCL = pt->pPix;
3988#else
3989 if (!bUseLineCache) {
3990 ix = 0;
3991 // skip the line number.
3992 do {
3993 if (pos < pt->size) {
3994 byNext = *lp++;
3995 pos++;
3996 } else {
3997 byNext = 0;
3998 }
3999 } while ((byNext & 0x80) != 0);
4000 goto nocachestart;
4001 }
4002 pCL = ifs_buf;
4003
4004 if (Size_X > ifs_bufsize) {
4005 unsigned char *tmp = ifs_buf;
4006 if (!(ifs_buf = (unsigned char *)realloc(ifs_buf, Size_X))) {
4007 free(tmp);
4008 FAIL;
4009 }
4010 ifs_bufsize = Size_X;
4011 }
4012#endif
4013 // At this point, the unexpanded, raw line is at *lp, and the expansion
4014 // destination is pCL
4015
4016 // skip the line number.
4017 do {
4018 if (pos < pt->size) {
4019 byNext = *lp++;
4020 pos++;
4021 } else {
4022 byNext = 0;
4023 }
4024 } while ((byNext & 0x80) != 0);
4025
4026 // Setup masking values.
4027 nValueShift = 7 - nColorSize;
4028 byValueMask = (((1 << nColorSize)) - 1) << nValueShift;
4029 byCountMask = (1 << (7 - nColorSize)) - 1;
4030
4031 // Read and expand runs.
4032 unsigned int iPixel = 0;
4033
4034#ifndef USE_OLD_CACHE
4035 pt->pTileOffset[0].offset = lp - pt->pPix;
4036 pt->pTileOffset[0].pixel = 0;
4037 unsigned int tileindex = 1, nextTile = TILE_SIZE;
4038#endif
4039 unsigned int nRunCount;
4040 unsigned char *end = pt->pPix + pt->size;
4041 while (iPixel < (unsigned int)Size_X)
4042#ifdef USE_OLD_CACHE
4043 {
4044 nPixValue = (byNext & byValueMask) >> nValueShift;
4045
4046 nRunCount = byNext & byCountMask;
4047
4048 while ((byNext & 0x80) != 0) {
4049 byNext = *lp++;
4050 nRunCount = nRunCount * 128 + (byNext & 0x7f);
4051 }
4052
4053 nRunCount++;
4054
4055 if (iPixel + nRunCount >
4056 (unsigned int)Size_X) // protection against corrupt data
4057 nRunCount = nRunCount - iPixel;
4058
4059 // Store nPixValue in the destination
4060 memset_short(pCL + iPixel, nPixValue, nRunCount);
4061 iPixel += nRunCount;
4062 }
4063#else
4064 // build tile offset table for faster random access
4065 {
4066 if (pos < pt->size) {
4067 byNext = *lp++;
4068 pos++;
4069 } else {
4070 byNext = 0;
4071 }
4072 unsigned char *offset = lp - 1;
4073 if (byNext == 0 || lp == end) {
4074 // finished early...corrupt?
4075 while (tileindex < (unsigned int)Size_X / TILE_SIZE + 1) {
4076 pt->pTileOffset[tileindex].offset = pt->pTileOffset[0].offset;
4077 pt->pTileOffset[tileindex].pixel = 0;
4078 tileindex++;
4079 }
4080 break;
4081 }
4082
4083 nRunCount = byNext & byCountMask;
4084
4085 while ((byNext & 0x80) != 0) {
4086 if (pos < pt->size) {
4087 byNext = *lp++;
4088 pos++;
4089 } else {
4090 byNext = 0;
4091 }
4092 nRunCount = nRunCount * 128 + (byNext & 0x7f);
4093 }
4094
4095 nRunCount++;
4096
4097 if (iPixel + nRunCount >
4098 (unsigned int)Size_X) // protection against corrupt data
4099 nRunCount = Size_X - iPixel;
4100
4101 while (iPixel + nRunCount > nextTile) {
4102 pt->pTileOffset[tileindex].offset = offset - pt->pPix;
4103 pt->pTileOffset[tileindex].pixel = iPixel;
4104 tileindex++;
4105 nextTile += TILE_SIZE;
4106 }
4107 iPixel += nRunCount;
4108 }
4109#endif
4110
4111 pt->bValid = true;
4112 }
4113
4114 // Line is valid, de-reference thru proper pallete directly to target
4115
4116 if (xl > Size_X) xl = Size_X;
4117
4118#ifdef USE_OLD_CACHE
4119 pCL = pt->pPix + xs;
4120
4121 // Optimization for most usual case
4122 if ((BPP == 24) && (1 == sub_samp)) {
4123 ix = xs;
4124 while (ix < xl - 1) {
4125 unsigned char cur_by = *pCL;
4126 rgbval = (int)(pPalette[cur_by]);
4127 while ((ix < xl - 1)) {
4128 if (cur_by != *pCL) break;
4129 *((int *)prgb) = rgbval;
4130 prgb += 3;
4131 pCL++;
4132 ix++;
4133 }
4134 }
4135 } else {
4136 int dest_inc_val_bytes = (BPP / 8) * sub_samp;
4137 ix = xs;
4138 while (ix < xl - 1) {
4139 unsigned char cur_by = *pCL;
4140 rgbval = (int)(pPalette[cur_by]);
4141 while ((ix < xl - 1)) {
4142 if (cur_by != *pCL) break;
4143 *((int *)prgb) = rgbval;
4144 prgb += dest_inc_val_bytes;
4145 pCL += sub_samp;
4146 ix += sub_samp;
4147 }
4148 }
4149 }
4150
4151 // Get the last pixel explicitely
4152 // irrespective of the sub_sampling factor
4153
4154 if (xs < xl - 1) {
4155 unsigned char *pCLast = pt->pPix + (xl - 1);
4156 unsigned char *prgb_last = pLineBuf + ((xl - 1) - xs) * BPP / 8;
4157
4158 rgbval = (int)(pPalette[*pCLast]); // last pixel
4159 unsigned char a = rgbval & 0xff;
4160 *prgb_last++ = a;
4161 a = (rgbval >> 8) & 0xff;
4162 *prgb_last++ = a;
4163 a = (rgbval >> 16) & 0xff;
4164 *prgb_last = a;
4165 }
4166#else
4167 {
4168 int tileindex = xs / TILE_SIZE;
4169 int tileoffset = pt->pTileOffset[tileindex].offset;
4170
4171 lp = pt->pPix + tileoffset;
4172 pos = tileoffset;
4173 ix = pt->pTileOffset[tileindex].pixel;
4174 }
4175
4176nocachestart:
4177 nValueShift = 7 - nColorSize;
4178 byValueMask = (((1 << nColorSize)) - 1) << nValueShift;
4179 byCountMask = (1 << (7 - nColorSize)) - 1;
4180 int nPixValue = 0; // satisfy stupid compiler warning
4181 bool bLastPixValueValid = false;
4182 while (ix < xl - 1) {
4183 if (pos < pt->size) {
4184 byNext = *lp++;
4185 pos++;
4186 } else {
4187 break;
4188 }
4189
4190 nPixValue = (byNext & byValueMask) >> nValueShift;
4191 unsigned int nRunCount;
4192
4193 if (byNext == 0)
4194 nRunCount = xl - ix; // corrupted chart, just run to the end
4195 else {
4196 nRunCount = byNext & byCountMask;
4197 while ((byNext & 0x80) != 0) {
4198 if (pos < pt->size) {
4199 byNext = *lp++;
4200 pos++;
4201 } else {
4202 nRunCount = xl - ix; // corrupted chart, just run to the end
4203 break;
4204 }
4205 nRunCount = nRunCount * 128 + (byNext & 0x7f);
4206 }
4207
4208 nRunCount++;
4209 }
4210
4211 if (ix < xs) {
4212 if (ix + nRunCount <= (unsigned int)xs) {
4213 ix += nRunCount;
4214 continue;
4215 }
4216 nRunCount -= xs - ix;
4217 ix = xs;
4218 }
4219
4220 if (ix + nRunCount >= (unsigned int)xl) {
4221 nRunCount = xl - 1 - ix;
4222 bLastPixValueValid = true;
4223 }
4224
4225 rgbval = (int)(pPalette[nPixValue]);
4226
4227 // Optimization for most usual case
4228 // currently this is the only case possible...
4229 // if((BPP == 24) && (1 == sub_samp))
4230 {
4231 int count = nRunCount;
4232 if (count < 16) {
4233 // for short runs, use simple loop
4234 while (count--) {
4235 *(uint32_t *)prgb = rgbval;
4236 prgb += 3;
4237 }
4238 } else if (rgbval == 0 || rgbval == 0xffffff) {
4239 // optimization for black or white (could work for any gray too)
4240 memset(prgb, rgbval, nRunCount * 3);
4241 prgb += nRunCount * 3;
4242 } else {
4243 // note: this may not be optimal for all processors and compilers
4244 // I optimized for x86_64 using gcc with -O3
4245 // it is probably possible to gain even faster performance by ensuring
4246 // alignment to 16 or 32byte boundary (depending on processor) then
4247 // using inline assembly
4248
4249#ifdef __ARM_ARCH
4250 // ARM needs 8 byte alignment for *(uint64_T *x) = *(uint64_T *y)
4251 // because the compiler will (probably) use the ldrd/strd instuction
4252 // pair. So, advance the prgb pointer until it is 8-byte aligned, and
4253 // then carry on if enough bytes are left to process as 64 bit elements
4254
4255 if ((long)prgb & 7) {
4256 while (count--) {
4257 *(uint32_t *)prgb = rgbval;
4258 prgb += 3;
4259 if (!((long)prgb & 7)) {
4260 if (count >= 8) break;
4261 }
4262 }
4263 }
4264#endif
4265
4266 // fill first 24 bytes
4267 uint64_t *b = (uint64_t *)prgb;
4268 for (int i = 0; i < 8; i++) {
4269 *(uint32_t *)prgb = rgbval;
4270 prgb += 3;
4271 }
4272 count -= 8;
4273
4274 // fill in blocks of 24 bytes
4275 uint64_t *y = (uint64_t *)prgb;
4276 int count_d8 = count >> 3;
4277 prgb += 24 * count_d8;
4278 while (count_d8--) {
4279 *y++ = b[0];
4280 *y++ = b[1];
4281 *y++ = b[2];
4282 }
4283
4284 // fill remaining bytes
4285 int rcount = count & 0x7;
4286 while (rcount--) {
4287 *(uint32_t *)prgb = rgbval;
4288 prgb += 3;
4289 }
4290 }
4291 }
4292
4293 ix += nRunCount;
4294 }
4295
4296 // Get the last pixel explicitely
4297 // irrespective of the sub_sampling factor
4298
4299 if (ix < xl) {
4300 if (!bLastPixValueValid) {
4301 if (pos < pt->size) {
4302 byNext = *lp++;
4303 pos++;
4304 } else {
4305 byNext = 0;
4306 }
4307 nPixValue = (byNext & byValueMask) >> nValueShift;
4308 }
4309 rgbval = (int)(pPalette[nPixValue]); // last pixel
4310 unsigned char a = rgbval & 0xff;
4311
4312 *prgb++ = a;
4313 a = (rgbval >> 8) & 0xff;
4314 *prgb++ = a;
4315 a = (rgbval >> 16) & 0xff;
4316 *prgb = a;
4317 }
4318#endif
4319
4320#ifdef PRINT_TIMINGS
4321 ttime += sw.Time();
4322
4323 if (cnt == 500000) {
4324 static int d;
4325 printf("cache time: %d %f\n", d, ttime / 1000.0);
4326 cnt = 0;
4327 d++;
4328 // ttime = 0;
4329 }
4330#endif
4331
4332 if (!bUseLineCache) {
4333#ifndef USE_OLD_CACHE
4334 free(pt->pTileOffset);
4335#endif
4336 free(pt->pPix);
4337 }
4338
4339 return 1;
4340}
4341
4342int *ChartBaseBSB::GetPalettePtr(BSB_Color_Capability color_index) {
4343 if (pPalettes[color_index]) {
4344 if (palette_direction == PaletteFwd)
4345 return (int *)(pPalettes[color_index]->FwdPalette);
4346 else
4347 return (int *)(pPalettes[color_index]->RevPalette);
4348 } else
4349 return NULL;
4350}
4351
4352PaletteDir ChartBaseBSB::GetPaletteDir() {
4353 // make a pixel cache
4354 PixelCache *pc = new PixelCache(4, 4, BPP);
4355 RGBO r = pc->GetRGBO();
4356 delete pc;
4357
4358 if (r == RGB)
4359 return PaletteFwd;
4360 else
4361 return PaletteRev;
4362}
4363
4364bool ChartBaseBSB::AnalyzeSkew() {
4365 double lonmin = 1000;
4366 double lonmax = -1000;
4367 double latmin = 90.;
4368 double latmax = -90.;
4369
4370 int nlonmin, nlonmax;
4371 nlonmin = 0;
4372 nlonmax = 0;
4373
4374 if (0 == nRefpoint) // bad chart georef...
4375 return (1);
4376
4377 for (int n = 0; n < nRefpoint; n++) {
4378 // Longitude
4379 if (pRefTable[n].lonr > lonmax) {
4380 lonmax = pRefTable[n].lonr;
4381 nlonmax = n;
4382 }
4383 if (pRefTable[n].lonr < lonmin) {
4384 lonmin = pRefTable[n].lonr;
4385 nlonmin = n;
4386 }
4387
4388 // Latitude
4389 if (pRefTable[n].latr < latmin) {
4390 latmin = pRefTable[n].latr;
4391 }
4392 if (pRefTable[n].latr > latmax) {
4393 latmax = pRefTable[n].latr;
4394 }
4395 }
4396
4397 // Special case for charts which cross the IDL
4398 if ((lonmin * lonmax) < 0) {
4399 if (pRefTable[nlonmin].xr > pRefTable[nlonmax].xr) {
4400 // walk the reference table and add 360 to any longitude which is < 0
4401 for (int n = 0; n < nRefpoint; n++) {
4402 if (pRefTable[n].lonr < 0.0) pRefTable[n].lonr += 360.;
4403 }
4404
4405 // And recalculate the min/max
4406 lonmin = 1000;
4407 lonmax = -1000;
4408
4409 for (int n = 0; n < nRefpoint; n++) {
4410 // Longitude
4411 if (pRefTable[n].lonr > lonmax) {
4412 lonmax = pRefTable[n].lonr;
4413 nlonmax = n;
4414 }
4415 if (pRefTable[n].lonr < lonmin) {
4416 lonmin = pRefTable[n].lonr;
4417 nlonmin = n;
4418 }
4419
4420 // Latitude
4421 if (pRefTable[n].latr < latmin) {
4422 latmin = pRefTable[n].latr;
4423 }
4424 if (pRefTable[n].latr > latmax) {
4425 latmax = pRefTable[n].latr;
4426 }
4427 }
4428 }
4429 }
4430
4431 // Find the two REF points that are farthest apart
4432 double dist_max = 0.;
4433 int imax = 0;
4434 int jmax = 0;
4435
4436 for (int i = 0; i < nRefpoint; i++) {
4437 for (int j = i + 1; j < nRefpoint; j++) {
4438 double dx = pRefTable[i].xr - pRefTable[j].xr;
4439 double dy = pRefTable[i].yr - pRefTable[j].yr;
4440 double dist = (dx * dx) + (dy * dy);
4441 if (dist > dist_max) {
4442 dist_max = dist;
4443 imax = i;
4444 jmax = j;
4445 }
4446 }
4447 }
4448
4449 double apparent_skew = 0;
4450
4451 if (m_projection == PROJECTION_MERCATOR) {
4452 double easting0, easting1, northing0, northing1;
4453 // Get the Merc projection of the two REF points
4454 toSM_ECC(pRefTable[imax].latr, pRefTable[imax].lonr, m_proj_lat, m_proj_lon,
4455 &easting0, &northing0);
4456 toSM_ECC(pRefTable[jmax].latr, pRefTable[jmax].lonr, m_proj_lat, m_proj_lon,
4457 &easting1, &northing1);
4458
4459 double skew_proj =
4460 atan2((easting1 - easting0), (northing1 - northing0)) * 180. / PI;
4461 double skew_points = atan2((pRefTable[jmax].yr - pRefTable[imax].yr),
4462 (pRefTable[jmax].xr - pRefTable[imax].xr)) *
4463 180. / PI;
4464
4465 apparent_skew = skew_points - skew_proj + 90.;
4466
4467 // normalize to +/- 180.
4468 if (fabs(apparent_skew) > 180.) {
4469 if (apparent_skew < 0.)
4470 apparent_skew += 360.;
4471 else
4472 apparent_skew -= 360.;
4473 }
4474 }
4475
4476 else if (m_projection == PROJECTION_TRANSVERSE_MERCATOR) {
4477 double easting0, easting1, northing0, northing1;
4478 // Get the TMerc projection of the two REF points
4479 toTM(pRefTable[imax].latr, pRefTable[imax].lonr, m_proj_lat, m_proj_lon,
4480 &easting0, &northing0);
4481 toTM(pRefTable[jmax].latr, pRefTable[jmax].lonr, m_proj_lat, m_proj_lon,
4482 &easting1, &northing1);
4483
4484 double skew_proj =
4485 atan2((easting1 - easting0), (northing1 - northing0)) * 180. / PI;
4486 double skew_points = atan2((pRefTable[jmax].yr - pRefTable[imax].yr),
4487 (pRefTable[jmax].xr - pRefTable[imax].xr)) *
4488 180. / PI;
4489
4490 apparent_skew = skew_points - skew_proj + 90.;
4491
4492 // normalize to +/- 180.
4493 if (fabs(apparent_skew) > 180.) {
4494 if (apparent_skew < 0.)
4495 apparent_skew += 360.;
4496 else
4497 apparent_skew -= 360.;
4498 }
4499
4500 if (fabs(apparent_skew - m_Chart_Skew) > 2) { // measured skew OK?
4501 // it may be that the projection longitude is simply wrong.
4502 // Check it.
4503 double dskew = fabs(apparent_skew - m_Chart_Skew);
4504 if ((m_proj_lon < lonmin) || (m_proj_lon > lonmax)) {
4505 // Try a projection longitude that is mid-meridian in the chart.
4506 double tentative_proj_lon = (lonmin + lonmax) / 2.;
4507
4508 toTM(pRefTable[imax].latr, pRefTable[imax].lonr, m_proj_lat,
4509 tentative_proj_lon, &easting0, &northing0);
4510 toTM(pRefTable[jmax].latr, pRefTable[jmax].lonr, m_proj_lat,
4511 tentative_proj_lon, &easting1, &northing1);
4512
4513 skew_proj =
4514 atan2((easting1 - easting0), (northing1 - northing0)) * 180. / PI;
4515 skew_points = atan2((pRefTable[jmax].yr - pRefTable[imax].yr),
4516 (pRefTable[jmax].xr - pRefTable[imax].xr)) *
4517 180. / PI;
4518
4519 apparent_skew = skew_points - skew_proj + 90.;
4520
4521 // normalize to +/- 180.
4522 if (fabs(apparent_skew) > 180.) {
4523 if (apparent_skew < 0.)
4524 apparent_skew += 360.;
4525 else
4526 apparent_skew -= 360.;
4527 }
4528
4529 // Better? If so, adopt the adjusted projection longitude
4530 if (fabs(apparent_skew - m_Chart_Skew) < dskew) {
4531 m_proj_lon = tentative_proj_lon;
4532 }
4533 }
4534 }
4535 } else // For all other projections, assume that skew specified in header is
4536 // correct
4537 apparent_skew = m_Chart_Skew;
4538
4539 if (fabs(apparent_skew - m_Chart_Skew) >
4540 2) { // measured skew is more than 2 degrees
4541 m_Chart_Skew = apparent_skew; // different from stated skew
4542
4543 wxString msg = " Warning: Skew override on chart ";
4544 msg.Append(m_FullPath);
4545 wxString msg1;
4546 msg1.Printf(" is %5g degrees", apparent_skew);
4547 msg.Append(msg1);
4548
4549 wxLogMessage(msg);
4550
4551 return false;
4552 }
4553
4554 return true;
4555}
4556
4557int ChartBaseBSB::AnalyzeRefpoints(bool b_testSolution) {
4558 int i, n;
4559 double elt, elg;
4560
4561 // Calculate the max/min reference points
4562
4563 float lonmin = 1000;
4564 float lonmax = -1000;
4565 float latmin = 90.;
4566 float latmax = -90.;
4567
4568 int plonmin = 100000;
4569 int plonmax = 0;
4570 int platmin = 100000;
4571 int platmax = 0;
4572 int nlonmin = 0, nlonmax = 0;
4573
4574 if (0 == nRefpoint) // bad chart georef...
4575 return (1);
4576
4577 for (n = 0; n < nRefpoint; n++) {
4578 // Longitude
4579 if (pRefTable[n].lonr > lonmax) {
4580 lonmax = pRefTable[n].lonr;
4581 plonmax = (int)pRefTable[n].xr;
4582 nlonmax = n;
4583 }
4584 if (pRefTable[n].lonr < lonmin) {
4585 lonmin = pRefTable[n].lonr;
4586 plonmin = (int)pRefTable[n].xr;
4587 nlonmin = n;
4588 }
4589
4590 // Latitude
4591 if (pRefTable[n].latr < latmin) {
4592 latmin = pRefTable[n].latr;
4593 platmin = (int)pRefTable[n].yr;
4594 }
4595 if (pRefTable[n].latr > latmax) {
4596 latmax = pRefTable[n].latr;
4597 platmax = (int)pRefTable[n].yr;
4598 }
4599 }
4600
4601 // Special case for charts which cross the IDL
4602 if ((lonmin * lonmax) < 0) {
4603 if (pRefTable[nlonmin].xr > pRefTable[nlonmax].xr) {
4604 // walk the reference table and add 360 to any longitude which is < 0
4605 for (n = 0; n < nRefpoint; n++) {
4606 if (pRefTable[n].lonr < 0.0) pRefTable[n].lonr += 360.;
4607 }
4608
4609 // And recalculate the min/max
4610 lonmin = 1000;
4611 lonmax = -1000;
4612
4613 for (n = 0; n < nRefpoint; n++) {
4614 // Longitude
4615 if (pRefTable[n].lonr > lonmax) {
4616 lonmax = pRefTable[n].lonr;
4617 plonmax = (int)pRefTable[n].xr;
4618 nlonmax = n;
4619 }
4620 if (pRefTable[n].lonr < lonmin) {
4621 lonmin = pRefTable[n].lonr;
4622 plonmin = (int)pRefTable[n].xr;
4623 nlonmin = n;
4624 }
4625
4626 // Latitude
4627 if (pRefTable[n].latr < latmin) {
4628 latmin = pRefTable[n].latr;
4629 platmin = (int)pRefTable[n].yr;
4630 }
4631 if (pRefTable[n].latr > latmax) {
4632 latmax = pRefTable[n].latr;
4633 platmax = (int)pRefTable[n].yr;
4634 }
4635 }
4636 }
4637 }
4638
4639 // Build the Control Point Structure, etc
4640 cPoints.count = nRefpoint;
4641 if (cPoints.status) {
4642 // AnalyzeRefpoints can be called twice
4643 free(cPoints.tx);
4644 free(cPoints.ty);
4645 free(cPoints.lon);
4646 free(cPoints.lat);
4647
4648 free(cPoints.pwx);
4649 free(cPoints.wpx);
4650 free(cPoints.pwy);
4651 free(cPoints.wpy);
4652 }
4653
4654 cPoints.tx = (double *)malloc(nRefpoint * sizeof(double));
4655 cPoints.ty = (double *)malloc(nRefpoint * sizeof(double));
4656 cPoints.lon = (double *)malloc(nRefpoint * sizeof(double));
4657 cPoints.lat = (double *)malloc(nRefpoint * sizeof(double));
4658
4659 cPoints.pwx = (double *)malloc(12 * sizeof(double));
4660 cPoints.wpx = (double *)malloc(12 * sizeof(double));
4661 cPoints.pwy = (double *)malloc(12 * sizeof(double));
4662 cPoints.wpy = (double *)malloc(12 * sizeof(double));
4663 cPoints.status = 1;
4664
4665 // Find the two REF points that are farthest apart
4666 double dist_max = 0.;
4667 int imax = 0;
4668 int jmax = 0;
4669
4670 for (i = 0; i < nRefpoint; i++) {
4671 for (int j = i + 1; j < nRefpoint; j++) {
4672 double dx = pRefTable[i].xr - pRefTable[j].xr;
4673 double dy = pRefTable[i].yr - pRefTable[j].yr;
4674 double dist = (dx * dx) + (dy * dy);
4675 if (dist > dist_max) {
4676 dist_max = dist;
4677 imax = i;
4678 jmax = j;
4679 }
4680 }
4681 }
4682
4683 // Georef solution depends on projection type
4684
4685 if (m_projection == PROJECTION_TRANSVERSE_MERCATOR) {
4686 double easting0, easting1, northing0, northing1;
4687 // Get the TMerc projection of the two REF points
4688 toTM(pRefTable[imax].latr, pRefTable[imax].lonr, m_proj_lat, m_proj_lon,
4689 &easting0, &northing0);
4690 toTM(pRefTable[jmax].latr, pRefTable[jmax].lonr, m_proj_lat, m_proj_lon,
4691 &easting1, &northing1);
4692
4693 // Calculate the scale factor using exact REF point math
4694 double dx2 = (pRefTable[jmax].xr - pRefTable[imax].xr) *
4695 (pRefTable[jmax].xr - pRefTable[imax].xr);
4696 double dy2 = (pRefTable[jmax].yr - pRefTable[imax].yr) *
4697 (pRefTable[jmax].yr - pRefTable[imax].yr);
4698 double dn2 = (northing1 - northing0) * (northing1 - northing0);
4699 double de2 = (easting1 - easting0) * (easting1 - easting0);
4700
4701 m_ppm_avg = sqrt(dx2 + dy2) / sqrt(dn2 + de2);
4702
4703 // Set up and solve polynomial solution for pix<->east/north as projected
4704 // Fill the cpoints structure with pixel points and transformed lat/lon
4705
4706 for (int n = 0; n < nRefpoint; n++) {
4707 double easting, northing;
4708 toTM(pRefTable[n].latr, pRefTable[n].lonr, m_proj_lat, m_proj_lon,
4709 &easting, &northing);
4710
4711 cPoints.tx[n] = pRefTable[n].xr;
4712 cPoints.ty[n] = pRefTable[n].yr;
4713 cPoints.lon[n] = easting;
4714 cPoints.lat[n] = northing;
4715 }
4716
4717 // Helper parameters
4718 cPoints.txmax = plonmax;
4719 cPoints.txmin = plonmin;
4720 cPoints.tymax = platmax;
4721 cPoints.tymin = platmin;
4722 toTM(latmax, lonmax, m_proj_lat, m_proj_lon, &cPoints.lonmax,
4723 &cPoints.latmax);
4724 toTM(latmin, lonmin, m_proj_lat, m_proj_lon, &cPoints.lonmin,
4725 &cPoints.latmin);
4726
4727 Georef_Calculate_Coefficients_Proj(&cPoints);
4728
4729 }
4730
4731 else if (m_projection == PROJECTION_MERCATOR) {
4732 double easting0, easting1, northing0, northing1;
4733 // Get the Merc projection of the two REF points
4734 toSM_ECC(pRefTable[imax].latr, pRefTable[imax].lonr, m_proj_lat, m_proj_lon,
4735 &easting0, &northing0);
4736 toSM_ECC(pRefTable[jmax].latr, pRefTable[jmax].lonr, m_proj_lat, m_proj_lon,
4737 &easting1, &northing1);
4738
4739 // Calculate the scale factor using exact REF point math
4740 // double dx = (pRefTable[jmax].xr - pRefTable[imax].xr);
4741 // double de = (easting1 - easting0);
4742 // m_ppm_avg = fabs(dx / de);
4743
4744 double dx2 = (pRefTable[jmax].xr - pRefTable[imax].xr) *
4745 (pRefTable[jmax].xr - pRefTable[imax].xr);
4746 double dy2 = (pRefTable[jmax].yr - pRefTable[imax].yr) *
4747 (pRefTable[jmax].yr - pRefTable[imax].yr);
4748 double dn2 = (northing1 - northing0) * (northing1 - northing0);
4749 double de2 = (easting1 - easting0) * (easting1 - easting0);
4750
4751 m_ppm_avg = sqrt(dx2 + dy2) / sqrt(dn2 + de2);
4752
4753 // Set up and solve polynomial solution for pix<->east/north as projected
4754 // Fill the cpoints structure with pixel points and transformed lat/lon
4755
4756 for (int n = 0; n < nRefpoint; n++) {
4757 double easting, northing;
4758 toSM_ECC(pRefTable[n].latr, pRefTable[n].lonr, m_proj_lat, m_proj_lon,
4759 &easting, &northing);
4760
4761 cPoints.tx[n] = pRefTable[n].xr;
4762 cPoints.ty[n] = pRefTable[n].yr;
4763 cPoints.lon[n] = easting;
4764 cPoints.lat[n] = northing;
4765 // printf(" x: %g y: %g east: %g north:
4766 // %g\n",pRefTable[n].xr, pRefTable[n].yr, easting,
4767 // northing);
4768 }
4769
4770 // Helper parameters
4771 cPoints.txmax = plonmax;
4772 cPoints.txmin = plonmin;
4773 cPoints.tymax = platmax;
4774 cPoints.tymin = platmin;
4775 toSM_ECC(latmax, lonmax, m_proj_lat, m_proj_lon, &cPoints.lonmax,
4776 &cPoints.latmax);
4777 toSM_ECC(latmin, lonmin, m_proj_lat, m_proj_lon, &cPoints.lonmin,
4778 &cPoints.latmin);
4779
4780 Georef_Calculate_Coefficients_Proj(&cPoints);
4781
4782 // for(int h=0 ; h < 10 ; h++)
4783 // printf("pix to east %d %g\n", h, cPoints.pwx[h]); //
4784 // pix to lon
4785 // for(int h=0 ; h < 10 ; h++)
4786 // printf("east to pix %d %g\n", h, cPoints.wpx[h]); //
4787 // lon to pix
4788
4789 /*
4790 if ((0 != m_Chart_DU ) && (0 != m_Chart_Scale))
4791 {
4792 double m_ppm_avg1 = m_Chart_DU * 39.37 / m_Chart_Scale;
4793 m_ppm_avg1 *= cos(m_proj_lat * PI / 180.); // correct to
4794 chart centroid
4795
4796 printf("BSB chart ppm_avg:%g ppm_avg1:%g\n", m_ppm_avg,
4797 m_ppm_avg1); m_ppm_avg = m_ppm_avg1;
4798 }
4799 */
4800 }
4801
4802 else if (m_projection == PROJECTION_POLYCONIC) {
4803 // This is interesting
4804 // On some BSB V 1.0 Polyconic charts (e.g. 14500_1, 1995), the projection
4805 // parameter Which is taken to be the central meridian of the projection
4806 // is of the wrong sign....
4807
4808 // We check for this case, and make a correction if necessary.....
4809 // Obviously, the projection meridian should be on the chart, i.e. between
4810 // the min and max longitudes....
4811 double proj_meridian = m_proj_lon;
4812
4813 if ((pRefTable[nlonmax].lonr >= -proj_meridian) &&
4814 (-proj_meridian >= pRefTable[nlonmin].lonr))
4815 m_proj_lon = -m_proj_lon;
4816
4817 double easting0, easting1, northing0, northing1;
4818 // Get the Poly projection of the two REF points
4819 toPOLY(pRefTable[imax].latr, pRefTable[imax].lonr, m_proj_lat, m_proj_lon,
4820 &easting0, &northing0);
4821 toPOLY(pRefTable[jmax].latr, pRefTable[jmax].lonr, m_proj_lat, m_proj_lon,
4822 &easting1, &northing1);
4823
4824 // Calculate the scale factor using exact REF point math
4825 double dx2 = (pRefTable[jmax].xr - pRefTable[imax].xr) *
4826 (pRefTable[jmax].xr - pRefTable[imax].xr);
4827 double dy2 = (pRefTable[jmax].yr - pRefTable[imax].yr) *
4828 (pRefTable[jmax].yr - pRefTable[imax].yr);
4829 double dn2 = (northing1 - northing0) * (northing1 - northing0);
4830 double de2 = (easting1 - easting0) * (easting1 - easting0);
4831
4832 m_ppm_avg = sqrt(dx2 + dy2) / sqrt(dn2 + de2);
4833
4834 // Sanity check
4835 // double ref_dist = DistGreatCircle(pRefTable[imax].latr,
4836 // pRefTable[imax].lonr, pRefTable[jmax].latr,
4837 // pRefTable[jmax].lonr); ref_dist *= 1852; //To Meters double
4838 // ref_dist_transform = sqrt(dn2 + de2); //Also meters
4839 // double error = (ref_dist - ref_dist_transform)/ref_dist;
4840
4841 // Set up and solve polynomial solution for pix<->cartesian east/north as
4842 // projected
4843 // Fill the cpoints structure with pixel points and transformed lat/lon
4844
4845 for (int n = 0; n < nRefpoint; n++) {
4846 double easting, northing;
4847 toPOLY(pRefTable[n].latr, pRefTable[n].lonr, m_proj_lat, m_proj_lon,
4848 &easting, &northing);
4849
4850 // Round trip check for debugging....
4851 // double lat, lon;
4852 // fromPOLY(easting, northing, m_proj_lat, m_proj_lon,
4853 // &lat, &lon);
4854
4855 cPoints.tx[n] = pRefTable[n].xr;
4856 cPoints.ty[n] = pRefTable[n].yr;
4857 cPoints.lon[n] = easting;
4858 cPoints.lat[n] = northing;
4859 // printf(" x: %g y: %g east: %g north:
4860 // %g\n",pRefTable[n].xr, pRefTable[n].yr, easting,
4861 // northing);
4862 }
4863
4864 // Helper parameters
4865 cPoints.txmax = plonmax;
4866 cPoints.txmin = plonmin;
4867 cPoints.tymax = platmax;
4868 cPoints.tymin = platmin;
4869 toPOLY(latmax, lonmax, m_proj_lat, m_proj_lon, &cPoints.lonmax,
4870 &cPoints.latmax);
4871 toPOLY(latmin, lonmin, m_proj_lat, m_proj_lon, &cPoints.lonmin,
4872 &cPoints.latmin);
4873
4874 Georef_Calculate_Coefficients_Proj(&cPoints);
4875
4876 // for(int h=0 ; h < 10 ; h++)
4877 // printf("pix to east %d %g\n", h, cPoints.pwx[h]); //
4878 // pix to lon
4879 // for(int h=0 ; h < 10 ; h++)
4880 // printf("east to pix %d %g\n", h, cPoints.wpx[h]); //
4881 // lon to pix
4882
4883 }
4884
4885 // Any other projection had better have embedded coefficients
4886 else if (bHaveEmbeddedGeoref) {
4887 // Use a Mercator Projection to get a rough ppm.
4888 double easting0, easting1, northing0, northing1;
4889 // Get the Merc projection of the two REF points
4890 toSM_ECC(pRefTable[imax].latr, pRefTable[imax].lonr, m_proj_lat, m_proj_lon,
4891 &easting0, &northing0);
4892 toSM_ECC(pRefTable[jmax].latr, pRefTable[jmax].lonr, m_proj_lat, m_proj_lon,
4893 &easting1, &northing1);
4894
4895 // Calculate the scale factor using exact REF point math in x(longitude)
4896 // direction
4897
4898 double dx = (pRefTable[jmax].xr - pRefTable[imax].xr);
4899 double de = (easting1 - easting0);
4900
4901 m_ppm_avg = fabs(dx / de);
4902
4903 m_ExtraInfo = "---<<< Warning: Chart georef accuracy may be poor. >>>---";
4904 }
4905
4906 else
4907 m_ppm_avg = 1.0; // absolute fallback to prevent div-0 errors
4908
4909#if 0
4910 // Alternate Skew verification
4911 ViewPort vps;
4912 vps.clat = pRefTable[0].latr;
4913 vps.clon = pRefTable[0].lonr;
4914 vps.view_scale_ppm = m_ppm_avg;
4915 vps.skew = 0.;
4916 vps.pix_width = 1000;
4917 vps.pix_height = 1000;
4918
4919 int x1, y1, x2, y2;
4920 latlong_to_pix_vp(latmin, (lonmax + lonmin)/2., x1, y1, vps);
4921 latlong_to_pix_vp(latmax, (lonmax + lonmin)/2., x2, y2, vps);
4922
4923 double apparent_skew = (atan2( (y2-y1), (x2-x1) ) * 180./PI) + 90.;
4924 if(apparent_skew < 0.)
4925 apparent_skew += 360;
4926 if(apparent_skew > 360.)
4927 apparent_skew -= 360;
4928
4929 if(fabs( apparent_skew - m_Chart_Skew ) > 2) { // measured skew is more than 2 degress different
4930 m_Chart_Skew = apparent_skew;
4931 }
4932#endif
4933
4934 if (!b_testSolution) return (0);
4935
4936 // Do a last little test using a synthetic ViewPort of nominal size.....
4937 ViewPort vp;
4938 vp.clat = pRefTable[0].latr;
4939 vp.clon = pRefTable[0].lonr;
4940 vp.view_scale_ppm = m_ppm_avg;
4941 vp.skew = 0.;
4942 vp.pix_width = 1000;
4943 vp.pix_height = 1000;
4944 // vp.rv_rect = wxRect(0,0, vp.pix_width, vp.pix_height);
4945 SetVPRasterParms(vp);
4946
4947 double xpl_err_max = 0;
4948 double ypl_err_max = 0;
4949 int px, py;
4950
4951 int pxref, pyref;
4952 pxref = (int)pRefTable[0].xr;
4953 pyref = (int)pRefTable[0].yr;
4954
4955 for (i = 0; i < nRefpoint; i++) {
4956 px = (int)(vp.pix_width / 2 + pRefTable[i].xr) - pxref;
4957 py = (int)(vp.pix_height / 2 + pRefTable[i].yr) - pyref;
4958
4959 vp_pix_to_latlong(vp, px, py, &elt, &elg);
4960
4961 double lat_error = elt - pRefTable[i].latr;
4962 pRefTable[i].ypl_error = lat_error;
4963
4964 double lon_error = elg - pRefTable[i].lonr;
4965
4966 // Longitude errors could be compounded by prior adjustment to ref points
4967 if (fabs(lon_error) > 180.) {
4968 if (lon_error > 0.)
4969 lon_error -= 360.;
4970 else if (lon_error < 0.)
4971 lon_error += 360.;
4972 }
4973 pRefTable[i].xpl_error = lon_error;
4974
4975 if (fabs(pRefTable[i].ypl_error) > fabs(ypl_err_max))
4976 ypl_err_max = pRefTable[i].ypl_error;
4977 if (fabs(pRefTable[i].xpl_error) > fabs(xpl_err_max))
4978 xpl_err_max = pRefTable[i].xpl_error;
4979 }
4980
4981 Chart_Error_Factor = fmax(fabs(xpl_err_max / (lonmax - lonmin)),
4982 fabs(ypl_err_max / (latmax - latmin)));
4983 double chart_error_meters =
4984 fmax(fabs(xpl_err_max * 60. * 1852.), fabs(ypl_err_max * 60. * 1852.));
4985 // calculate a nominal pixel error
4986 // Assume a modern display has about 4000 pixels/meter.
4987 // Assume the chart is to be displayed at nominal printed scale
4988 double chart_error_pixels = chart_error_meters * 4000. / m_Chart_Scale;
4989
4990 // Good enough for navigation?
4991 int max_pixel_error = 4;
4992
4993 if (chart_error_pixels > max_pixel_error) {
4994 wxString msg =
4995 " VP Final Check: Georeference Chart_Error_Factor on chart ";
4996 msg.Append(m_FullPath);
4997 wxString msg1;
4998 msg1.Printf(" is %5g \n nominal pixel error is: %5g",
4999 Chart_Error_Factor, chart_error_pixels);
5000 msg.Append(msg1);
5001
5002 wxLogMessage(msg);
5003
5004 m_ExtraInfo = "---<<< Warning: Chart georef accuracy is poor. >>>---";
5005 }
5006
5007 // Try again with my calculated georef
5008 // This problem was found on NOAA 514_1.KAP. The embedded coefficients are
5009 // just wrong....
5010 if ((chart_error_pixels > max_pixel_error) && bHaveEmbeddedGeoref) {
5011 wxString msg =
5012 " Trying again with internally calculated georef solution ";
5013 wxLogMessage(msg);
5014
5015 bHaveEmbeddedGeoref = false;
5016 SetVPRasterParms(vp);
5017
5018 xpl_err_max = 0;
5019 ypl_err_max = 0;
5020
5021 pxref = (int)pRefTable[0].xr;
5022 pyref = (int)pRefTable[0].yr;
5023
5024 for (i = 0; i < nRefpoint; i++) {
5025 px = (int)(vp.pix_width / 2 + pRefTable[i].xr) - pxref;
5026 py = (int)(vp.pix_height / 2 + pRefTable[i].yr) - pyref;
5027
5028 vp_pix_to_latlong(vp, px, py, &elt, &elg);
5029
5030 double lat_error = elt - pRefTable[i].latr;
5031 pRefTable[i].ypl_error = lat_error;
5032
5033 double lon_error = elg - pRefTable[i].lonr;
5034
5035 // Longitude errors could be compounded by prior adjustment to ref points
5036 if (fabs(lon_error) > 180.) {
5037 if (lon_error > 0.)
5038 lon_error -= 360.;
5039 else if (lon_error < 0.)
5040 lon_error += 360.;
5041 }
5042 pRefTable[i].xpl_error = lon_error;
5043
5044 if (fabs(pRefTable[i].ypl_error) > fabs(ypl_err_max))
5045 ypl_err_max = pRefTable[i].ypl_error;
5046 if (fabs(pRefTable[i].xpl_error) > fabs(xpl_err_max))
5047 xpl_err_max = pRefTable[i].xpl_error;
5048 }
5049
5050 Chart_Error_Factor = fmax(fabs(xpl_err_max / (lonmax - lonmin)),
5051 fabs(ypl_err_max / (latmax - latmin)));
5052
5053 chart_error_meters =
5054 fmax(fabs(xpl_err_max * 60. * 1852.), fabs(ypl_err_max * 60. * 1852.));
5055 chart_error_pixels = chart_error_meters * 4000. / m_Chart_Scale;
5056
5057 // Good enough for navigation?
5058 if (chart_error_pixels > max_pixel_error) {
5059 wxString msg =
5060 " VP Final Check with internal georef: Georeference "
5061 "Chart_Error_Factor on chart ";
5062 msg.Append(m_FullPath);
5063 wxString msg1;
5064 msg1.Printf(" is %5g\n nominal pixel error is: %5g",
5065 Chart_Error_Factor, chart_error_pixels);
5066 msg.Append(msg1);
5067
5068 wxLogMessage(msg);
5069
5070 m_ExtraInfo = "---<<< Warning: Chart georef accuracy is poor. >>>---";
5071 } else {
5072 wxString msg = " Result: OK, Internal georef solution used.";
5073
5074 wxLogMessage(msg);
5075
5076 m_ExtraInfo = "";
5077 }
5078 }
5079
5080 return (0);
5081}
5082
5083double ChartBaseBSB::AdjustLongitude(double lon) {
5084 double lond = (m_LonMin + m_LonMax) / 2 - lon;
5085 if (lond > 180)
5086 return lon + 360;
5087 else if (lond < -180)
5088 return lon - 360;
5089 return lon;
5090}
5091
5092/*
5093 * Extracted from bsb_io.c - implementation of libbsb reading and writing
5094 *
5095 * Copyright (C) 2000 Stuart Cunningham <stuart_hc@users.sourceforge.net>
5096 *
5097 * This library is free software; you can redistribute it and/or
5098 * modify it under the terms of the GNU Lesser General Public
5099 * License as published by the Free Software Foundation; either
5100 * version 2.1 of the License, or (at your option) any later version.
5101 *
5102 * This library is distributed in the hope that it will be useful,
5103 * but WITHOUT ANY WARRANTY; without even the implied warranty of
5104 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5105 * Lesser General Public License for more details.
5106 *
5107 * You should have received a copy of the GNU Lesser General Public
5108 * License along with this library; if not, write to the Free Software
5109 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
5110 *
5111 *
5112 */
5113
5123static double polytrans(double *coeff, double lon, double lat) {
5124 double ret = coeff[0] + coeff[1] * lon + coeff[2] * lat;
5125 ret += coeff[3] * lon * lon;
5126 ret += coeff[4] * lon * lat;
5127 ret += coeff[5] * lat * lat;
5128 ret += coeff[6] * lon * lon * lon;
5129 ret += coeff[7] * lon * lon * lat;
5130 ret += coeff[8] * lon * lat * lat;
5131 ret += coeff[9] * lat * lat * lat;
5132 // ret += coeff[10]*lat*lat*lat*lat;
5133 // ret += coeff[11]*lat*lat*lat*lat*lat;
5134
5135 // for(int n = 0 ; n < 10 ; n++)
5136 // printf(" %g\n", coeff[n]);
5137
5138 return ret;
5139}
5140
5141#if 0
5153extern int bsb_LLtoXY(BSBImage *p, double lon, double lat, int* x, int* y)
5154{
5155 /* change longitude phase (CPH) */
5156 lon = (lon < 0) ? lon + p->cph : lon - p->cph;
5157 double xd = polytrans( p->wpx, lon, lat );
5158 double yd = polytrans( p->wpy, lon, lat );
5159 *x = (int)(xd + 0.5);
5160 *y = (int)(yd + 0.5);
5161 return 1;
5162}
5163
5175extern int bsb_XYtoLL(BSBImage *p, int x, int y, double* lonout, double* latout)
5176{
5177 double lon = polytrans( p->pwx, x, y );
5178 lon = (lon < 0) ? lon + p->cph : lon - p->cph;
5179 *lonout = lon;
5180 *latout = polytrans( p->pwy, x, y );
5181 return 1;
5182}
5183
5184#endif
XZ compressed charts support.
BSB chart management.
virtual double GetNearestPreferredScalePPM(double target_scale_ppm)
Find the nearest preferred viewport scale (in pixels/meter) for this chart.
double GetClosestValidNaturalScalePPM(double target_scale, double scale_factor_min, double scale_factor_max)
Find closest valid scale that's a power of 2 multiple of chart's native scale.
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
int pix_width
Width of the viewport in physical pixels.
Definition viewport.h:231
double skew
Angular distortion (shear transform) applied to the viewport in radians.
Definition viewport.h:212
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
MyConfig * pConfig
Global instance.
Definition navutil.cpp:118
Utility functions.
Optimized wxBitmap Object.
double lonmin
Minimum longitude in reference data.
Definition georef.h:83
int count
Number of reference points used.
Definition georef.h:66
double lonmax
Maximum longitude in reference data.
Definition georef.h:82
int txmin
Minimum x value in target space.
Definition georef.h:80
double * pwx
Polynomial coefficients for pixel-to-world longitude transformation.
Definition georef.h:72
double * tx
Array of x-coordinates in target (typically pixel) space.
Definition georef.h:68
double * wpx
Polynomial coefficients for world-to-pixel x transformation.
Definition georef.h:76
double * lat
Array of latitudes corresponding to reference points.
Definition georef.h:71
double * lon
Array of longitudes corresponding to reference points.
Definition georef.h:70
int status
Status of the georeferencing (0 = valid, other values indicate errors)
Definition georef.h:64
int tymin
Minimum y value in target space.
Definition georef.h:81
double * ty
Array of y-coordinates in target (typically pixel) space.
Definition georef.h:69
int tymax
Maximum y value in target space.
Definition georef.h:79
double latmin
Minimum latitude in reference data.
Definition georef.h:85
int txmax
Maximum x value in target space.
Definition georef.h:78
double latmax
Maximum latitude in reference data.
Definition georef.h:84
double * wpy
Polynomial coefficients for world-to-pixel y transformation.
Definition georef.h:77
double * pwy
Polynomial coefficients for pixel-to-world latitude transformation.
Definition georef.h:74