31#include <wx/filename.h> 
   41static void ReportError(
const std::string zmsg);  
 
   43static bool executeSQL(sqlite3* db, 
const char* sql) {
 
   44  char* errMsg = 
nullptr;
 
   45  if (sqlite3_exec(db, sql, 
nullptr, 
nullptr, &errMsg) != SQLITE_OK) {
 
   47        wxString::Format(_(
"navobj database error.") + 
" %s", errMsg);
 
   49    auto& noteman = NotificationManager::GetInstance();
 
   50    noteman.AddNotification(NotificationSeverity::kWarning, msg.ToStdString());
 
   57static bool executeSQL(sqlite3* db, wxString& sql) {
 
   58  return executeSQL(db, sql.ToStdString().c_str());
 
   61bool CreateTables(sqlite3* db) {
 
   63  const char* create_tables_sql = R
"( 
   64        CREATE TABLE IF NOT EXISTS tracks ( 
   65            guid TEXT PRIMARY KEY NOT NULL, 
   74            created_at DATETIME DEFAULT CURRENT_TIMESTAMP 
   77        CREATE TABLE IF NOT EXISTS trk_points ( 
   78            track_guid TEXT NOT NULL, 
   79            latitude REAL NOT NULL, 
   80            longitude REAL NOT NULL, 
   81            timestamp TEXT NOT NULL, 
   83            FOREIGN KEY (track_guid) REFERENCES tracks(guid) ON DELETE CASCADE 
   87        CREATE TABLE IF NOT EXISTS track_html_links ( 
   88            guid TEXT PRIMARY KEY, 
   89            track_guid TEXT NOT NULL, 
   91            html_description TEXT, 
   93            FOREIGN KEY (track_guid) REFERENCES tracks(guid) ON DELETE CASCADE 
   97        CREATE TABLE IF NOT EXISTS routes ( 
   98            guid TEXT PRIMARY KEY NOT NULL, 
  103            planned_departure TEXT, 
  110            shared_wp_viz INTEGER, 
  111            created_at DATETIME DEFAULT CURRENT_TIMESTAMP 
  115        CREATE TABLE IF NOT EXISTS routepoints ( 
  116            guid TEXT PRIMARY KEY NOT NULL, 
  128            RangeRingsNumber INTEGER, 
  130            RangeRingsStepUnits INTEGER, 
  131            RangeRingsVisible INTEGER, 
  132            RangeRingsColour TEXT, 
  140            created_at DATETIME DEFAULT CURRENT_TIMESTAMP 
  143        CREATE TABLE IF NOT EXISTS routepoints_link ( 
  147            PRIMARY KEY (route_guid, point_order), 
  148            FOREIGN KEY (route_guid) REFERENCES routes(guid) ON DELETE CASCADE 
  151        CREATE TABLE IF NOT EXISTS route_html_links ( 
  152            guid TEXT PRIMARY KEY, 
  153            route_guid TEXT NOT NULL, 
  155            html_description TEXT, 
  157            FOREIGN KEY (route_guid) REFERENCES routes(guid) ON DELETE CASCADE 
  160        CREATE TABLE IF NOT EXISTS routepoint_html_links ( 
  161            guid TEXT PRIMARY KEY, 
  162            routepoint_guid TEXT NOT NULL, 
  164            html_description TEXT, 
  166            FOREIGN KEY (routepoint_guid) REFERENCES routepoints(guid) ON DELETE CASCADE 
  171  if (!executeSQL(db, create_tables_sql)) 
return false;
 
  176bool TrackExists(sqlite3* db, 
const std::string& track_guid) {
 
  177  const char* sql = 
"SELECT 1 FROM tracks WHERE guid = ? LIMIT 1";
 
  181  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  182    sqlite3_bind_text(stmt, 1, track_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  184    if (sqlite3_step(stmt) == SQLITE_ROW) {
 
  188    sqlite3_finalize(stmt);
 
  190    ReportError(
"TrackExists:prepare");
 
  196bool TrackHtmlLinkExists(sqlite3* db, 
const std::string& link_guid) {
 
  197  const char* sql = 
"SELECT 1 FROM track_html_links WHERE guid = ? LIMIT 1";
 
  201  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  202    sqlite3_bind_text(stmt, 1, link_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  204    if (sqlite3_step(stmt) == SQLITE_ROW) {
 
  208    sqlite3_finalize(stmt);
 
  210    ReportError(
"TrackHtmlLinkExists:prepare");
 
  216bool DeleteAllCommentsForTrack(sqlite3* db, 
const std::string& track_guid) {
 
  217  const char* sql = 
"DELETE FROM track_html_links WHERE track_guid = ?";
 
  220  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  221    sqlite3_bind_text(stmt, 1, track_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  222    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  223      ReportError(
"DeleteAllCommentsForTrack:step");
 
  227    sqlite3_finalize(stmt);
 
  229    ReportError(
"DeleteAllCommentsForTrack:prepare");
 
  235bool InsertTrackPoint(sqlite3* db, 
const std::string& track_guid, 
double lat,
 
  236                      double lon, 
const std::string& timestamp, 
int i_point) {
 
  237  const char* sql = R
"( 
  238        INSERT INTO trk_points (track_guid, latitude, longitude, timestamp, point_order) 
  239        VALUES (?, ?, ?, ?, ?) 
  243  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  244    sqlite3_bind_text(stmt, 1, track_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  245    sqlite3_bind_double(stmt, 2, lat);
 
  246    sqlite3_bind_double(stmt, 3, lon);
 
  247    sqlite3_bind_text(stmt, 4, timestamp.c_str(), -1, SQLITE_TRANSIENT);
 
  248    sqlite3_bind_int(stmt, 5, i_point);
 
  249    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  250      ReportError(
"InsertTrackPoint:step");
 
  251      sqlite3_finalize(stmt);
 
  254    sqlite3_finalize(stmt);
 
  261bool InsertTrackHTML(sqlite3* db, 
const std::string& track_guid,
 
  262                     const std::string& link_guid, 
const std::string& descrText,
 
  263                     const std::string& link, 
const std::string& ltype) {
 
  264  const char* sql = R
"( 
  265        INSERT INTO track_html_links (guid, track_guid, html_link, html_description, html_type) 
  266        VALUES (?, ?, ?, ?, ?) 
  270  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  271    sqlite3_bind_text(stmt, 1, link_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  272    sqlite3_bind_text(stmt, 2, track_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  273    sqlite3_bind_text(stmt, 3, link.c_str(), -1, SQLITE_TRANSIENT);
 
  274    sqlite3_bind_text(stmt, 4, descrText.c_str(), -1, SQLITE_TRANSIENT);
 
  275    sqlite3_bind_text(stmt, 5, ltype.c_str(), -1, SQLITE_TRANSIENT);
 
  276    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  277      ReportError(
"InsertTrackHTML:step");
 
  278      sqlite3_finalize(stmt);
 
  281    sqlite3_finalize(stmt);
 
  290bool DeleteAllCommentsForRoute(sqlite3* db, 
const std::string& route_guid) {
 
  291  const char* sql = R
"( 
  292        DELETE FROM route_html_links WHERE route_guid = ? 
  295  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  296    sqlite3_bind_text(stmt, 1, route_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  297    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  298      ReportError(
"DeleteAllCommentsForRoute:step");
 
  299      sqlite3_finalize(stmt);
 
  302    sqlite3_finalize(stmt);
 
  309bool RouteHtmlLinkExists(sqlite3* db, 
const std::string& link_guid) {
 
  310  const char* sql = 
"SELECT 1 FROM route_html_links WHERE guid = ? LIMIT 1";
 
  314  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  315    sqlite3_bind_text(stmt, 1, link_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  317    if (sqlite3_step(stmt) == SQLITE_ROW) {
 
  321    sqlite3_finalize(stmt);
 
  328bool RoutePointHtmlLinkExists(sqlite3* db, 
const std::string& link_guid) {
 
  330      "SELECT 1 FROM routepoint_html_links WHERE guid = ? LIMIT 1";
 
  334  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  335    sqlite3_bind_text(stmt, 1, link_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  337    if (sqlite3_step(stmt) == SQLITE_ROW) {
 
  341    sqlite3_finalize(stmt);
 
  348bool RouteExistsDB(sqlite3* db, 
const std::string& route_guid) {
 
  349  const char* sql = 
"SELECT 1 FROM routes WHERE guid = ? LIMIT 1";
 
  353  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  354    sqlite3_bind_text(stmt, 1, route_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  356    if (sqlite3_step(stmt) == SQLITE_ROW) {
 
  360    sqlite3_finalize(stmt);
 
  367bool InsertRouteHTML(sqlite3* db, 
const std::string& route_guid,
 
  368                     const std::string& link_guid, 
const std::string& descrText,
 
  369                     const std::string& link, 
const std::string& ltype) {
 
  370  const char* sql = R
"( 
  371        INSERT INTO route_html_links (guid, route_guid, html_link, html_description, html_type) 
  372        VALUES (?, ?, ?, ?, ?) 
  376  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  377    sqlite3_bind_text(stmt, 1, link_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  378    sqlite3_bind_text(stmt, 2, route_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  379    sqlite3_bind_text(stmt, 3, link.c_str(), -1, SQLITE_TRANSIENT);
 
  380    sqlite3_bind_text(stmt, 4, descrText.c_str(), -1, SQLITE_TRANSIENT);
 
  381    sqlite3_bind_text(stmt, 5, ltype.c_str(), -1, SQLITE_TRANSIENT);
 
  382    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  383      ReportError(
"InsertRouteHTML:step");
 
  384      sqlite3_finalize(stmt);
 
  387    sqlite3_finalize(stmt);
 
  394bool RoutePointExists(sqlite3* db, 
const std::string& routepoint_guid) {
 
  395  const char* sql = 
"SELECT 1 FROM routepoints WHERE guid = ? LIMIT 1";
 
  399  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  400    sqlite3_bind_text(stmt, 1, routepoint_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  402    if (sqlite3_step(stmt) == SQLITE_ROW) {
 
  406    sqlite3_finalize(stmt);
 
  413bool InsertRoutePointHTML(sqlite3* db, 
const std::string& point_guid,
 
  414                          const std::string& link_guid,
 
  415                          const std::string& descrText, 
const std::string& link,
 
  416                          const std::string& ltype) {
 
  417  const char* sql = R
"( 
  418        INSERT INTO routepoint_html_links (guid, routepoint_guid, html_link, html_description, html_type) 
  419        VALUES (?, ?, ?, ?, ?) 
  423  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  424    sqlite3_bind_text(stmt, 1, link_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  425    sqlite3_bind_text(stmt, 2, point_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  426    sqlite3_bind_text(stmt, 3, link.c_str(), -1, SQLITE_TRANSIENT);
 
  427    sqlite3_bind_text(stmt, 4, descrText.c_str(), -1, SQLITE_TRANSIENT);
 
  428    sqlite3_bind_text(stmt, 5, ltype.c_str(), -1, SQLITE_TRANSIENT);
 
  429    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  430      ReportError(
"InsertRoutePointHTML:step");
 
  431      sqlite3_finalize(stmt);
 
  434    sqlite3_finalize(stmt);
 
  440bool DeleteAllCommentsForRoutePoint(sqlite3* db,
 
  441                                    const std::string& routepoint_guid) {
 
  443      "DELETE FROM routepoint_html_links WHERE routepoint_guid = ?";
 
  446  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  447    sqlite3_bind_text(stmt, 1, routepoint_guid.c_str(), -1, SQLITE_TRANSIENT);
 
  448    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  449      ReportError(
"DeleteAllCommentsForRoutepoint:step");
 
  453    sqlite3_finalize(stmt);
 
  455    ReportError(
"DeleteAllCommentsForRoutepoint:prepare");
 
  461bool InsertRoutePointDB(sqlite3* db, 
RoutePoint* point) {
 
  462  const char* sql = R
"( 
  463        INSERT or REPLACE INTO routepoints(guid) 
  468  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  469    sqlite3_bind_text(stmt, 1, point->
m_GUID.ToStdString().c_str(), -1,
 
  471    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  472      ReportError(
"InsertRoutePointDB:step");
 
  473      sqlite3_finalize(stmt);
 
  476    sqlite3_finalize(stmt);
 
  485  const char* sql = R
"( 
  486        INSERT or IGNORE INTO routepoints_link (route_guid, point_guid, point_order) 
  492  if (sqlite3_prepare_v2(db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  493    sqlite3_bind_text(stmt, 1, route->
m_GUID.ToStdString().c_str(), -1,
 
  495    sqlite3_bind_text(stmt, 2, point->
m_GUID.ToStdString().c_str(), -1,
 
  497    sqlite3_bind_int(stmt, 3, point_order);
 
  498    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  499      ReportError(
"InsertTrackPointLink:step");
 
  500      sqlite3_finalize(stmt);
 
  503    sqlite3_finalize(stmt);
 
  510void DeleteOrphanedRoutepoint(sqlite3* db) {
 
  511  const char* sql = R
"( 
  512        DELETE FROM routepoints 
  513        WHERE guid NOT IN (SELECT point_guid FROM routepoints_link) 
  515  char* errMsg = 
nullptr;
 
  517  if (sqlite3_exec(db, sql, 
nullptr, 
nullptr, &errMsg) != SQLITE_OK) {
 
  522void errorLogCallback(
void* pArg, 
int iErrCode, 
const char* zMsg) {
 
  524      wxString::Format(_(
"navobj database error.") + 
" %d: %s", iErrCode, zMsg);
 
  526  auto& noteman = NotificationManager::GetInstance();
 
  527  noteman.AddNotification(NotificationSeverity::kWarning, msg.ToStdString());
 
  530static void ReportError(
const std::string zmsg) {
 
  532      wxString::Format(_(
"navobj database error.") + 
" %s", zmsg.c_str());
 
  534  auto& noteman = NotificationManager::GetInstance();
 
  535  noteman.AddNotification(NotificationSeverity::kWarning, msg.ToStdString());
 
  543NavObj_dB::NavObj_dB() {
 
  544  m_pImportProgress = 
nullptr;
 
  547  int ie = sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, 
nullptr);
 
  551                         wxFileName::GetPathSeparator() + 
"navobj.db";
 
  552  if (!wxFileExists(db_filename)) {
 
  555                            wxFileName::GetPathSeparator() + 
"navobj.xml";
 
  556    if (wxFileExists(xml_filename)) {
 
  557      wxCopyFile(xml_filename, xml_filename + 
".backup");
 
  561                                      wxFileName::GetPathSeparator() +
 
  562                                      "navobj.xml.import_backup";
 
  563      if (!wxFileExists(deep_backup_filename)) {
 
  564        wxCopyFile(xml_filename, deep_backup_filename);
 
  570    int create_result = sqlite3_open_v2(
 
  571        db_filename.ToStdString().c_str(),
 
  573        SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,  
 
  576    if (create_result != SQLITE_OK) {
 
  577      wxLogMessage(
"Cannot create new navobj.db database file");
 
  586    int close_result = sqlite3_close_v2(m_db);
 
  587    if (close_result != SQLITE_OK) {
 
  593  int m_open_result = sqlite3_open_v2(db_filename.ToStdString().c_str(), &m_db,
 
  594                                      SQLITE_OPEN_READWRITE, NULL);
 
  595  sqlite3_exec(m_db, 
"PRAGMA foreign_keys = ON;", 
nullptr, 
nullptr, 
nullptr);
 
  599  sqlite3_close_v2(m_db);
 
  601  m_open_result = sqlite3_open_v2(db_filename.ToStdString().c_str(), &m_db,
 
  602                                  SQLITE_OPEN_READWRITE, NULL);
 
  603  sqlite3_exec(m_db, 
"PRAGMA foreign_keys = ON;", 
nullptr, 
nullptr, 
nullptr);
 
  609NavObj_dB::~NavObj_dB() { sqlite3_close_v2(m_db); }
 
  611void NavObj_dB::Close() {
 
  612  sqlite3_close_v2(m_db);
 
  616bool NavObj_dB::ImportLegacyNavobj(wxFrame* frame) {
 
  618                             wxFileName::GetPathSeparator() + 
"navobj.xml";
 
  620  if (::wxFileExists(navobj_filename)) {
 
  622    CountImportNavObjects();
 
  623    m_pImportProgress = 
new wxProgressDialog(_(
"Importing Navobj database"), 
"",
 
  624                                             m_nImportObjects, frame);
 
  625    m_import_progesscount = 0;
 
  627    rv = ImportLegacyPoints();
 
  628    rv |= ImportLegacyRoutes();
 
  629    rv |= ImportLegacyTracks();
 
  631    m_pImportProgress->Destroy();
 
  635  if (::wxFileExists(navobj_filename)) ::wxRemoveFile(navobj_filename);
 
  640void NavObj_dB::CountImportNavObjects() {
 
  641  m_nImportObjects = 0;
 
  648                             wxFileName::GetPathSeparator() + 
"navobj.xml";
 
  650  if (::wxFileExists(navobj_filename) &&
 
  651      input_set->load_file(navobj_filename.ToStdString().c_str()).status ==
 
  652          pugi::xml_parse_status::status_ok) {
 
  653    input_set->LoadAllGPXPointObjects();
 
  654    auto pointlist = pWayPointMan->GetWaypointList();
 
  662    input_set->LoadAllGPXRouteObjects();
 
  666      m_nImportObjects += route_import->GetnPoints();
 
  669    input_set->LoadAllGPXTrackObjects();
 
  674      m_nImportObjects += track_import->GetnPoints();
 
  680bool NavObj_dB::ImportLegacyTracks() {
 
  681  std::vector<Track*> tracks_added;
 
  685    if (InsertTrack(track_import)) {
 
  686      tracks_added.push_back(track_import);
 
  689    m_import_progesscount += track_import->GetnPoints() + 1;
 
  690    wxString msg = wxString::Format(
"Tracks  %d/%d", ntrack, m_nimportTracks);
 
  691    m_pImportProgress->Update(m_import_progesscount, msg);
 
  692    m_pImportProgress->Show();
 
  696  for (
Track* ptrack : tracks_added) {
 
  697    if (ptrack->m_bIsInLayer) 
continue;
 
  704bool NavObj_dB::ImportLegacyRoutes() {
 
  705  std::vector<Route*> routes_added;
 
  709    if (InsertRoute(route_import)) {
 
  710      routes_added.push_back(route_import);
 
  713    m_import_progesscount += route_import->GetnPoints() + 1;
 
  714    wxString msg = wxString::Format(
"Routes  %d/%d", nroute, m_nimportRoutes);
 
  715    m_pImportProgress->Update(m_import_progesscount, msg);
 
  716    m_pImportProgress->Show();
 
  720  for (
Route* route : routes_added) {
 
  726  pWayPointMan->DeleteAllWaypoints(
true);
 
  731bool NavObj_dB::ImportLegacyPoints() {
 
  732  std::vector<RoutePoint*> points_added;
 
  736  if (m_nimportPoints > 1000) nmod = 10;
 
  737  if (m_nimportPoints > 10000) nmod = 100;
 
  739  for (
RoutePoint* point : *pWayPointMan->GetWaypointList()) {
 
  741      if (InsertRoutePointDB(m_db, point)) {
 
  742        points_added.push_back(point);
 
  745      UpdateDBRoutePointAttributes(point);
 
  746      m_import_progesscount += 1;
 
  747      if ((npoint % nmod) == 0) {
 
  749            wxString::Format(
"Points  %d/%d", npoint, m_nimportPoints);
 
  750        m_pImportProgress->Update(m_import_progesscount, msg);
 
  751        m_pImportProgress->Show();
 
  766void NavObj_dB::LoadNavObjects() {
 
  772bool NavObj_dB::InsertTrack(
Track* track) {
 
  773  if (TrackExists(m_db, track->m_GUID.ToStdString())) 
return false;
 
  777  sqlite3_exec(m_db, 
"BEGIN TRANSACTION", 0, 0, &errMsg);
 
  779    ReportError(
"InsertTrack:BEGIN TRANSACTION");
 
  784  wxString sql = wxString::Format(
"INSERT INTO tracks (guid) VALUES ('%s')",
 
  785                                  track->m_GUID.ToStdString().c_str());
 
  786  if (!executeSQL(m_db, sql)) {
 
  787    sqlite3_exec(m_db, 
"COMMIT", 0, 0, &errMsg);
 
  791  UpdateDBTrackAttributes(track);
 
  794  for (
int i = 0; i < track->GetnPoints(); i++) {
 
  795    auto point = track->GetPoint(i);
 
  797    InsertTrackPoint(m_db, track->m_GUID.ToStdString(), point->m_lat,
 
  798                     point->m_lon, point->GetTimeString(), i);
 
  802  int NbrOfLinks = track->m_TrackHyperlinkList->size();
 
  803  if (NbrOfLinks > 0) {
 
  804    auto& list = track->m_TrackHyperlinkList;
 
  805    for (
auto it = list->begin(); it != list->end(); ++it) {
 
  807      if (!TrackHtmlLinkExists(m_db, link->GUID)) {
 
  808        InsertTrackHTML(m_db, track->m_GUID.ToStdString(), link->GUID,
 
  809                        link->DescrText.ToStdString(), link->Link.ToStdString(),
 
  810                        link->LType.ToStdString());
 
  814  sqlite3_exec(m_db, 
"COMMIT", 0, 0, &errMsg);
 
  816  if (errMsg) rv = 
false;
 
  821bool NavObj_dB::UpdateTrack(
Track* track) {
 
  825  if (!TrackExists(m_db, track->m_GUID.ToStdString())) 
return false;
 
  827  sqlite3_exec(m_db, 
"BEGIN TRANSACTION", 0, 0, &errMsg);
 
  829    ReportError(
"UpdateTrack:BEGIN TRANSACTION");
 
  833  UpdateDBTrackAttributes(track);
 
  836  const char* sql = 
"DELETE FROM trk_points WHERE track_guid = ?";
 
  838  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  839    sqlite3_bind_text(stmt, 1, track->m_GUID.ToStdString().c_str(), -1,
 
  842    ReportError(
"UpdateTrack:prepare");
 
  843    sqlite3_exec(m_db, 
"COMMIT", 0, 0, &errMsg);
 
  846  if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  847    ReportError(
"UpdateTrack:step");
 
  848    sqlite3_finalize(stmt);
 
  849    sqlite3_exec(m_db, 
"COMMIT", 0, 0, &errMsg);
 
  852  sqlite3_finalize(stmt);
 
  855  for (
int i = 0; i < track->GetnPoints(); i++) {
 
  856    auto point = track->GetPoint(i);
 
  859      InsertTrackPoint(m_db, track->m_GUID.ToStdString(), point->m_lat,
 
  860                       point->m_lon, point->GetTimeString(), i);
 
  864  sqlite3_exec(m_db, 
"COMMIT", 0, 0, 
nullptr);
 
  867  if (errMsg) rv = 
false;
 
  871bool NavObj_dB::UpdateDBTrackAttributes(
Track* track) {
 
  885  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  886    sqlite3_bind_text(stmt, 1, track->GetName().ToStdString().c_str(), -1,
 
  888    sqlite3_bind_text(stmt, 2, track->m_TrackDescription.ToStdString().c_str(),
 
  889                      -1, SQLITE_TRANSIENT);
 
  890    sqlite3_bind_int(stmt, 3, track->m_bVisible);
 
  891    sqlite3_bind_text(stmt, 4, track->m_TrackStartString.ToStdString().c_str(),
 
  892                      -1, SQLITE_TRANSIENT);
 
  893    sqlite3_bind_text(stmt, 5, track->m_TrackEndString.ToStdString().c_str(),
 
  894                      -1, SQLITE_TRANSIENT);
 
  895    sqlite3_bind_int(stmt, 6, track->m_width);
 
  896    sqlite3_bind_int(stmt, 7,
 
  897                     (
int)(track->m_style));  
 
  898    sqlite3_bind_text(stmt, 8, track->m_Colour.ToStdString().c_str(), -1,
 
  900    sqlite3_bind_text(stmt, 9, track->m_GUID.c_str(), track->m_GUID.size(),
 
  906  if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  907    ReportError(
"UpdateDBTrackAttributesA:step");
 
  908    sqlite3_finalize(stmt);
 
  912  sqlite3_finalize(stmt);
 
  917  DeleteAllCommentsForTrack(m_db, track->m_GUID.ToStdString());
 
  920  int NbrOfLinks = track->m_TrackHyperlinkList->size();
 
  921  if (NbrOfLinks > 0) {
 
  922    auto& list = track->m_TrackHyperlinkList;
 
  923    for (
auto it = list->begin(); it != list->end(); ++it) {
 
  926      if (!TrackHtmlLinkExists(m_db, link->GUID)) {
 
  927        InsertTrackHTML(m_db, track->m_GUID.ToStdString(), link->GUID,
 
  928                        link->DescrText.ToStdString(), link->Link.ToStdString(),
 
  929                        link->LType.ToStdString());
 
  932            "UPDATE track_html_links SET " 
  934            "html_description = ?, " 
  938        if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
  939          sqlite3_bind_text(stmt, 3, link->Link.ToStdString().c_str(), -1,
 
  941          sqlite3_bind_text(stmt, 4, link->DescrText.ToStdString().c_str(), -1,
 
  943          sqlite3_bind_text(stmt, 5, link->LType.ToStdString().c_str(), -1,
 
  946        if (sqlite3_step(stmt) != SQLITE_DONE) {
 
  947          ReportError(
"UpdateDBTRackAttributesB:step");
 
  948          sqlite3_finalize(stmt);
 
  951        sqlite3_finalize(stmt);
 
  961  if (!TrackExists(m_db, track->m_GUID.ToStdString())) 
return false;
 
  964  int this_point_index = track->GetnPoints();
 
  967  if (!InsertTrackPoint(m_db, track->m_GUID.ToStdString(), point->m_lat,
 
  968                        point->m_lon, point->GetTimeString(),
 
  969                        this_point_index - 1))
 
  975bool NavObj_dB::LoadAllTracks() {
 
  976  const char* sql = R
"( 
  978        description, visibility, start_string, end_string, 
  982        ORDER BY created_at ASC 
  986  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) != SQLITE_OK) {
 
  990  while (sqlite3_step(stmt) == SQLITE_ROW) {
 
  992        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
 
  994        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
 
  995    std::string description =
 
  996        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 2));
 
  997    int visibility = sqlite3_column_int(stmt, 3);
 
  998    std::string start_string =
 
  999        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 4));
 
 1000    std::string end_string =
 
 1001        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 5));
 
 1002    int width = sqlite3_column_int(stmt, 6);
 
 1003    int style = sqlite3_column_int(stmt, 7);
 
 1005        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 8));
 
 1006    std::string created =
 
 1007        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 9));
 
 1009    Track* new_trk = NULL;
 
 1012    const char* sql = R
"( 
 1013        SELECT  latitude, longitude, timestamp, point_order 
 1015        WHERE track_guid = ? 
 1016        ORDER BY point_order ASC 
 1019    sqlite3_stmt* stmtp; 
 1020    if (sqlite3_prepare_v2(m_db, sql, -1, &stmtp, 
nullptr) != SQLITE_OK) {
 
 1024    sqlite3_bind_text(stmtp, 1, guid.c_str(), -1, SQLITE_TRANSIENT);
 
 1027    while (sqlite3_step(stmtp) == SQLITE_ROW) {
 
 1029        new_trk = 
new Track;
 
 1030        new_trk->m_GUID = guid;
 
 1033        new_trk->SetVisible(visibility == 1);
 
 1034        new_trk->SetName(name.c_str());
 
 1035        new_trk->m_TrackStartString = start_string.c_str();
 
 1036        new_trk->m_TrackEndString = end_string.c_str();
 
 1037        new_trk->m_width = width;
 
 1038        new_trk->m_style = (wxPenStyle)style;
 
 1039        new_trk->m_Colour = color;
 
 1042      double latitude = sqlite3_column_double(stmtp, 0);
 
 1043      double longitude = sqlite3_column_double(stmtp, 1);
 
 1044      std::string timestamp =
 
 1045          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, 2));
 
 1046      int point_order = sqlite3_column_int(stmtp, 3);
 
 1048      auto point = 
new TrackPoint(latitude, longitude, timestamp);
 
 1050      point->m_GPXTrkSegNo = GPXTrkSeg;
 
 1051      new_trk->AddPoint(point);
 
 1053    sqlite3_finalize(stmtp);
 
 1056      new_trk->SetCurrentTrackSeg(GPXTrkSeg);
 
 1059      const char* sqlh = R
"( 
 1060        SELECT guid, html_link, html_description, html_type 
 1061        FROM track_html_links 
 1062        WHERE track_guid = ? 
 1063        ORDER BY html_type ASC 
 1068      if (sqlite3_prepare_v2(m_db, sqlh, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1069        sqlite3_bind_text(stmt, 1, new_trk->m_GUID.ToStdString().c_str(), -1,
 
 1072        while (sqlite3_step(stmt) == SQLITE_ROW) {
 
 1073          std::string link_guid =
 
 1074              reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
 
 1075          std::string link_link =
 
 1076              reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
 
 1077          std::string link_description =
 
 1078              reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 2));
 
 1079          std::string link_type =
 
 1080              reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 3));
 
 1083          h->DescrText = link_description;
 
 1084          h->Link = link_link;
 
 1085          h->LType = link_type;
 
 1087          new_trk->m_TrackHyperlinkList->push_back(h);
 
 1091        sqlite3_finalize(stmt);
 
 1100      pSelect->AddAllSelectableTrackSegments(new_trk);
 
 1106bool NavObj_dB::DeleteTrack(
Track* track) {
 
 1107  if (!track) 
return false;
 
 1108  std::string track_guid = track->m_GUID.ToStdString();
 
 1109  const char* sql = 
"DELETE FROM tracks WHERE guid = ?";
 
 1112  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1113    sqlite3_bind_text(stmt, 1, track_guid.c_str(), -1, SQLITE_TRANSIENT);
 
 1114    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1115      ReportError(
"DeleteTrack:step");
 
 1116      sqlite3_finalize(stmt);
 
 1120    sqlite3_finalize(stmt);
 
 1129bool NavObj_dB::InsertRoute(
Route* route) {
 
 1133  if (!RouteExistsDB(m_db, route->
m_GUID.ToStdString())) {
 
 1135    wxString sql = wxString::Format(
"INSERT INTO routes (guid) VALUES ('%s')",
 
 1136                                    route->
m_GUID.ToStdString().c_str());
 
 1137    if (!executeSQL(m_db, sql)) {
 
 1140    UpdateDBRouteAttributes(route);
 
 1143  sqlite3_exec(m_db, 
"BEGIN TRANSACTION", 0, 0, &errMsg);
 
 1145    ReportError(
"InsertRoute:BEGIN TRANSACTION");
 
 1150  for (
int i = 0; i < route->GetnPoints(); i++) {
 
 1151    auto point = route->GetPoint(i + 1);
 
 1154      if (!RoutePointExists(m_db, point->m_GUID.ToStdString())) {
 
 1155        InsertRoutePointDB(m_db, point);
 
 1156        UpdateDBRoutePointAttributes(point);
 
 1162  for (
int i = 0; i < route->GetnPoints(); i++) {
 
 1163    auto point = route->GetPoint(i + 1);
 
 1166      InsertRoutePointLink(m_db, route, point, i + 1);
 
 1172  if (NbrOfLinks > 0) {
 
 1174    for (
auto it = list.begin(); it != list.end(); ++it) {
 
 1176      if (!RouteHtmlLinkExists(m_db, link->GUID)) {
 
 1177        InsertRouteHTML(m_db, route->
m_GUID.ToStdString(), link->GUID,
 
 1178                        link->DescrText.ToStdString(), link->Link.ToStdString(),
 
 1179                        link->LType.ToStdString());
 
 1184  sqlite3_exec(m_db, 
"COMMIT", 0, 0, &errMsg);
 
 1187    ReportError(
"InsertRoute:commit");
 
 1193bool NavObj_dB::UpdateRoute(
Route* route) {
 
 1197  if (!RouteExistsDB(m_db, route->
m_GUID.ToStdString())) 
return false;
 
 1199  sqlite3_exec(m_db, 
"BEGIN TRANSACTION", 0, 0, &errMsg);
 
 1201    ReportError(
"UpdateRoute:BEGIN TRANSACTION");
 
 1205  UpdateDBRouteAttributes(route);
 
 1208  for (
int i = 0; i < route->GetnPoints(); i++) {
 
 1209    auto point = route->GetPoint(i + 1);
 
 1212      if (!RoutePointExists(m_db, point->m_GUID.ToStdString())) {
 
 1213        InsertRoutePointDB(m_db, point);
 
 1215      UpdateDBRoutePointAttributes(point);
 
 1220  const char* sql = 
"DELETE FROM routepoints_link WHERE route_guid = ?";
 
 1222  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1223    sqlite3_bind_text(stmt, 1, route->
m_GUID.ToStdString().c_str(), -1,
 
 1226    sqlite3_exec(m_db, 
"COMMIT", 0, 0, &errMsg);
 
 1229  if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1230    ReportError(
"UpdateRoute:step");
 
 1231    sqlite3_finalize(stmt);
 
 1232    sqlite3_exec(m_db, 
"COMMIT", 0, 0, &errMsg);
 
 1236  sqlite3_finalize(stmt);
 
 1238  for (
int i = 0; i < route->GetnPoints(); i++) {
 
 1239    auto point = route->GetPoint(i + 1);
 
 1241      InsertRoutePointLink(m_db, route, point, i + 1);
 
 1247  if (NbrOfLinks > 0) {
 
 1249    for (
auto it = list.begin(); it != list.end(); ++it) {
 
 1251      if (!RouteHtmlLinkExists(m_db, link->GUID)) {
 
 1252        InsertRouteHTML(m_db, route->
m_GUID.ToStdString(), link->GUID,
 
 1253                        link->DescrText.ToStdString(), link->Link.ToStdString(),
 
 1254                        link->LType.ToStdString());
 
 1258  sqlite3_exec(m_db, 
"COMMIT", 0, 0, 
nullptr);
 
 1261  if (errMsg) rv = 
false;
 
 1266bool NavObj_dB::UpdateRouteViz(
Route* route) {
 
 1269  if (!RouteExistsDB(m_db, route->
m_GUID.ToStdString())) 
return false;
 
 1271  UpdateDBRouteAttributes(route);
 
 1273  for (
int i = 0; i < route->GetnPoints(); i++) {
 
 1274    auto point = route->GetPoint(i + 1);
 
 1277      UpdateDBRoutePointViz(point);
 
 1281  if (errMsg) rv = 
false;
 
 1286bool NavObj_dB::UpdateDBRouteAttributes(
Route* route) {
 
 1288      "UPDATE routes SET " 
 1291      "start_string = ?, " 
 1294      "shared_wp_viz = ?, " 
 1295      "planned_departure = ?, " 
 1304  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1305    sqlite3_bind_text(stmt, 1, route->GetName().ToStdString().c_str(), -1,
 
 1308                      -1, SQLITE_TRANSIENT);
 
 1310                      -1, SQLITE_TRANSIENT);
 
 1312                      -1, SQLITE_TRANSIENT);
 
 1313    sqlite3_bind_int(stmt, 5, route->IsVisible());
 
 1314    sqlite3_bind_int(stmt, 6, route->GetSharedWPViz());
 
 1319                      -1, SQLITE_TRANSIENT);
 
 1320    sqlite3_bind_int(stmt, 10, route->
m_width);
 
 1321    sqlite3_bind_int(stmt, 11,
 
 1323    sqlite3_bind_text(stmt, 12, route->
m_Colour.ToStdString().c_str(), -1,
 
 1325    sqlite3_bind_text(stmt, 13, route->
m_GUID.c_str(), route->
m_GUID.size(),
 
 1331  if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1332    ReportError(
"UpdateDBRouteAttributesA:step");
 
 1333    sqlite3_finalize(stmt);
 
 1337  sqlite3_finalize(stmt);
 
 1342  DeleteAllCommentsForRoute(m_db, route->
m_GUID.ToStdString());
 
 1346  if (NbrOfLinks > 0) {
 
 1348    for (
auto it = list->begin(); it != list->end(); ++it) {
 
 1350      if (!RouteHtmlLinkExists(m_db, link->GUID)) {
 
 1351        InsertRouteHTML(m_db, route->
m_GUID.ToStdString(), link->GUID,
 
 1352                        link->DescrText.ToStdString(), link->Link.ToStdString(),
 
 1353                        link->LType.ToStdString());
 
 1356            "UPDATE route_html_links SET " 
 1358            "html_description = ?, " 
 1362        if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1363          sqlite3_bind_text(stmt, 3, link->Link.ToStdString().c_str(), -1,
 
 1365          sqlite3_bind_text(stmt, 4, link->DescrText.ToStdString().c_str(), -1,
 
 1367          sqlite3_bind_text(stmt, 5, link->LType.ToStdString().c_str(), -1,
 
 1370        if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1373        if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1374          ReportError(
"UpdateDBRouteAttributesB:step");
 
 1375          sqlite3_finalize(stmt);
 
 1378        sqlite3_finalize(stmt);
 
 1385bool NavObj_dB::UpdateDBRoutePointAttributes(
RoutePoint* point) {
 
 1387      "UPDATE routepoints SET " 
 1398      "ArrivalRadius = ?, " 
 1399      "RangeRingsNumber = ?, " 
 1400      "RangeRingsStep = ?, " 
 1401      "RangeRingsStepUnits = ?, " 
 1402      "RangeRingsVisible = ?, " 
 1403      "RangeRingsColour = ?, " 
 1414  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1415    sqlite3_bind_double(stmt, 1, point->GetLatitude());
 
 1416    sqlite3_bind_double(stmt, 2, point->GetLongitude());
 
 1417    sqlite3_bind_text(stmt, 3, point->GetIconName().ToStdString().c_str(), -1,
 
 1419    sqlite3_bind_text(stmt, 4, point->GetName().ToStdString().c_str(), -1,
 
 1421    sqlite3_bind_text(stmt, 5, point->GetDescription().ToStdString().c_str(),
 
 1422                      -1, SQLITE_TRANSIENT);
 
 1423    sqlite3_bind_text(stmt, 6, point->
m_TideStation.ToStdString().c_str(), -1,
 
 1428    sqlite3_bind_int(stmt, 8, etd);
 
 1429    sqlite3_bind_text(stmt, 9, 
"type", -1, SQLITE_TRANSIENT);
 
 1430    std::string timit = point->
m_timestring.ToStdString().c_str();
 
 1431    sqlite3_bind_text(stmt, 10, point->
m_timestring.ToStdString().c_str(), -1,
 
 1444        -1, SQLITE_TRANSIENT);
 
 1446    sqlite3_bind_int(stmt, 17, point->GetScaMin());
 
 1447    sqlite3_bind_int(stmt, 18, point->GetScaMax());
 
 1448    sqlite3_bind_int(stmt, 19, point->GetUseSca());
 
 1450    sqlite3_bind_int(stmt, 20, point->IsVisible());
 
 1451    sqlite3_bind_int(stmt, 21, point->IsNameShown());
 
 1452    sqlite3_bind_int(stmt, 22, point->IsShared());
 
 1454    sqlite3_bind_int(stmt, 23, iso);  
 
 1456    sqlite3_bind_text(stmt, 24, point->
m_GUID.ToStdString().c_str(), -1,
 
 1463  if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1464    ReportError(
"UpdateDBRoutePointAttributesA:step");
 
 1465    sqlite3_finalize(stmt);
 
 1469  sqlite3_finalize(stmt);
 
 1474  DeleteAllCommentsForRoutePoint(m_db, point->
m_GUID.ToStdString());
 
 1478  if (NbrOfLinks > 0) {
 
 1480    for (
auto it = list->begin(); it != list->end(); ++it) {
 
 1482      if (!RoutePointHtmlLinkExists(m_db, link->GUID)) {
 
 1483        InsertRoutePointHTML(m_db, point->
m_GUID.ToStdString(), link->GUID,
 
 1484                             link->DescrText.ToStdString(),
 
 1485                             link->Link.ToStdString(),
 
 1486                             link->LType.ToStdString());
 
 1489            "UPDATE routepoint_html_links SET " 
 1491            "html_description = ?, " 
 1495        if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1496          sqlite3_bind_text(stmt, 3, link->Link.ToStdString().c_str(), -1,
 
 1498          sqlite3_bind_text(stmt, 4, link->DescrText.ToStdString().c_str(), -1,
 
 1500          sqlite3_bind_text(stmt, 5, link->LType.ToStdString().c_str(), -1,
 
 1503        if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1506        if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1507          ReportError(
"UpdateDBRoutePointAttributesB:step-h");
 
 1508          sqlite3_finalize(stmt);
 
 1511        sqlite3_finalize(stmt);
 
 1519bool NavObj_dB::UpdateDBRoutePointViz(
RoutePoint* point) {
 
 1521      "UPDATE routepoints SET " 
 1526  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1527    sqlite3_bind_int(stmt, 1, point->IsVisible());
 
 1528    sqlite3_bind_text(stmt, 2, point->
m_GUID.ToStdString().c_str(), -1,
 
 1535  if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1536    ReportError(
"UpdateDBRoutePointVizA:step");
 
 1537    sqlite3_finalize(stmt);
 
 1541  sqlite3_finalize(stmt);
 
 1546bool NavObj_dB::DeleteRoute(
Route* route) {
 
 1547  if (m_importing) 
return false;
 
 1548  if (!route) 
return false;
 
 1549  std::string route_guid = route->
m_GUID.ToStdString();
 
 1550  const char* sql = 
"DELETE FROM routes WHERE guid = ?";
 
 1553  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1554    sqlite3_bind_text(stmt, 1, route_guid.c_str(), -1, SQLITE_TRANSIENT);
 
 1555    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 1556      ReportError(
"DeleteRoute:step");
 
 1557      sqlite3_finalize(stmt);
 
 1560    sqlite3_finalize(stmt);
 
 1567bool NavObj_dB::LoadAllRoutes() {
 
 1577      "planned_departure, " 
 1584      "ORDER BY created_at ASC";
 
 1587  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) != SQLITE_OK) {
 
 1591  int errcode0 = SQLITE_OK;
 
 1592  while ((errcode0 = sqlite3_step(stmt)) == SQLITE_ROW) {
 
 1594        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
 
 1596        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
 
 1597    std::string description =
 
 1598        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 2));
 
 1599    std::string start_string =
 
 1600        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 3));
 
 1601    std::string end_string =
 
 1602        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 4));
 
 1603    int visibility = sqlite3_column_int(stmt, 5);
 
 1604    int sharewp_viz = sqlite3_column_int(stmt, 6);
 
 1605    time_t planned_departure_ticks = sqlite3_column_int(stmt, 7);
 
 1606    double plan_speed = sqlite3_column_double(stmt, 8);
 
 1607    std::string time_format =
 
 1608        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 9));
 
 1610    int width = sqlite3_column_int(stmt, 10);
 
 1611    int style = sqlite3_column_int(stmt, 11);
 
 1613        reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 12));
 
 1615    Route* route = NULL;
 
 1618    const char* sql = R
"( 
 1619        SELECT  latitude, longitude, timestamp, point_order 
 1621        WHERE track_guid = ? 
 1622        ORDER BY point_order ASC 
 1638        "p.RangeRingsNumber, " 
 1639        "p.RangeRingsStep, " 
 1640        "p.RangeRingsStepUnits, " 
 1641        "p.RangeRingsVisible, " 
 1642        "p.RangeRingsColour, " 
 1651        "FROM routepoints_link tp " 
 1652        "JOIN routepoints p ON p.guid = tp.point_guid " 
 1653        "WHERE tp.route_guid = ? " 
 1654        "ORDER BY tp.point_order ASC";
 
 1656    sqlite3_stmt* stmtp;
 
 1657    if (sqlite3_prepare_v2(m_db, sqlp, -1, &stmtp, 
nullptr) != SQLITE_OK) {
 
 1658      ReportError(
"LoadAllRoutes-B:prepare");
 
 1662    sqlite3_bind_text(stmtp, 1, guid.c_str(), -1, SQLITE_TRANSIENT);
 
 1665    int errcode = SQLITE_OK;
 
 1666    while ((errcode = sqlite3_step(stmtp)) == SQLITE_ROW) {
 
 1672        route->SetVisible(visibility == 1);
 
 1677        route->SetVisible(visibility == 1);
 
 1678        route->SetSharedWPViz(sharewp_viz == 1);
 
 1684        route->
m_style = (wxPenStyle)style;
 
 1690      std::string point_guid =
 
 1691          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1692      double latitude = sqlite3_column_double(stmtp, col++);
 
 1693      double longitude = sqlite3_column_double(stmtp, col++);
 
 1694      std::string symbol =
 
 1695          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1697          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1698      std::string description =
 
 1699          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1700      std::string tide_station =
 
 1701          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1702      double plan_speed = sqlite3_column_double(stmtp, col++);
 
 1703      time_t etd_epoch = sqlite3_column_int(stmtp, col++);
 
 1705          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1707          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1708      double arrival_radius = sqlite3_column_double(stmtp, col++);
 
 1710      int range_ring_number = sqlite3_column_int(stmtp, col++);
 
 1711      double range_ring_step = sqlite3_column_double(stmtp, col++);
 
 1712      int range_ring_units = sqlite3_column_int(stmtp, col++);
 
 1713      int range_ring_visible = sqlite3_column_int(stmtp, col++);
 
 1714      std::string range_ring_color =
 
 1715          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1717      int scamin = sqlite3_column_int(stmtp, col++);
 
 1718      int scamax = sqlite3_column_int(stmtp, col++);
 
 1719      int use_scaminmax = sqlite3_column_int(stmtp, col++);
 
 1721      int visibility = sqlite3_column_int(stmtp, col++);
 
 1722      int viz_name = sqlite3_column_int(stmtp, col++);
 
 1723      int shared = sqlite3_column_int(stmtp, col++);
 
 1724      int isolated = sqlite3_column_int(stmtp, col++);
 
 1725      std::string point_created_at =
 
 1726          reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1731      auto containing_route =
 
 1732          g_pRouteMan->FindRouteContainingWaypoint(point_guid);
 
 1734      if (containing_route) {  
 
 1735        existing_point = containing_route->GetPoint(point_guid);
 
 1738      if (!existing_point) {
 
 1739        existing_point = pWayPointMan->FindRoutePointByGUID(point_guid.c_str());
 
 1742      if (existing_point) {
 
 1743        point = existing_point;
 
 1744        point->SetShared(
true);  
 
 1748            new RoutePoint(latitude, longitude, symbol, name, point_guid, 
true);
 
 1752        point->SetPlannedSpeed(plan_speed);
 
 1755        etd.Set((time_t)etd_epoch);
 
 1756        if (etd.IsValid()) point->
SetETD(etd);
 
 1763        point->SetShowWaypointRangeRings(range_ring_visible == 1);
 
 1767        point->SetScaMin(scamin);
 
 1768        point->SetScaMax(scamax);
 
 1769        point->SetUseSca(use_scaminmax == 1);
 
 1771        point->SetVisible(visibility == 1);
 
 1772        point->SetNameShown(viz_name == 1);
 
 1773        point->SetShared(shared == 1);
 
 1776        if (point_created_at.size()) {
 
 1780          std::istringstream ss(point_created_at);
 
 1781          ss >> std::get_time(&tm, 
"%Y-%m-%d %H:%M:%S");
 
 1782          time_t epoch_time = mktime(&tm);
 
 1787        const char* sqlh = R
"( 
 1788        SELECT guid, html_link, html_description, html_type 
 1789        FROM routepoint_html_links 
 1790        WHERE routepoint_guid = ? 
 1791        ORDER BY html_type ASC 
 1796        if (sqlite3_prepare_v2(m_db, sqlh, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1797          sqlite3_bind_text(stmt, 1, point->
m_GUID.ToStdString().c_str(), -1,
 
 1800          while (sqlite3_step(stmt) == SQLITE_ROW) {
 
 1801            std::string link_guid =
 
 1802                reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
 
 1803            std::string link_link =
 
 1804                reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
 
 1805            std::string link_description =
 
 1806                reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 2));
 
 1807            std::string link_type =
 
 1808                reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 3));
 
 1811            h->DescrText = link_description;
 
 1812            h->Link = link_link;
 
 1813            h->LType = link_type;
 
 1820      route->AddPoint(point);
 
 1822    sqlite3_finalize(stmtp);
 
 1823    if (errcode != SQLITE_DONE) {
 
 1824      ReportError(
"LoadAllRoutes-A:step");
 
 1831      const char* sqlh = R
"( 
 1832        SELECT guid, html_link, html_description, html_type 
 1833        FROM route_html_links 
 1834        WHERE route_guid = ? 
 1835        ORDER BY html_type ASC 
 1840      if (sqlite3_prepare_v2(m_db, sqlh, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 1841        sqlite3_bind_text(stmt, 1, route->
m_GUID.ToStdString().c_str(), -1,
 
 1844        int errcode2 = SQLITE_OK;
 
 1845        while ((errcode2 = sqlite3_step(stmt)) == SQLITE_ROW) {
 
 1846          std::string link_guid =
 
 1847              reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
 
 1848          std::string link_link =
 
 1849              reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
 
 1850          std::string link_description =
 
 1851              reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 2));
 
 1852          std::string link_type =
 
 1853              reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 3));
 
 1856          h->DescrText = link_description;
 
 1857          h->Link = link_link;
 
 1858          h->LType = link_type;
 
 1862        if (errcode != SQLITE_DONE) {
 
 1863          ReportError(
"LoadAllRoutes-B:step");
 
 1867        sqlite3_finalize(stmt);
 
 1870        ReportError(
"LoadAllRoutes-B:prepare");
 
 1881  if (errcode0 != SQLITE_DONE) {
 
 1882    ReportError(
"LoadAllRoutes-C:step");
 
 1889bool NavObj_dB::LoadAllPoints() {
 
 1904      "p.RangeRingsNumber, " 
 1905      "p.RangeRingsStep, " 
 1906      "p.RangeRingsStepUnits, " 
 1907      "p.RangeRingsVisible, " 
 1908      "p.RangeRingsColour, " 
 1917      "FROM routepoints p ";
 
 1921  sqlite3_stmt* stmtp;
 
 1922  if (sqlite3_prepare_v2(m_db, sqlp, -1, &stmtp, 
nullptr) != SQLITE_OK) {
 
 1926  while (sqlite3_step(stmtp) == SQLITE_ROW) {
 
 1929    std::string point_guid =
 
 1930        reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1931    double latitude = sqlite3_column_double(stmtp, col++);
 
 1932    double longitude = sqlite3_column_double(stmtp, col++);
 
 1933    std::string symbol =
 
 1934        reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1936        reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1937    std::string description =
 
 1938        reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1939    std::string tide_station =
 
 1940        reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1941    double plan_speed = sqlite3_column_double(stmtp, col++);
 
 1942    time_t etd = sqlite3_column_int(stmtp, col++);
 
 1944        reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1945    std::string point_time_string =
 
 1946        reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1947    double arrival_radius = sqlite3_column_double(stmtp, col++);
 
 1949    int range_ring_number = sqlite3_column_int(stmtp, col++);
 
 1950    double range_ring_step = sqlite3_column_double(stmtp, col++);
 
 1951    int range_ring_units = sqlite3_column_int(stmtp, col++);
 
 1952    int range_ring_visible = sqlite3_column_int(stmtp, col++);
 
 1953    std::string range_ring_color =
 
 1954        reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1956    int scamin = sqlite3_column_int(stmtp, col++);
 
 1957    int scamax = sqlite3_column_int(stmtp, col++);
 
 1958    int use_scaminmax = sqlite3_column_int(stmtp, col++);
 
 1960    int visibility = sqlite3_column_int(stmtp, col++);
 
 1961    int viz_name = sqlite3_column_int(stmtp, col++);
 
 1962    int shared = sqlite3_column_int(stmtp, col++);
 
 1963    int isolated = sqlite3_column_int(stmtp, col++);
 
 1964    std::string point_created_at =
 
 1965        reinterpret_cast<const char*
>(sqlite3_column_text(stmtp, col++));
 
 1969          new RoutePoint(latitude, longitude, symbol, name, point_guid, 
false);
 
 1973      point->SetPlannedSpeed(plan_speed);
 
 1979      point->SetShowWaypointRangeRings(range_ring_visible == 1);
 
 1983      point->SetScaMin(scamin);
 
 1984      point->SetScaMax(scamax);
 
 1985      point->SetUseSca(use_scaminmax == 1);
 
 1987      point->SetVisible(visibility == 1);
 
 1988      point->SetNameShown(viz_name == 1);
 
 1989      point->SetShared(shared == 1);
 
 1992      if (point_created_at.size()) {
 
 1996        std::istringstream ss(point_created_at);
 
 1997        ss >> std::get_time(&tm, 
"%Y-%m-%d %H:%M:%S");
 
 1998        time_t epoch_time = mktime(&tm);
 
 2004      pSelect->AddSelectableRoutePoint(point->m_lat, point->m_lon, point);
 
 2007  sqlite3_finalize(stmtp);
 
 2011    const char* sqlh = R
"( 
 2012        SELECT guid, html_link, html_description, html_type 
 2013        FROM routepoint_html_links 
 2014        WHERE routepoint_guid = ? 
 2015        ORDER BY html_type ASC 
 2020    if (sqlite3_prepare_v2(m_db, sqlh, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 2021      sqlite3_bind_text(stmt, 1, point->
m_GUID.ToStdString().c_str(), -1,
 
 2024      while (sqlite3_step(stmt) == SQLITE_ROW) {
 
 2025        std::string link_guid =
 
 2026            reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 0));
 
 2027        std::string link_link =
 
 2028            reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 1));
 
 2029        std::string link_description =
 
 2030            reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 2));
 
 2031        std::string link_type =
 
 2032            reinterpret_cast<const char*
>(sqlite3_column_text(stmt, 3));
 
 2035        h->DescrText = link_description;
 
 2036        h->Link = link_link;
 
 2037        h->LType = link_type;
 
 2042      sqlite3_finalize(stmt);
 
 2050bool NavObj_dB::InsertRoutePoint(
RoutePoint* point) {
 
 2054  if (!RoutePointExists(m_db, point->
m_GUID.ToStdString())) {
 
 2057        wxString::Format(
"INSERT INTO routepoints (guid) VALUES ('%s')",
 
 2058                         point->
m_GUID.ToStdString().c_str());
 
 2059    if (!executeSQL(m_db, sql)) {
 
 2064  UpdateDBRoutePointAttributes(point);
 
 2068  if (NbrOfLinks > 0) {
 
 2070    for (
auto it = list->begin(); it != list->end(); ++it) {
 
 2072      if (!RoutePointHtmlLinkExists(m_db, link->GUID)) {
 
 2073        InsertRoutePointHTML(m_db, point->
m_GUID.ToStdString(), link->GUID,
 
 2074                             link->DescrText.ToStdString(),
 
 2075                             link->Link.ToStdString(),
 
 2076                             link->LType.ToStdString());
 
 2084bool NavObj_dB::DeleteRoutePoint(
RoutePoint* point) {
 
 2085  if (m_importing) 
return false;
 
 2086  if (!point) 
return false;
 
 2088  std::string point_guid = point->
m_GUID.ToStdString();
 
 2092  const char* sql = 
"DELETE FROM routepoints WHERE guid = ?";
 
 2095  if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, 
nullptr) == SQLITE_OK) {
 
 2096    sqlite3_bind_text(stmt, 1, point_guid.c_str(), -1, SQLITE_TRANSIENT);
 
 2097    if (sqlite3_step(stmt) != SQLITE_DONE) {
 
 2098      ReportError(
"DeleteRoutePoint:step");
 
 2099      sqlite3_finalize(stmt);
 
 2103    sqlite3_finalize(stmt);
 
 2110bool NavObj_dB::UpdateRoutePoint(
RoutePoint* point) {
 
 2111  if (m_importing) 
return false;
 
 2112  if (!RoutePointExists(m_db, point->
m_GUID.ToStdString())) 
return false;
 
 2113  UpdateDBRoutePointAttributes(point);
 
 2117bool NavObj_dB::Backup(wxString fileName) {
 
 2118  sqlite3_backup* pBackup;
 
 2119  sqlite3* backupDatabase;
 
 2121  if (sqlite3_open(fileName.c_str(), &backupDatabase) == SQLITE_OK) {
 
 2122    pBackup = sqlite3_backup_init(backupDatabase, 
"main", m_db, 
"main");
 
 2124      int result = sqlite3_backup_step(pBackup, -1);
 
 2125      if ((result == SQLITE_OK) || (result == SQLITE_DONE)) {
 
 2126        if (sqlite3_backup_finish(pBackup) == SQLITE_OK) {
 
 2127          sqlite3_close_v2(backupDatabase);
 
 2133  wxLogMessage(
"navobj database backup error: %s", sqlite3_errmsg(m_db));
 
The navobj SQLite container object, a singleton.
Represents a waypoint or mark within the navigation system.
HyperlinkList * m_HyperlinkList
List of hyperlinks associated with this waypoint.
wxColour m_wxcWaypointRangeRingsColour
Color for the range rings display.
wxString m_MarkDescription
Description text for the waypoint.
int m_iWaypointRangeRingsNumber
Number of range rings to display around the waypoint.
wxString m_GUID
Globally Unique Identifier for the waypoint.
wxDateTime m_CreateTimeX
Creation timestamp for the waypoint, in UTC.
bool m_bIsolatedMark
Flag indicating if the waypoint is a standalone mark.
wxDateTime GetManualETD()
Retrieves the manually set Estimated Time of Departure for this waypoint, in UTC.
wxString m_timestring
String representation of the waypoint creation time.
double GetPlannedSpeed()
Return the planned speed associated with this waypoint.
double m_WaypointArrivalRadius
Arrival radius in nautical miles.
int m_iWaypointRangeRingsStepUnits
Units for the range rings step (0=nm, 1=km).
float m_fWaypointRangeRingsStep
Distance between consecutive range rings.
wxString m_TideStation
Associated tide station identifier.
bool m_bShowWaypointRangeRings
Flag indicating if range rings should be shown around the waypoint.
void SetETD(const wxDateTime &etd)
Sets the Estimated Time of Departure for this waypoint, in UTC.
Represents a navigational route in the navigation system.
double m_PlannedSpeed
Default planned speed for the route in knots.
wxString m_RouteStartString
Name or description of the route's starting point.
wxString m_RouteDescription
Additional descriptive information about the route.
wxString m_Colour
Color name for rendering the route on the chart.
wxString m_RouteEndString
Name or description of the route's ending point.
wxPenStyle m_style
Style of the route line when rendered on the chart.
wxString m_TimeDisplayFormat
Format for displaying times in the UI.
int m_width
Width of the route line in pixels when rendered on the chart.
wxString m_RouteNameString
User-assigned name for the route.
wxString m_GUID
Globally unique identifier for this route.
wxDateTime m_PlannedDeparture
Planned departure time for the route, in UTC.
HyperlinkList * m_HyperlinkList
List of hyperlinks associated with this route.
bool DeleteRoute(Route *pRoute)
Represents a single point in a track.
Represents a track, which is a series of connected track points.
bool AddRoutePoint(RoutePoint *prp)
Add a point to list which owns it.
bool RemoveRoutePoint(RoutePoint *prp)
Remove a routepoint from list if present, deallocate it all cases.
Decoded messages send/receive support.
bool exists(const std::string &name)
MySQL based storage for routes, tracks, etc.
Navigation Utility Functions without GUI dependencies.
User notification container.
User notifications manager.
Routeman * g_pRouteMan
Global instance.
RouteList * pRouteList
Global instance.
Select * pSelect
Global instance.
std::vector< Track * > g_TrackList
Global instance.