diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index aab84959c..b75d1b8e1 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -190,6 +190,7 @@ set(MM_HDRS workspacesmodel.h workspacesproxymodel.h mmstyle.h + mmtypeutils.h ) if (NOT WIN32) diff --git a/app/inpututils.cpp b/app/inpututils.cpp index 855e5b2c2..3fb7a2564 100644 --- a/app/inpututils.cpp +++ b/app/inpututils.cpp @@ -450,7 +450,7 @@ QgsGeometry InputUtils::transformGeometryToMapWithLayer( const QgsGeometry &geom { if ( !sourceLayer || !sourceLayer->isValid() || !targetSettings ) { - return QgsGeometry(); + return {}; } return transformGeometry( geometry, sourceLayer->crs(), targetSettings->destinationCrs(), targetSettings->transformContext() ); @@ -818,7 +818,7 @@ QgsPoint InputUtils::point( double x, double y, double z, double m ) QgsGeometry InputUtils::emptyGeometry() { - return QgsGeometry(); + return {}; } QgsFeature InputUtils::emptyFeature() @@ -1670,12 +1670,12 @@ QgsRectangle InputUtils::stakeoutPathExtent( QgsGeometry InputUtils::stakeoutGeometry( const QgsPoint &mapPosition, const FeatureLayerPair &target, InputMapSettings *mapSettings ) { if ( !mapSettings || !target.isValid() ) - return QgsGeometry(); + return {}; - QgsPointXY targetInLayerCoordinates = target.feature().geometry().asPoint(); - QgsPointXY t = transformPointXY( target.layer()->crs(), mapSettings->destinationCrs(), mapSettings->transformContext(), targetInLayerCoordinates ); + const QgsPointXY targetInLayerCoordinates = target.feature().geometry().asPoint(); + const QgsPointXY t = transformPointXY( target.layer()->crs(), mapSettings->destinationCrs(), mapSettings->transformContext(), targetInLayerCoordinates ); - QVector points { mapPosition, QgsPoint( t ) }; + const QVector points { mapPosition, QgsPoint( t ) }; return QgsGeometry::fromPolyline( points ); } diff --git a/app/mmtypeutils.h b/app/mmtypeutils.h new file mode 100644 index 000000000..76f4da156 --- /dev/null +++ b/app/mmtypeutils.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +/** + * This file should be used for explicit declaration of 3rd party types into QML. + */ + +#ifndef MMTYPEUTILS_H +#define MMTYPEUTILS_H + +#include +#include + +struct ForeignGeometry +{ + Q_GADGET + QML_FOREIGN( QgsGeometry ) + QML_VALUE_TYPE( qgsGeometry ); +}; + +#endif //MMTYPEUTILS_H diff --git a/app/multieditmanager.h b/app/multieditmanager.h index 134d4ef37..c39ff31ef 100644 --- a/app/multieditmanager.h +++ b/app/multieditmanager.h @@ -10,6 +10,7 @@ #ifndef MULTIEDITMANAGER_H #define MULTIEDITMANAGER_H +#include #include #include "featurelayerpair.h" diff --git a/app/position/tracking/positiontrackinghighlight.cpp b/app/position/tracking/positiontrackinghighlight.cpp index 4ad84b270..adbe0b3db 100644 --- a/app/position/tracking/positiontrackinghighlight.cpp +++ b/app/position/tracking/positiontrackinghighlight.cpp @@ -9,6 +9,8 @@ #include "positiontrackinghighlight.h" +#include "inpututils.h" + PositionTrackingHighlight::PositionTrackingHighlight( QObject *parent ) : QObject( parent ) { @@ -20,13 +22,13 @@ void PositionTrackingHighlight::recalculate() { if ( mMapPosition.isEmpty() ) { - setHighlightGeometry( QgsGeometry() ); + setHighlightGeometry( InputUtils::emptyGeometry() ); return; } if ( mTrackedGeometry.isEmpty() ) { - setHighlightGeometry( QgsGeometry() ); + setHighlightGeometry( InputUtils::emptyGeometry() ); return; } diff --git a/app/qml/map/MMHighlight.qml b/app/qml/map/MMHighlight.qml index 7c813c1cc..fb16f35a4 100644 --- a/app/qml/map/MMHighlight.qml +++ b/app/qml/map/MMHighlight.qml @@ -9,8 +9,10 @@ import QtQuick import QtQuick.Shapes +import QtQml import mm 1.0 as MM +import MMInput import ".." @@ -19,7 +21,7 @@ Item { // geometry to highlight // geometry must be in map canvas CRS! - property var geometry + property qgsGeometry geometry // for transformation of the highlight to the correct location on the map property MM.MapSettings mapSettings @@ -108,7 +110,7 @@ Item { { if ( !mapSettings ) return - if ( !geometry ) + if ( __inputUtils.isEmptyGeometry( geometry ) ) { // trigger repaint for empty geometries markerItems = markerItems.map( function (marker) { return marker.destroy() } ) diff --git a/app/qml/map/MMMapController.qml b/app/qml/map/MMMapController.qml index f3b3601ee..02afcbf78 100644 --- a/app/qml/map/MMMapController.qml +++ b/app/qml/map/MMMapController.qml @@ -1380,7 +1380,7 @@ Item { } function jumpToHighlighted( mapOffset ) { - if ( identifyHighlight.geometry === null ) + if ( identifyHighlight.geometry.isNull ) return let screenPt = __inputUtils.relevantGeometryCenterToScreenCoordinates( identifyHighlight.geometry, mapCanvas.mapSettings ) @@ -1394,7 +1394,7 @@ Item { } function hideHighlight() { - identifyHighlight.geometry = null + identifyHighlight.geometry = __inputUtils.emptyGeometry() updatePosition() } @@ -1441,7 +1441,7 @@ Item { case "view": { // While a feature is highlighted we want to keep it visible in the map extent // so in that case we skip centering to position - if ( identifyHighlight.geometry !== null ) + if ( !__inputUtils.isEmptyGeometry( identifyHighlight.geometry ) ) { break } @@ -1476,7 +1476,7 @@ Item { // clear all previous references to old project (if we don't clear references to the previous project, // highlights may end up with dangling pointers to map layers and cause crashes) - identifyHighlight.geometry = null + identifyHighlight.geometry = __inputUtils.emptyGeometry() } function setTracking( shouldTrack ) { diff --git a/app/qml/map/MMRecordingTools.qml b/app/qml/map/MMRecordingTools.qml index da976654c..22ffd1012 100644 --- a/app/qml/map/MMRecordingTools.qml +++ b/app/qml/map/MMRecordingTools.qml @@ -10,6 +10,8 @@ import QtQuick import QtQuick.Shapes import QtMultimedia +import QtQml +import QtQml.Models import mm 1.0 as MM import MMInput diff --git a/vcpkg/ports/qgis/geometry-equals-operator.patch b/vcpkg/ports/qgis/geometry-equals-operator.patch new file mode 100644 index 000000000..8d7207dbb --- /dev/null +++ b/vcpkg/ports/qgis/geometry-equals-operator.patch @@ -0,0 +1,21 @@ +diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h +index 98e40e74e14..06fa2a78ddd 100644 +--- a/src/core/geometry/qgsgeometry.h ++++ b/src/core/geometry/qgsgeometry.h +@@ -177,6 +177,16 @@ class CORE_EXPORT QgsGeometry + */ + QgsGeometry &operator=( QgsGeometry const &rhs ) SIP_SKIP; + ++ /** ++ * Upstream QgsGeometry is missing equals operator overload and uses equals() to correctly check for equality. ++ * However QML in Qt 6.9+ also checks equality, before firing onChanged signal. The incorrect equality evaluation ++ * is breaking property bindings. ++ */ ++ bool operator==( const QgsGeometry &rhs ) const ++ { ++ return equals( rhs ); ++ } ++ + /** + * Creates a geometry from an abstract geometry object. Ownership of + * geom is transferred. diff --git a/vcpkg/ports/qgis/portfile.cmake b/vcpkg/ports/qgis/portfile.cmake index d98e62100..0cd68add3 100644 --- a/vcpkg/ports/qgis/portfile.cmake +++ b/vcpkg/ports/qgis/portfile.cmake @@ -21,6 +21,9 @@ vcpkg_from_github( snapping-casting.patch qflags-qstring-arg.patch point-cloud-guard.patch + # TODO: when updating to QGIS 4, this has to be reworked to support new QgsGeometry + # comparison in QGIS 4 + geometry-equals-operator.patch ) file(REMOVE ${SOURCE_PATH}/cmake/FindQtKeychain.cmake)