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->push_back(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->push_back(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->push_back(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->size()) {
695 for (Hyperlink *link : *pr->m_HyperlinkList) {
696 pugi::xml_node child_link = node.append_child("link");
697 ;
698 wxCharBuffer buffer = link->Link.ToUTF8();
699 if (buffer.data()) child_link.append_attribute("href") = buffer.data();
700
701 buffer = link->DescrText.ToUTF8();
702 if (buffer.data()) {
703 child = child_link.append_child("text");
704 child.append_child(pugi::node_pcdata).set_value(buffer.data());
705 }
706
707 buffer = link->LType.ToUTF8();
708 if (buffer.data() && strlen(buffer.data()) > 0) {
709 child = child_link.append_child("type");
710 child.append_child(pugi::node_pcdata).set_value(buffer.data());
711 }
712 }
713 }
714 }
715
716 if (flags & OUT_SYM_FORCE) {
717 child = node.append_child("sym");
718 if (!pr->GetIconName().IsEmpty()) {
719 child.append_child(pugi::node_pcdata)
720 .set_value(pr->GetIconName().mb_str());
721 } else {
722 child.append_child("empty");
723 }
724 }
725
726 if (flags & OUT_TYPE) {
727 child = node.append_child("type");
728 child.append_child(pugi::node_pcdata).set_value("WPT");
729 }
730
731 if ((flags & OUT_GUID) || (flags & OUT_VIZ) || (flags & OUT_VIZ_NAME) ||
732 (flags & OUT_SHARED) || (flags & OUT_EXTENSION) ||
733 (flags & OUT_TIDE_STATION) || (flags & OUT_RTE_PROPERTIES)) {
734 pugi::xml_node child_ext = node.append_child("extensions");
735
736 if (!pr->m_GUID.IsEmpty() && (flags & OUT_GUID)) {
737 child = child_ext.append_child("opencpn:guid");
738 child.append_child(pugi::node_pcdata).set_value(pr->m_GUID.mb_str());
739 }
740
741 if ((flags & OUT_VIZ) && !pr->m_bIsVisible) {
742 child = child_ext.append_child("opencpn:viz");
743 child.append_child(pugi::node_pcdata).set_value("0");
744 }
745
746 if ((flags & OUT_VIZ_NAME) && pr->m_bShowName) {
747 child = child_ext.append_child("opencpn:viz_name");
748 child.append_child(pugi::node_pcdata).set_value("1");
749 }
750
751 if ((flags & OUT_SHARED) && pr->IsShared()) {
752 child = child_ext.append_child("opencpn:shared");
753 child.append_child(pugi::node_pcdata).set_value("1");
754 }
755 if (flags & OUT_ARRIVAL_RADIUS) {
756 child = child_ext.append_child("opencpn:arrival_radius");
757 s.Printf(_T("%.3f"), pr->GetWaypointArrivalRadius());
758 child.append_child(pugi::node_pcdata).set_value(s.mbc_str());
759 }
760 if (flags & OUT_WAYPOINT_RANGE_RINGS) {
761 child = child_ext.append_child("opencpn:waypoint_range_rings");
762 pugi::xml_attribute viz = child.append_attribute("visible");
763 viz.set_value(pr->m_bShowWaypointRangeRings);
764 pugi::xml_attribute number = child.append_attribute("number");
765 number.set_value(pr->m_iWaypointRangeRingsNumber);
766 pugi::xml_attribute step = child.append_attribute("step");
767 step.set_value(pr->m_fWaypointRangeRingsStep);
768 pugi::xml_attribute units = child.append_attribute("units");
769 units.set_value(pr->m_iWaypointRangeRingsStepUnits);
770
771 // Color specification in GPX file must be fully opaque
772 if (pr->m_wxcWaypointRangeRingsColour.IsOk()) {
776 pr->m_wxcWaypointRangeRingsColour.Blue(), wxALPHA_OPAQUE);
777 } else {
778 pr->m_wxcWaypointRangeRingsColour.Set(0, 0, 0, wxALPHA_OPAQUE);
779 }
780
781 pugi::xml_attribute colour = child.append_attribute("colour");
782 colour.set_value(
783 pr->m_wxcWaypointRangeRingsColour.GetAsString(wxC2S_HTML_SYNTAX)
784 .utf8_str());
785 }
786 if (flags & OUT_WAYPOINT_SCALE) {
787 child = child_ext.append_child("opencpn:scale_min_max");
788 pugi::xml_attribute use = child.append_attribute("UseScale");
789 use.set_value(pr->GetUseSca());
790 pugi::xml_attribute sca = child.append_attribute("ScaleMin");
791 sca.set_value(pr->GetScaMin());
792 pugi::xml_attribute max = child.append_attribute("ScaleMax");
793 max.set_value(pr->GetScaMax());
794 }
795 if ((flags & OUT_TIDE_STATION) && !pr->m_TideStation.IsEmpty()) {
796 child = child_ext.append_child("opencpn:tidestation");
797 child.append_child(pugi::node_pcdata)
798 .set_value(pr->m_TideStation.mb_str());
799 }
800 if ((flags & OUT_RTE_PROPERTIES) &&
801 (pr->GetPlannedSpeed() > 0.0001 || pr->m_manual_etd)) {
802 child = child_ext.append_child("opencpn:rte_properties");
803 if (pr->GetPlannedSpeed() > 0.0001) {
804 pugi::xml_attribute use = child.append_attribute("planned_speed");
805 use.set_value(
806 wxString::Format(_T("%.1lf"), pr->GetPlannedSpeed()).mb_str());
807 }
808 if (pr->m_manual_etd && pr->GetManualETD().IsValid()) {
809 pugi::xml_attribute use = child.append_attribute("etd");
810 // Currently, the serialization format is YYYY-MM-DDTHH:MM:SS
811 // without timezone information, e.g., etd="2025-04-03T20:00:27"
812 // TODO: serialize using ISO 8601 or RFC 3339 format to ensure
813 // the serialized date/time is unambiguous.
814 use.set_value(pr->GetManualETD().FormatISOCombined().mb_str());
815 }
816 }
817 }
818
819 return true;
820}
821
822static bool GPXCreateTrkpt(pugi::xml_node node, TrackPoint *pt,
823 unsigned int flags) {
824 wxString s;
825 pugi::xml_node child;
827
828 s.Printf(_T("%.9f"), pt->m_lat);
829 node.append_attribute("lat") = s.mb_str();
830 s.Printf(_T("%.9f"), pt->m_lon);
831 node.append_attribute("lon") = s.mb_str();
832
833 if (flags & OUT_TIME && pt->HasValidTimestamp()) {
834 child = node.append_child("time");
835 child.append_child(pugi::node_pcdata).set_value(pt->GetTimeString());
836 }
837
838 return true;
839}
840
841static bool GPXCreateTrk(pugi::xml_node node, Track *pTrack,
842 unsigned int flags) {
843 pugi::xml_node child;
844
845 if (pTrack->GetName().Len()) {
846 wxCharBuffer buffer = pTrack->GetName().ToUTF8();
847 if (buffer.data()) {
848 child = node.append_child("name");
849 child.append_child(pugi::node_pcdata).set_value(buffer.data());
850 }
851 }
852
853 if (pTrack->m_TrackDescription.Len()) {
854 wxCharBuffer buffer = pTrack->m_TrackDescription.ToUTF8();
855 if (buffer.data()) {
856 child = node.append_child("desc");
857 child.append_child(pugi::node_pcdata).set_value(buffer.data());
858 }
859 }
860
861 // Hyperlinks
862 HyperlinkList *linklist = pTrack->m_TrackHyperlinkList;
863 if (linklist && linklist->size()) {
864 for (Hyperlink *link : *linklist) {
865 pugi::xml_node child_link = node.append_child("link");
866 wxCharBuffer buffer = link->Link.ToUTF8();
867 if (buffer.data()) child_link.append_attribute("href") = buffer.data();
868
869 buffer = link->DescrText.ToUTF8();
870 if (buffer.data()) {
871 child = child_link.append_child("text");
872 child.append_child(pugi::node_pcdata).set_value(buffer.data());
873 }
874
875 buffer = link->LType.ToUTF8();
876 if (buffer.data() && strlen(buffer.data()) > 0) {
877 child = child_link.append_child("type");
878 child.append_child(pugi::node_pcdata).set_value(buffer.data());
879 }
880 }
881 }
882
883 pugi::xml_node child_ext = node.append_child("extensions");
884
885 child = child_ext.append_child("opencpn:guid");
886 child.append_child(pugi::node_pcdata).set_value(pTrack->m_GUID.mb_str());
887
888 child = child_ext.append_child("opencpn:viz");
889 child.append_child(pugi::node_pcdata)
890 .set_value(pTrack->IsVisible() == true ? "1" : "0");
891
892 if (pTrack->m_TrackStartString.Len()) {
893 wxCharBuffer buffer = pTrack->m_TrackStartString.ToUTF8();
894 if (buffer.data()) {
895 child = child_ext.append_child("opencpn:start");
896 child.append_child(pugi::node_pcdata).set_value(buffer.data());
897 }
898 }
899
900 if (pTrack->m_TrackEndString.Len()) {
901 wxCharBuffer buffer = pTrack->m_TrackEndString.ToUTF8();
902 if (buffer.data()) {
903 child = child_ext.append_child("opencpn:end");
904 child.append_child(pugi::node_pcdata).set_value(buffer.data());
905 }
906 }
907
908 if (pTrack->m_width != WIDTH_UNDEFINED ||
909 pTrack->m_style != wxPENSTYLE_INVALID) {
910 child = child_ext.append_child("opencpn:style");
911
912 if (pTrack->m_width != WIDTH_UNDEFINED)
913 child.append_attribute("width") = pTrack->m_width;
914 if (pTrack->m_style != wxPENSTYLE_INVALID)
915 child.append_attribute("style") = pTrack->m_style;
916 }
917
918 if (pTrack->m_Colour != wxEmptyString) {
919 pugi::xml_node gpxx_ext = child_ext.append_child("gpxx:TrackExtension");
920 child = gpxx_ext.append_child("gpxx:DisplayColor");
921 child.append_child(pugi::node_pcdata).set_value(pTrack->m_Colour.mb_str());
922 }
923
924 if (flags & RT_OUT_NO_RTPTS) return true;
925
926 int node2 = 0;
927 TrackPoint *prp;
928
929 unsigned short int GPXTrkSegNo1 = 1;
930
931 do {
932 unsigned short int GPXTrkSegNo2 = GPXTrkSegNo1;
933
934 pugi::xml_node seg = node.append_child("trkseg");
935
936 while (node2 < pTrack->GetnPoints()) {
937 prp = pTrack->GetPoint(node2);
938 GPXTrkSegNo1 = prp->m_GPXTrkSegNo;
939 if (GPXTrkSegNo1 != GPXTrkSegNo2) break;
940
941 GPXCreateTrkpt(seg.append_child("trkpt"), prp, OPT_TRACKPT);
942
943 node2++;
944 }
945 } while (node2 < pTrack->GetnPoints());
946
947 return true;
948}
949
950static bool GPXCreateRoute(pugi::xml_node node, Route *pRoute) {
951 pugi::xml_node child;
952
953 if (pRoute->m_RouteNameString.Len()) {
954 wxCharBuffer buffer = pRoute->m_RouteNameString.ToUTF8();
955 if (buffer.data()) {
956 child = node.append_child("name");
957 child.append_child(pugi::node_pcdata).set_value(buffer.data());
958 }
959 }
960
961 if (pRoute->m_RouteDescription.Len()) {
962 wxCharBuffer buffer = pRoute->m_RouteDescription.ToUTF8();
963 if (buffer.data()) {
964 child = node.append_child("desc");
965 child.append_child(pugi::node_pcdata).set_value(buffer.data());
966 }
967 }
968
969 // Hyperlinks
970 HyperlinkList *linklist = pRoute->m_HyperlinkList;
971 if (linklist && linklist->size()) {
972 for (Hyperlink *link : *linklist) {
973 pugi::xml_node child_link = node.append_child("link");
974 wxCharBuffer buffer = link->Link.ToUTF8();
975 if (buffer.data()) child_link.append_attribute("href") = buffer.data();
976
977 buffer = link->DescrText.ToUTF8();
978 if (buffer.data()) {
979 child = child_link.append_child("text");
980 child.append_child(pugi::node_pcdata).set_value(buffer.data());
981 }
982 buffer = link->LType.ToUTF8();
983 if (buffer.data() && strlen(buffer.data()) > 0) {
984 child = child_link.append_child("type");
985 child.append_child(pugi::node_pcdata).set_value(buffer.data());
986 }
987 }
988 }
989
990 pugi::xml_node child_ext = node.append_child("extensions");
991
992 child = child_ext.append_child("opencpn:guid");
993 child.append_child(pugi::node_pcdata).set_value(pRoute->m_GUID.mb_str());
994
995 child = child_ext.append_child("opencpn:viz");
996 child.append_child(pugi::node_pcdata)
997 .set_value(pRoute->IsVisible() == true ? "1" : "0");
998
999 if (pRoute->ContainsSharedWP()) {
1000 child = child_ext.append_child("opencpn:sharedWPviz");
1001 child.append_child(pugi::node_pcdata)
1002 .set_value(pRoute->GetSharedWPViz() == true ? "1" : "0");
1003 }
1004
1005 if (pRoute->m_RouteStartString.Len()) {
1006 wxCharBuffer buffer = pRoute->m_RouteStartString.ToUTF8();
1007 if (buffer.data()) {
1008 child = child_ext.append_child("opencpn:start");
1009 child.append_child(pugi::node_pcdata).set_value(buffer.data());
1010 }
1011 }
1012
1013 if (pRoute->m_RouteEndString.Len()) {
1014 wxCharBuffer buffer = pRoute->m_RouteEndString.ToUTF8();
1015 if (buffer.data()) {
1016 child = child_ext.append_child("opencpn:end");
1017 child.append_child(pugi::node_pcdata).set_value(buffer.data());
1018 }
1019 }
1020
1021 if (pRoute->m_PlannedSpeed != ROUTE_DEFAULT_SPEED) {
1022 child = child_ext.append_child("opencpn:planned_speed");
1023 wxString s;
1024 s.Printf(_T("%.2f"), pRoute->m_PlannedSpeed);
1025 child.append_child(pugi::node_pcdata).set_value(s.mb_str());
1026 }
1027
1028 if (pRoute->m_PlannedDeparture.IsValid()) {
1029 child = child_ext.append_child("opencpn:planned_departure");
1030 wxString t = pRoute->m_PlannedDeparture.FormatISODate()
1031 .Append(_T("T"))
1032 .Append(pRoute->m_PlannedDeparture.FormatISOTime())
1033 .Append(_T("Z"));
1034 child.append_child(pugi::node_pcdata).set_value(t.mb_str());
1035 }
1036
1037 child = child_ext.append_child("opencpn:time_display");
1038 child.append_child(pugi::node_pcdata)
1039 .set_value(pRoute->m_TimeDisplayFormat.mb_str());
1040
1041 if (pRoute->m_width != WIDTH_UNDEFINED ||
1042 pRoute->m_style != wxPENSTYLE_INVALID) {
1043 child = child_ext.append_child("opencpn:style");
1044
1045 if (pRoute->m_width != WIDTH_UNDEFINED)
1046 child.append_attribute("width") = pRoute->m_width;
1047 if (pRoute->m_style != wxPENSTYLE_INVALID)
1048 child.append_attribute("style") = pRoute->m_style;
1049 }
1050
1051 pugi::xml_node gpxx_ext = child_ext.append_child("gpxx:RouteExtension");
1052 child = gpxx_ext.append_child("gpxx:IsAutoNamed");
1053 child.append_child(pugi::node_pcdata).set_value("false");
1054
1055 if (pRoute->m_Colour != wxEmptyString) {
1056 child = gpxx_ext.append_child("gpxx:DisplayColor");
1057 child.append_child(pugi::node_pcdata).set_value(pRoute->m_Colour.mb_str());
1058 }
1059
1060 RoutePointList *pRoutePointList = pRoute->pRoutePointList;
1061 wxRoutePointListNode *node2 = pRoutePointList->GetFirst();
1062 RoutePoint *prp;
1063
1064 while (node2) {
1065 prp = node2->GetData();
1066
1067 GPXCreateWpt(node.append_child("rtept"), prp, OPT_ROUTEPT);
1068
1069 node2 = node2->GetNext();
1070 }
1071
1072 return true;
1073}
1074
1075bool InsertRouteA(Route *pTentRoute, NavObjectCollection1 *navobj) {
1076 if (!pTentRoute) return false;
1077
1078 bool bAddroute = true;
1079 // If the route has only 1 point, don't load it.
1080 if (pTentRoute->GetnPoints() < 2) bAddroute = false;
1081
1082 // TODO All this trouble for a tentative route.......Should make some
1083 // Route methods????
1084 if (bAddroute) {
1085 pRouteList->Append(pTentRoute);
1086
1087 // Do the (deferred) calculation of BBox
1088 pTentRoute->FinalizeForRendering();
1089
1090 // Add the selectable points and segments
1091
1092 int ip = 0;
1093 float prev_rlat = 0., prev_rlon = 0.;
1094 RoutePoint *prev_pConfPoint = NULL;
1095
1096 wxRoutePointListNode *node = pTentRoute->pRoutePointList->GetFirst();
1097 while (node) {
1098 RoutePoint *prp = node->GetData();
1099
1100 if (ip)
1101 pSelect->AddSelectableRouteSegment(prev_rlat, prev_rlon, prp->m_lat,
1102 prp->m_lon, prev_pConfPoint, prp,
1103 pTentRoute);
1104 pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
1105 prev_rlat = prp->m_lat;
1106 prev_rlon = prp->m_lon;
1107 prev_pConfPoint = prp;
1108
1109 ip++;
1110
1111 node = node->GetNext();
1112 }
1113 } else {
1114 // walk the route, deleting points used only by this route
1115 wxRoutePointListNode *pnode = (pTentRoute->pRoutePointList)->GetFirst();
1116 while (pnode) {
1117 RoutePoint *prp = pnode->GetData();
1118
1119 // check all other routes to see if this point appears in any other route
1120 Route *pcontainer_route = g_pRouteMan->FindRouteContainingWaypoint(prp);
1121
1122 if (pcontainer_route == NULL) {
1123 prp->m_bIsInRoute =
1124 false; // Take this point out of this (and only) track/route
1125 if (!prp->IsShared()) {
1126 if (navobj) {
1127 delete prp;
1128 }
1129 }
1130 }
1131
1132 pnode = pnode->GetNext();
1133 }
1134
1135 delete pTentRoute;
1136 }
1137 return bAddroute;
1138}
1139
1140bool InsertTrack(Track *pTentTrack, bool bApplyChanges) {
1141 if (!pTentTrack) return false;
1142
1143 bool bAddtrack = true;
1144 // If the track has only 1 point, don't load it.
1145 // This usually occurs if some points were discarded as being co-incident.
1146 if (!bApplyChanges && pTentTrack->GetnPoints() < 2) bAddtrack = false;
1147
1148 // TODO All this trouble for a tentative track.......Should make some
1149 // Track methods????
1150 if (bAddtrack) {
1151 g_TrackList.push_back(pTentTrack);
1152
1153 // Do the (deferred) calculation of Track BBox
1154 // pTentTrack->FinalizeForRendering();
1155
1156 // Add the selectable points and segments
1157
1158 float prev_rlat = 0., prev_rlon = 0.;
1159 TrackPoint *prev_pConfPoint = NULL;
1160
1161 for (int i = 0; i < pTentTrack->GetnPoints(); i++) {
1162 TrackPoint *prp = pTentTrack->GetPoint(i);
1163
1164 if (i)
1165 pSelect->AddSelectableTrackSegment(prev_rlat, prev_rlon, prp->m_lat,
1166 prp->m_lon, prev_pConfPoint, prp,
1167 pTentTrack);
1168
1169 prev_rlat = prp->m_lat;
1170 prev_rlon = prp->m_lon;
1171 prev_pConfPoint = prp;
1172 }
1173 } else
1174 delete pTentTrack;
1175
1176 return bAddtrack;
1177}
1178
1179bool InsertWpt(RoutePoint *pWp, bool overwrite) {
1180 bool res = false;
1181 RoutePoint *pExisting =
1182 WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon);
1183 if (!pExisting || overwrite) {
1184 if (NULL != pWayPointMan) {
1185 if (pExisting) {
1186 pWayPointMan->DestroyWaypoint(pExisting);
1187 }
1188 pWayPointMan->AddRoutePoint(pWp);
1189 res = true;
1190 }
1191 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1192 }
1193 return res;
1194}
1195
1196static void UpdateRouteA(Route *pTentRoute, NavObjectCollection1 *navobj) {
1197 if (!pTentRoute) return;
1198 if (pTentRoute->GetnPoints() < 2) return;
1199
1200 // first delete the route to be modified if exists
1201 Route *pExisting = ::RouteExists(pTentRoute->m_GUID);
1202 if (pExisting) {
1203 g_pRouteMan->DeleteRoute(pExisting);
1204 }
1205
1206 // create a new route
1207 Route *pChangeRoute = new Route();
1208 pRouteList->Append(pChangeRoute);
1209
1210 // update new route keeping the same gui
1211 pChangeRoute->m_GUID = pTentRoute->m_GUID;
1212 pChangeRoute->m_RouteNameString = pTentRoute->m_RouteNameString;
1213 pChangeRoute->m_RouteStartString = pTentRoute->m_RouteStartString;
1214 pChangeRoute->m_RouteEndString = pTentRoute->m_RouteEndString;
1215 pChangeRoute->SetVisible(pTentRoute->IsVisible());
1216
1217 // Add points and segments to new route
1218 int ip = 0;
1219 float prev_rlat = 0., prev_rlon = 0.;
1220 RoutePoint *prev_pConfPoint = NULL;
1221
1222 wxRoutePointListNode *node = pTentRoute->pRoutePointList->GetFirst();
1223 while (node) {
1224 RoutePoint *prp = node->GetData();
1225
1226 // if some wpts have been not deleted, that meens they should be used in
1227 // other routes or are isolated way points so need to be updated
1228 RoutePoint *ex_rp = ::WaypointExists(prp->m_GUID);
1229 if (ex_rp) {
1230 pSelect->DeleteSelectableRoutePoint(ex_rp);
1231 ex_rp->m_lat = prp->m_lat;
1232 ex_rp->m_lon = prp->m_lon;
1233 ex_rp->SetIconName(prp->GetIconName());
1235 ex_rp->SetName(prp->GetName());
1236 ex_rp->m_TideStation = prp->m_TideStation;
1237 ex_rp->SetPlannedSpeed(prp->GetPlannedSpeed());
1238 pChangeRoute->AddPoint(ex_rp);
1239 pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, ex_rp);
1240
1241 } else {
1242 pChangeRoute->AddPoint(prp);
1243 pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
1244 pWayPointMan->AddRoutePoint(prp);
1245 }
1246
1247 if (ip)
1248 pSelect->AddSelectableRouteSegment(prev_rlat, prev_rlon, prp->m_lat,
1249 prp->m_lon, prev_pConfPoint, prp,
1250 pChangeRoute);
1251 prev_rlat = prp->m_lat;
1252 prev_rlon = prp->m_lon;
1253 prev_pConfPoint = prp;
1254
1255 ip++;
1256
1257 node = node->GetNext();
1258 }
1259 // Do the (deferred) calculation of BBox
1260 pChangeRoute->FinalizeForRendering();
1261}
1262
1263Route *FindRouteContainingWaypoint(RoutePoint *pWP) {
1264 wxRouteListNode *node = pRouteList->GetFirst();
1265 while (node) {
1266 Route *proute = node->GetData();
1267
1268 wxRoutePointListNode *pnode = (proute->pRoutePointList)->GetFirst();
1269 while (pnode) {
1270 RoutePoint *prp = pnode->GetData();
1271 if (prp == pWP) return proute;
1272 pnode = pnode->GetNext();
1273 }
1274
1275 node = node->GetNext();
1276 }
1277
1278 return NULL; // not found
1279}
1280
1281bool NavObjectCollection1::CreateNavObjGPXPoints(void) {
1282 // Iterate over the Routepoint list, creating Nodes for
1283 // Routepoints that are not in any Route
1284 // as indicated by m_bIsolatedMark == false
1285
1286 if (!pWayPointMan) return false;
1287
1288 wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1289
1290 RoutePoint *pr;
1291
1292 while (node) {
1293 pr = node->GetData();
1294
1295 if ((pr->m_bIsolatedMark) && !(pr->m_bIsInLayer) && !(pr->m_btemp)) {
1296 pugi::xml_node doc = root();
1297 pugi::xml_node gpx = doc.first_child();
1298 pugi::xml_node new_node = gpx.append_child("wpt");
1299
1300 GPXCreateWpt(new_node, pr, OPT_WPT);
1301 }
1302 node = node->GetNext();
1303 }
1304
1305 return true;
1306}
1307
1308bool NavObjectCollection1::CreateNavObjGPXRoutes(void) {
1309 // Routes
1310 if (!pRouteList) return false;
1311
1312 wxRouteListNode *node1 = pRouteList->GetFirst();
1313 while (node1) {
1314 Route *pRoute = node1->GetData();
1315
1316 if (!pRoute->m_bIsInLayer && !pRoute->m_btemp) {
1317 pugi::xml_node doc = root();
1318 pugi::xml_node gpx = doc.first_child();
1319 pugi::xml_node new_node = gpx.append_child("rte");
1320
1321 GPXCreateRoute(new_node, pRoute);
1322 }
1323
1324 node1 = node1->GetNext();
1325 }
1326
1327 return true;
1328}
1329
1330bool NavObjectCollection1::CreateNavObjGPXTracks(void) {
1331 // Tracks
1332 for (Track *pTrack : g_TrackList) {
1333 if (pTrack->GetnPoints()) {
1334 if (!pTrack->m_bIsInLayer && !pTrack->m_btemp) {
1335 pugi::xml_node doc = root();
1336 pugi::xml_node gpx = doc.first_child();
1337 pugi::xml_node new_node = gpx.append_child("trk");
1338
1339 GPXCreateTrk(new_node, pTrack, 0);
1340 }
1341 }
1342 }
1343
1344 return true;
1345}
1346
1347bool NavObjectCollection1::CreateAllGPXObjects() {
1348 SetRootGPXNode();
1349
1350 // CreateNavObjGPXPoints();
1351 // CreateNavObjGPXRoutes();
1352 // CreateNavObjGPXTracks();
1353
1354 return true;
1355}
1356
1357bool NavObjectCollection1::AddGPXRoute(Route *pRoute) {
1358 SetRootGPXNode();
1359 pugi::xml_node doc = root();
1360 pugi::xml_node gpx = doc.first_child();
1361 pugi::xml_node new_node = gpx.append_child("rte");
1362
1363 GPXCreateRoute(new_node, pRoute);
1364 return true;
1365}
1366
1367bool NavObjectCollection1::AddGPXTrack(Track *pTrk) {
1368 SetRootGPXNode();
1369 pugi::xml_node doc = root();
1370 pugi::xml_node gpx = doc.first_child();
1371 pugi::xml_node new_node = gpx.append_child("trk");
1372
1373 GPXCreateTrk(new_node, pTrk, 0);
1374 return true;
1375}
1376
1377bool NavObjectCollection1::AddGPXWaypoint(RoutePoint *pWP) {
1378 SetRootGPXNode();
1379 pugi::xml_node doc = root();
1380 pugi::xml_node gpx = doc.first_child();
1381 pugi::xml_node new_node = gpx.append_child("wpt");
1382
1383 GPXCreateWpt(new_node, pWP, OPT_WPT);
1384 return true;
1385}
1386
1387void NavObjectCollection1::AddGPXRoutesList(RouteList *pRoutes) {
1388 SetRootGPXNode();
1389
1390 wxRouteListNode *pRoute = pRoutes->GetFirst();
1391 while (pRoute) {
1392 Route *pRData = pRoute->GetData();
1393 AddGPXRoute(pRData);
1394 pRoute = pRoute->GetNext();
1395 }
1396}
1397
1398void NavObjectCollection1::AddGPXTracksList(std::vector<Track *> *pTracks) {
1399 SetRootGPXNode();
1400
1401 for (Track *pRData : *pTracks) {
1402 AddGPXTrack(pRData);
1403 }
1404}
1405
1406bool NavObjectCollection1::AddGPXPointsList(RoutePointList *pRoutePoints) {
1407 SetRootGPXNode();
1408
1409 wxRoutePointListNode *pRoutePointNode = pRoutePoints->GetFirst();
1410 while (pRoutePointNode) {
1411 RoutePoint *pRP = pRoutePointNode->GetData();
1412 AddGPXWaypoint(pRP);
1413 pRoutePointNode = pRoutePointNode->GetNext();
1414 }
1415
1416 return true;
1417}
1418
1419void NavObjectCollection1::SetRootGPXNode(void) {
1420 if (!strlen(first_child().name())) {
1421 pugi::xml_node gpx_root = append_child("gpx");
1422 gpx_root.append_attribute("version") = "1.1";
1423 gpx_root.append_attribute("creator") = "OpenCPN";
1424 gpx_root.append_attribute("xmlns:xsi") =
1425 "http://www.w3.org/2001/XMLSchema-instance";
1426 gpx_root.append_attribute("xmlns") = "http://www.topografix.com/GPX/1/1";
1427 gpx_root.append_attribute("xmlns:gpxx") =
1428 "http://www.garmin.com/xmlschemas/GpxExtensions/v3";
1429 gpx_root.append_attribute("xsi:schemaLocation") =
1430 "http://www.topografix.com/GPX/1/1 "
1431 "http://www.topografix.com/GPX/1/1/gpx.xsd "
1432 "http://www.garmin.com/xmlschemas/GpxExtensions/v3 "
1433 "http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd";
1434 gpx_root.append_attribute("xmlns:opencpn") = "http://www.opencpn.org";
1435 }
1436}
1437
1438bool NavObjectCollection1::IsOpenCPN() {
1439 for (pugi::xml_attribute attr = root().first_child().first_attribute(); attr;
1440 attr = attr.next_attribute())
1441 if (!strcmp(attr.name(), "creator") && !strcmp(attr.value(), "OpenCPN"))
1442 return true;
1443 return false;
1444}
1445
1446bool NavObjectCollection1::SaveFile(const wxString filename) {
1447 wxString tmp_filename = filename + ".tmp";
1448 if (wxFileExists(tmp_filename)) {
1449 wxRemoveFile(tmp_filename);
1450 }
1451 save_file(tmp_filename.fn_str(), " ");
1452 wxRenameFile(tmp_filename.fn_str(), filename.fn_str(), true);
1453 return true;
1454}
1455
1456bool NavObjectCollection1::LoadAllGPXObjects(bool b_full_viz,
1457 int &wpt_duplicates,
1458 bool b_compute_bbox) {
1459 wpt_duplicates = 0;
1460 pugi::xml_node objects = this->child("gpx");
1461
1462 for (pugi::xml_node object = objects.first_child(); object;
1463 object = object.next_sibling()) {
1464 if (!strcmp(object.name(), "wpt")) {
1465 RoutePoint *pWp = ::GPXLoadWaypoint1(object, _T("circle"), _T(""),
1466 b_full_viz, false, false, 0);
1467
1468 pWp->m_bIsolatedMark = true; // This is an isolated mark
1469 RoutePoint *pExisting =
1470 WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon);
1471 if (!pExisting) {
1472 if (NULL != pWayPointMan) pWayPointMan->AddRoutePoint(pWp);
1473 NavObj_dB::GetInstance().InsertRoutePoint(pWp);
1474 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1475 LLBBox wptbox;
1476 wptbox.Set(pWp->m_lat, pWp->m_lon, pWp->m_lat, pWp->m_lon);
1477 BBox.Expand(wptbox);
1478 } else {
1479 delete pWp;
1480 wpt_duplicates++;
1481 }
1482 } else if (!strcmp(object.name(), "trk")) {
1483 Track *pTrack = GPXLoadTrack1(object, b_full_viz, false, false, 0);
1484 if (InsertTrack(pTrack)) {
1485 NavObj_dB::GetInstance().InsertTrack(pTrack);
1486 }
1487 } else if (!strcmp(object.name(), "rte")) {
1488 Route *pRoute = GPXLoadRoute1(object, b_full_viz, false, false, 0, false);
1489 if (InsertRouteA(pRoute, this)) {
1490 NavObj_dB::GetInstance().InsertRoute(pRoute);
1491 if (b_compute_bbox && pRoute->IsVisible())
1492 BBox.Expand(pRoute->GetBBox());
1493 }
1494 }
1495 }
1496
1497 return true;
1498}
1499
1500int NavObjectCollection1::LoadAllGPXObjectsAsLayer(int layer_id,
1501 bool b_layerviz,
1502 wxCheckBoxState b_namesviz) {
1503 if (!pWayPointMan) return 0;
1504
1505 int n_obj = 0;
1506 pugi::xml_node objects = this->child("gpx");
1507
1508 for (pugi::xml_node object = objects.first_child(); object;
1509 object = object.next_sibling()) {
1510 if (!strcmp(object.name(), "wpt")) {
1511 RoutePoint *pWp = ::GPXLoadWaypoint1(object, _T("circle"), _T(""),
1512 b_namesviz != wxCHK_UNDETERMINED,
1513 true, b_layerviz, layer_id);
1514 if (b_namesviz != wxCHK_UNDETERMINED) {
1515 pWp->SetNameShown(b_namesviz == wxCHK_CHECKED);
1516 }
1517 pWp->m_bIsolatedMark = true; // This is an isolated mark
1518 pWayPointMan->AddRoutePoint(pWp);
1519 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1520 n_obj++;
1521 } else {
1522 if (!strcmp(object.name(), "trk")) {
1523 Track *pTrack =
1524 GPXLoadTrack1(object, false, true, b_layerviz, layer_id);
1525 n_obj++;
1526 InsertTrack(pTrack);
1527 } else if (!strcmp(object.name(), "rte")) {
1528 Route *pRoute =
1529 GPXLoadRoute1(object, true, true, b_layerviz, layer_id, false);
1530 n_obj++;
1531 InsertRouteA(pRoute, this);
1532 }
1533 }
1534 }
1535
1536 return n_obj;
1537}
1538
1539bool NavObjectCollection1::LoadAllGPXTrackObjects() {
1540 pugi::xml_node objects = this->child("gpx");
1541
1542 for (pugi::xml_node object = objects.first_child(); object;
1543 object = object.next_sibling()) {
1544 if (!strcmp(object.name(), "trk")) {
1545 Track *pTrack = GPXLoadTrack1(object, true, false, false, 0);
1546 InsertTrack(pTrack);
1547 }
1548 }
1549
1550 return true;
1551}
1552
1553bool NavObjectCollection1::LoadAllGPXRouteObjects() {
1554 pugi::xml_node objects = this->child("gpx");
1555
1556 for (pugi::xml_node object = objects.first_child(); object;
1557 object = object.next_sibling()) {
1558 if (!strcmp(object.name(), "rte")) {
1559 Route *route = GPXLoadRoute1(object, true, false, false, 0, false, true);
1560 InsertRouteA(route, nullptr);
1561 }
1562 }
1563
1564 return true;
1565}
1566
1567bool NavObjectCollection1::LoadAllGPXPointObjects() {
1568 pugi::xml_node objects = this->child("gpx");
1569
1570 for (pugi::xml_node object = objects.first_child(); object;
1571 object = object.next_sibling()) {
1572 if (!strcmp(object.name(), "wpt")) {
1573 RoutePoint *rp =
1574 GPXLoadWaypoint1(object, "circle", "", false, false, false, 0);
1575 rp->m_bIsolatedMark = true;
1576 pWayPointMan->AddRoutePoint(rp);
1577 }
1578 }
1579 return true;
1580}
1581
1582RoutePoint *WaypointExists(const wxString &name, double lat, double lon) {
1583 RoutePoint *pret = NULL;
1584 // if( g_bIsNewLayer ) return NULL;
1585 wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1586 while (node) {
1587 RoutePoint *pr = node->GetData();
1588
1589 // if( pr->m_bIsInLayer ) return NULL;
1590
1591 if (name == pr->GetName()) {
1592 if (fabs(lat - pr->m_lat) < 1.e-6 && fabs(lon - pr->m_lon) < 1.e-6) {
1593 pret = pr;
1594 break;
1595 }
1596 }
1597 node = node->GetNext();
1598 }
1599
1600 return pret;
1601}
1602
1603RoutePoint *WaypointExists(const wxString &guid) {
1604 wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1605 while (node) {
1606 RoutePoint *pr = node->GetData();
1607
1608 // if( pr->m_bIsInLayer ) return NULL;
1609
1610 if (guid == pr->m_GUID) {
1611 return pr;
1612 }
1613 node = node->GetNext();
1614 }
1615
1616 return NULL;
1617}
1618
1619bool WptIsInRouteList(RoutePoint *pr) {
1620 bool IsInList = false;
1621
1622 wxRouteListNode *node1 = pRouteList->GetFirst();
1623 while (node1) {
1624 Route *pRoute = node1->GetData();
1625 RoutePointList *pRoutePointList = pRoute->pRoutePointList;
1626
1627 wxRoutePointListNode *node2 = pRoutePointList->GetFirst();
1628 RoutePoint *prp;
1629
1630 while (node2) {
1631 prp = node2->GetData();
1632
1633 if (pr->IsSame(prp)) {
1634 IsInList = true;
1635 break;
1636 }
1637
1638 node2 = node2->GetNext();
1639 }
1640 node1 = node1->GetNext();
1641 }
1642 return IsInList;
1643}
1644
1645Route *RouteExists(const wxString &guid) {
1646 wxRouteListNode *route_node = pRouteList->GetFirst();
1647
1648 while (route_node) {
1649 Route *proute = route_node->GetData();
1650
1651 if (guid == proute->m_GUID) return proute;
1652
1653 route_node = route_node->GetNext();
1654 }
1655 return NULL;
1656}
1657
1658Route *RouteExists(Route *pTentRoute) {
1659 wxRouteListNode *route_node = pRouteList->GetFirst();
1660 while (route_node) {
1661 Route *proute = route_node->GetData();
1662
1663 if (proute->IsEqualTo(pTentRoute)) return proute;
1664
1665 route_node = route_node->GetNext(); // next route
1666 }
1667 return NULL;
1668}
1669
1670Track *TrackExists(const wxString &guid) {
1671 for (Track *ptrack : g_TrackList) {
1672 if (guid == ptrack->m_GUID) return ptrack;
1673 }
1674 return NULL;
1675}
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:818
Represents a single point in a track.
Definition track.h:56
Represents a track, which is a series of connected track points.
Definition track.h:114
bool AddRoutePoint(RoutePoint *prp)
Add a point to list which owns it.
Class NavObj_dB.