41#include <wx/datetime.h>
42#include <wx/clipbrd.h>
44#include "model/ocpn_types.h"
48#include "model/track.h"
49#include "model/route.h"
50#include "ocpn_frame.h"
51#include "model/own_ship.h"
53int Kml::seqCounter = 0;
54bool Kml::insertQtVlmExtendedData =
false;
56int Kml::ParseCoordinates(TiXmlNode* node, dPointList& points) {
57 TiXmlElement* e = node->FirstChildElement(
"coordinates");
59 wxString msg(_T(
"KML Parser found no <coordinates> for the element: "));
60 msg << wxString(node->ToElement()->Value(), wxConvUTF8);
69 std::stringstream ss(e->GetText());
73 if (!std::getline(ss, txtCoord,
','))
break;
75 if (txtCoord.length() == 0)
break;
77 point.x = atof(txtCoord.c_str());
78 std::getline(ss, txtCoord,
',');
79 point.y = atof(txtCoord.c_str());
80 std::getline(ss, txtCoord,
' ');
81 point.z = atof(txtCoord.c_str());
83 points.push_back(point);
88KmlPastebufferType Kml::ParseTrack(TiXmlNode* node, wxString& name) {
89 parsedTrack =
new Track();
90 parsedTrack->SetName(name);
92 if (0 == strncmp(node->ToElement()->Value(),
"LineString", 10)) {
93 dPointList coordinates;
94 if (ParseCoordinates(node, coordinates) > 2) {
97 for (
unsigned int i = 0; i < coordinates.size(); i++) {
98 trackpoint =
new TrackPoint(coordinates[i].y, coordinates[i].x);
99 parsedTrack->AddPoint(trackpoint);
102 return KML_PASTE_TRACK;
105 if (0 == strncmp(node->ToElement()->Value(),
"gx:Track", 8)) {
107 TiXmlElement* point = node->FirstChildElement(
"gx:coord");
108 int pointCounter = 0;
110 for (; point; point = point->NextSiblingElement(
"gx:coord")) {
112 std::stringstream ss(point->GetText());
113 std::string txtCoord;
114 std::getline(ss, txtCoord,
' ');
115 lon = atof(txtCoord.c_str());
116 std::getline(ss, txtCoord,
' ');
117 lat = atof(txtCoord.c_str());
119 parsedTrack->AddPoint(
new TrackPoint(lat, lon));
123 TiXmlElement* when = node->FirstChildElement(
"when");
128 for (; when; when = when->NextSiblingElement(
"when")) {
129 trackpoint = parsedTrack->GetPoint(i);
130 if (!trackpoint)
continue;
131 whenTime.ParseFormat(wxString(when->GetText(), wxConvUTF8),
132 _T(
"%Y-%m-%dT%H:%M:%SZ"));
137 return KML_PASTE_TRACK;
139 return KML_PASTE_INVALID;
142KmlPastebufferType Kml::ParseOnePlacemarkPoint(TiXmlNode* node,
144 double newLat = 0., newLon = 0.;
145 dPointList coordinates;
147 if (ParseCoordinates(node->ToElement(), coordinates)) {
148 newLat = coordinates[0].y;
149 newLon = coordinates[0].x;
152 if (newLat == 0.0 && newLon == 0.0) {
153 wxString msg(_T(
"KML Parser failed to convert <Point> coordinates."));
155 return KML_PASTE_INVALID;
157 wxString pointName = wxEmptyString;
158 TiXmlElement* e = node->Parent()->FirstChild(
"name")->ToElement();
159 if (e) pointName = wxString(e->GetText(), wxConvUTF8);
161 wxString pointDescr = wxEmptyString;
162 e = node->Parent()->FirstChildElement(
"description");
167 TiXmlNode* n = e->FirstChild();
168 if (n)
switch (n->Type()) {
169 case TiXmlNode::TINYXML_TEXT:
170 pointDescr = wxString(e->GetText(), wxConvUTF8);
172 case TiXmlNode::TINYXML_ELEMENT:
173 TiXmlPrinter printer;
174 printer.SetIndent(
"\t");
176 pointDescr = wxString(printer.CStr(), wxConvUTF8);
182 TiXmlNode* n = node->Parent()->FirstChild(
"ExtendedData");
184 TiXmlPrinter printer;
185 printer.SetIndent(
"\t");
187 pointDescr = wxString(printer.CStr(), wxConvUTF8);
192 parsedRoutePoint->m_lat = newLat;
193 parsedRoutePoint->m_lon = newLon;
195 parsedRoutePoint->m_bPtIsSelected =
false;
196 parsedRoutePoint->m_MarkDescription = pointDescr;
197 parsedRoutePoint->SetName(pointName);
199 return KML_PASTE_WAYPOINT;
202KmlPastebufferType Kml::ParsePasteBuffer() {
203 if (!wxTheClipboard->IsOpened())
204 if (!wxTheClipboard->Open())
return KML_PASTE_INVALID;
206 wxTextDataObject data;
207 wxTheClipboard->GetData(data);
208 kmlText = data.GetText();
209 wxTheClipboard->Close();
211 if (kmlText.Find(_T(
"<kml")) == wxNOT_FOUND)
return KML_PASTE_INVALID;
214 if (!doc.Parse(kmlText.mb_str(wxConvUTF8), 0, TIXML_ENCODING_UTF8)) {
215 wxLogError(wxString(doc.ErrorDesc(), wxConvUTF8));
216 return KML_PASTE_INVALID;
218 if (0 != strncmp(doc.RootElement()->Value(),
"kml", 3))
219 return KML_PASTE_INVALID;
221 TiXmlHandle docHandle(doc.RootElement());
224 TiXmlElement* placemark =
225 docHandle.FirstChild(
"Document").FirstChild(
"Placemark").ToElement();
227 placemark = docHandle.FirstChild(
"Placemark").ToElement();
230 wxString msg(_T(
"KML Parser found no <Placemark> tag in the KML."));
232 return KML_PASTE_INVALID;
235 int pointCounter = 0;
237 for (; placemark; placemark = placemark->NextSiblingElement()) {
238 TiXmlElement* e = placemark->FirstChildElement(
"name");
239 if (e) name = wxString(e->GetText(), wxConvUTF8);
243 if (pointCounter == 1) {
245 TiXmlNode* element = docHandle.FirstChild(
"Document")
246 .FirstChild(
"Placemark")
250 element = docHandle.FirstChild(
"Placemark").FirstChild(
"Point").ToNode();
251 if (element)
return ParseOnePlacemarkPoint(element, name);
254 element = docHandle.FirstChild(
"Document")
255 .FirstChild(
"Placemark")
256 .FirstChild(
"LineString")
260 docHandle.FirstChild(
"Placemark").FirstChild(
"LineString").ToNode();
261 if (element)
return ParseTrack(element, name);
264 element = docHandle.FirstChild(
"Document")
265 .FirstChild(
"Placemark")
266 .FirstChild(
"gx:Track")
270 docHandle.FirstChild(
"Placemark").FirstChild(
"gx:Track").ToNode();
271 if (element)
return ParseTrack(element, name);
274 _T(
"KML Parser found a single <Placemark> in the KML, but no useable ")
277 return KML_PASTE_INVALID;
282 parsedRoute =
new Route();
283 bool foundPoints =
false;
284 bool foundTrack =
false;
285 TiXmlElement* element =
286 docHandle.FirstChild(
"Document").FirstChild(
"name").ToElement();
291 docHandle.FirstChild(
"Document").FirstChild(
"Placemark").ToElement();
292 for (; placemark; placemark = placemark->NextSiblingElement()) {
293 TiXmlNode* n = placemark->FirstChild(
"Point");
295 if (ParseOnePlacemarkPoint(n->ToElement(), name) == KML_PASTE_WAYPOINT) {
296 parsedRoute->AddPoint(
new RoutePoint(parsedRoutePoint));
297 delete parsedRoutePoint;
298 parsedRoutePoint = 0;
303 n = placemark->FirstChild(
"LineString");
305 ParseTrack(n->ToElement(), name);
308 n = placemark->FirstChild(
"gx:Track");
310 ParseTrack(n->ToElement(), name);
315 if (foundPoints && parsedRoute->GetnPoints() < 2) {
317 _T(
"KML Parser did not find enough <Point>s to make a route."));
322 if (foundPoints && !foundTrack)
return KML_PASTE_ROUTE;
323 if (foundPoints && foundTrack)
return KML_PASTE_ROUTE_TRACK;
324 if (!foundPoints && foundTrack)
return KML_PASTE_TRACK;
325 return KML_PASTE_INVALID;
328TiXmlElement* Kml::StandardHead(TiXmlDocument& xmlDoc, wxString name) {
329 TiXmlDeclaration* decl =
new TiXmlDeclaration(
"1.0",
"UTF-8",
"");
330 xmlDoc.LinkEndChild(decl);
332 TiXmlElement* kml =
new TiXmlElement(
"kml");
333 kml->SetAttribute(
"xmlns:atom",
"http://www.w3.org/2005/Atom");
334 kml->SetAttribute(
"xmlns",
"http://www.opengis.net/kml/2.2");
335 kml->SetAttribute(
"xmlns:gx",
"http://www.google.com/kml/ext/2.2");
336 kml->SetAttribute(
"xmlns:kml",
"http://www.opengis.net/kml/2.2");
338 if (insertQtVlmExtendedData)
339 kml->SetAttribute(
"xmlns:vlm",
"http://virtual-loup-de-mer.org");
341 xmlDoc.LinkEndChild(kml);
343 TiXmlElement* document =
new TiXmlElement(
"Document");
344 kml->LinkEndChild(document);
345 TiXmlElement* docName =
new TiXmlElement(
"name");
346 document->LinkEndChild(docName);
347 TiXmlText* docNameVal =
new TiXmlText(name.mb_str(wxConvUTF8));
348 docName->LinkEndChild(docNameVal);
352std::string Kml::PointPlacemark(TiXmlElement* document,
354 TiXmlElement* pmPoint =
new TiXmlElement(
"Placemark");
355 document->LinkEndChild(pmPoint);
356 TiXmlElement* pmPointName =
new TiXmlElement(
"name");
357 pmPoint->LinkEndChild(pmPointName);
358 TiXmlText* pmPointNameVal =
359 new TiXmlText(routepoint->GetName().mb_str(wxConvUTF8));
360 pmPointName->LinkEndChild(pmPointNameVal);
362 TiXmlElement* pointDescr =
new TiXmlElement(
"description");
363 pmPoint->LinkEndChild(pointDescr);
365 bool descrIsPlainText =
true;
366 wxCharBuffer descrString = routepoint->m_MarkDescription.mb_str(wxConvUTF8);
368 if (insertQtVlmExtendedData) {
371 TiXmlDocument descrDoc;
372 TiXmlElement* extendedData;
373 if (descrDoc.Parse(descrString, 0, TIXML_ENCODING_UTF8)) {
374 if (0 == strncmp(descrDoc.RootElement()->Value(),
"ExtendedData", 12)) {
375 descrIsPlainText =
false;
376 extendedData = descrDoc.RootElement();
377 TiXmlHandle docHandle(&descrDoc);
378 TiXmlElement* seq = docHandle.FirstChild(
"ExtendedData")
379 .FirstChild(
"vlm:sequence")
382 seq =
new TiXmlElement(
"vlm:sequence");
383 TiXmlText* snVal =
new TiXmlText(
384 wxString::Format(_T(
"%04d"), seqCounter).mb_str(wxConvUTF8));
385 seq->LinkEndChild(snVal);
386 descrDoc.RootElement()->LinkEndChild(seq);
388 pmPoint->LinkEndChild(descrDoc.RootElement()->Clone());
391 if (descrIsPlainText) {
394 extendedData =
new TiXmlElement(
"ExtendedData");
395 pmPoint->LinkEndChild(extendedData);
396 TiXmlElement* seq =
new TiXmlElement(
"vlm:sequence");
397 extendedData->LinkEndChild(seq);
398 TiXmlText* snVal =
new TiXmlText(
399 wxString::Format(_T(
"%04d"), seqCounter).mb_str(wxConvUTF8));
400 seq->LinkEndChild(snVal);
402 if (routepoint->m_MarkDescription.Length()) {
403 TiXmlElement* data =
new TiXmlElement(
"Data");
404 data->SetAttribute(
"name",
"Description");
405 extendedData->LinkEndChild(data);
407 TiXmlElement* value =
new TiXmlElement(
"value");
408 data->LinkEndChild(value);
409 TiXmlText* txtVal =
new TiXmlText(descrString);
410 value->LinkEndChild(txtVal);
413 if (extendedData && seqCounter == 0) {
414 const wxCharBuffer ownshipPos =
415 wxString::Format(_T(
"%f %f"), gLon, gLat).mb_str(wxConvUTF8);
416 TiXmlHandle h(extendedData);
417 TiXmlElement* route = h.FirstChild(
"vlm:route").ToElement();
418 TiXmlElement* ownship =
419 h.FirstChild(
"vlm:route").FirstChild(
"ownship").ToElement();
422 TiXmlText* owns = ownship->FirstChild()->ToText();
424 owns->SetValue(ownshipPos);
426 owns =
new TiXmlText(ownshipPos);
427 ownship->LinkEndChild(owns);
430 ownship =
new TiXmlElement(
"ownship");
431 route->LinkEndChild(ownship);
432 TiXmlText* owns =
new TiXmlText(ownshipPos);
433 ownship->LinkEndChild(owns);
436 route =
new TiXmlElement(
"vlm:route");
437 extendedData->LinkEndChild(route);
438 ownship =
new TiXmlElement(
"ownship");
439 route->LinkEndChild(ownship);
440 TiXmlText* owns =
new TiXmlText(ownshipPos);
441 ownship->LinkEndChild(owns);
448 TiXmlText* pointDescrVal =
new TiXmlText(descrString);
449 pointDescr->LinkEndChild(pointDescrVal);
452 TiXmlElement* point =
new TiXmlElement(
"Point");
453 pmPoint->LinkEndChild(point);
455 TiXmlElement* pointCoord =
new TiXmlElement(
"coordinates");
456 point->LinkEndChild(pointCoord);
458 std::stringstream pointCoordStr;
459 pointCoordStr << routepoint->m_lon <<
"," << routepoint->m_lat <<
",0. ";
461 TiXmlText* pointText =
new TiXmlText(pointCoordStr.str());
462 pointCoord->LinkEndChild(pointText);
464 return pointCoordStr.str();
467wxString Kml::MakeKmlFromRoute(
Route* route,
bool insertSeq) {
468 insertQtVlmExtendedData = insertSeq;
470 TiXmlDocument xmlDoc;
471 wxString name = _(
"OpenCPN Route");
473 TiXmlElement* document = StandardHead(xmlDoc, name);
475 std::stringstream lineStringCoords;
478 wxRoutePointListNode* pointnode = pointList->GetFirst();
482 routepoint = pointnode->GetData();
484 lineStringCoords << PointPlacemark(document, routepoint);
486 pointnode = pointnode->GetNext();
489 TiXmlElement* pmPath =
new TiXmlElement(
"Placemark");
490 document->LinkEndChild(pmPath);
492 TiXmlElement* pmName =
new TiXmlElement(
"name");
493 pmPath->LinkEndChild(pmName);
494 TiXmlText* pmNameVal =
new TiXmlText(
"Path");
495 pmName->LinkEndChild(pmNameVal);
497 TiXmlElement* linestring =
new TiXmlElement(
"LineString");
498 pmPath->LinkEndChild(linestring);
500 TiXmlElement* coordinates =
new TiXmlElement(
"coordinates");
501 linestring->LinkEndChild(coordinates);
503 TiXmlText* text =
new TiXmlText(lineStringCoords.str());
504 coordinates->LinkEndChild(text);
506 TiXmlPrinter printer;
507 printer.SetIndent(
" ");
508 xmlDoc.Accept(&printer);
510 return wxString(printer.CStr(), wxConvUTF8);
513wxString Kml::MakeKmlFromTrack(
Track* track) {
514 TiXmlDocument xmlDoc;
515 wxString name = _(
"OpenCPN Track");
516 if (track->GetName().Length()) name = track->GetName();
517 TiXmlElement* document = StandardHead(xmlDoc, name);
519 TiXmlElement* pmTrack =
new TiXmlElement(
"Placemark");
520 document->LinkEndChild(pmTrack);
522 TiXmlElement* pmName =
new TiXmlElement(
"name");
523 pmTrack->LinkEndChild(pmName);
524 TiXmlText* pmNameVal =
new TiXmlText(track->GetName().mb_str(wxConvUTF8));
525 pmName->LinkEndChild(pmNameVal);
527 TiXmlElement* gxTrack =
new TiXmlElement(
"gx:Track");
528 pmTrack->LinkEndChild(gxTrack);
530 std::stringstream lineStringCoords;
532 for (
int i = 0; i < track->GetnPoints(); i++) {
535 TiXmlElement* when =
new TiXmlElement(
"when");
536 gxTrack->LinkEndChild(when);
539 TiXmlText* whenVal =
new TiXmlText(
540 whenTime.Format(_T(
"%Y-%m-%dT%H:%M:%SZ")).mb_str(wxConvUTF8));
541 when->LinkEndChild(whenVal);
544 for (
int i = 0; i < track->GetnPoints(); i++) {
547 TiXmlElement* coord =
new TiXmlElement(
"gx:coord");
548 gxTrack->LinkEndChild(coord);
550 wxString::Format(_T(
"%f %f 0.0"), trackpoint->m_lon, trackpoint->m_lat);
551 TiXmlText* coordVal =
new TiXmlText(coordStr.mb_str(wxConvUTF8));
552 coord->LinkEndChild(coordVal);
555 TiXmlPrinter printer;
556 printer.SetIndent(
" ");
557 xmlDoc.Accept(&printer);
559 return wxString(printer.CStr(), wxConvUTF8);
562wxString Kml::MakeKmlFromWaypoint(
RoutePoint* routepoint) {
563 TiXmlDocument xmlDoc;
564 wxString name = _(
"OpenCPN Waypoint");
565 if (routepoint->GetName().Length()) name = routepoint->GetName();
566 TiXmlElement* document = StandardHead(xmlDoc, name);
568 insertQtVlmExtendedData =
false;
569 PointPlacemark(document, routepoint);
571 TiXmlPrinter printer;
572 printer.SetIndent(
" ");
573 xmlDoc.Accept(&printer);
575 return wxString(printer.CStr(), wxConvUTF8);
578void Kml::CopyRouteToClipboard(
Route* route) {
580 int format = formatDlg->ShowModal();
582 if (format != wxID_CANCEL) {
583 format = formatDlg->GetSelectedFormat();
584 bool extradata = (format == KML_COPY_EXTRADATA);
586 ::wxBeginBusyCursor();
587 if (wxTheClipboard->Open()) {
588 wxTextDataObject* data =
new wxTextDataObject;
589 data->SetText(MakeKmlFromRoute(route, extradata));
590 wxTheClipboard->SetData(data);
597void Kml::CopyTrackToClipboard(
Track* track) {
598 ::wxBeginBusyCursor();
599 if (wxTheClipboard->Open()) {
600 wxTextDataObject* data =
new wxTextDataObject;
601 data->SetText(MakeKmlFromTrack(track));
602 wxTheClipboard->SetData(data);
607void Kml::CopyWaypointToClipboard(
RoutePoint* rp) {
608 if (wxTheClipboard->Open()) {
609 wxTextDataObject* data =
new wxTextDataObject;
610 data->SetText(MakeKmlFromWaypoint(rp));
611 wxTheClipboard->SetData(data);
618 parsedRoutePoint = NULL;
624 for (
int i = 1; i <= parsedRoute->GetnPoints(); i++) {
625 if (parsedRoute->GetPoint(i))
delete parsedRoute->GetPoint(i);
629 delete parsedRoutePoint;
634KmlFormatDialog::KmlFormatDialog(wxWindow* parent)
635 : wxDialog(parent, wxID_ANY, _(
"Choose Format for Copy"), wxDefaultPosition,
637 wxBoxSizer* topSizer =
new wxBoxSizer(wxVERTICAL);
639 wxBoxSizer* sizer =
new wxBoxSizer(wxVERTICAL);
640 topSizer->Add(sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
642 choices.push_back(
new wxRadioButton(
643 this, KML_COPY_STANDARD, _(
"KML Standard (Google Earth and others)"),
644 wxDefaultPosition, wxDefaultSize, wxRB_GROUP));
646 choices.push_back(
new wxRadioButton(
647 this, KML_COPY_EXTRADATA, _(
"KML with extended waypoint data (QtVlm)"),
650 wxStdDialogButtonSizer* buttonSizer =
651 CreateStdDialogButtonSizer(wxOK | wxCANCEL);
653 sizer->Add(choices[0], 0, wxEXPAND | wxALL, 5);
654 sizer->Add(choices[1], 0, wxEXPAND | wxALL, 5);
655 sizer->Add(buttonSizer, 0, wxEXPAND | wxTOP, 5);
657 topSizer->SetSizeHints(
this);
661int KmlFormatDialog::GetSelectedFormat() {
662 for (
unsigned int i = 0; i < choices.size(); i++) {
663 if (choices[i]->GetValue())
return choices[i]->GetId();
Represents a waypoint or mark within the navigation system.
bool m_bIsolatedMark
Flag indicating if the waypoint is a standalone mark.
Represents a navigational route in the navigation system.
RoutePointList * pRoutePointList
Ordered list of waypoints (RoutePoints) that make up this route.
wxString m_RouteNameString
User-assigned name for the route.
Represents a single point in a track.
wxDateTime GetCreateTime(void)
Retrieves the creation timestamp of a track point as a wxDateTime object.
void SetCreateTime(wxDateTime dt)
Sets the creation timestamp for a track point.
Represents a track, which is a series of connected track points.