33#include <wx/stopwatch.h>
35#include <wx/tokenzr.h>
45#include "CanvasConfig.h"
47#include "ocpn_frame.h"
48#ifdef __OCPN__ANDROID__
49#include "androidUTIL.h"
53#include "glChartCanvas.h"
59#include <wx/progdlg.h>
66extern ColorScheme GetColorScheme();
71extern int g_nCacheLimit;
72extern int g_memCacheLimit;
73extern s52plib *ps52plib;
75extern unsigned int g_canvasConfig;
76extern std::vector<std::string> ChartDirectoryExcludedVector;
78bool G_FloatPtInPolygon(
MyFlPoint *rgpts,
int wnumpts,
float x,
float y);
79bool GetMemoryStatus(
int *mem_total,
int *mem_used);
85int ChartStack::GetCurrentEntrydbIndex(
void) {
86 if (nEntry && (CurrentStackEntry >= 0) )
87 return DBIndex[CurrentStackEntry];
92void ChartStack::SetCurrentEntryFromdbIndex(
int current_db_index) {
93 for (
int i = 0; i < nEntry; i++) {
94 if (current_db_index == DBIndex[i]) CurrentStackEntry = i;
98int ChartStack::GetDBIndex(
int stack_index) {
99 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
100 return DBIndex[stack_index];
105void ChartStack::SetDBIndex(
int stack_index,
int db_index) {
106 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
107 DBIndex[stack_index] = db_index;
110bool ChartStack::DoesStackContaindbIndex(
int db_index) {
111 for (
int i = 0; i < nEntry; i++) {
112 if (db_index == DBIndex[i])
return true;
118void ChartStack::AddChart(
int db_add) {
119 if (!ChartData)
return;
121 if (!ChartData->IsValid())
return;
123 int db_index = db_add;
130 SetDBIndex(j - 1, db_index);
140 for (
int id = 0;
id < j - 1;
id++) {
141 if (GetDBIndex(
id) != -1) {
144 for (
int jd =
id + 1; jd < j; jd++) {
145 if (GetDBIndex(jd) != -1) {
147 if (pm->GetFileTime() && pn->GetFileTime()) {
148 if (labs(pm->GetFileTime() - pn->GetFileTime()) <
150 if (pn->GetpFileName()->IsSameAs(*(pm->GetpFileName())))
161 if (GetDBIndex(
id) == -1) {
164 int db_index = GetDBIndex(jd);
165 SetDBIndex(jd - 1, db_index);
182 for (
int i = 0; i < j - 1; i++) {
183 const ChartTableEntry &m = ChartData->GetChartTableEntry(GetDBIndex(i));
185 ChartData->GetChartTableEntry(GetDBIndex(i + 1));
187 if (n.GetScale() < m.GetScale()) {
189 SetDBIndex(i, GetDBIndex(i + 1));
190 SetDBIndex(i + 1, ti);
202 pChartCache =
new wxArrayPtrVoid;
211 if (g_memCacheLimit) {
213 msg.Printf(_T(
"ChartDB Cache policy: Application target is %d MBytes"),
214 g_memCacheLimit / 1024);
218 msg.Printf(_T(
"ChartDB Cache policy: Max open chart limit is %d."),
223 m_checkGroupIndex[0] = m_checkGroupIndex[1] = -1;
224 m_checkedTileOnly[0] = m_checkedTileOnly[1] =
false;
235 ArrayOfCDI &dir_array_check) {
236 m_dir_array = dir_array_check;
237 return ChartDatabase::Read(filename);
242void ChartDB::DeleteCacheEntry(
CacheEntry *pce,
bool bDelTexture,
243 const wxString &msg) {
246 if (msg != wxEmptyString) {
247 wxLogMessage(_T(
"%s%s"), msg.c_str(), ch->GetFullPath().c_str());
252 if (pthumbwin->pThumbChart == ch) pthumbwin->pThumbChart = NULL;
257 if (g_glTextureManager)
258 g_glTextureManager->PurgeChartTextures(ch, bDelTexture);
261 pChartCache->Remove(pce);
266void ChartDB::DeleteCacheEntry(
int i,
bool bDelTexture,
const wxString &msg) {
268 if (pce) DeleteCacheEntry(pce, bDelTexture, msg);
271void ChartDB::PurgeCache() {
275 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
276 unsigned int nCache = pChartCache->GetCount();
277 for (
unsigned int i = 0; i < nCache; i++) {
278 DeleteCacheEntry(0,
true);
280 pChartCache->Clear();
282 m_cache_mutex.Unlock();
286void ChartDB::PurgeCachePlugins() {
288 wxLogMessage(_T(
"Chart cache PlugIn purge"));
290 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
291 unsigned int nCache = pChartCache->GetCount();
297 if (CHART_TYPE_PLUGIN == Ch->GetChartType()) {
298 DeleteCacheEntry(pce,
true);
300 nCache = pChartCache->GetCount();
307 m_cache_mutex.Unlock();
311void ChartDB::ClearCacheInUseFlags(
void) {
312 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
313 unsigned int nCache = pChartCache->GetCount();
314 for (
unsigned int i = 0; i < nCache; i++) {
316 pce->b_in_use =
false;
318 m_cache_mutex.Unlock();
325void ChartDB::PurgeCacheUnusedCharts(
double factor) {
327 if (g_memCacheLimit) {
328 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
331 GetMemoryStatus(0, &mem_used);
332 int mem_limit = g_memCacheLimit * factor;
334 int nl = pChartCache->GetCount();
336 wxString msg(_T(
"Purging unused chart from cache: "));
338 while ((mem_used > mem_limit) && (nl > 0)) {
339 if (pChartCache->GetCount() < 2) {
344 CacheEntry *pce = FindOldestDeleteCandidate(
false);
347 DeleteCacheEntry(pce,
false , msg);
353 GetMemoryStatus(0, &mem_used);
358 m_cache_mutex.Unlock();
362 else if (g_nCacheLimit) {
363 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
365 double fac10 = factor * 10;
366 int chart_limit = g_nCacheLimit * fac10 / 10;
368 int nl = pChartCache->GetCount();
370 wxString msg(_T(
"Purging unused chart from cache: "));
371 while ((nl > chart_limit) && (nl > 0)) {
372 if (pChartCache->GetCount() < 2) {
377 CacheEntry *pce = FindOldestDeleteCandidate(
false);
380 DeleteCacheEntry(pce,
false , msg);
385 nl = pChartCache->GetCount();
388 m_cache_mutex.Unlock();
397ChartBase *ChartDB::GetChart(
const wxChar *theFilePath,
399 wxFileName fn(theFilePath);
401 if (!fn.FileExists()) {
403 if (!wxDir::Exists(theFilePath)) {
404 wxLogMessage(wxT(
" ...file does not exist: %s"), theFilePath);
410 wxString chartExt = fn.GetExt().Upper();
412 if (chartExt == wxT(
"XZ")) {
413 wxString npath = theFilePath;
414 npath = npath.Left(npath.length() - 3);
415 wxFileName fn(npath);
416 chartExt = fn.GetExt().Upper();
419 if (chartExt == wxT(
"KAP")) {
421 }
else if (chartExt == wxT(
"GEO")) {
423 }
else if (chartExt == wxT(
"MBTILES")) {
425 }
else if (chartExt == wxT(
"000") || chartExt == wxT(
"S57")) {
428 }
else if (chart_desc.m_descriptor_type == PLUGIN_DESCRIPTOR) {
435 wxRegEx rxName(wxT(
"[0-9]+"));
436 wxRegEx rxExt(wxT(
"[A-G]"));
437 if (rxName.Matches(fn.GetName()) && rxExt.Matches(chartExt))
448bool ChartDB::IsChartDirectoryExcluded(
const std::string &chart_file) {
449 for (
auto excluded_dir : ChartDirectoryExcludedVector) {
458int ChartDB::BuildChartStack(
ChartStack *cstk,
float lat,
float lon,
int db_add,
460 BuildChartStack(cstk, lat, lon, groupIndex);
462 if (db_add >= 0) cstk->AddChart(db_add);
467int ChartDB::BuildChartStack(
ChartStack *cstk,
float lat,
float lon,
472 if (!IsValid())
return 0;
476 int nEntry = GetChartTableEntries();
478 for (
int db_index = 0; db_index < nEntry; db_index++) {
482 if (IsChartDirectoryExcluded(cte.GetFullPath()))
continue;
485 bool b_group_add =
false;
486 if (groupIndex > 0) {
487 const int ng = cte.GetGroupArray().size();
488 for (
int ig = 0; ig < ng; ig++) {
489 if (groupIndex == cte.GetGroupArray()[ig]) {
497 bool b_writable_add =
true;
500#ifdef __OCPN__ANDROID__
501 wxFileName fn(cte.GetFullSystemPath());
502 if (!androidIsDirWritable(fn.GetPath())) b_writable_add =
false;
505 bool b_pos_add =
false;
506 if (b_group_add && b_writable_add) {
510 if (cte.GetChartType() == CHART_TYPE_PLUGIN) {
515 if (CheckPositionWithinChart(db_index, lat, lon) && (j < MAXSTACK))
519 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() < 180.)) {
520 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
525 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() > 180.)) {
526 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
532 bool b_available =
true;
534 if (b_group_add && b_pos_add && (cte.GetChartType() == CHART_TYPE_PLUGIN)) {
536 if (!IsChartAvailable(db_index)) {
537 pcte->SetAvailable(
false);
540 pcte->SetAvailable(
true);
545 if (b_group_add && b_pos_add && b_available) {
548 cstk->SetDBIndex(j - 1, db_index);
564 for (
int id = 0;
id < j - 1;
id++) {
565 if (cstk->GetDBIndex(
id) != -1) {
566 const ChartTableEntry &ctem = GetChartTableEntry(cstk->GetDBIndex(
id));
568 for (
int jd =
id + 1; jd < j; jd++) {
569 if (cstk->GetDBIndex(jd) != -1) {
571 GetChartTableEntry(cstk->GetDBIndex(jd));
572 bool bsameTime =
false;
573 if (ctem.GetFileTime() && cten.GetFileTime()) {
574 if (labs(ctem.GetFileTime() - cten.GetFileTime()) < 60)
577 if (ctem.GetChartEditionDate() == cten.GetChartEditionDate())
581 if (cten.GetpFileName()->IsSameAs(*(ctem.GetpFileName())))
582 cstk->SetDBIndex(jd, -1);
591 if (cstk->GetDBIndex(
id) == -1) {
594 int db_index = cstk->GetDBIndex(jd);
595 cstk->SetDBIndex(jd - 1, db_index);
612 for (i = 0; i < j - 1; i++) {
614 const ChartTableEntry &n = GetChartTableEntry(cstk->GetDBIndex(i + 1));
616 if (n.GetScale() < m.GetScale()) {
617 ti = cstk->GetDBIndex(i);
618 cstk->SetDBIndex(i, cstk->GetDBIndex(i + 1));
619 cstk->SetDBIndex(i + 1, ti);
625 cstk->b_valid =
true;
630bool ChartDB::IsChartInGroup(
const int db_index,
const int group) {
634 bool b_in_group =
false;
636 for (
unsigned int ig = 0; ig < pt->GetGroupArray().size(); ig++) {
637 if (group == pt->GetGroupArray()[ig]) {
648bool ChartDB::IsENCInGroup(
const int groupIndex) {
652 for (
int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
656 bool b_group_add =
false;
657 if (groupIndex > 0) {
658 const int ng = cte.GetGroupArray().size();
659 for (
int ig = 0; ig < ng; ig++) {
660 if (groupIndex == cte.GetGroupArray()[ig]) {
669 if (cte.GetChartFamily() == CHART_FAMILY_VECTOR) {
679bool ChartDB::IsNonMBTileInGroup(
const int groupIndex) {
684 for (
int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
688 bool b_group_add =
false;
689 if (groupIndex > 0) {
690 const int ng = cte.GetGroupArray().size();
691 for (
int ig = 0; ig < ng; ig++) {
692 if (groupIndex == cte.GetGroupArray()[ig]) {
701 if (cte.GetChartType() != CHART_TYPE_MBTILES) {
714bool ChartDB::CheckPositionWithinChart(
int index,
float lat,
float lon) {
719 if ((lat <= pt->GetLatMax()) && (lat >= pt->GetLatMin()) &&
720 (lon >= pt->GetLonMin()) && (lon <= pt->GetLonMax())) {
723 bool bInside = G_FloatPtInPolygon((
MyFlPoint *)pt->GetpPlyTable(),
724 pt->GetnPlyEntries(), lon, lat);
727 if (pt->GetnAuxPlyEntries()) {
728 for (
int k = 0; k < pt->GetnAuxPlyEntries(); k++) {
730 G_FloatPtInPolygon((
MyFlPoint *)pt->GetpAuxPlyTableEntry(k),
731 pt->GetAuxCntTableEntry(k), lon, lat);
732 if (bAuxInside)
return true;
748 if ((pa == 0) || (pb == 0))
return false;
749 if ((!pa->b_valid) || (!pb->b_valid))
return false;
750 if (pa->nEntry != pb->nEntry)
return false;
752 for (
int i = 0; i < pa->nEntry; i++) {
753 if (pa->GetDBIndex(i) != pb->GetDBIndex(i))
return false;
763 if ((pa == 0) || (pb == 0))
return false;
764 pa->nEntry = pb->nEntry;
766 for (
int i = 0; i < pa->nEntry; i++) pa->SetDBIndex(i, pb->GetDBIndex(i));
768 pa->CurrentStackEntry = pb->CurrentStackEntry;
770 pa->b_valid = pb->b_valid;
775wxString ChartDB::GetFullPath(
ChartStack *ps,
int stackindex) {
776 int dbIndex = ps->GetDBIndex(stackindex);
777 return GetChartTableEntry(dbIndex).GetFullSystemPath();
784int ChartDB::GetCSPlyPoint(
ChartStack *ps,
int stackindex,
int plyindex,
785 float *lat,
float *lon) {
786 int dbIndex = ps->GetDBIndex(stackindex);
787 wxASSERT(dbIndex >= 0);
790 if (entry.GetnPlyEntries()) {
791 float *fp = entry.GetpPlyTable();
798 return entry.GetnPlyEntries();
804int ChartDB::GetStackChartScale(
ChartStack *ps,
int stackindex,
char *buf,
806 int dbindex = ps->GetDBIndex(stackindex);
807 wxASSERT(dbindex >= 0);
810 int sc = entry.GetScale();
811 if (buf) sprintf(buf,
"%d", sc);
819int ChartDB::GetStackEntry(
ChartStack *ps, wxString fp) {
820 for (
int i = 0; i < ps->nEntry; i++) {
822 if (fp.IsSameAs(entry.GetFullSystemPath()))
return i;
831ChartTypeEnum ChartDB::GetCSChartType(
ChartStack *ps,
int stackindex) {
833 int dbindex = ps->GetDBIndex(stackindex);
835 return (ChartTypeEnum)GetChartTableEntry(dbindex).GetChartType();
837 return CHART_TYPE_UNKNOWN;
840ChartFamilyEnum ChartDB::GetCSChartFamily(
ChartStack *ps,
int stackindex) {
842 int dbindex = ps->GetDBIndex(stackindex);
846 ChartTypeEnum type = (ChartTypeEnum)entry.GetChartType();
849 return CHART_FAMILY_RASTER;
851 return CHART_FAMILY_RASTER;
853 return CHART_FAMILY_VECTOR;
854 case CHART_TYPE_CM93:
855 return CHART_FAMILY_VECTOR;
856 case CHART_TYPE_CM93COMP:
857 return CHART_FAMILY_VECTOR;
858 case CHART_TYPE_DUMMY:
859 return CHART_FAMILY_RASTER;
861 return CHART_FAMILY_UNKNOWN;
865 return CHART_FAMILY_UNKNOWN;
868std::vector<int> ChartDB::GetCSArray(
ChartStack *ps) {
869 std::vector<int> ret;
872 ret.reserve(ps->nEntry);
873 for (
int i = 0; i < ps->nEntry; i++) {
874 ret.push_back(ps->GetDBIndex(i));
881bool ChartDB::IsChartInCache(
int dbindex) {
882 bool bInCache =
false;
885 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
886 unsigned int nCache = pChartCache->GetCount();
887 for (
unsigned int i = 0; i < nCache; i++) {
889 if (pce->dbIndex == dbindex) {
890 if (pce->pChart != 0 && ((
ChartBase *)pce->pChart)->IsReadyToRender())
895 m_cache_mutex.Unlock();
901bool ChartDB::IsChartInCache(wxString path) {
902 bool bInCache =
false;
903 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
905 unsigned int nCache = pChartCache->GetCount();
906 for (
unsigned int i = 0; i < nCache; i++) {
908 if (pce->FullPath == path) {
909 if (pce->pChart != 0 && ((
ChartBase *)pce->pChart)->IsReadyToRender())
915 m_cache_mutex.Unlock();
920bool ChartDB::IsChartLocked(
int index) {
921 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
922 unsigned int nCache = pChartCache->GetCount();
923 for (
unsigned int i = 0; i < nCache; i++) {
925 if (pce->dbIndex == index) {
926 bool ret = pce->n_lock > 0;
927 m_cache_mutex.Unlock();
931 m_cache_mutex.Unlock();
937bool ChartDB::LockCacheChart(
int index) {
940 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
941 unsigned int nCache = pChartCache->GetCount();
942 for (
unsigned int i = 0; i < nCache; i++) {
944 if (pce->dbIndex == index) {
950 m_cache_mutex.Unlock();
955void ChartDB::UnLockCacheChart(
int index) {
957 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
958 unsigned int nCache = pChartCache->GetCount();
959 for (
unsigned int i = 0; i < nCache; i++) {
961 if (pce->dbIndex == index) {
962 if (pce->n_lock > 0) pce->n_lock--;
966 m_cache_mutex.Unlock();
970void ChartDB::UnLockAllCacheCharts() {
972 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
973 unsigned int nCache = pChartCache->GetCount();
974 for (
unsigned int i = 0; i < nCache; i++) {
976 if (pce->n_lock > 0) pce->n_lock--;
978 m_cache_mutex.Unlock();
985ChartBase *ChartDB::OpenChartFromDB(
int index, ChartInitFlag init_flag) {
986 return OpenChartUsingCache(index, init_flag);
989ChartBase *ChartDB::OpenChartFromDB(wxString chart_path,
990 ChartInitFlag init_flag) {
991 int dbii = FinddbIndex(chart_path);
992 return OpenChartUsingCache(dbii, init_flag);
996 ChartInitFlag init_flag) {
997 return OpenChartUsingCache(pStack->GetDBIndex(StackEntry), init_flag);
1000ChartBase *ChartDB::OpenChartFromDBAndLock(
int index, ChartInitFlag init_flag,
1002 wxCriticalSectionLocker locker(m_critSect);
1003 ChartBase *pret = OpenChartUsingCache(index, init_flag);
1004 if (lock && pret) LockCacheChart(index);
1008ChartBase *ChartDB::OpenChartFromDBAndLock(wxString chart_path,
1009 ChartInitFlag init_flag) {
1010 int dbii = FinddbIndex(chart_path);
1011 return OpenChartFromDBAndLock(dbii, init_flag);
1014CacheEntry *ChartDB::FindOldestDeleteCandidate(
bool blog) {
1017 unsigned int nCache = pChartCache->GetCount();
1019 if (blog) wxLogMessage(_T(
"Searching chart cache for oldest entry"));
1020 int LRUTime = m_ticks;
1022 for (
unsigned int i = 0; i < nCache; i++) {
1024 if (pce->RecentTime < LRUTime && !pce->n_lock) {
1025 if (!isSingleChart((
ChartBase *)(pce->pChart))) {
1026 LRUTime = pce->RecentTime;
1031 int dt = m_ticks - LRUTime;
1036 if (!pce->n_lock && !isSingleChart(pDeleteCandidate)) {
1038 wxLogMessage(_T(
"Oldest unlocked cache index is %d, delta t is %d"),
1043 wxLogMessage(_T(
"All chart in cache locked, size: %d"), nCache);
1049ChartBase *ChartDB::OpenChartUsingCache(
int dbindex, ChartInitFlag init_flag) {
1050 if ((dbindex < 0) || (dbindex > GetChartTableEntries() - 1))
return NULL;
1055 wxString ChartFullPath = cte.GetFullSystemPath();
1056 ChartTypeEnum chart_type = (ChartTypeEnum)cte.GetChartType();
1057 ChartFamilyEnum chart_family = (ChartFamilyEnum)cte.GetChartFamily();
1060 msg1.Printf(_T(
"OpenChartUsingCache: type %d "), chart_type);
1063 if (cte.GetLatMax() > 90.0)
1070 bool bInCache =
false;
1074 wxMutexLocker lock(m_cache_mutex);
1076 unsigned int nCache = pChartCache->GetCount();
1078 for (
unsigned int i = 0; i < nCache; i++) {
1080 if (pce->FullPath == ChartFullPath) {
1089 msg.Printf(_T(
"OpenChartUsingCache, IN cache: cache size: %d\n"),
1090 (
int)pChartCache->GetCount());
1092 if (FULL_INIT == init_flag)
1094 if (Ch->IsReadyToRender()) {
1096 pce->RecentTime = m_ticks;
1097 pce->b_in_use =
true;
1101 if (pthumbwin && pthumbwin->pThumbChart == Ch)
1102 pthumbwin->pThumbChart = NULL;
1104 old_lock = pce->n_lock;
1105 pChartCache->Remove(pce);
1113 pce->RecentTime = m_ticks;
1114 pce->b_in_use =
true;
1125 if (g_memCacheLimit) {
1128 GetMemoryStatus(0, &mem_used);
1132 _T(
"OpenChartUsingCache, NOT in cache: cache size: %d\n"),
1133 (
int)pChartCache->GetCount());
1136 msg1.Printf(_T(
" OpenChartUsingCache: type %d "), chart_type);
1137 wxLogMessage(msg1 + ChartFullPath);
1139 if ((mem_used > g_memCacheLimit * 8 / 10) &&
1140 (pChartCache->GetCount() > 2)) {
1141 wxString msg(_T(
"Removing oldest chart from cache: "));
1143 CacheEntry *pce = FindOldestDeleteCandidate(
true);
1144 if (pce == 0)
break;
1147 DeleteCacheEntry(pce,
true, msg);
1149 GetMemoryStatus(0, &mem_used);
1150 if ((mem_used < g_memCacheLimit * 8 / 10) ||
1151 (pChartCache->GetCount() <= 2))
1162 unsigned int nCache = pChartCache->GetCount();
1163 if (nCache > (
unsigned int)g_nCacheLimit && nCache > 2) {
1164 wxString msg(_T(
"Removing oldest chart from cache: "));
1165 while (nCache > (
unsigned int)g_nCacheLimit) {
1166 CacheEntry *pce = FindOldestDeleteCandidate(
true);
1167 if (pce == 0)
break;
1169 DeleteCacheEntry(pce,
true, msg);
1180 wxLogMessage(_T(
"Creating new chart"));
1182 if (chart_type == CHART_TYPE_KAP)
1185 else if (chart_type == CHART_TYPE_GEO)
1188 else if (chart_type == CHART_TYPE_MBTILES)
1191 else if (chart_type == CHART_TYPE_S57) {
1196 Chs57->SetNativeScale(cte.GetScale());
1201 ext.NLAT = cte.GetLatMax();
1202 ext.SLAT = cte.GetLatMin();
1203 ext.WLON = cte.GetLonMin();
1204 ext.ELON = cte.GetLonMax();
1205 Chs57->SetFullExtent(ext);
1208 else if (chart_type == CHART_TYPE_CM93) {
1213 Chcm93->SetNativeScale(cte.GetScale());
1218 ext.NLAT = cte.GetLatMax();
1219 ext.SLAT = cte.GetLatMin();
1220 ext.WLON = cte.GetLonMin();
1221 ext.ELON = cte.GetLonMax();
1222 Chcm93->SetFullExtent(ext);
1225 else if (chart_type == CHART_TYPE_CM93COMP) {
1231 Chcm93->SetNativeScale(cte.GetScale());
1236 ext.NLAT = cte.GetLatMax();
1237 ext.SLAT = cte.GetLatMin();
1238 ext.WLON = cte.GetLonMin();
1239 ext.ELON = cte.GetLonMax();
1240 Chcm93->SetFullExtent(ext);
1243 else if (chart_type == CHART_TYPE_PLUGIN) {
1244 wxFileName fn(ChartFullPath);
1245 wxString ext = fn.GetExt();
1246 ext.Prepend(_T(
"*."));
1247 wxString ext_upper = ext.MakeUpper();
1248 wxString ext_lower = ext.MakeLower();
1249 wxString chart_class_name;
1254 for (
auto &cd : m_ChartClassDescriptorArray) {
1255 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
1256 if (cd.m_search_mask == ext_upper) {
1257 chart_class_name = cd.m_class_name;
1260 if (cd.m_search_mask == ext_lower) {
1261 chart_class_name = cd.m_class_name;
1264 if (ChartFullPath.Matches(cd.m_search_mask)) {
1265 chart_class_name = cd.m_class_name;
1272 if (chart_class_name.Len()) {
1275 if (chart_family == CHART_FAMILY_VECTOR) LoadS57();
1281 wxLogMessage(_T(
"Unknown chart type"));
1287 s52plib *plib = ps52plib;
1288 wxString msg_fn(ChartFullPath);
1289 msg_fn.Replace(_T(
"%"), _T(
"%%"));
1292 if ((chart_family != CHART_FAMILY_VECTOR) ||
1293 ((chart_family == CHART_FAMILY_VECTOR) && plib)) {
1295 wxString::Format(_T(
"Initializing Chart %s"), msg_fn.c_str()));
1297 ir = Ch->Init(ChartFullPath, init_flag);
1298 Ch->SetColorScheme( GetColorScheme());
1300 wxLogMessage(wxString::Format(
1301 _T(
" No PLIB, Skipping vector chart %s"), msg_fn.c_str()));
1303 ir = INIT_FAIL_REMOVE;
1306 if (INIT_OK == ir) {
1312 pce->FullPath = ChartFullPath;
1314 pce->dbIndex = dbindex;
1317 pce->RecentTime = m_ticks;
1318 pce->n_lock = old_lock;
1320 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1321 pChartCache->Add((
void *)pce);
1322 m_cache_mutex.Unlock();
1337 if (chart_type == CHART_TYPE_MBTILES) {
1338 wxFileName tileFile(ChartFullPath);
1340 wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
1342 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1344 if (!CheckAnyCanvasExclusiveTileGroup() ||
1345 (tileSizeMB.GetLo() > 5000)) {
1348 bool b_clicked =
false;
1352 switch (g_canvasConfig) {
1354 cc = config_array.Item(0);
1358 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1360 cc = config_array.Item(1);
1364 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1368 cc = config_array.Item(0);
1372 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1379 switch (g_canvasConfig) {
1381 cc = config_array.Item(0);
1384 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1386 cc = config_array.Item(1);
1389 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1393 cc = config_array.Item(0);
1396 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1403 }
else if (INIT_FAIL_REMOVE == ir)
1405 wxLogMessage(wxString::Format(_T(
"Problem initializing Chart %s"),
1413 DisableChart(ChartFullPath);
1414 }
else if ((INIT_FAIL_RETRY == ir) ||
1415 (INIT_FAIL_NOERROR ==
1418 wxLogMessage(wxString::Format(
1419 _T(
"Recoverable problem initializing Chart %s"), msg_fn.c_str()));
1424 if (INIT_OK != ir) {
1427 wxString::Format(_T(
" OpenChartFromStack... Error opening ")
1428 _T(
"chart %s ... return code %d"),
1429 msg_fn.c_str(), ir));
1443bool ChartDB::DeleteCacheChart(
ChartBase *pDeleteCandidate) {
1444 bool retval =
false;
1446 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1447 if (!isSingleChart(pDeleteCandidate)) {
1450 for (
unsigned int i = 0; i < pChartCache->GetCount(); i++) {
1452 if ((
ChartBase *)(pce->pChart) == pDeleteCandidate) {
1458 if (pce->n_lock > 0) pce->n_lock--;
1460 if (pce->n_lock == 0) {
1461 DeleteCacheEntry(pce);
1466 m_cache_mutex.Unlock();
1474void ChartDB::ApplyColorSchemeToCachedCharts(ColorScheme cs) {
1479 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1480 unsigned int nCache = pChartCache->GetCount();
1481 for (
unsigned int i = 0; i < nCache; i++) {
1484 if (Ch) Ch->SetColorScheme(cs,
true);
1487 m_cache_mutex.Unlock();
1497ChartBase *ChartDB::OpenStackChartConditional(
1498 ChartStack *ps,
int index_start,
bool bSearchDir, ChartTypeEnum New_Type,
1499 ChartFamilyEnum New_Family_Fallback) {
1505 if (bSearchDir == 1)
1511 index = index_start;
1513 while ((index >= 0) && (index < ps->nEntry)) {
1514 ChartTypeEnum chart_type = (ChartTypeEnum)GetCSChartType(ps, index);
1515 if ((chart_type == New_Type) || (New_Type == CHART_TYPE_DONTCARE)) {
1516 ptc = OpenChartFromStack(ps, index);
1517 if (NULL != ptc)
break;
1519 index += delta_index;
1525 index = index_start;
1527 while ((index >= 0) && (index < ps->nEntry)) {
1528 ChartFamilyEnum chart_family = GetCSChartFamily(ps, index);
1529 if (chart_family == New_Family_Fallback) {
1530 ptc = OpenChartFromStack(ps, index);
1532 if (NULL != ptc)
break;
1534 index += delta_index;
1541wxXmlDocument ChartDB::GetXMLDescription(
int dbIndex,
bool b_getGeom) {
1543 if (!IsValid() || (dbIndex >= GetChartTableEntries()))
return doc;
1545 bool b_remove = !IsChartInCache(dbIndex);
1547 wxXmlNode *pcell_node = NULL;
1552 ChartBase *pc = OpenChartFromDB(dbIndex, HEADER_ONLY);
1553 b_remove = !IsChartInCache(dbIndex);
1556 if (CHART_FAMILY_RASTER == (ChartFamilyEnum)cte.GetChartFamily()) {
1557 pcell_node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"chart" ));
1559 wxString path = GetDBChartFileName(dbIndex);
1560 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"path" ));
1561 pcell_node->AddChild(node);
1562 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), path);
1563 node->AddChild(tnode);
1565 wxFileName name(path);
1566 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"name" ));
1567 pcell_node->AddChild(node);
1568 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), name.GetName());
1569 node->AddChild(tnode);
1572 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lname" ));
1573 pcell_node->AddChild(node);
1574 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pc->GetName());
1575 node->AddChild(tnode);
1579 scale.Printf(_T(
"%d"), cte.GetScale());
1580 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cscale" ));
1581 pcell_node->AddChild(node);
1582 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ),
scale);
1583 node->AddChild(tnode);
1585 wxDateTime file_date(cte.GetFileTime());
1586 file_date.MakeUTC();
1587 wxString sfile_date = file_date.FormatISODate();
1588 sfile_date += _T(
"T");
1589 sfile_date += file_date.FormatISOTime();
1590 sfile_date += _T(
"Z");
1592 new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"local_file_datetime_iso8601" ));
1593 pcell_node->AddChild(node);
1594 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sfile_date);
1595 node->AddChild(tnode);
1598 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"source_edition" ));
1599 pcell_node->AddChild(node);
1600 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pc->GetSE());
1601 node->AddChild(tnode);
1603 wxDateTime sdt = pc->GetEditionDate();
1604 wxString ssdt = _T(
"Unknown");
1605 if (sdt.IsValid()) ssdt = sdt.Format(_T(
"%Y%m%d"));
1607 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"source_date" ));
1608 pcell_node->AddChild(node);
1609 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), ssdt);
1610 node->AddChild(tnode);
1616 if (s == _T("raster_edition"))
1617 if (s == _T("ntm_edition"))
1619 if (s == _T("ntm_date"))
1620 if (s == _T("source_edition_last_correction"))
1621 if (s == _T("raster_edition_last_correction"))
1622 if (s == _T("ntm_edition_last_correction"))
1626 else if (CHART_FAMILY_VECTOR == (ChartFamilyEnum)cte.GetChartFamily()) {
1627 pcell_node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cell" ));
1629 wxString path = GetDBChartFileName(dbIndex);
1630 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"path" ));
1631 pcell_node->AddChild(node);
1632 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), path);
1633 node->AddChild(tnode);
1635 wxFileName name(path);
1636 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"name" ));
1637 pcell_node->AddChild(node);
1638 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), name.GetName());
1639 node->AddChild(tnode);
1642 scale.Printf(_T(
"%d"), cte.GetScale());
1643 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cscale" ));
1644 pcell_node->AddChild(node);
1645 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ),
scale);
1646 node->AddChild(tnode);
1648 wxDateTime file_date(cte.GetFileTime());
1649 file_date.MakeUTC();
1650 wxString sfile_date = file_date.FormatISODate();
1651 sfile_date += _T(
"T");
1652 sfile_date += file_date.FormatISOTime();
1653 sfile_date += _T(
"Z");
1655 new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"local_file_datetime_iso8601" ));
1656 pcell_node->AddChild(node);
1657 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sfile_date);
1658 node->AddChild(tnode);
1661 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"edtn" ));
1662 pcell_node->AddChild(node);
1663 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pc->GetSE());
1664 node->AddChild(tnode);
1669 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"isdt" ));
1670 pcell_node->AddChild(node);
1671 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pcs57->GetISDT());
1672 node->AddChild(tnode);
1674 wxString LastUpdateDate;
1676 pcs57->ValidateAndCountUpdates(path, _T(
""), LastUpdateDate,
false);
1679 supdn.Printf(_T(
"%d"), updn);
1680 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"updn" ));
1681 pcell_node->AddChild(node);
1682 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), supdn);
1683 node->AddChild(tnode);
1685 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"uadt" ));
1686 pcell_node->AddChild(node);
1687 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), LastUpdateDate);
1688 node->AddChild(tnode);
1692 if (pcell_node && b_getGeom) {
1693 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cov" ));
1694 pcell_node->AddChild(node);
1697 if (cte.GetnPlyEntries()) {
1698 wxXmlNode *panelnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"panel" ));
1699 node->AddChild(panelnode);
1702 panel_no.Printf(_T(
"%d"), 0);
1703 wxXmlNode *anode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), panel_no);
1704 panelnode->AddChild(anode);
1706 float *pf = cte.GetpPlyTable();
1707 for (
int j = 0; j < cte.GetnPlyEntries(); j++) {
1708 wxXmlNode *vnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"vertex" ));
1709 panelnode->AddChild(vnode);
1711 wxXmlNode *latnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lat" ));
1712 vnode->AddChild(latnode);
1716 sl.Printf(_T(
"%.5f"), l);
1717 wxXmlNode *vtnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sl);
1718 latnode->AddChild(vtnode);
1720 wxXmlNode *lonnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lon" ));
1721 vnode->AddChild(lonnode);
1725 sll.Printf(_T(
"%.5f"), ll);
1726 wxXmlNode *vtlnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sll);
1727 lonnode->AddChild(vtlnode);
1731 for (
int i = 0; i < cte.GetnAuxPlyEntries(); i++) {
1732 wxXmlNode *panelnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"panel" ));
1733 node->AddChild(panelnode);
1736 panel_no.Printf(_T(
"%d"), i + 1);
1737 wxXmlNode *anode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), panel_no);
1738 panelnode->AddChild(anode);
1740 float *pf = cte.GetpAuxPlyTableEntry(i);
1741 for (
int j = 0; j < cte.GetAuxCntTableEntry(i); j++) {
1742 wxXmlNode *vnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"vertex" ));
1743 panelnode->AddChild(vnode);
1745 wxXmlNode *latnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lat" ));
1746 vnode->AddChild(latnode);
1750 sl.Printf(_T(
"%.5f"), l);
1751 wxXmlNode *vtnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sl);
1752 latnode->AddChild(vtnode);
1754 wxXmlNode *lonnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lon" ));
1755 vnode->AddChild(lonnode);
1759 sll.Printf(_T(
"%.5f"), ll);
1760 wxXmlNode *vtlnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sll);
1761 lonnode->AddChild(vtlnode);
1766 doc.SetRoot(pcell_node);
1768 if (b_remove) DeleteCacheChart(pc);
1773bool ChartDB::CheckExclusiveTileGroup(
int canvasIndex) {
1781 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1783 switch (g_canvasConfig) {
1785 if (canvasIndex == 0) {
1786 cc = config_array.Item(0);
1787 if (cc) canvas = cc->
canvas;
1789 cc = config_array.Item(1);
1790 if (cc) canvas = cc->
canvas;
1795 cc = config_array.Item(0);
1796 if (cc) canvas = cc->
canvas;
1799 if (!canvas)
return false;
1802 if (canvas->m_groupIndex == m_checkGroupIndex[canvasIndex])
1803 return m_checkedTileOnly[canvasIndex];
1806 bool rv = IsNonMBTileInGroup(canvas->m_groupIndex);
1808 m_checkGroupIndex[canvasIndex] = canvas->m_groupIndex;
1809 m_checkedTileOnly[canvasIndex] = !rv;
1814bool ChartDB::CheckAnyCanvasExclusiveTileGroup() {
1822 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1824 switch (g_canvasConfig) {
1826 cc = config_array.Item(0);
1830 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1831 rv |= m_checkedTileOnly[0];
1835 cc = config_array.Item(1);
1839 if (canvas->m_groupIndex == m_checkGroupIndex[1])
1840 rv |= m_checkedTileOnly[1];
1846 cc = config_array.Item(0);
1850 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1851 rv |= m_checkedTileOnly[0];
1882bool G_FloatPtInPolygon(
MyFlPoint *rgpts,
int wnumpts,
float x,
float y)
1898 for (i = 0, ppt = rgpts; i < wnumpts - 1; i++, ppt++) {
1901 if (Intersect(pt0, pt2, *ppt, *(ppt1))) wnumintsct++;
1905 if (Intersect(pt0, pt2, *ppt, *rgpts)) wnumintsct++;
1911 if (!(wnumintsct & 1)) {
1912 for (i = 0, ppt = rgpts; i < wnumpts; i++, ppt++) {
1913 if (((*ppt).x == x) && ((*ppt).y == y))
return true;
1934 return (((CCW(p1, p2, p3) * CCW(p1, p2, p4)) <= 0) &&
1935 ((CCW(p3, p4, p1) * CCW(p3, p4, p2) <= 0)));
1965 return ((dx1 * dy2 > dy1 * dx2) ? 1 : -1);
Base class for all chart types.
ChartCanvas - Main chart display and interaction component.
Manages the chart database and provides access to chart data.
bool LoadBinary(const wxString &filename, ArrayOfCDI &dir_array_check)
Load the chart database from a binary file.
Represents a KAP format chart, derived from ChartBaseBSB.
Represents an MBTiles format chart.
Wrapper class for plugin-based charts.
Window for displaying chart thumbnails.
Encapsulates persistent canvas configuration.
ChartCanvas * canvas
Pointer to associated chart canvas.
Represents a single CM93 chart at a specific scale.
Represents a composite CM93 chart covering multiple scales.
Represents an S57 format electronic navigational chart in OpenCPN.
bool startswith(const std::string &str, const std::string &prefix)
Return true if s starts with given prefix.
Represents an entry in the chart table, containing information about a single chart.