OpenCPN Partial API docs
Loading...
Searching...
No Matches
nav_object_database.cpp
1/******************************************************************************
2 *
3 * Project: OpenCPN
4 *
5 ***************************************************************************
6 * Copyright (C) 2010 by David S. Register *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 ***************************************************************************
23 */
24#include <wx/string.h>
25
26#include "model/nav_object_database.h"
27#include "model/routeman.h"
28#include "model/navutil_base.h"
29#include "model/select.h"
30#include "model/track.h"
31#include "model/route.h"
32#include "model/navobj_db.h"
33
34#ifdef __ANDROID__
35#include <QDebug>
36#endif
37
38NavObjectCollection1::NavObjectCollection1() : pugi::xml_document() {}
39
40NavObjectCollection1::~NavObjectCollection1() {}
41
42RoutePoint *GPXLoadWaypoint1(pugi::xml_node &wpt_node, wxString def_symbol_name,
43 wxString GUID, bool b_fullviz, bool b_layer,
44 bool b_layerviz, int layer_id, bool b_nameviz) {
45 bool bviz = false;
46 bool bviz_name = false;
47 bool bshared = false;
48 bool b_propvizname = false;
49 bool b_propviz = false;
50
51 wxString SymString = def_symbol_name; // default icon
52 wxString NameString;
53 wxString DescString;
54 wxString TideStation;
55 double plan_speed = 0.0;
56 wxString etd;
57 wxString TypeString;
58 wxString GuidString = GUID; // default
59 wxString TimeString;
60 wxDateTime dt;
61 RoutePoint *pWP;
62
63 HyperlinkList *linklist = NULL;
64
65 double rlat = wpt_node.attribute("lat").as_double();
66 double rlon = wpt_node.attribute("lon").as_double();
67 double ArrivalRadius = 0;
68 int l_iWaypointRangeRingsNumber = -1;
69 float l_fWaypointRangeRingsStep = -1;
70 int l_pWaypointRangeRingsStepUnits = -1;
71 bool l_bWaypointRangeRingsVisible = false;
72 long l_iWaypointScaleMin = 2147483646;
73 long l_iWaypoinScaleMax = 0;
74 bool l_bWaypointUseScale = false;
75 wxColour l_wxcWaypointRangeRingsColour;
76 l_wxcWaypointRangeRingsColour.Set(_T( "#FFFFFF" ));
77
78 for (pugi::xml_node child = wpt_node.first_child(); child != 0;
79 child = child.next_sibling()) {
80 const char *pcn = child.name();
81
82 if (!strcmp(pcn, "sym")) {
83 SymString = wxString::FromUTF8(child.first_child().value());
84 } else if (!strcmp(pcn, "time"))
85 TimeString = wxString::FromUTF8(child.first_child().value());
86
87 else if (!strcmp(pcn, "name")) {
88 NameString = wxString::FromUTF8(child.first_child().value());
89 if (NameString.StartsWith("@~~")) {
90 // Convert the legacy tidal event definition and change the name so
91 // that it does not kick in next time and cause overiding subsequent
92 // changes
93 TideStation = NameString.Right(NameString.length() - 3);
94 NameString.Replace("@~~", "@-~");
95 }
96 }
97
98 else if (!strcmp(pcn, "desc")) {
99 DescString = wxString::FromUTF8(child.first_child().value());
100 }
101
102 else if (!strcmp(pcn, "type")) {
103 TypeString = wxString::FromUTF8(child.first_child().value());
104 }
105
106 else // Read hyperlink
107 if (!strcmp(pcn, "link")) {
108 wxString HrefString;
109 wxString HrefTextString;
110 wxString HrefTypeString;
111 if (linklist == NULL) linklist = new HyperlinkList;
112 HrefString = wxString::FromUTF8(child.first_attribute().value());
113
114 for (pugi::xml_node child1 = child.first_child(); child1;
115 child1 = child1.next_sibling()) {
116 wxString LinkString = wxString::FromUTF8(child1.name());
117
118 if (LinkString == _T ( "text" ))
119 HrefTextString = wxString::FromUTF8(child1.first_child().value());
120 if (LinkString == _T ( "type" ))
121 HrefTypeString = wxString::FromUTF8(child1.first_child().value());
122 }
123
124 Hyperlink *link = new Hyperlink;
125 link->Link = HrefString;
126 link->DescrText = HrefTextString;
127 link->LType = HrefTypeString;
128 linklist->Append(link);
129 }
130
131 // OpenCPN Extensions....
132 else if (!strcmp(pcn, "extensions")) {
133 for (pugi::xml_node ext_child = child.first_child(); ext_child;
134 ext_child = ext_child.next_sibling()) {
135 wxString ext_name = wxString::FromUTF8(ext_child.name());
136 if (ext_name == _T ( "opencpn:guid" )) {
137 GuidString = wxString::FromUTF8(ext_child.first_child().value());
138 } else if (ext_name == _T ( "opencpn:viz" )) {
139 b_propviz = true;
140 wxString s = wxString::FromUTF8(ext_child.first_child().value());
141 long v = 0;
142 if (s.ToLong(&v)) bviz = (v != 0);
143 } else if (ext_name == _T ( "opencpn:viz_name" )) {
144 b_propvizname = true;
145 wxString s = wxString::FromUTF8(ext_child.first_child().value());
146 long v = 0;
147 if (s.ToLong(&v)) bviz_name = (v != 0);
148 } else if (ext_name == _T ( "opencpn:shared" )) {
149 wxString s = wxString::FromUTF8(ext_child.first_child().value());
150 long v = 0;
151 if (s.ToLong(&v)) bshared = (v != 0);
152 }
153 if (ext_name == _T ( "opencpn:arrival_radius" )) {
154 wxString::FromUTF8(ext_child.first_child().value())
155 .ToDouble(&ArrivalRadius);
156 }
157 if (ext_name == _T("opencpn:waypoint_range_rings")) {
158 for (pugi::xml_attribute attr = ext_child.first_attribute(); attr;
159 attr = attr.next_attribute()) {
160 if (wxString::FromUTF8(attr.name()) == _T("number"))
161 l_iWaypointRangeRingsNumber = attr.as_int();
162 else if (wxString::FromUTF8(attr.name()) == _T("step"))
163 l_fWaypointRangeRingsStep = attr.as_float();
164 else if (wxString::FromUTF8(attr.name()) == _T("units"))
165 l_pWaypointRangeRingsStepUnits = attr.as_int();
166 else if (wxString::FromUTF8(attr.name()) == _T("visible"))
167 l_bWaypointRangeRingsVisible = attr.as_bool();
168 else if (wxString::FromUTF8(attr.name()) == _T("colour"))
169 l_wxcWaypointRangeRingsColour.Set(
170 wxString::FromUTF8(attr.as_string()));
171 }
172 }
173 if (ext_name == _T("opencpn:scale_min_max")) {
174 for (pugi::xml_attribute attr = ext_child.first_attribute(); attr;
175 attr = attr.next_attribute()) {
176 if (wxString::FromUTF8(attr.name()) == _T("UseScale"))
177 l_bWaypointUseScale = attr.as_bool();
178 else if (wxString::FromUTF8(attr.name()) == _T("ScaleMin"))
179 l_iWaypointScaleMin = attr.as_int();
180 else if (wxString::FromUTF8(attr.name()) == _T("ScaleMax"))
181 l_iWaypoinScaleMax = attr.as_float();
182 }
183 }
184 if (ext_name == _T ( "opencpn:tidestation" )) {
185 TideStation = wxString::FromUTF8(ext_child.first_child().value());
186 }
187 if (ext_name == _T ( "opencpn:rte_properties" )) {
188 for (pugi::xml_attribute attr = ext_child.first_attribute(); attr;
189 attr = attr.next_attribute()) {
190 if (!strcmp(attr.name(), "planned_speed"))
191 plan_speed = attr.as_double();
192 else if (!strcmp(attr.name(), "etd"))
193 // The timestamp is serialized without timezone information,
194 // e.g., etd="2025-04-03T20:00:27"
195 // So assume the ETD has always been saved in UTC.
196 etd = attr.as_string();
197 }
198 }
199 } // for
200 } // extensions
201 } // for
202
203 // Create waypoint
204
205 if (b_layer) {
206 if (GuidString.IsEmpty()) GuidString = pWayPointMan->CreateGUID(NULL);
207 }
208
209 pWP = new RoutePoint(rlat, rlon, SymString, NameString, GuidString,
210 false); // do not add to global WP list yet...
211 pWP->m_MarkDescription = DescString;
212 pWP->m_TideStation = TideStation;
213 pWP->m_bIsolatedMark = bshared; // This is an isolated mark
214 pWP->SetWaypointArrivalRadius(ArrivalRadius);
215 pWP->SetWaypointRangeRingsNumber(l_iWaypointRangeRingsNumber);
216 pWP->SetWaypointRangeRingsStep(l_fWaypointRangeRingsStep);
217 pWP->SetWaypointRangeRingsStepUnits(l_pWaypointRangeRingsStepUnits);
218 pWP->SetShowWaypointRangeRings(l_bWaypointRangeRingsVisible);
219
220 // Migrate from O4.x XML format.
221 // In O5, the attribute "range rings visible" is synonymous with ( "range
222 // rings number" != 0 ) So, if we see an attribute "visible"=false in
223 // importing from XML, we must set "number" = 0 to be consistent
224 if (!l_bWaypointRangeRingsVisible) pWP->SetWaypointRangeRingsNumber(0);
225
226 pWP->SetWaypointRangeRingsColour(l_wxcWaypointRangeRingsColour);
227 pWP->SetScaMin(l_iWaypointScaleMin);
228 pWP->SetScaMax(l_iWaypoinScaleMax);
229 pWP->SetUseSca(l_bWaypointUseScale);
230 pWP->SetPlannedSpeed(plan_speed);
231 pWP->SetETD(etd);
232
233 pWP->m_bShowNameData = bviz_name;
234 if (b_propvizname)
235 pWP->m_bShowName = bviz_name;
236 else if (b_fullviz)
237 pWP->m_bShowName = true;
238 else
239 pWP->m_bShowName = false;
240
241 // Special case handling of un-named points in a route
242 if (!b_nameviz && !b_propvizname) pWP->m_bShowName = false;
243
244 if (b_propviz)
245 pWP->m_bIsVisible = bviz;
246 else if (b_fullviz)
247 pWP->m_bIsVisible = true;
248
249 if (b_layer) {
250 pWP->m_bIsInLayer = true;
251 pWP->m_LayerID = layer_id;
252 pWP->m_bIsVisible = b_layerviz;
253 pWP->SetListed(false);
254 }
255
256 pWP->SetShared(bshared);
257
258 if (TimeString.Len()) {
259 pWP->m_timestring = TimeString;
260 pWP->SetCreateTime(wxInvalidDateTime); // cause deferred timestamp parsing
261 }
262
263 if (linklist) {
264 delete pWP->m_HyperlinkList; // created in RoutePoint ctor
265 pWP->m_HyperlinkList = linklist;
266 }
267
268 return pWP;
269}
270
271static TrackPoint *GPXLoadTrackPoint1(pugi::xml_node &wpt_node) {
272 wxString TimeString;
273
274 double rlat = wpt_node.attribute("lat").as_double();
275 double rlon = wpt_node.attribute("lon").as_double();
276
277 for (pugi::xml_node child = wpt_node.first_child(); child != 0;
278 child = child.next_sibling()) {
279 const char *pcn = child.name();
280 if (!strcmp(pcn, "time"))
281 TimeString = wxString::FromUTF8(child.first_child().value());
282
283 // OpenCPN Extensions....
284 else if (!strcmp(pcn, "extensions")) {
285 for (pugi::xml_node ext_child = child.first_child(); ext_child;
286 ext_child = ext_child.next_sibling()) {
287 wxString ext_name = wxString::FromUTF8(ext_child.name());
288 if (ext_name == _T ( "opencpn:action" )) {
289 }
290 } // for
291 } // extensions
292 } // for
293
294 // Create trackpoint
295 return new TrackPoint(rlat, rlon, TimeString);
296}
297
298Track *GPXLoadTrack1(pugi::xml_node &trk_node, bool b_fullviz, bool b_layer,
299 bool b_layerviz, int layer_id) {
300 wxString TrackName;
301 wxString DescString;
302 unsigned short int GPXSeg;
303 bool b_propviz = false;
304 bool b_viz = true;
305 Track *pTentTrack = NULL;
306 HyperlinkList *linklist = NULL;
307
308 wxString Name = wxString::FromUTF8(trk_node.name());
309 if (Name == _T ( "trk" )) {
310 pTentTrack = new Track();
311 GPXSeg = 0;
312
313 TrackPoint *pWp = NULL;
314
315 for (pugi::xml_node tschild = trk_node.first_child(); tschild;
316 tschild = tschild.next_sibling()) {
317 wxString ChildName = wxString::FromUTF8(tschild.name());
318 if (ChildName == _T ( "trkseg" )) {
319 GPXSeg += 1;
320
321 // Official GPX spec calls for trkseg to have children trkpt
322 for (pugi::xml_node tpchild = tschild.first_child(); tpchild;
323 tpchild = tpchild.next_sibling()) {
324 wxString tpChildName = wxString::FromUTF8(tpchild.name());
325 if (tpChildName == _T("trkpt")) {
326 pWp = ::GPXLoadTrackPoint1(tpchild);
327 if (pWp) {
328 pTentTrack->AddPoint(pWp); // defer BBox calculation
329 pWp->m_GPXTrkSegNo = GPXSeg;
330 }
331 }
332 }
333 } else if (ChildName == _T ( "name" ))
334 TrackName = wxString::FromUTF8(tschild.first_child().value());
335 else if (ChildName == _T ( "desc" ))
336 DescString = wxString::FromUTF8(tschild.first_child().value());
337 else
338
339 if (ChildName == _T ( "link")) {
340 wxString HrefString;
341 wxString HrefTextString;
342 wxString HrefTypeString;
343 if (linklist == NULL) linklist = new HyperlinkList;
344 HrefString = wxString::FromUTF8(tschild.first_attribute().value());
345
346 for (pugi::xml_node child1 = tschild.first_child(); child1;
347 child1 = child1.next_sibling()) {
348 wxString LinkString = wxString::FromUTF8(child1.name());
349
350 if (LinkString == _T ( "text" ))
351 HrefTextString = wxString::FromUTF8(child1.first_child().value());
352 if (LinkString == _T ( "type" ))
353 HrefTypeString = wxString::FromUTF8(child1.first_child().value());
354 }
355
356 Hyperlink *link = new Hyperlink;
357 link->Link = HrefString;
358 link->DescrText = HrefTextString;
359 link->LType = HrefTypeString;
360 linklist->Append(link);
361 }
362
363 else if (ChildName == _T ( "extensions" )) {
364 for (pugi::xml_node ext_child = tschild.first_child(); ext_child;
365 ext_child = ext_child.next_sibling()) {
366 wxString ext_name = wxString::FromUTF8(ext_child.name());
367 if (ext_name == _T ( "opencpn:start" )) {
368 pTentTrack->m_TrackStartString =
369 wxString::FromUTF8(ext_child.first_child().value());
370 } else if (ext_name == _T ( "opencpn:end" )) {
371 pTentTrack->m_TrackEndString =
372 wxString::FromUTF8(ext_child.first_child().value());
373 }
374
375 else if (ext_name == _T ( "opencpn:viz" )) {
376 wxString viz = wxString::FromUTF8(ext_child.first_child().value());
377 b_propviz = true;
378 b_viz = (viz == _T("1"));
379 } else if (ext_name == _T ( "opencpn:style" )) {
380 for (pugi::xml_attribute attr = ext_child.first_attribute(); attr;
381 attr = attr.next_attribute()) {
382 if (!strcmp(attr.name(), "style"))
383 pTentTrack->m_style = (wxPenStyle)attr.as_int();
384 else if (!strcmp(attr.name(), "width"))
385 pTentTrack->m_width = attr.as_int();
386 }
387 }
388
389 else if (ext_name == _T ( "opencpn:guid" )) {
390 pTentTrack->m_GUID =
391 wxString::FromUTF8(ext_child.first_child().value());
392 }
393
394 else if (ext_name.EndsWith(
395 _T ( "TrackExtension" ))) // Parse GPXX color
396 {
397 for (pugi::xml_node gpxx_child = ext_child.first_child();
398 gpxx_child; gpxx_child = gpxx_child.next_sibling()) {
399 wxString gpxx_name = wxString::FromUTF8(gpxx_child.name());
400 if (gpxx_name.EndsWith(_T ( "DisplayColor" )))
401 pTentTrack->m_Colour =
402 wxString::FromUTF8(gpxx_child.first_child().value());
403 }
404 }
405 } // extensions
406 }
407 }
408
409 pTentTrack->SetName(TrackName);
410 pTentTrack->m_TrackDescription = DescString;
411
412 if (b_propviz)
413 pTentTrack->SetVisible(b_viz);
414 else {
415 if (b_fullviz) pTentTrack->SetVisible();
416 }
417
418 if (b_layer) {
419 pTentTrack->SetVisible(b_layerviz);
420 pTentTrack->m_bIsInLayer = true;
421 pTentTrack->m_LayerID = layer_id;
422 pTentTrack->SetListed(false);
423 }
424
425 pTentTrack->SetCurrentTrackSeg(GPXSeg);
426 }
427
428 if (linklist) {
429 delete pTentTrack->m_TrackHyperlinkList; // created in TrackPoint ctor
430 pTentTrack->m_TrackHyperlinkList = linklist;
431 }
432
433 return pTentTrack;
434}
435
436Route *GPXLoadRoute1(pugi::xml_node &wpt_node, bool b_fullviz, bool b_layer,
437 bool b_layerviz, int layer_id, bool b_change,
438 bool load_points) {
439 wxString RouteName;
440 wxString DescString;
441 bool b_propviz = false;
442 bool b_propSWPviz = false;
443 bool b_viz = true;
444 bool swpViz = false;
445 Route *pTentRoute = NULL;
446
447 wxString Name = wxString::FromUTF8(wpt_node.name());
448 if (Name == _T ( "rte" )) {
449 pTentRoute = new Route();
450 HyperlinkList *linklist = NULL;
451
452 RoutePoint *pWp = NULL;
453 bool route_existing = false;
454 pTentRoute->m_TimeDisplayFormat = RTE_TIME_DISP_UTC;
455
456 for (pugi::xml_node tschild = wpt_node.first_child(); tschild;
457 tschild = tschild.next_sibling()) {
458 wxString ChildName = wxString::FromUTF8(tschild.name());
459
460 // load extentions first to determine if the route still exists
461 if (ChildName == _T ( "extensions" )) {
462 for (pugi::xml_node ext_child = tschild.first_child(); ext_child;
463 ext_child = ext_child.next_sibling()) {
464 wxString ext_name = wxString::FromUTF8(ext_child.name());
465
466 if (ext_name == _T ( "opencpn:start" )) {
467 pTentRoute->m_RouteStartString =
468 wxString::FromUTF8(ext_child.first_child().value());
469 } else if (ext_name == _T ( "opencpn:end" )) {
470 pTentRoute->m_RouteEndString =
471 wxString::FromUTF8(ext_child.first_child().value());
472 }
473
474 else if (ext_name == _T ( "opencpn:viz" )) {
475 wxString viz = wxString::FromUTF8(ext_child.first_child().value());
476 b_propviz = true;
477 b_viz = (viz == _T("1"));
478 }
479
480 else if (ext_name == _T ( "opencpn:sharedWPviz" )) {
481 wxString viz = wxString::FromUTF8(ext_child.first_child().value());
482 b_propSWPviz = true;
483 swpViz = (viz == _T("1"));
484 } else if (ext_name == _T ( "opencpn:style" )) {
485 for (pugi::xml_attribute attr = ext_child.first_attribute(); attr;
486 attr = attr.next_attribute()) {
487 if (!strcmp(attr.name(), "style"))
488 pTentRoute->m_style = (wxPenStyle)attr.as_int();
489 else if (!strcmp(attr.name(), "width"))
490 pTentRoute->m_width = attr.as_int();
491 }
492 }
493
494 else if (ext_name == _T ( "opencpn:guid" )) {
495 pTentRoute->m_GUID =
496 wxString::FromUTF8(ext_child.first_child().value());
497 }
498
499 else if (ext_name == _T ( "opencpn:planned_speed" )) {
500 pTentRoute->m_PlannedSpeed = atof(ext_child.first_child().value());
501 }
502
503 else if (ext_name == _T ( "opencpn:planned_departure" )) {
504 ParseGPXDateTime(
505 pTentRoute->m_PlannedDeparture,
506 wxString::FromUTF8(ext_child.first_child().value()));
507 }
508
509 else if (ext_name == _T ( "opencpn:time_display" )) {
510 pTentRoute->m_TimeDisplayFormat =
511 wxString::FromUTF8(ext_child.first_child().value());
512 } else if (ext_name.EndsWith(
513 _T ( "RouteExtension" ))) // Parse GPXX color
514 {
515 for (pugi::xml_node gpxx_child = ext_child.first_child();
516 gpxx_child; gpxx_child = gpxx_child.next_sibling()) {
517 wxString gpxx_name = wxString::FromUTF8(gpxx_child.name());
518 if (gpxx_name.EndsWith(_T ( "DisplayColor" )))
519 pTentRoute->m_Colour =
520 wxString::FromUTF8(gpxx_child.first_child().value());
521 }
522 }
523 }
524 if (!b_change) {
525 if (RouteExists(pTentRoute->m_GUID)) { // we are loading a different
526 // route with the same guid so
527 // let's generate a new guid
528 // HACK FOR TESTING NAVOBJ_DB
529 return nullptr;
530
531 pTentRoute->m_GUID = pWayPointMan->CreateGUID(NULL);
532 route_existing = true;
533 }
534 }
535 } // extension
536 else if (load_points && ChildName == _T ( "rtept" )) {
537 RoutePoint *tpWp =
538 ::GPXLoadWaypoint1(tschild, _T("square"), _T(""), b_fullviz,
539 b_layer, b_layerviz, layer_id, false);
540 RoutePoint *erp = NULL;
541 if (!b_layer) erp = ::WaypointExists(tpWp->m_GUID);
542 // 1) if b_change is true, that means we are after crash - load the
543 // route and points as found in source file 2) if route_existing, we are
544 // loading a different route with the same guid. In this case load
545 // points as found in
546 // source file, changing the guid, but keep existing "isolated point" as
547 // found in the DB
548 // 3) in all other cases keep existing points if found and load new
549 // points if not found
550 bool new_wpt = true;
551 if (b_change) {
552 pWp = tpWp;
553 } else {
554 if (erp != NULL &&
555 (!route_existing || (route_existing && tpWp->IsShared()))) {
556 pWp = erp;
557 new_wpt = false;
558 } else {
559 if (route_existing) tpWp->m_GUID = pWayPointMan->CreateGUID(NULL);
560 pWp = tpWp;
561 }
562 }
563
564 pTentRoute->AddPoint(pWp, false, true); // defer BBox calculation
565 pWp->m_bIsInRoute = true; // Hack
566
567 if (new_wpt) {
568 if (erp == NULL) {
569 pWayPointMan->AddRoutePoint(pWp);
570 }
571 } else {
572 delete tpWp;
573 }
574 } else if (ChildName == _T ( "name" )) {
575 RouteName = wxString::FromUTF8(tschild.first_child().value());
576 } else if (ChildName == _T ( "desc" )) {
577 DescString = wxString::FromUTF8(tschild.first_child().value());
578 }
579
580 if (ChildName == _T ( "link")) {
581 wxString HrefString;
582 wxString HrefTextString;
583 wxString HrefTypeString;
584 if (linklist == NULL) linklist = new HyperlinkList;
585 HrefString = wxString::FromUTF8(tschild.first_attribute().value());
586
587 for (pugi::xml_node child1 = tschild.first_child(); child1;
588 child1 = child1.next_sibling()) {
589 wxString LinkString = wxString::FromUTF8(child1.name());
590
591 if (LinkString == _T ( "text" ))
592 HrefTextString = wxString::FromUTF8(child1.first_child().value());
593 if (LinkString == _T ( "type" ))
594 HrefTypeString = wxString::FromUTF8(child1.first_child().value());
595 }
596
597 Hyperlink *link = new Hyperlink;
598 link->Link = HrefString;
599 link->DescrText = HrefTextString;
600 link->LType = HrefTypeString;
601 linklist->Append(link);
602 }
603
604 else
605 // TODO: This is wrong, left here just to save data of the 3.3 beta
606 // series users.
607 if (ChildName.EndsWith(_T ( "RouteExtension" ))) // Parse GPXX color
608 {
609 for (pugi::xml_node gpxx_child = tschild.first_child(); gpxx_child;
610 gpxx_child = gpxx_child.next_sibling()) {
611 wxString gpxx_name = wxString::FromUTF8(gpxx_child.name());
612 if (gpxx_name.EndsWith(_T ( "DisplayColor" )))
613 pTentRoute->m_Colour =
614 wxString::FromUTF8(gpxx_child.first_child().value());
615 }
616 }
617 }
618
619 pTentRoute->m_RouteNameString = RouteName;
620 pTentRoute->m_RouteDescription = DescString;
621 if (linklist) {
622 pTentRoute->m_HyperlinkList = linklist;
623 }
624
625 if (b_propviz) {
626 pTentRoute->SetVisible(b_viz);
627 } else if (b_fullviz) {
628 pTentRoute->SetVisible();
629 }
630
631 if (b_propSWPviz) pTentRoute->SetSharedWPViz(swpViz);
632
633 if (b_layer) {
634 pTentRoute->SetVisible(b_layerviz);
635 pTentRoute->m_bIsInLayer = true;
636 pTentRoute->m_LayerID = layer_id;
637 pTentRoute->SetListed(false);
638 }
639 }
640
641 return pTentRoute;
642}
643
644static bool GPXCreateWpt(pugi::xml_node node, RoutePoint *pr,
645 unsigned int flags) {
646 wxString s;
647 pugi::xml_node child;
649
650 s.Printf(_T("%.9f"), pr->m_lat);
651 node.append_attribute("lat") = s.mb_str();
652 s.Printf(_T("%.9f"), pr->m_lon);
653 node.append_attribute("lon") = s.mb_str();
654
655 if (flags & OUT_TIME) {
656 child = node.append_child("time");
657 if (pr->m_timestring.Len())
658 child.append_child(pugi::node_pcdata)
659 .set_value(pr->m_timestring.mb_str());
660 else {
661 wxDateTime dt = pr->GetCreateTime();
662 if (!dt.IsValid()) dt = wxDateTime::Now();
663
664 wxString t = dt.ToUTC()
665 .FormatISODate()
666 .Append(_T("T"))
667 .Append(dt.ToUTC().FormatISOTime())
668 .Append(_T("Z"));
669 child.append_child(pugi::node_pcdata).set_value(t.mb_str());
670 }
671 }
672
673 if ((!pr->GetName().IsEmpty() && (flags & OUT_NAME)) ||
674 (flags & OUT_NAME_FORCE)) {
675 wxCharBuffer buffer = pr->GetName().ToUTF8();
676 if (buffer.data()) {
677 child = node.append_child("name");
678 child.append_child(pugi::node_pcdata).set_value(buffer.data());
679 }
680 }
681
682 if ((!pr->GetDescription().IsEmpty() && (flags & OUT_DESC)) ||
683 (flags & OUT_DESC_FORCE)) {
684 wxCharBuffer buffer = pr->GetDescription().ToUTF8();
685 if (buffer.data()) {
686 child = node.append_child("desc");
687 child.append_child(pugi::node_pcdata).set_value(buffer.data());
688 }
689 }
690
691 // Hyperlinks
692 if (flags & OUT_HYPERLINKS) {
693 HyperlinkList *linklist = pr->m_HyperlinkList;
694 if (linklist && linklist->GetCount()) {
695 wxHyperlinkListNode *linknode = linklist->GetFirst();
696 while (linknode) {
697 Hyperlink *link = linknode->GetData();
698
699 pugi::xml_node child_link = node.append_child("link");
700 ;
701 wxCharBuffer buffer = link->Link.ToUTF8();
702 if (buffer.data()) child_link.append_attribute("href") = buffer.data();
703
704 buffer = link->DescrText.ToUTF8();
705 if (buffer.data()) {
706 child = child_link.append_child("text");
707 child.append_child(pugi::node_pcdata).set_value(buffer.data());
708 }
709
710 buffer = link->LType.ToUTF8();
711 if (buffer.data() && strlen(buffer.data()) > 0) {
712 child = child_link.append_child("type");
713 child.append_child(pugi::node_pcdata).set_value(buffer.data());
714 }
715
716 linknode = linknode->GetNext();
717 }
718 }
719 }
720
721 if (flags & OUT_SYM_FORCE) {
722 child = node.append_child("sym");
723 if (!pr->GetIconName().IsEmpty()) {
724 child.append_child(pugi::node_pcdata)
725 .set_value(pr->GetIconName().mb_str());
726 } else {
727 child.append_child("empty");
728 }
729 }
730
731 if (flags & OUT_TYPE) {
732 child = node.append_child("type");
733 child.append_child(pugi::node_pcdata).set_value("WPT");
734 }
735
736 if ((flags & OUT_GUID) || (flags & OUT_VIZ) || (flags & OUT_VIZ_NAME) ||
737 (flags & OUT_SHARED) || (flags & OUT_EXTENSION) ||
738 (flags & OUT_TIDE_STATION) || (flags & OUT_RTE_PROPERTIES)) {
739 pugi::xml_node child_ext = node.append_child("extensions");
740
741 if (!pr->m_GUID.IsEmpty() && (flags & OUT_GUID)) {
742 child = child_ext.append_child("opencpn:guid");
743 child.append_child(pugi::node_pcdata).set_value(pr->m_GUID.mb_str());
744 }
745
746 if ((flags & OUT_VIZ) && !pr->m_bIsVisible) {
747 child = child_ext.append_child("opencpn:viz");
748 child.append_child(pugi::node_pcdata).set_value("0");
749 }
750
751 if ((flags & OUT_VIZ_NAME) && pr->m_bShowName) {
752 child = child_ext.append_child("opencpn:viz_name");
753 child.append_child(pugi::node_pcdata).set_value("1");
754 }
755
756 if ((flags & OUT_SHARED) && pr->IsShared()) {
757 child = child_ext.append_child("opencpn:shared");
758 child.append_child(pugi::node_pcdata).set_value("1");
759 }
760 if (flags & OUT_ARRIVAL_RADIUS) {
761 child = child_ext.append_child("opencpn:arrival_radius");
762 s.Printf(_T("%.3f"), pr->GetWaypointArrivalRadius());
763 child.append_child(pugi::node_pcdata).set_value(s.mbc_str());
764 }
765 if (flags & OUT_WAYPOINT_RANGE_RINGS) {
766 child = child_ext.append_child("opencpn:waypoint_range_rings");
767 pugi::xml_attribute viz = child.append_attribute("visible");
768 viz.set_value(pr->m_bShowWaypointRangeRings);
769 pugi::xml_attribute number = child.append_attribute("number");
770 number.set_value(pr->m_iWaypointRangeRingsNumber);
771 pugi::xml_attribute step = child.append_attribute("step");
772 step.set_value(pr->m_fWaypointRangeRingsStep);
773 pugi::xml_attribute units = child.append_attribute("units");
774 units.set_value(pr->m_iWaypointRangeRingsStepUnits);
775
776 // Color specification in GPX file must be fully opaque
777 if (pr->m_wxcWaypointRangeRingsColour.IsOk()) {
781 pr->m_wxcWaypointRangeRingsColour.Blue(), wxALPHA_OPAQUE);
782 } else {
783 pr->m_wxcWaypointRangeRingsColour.Set(0, 0, 0, wxALPHA_OPAQUE);
784 }
785
786 pugi::xml_attribute colour = child.append_attribute("colour");
787 colour.set_value(
788 pr->m_wxcWaypointRangeRingsColour.GetAsString(wxC2S_HTML_SYNTAX)
789 .utf8_str());
790 }
791 if (flags & OUT_WAYPOINT_SCALE) {
792 child = child_ext.append_child("opencpn:scale_min_max");
793 pugi::xml_attribute use = child.append_attribute("UseScale");
794 use.set_value(pr->GetUseSca());
795 pugi::xml_attribute sca = child.append_attribute("ScaleMin");
796 sca.set_value(pr->GetScaMin());
797 pugi::xml_attribute max = child.append_attribute("ScaleMax");
798 max.set_value(pr->GetScaMax());
799 }
800 if ((flags & OUT_TIDE_STATION) && !pr->m_TideStation.IsEmpty()) {
801 child = child_ext.append_child("opencpn:tidestation");
802 child.append_child(pugi::node_pcdata)
803 .set_value(pr->m_TideStation.mb_str());
804 }
805 if ((flags & OUT_RTE_PROPERTIES) &&
806 (pr->GetPlannedSpeed() > 0.0001 || pr->m_manual_etd)) {
807 child = child_ext.append_child("opencpn:rte_properties");
808 if (pr->GetPlannedSpeed() > 0.0001) {
809 pugi::xml_attribute use = child.append_attribute("planned_speed");
810 use.set_value(
811 wxString::Format(_T("%.1lf"), pr->GetPlannedSpeed()).mb_str());
812 }
813 if (pr->m_manual_etd && pr->GetManualETD().IsValid()) {
814 pugi::xml_attribute use = child.append_attribute("etd");
815 // Currently, the serialization format is YYYY-MM-DDTHH:MM:SS
816 // without timezone information, e.g., etd="2025-04-03T20:00:27"
817 // TODO: serialize using ISO 8601 or RFC 3339 format to ensure
818 // the serialized date/time is unambiguous.
819 use.set_value(pr->GetManualETD().FormatISOCombined().mb_str());
820 }
821 }
822 }
823
824 return true;
825}
826
827static bool GPXCreateTrkpt(pugi::xml_node node, TrackPoint *pt,
828 unsigned int flags) {
829 wxString s;
830 pugi::xml_node child;
832
833 s.Printf(_T("%.9f"), pt->m_lat);
834 node.append_attribute("lat") = s.mb_str();
835 s.Printf(_T("%.9f"), pt->m_lon);
836 node.append_attribute("lon") = s.mb_str();
837
838 if (flags & OUT_TIME && pt->HasValidTimestamp()) {
839 child = node.append_child("time");
840 child.append_child(pugi::node_pcdata).set_value(pt->GetTimeString());
841 }
842
843 return true;
844}
845
846static bool GPXCreateTrk(pugi::xml_node node, Track *pTrack,
847 unsigned int flags) {
848 pugi::xml_node child;
849
850 if (pTrack->GetName().Len()) {
851 wxCharBuffer buffer = pTrack->GetName().ToUTF8();
852 if (buffer.data()) {
853 child = node.append_child("name");
854 child.append_child(pugi::node_pcdata).set_value(buffer.data());
855 }
856 }
857
858 if (pTrack->m_TrackDescription.Len()) {
859 wxCharBuffer buffer = pTrack->m_TrackDescription.ToUTF8();
860 if (buffer.data()) {
861 child = node.append_child("desc");
862 child.append_child(pugi::node_pcdata).set_value(buffer.data());
863 }
864 }
865
866 // Hyperlinks
867 HyperlinkList *linklist = pTrack->m_TrackHyperlinkList;
868 if (linklist && linklist->GetCount()) {
869 wxHyperlinkListNode *linknode = linklist->GetFirst();
870 while (linknode) {
871 Hyperlink *link = linknode->GetData();
872
873 pugi::xml_node child_link = node.append_child("link");
874 wxCharBuffer buffer = link->Link.ToUTF8();
875 if (buffer.data()) child_link.append_attribute("href") = buffer.data();
876
877 buffer = link->DescrText.ToUTF8();
878 if (buffer.data()) {
879 child = child_link.append_child("text");
880 child.append_child(pugi::node_pcdata).set_value(buffer.data());
881 }
882
883 buffer = link->LType.ToUTF8();
884 if (buffer.data() && strlen(buffer.data()) > 0) {
885 child = child_link.append_child("type");
886 child.append_child(pugi::node_pcdata).set_value(buffer.data());
887 }
888
889 linknode = linknode->GetNext();
890 }
891 }
892
893 pugi::xml_node child_ext = node.append_child("extensions");
894
895 child = child_ext.append_child("opencpn:guid");
896 child.append_child(pugi::node_pcdata).set_value(pTrack->m_GUID.mb_str());
897
898 child = child_ext.append_child("opencpn:viz");
899 child.append_child(pugi::node_pcdata)
900 .set_value(pTrack->IsVisible() == true ? "1" : "0");
901
902 if (pTrack->m_TrackStartString.Len()) {
903 wxCharBuffer buffer = pTrack->m_TrackStartString.ToUTF8();
904 if (buffer.data()) {
905 child = child_ext.append_child("opencpn:start");
906 child.append_child(pugi::node_pcdata).set_value(buffer.data());
907 }
908 }
909
910 if (pTrack->m_TrackEndString.Len()) {
911 wxCharBuffer buffer = pTrack->m_TrackEndString.ToUTF8();
912 if (buffer.data()) {
913 child = child_ext.append_child("opencpn:end");
914 child.append_child(pugi::node_pcdata).set_value(buffer.data());
915 }
916 }
917
918 if (pTrack->m_width != WIDTH_UNDEFINED ||
919 pTrack->m_style != wxPENSTYLE_INVALID) {
920 child = child_ext.append_child("opencpn:style");
921
922 if (pTrack->m_width != WIDTH_UNDEFINED)
923 child.append_attribute("width") = pTrack->m_width;
924 if (pTrack->m_style != wxPENSTYLE_INVALID)
925 child.append_attribute("style") = pTrack->m_style;
926 }
927
928 if (pTrack->m_Colour != wxEmptyString) {
929 pugi::xml_node gpxx_ext = child_ext.append_child("gpxx:TrackExtension");
930 child = gpxx_ext.append_child("gpxx:DisplayColor");
931 child.append_child(pugi::node_pcdata).set_value(pTrack->m_Colour.mb_str());
932 }
933
934 if (flags & RT_OUT_NO_RTPTS) return true;
935
936 int node2 = 0;
937 TrackPoint *prp;
938
939 unsigned short int GPXTrkSegNo1 = 1;
940
941 do {
942 unsigned short int GPXTrkSegNo2 = GPXTrkSegNo1;
943
944 pugi::xml_node seg = node.append_child("trkseg");
945
946 while (node2 < pTrack->GetnPoints()) {
947 prp = pTrack->GetPoint(node2);
948 GPXTrkSegNo1 = prp->m_GPXTrkSegNo;
949 if (GPXTrkSegNo1 != GPXTrkSegNo2) break;
950
951 GPXCreateTrkpt(seg.append_child("trkpt"), prp, OPT_TRACKPT);
952
953 node2++;
954 }
955 } while (node2 < pTrack->GetnPoints());
956
957 return true;
958}
959
960static bool GPXCreateRoute(pugi::xml_node node, Route *pRoute) {
961 pugi::xml_node child;
962
963 if (pRoute->m_RouteNameString.Len()) {
964 wxCharBuffer buffer = pRoute->m_RouteNameString.ToUTF8();
965 if (buffer.data()) {
966 child = node.append_child("name");
967 child.append_child(pugi::node_pcdata).set_value(buffer.data());
968 }
969 }
970
971 if (pRoute->m_RouteDescription.Len()) {
972 wxCharBuffer buffer = pRoute->m_RouteDescription.ToUTF8();
973 if (buffer.data()) {
974 child = node.append_child("desc");
975 child.append_child(pugi::node_pcdata).set_value(buffer.data());
976 }
977 }
978
979 // Hyperlinks
980 HyperlinkList *linklist = pRoute->m_HyperlinkList;
981 if (linklist && linklist->GetCount()) {
982 wxHyperlinkListNode *linknode = linklist->GetFirst();
983 while (linknode) {
984 Hyperlink *link = linknode->GetData();
985
986 pugi::xml_node child_link = node.append_child("link");
987 wxCharBuffer buffer = link->Link.ToUTF8();
988 if (buffer.data()) child_link.append_attribute("href") = buffer.data();
989
990 buffer = link->DescrText.ToUTF8();
991 if (buffer.data()) {
992 child = child_link.append_child("text");
993 child.append_child(pugi::node_pcdata).set_value(buffer.data());
994 }
995
996 buffer = link->LType.ToUTF8();
997 if (buffer.data() && strlen(buffer.data()) > 0) {
998 child = child_link.append_child("type");
999 child.append_child(pugi::node_pcdata).set_value(buffer.data());
1000 }
1001
1002 linknode = linknode->GetNext();
1003 }
1004 }
1005
1006 pugi::xml_node child_ext = node.append_child("extensions");
1007
1008 child = child_ext.append_child("opencpn:guid");
1009 child.append_child(pugi::node_pcdata).set_value(pRoute->m_GUID.mb_str());
1010
1011 child = child_ext.append_child("opencpn:viz");
1012 child.append_child(pugi::node_pcdata)
1013 .set_value(pRoute->IsVisible() == true ? "1" : "0");
1014
1015 if (pRoute->ContainsSharedWP()) {
1016 child = child_ext.append_child("opencpn:sharedWPviz");
1017 child.append_child(pugi::node_pcdata)
1018 .set_value(pRoute->GetSharedWPViz() == true ? "1" : "0");
1019 }
1020
1021 if (pRoute->m_RouteStartString.Len()) {
1022 wxCharBuffer buffer = pRoute->m_RouteStartString.ToUTF8();
1023 if (buffer.data()) {
1024 child = child_ext.append_child("opencpn:start");
1025 child.append_child(pugi::node_pcdata).set_value(buffer.data());
1026 }
1027 }
1028
1029 if (pRoute->m_RouteEndString.Len()) {
1030 wxCharBuffer buffer = pRoute->m_RouteEndString.ToUTF8();
1031 if (buffer.data()) {
1032 child = child_ext.append_child("opencpn:end");
1033 child.append_child(pugi::node_pcdata).set_value(buffer.data());
1034 }
1035 }
1036
1037 if (pRoute->m_PlannedSpeed != ROUTE_DEFAULT_SPEED) {
1038 child = child_ext.append_child("opencpn:planned_speed");
1039 wxString s;
1040 s.Printf(_T("%.2f"), pRoute->m_PlannedSpeed);
1041 child.append_child(pugi::node_pcdata).set_value(s.mb_str());
1042 }
1043
1044 if (pRoute->m_PlannedDeparture.IsValid()) {
1045 child = child_ext.append_child("opencpn:planned_departure");
1046 wxString t = pRoute->m_PlannedDeparture.FormatISODate()
1047 .Append(_T("T"))
1048 .Append(pRoute->m_PlannedDeparture.FormatISOTime())
1049 .Append(_T("Z"));
1050 child.append_child(pugi::node_pcdata).set_value(t.mb_str());
1051 }
1052
1053 child = child_ext.append_child("opencpn:time_display");
1054 child.append_child(pugi::node_pcdata)
1055 .set_value(pRoute->m_TimeDisplayFormat.mb_str());
1056
1057 if (pRoute->m_width != WIDTH_UNDEFINED ||
1058 pRoute->m_style != wxPENSTYLE_INVALID) {
1059 child = child_ext.append_child("opencpn:style");
1060
1061 if (pRoute->m_width != WIDTH_UNDEFINED)
1062 child.append_attribute("width") = pRoute->m_width;
1063 if (pRoute->m_style != wxPENSTYLE_INVALID)
1064 child.append_attribute("style") = pRoute->m_style;
1065 }
1066
1067 pugi::xml_node gpxx_ext = child_ext.append_child("gpxx:RouteExtension");
1068 child = gpxx_ext.append_child("gpxx:IsAutoNamed");
1069 child.append_child(pugi::node_pcdata).set_value("false");
1070
1071 if (pRoute->m_Colour != wxEmptyString) {
1072 child = gpxx_ext.append_child("gpxx:DisplayColor");
1073 child.append_child(pugi::node_pcdata).set_value(pRoute->m_Colour.mb_str());
1074 }
1075
1076 RoutePointList *pRoutePointList = pRoute->pRoutePointList;
1077 wxRoutePointListNode *node2 = pRoutePointList->GetFirst();
1078 RoutePoint *prp;
1079
1080 while (node2) {
1081 prp = node2->GetData();
1082
1083 GPXCreateWpt(node.append_child("rtept"), prp, OPT_ROUTEPT);
1084
1085 node2 = node2->GetNext();
1086 }
1087
1088 return true;
1089}
1090
1091bool InsertRouteA(Route *pTentRoute, NavObjectCollection1 *navobj) {
1092 if (!pTentRoute) return false;
1093
1094 bool bAddroute = true;
1095 // If the route has only 1 point, don't load it.
1096 if (pTentRoute->GetnPoints() < 2) bAddroute = false;
1097
1098 // TODO All this trouble for a tentative route.......Should make some
1099 // Route methods????
1100 if (bAddroute) {
1101 pRouteList->Append(pTentRoute);
1102
1103 // Do the (deferred) calculation of BBox
1104 pTentRoute->FinalizeForRendering();
1105
1106 // Add the selectable points and segments
1107
1108 int ip = 0;
1109 float prev_rlat = 0., prev_rlon = 0.;
1110 RoutePoint *prev_pConfPoint = NULL;
1111
1112 wxRoutePointListNode *node = pTentRoute->pRoutePointList->GetFirst();
1113 while (node) {
1114 RoutePoint *prp = node->GetData();
1115
1116 if (ip)
1117 pSelect->AddSelectableRouteSegment(prev_rlat, prev_rlon, prp->m_lat,
1118 prp->m_lon, prev_pConfPoint, prp,
1119 pTentRoute);
1120 pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
1121 prev_rlat = prp->m_lat;
1122 prev_rlon = prp->m_lon;
1123 prev_pConfPoint = prp;
1124
1125 ip++;
1126
1127 node = node->GetNext();
1128 }
1129 } else {
1130 // walk the route, deleting points used only by this route
1131 wxRoutePointListNode *pnode = (pTentRoute->pRoutePointList)->GetFirst();
1132 while (pnode) {
1133 RoutePoint *prp = pnode->GetData();
1134
1135 // check all other routes to see if this point appears in any other route
1136 Route *pcontainer_route = g_pRouteMan->FindRouteContainingWaypoint(prp);
1137
1138 if (pcontainer_route == NULL) {
1139 prp->m_bIsInRoute =
1140 false; // Take this point out of this (and only) track/route
1141 if (!prp->IsShared()) {
1142 if (navobj) {
1143 delete prp;
1144 }
1145 }
1146 }
1147
1148 pnode = pnode->GetNext();
1149 }
1150
1151 delete pTentRoute;
1152 }
1153 return bAddroute;
1154}
1155
1156bool InsertTrack(Track *pTentTrack, bool bApplyChanges) {
1157 if (!pTentTrack) return false;
1158
1159 bool bAddtrack = true;
1160 // If the track has only 1 point, don't load it.
1161 // This usually occurs if some points were discarded as being co-incident.
1162 if (!bApplyChanges && pTentTrack->GetnPoints() < 2) bAddtrack = false;
1163
1164 // TODO All this trouble for a tentative track.......Should make some
1165 // Track methods????
1166 if (bAddtrack) {
1167 g_TrackList.push_back(pTentTrack);
1168
1169 // Do the (deferred) calculation of Track BBox
1170 // pTentTrack->FinalizeForRendering();
1171
1172 // Add the selectable points and segments
1173
1174 float prev_rlat = 0., prev_rlon = 0.;
1175 TrackPoint *prev_pConfPoint = NULL;
1176
1177 for (int i = 0; i < pTentTrack->GetnPoints(); i++) {
1178 TrackPoint *prp = pTentTrack->GetPoint(i);
1179
1180 if (i)
1181 pSelect->AddSelectableTrackSegment(prev_rlat, prev_rlon, prp->m_lat,
1182 prp->m_lon, prev_pConfPoint, prp,
1183 pTentTrack);
1184
1185 prev_rlat = prp->m_lat;
1186 prev_rlon = prp->m_lon;
1187 prev_pConfPoint = prp;
1188 }
1189 } else
1190 delete pTentTrack;
1191
1192 return bAddtrack;
1193}
1194
1195bool InsertWpt(RoutePoint *pWp, bool overwrite) {
1196 bool res = false;
1197 RoutePoint *pExisting =
1198 WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon);
1199 if (!pExisting || overwrite) {
1200 if (NULL != pWayPointMan) {
1201 if (pExisting) {
1202 pWayPointMan->DestroyWaypoint(pExisting);
1203 }
1204 pWayPointMan->AddRoutePoint(pWp);
1205 res = true;
1206 }
1207 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1208 }
1209 return res;
1210}
1211
1212static void UpdateRouteA(Route *pTentRoute, NavObjectCollection1 *navobj) {
1213 if (!pTentRoute) return;
1214 if (pTentRoute->GetnPoints() < 2) return;
1215
1216 // first delete the route to be modified if exists
1217 Route *pExisting = ::RouteExists(pTentRoute->m_GUID);
1218 if (pExisting) {
1219 g_pRouteMan->DeleteRoute(pExisting);
1220 }
1221
1222 // create a new route
1223 Route *pChangeRoute = new Route();
1224 pRouteList->Append(pChangeRoute);
1225
1226 // update new route keeping the same gui
1227 pChangeRoute->m_GUID = pTentRoute->m_GUID;
1228 pChangeRoute->m_RouteNameString = pTentRoute->m_RouteNameString;
1229 pChangeRoute->m_RouteStartString = pTentRoute->m_RouteStartString;
1230 pChangeRoute->m_RouteEndString = pTentRoute->m_RouteEndString;
1231 pChangeRoute->SetVisible(pTentRoute->IsVisible());
1232
1233 // Add points and segments to new route
1234 int ip = 0;
1235 float prev_rlat = 0., prev_rlon = 0.;
1236 RoutePoint *prev_pConfPoint = NULL;
1237
1238 wxRoutePointListNode *node = pTentRoute->pRoutePointList->GetFirst();
1239 while (node) {
1240 RoutePoint *prp = node->GetData();
1241
1242 // if some wpts have been not deleted, that meens they should be used in
1243 // other routes or are isolated way points so need to be updated
1244 RoutePoint *ex_rp = ::WaypointExists(prp->m_GUID);
1245 if (ex_rp) {
1246 pSelect->DeleteSelectableRoutePoint(ex_rp);
1247 ex_rp->m_lat = prp->m_lat;
1248 ex_rp->m_lon = prp->m_lon;
1249 ex_rp->SetIconName(prp->GetIconName());
1251 ex_rp->SetName(prp->GetName());
1252 ex_rp->m_TideStation = prp->m_TideStation;
1253 ex_rp->SetPlannedSpeed(prp->GetPlannedSpeed());
1254 pChangeRoute->AddPoint(ex_rp);
1255 pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, ex_rp);
1256
1257 } else {
1258 pChangeRoute->AddPoint(prp);
1259 pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
1260 pWayPointMan->AddRoutePoint(prp);
1261 }
1262
1263 if (ip)
1264 pSelect->AddSelectableRouteSegment(prev_rlat, prev_rlon, prp->m_lat,
1265 prp->m_lon, prev_pConfPoint, prp,
1266 pChangeRoute);
1267 prev_rlat = prp->m_lat;
1268 prev_rlon = prp->m_lon;
1269 prev_pConfPoint = prp;
1270
1271 ip++;
1272
1273 node = node->GetNext();
1274 }
1275 // Do the (deferred) calculation of BBox
1276 pChangeRoute->FinalizeForRendering();
1277}
1278
1279Route *FindRouteContainingWaypoint(RoutePoint *pWP) {
1280 wxRouteListNode *node = pRouteList->GetFirst();
1281 while (node) {
1282 Route *proute = node->GetData();
1283
1284 wxRoutePointListNode *pnode = (proute->pRoutePointList)->GetFirst();
1285 while (pnode) {
1286 RoutePoint *prp = pnode->GetData();
1287 if (prp == pWP) return proute;
1288 pnode = pnode->GetNext();
1289 }
1290
1291 node = node->GetNext();
1292 }
1293
1294 return NULL; // not found
1295}
1296
1297bool NavObjectCollection1::CreateNavObjGPXPoints(void) {
1298 // Iterate over the Routepoint list, creating Nodes for
1299 // Routepoints that are not in any Route
1300 // as indicated by m_bIsolatedMark == false
1301
1302 if (!pWayPointMan) return false;
1303
1304 wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1305
1306 RoutePoint *pr;
1307
1308 while (node) {
1309 pr = node->GetData();
1310
1311 if ((pr->m_bIsolatedMark) && !(pr->m_bIsInLayer) && !(pr->m_btemp)) {
1312 pugi::xml_node doc = root();
1313 pugi::xml_node gpx = doc.first_child();
1314 pugi::xml_node new_node = gpx.append_child("wpt");
1315
1316 GPXCreateWpt(new_node, pr, OPT_WPT);
1317 }
1318 node = node->GetNext();
1319 }
1320
1321 return true;
1322}
1323
1324bool NavObjectCollection1::CreateNavObjGPXRoutes(void) {
1325 // Routes
1326 if (!pRouteList) return false;
1327
1328 wxRouteListNode *node1 = pRouteList->GetFirst();
1329 while (node1) {
1330 Route *pRoute = node1->GetData();
1331
1332 if (!pRoute->m_bIsInLayer && !pRoute->m_btemp) {
1333 pugi::xml_node doc = root();
1334 pugi::xml_node gpx = doc.first_child();
1335 pugi::xml_node new_node = gpx.append_child("rte");
1336
1337 GPXCreateRoute(new_node, pRoute);
1338 }
1339
1340 node1 = node1->GetNext();
1341 }
1342
1343 return true;
1344}
1345
1346bool NavObjectCollection1::CreateNavObjGPXTracks(void) {
1347 // Tracks
1348 for (Track *pTrack : g_TrackList) {
1349 if (pTrack->GetnPoints()) {
1350 if (!pTrack->m_bIsInLayer && !pTrack->m_btemp) {
1351 pugi::xml_node doc = root();
1352 pugi::xml_node gpx = doc.first_child();
1353 pugi::xml_node new_node = gpx.append_child("trk");
1354
1355 GPXCreateTrk(new_node, pTrack, 0);
1356 }
1357 }
1358 }
1359
1360 return true;
1361}
1362
1363bool NavObjectCollection1::CreateAllGPXObjects() {
1364 SetRootGPXNode();
1365
1366 // CreateNavObjGPXPoints();
1367 // CreateNavObjGPXRoutes();
1368 // CreateNavObjGPXTracks();
1369
1370 return true;
1371}
1372
1373bool NavObjectCollection1::AddGPXRoute(Route *pRoute) {
1374 SetRootGPXNode();
1375 pugi::xml_node doc = root();
1376 pugi::xml_node gpx = doc.first_child();
1377 pugi::xml_node new_node = gpx.append_child("rte");
1378
1379 GPXCreateRoute(new_node, pRoute);
1380 return true;
1381}
1382
1383bool NavObjectCollection1::AddGPXTrack(Track *pTrk) {
1384 SetRootGPXNode();
1385 pugi::xml_node doc = root();
1386 pugi::xml_node gpx = doc.first_child();
1387 pugi::xml_node new_node = gpx.append_child("trk");
1388
1389 GPXCreateTrk(new_node, pTrk, 0);
1390 return true;
1391}
1392
1393bool NavObjectCollection1::AddGPXWaypoint(RoutePoint *pWP) {
1394 SetRootGPXNode();
1395 pugi::xml_node doc = root();
1396 pugi::xml_node gpx = doc.first_child();
1397 pugi::xml_node new_node = gpx.append_child("wpt");
1398
1399 GPXCreateWpt(new_node, pWP, OPT_WPT);
1400 return true;
1401}
1402
1403void NavObjectCollection1::AddGPXRoutesList(RouteList *pRoutes) {
1404 SetRootGPXNode();
1405
1406 wxRouteListNode *pRoute = pRoutes->GetFirst();
1407 while (pRoute) {
1408 Route *pRData = pRoute->GetData();
1409 AddGPXRoute(pRData);
1410 pRoute = pRoute->GetNext();
1411 }
1412}
1413
1414void NavObjectCollection1::AddGPXTracksList(std::vector<Track *> *pTracks) {
1415 SetRootGPXNode();
1416
1417 for (Track *pRData : *pTracks) {
1418 AddGPXTrack(pRData);
1419 }
1420}
1421
1422bool NavObjectCollection1::AddGPXPointsList(RoutePointList *pRoutePoints) {
1423 SetRootGPXNode();
1424
1425 wxRoutePointListNode *pRoutePointNode = pRoutePoints->GetFirst();
1426 while (pRoutePointNode) {
1427 RoutePoint *pRP = pRoutePointNode->GetData();
1428 AddGPXWaypoint(pRP);
1429 pRoutePointNode = pRoutePointNode->GetNext();
1430 }
1431
1432 return true;
1433}
1434
1435void NavObjectCollection1::SetRootGPXNode(void) {
1436 if (!strlen(first_child().name())) {
1437 pugi::xml_node gpx_root = append_child("gpx");
1438 gpx_root.append_attribute("version") = "1.1";
1439 gpx_root.append_attribute("creator") = "OpenCPN";
1440 gpx_root.append_attribute("xmlns:xsi") =
1441 "http://www.w3.org/2001/XMLSchema-instance";
1442 gpx_root.append_attribute("xmlns") = "http://www.topografix.com/GPX/1/1";
1443 gpx_root.append_attribute("xmlns:gpxx") =
1444 "http://www.garmin.com/xmlschemas/GpxExtensions/v3";
1445 gpx_root.append_attribute("xsi:schemaLocation") =
1446 "http://www.topografix.com/GPX/1/1 "
1447 "http://www.topografix.com/GPX/1/1/gpx.xsd "
1448 "http://www.garmin.com/xmlschemas/GpxExtensions/v3 "
1449 "http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd";
1450 gpx_root.append_attribute("xmlns:opencpn") = "http://www.opencpn.org";
1451 }
1452}
1453
1454bool NavObjectCollection1::IsOpenCPN() {
1455 for (pugi::xml_attribute attr = root().first_child().first_attribute(); attr;
1456 attr = attr.next_attribute())
1457 if (!strcmp(attr.name(), "creator") && !strcmp(attr.value(), "OpenCPN"))
1458 return true;
1459 return false;
1460}
1461
1462bool NavObjectCollection1::SaveFile(const wxString filename) {
1463 wxString tmp_filename = filename + ".tmp";
1464 if (wxFileExists(tmp_filename)) {
1465 wxRemoveFile(tmp_filename);
1466 }
1467 save_file(tmp_filename.fn_str(), " ");
1468 wxRenameFile(tmp_filename.fn_str(), filename.fn_str(), true);
1469 return true;
1470}
1471
1472bool NavObjectCollection1::LoadAllGPXObjects(bool b_full_viz,
1473 int &wpt_duplicates,
1474 bool b_compute_bbox) {
1475 wpt_duplicates = 0;
1476 pugi::xml_node objects = this->child("gpx");
1477
1478 for (pugi::xml_node object = objects.first_child(); object;
1479 object = object.next_sibling()) {
1480 if (!strcmp(object.name(), "wpt")) {
1481 RoutePoint *pWp = ::GPXLoadWaypoint1(object, _T("circle"), _T(""),
1482 b_full_viz, false, false, 0);
1483
1484 pWp->m_bIsolatedMark = true; // This is an isolated mark
1485 RoutePoint *pExisting =
1486 WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon);
1487 if (!pExisting) {
1488 if (NULL != pWayPointMan) pWayPointMan->AddRoutePoint(pWp);
1489 NavObj_dB::GetInstance().InsertRoutePoint(pWp);
1490 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1491 LLBBox wptbox;
1492 wptbox.Set(pWp->m_lat, pWp->m_lon, pWp->m_lat, pWp->m_lon);
1493 BBox.Expand(wptbox);
1494 } else {
1495 delete pWp;
1496 wpt_duplicates++;
1497 }
1498 } else if (!strcmp(object.name(), "trk")) {
1499 Track *pTrack = GPXLoadTrack1(object, b_full_viz, false, false, 0);
1500 if (InsertTrack(pTrack)) {
1501 NavObj_dB::GetInstance().InsertTrack(pTrack);
1502 }
1503 } else if (!strcmp(object.name(), "rte")) {
1504 Route *pRoute = GPXLoadRoute1(object, b_full_viz, false, false, 0, false);
1505 if (InsertRouteA(pRoute, this)) {
1506 NavObj_dB::GetInstance().InsertRoute(pRoute);
1507 if (b_compute_bbox && pRoute->IsVisible())
1508 BBox.Expand(pRoute->GetBBox());
1509 }
1510 }
1511 }
1512
1513 return true;
1514}
1515
1516int NavObjectCollection1::LoadAllGPXObjectsAsLayer(int layer_id,
1517 bool b_layerviz,
1518 wxCheckBoxState b_namesviz) {
1519 if (!pWayPointMan) return 0;
1520
1521 int n_obj = 0;
1522 pugi::xml_node objects = this->child("gpx");
1523
1524 for (pugi::xml_node object = objects.first_child(); object;
1525 object = object.next_sibling()) {
1526 if (!strcmp(object.name(), "wpt")) {
1527 RoutePoint *pWp = ::GPXLoadWaypoint1(object, _T("circle"), _T(""),
1528 b_namesviz != wxCHK_UNDETERMINED,
1529 true, b_layerviz, layer_id);
1530 if (b_namesviz != wxCHK_UNDETERMINED) {
1531 pWp->SetNameShown(b_namesviz == wxCHK_CHECKED);
1532 }
1533 pWp->m_bIsolatedMark = true; // This is an isolated mark
1534 pWayPointMan->AddRoutePoint(pWp);
1535 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1536 n_obj++;
1537 } else {
1538 if (!strcmp(object.name(), "trk")) {
1539 Track *pTrack =
1540 GPXLoadTrack1(object, false, true, b_layerviz, layer_id);
1541 n_obj++;
1542 InsertTrack(pTrack);
1543 } else if (!strcmp(object.name(), "rte")) {
1544 Route *pRoute =
1545 GPXLoadRoute1(object, true, true, b_layerviz, layer_id, false);
1546 n_obj++;
1547 InsertRouteA(pRoute, this);
1548 }
1549 }
1550 }
1551
1552 return n_obj;
1553}
1554
1555bool NavObjectCollection1::LoadAllGPXTrackObjects() {
1556 pugi::xml_node objects = this->child("gpx");
1557
1558 for (pugi::xml_node object = objects.first_child(); object;
1559 object = object.next_sibling()) {
1560 if (!strcmp(object.name(), "trk")) {
1561 Track *pTrack = GPXLoadTrack1(object, true, false, false, 0);
1562 InsertTrack(pTrack);
1563 }
1564 }
1565
1566 return true;
1567}
1568
1569bool NavObjectCollection1::LoadAllGPXRouteObjects() {
1570 pugi::xml_node objects = this->child("gpx");
1571
1572 for (pugi::xml_node object = objects.first_child(); object;
1573 object = object.next_sibling()) {
1574 if (!strcmp(object.name(), "rte")) {
1575 Route *route = GPXLoadRoute1(object, true, false, false, 0, false, true);
1576 InsertRouteA(route, nullptr);
1577 }
1578 }
1579
1580 return true;
1581}
1582
1583bool NavObjectCollection1::LoadAllGPXPointObjects() {
1584 pugi::xml_node objects = this->child("gpx");
1585
1586 for (pugi::xml_node object = objects.first_child(); object;
1587 object = object.next_sibling()) {
1588 if (!strcmp(object.name(), "wpt")) {
1589 RoutePoint *rp =
1590 GPXLoadWaypoint1(object, "circle", "", false, false, false, 0);
1591 rp->m_bIsolatedMark = true;
1592 pWayPointMan->AddRoutePoint(rp);
1593 }
1594 }
1595 return true;
1596}
1597
1598RoutePoint *WaypointExists(const wxString &name, double lat, double lon) {
1599 RoutePoint *pret = NULL;
1600 // if( g_bIsNewLayer ) return NULL;
1601 wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1602 while (node) {
1603 RoutePoint *pr = node->GetData();
1604
1605 // if( pr->m_bIsInLayer ) return NULL;
1606
1607 if (name == pr->GetName()) {
1608 if (fabs(lat - pr->m_lat) < 1.e-6 && fabs(lon - pr->m_lon) < 1.e-6) {
1609 pret = pr;
1610 break;
1611 }
1612 }
1613 node = node->GetNext();
1614 }
1615
1616 return pret;
1617}
1618
1619RoutePoint *WaypointExists(const wxString &guid) {
1620 wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1621 while (node) {
1622 RoutePoint *pr = node->GetData();
1623
1624 // if( pr->m_bIsInLayer ) return NULL;
1625
1626 if (guid == pr->m_GUID) {
1627 return pr;
1628 }
1629 node = node->GetNext();
1630 }
1631
1632 return NULL;
1633}
1634
1635bool WptIsInRouteList(RoutePoint *pr) {
1636 bool IsInList = false;
1637
1638 wxRouteListNode *node1 = pRouteList->GetFirst();
1639 while (node1) {
1640 Route *pRoute = node1->GetData();
1641 RoutePointList *pRoutePointList = pRoute->pRoutePointList;
1642
1643 wxRoutePointListNode *node2 = pRoutePointList->GetFirst();
1644 RoutePoint *prp;
1645
1646 while (node2) {
1647 prp = node2->GetData();
1648
1649 if (pr->IsSame(prp)) {
1650 IsInList = true;
1651 break;
1652 }
1653
1654 node2 = node2->GetNext();
1655 }
1656 node1 = node1->GetNext();
1657 }
1658 return IsInList;
1659}
1660
1661Route *RouteExists(const wxString &guid) {
1662 wxRouteListNode *route_node = pRouteList->GetFirst();
1663
1664 while (route_node) {
1665 Route *proute = route_node->GetData();
1666
1667 if (guid == proute->m_GUID) return proute;
1668
1669 route_node = route_node->GetNext();
1670 }
1671 return NULL;
1672}
1673
1674Route *RouteExists(Route *pTentRoute) {
1675 wxRouteListNode *route_node = pRouteList->GetFirst();
1676 while (route_node) {
1677 Route *proute = route_node->GetData();
1678
1679 if (proute->IsEqualTo(pTentRoute)) return proute;
1680
1681 route_node = route_node->GetNext(); // next route
1682 }
1683 return NULL;
1684}
1685
1686Track *TrackExists(const wxString &guid) {
1687 for (Track *ptrack : g_TrackList) {
1688 if (guid == ptrack->m_GUID) return ptrack;
1689 }
1690 return NULL;
1691}
Represents a waypoint or mark within the navigation system.
Definition route_point.h:70
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.
void SetCreateTime(wxDateTime dt)
Sets the create time of this RoutePoint in UTC.
wxString m_GUID
Globally Unique Identifier for the waypoint.
bool m_bIsolatedMark
Flag indicating if the waypoint is a standalone mark.
bool m_bShowNameData
Flag indicating if waypoint data should be shown with the name.
wxDateTime GetManualETD()
Retrieves the manually set Estimated Time of Departure for this waypoint, in UTC.
bool m_bIsInRoute
Flag indicating if this waypoint is part of a route.
bool m_bShowName
Flag indicating if the waypoint name should be shown.
wxString m_timestring
String representation of the waypoint creation time.
double GetPlannedSpeed()
Return the planned speed associated with this waypoint.
bool m_bIsVisible
Flag indicating if the waypoint should be drawn on the chart.
bool m_bIsInLayer
Flag indicating if the waypoint belongs to a layer.
bool m_btemp
Flag indicating if this is a temporary waypoint.
bool m_manual_etd
Flag indicating whether the ETD has been manually set by the user.
int m_LayerID
Layer identifier if the waypoint belongs to a layer.
int m_iWaypointRangeRingsStepUnits
Units for the range rings step (0=nm, 1=km).
wxDateTime GetCreateTime(void)
Returns the Create Time of this RoutePoint in UTC.
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.
Definition route.h:98
double m_PlannedSpeed
Default planned speed for the route in knots.
Definition route.h:320
wxString m_RouteStartString
Name or description of the route's starting point.
Definition route.h:251
wxString m_RouteDescription
Additional descriptive information about the route.
Definition route.h:261
RoutePointList * pRoutePointList
Ordered list of waypoints (RoutePoints) that make up this route.
Definition route.h:335
wxString m_Colour
Color name for rendering the route on the chart.
Definition route.h:345
wxString m_RouteEndString
Name or description of the route's ending point.
Definition route.h:256
bool m_btemp
Flag indicating if this is a temporary route.
Definition route.h:350
wxPenStyle m_style
Style of the route line when rendered on the chart.
Definition route.h:292
wxString m_TimeDisplayFormat
Format for displaying times in the UI.
Definition route.h:330
int m_width
Width of the route line in pixels when rendered on the chart.
Definition route.h:287
wxString m_RouteNameString
User-assigned name for the route.
Definition route.h:246
wxString m_GUID
Globally unique identifier for this route.
Definition route.h:272
wxDateTime m_PlannedDeparture
Planned departure time for the route, in UTC.
Definition route.h:325
bool m_bIsInLayer
Flag indicating whether this route belongs to a layer.
Definition route.h:277
HyperlinkList * m_HyperlinkList
List of hyperlinks associated with this route.
Definition route.h:360
int m_LayerID
Identifier of the layer containing this route.
Definition route.h:282
bool DeleteRoute(Route *pRoute)
Definition routeman.cpp:856
Represents a single point in a track.
Definition track.h:53
Represents a track, which is a series of connected track points.
Definition track.h:111
bool AddRoutePoint(RoutePoint *prp)
Add a point to list which owns it.
Class NavObj_dB.