31#include <wx/stopwatch.h>
33#include <wx/tokenzr.h>
45#include "ocpn_frame.h"
47#include "androidUTIL.h"
51#include "glChartCanvas.h"
57#include <wx/progdlg.h>
64extern ColorScheme GetColorScheme();
67extern bool GetMemoryStatus(
int *mem_total,
int *mem_used);
70extern s52plib *ps52plib;
76bool G_FloatPtInPolygon(
MyFlPoint *rgpts,
int wnumpts,
float x,
float y);
82int ChartStack::GetCurrentEntrydbIndex(
void) {
83 if (nEntry && (CurrentStackEntry >= 0) )
84 return DBIndex[CurrentStackEntry];
89void ChartStack::SetCurrentEntryFromdbIndex(
int current_db_index) {
90 for (
int i = 0; i < nEntry; i++) {
91 if (current_db_index == DBIndex[i]) CurrentStackEntry = i;
95int ChartStack::GetDBIndex(
int stack_index) {
96 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
97 return DBIndex[stack_index];
102void ChartStack::SetDBIndex(
int stack_index,
int db_index) {
103 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
104 DBIndex[stack_index] = db_index;
107bool ChartStack::DoesStackContaindbIndex(
int db_index) {
108 for (
int i = 0; i < nEntry; i++) {
109 if (db_index == DBIndex[i])
return true;
115void ChartStack::AddChart(
int db_add) {
120 int db_index = db_add;
127 SetDBIndex(j - 1, db_index);
137 for (
int id = 0;
id < j - 1;
id++) {
138 if (GetDBIndex(
id) != -1) {
141 for (
int jd =
id + 1; jd < j; jd++) {
142 if (GetDBIndex(jd) != -1) {
144 if (pm->GetFileTime() && pn->GetFileTime()) {
145 if (labs(pm->GetFileTime() - pn->GetFileTime()) <
147 if (pn->GetpFileName()->IsSameAs(*(pm->GetpFileName())))
158 if (GetDBIndex(
id) == -1) {
161 int db_index = GetDBIndex(jd);
162 SetDBIndex(jd - 1, db_index);
179 for (
int i = 0; i < j - 1; i++) {
182 ChartData->GetChartTableEntry(GetDBIndex(i + 1));
184 if (n.GetScale() < m.GetScale()) {
186 SetDBIndex(i, GetDBIndex(i + 1));
187 SetDBIndex(i + 1, ti);
199 pChartCache =
new wxArrayPtrVoid;
208 if (g_memCacheLimit) {
210 msg.Printf(
"ChartDB Cache policy: Application target is %d MBytes",
211 g_memCacheLimit / 1024);
215 msg.Printf(
"ChartDB Cache policy: Max open chart limit is %d.",
220 m_checkGroupIndex[0] = m_checkGroupIndex[1] = -1;
221 m_checkedTileOnly[0] = m_checkedTileOnly[1] =
false;
232 ArrayOfCDI &dir_array_check) {
233 m_dir_array = dir_array_check;
234 return ChartDatabase::Read(filename);
239void ChartDB::DeleteCacheEntry(
CacheEntry *pce,
bool bDelTexture,
240 const wxString &msg) {
243 if (msg != wxEmptyString) {
244 wxLogMessage(
"%s%s", msg.c_str(), ch->GetFullPath().c_str());
249 if (pthumbwin->pThumbChart == ch) pthumbwin->pThumbChart = NULL;
254 if (g_glTextureManager)
255 g_glTextureManager->PurgeChartTextures(ch, bDelTexture);
258 pChartCache->Remove(pce);
263void ChartDB::DeleteCacheEntry(
int i,
bool bDelTexture,
const wxString &msg) {
265 if (pce) DeleteCacheEntry(pce, bDelTexture, msg);
268void ChartDB::PurgeCache() {
272 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
273 unsigned int nCache = pChartCache->GetCount();
274 for (
unsigned int i = 0; i < nCache; i++) {
275 DeleteCacheEntry(0,
true);
277 pChartCache->Clear();
279 m_cache_mutex.Unlock();
283void ChartDB::PurgeCachePlugins() {
285 wxLogMessage(
"Chart cache PlugIn purge");
287 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
288 unsigned int nCache = pChartCache->GetCount();
294 if (CHART_TYPE_PLUGIN == Ch->GetChartType()) {
295 DeleteCacheEntry(pce,
true);
297 nCache = pChartCache->GetCount();
304 m_cache_mutex.Unlock();
308void ChartDB::ClearCacheInUseFlags(
void) {
309 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
310 unsigned int nCache = pChartCache->GetCount();
311 for (
unsigned int i = 0; i < nCache; i++) {
313 pce->b_in_use =
false;
315 m_cache_mutex.Unlock();
322void ChartDB::PurgeCacheUnusedCharts(
double factor) {
324 if (g_memCacheLimit) {
325 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
328 GetMemoryStatus(0, &mem_used);
329 int mem_limit = g_memCacheLimit * factor;
331 int nl = pChartCache->GetCount();
333 wxString msg(
"Purging unused chart from cache: ");
335 while ((mem_used > mem_limit) && (nl > 0)) {
336 if (pChartCache->GetCount() < 2) {
341 CacheEntry *pce = FindOldestDeleteCandidate(
false);
344 DeleteCacheEntry(pce,
false , msg);
350 GetMemoryStatus(0, &mem_used);
355 m_cache_mutex.Unlock();
359 else if (g_nCacheLimit) {
360 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
362 double fac10 = factor * 10;
363 int chart_limit = g_nCacheLimit * fac10 / 10;
365 int nl = pChartCache->GetCount();
367 wxString msg(
"Purging unused chart from cache: ");
368 while ((nl > chart_limit) && (nl > 0)) {
369 if (pChartCache->GetCount() < 2) {
374 CacheEntry *pce = FindOldestDeleteCandidate(
false);
377 DeleteCacheEntry(pce,
false , msg);
382 nl = pChartCache->GetCount();
385 m_cache_mutex.Unlock();
394ChartBase *ChartDB::GetChart(
const wxChar *theFilePath,
396 wxFileName fn(theFilePath);
398 if (!fn.FileExists()) {
400 if (!wxDir::Exists(theFilePath)) {
401 wxLogMessage(
" ...file does not exist: %s", theFilePath);
407 wxString chartExt = fn.GetExt().Upper();
409 if (chartExt ==
"XZ") {
410 wxString npath = theFilePath;
411 npath = npath.Left(npath.length() - 3);
412 wxFileName fn(npath);
413 chartExt = fn.GetExt().Upper();
416 if (chartExt ==
"KAP") {
418 }
else if (chartExt ==
"GEO") {
420 }
else if (chartExt ==
"MBTILES") {
422 }
else if (chartExt ==
"000" || chartExt ==
"S57") {
425 }
else if (chart_desc.m_descriptor_type == PLUGIN_DESCRIPTOR) {
432 wxRegEx rxName(
"[0-9]+");
433 wxRegEx rxExt(
"[A-G]");
434 if (rxName.Matches(fn.GetName()) && rxExt.Matches(chartExt))
445bool ChartDB::IsChartDirectoryExcluded(
const std::string &chart_file) {
455int ChartDB::BuildChartStack(
ChartStack *cstk,
float lat,
float lon,
int db_add,
457 BuildChartStack(cstk, lat, lon, groupIndex);
459 if (db_add >= 0) cstk->AddChart(db_add);
464int ChartDB::BuildChartStack(
ChartStack *cstk,
float lat,
float lon,
469 if (!IsValid())
return 0;
473 int nEntry = GetChartTableEntries();
475 for (
int db_index = 0; db_index < nEntry; db_index++) {
479 if (IsChartDirectoryExcluded(cte.GetFullPath()))
continue;
482 bool b_group_add =
false;
483 if (groupIndex > 0) {
484 const int ng = cte.GetGroupArray().size();
485 for (
int ig = 0; ig < ng; ig++) {
486 if (groupIndex == cte.GetGroupArray()[ig]) {
494 bool b_writable_add =
true;
498 wxFileName fn(cte.GetFullSystemPath());
499 if (!androidIsDirWritable(fn.GetPath())) b_writable_add =
false;
502 bool b_pos_add =
false;
503 if (b_group_add && b_writable_add) {
507 if (cte.GetChartType() == CHART_TYPE_PLUGIN) {
512 if (CheckPositionWithinChart(db_index, lat, lon) && (j < MAXSTACK))
516 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() < 180.)) {
517 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
522 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() > 180.)) {
523 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
529 bool b_available =
true;
531 if (b_group_add && b_pos_add && (cte.GetChartType() == CHART_TYPE_PLUGIN)) {
533 if (!IsChartAvailable(db_index)) {
534 pcte->SetAvailable(
false);
537 pcte->SetAvailable(
true);
542 if (b_group_add && b_pos_add && b_available) {
545 cstk->SetDBIndex(j - 1, db_index);
561 for (
int id = 0;
id < j - 1;
id++) {
562 if (cstk->GetDBIndex(
id) != -1) {
563 const ChartTableEntry &ctem = GetChartTableEntry(cstk->GetDBIndex(
id));
565 for (
int jd =
id + 1; jd < j; jd++) {
566 if (cstk->GetDBIndex(jd) != -1) {
568 GetChartTableEntry(cstk->GetDBIndex(jd));
569 bool bsameTime =
false;
570 if (ctem.GetFileTime() && cten.GetFileTime()) {
571 if (labs(ctem.GetFileTime() - cten.GetFileTime()) < 60)
574 if (ctem.GetChartEditionDate() == cten.GetChartEditionDate())
578 if (cten.GetpFileName()->IsSameAs(*(ctem.GetpFileName())))
579 cstk->SetDBIndex(jd, -1);
588 if (cstk->GetDBIndex(
id) == -1) {
591 int db_index = cstk->GetDBIndex(jd);
592 cstk->SetDBIndex(jd - 1, db_index);
609 for (i = 0; i < j - 1; i++) {
611 const ChartTableEntry &n = GetChartTableEntry(cstk->GetDBIndex(i + 1));
613 if (n.GetScale() < m.GetScale()) {
614 ti = cstk->GetDBIndex(i);
615 cstk->SetDBIndex(i, cstk->GetDBIndex(i + 1));
616 cstk->SetDBIndex(i + 1, ti);
622 cstk->b_valid =
true;
627bool ChartDB::IsChartInGroup(
const int db_index,
const int group) {
631 bool b_in_group =
false;
633 for (
unsigned int ig = 0; ig < pt->GetGroupArray().size(); ig++) {
634 if (group == pt->GetGroupArray()[ig]) {
645bool ChartDB::IsENCInGroup(
const int groupIndex) {
649 for (
int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
653 bool b_group_add =
false;
654 if (groupIndex > 0) {
655 const int ng = cte.GetGroupArray().size();
656 for (
int ig = 0; ig < ng; ig++) {
657 if (groupIndex == cte.GetGroupArray()[ig]) {
666 if (cte.GetChartFamily() == CHART_FAMILY_VECTOR) {
676bool ChartDB::IsNonMBTileInGroup(
const int groupIndex) {
681 for (
int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
685 bool b_group_add =
false;
686 if (groupIndex > 0) {
687 const int ng = cte.GetGroupArray().size();
688 for (
int ig = 0; ig < ng; ig++) {
689 if (groupIndex == cte.GetGroupArray()[ig]) {
698 if (cte.GetChartType() != CHART_TYPE_MBTILES) {
711bool ChartDB::CheckPositionWithinChart(
int index,
float lat,
float lon) {
716 if ((lat <= pt->GetLatMax()) && (lat >= pt->GetLatMin()) &&
717 (lon >= pt->GetLonMin()) && (lon <= pt->GetLonMax())) {
720 bool bInside = G_FloatPtInPolygon((
MyFlPoint *)pt->GetpPlyTable(),
721 pt->GetnPlyEntries(), lon, lat);
724 if (pt->GetnAuxPlyEntries()) {
725 for (
int k = 0; k < pt->GetnAuxPlyEntries(); k++) {
727 G_FloatPtInPolygon((
MyFlPoint *)pt->GetpAuxPlyTableEntry(k),
728 pt->GetAuxCntTableEntry(k), lon, lat);
729 if (bAuxInside)
return true;
745 if ((pa == 0) || (pb == 0))
return false;
746 if ((!pa->b_valid) || (!pb->b_valid))
return false;
747 if (pa->nEntry != pb->nEntry)
return false;
749 for (
int i = 0; i < pa->nEntry; i++) {
750 if (pa->GetDBIndex(i) != pb->GetDBIndex(i))
return false;
760 if ((pa == 0) || (pb == 0))
return false;
761 pa->nEntry = pb->nEntry;
763 for (
int i = 0; i < pa->nEntry; i++) pa->SetDBIndex(i, pb->GetDBIndex(i));
765 pa->CurrentStackEntry = pb->CurrentStackEntry;
767 pa->b_valid = pb->b_valid;
772wxString ChartDB::GetFullPath(
ChartStack *ps,
int stackindex) {
773 int dbIndex = ps->GetDBIndex(stackindex);
774 return GetChartTableEntry(dbIndex).GetFullSystemPath();
781int ChartDB::GetCSPlyPoint(
ChartStack *ps,
int stackindex,
int plyindex,
782 float *lat,
float *lon) {
783 int dbIndex = ps->GetDBIndex(stackindex);
784 wxASSERT(dbIndex >= 0);
787 if (entry.GetnPlyEntries()) {
788 float *fp = entry.GetpPlyTable();
795 return entry.GetnPlyEntries();
801int ChartDB::GetStackChartScale(
ChartStack *ps,
int stackindex,
char *buf,
803 int dbindex = ps->GetDBIndex(stackindex);
804 wxASSERT(dbindex >= 0);
807 int sc = entry.GetScale();
808 if (buf) sprintf(buf,
"%d", sc);
816int ChartDB::GetStackEntry(
ChartStack *ps, wxString fp) {
817 for (
int i = 0; i < ps->nEntry; i++) {
819 if (fp.IsSameAs(entry.GetFullSystemPath()))
return i;
828ChartTypeEnum ChartDB::GetCSChartType(
ChartStack *ps,
int stackindex) {
830 int dbindex = ps->GetDBIndex(stackindex);
832 return (ChartTypeEnum)GetChartTableEntry(dbindex).GetChartType();
834 return CHART_TYPE_UNKNOWN;
837ChartFamilyEnum ChartDB::GetCSChartFamily(
ChartStack *ps,
int stackindex) {
839 int dbindex = ps->GetDBIndex(stackindex);
843 ChartTypeEnum type = (ChartTypeEnum)entry.GetChartType();
846 return CHART_FAMILY_RASTER;
848 return CHART_FAMILY_RASTER;
850 return CHART_FAMILY_VECTOR;
851 case CHART_TYPE_CM93:
852 return CHART_FAMILY_VECTOR;
853 case CHART_TYPE_CM93COMP:
854 return CHART_FAMILY_VECTOR;
855 case CHART_TYPE_DUMMY:
856 return CHART_FAMILY_RASTER;
858 return CHART_FAMILY_UNKNOWN;
862 return CHART_FAMILY_UNKNOWN;
865std::vector<int> ChartDB::GetCSArray(
ChartStack *ps) {
866 std::vector<int> ret;
869 ret.reserve(ps->nEntry);
870 for (
int i = 0; i < ps->nEntry; i++) {
871 ret.push_back(ps->GetDBIndex(i));
878bool ChartDB::IsChartInCache(
int dbindex) {
879 bool bInCache =
false;
882 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
883 unsigned int nCache = pChartCache->GetCount();
884 for (
unsigned int i = 0; i < nCache; i++) {
886 if (pce->dbIndex == dbindex) {
887 if (pce->pChart != 0 && ((
ChartBase *)pce->pChart)->IsReadyToRender())
892 m_cache_mutex.Unlock();
898bool ChartDB::IsChartInCache(wxString path) {
899 bool bInCache =
false;
900 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
902 unsigned int nCache = pChartCache->GetCount();
903 for (
unsigned int i = 0; i < nCache; i++) {
905 if (pce->FullPath == path) {
906 if (pce->pChart != 0 && ((
ChartBase *)pce->pChart)->IsReadyToRender())
912 m_cache_mutex.Unlock();
917bool ChartDB::IsChartLocked(
int index) {
918 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
919 unsigned int nCache = pChartCache->GetCount();
920 for (
unsigned int i = 0; i < nCache; i++) {
922 if (pce->dbIndex == index) {
923 bool ret = pce->n_lock > 0;
924 m_cache_mutex.Unlock();
928 m_cache_mutex.Unlock();
934bool ChartDB::LockCacheChart(
int index) {
937 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
938 unsigned int nCache = pChartCache->GetCount();
939 for (
unsigned int i = 0; i < nCache; i++) {
941 if (pce->dbIndex == index) {
947 m_cache_mutex.Unlock();
952void ChartDB::UnLockCacheChart(
int index) {
954 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
955 unsigned int nCache = pChartCache->GetCount();
956 for (
unsigned int i = 0; i < nCache; i++) {
958 if (pce->dbIndex == index) {
959 if (pce->n_lock > 0) pce->n_lock--;
963 m_cache_mutex.Unlock();
967void ChartDB::UnLockAllCacheCharts() {
969 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
970 unsigned int nCache = pChartCache->GetCount();
971 for (
unsigned int i = 0; i < nCache; i++) {
973 if (pce->n_lock > 0) pce->n_lock--;
975 m_cache_mutex.Unlock();
982ChartBase *ChartDB::OpenChartFromDB(
int index, ChartInitFlag init_flag) {
983 return OpenChartUsingCache(index, init_flag);
986ChartBase *ChartDB::OpenChartFromDB(wxString chart_path,
987 ChartInitFlag init_flag) {
988 int dbii = FinddbIndex(chart_path);
989 return OpenChartUsingCache(dbii, init_flag);
993 ChartInitFlag init_flag) {
994 return OpenChartUsingCache(pStack->GetDBIndex(StackEntry), init_flag);
997ChartBase *ChartDB::OpenChartFromDBAndLock(
int index, ChartInitFlag init_flag,
999 wxCriticalSectionLocker locker(m_critSect);
1000 ChartBase *pret = OpenChartUsingCache(index, init_flag);
1001 if (lock && pret) LockCacheChart(index);
1005ChartBase *ChartDB::OpenChartFromDBAndLock(wxString chart_path,
1006 ChartInitFlag init_flag) {
1007 int dbii = FinddbIndex(chart_path);
1008 return OpenChartFromDBAndLock(dbii, init_flag);
1011CacheEntry *ChartDB::FindOldestDeleteCandidate(
bool blog) {
1014 unsigned int nCache = pChartCache->GetCount();
1016 if (blog) wxLogMessage(
"Searching chart cache for oldest entry");
1017 int LRUTime = m_ticks;
1019 for (
unsigned int i = 0; i < nCache; i++) {
1021 if (pce->RecentTime < LRUTime && !pce->n_lock) {
1022 if (!isSingleChart((
ChartBase *)(pce->pChart))) {
1023 LRUTime = pce->RecentTime;
1028 int dt = m_ticks - LRUTime;
1033 if (!pce->n_lock && !isSingleChart(pDeleteCandidate)) {
1035 wxLogMessage(
"Oldest unlocked cache index is %d, delta t is %d",
1040 wxLogMessage(
"All chart in cache locked, size: %d", nCache);
1046ChartBase *ChartDB::OpenChartUsingCache(
int dbindex, ChartInitFlag init_flag) {
1047 if ((dbindex < 0) || (dbindex > GetChartTableEntries() - 1))
return NULL;
1052 wxString ChartFullPath = cte.GetFullSystemPath();
1053 ChartTypeEnum chart_type = (ChartTypeEnum)cte.GetChartType();
1054 ChartFamilyEnum chart_family = (ChartFamilyEnum)cte.GetChartFamily();
1057 msg1.Printf(
"OpenChartUsingCache: type %d ", chart_type);
1060 if (cte.GetLatMax() > 90.0)
1067 bool bInCache =
false;
1071 wxMutexLocker lock(m_cache_mutex);
1073 unsigned int nCache = pChartCache->GetCount();
1075 for (
unsigned int i = 0; i < nCache; i++) {
1077 if (pce->FullPath == ChartFullPath) {
1086 msg.Printf(
"OpenChartUsingCache, IN cache: cache size: %d\n",
1087 (
int)pChartCache->GetCount());
1089 if (FULL_INIT == init_flag)
1091 if (Ch->IsReadyToRender()) {
1093 pce->RecentTime = m_ticks;
1094 pce->b_in_use =
true;
1098 if (pthumbwin && pthumbwin->pThumbChart == Ch)
1099 pthumbwin->pThumbChart = NULL;
1101 old_lock = pce->n_lock;
1102 pChartCache->Remove(pce);
1110 pce->RecentTime = m_ticks;
1111 pce->b_in_use =
true;
1122 if (g_memCacheLimit) {
1125 GetMemoryStatus(0, &mem_used);
1128 msg.Printf(
"OpenChartUsingCache, NOT in cache: cache size: %d\n",
1129 (
int)pChartCache->GetCount());
1132 msg1.Printf(
" OpenChartUsingCache: type %d ", chart_type);
1133 wxLogMessage(msg1 + ChartFullPath);
1135 if ((mem_used > g_memCacheLimit * 8 / 10) &&
1136 (pChartCache->GetCount() > 2)) {
1137 wxString msg(
"Removing oldest chart from cache: ");
1139 CacheEntry *pce = FindOldestDeleteCandidate(
true);
1140 if (pce == 0)
break;
1143 DeleteCacheEntry(pce,
true, msg);
1145 GetMemoryStatus(0, &mem_used);
1146 if ((mem_used < g_memCacheLimit * 8 / 10) ||
1147 (pChartCache->GetCount() <= 2))
1158 unsigned int nCache = pChartCache->GetCount();
1159 if (nCache > (
unsigned int)g_nCacheLimit && nCache > 2) {
1160 wxString msg(
"Removing oldest chart from cache: ");
1161 while (nCache > (
unsigned int)g_nCacheLimit) {
1162 CacheEntry *pce = FindOldestDeleteCandidate(
true);
1163 if (pce == 0)
break;
1165 DeleteCacheEntry(pce,
true, msg);
1176 wxLogMessage(
"Creating new chart");
1178 if (chart_type == CHART_TYPE_KAP)
1181 else if (chart_type == CHART_TYPE_GEO)
1184 else if (chart_type == CHART_TYPE_MBTILES)
1187 else if (chart_type == CHART_TYPE_S57) {
1192 Chs57->SetNativeScale(cte.GetScale());
1197 ext.NLAT = cte.GetLatMax();
1198 ext.SLAT = cte.GetLatMin();
1199 ext.WLON = cte.GetLonMin();
1200 ext.ELON = cte.GetLonMax();
1201 Chs57->SetFullExtent(ext);
1204 else if (chart_type == CHART_TYPE_CM93) {
1209 Chcm93->SetNativeScale(cte.GetScale());
1214 ext.NLAT = cte.GetLatMax();
1215 ext.SLAT = cte.GetLatMin();
1216 ext.WLON = cte.GetLonMin();
1217 ext.ELON = cte.GetLonMax();
1218 Chcm93->SetFullExtent(ext);
1221 else if (chart_type == CHART_TYPE_CM93COMP) {
1227 Chcm93->SetNativeScale(cte.GetScale());
1232 ext.NLAT = cte.GetLatMax();
1233 ext.SLAT = cte.GetLatMin();
1234 ext.WLON = cte.GetLonMin();
1235 ext.ELON = cte.GetLonMax();
1236 Chcm93->SetFullExtent(ext);
1239 else if (chart_type == CHART_TYPE_PLUGIN) {
1240 wxFileName fn(ChartFullPath);
1241 wxString ext = fn.GetExt();
1243 wxString ext_upper = ext.MakeUpper();
1244 wxString ext_lower = ext.MakeLower();
1245 wxString chart_class_name;
1250 for (
auto &cd : m_ChartClassDescriptorArray) {
1251 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
1252 if (cd.m_search_mask == ext_upper) {
1253 chart_class_name = cd.m_class_name;
1256 if (cd.m_search_mask == ext_lower) {
1257 chart_class_name = cd.m_class_name;
1260 if (ChartFullPath.Matches(cd.m_search_mask)) {
1261 chart_class_name = cd.m_class_name;
1268 if (chart_class_name.Len()) {
1271 if (chart_family == CHART_FAMILY_VECTOR) LoadS57();
1277 wxLogMessage(
"Unknown chart type");
1283 s52plib *plib = ps52plib;
1284 wxString msg_fn(ChartFullPath);
1285 msg_fn.Replace(
"%",
"%%");
1288 if ((chart_family != CHART_FAMILY_VECTOR) ||
1289 ((chart_family == CHART_FAMILY_VECTOR) && plib)) {
1290 wxLogMessage(wxString::Format(
"Initializing Chart %s", msg_fn.c_str()));
1292 ir = Ch->Init(ChartFullPath, init_flag);
1293 Ch->SetColorScheme( GetColorScheme());
1295 wxLogMessage(wxString::Format(
" No PLIB, Skipping vector chart %s",
1298 ir = INIT_FAIL_REMOVE;
1301 if (INIT_OK == ir) {
1307 pce->FullPath = ChartFullPath;
1309 pce->dbIndex = dbindex;
1312 pce->RecentTime = m_ticks;
1313 pce->n_lock = old_lock;
1315 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1316 pChartCache->Add((
void *)pce);
1317 m_cache_mutex.Unlock();
1332 if (chart_type == CHART_TYPE_MBTILES) {
1333 wxFileName tileFile(ChartFullPath);
1335 wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
1337 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1339 if (!CheckAnyCanvasExclusiveTileGroup() ||
1340 (tileSizeMB.GetLo() > 5000)) {
1343 bool b_clicked =
false;
1347 switch (g_canvasConfig) {
1349 cc = config_array.Item(0);
1353 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1355 cc = config_array.Item(1);
1359 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1363 cc = config_array.Item(0);
1367 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1374 switch (g_canvasConfig) {
1376 cc = config_array.Item(0);
1379 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1381 cc = config_array.Item(1);
1384 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1388 cc = config_array.Item(0);
1391 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1398 }
else if (INIT_FAIL_REMOVE == ir)
1401 wxString::Format(
"Problem initializing Chart %s", msg_fn.c_str()));
1408 DisableChart(ChartFullPath);
1409 }
else if ((INIT_FAIL_RETRY == ir) ||
1410 (INIT_FAIL_NOERROR ==
1413 wxLogMessage(wxString::Format(
1414 "Recoverable problem initializing Chart %s", msg_fn.c_str()));
1419 if (INIT_OK != ir) {
1422 wxString::Format(
" OpenChartFromStack... Error opening "
1423 "chart %s ... return code %d",
1424 msg_fn.c_str(), ir));
1438bool ChartDB::DeleteCacheChart(
ChartBase *pDeleteCandidate) {
1439 bool retval =
false;
1441 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1442 if (!isSingleChart(pDeleteCandidate)) {
1445 for (
unsigned int i = 0; i < pChartCache->GetCount(); i++) {
1447 if ((
ChartBase *)(pce->pChart) == pDeleteCandidate) {
1453 if (pce->n_lock > 0) pce->n_lock--;
1455 if (pce->n_lock == 0) {
1456 DeleteCacheEntry(pce);
1461 m_cache_mutex.Unlock();
1469void ChartDB::ApplyColorSchemeToCachedCharts(ColorScheme cs) {
1474 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1475 unsigned int nCache = pChartCache->GetCount();
1476 for (
unsigned int i = 0; i < nCache; i++) {
1479 if (Ch) Ch->SetColorScheme(cs,
true);
1482 m_cache_mutex.Unlock();
1492ChartBase *ChartDB::OpenStackChartConditional(
1493 ChartStack *ps,
int index_start,
bool bSearchDir, ChartTypeEnum New_Type,
1494 ChartFamilyEnum New_Family_Fallback) {
1500 if (bSearchDir == 1)
1506 index = index_start;
1508 while ((index >= 0) && (index < ps->nEntry)) {
1509 ChartTypeEnum chart_type = (ChartTypeEnum)GetCSChartType(ps, index);
1510 if ((chart_type == New_Type) || (New_Type == CHART_TYPE_DONTCARE)) {
1511 ptc = OpenChartFromStack(ps, index);
1512 if (NULL != ptc)
break;
1514 index += delta_index;
1520 index = index_start;
1522 while ((index >= 0) && (index < ps->nEntry)) {
1523 ChartFamilyEnum chart_family = GetCSChartFamily(ps, index);
1524 if (chart_family == New_Family_Fallback) {
1525 ptc = OpenChartFromStack(ps, index);
1527 if (NULL != ptc)
break;
1529 index += delta_index;
1536wxXmlDocument ChartDB::GetXMLDescription(
int dbIndex,
bool b_getGeom) {
1538 if (!IsValid() || (dbIndex >= GetChartTableEntries()))
return doc;
1540 bool b_remove = !IsChartInCache(dbIndex);
1542 wxXmlNode *pcell_node = NULL;
1547 ChartBase *pc = OpenChartFromDB(dbIndex, HEADER_ONLY);
1548 b_remove = !IsChartInCache(dbIndex);
1551 if (CHART_FAMILY_RASTER == (ChartFamilyEnum)cte.GetChartFamily()) {
1552 pcell_node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"chart" ));
1554 wxString path = GetDBChartFileName(dbIndex);
1555 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"path" ));
1556 pcell_node->AddChild(node);
1557 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), path);
1558 node->AddChild(tnode);
1560 wxFileName name(path);
1561 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"name" ));
1562 pcell_node->AddChild(node);
1563 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), name.GetName());
1564 node->AddChild(tnode);
1567 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lname" ));
1568 pcell_node->AddChild(node);
1569 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pc->GetName());
1570 node->AddChild(tnode);
1574 scale.Printf(
"%d", cte.GetScale());
1575 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cscale" ));
1576 pcell_node->AddChild(node);
1577 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ),
scale);
1578 node->AddChild(tnode);
1580 wxDateTime file_date(cte.GetFileTime());
1581 file_date.MakeUTC();
1582 wxString sfile_date = file_date.FormatISODate();
1584 sfile_date += file_date.FormatISOTime();
1587 new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"local_file_datetime_iso8601" ));
1588 pcell_node->AddChild(node);
1589 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sfile_date);
1590 node->AddChild(tnode);
1593 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"source_edition" ));
1594 pcell_node->AddChild(node);
1595 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pc->GetSE());
1596 node->AddChild(tnode);
1598 wxDateTime sdt = pc->GetEditionDate();
1599 wxString ssdt =
"Unknown";
1600 if (sdt.IsValid()) ssdt = sdt.Format(
"%Y%m%d");
1602 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"source_date" ));
1603 pcell_node->AddChild(node);
1604 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), ssdt);
1605 node->AddChild(tnode);
1611 if (s == "raster_edition")
1612 if (s == "ntm_edition")
1614 if (s == "ntm_date")
1615 if (s == "source_edition_last_correction")
1616 if (s == "raster_edition_last_correction")
1617 if (s == "ntm_edition_last_correction")
1621 else if (CHART_FAMILY_VECTOR == (ChartFamilyEnum)cte.GetChartFamily()) {
1622 pcell_node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cell" ));
1624 wxString path = GetDBChartFileName(dbIndex);
1625 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"path" ));
1626 pcell_node->AddChild(node);
1627 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), path);
1628 node->AddChild(tnode);
1630 wxFileName name(path);
1631 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"name" ));
1632 pcell_node->AddChild(node);
1633 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), name.GetName());
1634 node->AddChild(tnode);
1637 scale.Printf(
"%d", cte.GetScale());
1638 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cscale" ));
1639 pcell_node->AddChild(node);
1640 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ),
scale);
1641 node->AddChild(tnode);
1643 wxDateTime file_date(cte.GetFileTime());
1644 file_date.MakeUTC();
1645 wxString sfile_date = file_date.FormatISODate();
1647 sfile_date += file_date.FormatISOTime();
1650 new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"local_file_datetime_iso8601" ));
1651 pcell_node->AddChild(node);
1652 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sfile_date);
1653 node->AddChild(tnode);
1656 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"edtn" ));
1657 pcell_node->AddChild(node);
1658 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pc->GetSE());
1659 node->AddChild(tnode);
1664 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"isdt" ));
1665 pcell_node->AddChild(node);
1666 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pcs57->GetISDT());
1667 node->AddChild(tnode);
1669 wxString LastUpdateDate;
1671 pcs57->ValidateAndCountUpdates(path,
"", LastUpdateDate,
false);
1674 supdn.Printf(
"%d", updn);
1675 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"updn" ));
1676 pcell_node->AddChild(node);
1677 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), supdn);
1678 node->AddChild(tnode);
1680 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"uadt" ));
1681 pcell_node->AddChild(node);
1682 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), LastUpdateDate);
1683 node->AddChild(tnode);
1687 if (pcell_node && b_getGeom) {
1688 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cov" ));
1689 pcell_node->AddChild(node);
1692 if (cte.GetnPlyEntries()) {
1693 wxXmlNode *panelnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"panel" ));
1694 node->AddChild(panelnode);
1697 panel_no.Printf(
"%d", 0);
1698 wxXmlNode *anode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), panel_no);
1699 panelnode->AddChild(anode);
1701 float *pf = cte.GetpPlyTable();
1702 for (
int j = 0; j < cte.GetnPlyEntries(); j++) {
1703 wxXmlNode *vnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"vertex" ));
1704 panelnode->AddChild(vnode);
1706 wxXmlNode *latnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lat" ));
1707 vnode->AddChild(latnode);
1711 sl.Printf(
"%.5f", l);
1712 wxXmlNode *vtnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sl);
1713 latnode->AddChild(vtnode);
1715 wxXmlNode *lonnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lon" ));
1716 vnode->AddChild(lonnode);
1720 sll.Printf(
"%.5f", ll);
1721 wxXmlNode *vtlnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sll);
1722 lonnode->AddChild(vtlnode);
1726 for (
int i = 0; i < cte.GetnAuxPlyEntries(); i++) {
1727 wxXmlNode *panelnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"panel" ));
1728 node->AddChild(panelnode);
1731 panel_no.Printf(
"%d", i + 1);
1732 wxXmlNode *anode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), panel_no);
1733 panelnode->AddChild(anode);
1735 float *pf = cte.GetpAuxPlyTableEntry(i);
1736 for (
int j = 0; j < cte.GetAuxCntTableEntry(i); j++) {
1737 wxXmlNode *vnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"vertex" ));
1738 panelnode->AddChild(vnode);
1740 wxXmlNode *latnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lat" ));
1741 vnode->AddChild(latnode);
1745 sl.Printf(
"%.5f", l);
1746 wxXmlNode *vtnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sl);
1747 latnode->AddChild(vtnode);
1749 wxXmlNode *lonnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lon" ));
1750 vnode->AddChild(lonnode);
1754 sll.Printf(
"%.5f", ll);
1755 wxXmlNode *vtlnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sll);
1756 lonnode->AddChild(vtlnode);
1761 doc.SetRoot(pcell_node);
1763 if (b_remove) DeleteCacheChart(pc);
1768bool ChartDB::CheckExclusiveTileGroup(
int canvasIndex) {
1776 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1778 switch (g_canvasConfig) {
1780 if (canvasIndex == 0) {
1781 cc = config_array.Item(0);
1782 if (cc) canvas = cc->
canvas;
1784 cc = config_array.Item(1);
1785 if (cc) canvas = cc->
canvas;
1790 cc = config_array.Item(0);
1791 if (cc) canvas = cc->
canvas;
1794 if (!canvas)
return false;
1797 if (canvas->m_groupIndex == m_checkGroupIndex[canvasIndex])
1798 return m_checkedTileOnly[canvasIndex];
1801 bool rv = IsNonMBTileInGroup(canvas->m_groupIndex);
1803 m_checkGroupIndex[canvasIndex] = canvas->m_groupIndex;
1804 m_checkedTileOnly[canvasIndex] = !rv;
1809bool ChartDB::CheckAnyCanvasExclusiveTileGroup() {
1817 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1819 switch (g_canvasConfig) {
1821 cc = config_array.Item(0);
1825 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1826 rv |= m_checkedTileOnly[0];
1830 cc = config_array.Item(1);
1834 if (canvas->m_groupIndex == m_checkGroupIndex[1])
1835 rv |= m_checkedTileOnly[1];
1841 cc = config_array.Item(0);
1845 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1846 rv |= m_checkedTileOnly[0];
1877bool G_FloatPtInPolygon(
MyFlPoint *rgpts,
int wnumpts,
float x,
float y)
1893 for (i = 0, ppt = rgpts; i < wnumpts - 1; i++, ppt++) {
1896 if (Intersect(pt0, pt2, *ppt, *(ppt1))) wnumintsct++;
1900 if (Intersect(pt0, pt2, *ppt, *rgpts)) wnumintsct++;
1906 if (!(wnumintsct & 1)) {
1907 for (i = 0, ppt = rgpts; i < wnumpts; i++, ppt++) {
1908 if (((*ppt).x == x) && ((*ppt).y == y))
return true;
1929 return (((CCW(p1, p2, p3) * CCW(p1, p2, p4)) <= 0) &&
1930 ((CCW(p3, p4, p1) * CCW(p3, p4, p2) <= 0)));
1960 return ((dx1 * dy2 > dy1 * dx2) ? 1 : -1);
Chart canvas configuration state
std::vector< std::string > ChartDirectoryExcludedVector
Global instance.
ChartDB * ChartData
Global instance.
Charts database management
std::vector< std::string > ChartDirectoryExcludedVector
Global instance.
ChartDB * ChartData
Global instance.
Generic Chart canvas base.
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.
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.
Class cm93chart and helpers – CM93 chart state.
Config file user configuration interface.
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.