40#include <wx/clipbrd.h>
41#include <wx/datetime.h>
56int Kml::seqCounter = 0;
57bool Kml::insertQtVlmExtendedData =
false;
59int Kml::ParseCoordinates(TiXmlNode* node, dPointList& points) {
60 TiXmlElement* e = node->FirstChildElement(
"coordinates");
62 wxString msg(
"KML Parser found no <coordinates> for the element: ");
63 msg << wxString(node->ToElement()->Value(), wxConvUTF8);
72 std::stringstream ss(e->GetText());
76 if (!std::getline(ss, txtCoord,
','))
break;
78 if (txtCoord.length() == 0)
break;
80 point.x = atof(txtCoord.c_str());
81 std::getline(ss, txtCoord,
',');
82 point.y = atof(txtCoord.c_str());
83 std::getline(ss, txtCoord,
' ');
84 point.z = atof(txtCoord.c_str());
86 points.push_back(point);
91KmlPastebufferType Kml::ParseTrack(TiXmlNode* node, wxString& name) {
92 parsedTrack =
new Track();
93 parsedTrack->SetName(name);
95 if (0 == strncmp(node->ToElement()->Value(),
"LineString", 10)) {
96 dPointList coordinates;
97 if (ParseCoordinates(node, coordinates) > 2) {
100 for (
unsigned int i = 0; i < coordinates.size(); i++) {
101 trackpoint =
new TrackPoint(coordinates[i].y, coordinates[i].x);
102 parsedTrack->AddPoint(trackpoint);
105 return KML_PASTE_TRACK;
108 if (0 == strncmp(node->ToElement()->Value(),
"gx:Track", 8)) {
110 TiXmlElement* point = node->FirstChildElement(
"gx:coord");
111 int pointCounter = 0;
113 for (; point; point = point->NextSiblingElement(
"gx:coord")) {
115 std::stringstream ss(point->GetText());
116 std::string txtCoord;
117 std::getline(ss, txtCoord,
' ');
118 lon = atof(txtCoord.c_str());
119 std::getline(ss, txtCoord,
' ');
120 lat = atof(txtCoord.c_str());
122 parsedTrack->AddPoint(
new TrackPoint(lat, lon));
126 TiXmlElement* when = node->FirstChildElement(
"when");
131 for (; when; when = when->NextSiblingElement(
"when")) {
132 trackpoint = parsedTrack->GetPoint(i);
133 if (!trackpoint)
continue;
134 whenTime.ParseFormat(wxString(when->GetText(), wxConvUTF8),
135 "%Y-%m-%dT%H:%M:%SZ");
140 return KML_PASTE_TRACK;
142 return KML_PASTE_INVALID;
145KmlPastebufferType Kml::ParseOnePlacemarkPoint(TiXmlNode* node,
147 double newLat = 0., newLon = 0.;
148 dPointList coordinates;
150 if (ParseCoordinates(node->ToElement(), coordinates)) {
151 newLat = coordinates[0].y;
152 newLon = coordinates[0].x;
155 if (newLat == 0.0 && newLon == 0.0) {
156 wxString msg(
"KML Parser failed to convert <Point> coordinates.");
158 return KML_PASTE_INVALID;
160 wxString pointName =
"";
161 TiXmlElement* e = node->Parent()->FirstChild(
"name")->ToElement();
162 if (e) pointName = wxString(e->GetText(), wxConvUTF8);
164 wxString pointDescr =
"";
165 e = node->Parent()->FirstChildElement(
"description");
170 TiXmlNode* n = e->FirstChild();
171 if (n)
switch (n->Type()) {
172 case TiXmlNode::TINYXML_TEXT:
173 pointDescr = wxString(e->GetText(), wxConvUTF8);
175 case TiXmlNode::TINYXML_ELEMENT:
176 TiXmlPrinter printer;
177 printer.SetIndent(
"\t");
179 pointDescr = wxString(printer.CStr(), wxConvUTF8);
185 TiXmlNode* n = node->Parent()->FirstChild(
"ExtendedData");
187 TiXmlPrinter printer;
188 printer.SetIndent(
"\t");
190 pointDescr = wxString(printer.CStr(), wxConvUTF8);
195 parsedRoutePoint->m_lat = newLat;
196 parsedRoutePoint->m_lon = newLon;
200 parsedRoutePoint->SetName(pointName);
202 return KML_PASTE_WAYPOINT;
205KmlPastebufferType Kml::ParsePasteBuffer() {
206 if (!wxTheClipboard->IsOpened())
207 if (!wxTheClipboard->Open())
return KML_PASTE_INVALID;
209 wxTextDataObject data;
210 wxTheClipboard->GetData(data);
211 kmlText = data.GetText();
212 wxTheClipboard->Close();
214 if (kmlText.Find(
"<kml") == wxNOT_FOUND)
return KML_PASTE_INVALID;
217 if (!doc.Parse(kmlText.mb_str(wxConvUTF8), 0, TIXML_ENCODING_UTF8)) {
218 wxLogError(wxString(doc.ErrorDesc(), wxConvUTF8));
219 return KML_PASTE_INVALID;
221 if (0 != strncmp(doc.RootElement()->Value(),
"kml", 3))
222 return KML_PASTE_INVALID;
224 TiXmlHandle docHandle(doc.RootElement());
227 TiXmlElement* placemark =
228 docHandle.FirstChild(
"Document").FirstChild(
"Placemark").ToElement();
230 placemark = docHandle.FirstChild(
"Placemark").ToElement();
233 wxString msg(
"KML Parser found no <Placemark> tag in the KML.");
235 return KML_PASTE_INVALID;
238 int pointCounter = 0;
240 for (; placemark; placemark = placemark->NextSiblingElement()) {
241 TiXmlElement* e = placemark->FirstChildElement(
"name");
242 if (e) name = wxString(e->GetText(), wxConvUTF8);
246 if (pointCounter == 1) {
248 TiXmlNode* element = docHandle.FirstChild(
"Document")
249 .FirstChild(
"Placemark")
253 element = docHandle.FirstChild(
"Placemark").FirstChild(
"Point").ToNode();
254 if (element)
return ParseOnePlacemarkPoint(element, name);
257 element = docHandle.FirstChild(
"Document")
258 .FirstChild(
"Placemark")
259 .FirstChild(
"LineString")
263 docHandle.FirstChild(
"Placemark").FirstChild(
"LineString").ToNode();
264 if (element)
return ParseTrack(element, name);
267 element = docHandle.FirstChild(
"Document")
268 .FirstChild(
"Placemark")
269 .FirstChild(
"gx:Track")
273 docHandle.FirstChild(
"Placemark").FirstChild(
"gx:Track").ToNode();
274 if (element)
return ParseTrack(element, name);
277 "KML Parser found a single <Placemark> in the KML, but no useable "
280 return KML_PASTE_INVALID;
285 parsedRoute =
new Route();
286 bool foundPoints =
false;
287 bool foundTrack =
false;
288 TiXmlElement* element =
289 docHandle.FirstChild(
"Document").FirstChild(
"name").ToElement();
294 docHandle.FirstChild(
"Document").FirstChild(
"Placemark").ToElement();
295 for (; placemark; placemark = placemark->NextSiblingElement()) {
296 TiXmlNode* n = placemark->FirstChild(
"Point");
298 if (ParseOnePlacemarkPoint(n->ToElement(), name) == KML_PASTE_WAYPOINT) {
299 parsedRoute->AddPoint(
new RoutePoint(parsedRoutePoint));
300 delete parsedRoutePoint;
301 parsedRoutePoint = 0;
306 n = placemark->FirstChild(
"LineString");
308 ParseTrack(n->ToElement(), name);
311 n = placemark->FirstChild(
"gx:Track");
313 ParseTrack(n->ToElement(), name);
318 if (foundPoints && parsedRoute->GetnPoints() < 2) {
319 wxString msg(
"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;
330TiXmlElement* 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);
354std::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;
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(
"%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(
"%04d", seqCounter).mb_str(wxConvUTF8));
402 seq->LinkEndChild(snVal);
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(
"%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();
469wxString Kml::MakeKmlFromRoute(
Route* route,
bool insertSeq) {
470 insertQtVlmExtendedData = insertSeq;
472 TiXmlDocument xmlDoc;
473 wxString name = _(
"OpenCPN Route");
475 TiXmlElement* document = StandardHead(xmlDoc, name);
477 std::stringstream lineStringCoords;
481 lineStringCoords << PointPlacemark(document, routepoint);
485 TiXmlElement* pmPath =
new TiXmlElement(
"Placemark");
486 document->LinkEndChild(pmPath);
488 TiXmlElement* pmName =
new TiXmlElement(
"name");
489 pmPath->LinkEndChild(pmName);
490 TiXmlText* pmNameVal =
new TiXmlText(
"Path");
491 pmName->LinkEndChild(pmNameVal);
493 TiXmlElement* linestring =
new TiXmlElement(
"LineString");
494 pmPath->LinkEndChild(linestring);
496 TiXmlElement* coordinates =
new TiXmlElement(
"coordinates");
497 linestring->LinkEndChild(coordinates);
499 TiXmlText* text =
new TiXmlText(lineStringCoords.str());
500 coordinates->LinkEndChild(text);
502 TiXmlPrinter printer;
503 printer.SetIndent(
" ");
504 xmlDoc.Accept(&printer);
506 return wxString(printer.CStr(), wxConvUTF8);
509wxString Kml::MakeKmlFromTrack(
Track* track) {
510 TiXmlDocument xmlDoc;
511 wxString name = _(
"OpenCPN Track");
512 if (track->GetName().Length()) name = track->GetName();
513 TiXmlElement* document = StandardHead(xmlDoc, name);
515 TiXmlElement* pmTrack =
new TiXmlElement(
"Placemark");
516 document->LinkEndChild(pmTrack);
518 TiXmlElement* pmName =
new TiXmlElement(
"name");
519 pmTrack->LinkEndChild(pmName);
520 TiXmlText* pmNameVal =
new TiXmlText(track->GetName().mb_str(wxConvUTF8));
521 pmName->LinkEndChild(pmNameVal);
523 TiXmlElement* gxTrack =
new TiXmlElement(
"gx:Track");
524 pmTrack->LinkEndChild(gxTrack);
526 std::stringstream lineStringCoords;
528 for (
int i = 0; i < track->GetnPoints(); i++) {
531 TiXmlElement* when =
new TiXmlElement(
"when");
532 gxTrack->LinkEndChild(when);
536 new TiXmlText(whenTime.Format(
"%Y-%m-%dT%H:%M:%SZ").mb_str(wxConvUTF8));
537 when->LinkEndChild(whenVal);
540 for (
int i = 0; i < track->GetnPoints(); i++) {
543 TiXmlElement* coord =
new TiXmlElement(
"gx:coord");
544 gxTrack->LinkEndChild(coord);
546 wxString::Format(
"%f %f 0.0", trackpoint->m_lon, trackpoint->m_lat);
547 TiXmlText* coordVal =
new TiXmlText(coordStr.mb_str(wxConvUTF8));
548 coord->LinkEndChild(coordVal);
551 TiXmlPrinter printer;
552 printer.SetIndent(
" ");
553 xmlDoc.Accept(&printer);
555 return wxString(printer.CStr(), wxConvUTF8);
558wxString Kml::MakeKmlFromWaypoint(
RoutePoint* routepoint) {
559 TiXmlDocument xmlDoc;
560 wxString name = _(
"OpenCPN Waypoint");
561 if (routepoint->GetName().Length()) name = routepoint->GetName();
562 TiXmlElement* document = StandardHead(xmlDoc, name);
564 insertQtVlmExtendedData =
false;
565 PointPlacemark(document, routepoint);
567 TiXmlPrinter printer;
568 printer.SetIndent(
" ");
569 xmlDoc.Accept(&printer);
571 return wxString(printer.CStr(), wxConvUTF8);
574void Kml::CopyRouteToClipboard(
Route* route) {
576 int format = formatDlg->ShowModal();
578 if (format != wxID_CANCEL) {
579 format = formatDlg->GetSelectedFormat();
580 bool extradata = (format == KML_COPY_EXTRADATA);
582 ::wxBeginBusyCursor();
583 if (wxTheClipboard->Open()) {
584 wxTextDataObject* data =
new wxTextDataObject;
585 data->SetText(MakeKmlFromRoute(route, extradata));
586 wxTheClipboard->SetData(data);
593void Kml::CopyTrackToClipboard(
Track* track) {
594 ::wxBeginBusyCursor();
595 if (wxTheClipboard->Open()) {
596 wxTextDataObject* data =
new wxTextDataObject;
597 data->SetText(MakeKmlFromTrack(track));
598 wxTheClipboard->SetData(data);
603void Kml::CopyWaypointToClipboard(
RoutePoint* rp) {
604 if (wxTheClipboard->Open()) {
605 wxTextDataObject* data =
new wxTextDataObject;
606 data->SetText(MakeKmlFromWaypoint(rp));
607 wxTheClipboard->SetData(data);
614 parsedRoutePoint = NULL;
620 for (
int i = 1; i <= parsedRoute->GetnPoints(); i++) {
621 if (parsedRoute->GetPoint(i))
delete parsedRoute->GetPoint(i);
625 delete parsedRoutePoint;
630KmlFormatDialog::KmlFormatDialog(wxWindow* parent)
631 : wxDialog(parent, wxID_ANY, _(
"Choose Format for Copy"), wxDefaultPosition,
633 wxBoxSizer* topSizer =
new wxBoxSizer(wxVERTICAL);
635 wxBoxSizer* sizer =
new wxBoxSizer(wxVERTICAL);
636 topSizer->Add(sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
638 choices.push_back(
new wxRadioButton(
639 this, KML_COPY_STANDARD, _(
"KML Standard (Google Earth and others)"),
640 wxDefaultPosition, wxDefaultSize, wxRB_GROUP));
642 choices.push_back(
new wxRadioButton(
643 this, KML_COPY_EXTRADATA, _(
"KML with extended waypoint data (QtVlm)"),
646 wxStdDialogButtonSizer* buttonSizer =
647 CreateStdDialogButtonSizer(wxOK | wxCANCEL);
649 sizer->Add(choices[0], 0, wxEXPAND | wxALL, 5);
650 sizer->Add(choices[1], 0, wxEXPAND | wxALL, 5);
651 sizer->Add(buttonSizer, 0, wxEXPAND | wxTOP, 5);
653 topSizer->SetSizeHints(
this);
657int KmlFormatDialog::GetSelectedFormat() {
658 for (
unsigned int i = 0; i < choices.size(); i++) {
659 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.