34#include "model/base_platform.h"
36#include "model/navutil_base.h"
39#include "wx/filename.h"
40#include "model/datetime.h"
41#include "model/comm_appmsg_bus.h"
44extern std::shared_ptr<ObservableListener> ack_listener;
46static bool executeSQL(sqlite3* db,
const char* sql) {
47 char* errMsg =
nullptr;
48 if (sqlite3_exec(db, sql,
nullptr,
nullptr, &errMsg) != SQLITE_OK) {
55static bool executeSQL(sqlite3* db, wxString& sql) {
56 return executeSQL(db, sql.ToStdString().c_str());
59bool CreateTables(sqlite3* db) {
61 const char* create_tables_sql = R
"(
62 CREATE TABLE IF NOT EXISTS tracks (
63 guid TEXT PRIMARY KEY,
72 created_at DATETIME DEFAULT CURRENT_TIMESTAMP
75 CREATE TABLE IF NOT EXISTS trk_points (
76 track_guid TEXT NOT NULL,
77 latitude REAL NOT NULL,
78 longitude REAL NOT NULL,
79 timestamp TEXT NOT NULL,
81 FOREIGN KEY (track_guid) REFERENCES tracks(guid) ON DELETE CASCADE
85 CREATE TABLE IF NOT EXISTS track_html_links (
86 guid TEXT PRIMARY KEY,
87 track_guid TEXT NOT NULL,
89 html_description TEXT,
91 FOREIGN KEY (track_guid) REFERENCES tracks(guid) ON DELETE CASCADE
96 if (!executeSQL(db, create_tables_sql))
return false;
101bool TrackExists(sqlite3* db,
const std::string& track_guid) {
102 const char* sql =
"SELECT 1 FROM tracks WHERE guid = ? LIMIT 1";
106 if (sqlite3_prepare_v2(db, sql, -1, &stmt,
nullptr) == SQLITE_OK) {
107 sqlite3_bind_text(stmt, 1, track_guid.c_str(), -1, SQLITE_STATIC);
109 if (sqlite3_step(stmt) == SQLITE_ROW) {
113 sqlite3_finalize(stmt);
120bool TrackHtmlLinkExists(sqlite3* db,
const std::string& link_guid) {
121 const char* sql =
"SELECT 1 FROM track_html_links WHERE guid = ? LIMIT 1";
125 if (sqlite3_prepare_v2(db, sql, -1, &stmt,
nullptr) == SQLITE_OK) {
126 sqlite3_bind_text(stmt, 1, link_guid.c_str(), -1, SQLITE_STATIC);
128 if (sqlite3_step(stmt) == SQLITE_ROW) {
132 sqlite3_finalize(stmt);
139bool DeleteAllCommentsForTrack(sqlite3* db,
const std::string& track_guid) {
140 const char* sql = R
"(
141 DELETE FROM track_html_links WHERE track_guid = ?
144 if (sqlite3_prepare_v2(db, sql, -1, &stmt,
nullptr) == SQLITE_OK) {
145 sqlite3_bind_text(stmt, 1, track_guid.c_str(), -1, SQLITE_STATIC);
146 if (sqlite3_step(stmt) != SQLITE_DONE) {
147 std::cerr <<
"Failed to delete comments: " << sqlite3_errmsg(db) <<
"\n";
149 sqlite3_finalize(stmt);
156bool InsertTrackPoint(sqlite3* db,
const std::string& track_guid,
double lat,
157 double lon,
const std::string& timestamp,
int i_point) {
158 const char* sql = R
"(
159 INSERT INTO trk_points (track_guid, latitude, longitude, timestamp, point_order)
160 VALUES (?, ?, ?, ?, ?)
164 if (sqlite3_prepare_v2(db, sql, -1, &stmt,
nullptr) == SQLITE_OK) {
165 sqlite3_bind_text(stmt, 1, track_guid.c_str(), -1, SQLITE_TRANSIENT);
166 sqlite3_bind_double(stmt, 2, lat);
167 sqlite3_bind_double(stmt, 3, lon);
168 sqlite3_bind_text(stmt, 4, timestamp.c_str(), -1, SQLITE_TRANSIENT);
169 sqlite3_bind_int(stmt, 5, i_point);
171 sqlite3_finalize(stmt);
178bool InsertTrackHTML(sqlite3* db,
const std::string& track_guid,
179 const std::string& link_guid,
const std::string& descrText,
180 const std::string& link,
const std::string& ltype) {
181 const char* sql = R
"(
182 INSERT INTO track_html_links (guid, track_guid, html_link, html_description, html_type)
183 VALUES (?, ?, ?, ?, ?)
187 if (sqlite3_prepare_v2(db, sql, -1, &stmt,
nullptr) == SQLITE_OK) {
188 sqlite3_bind_text(stmt, 1, link_guid.c_str(), -1, SQLITE_TRANSIENT);
189 sqlite3_bind_text(stmt, 2, track_guid.c_str(), -1, SQLITE_TRANSIENT);
190 sqlite3_bind_text(stmt, 3, link.c_str(), -1, SQLITE_TRANSIENT);
191 sqlite3_bind_text(stmt, 4, descrText.c_str(), -1, SQLITE_TRANSIENT);
192 sqlite3_bind_text(stmt, 5, ltype.c_str(), -1, SQLITE_TRANSIENT);
194 sqlite3_finalize(stmt);
201void errorLogCallback(
void* pArg,
int iErrCode,
const char* zMsg) {
203 wxString::Format(
"navobj database error. %d: %s", iErrCode, zMsg);
205 auto& noteman = NotificationManager::GetInstance();
206 noteman.AddNotification(NotificationSeverity::kCritical, msg.ToStdString());
214NavObj_dB::NavObj_dB() {
216 int ie = sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback,
nullptr);
220 wxFileName::GetPathSeparator() +
"navobj.db";
221 if (!wxFileExists(db_filename)) {
224 wxFileName::GetPathSeparator() +
"navobj.xml";
225 if (wxFileExists(noxml_filename)) {
226 wxCopyFile(noxml_filename, noxml_filename +
".backup");
231 int create_result = sqlite3_open_v2(
232 db_filename.ToStdString().c_str(),
234 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
237 if (create_result != SQLITE_OK) {
238 wxLogMessage(
"Cannot create new navobj.db database file");
247 int close_result = sqlite3_close_v2(m_db);
248 if (close_result != SQLITE_OK) {
254 int m_open_result = sqlite3_open_v2(db_filename.ToStdString().c_str(), &m_db,
255 SQLITE_OPEN_READWRITE, NULL);
256 sqlite3_exec(m_db,
"PRAGMA foreign_keys = ON;",
nullptr,
nullptr,
nullptr);
257 ImportLegacyNavobj();
258 sqlite3_close_v2(m_db);
260 m_open_result = sqlite3_open_v2(db_filename.ToStdString().c_str(), &m_db,
261 SQLITE_OPEN_READWRITE, NULL);
262 sqlite3_exec(m_db,
"PRAGMA foreign_keys = ON;",
nullptr,
nullptr,
nullptr);
265NavObj_dB::~NavObj_dB() { sqlite3_close_v2(m_db); }
267void NavObj_dB::Close() {
268 sqlite3_close_v2(m_db);
272bool NavObj_dB::ImportLegacyNavobj() {
273 bool rv = ImportLegacyTracks();
277bool NavObj_dB::ImportLegacyTracks() {
280 wxFileName::GetPathSeparator() +
"navobj.xml";
282 if (::wxFileExists(navobj_filename) &&
283 input_set->load_file(navobj_filename.ToStdString().c_str()).status ==
284 pugi::xml_parse_status::status_ok) {
285 input_set->LoadAllGPXTrackObjects();
288 std::vector<Track*> tracks_added;
290 for (
Track* track_import : g_TrackList) {
291 if (AddNewTrack(track_import)) {
292 tracks_added.push_back(track_import);
297 for (
Track* ptrack : tracks_added) {
298 if (ptrack->m_bIsInLayer)
continue;
299 g_pRouteMan->DeleteTrack(ptrack);
306void NavObj_dB::LoadNavObjects() { LoadAllTracks(); }
308bool NavObj_dB::AddNewTrack(
Track* track) {
309 if (TrackExists(m_db, track->m_GUID.ToStdString()))
return false;
313 sqlite3_exec(m_db,
"BEGIN TRANSACTION", 0, 0, &errMsg);
316 wxString sql = wxString::Format(
"INSERT INTO tracks (guid) VALUES ('%s')",
317 track->m_GUID.ToStdString().c_str());
318 if (!executeSQL(m_db, sql)) {
319 sqlite3_exec(m_db,
"COMMIT", 0, 0, &errMsg);
323 UpdateDBTrackAttributes(track);
326 for (
int i = 0; i < track->GetnPoints(); i++) {
327 auto point = track->GetPoint(i);
329 InsertTrackPoint(m_db, track->m_GUID.ToStdString(), point->m_lat,
330 point->m_lon, point->GetTimeString(), i);
334 int NbrOfLinks = track->m_TrackHyperlinkList->GetCount();
335 if (NbrOfLinks > 0) {
336 wxHyperlinkListNode* linknode = track->m_TrackHyperlinkList->GetFirst();
340 if (!TrackHtmlLinkExists(m_db, link->GUID)) {
341 InsertTrackHTML(m_db, track->m_GUID.ToStdString(), link->GUID,
342 link->DescrText.ToStdString(), link->Link.ToStdString(),
343 link->LType.ToStdString());
345 linknode = linknode->GetNext();
348 sqlite3_exec(m_db,
"COMMIT", 0, 0, &errMsg);
350 if (errMsg) rv =
false;
355bool NavObj_dB::UpdateDBTrackAttributes(
Track* track) {
369 if (sqlite3_prepare_v2(m_db, sql, -1, &stmt,
nullptr) == SQLITE_OK) {
370 sqlite3_bind_text(stmt, 1, track->GetName().ToStdString().c_str(),
371 track->GetName().Length(), SQLITE_TRANSIENT);
372 sqlite3_bind_text(stmt, 2, track->m_TrackDescription.ToStdString().c_str(),
373 track->m_TrackDescription.Length(), SQLITE_TRANSIENT);
374 sqlite3_bind_int(stmt, 3, track->m_bVisible);
375 sqlite3_bind_text(stmt, 4, track->m_TrackStartString.ToStdString().c_str(),
376 track->m_TrackStartString.Length(), SQLITE_TRANSIENT);
377 sqlite3_bind_text(stmt, 5, track->m_TrackEndString.ToStdString().c_str(),
378 track->m_TrackEndString.Length(), SQLITE_TRANSIENT);
379 sqlite3_bind_int(stmt, 6, track->m_width);
380 sqlite3_bind_int(stmt, 7,
381 (
int)(track->m_style));
382 sqlite3_bind_text(stmt, 8, track->m_Colour.ToStdString().c_str(), -1,
384 sqlite3_bind_text(stmt, 9, track->m_GUID.c_str(), track->m_GUID.size(),
390 if (sqlite3_step(stmt) != SQLITE_DONE) {
394 sqlite3_finalize(stmt);
399 DeleteAllCommentsForTrack(m_db, track->m_GUID.ToStdString());
402 int NbrOfLinks = track->m_TrackHyperlinkList->GetCount();
403 if (NbrOfLinks > 0) {
404 wxHyperlinkListNode* linknode = track->m_TrackHyperlinkList->GetFirst();
408 if (!TrackHtmlLinkExists(m_db, link->GUID)) {
409 InsertTrackHTML(m_db, track->m_GUID.ToStdString(), link->GUID,
410 link->DescrText.ToStdString(), link->Link.ToStdString(),
411 link->LType.ToStdString());
414 "UPDATE track_html_links SET "
416 "html_description = ?, "
420 if (sqlite3_prepare_v2(m_db, sql, -1, &stmt,
nullptr) == SQLITE_OK) {
421 sqlite3_bind_text(stmt, 3, link->Link.ToStdString().c_str(), -1,
423 sqlite3_bind_text(stmt, 4, link->DescrText.ToStdString().c_str(), -1,
425 sqlite3_bind_text(stmt, 5, link->LType.ToStdString().c_str(), -1,
428 if (sqlite3_step(stmt) != SQLITE_DONE) {
431 sqlite3_finalize(stmt);
434 linknode = linknode->GetNext();
443 if (!TrackExists(m_db, track->m_GUID.ToStdString()))
return false;
446 int this_point_index = track->GetnPoints();
449 if (!InsertTrackPoint(m_db, track->m_GUID.ToStdString(), point->m_lat,
450 point->m_lon, point->GetTimeString(),
451 this_point_index - 1))
457bool NavObj_dB::LoadAllTracks() {
458 const char* sql = R
"(
460 description, visibility, start_string, end_string,
464 ORDER BY created_at ASC
468 if (sqlite3_prepare_v2(m_db, sql, -1, &stmt,
nullptr) != SQLITE_OK) {
472 while (sqlite3_step(stmt) == SQLITE_ROW) {
474 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
476 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
477 std::string description =
478 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 2));
479 int visibility = sqlite3_column_int(stmt, 3);
480 std::string start_string =
481 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 4));
482 std::string end_string =
483 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 5));
484 int width = sqlite3_column_int(stmt, 6);
485 int style = sqlite3_column_int(stmt, 7);
487 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 8));
488 std::string created =
489 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 9));
491 Track* new_trk = NULL;
494 const char* sql = R
"(
495 SELECT latitude, longitude, timestamp, point_order
498 ORDER BY point_order ASC
502 if (sqlite3_prepare_v2(m_db, sql, -1, &stmtp,
nullptr) != SQLITE_OK) {
506 sqlite3_bind_text(stmtp, 1, guid.c_str(), -1, SQLITE_STATIC);
509 while (sqlite3_step(stmtp) == SQLITE_ROW) {
512 new_trk->m_GUID = guid;
515 new_trk->SetVisible(visibility == 1);
516 new_trk->SetName(name.c_str());
517 new_trk->m_TrackStartString = start_string.c_str();
518 new_trk->m_TrackEndString = end_string.c_str();
519 new_trk->m_width = width;
520 new_trk->m_style = (wxPenStyle)style;
521 new_trk->m_Colour = color;
526 double latitude = sqlite3_column_double(stmtp, 0);
527 double longitude = sqlite3_column_double(stmtp, 1);
528 std::string timestamp =
529 reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, 2));
530 int point_order = sqlite3_column_int(stmtp, 3);
532 auto point =
new TrackPoint(latitude, longitude, timestamp);
534 point->m_GPXTrkSegNo = GPXSeg;
535 new_trk->AddPoint(point);
537 sqlite3_finalize(stmtp);
540 new_trk->SetCurrentTrackSeg(GPXSeg);
543 const char* sqlh = R
"(
544 SELECT guid, html_link, html_description, html_type
545 FROM track_html_links
547 ORDER BY html_type ASC
552 if (sqlite3_prepare_v2(m_db, sqlh, -1, &stmt,
nullptr) == SQLITE_OK) {
553 sqlite3_bind_text(stmt, 1, new_trk->m_GUID.ToStdString().c_str(), -1,
556 while (sqlite3_step(stmt) == SQLITE_ROW) {
557 std::string link_guid =
558 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
559 std::string link_link =
560 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
561 std::string link_description =
562 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 2));
563 std::string link_type =
564 reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 3));
567 h->DescrText = link_description;
569 h->LType = link_type;
571 new_trk->m_TrackHyperlinkList->Append(h);
575 sqlite3_finalize(stmt);
582 g_TrackList.push_back(new_trk);
584 pSelect->AddAllSelectableTrackSegments(new_trk);
590bool NavObj_dB::DeleteTrack(
Track* track) {
591 std::string track_guid = track->m_GUID.ToStdString();
592 const char* sql =
"DELETE FROM tracks WHERE guid = ?";
595 if (sqlite3_prepare_v2(m_db, sql, -1, &stmt,
nullptr) == SQLITE_OK) {
596 sqlite3_bind_text(stmt, 1, track_guid.c_str(), -1, SQLITE_STATIC);
597 if (sqlite3_step(stmt) != SQLITE_DONE) {
600 sqlite3_finalize(stmt);
The navobj SQLite container object, a singleton.
Represents a single point in a track.
Represents a track, which is a series of connected track points.
bool exists(const std::string &name)
Class NotificationManager.