Commit 6e388c70 authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #5499 from HorusAeronaves/kml

Add basic KML functionality
parents c374d0e1 f55d443a
......@@ -501,6 +501,7 @@ HEADERS += \
src/MissionManager/FixedWingLandingComplexItem.h \
src/MissionManager/GeoFenceController.h \
src/MissionManager/GeoFenceManager.h \
src/MissionManager/KML.h \
src/MissionManager/MissionCommandList.h \
src/MissionManager/MissionCommandTree.h \
src/MissionManager/MissionCommandUIInfo.h \
......@@ -685,6 +686,7 @@ SOURCES += \
src/MissionManager/FixedWingLandingComplexItem.cc \
src/MissionManager/GeoFenceController.cc \
src/MissionManager/GeoFenceManager.cc \
src/MissionManager/KML.cc \
src/MissionManager/MissionCommandList.cc \
src/MissionManager/MissionCommandTree.cc \
src/MissionManager/MissionCommandUIInfo.cc \
......
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include "KML.h"
#include <QDomDocument>
#include <QStringList>
const QString Kml::_version("version=\"1.0\"");
const QString Kml::_encoding("encoding=\"UTF-8\"");
const QString Kml::_opengis("http://www.opengis.net/kml/2.2");
const QString Kml::_qgckml("QGC KML");
Kml::Kml()
{
//create header
createHeader();
//name
createTextElement(_docEle, "name", _qgckml);
//open
createTextElement(_docEle, "open", "1");
//create style
createStyles();
}
void Kml::points(const QStringList& points)
{
//create placemark
QDomElement placemark = _domDocument.createElement("Placemark");
_docEle.appendChild(placemark);
createTextElement(placemark, "styleUrl", "yellowLineGreenPoly");
createTextElement(placemark, "name", "Absolute");
createTextElement(placemark, "visibility", "0");
createTextElement(placemark, "description", "Transparent purple line");
QStringList latLonAlt = points[0].split(",");
QStringList lookAtList({latLonAlt[0], latLonAlt[1], "0" \
, "-100", "45", "2500"});
createLookAt(placemark, lookAtList);
//Add linestring
QDomElement lineString = _domDocument.createElement("LineString");
placemark.appendChild(lineString);
//extruder
createTextElement(lineString, "extruder", "1");
createTextElement(lineString, "tessellate", "1");
createTextElement(lineString, "altitudeMode", "absolute");
QString coordinates;
for(const auto& point : points) {
coordinates += point + "\n";
}
createTextElement(lineString, "coordinates", coordinates);
}
void Kml::save(QDomDocument& document)
{
document = _domDocument;
}
void Kml::createHeader()
{
QDomProcessingInstruction header = _domDocument.createProcessingInstruction("xml", _version + " " + _encoding);
_domDocument.appendChild(header);
QDomElement kml = _domDocument.createElement("kml");
kml.setAttribute("xmlns", _opengis);
_docEle = _domDocument.createElement("Document");
kml.appendChild(_docEle);
_domDocument.appendChild(kml);
}
void Kml::createStyles()
{
QDomElement style = _domDocument.createElement("Style");
style.setAttribute("id", "yellowLineGreenPoly");
createStyleLine(style, "7f00ffff", "4", "7f00ff00");
_docEle.appendChild(style);
}
void Kml::createLookAt(QDomElement& placemark, const QStringList &lookAtList)
{
QDomElement lookAt = _domDocument.createElement("LookAt");
placemark.appendChild(lookAt);
createTextElement(lookAt, "longitude", lookAtList[0]);
createTextElement(lookAt, "latitude", lookAtList[1]);
createTextElement(lookAt, "altitude", lookAtList[2]);
createTextElement(lookAt, "heading", lookAtList[3]);
createTextElement(lookAt, "tilt", lookAtList[4]);
createTextElement(lookAt, "range", lookAtList[5]);
}
void Kml::createTextElement(QDomElement& domEle, const QString& elementName, const QString& textElement)
{
// <elementName>textElement</elementName>
auto element = _domDocument.createElement(elementName);
element.appendChild(_domDocument.createTextNode(textElement));
domEle.appendChild(element);
}
void Kml::createStyleLine(QDomElement& domEle, const QString& lineColor, const QString& lineWidth, const QString& polyColor)
{
/*
<LineStyle>
<color>7f00ffff</color>
<width>4</width>
</LineStyle>
<PolyStyle>
<color>7f00ff00</color>
</PolyStyle>
*/
auto lineStyle = _domDocument.createElement("LineStyle");
auto polyStyle = _domDocument.createElement("PolyStyle");
domEle.appendChild(lineStyle);
domEle.appendChild(polyStyle);
createTextElement(lineStyle, "color", lineColor);
createTextElement(lineStyle, "width", lineWidth);
createTextElement(polyStyle, "color", polyColor);
}
Kml::~Kml()
{
}
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#ifndef KML_H
#define KML_H
#include <QDomDocument>
#include <QDomElement>
class Kml
{
public:
Kml();
~Kml();
void points(const QStringList& points);
void polygon(const QStringList& points);
void save(QDomDocument& document);
private:
void createHeader();
void createLookAt(QDomElement& placemark, const QStringList &lookAtList);
void createStyles();
void createStyleLine(QDomElement& domEle, const QString& lineColor, const QString& lineWidth, const QString& polyColor);
void createTextElement(QDomElement& domEle, const QString& elementName, const QString& textElement);
QDomDocument _domDocument;
QDomElement _docEle;
static const QString _encoding;
static const QString _opengis;
static const QString _qgckml;
static const QString _version;
};
#endif
......@@ -8,6 +8,7 @@
****************************************************************************/
#include "MissionCommandUIInfo.h"
#include "MissionController.h"
#include "MultiVehicleManager.h"
#include "MissionManager.h"
......@@ -25,6 +26,7 @@
#include "MissionSettingsItem.h"
#include "QGCQGeoCoordinate.h"
#include "PlanMasterController.h"
#include "KML.h"
#ifndef __mobile__
#include "MainWindow.h"
......@@ -256,6 +258,44 @@ bool MissionController::_convertToMissionItems(QmlObjectListModel* visualMission
return endActionSet;
}
void MissionController::convertToKMLDocument(QDomDocument& document)
{
QJsonObject missionJson;
QmlObjectListModel* visualItems = new QmlObjectListModel();
QList<MissionItem*> missionItens;
QString error;
save(missionJson);
_loadItemsFromJson(missionJson, visualItems, error);
_convertToMissionItems(visualItems, missionItens, this);
float altitude = missionJson[_jsonPlannedHomePositionKey].toArray()[2].toDouble();
QString coord;
QStringList coords;
// Drop home position
bool dropPoint = true;
for(const auto& item : missionItens) {
if(dropPoint) {
dropPoint = false;
continue;
}
const MissionCommandUIInfo* uiInfo = \
qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_controllerVehicle, item->command());
if (uiInfo && uiInfo->specifiesCoordinate() && !uiInfo->isStandaloneCoordinate()) {
coord = QString::number(item->param6()) \
+ "," \
+ QString::number(item->param5()) \
+ "," \
+ QString::number(item->param7() + altitude);
coords.append(coord);
}
}
Kml kml;
kml.points(coords);
kml.save(document);
}
void MissionController::sendItemsToVehicle(Vehicle* vehicle, QmlObjectListModel* visualMissionItems)
{
if (vehicle) {
......
......@@ -26,6 +26,7 @@ class MissionSettingsItem;
class AppSettings;
class MissionManager;
class SimpleMissionItem;
class QDomDocument;
Q_DECLARE_LOGGING_CATEGORY(MissionControllerLog)
......@@ -125,6 +126,9 @@ public:
void managerVehicleChanged (Vehicle* managerVehicle) final;
bool showPlanFromManagerVehicle (void) final;
// Create KML file
void convertToKMLDocument(QDomDocument& document);
// Property accessors
QmlObjectListModel* visualItems (void) { return _visualItems; }
......
......@@ -14,7 +14,9 @@
#include "AppSettings.h"
#include "JsonHelper.h"
#include "MissionManager.h"
#include "KML.h"
#include <QDomDocument>
#include <QJsonDocument>
#include <QFileInfo>
......@@ -372,6 +374,30 @@ void PlanMasterController::saveToFile(const QString& filename)
}
}
void PlanMasterController::saveToKml(const QString& filename)
{
if (filename.isEmpty()) {
return;
}
QString kmlFilename = filename;
if (!QFileInfo(filename).fileName().contains(".")) {
kmlFilename += QString(".%1").arg(kmlFileExtension());
}
QFile file(kmlFilename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qgcApp()->showMessage(tr("KML save error %1 : %2").arg(filename).arg(file.errorString()));
} else {
QDomDocument domDocument;
_missionController.convertToKMLDocument(domDocument);
QTextStream stream(&file);
stream << domDocument.toString();
file.close();
}
}
void PlanMasterController::removeAll(void)
{
_missionController.removeAll();
......@@ -417,6 +443,11 @@ QString PlanMasterController::fileExtension(void) const
return AppSettings::planFileExtension;
}
QString PlanMasterController::kmlFileExtension(void) const
{
return AppSettings::kmlFileExtension;
}
QStringList PlanMasterController::loadNameFilters(void) const
{
QStringList filters;
......@@ -435,6 +466,14 @@ QStringList PlanMasterController::saveNameFilters(void) const
return filters;
}
QStringList PlanMasterController::saveKmlFilters(void) const
{
QStringList filters;
filters << tr("KML Files (*.%1)").arg(kmlFileExtension()) << tr("All Files (*.*)");
return filters;
}
void PlanMasterController::sendPlanToVehicle(Vehicle* vehicle, const QString& filename)
{
// Use a transient PlanMasterController to accomplish this
......
......@@ -39,8 +39,11 @@ public:
Q_PROPERTY(bool syncInProgress READ syncInProgress NOTIFY syncInProgressChanged) ///< true: Information is currently being saved/sent, false: no active save/send in progress
Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) ///< true: Unsaved/sent changes are present, false: no changes since last save/send
Q_PROPERTY(QString fileExtension READ fileExtension CONSTANT) ///< File extension for missions
Q_PROPERTY(QString kmlFileExtension READ kmlFileExtension CONSTANT)
///< kml file extension for missions
Q_PROPERTY(QStringList loadNameFilters READ loadNameFilters CONSTANT) ///< File filter list loading plan files
Q_PROPERTY(QStringList saveNameFilters READ saveNameFilters CONSTANT) ///< File filter list saving plan files
Q_PROPERTY(QStringList saveKmlFilters READ saveKmlFilters CONSTANT) ///< File filter list saving KML files
/// Should be called immediately upon Component.onCompleted.
/// @param editMode true: controller being used in Plan view, false: controller being used in Fly view
......@@ -58,6 +61,7 @@ public:
Q_INVOKABLE void sendToVehicle(void);
Q_INVOKABLE void loadFromFile(const QString& filename);
Q_INVOKABLE void saveToFile(const QString& filename);
Q_INVOKABLE void saveToKml(const QString& filename);
Q_INVOKABLE void removeAll(void); ///< Removes all from controller only, synce required to remove from vehicle
Q_INVOKABLE void removeAllFromVehicle(void); ///< Removes all from vehicle and controller
......@@ -71,8 +75,10 @@ public:
bool dirty (void) const;
void setDirty (bool dirty);
QString fileExtension (void) const;
QString kmlFileExtension(void) const;
QStringList loadNameFilters (void) const;
QStringList saveNameFilters (void) const;
QStringList saveKmlFilters (void) const;
Vehicle* controllerVehicle(void) { return _controllerVehicle; }
Vehicle* managerVehicle(void) { return _managerVehicle; }
......
......@@ -169,6 +169,7 @@ QGCView {
function saveToSelectedFile() {
fileDialog.title = qsTr("Save Plan")
fileDialog.plan = true
fileDialog.selectExisting = false
fileDialog.nameFilters = masterController.saveNameFilters
fileDialog.openForSave()
......@@ -177,6 +178,14 @@ QGCView {
function fitViewportToItems() {
mapFitFunctions.fitMapViewportToMissionItems()
}
function saveKmlToSelectedFile() {
fileDialog.title = qsTr("Save KML")
fileDialog.plan = false
fileDialog.selectExisting = false
fileDialog.nameFilters = masterController.saveKmlFilters
fileDialog.openForSave()
}
}
Connections {
......@@ -228,12 +237,13 @@ QGCView {
QGCFileDialog {
id: fileDialog
qgcView: _qgcView
property var plan: true
folder: QGroundControl.settingsManager.appSettings.missionSavePath
fileExtension: QGroundControl.settingsManager.appSettings.planFileExtension
fileExtension2: QGroundControl.settingsManager.appSettings.missionFileExtension
onAcceptedForSave: {
masterController.saveToFile(file)
plan ? masterController.saveToFile(file) : masterController.saveToKml(file)
close()
}
......@@ -792,6 +802,16 @@ QGCView {
_qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
}
}
QGCButton {
text: qsTr("Save KML...")
Layout.fillWidth: true
enabled: !masterController.syncInProgress
onClicked: {
dropPanel.hide()
masterController.saveKmlToSelectedFile()
}
}
}
}
}
......
......@@ -44,6 +44,7 @@ const char* AppSettings::waypointsFileExtension = "waypoints";
const char* AppSettings::fenceFileExtension = "fence";
const char* AppSettings::rallyPointFileExtension = "rally";
const char* AppSettings::telemetryFileExtension = "tlog";
const char* AppSettings::kmlFileExtension = "kml";
const char* AppSettings::logFileExtension = "ulg";
const char* AppSettings::parameterDirectory = "Parameters";
......
......@@ -52,6 +52,7 @@ public:
Q_PROPERTY(QString waypointsFileExtension MEMBER waypointsFileExtension CONSTANT)
Q_PROPERTY(QString parameterFileExtension MEMBER parameterFileExtension CONSTANT)
Q_PROPERTY(QString telemetryFileExtension MEMBER telemetryFileExtension CONSTANT)
Q_PROPERTY(QString kmlFileExtension MEMBER kmlFileExtension CONSTANT)
Q_PROPERTY(QString logFileExtension MEMBER logFileExtension CONSTANT)
Fact* offlineEditingFirmwareType (void);
......@@ -115,6 +116,7 @@ public:
static const char* fenceFileExtension;
static const char* rallyPointFileExtension;
static const char* telemetryFileExtension;
static const char* kmlFileExtension;
static const char* logFileExtension;
// Child directories of savePath for specific file types
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment