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
33#ifdef __ANDROID__
34#include <QDebug>
35#endif
36
37NavObjectCollection1::NavObjectCollection1()
38 : pugi::xml_document(), m_bSkipChangeSetUpdate(false) {}
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) {
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 if (b_propviz)
242 pWP->m_bIsVisible = bviz;
243 else if (b_fullviz)
244 pWP->m_bIsVisible = true;
245
246 if (b_layer) {
247 pWP->m_bIsInLayer = true;
248 pWP->m_LayerID = layer_id;
249 pWP->m_bIsVisible = b_layerviz;
250 pWP->SetListed(false);
251 }
252
253 pWP->SetShared(bshared);
254
255 if (TimeString.Len()) {
256 pWP->m_timestring = TimeString;
257 pWP->SetCreateTime(wxInvalidDateTime); // cause deferred timestamp parsing
258 }
259
260 if (linklist) {
261 delete pWP->m_HyperlinkList; // created in RoutePoint ctor
262 pWP->m_HyperlinkList = linklist;
263 }
264
265 return pWP;
266}
267
268static TrackPoint *GPXLoadTrackPoint1(pugi::xml_node &wpt_node) {
269 wxString TimeString;
270
271 double rlat = wpt_node.attribute("lat").as_double();
272 double rlon = wpt_node.attribute("lon").as_double();
273
274 for (pugi::xml_node child = wpt_node.first_child(); child != 0;
275 child = child.next_sibling()) {
276 const char *pcn = child.name();
277 if (!strcmp(pcn, "time"))
278 TimeString = wxString::FromUTF8(child.first_child().value());
279
280 // OpenCPN Extensions....
281 else if (!strcmp(pcn, "extensions")) {
282 for (pugi::xml_node ext_child = child.first_child(); ext_child;
283 ext_child = ext_child.next_sibling()) {
284 wxString ext_name = wxString::FromUTF8(ext_child.name());
285 if (ext_name == _T ( "opencpn:action" )) {
286 }
287 } // for
288 } // extensions
289 } // for
290
291 // Create trackpoint
292 return new TrackPoint(rlat, rlon, TimeString);
293}
294
295Track *GPXLoadTrack1(pugi::xml_node &trk_node, bool b_fullviz, bool b_layer,
296 bool b_layerviz, int layer_id) {
297 wxString TrackName;
298 wxString DescString;
299 unsigned short int GPXSeg;
300 bool b_propviz = false;
301 bool b_viz = true;
302 Track *pTentTrack = NULL;
303 HyperlinkList *linklist = NULL;
304
305 wxString Name = wxString::FromUTF8(trk_node.name());
306 if (Name == _T ( "trk" )) {
307 pTentTrack = new Track();
308 GPXSeg = 0;
309
310 TrackPoint *pWp = NULL;
311
312 for (pugi::xml_node tschild = trk_node.first_child(); tschild;
313 tschild = tschild.next_sibling()) {
314 wxString ChildName = wxString::FromUTF8(tschild.name());
315 if (ChildName == _T ( "trkseg" )) {
316 GPXSeg += 1;
317
318 // Official GPX spec calls for trkseg to have children trkpt
319 for (pugi::xml_node tpchild = tschild.first_child(); tpchild;
320 tpchild = tpchild.next_sibling()) {
321 wxString tpChildName = wxString::FromUTF8(tpchild.name());
322 if (tpChildName == _T("trkpt")) {
323 pWp = ::GPXLoadTrackPoint1(tpchild);
324 if (pWp) {
325 pTentTrack->AddPoint(pWp); // defer BBox calculation
326 pWp->m_GPXTrkSegNo = GPXSeg;
327 }
328 }
329 }
330 } else if (ChildName == _T ( "name" ))
331 TrackName = wxString::FromUTF8(tschild.first_child().value());
332 else if (ChildName == _T ( "desc" ))
333 DescString = wxString::FromUTF8(tschild.first_child().value());
334 else
335
336 if (ChildName == _T ( "link")) {
337 wxString HrefString;
338 wxString HrefTextString;
339 wxString HrefTypeString;
340 if (linklist == NULL) linklist = new HyperlinkList;
341 HrefString = wxString::FromUTF8(tschild.first_attribute().value());
342
343 for (pugi::xml_node child1 = tschild.first_child(); child1;
344 child1 = child1.next_sibling()) {
345 wxString LinkString = wxString::FromUTF8(child1.name());
346
347 if (LinkString == _T ( "text" ))
348 HrefTextString = wxString::FromUTF8(child1.first_child().value());
349 if (LinkString == _T ( "type" ))
350 HrefTypeString = wxString::FromUTF8(child1.first_child().value());
351 }
352
353 Hyperlink *link = new Hyperlink;
354 link->Link = HrefString;
355 link->DescrText = HrefTextString;
356 link->LType = HrefTypeString;
357 linklist->Append(link);
358 }
359
360 else if (ChildName == _T ( "extensions" )) {
361 for (pugi::xml_node ext_child = tschild.first_child(); ext_child;
362 ext_child = ext_child.next_sibling()) {
363 wxString ext_name = wxString::FromUTF8(ext_child.name());
364 if (ext_name == _T ( "opencpn:start" )) {
365 pTentTrack->m_TrackStartString =
366 wxString::FromUTF8(ext_child.first_child().value());
367 } else if (ext_name == _T ( "opencpn:end" )) {
368 pTentTrack->m_TrackEndString =
369 wxString::FromUTF8(ext_child.first_child().value());
370 }
371
372 else if (ext_name == _T ( "opencpn:viz" )) {
373 wxString viz = wxString::FromUTF8(ext_child.first_child().value());
374 b_propviz = true;
375 b_viz = (viz == _T("1"));
376 } else if (ext_name == _T ( "opencpn:style" )) {
377 for (pugi::xml_attribute attr = ext_child.first_attribute(); attr;
378 attr = attr.next_attribute()) {
379 if (!strcmp(attr.name(), "style"))
380 pTentTrack->m_style = (wxPenStyle)attr.as_int();
381 else if (!strcmp(attr.name(), "width"))
382 pTentTrack->m_width = attr.as_int();
383 }
384 }
385
386 else if (ext_name == _T ( "opencpn:guid" )) {
387 pTentTrack->m_GUID =
388 wxString::FromUTF8(ext_child.first_child().value());
389 }
390
391 else if (ext_name.EndsWith(
392 _T ( "TrackExtension" ))) // Parse GPXX color
393 {
394 for (pugi::xml_node gpxx_child = ext_child.first_child();
395 gpxx_child; gpxx_child = gpxx_child.next_sibling()) {
396 wxString gpxx_name = wxString::FromUTF8(gpxx_child.name());
397 if (gpxx_name.EndsWith(_T ( "DisplayColor" )))
398 pTentTrack->m_Colour =
399 wxString::FromUTF8(gpxx_child.first_child().value());
400 }
401 }
402 } // extensions
403 }
404 }
405
406 pTentTrack->SetName(TrackName);
407 pTentTrack->m_TrackDescription = DescString;
408
409 if (b_propviz)
410 pTentTrack->SetVisible(b_viz);
411 else {
412 if (b_fullviz) pTentTrack->SetVisible();
413 }
414
415 if (b_layer) {
416 pTentTrack->SetVisible(b_layerviz);
417 pTentTrack->m_bIsInLayer = true;
418 pTentTrack->m_LayerID = layer_id;
419 pTentTrack->SetListed(false);
420 }
421
422 pTentTrack->SetCurrentTrackSeg(GPXSeg);
423 }
424
425 if (linklist) {
426 delete pTentTrack->m_HyperlinkList; // created in TrackPoint ctor
427 pTentTrack->m_HyperlinkList = linklist;
428 }
429
430 return pTentTrack;
431}
432
433Route *GPXLoadRoute1(pugi::xml_node &wpt_node, bool b_fullviz, bool b_layer,
434 bool b_layerviz, int layer_id, bool b_change,
435 bool load_points) {
436 wxString RouteName;
437 wxString DescString;
438 bool b_propviz = false;
439 bool b_propSWPviz = false;
440 bool b_viz = true;
441 bool swpViz = false;
442 Route *pTentRoute = NULL;
443
444 wxString Name = wxString::FromUTF8(wpt_node.name());
445 if (Name == _T ( "rte" )) {
446 pTentRoute = new Route();
447 HyperlinkList *linklist = NULL;
448
449 RoutePoint *pWp = NULL;
450 bool route_existing = false;
451 pTentRoute->m_TimeDisplayFormat = RTE_TIME_DISP_UTC;
452
453 for (pugi::xml_node tschild = wpt_node.first_child(); tschild;
454 tschild = tschild.next_sibling()) {
455 wxString ChildName = wxString::FromUTF8(tschild.name());
456
457 // load extentions first to determine if the route still exists
458 if (ChildName == _T ( "extensions" )) {
459 for (pugi::xml_node ext_child = tschild.first_child(); ext_child;
460 ext_child = ext_child.next_sibling()) {
461 wxString ext_name = wxString::FromUTF8(ext_child.name());
462
463 if (ext_name == _T ( "opencpn:start" )) {
464 pTentRoute->m_RouteStartString =
465 wxString::FromUTF8(ext_child.first_child().value());
466 } else if (ext_name == _T ( "opencpn:end" )) {
467 pTentRoute->m_RouteEndString =
468 wxString::FromUTF8(ext_child.first_child().value());
469 }
470
471 else if (ext_name == _T ( "opencpn:viz" )) {
472 wxString viz = wxString::FromUTF8(ext_child.first_child().value());
473 b_propviz = true;
474 b_viz = (viz == _T("1"));
475 }
476
477 else if (ext_name == _T ( "opencpn:sharedWPviz" )) {
478 wxString viz = wxString::FromUTF8(ext_child.first_child().value());
479 b_propSWPviz = true;
480 swpViz = (viz == _T("1"));
481 } else if (ext_name == _T ( "opencpn:style" )) {
482 for (pugi::xml_attribute attr = ext_child.first_attribute(); attr;
483 attr = attr.next_attribute()) {
484 if (!strcmp(attr.name(), "style"))
485 pTentRoute->m_style = (wxPenStyle)attr.as_int();
486 else if (!strcmp(attr.name(), "width"))
487 pTentRoute->m_width = attr.as_int();
488 }
489 }
490
491 else if (ext_name == _T ( "opencpn:guid" )) {
492 pTentRoute->m_GUID =
493 wxString::FromUTF8(ext_child.first_child().value());
494 }
495
496 else if (ext_name == _T ( "opencpn:planned_speed" )) {
497 pTentRoute->m_PlannedSpeed = atof(ext_child.first_child().value());
498 }
499
500 else if (ext_name == _T ( "opencpn:planned_departure" )) {
501 ParseGPXDateTime(
502 pTentRoute->m_PlannedDeparture,
503 wxString::FromUTF8(ext_child.first_child().value()));
504 }
505
506 else if (ext_name == _T ( "opencpn:time_display" )) {
507 pTentRoute->m_TimeDisplayFormat =
508 wxString::FromUTF8(ext_child.first_child().value());
509 } else if (ext_name.EndsWith(
510 _T ( "RouteExtension" ))) // Parse GPXX color
511 {
512 for (pugi::xml_node gpxx_child = ext_child.first_child();
513 gpxx_child; gpxx_child = gpxx_child.next_sibling()) {
514 wxString gpxx_name = wxString::FromUTF8(gpxx_child.name());
515 if (gpxx_name.EndsWith(_T ( "DisplayColor" )))
516 pTentRoute->m_Colour =
517 wxString::FromUTF8(gpxx_child.first_child().value());
518 }
519 }
520 }
521 if (!b_change) {
522 if (RouteExists(pTentRoute->m_GUID)) { // we are loading a different
523 // route with the same guid so
524 // let's generate a new guid
525 pTentRoute->m_GUID = pWayPointMan->CreateGUID(NULL);
526 route_existing = true;
527 }
528 }
529 } // extension
530 else if (load_points && ChildName == _T ( "rtept" )) {
531 RoutePoint *tpWp =
532 ::GPXLoadWaypoint1(tschild, _T("square"), _T(""), b_fullviz,
533 b_layer, b_layerviz, layer_id);
534 RoutePoint *erp = NULL;
535 if (!b_layer) erp = ::WaypointExists(tpWp->m_GUID);
536 // 1) if b_change is true, that means we are after crash - load the
537 // route and points as found in source file 2) if route_existing, we are
538 // loading a different route with the same guid. In this case load
539 // points as found in
540 // source file, changing the guid, but keep existing "isolated point" as
541 // found in the DB
542 // 3) in all other cases keep existing points if found and load new
543 // points if not found
544 bool new_wpt = true;
545 if (b_change) {
546 pWp = tpWp;
547 } else {
548 if (erp != NULL &&
549 (!route_existing || (route_existing && tpWp->IsShared()))) {
550 pWp = erp;
551 new_wpt = false;
552 } else {
553 if (route_existing) tpWp->m_GUID = pWayPointMan->CreateGUID(NULL);
554 pWp = tpWp;
555 }
556 }
557
558 pTentRoute->AddPoint(pWp, false, true); // defer BBox calculation
559 pWp->m_bIsInRoute = true; // Hack
560
561 if (new_wpt) {
562 if (erp == NULL) {
563 pWayPointMan->AddRoutePoint(pWp);
564 }
565 } else {
566 delete tpWp;
567 }
568 } else if (ChildName == _T ( "name" )) {
569 RouteName = wxString::FromUTF8(tschild.first_child().value());
570 } else if (ChildName == _T ( "desc" )) {
571 DescString = wxString::FromUTF8(tschild.first_child().value());
572 }
573
574 if (ChildName == _T ( "link")) {
575 wxString HrefString;
576 wxString HrefTextString;
577 wxString HrefTypeString;
578 if (linklist == NULL) linklist = new HyperlinkList;
579 HrefString = wxString::FromUTF8(tschild.first_attribute().value());
580
581 for (pugi::xml_node child1 = tschild.first_child(); child1;
582 child1 = child1.next_sibling()) {
583 wxString LinkString = wxString::FromUTF8(child1.name());
584
585 if (LinkString == _T ( "text" ))
586 HrefTextString = wxString::FromUTF8(child1.first_child().value());
587 if (LinkString == _T ( "type" ))
588 HrefTypeString = wxString::FromUTF8(child1.first_child().value());
589 }
590
591 Hyperlink *link = new Hyperlink;
592 link->Link = HrefString;
593 link->DescrText = HrefTextString;
594 link->LType = HrefTypeString;
595 linklist->Append(link);
596 }
597
598 else
599 // TODO: This is wrong, left here just to save data of the 3.3 beta
600 // series users.
601 if (ChildName.EndsWith(_T ( "RouteExtension" ))) // Parse GPXX color
602 {
603 for (pugi::xml_node gpxx_child = tschild.first_child(); gpxx_child;
604 gpxx_child = gpxx_child.next_sibling()) {
605 wxString gpxx_name = wxString::FromUTF8(gpxx_child.name());
606 if (gpxx_name.EndsWith(_T ( "DisplayColor" )))
607 pTentRoute->m_Colour =
608 wxString::FromUTF8(gpxx_child.first_child().value());
609 }
610 }
611 }
612
613 pTentRoute->m_RouteNameString = RouteName;
614 pTentRoute->m_RouteDescription = DescString;
615 if (linklist) {
616 pTentRoute->m_HyperlinkList = linklist;
617 }
618
619 if (b_propviz) {
620 pTentRoute->SetVisible(b_viz);
621 } else if (b_fullviz) {
622 pTentRoute->SetVisible();
623 }
624
625 if (b_propSWPviz) pTentRoute->SetSharedWPViz(swpViz);
626
627 if (b_layer) {
628 pTentRoute->SetVisible(b_layerviz);
629 pTentRoute->m_bIsInLayer = true;
630 pTentRoute->m_LayerID = layer_id;
631 pTentRoute->SetListed(false);
632 }
633 }
634
635 return pTentRoute;
636}
637
638static bool GPXCreateWpt(pugi::xml_node node, RoutePoint *pr,
639 unsigned int flags) {
640 wxString s;
641 pugi::xml_node child;
643
644 s.Printf(_T("%.9f"), pr->m_lat);
645 node.append_attribute("lat") = s.mb_str();
646 s.Printf(_T("%.9f"), pr->m_lon);
647 node.append_attribute("lon") = s.mb_str();
648
649 if (flags & OUT_TIME) {
650 child = node.append_child("time");
651 if (pr->m_timestring.Len())
652 child.append_child(pugi::node_pcdata)
653 .set_value(pr->m_timestring.mb_str());
654 else {
655 wxDateTime dt = pr->GetCreateTime();
656 if (!dt.IsValid()) dt = wxDateTime::Now();
657
658 wxString t = dt.ToUTC()
659 .FormatISODate()
660 .Append(_T("T"))
661 .Append(dt.ToUTC().FormatISOTime())
662 .Append(_T("Z"));
663 child.append_child(pugi::node_pcdata).set_value(t.mb_str());
664 }
665 }
666
667 if ((!pr->GetName().IsEmpty() && (flags & OUT_NAME)) ||
668 (flags & OUT_NAME_FORCE)) {
669 wxCharBuffer buffer = pr->GetName().ToUTF8();
670 if (buffer.data()) {
671 child = node.append_child("name");
672 child.append_child(pugi::node_pcdata).set_value(buffer.data());
673 }
674 }
675
676 if ((!pr->GetDescription().IsEmpty() && (flags & OUT_DESC)) ||
677 (flags & OUT_DESC_FORCE)) {
678 wxCharBuffer buffer = pr->GetDescription().ToUTF8();
679 if (buffer.data()) {
680 child = node.append_child("desc");
681 child.append_child(pugi::node_pcdata).set_value(buffer.data());
682 }
683 }
684
685 // Hyperlinks
686 if (flags & OUT_HYPERLINKS) {
687 HyperlinkList *linklist = pr->m_HyperlinkList;
688 if (linklist && linklist->GetCount()) {
689 wxHyperlinkListNode *linknode = linklist->GetFirst();
690 while (linknode) {
691 Hyperlink *link = linknode->GetData();
692
693 pugi::xml_node child_link = node.append_child("link");
694 ;
695 wxCharBuffer buffer = link->Link.ToUTF8();
696 if (buffer.data()) child_link.append_attribute("href") = buffer.data();
697
698 buffer = link->DescrText.ToUTF8();
699 if (buffer.data()) {
700 child = child_link.append_child("text");
701 child.append_child(pugi::node_pcdata).set_value(buffer.data());
702 }
703
704 buffer = link->LType.ToUTF8();
705 if (buffer.data() && strlen(buffer.data()) > 0) {
706 child = child_link.append_child("type");
707 child.append_child(pugi::node_pcdata).set_value(buffer.data());
708 }
709
710 linknode = linknode->GetNext();
711 }
712 }
713 }
714
715 if (flags & OUT_SYM_FORCE) {
716 child = node.append_child("sym");
717 if (!pr->GetIconName().IsEmpty()) {
718 child.append_child(pugi::node_pcdata)
719 .set_value(pr->GetIconName().mb_str());
720 } else {
721 child.append_child("empty");
722 }
723 }
724
725 if (flags & OUT_TYPE) {
726 child = node.append_child("type");
727 child.append_child(pugi::node_pcdata).set_value("WPT");
728 }
729
730 if ((flags & OUT_GUID) || (flags & OUT_VIZ) || (flags & OUT_VIZ_NAME) ||
731 (flags & OUT_SHARED) || (flags & OUT_EXTENSION) ||
732 (flags & OUT_TIDE_STATION) || (flags & OUT_RTE_PROPERTIES)) {
733 pugi::xml_node child_ext = node.append_child("extensions");
734
735 if (!pr->m_GUID.IsEmpty() && (flags & OUT_GUID)) {
736 child = child_ext.append_child("opencpn:guid");
737 child.append_child(pugi::node_pcdata).set_value(pr->m_GUID.mb_str());
738 }
739
740 if ((flags & OUT_VIZ) && !pr->m_bIsVisible) {
741 child = child_ext.append_child("opencpn:viz");
742 child.append_child(pugi::node_pcdata).set_value("0");
743 }
744
745 if ((flags & OUT_VIZ_NAME) && pr->m_bShowName) {
746 child = child_ext.append_child("opencpn:viz_name");
747 child.append_child(pugi::node_pcdata).set_value("1");
748 }
749
750 if ((flags & OUT_SHARED) && pr->IsShared()) {
751 child = child_ext.append_child("opencpn:shared");
752 child.append_child(pugi::node_pcdata).set_value("1");
753 }
754 if (flags & OUT_ARRIVAL_RADIUS) {
755 child = child_ext.append_child("opencpn:arrival_radius");
756 s.Printf(_T("%.3f"), pr->GetWaypointArrivalRadius());
757 child.append_child(pugi::node_pcdata).set_value(s.mbc_str());
758 }
759 if (flags & OUT_WAYPOINT_RANGE_RINGS) {
760 child = child_ext.append_child("opencpn:waypoint_range_rings");
761 pugi::xml_attribute viz = child.append_attribute("visible");
762 viz.set_value(pr->m_bShowWaypointRangeRings);
763 pugi::xml_attribute number = child.append_attribute("number");
764 number.set_value(pr->m_iWaypointRangeRingsNumber);
765 pugi::xml_attribute step = child.append_attribute("step");
766 step.set_value(pr->m_fWaypointRangeRingsStep);
767 pugi::xml_attribute units = child.append_attribute("units");
768 units.set_value(pr->m_iWaypointRangeRingsStepUnits);
769
770 // Color specification in GPX file must be fully opaque
771 if (pr->m_wxcWaypointRangeRingsColour.IsOk()) {
775 pr->m_wxcWaypointRangeRingsColour.Blue(), wxALPHA_OPAQUE);
776 } else {
777 pr->m_wxcWaypointRangeRingsColour.Set(0, 0, 0, wxALPHA_OPAQUE);
778 }
779
780 pugi::xml_attribute colour = child.append_attribute("colour");
781 colour.set_value(
782 pr->m_wxcWaypointRangeRingsColour.GetAsString(wxC2S_HTML_SYNTAX)
783 .utf8_str());
784 }
785 if (flags & OUT_WAYPOINT_SCALE) {
786 child = child_ext.append_child("opencpn:scale_min_max");
787 pugi::xml_attribute use = child.append_attribute("UseScale");
788 use.set_value(pr->GetUseSca());
789 pugi::xml_attribute sca = child.append_attribute("ScaleMin");
790 sca.set_value(pr->GetScaMin());
791 pugi::xml_attribute max = child.append_attribute("ScaleMax");
792 max.set_value(pr->GetScaMax());
793 }
794 if ((flags & OUT_TIDE_STATION) && !pr->m_TideStation.IsEmpty()) {
795 child = child_ext.append_child("opencpn:tidestation");
796 child.append_child(pugi::node_pcdata)
797 .set_value(pr->m_TideStation.mb_str());
798 }
799 if ((flags & OUT_RTE_PROPERTIES) &&
800 (pr->GetPlannedSpeed() > 0.0001 || pr->m_manual_etd)) {
801 child = child_ext.append_child("opencpn:rte_properties");
802 if (pr->GetPlannedSpeed() > 0.0001) {
803 pugi::xml_attribute use = child.append_attribute("planned_speed");
804 use.set_value(
805 wxString::Format(_T("%.1lf"), pr->GetPlannedSpeed()).mb_str());
806 }
807 if (pr->m_manual_etd && pr->GetManualETD().IsValid()) {
808 pugi::xml_attribute use = child.append_attribute("etd");
809 // Currently, the serialization format is YYYY-MM-DDTHH:MM:SS
810 // without timezone information, e.g., etd="2025-04-03T20:00:27"
811 // TODO: serialize using ISO 8601 or RFC 3339 format to ensure
812 // the serialized date/time is unambiguous.
813 use.set_value(pr->GetManualETD().FormatISOCombined().mb_str());
814 }
815 }
816 }
817
818 return true;
819}
820
821static bool GPXCreateTrkpt(pugi::xml_node node, TrackPoint *pt,
822 unsigned int flags) {
823 wxString s;
824 pugi::xml_node child;
826
827 s.Printf(_T("%.9f"), pt->m_lat);
828 node.append_attribute("lat") = s.mb_str();
829 s.Printf(_T("%.9f"), pt->m_lon);
830 node.append_attribute("lon") = s.mb_str();
831
832 if (flags & OUT_TIME && pt->HasValidTimestamp()) {
833 child = node.append_child("time");
834 child.append_child(pugi::node_pcdata).set_value(pt->GetTimeString());
835 }
836
837 return true;
838}
839
840static bool GPXCreateTrk(pugi::xml_node node, Track *pTrack,
841 unsigned int flags) {
842 pugi::xml_node child;
843
844 if (pTrack->GetName().Len()) {
845 wxCharBuffer buffer = pTrack->GetName().ToUTF8();
846 if (buffer.data()) {
847 child = node.append_child("name");
848 child.append_child(pugi::node_pcdata).set_value(buffer.data());
849 }
850 }
851
852 if (pTrack->m_TrackDescription.Len()) {
853 wxCharBuffer buffer = pTrack->m_TrackDescription.ToUTF8();
854 if (buffer.data()) {
855 child = node.append_child("desc");
856 child.append_child(pugi::node_pcdata).set_value(buffer.data());
857 }
858 }
859
860 // Hyperlinks
861 HyperlinkList *linklist = pTrack->m_HyperlinkList;
862 if (linklist && linklist->GetCount()) {
863 wxHyperlinkListNode *linknode = linklist->GetFirst();
864 while (linknode) {
865 Hyperlink *link = linknode->GetData();
866
867 pugi::xml_node child_link = node.append_child("link");
868 wxCharBuffer buffer = link->Link.ToUTF8();
869 if (buffer.data()) child_link.append_attribute("href") = buffer.data();
870
871 buffer = link->DescrText.ToUTF8();
872 if (buffer.data()) {
873 child = child_link.append_child("text");
874 child.append_child(pugi::node_pcdata).set_value(buffer.data());
875 }
876
877 buffer = link->LType.ToUTF8();
878 if (buffer.data() && strlen(buffer.data()) > 0) {
879 child = child_link.append_child("type");
880 child.append_child(pugi::node_pcdata).set_value(buffer.data());
881 }
882
883 linknode = linknode->GetNext();
884 }
885 }
886
887 pugi::xml_node child_ext = node.append_child("extensions");
888
889 child = child_ext.append_child("opencpn:guid");
890 child.append_child(pugi::node_pcdata).set_value(pTrack->m_GUID.mb_str());
891
892 child = child_ext.append_child("opencpn:viz");
893 child.append_child(pugi::node_pcdata)
894 .set_value(pTrack->IsVisible() == true ? "1" : "0");
895
896 if (pTrack->m_TrackStartString.Len()) {
897 wxCharBuffer buffer = pTrack->m_TrackStartString.ToUTF8();
898 if (buffer.data()) {
899 child = child_ext.append_child("opencpn:start");
900 child.append_child(pugi::node_pcdata).set_value(buffer.data());
901 }
902 }
903
904 if (pTrack->m_TrackEndString.Len()) {
905 wxCharBuffer buffer = pTrack->m_TrackEndString.ToUTF8();
906 if (buffer.data()) {
907 child = child_ext.append_child("opencpn:end");
908 child.append_child(pugi::node_pcdata).set_value(buffer.data());
909 }
910 }
911
912 if (pTrack->m_width != WIDTH_UNDEFINED ||
913 pTrack->m_style != wxPENSTYLE_INVALID) {
914 child = child_ext.append_child("opencpn:style");
915
916 if (pTrack->m_width != WIDTH_UNDEFINED)
917 child.append_attribute("width") = pTrack->m_width;
918 if (pTrack->m_style != wxPENSTYLE_INVALID)
919 child.append_attribute("style") = pTrack->m_style;
920 }
921
922 if (pTrack->m_Colour != wxEmptyString) {
923 pugi::xml_node gpxx_ext = child_ext.append_child("gpxx:TrackExtension");
924 child = gpxx_ext.append_child("gpxx:DisplayColor");
925 child.append_child(pugi::node_pcdata).set_value(pTrack->m_Colour.mb_str());
926 }
927
928 if (flags & RT_OUT_NO_RTPTS) return true;
929
930 int node2 = 0;
931 TrackPoint *prp;
932
933 unsigned short int GPXTrkSegNo1 = 1;
934
935 do {
936 unsigned short int GPXTrkSegNo2 = GPXTrkSegNo1;
937
938 pugi::xml_node seg = node.append_child("trkseg");
939
940 while (node2 < pTrack->GetnPoints()) {
941 prp = pTrack->GetPoint(node2);
942 GPXTrkSegNo1 = prp->m_GPXTrkSegNo;
943 if (GPXTrkSegNo1 != GPXTrkSegNo2) break;
944
945 GPXCreateTrkpt(seg.append_child("trkpt"), prp, OPT_TRACKPT);
946
947 node2++;
948 }
949 } while (node2 < pTrack->GetnPoints());
950
951 return true;
952}
953
954static bool GPXCreateRoute(pugi::xml_node node, Route *pRoute) {
955 pugi::xml_node child;
956
957 if (pRoute->m_RouteNameString.Len()) {
958 wxCharBuffer buffer = pRoute->m_RouteNameString.ToUTF8();
959 if (buffer.data()) {
960 child = node.append_child("name");
961 child.append_child(pugi::node_pcdata).set_value(buffer.data());
962 }
963 }
964
965 if (pRoute->m_RouteDescription.Len()) {
966 wxCharBuffer buffer = pRoute->m_RouteDescription.ToUTF8();
967 if (buffer.data()) {
968 child = node.append_child("desc");
969 child.append_child(pugi::node_pcdata).set_value(buffer.data());
970 }
971 }
972
973 // Hyperlinks
974 HyperlinkList *linklist = pRoute->m_HyperlinkList;
975 if (linklist && linklist->GetCount()) {
976 wxHyperlinkListNode *linknode = linklist->GetFirst();
977 while (linknode) {
978 Hyperlink *link = linknode->GetData();
979
980 pugi::xml_node child_link = node.append_child("link");
981 wxCharBuffer buffer = link->Link.ToUTF8();
982 if (buffer.data()) child_link.append_attribute("href") = buffer.data();
983
984 buffer = link->DescrText.ToUTF8();
985 if (buffer.data()) {
986 child = child_link.append_child("text");
987 child.append_child(pugi::node_pcdata).set_value(buffer.data());
988 }
989
990 buffer = link->LType.ToUTF8();
991 if (buffer.data() && strlen(buffer.data()) > 0) {
992 child = child_link.append_child("type");
993 child.append_child(pugi::node_pcdata).set_value(buffer.data());
994 }
995
996 linknode = linknode->GetNext();
997 }
998 }
999
1000 pugi::xml_node child_ext = node.append_child("extensions");
1001
1002 child = child_ext.append_child("opencpn:guid");
1003 child.append_child(pugi::node_pcdata).set_value(pRoute->m_GUID.mb_str());
1004
1005 child = child_ext.append_child("opencpn:viz");
1006 child.append_child(pugi::node_pcdata)
1007 .set_value(pRoute->IsVisible() == true ? "1" : "0");
1008
1009 if (pRoute->ContainsSharedWP()) {
1010 child = child_ext.append_child("opencpn:sharedWPviz");
1011 child.append_child(pugi::node_pcdata)
1012 .set_value(pRoute->GetSharedWPViz() == true ? "1" : "0");
1013 }
1014
1015 if (pRoute->m_RouteStartString.Len()) {
1016 wxCharBuffer buffer = pRoute->m_RouteStartString.ToUTF8();
1017 if (buffer.data()) {
1018 child = child_ext.append_child("opencpn:start");
1019 child.append_child(pugi::node_pcdata).set_value(buffer.data());
1020 }
1021 }
1022
1023 if (pRoute->m_RouteEndString.Len()) {
1024 wxCharBuffer buffer = pRoute->m_RouteEndString.ToUTF8();
1025 if (buffer.data()) {
1026 child = child_ext.append_child("opencpn:end");
1027 child.append_child(pugi::node_pcdata).set_value(buffer.data());
1028 }
1029 }
1030
1031 if (pRoute->m_PlannedSpeed != ROUTE_DEFAULT_SPEED) {
1032 child = child_ext.append_child("opencpn:planned_speed");
1033 wxString s;
1034 s.Printf(_T("%.2f"), pRoute->m_PlannedSpeed);
1035 child.append_child(pugi::node_pcdata).set_value(s.mb_str());
1036 }
1037
1038 if (pRoute->m_PlannedDeparture.IsValid()) {
1039 child = child_ext.append_child("opencpn:planned_departure");
1040 wxString t = pRoute->m_PlannedDeparture.FormatISODate()
1041 .Append(_T("T"))
1042 .Append(pRoute->m_PlannedDeparture.FormatISOTime())
1043 .Append(_T("Z"));
1044 child.append_child(pugi::node_pcdata).set_value(t.mb_str());
1045 }
1046
1047 child = child_ext.append_child("opencpn:time_display");
1048 child.append_child(pugi::node_pcdata)
1049 .set_value(pRoute->m_TimeDisplayFormat.mb_str());
1050
1051 if (pRoute->m_width != WIDTH_UNDEFINED ||
1052 pRoute->m_style != wxPENSTYLE_INVALID) {
1053 child = child_ext.append_child("opencpn:style");
1054
1055 if (pRoute->m_width != WIDTH_UNDEFINED)
1056 child.append_attribute("width") = pRoute->m_width;
1057 if (pRoute->m_style != wxPENSTYLE_INVALID)
1058 child.append_attribute("style") = pRoute->m_style;
1059 }
1060
1061 pugi::xml_node gpxx_ext = child_ext.append_child("gpxx:RouteExtension");
1062 child = gpxx_ext.append_child("gpxx:IsAutoNamed");
1063 child.append_child(pugi::node_pcdata).set_value("false");
1064
1065 if (pRoute->m_Colour != wxEmptyString) {
1066 child = gpxx_ext.append_child("gpxx:DisplayColor");
1067 child.append_child(pugi::node_pcdata).set_value(pRoute->m_Colour.mb_str());
1068 }
1069
1070 RoutePointList *pRoutePointList = pRoute->pRoutePointList;
1071 wxRoutePointListNode *node2 = pRoutePointList->GetFirst();
1072 RoutePoint *prp;
1073
1074 while (node2) {
1075 prp = node2->GetData();
1076
1077 GPXCreateWpt(node.append_child("rtept"), prp, OPT_ROUTEPT);
1078
1079 node2 = node2->GetNext();
1080 }
1081
1082 return true;
1083}
1084
1085bool InsertRouteA(Route *pTentRoute, NavObjectCollection1 *navobj) {
1086 if (!pTentRoute) return false;
1087
1088 bool bAddroute = true;
1089 // If the route has only 1 point, don't load it.
1090 if (pTentRoute->GetnPoints() < 2) bAddroute = false;
1091
1092 // TODO All this trouble for a tentative route.......Should make some
1093 // Route methods????
1094 if (bAddroute) {
1095 pRouteList->Append(pTentRoute);
1096
1097 // Do the (deferred) calculation of BBox
1098 pTentRoute->FinalizeForRendering();
1099
1100 // Add the selectable points and segments
1101
1102 int ip = 0;
1103 float prev_rlat = 0., prev_rlon = 0.;
1104 RoutePoint *prev_pConfPoint = NULL;
1105
1106 wxRoutePointListNode *node = pTentRoute->pRoutePointList->GetFirst();
1107 while (node) {
1108 RoutePoint *prp = node->GetData();
1109
1110 if (ip)
1111 pSelect->AddSelectableRouteSegment(prev_rlat, prev_rlon, prp->m_lat,
1112 prp->m_lon, prev_pConfPoint, prp,
1113 pTentRoute);
1114 pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
1115 prev_rlat = prp->m_lat;
1116 prev_rlon = prp->m_lon;
1117 prev_pConfPoint = prp;
1118
1119 ip++;
1120
1121 node = node->GetNext();
1122 }
1123 } else {
1124 // walk the route, deleting points used only by this route
1125 wxRoutePointListNode *pnode = (pTentRoute->pRoutePointList)->GetFirst();
1126 while (pnode) {
1127 RoutePoint *prp = pnode->GetData();
1128
1129 // check all other routes to see if this point appears in any other route
1130 Route *pcontainer_route = g_pRouteMan->FindRouteContainingWaypoint(prp);
1131
1132 if (pcontainer_route == NULL) {
1133 prp->m_bIsInRoute =
1134 false; // Take this point out of this (and only) track/route
1135 if (!prp->IsShared()) {
1136 navobj->m_bSkipChangeSetUpdate = true;
1137 NavObjectChanges::getInstance()->DeleteWayPoint(prp);
1138 navobj->m_bSkipChangeSetUpdate = false;
1139 delete prp;
1140 }
1141 }
1142
1143 pnode = pnode->GetNext();
1144 }
1145
1146 delete pTentRoute;
1147 }
1148 return bAddroute;
1149}
1150
1151bool InsertTrack(Track *pTentTrack, bool bApplyChanges) {
1152 if (!pTentTrack) return false;
1153
1154 bool bAddtrack = true;
1155 // If the track has only 1 point, don't load it.
1156 // This usually occurs if some points were discarded as being co-incident.
1157 if (!bApplyChanges && pTentTrack->GetnPoints() < 2) bAddtrack = false;
1158
1159 // TODO All this trouble for a tentative track.......Should make some
1160 // Track methods????
1161 if (bAddtrack) {
1162 g_TrackList.push_back(pTentTrack);
1163
1164 // Do the (deferred) calculation of Track BBox
1165 // pTentTrack->FinalizeForRendering();
1166
1167 // Add the selectable points and segments
1168
1169 float prev_rlat = 0., prev_rlon = 0.;
1170 TrackPoint *prev_pConfPoint = NULL;
1171
1172 for (int i = 0; i < pTentTrack->GetnPoints(); i++) {
1173 TrackPoint *prp = pTentTrack->GetPoint(i);
1174
1175 if (i)
1176 pSelect->AddSelectableTrackSegment(prev_rlat, prev_rlon, prp->m_lat,
1177 prp->m_lon, prev_pConfPoint, prp,
1178 pTentTrack);
1179
1180 prev_rlat = prp->m_lat;
1181 prev_rlon = prp->m_lon;
1182 prev_pConfPoint = prp;
1183 }
1184 } else
1185 delete pTentTrack;
1186
1187 return bAddtrack;
1188}
1189
1190bool InsertWpt(RoutePoint *pWp, bool overwrite) {
1191 bool res = false;
1192 RoutePoint *pExisting =
1193 WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon);
1194 if (!pExisting || overwrite) {
1195 if (NULL != pWayPointMan) {
1196 if (pExisting) {
1197 pWayPointMan->DestroyWaypoint(pExisting);
1198 }
1199 pWayPointMan->AddRoutePoint(pWp);
1200 res = true;
1201 }
1202 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1203 }
1204 return res;
1205}
1206
1207static void UpdateRouteA(Route *pTentRoute, NavObjectCollection1 *navobj,
1208 NavObjectChanges *nav_obj_changes) {
1209 if (!pTentRoute) return;
1210 if (pTentRoute->GetnPoints() < 2) return;
1211
1212 // first delete the route to be modified if exists
1213 Route *pExisting = ::RouteExists(pTentRoute->m_GUID);
1214 if (pExisting) {
1215 navobj->m_bSkipChangeSetUpdate = true;
1216 g_pRouteMan->DeleteRoute(pExisting, nav_obj_changes);
1217 navobj->m_bSkipChangeSetUpdate = false;
1218 }
1219
1220 // create a new route
1221 Route *pChangeRoute = new Route();
1222 pRouteList->Append(pChangeRoute);
1223
1224 // update new route keeping the same gui
1225 pChangeRoute->m_GUID = pTentRoute->m_GUID;
1226 pChangeRoute->m_RouteNameString = pTentRoute->m_RouteNameString;
1227 pChangeRoute->m_RouteStartString = pTentRoute->m_RouteStartString;
1228 pChangeRoute->m_RouteEndString = pTentRoute->m_RouteEndString;
1229 pChangeRoute->SetVisible(pTentRoute->IsVisible());
1230
1231 // Add points and segments to new route
1232 int ip = 0;
1233 float prev_rlat = 0., prev_rlon = 0.;
1234 RoutePoint *prev_pConfPoint = NULL;
1235
1236 wxRoutePointListNode *node = pTentRoute->pRoutePointList->GetFirst();
1237 while (node) {
1238 RoutePoint *prp = node->GetData();
1239
1240 // if some wpts have been not deleted, that meens they should be used in
1241 // other routes or are isolated way points so need to be updated
1242 RoutePoint *ex_rp = ::WaypointExists(prp->m_GUID);
1243 if (ex_rp) {
1244 pSelect->DeleteSelectableRoutePoint(ex_rp);
1245 ex_rp->m_lat = prp->m_lat;
1246 ex_rp->m_lon = prp->m_lon;
1247 ex_rp->SetIconName(prp->GetIconName());
1249 ex_rp->SetName(prp->GetName());
1250 ex_rp->m_TideStation = prp->m_TideStation;
1251 ex_rp->SetPlannedSpeed(prp->GetPlannedSpeed());
1252 pChangeRoute->AddPoint(ex_rp);
1253 pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, ex_rp);
1254
1255 } else {
1256 pChangeRoute->AddPoint(prp);
1257 pSelect->AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
1258 pWayPointMan->AddRoutePoint(prp);
1259 }
1260
1261 if (ip)
1262 pSelect->AddSelectableRouteSegment(prev_rlat, prev_rlon, prp->m_lat,
1263 prp->m_lon, prev_pConfPoint, prp,
1264 pChangeRoute);
1265 prev_rlat = prp->m_lat;
1266 prev_rlon = prp->m_lon;
1267 prev_pConfPoint = prp;
1268
1269 ip++;
1270
1271 node = node->GetNext();
1272 }
1273 // Do the (deferred) calculation of BBox
1274 pChangeRoute->FinalizeForRendering();
1275}
1276
1277Route *FindRouteContainingWaypoint(RoutePoint *pWP) {
1278 wxRouteListNode *node = pRouteList->GetFirst();
1279 while (node) {
1280 Route *proute = node->GetData();
1281
1282 wxRoutePointListNode *pnode = (proute->pRoutePointList)->GetFirst();
1283 while (pnode) {
1284 RoutePoint *prp = pnode->GetData();
1285 if (prp == pWP) return proute;
1286 pnode = pnode->GetNext();
1287 }
1288
1289 node = node->GetNext();
1290 }
1291
1292 return NULL; // not found
1293}
1294
1295bool NavObjectCollection1::CreateNavObjGPXPoints(void) {
1296 // Iterate over the Routepoint list, creating Nodes for
1297 // Routepoints that are not in any Route
1298 // as indicated by m_bIsolatedMark == false
1299
1300 if (!pWayPointMan) return false;
1301
1302 wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1303
1304 RoutePoint *pr;
1305
1306 while (node) {
1307 pr = node->GetData();
1308
1309 if ((pr->m_bIsolatedMark) && !(pr->m_bIsInLayer) && !(pr->m_btemp)) {
1310 pugi::xml_node doc = root();
1311 pugi::xml_node gpx = doc.first_child();
1312 pugi::xml_node new_node = gpx.append_child("wpt");
1313
1314 GPXCreateWpt(new_node, pr, OPT_WPT);
1315 }
1316 node = node->GetNext();
1317 }
1318
1319 return true;
1320}
1321
1322bool NavObjectCollection1::CreateNavObjGPXRoutes(void) {
1323 // Routes
1324 if (!pRouteList) return false;
1325
1326 wxRouteListNode *node1 = pRouteList->GetFirst();
1327 while (node1) {
1328 Route *pRoute = node1->GetData();
1329
1330 if (!pRoute->m_bIsInLayer && !pRoute->m_btemp) {
1331 pugi::xml_node doc = root();
1332 pugi::xml_node gpx = doc.first_child();
1333 pugi::xml_node new_node = gpx.append_child("rte");
1334
1335 GPXCreateRoute(new_node, pRoute);
1336 }
1337
1338 node1 = node1->GetNext();
1339 }
1340
1341 return true;
1342}
1343
1344bool NavObjectCollection1::CreateNavObjGPXTracks(void) {
1345 // Tracks
1346 for (Track *pTrack : g_TrackList) {
1347 if (pTrack->GetnPoints()) {
1348 if (!pTrack->m_bIsInLayer && !pTrack->m_btemp) {
1349 pugi::xml_node doc = root();
1350 pugi::xml_node gpx = doc.first_child();
1351 pugi::xml_node new_node = gpx.append_child("trk");
1352
1353 GPXCreateTrk(new_node, pTrack, 0);
1354 }
1355 }
1356 }
1357
1358 return true;
1359}
1360
1361bool NavObjectCollection1::CreateAllGPXObjects() {
1362 SetRootGPXNode();
1363
1364 CreateNavObjGPXPoints();
1365 CreateNavObjGPXRoutes();
1366 CreateNavObjGPXTracks();
1367
1368 return true;
1369}
1370
1371bool NavObjectCollection1::AddGPXRoute(Route *pRoute) {
1372 SetRootGPXNode();
1373 pugi::xml_node doc = root();
1374 pugi::xml_node gpx = doc.first_child();
1375 pugi::xml_node new_node = gpx.append_child("rte");
1376
1377 GPXCreateRoute(new_node, pRoute);
1378 return true;
1379}
1380
1381bool NavObjectCollection1::AddGPXTrack(Track *pTrk) {
1382 SetRootGPXNode();
1383 pugi::xml_node doc = root();
1384 pugi::xml_node gpx = doc.first_child();
1385 pugi::xml_node new_node = gpx.append_child("trk");
1386
1387 GPXCreateTrk(new_node, pTrk, 0);
1388 return true;
1389}
1390
1391bool NavObjectCollection1::AddGPXWaypoint(RoutePoint *pWP) {
1392 SetRootGPXNode();
1393 pugi::xml_node doc = root();
1394 pugi::xml_node gpx = doc.first_child();
1395 pugi::xml_node new_node = gpx.append_child("wpt");
1396
1397 GPXCreateWpt(new_node, pWP, OPT_WPT);
1398 return true;
1399}
1400
1401void NavObjectCollection1::AddGPXRoutesList(RouteList *pRoutes) {
1402 SetRootGPXNode();
1403
1404 wxRouteListNode *pRoute = pRoutes->GetFirst();
1405 while (pRoute) {
1406 Route *pRData = pRoute->GetData();
1407 AddGPXRoute(pRData);
1408 pRoute = pRoute->GetNext();
1409 }
1410}
1411
1412void NavObjectCollection1::AddGPXTracksList(std::vector<Track *> *pTracks) {
1413 SetRootGPXNode();
1414
1415 for (Track *pRData : *pTracks) {
1416 AddGPXTrack(pRData);
1417 }
1418}
1419
1420bool NavObjectCollection1::AddGPXPointsList(RoutePointList *pRoutePoints) {
1421 SetRootGPXNode();
1422
1423 wxRoutePointListNode *pRoutePointNode = pRoutePoints->GetFirst();
1424 while (pRoutePointNode) {
1425 RoutePoint *pRP = pRoutePointNode->GetData();
1426 AddGPXWaypoint(pRP);
1427 pRoutePointNode = pRoutePointNode->GetNext();
1428 }
1429
1430 return true;
1431}
1432
1433void NavObjectCollection1::SetRootGPXNode(void) {
1434 if (!strlen(first_child().name())) {
1435 pugi::xml_node gpx_root = append_child("gpx");
1436 gpx_root.append_attribute("version") = "1.1";
1437 gpx_root.append_attribute("creator") = "OpenCPN";
1438 gpx_root.append_attribute("xmlns:xsi") =
1439 "http://www.w3.org/2001/XMLSchema-instance";
1440 gpx_root.append_attribute("xmlns") = "http://www.topografix.com/GPX/1/1";
1441 gpx_root.append_attribute("xmlns:gpxx") =
1442 "http://www.garmin.com/xmlschemas/GpxExtensions/v3";
1443 gpx_root.append_attribute("xsi:schemaLocation") =
1444 "http://www.topografix.com/GPX/1/1 "
1445 "http://www.topografix.com/GPX/1/1/gpx.xsd "
1446 "http://www.garmin.com/xmlschemas/GpxExtensions/v3 "
1447 "http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd";
1448 gpx_root.append_attribute("xmlns:opencpn") = "http://www.opencpn.org";
1449 }
1450}
1451
1452bool NavObjectCollection1::IsOpenCPN() {
1453 for (pugi::xml_attribute attr = root().first_child().first_attribute(); attr;
1454 attr = attr.next_attribute())
1455 if (!strcmp(attr.name(), "creator") && !strcmp(attr.value(), "OpenCPN"))
1456 return true;
1457 return false;
1458}
1459
1460bool NavObjectCollection1::SaveFile(const wxString filename) {
1461 wxString tmp_filename = filename + ".tmp";
1462 if (wxFileExists(tmp_filename)) {
1463 wxRemoveFile(tmp_filename);
1464 }
1465 save_file(tmp_filename.fn_str(), " ");
1466 wxRenameFile(tmp_filename.fn_str(), filename.fn_str(), true);
1467 return true;
1468}
1469
1470bool NavObjectCollection1::LoadAllGPXObjects(bool b_full_viz,
1471 int &wpt_duplicates,
1472 bool b_compute_bbox) {
1473 wpt_duplicates = 0;
1474 pugi::xml_node objects = this->child("gpx");
1475
1476 for (pugi::xml_node object = objects.first_child(); object;
1477 object = object.next_sibling()) {
1478 if (!strcmp(object.name(), "wpt")) {
1479 RoutePoint *pWp = ::GPXLoadWaypoint1(object, _T("circle"), _T(""),
1480 b_full_viz, false, false, 0);
1481
1482 pWp->m_bIsolatedMark = true; // This is an isolated mark
1483 RoutePoint *pExisting =
1484 WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon);
1485 if (!pExisting) {
1486 if (NULL != pWayPointMan) pWayPointMan->AddRoutePoint(pWp);
1487 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1488 LLBBox wptbox;
1489 wptbox.Set(pWp->m_lat, pWp->m_lon, pWp->m_lat, pWp->m_lon);
1490 BBox.Expand(wptbox);
1491 } else {
1492 delete pWp;
1493 wpt_duplicates++;
1494 }
1495 } else if (!strcmp(object.name(), "trk")) {
1496 Track *pTrack = GPXLoadTrack1(object, b_full_viz, false, false, 0);
1497 if (InsertTrack(pTrack) && b_compute_bbox && pTrack->IsVisible()) {
1498 // BBox.Expand(pTrack->GetBBox());
1499 }
1500 } else if (!strcmp(object.name(), "rte")) {
1501 Route *pRoute = GPXLoadRoute1(object, b_full_viz, false, false, 0, false);
1502 if (InsertRouteA(pRoute, this) && b_compute_bbox && pRoute->IsVisible()) {
1503 BBox.Expand(pRoute->GetBBox());
1504 }
1505 }
1506 }
1507
1508 return true;
1509}
1510
1511int NavObjectCollection1::LoadAllGPXObjectsAsLayer(int layer_id,
1512 bool b_layerviz,
1513 wxCheckBoxState b_namesviz) {
1514 if (!pWayPointMan) return 0;
1515
1516 int n_obj = 0;
1517 pugi::xml_node objects = this->child("gpx");
1518
1519 for (pugi::xml_node object = objects.first_child(); object;
1520 object = object.next_sibling()) {
1521 if (!strcmp(object.name(), "wpt")) {
1522 RoutePoint *pWp = ::GPXLoadWaypoint1(object, _T("circle"), _T(""),
1523 b_namesviz != wxCHK_UNDETERMINED,
1524 true, b_layerviz, layer_id);
1525 if (b_namesviz != wxCHK_UNDETERMINED) {
1526 pWp->SetNameShown(b_namesviz == wxCHK_CHECKED);
1527 }
1528 pWp->m_bIsolatedMark = true; // This is an isolated mark
1529 pWayPointMan->AddRoutePoint(pWp);
1530 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1531 n_obj++;
1532 } else {
1533 if (!strcmp(object.name(), "trk")) {
1534 Track *pTrack =
1535 GPXLoadTrack1(object, false, true, b_layerviz, layer_id);
1536 n_obj++;
1537 InsertTrack(pTrack);
1538 } else if (!strcmp(object.name(), "rte")) {
1539 Route *pRoute =
1540 GPXLoadRoute1(object, true, true, b_layerviz, layer_id, false);
1541 n_obj++;
1542 InsertRouteA(pRoute, this);
1543 }
1544 }
1545 }
1546
1547 return n_obj;
1548}
1549
1550NavObjectChanges::NavObjectChanges(wxString file_name)
1552 m_filename = file_name;
1553 m_changes_file = fopen(m_filename.mb_str(), "a");
1554 m_bdirty = false;
1555}
1556
1557NavObjectChanges::~NavObjectChanges() {
1558 if (m_changes_file) fclose(m_changes_file);
1559 if (::wxFileExists(m_filename)) ::wxRemoveFile(m_filename);
1560}
1561
1562void NavObjectChanges::AddRoute(Route *pr, const char *action) {
1563 SetRootGPXNode();
1564
1565 pugi::xml_node object = root().append_child("rte");
1566 GPXCreateRoute(object, pr);
1567
1568 pugi::xml_node xchild = object.child("extensions");
1569 // FIXME What if extensions do not exist?
1570 pugi::xml_node child = xchild.append_child("opencpn:action");
1571 child.append_child(pugi::node_pcdata).set_value(action);
1572
1573 if (m_changes_file) {
1574 pugi::xml_writer_file writer(m_changes_file);
1575 object.print(writer, " ");
1576 fflush(m_changes_file);
1577 m_bdirty = true;
1578 }
1579}
1580
1581void NavObjectChanges::AddTrack(Track *pr, const char *action) {
1582 SetRootGPXNode();
1583
1584 pugi::xml_node object = root().append_child("trk");
1585 GPXCreateTrk(object, pr, RT_OUT_NO_RTPTS); // emit a void track, no waypoints
1586
1587 pugi::xml_node xchild = object.child("extensions");
1588 pugi::xml_node child = xchild.append_child("opencpn:action");
1589 child.append_child(pugi::node_pcdata).set_value(action);
1590
1591 if (m_changes_file) {
1592 pugi::xml_writer_file writer(m_changes_file);
1593 object.print(writer, " ");
1594 fflush(m_changes_file);
1595 m_bdirty = true;
1596 }
1597}
1598
1599void NavObjectChanges::AddWP(RoutePoint *pWP, const char *action) {
1600 SetRootGPXNode();
1601
1602 pugi::xml_node object = root().append_child("wpt");
1603
1604 int flags = OPT_WPT;
1605 // If the action is a simple deletion, simplify the output flags
1606 if (!strncmp(action, "delete", 6)) flags = OUT_GUID | OUT_NAME;
1607
1608 GPXCreateWpt(object, pWP, flags);
1609
1610 pugi::xml_node xchild = object.child("extensions");
1611 pugi::xml_node child = xchild.append_child("opencpn:action");
1612 child.append_child(pugi::node_pcdata).set_value(action);
1613
1614 if (m_changes_file) {
1615 pugi::xml_writer_file writer(m_changes_file);
1616 object.print(writer, " ");
1617 fflush(m_changes_file);
1618 m_bdirty = true;
1619 }
1620}
1621
1622void NavObjectChanges::AddTrackPoint(TrackPoint *pWP, const char *action,
1623 const wxString &parent_GUID) {
1624 SetRootGPXNode();
1625
1626 pugi::xml_node object = root().append_child("tkpt");
1627 GPXCreateTrkpt(object, pWP, OPT_TRACKPT);
1628
1629 pugi::xml_node xchild = object.append_child("extensions");
1630
1631 pugi::xml_node child = xchild.append_child("opencpn:action");
1632 child.append_child(pugi::node_pcdata).set_value(action);
1633
1634 pugi::xml_node gchild = xchild.append_child("opencpn:track_GUID");
1635 gchild.append_child(pugi::node_pcdata).set_value(parent_GUID.mb_str());
1636
1637 if (m_changes_file) {
1638 pugi::xml_writer_file writer(m_changes_file);
1639 object.print(writer, " ");
1640 fflush(m_changes_file);
1641 m_bdirty = true;
1642 }
1643}
1644
1645bool NavObjectChanges::ApplyChanges(void) {
1646 // Let's reconstruct the unsaved changes
1647
1648 pugi::xml_node object = this->first_child();
1649
1650 while (strlen(object.name())) {
1651 if (!strcmp(object.name(), "wpt") && pWayPointMan) {
1652 RoutePoint *pWp = ::GPXLoadWaypoint1(object, _T("circle"), _T(""), false,
1653 false, false, 0);
1654
1655 pWp->m_bIsolatedMark = true;
1656 RoutePoint *pExisting = WaypointExists(pWp->m_GUID);
1657
1658 pugi::xml_node xchild = object.child("extensions");
1659 pugi::xml_node child = xchild.child("opencpn:action");
1660
1661 if (!strcmp(child.first_child().value(), "add")) {
1662 if (!pExisting) pWayPointMan->AddRoutePoint(pWp);
1663 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1664 }
1665
1666 else if (!strcmp(child.first_child().value(), "update")) {
1667 if (pExisting) {
1668 pWayPointMan->RemoveRoutePoint(pExisting);
1669 pWayPointMan->DestroyWaypoint(pExisting, false);
1670 delete pExisting;
1671 pWayPointMan->AddRoutePoint(pWp);
1672 pSelect->AddSelectableRoutePoint(pWp->m_lat, pWp->m_lon, pWp);
1673 } else {
1674 delete pWp;
1675 }
1676 }
1677
1678 else if (!strcmp(child.first_child().value(), "delete")) {
1679 if (pExisting) pWayPointMan->DestroyWaypoint(pExisting, false);
1680 delete pExisting;
1681 delete pWp;
1682 } else
1683 delete pWp;
1684 } else if (!strcmp(object.name(), "trk") && g_pRouteMan) {
1685 Track *pTrack = GPXLoadTrack1(object, false, false, false, 0);
1686
1687 if (pTrack) {
1688 pugi::xml_node xchild = object.child("extensions");
1689 pugi::xml_node child = xchild.child("opencpn:action");
1690
1691 Track *pExisting = TrackExists(pTrack->m_GUID);
1692 if (!strcmp(child.first_child().value(), "update")) {
1693 if (pExisting) {
1694 pExisting->SetName(pTrack->GetName());
1695 pExisting->m_TrackStartString = pTrack->m_TrackStartString;
1696 pExisting->m_TrackEndString = pTrack->m_TrackEndString;
1697 }
1698 delete pTrack;
1699 }
1700
1701 else if (!strcmp(child.first_child().value(), "delete")) {
1702 if (pExisting) {
1703 m_bSkipChangeSetUpdate = true;
1704 // evt_delete_track.Notify(std::make_shared<Track>(*pExisting), "");
1705 // // Why were we doing this? pExisting got destroyed immediately...
1706 g_pRouteMan->DeleteTrack(pExisting);
1707 m_bSkipChangeSetUpdate = false;
1708 }
1709 delete pTrack;
1710 }
1711
1712 else if (!strcmp(child.first_child().value(), "add")) {
1713 if (!pExisting) ::InsertTrack(pTrack, true);
1714 }
1715
1716 else
1717 delete pTrack;
1718 }
1719 }
1720
1721 else if (!strcmp(object.name(), "rte") && g_pRouteMan) {
1722 Route *pRoute =
1723 GPXLoadRoute1(object, false, false, false, 0, true, false);
1724
1725 if (pRoute) {
1726 Route *pExisting = RouteExists(pRoute->m_GUID);
1727 pugi::xml_node xchild = object.child("extensions");
1728 pugi::xml_node child = xchild.child("opencpn:action");
1729
1730 if (!strcmp(child.first_child().value(), "add")) {
1731 delete pRoute;
1732 pRoute = GPXLoadRoute1(object, false, false, false, 0, true, true);
1733 ::UpdateRouteA(pRoute, this, this);
1734 delete pRoute;
1735 }
1736
1737 else if (!strcmp(child.first_child().value(), "update")) {
1738 if (pExisting) {
1739 delete pRoute;
1740 pRoute = GPXLoadRoute1(object, false, false, false, 0, true, true);
1741 ::UpdateRouteA(pRoute, this, this);
1742 }
1743 delete pRoute;
1744 }
1745
1746 else if (!strcmp(child.first_child().value(), "delete")) {
1747 if (pExisting) {
1748 m_bSkipChangeSetUpdate = true;
1749 // evt_delete_route.Notify(std::make_shared<Route>(*pExisting), "");
1750 // // Why were we doing this? pExisting got destroyed immediately...
1751 g_pRouteMan->DeleteRoute(pExisting, this);
1752 m_bSkipChangeSetUpdate = false;
1753 }
1754 delete pRoute;
1755 }
1756
1757 else {
1758 delete pRoute;
1759 }
1760 }
1761 } else if (!strcmp(object.name(), "tkpt") && pWayPointMan) {
1762 TrackPoint *pWp = ::GPXLoadTrackPoint1(object);
1763
1764 // RoutePoint *pExisting = WaypointExists(
1765 // pWp->GetName(), pWp->m_lat, pWp->m_lon );
1766
1767 pugi::xml_node xchild = object.child("extensions");
1768 pugi::xml_node child = xchild.child("opencpn:action");
1769
1770 pugi::xml_node guid_child = xchild.child("opencpn:track_GUID");
1771 wxString track_GUID(guid_child.first_child().value(), wxConvUTF8);
1772
1773 Track *pExistingTrack = TrackExists(track_GUID);
1774
1775 if (!strcmp(child.first_child().value(), "add") && pExistingTrack &&
1776 pWp) {
1777 pExistingTrack->AddPoint(pWp);
1778 pWp->m_GPXTrkSegNo = pExistingTrack->GetCurrentTrackSeg() + 1;
1779 } else
1780 delete pWp;
1781 }
1782
1783 object = object.next_sibling();
1784 }
1785 // Check to make sure we haven't loaded tracks with less than 2 points
1786 auto it = g_TrackList.begin();
1787 while (it != g_TrackList.end()) {
1788 Track *pTrack = *it;
1789 if (pTrack->GetnPoints() < 2) {
1790 auto to_erase = it;
1791 --it;
1792 g_TrackList.erase(to_erase);
1793 delete pTrack;
1794 }
1795 ++it;
1796 }
1797
1798 return true;
1799}
1800
1801void NavObjectChanges::AddNewRoute(Route *pr) {
1802 // if( pr->m_bIsInLayer )
1803 // return true;
1804 if (!m_bSkipChangeSetUpdate) AddRoute(pr, "add");
1805}
1806
1807void NavObjectChanges::UpdateRoute(Route *pr) {
1808 // if( pr->m_bIsInLayer ) return true;
1809 if (!m_bSkipChangeSetUpdate) AddRoute(pr, "update");
1810}
1811
1812void NavObjectChanges::DeleteConfigRoute(Route *pr) {
1813 // if( pr->m_bIsInLayer )
1814 // return true;
1815 if (!m_bSkipChangeSetUpdate) AddRoute(pr, "delete");
1816}
1817
1818void NavObjectChanges::AddNewTrack(Track *pt) {
1819 if (!pt->m_bIsInLayer && !m_bSkipChangeSetUpdate) AddTrack(pt, "add");
1820}
1821
1822void NavObjectChanges::UpdateTrack(Track *pt) {
1823 if (pt->m_bIsInLayer && !m_bSkipChangeSetUpdate) AddTrack(pt, "update");
1824}
1825
1826void NavObjectChanges::DeleteConfigTrack(Track *pt) {
1827 if (!pt->m_bIsInLayer && !m_bSkipChangeSetUpdate) AddTrack(pt, "delete");
1828}
1829
1830void NavObjectChanges::AddNewWayPoint(RoutePoint *pWP, int crm) {
1831 if (!pWP->m_bIsInLayer && pWP->m_bIsolatedMark && !m_bSkipChangeSetUpdate)
1832 AddWP(pWP, "add");
1833}
1834
1835void NavObjectChanges::UpdateWayPoint(RoutePoint *pWP) {
1836 if (!pWP->m_bIsInLayer && !m_bSkipChangeSetUpdate) AddWP(pWP, "update");
1837}
1838
1839void NavObjectChanges::DeleteWayPoint(RoutePoint *pWP) {
1840 if (!pWP->m_bIsInLayer && !m_bSkipChangeSetUpdate) AddWP(pWP, "delete");
1841}
1842
1843void NavObjectChanges::AddNewTrackPoint(TrackPoint *pWP,
1844 const wxString &parent_GUID) {
1845 if (!m_bSkipChangeSetUpdate) AddTrackPoint(pWP, "add", parent_GUID);
1846}
1847
1848RoutePoint *WaypointExists(const wxString &name, double lat, double lon) {
1849 RoutePoint *pret = NULL;
1850 // if( g_bIsNewLayer ) return NULL;
1851 wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1852 while (node) {
1853 RoutePoint *pr = node->GetData();
1854
1855 // if( pr->m_bIsInLayer ) return NULL;
1856
1857 if (name == pr->GetName()) {
1858 if (fabs(lat - pr->m_lat) < 1.e-6 && fabs(lon - pr->m_lon) < 1.e-6) {
1859 pret = pr;
1860 break;
1861 }
1862 }
1863 node = node->GetNext();
1864 }
1865
1866 return pret;
1867}
1868
1869RoutePoint *WaypointExists(const wxString &guid) {
1870 wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1871 while (node) {
1872 RoutePoint *pr = node->GetData();
1873
1874 // if( pr->m_bIsInLayer ) return NULL;
1875
1876 if (guid == pr->m_GUID) {
1877 return pr;
1878 }
1879 node = node->GetNext();
1880 }
1881
1882 return NULL;
1883}
1884
1885bool WptIsInRouteList(RoutePoint *pr) {
1886 bool IsInList = false;
1887
1888 wxRouteListNode *node1 = pRouteList->GetFirst();
1889 while (node1) {
1890 Route *pRoute = node1->GetData();
1891 RoutePointList *pRoutePointList = pRoute->pRoutePointList;
1892
1893 wxRoutePointListNode *node2 = pRoutePointList->GetFirst();
1894 RoutePoint *prp;
1895
1896 while (node2) {
1897 prp = node2->GetData();
1898
1899 if (pr->IsSame(prp)) {
1900 IsInList = true;
1901 break;
1902 }
1903
1904 node2 = node2->GetNext();
1905 }
1906 node1 = node1->GetNext();
1907 }
1908 return IsInList;
1909}
1910
1911Route *RouteExists(const wxString &guid) {
1912 wxRouteListNode *route_node = pRouteList->GetFirst();
1913
1914 while (route_node) {
1915 Route *proute = route_node->GetData();
1916
1917 if (guid == proute->m_GUID) return proute;
1918
1919 route_node = route_node->GetNext();
1920 }
1921 return NULL;
1922}
1923
1924Route *RouteExists(Route *pTentRoute) {
1925 wxRouteListNode *route_node = pRouteList->GetFirst();
1926 while (route_node) {
1927 Route *proute = route_node->GetData();
1928
1929 if (proute->IsEqualTo(pTentRoute)) return proute;
1930
1931 route_node = route_node->GetNext(); // next route
1932 }
1933 return NULL;
1934}
1935
1936Track *TrackExists(const wxString &guid) {
1937 for (Track *ptrack : g_TrackList) {
1938 if (guid == ptrack->m_GUID) return ptrack;
1939 }
1940 return NULL;
1941}
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.
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, 2=miles).
float m_fWaypointRangeRingsStep
Distance between consecutive range rings in nautical miles.
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, NavObjectChanges *nav_obj_changes)
Definition routeman.cpp:835
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.
bool RemoveRoutePoint(RoutePoint *prp)
Remove a routepoint from list if present, deallocate it all cases.