Commit dd135422 authored by Valentin Platzgummer's avatar Valentin Platzgummer

editing added to RouteComplexItem

parent 0877c5b7
......@@ -421,6 +421,8 @@ INCLUDEPATH += \
src/Vehicle \
src/Audio \
src/comm \
src/RouteMissionItem \
src/RouteMissionItem/geometry \
src/comm/ros_bridge \
src/input \
src/lib/qmapcontrol \
......@@ -445,7 +447,11 @@ contains (DEFINES, QGC_ENABLE_PAIRING) {
HEADERS += \
src/QmlControls/QmlUnitsConversion.h \
src/RouteMissionItem/geometry/GeoArea.h \
src/RouteMissionItem/geometry/MeasurementArea.h \
src/RouteMissionItem/geometry/SafeArea.h \
src/Vehicle/VehicleEscStatusFactGroup.h \
src/RouteMissionItem/AreaData.h \
src/RouteMissionItem/RouteComplexItem.h \
src/RouteMissionItem/GenericSingelton.h \
src/RouteMissionItem/geometry/GenericCircle.h \
......@@ -492,12 +498,6 @@ HEADERS += \
src/api/QGCSettings.h \
src/api/QmlComponentInfo.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/comm/ros_bridge/include/RosBridgeClient.h \
src/comm/ros_bridge/include/com_private.h \
......@@ -522,6 +522,9 @@ contains (DEFINES, QGC_ENABLE_PAIRING) {
}
SOURCES += \
src/RouteMissionItem/geometry/GeoArea.cc \
src/RouteMissionItem/geometry/MeasurementArea.cc \
src/RouteMissionItem/geometry/SafeArea.cc \
src/Vehicle/VehicleEscStatusFactGroup.cc \
src/RouteMissionItem/AreaData.cc \
src/api/QGCCorePlugin.cc \
......@@ -554,12 +557,6 @@ SOURCES += \
src/comm/ros_bridge/include/server.cpp \
src/comm/ros_bridge/include/topic_publisher.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/comm/ros_bridge/src/ros_bridge.cpp
......
......@@ -261,7 +261,6 @@
<file alias="QmlTest.qml">src/QmlControls/QmlTest.qml</file>
<file alias="RadioComponent.qml">src/AutoPilotPlugins/Common/RadioComponent.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="QGroundControl/Controls/CircularGeneratorMapVisual.qml">src/WimaView/CircularGeneratorMapVisual.qml</file>
<file alias="CircularSurveyItemEditor.qml">src/WimaView/CircularSurveyItemEditor.qml</file>
......@@ -273,21 +272,14 @@
<file alias="Wima/ProgressIndicator.qml">src/WimaView/ProgressIndicator.qml</file>
<file alias="Wima/qmldir">src/WimaView/Wima.qmldir</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/WimaCorridorMapVisual.qml">src/WimaView/WimaCorridorMapVisual.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/WimaMapPolylineVisuals.qml">src/WimaView/WimaMapPolylineVisuals.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/WimaMeasurementAreaEditor.qml</file>
<file alias="Wima/WimaMeasurementAreaMapVisual.qml">src/WimaView/WimaMeasurementAreaMapVisual.qml</file>
<file alias="Wima/WimaServiceAreaDataVisual.qml">src/WimaView/WimaServiceAreaDataVisual.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/WimaMeasurementAreaEditor.qml">src/WimaView/MeasurementAreaEditor.qml</file>
<file alias="Wima/WimaMeasurementAreaMapVisual.qml">src/WimaView/MeasurementAreaMapVisual.qml</file>
<file alias="Wima/WimaServiceAreaEditor.qml">src/WimaView/SafeAreaEditor.qml</file>
<file alias="Wima/WimaServiceAreaMapVisual.qml">src/WimaView/SafeAreaMapVisual.qml</file>
<file alias="Wima/WimaToolBar.qml">src/WimaView/WimaToolBar.qml</file>
<file alias="SetupParameterEditor.qml">src/VehicleSetup/SetupParameterEditor.qml</file>
<file alias="SetupView.qml">src/VehicleSetup/SetupView.qml</file>
......@@ -360,9 +352,9 @@
<file alias="Video.SettingsGroup.json">src/Settings/Video.SettingsGroup.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="CircularSurvey.SettingsGroup.json">src/Wima/json/CircularSurvey.SettingsGroup.json</file>
<file alias="LinearGenerator.SettingsGroup.json">src/Wima/Snake/json/LinearGenerator.SettingsGroup.json</file>
<file alias="CircularGenerator.SettingsGroup.json">src/Wima/Snake/json/CircularGenerator.SettingsGroup.json</file>
<file alias="RouteComplexItem.SettingsGroup.json">src/RouteMissionItem/json/RouteComplexItem.SettingsGroup.json</file>
<file alias="LinearGenerator.SettingsGroup.json">src/RouteMissionItem/json/LinearGenerator.SettingsGroup.json</file>
<file alias="CircularGenerator.SettingsGroup.json">src/RouteMissionItem/json/CircularGenerator.SettingsGroup.json</file>
</qresource>
<qresource prefix="/MockLink">
<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 @@
#include "QmlObjectListModel.h"
class SurveyComplexItem;
class CircularSurvey;
class RouteComplexItem;
class SimpleMissionItem;
class MissionController;
#ifdef UNITTEST_BUILD
......@@ -153,7 +153,7 @@ private:
static const char* _jsonParam4Key;
friend class SurveyComplexItem;
friend class CircularSurvey;
friend class RouteComplexItem;
friend class SimpleMissionItem;
friend class MissionController;
#ifdef UNITTEST_BUILD
......
......@@ -7,7 +7,6 @@
*
****************************************************************************/
/**
* @file
* @brief Implementation of class QGCApplication
......@@ -16,19 +15,19 @@
*
*/
#include <QAction>
#include <QDesktopWidget>
#include <QFile>
#include <QFlags>
#include <QPixmap>
#include <QDesktopWidget>
#include <QPainter>
#include <QStyleFactory>
#include <QAction>
#include <QStringListModel>
#include <QRegularExpression>
#include <QFontDatabase>
#include <QQuickWindow>
#include <QPainter>
#include <QPixmap>
#include <QQuickImageProvider>
#include <QQuickStyle>
#include <QQuickWindow>
#include <QRegularExpression>
#include <QStringListModel>
#include <QStyleFactory>
#ifdef QGC_ENABLE_BLUETOOTH
#include <QBluetoothLocalDevice>
......@@ -40,78 +39,79 @@
#include "GStreamer.h"
#endif
#include "QGC.h"
#include "QGCApplication.h"
#include "AutoPilotPlugin.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 "ScreenToolsController.h"
#include "QGCFileDialogController.h"
#include "RCChannelMonitorController.h"
#include "SyslinkComponentController.h"
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
#include "FirmwarePluginManager.h"
#include "MultiVehicleManager.h"
#include "Vehicle.h"
#include "FlightMapSettings.h"
#include "FlightPathSegment.h"
#include "JoystickConfigController.h"
#include "JoystickManager.h"
#include "QmlObjectListModel.h"
#include "QGCGeoBoundingCube.h"
#include "LinkManager.h"
#include "LogDownloadController.h"
#include "MissionManager.h"
#include "QGroundControlQmlGlobal.h"
#include "FlightMapSettings.h"
#include "FlightPathSegment.h"
#include "MultiVehicleManager.h"
#include "ParameterEditorController.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 "VideoReceiver.h"
#include "LogDownloadController.h"
#if defined(QGC_ENABLE_MAVLINK_INSPECTOR)
#include "MAVLinkInspectorController.h"
#endif
#include "HorizontalFactValueGrid.h"
#include "InstrumentValueData.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 "VisualMissionItem.h"
#include "EditPositionDialogController.h"
#include "FactValueSliderListModel.h"
#include "ShapeFileHelper.h"
#include "QGCFileDownload.h"
#include "FirmwareImage.h"
#include "MavlinkConsoleController.h"
#include "FollowMe.h"
#include "GeoTagController.h"
#include "HorizontalFactValueGrid.h"
#include "InstrumentValueData.h"
#include "LogReplayLink.h"
#include "VehicleObjectAvoidance.h"
#include "TrajectoryPoints.h"
#include "RCToParamDialogController.h"
#include "MavlinkConsoleController.h"
#include "MissionCommandTree.h"
#include "ParameterManager.h"
#include "PositionManager.h"
#include "QGCCameraManager.h"
#include "QGCCorePlugin.h"
#include "QGCFileDownload.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 "ToolStripAction.h"
#include "ToolStripActionList.h"
#include "QGCMAVLink.h"
#include "TrajectoryPoints.h"
#include "VehicleLinkManager.h"
#include "VehicleObjectAvoidance.h"
#include "VisualMissionItem.h"
#include "Wima/Snake/CircularGenerator.h"
#include "Wima/Snake/LinearGenerator.h"
#include "Wima/Snake/NemoInterface.h"
#include "AreaData.h"
#include "CircularGenerator.h"
#include "LinearGenerator.h"
#include "NemoInterface.h"
#if defined(QGC_ENABLE_PAIRING)
#include "PairingManager.h"
......@@ -135,849 +135,961 @@
#ifdef Q_OS_LINUX
#ifndef __mobile__
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#endif
#include "QGCMapEngine.h"
class FinishVideoInitialization : public QRunnable
{
class FinishVideoInitialization : public QRunnable {
public:
FinishVideoInitialization(VideoManager* manager)
: _manager(manager)
{}
FinishVideoInitialization(VideoManager *manager) : _manager(manager) {}
void run () {
_manager->_initVideo();
}
void run() { _manager->_initVideo(); }
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_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS];
// Qml Singleton factories
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
{
ScreenToolsController* screenToolsController = new ScreenToolsController;
return screenToolsController;
static QObject *screenToolsControllerSingletonFactory(QQmlEngine *,
QJSEngine *) {
ScreenToolsController *screenToolsController = new ScreenToolsController;
return screenToolsController;
}
static QObject* mavlinkSingletonFactory(QQmlEngine*, QJSEngine*)
{
return new QGCMAVLink();
static QObject *mavlinkSingletonFactory(QQmlEngine *, QJSEngine *) {
return new QGCMAVLink();
}
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
// We create this object as a QGCTool even though it isn't in the toolbox
QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp(), qgcApp()->toolbox());
qmlGlobal->setToolbox(qgcApp()->toolbox());
static QObject *qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine *,
QJSEngine *) {
// We create this object as a QGCTool even though it isn't in the toolbox
QGroundControlQmlGlobal *qmlGlobal =
new QGroundControlQmlGlobal(qgcApp(), qgcApp()->toolbox());
qmlGlobal->setToolbox(qgcApp()->toolbox());
return qmlGlobal;
return qmlGlobal;
}
static QObject* shapeFileHelperSingletonFactory(QQmlEngine*, QJSEngine*)
{
return new ShapeFileHelper;
static QObject *shapeFileHelperSingletonFactory(QQmlEngine *, QJSEngine *) {
return new ShapeFileHelper;
}
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
: QApplication (argc, argv)
, _runningUnitTests (unitTesting)
{
_app = this;
_msecsElapsedTime.start();
QGCApplication::QGCApplication(int &argc, char *argv[], bool unitTesting)
: QApplication(argc, argv), _runningUnitTests(unitTesting) {
_app = this;
_msecsElapsedTime.start();
#ifdef Q_OS_LINUX
#ifndef __mobile__
if (!_runningUnitTests) {
if (getuid() == 0) {
_exitWithError(QString(
tr("You are running %1 as root. "
"You should not do this since it will cause other issues with %1."
"%1 will now exit.<br/><br/>"
"If you are having serial port issues on Ubuntu, execute the following commands to fix most issues:<br/>"
"<pre>sudo usermod -a -G dialout $USER<br/>"
"sudo apt-get remove modemmanager</pre>").arg(qgcApp()->applicationName())));
return;
}
// Determine if we have the correct permissions to access USB serial devices
QFile permFile("/etc/group");
if(permFile.open(QIODevice::ReadOnly)) {
while(!permFile.atEnd()) {
QString line = permFile.readLine();
if (line.contains("dialout") && !line.contains(getenv("USER"))) {
permFile.close();
_exitWithError(QString(
tr("The current user does not have the correct permissions to access serial devices. "
"You should also remove modemmanager since it also interferes.<br/><br/>"
"If you are using Ubuntu, execute the following commands to fix these issues:<br/>"
"<pre>sudo usermod -a -G dialout $USER<br/>"
"sudo apt-get remove modemmanager</pre>")));
return;
}
}
permFile.close();
if (!_runningUnitTests) {
if (getuid() == 0) {
_exitWithError(QString(
tr("You are running %1 as root. "
"You should not do this since it will cause other issues with %1."
"%1 will now exit.<br/><br/>"
"If you are having serial port issues on Ubuntu, execute the "
"following commands to fix most issues:<br/>"
"<pre>sudo usermod -a -G dialout $USER<br/>"
"sudo apt-get remove modemmanager</pre>")
.arg(qgcApp()->applicationName())));
return;
}
// Determine if we have the correct permissions to access USB serial devices
QFile permFile("/etc/group");
if (permFile.open(QIODevice::ReadOnly)) {
while (!permFile.atEnd()) {
QString line = permFile.readLine();
if (line.contains("dialout") && !line.contains(getenv("USER"))) {
permFile.close();
_exitWithError(
QString(tr("The current user does not have the correct "
"permissions to access serial devices. "
"You should also remove modemmanager since it also "
"interferes.<br/><br/>"
"If you are using Ubuntu, execute the following "
"commands to fix these issues:<br/>"
"<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
QQuickStyle::setStyle("Default");
}
permFile.close();
}
// 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
// Setup for network proxy support
QNetworkProxyFactory::setUseSystemConfiguration(true);
// Parse command line options
bool fClearSettingsOptions = false; // Clear stored settings
bool fClearCache = false; // Clear parameter/airframe caches
bool logging = false; // Turn on logging
QString loggingOptions;
CmdLineOpt_t rgCmdLineOptions[] = {
{ "--clear-settings", &fClearSettingsOptions, nullptr },
{ "--clear-cache", &fClearCache, nullptr },
{ "--logging", &logging, &loggingOptions },
{ "--fake-mobile", &_fakeMobile, nullptr },
{ "--log-output", &_logOutput, nullptr },
// Add additional command line option flags here
};
ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
// Set up timer for delayed missing fact display
_missingParamsDelayedDisplayTimer.setSingleShot(true);
_missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
// Set application information
QString applicationName;
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 {
// Setup for network proxy support
QNetworkProxyFactory::setUseSystemConfiguration(true);
// Parse command line options
bool fClearSettingsOptions = false; // Clear stored settings
bool fClearCache = false; // Clear parameter/airframe caches
bool logging = false; // Turn on logging
QString loggingOptions;
CmdLineOpt_t rgCmdLineOptions[] = {
{"--clear-settings", &fClearSettingsOptions, nullptr},
{"--clear-cache", &fClearCache, nullptr},
{"--logging", &logging, &loggingOptions},
{"--fake-mobile", &_fakeMobile, nullptr},
{"--log-output", &_logOutput, nullptr},
// Add additional command line option flags here
};
ParseCmdLineOptions(argc, argv, rgCmdLineOptions,
sizeof(rgCmdLineOptions) / sizeof(rgCmdLineOptions[0]),
false);
// Set up timer for delayed missing fact display
_missingParamsDelayedDisplayTimer.setSingleShot(true);
_missingParamsDelayedDisplayTimer.setInterval(
_missingParamsDelayedDisplayTimerTimeout);
connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this,
&QGCApplication::_missingParamsDisplay);
// Set application information
QString applicationName;
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
// This gives daily builds their own separate settings space. Allowing you to use daily and stable builds
// side by side without daily screwing up your stable settings.
applicationName = QStringLiteral("%1 Daily").arg(QGC_APPLICATION_NAME);
// This gives daily builds their own separate settings space. Allowing you
// to use daily and stable builds side by side without daily screwing up
// your stable settings.
applicationName = QStringLiteral("%1 Daily").arg(QGC_APPLICATION_NAME);
#else
applicationName = QGC_APPLICATION_NAME;
applicationName = QGC_APPLICATION_NAME;
#endif
}
setApplicationName(applicationName);
setOrganizationName(QGC_ORG_NAME);
setOrganizationDomain(QGC_ORG_DOMAIN);
}
setApplicationName(applicationName);
setOrganizationName(QGC_ORG_NAME);
setOrganizationDomain(QGC_ORG_DOMAIN);
this->setApplicationVersion(QString(GIT_VERSION));
this->setApplicationVersion(QString(GIT_VERSION));
// Set settings format
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings settings;
qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
// Set settings format
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings settings;
qDebug() << "Settings location" << settings.fileName()
<< "Is writable?:" << settings.isWritable();
#ifdef UNITTEST_BUILD
if (!settings.isWritable()) {
qWarning() << "Setings location is not writable";
}
if (!settings.isWritable()) {
qWarning() << "Setings location is not writable";
}
#endif
// The setting will delete all settings on this boot
fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
// The setting will delete all settings on this boot
fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
if (_runningUnitTests) {
// Unit tests run with clean settings
fClearSettingsOptions = true;
}
if (_runningUnitTests) {
// Unit tests run with clean settings
fClearSettingsOptions = true;
}
if (fClearSettingsOptions) {
// User requested settings to be cleared on command line
if (fClearSettingsOptions) {
// 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();
// 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();
_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();
}
// Set up our logging filters
QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(loggingOptions);
// Set up our logging filters
QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(
loggingOptions);
// Initialize Bluetooth
// Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
QBluetoothLocalDevice localDevice;
if (localDevice.isValid())
{
_bluetoothAvailable = true;
}
QBluetoothLocalDevice localDevice;
if (localDevice.isValid()) {
_bluetoothAvailable = true;
}
#endif
// Gstreamer debug settings
int gstDebugLevel = 0;
if (settings.contains(AppSettings::gstDebugLevelName)) {
gstDebugLevel = settings.value(AppSettings::gstDebugLevelName).toInt();
}
// Gstreamer debug settings
int gstDebugLevel = 0;
if (settings.contains(AppSettings::gstDebugLevelName)) {
gstDebugLevel = settings.value(AppSettings::gstDebugLevelName).toInt();
}
#if defined(QGC_GST_STREAMING)
// Initialize Video Receiver
GStreamer::initialize(argc, argv, gstDebugLevel);
// Initialize Video Receiver
GStreamer::initialize(argc, argv, gstDebugLevel);
#else
Q_UNUSED(gstDebugLevel)
Q_UNUSED(gstDebugLevel)
#endif
// We need to set language as early as possible prior to loading on JSON files.
setLanguage();
// We need to set language as early as possible prior to loading on JSON
// files.
setLanguage();
_toolbox = new QGCToolbox(this);
_toolbox->setChildToolboxes();
_toolbox = new QGCToolbox(this);
_toolbox->setChildToolboxes();
#ifndef __mobile__
_gpsRtkFactGroup = new GPSRTKFactGroup(this);
GPSManager *gpsManager = _toolbox->gpsManager();
if (gpsManager) {
connect(gpsManager, &GPSManager::onConnect, this, &QGCApplication::_onGPSConnect);
connect(gpsManager, &GPSManager::onDisconnect, this, &QGCApplication::_onGPSDisconnect);
connect(gpsManager, &GPSManager::surveyInStatus, this, &QGCApplication::_gpsSurveyInStatus);
connect(gpsManager, &GPSManager::satelliteUpdate, this, &QGCApplication::_gpsNumSatellites);
}
_gpsRtkFactGroup = new GPSRTKFactGroup(this);
GPSManager *gpsManager = _toolbox->gpsManager();
if (gpsManager) {
connect(gpsManager, &GPSManager::onConnect, this,
&QGCApplication::_onGPSConnect);
connect(gpsManager, &GPSManager::onDisconnect, this,
&QGCApplication::_onGPSDisconnect);
connect(gpsManager, &GPSManager::surveyInStatus, this,
&QGCApplication::_gpsSurveyInStatus);
connect(gpsManager, &GPSManager::satelliteUpdate, this,
&QGCApplication::_gpsNumSatellites);
}
#endif /* __mobile__ */
_checkForNewVersion();
}
void QGCApplication::_exitWithError(QString errorMessage)
{
_error = true;
QQmlApplicationEngine* pEngine = new QQmlApplicationEngine(this);
pEngine->addImportPath("qrc:/qml");
pEngine->rootContext()->setContextProperty("errorMessage", errorMessage);
pEngine->load(QUrl(QStringLiteral("qrc:/qml/ExitWithErrorWindow.qml")));
// Exit main application when last window is closed
connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
}
void QGCApplication::setLanguage()
{
_locale = QLocale::system();
qDebug() << "System reported locale:" << _locale << "; Name" << _locale.name() << "; Preffered (used in maps): " << (QLocale::system().uiLanguages().length() > 0 ? QLocale::system().uiLanguages()[0] : "None");
int langID = AppSettings::_languageID();
//-- See App.SettinsGroup.json for index
if(langID) {
switch(langID) {
case 1:
_locale = QLocale(QLocale::Bulgarian);
break;
case 2:
_locale = QLocale(QLocale::Chinese);
break;
case 3:
_locale = QLocale(QLocale::Dutch);
break;
case 4:
_locale = QLocale(QLocale::English);
break;
case 5:
_locale = QLocale(QLocale::Finnish);
break;
case 6:
_locale = QLocale(QLocale::French);
break;
case 7:
_locale = QLocale(QLocale::German);
break;
case 8:
_locale = QLocale(QLocale::Greek);
break;
case 9:
_locale = QLocale(QLocale::Hebrew);
break;
case 10:
_locale = QLocale(QLocale::Italian);
break;
case 11:
_locale = QLocale(QLocale::Japanese);
break;
case 12:
_locale = QLocale(QLocale::Korean);
break;
case 13:
_locale = QLocale(QLocale::Norwegian);
break;
case 14:
_locale = QLocale(QLocale::Polish);
break;
case 15:
_locale = QLocale(QLocale::Portuguese);
break;
case 16:
_locale = QLocale(QLocale::Russian);
break;
case 17:
_locale = QLocale(QLocale::Spanish);
break;
case 18:
_locale = QLocale(QLocale::Swedish);
break;
case 19:
_locale = QLocale(QLocale::Turkish);
break;
case 20:
_locale = QLocale(QLocale::Azerbaijani);
break;
}
_checkForNewVersion();
}
void QGCApplication::_exitWithError(QString errorMessage) {
_error = true;
QQmlApplicationEngine *pEngine = new QQmlApplicationEngine(this);
pEngine->addImportPath("qrc:/qml");
pEngine->rootContext()->setContextProperty("errorMessage", errorMessage);
pEngine->load(QUrl(QStringLiteral("qrc:/qml/ExitWithErrorWindow.qml")));
// Exit main application when last window is closed
connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
}
void QGCApplication::setLanguage() {
_locale = QLocale::system();
qDebug() << "System reported locale:" << _locale << "; Name" << _locale.name()
<< "; Preffered (used in maps): "
<< (QLocale::system().uiLanguages().length() > 0
? QLocale::system().uiLanguages()[0]
: "None");
int langID = AppSettings::_languageID();
//-- See App.SettinsGroup.json for index
if (langID) {
switch (langID) {
case 1:
_locale = QLocale(QLocale::Bulgarian);
break;
case 2:
_locale = QLocale(QLocale::Chinese);
break;
case 3:
_locale = QLocale(QLocale::Dutch);
break;
case 4:
_locale = QLocale(QLocale::English);
break;
case 5:
_locale = QLocale(QLocale::Finnish);
break;
case 6:
_locale = QLocale(QLocale::French);
break;
case 7:
_locale = QLocale(QLocale::German);
break;
case 8:
_locale = QLocale(QLocale::Greek);
break;
case 9:
_locale = QLocale(QLocale::Hebrew);
break;
case 10:
_locale = QLocale(QLocale::Italian);
break;
case 11:
_locale = QLocale(QLocale::Japanese);
break;
case 12:
_locale = QLocale(QLocale::Korean);
break;
case 13:
_locale = QLocale(QLocale::Norwegian);
break;
case 14:
_locale = QLocale(QLocale::Polish);
break;
case 15:
_locale = QLocale(QLocale::Portuguese);
break;
case 16:
_locale = QLocale(QLocale::Russian);
break;
case 17:
_locale = QLocale(QLocale::Spanish);
break;
case 18:
_locale = QLocale(QLocale::Swedish);
break;
case 19:
_locale = QLocale(QLocale::Turkish);
break;
case 20:
_locale = QLocale(QLocale::Azerbaijani);
break;
}
//-- We have specific fonts for Korean
if(_locale == QLocale::Korean) {
qCDebug(LocalizationLog) << "Loading Korean fonts" << _locale.name();
if(QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Regular") < 0) {
qCWarning(LocalizationLog) << "Could not load /fonts/NanumGothic-Regular font";
}
if(QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Bold") < 0) {
qCWarning(LocalizationLog) << "Could not load /fonts/NanumGothic-Bold font";
}
}
//-- We have specific fonts for Korean
if (_locale == QLocale::Korean) {
qCDebug(LocalizationLog) << "Loading Korean fonts" << _locale.name();
if (QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Regular") < 0) {
qCWarning(LocalizationLog)
<< "Could not load /fonts/NanumGothic-Regular font";
}
qCDebug(LocalizationLog) << "Loading localizations for" << _locale.name();
_app->removeTranslator(&_qgcTranslatorJSON);
_app->removeTranslator(&_qgcTranslatorSourceCode);
_app->removeTranslator(&_qgcTranslatorQtLibs);
if (_locale.name() != "en_US") {
QLocale::setDefault(_locale);
if(_qgcTranslatorQtLibs.load("qt_" + _locale.name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
_app->installTranslator(&_qgcTranslatorQtLibs);
} else {
qCWarning(LocalizationLog) << "Qt lib localization for" << _locale.name() << "is not present";
}
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 (QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Bold") < 0) {
qCWarning(LocalizationLog)
<< "Could not load /fonts/NanumGothic-Bold font";
}
}
qCDebug(LocalizationLog) << "Loading localizations for" << _locale.name();
_app->removeTranslator(&_qgcTranslatorJSON);
_app->removeTranslator(&_qgcTranslatorSourceCode);
_app->removeTranslator(&_qgcTranslatorQtLibs);
if (_locale.name() != "en_US") {
QLocale::setDefault(_locale);
if (_qgcTranslatorQtLibs.load(
"qt_" + _locale.name(),
QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
_app->installTranslator(&_qgcTranslatorQtLibs);
} else {
qCWarning(LocalizationLog)
<< "Qt lib localization for" << _locale.name() << "is not present";
}
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();
emit languageChanged(_locale);
}
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.
delete _qmlAppEngine;
delete _toolbox;
delete _gpsRtkFactGroup;
}
QGCApplication::~QGCApplication()
{
// Place shutdown code in _shutdown
_app = nullptr;
}
void QGCApplication::_initCommon()
{
static const char* kRefOnly = "Reference only";
static const char* kQGroundControl = "QGroundControl";
static const char* kQGCControllers = "QGroundControl.Controllers";
static const char* kQGCVehicle = "QGroundControl.Vehicle";
static const char* kQGCTemplates = "QGroundControl.Templates";
QSettings settings;
// Register our Qml objects
qmlRegisterType<QGCPalette> ("QGroundControl.Palette", 1, 0, "QGCPalette");
qmlRegisterType<QGCMapPalette> ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
qmlRegisterUncreatableType<Vehicle> (kQGCVehicle, 1, 0, "Vehicle", kRefOnly);
qmlRegisterUncreatableType<MissionManager> (kQGCVehicle, 1, 0, "MissionManager", kRefOnly);
qmlRegisterUncreatableType<ParameterManager> (kQGCVehicle, 1, 0, "ParameterManager", kRefOnly);
qmlRegisterUncreatableType<VehicleObjectAvoidance> (kQGCVehicle, 1, 0, "VehicleObjectAvoidance", kRefOnly);
qmlRegisterUncreatableType<QGCCameraManager> (kQGCVehicle, 1, 0, "QGCCameraManager", kRefOnly);
qmlRegisterUncreatableType<QGCCameraControl> (kQGCVehicle, 1, 0, "QGCCameraControl", kRefOnly);
qmlRegisterUncreatableType<QGCVideoStreamInfo> (kQGCVehicle, 1, 0, "QGCVideoStreamInfo", kRefOnly);
qmlRegisterUncreatableType<LinkInterface> (kQGCVehicle, 1, 0, "LinkInterface", kRefOnly);
qmlRegisterUncreatableType<VehicleLinkManager> (kQGCVehicle, 1, 0, "VehicleLinkManager", kRefOnly);
qmlRegisterUncreatableType<MissionController> (kQGCControllers, 1, 0, "MissionController", kRefOnly);
qmlRegisterUncreatableType<GeoFenceController> (kQGCControllers, 1, 0, "GeoFenceController", kRefOnly);
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 (_qmlAppEngine)
_qmlAppEngine->retranslate();
emit languageChanged(_locale);
}
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.
delete _qmlAppEngine;
delete _toolbox;
delete _gpsRtkFactGroup;
}
QGCApplication::~QGCApplication() {
// Place shutdown code in _shutdown
_app = nullptr;
}
void QGCApplication::_initCommon() {
static const char *kRefOnly = "Reference only";
static const char *kQGroundControl = "QGroundControl";
static const char *kQGCControllers = "QGroundControl.Controllers";
static const char *kQGCVehicle = "QGroundControl.Vehicle";
static const char *kQGCTemplates = "QGroundControl.Templates";
QSettings settings;
// Register our Qml objects
qmlRegisterType<QGCPalette>("QGroundControl.Palette", 1, 0, "QGCPalette");
qmlRegisterType<QGCMapPalette>("QGroundControl.Palette", 1, 0,
"QGCMapPalette");
qmlRegisterUncreatableType<Vehicle>(kQGCVehicle, 1, 0, "Vehicle", kRefOnly);
qmlRegisterUncreatableType<MissionManager>(kQGCVehicle, 1, 0,
"MissionManager", kRefOnly);
qmlRegisterUncreatableType<ParameterManager>(kQGCVehicle, 1, 0,
"ParameterManager", kRefOnly);
qmlRegisterUncreatableType<VehicleObjectAvoidance>(
kQGCVehicle, 1, 0, "VehicleObjectAvoidance", kRefOnly);
qmlRegisterUncreatableType<QGCCameraManager>(kQGCVehicle, 1, 0,
"QGCCameraManager", kRefOnly);
qmlRegisterUncreatableType<QGCCameraControl>(kQGCVehicle, 1, 0,
"QGCCameraControl", kRefOnly);
qmlRegisterUncreatableType<QGCVideoStreamInfo>(
kQGCVehicle, 1, 0, "QGCVideoStreamInfo", kRefOnly);
qmlRegisterUncreatableType<LinkInterface>(kQGCVehicle, 1, 0, "LinkInterface",
kRefOnly);
qmlRegisterUncreatableType<VehicleLinkManager>(
kQGCVehicle, 1, 0, "VehicleLinkManager", kRefOnly);
qmlRegisterUncreatableType<MissionController>(kQGCControllers, 1, 0,
"MissionController", kRefOnly);
qmlRegisterUncreatableType<GeoFenceController>(
kQGCControllers, 1, 0, "GeoFenceController", kRefOnly);
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)
qmlRegisterUncreatableType<MAVLinkChartController> (kQGroundControl, 1, 0, "MAVLinkChart", kRefOnly);
qmlRegisterUncreatableType<MAVLinkChartController>(kQGroundControl, 1, 0,
"MAVLinkChart", kRefOnly);
#endif
#if defined(QGC_ENABLE_PAIRING)
qmlRegisterUncreatableType<PairingManager> (kQGroundControl, 1, 0, "PairingManager", kRefOnly);
qmlRegisterUncreatableType<PairingManager>(kQGroundControl, 1, 0,
"PairingManager", kRefOnly);
#endif
qmlRegisterUncreatableType<AutoPilotPlugin> ("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", kRefOnly);
qmlRegisterUncreatableType<VehicleComponent> ("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", kRefOnly);
qmlRegisterUncreatableType<JoystickManager> ("QGroundControl.JoystickManager", 1, 0, "JoystickManager", kRefOnly);
qmlRegisterUncreatableType<Joystick> ("QGroundControl.JoystickManager", 1, 0, "Joystick", kRefOnly);
qmlRegisterUncreatableType<QGCPositionManager> ("QGroundControl.QGCPositionManager", 1, 0, "QGCPositionManager", kRefOnly);
qmlRegisterUncreatableType<FactValueSliderListModel>("QGroundControl.FactControls", 1, 0, "FactValueSliderListModel", kRefOnly);
qmlRegisterUncreatableType<QGCMapPolygon> ("QGroundControl.FlightMap", 1, 0, "QGCMapPolygon", kRefOnly);
qmlRegisterUncreatableType<QGCGeoBoundingCube> ("QGroundControl.FlightMap", 1, 0, "QGCGeoBoundingCube", kRefOnly);
qmlRegisterUncreatableType<TrajectoryPoints> ("QGroundControl.FlightMap", 1, 0, "TrajectoryPoints", kRefOnly);
qmlRegisterUncreatableType<FactValueGrid> (kQGCTemplates, 1, 0, "FactValueGrid", kRefOnly);
qmlRegisterType<HorizontalFactValueGrid> (kQGCTemplates, 1, 0, "HorizontalFactValueGrid");
qmlRegisterType<QGCMapCircle> ("QGroundControl.FlightMap", 1, 0, "QGCMapCircle");
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");
qmlRegisterUncreatableType<AutoPilotPlugin>(
"QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", kRefOnly);
qmlRegisterUncreatableType<VehicleComponent>(
"QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", kRefOnly);
qmlRegisterUncreatableType<JoystickManager>(
"QGroundControl.JoystickManager", 1, 0, "JoystickManager", kRefOnly);
qmlRegisterUncreatableType<Joystick>("QGroundControl.JoystickManager", 1, 0,
"Joystick", kRefOnly);
qmlRegisterUncreatableType<QGCPositionManager>(
"QGroundControl.QGCPositionManager", 1, 0, "QGCPositionManager",
kRefOnly);
qmlRegisterUncreatableType<FactValueSliderListModel>(
"QGroundControl.FactControls", 1, 0, "FactValueSliderListModel",
kRefOnly);
qmlRegisterUncreatableType<QGCMapPolygon>("QGroundControl.FlightMap", 1, 0,
"QGCMapPolygon", kRefOnly);
qmlRegisterUncreatableType<QGCGeoBoundingCube>(
"QGroundControl.FlightMap", 1, 0, "QGCGeoBoundingCube", kRefOnly);
qmlRegisterUncreatableType<TrajectoryPoints>("QGroundControl.FlightMap", 1, 0,
"TrajectoryPoints", kRefOnly);
qmlRegisterUncreatableType<FactValueGrid>(kQGCTemplates, 1, 0,
"FactValueGrid", kRefOnly);
qmlRegisterType<HorizontalFactValueGrid>(kQGCTemplates, 1, 0,
"HorizontalFactValueGrid");
qmlRegisterType<QGCMapCircle>("QGroundControl.FlightMap", 1, 0,
"QGCMapCircle");
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 NO_SERIAL_LINK
qmlRegisterType<FirmwareUpgradeController> (kQGCControllers, 1, 0, "FirmwareUpgradeController");
qmlRegisterType<FirmwareUpgradeController>(kQGCControllers, 1, 0,
"FirmwareUpgradeController");
#endif
#endif
// Wima
qmlRegisterType<NemoInterface>("Wima", 1, 0, "NemoInterface");
qmlRegisterUncreatableType<routing::LinearGenerator>(
"RouteComplexItem", 1, 0, "LinearGenerator", kRefOnly);
qmlRegisterType<AreaData>("RouteComplexItem", 1, 0, "AreaData");
qmlRegisterUncreatableType<NemoInterface>("RouteComplexItem", 1, 0,
"NemoInterface", kRefOnly);
qmlRegisterInterface<routing::GeneratorBase>("GeneratorBase");
qmlRegisterType<routing::CircularGenerator>("Wima", 1, 0,
"CircularGenerator");
qmlRegisterType<routing::LinearGenerator>("Wima", 1, 0, "LinearGenerator");
qmlRegisterUncreatableType<routing::CircularGenerator>(
"RouteComplexItem", 1, 0, "CircularGenerator", kRefOnly);
qmlRegisterType<GeoTagController> (kQGCControllers, 1, 0, "GeoTagController");
qmlRegisterType<MavlinkConsoleController> (kQGCControllers, 1, 0, "MavlinkConsoleController");
qmlRegisterType<GeoTagController>(kQGCControllers, 1, 0, "GeoTagController");
qmlRegisterType<MavlinkConsoleController>(kQGCControllers, 1, 0,
"MavlinkConsoleController");
#if defined(QGC_ENABLE_MAVLINK_INSPECTOR)
qmlRegisterType<MAVLinkInspectorController> (kQGCControllers, 1, 0, "MAVLinkInspectorController");
qmlRegisterType<MAVLinkInspectorController>(kQGCControllers, 1, 0,
"MAVLinkInspectorController");
#endif
// Register Qml Singletons
qmlRegisterSingletonType<QGroundControlQmlGlobal> ("QGroundControl", 1, 0, "QGroundControl", qgroundcontrolQmlGlobalSingletonFactory);
qmlRegisterSingletonType<ScreenToolsController> ("QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController", screenToolsControllerSingletonFactory);
qmlRegisterSingletonType<ShapeFileHelper> ("QGroundControl.ShapeFileHelper", 1, 0, "ShapeFileHelper", shapeFileHelperSingletonFactory);
qmlRegisterSingletonType<ShapeFileHelper> ("MAVLink", 1, 0, "MAVLink", 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";
}
// Register Qml Singletons
qmlRegisterSingletonType<QGroundControlQmlGlobal>(
"QGroundControl", 1, 0, "QGroundControl",
qgroundcontrolQmlGlobalSingletonFactory);
qmlRegisterSingletonType<ScreenToolsController>(
"QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController",
screenToolsControllerSingletonFactory);
qmlRegisterSingletonType<ShapeFileHelper>("QGroundControl.ShapeFileHelper", 1,
0, "ShapeFileHelper",
shapeFileHelperSingletonFactory);
qmlRegisterSingletonType<ShapeFileHelper>("MAVLink", 1, 0, "MAVLink",
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()
{
QSettings settings;
bool QGCApplication::_initForNormalAppBoot() {
QSettings settings;
_qmlAppEngine = toolbox()->corePlugin()->createQmlApplicationEngine(this);
toolbox()->corePlugin()->createRootWindow(_qmlAppEngine);
_qmlAppEngine = toolbox()->corePlugin()->createQmlApplicationEngine(this);
toolbox()->corePlugin()->createRootWindow(_qmlAppEngine);
// Image provider for PX4 Flow
QQuickImageProvider* pImgProvider = dynamic_cast<QQuickImageProvider*>(qgcApp()->toolbox()->imageProvider());
_qmlAppEngine->addImageProvider(QStringLiteral("QGCImages"), pImgProvider);
// Image provider for PX4 Flow
QQuickImageProvider *pImgProvider =
dynamic_cast<QQuickImageProvider *>(qgcApp()->toolbox()->imageProvider());
_qmlAppEngine->addImageProvider(QStringLiteral("QGCImages"), pImgProvider);
QQuickWindow* rootWindow = (QQuickWindow*)qgcApp()->mainRootWindow();
QQuickWindow *rootWindow = (QQuickWindow *)qgcApp()->mainRootWindow();
if (rootWindow) {
rootWindow->scheduleRenderJob (new FinishVideoInitialization (toolbox()->videoManager()),
QQuickWindow::BeforeSynchronizingStage);
}
if (rootWindow) {
rootWindow->scheduleRenderJob(
new FinishVideoInitialization(toolbox()->videoManager()),
QQuickWindow::BeforeSynchronizingStage);
}
// Safe to show popup error messages now that main window is created
UASMessageHandler* msgHandler = qgcApp()->toolbox()->uasMessageHandler();
if (msgHandler) {
msgHandler->showErrorsInToolbar();
}
// Safe to show popup error messages now that main window is created
UASMessageHandler *msgHandler = qgcApp()->toolbox()->uasMessageHandler();
if (msgHandler) {
msgHandler->showErrorsInToolbar();
}
// Now that main window is up check for lost log files
connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
emit checkForLostLogFiles();
// Now that main window is up check for lost log files
connect(this, &QGCApplication::checkForLostLogFiles,
toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
emit checkForLostLogFiles();
// Load known link configurations
toolbox()->linkManager()->loadLinkConfigurationList();
// Load known link configurations
toolbox()->linkManager()->loadLinkConfigurationList();
// Probe for joysticks
toolbox()->joystickManager()->init();
// Probe for joysticks
toolbox()->joystickManager()->init();
if (_settingsUpgraded) {
showAppMessage(QString(tr("The format for %1 saved settings has been modified. "
"Your saved settings have been reset to defaults.")).arg(applicationName()));
}
if (_settingsUpgraded) {
showAppMessage(
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
toolbox()->linkManager()->startAutoConnectedLinks();
// Connect links with flag AutoconnectLink
toolbox()->linkManager()->startAutoConnectedLinks();
if (getQGCMapEngine()->wasCacheReset()) {
showAppMessage(tr("The Offline Map Cache database has been upgraded. "
"Your old map cache sets have been reset."));
}
if (getQGCMapEngine()->wasCacheReset()) {
showAppMessage(tr("The Offline Map Cache database has been upgraded. "
"Your old map cache sets have been reset."));
}
settings.sync();
return true;
settings.sync();
return true;
}
bool QGCApplication::_initForUnitTests()
{
return true;
}
bool QGCApplication::_initForUnitTests() { return true; }
void QGCApplication::deleteAllSettingsNextBoot(void)
{
QSettings settings;
settings.setValue(_deleteAllSettingsKey, true);
void QGCApplication::deleteAllSettingsNextBoot(void) {
QSettings settings;
settings.setValue(_deleteAllSettingsKey, true);
}
void QGCApplication::clearDeleteAllSettingsNextBoot(void)
{
QSettings settings;
settings.remove(_deleteAllSettingsKey);
void QGCApplication::clearDeleteAllSettingsNextBoot(void) {
QSettings settings;
settings.remove(_deleteAllSettingsKey);
}
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
return QGCApplication::_app;
}
QGCApplication *qgcApp(void) { return QGCApplication::_app; }
void QGCApplication::informationMessageBoxOnMainThread(const QString& /*title*/, const QString& msg)
{
showAppMessage(msg);
void QGCApplication::informationMessageBoxOnMainThread(
const QString & /*title*/, const QString &msg) {
showAppMessage(msg);
}
void QGCApplication::warningMessageBoxOnMainThread(const QString& /*title*/, const QString& msg)
{
showAppMessage(msg);
void QGCApplication::warningMessageBoxOnMainThread(const QString & /*title*/,
const QString &msg) {
showAppMessage(msg);
}
void QGCApplication::criticalMessageBoxOnMainThread(const QString& /*title*/, const QString& msg)
{
showAppMessage(msg);
void QGCApplication::criticalMessageBoxOnMainThread(const QString & /*title*/,
const QString &msg) {
showAppMessage(msg);
}
void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile)
{
// 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
if (_checkTelemetrySavePath(true /* useMessageBox */)) {
QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath();
QDir saveDir(saveDirPath);
void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile) {
// 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
if (_checkTelemetrySavePath(true /* useMessageBox */)) {
QString nameFormat("%1%2.%3");
QString dtFormat("yyyy-MM-dd hh-mm-ss");
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);
QString saveDirPath =
_toolbox->settingsManager()->appSettings()->telemetrySavePath();
QDir saveDir(saveDirPath);
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);
}
QString nameFormat("%1%2.%3");
QString dtFormat("yyyy-MM-dd hh-mm-ss");
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()
{
// This is called with an active vehicle so don't pop message boxes which holds ui thread
_checkTelemetrySavePath(false /* useMessageBox */);
void QGCApplication::checkTelemetrySavePathOnMainThread() {
// This is called with an active vehicle so don't pop message boxes which
// holds ui thread
_checkTelemetrySavePath(false /* useMessageBox */);
}
bool QGCApplication::_checkTelemetrySavePath(bool /*useMessageBox*/)
{
QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath();
if (saveDirPath.isEmpty()) {
QString error = tr("Unable to save telemetry log. Application save directory is not set.");
showAppMessage(error);
return false;
}
bool QGCApplication::_checkTelemetrySavePath(bool /*useMessageBox*/) {
QString saveDirPath =
_toolbox->settingsManager()->appSettings()->telemetrySavePath();
if (saveDirPath.isEmpty()) {
QString error = tr(
"Unable to save telemetry log. Application save directory is not set.");
showAppMessage(error);
return false;
}
QDir saveDir(saveDirPath);
if (!saveDir.exists()) {
QString error = tr("Unable to save telemetry log. Telemetry save directory \"%1\" does not exist.").arg(saveDirPath);
showAppMessage(error);
return false;
}
QDir saveDir(saveDirPath);
if (!saveDir.exists()) {
QString error = tr("Unable to save telemetry log. Telemetry save directory "
"\"%1\" does not exist.")
.arg(saveDirPath);
showAppMessage(error);
return false;
}
return true;
return true;
}
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
{
QPair<int, QString> missingParam(componentId, name);
void QGCApplication::reportMissingParameter(int componentId,
const QString &name) {
QPair<int, QString> missingParam(componentId, name);
if (!_missingParams.contains(missingParam)) {
_missingParams.append(missingParam);
}
_missingParamsDelayedDisplayTimer.start();
if (!_missingParams.contains(missingParam)) {
_missingParams.append(missingParam);
}
_missingParamsDelayedDisplayTimer.start();
}
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
{
if (_missingParams.count()) {
QString params;
for (QPair<int, QString>& missingParam: _missingParams) {
QString param = QStringLiteral("%1:%2").arg(missingParam.first).arg(missingParam.second);
if (params.isEmpty()) {
params += 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));
void QGCApplication::_missingParamsDisplay(void) {
if (_missingParams.count()) {
QString params;
for (QPair<int, QString> &missingParam : _missingParams) {
QString param = QStringLiteral("%1:%2")
.arg(missingParam.first)
.arg(missingParam.second);
if (params.isEmpty()) {
params += param;
} else {
params += QStringLiteral(", %1").arg(param);
}
}
}
_missingParams.clear();
QObject* QGCApplication::_rootQmlObject()
{
if (_qmlAppEngine && _qmlAppEngine->rootObjects().size())
return _qmlAppEngine->rootObjects()[0];
return nullptr;
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));
}
}
void QGCApplication::showCriticalVehicleMessage(const QString& message)
{
// PreArm messages are handled by Vehicle and shown in Map
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 *QGCApplication::_rootQmlObject() {
if (_qmlAppEngine && _qmlAppEngine->rootObjects().size())
return _qmlAppEngine->rootObjects()[0];
return nullptr;
}
void QGCApplication::showAppMessage(const QString& message, const QString& title)
{
QString dialogTitle = title.isEmpty() ? applicationName() : title;
void QGCApplication::showCriticalVehicleMessage(const QString &message) {
// PreArm messages are handled by Vehicle and shown in Map
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();
if (rootQmlObject) {
QVariant varReturn;
QVariant varMessage = QVariant::fromValue(message);
QMetaObject::invokeMethod(_rootQmlObject(), "showMessageDialog", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, dialogTitle), 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::showAppMessage(const QString &message,
const QString &title) {
QString dialogTitle = title.isEmpty() ? applicationName() : title;
QObject *rootQmlObject = _rootQmlObject();
if (rootQmlObject) {
QVariant varReturn;
QVariant varMessage = QVariant::fromValue(message);
QMetaObject::invokeMethod(_rootQmlObject(), "showMessageDialog",
Q_RETURN_ARG(QVariant, varReturn),
Q_ARG(QVariant, dialogTitle),
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)
{
if (_rootQmlObject()) {
for (const QPair<QString, QString>& appMsg: _delayedAppMessages) {
showAppMessage(appMsg.second, appMsg.first);
}
_delayedAppMessages.clear();
} else {
QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages);
void QGCApplication::_showDelayedAppMessages(void) {
if (_rootQmlObject()) {
for (const QPair<QString, QString> &appMsg : _delayedAppMessages) {
showAppMessage(appMsg.second, appMsg.first);
}
_delayedAppMessages.clear();
} else {
QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages);
}
}
QQuickItem* QGCApplication::mainRootWindow()
{
if(!_mainRootWindow) {
_mainRootWindow = reinterpret_cast<QQuickItem*>(_rootQmlObject());
}
return _mainRootWindow;
QQuickItem *QGCApplication::mainRootWindow() {
if (!_mainRootWindow) {
_mainRootWindow = reinterpret_cast<QQuickItem *>(_rootQmlObject());
}
return _mainRootWindow;
}
void QGCApplication::showSetupView()
{
if(_rootQmlObject()) {
QMetaObject::invokeMethod(_rootQmlObject(), "showSetupTool");
}
void QGCApplication::showSetupView() {
if (_rootQmlObject()) {
QMetaObject::invokeMethod(_rootQmlObject(), "showSetupTool");
}
}
void QGCApplication::qmlAttemptWindowClose()
{
if(_rootQmlObject()) {
QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
}
void QGCApplication::qmlAttemptWindowClose() {
if (_rootQmlObject()) {
QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
}
}
bool QGCApplication::isInternetAvailable()
{
if(_toolbox->settingsManager()->appSettings()->checkInternet()->rawValue().toBool())
return getQGCMapEngine()->isInternetActive();
return true;
bool QGCApplication::isInternetAvailable() {
if (_toolbox->settingsManager()
->appSettings()
->checkInternet()
->rawValue()
.toBool())
return getQGCMapEngine()->isInternetActive();
return true;
}
void QGCApplication::_checkForNewVersion()
{
void QGCApplication::_checkForNewVersion() {
#ifndef __mobile__
if (!_runningUnitTests) {
if (_parseVersionText(applicationVersion(), _majorVersion, _minorVersion, _buildVersion)) {
QString versionCheckFile = toolbox()->corePlugin()->stableVersionCheckFileUrl();
if (!versionCheckFile.isEmpty()) {
QGCFileDownload* download = new QGCFileDownload(this);
connect(download, &QGCFileDownload::downloadComplete, this, &QGCApplication::_qgcCurrentStableVersionDownloadComplete);
download->download(versionCheckFile);
}
}
if (!_runningUnitTests) {
if (_parseVersionText(applicationVersion(), _majorVersion, _minorVersion,
_buildVersion)) {
QString versionCheckFile =
toolbox()->corePlugin()->stableVersionCheckFileUrl();
if (!versionCheckFile.isEmpty()) {
QGCFileDownload *download = new QGCFileDownload(this);
connect(download, &QGCFileDownload::downloadComplete, this,
&QGCApplication::_qgcCurrentStableVersionDownloadComplete);
download->download(versionCheckFile);
}
}
}
#endif
}
void QGCApplication::_qgcCurrentStableVersionDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg)
{
void QGCApplication::_qgcCurrentStableVersionDownloadComplete(
QString /*remoteFile*/, QString localFile, QString errorMsg) {
#ifdef __mobile__
Q_UNUSED(localFile)
Q_UNUSED(errorMsg)
Q_UNUSED(localFile)
Q_UNUSED(errorMsg)
#else
if (errorMsg.isEmpty()) {
QFile versionFile(localFile);
if (versionFile.open(QIODevice::ReadOnly)) {
QTextStream textStream(&versionFile);
QString version = textStream.readLine();
qDebug() << version;
int majorVersion, minorVersion, buildVersion;
if (_parseVersionText(version, majorVersion, minorVersion, buildVersion)) {
if (_majorVersion < majorVersion ||
(_majorVersion == majorVersion && _minorVersion < minorVersion) ||
(_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"));
}
}
if (errorMsg.isEmpty()) {
QFile versionFile(localFile);
if (versionFile.open(QIODevice::ReadOnly)) {
QTextStream textStream(&versionFile);
QString version = textStream.readLine();
qDebug() << version;
int majorVersion, minorVersion, buildVersion;
if (_parseVersionText(version, majorVersion, minorVersion,
buildVersion)) {
if (_majorVersion < majorVersion ||
(_majorVersion == majorVersion && _minorVersion < minorVersion) ||
(_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
}
bool QGCApplication::_parseVersionText(const QString& versionString, int& majorVersion, int& minorVersion, int& buildVersion)
{
QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)");
QRegularExpressionMatch match = regExp.match(versionString);
if (match.hasMatch() && match.lastCapturedIndex() == 3) {
majorVersion = match.captured(1).toInt();
minorVersion = match.captured(2).toInt();
buildVersion = match.captured(3).toInt();
return true;
}
bool QGCApplication::_parseVersionText(const QString &versionString,
int &majorVersion, int &minorVersion,
int &buildVersion) {
QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)");
QRegularExpressionMatch match = regExp.match(versionString);
if (match.hasMatch() && match.lastCapturedIndex() == 3) {
majorVersion = match.captured(1).toInt();
minorVersion = match.captured(2).toInt();
buildVersion = match.captured(3).toInt();
return true;
}
return false;
return false;
}
void QGCApplication::_onGPSConnect()
{
_gpsRtkFactGroup->connected()->setRawValue(true);
void QGCApplication::_onGPSConnect() {
_gpsRtkFactGroup->connected()->setRawValue(true);
}
void QGCApplication::_onGPSDisconnect()
{
_gpsRtkFactGroup->connected()->setRawValue(false);
void QGCApplication::_onGPSDisconnect() {
_gpsRtkFactGroup->connected()->setRawValue(false);
}
void QGCApplication::_gpsSurveyInStatus(float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active)
{
_gpsRtkFactGroup->currentDuration()->setRawValue(duration);
_gpsRtkFactGroup->currentAccuracy()->setRawValue(static_cast<double>(accuracyMM) / 1000.0);
_gpsRtkFactGroup->currentLatitude()->setRawValue(latitude);
_gpsRtkFactGroup->currentLongitude()->setRawValue(longitude);
_gpsRtkFactGroup->currentAltitude()->setRawValue(altitude);
_gpsRtkFactGroup->valid()->setRawValue(valid);
_gpsRtkFactGroup->active()->setRawValue(active);
void QGCApplication::_gpsSurveyInStatus(float duration, float accuracyMM,
double latitude, double longitude,
float altitude, bool valid,
bool active) {
_gpsRtkFactGroup->currentDuration()->setRawValue(duration);
_gpsRtkFactGroup->currentAccuracy()->setRawValue(
static_cast<double>(accuracyMM) / 1000.0);
_gpsRtkFactGroup->currentLatitude()->setRawValue(latitude);
_gpsRtkFactGroup->currentLongitude()->setRawValue(longitude);
_gpsRtkFactGroup->currentAltitude()->setRawValue(altitude);
_gpsRtkFactGroup->valid()->setRawValue(valid);
_gpsRtkFactGroup->active()->setRawValue(active);
}
void QGCApplication::_gpsNumSatellites(int numSatellites)
{
_gpsRtkFactGroup->numSatellites()->setRawValue(numSatellites);
void QGCApplication::_gpsNumSatellites(int numSatellites) {
_gpsRtkFactGroup->numSatellites()->setRawValue(numSatellites);
}
QString QGCApplication::cachedParameterMetaDataFile(void)
{
QSettings settings;
QDir parameterDir = QFileInfo(settings.fileName()).dir();
return parameterDir.filePath(QStringLiteral("ParameterFactMetaData.xml"));
QString QGCApplication::cachedParameterMetaDataFile(void) {
QSettings settings;
QDir parameterDir = QFileInfo(settings.fileName()).dir();
return parameterDir.filePath(QStringLiteral("ParameterFactMetaData.xml"));
}
QString QGCApplication::cachedAirframeMetaDataFile(void)
{
QSettings settings;
QDir airframeDir = QFileInfo(settings.fileName()).dir();
return airframeDir.filePath(QStringLiteral("PX4AirframeFactMetaData.xml"));
QString QGCApplication::cachedAirframeMetaDataFile(void) {
QSettings settings;
QDir airframeDir = QFileInfo(settings.fileName()).dir();
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)
: QObject(parent) {
*this = other;
}
#include "QGCLoggingCategory.h"
#include "QGCQGeoCoordinate.h"
AreaData &AreaData::operator=(const AreaData &other) {
this->append(other.measurementArea());
this->append(other.serviceArea());
this->append(other.joinedArea());
this->append(other.corridor());
QGC_LOGGING_CATEGORY(AreaDataLog, "AreaDataLog")
return *this;
}
AreaData::AreaData(QObject *parent) : QObject(parent) {}
void AreaData::append(const WimaJoinedAreaData &areaData) {
if (_joinedArea != areaData) {
_joinedArea = areaData;
emit joinedAreaChanged();
}
}
AreaData::~AreaData() {}
void AreaData::append(const WimaServiceAreaData &areaData) {
if (_serviceArea != areaData) {
_serviceArea = areaData;
emit serviceAreaChanged();
AreaData::AreaData(const AreaData &other, QObject *parent) : QObject(parent) {
if (!copyAreaList(other._areaList, _areaList, this)) {
qCWarning(AreaDataLog) << "AreaData(): not able to copy other._areaList";
} else {
_origin = other._origin;
}
}
void AreaData::append(const WimaCorridorData &areaData) {
if (_corridor != areaData) {
_corridor = areaData;
emit corridorChanged();
AreaData &AreaData::operator=(const AreaData &other) {
if (!copyAreaList(other._areaList, _areaList, this)) {
qCWarning(AreaDataLog) << "operator=(): not able to copy other._areaList";
} else {
_origin = other._origin;
}
return *this;
}
void AreaData::append(const WimaMeasurementAreaData &areaData) {
if (_measurementArea != areaData) {
_measurementArea = areaData;
emit measurementAreaChanged();
if (_measurementArea.coordinateList().size() > 0) {
setOrigin(_measurementArea.coordinateList().first());
} else {
setOrigin(QGeoCoordinate());
bool AreaData::insert(GeoArea *areaData) {
{
SafeArea *area = qobject_cast<SafeArea *>(areaData);
if (area != nullptr) {
if (Q_LIKELY(!this->_areaList.contains(area))) {
_areaList.append(area);
emit areaList();
return true;
} else {
return false;
}
}
}
}
void AreaData::append(const WimaJoinedArea &areaData) {
if (_joinedArea != areaData) {
_joinedArea = areaData;
emit joinedAreaChanged();
{
MeasurementArea *area = qobject_cast<MeasurementArea *>(areaData);
if (area != nullptr) {
if (Q_LIKELY(!this->_areaList.contains(area))) {
_areaList.append(area);
emit areaList();
return true;
} else {
return false;
}
}
}
}
void AreaData::append(const WimaArea &areaData) {
if (_serviceArea != areaData) {
_serviceArea = areaData;
emit serviceAreaChanged();
}
return false;
}
void AreaData::append(const WimaCorridor &areaData) {
if (_corridor != areaData) {
_corridor = areaData;
emit corridorChanged();
}
}
void AreaData::remove(GeoArea *areaData) {
int index = _areaList.indexOf(areaData);
if (index >= 0) {
QObject *obj = _areaList.removeAt(index);
void AreaData::append(const WimaMeasurementArea &areaData) {
if (_measurementArea != areaData) {
_measurementArea = areaData;
emit measurementAreaChanged();
_setOrigin(_newOrigin());
if (_measurementArea.coordinateList().size() > 0) {
setOrigin(_measurementArea.coordinateList().first());
} else {
setOrigin(QGeoCoordinate());
if (obj->parent() == nullptr) {
obj->deleteLater();
}
}
}
void AreaData::clear() { *this = AreaData(); }
const QGeoCoordinate &AreaData::origin() const { return _origin; }
bool AreaData::isValid() {
return _measurementArea.coordinateList().size() >= 3 &&
_serviceArea.coordinateList().size() >= 3 && _origin.isValid();
emit areaListChanged();
}
}
const WimaJoinedAreaData &AreaData::joinedArea() const {
return this->_joinedArea;
void AreaData::clear() {
if (_areaList.count() > 0) {
for (int i = 0; i < _areaList.count(); ++i) {
remove(_areaList.value<GeoArea *>(i));
}
emit areaListChanged();
}
}
const WimaServiceAreaData &AreaData::serviceArea() const {
return this->_serviceArea;
}
QmlObjectListModel *AreaData::areaList() { return &_areaList; }
const WimaCorridorData &AreaData::corridor() const {
return this->_corridor;
}
const QmlObjectListModel *AreaData::areaList() const { return &_areaList; }
const WimaMeasurementAreaData &AreaData::measurementArea() const {
return this->_measurementArea;
}
const QGeoCoordinate &AreaData::origin() const { return _origin; }
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() {
return this->_measurementArea;
bool AreaData::initialized() {
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 {
return this->_joinedArea == other._joinedArea &&
this->_measurementArea == other._measurementArea &&
this->_corridor == other._corridor &&
this->_serviceArea == other._serviceArea;
if (_areaList.count() == other._areaList.count()) {
for (int i = 0; i < _areaList.count(); ++i) {
if (_areaList[i] != other._areaList[i]) {
return false;
}
}
return true;
} else {
return false;
}
}
bool AreaData::operator!=(const AreaData &other) const {
return !(*this == other);
}
void AreaData::setOrigin(const QGeoCoordinate &origin) {
void AreaData::_setOrigin(const QGeoCoordinate &origin) {
if (this->_origin != origin) {
this->_origin = origin;
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 "QGCLoggingCategory.h"
QGC_LOGGING_CATEGORY(CircularGeneratorLog, "CircularGeneratorLog")
#include "SettingsFact.h"
#define CLIPPER_SCALE 1000000
#include "Wima/Geometry/GenericCircle.h"
#include "clipper/clipper.hpp"
#include "RoutingThread.h"
#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;
template <> inline auto get<0>(const IntPoint &p) { return p.X; }
template <> inline auto get<1>(const IntPoint &p) { return p.Y; }
#include "SnakeTile.h"
#include "Wima/RoutingThread.h"
namespace routing {
bool circularTransects(const snake::FPoint &reference,
......@@ -77,7 +80,13 @@ bool CircularGenerator::get(Generator &generator) {
snake::FPoint 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) {
if (v.isValid()) {
v.setAltitude(0);
......@@ -93,8 +102,8 @@ bool CircularGenerator::get(Generator &generator) {
snake::areaToEnu(origin, geoPolygon, *pPolygon);
// Progress and tiles.
const auto &progress = this->_d->measurementArea().progress();
const auto *tiles = this->_d->measurementArea().tiles();
const auto &progress = measurementArea->progress();
const auto *tiles = measurementArea->tiles();
auto pTiles = std::make_shared<std::vector<snake::FPolygon>>();
if (progress.size() == tiles->count()) {
for (int i = 0; i < tiles->count(); ++i) {
......@@ -119,7 +128,12 @@ bool CircularGenerator::get(Generator &generator) {
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()) {
qCDebug(CircularGeneratorLog) << "get(): depot invalid." << geoDepot;
return false;
......@@ -169,73 +183,85 @@ void CircularGenerator::resetReferenceIfInvalid() {
}
void CircularGenerator::resetReference() {
if (this->_d->measurementArea().center().isValid()) {
setReference(this->_d->measurementArea().center());
auto measurementArea =
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 {
qCWarning(CircularGeneratorLog)
<< "measurement area center" << this->_d->measurementArea().center();
qCDebug(CircularGeneratorLog)
<< "resetReference(): measurement area == nullptr";
}
}
void CircularGenerator::establishConnections() {
if (this->_d && !this->_connectionsEstablished) {
connect(this->_d.get(), &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::centerChanged, this,
&CircularGenerator::resetReferenceIfInvalid);
connect(&this->_d->measurementArea(), &WimaMeasurementAreaData::pathChanged,
this, &GeneratorBase::generatorChanged);
connect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this,
&GeneratorBase::generatorChanged);
connect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->deltaAlpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this, &CircularGenerator::referenceChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
if (measurementArea != nullptr && serviceArea != nullptr) {
GeneratorBase::establishConnections();
connect(this->_d, &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
connect(measurementArea, &MeasurementArea::progressChanged, this,
&GeneratorBase::generatorChanged);
connect(measurementArea, &MeasurementArea::tilesChanged, this,
&GeneratorBase::generatorChanged);
connect(measurementArea, &MeasurementArea::centerChanged, this,
&CircularGenerator::resetReferenceIfInvalid);
connect(measurementArea, &MeasurementArea::pathChanged, this,
&GeneratorBase::generatorChanged);
connect(serviceArea, &SafeArea::depotChanged, this,
&GeneratorBase::generatorChanged);
connect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->deltaAlpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this, &CircularGenerator::referenceChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
}
}
void CircularGenerator::deleteConnections() {
if (this->_d && this->_connectionsEstablished) {
disconnect(this->_d.get(), &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->measurementArea(), &WimaMeasurementAreaData::center,
this, &CircularGenerator::resetReferenceIfInvalid);
disconnect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::pathChanged, this,
&GeneratorBase::generatorChanged);
disconnect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged,
this, &GeneratorBase::generatorChanged);
disconnect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->deltaAlpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this, &CircularGenerator::referenceChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = false;
auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
if (measurementArea != nullptr && serviceArea != nullptr) {
GeneratorBase::deleteConnections();
disconnect(this->_d, &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
disconnect(measurementArea, &MeasurementArea::progressChanged, this,
&GeneratorBase::generatorChanged);
disconnect(measurementArea, &MeasurementArea::tilesChanged, this,
&GeneratorBase::generatorChanged);
disconnect(measurementArea, &MeasurementArea::centerChanged, this,
&CircularGenerator::resetReferenceIfInvalid);
disconnect(measurementArea, &MeasurementArea::pathChanged, this,
&GeneratorBase::generatorChanged);
disconnect(serviceArea, &SafeArea::depotChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->deltaAlpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this, &CircularGenerator::referenceChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
}
}
......
......@@ -2,6 +2,8 @@
#include <QGeoCoordinate>
#include "SettingsFact.h"
namespace routing {
class CircularGenerator : public GeneratorBase {
......
......@@ -12,10 +12,6 @@ GeneratorBase::GeneratorBase(GeneratorBase::Data d, QObject *parent)
GeneratorBase::~GeneratorBase() {}
QString GeneratorBase::editorQml() { return QStringLiteral(""); }
QString GeneratorBase::mapVisualQml() { return QStringLiteral(""); }
GeneratorBase::Data GeneratorBase::data() const { return _d; }
void GeneratorBase::setData(const Data &d) {
......@@ -28,4 +24,9 @@ void GeneratorBase::establishConnections() {}
void GeneratorBase::deleteConnections() {}
void GeneratorBase::_areaListChangedHandler() {
deleteConnections();
establishConnections();
}
} // namespace routing
......@@ -5,16 +5,16 @@
#include <functional>
#include <memory>
#include "snake.h"
#include "geometry/snake.h"
#include "Wima/WimaPlanData.h"
#include "AreaData.h"
namespace routing {
class GeneratorBase : public QObject {
Q_OBJECT
public:
using Data = std::shared_ptr<AreaData>;
using Data = AreaData *;
using Generator = std::function<bool(snake::Transects &)>;
explicit GeneratorBase(QObject *parent = nullptr);
......@@ -24,8 +24,8 @@ public:
Q_PROPERTY(QString editorQml READ editorQml CONSTANT)
Q_PROPERTY(QString mapVisualQml READ mapVisualQml CONSTANT)
virtual QString editorQml();
virtual QString mapVisualQml();
virtual QString editorQml() = 0;
virtual QString mapVisualQml() = 0;
virtual QString name() = 0;
virtual QString abbreviation() = 0;
......@@ -42,6 +42,9 @@ protected:
virtual void establishConnections();
virtual void deleteConnections();
Data _d;
private:
void _areaListChangedHandler();
};
} // namespace routing
......@@ -4,10 +4,12 @@
QGC_LOGGING_CATEGORY(LinearGeneratorLog, "LinearGeneratorLog")
#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 "Wima/RoutingThread.h"
#include "RoutingThread.h"
#include "nemo_interface/SnakeTile.h"
namespace routing {
......@@ -38,6 +40,8 @@ QString LinearGenerator::editorQml() {
return QStringLiteral("LinearGeneratorEditor.qml");
}
QString LinearGenerator::mapVisualQml() { return QStringLiteral(""); }
QString LinearGenerator::name() { return QStringLiteral("Linear Generator"); }
QString LinearGenerator::abbreviation() { return QStringLiteral("L. Gen."); }
......@@ -52,7 +56,13 @@ bool LinearGenerator::get(Generator &generator) {
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) {
if (v.isValid()) {
v.setAltitude(0);
......@@ -68,8 +78,8 @@ bool LinearGenerator::get(Generator &generator) {
snake::areaToEnu(origin, geoPolygon, *pPolygon);
// Progress and tiles.
const auto &progress = this->_d->measurementArea().progress();
const auto *tiles = this->_d->measurementArea().tiles();
const auto &progress = measurementArea->progress();
const auto *tiles = measurementArea->tiles();
auto pTiles = std::make_shared<std::vector<snake::FPolygon>>();
if (progress.size() == tiles->count()) {
for (int i = 0; i < tiles->count(); ++i) {
......@@ -93,7 +103,12 @@ bool LinearGenerator::get(Generator &generator) {
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()) {
qCDebug(LinearGeneratorLog) << "get(): depot invalid." << geoDepot;
return false;
......@@ -134,53 +149,61 @@ Fact *LinearGenerator::minLength() { return &_minLength; }
void LinearGenerator::establishConnections() {
if (this->_d && !this->_connectionsEstablished) {
connect(this->_d.get(), &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(), &WimaMeasurementAreaData::pathChanged,
this, &GeneratorBase::generatorChanged);
connect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this,
&GeneratorBase::generatorChanged);
connect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->alpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
if (measurementArea != nullptr && serviceArea != nullptr) {
GeneratorBase::establishConnections();
connect(this->_d, &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
connect(measurementArea, &MeasurementArea::progressChanged, this,
&GeneratorBase::generatorChanged);
connect(measurementArea, &MeasurementArea::tilesChanged, this,
&GeneratorBase::generatorChanged);
connect(measurementArea, &MeasurementArea::pathChanged, this,
&GeneratorBase::generatorChanged);
connect(serviceArea, &SafeArea::depotChanged, this,
&GeneratorBase::generatorChanged);
connect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->alpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
}
}
void LinearGenerator::deleteConnections() {
if (this->_d && this->_connectionsEstablished) {
connect(this->_d.get(), &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::progressChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(),
&WimaMeasurementAreaData::tileDataChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->measurementArea(), &WimaMeasurementAreaData::pathChanged,
this, &GeneratorBase::generatorChanged);
connect(&this->_d->serviceArea(), &WimaServiceAreaData::depotChanged, this,
&GeneratorBase::generatorChanged);
connect(&this->_d->joinedArea(), &WimaJoinedAreaData::pathChanged, this,
&GeneratorBase::generatorChanged);
connect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->alpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
connect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = false;
auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
if (measurementArea != nullptr && serviceArea != nullptr) {
GeneratorBase::deleteConnections();
disconnect(this->_d, &AreaData::originChanged, this,
&GeneratorBase::generatorChanged);
disconnect(measurementArea, &MeasurementArea::progressChanged, this,
&GeneratorBase::generatorChanged);
disconnect(measurementArea, &MeasurementArea::tilesChanged, this,
&GeneratorBase::generatorChanged);
disconnect(measurementArea, &MeasurementArea::pathChanged, this,
&GeneratorBase::generatorChanged);
disconnect(serviceArea, &SafeArea::depotChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->distance(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->alpha(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
disconnect(this->minLength(), &Fact::rawValueChanged, this,
&GeneratorBase::generatorChanged);
this->_connectionsEstablished = true;
}
}
}
......
......@@ -2,6 +2,8 @@
#include <QGeoCoordinate>
#include "SettingsFact.h"
namespace routing {
class LinearGenerator : public GeneratorBase {
......@@ -15,6 +17,7 @@ public:
Q_PROPERTY(Fact *minLength READ minLength CONSTANT)
virtual QString editorQml() override;
virtual QString mapVisualQml() override;
virtual QString name() override;
virtual QString abbreviation() override;
......
#include "NemoInterface.h"
#include "SnakeTilesLocal.h"
#include "nemo_interface/SnakeTilesLocal.h"
#include "QGCApplication.h"
#include "QGCLoggingCategory.h"
......@@ -12,11 +12,11 @@
#include <QTimer>
#include "QNemoHeartbeat.h"
#include "QNemoProgress.h"
#include "Wima/Geometry/WimaMeasurementArea.h"
#include "Wima/Snake/SnakeTile.h"
#include "Wima/Snake/snake.h"
#include "geometry/MeasurementArea.h"
#include "geometry/snake.h"
#include "nemo_interface/QNemoHeartbeat.h"
#include "nemo_interface/QNemoProgress.h"
#include "nemo_interface/SnakeTile.h"
#include "ros_bridge/include/messages/geographic_msgs/geopoint.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 "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
#include "JsonHelper.h"
#include "QGCApplication.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
#include <boost/units/io.hpp>
#include <boost/units/systems/si.hpp>
#include "CircularGenerator.h"
#include "LinearGenerator.h"
// ToDo: Check what happened to _transectsDirty
#define CLIPPER_SCALE 1000000
QGC_LOGGING_CATEGORY(CircularSurveyLog, "CircularSurveyLog")
QGC_LOGGING_CATEGORY(RouteComplexItemLog, "RouteComplexItemLog")
template <typename T>
constexpr typename std::underlying_type<T>::type integral(T value) {
return static_cast<typename std::underlying_type<T>::type>(value);
}
const char *CircularSurvey::settingsGroup = "CircularSurvey";
const char *CircularSurvey::jsonComplexItemTypeValue = "CircularSurvey";
const char *CircularSurvey::variantName = "Variant";
const QString CircularSurvey::name(tr("Circular Survey"));
const char *RouteComplexItem::settingsGroup = "Route";
const char *RouteComplexItem::jsonComplexItemTypeValue = "Route";
const char *RouteComplexItem::variantName = "Variant";
const QString RouteComplexItem::name(tr("Route"));
CircularSurvey::CircularSurvey(PlanMasterController *masterController,
bool flyView, const QString &kmlOrShpFile,
QObject *parent)
RouteComplexItem::RouteComplexItem(PlanMasterController *masterController,
bool flyView, const QString &kmlOrShpFile,
QObject *parent)
: TransectStyleComplexItem(masterController, flyView, settingsGroup,
parent),
_state(STATE::IDLE),
_metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/CircularSurvey.SettingsGroup.json"), this)),
QStringLiteral(":/json/RouteComplexItem.SettingsGroup.json"), this)),
_variant(settingsGroup, _metaDataMap[variantName]),
_areaData(std::make_shared<AreaData>()),
_pWorker(std::make_unique<RoutingThread>()) {
_areaData(new AreaData(this)), _editorData(new AreaData(this)),
_currentData(_areaData), _pWorker(new RoutingThread(this)) {
Q_UNUSED(kmlOrShpFile)
_editorQml = "qrc:/qml/CircularSurveyItemEditor.qml";
// Connect facts.
connect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant);
&RouteComplexItem::_changeVariant);
// Connect worker.
connect(this->_pWorker.get(), &RoutingThread::result, this,
&CircularSurvey::_setTransects);
connect(this->_pWorker.get(), &RoutingThread::calculatingChanged, this,
&CircularSurvey::calculatingChanged);
connect(this->_pWorker, &RoutingThread::result, this,
&RouteComplexItem::_setTransects);
connect(this->_pWorker, &RoutingThread::calculatingChanged, this,
&RouteComplexItem::calculatingChanged);
// Register Generators.
auto lg = std::make_shared<routing::LinearGenerator>(this->_areaData);
auto lg = new routing::LinearGenerator(this->_areaData, this);
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);
}
CircularSurvey::~CircularSurvey() {}
RouteComplexItem::~RouteComplexItem() {}
void CircularSurvey::reverse() {
void RouteComplexItem::revertPath() {
this->_setState(STATE::REVERT_PATH);
this->_rebuildTransects();
}
void CircularSurvey::setPlanData(const AreaData &d) {
*this->_areaData = d;
}
const AreaData &CircularSurvey::planData() const {
return *this->_areaData;
const AreaData *RouteComplexItem::areaData() const {
return this->_currentData;
}
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,
QString &errorString) {
bool RouteComplexItem::load(const QJsonObject &complexObject,
int sequenceNumber, QString &errorString) {
// We need to pull version first to determine what validation/conversion
// needs to be performed
QList<JsonHelper::KeyValidateInfo> versionKeyInfoList = {
......@@ -152,11 +148,11 @@ bool CircularSurvey::load(const QJsonObject &complexObject, int sequenceNumber,
return true;
}
QString CircularSurvey::mapVisualQML() const {
QString RouteComplexItem::mapVisualQML() const {
return QStringLiteral("CircularSurveyMapVisual.qml");
}
void CircularSurvey::save(QJsonArray &planItems) {
void RouteComplexItem::save(QJsonArray &planItems) {
QJsonObject saveObject;
_save(saveObject);
......@@ -175,22 +171,20 @@ void CircularSurvey::save(QJsonArray &planItems) {
planItems.append(saveObject);
}
bool CircularSurvey::specifiesCoordinate() const {
bool RouteComplexItem::specifiesCoordinate() const {
return _transects.count() > 0 ? _transects.first().count() > 0 : false;
}
bool CircularSurvey::_switchToGenerator(
const CircularSurvey::PtrGenerator &newG) {
bool RouteComplexItem::_setGenerator(PtrGenerator newG) {
if (this->_pGenerator != newG) {
if (this->_pGenerator != nullptr) {
disconnect(this->_pGenerator.get(),
&routing::GeneratorBase::generatorChanged, this,
&CircularSurvey::_rebuildTransects);
disconnect(this->_pGenerator, &routing::GeneratorBase::generatorChanged,
this, &RouteComplexItem::_rebuildTransects);
}
this->_pGenerator = newG;
connect(this->_pGenerator.get(), &routing::GeneratorBase::generatorChanged,
this, &CircularSurvey::_rebuildTransects);
connect(this->_pGenerator, &routing::GeneratorBase::generatorChanged, this,
&RouteComplexItem::_rebuildTransects);
emit generatorChanged();
this->_setState(STATE::IDLE);
......@@ -202,7 +196,7 @@ bool CircularSurvey::_switchToGenerator(
}
}
void CircularSurvey::_setState(CircularSurvey::STATE state) {
void RouteComplexItem::_setState(RouteComplexItem::STATE state) {
if (this->_state != state) {
auto oldState = this->_state;
this->_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;
}
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->_rebuildTransects();
}
bool CircularSurvey::_updateWorker() {
bool RouteComplexItem::_updateWorker() {
// Reset data.
this->_transects.clear();
this->_variantVector.clear();
......@@ -234,15 +242,17 @@ bool CircularSurvey::_updateWorker() {
auto origin = this->_areaData->origin();
origin.setAltitude(0);
if (!origin.isValid()) {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "_updateWorker(): origin invalid." << origin;
return false;
}
// 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)) {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "_updateWorker(): safe area invalid." << geoSafeArea;
return false;
}
......@@ -250,7 +260,7 @@ bool CircularSurvey::_updateWorker() {
if (v.isValid()) {
v.setAltitude(0);
} else {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "_updateWorker(): safe area contains invalid coordinate."
<< geoSafeArea;
return false;
......@@ -271,24 +281,24 @@ bool CircularSurvey::_updateWorker() {
this->_pWorker->route(par, g);
return true;
} else {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "_updateWorker(): generator creation failed.";
return false;
}
} else {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "_updateWorker(): pGenerator == nullptr, number of registered "
"generators: "
<< this->_generatorList.size();
return false;
}
} else {
qCDebug(CircularSurveyLog) << "_updateWorker(): plan data invalid.";
qCDebug(RouteComplexItemLog) << "_updateWorker(): plan data invalid.";
return false;
}
}
void CircularSurvey::_changeVariantWorker() {
void RouteComplexItem::_changeVariantWorker() {
auto variant = this->_variant.rawValue().toUInt();
// Find old variant and run. Old run corresponts with empty list.
......@@ -315,15 +325,15 @@ void CircularSurvey::_changeVariantWorker() {
this->_transects.swap(newVariantCoordinates);
} else { // error
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "Variant out of bounds (variant =" << variant << ").";
qCDebug(CircularSurveyLog) << "Resetting variant to zero.";
qCDebug(RouteComplexItemLog) << "Resetting variant to zero.";
disconnect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant);
&RouteComplexItem::_changeVariant);
this->_variant.setCookedValue(QVariant(0));
connect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant);
&RouteComplexItem::_changeVariant);
if (this->_variantVector.size() > 0) {
this->_changeVariantWorker();
......@@ -332,25 +342,23 @@ void CircularSurvey::_changeVariantWorker() {
}
}
void CircularSurvey::_reverseWorker() {
void RouteComplexItem::_reverseWorker() {
if (this->_transects.size() > 0) {
auto &t = this->_transects.front();
std::reverse(t.begin(), t.end());
}
}
double CircularSurvey::timeBetweenShots() { return 0; }
double RouteComplexItem::timeBetweenShots() { return 0; }
QString CircularSurvey::commandDescription() const {
return tr("Circular Survey");
}
QString RouteComplexItem::commandDescription() const { return tr("Route"); }
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
CircularSurvey::readyForSaveState() const {
RouteComplexItem::readyForSaveState() const {
if (TransectStyleComplexItem::readyForSaveState() ==
TransectStyleComplexItem::ReadyForSaveState::ReadyForSave) {
if (this->_state == STATE::IDLE) {
......@@ -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(
const QString &name, std::shared_ptr<routing::GeneratorBase> g) {
bool RouteComplexItem::registerGenerator(const QString &name,
routing::GeneratorBase *g) {
if (name.isEmpty()) {
qCDebug(CircularSurveyLog) << "registerGenerator(): empty name string.";
qCDebug(RouteComplexItemLog) << "registerGenerator(): empty name string.";
return false;
}
if (!g) {
qCDebug(CircularSurveyLog) << "registerGenerator(): empty generator.";
qCDebug(RouteComplexItemLog) << "registerGenerator(): empty generator.";
return false;
}
if (this->_generatorNameList.contains(name)) {
qCDebug(CircularSurveyLog) << "registerGenerator(): generator "
"already registered.";
qCDebug(RouteComplexItemLog) << "registerGenerator(): generator "
"already registered.";
return false;
} else {
this->_generatorNameList.push_back(name);
this->_generatorList.push_back(g);
if (this->_generatorList.size() == 1) {
_switchToGenerator(g);
_setGenerator(g);
}
emit generatorNameListChanged();
......@@ -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);
if (index >= 0) {
// Is this the current generator?
const auto &g = this->_generatorList.at(index);
if (g == this->_pGenerator) {
if (index > 0) {
_switchToGenerator(this->_generatorList.at(index - 1));
_setGenerator(this->_generatorList.at(index - 1));
} else {
_switchToGenerator(nullptr);
qCDebug(CircularSurveyLog)
_setGenerator(nullptr);
qCDebug(RouteComplexItemLog)
<< "unregisterGenerator(): last generator unregistered.";
}
}
this->_generatorNameList.removeAt(index);
this->_generatorList.removeAt(index);
auto gen = this->_generatorList.takeAt(index);
gen->deleteLater();
emit generatorNameListChanged();
return true;
} else {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "unregisterGenerator(): generator " << name << " not registered.";
return false;
}
}
bool CircularSurvey::unregisterGenerator(int index) {
bool RouteComplexItem::unregisterGenerator(int index) {
if (index > 0 && index < this->_generatorNameList.size()) {
return unregisterGenerator(this->_generatorNameList.at(index));
} else {
qCDebug(CircularSurveyLog) << "unregisterGenerator(): index (" << index
<< ") out"
"of bounds ( "
<< this->_generatorList.size() << " ).";
qCDebug(RouteComplexItemLog) << "unregisterGenerator(): index (" << index
<< ") out"
"of bounds ( "
<< this->_generatorList.size() << " ).";
return false;
}
}
bool CircularSurvey::switchToGenerator(const QString &name) {
bool RouteComplexItem::switchToGenerator(const QString &name) {
auto index = this->_generatorNameList.indexOf(name);
if (index >= 0) {
_switchToGenerator(this->_generatorList.at(index));
_setGenerator(this->_generatorList.at(index));
return true;
} else {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "switchToGenerator(): generator " << name << " not registered.";
return false;
}
}
bool CircularSurvey::switchToGenerator(int index) {
bool RouteComplexItem::switchToGenerator(int index) {
if (index >= 0) {
_switchToGenerator(this->_generatorList.at(index));
_setGenerator(this->_generatorList.at(index));
return true;
} else {
qCDebug(CircularSurveyLog) << "unregisterGenerator(): index (" << index
<< ") out"
"of bounds ( "
<< this->_generatorNameList.size() << " ).";
qCDebug(RouteComplexItemLog) << "unregisterGenerator(): index (" << index
<< ") out"
"of bounds ( "
<< this->_generatorNameList.size() << " ).";
return false;
}
}
QStringList CircularSurvey::generatorNameList() {
QStringList RouteComplexItem::generatorNameList() {
return this->_generatorNameList;
}
routing::GeneratorBase *CircularSurvey::generator() {
return _pGenerator.get();
}
routing::GeneratorBase *RouteComplexItem::generator() { return _pGenerator; }
int CircularSurvey::generatorIndex() {
int RouteComplexItem::generatorIndex() {
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();
switch (this->_state) {
case STATE::SKIPP:
qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: skipp.";
qCDebug(RouteComplexItemLog) << "rebuildTransectsPhase1: skipp.";
this->_setState(STATE::IDLE);
break;
case STATE::CHANGE_VARIANT:
qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: variant change.";
qCDebug(RouteComplexItemLog) << "rebuildTransectsPhase1: variant change.";
this->_changeVariantWorker();
this->_setState(STATE::IDLE);
break;
case STATE::REVERT_PATH:
qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: reverse.";
qCDebug(RouteComplexItemLog) << "rebuildTransectsPhase1: reverse.";
this->_reverseWorker();
this->_setState(STATE::IDLE);
break;
case STATE::IDLE:
case STATE::ROUTING:
this->_setState(STATE::ROUTING);
qCDebug(CircularSurveyLog) << "rebuildTransectsPhase1: update.";
qCDebug(RouteComplexItemLog) << "rebuildTransectsPhase1: update.";
if (!this->_updateWorker()) {
this->_setState(STATE::IDLE);
}
break;
}
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "rebuildTransectsPhase1(): "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
......@@ -507,10 +532,9 @@ void CircularSurvey::_rebuildTransectsPhase1(void) {
<< " ms";
}
// no cameraShots in Circular Survey, add if desired
void CircularSurvey::_recalcCameraShots() { _cameraShots = 0; }
void RouteComplexItem::_recalcCameraShots() { _cameraShots = 0; }
void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) {
void RouteComplexItem::_setTransects(RouteComplexItem::PtrRoutingData pRoute) {
// Store solutions.
auto ori = this->_areaData->origin();
ori.setAltitude(0);
......@@ -567,19 +591,19 @@ void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) {
}
} else {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "_setTransects(): lastTransect.size() == 0";
}
} else {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "_setTransects(): firstTransect.size() == 0";
}
} else {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "_setTransects(): transectsInfo.size() <= 1";
}
} else {
qCDebug(CircularSurveyLog) << "_setTransects(): solution.size() == 0";
qCDebug(RouteComplexItemLog) << "_setTransects(): solution.size() == 0";
}
if (var.size() > 0 && var.front().size() > 0) {
......@@ -609,38 +633,25 @@ void CircularSurvey::_setTransects(CircularSurvey::PtrRoutingData pRoute) {
emit variantNamesChanged();
disconnect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant);
&RouteComplexItem::_changeVariant);
this->_variant.setCookedValue(QVariant(0));
connect(&this->_variant, &Fact::rawValueChanged, this,
&CircularSurvey::_changeVariant);
&RouteComplexItem::_changeVariant);
this->_changeVariantWorker();
this->_setState(STATE::SKIPP);
this->_rebuildTransects();
} else {
qCDebug(CircularSurveyLog)
qCDebug(RouteComplexItemLog)
<< "_setTransects(): failed, variantVector empty.";
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);
}
/*!
\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
*/
bool RouteComplexItem::editing() const { return this->_editing; }
......@@ -7,9 +7,7 @@
#include "SettingsFact.h"
#include "TransectStyleComplexItem.h"
#include "Geometry/WimaJoinedAreaData.h"
#include "Geometry/WimaMeasurementAreaData.h"
#include "WimaPlanData.h"
#include "AreaData.h"
class RoutingThread;
class RoutingData;
......@@ -18,21 +16,22 @@ namespace routing {
class GeneratorBase;
}
class CircularSurvey : public TransectStyleComplexItem {
class RouteComplexItem : public TransectStyleComplexItem {
Q_OBJECT
using PtrGenerator = std::shared_ptr<routing::GeneratorBase>;
using PtrGenerator = routing::GeneratorBase *;
using PtrAreaData = AreaData *;
using PtrRoutingData = std::shared_ptr<RoutingData>;
using PtrWorker = std::unique_ptr<RoutingThread>;
using PtrWorker = RoutingThread *;
using Transects = QList<QList<CoordInfo_t>>;
using Variant = Transects;
enum class STATE { IDLE, ROUTING, SKIPP, REVERT_PATH, CHANGE_VARIANT };
public:
CircularSurvey(PlanMasterController *masterController, bool flyView,
const QString &kmlOrShpFile, QObject *parent);
~CircularSurvey();
RouteComplexItem(PlanMasterController *masterController, bool flyView,
const QString &kmlOrShpFile, QObject *parent);
~RouteComplexItem();
Q_PROPERTY(Fact *variant READ variant CONSTANT)
Q_PROPERTY(
......@@ -43,14 +42,18 @@ public:
Q_PROPERTY(
routing::GeneratorBase *generator READ generator 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
const AreaData &planData() const;
const AreaData *areaData() const;
AreaData *areaData();
Fact *variant();
QStringList variantNames() const;
bool calculating() const;
bool editing() const;
// Overrides
virtual bool load(const QJsonObject &complexObject, int sequenceNumber,
......@@ -67,8 +70,7 @@ public:
virtual QString patternName(void) const override;
// Generator
bool registerGenerator(const QString &name,
std::shared_ptr<routing::GeneratorBase> g);
bool registerGenerator(const QString &name, routing::GeneratorBase *g);
bool unregisterGenerator(const QString &name);
bool unregisterGenerator(int index);
Q_INVOKABLE bool switchToGenerator(const QString &name);
......@@ -77,6 +79,22 @@ public:
routing::GeneratorBase *generator();
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 *variantName;
static const char *jsonComplexItemTypeValue;
......@@ -87,6 +105,8 @@ signals:
void variantNamesChanged();
void generatorNameListChanged();
void generatorChanged();
void editingChanged();
void areaDataChanged();
private slots:
// Overrides from TransectStyleComplexItem
......@@ -101,20 +121,25 @@ private slots:
void _reverseWorker();
private:
bool _switchToGenerator(const PtrGenerator &newG);
bool _setGenerator(PtrGenerator newG);
void _setState(STATE state);
bool _calculating(STATE state) const;
void _setEditing(bool editing);
void _setAreaData(PtrAreaData data);
// State.
STATE _state;
// center of the circular lanes, e.g. base station
// Facts
QMap<QString, FactMetaData *> _metaDataMap;
SettingsFact _variant;
QStringList _variantNames;
// Area data
AreaData _areaData;
PtrAreaData _areaData;
PtrAreaData _editorData;
PtrAreaData _currentData;
bool _editing;
// Generators
QList<PtrGenerator> _generatorList;
......
......@@ -4,7 +4,7 @@
#include <QSharedPointer>
#include <QThread>
#include "snake.h"
#include "geometry/snake.h"
#include <atomic>
#include <condition_variable>
#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 "SnakeTile.h"
#include "nemo_interface/SnakeTile.h"
#include "snake.h"
#include <boost/units/systems/si.hpp>
......@@ -11,7 +11,7 @@
#define SNAKE_MAX_TILES 1000
#endif
QGC_LOGGING_CATEGORY(WimaMeasurementAreaLog, "WimaMeasurementAreaLog")
QGC_LOGGING_CATEGORY(MeasurementAreaLog, "MeasurementAreaLog")
TileData::TileData() : tiles(this) {}
......@@ -25,7 +25,7 @@ TileData &TileData::operator=(const TileData &other) {
if (tile != nullptr) {
this->tiles.append(new SnakeTile(*tile, this));
} else {
qCWarning(WimaMeasurementAreaLog) << "TileData::operator=: nullptr";
qCWarning(MeasurementAreaLog) << "TileData::operator=: nullptr";
}
}
this->tileCenterPoints = other.tileCenterPoints;
......@@ -63,15 +63,15 @@ size_t TileData::size() const {
}
}
const char *WimaMeasurementArea::settingsGroup = "MeasurementArea";
const char *WimaMeasurementArea::tileHeightName = "TileHeight";
const char *WimaMeasurementArea::tileWidthName = "TileWidth";
const char *WimaMeasurementArea::minTileAreaName = "MinTileAreaPercent";
const char *WimaMeasurementArea::showTilesName = "ShowTiles";
const char *WimaMeasurementArea::WimaMeasurementAreaName = "Measurement Area";
const char *MeasurementArea::settingsGroup = "MeasurementArea";
const char *MeasurementArea::tileHeightName = "TileHeight";
const char *MeasurementArea::tileWidthName = "TileWidth";
const char *MeasurementArea::minTileAreaName = "MinTileAreaPercent";
const char *MeasurementArea::showTilesName = "ShowTiles";
const char *MeasurementArea::MeasurementAreaName = "Measurement Area";
WimaMeasurementArea::WimaMeasurementArea(QObject *parent)
: WimaArea(parent),
MeasurementArea::MeasurementArea(QObject *parent)
: GeoArea(parent),
_metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
this /* QObject parent */)),
......@@ -88,9 +88,8 @@ WimaMeasurementArea::WimaMeasurementArea(QObject *parent)
init();
}
WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other,
QObject *parent)
: WimaArea(other, parent),
MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
: GeoArea(other, parent),
_metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
this /* QObject parent */)),
......@@ -107,79 +106,71 @@ WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other,
init();
}
/*!
* \overload operator=()
*
* Calls the inherited operator WimaArea::operator=().
*/
WimaMeasurementArea &WimaMeasurementArea::
operator=(const WimaMeasurementArea &other) {
WimaArea::operator=(other);
MeasurementArea &MeasurementArea::operator=(const MeasurementArea &other) {
GeoArea::operator=(other);
return *this;
}
WimaMeasurementArea::~WimaMeasurementArea() {}
MeasurementArea::~MeasurementArea() {}
QString WimaMeasurementArea::mapVisualQML() const {
return QStringLiteral("WimaMeasurementAreaMapVisual.qml");
QString MeasurementArea::mapVisualQML() const {
return QStringLiteral("MeasurementAreaMapVisual.qml");
}
QString WimaMeasurementArea::editorQML() const {
return QStringLiteral("WimaMeasurementAreaEditor.qml");
QString MeasurementArea::editorQML() const {
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() {
return &this->_tileData.tiles;
}
Fact *MeasurementArea::showTiles() { return &_showTiles; }
const QVector<int> &WimaMeasurementArea::progress() const {
return this->_progress;
}
QmlObjectListModel *MeasurementArea::tiles() { return &this->_tileData.tiles; }
QVector<int> WimaMeasurementArea::progressQml() const {
const QVector<int> &MeasurementArea::progress() const {
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;
}
const QVariantList &WimaMeasurementArea::tileCenterPoints() const {
const QVariantList &MeasurementArea::tileCenterPoints() const {
return this->_tileData.tileCenterPoints;
}
const TileData &WimaMeasurementArea::tileData() const {
return this->_tileData;
}
const TileData &MeasurementArea::tileData() const { 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()) {
this->WimaArea::saveToJson(json);
this->GeoArea::saveToJson(json);
json[tileHeightName] = _tileHeight.rawValue().toDouble();
json[tileWidthName] = _tileWidth.rawValue().toDouble();
json[minTileAreaName] = _minTileAreaPercent.rawValue().toDouble();
json[showTilesName] = _showTiles.rawValue().toBool();
json[areaTypeName] = WimaMeasurementAreaName;
json[areaTypeName] = MeasurementAreaName;
} else {
qCDebug(WimaMeasurementAreaLog) << "saveToJson(): not ready for saveing.";
qCDebug(MeasurementAreaLog) << "saveToJson(): not ready for saveing.";
}
}
bool WimaMeasurementArea::loadFromJson(const QJsonObject &json,
QString &errorString) {
if (this->WimaArea::loadFromJson(json, errorString)) {
bool MeasurementArea::loadFromJson(const QJsonObject &json,
QString &errorString) {
if (this->GeoArea::loadFromJson(json, errorString)) {
disableUpdate();
bool retVal = true;
......@@ -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 (p.size() == this->tiles()->count() && this->_progress != p) {
this->_progress = p;
......@@ -232,10 +223,10 @@ bool WimaMeasurementArea::setProgress(const QVector<int> &p) {
return false;
}
//!
//! \brief WimaMeasurementArea::doUpdate
//! \pre WimaMeasurementArea::deferUpdate must be called first, don't call
//! \brief MeasurementArea::doUpdate
//! \pre MeasurementArea::deferUpdate must be called first, don't call
//! this function directly!
void WimaMeasurementArea::doUpdate() {
void MeasurementArea::doUpdate() {
using namespace snake;
using namespace boost::units;
......@@ -292,7 +283,7 @@ void WimaMeasurementArea::doUpdate() {
}
pData->moveToThread(th);
qCDebug(WimaMeasurementAreaLog)
qCDebug(MeasurementAreaLog)
<< "doUpdate(): update time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
......@@ -305,7 +296,7 @@ void WimaMeasurementArea::doUpdate() {
this->_watcher.setFuture(future);
}
}
qCDebug(WimaMeasurementAreaLog)
qCDebug(MeasurementAreaLog)
<< "doUpdate(): execution time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
......@@ -313,9 +304,9 @@ void WimaMeasurementArea::doUpdate() {
<< " ms";
}
void WimaMeasurementArea::deferUpdate() {
void MeasurementArea::deferUpdate() {
if (this->_state == STATE::IDLE || this->_state == STATE::DEFERED) {
qCDebug(WimaMeasurementAreaLog) << "defereUpdate(): defer update.";
qCDebug(MeasurementAreaLog) << "defereUpdate(): defer update.";
if (this->_state == STATE::IDLE) {
this->_progress.clear();
this->_tileData.clear();
......@@ -325,16 +316,16 @@ void WimaMeasurementArea::deferUpdate() {
this->setState(STATE::DEFERED);
this->_timer.start(100);
} else if (this->_state == STATE::UPDATEING) {
qCDebug(WimaMeasurementAreaLog) << "defereUpdate(): restart.";
qCDebug(MeasurementAreaLog) << "defereUpdate(): restart.";
setState(STATE::RESTARTING);
}
}
void WimaMeasurementArea::storeTiles() {
void MeasurementArea::storeTiles() {
auto start = std::chrono::high_resolution_clock::now();
if (this->_state == STATE::UPDATEING) {
qCDebug(WimaMeasurementAreaLog) << "storeTiles(): update.";
qCDebug(MeasurementAreaLog) << "storeTiles(): update.";
this->_tileData = *this->_watcher.result();
// This is expensive. Drawing tiles is expensive too.
......@@ -343,12 +334,12 @@ void WimaMeasurementArea::storeTiles() {
emit this->tilesChanged();
setState(STATE::IDLE);
} else if (this->_state == STATE::RESTARTING) {
qCDebug(WimaMeasurementAreaLog) << "storeTiles(): restart.";
qCDebug(MeasurementAreaLog) << "storeTiles(): restart.";
doUpdate();
} else if (this->_state == STATE::STOP) {
qCDebug(WimaMeasurementAreaLog) << "storeTiles(): stop.";
qCDebug(MeasurementAreaLog) << "storeTiles(): stop.";
}
qCDebug(WimaMeasurementAreaLog)
qCDebug(MeasurementAreaLog)
<< "storeTiles() execution time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
......@@ -356,36 +347,34 @@ void WimaMeasurementArea::storeTiles() {
<< " ms";
}
void WimaMeasurementArea::disableUpdate() {
void MeasurementArea::disableUpdate() {
setState(STATE::IDLE);
this->_timer.stop();
}
void WimaMeasurementArea::enableUpdate() {
void MeasurementArea::enableUpdate() {
if (this->_state == STATE::STOP) {
setState(STATE::IDLE);
}
}
void WimaMeasurementArea::init() {
this->setObjectName(WimaMeasurementAreaName);
void MeasurementArea::init() {
this->setObjectName(MeasurementAreaName);
connect(&this->_tileHeight, &Fact::rawValueChanged, this,
&WimaMeasurementArea::deferUpdate);
&MeasurementArea::deferUpdate);
connect(&this->_tileWidth, &Fact::rawValueChanged, this,
&WimaMeasurementArea::deferUpdate);
&MeasurementArea::deferUpdate);
connect(&this->_minTileAreaPercent, &Fact::rawValueChanged, this,
&WimaMeasurementArea::deferUpdate);
connect(this, &WimaArea::pathChanged, this,
&WimaMeasurementArea::deferUpdate);
&MeasurementArea::deferUpdate);
connect(this, &GeoArea::pathChanged, this, &MeasurementArea::deferUpdate);
this->_timer.setSingleShot(true);
connect(&this->_timer, &QTimer::timeout, this,
&WimaMeasurementArea::doUpdate);
connect(&this->_timer, &QTimer::timeout, this, &MeasurementArea::doUpdate);
connect(&this->_watcher,
&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) {
auto oldState = this->_state;
this->_state = s;
......
......@@ -2,9 +2,10 @@
#include <QFutureWatcher>
#include <QObject>
#include <QSharedPointer>
#include <QTimer>
#include "WimaArea.h"
#include "GeoArea.h"
#include "SettingsFact.h"
......@@ -24,17 +25,16 @@ public:
std::size_t size() const;
};
class WimaMeasurementArea : public WimaArea {
class MeasurementArea : public GeoArea {
Q_OBJECT
enum class STATE { IDLE, DEFERED, UPDATEING, RESTARTING, STOP };
using DataPtr = QSharedPointer<TileData>;
public:
WimaMeasurementArea(QObject *parent = nullptr);
WimaMeasurementArea(const WimaMeasurementArea &other,
QObject *parent = nullptr);
WimaMeasurementArea &operator=(const WimaMeasurementArea &other);
~WimaMeasurementArea();
MeasurementArea(QObject *parent = nullptr);
MeasurementArea(const MeasurementArea &other, QObject *parent = nullptr);
MeasurementArea &operator=(const MeasurementArea &other);
~MeasurementArea();
Q_PROPERTY(Fact *tileHeight READ tileHeight CONSTANT)
Q_PROPERTY(Fact *tileWidth READ tileWidth CONSTANT)
......@@ -44,9 +44,12 @@ public:
Q_PROPERTY(int maxTiles READ maxTiles NOTIFY maxTilesChanged)
Q_PROPERTY(QVector<int> progress READ progressQml NOTIFY progressChanged)
// Overrides from WimaPolygon
QString mapVisualQML(void) const;
QString editorQML(void) const;
// Overrides from GeoArea
QString mapVisualQML(void) const override;
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.
Fact *tileHeight();
......@@ -62,17 +65,13 @@ public:
int maxTiles() const;
bool ready() const;
// Member Methodes
void saveToJson(QJsonObject &json);
bool loadFromJson(const QJsonObject &json, QString &errorString);
// Static Variables
static const char *settingsGroup;
static const char *tileHeightName;
static const char *tileWidthName;
static const char *minTileAreaName;
static const char *showTilesName;
static const char *WimaMeasurementAreaName;
static const char *MeasurementAreaName;
signals:
void tilesChanged();
......@@ -109,7 +108,6 @@ private:
// Tile stuff.
// Tile stuff.
mutable QTimer _timer;
using DataPtr = std::shared_ptr<TileData>;
mutable STATE _state;
mutable TileData _tileData;
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 "QGCQGeoCoordinate.h"
QGC_LOGGING_CATEGORY(WimaServiceAreaLog, "WimaServiceAreaLog")
#include <QJsonObject>
const char *WimaServiceArea::wimaServiceAreaName = "Service Area";
const char *WimaServiceArea::depotLatitudeName = "DepotLatitude";
const char *WimaServiceArea::depotLongitudeName = "DepotLongitude";
const char *WimaServiceArea::depotAltitudeName = "DepotAltitude";
QGC_LOGGING_CATEGORY(SafeAreaLog, "SafeAreaLog")
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)
: WimaArea(other, parent), _depot(other.depot()) {
SafeArea::SafeArea(QObject *parent) : GeoArea(parent) { init(); }
SafeArea::SafeArea(const SafeArea &other, QObject *parent)
: GeoArea(other, parent), _depot(other.depot()) {
init();
}
/*!
* \overload operator=()
*
* Calls the inherited operator WimaArea::operator=().
*/
WimaServiceArea &WimaServiceArea::operator=(const WimaServiceArea &other) {
WimaArea::operator=(other);
SafeArea &SafeArea::operator=(const SafeArea &other) {
GeoArea::operator=(other);
this->setDepot(other.depot());
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() ||
_depot.longitude() != coordinate.longitude()) {
if (this->containsCoordinate(coordinate)) {
......@@ -45,18 +50,17 @@ bool WimaServiceArea::setDepot(const QGeoCoordinate &coordinate) {
return false;
}
void WimaServiceArea::saveToJson(QJsonObject &json) {
this->WimaArea::saveToJson(json);
json[areaTypeName] = wimaServiceAreaName;
void SafeArea::saveToJson(QJsonObject &json) {
this->GeoArea::saveToJson(json);
json[areaTypeName] = safeAreaName;
json[depotLatitudeName] = _depot.latitude();
json[depotLongitudeName] = _depot.longitude();
json[depotAltitudeName] = _depot.altitude();
}
bool WimaServiceArea::loadFromJson(const QJsonObject &json,
QString &errorString) {
bool SafeArea::loadFromJson(const QJsonObject &json, QString &errorString) {
bool retVal = false;
if (this->WimaArea::loadFromJson(json, errorString)) {
if (this->GeoArea::loadFromJson(json, errorString)) {
double lat = 0;
if (json.contains(depotLatitudeName) &&
json[depotLatitudeName].isDouble()) {
......@@ -85,9 +89,9 @@ bool WimaServiceArea::loadFromJson(const QJsonObject &json,
return retVal;
}
void WimaServiceArea::init() {
this->setObjectName(wimaServiceAreaName);
connect(this, &WimaArea::pathChanged, [this] {
void SafeArea::init() {
this->setObjectName(safeAreaName);
connect(this, &GeoArea::pathChanged, [this] {
if (!this->_depot.isValid() || !this->containsCoordinate(this->_depot)) {
if (this->containsCoordinate(this->center())) {
// Use center.
......@@ -106,13 +110,16 @@ void WimaServiceArea::init() {
minIndex = idx;
}
} 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) {
// Use first coordinate.
this->setDepot(this->pathModel().value<QGCQGeoCoordinate *>(0)->coordinate());
this->setDepot(
this->pathModel().value<QGCQGeoCoordinate *>(0)->coordinate());
}
}
});
......
#pragma once
#include "WimaArea.h"
#include "WimaTrackerPolyline.h"
#include "GeoArea.h"
#include <QObject>
class WimaServiceArea : public WimaArea {
class SafeArea : public GeoArea {
Q_OBJECT
public:
WimaServiceArea(QObject *parent = nullptr);
WimaServiceArea(const WimaServiceArea &other, QObject *parent);
WimaServiceArea &operator=(const WimaServiceArea &other);
SafeArea(QObject *parent = nullptr);
SafeArea(const SafeArea &other, QObject *parent);
SafeArea &operator=(const SafeArea &other);
Q_PROPERTY(
QGeoCoordinate depot READ depotQml WRITE setDepot NOTIFY depotChanged)
// Overrides from WimaPolygon
QString mapVisualQML(void) const { return "WimaServiceAreaMapVisual.qml"; }
QString editorQML(void) const { return "WimaServiceAreaEditor.qml"; }
QString mapVisualQML(void) const override;
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
const QGeoCoordinate &depot(void) const;
QGeoCoordinate depotQml(void) const;
// Member Methodes
void saveToJson(QJsonObject &json);
bool loadFromJson(const QJsonObject &json, QString &errorString);
// static Members
static const char *wimaServiceAreaName;
static const char *safeAreaName;
static const char *depotLatitudeName;
static const char *depotLongitudeName;
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"
SnakeTile::SnakeTile(QObject *parent) : WimaAreaData(parent) {}
SnakeTile::SnakeTile(QObject *parent) : GeoArea(parent) {}
SnakeTile::SnakeTile(const SnakeTile &other, QObject *parent)
: WimaAreaData(parent) {
: GeoArea(parent) {
*this = other;
}
SnakeTile::~SnakeTile() {}
QString SnakeTile::mapVisualQML() const {
return QStringLiteral("WimaAreaNoVisual.qml");
}
QString SnakeTile::type() const { return "Tile"; }
QString SnakeTile::mapVisualQML() const { return QStringLiteral(""); }
SnakeTile *SnakeTile::Clone() const { return new SnakeTile(*this); }
QString SnakeTile::editorQML() const { return QStringLiteral(""); }
SnakeTile &SnakeTile::operator=(const SnakeTile &other) {
this->WimaAreaData::operator=(other);
return *this;
SnakeTile *SnakeTile::clone(QObject *parent) const {
return new SnakeTile(*this, parent);
}
void SnakeTile::push_back(const QGeoCoordinate &c) { this->appendVertex(c); }
#pragma once
#include "Wima/Geometry/WimaAreaData.h"
#include "geometry/GeoArea.h"
class SnakeTile : public WimaAreaData {
#include <QGeoCoordinate>
class SnakeTile : public GeoArea {
Q_OBJECT
public:
SnakeTile(QObject *parent = nullptr);
......@@ -10,12 +12,8 @@ public:
~SnakeTile();
virtual QString mapVisualQML() const override;
virtual QString editorQML() const override;
virtual SnakeTile *clone(QObject *parent) const;
QString type() const override;
SnakeTile *Clone() const;
SnakeTile &operator=(const SnakeTile &other);
protected:
void assign(const SnakeTile &other);
void push_back(const QGeoCoordinate &c);
};
#pragma once
#include "Wima/Geometry/GenericPolygon.h"
#include "geometry/GenericPolygon.h"
using SnakeTileLocal = GenericPolygon<QPointF, std::vector>;
#pragma once
#include "Wima/Geometry/GenericPolygonArray.h"
#include "Wima/Snake/SnakeTileLocal.h"
#include "RouteMissionItem/geometry/GenericPolygonArray.h"
#include "RouteMissionItem/nemo_interface/SnakeTileLocal.h"
#include <vector>
typedef GenericPolygonArray<SnakeTileLocal, std::vector> SnakeTilesLocal;
......@@ -26,17 +26,26 @@ Item {
property var qgcView ///< QGCView to use for popping dialogs
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 _entryCoordinate: undefined
property var _exitCoordinate: undefined
property var _generatorVisuals: undefined
property var _transectsComponent: undefined
property var _entryCoordinate: undefined
property var _exitCoordinate: undefined
property var _generatorVisuals: undefined
property bool _isCurrentItem: _missionItem.isCurrentItem
signal clicked(int sequenceNumber)
on_EditingChanged: {
_destroyEntryCoordinate()
_destroyExitCoordinate()
_destroyTransectsComponent()
_destroyGeneratorVisuals()
}
Component.onCompleted: {
_addEntryCoordinate()
_addExitCoordinate()
......@@ -108,6 +117,19 @@ Item {
}
}
Repeater {
model: _areaData.areaList
delegate: WimaMapVisual {
map: _root.map
qgcView: _root.qgcView
}
onItemAdded: {
//console.log("Item added")
}
}
// Generator visuals
function _addGeneratorVisuals(){
if (_generator.mapVisualQml && !_generatorVisuals) {
......@@ -183,6 +205,4 @@ Item {
_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