39#include <wx/clipbrd.h>
40#include <wx/datetime.h>
55int Kml::seqCounter = 0;
56bool Kml::insertQtVlmExtendedData =
false;
58int Kml::ParseCoordinates(TiXmlNode* node, dPointList& points) {
59 TiXmlElement* e = node->FirstChildElement(
"coordinates");
61 wxString msg(
"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);
90KmlPastebufferType 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 "%Y-%m-%dT%H:%M:%SZ");
139 return KML_PASTE_TRACK;
141 return KML_PASTE_INVALID;
144KmlPastebufferType 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(
"KML Parser failed to convert <Point> coordinates.");
157 return KML_PASTE_INVALID;
159 wxString pointName =
"";
160 TiXmlElement* e = node->Parent()->FirstChild(
"name")->ToElement();
161 if (e) pointName = wxString(e->GetText(), wxConvUTF8);
163 wxString pointDescr =
"";
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;
199 parsedRoutePoint->SetName(pointName);
201 return KML_PASTE_WAYPOINT;
204KmlPastebufferType 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(
"<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(
"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 "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();
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) {
318 wxString msg(
"KML Parser did not find enough <Point>s to make a route.");
323 if (foundPoints && !foundTrack)
return KML_PASTE_ROUTE;
324 if (foundPoints && foundTrack)
return KML_PASTE_ROUTE_TRACK;
325 if (!foundPoints && foundTrack)
return KML_PASTE_TRACK;
326 return KML_PASTE_INVALID;
329TiXmlElement* Kml::StandardHead(TiXmlDocument& xmlDoc, wxString name) {
330 TiXmlDeclaration* decl =
new TiXmlDeclaration(
"1.0",
"UTF-8",
"");
331 xmlDoc.LinkEndChild(decl);
333 TiXmlElement* kml =
new TiXmlElement(
"kml");
334 kml->SetAttribute(
"xmlns:atom",
"http://www.w3.org/2005/Atom");
335 kml->SetAttribute(
"xmlns",
"http://www.opengis.net/kml/2.2");
336 kml->SetAttribute(
"xmlns:gx",
"http://www.google.com/kml/ext/2.2");
337 kml->SetAttribute(
"xmlns:kml",
"http://www.opengis.net/kml/2.2");
339 if (insertQtVlmExtendedData)
340 kml->SetAttribute(
"xmlns:vlm",
"http://virtual-loup-de-mer.org");
342 xmlDoc.LinkEndChild(kml);
344 TiXmlElement* document =
new TiXmlElement(
"Document");
345 kml->LinkEndChild(document);
346 TiXmlElement* docName =
new TiXmlElement(
"name");
347 document->LinkEndChild(docName);
348 TiXmlText* docNameVal =
new TiXmlText(name.mb_str(wxConvUTF8));
349 docName->LinkEndChild(docNameVal);
353std::string Kml::PointPlacemark(TiXmlElement* document,
355 TiXmlElement* pmPoint =
new TiXmlElement(
"Placemark");
356 document->LinkEndChild(pmPoint);
357 TiXmlElement* pmPointName =
new TiXmlElement(
"name");
358 pmPoint->LinkEndChild(pmPointName);
359 TiXmlText* pmPointNameVal =
360 new TiXmlText(routepoint->GetName().mb_str(wxConvUTF8));
361 pmPointName->LinkEndChild(pmPointNameVal);
363 TiXmlElement* pointDescr =
new TiXmlElement(
"description");
364 pmPoint->LinkEndChild(pointDescr);
366 bool descrIsPlainText =
true;
369 if (insertQtVlmExtendedData) {
372 TiXmlDocument descrDoc;
373 TiXmlElement* extendedData;
374 if (descrDoc.Parse(descrString, 0, TIXML_ENCODING_UTF8)) {
375 if (0 == strncmp(descrDoc.RootElement()->Value(),
"ExtendedData", 12)) {
376 descrIsPlainText =
false;
377 extendedData = descrDoc.RootElement();
378 TiXmlHandle docHandle(&descrDoc);
379 TiXmlElement* seq = docHandle.FirstChild(
"ExtendedData")
380 .FirstChild(
"vlm:sequence")
383 seq =
new TiXmlElement(
"vlm:sequence");
384 TiXmlText* snVal =
new TiXmlText(
385 wxString::Format(
"%04d", seqCounter).mb_str(wxConvUTF8));
386 seq->LinkEndChild(snVal);
387 descrDoc.RootElement()->LinkEndChild(seq);
389 pmPoint->LinkEndChild(descrDoc.RootElement()->Clone());
392 if (descrIsPlainText) {
395 extendedData =
new TiXmlElement(
"ExtendedData");
396 pmPoint->LinkEndChild(extendedData);
397 TiXmlElement* seq =
new TiXmlElement(
"vlm:sequence");
398 extendedData->LinkEndChild(seq);
399 TiXmlText* snVal =
new TiXmlText(
400 wxString::Format(
"%04d", seqCounter).mb_str(wxConvUTF8));
401 seq->LinkEndChild(snVal);
404 TiXmlElement* data =
new TiXmlElement(
"Data");
405 data->SetAttribute(
"name",
"Description");
406 extendedData->LinkEndChild(data);
408 TiXmlElement* value =
new TiXmlElement(
"value");
409 data->LinkEndChild(value);
410 TiXmlText* txtVal =
new TiXmlText(descrString);
411 value->LinkEndChild(txtVal);
414 if (extendedData && seqCounter == 0) {
415 const wxCharBuffer ownshipPos =
416 wxString::Format(
"%f %f",
gLon,
gLat).mb_str(wxConvUTF8);
417 TiXmlHandle h(extendedData);
418 TiXmlElement* route = h.FirstChild(
"vlm:route").ToElement();
419 TiXmlElement* ownship =
420 h.FirstChild(
"vlm:route").FirstChild(
"ownship").ToElement();
423 TiXmlText* owns = ownship->FirstChild()->ToText();
425 owns->SetValue(ownshipPos);
427 owns =
new TiXmlText(ownshipPos);
428 ownship->LinkEndChild(owns);
431 ownship =
new TiXmlElement(
"ownship");
432 route->LinkEndChild(ownship);
433 TiXmlText* owns =
new TiXmlText(ownshipPos);
434 ownship->LinkEndChild(owns);
437 route =
new TiXmlElement(
"vlm:route");
438 extendedData->LinkEndChild(route);
439 ownship =
new TiXmlElement(
"ownship");
440 route->LinkEndChild(ownship);
441 TiXmlText* owns =
new TiXmlText(ownshipPos);
442 ownship->LinkEndChild(owns);
449 TiXmlText* pointDescrVal =
new TiXmlText(descrString);
450 pointDescr->LinkEndChild(pointDescrVal);
453 TiXmlElement* point =
new TiXmlElement(
"Point");
454 pmPoint->LinkEndChild(point);
456 TiXmlElement* pointCoord =
new TiXmlElement(
"coordinates");
457 point->LinkEndChild(pointCoord);
459 std::stringstream pointCoordStr;
460 pointCoordStr << routepoint->m_lon <<
"," << routepoint->m_lat <<
",0. ";
462 TiXmlText* pointText =
new TiXmlText(pointCoordStr.str());
463 pointCoord->LinkEndChild(pointText);
465 return pointCoordStr.str();
468wxString Kml::MakeKmlFromRoute(
Route* route,
bool insertSeq) {
469 insertQtVlmExtendedData = insertSeq;
471 TiXmlDocument xmlDoc;
472 wxString name = _(
"OpenCPN Route");
474 TiXmlElement* document = StandardHead(xmlDoc, name);
476 std::stringstream lineStringCoords;
480 lineStringCoords << PointPlacemark(document, routepoint);
484 TiXmlElement* pmPath =
new TiXmlElement(
"Placemark");
485 document->LinkEndChild(pmPath);
487 TiXmlElement* pmName =
new TiXmlElement(
"name");
488 pmPath->LinkEndChild(pmName);
489 TiXmlText* pmNameVal =
new TiXmlText(
"Path");
490 pmName->LinkEndChild(pmNameVal);
492 TiXmlElement* linestring =
new TiXmlElement(
"LineString");
493 pmPath->LinkEndChild(linestring);
495 TiXmlElement* coordinates =
new TiXmlElement(
"coordinates");
496 linestring->LinkEndChild(coordinates);
498 TiXmlText* text =
new TiXmlText(lineStringCoords.str());
499 coordinates->LinkEndChild(text);
501 TiXmlPrinter printer;
502 printer.SetIndent(
" ");
503 xmlDoc.Accept(&printer);
505 return wxString(printer.CStr(), wxConvUTF8);
508wxString Kml::MakeKmlFromTrack(
Track* track) {
509 TiXmlDocument xmlDoc;
510 wxString name = _(
"OpenCPN Track");
511 if (track->GetName().Length()) name = track->GetName();
512 TiXmlElement* document = StandardHead(xmlDoc, name);
514 TiXmlElement* pmTrack =
new TiXmlElement(
"Placemark");
515 document->LinkEndChild(pmTrack);
517 TiXmlElement* pmName =
new TiXmlElement(
"name");
518 pmTrack->LinkEndChild(pmName);
519 TiXmlText* pmNameVal =
new TiXmlText(track->GetName().mb_str(wxConvUTF8));
520 pmName->LinkEndChild(pmNameVal);
522 TiXmlElement* gxTrack =
new TiXmlElement(
"gx:Track");
523 pmTrack->LinkEndChild(gxTrack);
525 std::stringstream lineStringCoords;
527 for (
int i = 0; i < track->GetnPoints(); i++) {
530 TiXmlElement* when =
new TiXmlElement(
"when");
531 gxTrack->LinkEndChild(when);
535 new TiXmlText(whenTime.Format(
"%Y-%m-%dT%H:%M:%SZ").mb_str(wxConvUTF8));
536 when->LinkEndChild(whenVal);
539 for (
int i = 0; i < track->GetnPoints(); i++) {
542 TiXmlElement* coord =
new TiXmlElement(
"gx:coord");
543 gxTrack->LinkEndChild(coord);
545 wxString::Format(
"%f %f 0.0", trackpoint->m_lon, trackpoint->m_lat);
546 TiXmlText* coordVal =
new TiXmlText(coordStr.mb_str(wxConvUTF8));
547 coord->LinkEndChild(coordVal);
550 TiXmlPrinter printer;
551 printer.SetIndent(
" ");
552 xmlDoc.Accept(&printer);
554 return wxString(printer.CStr(), wxConvUTF8);
557wxString Kml::MakeKmlFromWaypoint(
RoutePoint* routepoint) {
558 TiXmlDocument xmlDoc;
559 wxString name = _(
"OpenCPN Waypoint");
560 if (routepoint->GetName().Length()) name = routepoint->GetName();
561 TiXmlElement* document = StandardHead(xmlDoc, name);
563 insertQtVlmExtendedData =
false;
564 PointPlacemark(document, routepoint);
566 TiXmlPrinter printer;
567 printer.SetIndent(
" ");
568 xmlDoc.Accept(&printer);
570 return wxString(printer.CStr(), wxConvUTF8);
573void Kml::CopyRouteToClipboard(
Route* route) {
575 int format = formatDlg->ShowModal();
577 if (format != wxID_CANCEL) {
578 format = formatDlg->GetSelectedFormat();
579 bool extradata = (format == KML_COPY_EXTRADATA);
581 ::wxBeginBusyCursor();
582 if (wxTheClipboard->Open()) {
583 wxTextDataObject* data =
new wxTextDataObject;
584 data->SetText(MakeKmlFromRoute(route, extradata));
585 wxTheClipboard->SetData(data);
592void Kml::CopyTrackToClipboard(
Track* track) {
593 ::wxBeginBusyCursor();
594 if (wxTheClipboard->Open()) {
595 wxTextDataObject* data =
new wxTextDataObject;
596 data->SetText(MakeKmlFromTrack(track));
597 wxTheClipboard->SetData(data);
602void Kml::CopyWaypointToClipboard(
RoutePoint* rp) {
603 if (wxTheClipboard->Open()) {
604 wxTextDataObject* data =
new wxTextDataObject;
605 data->SetText(MakeKmlFromWaypoint(rp));
606 wxTheClipboard->SetData(data);
613 parsedRoutePoint = NULL;
619 for (
int i = 1; i <= parsedRoute->GetnPoints(); i++) {
620 if (parsedRoute->GetPoint(i))
delete parsedRoute->GetPoint(i);
624 delete parsedRoutePoint;
629KmlFormatDialog::KmlFormatDialog(wxWindow* parent)
630 : wxDialog(parent, wxID_ANY, _(
"Choose Format for Copy"), wxDefaultPosition,
632 wxBoxSizer* topSizer =
new wxBoxSizer(wxVERTICAL);
634 wxBoxSizer* sizer =
new wxBoxSizer(wxVERTICAL);
635 topSizer->Add(sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
637 choices.push_back(
new wxRadioButton(
638 this, KML_COPY_STANDARD, _(
"KML Standard (Google Earth and others)"),
639 wxDefaultPosition, wxDefaultSize, wxRB_GROUP));
641 choices.push_back(
new wxRadioButton(
642 this, KML_COPY_EXTRADATA, _(
"KML with extended waypoint data (QtVlm)"),
645 wxStdDialogButtonSizer* buttonSizer =
646 CreateStdDialogButtonSizer(wxOK | wxCANCEL);
648 sizer->Add(choices[0], 0, wxEXPAND | wxALL, 5);
649 sizer->Add(choices[1], 0, wxEXPAND | wxALL, 5);
650 sizer->Add(buttonSizer, 0, wxEXPAND | wxTOP, 5);
652 topSizer->SetSizeHints(
this);
656int KmlFormatDialog::GetSelectedFormat() {
657 for (
unsigned int i = 0; i < choices.size(); i++) {
658 if (choices[i]->GetValue())
return choices[i]->GetId();
Represents a waypoint or mark within the navigation system.
wxString m_MarkDescription
Description text for the waypoint.
bool m_bIsolatedMark
Flag indicating if the waypoint is a standalone mark.
bool m_bPtIsSelected
Flag indicating if this waypoint is currently selected.
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.
Read and write KML Format.
double gLat
Vessel's current latitude in decimal degrees.
double gLon
Vessel's current longitude in decimal degrees.
Position, course, speed, etc.
Recorded track abstraction.