32 #include <wx/wxprec.h>
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"
55 int Kml::seqCounter = 0;
56 bool Kml::insertQtVlmExtendedData =
false;
58 int Kml::ParseCoordinates(TiXmlNode* node, dPointList& points) {
59 TiXmlElement* e = node->FirstChildElement(
"coordinates");
61 wxString msg(_T(
"KML Parser found no <coordinates> for the element: "));
62 msg << wxString(node->ToElement()->Value(), wxConvUTF8);
71 std::stringstream ss(e->GetText());
75 if (!std::getline(ss, txtCoord,
','))
break;
77 if (txtCoord.length() == 0)
break;
79 point.x = atof(txtCoord.c_str());
80 std::getline(ss, txtCoord,
',');
81 point.y = atof(txtCoord.c_str());
82 std::getline(ss, txtCoord,
' ');
83 point.z = atof(txtCoord.c_str());
85 points.push_back(point);
90 KmlPastebufferType Kml::ParseTrack(TiXmlNode* node, wxString& name) {
91 parsedTrack =
new Track();
92 parsedTrack->SetName(name);
94 if (0 == strncmp(node->ToElement()->Value(),
"LineString", 10)) {
95 dPointList coordinates;
96 if (ParseCoordinates(node, coordinates) > 2) {
99 for (
unsigned int i = 0; i < coordinates.size(); i++) {
100 trackpoint =
new TrackPoint(coordinates[i].y, coordinates[i].x);
101 parsedTrack->AddPoint(trackpoint);
104 return KML_PASTE_TRACK;
107 if (0 == strncmp(node->ToElement()->Value(),
"gx:Track", 8)) {
109 TiXmlElement* point = node->FirstChildElement(
"gx:coord");
110 int pointCounter = 0;
112 for (; point; point = point->NextSiblingElement(
"gx:coord")) {
114 std::stringstream ss(point->GetText());
115 std::string txtCoord;
116 std::getline(ss, txtCoord,
' ');
117 lon = atof(txtCoord.c_str());
118 std::getline(ss, txtCoord,
' ');
119 lat = atof(txtCoord.c_str());
121 parsedTrack->AddPoint(
new TrackPoint(lat, lon));
125 TiXmlElement* when = node->FirstChildElement(
"when");
130 for (; when; when = when->NextSiblingElement(
"when")) {
131 trackpoint = parsedTrack->GetPoint(i);
132 if (!trackpoint)
continue;
133 whenTime.ParseFormat(wxString(when->GetText(), wxConvUTF8),
134 _T(
"%Y-%m-%dT%H:%M:%SZ"));
135 trackpoint->SetCreateTime(whenTime);
139 return KML_PASTE_TRACK;
141 return KML_PASTE_INVALID;
144 KmlPastebufferType Kml::ParseOnePlacemarkPoint(TiXmlNode* node,
146 double newLat = 0., newLon = 0.;
147 dPointList coordinates;
149 if (ParseCoordinates(node->ToElement(), coordinates)) {
150 newLat = coordinates[0].y;
151 newLon = coordinates[0].x;
154 if (newLat == 0.0 && newLon == 0.0) {
155 wxString msg(_T(
"KML Parser failed to convert <Point> coordinates."));
157 return KML_PASTE_INVALID;
159 wxString pointName = wxEmptyString;
160 TiXmlElement* e = node->Parent()->FirstChild(
"name")->ToElement();
161 if (e) pointName = wxString(e->GetText(), wxConvUTF8);
163 wxString pointDescr = wxEmptyString;
164 e = node->Parent()->FirstChildElement(
"description");
169 TiXmlNode* n = e->FirstChild();
170 if (n)
switch (n->Type()) {
171 case TiXmlNode::TINYXML_TEXT:
172 pointDescr = wxString(e->GetText(), wxConvUTF8);
174 case TiXmlNode::TINYXML_ELEMENT:
175 TiXmlPrinter printer;
176 printer.SetIndent(
"\t");
178 pointDescr = wxString(printer.CStr(), wxConvUTF8);
184 TiXmlNode* n = node->Parent()->FirstChild(
"ExtendedData");
186 TiXmlPrinter printer;
187 printer.SetIndent(
"\t");
189 pointDescr = wxString(printer.CStr(), wxConvUTF8);
194 parsedRoutePoint->m_lat = newLat;
195 parsedRoutePoint->m_lon = newLon;
196 parsedRoutePoint->m_bIsolatedMark =
true;
197 parsedRoutePoint->m_bPtIsSelected =
false;
198 parsedRoutePoint->m_MarkDescription = pointDescr;
199 parsedRoutePoint->SetName(pointName);
201 return KML_PASTE_WAYPOINT;
204 KmlPastebufferType Kml::ParsePasteBuffer() {
205 if (!wxTheClipboard->IsOpened())
206 if (!wxTheClipboard->Open())
return KML_PASTE_INVALID;
208 wxTextDataObject data;
209 wxTheClipboard->GetData(data);
210 kmlText = data.GetText();
211 wxTheClipboard->Close();
213 if (kmlText.Find(_T(
"<kml")) == wxNOT_FOUND)
return KML_PASTE_INVALID;
216 if (!doc.Parse(kmlText.mb_str(wxConvUTF8), 0, TIXML_ENCODING_UTF8)) {
217 wxLogError(wxString(doc.ErrorDesc(), wxConvUTF8));
218 return KML_PASTE_INVALID;
220 if (0 != strncmp(doc.RootElement()->Value(),
"kml", 3))
221 return KML_PASTE_INVALID;
223 TiXmlHandle docHandle(doc.RootElement());
226 TiXmlElement* placemark =
227 docHandle.FirstChild(
"Document").FirstChild(
"Placemark").ToElement();
229 placemark = docHandle.FirstChild(
"Placemark").ToElement();
232 wxString msg(_T(
"KML Parser found no <Placemark> tag in the KML."));
234 return KML_PASTE_INVALID;
237 int pointCounter = 0;
239 for (; placemark; placemark = placemark->NextSiblingElement()) {
240 TiXmlElement* e = placemark->FirstChildElement(
"name");
241 if (e) name = wxString(e->GetText(), wxConvUTF8);
245 if (pointCounter == 1) {
247 TiXmlNode* element = docHandle.FirstChild(
"Document")
248 .FirstChild(
"Placemark")
252 element = docHandle.FirstChild(
"Placemark").FirstChild(
"Point").ToNode();
253 if (element)
return ParseOnePlacemarkPoint(element, name);
256 element = docHandle.FirstChild(
"Document")
257 .FirstChild(
"Placemark")
258 .FirstChild(
"LineString")
262 docHandle.FirstChild(
"Placemark").FirstChild(
"LineString").ToNode();
263 if (element)
return ParseTrack(element, name);
266 element = docHandle.FirstChild(
"Document")
267 .FirstChild(
"Placemark")
268 .FirstChild(
"gx:Track")
272 docHandle.FirstChild(
"Placemark").FirstChild(
"gx:Track").ToNode();
273 if (element)
return ParseTrack(element, name);
276 _T(
"KML Parser found a single <Placemark> in the KML, but no useable ")
279 return KML_PASTE_INVALID;
284 parsedRoute =
new Route();
285 bool foundPoints =
false;
286 bool foundTrack =
false;
287 TiXmlElement* element =
288 docHandle.FirstChild(
"Document").FirstChild(
"name").ToElement();
290 parsedRoute->m_RouteNameString = wxString(element->GetText(), wxConvUTF8);
293 docHandle.FirstChild(
"Document").FirstChild(
"Placemark").ToElement();
294 for (; placemark; placemark = placemark->NextSiblingElement()) {
295 TiXmlNode* n = placemark->FirstChild(
"Point");
297 if (ParseOnePlacemarkPoint(n->ToElement(), name) == KML_PASTE_WAYPOINT) {
298 parsedRoute->AddPoint(
new RoutePoint(parsedRoutePoint));
299 delete parsedRoutePoint;
300 parsedRoutePoint = 0;
305 n = placemark->FirstChild(
"LineString");
307 ParseTrack(n->ToElement(), name);
310 n = placemark->FirstChild(
"gx:Track");
312 ParseTrack(n->ToElement(), name);
317 if (foundPoints && parsedRoute->GetnPoints() < 2) {
319 _T(
"KML Parser did not find enough <Point>s to make a route."));
324 if (foundPoints && !foundTrack)
return KML_PASTE_ROUTE;
325 if (foundPoints && foundTrack)
return KML_PASTE_ROUTE_TRACK;
326 if (!foundPoints && foundTrack)
return KML_PASTE_TRACK;
327 return KML_PASTE_INVALID;
330 TiXmlElement* Kml::StandardHead(TiXmlDocument& xmlDoc, wxString name) {
331 TiXmlDeclaration* decl =
new TiXmlDeclaration(
"1.0",
"UTF-8",
"");
332 xmlDoc.LinkEndChild(decl);
334 TiXmlElement* kml =
new TiXmlElement(
"kml");
335 kml->SetAttribute(
"xmlns:atom",
"http://www.w3.org/2005/Atom");
336 kml->SetAttribute(
"xmlns",
"http://www.opengis.net/kml/2.2");
337 kml->SetAttribute(
"xmlns:gx",
"http://www.google.com/kml/ext/2.2");
338 kml->SetAttribute(
"xmlns:kml",
"http://www.opengis.net/kml/2.2");
340 if (insertQtVlmExtendedData)
341 kml->SetAttribute(
"xmlns:vlm",
"http://virtual-loup-de-mer.org");
343 xmlDoc.LinkEndChild(kml);
345 TiXmlElement* document =
new TiXmlElement(
"Document");
346 kml->LinkEndChild(document);
347 TiXmlElement* docName =
new TiXmlElement(
"name");
348 document->LinkEndChild(docName);
349 TiXmlText* docNameVal =
new TiXmlText(name.mb_str(wxConvUTF8));
350 docName->LinkEndChild(docNameVal);
354 std::string Kml::PointPlacemark(TiXmlElement* document,
356 TiXmlElement* pmPoint =
new TiXmlElement(
"Placemark");
357 document->LinkEndChild(pmPoint);
358 TiXmlElement* pmPointName =
new TiXmlElement(
"name");
359 pmPoint->LinkEndChild(pmPointName);
360 TiXmlText* pmPointNameVal =
361 new TiXmlText(routepoint->GetName().mb_str(wxConvUTF8));
362 pmPointName->LinkEndChild(pmPointNameVal);
364 TiXmlElement* pointDescr =
new TiXmlElement(
"description");
365 pmPoint->LinkEndChild(pointDescr);
367 bool descrIsPlainText =
true;
368 wxCharBuffer descrString = routepoint->m_MarkDescription.mb_str(wxConvUTF8);
370 if (insertQtVlmExtendedData) {
373 TiXmlDocument descrDoc;
374 TiXmlElement* extendedData;
375 if (descrDoc.Parse(descrString, 0, TIXML_ENCODING_UTF8)) {
376 if (0 == strncmp(descrDoc.RootElement()->Value(),
"ExtendedData", 12)) {
377 descrIsPlainText =
false;
378 extendedData = descrDoc.RootElement();
379 TiXmlHandle docHandle(&descrDoc);
380 TiXmlElement* seq = docHandle.FirstChild(
"ExtendedData")
381 .FirstChild(
"vlm:sequence")
384 seq =
new TiXmlElement(
"vlm:sequence");
385 TiXmlText* snVal =
new TiXmlText(
386 wxString::Format(_T(
"%04d"), seqCounter).mb_str(wxConvUTF8));
387 seq->LinkEndChild(snVal);
388 descrDoc.RootElement()->LinkEndChild(seq);
390 pmPoint->LinkEndChild(descrDoc.RootElement()->Clone());
393 if (descrIsPlainText) {
396 extendedData =
new TiXmlElement(
"ExtendedData");
397 pmPoint->LinkEndChild(extendedData);
398 TiXmlElement* seq =
new TiXmlElement(
"vlm:sequence");
399 extendedData->LinkEndChild(seq);
400 TiXmlText* snVal =
new TiXmlText(
401 wxString::Format(_T(
"%04d"), seqCounter).mb_str(wxConvUTF8));
402 seq->LinkEndChild(snVal);
404 if (routepoint->m_MarkDescription.Length()) {
405 TiXmlElement* data =
new TiXmlElement(
"Data");
406 data->SetAttribute(
"name",
"Description");
407 extendedData->LinkEndChild(data);
409 TiXmlElement* value =
new TiXmlElement(
"value");
410 data->LinkEndChild(value);
411 TiXmlText* txtVal =
new TiXmlText(descrString);
412 value->LinkEndChild(txtVal);
415 if (extendedData && seqCounter == 0) {
416 const wxCharBuffer ownshipPos =
417 wxString::Format(_T(
"%f %f"), gLon, gLat).mb_str(wxConvUTF8);
418 TiXmlHandle h(extendedData);
419 TiXmlElement* route = h.FirstChild(
"vlm:route").ToElement();
420 TiXmlElement* ownship =
421 h.FirstChild(
"vlm:route").FirstChild(
"ownship").ToElement();
424 TiXmlText* owns = ownship->FirstChild()->ToText();
426 owns->SetValue(ownshipPos);
428 owns =
new TiXmlText(ownshipPos);
429 ownship->LinkEndChild(owns);
432 ownship =
new TiXmlElement(
"ownship");
433 route->LinkEndChild(ownship);
434 TiXmlText* owns =
new TiXmlText(ownshipPos);
435 ownship->LinkEndChild(owns);
438 route =
new TiXmlElement(
"vlm:route");
439 extendedData->LinkEndChild(route);
440 ownship =
new TiXmlElement(
"ownship");
441 route->LinkEndChild(ownship);
442 TiXmlText* owns =
new TiXmlText(ownshipPos);
443 ownship->LinkEndChild(owns);
450 TiXmlText* pointDescrVal =
new TiXmlText(descrString);
451 pointDescr->LinkEndChild(pointDescrVal);
454 TiXmlElement* point =
new TiXmlElement(
"Point");
455 pmPoint->LinkEndChild(point);
457 TiXmlElement* pointCoord =
new TiXmlElement(
"coordinates");
458 point->LinkEndChild(pointCoord);
460 std::stringstream pointCoordStr;
461 pointCoordStr << routepoint->m_lon <<
"," << routepoint->m_lat <<
",0. ";
463 TiXmlText* pointText =
new TiXmlText(pointCoordStr.str());
464 pointCoord->LinkEndChild(pointText);
466 return pointCoordStr.str();
469 wxString Kml::MakeKmlFromRoute(
Route* route,
bool insertSeq) {
470 insertQtVlmExtendedData = insertSeq;
472 TiXmlDocument xmlDoc;
473 wxString name = _(
"OpenCPN Route");
474 if (route->m_RouteNameString.Length()) name = route->m_RouteNameString;
475 TiXmlElement* document = StandardHead(xmlDoc, name);
477 std::stringstream lineStringCoords;
479 RoutePointList* pointList = route->pRoutePointList;
480 wxRoutePointListNode* pointnode = pointList->GetFirst();
484 routepoint = pointnode->GetData();
486 lineStringCoords << PointPlacemark(document, routepoint);
488 pointnode = pointnode->GetNext();
491 TiXmlElement* pmPath =
new TiXmlElement(
"Placemark");
492 document->LinkEndChild(pmPath);
494 TiXmlElement* pmName =
new TiXmlElement(
"name");
495 pmPath->LinkEndChild(pmName);
496 TiXmlText* pmNameVal =
new TiXmlText(
"Path");
497 pmName->LinkEndChild(pmNameVal);
499 TiXmlElement* linestring =
new TiXmlElement(
"LineString");
500 pmPath->LinkEndChild(linestring);
502 TiXmlElement* coordinates =
new TiXmlElement(
"coordinates");
503 linestring->LinkEndChild(coordinates);
505 TiXmlText* text =
new TiXmlText(lineStringCoords.str());
506 coordinates->LinkEndChild(text);
508 TiXmlPrinter printer;
509 printer.SetIndent(
" ");
510 xmlDoc.Accept(&printer);
512 return wxString(printer.CStr(), wxConvUTF8);
515 wxString Kml::MakeKmlFromTrack(
Track* track) {
516 TiXmlDocument xmlDoc;
517 wxString name = _(
"OpenCPN Track");
518 if (track->GetName().Length()) name = track->GetName();
519 TiXmlElement* document = StandardHead(xmlDoc, name);
521 TiXmlElement* pmTrack =
new TiXmlElement(
"Placemark");
522 document->LinkEndChild(pmTrack);
524 TiXmlElement* pmName =
new TiXmlElement(
"name");
525 pmTrack->LinkEndChild(pmName);
526 TiXmlText* pmNameVal =
new TiXmlText(track->GetName().mb_str(wxConvUTF8));
527 pmName->LinkEndChild(pmNameVal);
529 TiXmlElement* gxTrack =
new TiXmlElement(
"gx:Track");
530 pmTrack->LinkEndChild(gxTrack);
532 std::stringstream lineStringCoords;
534 for (
int i = 0; i < track->GetnPoints(); i++) {
537 TiXmlElement* when =
new TiXmlElement(
"when");
538 gxTrack->LinkEndChild(when);
540 wxDateTime whenTime(trackpoint->GetCreateTime());
541 TiXmlText* whenVal =
new TiXmlText(
542 whenTime.Format(_T(
"%Y-%m-%dT%H:%M:%SZ")).mb_str(wxConvUTF8));
543 when->LinkEndChild(whenVal);
546 for (
int i = 0; i < track->GetnPoints(); i++) {
549 TiXmlElement* coord =
new TiXmlElement(
"gx:coord");
550 gxTrack->LinkEndChild(coord);
552 wxString::Format(_T(
"%f %f 0.0"), trackpoint->m_lon, trackpoint->m_lat);
553 TiXmlText* coordVal =
new TiXmlText(coordStr.mb_str(wxConvUTF8));
554 coord->LinkEndChild(coordVal);
557 TiXmlPrinter printer;
558 printer.SetIndent(
" ");
559 xmlDoc.Accept(&printer);
561 return wxString(printer.CStr(), wxConvUTF8);
564 wxString Kml::MakeKmlFromWaypoint(
RoutePoint* routepoint) {
565 TiXmlDocument xmlDoc;
566 wxString name = _(
"OpenCPN Waypoint");
567 if (routepoint->GetName().Length()) name = routepoint->GetName();
568 TiXmlElement* document = StandardHead(xmlDoc, name);
570 insertQtVlmExtendedData =
false;
571 PointPlacemark(document, routepoint);
573 TiXmlPrinter printer;
574 printer.SetIndent(
" ");
575 xmlDoc.Accept(&printer);
577 return wxString(printer.CStr(), wxConvUTF8);
580 void Kml::CopyRouteToClipboard(
Route* route) {
582 int format = formatDlg->ShowModal();
584 if (format != wxID_CANCEL) {
585 format = formatDlg->GetSelectedFormat();
586 bool extradata = (format == KML_COPY_EXTRADATA);
588 ::wxBeginBusyCursor();
589 if (wxTheClipboard->Open()) {
590 wxTextDataObject* data =
new wxTextDataObject;
591 data->SetText(MakeKmlFromRoute(route, extradata));
592 wxTheClipboard->SetData(data);
599 void Kml::CopyTrackToClipboard(
Track* track) {
600 ::wxBeginBusyCursor();
601 if (wxTheClipboard->Open()) {
602 wxTextDataObject* data =
new wxTextDataObject;
603 data->SetText(MakeKmlFromTrack(track));
604 wxTheClipboard->SetData(data);
609 void Kml::CopyWaypointToClipboard(
RoutePoint* rp) {
610 if (wxTheClipboard->Open()) {
611 wxTextDataObject* data =
new wxTextDataObject;
612 data->SetText(MakeKmlFromWaypoint(rp));
613 wxTheClipboard->SetData(data);
620 parsedRoutePoint = NULL;
626 for (
int i = 1; i <= parsedRoute->GetnPoints(); i++) {
627 if (parsedRoute->GetPoint(i))
delete parsedRoute->GetPoint(i);
631 delete parsedRoutePoint;
636 KmlFormatDialog::KmlFormatDialog(wxWindow* parent)
637 : wxDialog(parent, wxID_ANY, _(
"Choose Format for Copy"), wxDefaultPosition,
639 wxBoxSizer* topSizer =
new wxBoxSizer(wxVERTICAL);
641 wxBoxSizer* sizer =
new wxBoxSizer(wxVERTICAL);
642 topSizer->Add(sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
644 choices.push_back(
new wxRadioButton(
645 this, KML_COPY_STANDARD, _(
"KML Standard (Google Earth and others)"),
646 wxDefaultPosition, wxDefaultSize, wxRB_GROUP));
648 choices.push_back(
new wxRadioButton(
649 this, KML_COPY_EXTRADATA, _(
"KML with extended waypoint data (QtVlm)"),
652 wxStdDialogButtonSizer* buttonSizer =
653 CreateStdDialogButtonSizer(wxOK | wxCANCEL);
655 sizer->Add(choices[0], 0, wxEXPAND | wxALL, 5);
656 sizer->Add(choices[1], 0, wxEXPAND | wxALL, 5);
657 sizer->Add(buttonSizer, 0, wxEXPAND | wxTOP, 5);
659 topSizer->SetSizeHints(
this);
663 int KmlFormatDialog::GetSelectedFormat() {
664 for (
unsigned int i = 0; i < choices.size(); i++) {
665 if (choices[i]->GetValue())
return choices[i]->GetId();