Commit dd135422 authored by Valentin Platzgummer's avatar Valentin Platzgummer

editing added to RouteComplexItem

parent 0877c5b7
...@@ -421,6 +421,8 @@ INCLUDEPATH += \ ...@@ -421,6 +421,8 @@ INCLUDEPATH += \
src/Vehicle \ src/Vehicle \
src/Audio \ src/Audio \
src/comm \ src/comm \
src/RouteMissionItem \
src/RouteMissionItem/geometry \
src/comm/ros_bridge \ src/comm/ros_bridge \
src/input \ src/input \
src/lib/qmapcontrol \ src/lib/qmapcontrol \
...@@ -445,7 +447,11 @@ contains (DEFINES, QGC_ENABLE_PAIRING) { ...@@ -445,7 +447,11 @@ contains (DEFINES, QGC_ENABLE_PAIRING) {
HEADERS += \ HEADERS += \
src/QmlControls/QmlUnitsConversion.h \ src/QmlControls/QmlUnitsConversion.h \
src/RouteMissionItem/geometry/GeoArea.h \
src/RouteMissionItem/geometry/MeasurementArea.h \
src/RouteMissionItem/geometry/SafeArea.h \
src/Vehicle/VehicleEscStatusFactGroup.h \ src/Vehicle/VehicleEscStatusFactGroup.h \
src/RouteMissionItem/AreaData.h \
src/RouteMissionItem/RouteComplexItem.h \ src/RouteMissionItem/RouteComplexItem.h \
src/RouteMissionItem/GenericSingelton.h \ src/RouteMissionItem/GenericSingelton.h \
src/RouteMissionItem/geometry/GenericCircle.h \ src/RouteMissionItem/geometry/GenericCircle.h \
...@@ -492,12 +498,6 @@ HEADERS += \ ...@@ -492,12 +498,6 @@ HEADERS += \
src/api/QGCSettings.h \ src/api/QGCSettings.h \
src/api/QmlComponentInfo.h \ src/api/QmlComponentInfo.h \
src/GPS/Drivers/src/base_station.h \ src/GPS/Drivers/src/base_station.h \
src/RouteMissionItem/geometry/WimaArea.h \
src/RouteMissionItem/geometry/WimaServiceArea.h \
src/RouteMissionItem/geometry/WimaMeasurementArea.h \
src/RouteMissionItem/geometry/PlanimetryCalculus.h \
src/RouteMissionItem/geometry/PolygonCalculus.h \
src/RouteMissionItem/OptimisationTools.h \
src/Settings/WimaSettings.h \ src/Settings/WimaSettings.h \
src/comm/ros_bridge/include/RosBridgeClient.h \ src/comm/ros_bridge/include/RosBridgeClient.h \
src/comm/ros_bridge/include/com_private.h \ src/comm/ros_bridge/include/com_private.h \
...@@ -522,6 +522,9 @@ contains (DEFINES, QGC_ENABLE_PAIRING) { ...@@ -522,6 +522,9 @@ contains (DEFINES, QGC_ENABLE_PAIRING) {
} }
SOURCES += \ SOURCES += \
src/RouteMissionItem/geometry/GeoArea.cc \
src/RouteMissionItem/geometry/MeasurementArea.cc \
src/RouteMissionItem/geometry/SafeArea.cc \
src/Vehicle/VehicleEscStatusFactGroup.cc \ src/Vehicle/VehicleEscStatusFactGroup.cc \
src/RouteMissionItem/AreaData.cc \ src/RouteMissionItem/AreaData.cc \
src/api/QGCCorePlugin.cc \ src/api/QGCCorePlugin.cc \
...@@ -554,12 +557,6 @@ SOURCES += \ ...@@ -554,12 +557,6 @@ SOURCES += \
src/comm/ros_bridge/include/server.cpp \ src/comm/ros_bridge/include/server.cpp \
src/comm/ros_bridge/include/topic_publisher.cpp \ src/comm/ros_bridge/include/topic_publisher.cpp \
src/comm/ros_bridge/include/topic_subscriber.cpp \ src/comm/ros_bridge/include/topic_subscriber.cpp \
src/RouteMissionItem/geometry/WimaArea.cc \
src/RouteMissionItem/geometry/WimaServiceArea.cc \
src/RouteMissionItem/geometry/WimaMeasurementArea.cc \
src/RouteMissionItem/geometry/PlanimetryCalculus.cc \
src/RouteMissionItem/OptimisationTools.cc \
src/RouteMissionItem/geometry/PolygonCalculus.cc \
src/Settings/WimaSettings.cc \ src/Settings/WimaSettings.cc \
src/comm/ros_bridge/src/ros_bridge.cpp src/comm/ros_bridge/src/ros_bridge.cpp
......
...@@ -261,7 +261,6 @@ ...@@ -261,7 +261,6 @@
<file alias="QmlTest.qml">src/QmlControls/QmlTest.qml</file> <file alias="QmlTest.qml">src/QmlControls/QmlTest.qml</file>
<file alias="RadioComponent.qml">src/AutoPilotPlugins/Common/RadioComponent.qml</file> <file alias="RadioComponent.qml">src/AutoPilotPlugins/Common/RadioComponent.qml</file>
<file alias="SerialSettings.qml">src/ui/preferences/SerialSettings.qml</file> <file alias="SerialSettings.qml">src/ui/preferences/SerialSettings.qml</file>
<file alias="Wima/WimaAreaNoVisual.qml">src/Wima/Snake/WimaAreaNoVisual.qml</file>
<file alias="CircularGeneratorEditor.qml">src/WimaView/CircularGeneratorEditor.qml</file> <file alias="CircularGeneratorEditor.qml">src/WimaView/CircularGeneratorEditor.qml</file>
<file alias="QGroundControl/Controls/CircularGeneratorMapVisual.qml">src/WimaView/CircularGeneratorMapVisual.qml</file> <file alias="QGroundControl/Controls/CircularGeneratorMapVisual.qml">src/WimaView/CircularGeneratorMapVisual.qml</file>
<file alias="CircularSurveyItemEditor.qml">src/WimaView/CircularSurveyItemEditor.qml</file> <file alias="CircularSurveyItemEditor.qml">src/WimaView/CircularSurveyItemEditor.qml</file>
...@@ -273,21 +272,14 @@ ...@@ -273,21 +272,14 @@
<file alias="Wima/ProgressIndicator.qml">src/WimaView/ProgressIndicator.qml</file> <file alias="Wima/ProgressIndicator.qml">src/WimaView/ProgressIndicator.qml</file>
<file alias="Wima/qmldir">src/WimaView/Wima.qmldir</file> <file alias="Wima/qmldir">src/WimaView/Wima.qmldir</file>
<file alias="Wima/WimaAreaMapVisual.qml">src/WimaView/WimaAreaMapVisual.qml</file> <file alias="Wima/WimaAreaMapVisual.qml">src/WimaView/WimaAreaMapVisual.qml</file>
<file alias="Wima/WimaCorridorDataVisual.qml">src/WimaView/WimaCorridorDataVisual.qml</file>
<file alias="Wima/WimaCorridorEditor.qml">src/WimaView/WimaCorridorEditor.qml</file> <file alias="Wima/WimaCorridorEditor.qml">src/WimaView/WimaCorridorEditor.qml</file>
<file alias="Wima/WimaCorridorMapVisual.qml">src/WimaView/WimaCorridorMapVisual.qml</file>
<file alias="Wima/WimaItemEditor.qml">src/WimaView/WimaItemEditor.qml</file> <file alias="Wima/WimaItemEditor.qml">src/WimaView/WimaItemEditor.qml</file>
<file alias="Wima/WimaJoinedAreaDataVisual.qml">src/WimaView/WimaJoinedAreaDataVisual.qml</file>
<file alias="Wima/WimaJoinedAreaMapVisual.qml">src/WimaView/WimaJoinedAreaMapVisual.qml</file>
<file alias="Wima/WimaMapPolygonVisuals.qml">src/WimaView/WimaMapPolygonVisuals.qml</file> <file alias="Wima/WimaMapPolygonVisuals.qml">src/WimaView/WimaMapPolygonVisuals.qml</file>
<file alias="Wima/WimaMapPolylineVisuals.qml">src/WimaView/WimaMapPolylineVisuals.qml</file>
<file alias="Wima/WimaMapVisual.qml">src/WimaView/WimaMapVisual.qml</file> <file alias="Wima/WimaMapVisual.qml">src/WimaView/WimaMapVisual.qml</file>
<file alias="Wima/WimaMeasurementAreaDataVisual.qml">src/WimaView/WimaMeasurementAreaDataVisual.qml</file> <file alias="Wima/WimaMeasurementAreaEditor.qml">src/WimaView/MeasurementAreaEditor.qml</file>
<file alias="Wima/WimaMeasurementAreaEditor.qml">src/WimaView/WimaMeasurementAreaEditor.qml</file> <file alias="Wima/WimaMeasurementAreaMapVisual.qml">src/WimaView/MeasurementAreaMapVisual.qml</file>
<file alias="Wima/WimaMeasurementAreaMapVisual.qml">src/WimaView/WimaMeasurementAreaMapVisual.qml</file> <file alias="Wima/WimaServiceAreaEditor.qml">src/WimaView/SafeAreaEditor.qml</file>
<file alias="Wima/WimaServiceAreaDataVisual.qml">src/WimaView/WimaServiceAreaDataVisual.qml</file> <file alias="Wima/WimaServiceAreaMapVisual.qml">src/WimaView/SafeAreaMapVisual.qml</file>
<file alias="Wima/WimaServiceAreaEditor.qml">src/WimaView/WimaServiceAreaEditor.qml</file>
<file alias="Wima/WimaServiceAreaMapVisual.qml">src/WimaView/WimaServiceAreaMapVisual.qml</file>
<file alias="Wima/WimaToolBar.qml">src/WimaView/WimaToolBar.qml</file> <file alias="Wima/WimaToolBar.qml">src/WimaView/WimaToolBar.qml</file>
<file alias="SetupParameterEditor.qml">src/VehicleSetup/SetupParameterEditor.qml</file> <file alias="SetupParameterEditor.qml">src/VehicleSetup/SetupParameterEditor.qml</file>
<file alias="SetupView.qml">src/VehicleSetup/SetupView.qml</file> <file alias="SetupView.qml">src/VehicleSetup/SetupView.qml</file>
...@@ -360,9 +352,9 @@ ...@@ -360,9 +352,9 @@
<file alias="Video.SettingsGroup.json">src/Settings/Video.SettingsGroup.json</file> <file alias="Video.SettingsGroup.json">src/Settings/Video.SettingsGroup.json</file>
<file alias="VTOLLandingPattern.FactMetaData.json">src/MissionManager/VTOLLandingPattern.FactMetaData.json</file> <file alias="VTOLLandingPattern.FactMetaData.json">src/MissionManager/VTOLLandingPattern.FactMetaData.json</file>
<file alias="Wima.SettingsGroup.json">src/Settings/Wima.SettingsGroup.json</file> <file alias="Wima.SettingsGroup.json">src/Settings/Wima.SettingsGroup.json</file>
<file alias="CircularSurvey.SettingsGroup.json">src/Wima/json/CircularSurvey.SettingsGroup.json</file> <file alias="RouteComplexItem.SettingsGroup.json">src/RouteMissionItem/json/RouteComplexItem.SettingsGroup.json</file>
<file alias="LinearGenerator.SettingsGroup.json">src/Wima/Snake/json/LinearGenerator.SettingsGroup.json</file> <file alias="LinearGenerator.SettingsGroup.json">src/RouteMissionItem/json/LinearGenerator.SettingsGroup.json</file>
<file alias="CircularGenerator.SettingsGroup.json">src/Wima/Snake/json/CircularGenerator.SettingsGroup.json</file> <file alias="CircularGenerator.SettingsGroup.json">src/RouteMissionItem/json/CircularGenerator.SettingsGroup.json</file>
</qresource> </qresource>
<qresource prefix="/MockLink"> <qresource prefix="/MockLink">
<file alias="APMArduSubMockLink.params">src/comm/APMArduSubMockLink.params</file> <file alias="APMArduSubMockLink.params">src/comm/APMArduSubMockLink.params</file>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include "QmlObjectListModel.h" #include "QmlObjectListModel.h"
class SurveyComplexItem; class SurveyComplexItem;
class CircularSurvey; class RouteComplexItem;
class SimpleMissionItem; class SimpleMissionItem;
class MissionController; class MissionController;
#ifdef UNITTEST_BUILD #ifdef UNITTEST_BUILD
...@@ -153,7 +153,7 @@ private: ...@@ -153,7 +153,7 @@ private:
static const char* _jsonParam4Key; static const char* _jsonParam4Key;
friend class SurveyComplexItem; friend class SurveyComplexItem;
friend class CircularSurvey; friend class RouteComplexItem;
friend class SimpleMissionItem; friend class SimpleMissionItem;
friend class MissionController; friend class MissionController;
#ifdef UNITTEST_BUILD #ifdef UNITTEST_BUILD
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
* *
****************************************************************************/ ****************************************************************************/
/** /**
* @file * @file
* @brief Implementation of class QGCApplication * @brief Implementation of class QGCApplication
...@@ -16,19 +15,19 @@ ...@@ -16,19 +15,19 @@
* *
*/ */
#include <QAction>
#include <QDesktopWidget>
#include <QFile> #include <QFile>
#include <QFlags> #include <QFlags>
#include <QPixmap>
#include <QDesktopWidget>
#include <QPainter>
#include <QStyleFactory>
#include <QAction>
#include <QStringListModel>
#include <QRegularExpression>
#include <QFontDatabase> #include <QFontDatabase>
#include <QQuickWindow> #include <QPainter>
#include <QPixmap>
#include <QQuickImageProvider> #include <QQuickImageProvider>
#include <QQuickStyle> #include <QQuickStyle>
#include <QQuickWindow>
#include <QRegularExpression>
#include <QStringListModel>
#include <QStyleFactory>
#ifdef QGC_ENABLE_BLUETOOTH #ifdef QGC_ENABLE_BLUETOOTH
#include <QBluetoothLocalDevice> #include <QBluetoothLocalDevice>
...@@ -40,78 +39,79 @@ ...@@ -40,78 +39,79 @@
#include "GStreamer.h" #include "GStreamer.h"
#endif #endif
#include "QGC.h" #include "AutoPilotPlugin.h"
#include "QGCApplication.h"
#include "CmdLineOptParser.h" #include "CmdLineOptParser.h"
#include "UDPLink.h"
#include "LinkManager.h"
#include "UASMessageHandler.h"
#include "QGCTemporaryFile.h"
#include "QGCPalette.h"
#include "QGCMapPalette.h"
#include "QGCLoggingCategory.h"
#include "ParameterEditorController.h"
#include "ESP8266ComponentController.h" #include "ESP8266ComponentController.h"
#include "ScreenToolsController.h"
#include "QGCFileDialogController.h"
#include "RCChannelMonitorController.h"
#include "SyslinkComponentController.h"
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
#include "FirmwarePluginManager.h" #include "FirmwarePluginManager.h"
#include "MultiVehicleManager.h" #include "FlightMapSettings.h"
#include "Vehicle.h" #include "FlightPathSegment.h"
#include "JoystickConfigController.h" #include "JoystickConfigController.h"
#include "JoystickManager.h" #include "JoystickManager.h"
#include "QmlObjectListModel.h" #include "LinkManager.h"
#include "QGCGeoBoundingCube.h" #include "LogDownloadController.h"
#include "MissionManager.h" #include "MissionManager.h"
#include "QGroundControlQmlGlobal.h" #include "MultiVehicleManager.h"
#include "FlightMapSettings.h" #include "ParameterEditorController.h"
#include "FlightPathSegment.h"
#include "PlanMasterController.h" #include "PlanMasterController.h"
#include "QGC.h"
#include "QGCApplication.h"
#include "QGCFileDialogController.h"
#include "QGCGeoBoundingCube.h"
#include "QGCLoggingCategory.h"
#include "QGCMapPalette.h"
#include "QGCPalette.h"
#include "QGCTemporaryFile.h"
#include "QGroundControlQmlGlobal.h"
#include "QmlObjectListModel.h"
#include "RCChannelMonitorController.h"
#include "ScreenToolsController.h"
#include "SyslinkComponentController.h"
#include "UASMessageHandler.h"
#include "UDPLink.h"
#include "Vehicle.h"
#include "VehicleComponent.h"
#include "VideoManager.h" #include "VideoManager.h"
#include "VideoReceiver.h" #include "VideoReceiver.h"
#include "LogDownloadController.h"
#if defined(QGC_ENABLE_MAVLINK_INSPECTOR) #if defined(QGC_ENABLE_MAVLINK_INSPECTOR)
#include "MAVLinkInspectorController.h" #include "MAVLinkInspectorController.h"
#endif #endif
#include "HorizontalFactValueGrid.h"
#include "InstrumentValueData.h"
#include "AppMessages.h" #include "AppMessages.h"
#include "SimulatedPosition.h"
#include "PositionManager.h"
#include "FollowMe.h"
#include "MissionCommandTree.h"
#include "QGCMapPolygon.h"
#include "QGCMapCircle.h"
#include "ParameterManager.h"
#include "SettingsManager.h"
#include "QGCCorePlugin.h"
#include "QGCCameraManager.h"
#include "CameraCalc.h" #include "CameraCalc.h"
#include "VisualMissionItem.h"
#include "EditPositionDialogController.h" #include "EditPositionDialogController.h"
#include "FactValueSliderListModel.h" #include "FactValueSliderListModel.h"
#include "ShapeFileHelper.h"
#include "QGCFileDownload.h"
#include "FirmwareImage.h" #include "FirmwareImage.h"
#include "MavlinkConsoleController.h" #include "FollowMe.h"
#include "GeoTagController.h" #include "GeoTagController.h"
#include "HorizontalFactValueGrid.h"
#include "InstrumentValueData.h"
#include "LogReplayLink.h" #include "LogReplayLink.h"
#include "VehicleObjectAvoidance.h" #include "MavlinkConsoleController.h"
#include "TrajectoryPoints.h" #include "MissionCommandTree.h"
#include "RCToParamDialogController.h" #include "ParameterManager.h"
#include "PositionManager.h"
#include "QGCCameraManager.h"
#include "QGCCorePlugin.h"
#include "QGCFileDownload.h"
#include "QGCImageProvider.h" #include "QGCImageProvider.h"
#include "QGCMAVLink.h"
#include "QGCMapCircle.h"
#include "QGCMapPolygon.h"
#include "RCToParamDialogController.h"
#include "SettingsManager.h"
#include "ShapeFileHelper.h"
#include "SimulatedPosition.h"
#include "TerrainProfile.h" #include "TerrainProfile.h"
#include "ToolStripAction.h" #include "ToolStripAction.h"
#include "ToolStripActionList.h" #include "ToolStripActionList.h"
#include "QGCMAVLink.h" #include "TrajectoryPoints.h"
#include "VehicleLinkManager.h" #include "VehicleLinkManager.h"
#include "VehicleObjectAvoidance.h"
#include "VisualMissionItem.h"
#include "Wima/Snake/CircularGenerator.h" #include "AreaData.h"
#include "Wima/Snake/LinearGenerator.h" #include "CircularGenerator.h"
#include "Wima/Snake/NemoInterface.h" #include "LinearGenerator.h"
#include "NemoInterface.h"
#if defined(QGC_ENABLE_PAIRING) #if defined(QGC_ENABLE_PAIRING)
#include "PairingManager.h" #include "PairingManager.h"
...@@ -135,849 +135,961 @@ ...@@ -135,849 +135,961 @@
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
#ifndef __mobile__ #ifndef __mobile__
#include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h>
#endif #endif
#endif #endif
#include "QGCMapEngine.h" #include "QGCMapEngine.h"
class FinishVideoInitialization : public QRunnable class FinishVideoInitialization : public QRunnable {
{
public: public:
FinishVideoInitialization(VideoManager* manager) FinishVideoInitialization(VideoManager *manager) : _manager(manager) {}
: _manager(manager)
{}
void run () { void run() { _manager->_initVideo(); }
_manager->_initVideo();
}
private: private:
VideoManager* _manager; VideoManager *_manager;
}; };
QGCApplication *QGCApplication::_app = nullptr;
QGCApplication* QGCApplication::_app = nullptr; const char *QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
const char *QGCApplication::_settingsVersionKey = "SettingsVersion";
const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey = "SettingsVersion";
// Mavlink status structures for entire app // Mavlink status structures for entire app
mavlink_status_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS]; mavlink_status_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS];
// Qml Singleton factories // Qml Singleton factories
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*) static QObject *screenToolsControllerSingletonFactory(QQmlEngine *,
{ QJSEngine *) {
ScreenToolsController* screenToolsController = new ScreenToolsController; ScreenToolsController *screenToolsController = new ScreenToolsController;
return screenToolsController; return screenToolsController;
} }
static QObject* mavlinkSingletonFactory(QQmlEngine*, QJSEngine*) static QObject *mavlinkSingletonFactory(QQmlEngine *, QJSEngine *) {
{ return new QGCMAVLink();
return new QGCMAVLink();
} }
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*) static QObject *qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine *,
{ QJSEngine *) {
// We create this object as a QGCTool even though it isn't in the toolbox // We create this object as a QGCTool even though it isn't in the toolbox
QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp(), qgcApp()->toolbox()); QGroundControlQmlGlobal *qmlGlobal =
qmlGlobal->setToolbox(qgcApp()->toolbox()); new QGroundControlQmlGlobal(qgcApp(), qgcApp()->toolbox());
qmlGlobal->setToolbox(qgcApp()->toolbox());
return qmlGlobal; return qmlGlobal;
} }
static QObject* shapeFileHelperSingletonFactory(QQmlEngine*, QJSEngine*) static QObject *shapeFileHelperSingletonFactory(QQmlEngine *, QJSEngine *) {
{ return new ShapeFileHelper;
return new ShapeFileHelper;
} }
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting)
: QApplication (argc, argv) : QApplication(argc, argv), _runningUnitTests(unitTesting) {
, _runningUnitTests (unitTesting) _app = this;
{ _msecsElapsedTime.start();
_app = this;
_msecsElapsedTime.start();
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
#ifndef __mobile__ #ifndef __mobile__
if (!_runningUnitTests) { if (!_runningUnitTests) {
if (getuid() == 0) { if (getuid() == 0) {
_exitWithError(QString( _exitWithError(QString(
tr("You are running %1 as root. " tr("You are running %1 as root. "
"You should not do this since it will cause other issues with %1." "You should not do this since it will cause other issues with %1."
"%1 will now exit.<br/><br/>" "%1 will now exit.<br/><br/>"
"If you are having serial port issues on Ubuntu, execute the following commands to fix most issues:<br/>" "If you are having serial port issues on Ubuntu, execute the "
"<pre>sudo usermod -a -G dialout $USER<br/>" "following commands to fix most issues:<br/>"
"sudo apt-get remove modemmanager</pre>").arg(qgcApp()->applicationName()))); "<pre>sudo usermod -a -G dialout $USER<br/>"
return; "sudo apt-get remove modemmanager</pre>")
} .arg(qgcApp()->applicationName())));
// Determine if we have the correct permissions to access USB serial devices return;
QFile permFile("/etc/group"); }
if(permFile.open(QIODevice::ReadOnly)) { // Determine if we have the correct permissions to access USB serial devices
while(!permFile.atEnd()) { QFile permFile("/etc/group");
QString line = permFile.readLine(); if (permFile.open(QIODevice::ReadOnly)) {
if (line.contains("dialout") && !line.contains(getenv("USER"))) { while (!permFile.atEnd()) {
permFile.close(); QString line = permFile.readLine();
_exitWithError(QString( if (line.contains("dialout") && !line.contains(getenv("USER"))) {
tr("The current user does not have the correct permissions to access serial devices. " permFile.close();
"You should also remove modemmanager since it also interferes.<br/><br/>" _exitWithError(
"If you are using Ubuntu, execute the following commands to fix these issues:<br/>" QString(tr("The current user does not have the correct "
"<pre>sudo usermod -a -G dialout $USER<br/>" "permissions to access serial devices. "
"sudo apt-get remove modemmanager</pre>"))); "You should also remove modemmanager since it also "
return; "interferes.<br/><br/>"
} "If you are using Ubuntu, execute the following "
} "commands to fix these issues:<br/>"
permFile.close(); "<pre>sudo usermod -a -G dialout $USER<br/>"
"sudo apt-get remove modemmanager</pre>")));
return;
} }
}
// Always set style to default, this way QT_QUICK_CONTROLS_STYLE environment variable doesn't cause random changes in ui permFile.close();
QQuickStyle::setStyle("Default");
} }
// Always set style to default, this way QT_QUICK_CONTROLS_STYLE environment
// variable doesn't cause random changes in ui
QQuickStyle::setStyle("Default");
}
#endif #endif
#endif #endif
// Setup for network proxy support // Setup for network proxy support
QNetworkProxyFactory::setUseSystemConfiguration(true); QNetworkProxyFactory::setUseSystemConfiguration(true);
// Parse command line options // Parse command line options
bool fClearSettingsOptions = false; // Clear stored settings bool fClearSettingsOptions = false; // Clear stored settings
bool fClearCache = false; // Clear parameter/airframe caches bool fClearCache = false; // Clear parameter/airframe caches
bool logging = false; // Turn on logging bool logging = false; // Turn on logging
QString loggingOptions; QString loggingOptions;
CmdLineOpt_t rgCmdLineOptions[] = { CmdLineOpt_t rgCmdLineOptions[] = {
{ "--clear-settings", &fClearSettingsOptions, nullptr }, {"--clear-settings", &fClearSettingsOptions, nullptr},
{ "--clear-cache", &fClearCache, nullptr }, {"--clear-cache", &fClearCache, nullptr},
{ "--logging", &logging, &loggingOptions }, {"--logging", &logging, &loggingOptions},
{ "--fake-mobile", &_fakeMobile, nullptr }, {"--fake-mobile", &_fakeMobile, nullptr},
{ "--log-output", &_logOutput, nullptr }, {"--log-output", &_logOutput, nullptr},
// Add additional command line option flags here // Add additional command line option flags here
}; };
ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false); ParseCmdLineOptions(argc, argv, rgCmdLineOptions,
sizeof(rgCmdLineOptions) / sizeof(rgCmdLineOptions[0]),
// Set up timer for delayed missing fact display false);
_missingParamsDelayedDisplayTimer.setSingleShot(true);
_missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout); // Set up timer for delayed missing fact display
connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay); _missingParamsDelayedDisplayTimer.setSingleShot(true);
_missingParamsDelayedDisplayTimer.setInterval(
// Set application information _missingParamsDelayedDisplayTimerTimeout);
QString applicationName; connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this,
if (_runningUnitTests) { &QGCApplication::_missingParamsDisplay);
// We don't want unit tests to use the same QSettings space as the normal app. So we tweak the app
// name. Also we want to run unit tests with clean settings every time. // Set application information
applicationName = QStringLiteral("%1_unittest").arg(QGC_APPLICATION_NAME); QString applicationName;
} else { if (_runningUnitTests) {
// We don't want unit tests to use the same QSettings space as the normal
// app. So we tweak the app name. Also we want to run unit tests with clean
// settings every time.
applicationName = QStringLiteral("%1_unittest").arg(QGC_APPLICATION_NAME);
} else {
#ifdef DAILY_BUILD #ifdef DAILY_BUILD
// This gives daily builds their own separate settings space. Allowing you to use daily and stable builds // This gives daily builds their own separate settings space. Allowing you
// side by side without daily screwing up your stable settings. // to use daily and stable builds side by side without daily screwing up
applicationName = QStringLiteral("%1 Daily").arg(QGC_APPLICATION_NAME); // your stable settings.
applicationName = QStringLiteral("%1 Daily").arg(QGC_APPLICATION_NAME);
#else #else
applicationName = QGC_APPLICATION_NAME; applicationName = QGC_APPLICATION_NAME;
#endif #endif
} }
setApplicationName(applicationName); setApplicationName(applicationName);
setOrganizationName(QGC_ORG_NAME); setOrganizationName(QGC_ORG_NAME);
setOrganizationDomain(QGC_ORG_DOMAIN); setOrganizationDomain(QGC_ORG_DOMAIN);
this->setApplicationVersion(QString(GIT_VERSION)); this->setApplicationVersion(QString(GIT_VERSION));
// Set settings format // Set settings format
QSettings::setDefaultFormat(QSettings::IniFormat); QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings settings; QSettings settings;
qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable(); qDebug() << "Settings location" << settings.fileName()
<< "Is writable?:" << settings.isWritable();
#ifdef UNITTEST_BUILD #ifdef UNITTEST_BUILD
if (!settings.isWritable()) { if (!settings.isWritable()) {
qWarning() << "Setings location is not writable"; qWarning() << "Setings location is not writable";
} }
#endif #endif
// The setting will delete all settings on this boot // The setting will delete all settings on this boot
fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey); fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
if (_runningUnitTests) { if (_runningUnitTests) {
// Unit tests run with clean settings // Unit tests run with clean settings
fClearSettingsOptions = true; fClearSettingsOptions = true;
} }
if (fClearSettingsOptions) { if (fClearSettingsOptions) {
// User requested settings to be cleared on command line // User requested settings to be cleared on command line
settings.clear();
// Clear parameter cache
QDir paramDir(ParameterManager::parameterCacheDir());
paramDir.removeRecursively();
paramDir.mkpath(paramDir.absolutePath());
} else {
// Determine if upgrade message for settings version bump is required. Check
// and clear must happen before toolbox is started since that will write
// some settings.
if (settings.contains(_settingsVersionKey)) {
if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
settings.clear(); settings.clear();
_settingsUpgraded = true;
// Clear parameter cache }
QDir paramDir(ParameterManager::parameterCacheDir());
paramDir.removeRecursively();
paramDir.mkpath(paramDir.absolutePath());
} else {
// Determine if upgrade message for settings version bump is required. Check and clear must happen before toolbox is started since
// that will write some settings.
if (settings.contains(_settingsVersionKey)) {
if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
settings.clear();
_settingsUpgraded = true;
}
}
}
settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
if (fClearCache) {
QDir dir(ParameterManager::parameterCacheDir());
dir.removeRecursively();
QFile airframe(cachedAirframeMetaDataFile());
airframe.remove();
QFile parameter(cachedParameterMetaDataFile());
parameter.remove();
} }
}
settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
if (fClearCache) {
QDir dir(ParameterManager::parameterCacheDir());
dir.removeRecursively();
QFile airframe(cachedAirframeMetaDataFile());
airframe.remove();
QFile parameter(cachedParameterMetaDataFile());
parameter.remove();
}
// Set up our logging filters // Set up our logging filters
QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(loggingOptions); QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(
loggingOptions);
// Initialize Bluetooth // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH #ifdef QGC_ENABLE_BLUETOOTH
QBluetoothLocalDevice localDevice; QBluetoothLocalDevice localDevice;
if (localDevice.isValid()) if (localDevice.isValid()) {
{ _bluetoothAvailable = true;
_bluetoothAvailable = true; }
}
#endif #endif
// Gstreamer debug settings // Gstreamer debug settings
int gstDebugLevel = 0; int gstDebugLevel = 0;
if (settings.contains(AppSettings::gstDebugLevelName)) { if (settings.contains(AppSettings::gstDebugLevelName)) {
gstDebugLevel = settings.value(AppSettings::gstDebugLevelName).toInt(); gstDebugLevel = settings.value(AppSettings::gstDebugLevelName).toInt();
} }
#if defined(QGC_GST_STREAMING) #if defined(QGC_GST_STREAMING)
// Initialize Video Receiver // Initialize Video Receiver
GStreamer::initialize(argc, argv, gstDebugLevel); GStreamer::initialize(argc, argv, gstDebugLevel);
#else #else
Q_UNUSED(gstDebugLevel) Q_UNUSED(gstDebugLevel)
#endif #endif
// We need to set language as early as possible prior to loading on JSON files. // We need to set language as early as possible prior to loading on JSON
setLanguage(); // files.
setLanguage();
_toolbox = new QGCToolbox(this); _toolbox = new QGCToolbox(this);
_toolbox->setChildToolboxes(); _toolbox->setChildToolboxes();
#ifndef __mobile__ #ifndef __mobile__
_gpsRtkFactGroup = new GPSRTKFactGroup(this); _gpsRtkFactGroup = new GPSRTKFactGroup(this);
GPSManager *gpsManager = _toolbox->gpsManager(); GPSManager *gpsManager = _toolbox->gpsManager();
if (gpsManager) { if (gpsManager) {
connect(gpsManager, &GPSManager::onConnect, this, &QGCApplication::_onGPSConnect); connect(gpsManager, &GPSManager::onConnect, this,
connect(gpsManager, &GPSManager::onDisconnect, this, &QGCApplication::_onGPSDisconnect); &QGCApplication::_onGPSConnect);
connect(gpsManager, &GPSManager::surveyInStatus, this, &QGCApplication::_gpsSurveyInStatus); connect(gpsManager, &GPSManager::onDisconnect, this,
connect(gpsManager, &GPSManager::satelliteUpdate, this, &QGCApplication::_gpsNumSatellites); &QGCApplication::_onGPSDisconnect);
} connect(gpsManager, &GPSManager::surveyInStatus, this,
&QGCApplication::_gpsSurveyInStatus);
connect(gpsManager, &GPSManager::satelliteUpdate, this,
&QGCApplication::_gpsNumSatellites);
}
#endif /* __mobile__ */ #endif /* __mobile__ */
_checkForNewVersion(); _checkForNewVersion();
} }
void QGCApplication::_exitWithError(QString errorMessage) void QGCApplication::_exitWithError(QString errorMessage) {
{ _error = true;
_error = true; QQmlApplicationEngine *pEngine = new QQmlApplicationEngine(this);
QQmlApplicationEngine* pEngine = new QQmlApplicationEngine(this); pEngine->addImportPath("qrc:/qml");
pEngine->addImportPath("qrc:/qml"); pEngine->rootContext()->setContextProperty("errorMessage", errorMessage);
pEngine->rootContext()->setContextProperty("errorMessage", errorMessage); pEngine->load(QUrl(QStringLiteral("qrc:/qml/ExitWithErrorWindow.qml")));
pEngine->load(QUrl(QStringLiteral("qrc:/qml/ExitWithErrorWindow.qml"))); // Exit main application when last window is closed
// Exit main application when last window is closed connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit); }
}
void QGCApplication::setLanguage() {
void QGCApplication::setLanguage() _locale = QLocale::system();
{ qDebug() << "System reported locale:" << _locale << "; Name" << _locale.name()
_locale = QLocale::system(); << "; Preffered (used in maps): "
qDebug() << "System reported locale:" << _locale << "; Name" << _locale.name() << "; Preffered (used in maps): " << (QLocale::system().uiLanguages().length() > 0 ? QLocale::system().uiLanguages()[0] : "None"); << (QLocale::system().uiLanguages().length() > 0
? QLocale::system().uiLanguages()[0]
int langID = AppSettings::_languageID(); : "None");
//-- See App.SettinsGroup.json for index
if(langID) { int langID = AppSettings::_languageID();
switch(langID) { //-- See App.SettinsGroup.json for index
case 1: if (langID) {
_locale = QLocale(QLocale::Bulgarian); switch (langID) {
break; case 1:
case 2: _locale = QLocale(QLocale::Bulgarian);
_locale = QLocale(QLocale::Chinese); break;
break; case 2:
case 3: _locale = QLocale(QLocale::Chinese);
_locale = QLocale(QLocale::Dutch); break;
break; case 3:
case 4: _locale = QLocale(QLocale::Dutch);
_locale = QLocale(QLocale::English); break;
break; case 4:
case 5: _locale = QLocale(QLocale::English);
_locale = QLocale(QLocale::Finnish); break;
break; case 5:
case 6: _locale = QLocale(QLocale::Finnish);
_locale = QLocale(QLocale::French); break;
break; case 6:
case 7: _locale = QLocale(QLocale::French);
_locale = QLocale(QLocale::German); break;
break; case 7:
case 8: _locale = QLocale(QLocale::German);
_locale = QLocale(QLocale::Greek); break;
break; case 8:
case 9: _locale = QLocale(QLocale::Greek);
_locale = QLocale(QLocale::Hebrew); break;
break; case 9:
case 10: _locale = QLocale(QLocale::Hebrew);
_locale = QLocale(QLocale::Italian); break;
break; case 10:
case 11: _locale = QLocale(QLocale::Italian);
_locale = QLocale(QLocale::Japanese); break;
break; case 11:
case 12: _locale = QLocale(QLocale::Japanese);
_locale = QLocale(QLocale::Korean); break;
break; case 12:
case 13: _locale = QLocale(QLocale::Korean);
_locale = QLocale(QLocale::Norwegian); break;
break; case 13:
case 14: _locale = QLocale(QLocale::Norwegian);
_locale = QLocale(QLocale::Polish); break;
break; case 14:
case 15: _locale = QLocale(QLocale::Polish);
_locale = QLocale(QLocale::Portuguese); break;
break; case 15:
case 16: _locale = QLocale(QLocale::Portuguese);
_locale = QLocale(QLocale::Russian); break;
break; case 16:
case 17: _locale = QLocale(QLocale::Russian);
_locale = QLocale(QLocale::Spanish); break;
break; case 17:
case 18: _locale = QLocale(QLocale::Spanish);
_locale = QLocale(QLocale::Swedish); break;
break; case 18:
case 19: _locale = QLocale(QLocale::Swedish);
_locale = QLocale(QLocale::Turkish); break;
break; case 19:
case 20: _locale = QLocale(QLocale::Turkish);
_locale = QLocale(QLocale::Azerbaijani); break;
break; case 20:
} _locale = QLocale(QLocale::Azerbaijani);
break;
} }
//-- We have specific fonts for Korean }
if(_locale == QLocale::Korean) { //-- We have specific fonts for Korean
qCDebug(LocalizationLog) << "Loading Korean fonts" << _locale.name(); if (_locale == QLocale::Korean) {
if(QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Regular") < 0) { qCDebug(LocalizationLog) << "Loading Korean fonts" << _locale.name();
qCWarning(LocalizationLog) << "Could not load /fonts/NanumGothic-Regular font"; if (QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Regular") < 0) {
} qCWarning(LocalizationLog)
if(QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Bold") < 0) { << "Could not load /fonts/NanumGothic-Regular font";
qCWarning(LocalizationLog) << "Could not load /fonts/NanumGothic-Bold font";
}
} }
qCDebug(LocalizationLog) << "Loading localizations for" << _locale.name(); if (QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Bold") < 0) {
_app->removeTranslator(&_qgcTranslatorJSON); qCWarning(LocalizationLog)
_app->removeTranslator(&_qgcTranslatorSourceCode); << "Could not load /fonts/NanumGothic-Bold font";
_app->removeTranslator(&_qgcTranslatorQtLibs); }
if (_locale.name() != "en_US") { }
QLocale::setDefault(_locale); qCDebug(LocalizationLog) << "Loading localizations for" << _locale.name();
if(_qgcTranslatorQtLibs.load("qt_" + _locale.name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { _app->removeTranslator(&_qgcTranslatorJSON);
_app->installTranslator(&_qgcTranslatorQtLibs); _app->removeTranslator(&_qgcTranslatorSourceCode);
} else { _app->removeTranslator(&_qgcTranslatorQtLibs);
qCWarning(LocalizationLog) << "Qt lib localization for" << _locale.name() << "is not present"; if (_locale.name() != "en_US") {
} QLocale::setDefault(_locale);
if(_qgcTranslatorSourceCode.load(_locale, QLatin1String("qgc_source_"), "", ":/i18n")) { if (_qgcTranslatorQtLibs.load(
_app->installTranslator(&_qgcTranslatorSourceCode); "qt_" + _locale.name(),
} else { QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
qCWarning(LocalizationLog) << "Error loading source localization for" << _locale.name(); _app->installTranslator(&_qgcTranslatorQtLibs);
} } else {
if(_qgcTranslatorJSON.load(_locale, QLatin1String("qgc_json_"), "", ":/i18n")) { qCWarning(LocalizationLog)
_app->installTranslator(&_qgcTranslatorJSON); << "Qt lib localization for" << _locale.name() << "is not present";
} else { }
qCWarning(LocalizationLog) << "Error loading json localization for" << _locale.name(); if (_qgcTranslatorSourceCode.load(_locale, QLatin1String("qgc_source_"), "",
} ":/i18n")) {
_app->installTranslator(&_qgcTranslatorSourceCode);
} else {
qCWarning(LocalizationLog)
<< "Error loading source localization for" << _locale.name();
}
if (_qgcTranslatorJSON.load(_locale, QLatin1String("qgc_json_"), "",
":/i18n")) {
_app->installTranslator(&_qgcTranslatorJSON);
} else {
qCWarning(LocalizationLog)
<< "Error loading json localization for" << _locale.name();
} }
if(_qmlAppEngine) }
_qmlAppEngine->retranslate(); if (_qmlAppEngine)
emit languageChanged(_locale); _qmlAppEngine->retranslate();
} emit languageChanged(_locale);
}
void QGCApplication::_shutdown()
{ void QGCApplication::_shutdown() {
// Close out all Qml before we delete toolbox. This way we don't get all sorts of null reference complaints from Qml. // Close out all Qml before we delete toolbox. This way we don't get all sorts
delete _qmlAppEngine; // of null reference complaints from Qml.
delete _toolbox; delete _qmlAppEngine;
delete _gpsRtkFactGroup; delete _toolbox;
} delete _gpsRtkFactGroup;
}
QGCApplication::~QGCApplication()
{ QGCApplication::~QGCApplication() {
// Place shutdown code in _shutdown // Place shutdown code in _shutdown
_app = nullptr; _app = nullptr;
} }
void QGCApplication::_initCommon() void QGCApplication::_initCommon() {
{ static const char *kRefOnly = "Reference only";
static const char* kRefOnly = "Reference only"; static const char *kQGroundControl = "QGroundControl";
static const char* kQGroundControl = "QGroundControl"; static const char *kQGCControllers = "QGroundControl.Controllers";
static const char* kQGCControllers = "QGroundControl.Controllers"; static const char *kQGCVehicle = "QGroundControl.Vehicle";
static const char* kQGCVehicle = "QGroundControl.Vehicle"; static const char *kQGCTemplates = "QGroundControl.Templates";
static const char* kQGCTemplates = "QGroundControl.Templates";
QSettings settings;
QSettings settings;
// Register our Qml objects
// Register our Qml objects
qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
qmlRegisterType<QGCPalette> ("QGroundControl.Palette", 1, 0, "QGCPalette"); qmlRegisterType<QGCMapPalette>("QGroundControl.Palette", 1, 0,
qmlRegisterType<QGCMapPalette> ("QGroundControl.Palette", 1, 0, "QGCMapPalette"); "QGCMapPalette");
qmlRegisterUncreatableType<Vehicle> (kQGCVehicle, 1, 0, "Vehicle", kRefOnly); qmlRegisterUncreatableType<Vehicle>(kQGCVehicle, 1, 0, "Vehicle", kRefOnly);
qmlRegisterUncreatableType<MissionManager> (kQGCVehicle, 1, 0, "MissionManager", kRefOnly); qmlRegisterUncreatableType<MissionManager>(kQGCVehicle, 1, 0,
qmlRegisterUncreatableType<ParameterManager> (kQGCVehicle, 1, 0, "ParameterManager", kRefOnly); "MissionManager", kRefOnly);
qmlRegisterUncreatableType<VehicleObjectAvoidance> (kQGCVehicle, 1, 0, "VehicleObjectAvoidance", kRefOnly); qmlRegisterUncreatableType<ParameterManager>(kQGCVehicle, 1, 0,
qmlRegisterUncreatableType<QGCCameraManager> (kQGCVehicle, 1, 0, "QGCCameraManager", kRefOnly); "ParameterManager", kRefOnly);
qmlRegisterUncreatableType<QGCCameraControl> (kQGCVehicle, 1, 0, "QGCCameraControl", kRefOnly); qmlRegisterUncreatableType<VehicleObjectAvoidance>(
qmlRegisterUncreatableType<QGCVideoStreamInfo> (kQGCVehicle, 1, 0, "QGCVideoStreamInfo", kRefOnly); kQGCVehicle, 1, 0, "VehicleObjectAvoidance", kRefOnly);
qmlRegisterUncreatableType<LinkInterface> (kQGCVehicle, 1, 0, "LinkInterface", kRefOnly); qmlRegisterUncreatableType<QGCCameraManager>(kQGCVehicle, 1, 0,
qmlRegisterUncreatableType<VehicleLinkManager> (kQGCVehicle, 1, 0, "VehicleLinkManager", kRefOnly); "QGCCameraManager", kRefOnly);
qmlRegisterUncreatableType<QGCCameraControl>(kQGCVehicle, 1, 0,
qmlRegisterUncreatableType<MissionController> (kQGCControllers, 1, 0, "MissionController", kRefOnly); "QGCCameraControl", kRefOnly);
qmlRegisterUncreatableType<GeoFenceController> (kQGCControllers, 1, 0, "GeoFenceController", kRefOnly); qmlRegisterUncreatableType<QGCVideoStreamInfo>(
qmlRegisterUncreatableType<RallyPointController> (kQGCControllers, 1, 0, "RallyPointController", kRefOnly); kQGCVehicle, 1, 0, "QGCVideoStreamInfo", kRefOnly);
qmlRegisterUncreatableType<LinkInterface>(kQGCVehicle, 1, 0, "LinkInterface",
qmlRegisterUncreatableType<MissionItem> (kQGroundControl, 1, 0, "MissionItem", kRefOnly); kRefOnly);
qmlRegisterUncreatableType<VisualMissionItem> (kQGroundControl, 1, 0, "VisualMissionItem", kRefOnly); qmlRegisterUncreatableType<VehicleLinkManager>(
qmlRegisterUncreatableType<FlightPathSegment> (kQGroundControl, 1, 0, "FlightPathSegment", kRefOnly); kQGCVehicle, 1, 0, "VehicleLinkManager", kRefOnly);
qmlRegisterUncreatableType<QmlObjectListModel> (kQGroundControl, 1, 0, "QmlObjectListModel", kRefOnly);
qmlRegisterUncreatableType<MissionCommandTree> (kQGroundControl, 1, 0, "MissionCommandTree", kRefOnly); qmlRegisterUncreatableType<MissionController>(kQGCControllers, 1, 0,
qmlRegisterUncreatableType<CameraCalc> (kQGroundControl, 1, 0, "CameraCalc", kRefOnly); "MissionController", kRefOnly);
qmlRegisterUncreatableType<LogReplayLink> (kQGroundControl, 1, 0, "LogReplayLink", kRefOnly); qmlRegisterUncreatableType<GeoFenceController>(
qmlRegisterUncreatableType<InstrumentValueData> (kQGroundControl, 1, 0, "InstrumentValueData", kRefOnly); kQGCControllers, 1, 0, "GeoFenceController", kRefOnly);
qmlRegisterType<LogReplayLinkController> (kQGroundControl, 1, 0, "LogReplayLinkController"); qmlRegisterUncreatableType<RallyPointController>(
kQGCControllers, 1, 0, "RallyPointController", kRefOnly);
qmlRegisterUncreatableType<MissionItem>(kQGroundControl, 1, 0, "MissionItem",
kRefOnly);
qmlRegisterUncreatableType<VisualMissionItem>(kQGroundControl, 1, 0,
"VisualMissionItem", kRefOnly);
qmlRegisterUncreatableType<FlightPathSegment>(kQGroundControl, 1, 0,
"FlightPathSegment", kRefOnly);
qmlRegisterUncreatableType<QmlObjectListModel>(
kQGroundControl, 1, 0, "QmlObjectListModel", kRefOnly);
qmlRegisterUncreatableType<MissionCommandTree>(
kQGroundControl, 1, 0, "MissionCommandTree", kRefOnly);
qmlRegisterUncreatableType<CameraCalc>(kQGroundControl, 1, 0, "CameraCalc",
kRefOnly);
qmlRegisterUncreatableType<LogReplayLink>(kQGroundControl, 1, 0,
"LogReplayLink", kRefOnly);
qmlRegisterUncreatableType<InstrumentValueData>(
kQGroundControl, 1, 0, "InstrumentValueData", kRefOnly);
qmlRegisterType<LogReplayLinkController>(kQGroundControl, 1, 0,
"LogReplayLinkController");
#if defined(QGC_ENABLE_MAVLINK_INSPECTOR) #if defined(QGC_ENABLE_MAVLINK_INSPECTOR)
qmlRegisterUncreatableType<MAVLinkChartController> (kQGroundControl, 1, 0, "MAVLinkChart", kRefOnly); qmlRegisterUncreatableType<MAVLinkChartController>(kQGroundControl, 1, 0,
"MAVLinkChart", kRefOnly);
#endif #endif
#if defined(QGC_ENABLE_PAIRING) #if defined(QGC_ENABLE_PAIRING)
qmlRegisterUncreatableType<PairingManager> (kQGroundControl, 1, 0, "PairingManager", kRefOnly); qmlRegisterUncreatableType<PairingManager>(kQGroundControl, 1, 0,
"PairingManager", kRefOnly);
#endif #endif
qmlRegisterUncreatableType<AutoPilotPlugin> ("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", kRefOnly); qmlRegisterUncreatableType<AutoPilotPlugin>(
qmlRegisterUncreatableType<VehicleComponent> ("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", kRefOnly); "QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", kRefOnly);
qmlRegisterUncreatableType<JoystickManager> ("QGroundControl.JoystickManager", 1, 0, "JoystickManager", kRefOnly); qmlRegisterUncreatableType<VehicleComponent>(
qmlRegisterUncreatableType<Joystick> ("QGroundControl.JoystickManager", 1, 0, "Joystick", kRefOnly); "QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", kRefOnly);
qmlRegisterUncreatableType<QGCPositionManager> ("QGroundControl.QGCPositionManager", 1, 0, "QGCPositionManager", kRefOnly); qmlRegisterUncreatableType<JoystickManager>(
qmlRegisterUncreatableType<FactValueSliderListModel>("QGroundControl.FactControls", 1, 0, "FactValueSliderListModel", kRefOnly); "QGroundControl.JoystickManager", 1, 0, "JoystickManager", kRefOnly);
qmlRegisterUncreatableType<Joystick>("QGroundControl.JoystickManager", 1, 0,
qmlRegisterUncreatableType<QGCMapPolygon> ("QGroundControl.FlightMap", 1, 0, "QGCMapPolygon", kRefOnly); "Joystick", kRefOnly);
qmlRegisterUncreatableType<QGCGeoBoundingCube> ("QGroundControl.FlightMap", 1, 0, "QGCGeoBoundingCube", kRefOnly); qmlRegisterUncreatableType<QGCPositionManager>(
qmlRegisterUncreatableType<TrajectoryPoints> ("QGroundControl.FlightMap", 1, 0, "TrajectoryPoints", kRefOnly); "QGroundControl.QGCPositionManager", 1, 0, "QGCPositionManager",
kRefOnly);
qmlRegisterUncreatableType<FactValueGrid> (kQGCTemplates, 1, 0, "FactValueGrid", kRefOnly); qmlRegisterUncreatableType<FactValueSliderListModel>(
qmlRegisterType<HorizontalFactValueGrid> (kQGCTemplates, 1, 0, "HorizontalFactValueGrid"); "QGroundControl.FactControls", 1, 0, "FactValueSliderListModel",
kRefOnly);
qmlRegisterType<QGCMapCircle> ("QGroundControl.FlightMap", 1, 0, "QGCMapCircle");
qmlRegisterUncreatableType<QGCMapPolygon>("QGroundControl.FlightMap", 1, 0,
qmlRegisterType<ParameterEditorController> (kQGCControllers, 1, 0, "ParameterEditorController"); "QGCMapPolygon", kRefOnly);
qmlRegisterType<ESP8266ComponentController> (kQGCControllers, 1, 0, "ESP8266ComponentController"); qmlRegisterUncreatableType<QGCGeoBoundingCube>(
qmlRegisterType<ScreenToolsController> (kQGCControllers, 1, 0, "ScreenToolsController"); "QGroundControl.FlightMap", 1, 0, "QGCGeoBoundingCube", kRefOnly);
qmlRegisterType<PlanMasterController> (kQGCControllers, 1, 0, "PlanMasterController"); qmlRegisterUncreatableType<TrajectoryPoints>("QGroundControl.FlightMap", 1, 0,
qmlRegisterType<QGCFileDialogController> (kQGCControllers, 1, 0, "QGCFileDialogController"); "TrajectoryPoints", kRefOnly);
qmlRegisterType<RCChannelMonitorController> (kQGCControllers, 1, 0, "RCChannelMonitorController");
qmlRegisterType<JoystickConfigController> (kQGCControllers, 1, 0, "JoystickConfigController"); qmlRegisterUncreatableType<FactValueGrid>(kQGCTemplates, 1, 0,
qmlRegisterType<LogDownloadController> (kQGCControllers, 1, 0, "LogDownloadController"); "FactValueGrid", kRefOnly);
qmlRegisterType<SyslinkComponentController> (kQGCControllers, 1, 0, "SyslinkComponentController"); qmlRegisterType<HorizontalFactValueGrid>(kQGCTemplates, 1, 0,
qmlRegisterType<EditPositionDialogController> (kQGCControllers, 1, 0, "EditPositionDialogController"); "HorizontalFactValueGrid");
qmlRegisterType<RCToParamDialogController> (kQGCControllers, 1, 0, "RCToParamDialogController");
qmlRegisterType<QGCMapCircle>("QGroundControl.FlightMap", 1, 0,
qmlRegisterType<TerrainProfile> ("QGroundControl.Controls", 1, 0, "TerrainProfile"); "QGCMapCircle");
qmlRegisterType<ToolStripAction> ("QGroundControl.Controls", 1, 0, "ToolStripAction");
qmlRegisterType<ToolStripActionList> ("QGroundControl.Controls", 1, 0, "ToolStripActionList"); qmlRegisterType<ParameterEditorController>(kQGCControllers, 1, 0,
"ParameterEditorController");
qmlRegisterType<ESP8266ComponentController>(kQGCControllers, 1, 0,
"ESP8266ComponentController");
qmlRegisterType<ScreenToolsController>(kQGCControllers, 1, 0,
"ScreenToolsController");
qmlRegisterType<PlanMasterController>(kQGCControllers, 1, 0,
"PlanMasterController");
qmlRegisterType<QGCFileDialogController>(kQGCControllers, 1, 0,
"QGCFileDialogController");
qmlRegisterType<RCChannelMonitorController>(kQGCControllers, 1, 0,
"RCChannelMonitorController");
qmlRegisterType<JoystickConfigController>(kQGCControllers, 1, 0,
"JoystickConfigController");
qmlRegisterType<LogDownloadController>(kQGCControllers, 1, 0,
"LogDownloadController");
qmlRegisterType<SyslinkComponentController>(kQGCControllers, 1, 0,
"SyslinkComponentController");
qmlRegisterType<EditPositionDialogController>(kQGCControllers, 1, 0,
"EditPositionDialogController");
qmlRegisterType<RCToParamDialogController>(kQGCControllers, 1, 0,
"RCToParamDialogController");
qmlRegisterType<TerrainProfile>("QGroundControl.Controls", 1, 0,
"TerrainProfile");
qmlRegisterType<ToolStripAction>("QGroundControl.Controls", 1, 0,
"ToolStripAction");
qmlRegisterType<ToolStripActionList>("QGroundControl.Controls", 1, 0,
"ToolStripActionList");
#ifndef __mobile__ #ifndef __mobile__
#ifndef NO_SERIAL_LINK #ifndef NO_SERIAL_LINK
qmlRegisterType<FirmwareUpgradeController> (kQGCControllers, 1, 0, "FirmwareUpgradeController"); qmlRegisterType<FirmwareUpgradeController>(kQGCControllers, 1, 0,
"FirmwareUpgradeController");
#endif #endif
#endif #endif
// Wima qmlRegisterUncreatableType<routing::LinearGenerator>(
qmlRegisterType<NemoInterface>("Wima", 1, 0, "NemoInterface"); "RouteComplexItem", 1, 0, "LinearGenerator", kRefOnly);
qmlRegisterType<AreaData>("RouteComplexItem", 1, 0, "AreaData");
qmlRegisterUncreatableType<NemoInterface>("RouteComplexItem", 1, 0,
"NemoInterface", kRefOnly);
qmlRegisterInterface<routing::GeneratorBase>("GeneratorBase"); qmlRegisterInterface<routing::GeneratorBase>("GeneratorBase");
qmlRegisterType<routing::CircularGenerator>("Wima", 1, 0, qmlRegisterUncreatableType<routing::CircularGenerator>(
"CircularGenerator"); "RouteComplexItem", 1, 0, "CircularGenerator", kRefOnly);
qmlRegisterType<routing::LinearGenerator>("Wima", 1, 0, "LinearGenerator");
qmlRegisterType<GeoTagController> (kQGCControllers, 1, 0, "GeoTagController"); qmlRegisterType<GeoTagController>(kQGCControllers, 1, 0, "GeoTagController");
qmlRegisterType<MavlinkConsoleController> (kQGCControllers, 1, 0, "MavlinkConsoleController"); qmlRegisterType<MavlinkConsoleController>(kQGCControllers, 1, 0,
"MavlinkConsoleController");
#if defined(QGC_ENABLE_MAVLINK_INSPECTOR) #if defined(QGC_ENABLE_MAVLINK_INSPECTOR)
qmlRegisterType<MAVLinkInspectorController> (kQGCControllers, 1, 0, "MAVLinkInspectorController"); qmlRegisterType<MAVLinkInspectorController>(kQGCControllers, 1, 0,
"MAVLinkInspectorController");
#endif #endif
// Register Qml Singletons // Register Qml Singletons
qmlRegisterSingletonType<QGroundControlQmlGlobal> ("QGroundControl", 1, 0, "QGroundControl", qgroundcontrolQmlGlobalSingletonFactory); qmlRegisterSingletonType<QGroundControlQmlGlobal>(
qmlRegisterSingletonType<ScreenToolsController> ("QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController", screenToolsControllerSingletonFactory); "QGroundControl", 1, 0, "QGroundControl",
qmlRegisterSingletonType<ShapeFileHelper> ("QGroundControl.ShapeFileHelper", 1, 0, "ShapeFileHelper", shapeFileHelperSingletonFactory); qgroundcontrolQmlGlobalSingletonFactory);
qmlRegisterSingletonType<ShapeFileHelper> ("MAVLink", 1, 0, "MAVLink", mavlinkSingletonFactory); qmlRegisterSingletonType<ScreenToolsController>(
"QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController",
// Although this should really be in _initForNormalAppBoot putting it here allowws us to create unit tests which pop up more easily screenToolsControllerSingletonFactory);
if(QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) { qmlRegisterSingletonType<ShapeFileHelper>("QGroundControl.ShapeFileHelper", 1,
qWarning() << "Could not load /fonts/opensans font"; 0, "ShapeFileHelper",
} shapeFileHelperSingletonFactory);
if(QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) { qmlRegisterSingletonType<ShapeFileHelper>("MAVLink", 1, 0, "MAVLink",
qWarning() << "Could not load /fonts/opensans-demibold font"; mavlinkSingletonFactory);
}
// Although this should really be in _initForNormalAppBoot putting it here
// allowws us to create unit tests which pop up more easily
if (QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) {
qWarning() << "Could not load /fonts/opensans font";
}
if (QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) {
qWarning() << "Could not load /fonts/opensans-demibold font";
}
} }
bool QGCApplication::_initForNormalAppBoot() bool QGCApplication::_initForNormalAppBoot() {
{ QSettings settings;
QSettings settings;
_qmlAppEngine = toolbox()->corePlugin()->createQmlApplicationEngine(this); _qmlAppEngine = toolbox()->corePlugin()->createQmlApplicationEngine(this);
toolbox()->corePlugin()->createRootWindow(_qmlAppEngine); toolbox()->corePlugin()->createRootWindow(_qmlAppEngine);
// Image provider for PX4 Flow // Image provider for PX4 Flow
QQuickImageProvider* pImgProvider = dynamic_cast<QQuickImageProvider*>(qgcApp()->toolbox()->imageProvider()); QQuickImageProvider *pImgProvider =
_qmlAppEngine->addImageProvider(QStringLiteral("QGCImages"), pImgProvider); dynamic_cast<QQuickImageProvider *>(qgcApp()->toolbox()->imageProvider());
_qmlAppEngine->addImageProvider(QStringLiteral("QGCImages"), pImgProvider);
QQuickWindow* rootWindow = (QQuickWindow*)qgcApp()->mainRootWindow(); QQuickWindow *rootWindow = (QQuickWindow *)qgcApp()->mainRootWindow();
if (rootWindow) { if (rootWindow) {
rootWindow->scheduleRenderJob (new FinishVideoInitialization (toolbox()->videoManager()), rootWindow->scheduleRenderJob(
QQuickWindow::BeforeSynchronizingStage); new FinishVideoInitialization(toolbox()->videoManager()),
} QQuickWindow::BeforeSynchronizingStage);
}
// Safe to show popup error messages now that main window is created // Safe to show popup error messages now that main window is created
UASMessageHandler* msgHandler = qgcApp()->toolbox()->uasMessageHandler(); UASMessageHandler *msgHandler = qgcApp()->toolbox()->uasMessageHandler();
if (msgHandler) { if (msgHandler) {
msgHandler->showErrorsInToolbar(); msgHandler->showErrorsInToolbar();
} }
// Now that main window is up check for lost log files // Now that main window is up check for lost log files
connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles); connect(this, &QGCApplication::checkForLostLogFiles,
emit checkForLostLogFiles(); toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
emit checkForLostLogFiles();
// Load known link configurations // Load known link configurations
toolbox()->linkManager()->loadLinkConfigurationList(); toolbox()->linkManager()->loadLinkConfigurationList();
// Probe for joysticks // Probe for joysticks
toolbox()->joystickManager()->init(); toolbox()->joystickManager()->init();
if (_settingsUpgraded) { if (_settingsUpgraded) {
showAppMessage(QString(tr("The format for %1 saved settings has been modified. " showAppMessage(
"Your saved settings have been reset to defaults.")).arg(applicationName())); QString(tr("The format for %1 saved settings has been modified. "
} "Your saved settings have been reset to defaults."))
.arg(applicationName()));
}
// Connect links with flag AutoconnectLink // Connect links with flag AutoconnectLink
toolbox()->linkManager()->startAutoConnectedLinks(); toolbox()->linkManager()->startAutoConnectedLinks();
if (getQGCMapEngine()->wasCacheReset()) { if (getQGCMapEngine()->wasCacheReset()) {
showAppMessage(tr("The Offline Map Cache database has been upgraded. " showAppMessage(tr("The Offline Map Cache database has been upgraded. "
"Your old map cache sets have been reset.")); "Your old map cache sets have been reset."));
} }
settings.sync(); settings.sync();
return true; return true;
} }
bool QGCApplication::_initForUnitTests() bool QGCApplication::_initForUnitTests() { return true; }
{
return true;
}
void QGCApplication::deleteAllSettingsNextBoot(void) void QGCApplication::deleteAllSettingsNextBoot(void) {
{ QSettings settings;
QSettings settings; settings.setValue(_deleteAllSettingsKey, true);
settings.setValue(_deleteAllSettingsKey, true);
} }
void QGCApplication::clearDeleteAllSettingsNextBoot(void) void QGCApplication::clearDeleteAllSettingsNextBoot(void) {
{ QSettings settings;
QSettings settings; settings.remove(_deleteAllSettingsKey);
settings.remove(_deleteAllSettingsKey);
} }
/// @brief Returns the QGCApplication object singleton. /// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void) QGCApplication *qgcApp(void) { return QGCApplication::_app; }
{
return QGCApplication::_app;
}
void QGCApplication::informationMessageBoxOnMainThread(const QString& /*title*/, const QString& msg) void QGCApplication::informationMessageBoxOnMainThread(
{ const QString & /*title*/, const QString &msg) {
showAppMessage(msg); showAppMessage(msg);
} }
void QGCApplication::warningMessageBoxOnMainThread(const QString& /*title*/, const QString& msg) void QGCApplication::warningMessageBoxOnMainThread(const QString & /*title*/,
{ const QString &msg) {
showAppMessage(msg); showAppMessage(msg);
} }
void QGCApplication::criticalMessageBoxOnMainThread(const QString& /*title*/, const QString& msg) void QGCApplication::criticalMessageBoxOnMainThread(const QString & /*title*/,
{ const QString &msg) {
showAppMessage(msg); showAppMessage(msg);
} }
void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile) void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile) {
{ // The vehicle is gone now and we are shutting down so we need to use a
// The vehicle is gone now and we are shutting down so we need to use a message box for errors to hold shutdown and show the error // message box for errors to hold shutdown and show the error
if (_checkTelemetrySavePath(true /* useMessageBox */)) { if (_checkTelemetrySavePath(true /* useMessageBox */)) {
QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath();
QDir saveDir(saveDirPath);
QString nameFormat("%1%2.%3"); QString saveDirPath =
QString dtFormat("yyyy-MM-dd hh-mm-ss"); _toolbox->settingsManager()->appSettings()->telemetrySavePath();
QDir saveDir(saveDirPath);
int tryIndex = 1;
QString saveFileName = nameFormat.arg(
QDateTime::currentDateTime().toString(dtFormat)).arg(QStringLiteral("")).arg(toolbox()->settingsManager()->appSettings()->telemetryFileExtension);
while (saveDir.exists(saveFileName)) {
saveFileName = nameFormat.arg(
QDateTime::currentDateTime().toString(dtFormat)).arg(QStringLiteral(".%1").arg(tryIndex++)).arg(toolbox()->settingsManager()->appSettings()->telemetryFileExtension);
}
QString saveFilePath = saveDir.absoluteFilePath(saveFileName);
QFile tempFile(tempLogfile); QString nameFormat("%1%2.%3");
if (!tempFile.copy(saveFilePath)) { QString dtFormat("yyyy-MM-dd hh-mm-ss");
QString error = tr("Unable to save telemetry log. Error copying telemetry to '%1': '%2'.").arg(saveFilePath).arg(tempFile.errorString());
showAppMessage(error); int tryIndex = 1;
} QString saveFileName =
nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat))
.arg(QStringLiteral(""))
.arg(toolbox()
->settingsManager()
->appSettings()
->telemetryFileExtension);
while (saveDir.exists(saveFileName)) {
saveFileName =
nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat))
.arg(QStringLiteral(".%1").arg(tryIndex++))
.arg(toolbox()
->settingsManager()
->appSettings()
->telemetryFileExtension);
} }
QFile::remove(tempLogfile); QString saveFilePath = saveDir.absoluteFilePath(saveFileName);
QFile tempFile(tempLogfile);
if (!tempFile.copy(saveFilePath)) {
QString error = tr("Unable to save telemetry log. Error copying "
"telemetry to '%1': '%2'.")
.arg(saveFilePath)
.arg(tempFile.errorString());
showAppMessage(error);
}
}
QFile::remove(tempLogfile);
} }
void QGCApplication::checkTelemetrySavePathOnMainThread() void QGCApplication::checkTelemetrySavePathOnMainThread() {
{ // This is called with an active vehicle so don't pop message boxes which
// This is called with an active vehicle so don't pop message boxes which holds ui thread // holds ui thread
_checkTelemetrySavePath(false /* useMessageBox */); _checkTelemetrySavePath(false /* useMessageBox */);
} }
bool QGCApplication::_checkTelemetrySavePath(bool /*useMessageBox*/) bool QGCApplication::_checkTelemetrySavePath(bool /*useMessageBox*/) {
{ QString saveDirPath =
QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath(); _toolbox->settingsManager()->appSettings()->telemetrySavePath();
if (saveDirPath.isEmpty()) { if (saveDirPath.isEmpty()) {
QString error = tr("Unable to save telemetry log. Application save directory is not set."); QString error = tr(
showAppMessage(error); "Unable to save telemetry log. Application save directory is not set.");
return false; showAppMessage(error);
} return false;
}
QDir saveDir(saveDirPath); QDir saveDir(saveDirPath);
if (!saveDir.exists()) { if (!saveDir.exists()) {
QString error = tr("Unable to save telemetry log. Telemetry save directory \"%1\" does not exist.").arg(saveDirPath); QString error = tr("Unable to save telemetry log. Telemetry save directory "
showAppMessage(error); "\"%1\" does not exist.")
return false; .arg(saveDirPath);
} showAppMessage(error);
return false;
}
return true; return true;
} }
void QGCApplication::reportMissingParameter(int componentId, const QString& name) void QGCApplication::reportMissingParameter(int componentId,
{ const QString &name) {
QPair<int, QString> missingParam(componentId, name); QPair<int, QString> missingParam(componentId, name);
if (!_missingParams.contains(missingParam)) { if (!_missingParams.contains(missingParam)) {
_missingParams.append(missingParam); _missingParams.append(missingParam);
} }
_missingParamsDelayedDisplayTimer.start(); _missingParamsDelayedDisplayTimer.start();
} }
/// Called when the delay timer fires to show the missing parameters warning /// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void) void QGCApplication::_missingParamsDisplay(void) {
{ if (_missingParams.count()) {
if (_missingParams.count()) { QString params;
QString params; for (QPair<int, QString> &missingParam : _missingParams) {
for (QPair<int, QString>& missingParam: _missingParams) { QString param = QStringLiteral("%1:%2")
QString param = QStringLiteral("%1:%2").arg(missingParam.first).arg(missingParam.second); .arg(missingParam.first)
if (params.isEmpty()) { .arg(missingParam.second);
params += param; if (params.isEmpty()) {
} else { params += param;
params += QStringLiteral(", %1").arg(param); } else {
} params += QStringLiteral(", %1").arg(param);
}
}
_missingParams.clear();
showAppMessage(tr("Parameters are missing from firmware. You may be running a version of firmware which is not fully supported or your firmware has a bug in it. Missing params: %1").arg(params));
} }
} _missingParams.clear();
QObject* QGCApplication::_rootQmlObject() showAppMessage(
{ tr("Parameters are missing from firmware. You may be running a version "
if (_qmlAppEngine && _qmlAppEngine->rootObjects().size()) "of firmware which is not fully supported or your firmware has a "
return _qmlAppEngine->rootObjects()[0]; "bug in it. Missing params: %1")
return nullptr; .arg(params));
}
} }
void QGCApplication::showCriticalVehicleMessage(const QString& message) QObject *QGCApplication::_rootQmlObject() {
{ if (_qmlAppEngine && _qmlAppEngine->rootObjects().size())
// PreArm messages are handled by Vehicle and shown in Map return _qmlAppEngine->rootObjects()[0];
if (message.startsWith(QStringLiteral("PreArm")) || message.startsWith(QStringLiteral("preflight"), Qt::CaseInsensitive)) { return nullptr;
return;
}
QObject* rootQmlObject = _rootQmlObject();
if (rootQmlObject) {
QVariant varReturn;
QVariant varMessage = QVariant::fromValue(message);
QMetaObject::invokeMethod(_rootQmlObject(), "showCriticalVehicleMessage", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varMessage));
} else if (runningUnitTests()) {
// Unit tests can run without UI
qDebug() << "QGCApplication::showCriticalVehicleMessage unittest" << message;
} else {
qWarning() << "Internal error";
}
} }
void QGCApplication::showAppMessage(const QString& message, const QString& title) void QGCApplication::showCriticalVehicleMessage(const QString &message) {
{ // PreArm messages are handled by Vehicle and shown in Map
QString dialogTitle = title.isEmpty() ? applicationName() : title; if (message.startsWith(QStringLiteral("PreArm")) ||
message.startsWith(QStringLiteral("preflight"), Qt::CaseInsensitive)) {
return;
}
QObject *rootQmlObject = _rootQmlObject();
if (rootQmlObject) {
QVariant varReturn;
QVariant varMessage = QVariant::fromValue(message);
QMetaObject::invokeMethod(_rootQmlObject(), "showCriticalVehicleMessage",
Q_RETURN_ARG(QVariant, varReturn),
Q_ARG(QVariant, varMessage));
} else if (runningUnitTests()) {
// Unit tests can run without UI
qDebug() << "QGCApplication::showCriticalVehicleMessage unittest"
<< message;
} else {
qWarning() << "Internal error";
}
}
QObject* rootQmlObject = _rootQmlObject(); void QGCApplication::showAppMessage(const QString &message,
if (rootQmlObject) { const QString &title) {
QVariant varReturn; QString dialogTitle = title.isEmpty() ? applicationName() : title;
QVariant varMessage = QVariant::fromValue(message);
QMetaObject::invokeMethod(_rootQmlObject(), "showMessageDialog", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, dialogTitle), Q_ARG(QVariant, varMessage)); QObject *rootQmlObject = _rootQmlObject();
} else if (runningUnitTests()) { if (rootQmlObject) {
// Unit tests can run without UI QVariant varReturn;
qDebug() << "QGCApplication::showAppMessage unittest title:message" << dialogTitle << message; QVariant varMessage = QVariant::fromValue(message);
} else { QMetaObject::invokeMethod(_rootQmlObject(), "showMessageDialog",
// UI isn't ready yet Q_RETURN_ARG(QVariant, varReturn),
_delayedAppMessages.append(QPair<QString, QString>(dialogTitle, message)); Q_ARG(QVariant, dialogTitle),
QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages); Q_ARG(QVariant, varMessage));
} } else if (runningUnitTests()) {
// Unit tests can run without UI
qDebug() << "QGCApplication::showAppMessage unittest title:message"
<< dialogTitle << message;
} else {
// UI isn't ready yet
_delayedAppMessages.append(QPair<QString, QString>(dialogTitle, message));
QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages);
}
} }
void QGCApplication::_showDelayedAppMessages(void) void QGCApplication::_showDelayedAppMessages(void) {
{ if (_rootQmlObject()) {
if (_rootQmlObject()) { for (const QPair<QString, QString> &appMsg : _delayedAppMessages) {
for (const QPair<QString, QString>& appMsg: _delayedAppMessages) { showAppMessage(appMsg.second, appMsg.first);
showAppMessage(appMsg.second, appMsg.first);
}
_delayedAppMessages.clear();
} else {
QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages);
} }
_delayedAppMessages.clear();
} else {
QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages);
}
} }
QQuickItem* QGCApplication::mainRootWindow() QQuickItem *QGCApplication::mainRootWindow() {
{ if (!_mainRootWindow) {
if(!_mainRootWindow) { _mainRootWindow = reinterpret_cast<QQuickItem *>(_rootQmlObject());
_mainRootWindow = reinterpret_cast<QQuickItem*>(_rootQmlObject()); }
} return _mainRootWindow;
return _mainRootWindow;
} }
void QGCApplication::showSetupView() void QGCApplication::showSetupView() {
{ if (_rootQmlObject()) {
if(_rootQmlObject()) { QMetaObject::invokeMethod(_rootQmlObject(), "showSetupTool");
QMetaObject::invokeMethod(_rootQmlObject(), "showSetupTool"); }
}
} }
void QGCApplication::qmlAttemptWindowClose() void QGCApplication::qmlAttemptWindowClose() {
{ if (_rootQmlObject()) {
if(_rootQmlObject()) { QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose"); }
}
} }
bool QGCApplication::isInternetAvailable() bool QGCApplication::isInternetAvailable() {
{ if (_toolbox->settingsManager()
if(_toolbox->settingsManager()->appSettings()->checkInternet()->rawValue().toBool()) ->appSettings()
return getQGCMapEngine()->isInternetActive(); ->checkInternet()
return true; ->rawValue()
.toBool())
return getQGCMapEngine()->isInternetActive();
return true;
} }
void QGCApplication::_checkForNewVersion() void QGCApplication::_checkForNewVersion() {
{
#ifndef __mobile__ #ifndef __mobile__
if (!_runningUnitTests) { if (!_runningUnitTests) {
if (_parseVersionText(applicationVersion(), _majorVersion, _minorVersion, _buildVersion)) { if (_parseVersionText(applicationVersion(), _majorVersion, _minorVersion,
QString versionCheckFile = toolbox()->corePlugin()->stableVersionCheckFileUrl(); _buildVersion)) {
if (!versionCheckFile.isEmpty()) { QString versionCheckFile =
QGCFileDownload* download = new QGCFileDownload(this); toolbox()->corePlugin()->stableVersionCheckFileUrl();
connect(download, &QGCFileDownload::downloadComplete, this, &QGCApplication::_qgcCurrentStableVersionDownloadComplete); if (!versionCheckFile.isEmpty()) {
download->download(versionCheckFile); QGCFileDownload *download = new QGCFileDownload(this);
} connect(download, &QGCFileDownload::downloadComplete, this,
} &QGCApplication::_qgcCurrentStableVersionDownloadComplete);
download->download(versionCheckFile);
}
} }
}
#endif #endif
} }
void QGCApplication::_qgcCurrentStableVersionDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg) void QGCApplication::_qgcCurrentStableVersionDownloadComplete(
{ QString /*remoteFile*/, QString localFile, QString errorMsg) {
#ifdef __mobile__ #ifdef __mobile__
Q_UNUSED(localFile) Q_UNUSED(localFile)
Q_UNUSED(errorMsg) Q_UNUSED(errorMsg)
#else #else
if (errorMsg.isEmpty()) { if (errorMsg.isEmpty()) {
QFile versionFile(localFile); QFile versionFile(localFile);
if (versionFile.open(QIODevice::ReadOnly)) { if (versionFile.open(QIODevice::ReadOnly)) {
QTextStream textStream(&versionFile); QTextStream textStream(&versionFile);
QString version = textStream.readLine(); QString version = textStream.readLine();
qDebug() << version; qDebug() << version;
int majorVersion, minorVersion, buildVersion; int majorVersion, minorVersion, buildVersion;
if (_parseVersionText(version, majorVersion, minorVersion, buildVersion)) { if (_parseVersionText(version, majorVersion, minorVersion,
if (_majorVersion < majorVersion || buildVersion)) {
(_majorVersion == majorVersion && _minorVersion < minorVersion) || if (_majorVersion < majorVersion ||
(_majorVersion == majorVersion && _minorVersion == minorVersion && _buildVersion < buildVersion)) { (_majorVersion == majorVersion && _minorVersion < minorVersion) ||
showAppMessage(tr("There is a newer version of %1 available. You can download it from %2.").arg(applicationName()).arg(toolbox()->corePlugin()->stableDownloadLocation()), tr("New Version Available")); (_majorVersion == majorVersion && _minorVersion == minorVersion &&
} _buildVersion < buildVersion)) {
} showAppMessage(
tr("There is a newer version of %1 available. You can download "
"it from %2.")
.arg(applicationName())
.arg(toolbox()->corePlugin()->stableDownloadLocation()),
tr("New Version Available"));
} }
} else { }
qDebug() << "Download QGC stable version failed" << errorMsg;
} }
} else {
qDebug() << "Download QGC stable version failed" << errorMsg;
}
sender()->deleteLater(); sender()->deleteLater();
#endif #endif
} }
bool QGCApplication::_parseVersionText(const QString& versionString, int& majorVersion, int& minorVersion, int& buildVersion) bool QGCApplication::_parseVersionText(const QString &versionString,
{ int &majorVersion, int &minorVersion,
QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)"); int &buildVersion) {
QRegularExpressionMatch match = regExp.match(versionString); QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)");
if (match.hasMatch() && match.lastCapturedIndex() == 3) { QRegularExpressionMatch match = regExp.match(versionString);
majorVersion = match.captured(1).toInt(); if (match.hasMatch() && match.lastCapturedIndex() == 3) {
minorVersion = match.captured(2).toInt(); majorVersion = match.captured(1).toInt();
buildVersion = match.captured(3).toInt(); minorVersion = match.captured(2).toInt();
return true; buildVersion = match.captured(3).toInt();
} return true;
}
return false; return false;
} }
void QGCApplication::_onGPSConnect() {
void QGCApplication::_onGPSConnect() _gpsRtkFactGroup->connected()->setRawValue(true);
{
_gpsRtkFactGroup->connected()->setRawValue(true);
} }
void QGCApplication::_onGPSDisconnect() void QGCApplication::_onGPSDisconnect() {
{ _gpsRtkFactGroup->connected()->setRawValue(false);
_gpsRtkFactGroup->connected()->setRawValue(false);
} }
void QGCApplication::_gpsSurveyInStatus(float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active) void QGCApplication::_gpsSurveyInStatus(float duration, float accuracyMM,
{ double latitude, double longitude,
_gpsRtkFactGroup->currentDuration()->setRawValue(duration); float altitude, bool valid,
_gpsRtkFactGroup->currentAccuracy()->setRawValue(static_cast<double>(accuracyMM) / 1000.0); bool active) {
_gpsRtkFactGroup->currentLatitude()->setRawValue(latitude); _gpsRtkFactGroup->currentDuration()->setRawValue(duration);
_gpsRtkFactGroup->currentLongitude()->setRawValue(longitude); _gpsRtkFactGroup->currentAccuracy()->setRawValue(
_gpsRtkFactGroup->currentAltitude()->setRawValue(altitude); static_cast<double>(accuracyMM) / 1000.0);
_gpsRtkFactGroup->valid()->setRawValue(valid); _gpsRtkFactGroup->currentLatitude()->setRawValue(latitude);
_gpsRtkFactGroup->active()->setRawValue(active); _gpsRtkFactGroup->currentLongitude()->setRawValue(longitude);
_gpsRtkFactGroup->currentAltitude()->setRawValue(altitude);
_gpsRtkFactGroup->valid()->setRawValue(valid);
_gpsRtkFactGroup->active()->setRawValue(active);
} }
void QGCApplication::_gpsNumSatellites(int numSatellites) void QGCApplication::_gpsNumSatellites(int numSatellites) {
{ _gpsRtkFactGroup->numSatellites()->setRawValue(numSatellites);
_gpsRtkFactGroup->numSatellites()->setRawValue(numSatellites);
} }
QString QGCApplication::cachedParameterMetaDataFile(void) QString QGCApplication::cachedParameterMetaDataFile(void) {
{ QSettings settings;
QSettings settings; QDir parameterDir = QFileInfo(settings.fileName()).dir();
QDir parameterDir = QFileInfo(settings.fileName()).dir(); return parameterDir.filePath(QStringLiteral("ParameterFactMetaData.xml"));
return parameterDir.filePath(QStringLiteral("ParameterFactMetaData.xml"));
} }
QString QGCApplication::cachedAirframeMetaDataFile(void) QString QGCApplication::cachedAirframeMetaDataFile(void) {
{ QSettings settings;
QSettings settings; QDir airframeDir = QFileInfo(settings.fileName()).dir();
QDir airframeDir = QFileInfo(settings.fileName()).dir(); return airframeDir.filePath(QStringLiteral("PX4AirframeFactMetaData.xml"));
return airframeDir.filePath(QStringLiteral("PX4AirframeFactMetaData.xml"));
} }
#include "WimaPlanData.h" #include "AreaData.h"
AreaData::AreaData(QObject *parent) : QObject(parent) {} #include "geometry/MeasurementArea.h"
#include "geometry/SafeArea.h"
AreaData::AreaData(const AreaData &other, QObject *parent) #include "QGCLoggingCategory.h"
: QObject(parent) { #include "QGCQGeoCoordinate.h"
*this = other;
}
AreaData &AreaData::operator=(const AreaData &other) { QGC_LOGGING_CATEGORY(AreaDataLog, "AreaDataLog")
this->append(other.measurementArea());
this->append(other.serviceArea());
this->append(other.joinedArea());
this->append(other.corridor());
return *this; AreaData::AreaData(QObject *parent) : QObject(parent) {}
}
void AreaData::append(const WimaJoinedAreaData &areaData) { AreaData::~AreaData() {}
if (_joinedArea != areaData) {
_joinedArea = areaData;
emit joinedAreaChanged();
}
}
void AreaData::append(const WimaServiceAreaData &areaData) { AreaData::AreaData(const AreaData &other, QObject *parent) : QObject(parent) {
if (_serviceArea != areaData) { if (!copyAreaList(other._areaList, _areaList, this)) {
_serviceArea = areaData; qCWarning(AreaDataLog) << "AreaData(): not able to copy other._areaList";
emit serviceAreaChanged(); } else {
_origin = other._origin;
} }
} }
void AreaData::append(const WimaCorridorData &areaData) { AreaData &AreaData::operator=(const AreaData &other) {
if (_corridor != areaData) { if (!copyAreaList(other._areaList, _areaList, this)) {
_corridor = areaData; qCWarning(AreaDataLog) << "operator=(): not able to copy other._areaList";
emit corridorChanged(); } else {
_origin = other._origin;
} }
return *this;
} }
void AreaData::append(const WimaMeasurementAreaData &areaData) { bool AreaData::insert(GeoArea *areaData) {
if (_measurementArea != areaData) { {
_measurementArea = areaData; SafeArea *area = qobject_cast<SafeArea *>(areaData);
emit measurementAreaChanged(); if (area != nullptr) {
if (Q_LIKELY(!this->_areaList.contains(area))) {
if (_measurementArea.coordinateList().size() > 0) { _areaList.append(area);
setOrigin(_measurementArea.coordinateList().first()); emit areaList();
} else { return true;
setOrigin(QGeoCoordinate()); } else {
return false;
}
} }
} }
}
void AreaData::append(const WimaJoinedArea &areaData) { {
if (_joinedArea != areaData) { MeasurementArea *area = qobject_cast<MeasurementArea *>(areaData);
_joinedArea = areaData; if (area != nullptr) {
emit joinedAreaChanged(); if (Q_LIKELY(!this->_areaList.contains(area))) {
_areaList.append(area);
emit areaList();
return true;
} else {
return false;
}
}
} }
}
void AreaData::append(const WimaArea &areaData) { return false;
if (_serviceArea != areaData) {
_serviceArea = areaData;
emit serviceAreaChanged();
}
} }
void AreaData::append(const WimaCorridor &areaData) { void AreaData::remove(GeoArea *areaData) {
if (_corridor != areaData) { int index = _areaList.indexOf(areaData);
_corridor = areaData; if (index >= 0) {
emit corridorChanged(); QObject *obj = _areaList.removeAt(index);
}
}
void AreaData::append(const WimaMeasurementArea &areaData) { _setOrigin(_newOrigin());
if (_measurementArea != areaData) {
_measurementArea = areaData;
emit measurementAreaChanged();
if (_measurementArea.coordinateList().size() > 0) { if (obj->parent() == nullptr) {
setOrigin(_measurementArea.coordinateList().first()); obj->deleteLater();
} else {
setOrigin(QGeoCoordinate());
} }
}
}
void AreaData::clear() { *this = AreaData(); }
const QGeoCoordinate &AreaData::origin() const { return _origin; }
bool AreaData::isValid() { emit areaListChanged();
return _measurementArea.coordinateList().size() >= 3 && }
_serviceArea.coordinateList().size() >= 3 && _origin.isValid();
} }
const WimaJoinedAreaData &AreaData::joinedArea() const { void AreaData::clear() {
return this->_joinedArea; if (_areaList.count() > 0) {
for (int i = 0; i < _areaList.count(); ++i) {
remove(_areaList.value<GeoArea *>(i));
}
emit areaListChanged();
}
} }
const WimaServiceAreaData &AreaData::serviceArea() const { QmlObjectListModel *AreaData::areaList() { return &_areaList; }
return this->_serviceArea;
}
const WimaCorridorData &AreaData::corridor() const { const QmlObjectListModel *AreaData::areaList() const { return &_areaList; }
return this->_corridor;
}
const WimaMeasurementAreaData &AreaData::measurementArea() const { const QGeoCoordinate &AreaData::origin() const { return _origin; }
return this->_measurementArea;
}
WimaJoinedAreaData &AreaData::joinedArea() { return this->_joinedArea; } bool AreaData::isValid() const {
qWarning("AreaData::isValid(): impl. incomplete.");
auto *measurementArea = getGeoArea<const MeasurementArea *>(_areaList);
auto *safeArea = getGeoArea<const SafeArea *>(_areaList);
return measurementArea != nullptr && safeArea != nullptr &&
measurementArea->count() >= 3 && safeArea->count() >= 3 &&
_origin.isValid();
}
bool AreaData::tryMakeValid() {
qWarning("AreaData::tryMakeValid(): impl. missing.");
return true;
}
bool AreaData::initialize(const QGeoCoordinate &bottomLeft,
const QGeoCoordinate &topRight) {
// bottomLeft and topRight define the bounding box.
if (bottomLeft.isValid() && topRight.isValid() && bottomLeft != topRight) {
auto *measurementArea = getGeoArea<MeasurementArea *>(_areaList);
auto *safeArea = getGeoArea<SafeArea *>(_areaList);
if (safeArea == nullptr) {
if (!insert(new SafeArea())) {
qCCritical(AreaDataLog)
<< "initialize(): safeArea == nullptr, but insert() failed.";
return false;
}
}
WimaServiceAreaData &AreaData::serviceArea() { return this->_serviceArea; } if (measurementArea == nullptr) {
if (!insert(new MeasurementArea())) {
qCCritical(AreaDataLog) << "initialize(): measurementArea == nullptr, "
"but insert() failed.";
return false;
}
}
WimaCorridorData &AreaData::corridor() { return this->_corridor; } // Fit safe area to bounding box.
safeArea->clear();
safeArea->appendVertex(bottomLeft);
safeArea->appendVertex(
QGeoCoordinate(topRight.latitude(), bottomLeft.longitude()));
safeArea->appendVertex(topRight);
safeArea->appendVertex(
QGeoCoordinate(bottomLeft.latitude(), topRight.longitude()));
// Put measurement area inside safeArea;
measurementArea->clear();
measurementArea->appendVertex(QGeoCoordinate(
0.8 * bottomLeft.latitude() + 0.2 * topRight.latitude(),
0.8 * bottomLeft.longitude() + 0.2 * topRight.longitude()));
measurementArea->appendVertex(QGeoCoordinate(
0.2 * bottomLeft.latitude() + 0.8 * topRight.latitude(),
0.8 * bottomLeft.longitude() + 0.2 * topRight.longitude()));
measurementArea->appendVertex(QGeoCoordinate(
0.2 * bottomLeft.latitude() + 0.8 * topRight.latitude(),
0.2 * bottomLeft.longitude() + 0.8 * topRight.longitude()));
measurementArea->appendVertex(QGeoCoordinate(
0.8 * bottomLeft.latitude() + 0.2 * topRight.latitude(),
0.2 * bottomLeft.longitude() + 0.8 * topRight.longitude()));
return true;
} else {
qCWarning(AreaDataLog)
<< "initialize(): bounding box invaldid (bottomLeft, topRight) "
<< bottomLeft << "," << topRight;
return false;
}
}
WimaMeasurementAreaData &AreaData::measurementArea() { bool AreaData::initialized() {
return this->_measurementArea; auto *measurementArea = getGeoArea<MeasurementArea *>(_areaList);
auto *safeArea = getGeoArea<SafeArea *>(_areaList);
return measurementArea != nullptr && safeArea != nullptr &&
measurementArea->count() >= 3 && safeArea->count() >= 3;
} }
bool AreaData::operator==(const AreaData &other) const { bool AreaData::operator==(const AreaData &other) const {
return this->_joinedArea == other._joinedArea && if (_areaList.count() == other._areaList.count()) {
this->_measurementArea == other._measurementArea && for (int i = 0; i < _areaList.count(); ++i) {
this->_corridor == other._corridor && if (_areaList[i] != other._areaList[i]) {
this->_serviceArea == other._serviceArea; return false;
}
}
return true;
} else {
return false;
}
} }
bool AreaData::operator!=(const AreaData &other) const { bool AreaData::operator!=(const AreaData &other) const {
return !(*this == other); return !(*this == other);
} }
void AreaData::setOrigin(const QGeoCoordinate &origin) { void AreaData::_setOrigin(const QGeoCoordinate &origin) {
if (this->_origin != origin) { if (this->_origin != origin) {
this->_origin = origin; this->_origin = origin;
emit originChanged(); emit originChanged();
} }
} }
QGeoCoordinate AreaData::_newOrigin() {
auto *measurementArea = getGeoArea<MeasurementArea *>(_areaList);
auto *safeArea = getGeoArea<SafeArea *>(_areaList);
if (measurementArea != nullptr && measurementArea->pathModel().count() > 0) {
QGCQGeoCoordinate *ori =
measurementArea->pathModel().value<QGCQGeoCoordinate *>(0);
if (ori != nullptr && ori->coordinate().isValid()) {
return ori->coordinate();
}
}
if (safeArea != nullptr && safeArea->pathModel().count() > 0) {
QGCQGeoCoordinate *ori =
measurementArea->pathModel().value<QGCQGeoCoordinate *>(0);
if (ori != nullptr && ori->coordinate().isValid()) {
return ori->coordinate();
}
}
return QGeoCoordinate();
}
#pragma once
#include <QGeoCoordinate>
#include <QObject>
#include "QmlObjectListModel.h"
class GeoArea;
class SafeArea;
class MeasurementArea;
class AreaData : public QObject {
Q_OBJECT
public:
AreaData(QObject *parent = nullptr);
~AreaData();
AreaData(const AreaData &other, QObject *parent = nullptr);
AreaData &operator=(const AreaData &other);
// Member Methodes
//!
//! \brief insert Inserts the area if areaList does not contain it.
//! \param areaData
bool insert(GeoArea *areaData);
//!
//! \brief remove
//! \param areaData Removes the area.
//! \note Deletes the area if it has no parent.
void remove(GeoArea *areaData);
void clear();
//!
//! \brief areaList
//! \return Returns the list of areas.
//! \note For Qml use only, don't alter the list, or risk to break invariants.
QmlObjectListModel *areaList();
//!
//! \brief areaList
//! \return Returns the list of areas.
const QmlObjectListModel *areaList() const;
//!
//! \brief origin
//! \return Returns an origin near one of the areas.
//! \note Origin might change if the list of areas changes.
const QGeoCoordinate &origin() const;
Q_INVOKABLE bool isValid() const;
Q_INVOKABLE bool tryMakeValid();
//!
//! \brief initialize Initializes the areas in a valid way, such that they
//! area inside the bounding box. \param bottomLeft bottom left corner of the
//! bounding box. \param topRight top right corner of the bounding box. \note
//! Behavior is undefined, if \p bottomLeft and \p topRight are not the bottom
//! left and the top right corner of the bounding box. \return Returns true on
//! succes, false either.
//!
Q_INVOKABLE bool initialize(const QGeoCoordinate &bottomLeft,
const QGeoCoordinate &topRight);
//!
//! \brief initialized Checks if area data is initialized
//! \return Returns true if area list contains a SafeArea and a
//! MeasurementArea and both areas have atleast three vertices, returns false
//! either.
//!
Q_INVOKABLE bool initialized();
bool operator==(const AreaData &other) const;
bool operator!=(const AreaData &other) const;
signals:
void areaListChanged();
void originChanged();
private:
void _setOrigin(const QGeoCoordinate &origin);
QGeoCoordinate _newOrigin();
QGeoCoordinate _origin;
QmlObjectListModel _areaList;
};
#include "CircularGenerator.h" #include "CircularGenerator.h"
#include "QGCLoggingCategory.h" #include "QGCLoggingCategory.h"
QGC_LOGGING_CATEGORY(CircularGeneratorLog, "CircularGeneratorLog") #include "SettingsFact.h"
#define CLIPPER_SCALE 1000000 #define CLIPPER_SCALE 1000000
#include "Wima/Geometry/GenericCircle.h" #include "RoutingThread.h"
#include "clipper/clipper.hpp" #include "geometry/GenericCircle.h"
#include "geometry/MeasurementArea.h"
#include "geometry/SafeArea.h"
#include "geometry/clipper/clipper.hpp"
#include "nemo_interface/SnakeTile.h"
QGC_LOGGING_CATEGORY(CircularGeneratorLog, "CircularGeneratorLog")
using namespace ClipperLib; using namespace ClipperLib;
template <> inline auto get<0>(const IntPoint &p) { return p.X; } template <> inline auto get<0>(const IntPoint &p) { return p.X; }
template <> inline auto get<1>(const IntPoint &p) { return p.Y; } template <> inline auto get<1>(const IntPoint &p) { return p.Y; }
#include "SnakeTile.h"
#include "Wima/RoutingThread.h"
namespace routing { namespace routing {
bool circularTransects(const snake::FPoint &reference, bool circularTransects(const snake::FPoint &reference,
...@@ -77,7 +80,13 @@ bool CircularGenerator::get(Generator &generator) { ...@@ -77,7 +80,13 @@ bool CircularGenerator::get(Generator &generator) {
snake::FPoint reference; snake::FPoint reference;
snake::toENU(origin, ref, reference); snake::toENU(origin, ref, reference);
auto geoPolygon = this->_d->measurementArea().coordinateList(); auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
if (measurementArea == nullptr) {
qCDebug(CircularGeneratorLog) << "get(): measurement area == nullptr";
return false;
}
auto geoPolygon = measurementArea->coordinateList();
for (auto &v : geoPolygon) { for (auto &v : geoPolygon) {
if (v.isValid()) { if (v.isValid()) {
v.setAltitude(0); v.setAltitude(0);
...@@ -93,8 +102,8 @@ bool CircularGenerator::get(Generator &generator) { ...@@ -93,8 +102,8 @@ bool CircularGenerator::get(Generator &generator) {
snake::areaToEnu(origin, geoPolygon, *pPolygon); snake::areaToEnu(origin, geoPolygon, *pPolygon);
// Progress and tiles. // Progress and tiles.
const auto &progress = this->_d->measurementArea().progress(); const auto &progress = measurementArea->progress();
const auto *tiles = this->_d->measurementArea().tiles(); const auto *tiles = measurementArea->tiles();
auto pTiles = std::make_shared<std::vector<snake::FPolygon>>(); auto pTiles = std::make_shared<std::vector<snake::FPolygon>>();
if (progress.size() == tiles->count()) { if (progress.size() == tiles->count()) {
for (int i = 0; i < tiles->count(); ++i) { for (int i = 0; i < tiles->count(); ++i) {
...@@ -119,7 +128,12 @@ bool CircularGenerator::get(Generator &generator) { ...@@ -119,7 +128,12 @@ bool CircularGenerator::get(Generator &generator) {
return false; return false;
} }
auto geoDepot = this->_d->serviceArea().depot(); auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
if (measurementArea == nullptr) {
qCDebug(CircularGeneratorLog) << "get(): measurement area == nullptr";
return false;
}
auto geoDepot = serviceArea->depot();
if (!geoDepot.isValid()) { if (!geoDepot.isValid()) {
qCDebug(CircularGeneratorLog) << "get(): depot invalid." << geoDepot; qCDebug(CircularGeneratorLog) << "get(): depot invalid." << geoDepot;
return false; return false;
...@@ -169,73 +183,85 @@ void CircularGenerator::resetReferenceIfInvalid() { ...@@ -169,73 +183,85 @@ void CircularGenerator::resetReferenceIfInvalid() {
} }
void CircularGenerator::resetReference() { void CircularGenerator::resetReference() {
if (this->_d->measurementArea().center().isValid()) { auto measurementArea =
setReference(this->_d->measurementArea().center()); getGeoArea<const MeasurementArea *>(*this->_d->areaList());
if (measurementArea != nullptr) {
if (measurementArea->center().isValid()) {
setReference(measurementArea->center());
} else {
qCWarning(CircularGeneratorLog)
<< "measurement area center" << measurementArea->center();
}
} else { } else {
qCWarning(CircularGeneratorLog) qCDebug(CircularGeneratorLog)
<< "measurement area center" << this->_d->measurementArea().center(); << "resetReference(): measurement area == nullptr";
} }
} }
void CircularGenerator::establishConnections() { void CircularGenerator::establishConnections() {
if (this->_d && !this->_connectionsEstablished) { if (this->_d && !this->_connectionsEstablished) {
connect(this->_d.get(), &AreaData::originChanged, this, auto measurementArea =
&GeneratorBase::generatorChanged); getGeoArea<const MeasurementArea *>(*this->_d->areaList());
connect(&this->_d->measurementArea(), auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
&WimaMeasurementAreaData::progressChanged, this, if (measurementArea != nullptr && serviceArea != nullptr) {
&GeneratorBase::generatorChanged); GeneratorBase::establishConnections();
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this, connect(this->_d, &AreaData::originChanged, this,
&GeneratorBase::generatorChanged); &GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(), connect(measurementArea, &MeasurementArea::progressChanged, this,
&WimaMeasurementAreaData::centerChanged, this, &GeneratorBase::generatorChanged);
&CircularGenerator::resetReferenceIfInvalid); connect(measurementArea, &MeasurementArea::tilesChanged, this,
connect(&this->_d->measurementArea(), &WimaMeasurementAreaData::pathChanged, &GeneratorBase::generatorChanged);
this, &GeneratorBase::generatorChanged); connect(measurementArea, &MeasurementArea::centerChanged, this,
connect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, this, &CircularGenerator::resetReferenceIfInvalid);
&GeneratorBase::generatorChanged); connect(measurementArea, &MeasurementArea::pathChanged, this,
connect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(serviceArea, &SafeArea::depotChanged, this,
connect(this->distance(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(this->distance(), &Fact::rawValueChanged, this,
connect(this->deltaAlpha(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(this->deltaAlpha(), &Fact::rawValueChanged, this,
connect(this->minLength(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(this->minLength(), &Fact::rawValueChanged, this,
connect(this, &CircularGenerator::referenceChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(this, &CircularGenerator::referenceChanged, this,
this->_connectionsEstablished = true; &GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
} }
} }
void CircularGenerator::deleteConnections() { void CircularGenerator::deleteConnections() {
if (this->_d && this->_connectionsEstablished) { if (this->_d && this->_connectionsEstablished) {
disconnect(this->_d.get(), &AreaData::originChanged, this, auto measurementArea =
&GeneratorBase::generatorChanged); getGeoArea<const MeasurementArea *>(*this->_d->areaList());
disconnect(&this->_d->measurementArea(), auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
&WimaMeasurementAreaData::progressChanged, this, if (measurementArea != nullptr && serviceArea != nullptr) {
&GeneratorBase::generatorChanged); GeneratorBase::deleteConnections();
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this, disconnect(this->_d, &AreaData::originChanged, this,
&GeneratorBase::generatorChanged); &GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(), &WimaMeasurementAreaData::center, disconnect(measurementArea, &MeasurementArea::progressChanged, this,
this, &CircularGenerator::resetReferenceIfInvalid); &GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(), disconnect(measurementArea, &MeasurementArea::tilesChanged, this,
&WimaMeasurementAreaData::pathChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(measurementArea, &MeasurementArea::centerChanged, this,
disconnect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, &CircularGenerator::resetReferenceIfInvalid);
this, &GeneratorBase::generatorChanged); disconnect(measurementArea, &MeasurementArea::pathChanged, this,
disconnect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(serviceArea, &SafeArea::depotChanged, this,
disconnect(this->distance(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(this->distance(), &Fact::rawValueChanged, this,
disconnect(this->deltaAlpha(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(this->deltaAlpha(), &Fact::rawValueChanged, this,
disconnect(this->minLength(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(this->minLength(), &Fact::rawValueChanged, this,
disconnect(this, &CircularGenerator::referenceChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(this, &CircularGenerator::referenceChanged, this,
this->_connectionsEstablished = false; &GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
} }
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
#include <QGeoCoordinate> #include <QGeoCoordinate>
#include "SettingsFact.h"
namespace routing { namespace routing {
class CircularGenerator : public GeneratorBase { class CircularGenerator : public GeneratorBase {
......
...@@ -12,10 +12,6 @@ GeneratorBase::GeneratorBase(GeneratorBase::Data d, QObject *parent) ...@@ -12,10 +12,6 @@ GeneratorBase::GeneratorBase(GeneratorBase::Data d, QObject *parent)
GeneratorBase::~GeneratorBase() {} GeneratorBase::~GeneratorBase() {}
QString GeneratorBase::editorQml() { return QStringLiteral(""); }
QString GeneratorBase::mapVisualQml() { return QStringLiteral(""); }
GeneratorBase::Data GeneratorBase::data() const { return _d; } GeneratorBase::Data GeneratorBase::data() const { return _d; }
void GeneratorBase::setData(const Data &d) { void GeneratorBase::setData(const Data &d) {
...@@ -28,4 +24,9 @@ void GeneratorBase::establishConnections() {} ...@@ -28,4 +24,9 @@ void GeneratorBase::establishConnections() {}
void GeneratorBase::deleteConnections() {} void GeneratorBase::deleteConnections() {}
void GeneratorBase::_areaListChangedHandler() {
deleteConnections();
establishConnections();
}
} // namespace routing } // namespace routing
...@@ -5,16 +5,16 @@ ...@@ -5,16 +5,16 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include "snake.h" #include "geometry/snake.h"
#include "Wima/WimaPlanData.h" #include "AreaData.h"
namespace routing { namespace routing {
class GeneratorBase : public QObject { class GeneratorBase : public QObject {
Q_OBJECT Q_OBJECT
public: public:
using Data = std::shared_ptr<AreaData>; using Data = AreaData *;
using Generator = std::function<bool(snake::Transects &)>; using Generator = std::function<bool(snake::Transects &)>;
explicit GeneratorBase(QObject *parent = nullptr); explicit GeneratorBase(QObject *parent = nullptr);
...@@ -24,8 +24,8 @@ public: ...@@ -24,8 +24,8 @@ public:
Q_PROPERTY(QString editorQml READ editorQml CONSTANT) Q_PROPERTY(QString editorQml READ editorQml CONSTANT)
Q_PROPERTY(QString mapVisualQml READ mapVisualQml CONSTANT) Q_PROPERTY(QString mapVisualQml READ mapVisualQml CONSTANT)
virtual QString editorQml(); virtual QString editorQml() = 0;
virtual QString mapVisualQml(); virtual QString mapVisualQml() = 0;
virtual QString name() = 0; virtual QString name() = 0;
virtual QString abbreviation() = 0; virtual QString abbreviation() = 0;
...@@ -42,6 +42,9 @@ protected: ...@@ -42,6 +42,9 @@ protected:
virtual void establishConnections(); virtual void establishConnections();
virtual void deleteConnections(); virtual void deleteConnections();
Data _d; Data _d;
private:
void _areaListChangedHandler();
}; };
} // namespace routing } // namespace routing
...@@ -4,10 +4,12 @@ ...@@ -4,10 +4,12 @@
QGC_LOGGING_CATEGORY(LinearGeneratorLog, "LinearGeneratorLog") QGC_LOGGING_CATEGORY(LinearGeneratorLog, "LinearGeneratorLog")
#define CLIPPER_SCALE 1000000 #define CLIPPER_SCALE 1000000
#include "clipper/clipper.hpp" #include "geometry/MeasurementArea.h"
#include "geometry/SafeArea.h"
#include "geometry/clipper/clipper.hpp"
#include "SnakeTile.h" #include "RoutingThread.h"
#include "Wima/RoutingThread.h" #include "nemo_interface/SnakeTile.h"
namespace routing { namespace routing {
...@@ -38,6 +40,8 @@ QString LinearGenerator::editorQml() { ...@@ -38,6 +40,8 @@ QString LinearGenerator::editorQml() {
return QStringLiteral("LinearGeneratorEditor.qml"); return QStringLiteral("LinearGeneratorEditor.qml");
} }
QString LinearGenerator::mapVisualQml() { return QStringLiteral(""); }
QString LinearGenerator::name() { return QStringLiteral("Linear Generator"); } QString LinearGenerator::name() { return QStringLiteral("Linear Generator"); }
QString LinearGenerator::abbreviation() { return QStringLiteral("L. Gen."); } QString LinearGenerator::abbreviation() { return QStringLiteral("L. Gen."); }
...@@ -52,7 +56,13 @@ bool LinearGenerator::get(Generator &generator) { ...@@ -52,7 +56,13 @@ bool LinearGenerator::get(Generator &generator) {
qCDebug(LinearGeneratorLog) << "get(): origin invalid." << origin; qCDebug(LinearGeneratorLog) << "get(): origin invalid." << origin;
} }
auto geoPolygon = this->_d->measurementArea().coordinateList(); auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
if (measurementArea == nullptr) {
qCDebug(LinearGeneratorLog) << "get(): measurement area == nullptr";
return false;
}
auto geoPolygon = measurementArea->coordinateList();
for (auto &v : geoPolygon) { for (auto &v : geoPolygon) {
if (v.isValid()) { if (v.isValid()) {
v.setAltitude(0); v.setAltitude(0);
...@@ -68,8 +78,8 @@ bool LinearGenerator::get(Generator &generator) { ...@@ -68,8 +78,8 @@ bool LinearGenerator::get(Generator &generator) {
snake::areaToEnu(origin, geoPolygon, *pPolygon); snake::areaToEnu(origin, geoPolygon, *pPolygon);
// Progress and tiles. // Progress and tiles.
const auto &progress = this->_d->measurementArea().progress(); const auto &progress = measurementArea->progress();
const auto *tiles = this->_d->measurementArea().tiles(); const auto *tiles = measurementArea->tiles();
auto pTiles = std::make_shared<std::vector<snake::FPolygon>>(); auto pTiles = std::make_shared<std::vector<snake::FPolygon>>();
if (progress.size() == tiles->count()) { if (progress.size() == tiles->count()) {
for (int i = 0; i < tiles->count(); ++i) { for (int i = 0; i < tiles->count(); ++i) {
...@@ -93,7 +103,12 @@ bool LinearGenerator::get(Generator &generator) { ...@@ -93,7 +103,12 @@ bool LinearGenerator::get(Generator &generator) {
return false; return false;
} }
auto geoDepot = this->_d->serviceArea().depot(); auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
if (serviceArea == nullptr) {
qCDebug(LinearGeneratorLog) << "get(): service area == nullptr";
return false;
}
auto geoDepot = serviceArea->depot();
if (!geoDepot.isValid()) { if (!geoDepot.isValid()) {
qCDebug(LinearGeneratorLog) << "get(): depot invalid." << geoDepot; qCDebug(LinearGeneratorLog) << "get(): depot invalid." << geoDepot;
return false; return false;
...@@ -134,53 +149,61 @@ Fact *LinearGenerator::minLength() { return &_minLength; } ...@@ -134,53 +149,61 @@ Fact *LinearGenerator::minLength() { return &_minLength; }
void LinearGenerator::establishConnections() { void LinearGenerator::establishConnections() {
if (this->_d && !this->_connectionsEstablished) { if (this->_d && !this->_connectionsEstablished) {
connect(this->_d.get(), &AreaData::originChanged, this, auto measurementArea =
&GeneratorBase::generatorChanged); getGeoArea<const MeasurementArea *>(*this->_d->areaList());
connect(&this->_d->measurementArea(), auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged); if (measurementArea != nullptr && serviceArea != nullptr) {
connect(&this->_d->measurementArea(), GeneratorBase::establishConnections();
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged); connect(this->_d, &AreaData::originChanged, this,
connect(&this->_d->measurementArea(), &WimaMeasurementAreaData::pathChanged, &GeneratorBase::generatorChanged);
this, &GeneratorBase::generatorChanged); connect(measurementArea, &MeasurementArea::progressChanged, this,
connect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(measurementArea, &MeasurementArea::tilesChanged, this,
connect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(measurementArea, &MeasurementArea::pathChanged, this,
connect(this->distance(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(serviceArea, &SafeArea::depotChanged, this,
connect(this->alpha(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(this->distance(), &Fact::rawValueChanged, this,
connect(this->minLength(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); connect(this->alpha(), &Fact::rawValueChanged, this,
this->_connectionsEstablished = true; &GeneratorBase::generatorChanged);
connect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
} }
} }
void LinearGenerator::deleteConnections() { void LinearGenerator::deleteConnections() {
if (this->_d && this->_connectionsEstablished) { if (this->_d && this->_connectionsEstablished) {
connect(this->_d.get(), &AreaData::originChanged, this, auto measurementArea =
&GeneratorBase::generatorChanged); getGeoArea<const MeasurementArea *>(*this->_d->areaList());
connect(&this->_d->measurementArea(), auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged); if (measurementArea != nullptr && serviceArea != nullptr) {
connect(&this->_d->measurementArea(), GeneratorBase::deleteConnections();
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged); disconnect(this->_d, &AreaData::originChanged, this,
connect(&this->_d->measurementArea(), &WimaMeasurementAreaData::pathChanged, &GeneratorBase::generatorChanged);
this, &GeneratorBase::generatorChanged); disconnect(measurementArea, &MeasurementArea::progressChanged, this,
connect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(measurementArea, &MeasurementArea::tilesChanged, this,
connect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(measurementArea, &MeasurementArea::pathChanged, this,
connect(this->distance(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(serviceArea, &SafeArea::depotChanged, this,
connect(this->alpha(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(this->distance(), &Fact::rawValueChanged, this,
connect(this->minLength(), &Fact::rawValueChanged, this, &GeneratorBase::generatorChanged);
&GeneratorBase::generatorChanged); disconnect(this->alpha(), &Fact::rawValueChanged, this,
this->_connectionsEstablished = false; &GeneratorBase::generatorChanged);
disconnect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
} }
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
#include <QGeoCoordinate> #include <QGeoCoordinate>
#include "SettingsFact.h"
namespace routing { namespace routing {
class LinearGenerator : public GeneratorBase { class LinearGenerator : public GeneratorBase {
...@@ -15,6 +17,7 @@ public: ...@@ -15,6 +17,7 @@ public:
Q_PROPERTY(Fact *minLength READ minLength CONSTANT) Q_PROPERTY(Fact *minLength READ minLength CONSTANT)
virtual QString editorQml() override; virtual QString editorQml() override;
virtual QString mapVisualQml() override;
virtual QString name() override; virtual QString name() override;
virtual QString abbreviation() override; virtual QString abbreviation() override;
......
#include "NemoInterface.h" #include "NemoInterface.h"
#include "SnakeTilesLocal.h" #include "nemo_interface/SnakeTilesLocal.h"
#include "QGCApplication.h" #include "QGCApplication.h"
#include "QGCLoggingCategory.h" #include "QGCLoggingCategory.h"
...@@ -12,11 +12,11 @@ ...@@ -12,11 +12,11 @@
#include <QTimer> #include <QTimer>
#include "QNemoHeartbeat.h" #include "geometry/MeasurementArea.h"
#include "QNemoProgress.h" #include "geometry/snake.h"
#include "Wima/Geometry/WimaMeasurementArea.h" #include "nemo_interface/QNemoHeartbeat.h"
#include "Wima/Snake/SnakeTile.h" #include "nemo_interface/QNemoProgress.h"
#include "Wima/Snake/snake.h" #include "nemo_interface/SnakeTile.h"
#include "ros_bridge/include/messages/geographic_msgs/geopoint.h" #include "ros_bridge/include/messages/geographic_msgs/geopoint.h"
#include "ros_bridge/include/messages/jsk_recognition_msgs/polygon_array.h" #include "ros_bridge/include/messages/jsk_recognition_msgs/polygon_array.h"
......
#include "OptimisationTools.h"
namespace OptimisationTools {
namespace {
} // end anonymous namespace
bool dijkstraAlgorithm(const int numElements, int startIndex, int endIndex, QVector<int> &elementPath, std::function<double (const int, const int)> distanceDij)
{
if ( numElements < 0
|| startIndex < 0
|| endIndex < 0
|| startIndex >= numElements
|| endIndex >= numElements
|| endIndex == startIndex) {
return false;
}
// Node struct
// predecessorIndex is the index of the predecessor node (nodeList[predecessorIndex])
// distance is the distance between the node and the start node
// node number is stored by the position in nodeList
struct Node{
int predecessorIndex = -1;
double distance = std::numeric_limits<qreal>::infinity();
};
// The list with all Nodes (elements)
QVector<Node> nodeList(numElements);
// This list will be initalized with indices referring to the elements of nodeList.
// Elements will be successively remove during the execution of the Dijkstra Algorithm.
QVector<int> workingSet(numElements);
//append elements to node list
for (int i = 0; i < numElements; ++i) workingSet[i] = i;
nodeList[startIndex].distance = 0;
// qDebug() << "nodeList" ;
// for (auto node : nodeList) {
// qDebug() << "predecessor: " << node.predecessorIndex;
// qDebug() << "distance: " << node.distance;
// }
// qDebug() << "workingSet";
// for (auto node : workingSet) {
// qDebug() << "index: " << node;
// }
// Dijkstra Algorithm
// https://de.wikipedia.org/wiki/Dijkstra-Algorithmus
while (workingSet.size() > 0) {
// serach Node with minimal distance
double minDist = std::numeric_limits<qreal>::infinity();
int minDistIndex_WS = -1; // WS = workinSet
for (int i = 0; i < workingSet.size(); ++i) {
const int nodeIndex = workingSet.at(i);
const double dist = nodeList.at(nodeIndex).distance;
if (dist < minDist) {
minDist = dist;
minDistIndex_WS = i;
}
}
if (minDistIndex_WS == -1)
return false;
int indexU_NL = workingSet.takeAt(minDistIndex_WS); // NL = nodeList
if (indexU_NL == endIndex) // shortest path found
break;
const double distanceU = nodeList.at(indexU_NL).distance;
//update distance
for (int i = 0; i < workingSet.size(); ++i) {
int indexV_NL = workingSet[i]; // NL = nodeList
Node* v = &nodeList[indexV_NL];
double dist = distanceDij(indexU_NL, indexV_NL);
// is ther an alternative path which is shorter?
double alternative = distanceU + dist;
if (alternative < v->distance) {
v->distance = alternative;
v->predecessorIndex = indexU_NL;
}
}
}
// end Djikstra Algorithm
// reverse assemble path
int e = endIndex;
while (1) {
if (e == -1) {
if (elementPath[0] == startIndex)// check if starting point was reached
break;
return false;
}
elementPath.prepend(e);
//Update Node
e = nodeList[e].predecessorIndex;
}
return true;
}
} // end OptimisationTools namespace
#pragma once
#include <QObject>
#include <QDebug>
#include <functional>
#include <QVector>
namespace OptimisationTools {
bool dijkstraAlgorithm(const int numElements, int startIndex, int endIndex, QVector<int> &elementPath, std::function<double(const int, const int)> distanceDij);
}
#include "CircularSurvey.h" #include "RouteComplexItem.h"
#include "CircularGenerator.h"
#include "LinearGenerator.h"
#include "RoutingThread.h" #include "RoutingThread.h"
#include "geometry/GenericCircle.h"
#include "geometry/MeasurementArea.h"
#include "geometry/SafeArea.h"
#include "geometry/clipper/clipper.hpp"
#include "geometry/snake.h"
#include "nemo_interface/SnakeTile.h"
// QGC // QGC
#include "JsonHelper.h" #include "JsonHelper.h"
#include "QGCApplication.h" #include "QGCApplication.h"
#include "QGCLoggingCategory.h" #include "QGCLoggingCategory.h"
// Wima
#include "snake.h"
#define CLIPPER_SCALE 1000000
#include "clipper/clipper.hpp"
#include "Geometry/GenericCircle.h"
#include "Snake/SnakeTile.h"
// boost // boost
#include <boost/units/io.hpp> #include <boost/units/io.hpp>
#include <boost/units/systems/si.hpp> #include <boost/units/systems/si.hpp>
#include "CircularGenerator.h" #define CLIPPER_SCALE 1000000
#include "LinearGenerator.h"
// ToDo: Check what happened to _transectsDirty
QGC_LOGGING_CATEGORY(CircularSurveyLog, "CircularSurveyLog") QGC_LOGGING_CATEGORY(RouteComplexItemLog, "RouteComplexItemLog")
template <typename T> template <typename T>
constexpr typename std::underlying_type<T>::type integral(T value) { constexpr typename std::underlying_type<T>::type integral(T value) {
return static_cast<typename std::underlying_type<T>::type>(value); return static_cast<typename std::underlying_type<T>::type>(value);
} }
const char *CircularSurvey::settingsGroup = "CircularSurvey"; const char *RouteComplexItem::settingsGroup = "Route";
const char *CircularSurvey::jsonComplexItemTypeValue = "CircularSurvey"; const char *RouteComplexItem::jsonComplexItemTypeValue = "Route";
const char *CircularSurvey::variantName = "Variant"; const char *RouteComplexItem::variantName = "Variant";
const QString CircularSurvey::name(tr("Circular Survey")); const QString RouteComplexItem::name(tr("Route"));
CircularSurvey::CircularSurvey(PlanMasterController *masterController, RouteComplexItem::RouteComplexItem(PlanMasterController *masterController,
bool flyView, const QString &kmlOrShpFile, bool flyView, const QString &kmlOrShpFile,
QObject *parent) QObject *parent)
: TransectStyleComplexItem(masterController, flyView, settingsGroup, : TransectStyleComplexItem(masterController, flyView, settingsGroup,
parent), parent),
_state(STATE::IDLE), _state(STATE::IDLE),
_metaDataMap(FactMetaData::createMapFromJsonFile( _metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/CircularSurvey.SettingsGroup.json"), this)), QStringLiteral(":/json/RouteComplexItem.SettingsGroup.json"), this)),
_variant(settingsGroup, _metaDataMap[variantName]), _variant(settingsGroup, _metaDataMap[variantName]),
_areaData(std::make_shared<AreaData>()), _areaData(new AreaData(this)), _editorData(new AreaData(this)),
_pWorker(std::make_unique<RoutingThread>()) { _currentData(_areaData), _pWorker(new RoutingThread(this)) {
Q_UNUSED(kmlOrShpFile) Q_UNUSED(kmlOrShpFile)
_editorQml = "qrc:/qml/CircularSurveyItemEditor.qml"; _editorQml = "qrc:/qml/CircularSurveyItemEditor.qml";
// Connect facts. // Connect facts.
connect(&this->_variant, &Fact::rawValueChanged, this, connect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant); &RouteComplexItem::_changeVariant);
// Connect worker. // Connect worker.
connect(this->_pWorker.get(), &RoutingThread::result, this, connect(this->_pWorker, &RoutingThread::result, this,
&CircularSurvey::_setTransects); &RouteComplexItem::_setTransects);
connect(this->_pWorker.get(), &RoutingThread::calculatingChanged, this, connect(this->_pWorker, &RoutingThread::calculatingChanged, this,
&CircularSurvey::calculatingChanged); &RouteComplexItem::calculatingChanged);
// Register Generators. // Register Generators.
auto lg = std::make_shared<routing::LinearGenerator>(this->_areaData); auto lg = new routing::LinearGenerator(this->_areaData, this);
registerGenerator(lg->name(), lg); registerGenerator(lg->name(), lg);
auto cg = std::make_shared<routing::CircularGenerator>(this->_areaData); auto cg = new routing::CircularGenerator(this->_areaData, this);
registerGenerator(cg->name(), cg); registerGenerator(cg->name(), cg);
} }
CircularSurvey::~CircularSurvey() {} RouteComplexItem::~RouteComplexItem() {}
void CircularSurvey::reverse() { void RouteComplexItem::revertPath() {
this->_setState(STATE::REVERT_PATH); this->_setState(STATE::REVERT_PATH);
this->_rebuildTransects(); this->_rebuildTransects();
} }
void CircularSurvey::setPlanData(const AreaData &d) { const AreaData *RouteComplexItem::areaData() const {
*this->_areaData = d; return this->_currentData;
}
const AreaData &CircularSurvey::planData() const {
return *this->_areaData;
} }
AreaData &CircularSurvey::planData() { return *this->_areaData; } AreaData *RouteComplexItem::areaData() { return this->_currentData; }
QStringList CircularSurvey::variantNames() const { return _variantNames; } QStringList RouteComplexItem::variantNames() const { return _variantNames; }
bool CircularSurvey::load(const QJsonObject &complexObject, int sequenceNumber, bool RouteComplexItem::load(const QJsonObject &complexObject,
QString &errorString) { int sequenceNumber, QString &errorString) {
// We need to pull version first to determine what validation/conversion // We need to pull version first to determine what validation/conversion
// needs to be performed // needs to be performed
QList<JsonHelper::KeyValidateInfo> versionKeyInfoList = { QList<JsonHelper::KeyValidateInfo> versionKeyInfoList = {
...@@ -152,11 +148,11 @@ bool CircularSurvey::load(const QJsonObject &complexObject, int sequenceNumber, ...@@ -152,11 +148,11 @@ bool CircularSurvey::load(const QJsonObject &complexObject, int sequenceNumber,
return true; return true;
} }
QString CircularSurvey::mapVisualQML() const { QString RouteComplexItem::mapVisualQML() const {
return QStringLiteral("CircularSurveyMapVisual.qml"); return QStringLiteral("CircularSurveyMapVisual.qml");
} }
void CircularSurvey::save(QJsonArray &planItems) { void RouteComplexItem::save(QJsonArray &planItems) {
QJsonObject saveObject; QJsonObject saveObject;
_save(saveObject); _save(saveObject);
...@@ -175,22 +171,20 @@ void CircularSurvey::save(QJsonArray &planItems) { ...@@ -175,22 +171,20 @@ void CircularSurvey::save(QJsonArray &planItems) {
planItems.append(saveObject); planItems.append(saveObject);
} }
bool CircularSurvey::specifiesCoordinate() const { bool RouteComplexItem::specifiesCoordinate() const {
return _transects.count() > 0 ? _transects.first().count() > 0 : false; return _transects.count() > 0 ? _transects.first().count() > 0 : false;
} }
bool CircularSurvey::_switchToGenerator( bool RouteComplexItem::_setGenerator(PtrGenerator newG) {
const CircularSurvey::PtrGenerator &newG) {
if (this->_pGenerator != newG) { if (this->_pGenerator != newG) {
if (this->_pGenerator != nullptr) { if (this->_pGenerator != nullptr) {
disconnect(this->_pGenerator.get(), disconnect(this->_pGenerator, &routing::GeneratorBase::generatorChanged,
&routing::GeneratorBase::generatorChanged, this, this, &RouteComplexItem::_rebuildTransects);
&CircularSurvey::_rebuildTransects);
} }
this->_pGenerator = newG; this->_pGenerator = newG;
connect(this->_pGenerator.get(), &routing::GeneratorBase::generatorChanged, connect(this->_pGenerator, &routing::GeneratorBase::generatorChanged, this,
this, &CircularSurvey::_rebuildTransects); &RouteComplexItem::_rebuildTransects);
emit generatorChanged(); emit generatorChanged();
this->_setState(STATE::IDLE); this->_setState(STATE::IDLE);
...@@ -202,7 +196,7 @@ bool CircularSurvey::_switchToGenerator( ...@@ -202,7 +196,7 @@ bool CircularSurvey::_switchToGenerator(
} }
} }
void CircularSurvey::_setState(CircularSurvey::STATE state) { void RouteComplexItem::_setState(RouteComplexItem::STATE state) {
if (this->_state != state) { if (this->_state != state) {
auto oldState = this->_state; auto oldState = this->_state;
this->_state = state; this->_state = state;
...@@ -212,16 +206,30 @@ void CircularSurvey::_setState(CircularSurvey::STATE state) { ...@@ -212,16 +206,30 @@ void CircularSurvey::_setState(CircularSurvey::STATE state) {
} }
} }
bool CircularSurvey::_calculating(CircularSurvey::STATE state) const { bool RouteComplexItem::_calculating(RouteComplexItem::STATE state) const {
return state == STATE::ROUTING; return state == STATE::ROUTING;
} }
void CircularSurvey::_changeVariant() { void RouteComplexItem::_setEditing(bool editing) {
if (editing != _editing) {
_editing = editing;
emit editingChanged();
}
}
void RouteComplexItem::_setAreaData(RouteComplexItem::PtrAreaData data) {
if (_currentData != data) {
_currentData = data;
emit areaDataChanged();
}
}
void RouteComplexItem::_changeVariant() {
this->_setState(STATE::CHANGE_VARIANT); this->_setState(STATE::CHANGE_VARIANT);
this->_rebuildTransects(); this->_rebuildTransects();
} }
bool CircularSurvey::_updateWorker() { bool RouteComplexItem::_updateWorker() {
// Reset data. // Reset data.
this->_transects.clear(); this->_transects.clear();
this->_variantVector.clear(); this->_variantVector.clear();
...@@ -234,15 +242,17 @@ bool CircularSurvey::_updateWorker() { ...@@ -234,15 +242,17 @@ bool CircularSurvey::_updateWorker() {
auto origin = this->_areaData->origin(); auto origin = this->_areaData->origin();
origin.setAltitude(0); origin.setAltitude(0);
if (!origin.isValid()) { if (!origin.isValid()) {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "_updateWorker(): origin invalid." << origin; << "_updateWorker(): origin invalid." << origin;
return false; return false;
} }
// Convert safe area. // Convert safe area.
auto geoSafeArea = this->_areaData->joinedArea().coordinateList(); auto serviceArea =
getGeoArea<const SafeArea *>(*this->_areaData->areaList());
auto geoSafeArea = serviceArea->coordinateList();
if (!(geoSafeArea.size() >= 3)) { if (!(geoSafeArea.size() >= 3)) {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "_updateWorker(): safe area invalid." << geoSafeArea; << "_updateWorker(): safe area invalid." << geoSafeArea;
return false; return false;
} }
...@@ -250,7 +260,7 @@ bool CircularSurvey::_updateWorker() { ...@@ -250,7 +260,7 @@ bool CircularSurvey::_updateWorker() {
if (v.isValid()) { if (v.isValid()) {
v.setAltitude(0); v.setAltitude(0);
} else { } else {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "_updateWorker(): safe area contains invalid coordinate." << "_updateWorker(): safe area contains invalid coordinate."
<< geoSafeArea; << geoSafeArea;
return false; return false;
...@@ -271,24 +281,24 @@ bool CircularSurvey::_updateWorker() { ...@@ -271,24 +281,24 @@ bool CircularSurvey::_updateWorker() {
this->_pWorker->route(par, g); this->_pWorker->route(par, g);
return true; return true;
} else { } else {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "_updateWorker(): generator creation failed."; << "_updateWorker(): generator creation failed.";
return false; return false;
} }
} else { } else {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "_updateWorker(): pGenerator == nullptr, number of registered " << "_updateWorker(): pGenerator == nullptr, number of registered "
"generators: " "generators: "
<< this->_generatorList.size(); << this->_generatorList.size();
return false; return false;
} }
} else { } else {
qCDebug(CircularSurveyLog) << "_updateWorker(): plan data invalid."; qCDebug(RouteComplexItemLog) << "_updateWorker(): plan data invalid.";
return false; return false;
} }
} }
void CircularSurvey::_changeVariantWorker() { void RouteComplexItem::_changeVariantWorker() {
auto variant = this->_variant.rawValue().toUInt(); auto variant = this->_variant.rawValue().toUInt();
// Find old variant and run. Old run corresponts with empty list. // Find old variant and run. Old run corresponts with empty list.
...@@ -315,15 +325,15 @@ void CircularSurvey::_changeVariantWorker() { ...@@ -315,15 +325,15 @@ void CircularSurvey::_changeVariantWorker() {
this->_transects.swap(newVariantCoordinates); this->_transects.swap(newVariantCoordinates);
} else { // error } else { // error
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "Variant out of bounds (variant =" << variant << ")."; << "Variant out of bounds (variant =" << variant << ").";
qCDebug(CircularSurveyLog) << "Resetting variant to zero."; qCDebug(RouteComplexItemLog) << "Resetting variant to zero.";
disconnect(&this->_variant, &Fact::rawValueChanged, this, disconnect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant); &RouteComplexItem::_changeVariant);
this->_variant.setCookedValue(QVariant(0)); this->_variant.setCookedValue(QVariant(0));
connect(&this->_variant, &Fact::rawValueChanged, this, connect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant); &RouteComplexItem::_changeVariant);
if (this->_variantVector.size() > 0) { if (this->_variantVector.size() > 0) {
this->_changeVariantWorker(); this->_changeVariantWorker();
...@@ -332,25 +342,23 @@ void CircularSurvey::_changeVariantWorker() { ...@@ -332,25 +342,23 @@ void CircularSurvey::_changeVariantWorker() {
} }
} }
void CircularSurvey::_reverseWorker() { void RouteComplexItem::_reverseWorker() {
if (this->_transects.size() > 0) { if (this->_transects.size() > 0) {
auto &t = this->_transects.front(); auto &t = this->_transects.front();
std::reverse(t.begin(), t.end()); std::reverse(t.begin(), t.end());
} }
} }
double CircularSurvey::timeBetweenShots() { return 0; } double RouteComplexItem::timeBetweenShots() { return 0; }
QString CircularSurvey::commandDescription() const { QString RouteComplexItem::commandDescription() const { return tr("Route"); }
return tr("Circular Survey");
}
QString CircularSurvey::commandName() const { return tr("Circular Survey"); } QString RouteComplexItem::commandName() const { return tr("Route"); }
QString CircularSurvey::abbreviation() const { return tr("C.S."); } QString RouteComplexItem::abbreviation() const { return tr("R"); }
TransectStyleComplexItem::ReadyForSaveState TransectStyleComplexItem::ReadyForSaveState
CircularSurvey::readyForSaveState() const { RouteComplexItem::readyForSaveState() const {
if (TransectStyleComplexItem::readyForSaveState() == if (TransectStyleComplexItem::readyForSaveState() ==
TransectStyleComplexItem::ReadyForSaveState::ReadyForSave) { TransectStyleComplexItem::ReadyForSaveState::ReadyForSave) {
if (this->_state == STATE::IDLE) { if (this->_state == STATE::IDLE) {
...@@ -363,31 +371,31 @@ CircularSurvey::readyForSaveState() const { ...@@ -363,31 +371,31 @@ CircularSurvey::readyForSaveState() const {
} }
} }
double CircularSurvey::additionalTimeDelay() const { return 0; } double RouteComplexItem::additionalTimeDelay() const { return 0; }
QString CircularSurvey::patternName() const { return name; } QString RouteComplexItem::patternName() const { return name; }
bool CircularSurvey::registerGenerator( bool RouteComplexItem::registerGenerator(const QString &name,
const QString &name, std::shared_ptr<routing::GeneratorBase> g) { routing::GeneratorBase *g) {
if (name.isEmpty()) { if (name.isEmpty()) {
qCDebug(CircularSurveyLog) << "registerGenerator(): empty name string."; qCDebug(RouteComplexItemLog) << "registerGenerator(): empty name string.";
return false; return false;
} }
if (!g) { if (!g) {
qCDebug(CircularSurveyLog) << "registerGenerator(): empty generator."; qCDebug(RouteComplexItemLog) << "registerGenerator(): empty generator.";
return false; return false;
} }
if (this->_generatorNameList.contains(name)) { if (this->_generatorNameList.contains(name)) {
qCDebug(CircularSurveyLog) << "registerGenerator(): generator " qCDebug(RouteComplexItemLog) << "registerGenerator(): generator "
"already registered."; "already registered.";
return false; return false;
} else { } else {
this->_generatorNameList.push_back(name); this->_generatorNameList.push_back(name);
this->_generatorList.push_back(g); this->_generatorList.push_back(g);
if (this->_generatorList.size() == 1) { if (this->_generatorList.size() == 1) {
_switchToGenerator(g); _setGenerator(g);
} }
emit generatorNameListChanged(); emit generatorNameListChanged();
...@@ -395,111 +403,128 @@ bool CircularSurvey::registerGenerator( ...@@ -395,111 +403,128 @@ bool CircularSurvey::registerGenerator(
} }
} }
bool CircularSurvey::unregisterGenerator(const QString &name) { bool RouteComplexItem::unregisterGenerator(const QString &name) {
auto index = this->_generatorNameList.indexOf(name); auto index = this->_generatorNameList.indexOf(name);
if (index >= 0) { if (index >= 0) {
// Is this the current generator? // Is this the current generator?
const auto &g = this->_generatorList.at(index); const auto &g = this->_generatorList.at(index);
if (g == this->_pGenerator) { if (g == this->_pGenerator) {
if (index > 0) { if (index > 0) {
_switchToGenerator(this->_generatorList.at(index - 1)); _setGenerator(this->_generatorList.at(index - 1));
} else { } else {
_switchToGenerator(nullptr); _setGenerator(nullptr);
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "unregisterGenerator(): last generator unregistered."; << "unregisterGenerator(): last generator unregistered.";
} }
} }
this->_generatorNameList.removeAt(index); this->_generatorNameList.removeAt(index);
this->_generatorList.removeAt(index); auto gen = this->_generatorList.takeAt(index);
gen->deleteLater();
emit generatorNameListChanged(); emit generatorNameListChanged();
return true; return true;
} else { } else {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "unregisterGenerator(): generator " << name << " not registered."; << "unregisterGenerator(): generator " << name << " not registered.";
return false; return false;
} }
} }
bool CircularSurvey::unregisterGenerator(int index) { bool RouteComplexItem::unregisterGenerator(int index) {
if (index > 0 && index < this->_generatorNameList.size()) { if (index > 0 && index < this->_generatorNameList.size()) {
return unregisterGenerator(this->_generatorNameList.at(index)); return unregisterGenerator(this->_generatorNameList.at(index));
} else { } else {
qCDebug(CircularSurveyLog) << "unregisterGenerator(): index (" << index qCDebug(RouteComplexItemLog) << "unregisterGenerator(): index (" << index
<< ") out" << ") out"
"of bounds ( " "of bounds ( "
<< this->_generatorList.size() << " )."; << this->_generatorList.size() << " ).";
return false; return false;
} }
} }
bool CircularSurvey::switchToGenerator(const QString &name) { bool RouteComplexItem::switchToGenerator(const QString &name) {
auto index = this->_generatorNameList.indexOf(name); auto index = this->_generatorNameList.indexOf(name);
if (index >= 0) { if (index >= 0) {
_switchToGenerator(this->_generatorList.at(index)); _setGenerator(this->_generatorList.at(index));
return true; return true;
} else { } else {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "switchToGenerator(): generator " << name << " not registered."; << "switchToGenerator(): generator " << name << " not registered.";
return false; return false;
} }
} }
bool CircularSurvey::switchToGenerator(int index) { bool RouteComplexItem::switchToGenerator(int index) {
if (index >= 0) { if (index >= 0) {
_switchToGenerator(this->_generatorList.at(index)); _setGenerator(this->_generatorList.at(index));
return true; return true;
} else { } else {
qCDebug(CircularSurveyLog) << "unregisterGenerator(): index (" << index qCDebug(RouteComplexItemLog) << "unregisterGenerator(): index (" << index
<< ") out" << ") out"
"of bounds ( " "of bounds ( "
<< this->_generatorNameList.size() << " )."; << this->_generatorNameList.size() << " ).";
return false; return false;
} }
} }
QStringList CircularSurvey::generatorNameList() { QStringList RouteComplexItem::generatorNameList() {
return this->_generatorNameList; return this->_generatorNameList;
} }
routing::GeneratorBase *CircularSurvey::generator() { routing::GeneratorBase *RouteComplexItem::generator() { return _pGenerator; }
return _pGenerator.get();
}
int CircularSurvey::generatorIndex() { int RouteComplexItem::generatorIndex() {
return this->_generatorList.indexOf(this->_pGenerator); return this->_generatorList.indexOf(this->_pGenerator);
} }
void CircularSurvey::_rebuildTransectsPhase1(void) { void RouteComplexItem::editingStart() {
if (!_editing) {
*_editorData = *_areaData;
_setAreaData(_editorData);
_setEditing(true);
}
}
void RouteComplexItem::editingStop() {
if (_editing) {
if (_editorData->isValid()) {
*_areaData = *_editorData;
}
_setAreaData(_areaData);
_setEditing(false);
}
}
void RouteComplexItem::_rebuildTransectsPhase1(void) {
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
switch (this->_state) { switch (this->_state) {
case STATE::SKIPP: case STATE::SKIPP:
qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: skipp."; qCDebug(RouteComplexItemLog) << "rebuildTransectsPhase1: skipp.";
this->_setState(STATE::IDLE); this->_setState(STATE::IDLE);
break; break;
case STATE::CHANGE_VARIANT: case STATE::CHANGE_VARIANT:
qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: variant change."; qCDebug(RouteComplexItemLog) << "rebuildTransectsPhase1: variant change.";
this->_changeVariantWorker(); this->_changeVariantWorker();
this->_setState(STATE::IDLE); this->_setState(STATE::IDLE);
break; break;
case STATE::REVERT_PATH: case STATE::REVERT_PATH:
qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: reverse."; qCDebug(RouteComplexItemLog) << "rebuildTransectsPhase1: reverse.";
this->_reverseWorker(); this->_reverseWorker();
this->_setState(STATE::IDLE); this->_setState(STATE::IDLE);
break; break;
case STATE::IDLE: case STATE::IDLE:
case STATE::ROUTING: case STATE::ROUTING:
this->_setState(STATE::ROUTING); this->_setState(STATE::ROUTING);
qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: update."; qCDebug(RouteComplexItemLog) << "rebuildTransectsPhase1: update.";
if (!this->_updateWorker()) { if (!this->_updateWorker()) {
this->_setState(STATE::IDLE); this->_setState(STATE::IDLE);
} }
break; break;
} }
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "rebuildTransectsPhase1(): " << "rebuildTransectsPhase1(): "
<< std::chrono::duration_cast<std::chrono::milliseconds>( << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start) std::chrono::high_resolution_clock::now() - start)
...@@ -507,10 +532,9 @@ void CircularSurvey::_rebuildTransectsPhase1(void) { ...@@ -507,10 +532,9 @@ void CircularSurvey::_rebuildTransectsPhase1(void) {
<< " ms"; << " ms";
} }
// no cameraShots in Circular Survey, add if desired void RouteComplexItem::_recalcCameraShots() { _cameraShots = 0; }
void CircularSurvey::_recalcCameraShots() { _cameraShots = 0; }
void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) { void RouteComplexItem::_setTransects(RouteComplexItem::PtrRoutingData pRoute) {
// Store solutions. // Store solutions.
auto ori = this->_areaData->origin(); auto ori = this->_areaData->origin();
ori.setAltitude(0); ori.setAltitude(0);
...@@ -567,19 +591,19 @@ void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) { ...@@ -567,19 +591,19 @@ void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) {
} }
} else { } else {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "_setTransects(): lastTransect.size() == 0"; << "_setTransects(): lastTransect.size() == 0";
} }
} else { } else {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "_setTransects(): firstTransect.size() == 0"; << "_setTransects(): firstTransect.size() == 0";
} }
} else { } else {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "_setTransects(): transectsInfo.size() <= 1"; << "_setTransects(): transectsInfo.size() <= 1";
} }
} else { } else {
qCDebug(CircularSurveyLog) << "_setTransects(): solution.size() == 0"; qCDebug(RouteComplexItemLog) << "_setTransects(): solution.size() == 0";
} }
if (var.size() > 0 && var.front().size() > 0) { if (var.size() > 0 && var.front().size() > 0) {
...@@ -609,38 +633,25 @@ void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) { ...@@ -609,38 +633,25 @@ void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) {
emit variantNamesChanged(); emit variantNamesChanged();
disconnect(&this->_variant, &Fact::rawValueChanged, this, disconnect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant); &RouteComplexItem::_changeVariant);
this->_variant.setCookedValue(QVariant(0)); this->_variant.setCookedValue(QVariant(0));
connect(&this->_variant, &Fact::rawValueChanged, this, connect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant); &RouteComplexItem::_changeVariant);
this->_changeVariantWorker(); this->_changeVariantWorker();
this->_setState(STATE::SKIPP); this->_setState(STATE::SKIPP);
this->_rebuildTransects(); this->_rebuildTransects();
} else { } else {
qCDebug(CircularSurveyLog) qCDebug(RouteComplexItemLog)
<< "_setTransects(): failed, variantVector empty."; << "_setTransects(): failed, variantVector empty.";
this->_setState(STATE::IDLE); this->_setState(STATE::IDLE);
} }
} }
Fact *CircularSurvey::variant() { return &_variant; } Fact *RouteComplexItem::variant() { return &_variant; }
bool CircularSurvey::calculating() const { bool RouteComplexItem::calculating() const {
return this->_calculating(this->_state); return this->_calculating(this->_state);
} }
/*! bool RouteComplexItem::editing() const { return this->_editing; }
\class CircularSurveyComplexItem
\inmodule Wima
\brief The \c CircularSurveyComplexItem class provides a survey mission
item with circular transects around a point of interest.
CircularSurveyComplexItem class provides a survey mission item with
circular transects around a point of interest. Within the \c Wima module
it's used to scan a defined area with constant angle (circular transects)
to the base station (point of interest).
\sa WimaArea
*/
...@@ -7,9 +7,7 @@ ...@@ -7,9 +7,7 @@
#include "SettingsFact.h" #include "SettingsFact.h"
#include "TransectStyleComplexItem.h" #include "TransectStyleComplexItem.h"
#include "Geometry/WimaJoinedAreaData.h" #include "AreaData.h"
#include "Geometry/WimaMeasurementAreaData.h"
#include "WimaPlanData.h"
class RoutingThread; class RoutingThread;
class RoutingData; class RoutingData;
...@@ -18,21 +16,22 @@ namespace routing { ...@@ -18,21 +16,22 @@ namespace routing {
class GeneratorBase; class GeneratorBase;
} }
class CircularSurvey : public TransectStyleComplexItem { class RouteComplexItem : public TransectStyleComplexItem {
Q_OBJECT Q_OBJECT
using PtrGenerator = std::shared_ptr<routing::GeneratorBase>; using PtrGenerator = routing::GeneratorBase *;
using PtrAreaData = AreaData *;
using PtrRoutingData = std::shared_ptr<RoutingData>; using PtrRoutingData = std::shared_ptr<RoutingData>;
using PtrWorker = std::unique_ptr<RoutingThread>; using PtrWorker = RoutingThread *;
using Transects = QList<QList<CoordInfo_t>>; using Transects = QList<QList<CoordInfo_t>>;
using Variant = Transects; using Variant = Transects;
enum class STATE { IDLE, ROUTING, SKIPP, REVERT_PATH, CHANGE_VARIANT }; enum class STATE { IDLE, ROUTING, SKIPP, REVERT_PATH, CHANGE_VARIANT };
public: public:
CircularSurvey(PlanMasterController *masterController, bool flyView, RouteComplexItem(PlanMasterController *masterController, bool flyView,
const QString &kmlOrShpFile, QObject *parent); const QString &kmlOrShpFile, QObject *parent);
~CircularSurvey(); ~RouteComplexItem();
Q_PROPERTY(Fact *variant READ variant CONSTANT) Q_PROPERTY(Fact *variant READ variant CONSTANT)
Q_PROPERTY( Q_PROPERTY(
...@@ -43,14 +42,18 @@ public: ...@@ -43,14 +42,18 @@ public:
Q_PROPERTY( Q_PROPERTY(
routing::GeneratorBase *generator READ generator NOTIFY generatorChanged) routing::GeneratorBase *generator READ generator NOTIFY generatorChanged)
Q_PROPERTY(int generatorIndex READ generatorIndex NOTIFY generatorChanged) Q_PROPERTY(int generatorIndex READ generatorIndex NOTIFY generatorChanged)
Q_PROPERTY(bool editing READ editing NOTIFY editingChanged)
Q_PROPERTY(AreaData *areaData READ areaData NOTIFY areaDataChanged)
Q_INVOKABLE void revert_path(void); Q_INVOKABLE void revertPath(void);
// Property getters // Property getters
const AreaData &planData() const; const AreaData *areaData() const;
AreaData *areaData();
Fact *variant(); Fact *variant();
QStringList variantNames() const; QStringList variantNames() const;
bool calculating() const; bool calculating() const;
bool editing() const;
// Overrides // Overrides
virtual bool load(const QJsonObject &complexObject, int sequenceNumber, virtual bool load(const QJsonObject &complexObject, int sequenceNumber,
...@@ -67,8 +70,7 @@ public: ...@@ -67,8 +70,7 @@ public:
virtual QString patternName(void) const override; virtual QString patternName(void) const override;
// Generator // Generator
bool registerGenerator(const QString &name, bool registerGenerator(const QString &name, routing::GeneratorBase *g);
std::shared_ptr<routing::GeneratorBase> g);
bool unregisterGenerator(const QString &name); bool unregisterGenerator(const QString &name);
bool unregisterGenerator(int index); bool unregisterGenerator(int index);
Q_INVOKABLE bool switchToGenerator(const QString &name); Q_INVOKABLE bool switchToGenerator(const QString &name);
...@@ -77,6 +79,22 @@ public: ...@@ -77,6 +79,22 @@ public:
routing::GeneratorBase *generator(); routing::GeneratorBase *generator();
int generatorIndex(); int generatorIndex();
// Editing.
//!
//! \brief editingStart Starts area data editing.
//!
//! Starts area data editing. Transects will not be updated bewteen a call
//! sequence of editingStart() and editingStop().
//!
void editingStart();
//!
//! \brief editingStop Stops area editing.
//!
//! Stops area editing. Will reset area data to the state before
//! editingStart() if it is invalid. Triggers a transect update.
//!
void editingStop();
static const char *settingsGroup; static const char *settingsGroup;
static const char *variantName; static const char *variantName;
static const char *jsonComplexItemTypeValue; static const char *jsonComplexItemTypeValue;
...@@ -87,6 +105,8 @@ signals: ...@@ -87,6 +105,8 @@ signals:
void variantNamesChanged(); void variantNamesChanged();
void generatorNameListChanged(); void generatorNameListChanged();
void generatorChanged(); void generatorChanged();
void editingChanged();
void areaDataChanged();
private slots: private slots:
// Overrides from TransectStyleComplexItem // Overrides from TransectStyleComplexItem
...@@ -101,20 +121,25 @@ private slots: ...@@ -101,20 +121,25 @@ private slots:
void _reverseWorker(); void _reverseWorker();
private: private:
bool _switchToGenerator(const PtrGenerator &newG); bool _setGenerator(PtrGenerator newG);
void _setState(STATE state); void _setState(STATE state);
bool _calculating(STATE state) const; bool _calculating(STATE state) const;
void _setEditing(bool editing);
void _setAreaData(PtrAreaData data);
// State. // State.
STATE _state; STATE _state;
// center of the circular lanes, e.g. base station // Facts
QMap<QString, FactMetaData *> _metaDataMap; QMap<QString, FactMetaData *> _metaDataMap;
SettingsFact _variant; SettingsFact _variant;
QStringList _variantNames; QStringList _variantNames;
// Area data // Area data
AreaData _areaData; PtrAreaData _areaData;
PtrAreaData _editorData;
PtrAreaData _currentData;
bool _editing;
// Generators // Generators
QList<PtrGenerator> _generatorList; QList<PtrGenerator> _generatorList;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QThread> #include <QThread>
#include "snake.h" #include "geometry/snake.h"
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <functional> #include <functional>
......
#include "GeoArea.h"
#include <QDebug>
const char *GeoArea::wimaAreaName = "GeoArea";
const char *GeoArea::areaTypeName = "AreaType";
const char *GeoArea::settingsGroup = "GeoArea";
// Constructors
GeoArea::GeoArea(QObject *parent) : QGCMapPolygon(parent) { init(); }
GeoArea::GeoArea(const GeoArea &other, QObject *parent)
: QGCMapPolygon(other, parent) {
init();
}
GeoArea &GeoArea::operator=(const GeoArea &other) {
QGCMapPolygon::operator=(other);
return *this;
}
void GeoArea::saveToJson(QJsonObject &json) {
this->QGCMapPolygon::saveToJson(json);
}
bool GeoArea::loadFromJson(const QJsonObject &json, QString &errorString) {
if (!this->QGCMapPolygon::loadFromJson(json, false /*no poly required*/,
errorString)) {
qWarning() << errorString;
return false;
}
return true;
}
bool GeoArea::isSimplePolygon() {
qWarning() << "WimaArea::isSimplePolygon: impl. missing.";
return false;
}
void GeoArea::init() { this->setObjectName(wimaAreaName); }
bool copyAreaList(const QmlObjectListModel &from, QmlObjectListModel &to,
QObject *parent) {
// Check if elements are valid.
for (int i = 0; i < from.count(); ++i) {
auto obj = from[i];
auto area = qobject_cast<const GeoArea *>(obj);
if (area == nullptr) {
return false;
}
}
// Clone elements.
for (int i = 0; i < from.count(); ++i) {
auto obj = from[i];
auto area = qobject_cast<const GeoArea *>(obj);
to.append(area->clone(parent));
}
return true;
}
#pragma once
#include <QObject>
#include "QGCMapPolygon.h"
class GeoArea : public QGCMapPolygon {
Q_OBJECT
public:
GeoArea(QObject *parent = nullptr);
GeoArea(const GeoArea &other, QObject *parent = nullptr);
GeoArea &operator=(const GeoArea &other);
Q_PROPERTY(QString mapVisualQML READ mapVisualQML CONSTANT)
Q_PROPERTY(QString editorQML READ editorQML CONSTANT)
virtual QString mapVisualQML(void) const = 0;
virtual QString editorQML(void) const = 0;
virtual void saveToJson(QJsonObject &jsonObject);
virtual bool loadFromJson(const QJsonObject &jsonObject,
QString &errorString);
virtual GeoArea *clone(QObject *parent = nullptr) const = 0;
bool isSimplePolygon();
// static Members
static const char *wimaAreaName;
static const char *areaTypeName;
static const char *settingsGroup;
private:
void init();
};
// Example usage:
// QmlObjecListModel list;
// .... add areas ....
// auto area = getArea<WimaMeasurementArea *>(list); // returns the first
// WimaMeasurementArea or nullptr
template <class AreaPtr, class QObjectList>
inline AreaPtr getGeoArea(QObjectList &list) {
static_assert(std::is_pointer<AreaPtr>::value,
"AreaPtr must be a pointer type.");
for (int i = 0; i < list.count(); ++i) {
auto obj = list[i];
auto area = qobject_cast<AreaPtr>(obj);
if (area != nullptr) {
return area;
}
}
return nullptr;
}
bool copyAreaList(const QmlObjectListModel &from, QmlObjectListModel &to,
QObject *parent);
#include "WimaMeasurementArea.h" #include "MeasurementArea.h"
#include "QtConcurrentRun" #include "QtConcurrentRun"
#include "SnakeTile.h" #include "nemo_interface/SnakeTile.h"
#include "snake.h" #include "snake.h"
#include <boost/units/systems/si.hpp> #include <boost/units/systems/si.hpp>
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#define SNAKE_MAX_TILES 1000 #define SNAKE_MAX_TILES 1000
#endif #endif
QGC_LOGGING_CATEGORY(WimaMeasurementAreaLog, "WimaMeasurementAreaLog") QGC_LOGGING_CATEGORY(MeasurementAreaLog, "MeasurementAreaLog")
TileData::TileData() : tiles(this) {} TileData::TileData() : tiles(this) {}
...@@ -25,7 +25,7 @@ TileData &TileData::operator=(const TileData &other) { ...@@ -25,7 +25,7 @@ TileData &TileData::operator=(const TileData &other) {
if (tile != nullptr) { if (tile != nullptr) {
this->tiles.append(new SnakeTile(*tile, this)); this->tiles.append(new SnakeTile(*tile, this));
} else { } else {
qCWarning(WimaMeasurementAreaLog) << "TileData::operator=: nullptr"; qCWarning(MeasurementAreaLog) << "TileData::operator=: nullptr";
} }
} }
this->tileCenterPoints = other.tileCenterPoints; this->tileCenterPoints = other.tileCenterPoints;
...@@ -63,15 +63,15 @@ size_t TileData::size() const { ...@@ -63,15 +63,15 @@ size_t TileData::size() const {
} }
} }
const char *WimaMeasurementArea::settingsGroup = "MeasurementArea"; const char *MeasurementArea::settingsGroup = "MeasurementArea";
const char *WimaMeasurementArea::tileHeightName = "TileHeight"; const char *MeasurementArea::tileHeightName = "TileHeight";
const char *WimaMeasurementArea::tileWidthName = "TileWidth"; const char *MeasurementArea::tileWidthName = "TileWidth";
const char *WimaMeasurementArea::minTileAreaName = "MinTileAreaPercent"; const char *MeasurementArea::minTileAreaName = "MinTileAreaPercent";
const char *WimaMeasurementArea::showTilesName = "ShowTiles"; const char *MeasurementArea::showTilesName = "ShowTiles";
const char *WimaMeasurementArea::WimaMeasurementAreaName = "Measurement Area"; const char *MeasurementArea::MeasurementAreaName = "Measurement Area";
WimaMeasurementArea::WimaMeasurementArea(QObject *parent) MeasurementArea::MeasurementArea(QObject *parent)
: WimaArea(parent), : GeoArea(parent),
_metaDataMap(FactMetaData::createMapFromJsonFile( _metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"), QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
this /* QObject parent */)), this /* QObject parent */)),
...@@ -88,9 +88,8 @@ WimaMeasurementArea::WimaMeasurementArea(QObject *parent) ...@@ -88,9 +88,8 @@ WimaMeasurementArea::WimaMeasurementArea(QObject *parent)
init(); init();
} }
WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other, MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
QObject *parent) : GeoArea(other, parent),
: WimaArea(other, parent),
_metaDataMap(FactMetaData::createMapFromJsonFile( _metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"), QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
this /* QObject parent */)), this /* QObject parent */)),
...@@ -107,79 +106,71 @@ WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other, ...@@ -107,79 +106,71 @@ WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other,
init(); init();
} }
/*! MeasurementArea &MeasurementArea::operator=(const MeasurementArea &other) {
* \overload operator=() GeoArea::operator=(other);
*
* Calls the inherited operator WimaArea::operator=().
*/
WimaMeasurementArea &WimaMeasurementArea::
operator=(const WimaMeasurementArea &other) {
WimaArea::operator=(other);
return *this; return *this;
} }
WimaMeasurementArea::~WimaMeasurementArea() {} MeasurementArea::~MeasurementArea() {}
QString WimaMeasurementArea::mapVisualQML() const { QString MeasurementArea::mapVisualQML() const {
return QStringLiteral("WimaMeasurementAreaMapVisual.qml"); return QStringLiteral("MeasurementAreaMapVisual.qml");
} }
QString WimaMeasurementArea::editorQML() const { QString MeasurementArea::editorQML() const {
return QStringLiteral("WimaMeasurementAreaEditor.qml"); return QStringLiteral("MeasurementAreaEditor.qml");
} }
Fact *WimaMeasurementArea::tileHeight() { return &_tileHeight; } MeasurementArea *MeasurementArea::clone(QObject *parent) const {
return new MeasurementArea(*this, parent);
}
Fact *WimaMeasurementArea::tileWidth() { return &_tileWidth; } Fact *MeasurementArea::tileHeight() { return &_tileHeight; }
Fact *WimaMeasurementArea::minTileArea() { return &_minTileAreaPercent; } Fact *MeasurementArea::tileWidth() { return &_tileWidth; }
Fact *WimaMeasurementArea::showTiles() { return &_showTiles; } Fact *MeasurementArea::minTileArea() { return &_minTileAreaPercent; }
QmlObjectListModel *WimaMeasurementArea::tiles() { Fact *MeasurementArea::showTiles() { return &_showTiles; }
return &this->_tileData.tiles;
}
const QVector<int> &WimaMeasurementArea::progress() const { QmlObjectListModel *MeasurementArea::tiles() { return &this->_tileData.tiles; }
return this->_progress;
}
QVector<int> WimaMeasurementArea::progressQml() const { const QVector<int> &MeasurementArea::progress() const {
return this->_progress; return this->_progress;
} }
const QmlObjectListModel *WimaMeasurementArea::tiles() const { QVector<int> MeasurementArea::progressQml() const { return this->_progress; }
const QmlObjectListModel *MeasurementArea::tiles() const {
return &this->_tileData.tiles; return &this->_tileData.tiles;
} }
const QVariantList &WimaMeasurementArea::tileCenterPoints() const { const QVariantList &MeasurementArea::tileCenterPoints() const {
return this->_tileData.tileCenterPoints; return this->_tileData.tileCenterPoints;
} }
const TileData &WimaMeasurementArea::tileData() const { const TileData &MeasurementArea::tileData() const { return this->_tileData; }
return this->_tileData;
}
int WimaMeasurementArea::maxTiles() const { return SNAKE_MAX_TILES; } int MeasurementArea::maxTiles() const { return SNAKE_MAX_TILES; }
bool WimaMeasurementArea::ready() const { return this->_state == STATE::IDLE; } bool MeasurementArea::ready() const { return this->_state == STATE::IDLE; }
void WimaMeasurementArea::saveToJson(QJsonObject &json) { void MeasurementArea::saveToJson(QJsonObject &json) {
if (ready()) { if (ready()) {
this->WimaArea::saveToJson(json); this->GeoArea::saveToJson(json);
json[tileHeightName] = _tileHeight.rawValue().toDouble(); json[tileHeightName] = _tileHeight.rawValue().toDouble();
json[tileWidthName] = _tileWidth.rawValue().toDouble(); json[tileWidthName] = _tileWidth.rawValue().toDouble();
json[minTileAreaName] = _minTileAreaPercent.rawValue().toDouble(); json[minTileAreaName] = _minTileAreaPercent.rawValue().toDouble();
json[showTilesName] = _showTiles.rawValue().toBool(); json[showTilesName] = _showTiles.rawValue().toBool();
json[areaTypeName] = WimaMeasurementAreaName; json[areaTypeName] = MeasurementAreaName;
} else { } else {
qCDebug(WimaMeasurementAreaLog) << "saveToJson(): not ready for saveing."; qCDebug(MeasurementAreaLog) << "saveToJson(): not ready for saveing.";
} }
} }
bool WimaMeasurementArea::loadFromJson(const QJsonObject &json, bool MeasurementArea::loadFromJson(const QJsonObject &json,
QString &errorString) { QString &errorString) {
if (this->WimaArea::loadFromJson(json, errorString)) { if (this->GeoArea::loadFromJson(json, errorString)) {
disableUpdate(); disableUpdate();
bool retVal = true; bool retVal = true;
...@@ -220,7 +211,7 @@ bool WimaMeasurementArea::loadFromJson(const QJsonObject &json, ...@@ -220,7 +211,7 @@ bool WimaMeasurementArea::loadFromJson(const QJsonObject &json,
} }
} }
bool WimaMeasurementArea::setProgress(const QVector<int> &p) { bool MeasurementArea::setProgress(const QVector<int> &p) {
if (ready()) { if (ready()) {
if (p.size() == this->tiles()->count() && this->_progress != p) { if (p.size() == this->tiles()->count() && this->_progress != p) {
this->_progress = p; this->_progress = p;
...@@ -232,10 +223,10 @@ bool WimaMeasurementArea::setProgress(const QVector<int> &p) { ...@@ -232,10 +223,10 @@ bool WimaMeasurementArea::setProgress(const QVector<int> &p) {
return false; return false;
} }
//! //!
//! \brief WimaMeasurementArea::doUpdate //! \brief MeasurementArea::doUpdate
//! \pre WimaMeasurementArea::deferUpdate must be called first, don't call //! \pre MeasurementArea::deferUpdate must be called first, don't call
//! this function directly! //! this function directly!
void WimaMeasurementArea::doUpdate() { void MeasurementArea::doUpdate() {
using namespace snake; using namespace snake;
using namespace boost::units; using namespace boost::units;
...@@ -292,7 +283,7 @@ void WimaMeasurementArea::doUpdate() { ...@@ -292,7 +283,7 @@ void WimaMeasurementArea::doUpdate() {
} }
pData->moveToThread(th); pData->moveToThread(th);
qCDebug(WimaMeasurementAreaLog) qCDebug(MeasurementAreaLog)
<< "doUpdate(): update time: " << "doUpdate(): update time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>( << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start) std::chrono::high_resolution_clock::now() - start)
...@@ -305,7 +296,7 @@ void WimaMeasurementArea::doUpdate() { ...@@ -305,7 +296,7 @@ void WimaMeasurementArea::doUpdate() {
this->_watcher.setFuture(future); this->_watcher.setFuture(future);
} }
} }
qCDebug(WimaMeasurementAreaLog) qCDebug(MeasurementAreaLog)
<< "doUpdate(): execution time: " << "doUpdate(): execution time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>( << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start) std::chrono::high_resolution_clock::now() - start)
...@@ -313,9 +304,9 @@ void WimaMeasurementArea::doUpdate() { ...@@ -313,9 +304,9 @@ void WimaMeasurementArea::doUpdate() {
<< " ms"; << " ms";
} }
void WimaMeasurementArea::deferUpdate() { void MeasurementArea::deferUpdate() {
if (this->_state == STATE::IDLE || this->_state == STATE::DEFERED) { if (this->_state == STATE::IDLE || this->_state == STATE::DEFERED) {
qCDebug(WimaMeasurementAreaLog) << "defereUpdate(): defer update."; qCDebug(MeasurementAreaLog) << "defereUpdate(): defer update.";
if (this->_state == STATE::IDLE) { if (this->_state == STATE::IDLE) {
this->_progress.clear(); this->_progress.clear();
this->_tileData.clear(); this->_tileData.clear();
...@@ -325,16 +316,16 @@ void WimaMeasurementArea::deferUpdate() { ...@@ -325,16 +316,16 @@ void WimaMeasurementArea::deferUpdate() {
this->setState(STATE::DEFERED); this->setState(STATE::DEFERED);
this->_timer.start(100); this->_timer.start(100);
} else if (this->_state == STATE::UPDATEING) { } else if (this->_state == STATE::UPDATEING) {
qCDebug(WimaMeasurementAreaLog) << "defereUpdate(): restart."; qCDebug(MeasurementAreaLog) << "defereUpdate(): restart.";
setState(STATE::RESTARTING); setState(STATE::RESTARTING);
} }
} }
void WimaMeasurementArea::storeTiles() { void MeasurementArea::storeTiles() {
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
if (this->_state == STATE::UPDATEING) { if (this->_state == STATE::UPDATEING) {
qCDebug(WimaMeasurementAreaLog) << "storeTiles(): update."; qCDebug(MeasurementAreaLog) << "storeTiles(): update.";
this->_tileData = *this->_watcher.result(); this->_tileData = *this->_watcher.result();
// This is expensive. Drawing tiles is expensive too. // This is expensive. Drawing tiles is expensive too.
...@@ -343,12 +334,12 @@ void WimaMeasurementArea::storeTiles() { ...@@ -343,12 +334,12 @@ void WimaMeasurementArea::storeTiles() {
emit this->tilesChanged(); emit this->tilesChanged();
setState(STATE::IDLE); setState(STATE::IDLE);
} else if (this->_state == STATE::RESTARTING) { } else if (this->_state == STATE::RESTARTING) {
qCDebug(WimaMeasurementAreaLog) << "storeTiles(): restart."; qCDebug(MeasurementAreaLog) << "storeTiles(): restart.";
doUpdate(); doUpdate();
} else if (this->_state == STATE::STOP) { } else if (this->_state == STATE::STOP) {
qCDebug(WimaMeasurementAreaLog) << "storeTiles(): stop."; qCDebug(MeasurementAreaLog) << "storeTiles(): stop.";
} }
qCDebug(WimaMeasurementAreaLog) qCDebug(MeasurementAreaLog)
<< "storeTiles() execution time: " << "storeTiles() execution time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>( << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start) std::chrono::high_resolution_clock::now() - start)
...@@ -356,36 +347,34 @@ void WimaMeasurementArea::storeTiles() { ...@@ -356,36 +347,34 @@ void WimaMeasurementArea::storeTiles() {
<< " ms"; << " ms";
} }
void WimaMeasurementArea::disableUpdate() { void MeasurementArea::disableUpdate() {
setState(STATE::IDLE); setState(STATE::IDLE);
this->_timer.stop(); this->_timer.stop();
} }
void WimaMeasurementArea::enableUpdate() { void MeasurementArea::enableUpdate() {
if (this->_state == STATE::STOP) { if (this->_state == STATE::STOP) {
setState(STATE::IDLE); setState(STATE::IDLE);
} }
} }
void WimaMeasurementArea::init() { void MeasurementArea::init() {
this->setObjectName(WimaMeasurementAreaName); this->setObjectName(MeasurementAreaName);
connect(&this->_tileHeight, &Fact::rawValueChanged, this, connect(&this->_tileHeight, &Fact::rawValueChanged, this,
&WimaMeasurementArea::deferUpdate); &MeasurementArea::deferUpdate);
connect(&this->_tileWidth, &Fact::rawValueChanged, this, connect(&this->_tileWidth, &Fact::rawValueChanged, this,
&WimaMeasurementArea::deferUpdate); &MeasurementArea::deferUpdate);
connect(&this->_minTileAreaPercent, &Fact::rawValueChanged, this, connect(&this->_minTileAreaPercent, &Fact::rawValueChanged, this,
&WimaMeasurementArea::deferUpdate); &MeasurementArea::deferUpdate);
connect(this, &WimaArea::pathChanged, this, connect(this, &GeoArea::pathChanged, this, &MeasurementArea::deferUpdate);
&WimaMeasurementArea::deferUpdate);
this->_timer.setSingleShot(true); this->_timer.setSingleShot(true);
connect(&this->_timer, &QTimer::timeout, this, connect(&this->_timer, &QTimer::timeout, this, &MeasurementArea::doUpdate);
&WimaMeasurementArea::doUpdate);
connect(&this->_watcher, connect(&this->_watcher,
&QFutureWatcher<std::unique_ptr<QmlObjectListModel>>::finished, this, &QFutureWatcher<std::unique_ptr<QmlObjectListModel>>::finished, this,
&WimaMeasurementArea::storeTiles); &MeasurementArea::storeTiles);
} }
void WimaMeasurementArea::setState(WimaMeasurementArea::STATE s) { void MeasurementArea::setState(MeasurementArea::STATE s) {
if (this->_state != s) { if (this->_state != s) {
auto oldState = this->_state; auto oldState = this->_state;
this->_state = s; this->_state = s;
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QObject> #include <QObject>
#include <QSharedPointer>
#include <QTimer> #include <QTimer>
#include "WimaArea.h" #include "GeoArea.h"
#include "SettingsFact.h" #include "SettingsFact.h"
...@@ -24,17 +25,16 @@ public: ...@@ -24,17 +25,16 @@ public:
std::size_t size() const; std::size_t size() const;
}; };
class WimaMeasurementArea : public WimaArea { class MeasurementArea : public GeoArea {
Q_OBJECT Q_OBJECT
enum class STATE { IDLE, DEFERED, UPDATEING, RESTARTING, STOP }; enum class STATE { IDLE, DEFERED, UPDATEING, RESTARTING, STOP };
using DataPtr = QSharedPointer<TileData>;
public: public:
WimaMeasurementArea(QObject *parent = nullptr); MeasurementArea(QObject *parent = nullptr);
WimaMeasurementArea(const WimaMeasurementArea &other, MeasurementArea(const MeasurementArea &other, QObject *parent = nullptr);
QObject *parent = nullptr); MeasurementArea &operator=(const MeasurementArea &other);
WimaMeasurementArea &operator=(const WimaMeasurementArea &other); ~MeasurementArea();
~WimaMeasurementArea();
Q_PROPERTY(Fact *tileHeight READ tileHeight CONSTANT) Q_PROPERTY(Fact *tileHeight READ tileHeight CONSTANT)
Q_PROPERTY(Fact *tileWidth READ tileWidth CONSTANT) Q_PROPERTY(Fact *tileWidth READ tileWidth CONSTANT)
...@@ -44,9 +44,12 @@ public: ...@@ -44,9 +44,12 @@ public:
Q_PROPERTY(int maxTiles READ maxTiles NOTIFY maxTilesChanged) Q_PROPERTY(int maxTiles READ maxTiles NOTIFY maxTilesChanged)
Q_PROPERTY(QVector<int> progress READ progressQml NOTIFY progressChanged) Q_PROPERTY(QVector<int> progress READ progressQml NOTIFY progressChanged)
// Overrides from WimaPolygon // Overrides from GeoArea
QString mapVisualQML(void) const; QString mapVisualQML(void) const override;
QString editorQML(void) const; QString editorQML(void) const override;
MeasurementArea *clone(QObject *parent = nullptr) const;
void saveToJson(QJsonObject &json) override;
bool loadFromJson(const QJsonObject &json, QString &errorString) override;
// Property getters. // Property getters.
Fact *tileHeight(); Fact *tileHeight();
...@@ -62,17 +65,13 @@ public: ...@@ -62,17 +65,13 @@ public:
int maxTiles() const; int maxTiles() const;
bool ready() const; bool ready() const;
// Member Methodes
void saveToJson(QJsonObject &json);
bool loadFromJson(const QJsonObject &json, QString &errorString);
// Static Variables // Static Variables
static const char *settingsGroup; static const char *settingsGroup;
static const char *tileHeightName; static const char *tileHeightName;
static const char *tileWidthName; static const char *tileWidthName;
static const char *minTileAreaName; static const char *minTileAreaName;
static const char *showTilesName; static const char *showTilesName;
static const char *WimaMeasurementAreaName; static const char *MeasurementAreaName;
signals: signals:
void tilesChanged(); void tilesChanged();
...@@ -109,7 +108,6 @@ private: ...@@ -109,7 +108,6 @@ private:
// Tile stuff. // Tile stuff.
// Tile stuff. // Tile stuff.
mutable QTimer _timer; mutable QTimer _timer;
using DataPtr = std::shared_ptr<TileData>;
mutable STATE _state; mutable STATE _state;
mutable TileData _tileData; mutable TileData _tileData;
mutable QFutureWatcher<DataPtr> _watcher; mutable QFutureWatcher<DataPtr> _watcher;
......
#include "PlanimetryCalculus.h"
template <int k> qreal get(QPointF &p);
template <> qreal get<0>(QPointF &p) { return p.x(); }
template <> qreal get<1>(QPointF &p) { return p.y(); }
namespace PlanimetryCalculus {
namespace {
/*!
\fn IntersectType intersects(const Circle &circle, const QLineF &line,
PointList &intersectionPoints, bool calcInstersect) Returns the Intersection
type of \a circle and \a line. Stores the intersection points in \a
intersectionPoints if \a calcIntersect is \c true. Returns \c Error if either
line or circe \c {isNull() == true}.
\sa QPointF, Circle
*/
bool intersects(const Circle &circle, const QLineF &line,
QPointFVector &intersectionPoints, IntersectType &type,
bool calcInstersect) {
if (!line.isNull()) {
QPointF translationVector = line.p1();
double alpha = angle(line); // angle between wold and line coordinate system
QPointF originCircleL = circle.origin() - translationVector;
rotateReference(originCircleL,
-alpha); // circle origin in line corrdinate system
double y = originCircleL.y();
double r = circle.radius();
if (qAbs(y) > r) {
type = NoIntersection;
return false;
} else if (qFuzzyCompare(qFabs(y), r)) { // tangent
double x_ori = originCircleL.x();
if (x_ori >= 0 && x_ori <= line.length()) {
if (calcInstersect) {
QPointF intersectionPt = QPointF(x_ori, 0);
rotateReference(intersectionPt, alpha);
intersectionPoints.append(intersectionPt + translationVector);
}
type = Tangent;
return true;
}
type = NoIntersection;
return false;
} else { // secant
double x_ori = originCircleL.x();
double y_ori = originCircleL.y();
double delta = qSqrt(qPow(r, 2) - qPow(y_ori, 2));
double x1 =
x_ori +
delta; // x coordinate (line system) of fist intersection point
double x2 =
x_ori -
delta; // x coordinate (line system) of second intersection point
bool doesIntersect =
false; // remember if actual intersection was on the line
if (x1 >= 0 &&
x1 <= line.length()) { // check if intersection point is on the line
if (calcInstersect) {
QPointF intersectionPt =
QPointF(x1, 0); // first intersection point (line system)
rotateReference(intersectionPt, alpha);
intersectionPoints.append(
intersectionPt +
translationVector); // transform (to world system) and append
// first intersection point
}
doesIntersect = true;
}
if (x2 >= 0 &&
x2 <= line.length()) { // check if intersection point is on the line
if (calcInstersect) {
QPointF intersectionPt =
QPointF(x2, 0); // second intersection point (line system)
rotateReference(intersectionPt, alpha);
intersectionPoints.append(
intersectionPt +
translationVector); // transform (to world system) and append
// second intersection point
}
doesIntersect = true;
}
type = doesIntersect ? Secant : NoIntersection;
return doesIntersect ? true : false;
}
}
type = Error;
return false;
}
/*!
\fn bool intersects(const Circle &circle1, const Circle &circle2, PointList
&intersectionPoints, IntersectType type) Calculates the intersection points
of two circles if present and stores the result in \a intersectionPoints.
Returns the intersection type of the two cirles \a circle1 and \a circle2.
The function assumes that the list \a intersectionPoints is empty.
\note Returns Error if circle.isNull() returns true;
\sa Circle
*/
bool intersects(const Circle &circle1, const Circle &circle2,
QPointFVector &intersectionPoints, IntersectType type,
bool calcIntersection) {
double r1 = circle1.radius();
double r2 = circle2.radius();
double d = distance(circle1.origin(), circle2.origin());
double r = 0;
double R = 0;
if (r1 > r2) {
R = r1; // large
r = r2; // smallline1
} else {
// this branch is also choosen if r1 == r2
R = r2;
r = r1;
}
// determine intersection type
if (r + d < R) {
// this branch is also reached if d < rLarge && rSmall == 0
type = InsideNoIntersection;
return false;
} else if (qFuzzyCompare(r + d, R)) {
if (qFuzzyIsNull(d)) {
type = CirclesEqual;
return true;
} else {
type = InsideTouching;
}
} else if (d < R) {
type = InsideIntersection;
} else if (d - r < R) {
type = OutsideIntersection;
} else if (qFuzzyCompare(d - r, R)) {
type = OutsideTouching;
} else {
type = OutsideNoIntersection;
return false;
}
if (calcIntersection) {
// calculate intersection
// Coordinate system circle1: origin = circle1.origin(), x-axis towars
// circle2.origin() y-axis such that the coordinate system is dextrorse
// with z-axis outward faceing with respect to the drawing plane.
double alpha =
angle(circle1.origin(),
circle2.origin()); // angle between world and circle1 system
QPointF translationVector =
circle1.origin(); // translation vector between world and circle1 system
if (type == InsideTouching || type == OutsideTouching) {
// Intersection point in coordinate system of circle 1.
// Coordinate system circle1: origin = circle1.origin(), x-axis towars
// circle2.origin() y-axis such that the coordinate system is dextrorse
// with z-axis outward faceing with respect to the drawing plane.
intersectionPoints.append(rotateReturn(QPointF(0, r1), alpha) +
translationVector);
} else { // triggered if ( type == InsideIntersection
// || type == OutsideIntersection)
// See fist branch for explanation
// this equations are obtained by solving x^2+y^2=R^2 and (x -
// d)^2+y^2=r^2
double x = (qPow(d, 2) - qPow(r, 2) + qPow(R, 2)) / 2 / d;
double y = 1 / 2 / d *
qSqrt(4 * qPow(d * R, 2) -
qPow(qPow(d, 2) - qPow(r, 2) + qPow(R, 2), 2));
intersectionPoints.append(rotateReturn(QPointF(x, y), alpha) +
translationVector);
intersectionPoints.append(rotateReturn(QPointF(x, -y), alpha) +
translationVector);
}
// Transform the coordinate to the world coordinate system. Alpha is the
// angle between world and circle1 coordinate system.
return true;
}
type = Error;
return false;
}
} // end anonymous namespace
/*!
\fn void rotatePoint(QPointF &point, double alpha)
Rotates the \a point counter clockwisely by the angle \a alpha (in
radiants).
*/
void rotateReference(QPointF &point, double alpha) {
if (!point.isNull()) {
double x = point.x();
double y = point.y();
point.setX(x * qCos(alpha) - y * qSin(alpha));
point.setY(x * qSin(alpha) + y * qCos(alpha));
}
}
void rotateReference(QPointFVector &points, double alpha) {
for (int i = 0; i < points.size(); i++) {
rotateReference(points[i], alpha);
}
}
/*!
\fn void rotatePointDegree(QPointF &point, double alpha)
Rotates the \a point counter clockwisely by the angle \a alpha (in degrees).
*/
void rotateReferenceDegree(QPointF &point, double alpha) {
rotateReference(point, alpha / 180 * M_PI);
}
void rotateReferenceDegree(QPointFVector &points, double alpha) {
for (int i = 0; i < points.size(); i++) {
rotateReferenceDegree(points[i], alpha);
}
}
/*!
\fn IntersectType intersects(const Circle &circle, const QLineF &line)
Returns the Intersection type of \a circle and \a line.
Returns \c Error if either line or circe \c {isNull() == true}.
\sa QPointF, Circle
*/
bool intersects(const Circle &circle, const QLineF &line) {
QPointFVector dummyList;
IntersectType type = NoIntersection;
return intersects(circle, line, dummyList, type,
false /* calculate intersection points*/);
}
bool intersects(const Circle &circle, const QLineF &line,
QPointFVector &intersectionPoints) {
IntersectType type = NoIntersection;
return intersects(circle, line, intersectionPoints, type,
true /* calculate intersection points*/);
}
/*!
\fn double distance(const QPointF &p1, const QPointF p2)
Calculates the distance (2-norm) between \a p1 and \a p2.
\sa QPointF
*/
double distance(const QPointF &p1, const QPointF p2) {
double dx = p2.x() - p1.x();
double dy = p2.y() - p1.y();
return norm(dx, dy);
}
/*!
\fn double distance(const QPointF &p1, const QPointF p2)
Calculates the angle (in radiants) between the line defined by \a p1 and \a
p2 and the x-axis according to the following rule. Angle = qAtan2(dy, dx),
where dx = p2.x()-p1.x() and dy = p2.y()-p1.y().
\note The order of \a p1 and \a p2 matters. Swapping \a p1 and \a p2 will
result in a angle of oposite sign. \sa QPointF
*/
double angle(const QPointF &p1, const QPointF p2) {
double dx = p2.x() - p1.x();
double dy = p2.y() - p1.y();
return qAtan2(dy, dx);
}
/*!
\fn double distance(const QPointF &p1, const QPointF p2)
Calculates the angle (in degrees) between the line defined by \a p1 and \a
p2 and the x-axis according to the following rule. Angle = qAtan2(dy,
dx)*180/pi, where dx = p2.x()-p1.x() and dy = p2.y()-p1.y(). angle \note The
order of \a p1 and \a p2 matters. Swapping \a p1 and \a p2 will result in a
angle of oposite sign. \sa QPointF
*/
double angleDegree(const QPointF &p1, const QPointF p2) {
return angle(p1, p2) * 180 / M_PI;
}
double truncateAngle(double angle) {
while (angle < 0) {
angle += 2 * M_PI;
}
while (angle > 2 * M_PI) {
angle -= 2 * M_PI;
}
return angle;
}
double truncateAngleDegree(double angle) {
return truncateAngle(angle / 180 * M_PI);
}
/*!
* \fn IntersectType intersects(const QLineF &line1, const QLineF &line2,
* QPointF &intersectionPt) Determines wheter \a line1 and \a line2 intersect
* and of which type the intersection is. Stores the intersection point in \a
* intersectionPt Returns the intersection type (\c IntersectType).
*
* Intersection Types:
* \c NoIntersection
* \c CornerCornerIntersection; A intersection is present such that two of the
* lines cornes touch. \c EdgeCornerIntersection; A intersection is present such
* that a corner and a edge touch. \c EdgeEdgeIntersection; A intersection is
* present such two edges intersect. \c LinesParallel \c LinesEqual \c Error;
* Returned if at least on line delivers isNULL() == true.
*
*
* \sa QPointF
*/
bool intersects(const QLineF &line1, const QLineF &line2,
QPointF &intersectionPt, IntersectType &type) {
if (line1.isNull() || line2.isNull()) {
type = Error;
return false;
}
// line 1 coordinate system: origin line1.p1(), x-axis towards line1.p2()
QPointF translationVector =
line1.p1(); // translation vector between world and line1 system
double alpha = angle(line1);
double l1 = line1.length();
QLineF line2L1 = line2;
line2L1.translate(-translationVector);
rotateReference(line2L1, -alpha);
double x1 = line2L1.x1();
double x2 = line2L1.x2();
double y1 = line2L1.y1();
double y2 = line2L1.y2();
if (x1 > x2) {
x1 = x2;
x2 = line2L1.x1();
y1 = y2;
y2 = line2L1.y1();
}
double dx = (x2 - x1);
double dy = (y2 - y1);
double xNull = 0; // (xNull, 0) intersection point in line1 system
if (!qFuzzyIsNull(dx)) {
double k = dy / dx;
if (qFuzzyIsNull(k)) {
if (qFuzzyIsNull(x1) && qFuzzyIsNull(y1) && qFuzzyCompare(x2, l1)) {
type = LinesEqual;
return true;
} else {
type = LinesParallel;
return false;
}
}
double d = (y1 * x2 - y2 * x1) / dx;
xNull = -d / k;
} else {
// lines orthogonal
if (signum(y1) != signum(y2)) {
xNull = x1;
} else {
type = NoIntersection;
return false;
}
}
if (xNull >= x1 && xNull <= x2) {
// determine intersection type#include "QVector3D"
if (qFuzzyIsNull(xNull) || qFuzzyCompare(xNull, l1)) {
if (qFuzzyIsNull(y1) || qFuzzyIsNull(y2))
type = CornerCornerIntersection;
else
type = EdgeCornerIntersection;
} else if (xNull > 0 && xNull < l1) {
if (qFuzzyIsNull(y1) || qFuzzyIsNull(y2))
type = EdgeCornerIntersection;
else
type = EdgeEdgeIntersection;
} else {
type = NoIntersection;
return false;
}
} else {
type = NoIntersection;
return false;
}
intersectionPt = QPointF(xNull, 0); // intersection point in line1 system
rotateReference(intersectionPt, alpha);
intersectionPt += translationVector;
return true;
}
/*!IntersectType type = NoIntersection;
* \overload bool intersects(const QPolygonF &polygon, const QLineF &line,
* PointList &intersectionList, NeighbourList &neighbourList, IntersectList
* &typeList) Checks if \a polygon intersect with \a line. Stores the
* intersection points in \a intersectionList.
*
* Stores the indices of the closest two \a area vetices for each of
* coorespoinding intersection points in \a neighbourList. * For example if an
* intersection point is found between the first and the second vertex of the \a
* area the intersection point will be stored in \a intersectionList and the
* indices 1 and 2 will be stored in \a neighbourList. \a neighbourList has
* entries of type \c {QPair<int, int>}, where \c{pair.first} would contain 1
* and \c{pair.second} would contain 2, when relating to the above example.
*
* Returns the \c IntersectionType of each intersection point within a QVector.
*
* \sa QPair, QVector
*/
bool intersects(const QPolygonF &polygon, const QLineF &line,
QPointFVector &intersectionList, NeighbourVector &neighbourList,
IntersectVector &typeList) {
if (polygon.size() >= 2) {
IntersectVector intersectionTypeList;
// Assemble a line form each tow consecutive polygon vertices and check
// whether it intersects with line
for (int i = 0; i < polygon.size(); i++) {
QLineF interatorLine;
QPointF currentVertex = polygon[i];
QPointF nextVertex =
polygon[PolygonCalculus::nextVertexIndex(polygon.size(), i)];
interatorLine.setP1(currentVertex);
interatorLine.setP2(nextVertex);
QPointF intersectionPoint;
IntersectType type;
if (intersects(line, interatorLine, intersectionPoint, type)) {
intersectionList.append(intersectionPoint);
QPair<int, int> neighbours;
neighbours.first = i;
neighbours.second = PolygonCalculus::nextVertexIndex(polygon.size(), i);
neighbourList.append(neighbours);
typeList.append(type);
}
}
if (typeList.size() > 0) {
return true;
} else {
return false;
}
} else {
return false;
}
}
/*!
* \overload IntersectType intersects(const QPolygonF &polygon, const QLineF
* &line) Returns \c true if any intersection between \a polygon and \a line
* exists, \c false else.
*
* \sa QPair, QVector
*/
bool intersects(const QPolygonF &polygon, const QLineF &line) {
QPointFVector dummyGeo;
QVector<QPair<int, int>> dummyNeighbour;
intersects(polygon, line, dummyGeo, dummyNeighbour);
if (dummyGeo.size() > 0)
return true;
return false;
}
void rotateReference(QLineF &line, double alpha) {
line.setP1(rotateReturn(line.p1(), alpha));
line.setP2(rotateReturn(line.p2(), alpha));
}
QPointF rotateReturn(QPointF point, double alpha) {
rotateReference(point, alpha);
return point;
}
QPointFVector rotateReturn(QPointFVector points, double alpha) {
rotateReference(points, alpha);
return points;
}
QLineF rotateReturn(QLineF line, double alpha) {
rotateReference(line, alpha);
return line;
}
bool intersects(const QLineF &line1, const QLineF &line2,
QPointF &intersectionPt) {
IntersectType dummyType;
return intersects(line1, line2, intersectionPt, dummyType);
}
bool intersects(const QPolygonF &polygon, const QLineF &line,
QPointFVector &intersectionList,
NeighbourVector &neighbourList) {
IntersectVector typeList;
return intersects(polygon, line, intersectionList, neighbourList, typeList);
}
bool intersects(const QPolygonF &polygon, const QLineF &line,
QPointFVector &intersectionList, IntersectVector &typeList) {
NeighbourVector neighbourList;
return intersects(polygon, line, intersectionList, neighbourList, typeList);
}
bool intersects(const QPolygonF &polygon, const QLineF &line,
QPointFVector &intersectionList) {
NeighbourVector neighbourList;
IntersectVector typeList;
return intersects(polygon, line, intersectionList, neighbourList, typeList);
}
/*!
\fn IntersectType intersects(const Circle &circle1, const Circle &circle2)
Returns the intersection type of the two cirles \a circle1 and \a circle2.
\note Returns Error if circle.isNull() returns true;
\sa Circle
*/
bool intersects(const Circle &circle1, const Circle &circle2) {
IntersectType type = NoIntersection;
QPointFVector intersectionPoints;
return intersects(circle1, circle2, intersectionPoints, type,
false /*calculate intersection points*/);
}
bool intersects(const Circle &circle1, const Circle &circle2,
IntersectType &type) {
QPointFVector intersectionPoints;
return intersects(circle1, circle2, intersectionPoints, type,
false /*calculate intersection points*/);
}
bool intersects(const Circle &circle1, const Circle &circle2,
QPointFVector &intersectionPoints) {
IntersectType type;
return intersects(circle1, circle2, intersectionPoints, type);
}
bool intersects(const Circle &circle1, const Circle &circle2,
QPointFVector &intersectionPoints, IntersectType &type) {
return intersects(circle1, circle2, intersectionPoints, type,
true /*calculate intersection points*/);
}
;
double angle(const QLineF &line) { return angle(line.p1(), line.p2()); }
bool contains(const QLineF &line, const QPointF &point, IntersectType &type) {
QPointF translationVector = line.p1();
double alpha = angle(line);
double l = line.length();
QPointF pointL1 = rotateReturn(point - translationVector, -alpha);
double x = pointL1.x();
double y = pointL1.y();
if (x >= 0 && x <= l) {
if (qFuzzyIsNull(x) || qFuzzyCompare(x, l)) {
if (qFuzzyIsNull(y)) {
type = CornerCornerIntersection;
return true;
}
} else {
if (qFuzzyIsNull(y)) {
type = EdgeCornerIntersection;
return true;
}
}
}
type = NoIntersection;
return false;
}
bool contains(const QLineF &line, const QPointF &point) {
IntersectType dummyType;
return contains(line, point, dummyType);
}
bool contains(const QPolygonF &polygon, const QPointF &point,
IntersectType &type) {
using namespace PolygonCalculus;
if (polygon.containsPoint(point, Qt::FillRule::OddEvenFill)) {
type = Interior;
return true;
}
int size = polygon.size();
for (int i = 0; i < size; i++) {
QLineF line(polygon[i], polygon[nextVertexIndex(size, i)]);
if (contains(line, point, type)) {
return true;
}
}
return false;
}
bool contains(const QPolygonF &polygon, const QPointF &point) {
IntersectType dummyType;
return contains(polygon, point, dummyType);
}
double norm(double x, double y) { return qSqrt(x * x + y * y); }
double norm(const QPointF &p) { return norm(p.x(), p.y()); }
double angle(const QPointF &p1) {
return truncateAngle(qAtan2(p1.y(), p1.x()));
}
bool intersects(const Circle &circle, const QPolygonF &polygon,
QVector<QPointFVector> &intersectionPoints,
NeighbourVector &neighbourList, IntersectVector &typeList) {
using namespace PolygonCalculus;
for (int i = 0; i < polygon.size(); i++) {
QPointF p1 = polygon[i];
int j = nextVertexIndex(polygon.size(), i);
QPointF p2 = polygon[j];
QLineF line(p1, p2);
QPointFVector lineIntersecPts;
IntersectType type;
if (intersects(circle, line, lineIntersecPts, type)) {
QPair<int, int> neigbours;
neigbours.first = i;
neigbours.second = j;
neighbourList.append(neigbours);
typeList.append(type);
intersectionPoints.append(lineIntersecPts);
}
}
if (intersectionPoints.size() > 0)
return true;
else {
return false;
}
}
bool intersects(const Circle &circle, const QPolygonF &polygon,
QVector<QPointFVector> &intersectionPoints,
NeighbourVector &neighbourList) {
QVector<IntersectType> types;
return intersects(circle, polygon, intersectionPoints, neighbourList, types);
}
bool intersects(const Circle &circle, const QPolygonF &polygon,
QVector<QPointFVector> &intersectionPoints,
IntersectVector &typeList) {
NeighbourVector neighbourList;
return intersects(circle, polygon, intersectionPoints, neighbourList,
typeList);
}
bool intersects(const Circle &circle, const QPolygonF &polygon,
QVector<QPointFVector> &intersectionPoints) {
NeighbourVector neighbourList;
return intersects(circle, polygon, intersectionPoints, neighbourList);
}
bool intersects(const Circle &circle, const QPolygonF &polygon) {
QVector<QPointFVector> intersectionPoints;
return intersects(circle, polygon, intersectionPoints);
}
bool intersects(const QLineF &line1, const QLineF &line2) {
QPointF intersectionPoint;
return intersects(line1, line2, intersectionPoint);
}
bool intersects(const Circle &circle, const QLineF &line,
QPointFVector &intersectionPoints, IntersectType &type) {
return intersects(circle, line, intersectionPoints, type,
true /* calculate intersection points*/);
}
bool intersectsFast(const QPolygonF &polygon, const QLineF &line) {
for (int i = 0; i < polygon.size() - 1; ++i) {
QLineF polygonSegment(polygon[i], polygon[i + 1]);
if (QLineF::BoundedIntersection == line.intersect(polygonSegment, nullptr))
return true;
}
return false;
}
// bool intersectsFast(const QLineF &line1, const QLineF &line2, QPointF
// &intersectionPt)
// {
// if (line1.isNull() || line2.isNull()){
// return false;
// }
// // determine line equations y = kx+d
// double dx1 = line1.dx();
// double dy1 = line1.dy();
// double k1;
// double d1 = 0;
// if (qFuzzyIsNull(dx1)) {
// k1 = std::numeric_limits<double>::infinity();
// } else {
// k1 = dy1/dx1;
// d1 = line1.y1()-k1*line1.x1();
// }
// double dx2 = line2.dx();
// double dy2 = line2.dy();
// double k2;
// double d2 = 0;
// if (qFuzzyIsNull(dx2)) {
// k2 = std::numeric_limits<double>::infinity();
// } else {
// k2 = dy2/dx2;
// d2 = line2.y1()-k2*line2.x1();
// }
// if ( !qFuzzyCompare(k1, std::numeric_limits<double>::infinity())
// && !qFuzzyCompare(k1, std::numeric_limits<double>::infinity()))
// {
// double dk = k2-k1;
// double dd = d2-d1;
// if (qFuzzyIsNull(dk)) { // lines parallel
// if (qFuzzyIsNull(dd)) { // lines colinear
// if ( ((line1.x1() >= line2.x1()) && ((line1.x1() <=
// line2.x2())))
// || ((line1.x2() >= line2.x1()) && ((line1.x2() <=
// line2.x2()))) ) // intersect? return true;
// return false;
// }
// return false;
// }
// double x_intersect = -dd/dk;
// if (((x_intersect >= line2.x1()) && ((line1.x1() <= line2.x2())))
// }
// return true;
// }
} // end namespace PlanimetryCalculus
/*!
\class PlanimetryCalculus
\inmodule Wima
\brief The \c PlanimetryCalculus class provides routines handy for
planimetrical (2D) calculations.
*/
#pragma once
#include <QLineF>
#include <QPointF>
#include <QPolygonF>
#include <QtMath>
#include "GenericCircle.h"
#include "PolygonCalculus.h"
using Circle = GenericCircle<qreal, QPointF>;
namespace PlanimetryCalculus {
enum IntersectType {
InsideNoIntersection,
InsideTouching,
InsideIntersection,
OutsideIntersection,
OutsideTouching,
OutsideNoIntersection,
CirclesEqual, // Circle Circle intersection
Tangent,
Secant, // Circle Line Intersetion
EdgeCornerIntersection,
EdgeEdgeIntersection,
CornerCornerIntersection,
LinesParallel,
LinesEqual, // Line Line intersection
Interior, // Polygon contains
NoIntersection,
Error // general
};
typedef QVector<QPair<int, int>> NeighbourVector;
typedef QVector<QPointF> QPointFVector;
typedef QVector<IntersectType> IntersectVector;
void rotateReference(QPointF &point, double alpha);
void rotateReference(QPointFVector &points, double alpha);
void rotateReference(QLineF &line, double alpha);
// void rotateReference(QPolygonF &polygon, double alpha);
QPointF rotateReturn(QPointF point, double alpha);
QPointFVector rotateReturn(QPointFVector points, double alpha);
QLineF rotateReturn(QLineF line, double alpha);
// QPolygonF rotateReturn(QPolygonF &polygon, double alpha);
bool intersects(const Circle &circle1, const Circle &circle2);
bool intersects(const Circle &circle1, const Circle &circle2,
IntersectType &type);
bool intersects(const Circle &circle1, const Circle &circle2,
QPointFVector &intersectionPoints);
bool intersects(const Circle &circle1, const Circle &circle2,
QPointFVector &intersectionPoints, IntersectType &type);
bool intersects(const Circle &circle, const QLineF &line);
bool intersects(const Circle &circle, const QLineF &line, IntersectType &type);
bool intersects(const Circle &circle, const QLineF &line,
QPointFVector &intersectionPoints);
bool intersects(const Circle &circle, const QLineF &line,
QPointFVector &intersectionPoints, IntersectType &type);
bool intersects(const Circle &circle, const QPolygonF &polygon);
bool intersects(const Circle &circle, const QPolygonF &polygon,
QVector<QPointFVector> &intersectionPoints);
bool intersects(const Circle &circle, const QPolygonF &polygon,
QVector<QPointFVector> &intersectionPoints,
IntersectVector &typeList);
bool intersects(const Circle &circle, const QPolygonF &polygon,
QVector<QPointFVector> &intersectionPoints,
NeighbourVector &neighbourList);
bool intersects(const Circle &circle, const QPolygonF &polygon,
QVector<QPointFVector> &intersectionPoints,
NeighbourVector &neighbourList, IntersectVector &typeList);
bool intersects(const QLineF &line1, const QLineF &line2);
bool intersects(const QLineF &line1, const QLineF &line2,
QPointF &intersectionPt);
bool intersects(const QLineF &line1, const QLineF &line2,
QPointF &intersectionPt, IntersectType &type);
// bool intersectsFast(const QLineF &line1, const QLineF &line2, QPointF
// &intersectionPt, IntersectType &type);
bool intersects(const QPolygonF &polygon, const QLineF &line,
QPointFVector &intersectionList);
bool intersects(const QPolygonF &polygon, const QLineF &line,
QPointFVector &intersectionList, IntersectVector &typeList);
bool intersects(const QPolygonF &polygon, const QLineF &line,
QPointFVector &intersectionList,
NeighbourVector &neighbourList);
bool intersects(const QPolygonF &polygon, const QLineF &line,
QPointFVector &intersectionList, NeighbourVector &neighbourList,
IntersectVector &typeList);
bool intersectsFast(const QPolygonF &polygon, const QLineF &line);
bool contains(const QLineF &line, const QPointF &point);
bool contains(const QLineF &line, const QPointF &point, IntersectType &type);
bool contains(const QPolygonF &polygon, const QPointF &point);
bool contains(const QPolygonF &polygon, const QPointF &point,
IntersectType &type);
double distance(const QPointF &p1, const QPointF p2);
double norm(double x, double y);
double norm(const QPointF &p);
double angle(const QPointF &p1, const QPointF p2);
double angle(const QPointF &p1);
double angle(const QLineF &line);
double angleDegree(const QPointF &p1, const QPointF p2);
double truncateAngle(double angle);
double truncateAngleDegree(double angle);
/*!
* \fntemplate <typename T> int signum(T val)
* Returns the signum of a value \a val.
*
* \sa QPair, QVector
*/
template <typename T> int signum(T val) { return (T(0) < val) - (val < T(0)); }
} // namespace PlanimetryCalculus
#include "PolygonCalculus.h"
#include "PlanimetryCalculus.h"
#include "Wima/OptimisationTools.h"
#include <QVector3D>
#include <functional>
namespace PolygonCalculus {
namespace {
bool isReflexVertex(const QPolygonF& polygon, const QPointF *vertex) {
// Original Code from SurveyComplexItem::_VertexIsReflex()
auto vertexBefore = vertex == polygon.begin() ? polygon.end() - 1 : vertex - 1;
auto vertexAfter = vertex == polygon.end() - 1 ? polygon.begin() : vertex + 1;
auto area = ( ((vertex->x() - vertexBefore->x())*(vertexAfter->y() - vertexBefore->y()))
-((vertexAfter->x() - vertexBefore->x())*(vertex->y() - vertexBefore->y())));
return area > 0;
}
} // end anonymous namespace
/*!
* \fn bool containsPath(QPolygonF polygon, const QPointF &c1, const QPointF &c2)
* Returns true if the shortest path between the two coordinates is not fully inside the \a area.
*
* \sa QPointF, QPolygonF
*/
bool containsPath(QPolygonF polygon, const QPointF &c1, const QPointF &c2)
{
if ( !polygon.isEmpty()) {
if ( !polygon.containsPoint(c1, Qt::FillRule::OddEvenFill)
|| !polygon.containsPoint(c2, Qt::FillRule::OddEvenFill))
return false;
QLineF line(c1, c2);
if (PlanimetryCalculus::intersectsFast(polygon, line))
return false;
return true;
} else {
return false;
}
}
/*!
* \fn int closestVertexIndex(const QPolygonF &polygon, const QPointF &coordinate)
* Returns the vertex index of \a polygon which has the least distance to \a coordinate.
*
* \sa QPointF, QPolygonF
*/
int closestVertexIndex(const QPolygonF &polygon, const QPointF &coordinate)
{
if (polygon.size() == 0) {
qWarning("Path is empty!");
return -1;
}else if (polygon.size() == 1) {
return 0;
}else {
int index = 0; // the index of the closest vertex
double min_dist = PlanimetryCalculus::distance(coordinate, polygon[index]);
for(int i = 1; i < polygon.size(); i++){
double dist = PlanimetryCalculus::distance(coordinate, polygon[i]);
if (dist < min_dist){
min_dist = dist;
index = i;
}
}
return index;
}
}
/*!auto distance
* \fn QPointF closestVertex(const QPolygonF &polygon, const QPointF &coordinate);
* Returns the vertex of \a polygon with the least distance to \a coordinate.
*
* \sa QPointF, QPolygonF
*/
QPointF closestVertex(const QPolygonF &polygon, const QPointF &coordinate)
{
int index = closestVertexIndex(polygon, coordinate);
if (index >=0 ) {
return polygon[index];
} else {
return QPointF();
}
}
/*!
* \fn int nextPolygonIndex(int pathsize, int index)
* Returns the index of the next vertex (of a polygon), which is \a index + 1 if \a index is smaller than \c {\a pathsize - 1},
* or 0 if \a index equals \c {\a pathsize - 1}, or -1 if the \a index is out of bounds.
* \note \a pathsize is usually obtained by invoking polygon.size()
*/
int nextVertexIndex(int pathsize, int index)
{
if (index >= 0 && index < pathsize-1) {
return index + 1;
} else if (index == pathsize-1) {
return 0;
} else {
qWarning("nextPolygonIndex(): Index out of bounds! index:count = %i:%i", index, pathsize);
return -1;
}
}
/*!
* \fn int previousPolygonIndex(int pathsize, int index)
* Returns the index of the previous vertex (of a polygon), which is \a index - 1 if \a index is larger 0,
* or \c {\a pathsize - 1} if \a index equals 0, or -1 if the \a index is out of bounds.
* \note pathsize is usually obtained by invoking polygon.size()
*/
int previousVertexIndex(int pathsize, int index)
{
if (index > 0 && index <pathsize) {
return index - 1;
} else if (index == 0) {
return pathsize-1;
} else {
qWarning("previousVertexIndex(): Index out of bounds! index:count = %i:%i", index, pathsize);
return -1;
}
}
/*!
* \fn JoinPolygonError joinPolygon(QPolygonF polygon1, QPolygonF polygon2, QPolygonF &joinedPolygon);
* Joins \a polygon1 and \a polygon2 such that a \l {Simple Polygon} is created.
* Stores the result inside \a joinedArea.
* Returns \c NotSimplePolygon1 if \a polygon1 isn't a Simple Polygon, \c NotSimplePolygon2 if \a polygon2 isn't a Simple Polygon, \c Disjoind if the polygons are disjoint,
* \c PathSizeLow if at least one polygon has a size samler than 3, or \c PolygonJoined on success.
* The algorithm will be able to join the areas, if either their edges intersect with each other,
* or one area is inside the other.
* The algorithm assumes that \a joinedPolygon is empty.
*/
JoinPolygonError join(QPolygonF polygon1, QPolygonF polygon2, QPolygonF &joinedPolygon)
{
using namespace PlanimetryCalculus;
if (polygon1.size() >= 3 && polygon2.size() >= 3) {
if ( !isSimplePolygon(polygon1) || !isSimplePolygon(polygon2)) {
return JoinPolygonError::NotSimplePolygon;
}
if (polygon1 == polygon2) {
joinedPolygon = polygon1;
return JoinPolygonError::PolygonJoined;
}
if ( !hasClockwiseWinding(polygon1)) {
reversePath(polygon1);
}
if ( !hasClockwiseWinding(polygon2)) {
reversePath(polygon2);
}
const QPolygonF *walkerPoly = &polygon1; // "walk" on this polygon towards higher indices
const QPolygonF *crossPoly = &polygon2; // check for crossings with this polygon while "walking"
// and swicht to this polygon on a intersection,
// continue to walk towards higher indices
// begin with the first index which is not inside crosspoly, if all Vertices are inside crosspoly return crosspoly
int startIndex = 0;
bool crossContainsWalker = true;
for (int i = 0; i < walkerPoly->size(); i++) {
if ( !contains(*crossPoly, walkerPoly->at(i)) ) {
crossContainsWalker = false;
startIndex = i;
break;
}
}
if ( crossContainsWalker == true) {
joinedPolygon.append(*crossPoly);
return JoinPolygonError::PolygonJoined;
}
QPointF currentVertex = walkerPoly->at(startIndex);
QPointF startVertex = currentVertex;
// possible nextVertex (if no intersection between currentVertex and protoVertex with crossPoly)
int nextVertexNumber = nextVertexIndex(walkerPoly->size(), startIndex);
QPointF protoNextVertex = walkerPoly->value(nextVertexNumber);
long counter = 0;
while (1) {
//qDebug("nextVertexNumber: %i", nextVertexNumber);
joinedPolygon.append(currentVertex);
QLineF walkerPolySegment;
walkerPolySegment.setP1(currentVertex);
walkerPolySegment.setP2(protoNextVertex);
QVector<QPair<int, int>> neighbourList;
QVector<QPointF> intersectionList;
//qDebug("IntersectionList.size() on init: %i", intersectionList.size());
PlanimetryCalculus::intersects(*crossPoly, walkerPolySegment, intersectionList, neighbourList);
//qDebug("IntersectionList.size(): %i", intersectionList.size());
if (intersectionList.size() > 0) {
int minDistIndex = -1;
double minDist = std::numeric_limits<double>::infinity();
for (int i = 0; i < intersectionList.size(); i++) {
double currentDist = PlanimetryCalculus::distance(currentVertex, intersectionList[i]);
if ( minDist > currentDist && !qFuzzyIsNull(distance(currentVertex, intersectionList[i])) ) {
minDist = currentDist;
minDistIndex = i;
}
}
if (minDistIndex != -1){
currentVertex = intersectionList.value(minDistIndex);
QPair<int, int> neighbours = neighbourList.value(minDistIndex);
protoNextVertex = crossPoly->value(neighbours.second);
nextVertexNumber = neighbours.second;
// switch walker and cross poly
const QPolygonF *temp = walkerPoly;
walkerPoly = crossPoly;
crossPoly = temp;
} else {
currentVertex = walkerPoly->value(nextVertexNumber);
nextVertexNumber = nextVertexIndex(walkerPoly->size(), nextVertexNumber);
protoNextVertex = walkerPoly->value(nextVertexNumber);
}
} else {
currentVertex = walkerPoly->value(nextVertexNumber);
nextVertexNumber = nextVertexIndex(walkerPoly->size(), nextVertexNumber);
protoNextVertex = walkerPoly->value(nextVertexNumber);
}
if (currentVertex == startVertex) {
if (polygon1.size() == joinedPolygon.size()) {
for (int i = 0; i < polygon1.size(); i++) {
if (polygon1[i] != joinedPolygon[i])
return PolygonJoined;
}
return Disjoint;
} else {
return PolygonJoined;
}
}
counter++;
if (counter > 1e5) {
qWarning("PolygonCalculus::join(): no successfull termination!");
return Error;
}
}
} else {
return PathSizeLow;
}
}
/*!
* \fn bool isSimplePolygon(const QPolygonF &polygon);
* Returns \c true if \a polygon is a \l {Simple Polygon}, \c false else.
* \note A polygon is a Simple Polygon iff it is not self intersecting.
*/
bool isSimplePolygon(const QPolygonF &polygon)
{
int i = 0;
if (polygon.size() > 3) {
// check if any edge of the area (formed by two adjacent vertices) intersects with any other edge of the area
while(i < polygon.size()-1) {
double cCIntersectCounter = 0; // corner corner intersection counter
QPointF refBeginCoordinate = polygon[i];
QPointF refEndCoordinate = polygon[nextVertexIndex(polygon.size(), i)];
QLineF refLine;
refLine.setP1(refBeginCoordinate);
refLine.setP2(refEndCoordinate);
int j = nextVertexIndex(polygon.size(), i);
while(j < polygon.size()) {
QPointF intersectionPt;
QLineF iteratorLine;
iteratorLine.setP1(polygon[j]);
iteratorLine.setP2(polygon[nextVertexIndex(polygon.size(), j)]);
PlanimetryCalculus::IntersectType intersectType;
PlanimetryCalculus::intersects(refLine, iteratorLine, intersectionPt, intersectType);
if ( intersectType == PlanimetryCalculus::CornerCornerIntersection) {
cCIntersectCounter++;
// max two corner corner intersections allowed, a specific coordinate can appear only once in a simple polygon
}
if ( cCIntersectCounter > 2
|| intersectType == PlanimetryCalculus::EdgeEdgeIntersection
|| intersectType == PlanimetryCalculus::EdgeCornerIntersection
|| intersectType == PlanimetryCalculus::LinesEqual
|| intersectType == PlanimetryCalculus::Error){
return false;
}
j++;
}
i++;
}
}
return true;
}
/*!
* \fn bool hasClockwiseWinding(const QPolygonF &polygon)
* Returns \c true if \a path has clockwiauto distancese winding, \c false else.
*/
bool hasClockwiseWinding(const QPolygonF &polygon)
{
if (polygon.size() <= 2) {
return false;
}
double sum = 0;
for (int i=0; i <polygon.size(); i++) {
QPointF coord1 = polygon[i];
QPointF coord2 = polygon[nextVertexIndex(polygon.size(), i)];
sum += (coord2.x() - coord1.x()) * (coord2.y() + coord1.y());
}
if (sum < 0.0)
return false;
return true;
}
/*!
* \fn void reversePath(QPolygonF &polygon)
* Reverses the path, i.e. changes the first Vertex with the last, the second with the befor last and so forth.
*/
void reversePath(QPolygonF &polygon)
{
QPolygonF pathReversed;
for (int i = 0; i < polygon.size(); i++) {
pathReversed.prepend(polygon[i]);
}
polygon.clear();
polygon.append(pathReversed);
}
/*!
* \fn void offsetPolygon(QPolygonF &polygon, double offset)
* Offsets a \a polygon by the given \a offset. The algorithm assumes that polygon is a \l {SimplePolygon}.
*/
void offsetPolygon(QPolygonF &polygon, double offset)
{
// Original code from QGCMapPolygon::offset()
QPolygonF newPolygon;
if (polygon.size() > 2) {
// Walk the edges, offsetting by theauto distance specified distance
QList<QLineF> rgOffsetEdges;
for (int i = 0; i < polygon.size(); i++) {
int nextIndex = nextVertexIndex(polygon.size(), i);
QLineF offsetEdge;
QLineF originalEdge(polygon[i], polygon[nextIndex]);
QLineF workerLine = originalEdge;
workerLine.setLength(offset);
workerLine.setAngle(workerLine.angle() - 90.0);
offsetEdge.setP1(workerLine.p2());
workerLine.setPoints(originalEdge.p2(), originalEdge.p1()); bool containsPath (const QPointF &c1, const QPointF &c2, QPolygonF polygon);
workerLine.setLength(offset);
workerLine.setAngle(workerLine.angle() + 90.0);
offsetEdge.setP2(workerLine.p2());
rgOffsetEdges << offsetEdge;
}
// Intersect the offset edges to generate new vertices
polygon.clear();
QPointF newVertex;
for (int i=0; i<rgOffsetEdges.count(); i++) {
int prevIndex = previousVertexIndex(rgOffsetEdges.size(), i);
if (rgOffsetEdges[prevIndex].intersect(rgOffsetEdges[i], &newVertex) == QLineF::NoIntersection) {
// FIXME: Better error handling?
qWarning("Intersection failed");
return;
}
polygon.append(newVertex);
}
}
}
void decomposeToConvex(const QPolygonF &polygon, QList<QPolygonF> &convexPolygons)
{
// Original Code SurveyComplexItem::_PolygonDecomposeConvex()
// this follows "Mark Keil's Algorithm" https://mpen.ca/406/keil
int decompSize = std::numeric_limits<int>::max();
if (polygon.size() < 3) return;
if (polygon.size() == 3) {
convexPolygons << polygon;
return;
}
QList<QPolygonF> decomposedPolygonsMin{};
for (const QPointF *vertex = polygon.begin(); vertex != polygon.end(); ++vertex)
{
// is vertex reflex?
bool vertexIsReflex = isReflexVertex(polygon, vertex);
if (!vertexIsReflex) continue;
for (const QPointF *vertexOther = polygon.begin(); vertexOther != polygon.end(); ++vertexOther)
{
const QPointF *vertexBefore = vertex == polygon.begin() ? polygon.end() - 1 : vertex - 1;
const QPointF *vertexAfter = vertex == polygon.end() - 1 ? polygon.begin() : vertex + 1;
if (vertexOther == vertex) continue;
if (vertexAfter == vertexOther) continue;
if (vertexBefore == vertexOther) continue;
bool canSee = containsPath(polygon, *vertex, *vertexOther);
if (!canSee) continue;
QPolygonF polyLeft;
const QPointF *v = vertex;
bool polyLeftContainsReflex = false;
while ( v != vertexOther) {
if (v != vertex && isReflexVertex(polygon, v)) {
polyLeftContainsReflex = true;
}
polyLeft << *v;
++v;
if (v == polygon.end()) v = polygon.begin();
}
polyLeft << *vertexOther;
bool polyLeftValid = !(polyLeftContainsReflex && polyLeft.size() == 3);
QPolygonF polyRight;
v = vertexOther;
bool polyRightContainsReflex = false;
while ( v != vertex) {
if (v != vertex && isReflexVertex(polygon, v)) {
polyRightContainsReflex = true;
}
polyRight << *v;
++v;
if (v == polygon.end()) v = polygon.begin();
}
polyRight << *vertex;
auto polyRightValid = !(polyRightContainsReflex && polyRight.size() == 3);
if (!polyLeftValid || ! polyRightValid) {
// decompSize = std::numeric_limits<int>::max();
continue;
}
// recursion
QList<QPolygonF> polyLeftDecomposed{};
decomposeToConvex(polyLeft, polyLeftDecomposed);
QList<QPolygonF> polyRightDecomposed{};
decomposeToConvex(polyRight, polyRightDecomposed);
// compositon
int subSize = polyLeftDecomposed.size() + polyRightDecomposed.size();
if ( (polyLeftContainsReflex && polyLeftDecomposed.size() == 1)
|| (polyRightContainsReflex && polyRightDecomposed.size() == 1))
{
// don't accept polygons that contian reflex vertices and were not split
subSize = std::numeric_limits<int>::max();
}
if (subSize < decompSize) {
decompSize = subSize;
decomposedPolygonsMin = polyLeftDecomposed + polyRightDecomposed;
}
}
}
// assemble output
if (decomposedPolygonsMin.size() > 0) {
convexPolygons << decomposedPolygonsMin;
} else {
convexPolygons << polygon;
}
return;
}
bool shortestPath(const QPolygonF &polygon,const QPointF &startVertex, const QPointF &endVertex, QVector<QPointF> &shortestPath)
{
using namespace PlanimetryCalculus;
QPolygonF bigPolygon(polygon);
offsetPolygon(bigPolygon, 0.5); // solves numerical errors
if ( bigPolygon.containsPoint(startVertex, Qt::FillRule::OddEvenFill)
&& bigPolygon.containsPoint(endVertex, Qt::FillRule::OddEvenFill)) {
QVector<QPointF> elementList;
elementList.append(startVertex);
elementList.append(endVertex);
for (auto vertex : polygon) elementList.append(vertex);
const int listSize = elementList.size();
std::function<double(const int, const int)> distanceDij = [bigPolygon, elementList](const int ind1, const int ind2) -> double {
QPointF p1 = elementList[ind1];
QPointF p2 = elementList[ind2];
double dist = std::numeric_limits<double>::infinity();
if (containsPathFast(bigPolygon, p1, p2)){ // containsPathFast can be used since all points are inside bigPolygon
double dx = p1.x()-p2.x();
double dy = p1.y()-p2.y();
dist = sqrt((dx*dx)+(dy*dy));
}
return dist;
};
QVector<int> shortestPathIndex;
bool retVal = OptimisationTools::dijkstraAlgorithm(listSize, 0, 1, shortestPathIndex, distanceDij);
for (auto index : shortestPathIndex) shortestPath.append(elementList[index]);
return retVal;
} else {
return false;
}
}
QVector3DList toQVector3DList(const QPolygonF &polygon)
{
QVector3DList list;
for ( auto vertex : polygon )
list.append(QVector3D(vertex));
return list;
}
QPolygonF toQPolygonF(const QPointFList &listF)
{
QPolygonF polygon;
for ( auto vertex : listF )
polygon.append(vertex);
return polygon;
}
QPointFList toQPointFList(const QPolygonF &polygon)
{
QPointFList listF;
for ( auto vertex : polygon )
listF.append(vertex);
return listF;
}
void reversePath(QPointFList &path)
{
QPointFList pathReversed;
for ( auto element : path) {
pathReversed.prepend(element);
}
path = pathReversed;
}
void reversePath(QPointFVector &path)
{
QPointFVector pathReversed;
for ( auto element : path) {
pathReversed.prepend(element);
}
path = pathReversed;
}
bool containsPathFast(QPolygonF polygon, const QPointF &c1, const QPointF &c2)
{
if ( !polygon.isEmpty()) {
QLineF line(c1, c2);
if (PlanimetryCalculus::intersectsFast(polygon, line))
return false;
return true;
} else {
return false;
}
}
} // end PolygonCalculus namespace
#pragma once
#include <QPointF>
#include <QPolygonF>
#include <QVector3D>
namespace PolygonCalculus {
enum JoinPolygonError { NotSimplePolygon, PolygonJoined, Disjoint, PathSizeLow, Error};
typedef QList<QVector3D> QVector3DList;
typedef QList<QPointF> QPointFList;
typedef QVector<QPointF> QPointFVector;
int closestVertexIndex (const QPolygonF &polygon, const QPointF &coordinate);
QPointF closestVertex (const QPolygonF &polygon, const QPointF &coordinate);
int nextVertexIndex (int pathsize, int index);
int previousVertexIndex (int pathsize, int index);
JoinPolygonError join (QPolygonF polygon1, QPolygonF polygon2, QPolygonF &joinedPolygon);
bool isSimplePolygon (const QPolygonF &polygon);
bool hasClockwiseWinding (const QPolygonF &path);
void reversePath (QPolygonF &path);
void reversePath (QPointFList &path);
void reversePath (QPointFVector &path);
void offsetPolygon (QPolygonF &polygon, double offset);
// returns true if the line c1-c2 is fully inside the polygon
bool containsPath (QPolygonF polygon, const QPointF &c1, const QPointF &c2);
// same as containsPath(), but works only if c1 and c2 are inside the polygon!
bool containsPathFast (QPolygonF polygon, const QPointF &c1, const QPointF &c2);
void decomposeToConvex (const QPolygonF &polygon, QList<QPolygonF> &convexPolygons);
bool shortestPath (const QPolygonF &polygon, const QPointF &startVertex, const QPointF &endVertex, QVector<QPointF> &shortestPath);
QPolygonF toQPolygonF(const QVector3DList &polygon);
QPolygonF toQPolygonF(const QPointFList &polygon);
QLineF toQLineF(const QVector3DList &line);
QPointFList toQPointFList(const QVector3DList &list);
QPointFList toQPointFList(const QPolygonF &list);
QVector3DList toQVector3DList(const QPointFList &listF);
QVector3DList toQVector3DList(const QPolygonF &listF);
}
#include "WimaServiceArea.h" #include "SafeArea.h"
#include "QGCLoggingCategory.h" #include "QGCLoggingCategory.h"
#include "QGCQGeoCoordinate.h" #include "QGCQGeoCoordinate.h"
QGC_LOGGING_CATEGORY(WimaServiceAreaLog, "WimaServiceAreaLog") #include <QJsonObject>
const char *WimaServiceArea::wimaServiceAreaName = "Service Area"; QGC_LOGGING_CATEGORY(SafeAreaLog, "SafeAreaLog")
const char *WimaServiceArea::depotLatitudeName = "DepotLatitude";
const char *WimaServiceArea::depotLongitudeName = "DepotLongitude";
const char *WimaServiceArea::depotAltitudeName = "DepotAltitude";
WimaServiceArea::WimaServiceArea(QObject *parent) : WimaArea(parent) { init(); } const char *SafeArea::safeAreaName = "Safe Area";
const char *SafeArea::depotLatitudeName = "DepotLatitude";
const char *SafeArea::depotLongitudeName = "DepotLongitude";
const char *SafeArea::depotAltitudeName = "DepotAltitude";
WimaServiceArea::WimaServiceArea(const WimaServiceArea &other, QObject *parent) SafeArea::SafeArea(QObject *parent) : GeoArea(parent) { init(); }
: WimaArea(other, parent), _depot(other.depot()) {
SafeArea::SafeArea(const SafeArea &other, QObject *parent)
: GeoArea(other, parent), _depot(other.depot()) {
init(); init();
} }
/*! SafeArea &SafeArea::operator=(const SafeArea &other) {
* \overload operator=() GeoArea::operator=(other);
*
* Calls the inherited operator WimaArea::operator=().
*/
WimaServiceArea &WimaServiceArea::operator=(const WimaServiceArea &other) {
WimaArea::operator=(other);
this->setDepot(other.depot()); this->setDepot(other.depot());
return *this; return *this;
} }
const QGeoCoordinate &WimaServiceArea::depot() const { return _depot; } QString SafeArea::mapVisualQML() const { return "SafeAreaMapVisual.qml"; }
QString SafeArea::editorQML() const { return "SafeAreaEditor.qml"; }
SafeArea *SafeArea::clone(QObject *parent) const {
return new SafeArea(*this, parent);
}
const QGeoCoordinate &SafeArea::depot() const { return _depot; }
QGeoCoordinate WimaServiceArea::depotQml() const { return _depot; } QGeoCoordinate SafeArea::depotQml() const { return _depot; }
bool WimaServiceArea::setDepot(const QGeoCoordinate &coordinate) { bool SafeArea::setDepot(const QGeoCoordinate &coordinate) {
if (_depot.latitude() != coordinate.latitude() || if (_depot.latitude() != coordinate.latitude() ||
_depot.longitude() != coordinate.longitude()) { _depot.longitude() != coordinate.longitude()) {
if (this->containsCoordinate(coordinate)) { if (this->containsCoordinate(coordinate)) {
...@@ -45,18 +50,17 @@ bool WimaServiceArea::setDepot(const QGeoCoordinate &coordinate) { ...@@ -45,18 +50,17 @@ bool WimaServiceArea::setDepot(const QGeoCoordinate &coordinate) {
return false; return false;
} }
void WimaServiceArea::saveToJson(QJsonObject &json) { void SafeArea::saveToJson(QJsonObject &json) {
this->WimaArea::saveToJson(json); this->GeoArea::saveToJson(json);
json[areaTypeName] = wimaServiceAreaName; json[areaTypeName] = safeAreaName;
json[depotLatitudeName] = _depot.latitude(); json[depotLatitudeName] = _depot.latitude();
json[depotLongitudeName] = _depot.longitude(); json[depotLongitudeName] = _depot.longitude();
json[depotAltitudeName] = _depot.altitude(); json[depotAltitudeName] = _depot.altitude();
} }
bool WimaServiceArea::loadFromJson(const QJsonObject &json, bool SafeArea::loadFromJson(const QJsonObject &json, QString &errorString) {
QString &errorString) {
bool retVal = false; bool retVal = false;
if (this->WimaArea::loadFromJson(json, errorString)) { if (this->GeoArea::loadFromJson(json, errorString)) {
double lat = 0; double lat = 0;
if (json.contains(depotLatitudeName) && if (json.contains(depotLatitudeName) &&
json[depotLatitudeName].isDouble()) { json[depotLatitudeName].isDouble()) {
...@@ -85,9 +89,9 @@ bool WimaServiceArea::loadFromJson(const QJsonObject &json, ...@@ -85,9 +89,9 @@ bool WimaServiceArea::loadFromJson(const QJsonObject &json,
return retVal; return retVal;
} }
void WimaServiceArea::init() { void SafeArea::init() {
this->setObjectName(wimaServiceAreaName); this->setObjectName(safeAreaName);
connect(this, &WimaArea::pathChanged, [this] { connect(this, &GeoArea::pathChanged, [this] {
if (!this->_depot.isValid() || !this->containsCoordinate(this->_depot)) { if (!this->_depot.isValid() || !this->containsCoordinate(this->_depot)) {
if (this->containsCoordinate(this->center())) { if (this->containsCoordinate(this->center())) {
// Use center. // Use center.
...@@ -106,13 +110,16 @@ void WimaServiceArea::init() { ...@@ -106,13 +110,16 @@ void WimaServiceArea::init() {
minIndex = idx; minIndex = idx;
} }
} else { } else {
qCCritical(WimaServiceAreaLog) << "init(): nullptr catched!"; qCCritical(SafeAreaLog) << "init(): nullptr catched!";
} }
} }
this->setDepot(this->pathModel().value<QGCQGeoCoordinate *>(minIndex)->coordinate()); this->setDepot(this->pathModel()
.value<QGCQGeoCoordinate *>(minIndex)
->coordinate());
} else if (this->pathModel().count() > 0) { } else if (this->pathModel().count() > 0) {
// Use first coordinate. // Use first coordinate.
this->setDepot(this->pathModel().value<QGCQGeoCoordinate *>(0)->coordinate()); this->setDepot(
this->pathModel().value<QGCQGeoCoordinate *>(0)->coordinate());
} }
} }
}); });
......
#pragma once #pragma once
#include "WimaArea.h" #include "GeoArea.h"
#include "WimaTrackerPolyline.h"
#include <QObject> #include <QObject>
class WimaServiceArea : public WimaArea { class SafeArea : public GeoArea {
Q_OBJECT Q_OBJECT
public: public:
WimaServiceArea(QObject *parent = nullptr); SafeArea(QObject *parent = nullptr);
WimaServiceArea(const WimaServiceArea &other, QObject *parent); SafeArea(const SafeArea &other, QObject *parent);
WimaServiceArea &operator=(const WimaServiceArea &other); SafeArea &operator=(const SafeArea &other);
Q_PROPERTY( Q_PROPERTY(
QGeoCoordinate depot READ depotQml WRITE setDepot NOTIFY depotChanged) QGeoCoordinate depot READ depotQml WRITE setDepot NOTIFY depotChanged)
// Overrides from WimaPolygon // Overrides from WimaPolygon
QString mapVisualQML(void) const { return "WimaServiceAreaMapVisual.qml"; } QString mapVisualQML(void) const override;
QString editorQML(void) const { return "WimaServiceAreaEditor.qml"; } QString editorQML(void) const override;
SafeArea *clone(QObject *parent = nullptr) const;
void saveToJson(QJsonObject &json) override;
bool loadFromJson(const QJsonObject &json, QString &errorString) override;
// Property acessors // Property acessors
const QGeoCoordinate &depot(void) const; const QGeoCoordinate &depot(void) const;
QGeoCoordinate depotQml(void) const; QGeoCoordinate depotQml(void) const;
// Member Methodes
void saveToJson(QJsonObject &json);
bool loadFromJson(const QJsonObject &json, QString &errorString);
// static Members // static Members
static const char *wimaServiceAreaName; static const char *safeAreaName;
static const char *depotLatitudeName; static const char *depotLatitudeName;
static const char *depotLongitudeName; static const char *depotLongitudeName;
static const char *depotAltitudeName; static const char *depotAltitudeName;
......
#include "WimaArea.h"
const char *WimaArea::wimaAreaName = "WimaArea";
const char *WimaArea::areaTypeName = "AreaType";
const char *WimaArea::settingsGroup = "MeasurementArea";
// Constructors
WimaArea::WimaArea(QObject *parent)
: QGCMapPolygon(parent),
_metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/WimaArea.SettingsGroup.json"),
this /* QObject parent */)),
_borderPolygonOffset(SettingsFact(settingsGroup,
_metaDataMap[borderPolygonOffsetName],
this /* QObject parent */)),
_showBorderPolygon(SettingsFact(settingsGroup,
_metaDataMap[showBorderPolygonName],
this /* QObject parent */)),
_borderPolygon(QGCMapPolygon(this)), _wimaAreaInteractive(false) {
init();
_maxAltitude = 30;
}
WimaArea::WimaArea(const WimaArea &other, QObject *parent)
: QGCMapPolygon(parent),
_metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/WimaArea.SettingsGroup.json"),
this /* QObject parent */)),
_borderPolygonOffset(SettingsFact(settingsGroup,
_metaDataMap[borderPolygonOffsetName],
this /* QObject parent */)),
_showBorderPolygon(SettingsFact(settingsGroup,
_metaDataMap[showBorderPolygonName],
this /* QObject parent */)),
_borderPolygon(QGCMapPolygon(this)), _wimaAreaInteractive(false) {
init();
*this = other;
}
/*!
*\fn WimaArea &WimaArea::operator=(const WimaArea &other)
*
* Assigns \a other to this \c WimaArea and returns a reference to this \c
*WimaArea.
*
* Copies only path and maximum altitude.
*/
WimaArea &WimaArea::operator=(const WimaArea &other) {
QGCMapPolygon::operator=(other);
this->setMaxAltitude(other._maxAltitude);
this->setPath(other.path());
return *this;
}
void WimaArea::setWimaAreaInteractive(bool interactive) {
if (WimaArea::_wimaAreaInteractive != interactive) {
WimaArea::_wimaAreaInteractive = interactive;
emit WimaArea::wimaAreaInteractiveChanged();
}
}
/*!
\fn void WimaArea::setMaxAltitude(double altitude)
Sets the \c _maxAltitude member to \a altitude and emits the signal \c
maxAltitudeChanged() if \c _maxAltitude is not equal to altitude.
*/
void WimaArea::setMaxAltitude(double altitude) {
if (altitude > 0 && qFuzzyCompare(altitude, _maxAltitude)) {
_maxAltitude = altitude;
emit maxAltitudeChanged();
}
}
void WimaArea::setShowBorderPolygon(bool showBorderPolygon) {
_showBorderPolygon.setRawValue(showBorderPolygon);
}
void WimaArea::setBorderPolygonOffset(double offset) {
if (!qFuzzyCompare(_borderPolygonOffset.rawValue().toDouble(), offset)) {
_borderPolygonOffset.setRawValue(offset);
emit borderPolygonOffsetChanged();
}
}
void WimaArea::recalcPolygons() {
if (_showBorderPolygon.rawValue().toBool() == true) {
if (_borderPolygon.count() >= 3) {
//_borderPolygon.verifyClockwiseWinding(); // causes seg. fault
this->setPath(_borderPolygon.coordinateList());
this->offset(-_borderPolygonOffset.rawValue().toDouble());
}
} else {
if (this->count() >= 3) {
// this->verifyClockwiseWinding(); // causes seg. fault
_borderPolygon.setPath(this->coordinateList());
_borderPolygon.offset(_borderPolygonOffset.rawValue().toDouble());
}
emit borderPolygonChanged();
}
}
void WimaArea::updatePolygonConnections(QVariant showBorderPolygon) {
if (showBorderPolygon.toBool() == true) {
connect(&_borderPolygon, &QGCMapPolygon::pathChanged, this,
&WimaArea::recalcPolygons);
disconnect(this, &QGCMapPolygon::pathChanged, this,
&WimaArea::recalcPolygons);
} else {
disconnect(&_borderPolygon, &QGCMapPolygon::pathChanged, this,
&WimaArea::recalcPolygons);
connect(this, &QGCMapPolygon::pathChanged, this, &WimaArea::recalcPolygons);
}
}
void WimaArea::recalcInteractivity() {
if (_wimaAreaInteractive == false) {
QGCMapPolygon::setInteractive(false);
_borderPolygon.setInteractive(false);
} else {
if (_showBorderPolygon.rawValue().toBool() == true) {
_borderPolygon.setInteractive(true);
QGCMapPolygon::setInteractive(false);
} else {
_borderPolygon.setInteractive(false);
QGCMapPolygon::setInteractive(true);
}
}
}
/*!
* \fn int WimaArea::getClosestVertexIndex(const QGeoCoordinate &coordinate)
* const Returns the index of the vertex (element of the polygon path) which has
* the least distance to \a coordinate.
*
* \sa QGeoCoordinate
*/
int WimaArea::getClosestVertexIndex(const QGeoCoordinate &coordinate) const {
if (this->count() == 0) {
qWarning("Polygon count == 0!");
return -1;
} else if (this->count() == 1) {
return 0;
} else {
int index = 0;
double min_dist = coordinate.distanceTo(this->vertexCoordinate(index));
for (int i = 1; i < this->count(); i++) {
double dist = coordinate.distanceTo(this->vertexCoordinate(i));
if (dist < min_dist) {
min_dist = dist;
index = i;
}
}
return index;
}
}
/*!
* \fn QGeoCoordinate WimaArea::getClosestVertex(const QGeoCoordinate&
* coordinate) const Returns the vertex of the polygon path with the least
* distance to \a coordinate.
*
* \sa QGeoCoordinate
*/
QGeoCoordinate
WimaArea::getClosestVertex(const QGeoCoordinate &coordinate) const {
return this->vertexCoordinate(getClosestVertexIndex(coordinate));
}
/*!
* \fn QGCMapPolygon WimaArea::toQGCPolygon(const WimaArea &area)
* Converts the \c WimaArea \a area to \c QGCMapPolygon by copying the path
* only.
*/
QGCMapPolygon WimaArea::toQGCPolygon(const WimaArea &area) {
QGCMapPolygon qgcPoly;
qgcPoly.setPath(area.path());
return QGCMapPolygon(qgcPoly);
}
/*!
* \fn QGCMapPolygon WimaArea::toQGCPolygon() const
* Converts the calling \c WimaArea to \c QGCMapPolygon by copying the path
* only.
*/
QGCMapPolygon WimaArea::toQGCPolygon() const { return toQGCPolygon(*this); }
/*!
* \fn bool WimaArea::join(WimaArea &area1, WimaArea &area2, WimaArea
* &joinedArea, QString &errorString) Joins the areas \a area1 and \a area2 such
* that a \l {Simple Polygon} is created. Stores the result inside \a
* joinedArea. Stores error messages in \a errorString. Returns \c true if the
* algorithm was able to join the areas; false else. The algorithm will be able
* to join the areas, if either their edges intersect with each other, or one
* area contains the other.
*/
bool WimaArea::join(const WimaArea &area1, const WimaArea &area2,
WimaArea &joinedArea, QString &errorString) {
using namespace GeoUtilities;
using namespace PolygonCalculus;
Q_UNUSED(errorString);
QList<QGeoCoordinate> GeoPolygon1 = area1.coordinateList();
QList<QGeoCoordinate> GeoPolygon2 = area2.coordinateList();
// qWarning("befor joining");
// qWarning() << GeoPolygon1;
// qWarning() << GeoPolygon2;
QGeoCoordinate origin = GeoPolygon1[0];
// QGeoCoordinate tset = GeoPolygon1[2];
// qWarning() << tset;qWarning() << toGeo(toCartesian2D(tset, origin),
// origin);
QPolygonF polygon1;
toCartesianList(GeoPolygon1, origin, polygon1);
QPolygonF polygon2;
toCartesianList(GeoPolygon2, origin, polygon2);
// qWarning("after 1 transform");
// qWarning() << polygon1;
// qWarning() << polygon2;
QPolygonF joinedPolygon;
JoinPolygonError retValue =
PolygonCalculus::join(polygon1, polygon2, joinedPolygon);
// qWarning("after joining");
// qWarning() << joinedPolygon;
if (retValue == JoinPolygonError::Disjoint) {
qWarning("Polygons are disjoint.");
} else if (retValue == JoinPolygonError::NotSimplePolygon) {
qWarning("Not a simple polygon.");
} else if (retValue == JoinPolygonError::PathSizeLow) {
qWarning("Polygon vertex count is low.");
} else {
QList<QGeoCoordinate> path;
toGeoList(joinedPolygon, origin, path);
// qWarning("after transform");
// qWarning() << path;
joinedArea.setPath(path);
return true;
}
return false;
}
/*!
* \fn bool WimaArea::join(WimaArea &area1, WimaArea &area2, WimaArea
* &joinedArea) Joins the areas \a area1 and \a area2 such that a \l {Simple
* Polygon} is created. Stores the result inside \a joinedArea. Returns \c true
* if the algorithm was able to join the areas; false else. The algorithm will
* be able to join the areas, if either their edges intersect with each other,
* or one area contains the other.
*/
bool WimaArea::join(const WimaArea &area1, const WimaArea &area2,
WimaArea &joinedArea) {
QString dummy;
return join(area1, area2, joinedArea, dummy);
}
/*!
* \fn bool WimaArea::join(WimaArea &area)
* Joins the calling \c WimaArea and the \a area such that a \l {Simple Polygon}
* is created. Overwrites the calling \c WimaArea with the result, if the
* algorithm was successful. Returns \c true if the algorithm was able to join
* the areas; false else. The algorithm will be able to join the areas, if
* either their edges intersect with each other, or one area contains the other.
*/
bool WimaArea::join(WimaArea &area) {
WimaArea joinedArea;
if (join(*this, area, joinedArea)) {
// qWarning("WimaArea::join(WimaArea &area)");
// qWarning() << joinedArea.coordinateList();
this->setPath(joinedArea.path());
return true;
} else {
return false;
}
}
/*!
* \fn bool WimaArea::join(WimaArea &area, QString &errorString)
* Joins the calling \c WimaArea and the \a area such that a \l {Simple Polygon}
* is created. Overwrites the calling \c WimaArea with the result, if the
* algorithm was successful.
*
* Returns \c true if the algorithm was able to join the areas; false else.
* Stores error messages in \a errorString.
*
* The algorithm will be able to join the areas, if either their edges intersect
* with each other, or one area contains the other.
*/
bool WimaArea::join(WimaArea &area, QString &errorString) {
WimaArea joinedArea;
if (join(*this, area, joinedArea, errorString)) {
this->setPath(joinedArea.path());
return true;
} else {
return false;
}
}
/*!
* \fn int WimaArea::nextVertexIndex(int index) const
* Returns the index of the next vertex (of the areas path), which is \a index +
* 1 if \a index is smaller than \c {area.count() - 1}, or 0 if \a index equals
* \c {area.count() - 1}, or -1 if the \a index is out of bounds. \note The
* function \c {area.count()} (derived from \c QGCMapPolygon) returns the number
* of vertices defining the area.
*/
int WimaArea::nextVertexIndex(int index) const {
if (index >= 0 && index < count() - 1) {
return index + 1;
} else if (index == count() - 1) {
return 0;
} else {
qWarning(
"WimaArea::nextVertexIndex(): Index out of bounds! index:count = %i:%i",
index, count());
return -1;
}
}
/*!
* \fn int WimaArea::previousVertexIndex(int index) const
* Returns the index of the previous vertex (of the areas path), which is \a
* index - 1 if \a index is larger 0, or \c {area.count() - 1} if \a index
* equals 0, or -1 if the \a index is out of bounds. \note The function \c
* {area.count()} (derived from \c QGCMapPolygon) returns the number of vertices
* defining the area.
*/
int WimaArea::previousVertexIndex(int index) const {
if (index > 0 && index < count()) {
return index - 1;
} else if (index == 0) {
return count() - 1;
} else {
qWarning("WimaArea::previousVertexIndex(): Index out of bounds! "
"index:count = %i:%i",
index, count());
return -1;
}
}
/*!
* \fn bool WimaArea::isSelfIntersecting()
* Returns \c true if the calling area is self intersecting, \c false else.
* \note If the calling area is self intersecting, it's not a \l {Simple
* Polygon}.
*/
bool WimaArea::isSimplePolygon() const {
using namespace PolygonCalculus;
using namespace GeoUtilities;
if (this->count() > 2) {
QPolygonF polygon;
toCartesianList(this->coordinateList(), this->vertexCoordinate(0), polygon);
return PolygonCalculus::isSimplePolygon(polygon);
} else
return false;
}
bool WimaArea::containsCoordinate(const QGeoCoordinate &coordinate) const {
using namespace PlanimetryCalculus;
using namespace PolygonCalculus;
using namespace GeoUtilities;
if (this->count() > 2) {
QPolygonF polygon;
toCartesianList(this->coordinateList(), coordinate, polygon);
return PlanimetryCalculus::contains(polygon, QPointF(0, 0));
} else
return false;
}
/*!
* \fn void WimaArea::saveToJson(QJsonObject &json)
* Saves the calling area to \c QJsonObject object and stores it inside \a json.
*
* \sa QJsonObject
*/
void WimaArea::saveToJson(QJsonObject &json) {
this->QGCMapPolygon::saveToJson(json);
json[maxAltitudeName] = _maxAltitude;
json[borderPolygonOffsetName] = _borderPolygonOffset.rawValue().toDouble();
json[showBorderPolygonName] = _showBorderPolygon.rawValue().toDouble();
json[areaTypeName] = wimaAreaName;
}
/*!
* \fn bool WimaArea::loadFromJson(const QJsonObject &json, QString&
* errorString) Loads data from \a json and stores it inside the calling area.
* Returns \c true if loading was successful, \c false else.
* Stores error messages inside \a errorString.
*
* \sa QJsonObject
*/
bool WimaArea::loadFromJson(const QJsonObject &json, QString &errorString) {
if (this->QGCMapPolygon::loadFromJson(json, false /*no poly required*/,
errorString)) {
if (json.contains(maxAltitudeName) && json[maxAltitudeName].isDouble()) {
_maxAltitude = json[maxAltitudeName].toDouble();
} else {
errorString.append(tr("Could not load Maximum Altitude value!\n"));
return false;
}
if (json.contains(borderPolygonOffsetName) &&
json[borderPolygonOffsetName].isDouble()) {
_borderPolygonOffset.setRawValue(
json[borderPolygonOffsetName].toDouble());
} else {
errorString.append(tr("Could not load border polygon offset value!\n"));
return false;
}
if (json.contains(showBorderPolygonName) &&
json[showBorderPolygonName].isDouble()) {
_showBorderPolygon.setRawValue(json[showBorderPolygonName].toBool());
} else {
errorString.append(tr("Could not load border polygon offset value!\n"));
return false;
}
} else {
qWarning() << errorString;
return false;
}
return true;
}
/*!
* \fn void WimaArea::init()
* Funtion to be called during construction.
*/
void WimaArea::init() {
this->setObjectName(wimaAreaName);
if (_showBorderPolygon.rawValue().toBool() == true) {
connect(&_borderPolygon, &QGCMapPolygon::pathChanged, this,
&WimaArea::recalcPolygons);
} else {
connect(this, &QGCMapPolygon::pathChanged, this, &WimaArea::recalcPolygons);
}
connect(&_borderPolygonOffset, &SettingsFact::rawValueChanged, this,
&WimaArea::recalcPolygons);
connect(&_showBorderPolygon, &SettingsFact::rawValueChanged, this,
&WimaArea::updatePolygonConnections);
connect(&_showBorderPolygon, &SettingsFact::rawValueChanged, this,
&WimaArea::recalcInteractivity);
connect(this, &WimaArea::wimaAreaInteractiveChanged, this,
&WimaArea::recalcInteractivity);
}
// QDoc Documentation
/*!
\group WimaAreaGroup
\title Group of WimaAreas
Every \c WimaArea of the equally named group uses a \l {Simple Polygon}
derived from \c {QGCMapPolygon} to define areas inside which certain taskts
are performed.
*/
/*!
\class WimaArea
\inmodule Wima
\ingroup WimaArea
\brief The \c WimaArea class provides the a base class for
all areas used within the Wima extension.
\c WimaArea uses a \l {Simple Polygon} derived from \c {QGCMapPolygon}
to define areas inside which certain taskts are performed. The polygon
(often refered to as the path) can be displayed visually on a map.
*/
/*!
\variable WimaArea::_maxAltitude
\brief The maximum altitude vehicles are allowed to fly inside this area.
*/
/*!
\property WimaArea::maxAltitude
\brief The maximum altitude at which vehicles are allowed to fly.
*/
/*!
\property WimaArea::mapVisualQML
\brief A string containing the name of the QML file used to displays this area
on a map.
*/
/*!
\property WimaArea::editorQML
\brief A string containing the name of the QML file allowing to edit the
area's properties.
*/
/*!
\externalpage https://en.wikipedia.org/wiki/Simple_polygon
\title Simple Polygon
*/
/*!
\externalpage https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
\title Dijkstra Algorithm
*/
#pragma once
#include "QGCGeo.h"
#include "QGCMapPolygon.h"
#include "QGCMapPolyline.h"
#include "Vehicle.h"
#include "qobject.h"
#include <QLineF>
#include <QPair>
#include <QPointF>
#include "GeoUtilities.h"
#include "PlanimetryCalculus.h"
#include "PolygonCalculus.h"
class WimaArea : public QGCMapPolygon // abstract base class for all WimaAreas
{
Q_OBJECT
public:
WimaArea(QObject *parent = nullptr);
WimaArea(const WimaArea &other, QObject *parent = nullptr);
WimaArea &operator=(const WimaArea &other);
Q_PROPERTY(QString mapVisualQML READ mapVisualQML CONSTANT)
Q_PROPERTY(QString editorQML READ editorQML CONSTANT)
virtual QString mapVisualQML(void) const = 0;
virtual QString editorQML(void) const = 0;
void saveToJson(QJsonObject &jsonObject);
bool loadFromJson(const QJsonObject &jsonObject, QString &errorString);
// static Members
static const char *wimaAreaName;
static const char *areaTypeName;
static const char *settingsGroup;
private:
void init();
};
#include "WimaAreaData.h"
WimaAreaData::WimaAreaData(QObject *parent) : QObject(parent) {}
WimaAreaData::WimaAreaData(const WimaAreaData &other, QObject *parent)
: QObject(parent), _path(other._path), _list(other._list),
_center(other._center) {}
WimaAreaData::WimaAreaData(const WimaArea &otherData, QObject *parent)
: QObject(parent), _center(otherData.center()) {
_setPathImpl(otherData.path());
}
bool WimaAreaData::operator==(const WimaAreaData &data) const {
return this->_path == data._path && this->_center == data._center;
}
bool WimaAreaData::operator!=(const WimaAreaData &data) const {
return !this->operator==(data);
}
QVariantList WimaAreaData::path() const { return _path; }
QGeoCoordinate WimaAreaData::center() const { return _center; }
const QList<QGeoCoordinate> &WimaAreaData::coordinateList() const {
return _list;
}
bool WimaAreaData::containsCoordinate(const QGeoCoordinate &coordinate) const {
using namespace PlanimetryCalculus;
using namespace PolygonCalculus;
using namespace GeoUtilities;
if (this->coordinateList().size() > 2) {
QPolygonF polygon;
toCartesianList(this->coordinateList(), coordinate, polygon);
return PlanimetryCalculus::contains(polygon, QPointF(0, 0));
} else
return false;
}
void WimaAreaData::append(const QGeoCoordinate &c) {
_list.append(c);
_path.push_back(QVariant::fromValue(c));
emit pathChanged();
}
void WimaAreaData::push_back(const QGeoCoordinate &c) { append(c); }
void WimaAreaData::clear() {
if (_list.size() > 0 || _path.size() > 0) {
_list.clear();
_path.clear();
emit pathChanged();
}
}
void WimaAreaData::setPath(const QVariantList &coordinateList) {
if (_path != coordinateList) {
_setPathImpl(coordinateList);
emit pathChanged();
}
}
void WimaAreaData::setCenter(const QGeoCoordinate &center) {
if (_center != center) {
_center = center;
emit centerChanged();
}
}
WimaAreaData &WimaAreaData::operator=(const WimaAreaData &otherData) {
setPath(otherData._list);
setCenter(otherData._center);
return *this;
}
WimaAreaData &WimaAreaData::operator=(const WimaArea &otherData) {
setPath(otherData.path());
setCenter(otherData.center());
return *this;
}
void WimaAreaData::_setPathImpl(const QList<QGeoCoordinate> &coordinateList) {
_list = coordinateList;
_path.clear();
// copy all coordinates to _path
for (const auto &vertex : coordinateList) {
_path.append(QVariant::fromValue(vertex));
}
}
void WimaAreaData::_setPathImpl(const QVariantList &coordinateList) {
_path = coordinateList;
_list.clear();
for (const auto &variant : coordinateList) {
_list.push_back(variant.value<QGeoCoordinate>());
}
}
/*!
* \fn void WimaAreaData::setPath(const QList<QGeoCoordinate> &coordinateList)
*
* Sets the path member to \a coordinateList by copying all entries of \a
* coordinateList. Emits the \c pathChanged() signal.
*/
void WimaAreaData::setPath(const QList<QGeoCoordinate> &coordinateList) {
if (_list != coordinateList) {
_setPathImpl(coordinateList);
emit pathChanged();
}
}
bool operator==(const WimaAreaData &m1, const WimaArea &m2) {
return m1.path() == m2.path() && m1.center() == m2.center();
}
bool operator!=(const WimaAreaData &m1, const WimaArea &m2) {
return !operator==(m1, m2);
}
bool operator==(const WimaArea &m1, const WimaAreaData &m2) { return m2 == m1; }
bool operator!=(const WimaArea &m1, const WimaAreaData &m2) {
return !operator==(m2, m1);
}
#pragma once
#include <QObject>
#include "QGeoCoordinate"
#include "WimaArea.h"
class WimaAreaData
: public QObject // Abstract class for all WimaAreaData derived objects
{
Q_OBJECT
public:
WimaAreaData(QObject *parent = nullptr);
Q_PROPERTY(const QVariantList path READ path NOTIFY pathChanged)
Q_PROPERTY(QString type READ type CONSTANT)
Q_PROPERTY(QString mapVisualQML READ mapVisualQML CONSTANT)
bool operator==(const WimaAreaData &data) const;
bool operator!=(const WimaAreaData &data) const;
virtual QString mapVisualQML(void) const = 0;
QVariantList path() const;
QGeoCoordinate center() const;
const QList<QGeoCoordinate> &coordinateList() const;
bool containsCoordinate(const QGeoCoordinate &coordinate) const;
virtual QString type() const = 0;
void append(const QGeoCoordinate &c);
void push_back(const QGeoCoordinate &c);
void clear();
signals:
void pathChanged();
void centerChanged();
public slots:
void setPath(const QList<QGeoCoordinate> &coordinateList);
void setPath(const QVariantList &coordinateList);
void setCenter(const QGeoCoordinate &center);
protected:
WimaAreaData(const WimaAreaData &otherData, QObject *parent);
WimaAreaData(const WimaArea &otherData, QObject *parent);
WimaAreaData &operator=(const WimaAreaData &otherData);
WimaAreaData &operator=(const WimaArea &otherData);
private:
void _setPathImpl(const QList<QGeoCoordinate> &coordinateList);
void _setPathImpl(const QVariantList &coordinateList);
// Member Variables
mutable QVariantList _path;
QList<QGeoCoordinate> _list;
QGeoCoordinate _center;
};
bool operator==(const WimaAreaData &m1, const WimaArea &m2);
bool operator!=(const WimaAreaData &m1, const WimaArea &m2);
bool operator==(const WimaArea &m1, const WimaAreaData &m2);
bool operator!=(const WimaArea &m1, const WimaAreaData &m2);
#include "WimaMeasurementAreaData.h"
#include "SnakeTile.h"
const char *WimaMeasurementAreaData::typeString = "WimaMeasurementAreaData";
WimaMeasurementAreaData::WimaMeasurementAreaData(QObject *parent)
: WimaAreaData(parent) {}
WimaMeasurementAreaData::WimaMeasurementAreaData(
const WimaMeasurementAreaData &other, QObject *parent)
: WimaAreaData(parent) {
*this = other;
}
WimaMeasurementAreaData::WimaMeasurementAreaData(
const WimaMeasurementArea &other, QObject *parent)
: WimaAreaData(parent) {
*this = other;
}
bool WimaMeasurementAreaData::
operator==(const WimaMeasurementAreaData &other) const {
return this->WimaAreaData::operator==(other) &&
this->_tileData == other.tileData() &&
this->_progress == other.progress() &&
this->center() == other.center();
}
bool WimaMeasurementAreaData::
operator!=(const WimaMeasurementAreaData &other) const {
return !(*this == other);
}
void WimaMeasurementAreaData::setTileData(const TileData &d) {
if (this->_tileData != d) {
this->_tileData = d;
this->_progress.fill(0, d.size());
emit progressChanged();
emit tileDataChanged();
}
}
void WimaMeasurementAreaData::setProgress(const QVector<int> &d) {
if (this->_progress != d && d.size() == this->_tileData.tiles.count()) {
this->_progress = d;
emit progressChanged();
}
}
/*!
* \overload operator=();
*
* Assigns \a other to the invoking object.
*/
WimaMeasurementAreaData &WimaMeasurementAreaData::
operator=(const WimaMeasurementAreaData &other) {
WimaAreaData::operator=(other);
setTileData(other._tileData);
setProgress(other._progress);
return *this;
}
/*!
* \overload operator=();
*
* Assigns \a other to the invoking object.
*/
WimaMeasurementAreaData &WimaMeasurementAreaData::
operator=(const WimaMeasurementArea &other) {
WimaAreaData::operator=(other);
if (other.ready()) {
setTileData(other.tileData());
setProgress(other.progress());
} else {
qWarning() << "WimaMeasurementAreaData::operator=(): WimaMeasurementArea "
"not ready.";
}
return *this;
}
QString WimaMeasurementAreaData::mapVisualQML() const {
return QStringLiteral("WimaMeasurementAreaDataVisual.qml");
}
QString WimaMeasurementAreaData::type() const { return this->typeString; }
QmlObjectListModel *WimaMeasurementAreaData::tiles() {
return &this->_tileData.tiles;
}
const QmlObjectListModel *WimaMeasurementAreaData::tiles() const {
return &this->_tileData.tiles;
}
const QVariantList &WimaMeasurementAreaData::tileCenterPoints() const {
return this->_tileData.tileCenterPoints;
}
QVariantList &WimaMeasurementAreaData::tileCenterPoints() {
return this->_tileData.tileCenterPoints;
}
const TileData &WimaMeasurementAreaData::tileData() const {
return this->_tileData;
}
TileData &WimaMeasurementAreaData::tileData() { return this->_tileData; }
const QVector<int> &WimaMeasurementAreaData::progress() const {
return this->_progress;
}
QVector<int> &WimaMeasurementAreaData::progress() { return this->_progress; }
bool operator==(const WimaMeasurementAreaData &m1,
const WimaMeasurementArea &m2) {
return operator==(*static_cast<const WimaAreaData *>(&m1),
*static_cast<const WimaArea *>(&m2)) &&
m1.tileData() == m2.tileData() && m1.progress() == m2.progress();
}
bool operator!=(const WimaMeasurementAreaData &m1,
const WimaMeasurementArea &m2) {
return !(m1 == m2);
}
bool operator==(const WimaMeasurementArea &m1,
const WimaMeasurementAreaData &m2) {
return m2 == m1;
}
bool operator!=(const WimaMeasurementArea &m1,
const WimaMeasurementAreaData &m2) {
return m2 != m1;
}
#pragma once
#include <QGeoCoordinate>
#include <QObject>
#include "WimaAreaData.h"
#include "WimaMeasurementArea.h"
class WimaMeasurementAreaData : public WimaAreaData {
Q_OBJECT
public:
WimaMeasurementAreaData(QObject *parent = nullptr);
WimaMeasurementAreaData(const WimaMeasurementAreaData &other,
QObject *parent = nullptr);
WimaMeasurementAreaData(const WimaMeasurementArea &other,
QObject *parent = nullptr);
WimaMeasurementAreaData &operator=(const WimaMeasurementAreaData &other);
WimaMeasurementAreaData &operator=(const WimaMeasurementArea &other);
Q_PROPERTY(QmlObjectListModel *tiles READ tiles NOTIFY tileDataChanged)
Q_PROPERTY(QVector<int> progress READ progress NOTIFY progressChanged)
virtual QString mapVisualQML() const override;
bool operator==(const WimaMeasurementAreaData &other) const;
bool operator!=(const WimaMeasurementAreaData &other) const;
// Property setters.
void setTileData(const TileData &d);
void setProgress(const QVector<int> &d);
// Property getters.
QString type() const;
WimaMeasurementAreaData *Clone() const {
return new WimaMeasurementAreaData(*this);
}
QmlObjectListModel *tiles();
const QmlObjectListModel *tiles() const;
const QVariantList &tileCenterPoints() const;
QVariantList &tileCenterPoints();
const TileData &tileData() const;
TileData &tileData();
const QVector<int> &progress() const;
QVector<int> &progress();
static const char *typeString;
signals:
void tileDataChanged();
void progressChanged();
private:
TileData _tileData;
QVector<int> _progress;
};
bool operator==(const WimaMeasurementAreaData &m1,
const WimaMeasurementArea &m2);
bool operator!=(const WimaMeasurementAreaData &m1,
const WimaMeasurementArea &m2);
bool operator==(const WimaMeasurementArea &m1,
const WimaMeasurementAreaData &m2);
bool operator!=(const WimaMeasurementArea &m1,
const WimaMeasurementAreaData &m2);
#include "WimaServiceAreaData.h"
const char *WimaServiceAreaData::typeString = "WimaServiceAreaData";
WimaServiceAreaData::WimaServiceAreaData(QObject *parent)
: WimaAreaData(parent) {}
WimaServiceAreaData::WimaServiceAreaData(const WimaServiceAreaData &other,
QObject *parent)
: WimaAreaData(other, parent), _depot(other._depot) {}
WimaServiceAreaData::WimaServiceAreaData(const WimaServiceArea &other,
QObject *parent)
: WimaAreaData(other, parent), _depot(other.depot()) {}
WimaServiceAreaData &WimaServiceAreaData::
operator=(const WimaServiceAreaData &otherData) {
WimaAreaData::operator=(otherData);
this->setDepot(otherData.depot());
return *this;
}
WimaServiceAreaData &WimaServiceAreaData::
operator=(const WimaServiceArea &otherArea) {
WimaAreaData::operator=(otherArea);
this->setDepot(otherArea.depot());
return *this;
}
QString WimaServiceAreaData::mapVisualQML() const {
return QStringLiteral("WimaServiceAreaDataVisual.qml");
}
/*!
* \fn const QGeoCoordinate &WimaServiceAreaData::takeOffPosition() const
* Returns a constant reference to the takeOffPosition.
*
*/
const QGeoCoordinate &WimaServiceAreaData::depot() const { return _depot; }
QString WimaServiceAreaData::type() const { return this->typeString; }
/*!
* \fn void WimaServiceAreaData::setTakeOffPosition(const QGeoCoordinate
* &newCoordinate) Sets the takeoff position to the \a newCoordinate and emits
* the takeOffPositionChanged() signal, if newCoordinate differs from the member
* value.
*
*/
void WimaServiceAreaData::setDepot(const QGeoCoordinate &newCoordinate) {
if (_depot != newCoordinate) {
_depot = newCoordinate;
emit depotChanged(_depot);
}
}
#pragma once
#include "QGeoCoordinate"
#include <QObject>
#include "WimaAreaData.h"
#include "WimaServiceArea.h"
class WimaServiceAreaData : public WimaAreaData {
Q_OBJECT
public:
WimaServiceAreaData(QObject *parent = nullptr);
WimaServiceAreaData(const WimaServiceAreaData &other,
QObject *parent = nullptr);
WimaServiceAreaData(const WimaServiceArea &other, QObject *parent = nullptr);
WimaServiceAreaData &operator=(const WimaServiceAreaData &otherData);
WimaServiceAreaData &operator=(const WimaServiceArea &otherArea);
virtual QString mapVisualQML() const override;
const QGeoCoordinate &depot() const;
QString type() const;
WimaServiceAreaData *Clone() const { return new WimaServiceAreaData(); }
static const char *typeString;
signals:
void depotChanged(const QGeoCoordinate &other);
public slots:
void setDepot(const QGeoCoordinate &newCoordinate);
private:
// see WimaServieArea.h for explanation
QGeoCoordinate _depot;
};
[
{
"name": "EnableWimaController",
"shortDescription": "Enables or disables the WimaController, which performes different tasks inside the flight view window.",
"type": "bool",
"defaultValue": 1
},
{
"name": "OverlapWaypoints",
"shortDescription": "Determines the number of overlapping waypoints between two consecutive mission phases.",
"type": "uint32",
"defaultValue": 2
},
{
"name": "MaxWaypointsPerPhase",
"shortDescription": "Determines the maximum number of waypoints per phase. This number does not include the arrival and return path.",
"type": "uint32",
"min" : 1,
"defaultValue": 30
},
{
"name": "StartWaypointIndex",
"shortDescription": "The index of the start waypoint for the next mission phase.",
"type": "uint32",
"min" : 1,
"defaultValue": 1
},
{
"name": "ShowAllMissionItems",
"shortDescription": "Determines whether the mission items of the overall mission are displayed or not.",
"type": "bool",
"defaultValue": 1
},
{
"name": "ShowCurrentMissionItems",
"shortDescription": "Determines whether the mission items of the current mission phase are displayed or not.",
"type": "bool",
"defaultValue": 1
},
{
"name": "FlightSpeed",
"shortDescription": "The phase flight speed.",
"type": "double",
"min" : 0.3,
"max" : 5,
"defaultValue": 2
},
{
"name": "ArrivalReturnSpeed",
"shortDescription": "The flight speed for arrival and return path.",
"type": "double",
"min" : 0.3,
"max" : 5,
"defaultValue": 5
},
{
"name": "Altitude",
"shortDescription": "The mission altitude.",
"type": "double",
"min" : 1,
"defaultValue": 5
},
{
"name": "Reverse",
"shortDescription": "Reverses the phase direction. Phases go from high to low waypoint numbers, if checked.",
"type": "bool",
"defaultValue": false
},
{
"name": "SnakeTileWidth",
"shortDescription": "Snake: Tile Width.",
"type": "double",
"min" : 0.3,
"defaultValue": 5
},
{
"name": "SnakeTileHeight",
"shortDescription": "Snake: Tile Height.",
"type": "double",
"min" : 0.3,
"defaultValue": 5
},
{
"name": "SnakeMinTileArea",
"shortDescription": "Snake: Minimal Tile Area.",
"type": "double",
"min" : 0.1,
"defaultValue": 5
},
{
"name": "SnakeLineDistance",
"shortDescription": "Snake: Line Distance.",
"type": "double",
"min" : 0.3,
"defaultValue": 2
},
{
"name": "SnakeMinTransectLength",
"shortDescription": "Snake: Minimal Transect Length.",
"type": "double",
"min" : 0.3,
"defaultValue": 2
}
]
#include "SnakeTile.h" #include "SnakeTile.h"
SnakeTile::SnakeTile(QObject *parent) : WimaAreaData(parent) {} SnakeTile::SnakeTile(QObject *parent) : GeoArea(parent) {}
SnakeTile::SnakeTile(const SnakeTile &other, QObject *parent) SnakeTile::SnakeTile(const SnakeTile &other, QObject *parent)
: WimaAreaData(parent) { : GeoArea(parent) {
*this = other; *this = other;
} }
SnakeTile::~SnakeTile() {} SnakeTile::~SnakeTile() {}
QString SnakeTile::mapVisualQML() const { QString SnakeTile::mapVisualQML() const { return QStringLiteral(""); }
return QStringLiteral("WimaAreaNoVisual.qml");
}
QString SnakeTile::type() const { return "Tile"; }
SnakeTile *SnakeTile::Clone() const { return new SnakeTile(*this); } QString SnakeTile::editorQML() const { return QStringLiteral(""); }
SnakeTile &SnakeTile::operator=(const SnakeTile &other) { SnakeTile *SnakeTile::clone(QObject *parent) const {
this->WimaAreaData::operator=(other); return new SnakeTile(*this, parent);
return *this;
} }
void SnakeTile::push_back(const QGeoCoordinate &c) { this->appendVertex(c); }
#pragma once #pragma once
#include "Wima/Geometry/WimaAreaData.h" #include "geometry/GeoArea.h"
class SnakeTile : public WimaAreaData { #include <QGeoCoordinate>
class SnakeTile : public GeoArea {
Q_OBJECT Q_OBJECT
public: public:
SnakeTile(QObject *parent = nullptr); SnakeTile(QObject *parent = nullptr);
...@@ -10,12 +12,8 @@ public: ...@@ -10,12 +12,8 @@ public:
~SnakeTile(); ~SnakeTile();
virtual QString mapVisualQML() const override; virtual QString mapVisualQML() const override;
virtual QString editorQML() const override;
virtual SnakeTile *clone(QObject *parent) const;
QString type() const override; void push_back(const QGeoCoordinate &c);
SnakeTile *Clone() const;
SnakeTile &operator=(const SnakeTile &other);
protected:
void assign(const SnakeTile &other);
}; };
#pragma once #pragma once
#include "Wima/Geometry/GenericPolygon.h" #include "geometry/GenericPolygon.h"
using SnakeTileLocal = GenericPolygon<QPointF, std::vector>; using SnakeTileLocal = GenericPolygon<QPointF, std::vector>;
#pragma once #pragma once
#include "Wima/Geometry/GenericPolygonArray.h" #include "RouteMissionItem/geometry/GenericPolygonArray.h"
#include "Wima/Snake/SnakeTileLocal.h" #include "RouteMissionItem/nemo_interface/SnakeTileLocal.h"
#include <vector> #include <vector>
typedef GenericPolygonArray<SnakeTileLocal, std::vector> SnakeTilesLocal; typedef GenericPolygonArray<SnakeTileLocal, std::vector> SnakeTilesLocal;
...@@ -26,17 +26,26 @@ Item { ...@@ -26,17 +26,26 @@ Item {
property var qgcView ///< QGCView to use for popping dialogs property var qgcView ///< QGCView to use for popping dialogs
property var _missionItem: object property var _missionItem: object
property var _generator: _missionItem.generator property var _areaData: _missionItem.areaData
property bool _editing: _missionItem.editing
property var _generator: _missionItem.generator
property var _transectsComponent: undefined property var _transectsComponent: undefined
property var _entryCoordinate: undefined property var _entryCoordinate: undefined
property var _exitCoordinate: undefined property var _exitCoordinate: undefined
property var _generatorVisuals: undefined property var _generatorVisuals: undefined
property bool _isCurrentItem: _missionItem.isCurrentItem property bool _isCurrentItem: _missionItem.isCurrentItem
signal clicked(int sequenceNumber) signal clicked(int sequenceNumber)
on_EditingChanged: {
_destroyEntryCoordinate()
_destroyExitCoordinate()
_destroyTransectsComponent()
_destroyGeneratorVisuals()
}
Component.onCompleted: { Component.onCompleted: {
_addEntryCoordinate() _addEntryCoordinate()
_addExitCoordinate() _addExitCoordinate()
...@@ -108,6 +117,19 @@ Item { ...@@ -108,6 +117,19 @@ Item {
} }
} }
Repeater {
model: _areaData.areaList
delegate: WimaMapVisual {
map: _root.map
qgcView: _root.qgcView
}
onItemAdded: {
//console.log("Item added")
}
}
// Generator visuals // Generator visuals
function _addGeneratorVisuals(){ function _addGeneratorVisuals(){
if (_generator.mapVisualQml && !_generatorVisuals) { if (_generator.mapVisualQml && !_generatorVisuals) {
...@@ -183,6 +205,4 @@ Item { ...@@ -183,6 +205,4 @@ Item {
_transectsComponent = undefined _transectsComponent = undefined
} }
} }
} }
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtLocation 5.3
import QtPositioning 5.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FlightMap 1.0
/// Wima Measurement Area Data visuals
Item {
id: _root
property var map ///< Map control to place item in
property var qgcView ///< QGCView to use for popping dialogs
property var areaItem: object
signal clicked(int sequenceNumber)
property var _polygonComponent
function _addPolygon(){
if(!_polygonComponent){
_polygonComponent = polygon.createObject(_root)
map.addMapItem(_polygonComponent)
}
}
function _destroyPolygon(){
if(_polygonComponent){
map.removeMapItem(_polygonComponent)
_polygonComponent.destroy()
}
}
Component.onCompleted: {
_addPolygon()
}
Component.onDestruction: {
_destroyPolygon()
}
// Polygon component.
Component{
id:polygon
MapPolygon {
path: object.path;
border.color: "black"
color: "blue"
opacity: 0.25
}
}
}
/****************************************************************************
*
* (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.
*
****************************************************************************/
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtLocation 5.3
import QtPositioning 5.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FlightMap 1.0
/// Wima Global Measurement Area visuals
Item {
id: _root
property var map ///< Map control to place item in
property var qgcView ///< QGCView to use for popping dialogs
property var areaItem: object
property var _polygon: areaItem
//property var _polyline: areaItem.polyline
signal clicked(int sequenceNumber)
/// Add an initial 4 sided polygon if there is none
function _addInitialPolygon() {
// Initial polygon is inset to take 2/3rds space
var rect = Qt.rect(map.centerViewport.x, map.centerViewport.y, map.centerViewport.width, map.centerViewport.height)
rect.x += (rect.width * 0.25) / 2
rect.y += (rect.height * 0.25) / 2
rect.width *= 0.75
rect.height *= 0.75
var centerCoord = map.toCoordinate(Qt.point(rect.x + (rect.width / 2), rect.y + (rect.height / 2)), false /* clipToViewPort */)
var topLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y), false /* clipToViewPort */)
var topRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y), false /* clipToViewPort */)
var bottomLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y + rect.height), false /* clipToViewPort */)
var bottomRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y + rect.height), false /* clipToViewPort */)
// Adjust polygon to max size
var maxSize = 100
var halfWidthMeters = Math.min(topLeftCoord.distanceTo(topRightCoord), maxSize) / 2
var halfHeightMeters = Math.min(topLeftCoord.distanceTo(bottomLeftCoord), maxSize) / 2
topLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 0)
topRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 0)
bottomLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 180)
bottomRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 180)
if (areaItem.showBorderPolygon.rawValue === true) {
if (areaItem.borderPolygon.count < 3) {
areaItem.borderPolygon.appendVertex(topLeftCoord)
areaItem.borderPolygon.appendVertex(topRightCoord)
areaItem.borderPolygon.appendVertex(bottomRightCoord)
areaItem.borderPolygon.appendVertex(bottomLeftCoord)
}
} else {
if (_polygon.count < 3) {
_polygon.appendVertex(topLeftCoord)
_polygon.appendVertex(topRightCoord)
_polygon.appendVertex(bottomRightCoord)
_polygon.appendVertex(bottomLeftCoord)
}
}
}
Component.onCompleted: {
_addInitialPolygon()
//_addInitialPolyline()
}
Component.onDestruction: {
}
WimaMapPolygonVisuals {
qgcView: _root.qgcView
mapControl: map
mapPolygon: _polygon
borderWidth: 1
borderColor: "black"
interiorColor: "blue"
interiorOpacity: 0.2
}
WimaMapPolygonVisuals {
qgcView: _root.qgcView
mapControl: map
mapPolygon: areaItem.borderPolygon
borderWidth: 1
borderColor: areaItem.borderPolygon.interactive ? "white" : "transparent"
interiorColor: "transparent"
interiorOpacity: 1
}
}
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtLocation 5.3
import QtPositioning 5.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FlightMap 1.0
/// Wima Measurement Area Data visuals
Item {
id: _root
property var map ///< Map control to place item in
property var qgcView ///< QGCView to use for popping dialogs
property var areaItem: object
signal clicked(int sequenceNumber)
property var _polygonComponent
function _addPolygon(){
if(!_polygonComponent){
_polygonComponent = polygon.createObject(_root)
map.addMapItem(_polygonComponent)
}
}
function _destroyPolygon(){
if(_polygonComponent){
map.removeMapItem(_polygonComponent)
_polygonComponent.destroy()
}
}
Component.onCompleted: {
_addPolygon()
}
Component.onDestruction: {
_destroyPolygon()
}
// Polygon component.
Component{
id:polygon
MapPolygon {
path: object.path;
border.color: "black"
color: "gray"
opacity: 0.25
}
}
}
/****************************************************************************
*
* (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.
*
****************************************************************************/
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtLocation 5.3
import QtPositioning 5.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FlightMap 1.0
/// Wima Global Measurement Area visuals
Item {
id: _root
property var map ///< Map control to place item in
property var qgcView ///< QGCView to use for popping dialogs
property var areaItem: object
property var _polygon: areaItem
//property var _polyline: areaItem.polyline
signal clicked(int sequenceNumber)
/// Add an initial 4 sided polygon if there is none
function _addInitialPolygon() {
if (_polygon.count < 3) {
// Initial polygon is inset to take 2/3rds space
var rect = Qt.rect(map.centerViewport.x, map.centerViewport.y, map.centerViewport.width, map.centerViewport.height)
rect.x += (rect.width * 0.25) / 2
rect.y += (rect.height * 0.25) / 2
rect.width *= 0.25
rect.height *= 0.25
var centerCoord = map.toCoordinate(Qt.point(rect.x + (rect.width / 2), rect.y + (rect.height / 2)), false /* clipToViewPort */)
var topLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y), false /* clipToViewPort */)
var topRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y), false /* clipToViewPort */)
var bottomLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y + rect.height), false /* clipToViewPort */)
var bottomRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y + rect.height), false /* clipToViewPort */)
// Adjust polygon to max size
var maxSize = 100
var halfWidthMeters = Math.min(topLeftCoord.distanceTo(topRightCoord), maxSize) / 2
var halfHeightMeters = Math.min(topLeftCoord.distanceTo(bottomLeftCoord), maxSize) / 2
topLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 0)
topRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 0)
bottomLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 180)
bottomRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 180)
_polygon.appendVertex(topLeftCoord)
_polygon.appendVertex(topRightCoord)
_polygon.appendVertex(bottomRightCoord)
_polygon.appendVertex(bottomLeftCoord)
}
}
/*function _addInitialPolyline(){
_polyline.setStartVertexIndex(0);
_polyline.setEndVertexIndex(1);
}*/
Component.onCompleted: {
//_addInitialPolygon()
//_addInitialPolyline()
}
Component.onDestruction: {
}
WimaMapPolygonVisuals {
qgcView: _root.qgcView
mapControl: map
mapPolygon: _polygon
borderWidth: 1
borderColor: "black"
interiorColor: "blue"
interiorOpacity: 0.25
}
/*WimaMapPolylineVisuals {
qgcView: _root.qgcView
mapControl: map
mapPolyline: _polyline
lineWidth: 4
lineColor: interactive ? "white" : "yellow"
enableSplitHandels: false
enableDragHandels: true
edgeHandelsOnly: true
}*/
}
/****************************************************************************
*
* (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.
*
****************************************************************************/
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtLocation 5.3
import QtPositioning 5.3
import QtQuick.Dialogs 1.2
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FlightMap 1.0
import QGroundControl.ShapeFileHelper 1.0
/// QGCmapPolyline map visuals
Item {
id: _root
property var qgcView ///< QGCView for popping dialogs
property var mapControl ///< Map control to place item in
property var mapPolyline ///< QGCMapPolyline object
property bool interactive: mapPolyline.interactive
property int lineWidth: 3
property color lineColor: "#be781c"
property bool enableSplitHandels: true
property bool enableDragHandels: true
property bool edgeHandelsOnly: false
property var _polylineComponent
property var _dragHandlesComponent
property var _splitHandlesComponent
property real _zorderDragHandle: QGroundControl.zOrderMapItems + 3 // Highest to prevent splitting when items overlap
property real _zorderSplitHandle: QGroundControl.zOrderMapItems + 2
function addVisuals() {
_polylineComponent = polylineComponent.createObject(mapControl)
mapControl.addMapItem(_polylineComponent)
}
function removeVisuals() {
_polylineComponent.destroy()
}
function addHandles() {
if (!_dragHandlesComponent) {
if (enableDragHandels){
_dragHandlesComponent = dragHandlesComponent.createObject(mapControl)
}
if (enableSplitHandels){
_splitHandlesComponent = splitHandlesComponent.createObject(mapControl)
}
}
}
function removeHandles() {
if (_dragHandlesComponent) {
_dragHandlesComponent.destroy()
_dragHandlesComponent = undefined
}
if (_splitHandlesComponent) {
_splitHandlesComponent.destroy()
_splitHandlesComponent = undefined
}
}
/// Calculate the default/initial polyline
function defaultPolylineVertices() {
var x = map.centerViewport.x + (map.centerViewport.width / 2)
var yInset = map.centerViewport.height / 4
var topPointCoord = map.toCoordinate(Qt.point(x, map.centerViewport.y + yInset), false /* clipToViewPort */)
var bottomPointCoord = map.toCoordinate(Qt.point(x, map.centerViewport.y + map.centerViewport.height - yInset), false /* clipToViewPort */)
return [ topPointCoord, bottomPointCoord ]
}
/// Add an initial 2 point polyline
function addInitialPolyline() {
if (mapPolyline.count < 2) {
mapPolyline.clear()
var initialVertices = defaultPolylineVertices()
mapPolyline.appendVertex(initialVertices[0])
mapPolyline.appendVertex(initialVertices[1])
}
}
/// Reset polyline back to initial default
function resetPolyline() {
mapPolyline.clear()
addInitialPolyline()
}
onInteractiveChanged: {
if (interactive) {
addHandles()
} else {
removeHandles()
}
}
Component.onCompleted: {
addInitialPolyline()
addVisuals()
if (interactive) {
addHandles()
}
}
Component.onDestruction: {
removeVisuals()
removeHandles()
}
QGCPalette { id: qgcPal }
QGCFileDialog {
id: kmlLoadDialog
qgcView: _root.qgcView
folder: QGroundControl.settingsManager.appSettings.missionSavePath
title: qsTr("Select KML File")
selectExisting: true
nameFilters: ShapeFileHelper.fileDialogKMLFilters
fileExtension: QGroundControl.settingsManager.appSettings.kmlFileExtension
onAcceptedForLoad: {
mapPolyline.loadKMLFile(file)
close()
}
}
Menu {
id: menu
property int _removeVertexIndex
function popUpWithIndex(curIndex) {
_removeVertexIndex = curIndex
removeVertexItem.visible = mapPolyline.count > 2
menu.popup()
}
MenuItem {
id: removeVertexItem
text: qsTr("Remove vertex" )
onTriggered: mapPolyline.removeVertex(menu._removeVertexIndex)
}
MenuItem {
id: swapEndPoints
text: qsTr("Swap End-Points" )
onTriggered: mapPolyline.swapEndPoints()
}
MenuSeparator {
visible: removeVertexItem.visible
}
MenuItem {
text: qsTr("Edit position..." )
onTriggered: qgcView.showDialog(editPositionDialog, qsTr("Edit Position"), qgcView.showDialogDefaultWidth, StandardButton.Cancel)
}
/*MenuItem {
text: qsTr("Load KML...")
onTriggered: kmlLoadDialog.openForLoad()
}*/
}
Component {
id: editPositionDialog
EditPositionDialog {
Component.onCompleted: coordinate = mapPolyline.path[menu._removeVertexIndex]
onCoordinateChanged: mapPolyline.adjustVertex(menu._removeVertexIndex,coordinate)
}
}
Component {
id: polylineComponent
MapPolyline {
line.width: lineWidth
line.color: lineColor
path: mapPolyline.path
}
}
Component {
id: splitHandleComponent
MapQuickItem {
id: mapQuickItem
anchorPoint.x: splitHandle.width / 2
anchorPoint.y: splitHandle.height / 2
property int vertexIndex
sourceItem: Rectangle {
id: splitHandle
width: ScreenTools.defaultFontPixelHeight * 1.5
height: width
radius: width / 2
border.color: "white"
color: "transparent"
opacity: .50
z: _zorderSplitHandle
QGCLabel {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: "+"
}
QGCMouseArea {
fillItem: parent
onClicked: mapPolyline.splitSegment(mapQuickItem.vertexIndex)
}
}
}
}
Component {
id: splitHandlesComponent
Repeater {
model: mapPolyline.path
delegate: Item {
property var _splitHandle
property var _vertices: mapPolyline.path
function _setHandlePosition() {
var nextIndex = index + 1
var distance = _vertices[index].distanceTo(_vertices[nextIndex])
var azimuth = _vertices[index].azimuthTo(_vertices[nextIndex])
_splitHandle.coordinate = _vertices[index].atDistanceAndAzimuth(distance / 2, azimuth)
}
Component.onCompleted: {
if (index + 1 <= _vertices.length - 1) {
_splitHandle = splitHandleComponent.createObject(mapControl)
_splitHandle.vertexIndex = index
_setHandlePosition()
mapControl.addMapItem(_splitHandle)
}
}
Component.onDestruction: {
if (_splitHandle) {
_splitHandle.destroy()
}
}
}
}
}
// Control which is used to drag polygon vertices
Component {
id: dragAreaComponent
MissionItemIndicatorDrag {
mapControl: _root.mapControl
id: dragArea
z: _zorderDragHandle
property int polylineVertex
property bool _creationComplete: false
Component.onCompleted: _creationComplete = true
onItemCoordinateChanged: {
if (_creationComplete) {
// During component creation some bad coordinate values got through which screws up draw
mapPolyline.adjustVertex(polylineVertex, itemCoordinate)
}
}
onClicked: {
menu.popUpWithIndex(polylineVertex)
}
onDragStop: {
if (_creationComplete) {
// During component creation some bad coordinate values got through which screws up draw
mapPolyline.snapVertex(polylineVertex)
}
}
}
}
Component {
id: dragHandleComponent
MapQuickItem {
id: mapQuickItem
anchorPoint.x: dragHandle.width / 2
anchorPoint.y: dragHandle.height / 2
z: _zorderDragHandle
property int polylineVertex
sourceItem: Rectangle {
id: dragHandle
width: ScreenTools.defaultFontPixelHeight * 1.5
height: width
radius: width * 0.5
color: Qt.rgba(1,1,1,0.8)
border.color: Qt.rgba(0,0,0,0.25)
border.width: 1
}
}
}
// Add all polygon vertex drag handles to the map
Component {
id: dragHandlesComponent
Repeater {
model: mapPolyline.pathModel
delegate: Item {
property var _visuals: [ ]
Component.onCompleted: {
var dragHandle = dragHandleComponent.createObject(mapControl)
dragHandle.coordinate = Qt.binding(function() { return object.coordinate })
dragHandle.polylineVertex = Qt.binding(function() { return index })
mapControl.addMapItem(dragHandle)
var dragArea = dragAreaComponent.createObject(mapControl, { "itemIndicator": dragHandle, "itemCoordinate": object.coordinate })
dragArea.polylineVertex = Qt.binding(function() { return index })
_visuals.push(dragHandle)
_visuals.push(dragArea)
}
Component.onDestruction: {
for (var i=0; i<_visuals.length; i++) {
_visuals[i].destroy()
}
_visuals = [ ]
}
}
}
}
}
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtLocation 5.3
import QtPositioning 5.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FlightMap 1.0
/// Wima Measurement Area Data visuals
Item {
id: _root
property var map ///< Map control to place item in
property var qgcView ///< QGCView to use for popping dialogs
property var areaItem: object
signal clicked(int sequenceNumber)
property var _polygonComponent
function _addPolygon(){
if(!_polygonComponent){
_polygonComponent = polygon.createObject(_root)
map.addMapItem(_polygonComponent)
}
}
function _destroyPolygon(){
if(_polygonComponent){
map.removeMapItem(_polygonComponent)
_polygonComponent.destroy()
}
}
Component.onCompleted: {
_addPolygon()
}
Component.onDestruction: {
_destroyPolygon()
}
// Add tiles.
Repeater {
id: progressRepeater
model: areaItem.tiles
Item{
property var _tileComponent
property int _progress: _root.areaItem.progress[index] ?
_root.areaItem.progress[index] : 0
Component.onCompleted: {
_tileComponent = tileComponent.createObject(map)
_tileComponent.polygon.path =
Qt.binding(function(){return object.path})
_tileComponent.polygon.opacity = 0.6
_tileComponent.polygon.border.color = "black"
_tileComponent.polygon.border.width = 1
_tileComponent.polygon.color =
Qt.binding(function(){return getColor(_progress)})
}
Component.onDestruction: {
_tileComponent.destroy()
}
}
}
// Polygon component.
Component{
id:polygon
MapPolygon {
path: object.path;
border.color: "black"
color: "green"
opacity: 0.25
}
}
// Tile component.
Component {
id: tileComponent
Item{
id: root
property MapPolygon polygon
MapPolygon{
id:mapPolygon
path: []
}
Component.onCompleted: {
polygon = mapPolygon
map.addMapItem(mapPolygon)
}
Component.onDestruction: {
map.removeMapItem(mapPolygon)
}
}
}
function getColor(progress) {
if (progress === 0)
return "transparent"
if (progress < 33)
return "orange"
if (progress < 66)
return "yellow"
if (progress < 100)
return "greenyellow"
return "limegreen"
}
}
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtLocation 5.3
import QtPositioning 5.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FlightMap 1.0
/// Wima Measurement Area Data visuals
Item {
id: _root
property var map ///< Map control to place item in
property var qgcView ///< QGCView to use for popping dialogs
property var areaItem: object
signal clicked(int sequenceNumber)
property var _polygonComponent
function _addPolygon(){
if(!_polygonComponent){
_polygonComponent = polygon.createObject(_root)
map.addMapItem(_polygonComponent)
}
}
function _destroyPolygon(){
if(_polygonComponent){
map.removeMapItem(_polygonComponent)
_polygonComponent.destroy()
}
}
Component.onCompleted: {
_addPolygon()
}
Component.onDestruction: {
_destroyPolygon()
}
// Polygon component.
Component{
id:polygon
MapPolygon {
path: object.path;
border.color: "black"
color: "yellow"
opacity: 0.25
}
}
}
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