1 /******************************************************************************
2  * $Id: chartcatalog.cpp,v 1.0 2011/02/26 01:54:37 nohal Exp $
3  *
4  * Project:  OpenCPN
5  * Purpose:  Chart downloader Plugin
6  * Author:   Pavel Kalian
7  *
8  ***************************************************************************
9  *   Copyright (C) 2011 by Pavel Kalian   *
10  *   $EMAIL$   *
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  *   This program is distributed in the hope that it will be useful,       *
18  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
19  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
20  *   GNU General Public License for more details.                          *
21  *                                                                         *
22  *   You should have received a copy of the GNU General Public License     *
23  *   along with this program; if not, write to the                         *
24  *   Free Software Foundation, Inc.,                                       *
25  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
26  ***************************************************************************
27  */
28 
29 #include "chartcatalog.h"
30 #include <wx/tokenzr.h>
31 
32 #include <wx/arrimpl.cpp>
33     //WX_DEFINE_OBJARRAY(wxArrayOfNoticeToMariners);
34     WX_DEFINE_OBJARRAY(wxArrayOfVertexes);
35     WX_DEFINE_OBJARRAY(wxArrayOfPanels);
36     WX_DEFINE_OBJARRAY(wxArrayOfCharts);
37 
38 
39 // Chart Catalog implementation
LoadFromFile(wxString path,bool headerOnly)40 bool ChartCatalog::LoadFromFile( wxString path, bool headerOnly )
41 {
42     dt_valid = wxInvalidDateTime;      // Invalidate all dates
43     date_created = dt_valid;            // so dates of one catalog
44     time_created = dt_valid;            // don't propagate into another
45     date_valid = dt_valid;
46     title = _("Catalog is not valid.");      // Invalidate the title in case we read a bad file
47     if( !wxFileExists(path) )
48         return false;
49 
50     pugi::xml_document *doc = new pugi::xml_document;
51     bool ret = doc->load_file( path.mb_str() );
52     if (ret)
53         ret = LoadFromXml( doc, headerOnly );
54     else
55         charts.Clear();
56 
57     wxDELETE(doc);
58 
59     return ret;
60 }
61 
ChartCatalog()62 ChartCatalog::ChartCatalog()
63 {
64 }
65 
~ChartCatalog()66 ChartCatalog::~ChartCatalog()
67 {
68 }
69 
GetReleaseDate()70 wxDateTime ChartCatalog::GetReleaseDate()
71 {
72     if( !dt_valid.IsValid() )
73     {
74         // date-time was invalid so we will create it from time_created and date_created
75         // If they are not valid then we return an invalid date for debugging purposes
76         if ( date_created.IsValid() && time_created.IsValid() )
77         {
78             dt_valid.ParseDate(date_created.FormatDate());
79             dt_valid.ParseTime(time_created.FormatTime());
80             dt_valid.MakeFromTimezone(wxDateTime::UTC);
81         }
82     }
83     wxASSERT(dt_valid.IsValid());
84     return dt_valid;
85 }
86 
LoadFromXml(pugi::xml_document * doc,bool headerOnly)87 bool ChartCatalog::LoadFromXml( pugi::xml_document * doc, bool headerOnly )
88 {
89     pugi::xml_node root = doc->first_child();
90 
91     wxString rootName = wxString::FromUTF8( root.name() );
92     charts.Clear();
93     if( rootName.StartsWith( _T("RncProductCatalog") ) )
94     {
95         if( !ParseNoaaHeader(root.first_child()) )
96         {
97             return false;
98         }
99         if (headerOnly)
100             return true;
101 
102         for (pugi::xml_node element = root.first_child(); element; element = element.next_sibling()){
103             if( !strcmp(element.name(), "chart") ){
104                 charts.Add(new RasterChart(element));
105             }
106         }
107     }
108     else if( rootName.StartsWith( _T("EncProductCatalog") ) )
109     {
110         if( !ParseNoaaHeader(root.first_child()) )
111         {
112             return false;
113         }
114         if (headerOnly)
115             return true;
116 
117         for (pugi::xml_node element = root.first_child(); element; element = element.next_sibling()){
118             if( !strcmp(element.name(), "cell") ){
119                 charts.Add(new EncCell(element));
120             }
121         }
122     }
123     // "IENCBuoyProductCatalog" and "IENCSouthwestPassProductCatalog" added by .Paul.
124     else if( rootName.StartsWith(_T("IENCU37ProductCatalog")) ||
125              rootName.StartsWith(_T("IENCBuoyProductCatalog")) ||
126              rootName.StartsWith(_T("IENCSouthwestPassProductCatalog")) )
127     {
128         if( !ParseNoaaHeader(root.first_child()) )
129         {
130             return false;
131         }
132         if( headerOnly )
133             return true;
134 
135         for (pugi::xml_node element = root.first_child(); element; element = element.next_sibling()){
136             if( !strcmp(element.name(), "Cell") ){
137                 charts.Add(new IEncCell(element));
138             }
139         }
140     }
141     else
142     {
143         return false;
144     }
145 
146     return true;
147 }
148 
ParseNoaaHeader(const pugi::xml_node & xmldata)149 bool ChartCatalog::ParseNoaaHeader( const pugi::xml_node &xmldata )
150 {
151     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
152         if( !strcmp(element.name(), "title") ){
153             title = wxString::FromUTF8(element.first_child().value());
154         }
155         else if( !strcmp(element.name(), "date_created")) {
156             date_created.ParseDate( wxString::FromUTF8(element.first_child().value()) );
157             wxASSERT(date_created.IsValid());
158         }
159         else if( !strcmp(element.name(), "time_created")) {
160             time_created.ParseTime( wxString::FromUTF8(element.first_child().value()) );
161             wxASSERT(time_created.IsValid());
162         }
163         else if( !strcmp(element.name(), "date_valid")) {
164             date_valid.ParseDate( wxString::FromUTF8(element.first_child().value()) );
165             wxASSERT(time_created.IsValid());
166         }
167         else if( !strcmp(element.name(), "time_valid")) {
168             time_valid.ParseTime( wxString::FromUTF8(element.first_child().value()) );
169             wxASSERT(time_created.IsValid());
170         }
171         else if( !strcmp(element.name(), "dt_valid")) {
172             wxStringTokenizer tk( wxString::FromUTF8(element.first_child().value()), _T("TZ") );
173             dt_valid.ParseDate(tk.GetNextToken());
174             dt_valid.ParseTime(tk.GetNextToken());
175             dt_valid.MakeFromTimezone(wxDateTime::UTC);
176             wxASSERT(dt_valid.IsValid());
177         }
178         else if( !strcmp(element.name(), "ref_spec")) {
179             ref_spec = wxString::FromUTF8(element.first_child().value());
180         }
181         else if( !strcmp(element.name(), "ref_spec_vers")) {
182             ref_spec_vers = wxString::FromUTF8(element.first_child().value());
183         }
184         else if( !strcmp(element.name(), "s62AgencyCode")) {
185             s62AgencyCode = wxString::FromUTF8(element.first_child().value());
186         }
187 
188     }
189 
190     return true;
191 }
192 
~Chart()193 Chart::~Chart()
194 {
195     coast_guard_districts->Clear();
196     wxDELETE(coast_guard_districts);
197     states->Clear();
198     wxDELETE(states);
199     regions->Clear();
200     wxDELETE(regions);
201     wxDELETE(nm);
202     wxDELETE(lnm);
203 }
204 
Chart(pugi::xml_node & xmldata)205 Chart::Chart( pugi::xml_node &xmldata )
206 {
207     coast_guard_districts = new wxArrayString();
208     states = new wxArrayString();
209     regions = new wxArrayString();
210     target_filename = wxEmptyString;
211     reference_file = wxEmptyString;
212     manual_download_url = wxEmptyString;
213     title = wxEmptyString;
214     zipfile_location = wxEmptyString;
215     zipfile_size = -1;
216     zipfile_datetime = wxInvalidDateTime;
217     zipfile_datetime_iso8601 = wxInvalidDateTime;
218     nm = NULL;
219     lnm = NULL;
220 
221     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
222         if( !strcmp(element.name(), "title")) {
223             title = wxString::FromUTF8(element.first_child().value());
224         }
225         else if( !strcmp(element.name(), "lname")) {
226             title = wxString::FromUTF8(element.first_child().value());
227         }
228         else if( !strcmp(element.name(), "coast_guard_districts")) {
229             for (pugi::xml_node subElement = element.first_child(); subElement; subElement = subElement.next_sibling()){
230                 coast_guard_districts->Add( wxString::FromUTF8(subElement.first_child().value()) );
231             }
232         }
233         else if( !strcmp(element.name(), "states")) {
234             for (pugi::xml_node subElement = element.first_child(); subElement; subElement = subElement.next_sibling()){
235                 states->Add( wxString::FromUTF8(subElement.first_child().value()) );
236             }
237         }
238         else if( !strcmp(element.name(), "regions")) {
239             for (pugi::xml_node subElement = element.first_child(); subElement; subElement = subElement.next_sibling()){
240                 regions->Add( wxString::FromUTF8(subElement.first_child().value()) );
241             }
242         }
243         else if( !strcmp(element.name(), "zipfile_location")) {
244             zipfile_location = wxString::FromUTF8(element.first_child().value());
245         }
246         else if( !strcmp(element.name(), "zipfile_datetime")) {
247             if( zipfile_datetime.ParseFormat(wxString::FromUTF8(element.first_child().value()), _T("%Y%m%d_%H%M%S")) )
248                     zipfile_datetime.MakeFromTimezone(wxDateTime::UTC);
249         }
250         else if( !strcmp(element.name(), "zipfile_datetime_iso8601")) {
251             wxStringTokenizer tk(wxString::FromUTF8(element.first_child().value()), _T("TZ"));
252             zipfile_datetime_iso8601.ParseDate(tk.GetNextToken());
253             zipfile_datetime_iso8601.ParseTime(tk.GetNextToken());
254             zipfile_datetime_iso8601.MakeFromTimezone(wxDateTime::UTC);
255         }
256         else if( !strcmp(element.name(), "zipfile_size")) {
257             zipfile_size = wxAtoi(wxString::FromUTF8(element.first_child().value()));
258         }
259         else if( !strcmp(element.name(), "cov")) {
260             for (pugi::xml_node subElement = element.first_child(); subElement; subElement = subElement.next_sibling()){
261                 coverage.Add(new Panel(subElement));
262             }
263         }
264         else if( !strcmp(element.name(), "target_filename")) {
265             target_filename = wxString::FromUTF8(element.first_child().value());
266         }
267         else if( !strcmp(element.name(), "reference_file")) {
268             reference_file = wxString::FromUTF8(element.first_child().value());
269         }
270         else if( !strcmp(element.name(), "manual_download_url")) {
271             manual_download_url = wxString::FromUTF8(element.first_child().value());
272         }
273         else if( !strcmp(element.name(), "nm")) {
274             // NOT USED
275             // nm = new NoticeToMariners(element);
276         }
277         else if( !strcmp(element.name(), "lnm")) {
278             // NOT USED
279             // lnm = new NoticeToMariners(element);
280         }
281     }
282 }
283 
GetChartFilename(bool to_check)284 wxString Chart::GetChartFilename( bool to_check )
285 {
286     if( to_check && reference_file != wxEmptyString )
287         return reference_file;
288     if( target_filename != wxEmptyString )
289         return target_filename;
290     wxString file;
291     wxStringTokenizer tk(zipfile_location, _T("/"));
292     do
293     {
294         file = tk.GetNextToken();
295     } while(tk.HasMoreTokens());
296     return file;
297 }
298 
RasterChart(pugi::xml_node & xmldata)299 RasterChart::RasterChart( pugi::xml_node &xmldata ) : Chart( xmldata )
300 {
301     number = wxEmptyString;
302     source_edition = -1;
303     raster_edition = -1;
304     ntm_edition = -1;
305     source_date = wxEmptyString;
306     ntm_date = wxEmptyString;
307     source_edition_last_correction = wxEmptyString;
308     raster_edition_last_correction = wxEmptyString;
309     ntm_edition_last_correction = wxEmptyString;
310 
311     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
312         if( !strcmp(element.name(), "number")) {
313             number = wxString::FromUTF8(element.first_child().value());
314         }
315         else if( !strcmp(element.name(), "source_edition")) {
316             source_edition = wxAtoi(wxString::FromUTF8(element.first_child().value()));
317         }
318         else if( !strcmp(element.name(), "raster_edition")) {
319             raster_edition = wxAtoi(wxString::FromUTF8(element.first_child().value()));
320         }
321         else if( !strcmp(element.name(), "ntm_edition")) {
322             ntm_edition = wxAtoi(wxString::FromUTF8(element.first_child().value()));
323         }
324         else if( !strcmp(element.name(), "source_date")) {
325             source_date = wxString::FromUTF8(element.first_child().value());
326         }
327         else if( !strcmp(element.name(), "ntm_date")) {
328             ntm_date = wxString::FromUTF8(element.first_child().value());
329         }
330         else if( !strcmp(element.name(), "source_edition_last_correction")) {
331             source_edition_last_correction = wxString::FromUTF8(element.first_child().value());
332         }
333         else if( !strcmp(element.name(), "raster_edition_last_correction")) {
334             raster_edition_last_correction = wxString::FromUTF8(element.first_child().value());
335         }
336         else if( !strcmp(element.name(), "ntm_edition_last_correction")) {
337             ntm_edition_last_correction = wxString::FromUTF8(element.first_child().value());
338         }
339     }
340 }
341 
EncCell(pugi::xml_node & xmldata)342 EncCell::EncCell( pugi::xml_node &xmldata ) : Chart( xmldata )
343 {
344     number = wxEmptyString;  //  Use number (not name) for zip file name and cell name  .Paul.
345     src_chart = wxEmptyString;
346     cscale = -1;
347     status = wxEmptyString;
348     edtn = -1;
349     updn = -1;
350     uadt = wxInvalidDateTime;
351     isdt = wxInvalidDateTime;
352 
353     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
354         if( !strcmp(element.name(), "name")) {
355             number = wxString::FromUTF8(element.first_child().value());
356         }
357         else if( !strcmp(element.name(), "src_chart")) {
358             src_chart = wxString::FromUTF8(element.first_child().value());
359         }
360         else if( !strcmp(element.name(), "cscale")) {
361             cscale = wxAtoi(wxString::FromUTF8(element.first_child().value()));
362         }
363         else if( !strcmp(element.name(), "status")) {
364             status = wxString::FromUTF8(element.first_child().value());
365         }
366         else if( !strcmp(element.name(), "edtn")) {
367             edtn = wxAtoi(wxString::FromUTF8(element.first_child().value()));
368         }
369         else if( !strcmp(element.name(), "updn")) {
370             updn = wxAtoi(wxString::FromUTF8(element.first_child().value()));
371         }
372         else if( !strcmp(element.name(), "uadt")) {
373             uadt.ParseDateTime(wxString::FromUTF8(element.first_child().value()));
374         }
375         else if( !strcmp(element.name(), "isdt")) {
376             isdt.ParseDateTime(wxString::FromUTF8(element.first_child().value()));
377         }
378     }
379 }
380 
IEncCell(pugi::xml_node & xmldata)381 IEncCell::IEncCell( pugi::xml_node &xmldata ) : Chart( xmldata )
382 {
383     //  Use number (not name) for zip file name and cell name  .Paul.
384     number = wxEmptyString;
385     location = NULL;
386     river_name = wxEmptyString;
387     river_miles = NULL;
388     area = NULL;
389     edition = wxEmptyString;
390     shp_file = NULL;
391     s57_file = NULL;
392     kml_file = NULL;
393 
394     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
395         if( !strcmp(element.name(), "name")) {
396             //  Use number (not name) for zip file name and cell name  .Paul.
397             number = wxString::FromUTF8(element.first_child().value());
398             zipfile_location = wxString::Format(_T("%s.zip"), number.c_str());
399         }
400         else if( !strcmp(element.name(), "location")) {
401             location = new Location(element);
402         }
403         else if( !strcmp(element.name(), "river_name")) {
404             river_name = wxString::FromUTF8(element.first_child().value());
405         }
406         else if( !strcmp(element.name(), "river_miles")) {
407             river_miles = new RiverMiles(element);
408         }
409         else if( !strcmp(element.name(), "river_miles")) {
410             river_miles = new RiverMiles(element);
411         }
412         else if( !strcmp(element.name(), "area")) {
413             area = new Area(element);
414         }
415         else if( !strcmp(element.name(), "shp_file")) {
416             shp_file = new ChartFile(element);
417         }
418         else if( !strcmp(element.name(), "s57_file")) {
419             s57_file = new ChartFile(element);
420         }
421         else if( !strcmp(element.name(), "kml_file")) {
422             kml_file = new ChartFile(element);
423         }
424         else if( !strcmp(element.name(), "edition")) {
425             edition = wxString::FromUTF8(element.first_child().value());
426         }
427     }
428 }
429 
~IEncCell()430 IEncCell::~IEncCell()
431 {
432     wxDELETE(location);
433     wxDELETE(river_miles);
434     wxDELETE(area);
435     wxDELETE(shp_file);
436     wxDELETE(s57_file);
437     wxDELETE(kml_file);
438 }
439 
GetChartTitle()440 wxString IEncCell::GetChartTitle()
441 // Revised by .Paul. to support IENC catalogs that do not identify rivers or river miles.
442 {
443     if( river_name != wxEmptyString )
444     {
445         // This formatting of river_name.c_str() ... river_miles->end works for "IENCU37ProductCatalog"
446         // where river_name is specified.
447         return wxString::Format(_("%s (%s to %s), river miles %3.1f - %3.1f"), river_name.c_str(), location->from.c_str(), location->to.c_str(), river_miles->begin, river_miles->end);
448     }
449     else
450     {
451         // Simply use the Cell_name for "IENCBuoyProductCatalog" or "IENCSouthwestPassProductCatalog"
452         // where river_name is not specified.
453         // Cell_name is in number.c_str()     Cell_name was in name.c_str()
454         return wxString::Format(_("%s"), number.c_str());// .Paul.
455     }
456 }
457 
GetDownloadLocation()458 wxString IEncCell::GetDownloadLocation()
459 {
460     return s57_file->location;
461 }
462 
GetUpdateDatetime()463 wxDateTime IEncCell::GetUpdateDatetime()
464 {
465     return s57_file->date_posted;
466 }
467 
ChartFile(pugi::xml_node & xmldata)468 ChartFile::ChartFile( pugi::xml_node &xmldata )
469 {
470     file_size = -1;
471     location = wxEmptyString;
472     date_posted = wxInvalidDateTime;
473     time_posted = wxInvalidDateTime;
474 
475     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
476         if( !strcmp(element.name(), "location")) {
477             location = wxString::FromUTF8(element.first_child().value());
478         }
479         else if( !strcmp(element.name(), "date_posted")) {
480             date_posted.ParseDate(wxString::FromUTF8(element.first_child().value()));
481         }
482         else if( !strcmp(element.name(), "time_posted")) {
483             if(strlen(element.first_child().value()))
484                 time_posted.ParseTime(wxString::FromUTF8(element.first_child().value()));
485             else
486                 time_posted.ParseTime(_T("00:00:00"));
487         }
488         else if( !strcmp(element.name(), "file_size")) {
489             if(strlen(element.first_child().value()))
490                 file_size = wxAtoi(wxString::FromUTF8(element.first_child().value()));
491             else
492                 file_size = -1;
493         }
494     }
495 }
496 
Area(pugi::xml_node & xmldata)497 Area::Area( pugi::xml_node &xmldata )
498 {
499     north = 0.0;
500     south = 0.0;
501     east = 0.0;
502     west = 0.0;
503 
504     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
505         if( !strcmp(element.name(), "north")) {
506                 north = wxAtof(wxString::FromUTF8(element.first_child().value()));
507         }
508         else if( !strcmp(element.name(), "south")) {
509                 south = wxAtof(wxString::FromUTF8(element.first_child().value()));
510         }
511         else if( !strcmp(element.name(), "east")) {
512                 east = wxAtof(wxString::FromUTF8(element.first_child().value()));
513         }
514         else if( !strcmp(element.name(), "west")) {
515                 west = wxAtof(wxString::FromUTF8(element.first_child().value()));
516         }
517     }
518 }
519 
RiverMiles(pugi::xml_node & xmldata)520 RiverMiles::RiverMiles( pugi::xml_node &xmldata )
521 {
522     begin = -1;
523     end = -1;
524     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
525         if( !strcmp(element.name(), "begin")) {
526                 begin = wxAtof(wxString::FromUTF8(element.first_child().value()));
527         }
528         else if( !strcmp(element.name(), "end")) {
529                 end = wxAtof(wxString::FromUTF8(element.first_child().value()));
530         }
531     }
532 }
533 
Location(pugi::xml_node & xmldata)534 Location::Location( pugi::xml_node &xmldata )
535 {
536     from = wxEmptyString;
537     to = wxEmptyString;
538     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
539         if( !strcmp(element.name(), "from")) {
540             from = wxString::FromUTF8(element.first_child().value());
541         }
542         else if( !strcmp(element.name(), "to")) {
543             to = wxString::FromUTF8(element.first_child().value());
544         }
545     }
546 }
547 
548 
NoticeToMariners(pugi::xml_node & xmldata)549 NoticeToMariners::NoticeToMariners( pugi::xml_node &xmldata )
550 {
551     agency = wxEmptyString;
552     doc = wxEmptyString;
553     date = wxInvalidDateTime;
554 
555     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
556         if( !strcmp(element.name(), "nm_agency")) {
557             agency = wxString::FromUTF8(element.first_child().value());
558         }
559         else if( !strcmp(element.name(), "lnm_agency")) {
560             agency = wxString::FromUTF8(element.first_child().value());
561         }
562         else if( !strcmp(element.name(), "doc")) {
563             doc = wxString::FromUTF8(element.first_child().value());
564         }
565         else if( !strcmp(element.name(), "date")) {
566             date.ParseDate(wxString::FromUTF8(element.first_child().value()));
567         }
568     }
569 }
570 
Panel(pugi::xml_node & xmldata)571 Panel::Panel( pugi::xml_node &xmldata )
572 {
573     panel_no = -1;
574 
575     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
576         if( !strcmp(element.name(), "panel_no")) {
577                 panel_no = wxAtoi(wxString::FromUTF8(element.first_child().value()));
578         }
579         else if( !strcmp(element.name(), "vertex")) {
580             // NOT USED
581             //vertexes.Add(new Vertex(element));
582         }
583     }
584 }
585 
~Panel()586 Panel::~Panel()
587 {
588 }
589 
RncPanel(pugi::xml_node & xmldata)590 RncPanel::RncPanel( pugi::xml_node &xmldata ) : Panel( xmldata )
591 {
592     panel_title = wxEmptyString;
593     file_name = wxEmptyString;
594     scale = 0;
595 
596     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
597         if( !strcmp(element.name(), "panel_title")) {
598             panel_title = wxString::FromUTF8(element.first_child().value());
599         }
600         else if( !strcmp(element.name(), "file_name")) {
601             file_name = wxString::FromUTF8(element.first_child().value());
602         }
603         else if( !strcmp(element.name(), "scale")) {
604             scale = wxAtoi(wxString::FromUTF8(element.first_child().value()));
605         }
606     }
607 }
608 
EncPanel(pugi::xml_node & xmldata)609 EncPanel::EncPanel( pugi::xml_node &xmldata ) : Panel( xmldata )
610 {
611     type = wxEmptyString;
612     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
613         if( !strcmp(element.name(), "type")) {
614             type = wxString::FromUTF8(element.first_child().value());
615         }
616     }
617 }
618 
Vertex(pugi::xml_node & xmldata)619 Vertex::Vertex( pugi::xml_node &xmldata )
620 {
621     //Init properties
622     lat = 999.0;
623     lon = 999.0;
624     for (pugi::xml_node element = xmldata.first_child(); element; element = element.next_sibling()){
625         if( !strcmp(element.name(), "lat")) {
626             wxString::FromUTF8(element.first_child().value()).ToDouble(&lat);
627         }
628         else if( !strcmp(element.name(), "lon")) {
629             wxString::FromUTF8(element.first_child().value()).ToDouble(&lon);
630         }
631     }
632 }
633