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