dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
kmlexport.cpp
Go to the documentation of this file.
1 
11 /*
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, see <http://www.gnu.org/licenses/>
24  */
25 
26 #include <QDebug>
27 #include <QMessageBox>
28 #include <QTextStream>
29 #include <QtGlobal>
30 
35 
36 #include "kmlexport.h"
37 
38 QString KmlExport::dateTimeFormat =
39  "yyyy-MM-ddThh:mm:ssZ"; // XML Schema time format. Required by KML specification
40 const double ColorMap_Jet[256][3] = COLORMAP_JET;
41 
42 #define maxVelocity \
43  20 // Vehicle velocity which corresponds to maximum color in color map. This shouldn't be
44  // hardcoded
45 #define numberOfWallAxes 5 // Number of wall axes to plot. This shouldn't be hardcoded
46 #define wallAxesSeparation 20 // Wall axes separation height in [m]. This shouldn't be hardcoded
47 
48 KmlExport::KmlExport(QString inputLogFileName, QString outputKmlFileName)
49  : outputFileName(outputKmlFileName)
50 {
51  logFile.setFileName(inputLogFileName);
52 
53  // Create new UAVObject manager and initialize it with all UAVObjects
54  UAVObjectManager *kmlUAVObjectManager = new UAVObjectManager;
55  UAVObjectsInitialize(kmlUAVObjectManager);
56 
57  // Connect new UAVO manager to a UAVTalk instance
58  kmlTalk = new UAVTalk(&logFile, kmlUAVObjectManager, false);
59 
60  // Get the UAVObjects
61  airspeedActual = AirspeedActual::GetInstance(kmlUAVObjectManager);
62  attitudeActual = AttitudeActual::GetInstance(kmlUAVObjectManager);
63  gpsPosition = GPSPosition::GetInstance(kmlUAVObjectManager);
64  homeLocation = HomeLocation::GetInstance(kmlUAVObjectManager);
65  positionActual = PositionActual::GetInstance(kmlUAVObjectManager);
66  velocityActual = VelocityActual::GetInstance(kmlUAVObjectManager);
67 
68  homeLocationData = homeLocation->getData();
69  gpsPositionData = gpsPosition->getData();
70 
71  // Connect position actual. This is the trigger event for plotting a new
72  // KML placemark.
73  connect(positionActual, SIGNAL(objectUpdated(UAVObject *)), this,
74  SLOT(positionActualUpdated(UAVObject *)), Qt::DirectConnection);
75  connect(homeLocation, SIGNAL(objectUpdated(UAVObject *)), this,
76  SLOT(homeLocationUpdated(UAVObject *)), Qt::DirectConnection);
77  connect(gpsPosition, SIGNAL(objectUpdated(UAVObject *)), this,
78  SLOT(gpsPositionUpdated(UAVObject *)), Qt::DirectConnection);
79 
80  // Get the factory singleton to create KML elements.
81  factory = KmlFactory::GetFactory();
82 
83  // Create <Document>
84  document = factory->CreateDocument();
85 
86  // Create folders
87  timestampFolder = factory->CreateFolder();
88  timestampFolder->set_name("Arrows");
89 
90  trackFolder = factory->CreateFolder();
91  trackFolder->set_name("Track");
92 
93  // Create custom styles. Add as document's first elements
94  StyleMapPtr styleCBS = createCustomBalloonStyle();
95  document->add_styleselector(styleCBS);
96 
97  StylePtr styleGT = createGroundTrackStyle();
98  document->add_styleselector(styleGT);
99 
100  StyleMapPtr wallAxesStyle = createWallAxesStyle();
101  document->add_styleselector(wallAxesStyle);
102 
103  // Create an array of lines which will make the wall axes.
104  for (int i = 0; i < numberOfWallAxes; i++) {
105  CoordinatesPtr coordinates = factory->CreateCoordinates();
106  wallAxes.append(coordinates);
107  }
108 }
109 
114 {
115  bool ret = open();
116  if (!ret) {
117  qDebug() << "Logfile failed to open during KML export";
118  return false;
119  }
120 
121  // Parses logfile and generates KML document
122  ret = preparseLogFile();
123  if (!ret) {
124  qDebug() << "Logfile preparsing failed";
125  return false;
126  }
127 
128  // Call parser.
129  parseLogFile();
130 
131  // Add track to <Document>
132  document->add_feature(trackFolder);
133 
134  // Add timespans to <Document>
135  document->add_feature(timestampFolder);
136 
137  // Add ground track to <Document>
138  {
139  LineStringPtr linestring = factory->CreateLineString();
140  linestring->set_extrude(false); // Do not extrude to ground
141  linestring->set_altitudemode(kmldom::ALTITUDEMODE_CLAMPTOGROUND);
142  linestring->set_coordinates(wallAxes[0]);
143 
144  MultiGeometryPtr multiGeometry = factory->CreateMultiGeometry();
145  multiGeometry->add_geometry(linestring);
146 
147  PlacemarkPtr placemark = factory->CreatePlacemark();
148  placemark->set_geometry(multiGeometry);
149  placemark->set_styleurl("#ts_2_tb");
150  placemark->set_name("Ground track");
151 
152  document->add_feature(placemark);
153  }
154 
155  // Add wall axes to <Document>
156  FolderPtr folder = factory->CreateFolder();
157  for (int i = 0; i < numberOfWallAxes; i++) {
158  LineStringPtr linestring = factory->CreateLineString();
159  linestring->set_extrude(false); // Do not extrude to ground
160  linestring->set_altitudemode(kmldom::ALTITUDEMODE_ABSOLUTE);
161  linestring->set_coordinates(wallAxes[i]);
162 
163  MultiGeometryPtr multiGeometry = factory->CreateMultiGeometry();
164  multiGeometry->add_geometry(linestring);
165 
166  PlacemarkPtr placemark = factory->CreatePlacemark();
167  placemark->set_geometry(multiGeometry);
168  placemark->set_styleurl("#ts_1_tb");
169 
170  folder->add_feature(placemark);
171  folder->set_name("Wall axes");
172  }
173  document->add_feature(folder);
174 
175  // Create <kml> and give it <Document>.
176  KmlPtr kml = factory->CreateKml();
177  kml->set_feature(document); // kml takes ownership.
178 
179  // Serialize to XML
180  std::string kml_data = kmldom::SerializePretty(kml);
181 
182  // Save to file
183  if (QFileInfo(outputFileName).suffix().toLower() == "kmz") {
184  if (!kmlengine::KmzFile::WriteKmz(outputFileName.toStdString().c_str(), kml_data)) {
185  qDebug() << "KMZ write failed: " << outputFileName;
186  QMessageBox::critical(Core::ICore::instance()->mainWindow(), "KMZ write failed",
187  "Failed to write KMZ file.");
188  return false;
189  }
190  } else if (QFileInfo(outputFileName).suffix().toLower() == "kml") {
191  if (!kmlbase::File::WriteStringToFile(kml_data, outputFileName.toStdString())) {
192  qDebug() << "KML write failed: " << outputFileName;
193  QMessageBox::critical(Core::ICore::instance()->mainWindow(), "KML write failed",
194  "Failed to write KML file.");
195  return false;
196  }
197  } else {
198  qDebug() << "Write failed. Invalid file name:" << outputFileName;
199  QMessageBox::critical(Core::ICore::instance()->mainWindow(), "Write failed",
200  "Failed to write file. Invalid filename");
201  return false;
202  }
203 
204  return true;
205 }
206 
212 {
213  if (logFile.isOpen()) {
214  logFile.close();
215  }
216 
217  // Open log file as ReadOnly
218  if (logFile.open(QIODevice::ReadOnly) == false) {
219  qDebug() << "Unable to open " << logFile.fileName() << " for logging";
220  return false;
221  }
222 
223  logFile.readLine(); // Read first line of log file. This assumes that the logfile is of the new
224  // format.
225  QString logGitHashString = logFile.readLine().trimmed(); // Read second line of log file. This
226  // assumes that the logfile is of the
227  // new format.
228  QString logUAVOHashString = logFile.readLine().trimmed(); // Read third line of log file. This
229  // assumes that the logfile is of the
230  // new format.
231  QString gitHash = QString::fromLatin1(Core::Constants::GCS_REVISION_STR);
232  QString uavoHash =
233  QString::fromLatin1(Core::Constants::UAVOSHA1_STR)
234  .replace("\"{ ", "")
235  .replace(" }\"", "")
236  .replace(",", "")
237  .replace("0x", ""); // See comment above for necessity for string replacements
238 
239  if (logUAVOHashString != uavoHash) {
240  QMessageBox msgBox(Core::ICore::instance()->mainWindow());
241  msgBox.setText("Likely log file incompatibility.");
242  msgBox.setInformativeText(QString("The log file was made with branch %1, UAVO hash %2. GCS "
243  "will attempt to export the file.")
244  .arg(logGitHashString)
245  .arg(logUAVOHashString));
246  msgBox.exec();
247  } else if (logGitHashString != gitHash) {
248  QMessageBox msgBox(Core::ICore::instance()->mainWindow());
249  msgBox.setText("Possible log file incompatibility.");
250  msgBox.setInformativeText(
251  QString("The log file was made with branch %1. GCS will attempt to export the file.")
252  .arg(logGitHashString));
253  msgBox.exec();
254  }
255 
256  QString tmpLine = logFile.readLine(); // Look for the header/body separation string.
257  int cnt = 0;
258  while (tmpLine != "##\n" && cnt < 10 && !logFile.atEnd()) {
259  tmpLine = logFile.readLine().trimmed();
260  cnt++;
261  }
262 
263  // Check if we reached the end of the file before finding the separation string
264  if (cnt >= 10 || logFile.atEnd()) {
265  QMessageBox msgBox(Core::ICore::instance()->mainWindow());
266  msgBox.setText("Corrupted file.");
267  msgBox.setInformativeText("GCS cannot find the separation byte. GCS will attempt to export "
268  "the file."); //<--TODO: add hyperlink to webpage with better
269  // description.
270  msgBox.exec();
271 
272  // Since we could not find the file separator, we need to return to the beginning of the
273  // file
274  logFile.seek(0);
275  }
276 
277  return true;
278 }
279 
285 {
286  // Read all log timestamps into array
287  timestampBuffer.clear(); // Save beginning of log for later use
288  timestampPos.clear();
289  quint64 logFileStartIdx = logFile.pos();
290  quint32 lastTimeStamp = 0;
291 
292  while (!logFile.atEnd()) {
293  qint64 dataSize;
294 
295  // Get time stamp position
296  timestampPos.append(logFile.pos());
297 
298  // Read timestamp and logfile packet size
299  logFile.read(reinterpret_cast<char *>(&lastTimeStamp), sizeof(lastTimeStamp));
300  logFile.read(reinterpret_cast<char *>(&dataSize), sizeof(dataSize));
301 
302  // Check if dataSize sync bytes are correct.
303  // TODO: LIKELY AS NOT, THIS WILL FAIL TO RESYNC BECAUSE THERE IS TOO LITTLE INFORMATION IN
304  // THE STRING OF SIX 0x00
305  if ((dataSize & 0xFFFFFFFFFFFF0000) != 0) {
306  qDebug() << "Wrong sync byte. At file location 0x"
307  << QString("%1").arg(logFile.pos(), 0, 16) << "Got 0x"
308  << QString("%1").arg(dataSize & 0xFFFFFFFFFFFF0000, 0, 16)
309  << ", but expected 0x"
310  "00"
311  ".";
312  logFile.seek(timestampPos.last() + 1);
313  timestampPos.pop_back();
314  continue;
315  }
316 
317  // Check if timestamps are sequential.
318  if (!timestampBuffer.isEmpty() && lastTimeStamp < timestampBuffer.last()) {
319  QMessageBox msgBox(Core::ICore::instance()->mainWindow());
320  msgBox.setText("Corrupted file.");
321  msgBox.setInformativeText("Timestamps are not sequential. Playback may have unexpected "
322  "behavior"); //<--TODO: add hyperlink to webpage with better
323  // description.
324  msgBox.exec();
325 
326  qDebug() << "Timestamp: " << timestampBuffer.last() << " " << lastTimeStamp;
327  }
328 
329  timestampBuffer.append(lastTimeStamp);
330 
331  logFile.seek(timestampPos.last() + sizeof(lastTimeStamp) + sizeof(dataSize) + dataSize);
332  }
333 
334  // Check if any timestamps were successfully read
335  if (timestampBuffer.size() == 0) {
336  QMessageBox msgBox(Core::ICore::instance()->mainWindow());
337  msgBox.setText("Empty logfile.");
338  msgBox.setInformativeText("No log data can be found.");
339  msgBox.exec();
340 
341  stopExport();
342  return false;
343  }
344 
345  // Reset to log beginning, including the timestamp.
346  logFile.seek(logFileStartIdx);
347 
348  return true;
349 }
350 
357 {
358  logFile.close();
359  return true;
360 }
361 
365 void KmlExport::parseLogFile()
366 {
367  qint64 packetSize;
368  quint32 timeStampIdx;
369 
370  // Read packets
371  while (!logFile.atEnd()) {
372  if (logFile.bytesAvailable() < 4) {
373  break;
374  }
375 
376  // Read timestamp and logfile packet size
377  logFile.read(reinterpret_cast<char *>(&timeStamp), sizeof(timeStamp));
378  logFile.read(reinterpret_cast<char *>(&packetSize), sizeof(packetSize));
379 
380  if (packetSize < 1 || packetSize > (1024 * 1024)) {
381  qDebug() << "Error: Logfile corrupted! Unlikely packet size: " << packetSize << "\n";
382  QMessageBox::critical(
383  Core::ICore::instance()->mainWindow(), "Corrupted file",
384  "Incorrect packet size. Stopping export. Data up to this point will be saved.");
385 
386  break;
387  }
388 
389  if (logFile.bytesAvailable() < packetSize) {
390  break;
391  }
392 
393  // Read the data packet from the file.
394  QByteArray dataBuffer;
395  dataBuffer.append(logFile.read(packetSize));
396 
397  // Parse the packet. This operation passes the data to the kmlTalk object, which internally
398  // parses the data
399  // and then emits objectUpdated(UAVObject *) signals. These signals are connected to in the
400  // KmlExport constructor.
401  for (int i = 0; i < dataBuffer.size(); i++) {
402  kmlTalk->processInputByte(dataBuffer[i]);
403  }
404 
405  timeStampIdx++;
406  }
407 
408  stopExport();
409 }
410 
416 StyleMapPtr KmlExport::createCustomBalloonStyle()
417 {
418  StyleMapPtr styleMap = factory->CreateStyleMap();
419 
420  {
421 
422  // Add custom balloon style (gets rid of "Directions to here...")
423  // https://groups.google.com/forum/?fromgroups#!topic/kml-support-getting-started/2CqF9oiynRY
424  BalloonStylePtr balloonStyle = factory->CreateBalloonStyle();
425  balloonStyle->set_text("$[description]");
426 
427  // Change the icon
428  IconStyleIconPtr iconStyleIcon = factory->CreateIconStyleIcon();
429  iconStyleIcon->set_href("http://maps.google.com/mapfiles/kml/shapes/arrow.png");
430 
431  // Create a label style
432  LabelStylePtr labelStyle = factory->CreateLabelStyle();
433  labelStyle->set_color(kmlbase::Color32(255, 0, 255, 255));
434  labelStyle->set_scale(0.75);
435 
436  // Create an icon style
437  IconStylePtr iconStyle = factory->CreateIconStyle();
438  iconStyle->set_icon(iconStyleIcon);
439  iconStyle->set_scale(0.65);
440 
441  // Create a line style
442  LineStylePtr lineStyle = factory->CreateLineStyle();
443  lineStyle->set_width(3.25);
444 
445  // Link the style to the icon
446  StylePtr style = factory->CreateStyle();
447  style->set_balloonstyle(balloonStyle);
448  style->set_iconstyle(iconStyle);
449  style->set_linestyle(lineStyle);
450  style->set_labelstyle(labelStyle);
451 
452  PairPtr pair = factory->CreatePair();
453  pair->set_styleselector(style);
454  pair->set_key(kmldom::STYLESTATE_NORMAL);
455 
456  styleMap->add_pair(pair);
457  }
458 
459  {
460  // Add custom balloon style (gets rid of "Directions to here...")
461  // https://groups.google.com/forum/?fromgroups#!topic/kml-support-getting-started/2CqF9oiynRY
462  BalloonStylePtr balloonStyle = factory->CreateBalloonStyle();
463  balloonStyle->set_text("$[description]");
464 
465  // Change the icon
466  IconStyleIconPtr iconStyleIcon = factory->CreateIconStyleIcon();
467  iconStyleIcon->set_href("http://maps.google.com/mapfiles/kml/shapes/arrow.png");
468 
469  // Create an icon style
470  IconStylePtr iconStyle = factory->CreateIconStyle();
471  iconStyle->set_icon(iconStyleIcon);
472  iconStyle->set_scale(0.65);
473 
474  // Create a label style
475  LabelStylePtr labelStyle = factory->CreateLabelStyle();
476  labelStyle->set_color(kmlbase::Color32(255, 0, 255, 255));
477  labelStyle->set_scale(0.9);
478 
479  // Create a line style
480  LineStylePtr lineStyle = factory->CreateLineStyle();
481  lineStyle->set_width(6.5);
482 
483  // Link the style to the icon
484  StylePtr style = factory->CreateStyle();
485  style->set_balloonstyle(balloonStyle);
486  style->set_iconstyle(iconStyle);
487  style->set_linestyle(lineStyle);
488  style->set_labelstyle(labelStyle);
489 
490  PairPtr pair = factory->CreatePair();
491  pair->set_styleselector(style);
492  pair->set_key(kmldom::STYLESTATE_HIGHLIGHT);
493 
494  styleMap->add_pair(pair);
495  }
496 
497  styleMap->set_id("directiveArrowStyle");
498 
499  return styleMap;
500 }
501 
506 StylePtr KmlExport::createGroundTrackStyle()
507 {
508  // Add custom balloon style (gets rid of "Directions to here...")
509  // https://groups.google.com/forum/?fromgroups#!topic/kml-support-getting-started/2CqF9oiynRY
510  BalloonStylePtr balloonStyle = factory->CreateBalloonStyle();
511  balloonStyle->set_text("$[id]");
512 
513  // Create an icon style
514  IconStylePtr iconStyle = factory->CreateIconStyle();
515  iconStyle->set_scale(0);
516 
517  // Create a label style
518  LabelStylePtr labelStyle = factory->CreateLabelStyle();
519  labelStyle->set_color(kmlbase::Color32(255, 0, 255, 255));
520  labelStyle->set_scale(0);
521 
522  // Create a line style
523  LineStylePtr lineStyle = factory->CreateLineStyle();
524  lineStyle->set_color(kmlbase::Color32(255, 0, 0, 0)); // Black
525  lineStyle->set_width(9);
526 
527  // Link the style to the icon
528  StylePtr style = factory->CreateStyle();
529  style->set_balloonstyle(balloonStyle);
530  style->set_iconstyle(iconStyle);
531  style->set_linestyle(lineStyle);
532  style->set_labelstyle(labelStyle);
533 
534  style->set_id("ts_2_tb");
535 
536  return style;
537 }
538 
539 StyleMapPtr KmlExport::createWallAxesStyle()
540 {
541  StyleMapPtr styleMap = factory->CreateStyleMap();
542 
543  {
544  // Add custom balloon style (gets rid of "Directions to here...")
545  // https://groups.google.com/forum/?fromgroups#!topic/kml-support-getting-started/2CqF9oiynRY
546  BalloonStylePtr balloonStyle = factory->CreateBalloonStyle();
547  balloonStyle->set_text("$[id]");
548 
549  // Create an icon style
550  IconStylePtr iconStyle = factory->CreateIconStyle();
551  iconStyle->set_scale(0);
552 
553  // Create a label style
554  LabelStylePtr labelStyle = factory->CreateLabelStyle();
555  labelStyle->set_color(kmlbase::Color32(255, 0, 255, 255));
556  labelStyle->set_scale(0);
557 
558  // Create a line style
559  LineStylePtr lineStyle = factory->CreateLineStyle();
560  lineStyle->set_color(kmlbase::Color32(255, 0, 0, 0)); // Black
561  lineStyle->set_width(.9);
562 
563  // Link the style to the icon
564  StylePtr style = factory->CreateStyle();
565  style->set_balloonstyle(balloonStyle);
566  style->set_iconstyle(iconStyle);
567  style->set_linestyle(lineStyle);
568  style->set_labelstyle(labelStyle);
569 
570  PairPtr pair = factory->CreatePair();
571  pair->set_styleselector(style);
572  pair->set_key(kmldom::STYLESTATE_NORMAL);
573 
574  styleMap->add_pair(pair);
575  }
576 
577  {
578  // Add custom balloon style (gets rid of "Directions to here...")
579  // https://groups.google.com/forum/?fromgroups#!topic/kml-support-getting-started/2CqF9oiynRY
580  BalloonStylePtr balloonStyle = factory->CreateBalloonStyle();
581  balloonStyle->set_text("$[id]");
582 
583  // Create an icon style
584  IconStylePtr iconStyle = factory->CreateIconStyle();
585  iconStyle->set_scale(0);
586 
587  // Create a label style
588  LabelStylePtr labelStyle = factory->CreateLabelStyle();
589  labelStyle->set_color(kmlbase::Color32(255, 0, 255, 255));
590  labelStyle->set_scale(0.75);
591 
592  // Create a line style
593  LineStylePtr lineStyle = factory->CreateLineStyle();
594  lineStyle->set_color(kmlbase::Color32(255, 0, 0, 0)); // Black
595  lineStyle->set_width(1.8);
596 
597  // Link the style to the icon
598  StylePtr style = factory->CreateStyle();
599  style->set_balloonstyle(balloonStyle);
600  style->set_iconstyle(iconStyle);
601  style->set_linestyle(lineStyle);
602  style->set_labelstyle(labelStyle);
603 
604  PairPtr pair = factory->CreatePair();
605  pair->set_styleselector(style);
606  pair->set_key(kmldom::STYLESTATE_HIGHLIGHT);
607 
608  styleMap->add_pair(pair);
609  }
610 
611  styleMap->set_id("ts_1_tb");
612 
613  return styleMap;
614 }
615 
623 PlacemarkPtr KmlExport::CreateLineStringPlacemark(const LLAVCoordinates &startPoint,
624  const LLAVCoordinates &endPoint,
625  quint32 newPlacemarkTime)
626 {
627  CoordinatesPtr coordinates = factory->CreateCoordinates();
628  coordinates->add_latlngalt(startPoint.latitude, startPoint.longitude, startPoint.altitude);
629  coordinates->add_latlngalt(endPoint.latitude, endPoint.longitude, endPoint.altitude);
630 
631  LineStringPtr linestring = factory->CreateLineString();
632  linestring->set_extrude(true); // Extrude to ground
633  linestring->set_altitudemode(kmldom::ALTITUDEMODE_ABSOLUTE);
634  linestring->set_coordinates(coordinates);
635 
636  StyleMapPtr styleMap = factory->CreateStyleMap();
637 
638  // Add custom balloon style (gets rid of "Directions to here...")
639  // https://groups.google.com/forum/?fromgroups#!topic/kml-support-getting-started/2CqF9oiynRY
640  BalloonStylePtr balloonStyle = factory->CreateBalloonStyle();
641  balloonStyle->set_text("$[description]");
642 
643  {
644  double currentVelocity = (startPoint.groundspeed + endPoint.groundspeed) / 2;
645 
646  // Set the linestyle. The color is a function of speed.
647  LineStylePtr lineStyle = factory->CreateLineStyle();
648  lineStyle->set_color(mapVelocity2Color(currentVelocity));
649 
650  PolyStylePtr polyStyle = factory->CreatePolyStyle();
651  polyStyle->set_color(mapVelocity2Color(currentVelocity, 100));
652 
653  // Link the style to the icon
654  StylePtr style = factory->CreateStyle();
655  style->set_balloonstyle(balloonStyle);
656  style->set_linestyle(lineStyle);
657  style->set_polystyle(polyStyle);
658 
659  PairPtr pair = factory->CreatePair();
660  pair->set_styleselector(style);
661  pair->set_key(kmldom::STYLESTATE_NORMAL);
662 
663  styleMap->add_pair(pair);
664  }
665 
666  {
667  double currentVelocity = (startPoint.groundspeed + endPoint.groundspeed) / 2;
668 
669  // Set the linestyle. The color is a function of speed.
670  LineStylePtr lineStyle = factory->CreateLineStyle();
671  lineStyle->set_color(mapVelocity2Color(currentVelocity));
672 
673  PolyStylePtr polyStyle = factory->CreatePolyStyle();
674  polyStyle->set_color(mapVelocity2Color(currentVelocity, 100));
675  polyStyle->set_fill(false);
676 
677  // Link the style to the icon
678  StylePtr style = factory->CreateStyle();
679  style->set_balloonstyle(balloonStyle);
680  style->set_linestyle(lineStyle);
681  style->set_polystyle(polyStyle);
682 
683  PairPtr pair = factory->CreatePair();
684  pair->set_styleselector(style);
685  pair->set_key(kmldom::STYLESTATE_HIGHLIGHT);
686 
687  styleMap->add_pair(pair);
688  }
689 
690  PlacemarkPtr placemark = factory->CreatePlacemark();
691  placemark->set_geometry(linestring);
692  placemark->set_styleselector(styleMap);
693  placemark->set_visibility(true);
694 
695  // Create the timespan
696  TimeSpanPtr timeSpan = factory->CreateTimeSpan();
697  QDateTime startTime =
698  QDateTime::currentDateTimeUtc().addMSecs(newPlacemarkTime); // FIXME: Make this a function
699  // of the true time, preferably
700  // gotten from the GPS
701  QDateTime endTime = QDateTime::currentDateTimeUtc().addMSecs(newPlacemarkTime);
702  timeSpan->set_begin(startTime.toString(dateTimeFormat).toStdString());
703  timeSpan->set_end(endTime.toString(dateTimeFormat).toStdString());
704 
705  // Set the name
706  QDateTime trackTime =
707  QDateTime::currentDateTimeUtc().addMSecs(newPlacemarkTime); // FIXME: Make it a function of
708  // the realtime preferably
709  // gotten from the GPS
710  placemark->set_name(trackTime.toString(dateTimeFormat).toStdString());
711 
712  // Add a nice description to the track placemark
713  placemark->set_description(informationString.toStdString());
714 
715  // Set the timespan
716  placemark->set_timeprimitive(timeSpan);
717 
718  return placemark;
719 }
720 
730 PlacemarkPtr KmlExport::createTimespanPlacemark(const LLAVCoordinates &timestampPoint,
731  quint32 lastPlacemarkTime, quint32 newPlacemarkTime)
732 {
733  // Create coordinates
734  CoordinatesPtr coordinates = factory->CreateCoordinates();
735  coordinates->add_latlngalt(timestampPoint.latitude, timestampPoint.longitude,
736  timestampPoint.altitude);
737 
738  // Create point, using previous coordinates
739  PointPtr point = factory->CreatePoint();
740  point->set_extrude(true); // Extrude to ground
741  point->set_altitudemode(kmldom::ALTITUDEMODE_ABSOLUTE);
742  point->set_coordinates(coordinates);
743 
744  // Create the timespan
745  TimeSpanPtr timeSpan = factory->CreateTimeSpan();
746  QDateTime startTime =
747  QDateTime::currentDateTimeUtc().addMSecs(lastPlacemarkTime); // FIXME: Make it a function of
748  // the realtime preferably
749  // gotten from the GPS
750  QDateTime endTime = QDateTime::currentDateTimeUtc().addMSecs(newPlacemarkTime);
751  timeSpan->set_begin(startTime.toString(dateTimeFormat).toStdString());
752  timeSpan->set_end(endTime.toString(dateTimeFormat).toStdString());
753 
754  // Create an icon style. This arrow icon will be rotated and colored to represent velocity
755  AttitudeActual::DataFields attitudeActualData = attitudeActual->getData();
756  AirspeedActual::DataFields airspeedActualData = airspeedActual->getData();
757  IconStylePtr iconStyle = factory->CreateIconStyle();
758  iconStyle->set_color(mapVelocity2Color(airspeedActualData.CalibratedAirspeed));
759  iconStyle->set_heading(
760  attitudeActualData.Yaw
761  + 180); // Adding 180 degrees because the arrow art points down, i.e. south.
762 
763  // Create a line style. This defines the style for the "legs" connecting the points to the
764  // ground.
765  LineStylePtr lineStyle = factory->CreateLineStyle();
766  lineStyle->set_color(mapVelocity2Color(timestampPoint.groundspeed));
767 
768  // Link the style to the icon
769  StylePtr style = factory->CreateStyle();
770  style->set_linestyle(lineStyle);
771  style->set_iconstyle(iconStyle);
772 
773  // Generate the placemark with all above attributes
774  PlacemarkPtr placemark = factory->CreatePlacemark();
775  placemark->set_geometry(point);
776  placemark->set_timeprimitive(timeSpan);
777  placemark->set_name(QString("%1").arg(timeStamp / 1000.0).toStdString());
778  placemark->set_visibility(true);
779 
780  // Set the placemark to use the custom rotated arrow style
781  placemark->set_styleurl("#directiveArrowStyle");
782  placemark->set_styleselector(style);
783 
784  // Add a nice description to the placemark
785  placemark->set_description(informationString.toStdString());
786 
787  return placemark;
788 }
789 
796 kmlbase::Color32 KmlExport::mapVelocity2Color(double velocity, quint8 alpha)
797 {
798  quint8 colorMapIdx = fmin(fabs(velocity / maxVelocity), 1) * 255;
799  quint8 r = round(ColorMap_Jet[colorMapIdx][0]
800  * 255); // Colormap is in [0,1], so it needs to be scaled to [0,255]
801  quint8 g = round(ColorMap_Jet[colorMapIdx][1] * 255);
802  quint8 b = round(ColorMap_Jet[colorMapIdx][2] * 255);
803 
804  return kmlbase::Color32(alpha, b, g, r);
805 }
806 
813 void KmlExport::positionActualUpdated(UAVObject *obj)
814 {
815  Q_UNUSED(obj);
816 
817  // Only export positional data if the home location has been set.
818  if (homeLocationData.Set == HomeLocation::SET_FALSE)
819  return;
820 
821  // Only plot positional data if we have a lock
822  if (gpsPositionData.Status != GPSPosition::STATUS_FIX2D
823  && gpsPositionData.Status != GPSPosition::STATUS_FIX3D)
824  return;
825 
826  AirspeedActual::DataFields airspeedActualData = airspeedActual->getData();
827  PositionActual::DataFields positionActualData = positionActual->getData();
828  VelocityActual::DataFields velocityActualData = velocityActual->getData();
829 
830  LLAVCoordinates newPoint;
831 
832  // Convert NED data to LLA data
833  double homeLLA[3] = { homeLocationData.Latitude / 1e7, homeLocationData.Longitude / 1e7,
834  homeLocationData.Altitude };
835  double NED[3] = { positionActualData.North, positionActualData.East, positionActualData.Down };
836  double LLA[3];
837  Utils::CoordinateConversions().NED2LLA_HomeLLA(homeLLA, NED, LLA);
838 
839  // Generate new placemark
840  newPoint.latitude = LLA[0];
841  newPoint.longitude = LLA[1];
842  newPoint.altitude = LLA[2];
843  newPoint.groundspeed = sqrt(velocityActualData.North * velocityActualData.North
844  + velocityActualData.East * velocityActualData.East);
845 
846  // Update UAV info string
847  informationString.clear();
848  informationString.append(QString("Latitude: %1 deg\nLongitude: %2 deg\nAltitude: %3 "
849  "m\nAirspeed: %4 m/s\nGroundspeed: %5 m/s\n")
850  .arg(newPoint.latitude)
851  .arg(newPoint.longitude)
852  .arg(newPoint.altitude)
853  .arg(airspeedActualData.CalibratedAirspeed)
854  .arg(newPoint.groundspeed));
855 
856  // In case this is the first time through, copy data and exit
857  static bool firstPoint;
858 
859  if (firstPoint == false) {
860  oldPoint.latitude = newPoint.latitude;
861  oldPoint.longitude = newPoint.longitude;
862  oldPoint.altitude = newPoint.altitude;
863  oldPoint.groundspeed = newPoint.groundspeed;
864 
865  firstPoint = true;
866  return;
867  }
868 
869  // Create wall axes
870  for (int i = 0; i < numberOfWallAxes; i++) {
871  wallAxes[i]->add_latlngalt(newPoint.latitude, newPoint.longitude,
872  i * wallAxesSeparation + homeLocationData.Altitude);
873  }
874 
875  // Create colored tracks and add to the KML document
876  PlacemarkPtr newPlacemark = CreateLineStringPlacemark(oldPoint, newPoint, timeStamp);
877  trackFolder->add_feature(newPlacemark);
878 
879  // Every 2 seconds generate a time stamp
880  if (timeStamp - lastPlacemarkTime > 2000) {
881 
882  PlacemarkPtr newPlacemarkTimestamp =
883  createTimespanPlacemark(newPoint, lastPlacemarkTime, timeStamp);
884  timestampFolder->add_feature(newPlacemarkTimestamp);
885  lastPlacemarkTime = timeStamp;
886  }
887 
888  // Copy newPoint to oldPoint
889  oldPoint.latitude = newPoint.latitude;
890  oldPoint.longitude = newPoint.longitude;
891  oldPoint.altitude = newPoint.altitude;
892  oldPoint.groundspeed = newPoint.groundspeed;
893 }
894 
895 void KmlExport::homeLocationUpdated(UAVObject *obj)
896 {
897  Q_UNUSED(obj);
898  homeLocationData = homeLocation->getData();
899 }
900 
901 void KmlExport::gpsPositionUpdated(UAVObject *obj)
902 {
903  Q_UNUSED(obj);
904  gpsPositionData = gpsPosition->getData();
905 }
906 
const char *const UAVOSHA1_STR
Definition: coreconstants.h:46
void UAVObjectsInitialize(UAVObjectManager *objMngr)
bool exportToKML()
KmlExport::exportToKML Triggers logfile export to KML.
Definition: kmlexport.cpp:113
double longitude
Definition: kmlexport.h:55
KmlExport(QString inputFileName, QString outputFileName)
Definition: kmlexport.cpp:48
double groundspeed
Definition: kmlexport.h:57
axis equal end function NED
Definition: OPPlots.m:63
QFile logFile
Definition: kmlexport.h:89
for i
Definition: OPPlots.m:140
bool open()
KmlExport::open Opens the logfile and ensures it's sane.
Definition: kmlexport.cpp:211
static ICore * instance()
Definition: coreimpl.cpp:46
double altitude
Definition: kmlexport.h:56
int NED2LLA_HomeLLA(double homeLLA[3], double NED[3], double LLA[3])
const char *const GCS_REVISION_STR
Definition: coreconstants.h:45
double latitude
Definition: kmlexport.h:54
const double ColorMap_Jet[256][3]
Definition: kmlexport.cpp:40
LLA
Definition: OPPlots.m:34
bool preparseLogFile()
KmlExport::preparseLogFile Ensures that the logfile has data to export.
Definition: kmlexport.cpp:284
bool stopExport()
KmlExport::stopExport Called to stop the export. Currently only closes the logfile.
Definition: kmlexport.cpp:356