OpenCPN Partial API docs
Loading...
Searching...
No Matches
chartcatalog.cpp
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#include <memory>
32
33// Chart Catalog implementation
34bool ChartCatalog::LoadFromFile(wxString path, bool headerOnly) {
35 dt_valid = wxInvalidDateTime; // Invalidate all dates
36 date_created = dt_valid; // so dates of one catalog
37 time_created = dt_valid; // don't propagate into another
38 date_valid = dt_valid;
39 title = _("Catalog is not valid."); // Invalidate the title in case we read a
40 // bad file
41 if (!wxFileExists(path)) return false;
42
44 bool ret = doc.load_file(path.mb_str());
45 if (ret)
46 ret = LoadFromXml(&doc, headerOnly);
47 else
48 charts.clear();
49
50 return ret;
51}
52
53ChartCatalog::ChartCatalog() {}
54
55ChartCatalog::~ChartCatalog() {}
56
57wxDateTime ChartCatalog::GetReleaseDate() {
58 if (!dt_valid.IsValid()) {
59 // date-time was invalid so we will create it from time_created and
60 // date_created If they are not valid then we return an invalid date for
61 // debugging purposes
62 if (date_created.IsValid() && time_created.IsValid()) {
63 dt_valid.ParseDate(date_created.FormatDate());
64 dt_valid.ParseTime(time_created.FormatTime());
65 dt_valid.MakeFromTimezone(wxDateTime::UTC);
66 }
67 }
68 wxASSERT(dt_valid.IsValid());
69 return dt_valid;
70}
71
72bool ChartCatalog::LoadFromXml(pugi::xml_document *doc, bool headerOnly) {
73 pugi::xml_node root = doc->first_child();
74
75 wxString rootName = wxString::FromUTF8(root.name());
76 charts.clear();
77 if (rootName.StartsWith(_T("RncProductCatalog"))) {
78 if (!ParseNoaaHeader(root.first_child())) {
79 return false;
80 }
81 if (headerOnly) return true;
82
83 for (pugi::xml_node element = root.first_child(); element;
84 element = element.next_sibling()) {
85 if (!strcmp(element.name(), "chart")) {
86 charts.push_back(std::make_unique<RasterChart>(element));
87 }
88 }
89 } else if (rootName.StartsWith(_T("EncProductCatalog"))) {
90 if (!ParseNoaaHeader(root.first_child())) {
91 return false;
92 }
93 if (headerOnly) return true;
94
95 for (pugi::xml_node element = root.first_child(); element;
96 element = element.next_sibling()) {
97 if (!strcmp(element.name(), "cell")) {
98 charts.push_back(std::make_unique<EncCell>(element));
99 }
100 }
101 }
102 // "IENCBuoyProductCatalog" and "IENCSouthwestPassProductCatalog" added by
103 // .Paul.
104 else if (rootName.StartsWith(_T("IENCU37ProductCatalog")) ||
105 rootName.StartsWith(_T("IENCBuoyProductCatalog")) ||
106 rootName.StartsWith(_T("IENCSouthwestPassProductCatalog"))) {
107 if (!ParseNoaaHeader(root.first_child())) {
108 return false;
109 }
110 if (headerOnly) return true;
111
112 for (pugi::xml_node element = root.first_child(); element;
113 element = element.next_sibling()) {
114 if (!strcmp(element.name(), "Cell")) {
115 charts.push_back(std::make_unique<IEncCell>(element));
116 }
117 }
118 } else {
119 return false;
120 }
121
122 return true;
123}
124
125bool ChartCatalog::ParseNoaaHeader(const pugi::xml_node &xmldata) {
126 for (pugi::xml_node element = xmldata.first_child(); element;
127 element = element.next_sibling()) {
128 if (!strcmp(element.name(), "title")) {
129 title = wxString::FromUTF8(element.first_child().value());
130 } else if (!strcmp(element.name(), "date_created")) {
131 date_created.ParseDate(wxString::FromUTF8(element.first_child().value()));
132 wxASSERT(date_created.IsValid());
133 } else if (!strcmp(element.name(), "time_created")) {
134 time_created.ParseTime(wxString::FromUTF8(element.first_child().value()));
135 wxASSERT(time_created.IsValid());
136 } else if (!strcmp(element.name(), "date_valid")) {
137 date_valid.ParseDate(wxString::FromUTF8(element.first_child().value()));
138 wxASSERT(time_created.IsValid());
139 } else if (!strcmp(element.name(), "time_valid")) {
140 time_valid.ParseTime(wxString::FromUTF8(element.first_child().value()));
141 wxASSERT(time_created.IsValid());
142 } else if (!strcmp(element.name(), "dt_valid")) {
143 wxStringTokenizer tk(wxString::FromUTF8(element.first_child().value()),
144 _T("TZ"));
145 dt_valid.ParseDate(tk.GetNextToken());
146 dt_valid.ParseTime(tk.GetNextToken());
147 dt_valid.MakeFromTimezone(wxDateTime::UTC);
148 wxASSERT(dt_valid.IsValid());
149 } else if (!strcmp(element.name(), "ref_spec")) {
150 ref_spec = wxString::FromUTF8(element.first_child().value());
151 } else if (!strcmp(element.name(), "ref_spec_vers")) {
152 ref_spec_vers = wxString::FromUTF8(element.first_child().value());
153 } else if (!strcmp(element.name(), "s62AgencyCode")) {
154 s62AgencyCode = wxString::FromUTF8(element.first_child().value());
155 }
156 }
157
158 return true;
159}
160
161Chart::~Chart() {
162 coast_guard_districts->Clear();
163 wxDELETE(coast_guard_districts);
164 states->Clear();
165 wxDELETE(states);
166 regions->Clear();
167 wxDELETE(regions);
168 wxDELETE(nm);
169 wxDELETE(lnm);
170}
171
172Chart::Chart(pugi::xml_node &xmldata) {
173 coast_guard_districts = new wxArrayString();
174 states = new wxArrayString();
175 regions = new wxArrayString();
176 target_filename = wxEmptyString;
177 reference_file = wxEmptyString;
178 manual_download_url = wxEmptyString;
179 title = wxEmptyString;
180 zipfile_location = wxEmptyString;
181 zipfile_size = -1;
182 zipfile_datetime = wxInvalidDateTime;
183 zipfile_datetime_iso8601 = wxInvalidDateTime;
184 nm = NULL;
185 lnm = NULL;
186
187 for (pugi::xml_node element = xmldata.first_child(); element;
188 element = element.next_sibling()) {
189 if (!strcmp(element.name(), "title")) {
190 title = wxString::FromUTF8(element.first_child().value());
191 } else if (!strcmp(element.name(), "lname")) {
192 title = wxString::FromUTF8(element.first_child().value());
193 } else if (!strcmp(element.name(), "coast_guard_districts")) {
194 for (pugi::xml_node subElement = element.first_child(); subElement;
195 subElement = subElement.next_sibling()) {
196 coast_guard_districts->Add(
197 wxString::FromUTF8(subElement.first_child().value()));
198 }
199 } else if (!strcmp(element.name(), "states")) {
200 for (pugi::xml_node subElement = element.first_child(); subElement;
201 subElement = subElement.next_sibling()) {
202 states->Add(wxString::FromUTF8(subElement.first_child().value()));
203 }
204 } else if (!strcmp(element.name(), "regions")) {
205 for (pugi::xml_node subElement = element.first_child(); subElement;
206 subElement = subElement.next_sibling()) {
207 regions->Add(wxString::FromUTF8(subElement.first_child().value()));
208 }
209 } else if (!strcmp(element.name(), "zipfile_location")) {
210 zipfile_location = wxString::FromUTF8(element.first_child().value());
211 } else if (!strcmp(element.name(), "zipfile_datetime")) {
212 if (zipfile_datetime.ParseFormat(
213 wxString::FromUTF8(element.first_child().value()),
214 _T("%Y%m%d_%H%M%S")))
215 zipfile_datetime.MakeFromTimezone(wxDateTime::UTC);
216 } else if (!strcmp(element.name(), "zipfile_datetime_iso8601")) {
217 wxStringTokenizer tk(wxString::FromUTF8(element.first_child().value()),
218 _T("TZ"));
219 zipfile_datetime_iso8601.ParseDate(tk.GetNextToken());
220 zipfile_datetime_iso8601.ParseTime(tk.GetNextToken());
221 zipfile_datetime_iso8601.MakeFromTimezone(wxDateTime::UTC);
222 } else if (!strcmp(element.name(), "zipfile_size")) {
223 zipfile_size = wxAtoi(wxString::FromUTF8(element.first_child().value()));
224 } else if (!strcmp(element.name(), "cov")) {
225 for (pugi::xml_node subElement = element.first_child(); subElement;
226 subElement = subElement.next_sibling()) {
227 coverage.push_back(std::make_unique<Panel>(subElement));
228 }
229 } else if (!strcmp(element.name(), "target_filename")) {
230 target_filename = wxString::FromUTF8(element.first_child().value());
231 } else if (!strcmp(element.name(), "reference_file")) {
232 reference_file = wxString::FromUTF8(element.first_child().value());
233 } else if (!strcmp(element.name(), "manual_download_url")) {
234 manual_download_url = wxString::FromUTF8(element.first_child().value());
235 } else if (!strcmp(element.name(), "nm")) {
236 // NOT USED
237 // nm = new NoticeToMariners(element);
238 } else if (!strcmp(element.name(), "lnm")) {
239 // NOT USED
240 // lnm = new NoticeToMariners(element);
241 }
242 }
243}
244
245wxString Chart::GetChartFilename(bool to_check) {
246 if (to_check && reference_file != wxEmptyString) return reference_file;
247 if (target_filename != wxEmptyString) return target_filename;
248 wxString file;
249 wxStringTokenizer tk(zipfile_location, _T("/"));
250 do {
251 file = tk.GetNextToken();
252 } while (tk.HasMoreTokens());
253 return file;
254}
255
256RasterChart::RasterChart(pugi::xml_node &xmldata) : Chart(xmldata) {
257 number = wxEmptyString;
258 source_edition = -1;
259 raster_edition = -1;
260 ntm_edition = -1;
261 source_date = wxEmptyString;
262 ntm_date = wxEmptyString;
263 source_edition_last_correction = wxEmptyString;
264 raster_edition_last_correction = wxEmptyString;
265 ntm_edition_last_correction = wxEmptyString;
266
267 for (pugi::xml_node element = xmldata.first_child(); element;
268 element = element.next_sibling()) {
269 if (!strcmp(element.name(), "number")) {
270 number = wxString::FromUTF8(element.first_child().value());
271 } else if (!strcmp(element.name(), "source_edition")) {
272 source_edition =
273 wxAtoi(wxString::FromUTF8(element.first_child().value()));
274 } else if (!strcmp(element.name(), "raster_edition")) {
275 raster_edition =
276 wxAtoi(wxString::FromUTF8(element.first_child().value()));
277 } else if (!strcmp(element.name(), "ntm_edition")) {
278 ntm_edition = wxAtoi(wxString::FromUTF8(element.first_child().value()));
279 } else if (!strcmp(element.name(), "source_date")) {
280 source_date = wxString::FromUTF8(element.first_child().value());
281 } else if (!strcmp(element.name(), "ntm_date")) {
282 ntm_date = wxString::FromUTF8(element.first_child().value());
283 } else if (!strcmp(element.name(), "source_edition_last_correction")) {
284 source_edition_last_correction =
285 wxString::FromUTF8(element.first_child().value());
286 } else if (!strcmp(element.name(), "raster_edition_last_correction")) {
287 raster_edition_last_correction =
288 wxString::FromUTF8(element.first_child().value());
289 } else if (!strcmp(element.name(), "ntm_edition_last_correction")) {
290 ntm_edition_last_correction =
291 wxString::FromUTF8(element.first_child().value());
292 }
293 }
294}
295
296EncCell::EncCell(pugi::xml_node &xmldata) : Chart(xmldata) {
297 number = wxEmptyString; // Use number (not name) for zip file name and cell
298 // name .Paul.
299 src_chart = wxEmptyString;
300 cscale = -1;
301 status = wxEmptyString;
302 edtn = -1;
303 updn = -1;
304 uadt = wxInvalidDateTime;
305 isdt = wxInvalidDateTime;
306
307 for (pugi::xml_node element = xmldata.first_child(); element;
308 element = element.next_sibling()) {
309 if (!strcmp(element.name(), "name")) {
310 number = wxString::FromUTF8(element.first_child().value());
311 } else if (!strcmp(element.name(), "src_chart")) {
312 src_chart = wxString::FromUTF8(element.first_child().value());
313 } else if (!strcmp(element.name(), "cscale")) {
314 cscale = wxAtoi(wxString::FromUTF8(element.first_child().value()));
315 } else if (!strcmp(element.name(), "status")) {
316 status = wxString::FromUTF8(element.first_child().value());
317 } else if (!strcmp(element.name(), "edtn")) {
318 edtn = wxAtoi(wxString::FromUTF8(element.first_child().value()));
319 } else if (!strcmp(element.name(), "updn")) {
320 updn = wxAtoi(wxString::FromUTF8(element.first_child().value()));
321 } else if (!strcmp(element.name(), "uadt")) {
322 uadt.ParseDateTime(wxString::FromUTF8(element.first_child().value()));
323 } else if (!strcmp(element.name(), "isdt")) {
324 isdt.ParseDateTime(wxString::FromUTF8(element.first_child().value()));
325 }
326 }
327}
328
329IEncCell::IEncCell(pugi::xml_node &xmldata) : Chart(xmldata) {
330 // Use number (not name) for zip file name and cell name .Paul.
331 number = wxEmptyString;
332 location = NULL;
333 river_name = wxEmptyString;
334 river_miles = NULL;
335 area = NULL;
336 edition = wxEmptyString;
337 shp_file = NULL;
338 s57_file = NULL;
339 kml_file = NULL;
340
341 for (pugi::xml_node element = xmldata.first_child(); element;
342 element = element.next_sibling()) {
343 if (!strcmp(element.name(), "name")) {
344 // Use number (not name) for zip file name and cell name .Paul.
345 number = wxString::FromUTF8(element.first_child().value());
346 zipfile_location = wxString::Format(_T("%s.zip"), number.c_str());
347 } else if (!strcmp(element.name(), "location")) {
348 location = new Location(element);
349 } else if (!strcmp(element.name(), "river_name")) {
350 river_name = wxString::FromUTF8(element.first_child().value());
351 } else if (!strcmp(element.name(), "river_miles")) {
352 river_miles = new RiverMiles(element);
353 } else if (!strcmp(element.name(), "river_miles")) {
354 river_miles = new RiverMiles(element);
355 } else if (!strcmp(element.name(), "area")) {
356 area = new Area(element);
357 } else if (!strcmp(element.name(), "shp_file")) {
358 shp_file = new ChartFile(element);
359 } else if (!strcmp(element.name(), "s57_file")) {
360 s57_file = new ChartFile(element);
361 } else if (!strcmp(element.name(), "kml_file")) {
362 kml_file = new ChartFile(element);
363 } else if (!strcmp(element.name(), "edition")) {
364 edition = wxString::FromUTF8(element.first_child().value());
365 }
366 }
367}
368
369IEncCell::~IEncCell() {
370 wxDELETE(location);
371 wxDELETE(river_miles);
372 wxDELETE(area);
373 wxDELETE(shp_file);
374 wxDELETE(s57_file);
375 wxDELETE(kml_file);
376}
377
378wxString IEncCell::GetChartTitle()
379// Revised by .Paul. to support IENC catalogs that do not identify rivers or
380// river miles.
381{
382 if (river_name != wxEmptyString) {
383 // This formatting of river_name.c_str() ... river_miles->end works for
384 // "IENCU37ProductCatalog" where river_name is specified.
385 return wxString::Format(_("%s (%s to %s), river miles %3.1f - %3.1f"),
386 river_name.c_str(), location->from.c_str(),
387 location->to.c_str(), river_miles->begin,
388 river_miles->end);
389 } else {
390 // Simply use the Cell_name for "IENCBuoyProductCatalog" or
391 // "IENCSouthwestPassProductCatalog" where river_name is not specified.
392 // Cell_name is in number.c_str() Cell_name was in name.c_str()
393 return wxString::Format(_("%s"), number.c_str()); // .Paul.
394 }
395}
396
397wxString IEncCell::GetDownloadLocation() { return s57_file->location; }
398
399wxDateTime IEncCell::GetUpdateDatetime() { return s57_file->date_posted; }
400
401ChartFile::ChartFile(pugi::xml_node &xmldata) {
402 file_size = -1;
403 location = wxEmptyString;
404 date_posted = wxInvalidDateTime;
405 time_posted = wxInvalidDateTime;
406
407 for (pugi::xml_node element = xmldata.first_child(); element;
408 element = element.next_sibling()) {
409 if (!strcmp(element.name(), "location")) {
410 location = wxString::FromUTF8(element.first_child().value());
411 } else if (!strcmp(element.name(), "date_posted")) {
412 date_posted.ParseDate(wxString::FromUTF8(element.first_child().value()));
413 } else if (!strcmp(element.name(), "time_posted")) {
414 if (strlen(element.first_child().value()))
415 time_posted.ParseTime(
416 wxString::FromUTF8(element.first_child().value()));
417 else
418 time_posted.ParseTime(_T("00:00:00"));
419 } else if (!strcmp(element.name(), "file_size")) {
420 if (strlen(element.first_child().value()))
421 file_size = wxAtoi(wxString::FromUTF8(element.first_child().value()));
422 else
423 file_size = -1;
424 }
425 }
426}
427
428Area::Area(pugi::xml_node &xmldata) {
429 north = 0.0;
430 south = 0.0;
431 east = 0.0;
432 west = 0.0;
433
434 for (pugi::xml_node element = xmldata.first_child(); element;
435 element = element.next_sibling()) {
436 if (!strcmp(element.name(), "north")) {
437 north = wxAtof(wxString::FromUTF8(element.first_child().value()));
438 } else if (!strcmp(element.name(), "south")) {
439 south = wxAtof(wxString::FromUTF8(element.first_child().value()));
440 } else if (!strcmp(element.name(), "east")) {
441 east = wxAtof(wxString::FromUTF8(element.first_child().value()));
442 } else if (!strcmp(element.name(), "west")) {
443 west = wxAtof(wxString::FromUTF8(element.first_child().value()));
444 }
445 }
446}
447
448RiverMiles::RiverMiles(pugi::xml_node &xmldata) {
449 begin = -1;
450 end = -1;
451 for (pugi::xml_node element = xmldata.first_child(); element;
452 element = element.next_sibling()) {
453 if (!strcmp(element.name(), "begin")) {
454 begin = wxAtof(wxString::FromUTF8(element.first_child().value()));
455 } else if (!strcmp(element.name(), "end")) {
456 end = wxAtof(wxString::FromUTF8(element.first_child().value()));
457 }
458 }
459}
460
461Location::Location(pugi::xml_node &xmldata) {
462 from = wxEmptyString;
463 to = wxEmptyString;
464 for (pugi::xml_node element = xmldata.first_child(); element;
465 element = element.next_sibling()) {
466 if (!strcmp(element.name(), "from")) {
467 from = wxString::FromUTF8(element.first_child().value());
468 } else if (!strcmp(element.name(), "to")) {
469 to = wxString::FromUTF8(element.first_child().value());
470 }
471 }
472}
473
474NoticeToMariners::NoticeToMariners(pugi::xml_node &xmldata) {
475 agency = wxEmptyString;
476 doc = wxEmptyString;
477 date = wxInvalidDateTime;
478
479 for (pugi::xml_node element = xmldata.first_child(); element;
480 element = element.next_sibling()) {
481 if (!strcmp(element.name(), "nm_agency")) {
482 agency = wxString::FromUTF8(element.first_child().value());
483 } else if (!strcmp(element.name(), "lnm_agency")) {
484 agency = wxString::FromUTF8(element.first_child().value());
485 } else if (!strcmp(element.name(), "doc")) {
486 doc = wxString::FromUTF8(element.first_child().value());
487 } else if (!strcmp(element.name(), "date")) {
488 date.ParseDate(wxString::FromUTF8(element.first_child().value()));
489 }
490 }
491}
492
493Panel::Panel(pugi::xml_node &xmldata) {
494 panel_no = -1;
495
496 for (pugi::xml_node element = xmldata.first_child(); element;
497 element = element.next_sibling()) {
498 if (!strcmp(element.name(), "panel_no")) {
499 panel_no = wxAtoi(wxString::FromUTF8(element.first_child().value()));
500 } else if (!strcmp(element.name(), "vertex")) {
501 // NOT USED
502 // vertexes.Add(new Vertex(element));
503 }
504 }
505}
506
507Panel::~Panel() {}
508
509RncPanel::RncPanel(pugi::xml_node &xmldata) : Panel(xmldata) {
510 panel_title = wxEmptyString;
511 file_name = wxEmptyString;
512 scale = 0;
513
514 for (pugi::xml_node element = xmldata.first_child(); element;
515 element = element.next_sibling()) {
516 if (!strcmp(element.name(), "panel_title")) {
517 panel_title = wxString::FromUTF8(element.first_child().value());
518 } else if (!strcmp(element.name(), "file_name")) {
519 file_name = wxString::FromUTF8(element.first_child().value());
520 } else if (!strcmp(element.name(), "scale")) {
521 scale = wxAtoi(wxString::FromUTF8(element.first_child().value()));
522 }
523 }
524}
525
526EncPanel::EncPanel(pugi::xml_node &xmldata) : Panel(xmldata) {
527 type = wxEmptyString;
528 for (pugi::xml_node element = xmldata.first_child(); element;
529 element = element.next_sibling()) {
530 if (!strcmp(element.name(), "type")) {
531 type = wxString::FromUTF8(element.first_child().value());
532 }
533 }
534}
535
536Vertex::Vertex(pugi::xml_node &xmldata) {
537 // Init properties
538 lat = 999.0;
539 lon = 999.0;
540 for (pugi::xml_node element = xmldata.first_child(); element;
541 element = element.next_sibling()) {
542 if (!strcmp(element.name(), "lat")) {
543 wxString::FromUTF8(element.first_child().value()).ToDouble(&lat);
544 } else if (!strcmp(element.name(), "lon")) {
545 wxString::FromUTF8(element.first_child().value()).ToDouble(&lon);
546 }
547 }
548}