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