From c59d22e30cea9fd1944441858cfbcb6ba01209ab Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Tue, 19 May 2026 14:31:05 +0300 Subject: [PATCH 1/2] Updated geometry properties to use QVariantList for map tools. Updated install md Updated cmake presets template Updated the code for MMHighlight and the other components --- INSTALL.md | 2 +- app/inpututils.cpp | 16 ++ app/inpututils.h | 1 + app/mapsketchingcontroller.cpp | 4 +- app/mapsketchingcontroller.h | 4 +- app/maptools/recordingmaptool.cpp | 57 +++++-- app/maptools/recordingmaptool.h | 14 ++ app/maptools/splittingmaptool.cpp | 5 + app/maptools/splittingmaptool.h | 4 + .../tracking/positiontrackinghighlight.cpp | 5 + .../tracking/positiontrackinghighlight.h | 5 + app/qml/components/MMButton.qml | 2 +- app/qml/form/editors/MMFormPhotoEditor.qml | 7 +- app/qml/map/MMHighlight.qml | 150 +++++++++++------- app/qml/map/MMMapController.qml | 26 +-- app/qml/map/MMMeasurementTools.qml | 9 +- app/qml/map/MMRecordingTools.qml | 44 +++-- app/qml/map/MMSplittingTools.qml | 12 +- app/qml/map/MMStakeoutTools.qml | 5 +- docs/CMakePresets-Template.json | 20 +-- 20 files changed, 273 insertions(+), 119 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 370cd3427..534b6adc3 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -229,7 +229,7 @@ For building ABIs see https://www.qt.io/blog/android-multi-abi-builds-are-back binary vcpkg cache. ``` - export ANDROID_NDK_HOME=/home//android/ndk/ + export ANDROID_NDK_ROOT=/home//android/ndk/ export ANDROID_SDK_ROOT=/home//android export QT_ANDROID_KEYSTORE_ALIAS= export QT_ANDROID_KEYSTORE_KEY_PASS= diff --git a/app/inpututils.cpp b/app/inpututils.cpp index 855e5b2c2..c4bfd5fdb 100644 --- a/app/inpututils.cpp +++ b/app/inpututils.cpp @@ -466,6 +466,22 @@ QgsGeometry InputUtils::transformGeometryToMapWithCRS( const QgsGeometry &geomet return transformGeometry( geometry, sourceCRS, targetSettings->destinationCrs(), targetSettings->transformContext() ); } +QVariantList InputUtils::extractGeometryToQml( const QgsGeometry &geometry ) +{ + QVector coords = extractGeometryCoordinates( geometry ); + QVariantList variantList; + + // Reserve space to avoid reallocation overhead during the loop + variantList.reserve( coords.size() ); + + for ( double val : coords ) + { + variantList.append( val ); + } + + return variantList; +} + QgsGeometry InputUtils::extractGeometry( const FeatureLayerPair &pair ) { if ( !pair.isValid() ) diff --git a/app/inpututils.h b/app/inpututils.h index d94e1803e..bd04112ea 100644 --- a/app/inpututils.h +++ b/app/inpututils.h @@ -116,6 +116,7 @@ class InputUtils: public QObject //! Helper methods to use for transforming geometry from QML as overriding does not work properly there Q_INVOKABLE static QgsGeometry transformGeometryToMapWithLayer( const QgsGeometry &geometry, QgsVectorLayer *sourceLayer, InputMapSettings *targetSettings ); Q_INVOKABLE static QgsGeometry transformGeometryToMapWithCRS( const QgsGeometry &geometry, const QgsCoordinateReferenceSystem &sourceCRS, InputMapSettings *targetSettings ); + Q_INVOKABLE static QVariantList extractGeometryToQml( const QgsGeometry &geometry ); /** * Function extracts QgsGeometry from the given pair. diff --git a/app/mapsketchingcontroller.cpp b/app/mapsketchingcontroller.cpp index af5cf5010..588ed5cdf 100644 --- a/app/mapsketchingcontroller.cpp +++ b/app/mapsketchingcontroller.cpp @@ -139,9 +139,9 @@ void MapSketchingController::undo() const mLayer->undoStack()->undo(); } -QgsGeometry MapSketchingController::highlightGeometry() const +QVariantList MapSketchingController::highlightGeometry() const { - return mHighlight; + return InputUtils::extractGeometryToQml( mHighlight ); } QStringList MapSketchingController::availableColors() const diff --git a/app/mapsketchingcontroller.h b/app/mapsketchingcontroller.h index ab0488571..7110ec0a0 100644 --- a/app/mapsketchingcontroller.h +++ b/app/mapsketchingcontroller.h @@ -22,7 +22,7 @@ class MapSketchingController : public QObject Q_OBJECT Q_PROPERTY( InputMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged ) - Q_PROPERTY( QgsGeometry highlightGeometry READ highlightGeometry NOTIFY highlightGeometryChanged ) + Q_PROPERTY( QVariantList highlightGeometry READ highlightGeometry NOTIFY highlightGeometryChanged ) Q_PROPERTY( QColor activeColor READ activeColor WRITE setActiveColor NOTIFY activeColorChanged ) Q_PROPERTY( bool canRedo READ canRedo NOTIFY canRedoChanged ) Q_PROPERTY( bool canUndo READ canUndo NOTIFY canUndoChanged ) @@ -51,7 +51,7 @@ class MapSketchingController : public QObject void eraserActiveChanged(); private: - QgsGeometry highlightGeometry() const; + QVariantList highlightGeometry() const; void clearHighlight(); void setMapSettings( InputMapSettings *settings ); InputMapSettings *mapSettings() const; diff --git a/app/maptools/recordingmaptool.cpp b/app/maptools/recordingmaptool.cpp index 289d755ef..4a4f17f91 100644 --- a/app/maptools/recordingmaptool.cpp +++ b/app/maptools/recordingmaptool.cpp @@ -114,38 +114,34 @@ void RecordingMapTool::addPoint( const QgsPoint &point ) if ( mRecordedGeometry.type() == Qgis::GeometryType::Polygon ) { - // if it is a polygon and ring is not correctly defined yet (e.g. only - // contains 1 point or not closed) we add point directly to the ring - // and close it - - QgsLineString *r; - const QgsPolygon *poly; - + // 1. Get a mutable pointer to the polygon directly from the geometry + QgsPolygon *mutablePoly = nullptr; if ( mRecordedGeometry.isMultipart() ) { - poly = qgsgeometry_cast( mRecordedGeometry.constGet() )->polygonN( mActivePart ); + mutablePoly = qgsgeometry_cast( mRecordedGeometry.get() )->polygonN( mActivePart ); } else { - poly = qgsgeometry_cast( mRecordedGeometry.constGet() ); + mutablePoly = qgsgeometry_cast( mRecordedGeometry.get() ); } - if ( !poly ) + if ( !mutablePoly ) { return; } + // 2. Get a mutable pointer to the specific ring + QgsLineString *mutableRing = nullptr; if ( mActiveRing == 0 ) { - r = qgsgeometry_cast( poly->exteriorRing()->clone() ); + mutableRing = qgsgeometry_cast( mutablePoly->exteriorRing() ); } else { - // interior rings starts indexing from 0 - r = qgsgeometry_cast( poly->interiorRing( mActiveRing - 1 )->clone() ); + mutableRing = qgsgeometry_cast( mutablePoly->interiorRing( mActiveRing - 1 ) ); } - if ( !r ) + if ( !mutableRing ) { return; } @@ -170,10 +166,11 @@ void RecordingMapTool::addPoint( const QgsPoint &point ) mRecordedGeometry.addPart( poly.clone(), Qgis::GeometryType::Polygon ); } - if ( r->nCoordinates() < 2 ) + // 3. Check the actual ring and add the vertex directly in place! + if ( mutableRing->nCoordinates() < 2 ) { - r->addVertex( pointToAdd ); - r->close(); + mutableRing->addVertex( pointToAdd ); + mutableRing->close(); mActiveLayer->beginEditCommand( QStringLiteral( "Add point" ) ); emit recordedGeometryChanged( mRecordedGeometry ); @@ -1694,6 +1691,32 @@ void RecordingMapTool::setActiveFeature( const QgsFeature &newActiveFeature ) emit activeFeatureChanged( mActiveFeature ); } + +QVariantList RecordingMapTool::recordedGeometryData() const +{ + return InputUtils::extractGeometryToQml( mRecordedGeometry ); +} + +QVariantList RecordingMapTool::existingVerticesData() const +{ + return InputUtils::extractGeometryToQml( mExistingVertices ); +} + +QVariantList RecordingMapTool::midPointsData() const +{ + return InputUtils::extractGeometryToQml( mMidPoints ); +} + +QVariantList RecordingMapTool::handlesData() const +{ + return InputUtils::extractGeometryToQml( mHandles ); +} + +QVariantList RecordingMapTool::activeVertexGeometryData() const +{ + return InputUtils::extractGeometryToQml( mActiveVertexGeometry ); +} + void RecordingMapTool::avoidIntersections() { diff --git a/app/maptools/recordingmaptool.h b/app/maptools/recordingmaptool.h index 0587fde9c..679acc241 100644 --- a/app/maptools/recordingmaptool.h +++ b/app/maptools/recordingmaptool.h @@ -93,8 +93,15 @@ class RecordingMapTool : public AbstractMapTool Q_PROPERTY( QgsGeometry midPoints READ midPoints WRITE setMidPoints NOTIFY midPointsChanged ) Q_PROPERTY( QgsGeometry handles READ handles WRITE setHandles NOTIFY handlesChanged ) + + Q_PROPERTY( QVariantList recordedGeometryData READ recordedGeometryData NOTIFY recordedGeometryChanged ) + Q_PROPERTY( QVariantList existingVerticesData READ existingVerticesData NOTIFY existingVerticesChanged ) + Q_PROPERTY( QVariantList midPointsData READ midPointsData NOTIFY midPointsChanged ) + Q_PROPERTY( QVariantList handlesData READ handlesData NOTIFY handlesChanged ) + Q_PROPERTY( Vertex activeVertex READ activeVertex WRITE setActiveVertex NOTIFY activeVertexChanged ) Q_PROPERTY( QgsGeometry activeVertexGeometry READ activeVertexGeometry WRITE setActiveVertexGeometry NOTIFY activeVertexGeometryChanged ) + Q_PROPERTY( QVariantList activeVertexGeometryData READ activeVertexGeometryData NOTIFY activeVertexGeometryChanged ) Q_PROPERTY( InsertPolicy insertPolicy READ insertPolicy WRITE setInsertPolicy NOTIFY insertPolicyChanged ) Q_PROPERTY( MapToolState state READ state WRITE setState NOTIFY stateChanged ) @@ -203,6 +210,13 @@ class RecordingMapTool : public AbstractMapTool QgsVectorLayer *activeLayer() const; void setActiveLayer( QgsVectorLayer *newActiveLayer ); + QVariantList recordedGeometryData() const; + QVariantList existingVerticesData() const; + QVariantList midPointsData() const; + QVariantList handlesData() const; + + QVariantList activeVertexGeometryData() const; + const QgsGeometry &recordedGeometry() const; void setRecordedGeometry( const QgsGeometry &newRecordedGeometry ); diff --git a/app/maptools/splittingmaptool.cpp b/app/maptools/splittingmaptool.cpp index a35abe789..676e3ca4c 100644 --- a/app/maptools/splittingmaptool.cpp +++ b/app/maptools/splittingmaptool.cpp @@ -111,6 +111,11 @@ const QgsGeometry &SplittingMapTool::recordedGeometry() const return mRecordedGeometry; } +QVariantList SplittingMapTool::recordedGeometryData() const +{ + return InputUtils::extractGeometryToQml( mRecordedGeometry ); +} + void SplittingMapTool::setRecordedGeometry( const QgsGeometry &newRecordedGeometry ) { if ( mRecordedGeometry.equals( newRecordedGeometry ) ) diff --git a/app/maptools/splittingmaptool.h b/app/maptools/splittingmaptool.h index b3164187f..2fc5a8871 100644 --- a/app/maptools/splittingmaptool.h +++ b/app/maptools/splittingmaptool.h @@ -16,12 +16,15 @@ #include "qgsgeometry.h" #include "featurelayerpair.h" +#include "inpututils.h" class SplittingMapTool : public AbstractMapTool { Q_OBJECT Q_PROPERTY( QgsGeometry recordedGeometry READ recordedGeometry WRITE setRecordedGeometry NOTIFY recordedGeometryChanged ) + + Q_PROPERTY( QVariantList recordedGeometryData READ recordedGeometryData NOTIFY recordedGeometryChanged ) Q_PROPERTY( FeatureLayerPair featureToSplit READ featureToSplit WRITE setFeatureToSplit NOTIFY featureToSplitChanged ) public: @@ -57,6 +60,7 @@ class SplittingMapTool : public AbstractMapTool // Getters/setters const QgsGeometry &recordedGeometry() const; + QVariantList recordedGeometryData() const; void setRecordedGeometry( const QgsGeometry &newRecordedGeometry ); const FeatureLayerPair &featureToSplit() const; diff --git a/app/position/tracking/positiontrackinghighlight.cpp b/app/position/tracking/positiontrackinghighlight.cpp index 4ad84b270..36f4a699e 100644 --- a/app/position/tracking/positiontrackinghighlight.cpp +++ b/app/position/tracking/positiontrackinghighlight.cpp @@ -60,6 +60,11 @@ QgsGeometry PositionTrackingHighlight::highlightGeometry() const return mHighlightGeometry; } +QVariantList PositionTrackingHighlight::highlightGeometryData() const +{ + return InputUtils::extractGeometryToQml( mHighlightGeometry ); +} + void PositionTrackingHighlight::setHighlightGeometry( const QgsGeometry &newHighlightGeometry ) { if ( mHighlightGeometry.equals( newHighlightGeometry ) ) diff --git a/app/position/tracking/positiontrackinghighlight.h b/app/position/tracking/positiontrackinghighlight.h index 396f1a7ed..3f4167eea 100644 --- a/app/position/tracking/positiontrackinghighlight.h +++ b/app/position/tracking/positiontrackinghighlight.h @@ -14,6 +14,7 @@ #include #include "qgsgeometry.h" +#include "inpututils.h" class PositionTrackingHighlight : public QObject { @@ -24,6 +25,8 @@ class PositionTrackingHighlight : public QObject // Geometry in Q_PROPERTY( QgsGeometry trackedGeometry READ trackedGeometry WRITE setTrackedGeometry NOTIFY trackedGeometryChanged ) + Q_PROPERTY( QVariantList highlightGeometryData READ highlightGeometryData NOTIFY highlightGeometryChanged ) + // Geometry out Q_PROPERTY( QgsGeometry highlightGeometry READ highlightGeometry NOTIFY highlightGeometryChanged ) @@ -35,6 +38,8 @@ class PositionTrackingHighlight : public QObject QgsGeometry highlightGeometry() const; + QVariantList highlightGeometryData() const; + QgsPoint mapPosition() const; void setMapPosition( QgsPoint newMapPosition ); diff --git a/app/qml/components/MMButton.qml b/app/qml/components/MMButton.qml index 019c651d0..91a7ce6f9 100644 --- a/app/qml/components/MMButton.qml +++ b/app/qml/components/MMButton.qml @@ -107,7 +107,7 @@ Button { State { name: "hovered" - when: root.hovered && root.enabled + when: (root.hovered || root.down) && root.enabled PropertyChanges { target: buttonContent diff --git a/app/qml/form/editors/MMFormPhotoEditor.qml b/app/qml/form/editors/MMFormPhotoEditor.qml index e0dccf360..e6687d594 100644 --- a/app/qml/form/editors/MMFormPhotoEditor.qml +++ b/app/qml/form/editors/MMFormPhotoEditor.qml @@ -133,8 +133,8 @@ MMFormPhotoViewer { onDeleteImage: { // schedule the image for deletion internal.imageSourceToDelete = imageDeleteDialog.imagePath - root.sketchingController.removeBackupSketches() - root.sketchingController.clear() + root.sketchingController?.removeBackupSketches() + root.sketchingController?.clear() resetValueAndClose() } @@ -142,6 +142,9 @@ MMFormPhotoViewer { function resetValueAndClose() { root.editorValueChanged( "", true ) + root.photoState = "notSet" + internal.resolvedImageSource = "" + internal.tempSketchedImageSource = "" errorMsg = "" imagePath = "" diff --git a/app/qml/map/MMHighlight.qml b/app/qml/map/MMHighlight.qml index 7c813c1cc..70c1210ed 100644 --- a/app/qml/map/MMHighlight.qml +++ b/app/qml/map/MMHighlight.qml @@ -7,6 +7,8 @@ * * ***************************************************************************/ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Shapes @@ -17,9 +19,9 @@ import ".." Item { id: highlight - // geometry to highlight + // geometry data array extracted from C++ // geometry must be in map canvas CRS! - property var geometry + property var geometryData // for transformation of the highlight to the correct location on the map property MM.MapSettings mapSettings @@ -37,6 +39,7 @@ Item { property real markerWidth: 40 * __dp // based on marker image size property real markerHeight: 53 * __dp // based on marker image size property real markerBorderWidth: 2 * __dp + readonly property url markerImageSource: __style.mapPinImage // line properties enum LineWidths { Normal, Narrow } @@ -90,7 +93,7 @@ Item { } } - onGeometryChanged: constructHighlights() + onGeometryDataChanged: constructHighlights() // Transforms X coordinate from map CRS to screen XY with regards to scale and HighDPI function transformX( xcoord ) @@ -108,22 +111,22 @@ Item { { if ( !mapSettings ) return - if ( !geometry ) + if ( !geometryData || geometryData.length === 0 ) { // trigger repaint for empty geometries markerItems = markerItems.map( function (marker) { return marker.destroy() } ) - - let newLineElements = []; - newLineElements.push( componentMoveTo.createObject( lineShapePath ) ) - - let newPolygonElements = []; - newPolygonElements.push( componentMoveTo.createObject( polygonShapePath ) ) + lineShapeItems = lineShapeItems.map( function (el) { return el.destroy() } ) + lineBorderShapeItems = lineBorderShapeItems.map( function (el) { return el.destroy() } ) + polygonShapeItems = polygonShapeItems.map( function (el) { return el.destroy() } ) + polygonRingBorderShapeItems = polygonRingBorderShapeItems.map( function (el) { return el.destroy() } ) markerItems = []; - lineShapePath.pathElements = newLineElements - polygonShapePath.pathElements = newPolygonElements - lineBorderShapePath.pathElements = newLineElements - polygonRingBorderPath.pathElements = newPolygonElements + lineShapeItems = []; lineBorderShapeItems = []; polygonShapeItems = []; polygonRingBorderShapeItems = []; + + lineShapePath.pathElements = [ componentMoveTo.createObject( lineShapePath ) ] + lineBorderShapePath.pathElements = [ componentMoveTo.createObject( lineBorderShapePath ) ] + polygonShapePath.pathElements = [ componentMoveTo.createObject( polygonShapePath ) ] + polygonRingBorderPath.pathElements = [ componentMoveTo.createObject( polygonRingBorderPath ) ] return; } @@ -132,35 +135,39 @@ Item { refTransformOffsetY = mapTransformOffsetY refTransformScale = mapTransformScale - let data = __inputUtils.extractGeometryCoordinates( highlight.geometry ) + let data = geometryData let newMarkerItems = [] let newLineElements = [] + let newLineBorderElements = [] let newPolygonElements = [] + let newPolygonRingBorderElements = [] - let geometryType = data[0] // type of geometry - 0: point, 1: linestring, 2: polygon - let dataStartIndex = ( geometryType === 0 ? 1 : 2 ) // point data starts from index 1, others from index 2 + let geometryType = data[0] + let dataStartIndex = ( geometryType === 0 ? 1 : 2 ) if ( data.length > dataStartIndex ) { if ( geometryType === 0 ) // point { + let coords = [] if ( data.length === 3 ) - { - newMarkerItems.push( componentMarker.createObject( highlight, { "posX": data[dataStartIndex], "posY": data[dataStartIndex + 1] } ) ) - } + coords.push( { x: data[dataStartIndex], y: data[dataStartIndex + 1] } ) else + for ( let it = dataStartIndex; it < data.length; it += 3 ) + coords.push( { x: data[it], y: data[it + 1] } ) + + for ( let c = 0; c < coords.length; c++ ) { - let it = 0; - // this is multipoint [0, x1, y1, 0, x2, y2, 0, x3, y3, 0,..] - for ( it = dataStartIndex; it < data.length; it += 3 ) - { - newMarkerItems.push( componentMarker.createObject( highlight, { - "posX": data[it], - "posY": data[it + 1] - } ) ) + if ( c < markerItems.length ) { + markerItems[c].posX = coords[c].x + markerItems[c].posY = coords[c].y + newMarkerItems.push( markerItems[c] ) + } else { + newMarkerItems.push( componentMarker.createObject( highlight, { "posX": coords[c].x, "posY": coords[c].y } ) ) } } + for ( let j = coords.length; j < markerItems.length; j++ ) markerItems[j].destroy() } else // line or polygon { @@ -174,10 +181,8 @@ Item { } ) ) } - let objOwner = ( geometryType === 1 ? lineShapePath : polygonShapePath ) - let elements = ( geometryType === 1 ? newLineElements : newPolygonElements ) - - // Create (multi) geometry for the highlight + let linePartIdx = 0 + let polygonPartIdx = 0 let i = 0 let k = 0 while ( i < data.length ) @@ -202,54 +207,88 @@ Item { } i = k - elements.push( componentPathPolyline.createObject( objOwner, { path: newPath } ) ) + if ( geometryType === 1 ) + { + let lineEl = linePartIdx < lineShapeItems.length ? lineShapeItems[ linePartIdx ] : componentPathPolyline.createObject( lineShapePath, { path: [] } ) + let borderEl = linePartIdx < lineBorderShapeItems.length ? lineBorderShapeItems[ linePartIdx ] : componentPathPolyline.createObject( lineBorderShapePath, { path: [] } ) + lineEl.path = newPath + borderEl.path = newPath + newLineElements.push( lineEl ) + newLineBorderElements.push( borderEl ) + linePartIdx++ + } + else + { + let polyEl = polygonPartIdx < polygonShapeItems.length ? polygonShapeItems[ polygonPartIdx ] : componentPathPolyline.createObject( polygonShapePath, { path: [] } ) + let ringEl = polygonPartIdx < polygonRingBorderShapeItems.length ? polygonRingBorderShapeItems[ polygonPartIdx ] : componentPathPolyline.createObject( polygonRingBorderPath, { path: [] } ) + polyEl.path = newPath + ringEl.path = newPath + newPolygonElements.push( polyEl ) + newPolygonRingBorderElements.push( ringEl ) + polygonPartIdx++ + } } + + for ( let j = newLineElements.length; j < lineShapeItems.length; j++ ) lineShapeItems[ j ].destroy() + for ( let j = newLineBorderElements.length; j < lineBorderShapeItems.length; j++ ) lineBorderShapeItems[ j ].destroy() + for ( let j = newPolygonElements.length; j < polygonShapeItems.length; j++ ) polygonShapeItems[ j ].destroy() + for ( let j = newPolygonRingBorderElements.length; j < polygonRingBorderShapeItems.length; j++ ) polygonRingBorderShapeItems[ j ].destroy() } } - // trigger repaint for empty geometries - markerItems = markerItems.map( function (marker) { return marker.destroy() } ) - if ( newLineElements.length === 0 ) + if ( geometryType !== 0 ) markerItems.forEach( function (m) { if (m) m.destroy() } ) + if ( geometryType !== 1 ) { lineShapeItems.forEach( function (el) { if (el) el.destroy() } ); lineBorderShapeItems.forEach( function (el) { if (el) el.destroy() } ) } + if ( geometryType !== 2 ) { polygonShapeItems.forEach( function (el) { if (el) el.destroy() } ); polygonRingBorderShapeItems.forEach( function (el) { if (el) el.destroy() } ) } + + markerItems = newMarkerItems + + lineShapeItems = newLineElements.slice() + lineBorderShapeItems = newLineBorderElements.slice() + polygonShapeItems = newPolygonElements.slice() + polygonRingBorderShapeItems = newPolygonRingBorderElements.slice() + + if ( newLineElements.length === 0 ) { newLineElements.push( componentMoveTo.createObject( lineShapePath ) ) - if ( newPolygonElements.length === 0 ) + newLineBorderElements.push( componentMoveTo.createObject( lineBorderShapePath ) ) + } + if ( newPolygonElements.length === 0 ) { newPolygonElements.push( componentMoveTo.createObject( polygonShapePath ) ) + newPolygonRingBorderElements.push( componentMoveTo.createObject( polygonRingBorderPath ) ) + } - markerItems = newMarkerItems - polygonShapePath.pathElements = newPolygonElements - polygonRingBorderPath.pathElements = newPolygonElements lineShapePath.pathElements = newLineElements - lineBorderShapePath.pathElements = newLineElements + lineBorderShapePath.pathElements = newLineBorderElements + polygonShapePath.pathElements = newPolygonElements + polygonRingBorderPath.pathElements = newPolygonRingBorderElements + + shape.update() } - // keeps list of currently displayed marker items (an internal property) property var markerItems: [] - - // enable anti-aliasing to make the higlight look nicer - // https://stackoverflow.com/questions/48895449/how-do-i-enable-antialiasing-on-qml-shapes - layer.enabled: true - layer.samples: 4 + property var lineShapeItems: [] + property var lineBorderShapeItems: [] + property var polygonShapeItems: [] + property var polygonRingBorderShapeItems: [] Component { id: componentMarker Item { + id: markerItem property real posX: 0 property real posY: 0 - property int markerType: highlight.markerType - x: ( posX * highlight.mapTransformScale + highlight.mapTransformOffsetX * highlight.mapTransformScale ) / displayDevicePixelRatio - ( highlight.markerWidth / 2 ) - y: ( posY * -highlight.mapTransformScale + highlight.mapTransformOffsetY * -highlight.mapTransformScale ) / displayDevicePixelRatio - highlight.markerHeight + x: ( posX * highlight.mapTransformScale + highlight.mapTransformOffsetX * highlight.mapTransformScale ) / highlight.displayDevicePixelRatio - ( highlight.markerWidth / 2 ) + y: ( posY * -highlight.mapTransformScale + highlight.mapTransformOffsetY * -highlight.mapTransformScale ) / highlight.displayDevicePixelRatio - highlight.markerHeight width: highlight.markerWidth height: highlight.markerHeight Rectangle { - width: internal.markerSize height: width - - visible: markerType === MMHighlight.MarkerTypes.Circle + visible: markerItem.markerType === 0 anchors { bottom: parent.bottom @@ -264,9 +303,8 @@ Item { } Image { - visible: markerType === MMHighlight.MarkerTypes.Image - - source: __style.mapPinImage + visible: markerItem.markerType !== 0 + source: highlight.markerImageSource sourceSize.width: parent.width sourceSize.height: parent.height } diff --git a/app/qml/map/MMMapController.qml b/app/qml/map/MMMapController.qml index f3b3601ee..319290a69 100644 --- a/app/qml/map/MMMapController.qml +++ b/app/qml/map/MMMapController.qml @@ -40,6 +40,8 @@ Item { measurementToolsLoader.active ? measurementToolsLoader.item.mapTool : null } + property var highlightedGeomRef: null + property MM.PositionTrackingManager trackingManager: tracking.item?.manager ?? null property MM.MultiEditManager multiEditManager: multiEditLoader.item?.manager ?? null @@ -407,7 +409,7 @@ Item { lineWidth: MMHighlight.LineWidths.Narrow mapSettings: mapCanvas.mapSettings - geometry: trackingHighlight.highlightGeometry + geometryData: trackingHighlight.highlightGeometryData } Component.onCompleted: { @@ -1043,7 +1045,7 @@ Item { markerType: MMHighlight.MarkerTypes.Circle mapSettings: mapCanvas.mapSettings - geometry: multiEditManager.geometry + geometryData: __inputUtils.extractGeometryToQml( multiEditManager.geometry ) } } } @@ -1080,7 +1082,7 @@ Item { lineWidth: sketchingController.eraserActive ? MMHighlight.LineWidths.Narrow : MMHighlight.LineWidths.Normal mapSettings: mapCanvas.mapSettings - geometry: sketchingController.highlightGeometry + geometryData: sketchingController.highlightGeometry } } } @@ -1380,21 +1382,24 @@ Item { } function jumpToHighlighted( mapOffset ) { - if ( identifyHighlight.geometry === null ) + if ( !highlightedGeomRef ) return - let screenPt = __inputUtils.relevantGeometryCenterToScreenCoordinates( identifyHighlight.geometry, mapCanvas.mapSettings ) - + let screenPt = __inputUtils.relevantGeometryCenterToScreenCoordinates( highlightedGeomRef.geom, mapCanvas.mapSettings ) screenPt.y += mapOffset / 2 mapCanvas.jumpTo( screenPt ) } function highlightPair( pair ) { let geometry = __inputUtils.extractGeometry( pair ) - identifyHighlight.geometry = __inputUtils.transformGeometryToMapWithLayer( geometry, pair.layer, mapCanvas.mapSettings ) + let mapGeom = __inputUtils.transformGeometryToMapWithLayer( geometry, pair.layer, mapCanvas.mapSettings ) + + identifyHighlight.geometryData = __inputUtils.extractGeometryToQml( mapGeom ) + highlightedGeomRef = { geom: mapGeom } } function hideHighlight() { - identifyHighlight.geometry = null + identifyHighlight.geometryData = [] + highlightedGeomRef = null updatePosition() } @@ -1441,7 +1446,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 ( highlightedGeomRef ) { break } @@ -1476,7 +1481,8 @@ 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.geometryData = [] + highlightedGeomRef = null } function setTracking( shouldTrack ) { diff --git a/app/qml/map/MMMeasurementTools.qml b/app/qml/map/MMMeasurementTools.qml index e4a785961..07bd84c59 100644 --- a/app/qml/map/MMMeasurementTools.qml +++ b/app/qml/map/MMMeasurementTools.qml @@ -55,7 +55,10 @@ Item { lineWidth: MMHighlight.LineWidths.Narrow mapSettings: root.map.mapSettings - geometry: guidelineController.guidelineGeometry + geometryData: { + let trigger = guidelineController.crosshairPosition; // Forces binding evaluation + return __inputUtils.extractGeometryCoordinates( guidelineController.guidelineGeometry ); + } } MMHighlight { @@ -69,7 +72,7 @@ Item { lineWidth: MMHighlight.LineWidths.Narrow mapSettings: root.map.mapSettings - geometry: mapTool.recordedGeometry + geometryData: __inputUtils.extractGeometryCoordinates( mapTool.recordedGeometry ) } MMHighlight { @@ -79,7 +82,7 @@ Item { width: root.map.width mapSettings: root.map.mapSettings - geometry: mapTool.existingVertices + geometryData: __inputUtils.extractGeometryCoordinates( mapTool.existingVertices ) markerType: MMHighlight.MarkerTypes.Circle markerSize: MMHighlight.MarkerSizes.Bigger diff --git a/app/qml/map/MMRecordingTools.qml b/app/qml/map/MMRecordingTools.qml index da976654c..8dfbe620a 100644 --- a/app/qml/map/MMRecordingTools.qml +++ b/app/qml/map/MMRecordingTools.qml @@ -106,8 +106,10 @@ Item { mapSettings: root.map.mapSettings insertPolicy: mapTool.insertPolicy crosshairPosition: crosshair.screenPoint - realGeometry: __inputUtils.transformGeometryToMapWithLayer( mapTool.recordedGeometry, __activeLayer.vectorLayer, root.map.mapSettings ) - + realGeometry: { + let trigger = mapTool.recordedGeometryData; // Forces binding evaluation + return __inputUtils.transformGeometryToMapWithLayer( mapTool.recordedGeometry, __activeLayer.vectorLayer, root.map.mapSettings ) + } activeVertex: mapTool.activeVertex activePart: mapTool.activePart activeRing: mapTool.activeRing @@ -122,8 +124,12 @@ Item { visible: !__inputUtils.isPointLayer(__activeLayer.vectorLayer) mapSettings: root.map.mapSettings - geometry: __inputUtils.transformGeometryToMapWithLayer( mapTool.recordedGeometry, __activeLayer.vectorLayer, root.map.mapSettings ) - + geometryData: { + let trigger = mapTool.recordedGeometryData; // Forces binding evaluation + return __inputUtils.extractGeometryToQml( + __inputUtils.transformGeometryToMapWithLayer( mapTool.recordedGeometry, __activeLayer.vectorLayer, root.map.mapSettings ) + ); + } lineBorderWidth: 0 } @@ -180,8 +186,12 @@ Item { width: root.map.width mapSettings: root.map.mapSettings - geometry: __inputUtils.transformGeometryToMapWithLayer( mapTool.existingVertices, __activeLayer.vectorLayer, root.map.mapSettings ) - + geometryData: { + let trigger = mapTool.existingVerticesData; // Forces binding evaluation + return __inputUtils.extractGeometryCoordinates( + __inputUtils.transformGeometryToMapWithLayer( mapTool.existingVertices, __activeLayer.vectorLayer, root.map.mapSettings ) + ); + } markerType: MMHighlight.MarkerTypes.Circle markerSize: MMHighlight.MarkerSizes.Bigger } @@ -328,7 +338,12 @@ Item { width: root.map.width mapSettings: root.map.mapSettings - geometry: __inputUtils.transformGeometryToMapWithLayer( mapTool.handles, __activeLayer.vectorLayer, root.map.mapSettings ) + geometryData: { + let trigger = mapTool.handlesData; // Forces binding evaluation + return __inputUtils.extractGeometryCoordinates( + __inputUtils.transformGeometryToMapWithLayer( mapTool.handles, __activeLayer.vectorLayer, root.map.mapSettings ) + ); + } lineStrokeStyle: ShapePath.DashLine lineWidth: MMHighlight.LineWidths.Narrow } @@ -347,8 +362,11 @@ Item { lineStrokeStyle: ShapePath.DashLine mapSettings: root.map.mapSettings - geometry: guidelineController.guidelineGeometry - } + geometryData: { + let trigger = guidelineController.crosshairPosition; // Forces binding evaluation + return __inputUtils.extractGeometryCoordinates( guidelineController.guidelineGeometry ); + } + } } Component { @@ -361,8 +379,12 @@ Item { width: root.map.width mapSettings: root.map.mapSettings - geometry: __inputUtils.transformGeometryToMapWithLayer( mapTool.midPoints, __activeLayer.vectorLayer, root.map.mapSettings ) - + geometryData: { + let trigger = mapTool.midPointsData; // Forces binding evaluation + return __inputUtils.extractGeometryCoordinates( + __inputUtils.transformGeometryToMapWithLayer( mapTool.midPoints, __activeLayer.vectorLayer, root.map.mapSettings ) + ); + } markerType: MMHighlight.MarkerTypes.Circle markerBorderColor: __style.grapeColor } diff --git a/app/qml/map/MMSplittingTools.qml b/app/qml/map/MMSplittingTools.qml index a237a6831..cd33e49c0 100644 --- a/app/qml/map/MMSplittingTools.qml +++ b/app/qml/map/MMSplittingTools.qml @@ -56,7 +56,10 @@ Item { lineWidth: MMHighlight.LineWidths.Narrow mapSettings: root.map.mapSettings - geometry: guidelineController.guidelineGeometry + geometryData: { + let trigger = guidelineController.crosshairPosition; // Forces binding evaluation + return __inputUtils.extractGeometryCoordinates( guidelineController.guidelineGeometry ); + } } MMHighlight { @@ -70,7 +73,12 @@ Item { lineWidth: MMHighlight.LineWidths.Narrow mapSettings: root.map.mapSettings - geometry: __inputUtils.transformGeometryToMapWithLayer( mapTool.recordedGeometry, __activeLayer.vectorLayer, root.map.mapSettings ) + geometryData: { + let trigger = mapTool.recordedGeometryData; + return __inputUtils.extractGeometryCoordinates( + __inputUtils.transformGeometryToMapWithLayer( mapTool.recordedGeometry, __activeLayer.vectorLayer, root.map.mapSettings ) + ); + } } MMCrosshair { diff --git a/app/qml/map/MMStakeoutTools.qml b/app/qml/map/MMStakeoutTools.qml index 530987503..43c0e687b 100644 --- a/app/qml/map/MMStakeoutTools.qml +++ b/app/qml/map/MMStakeoutTools.qml @@ -42,6 +42,7 @@ Item { Component.onCompleted: updateStakeout() function updateStakeout() { - highlight.geometry = __inputUtils.stakeoutGeometry( mapPositioning.mapPosition, target, map.mapSettings ) - } + highlight.geometryData = __inputUtils.extractGeometryCoordinates( + __inputUtils.stakeoutGeometry( mapPositioning.mapPosition, target, map.mapSettings ) + ) } } diff --git a/docs/CMakePresets-Template.json b/docs/CMakePresets-Template.json index 84f5a4627..6ea02b627 100644 --- a/docs/CMakePresets-Template.json +++ b/docs/CMakePresets-Template.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 4, "cmakeMinimumRequired": { "major": 3, - "minor": 21, + "minor": 23, "patch": 0 }, "configurePresets": [ @@ -52,7 +52,7 @@ "QT_ANDROID_SIGN_APK": "Yes" }, "environment": { - "ANDROID_NDK_HOME": "", + "ANDROID_NDK_ROOT": "", "ANDROID_SDK_ROOT": "", "QT_ANDROID_KEYSTORE_PATH": "", @@ -78,7 +78,7 @@ "VCPKG_HOST_TRIPLET": "arm64-osx" }, "environment": { - "PATH": "$penv{PATH}:$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:/Applications/CMake.app/Contents/bin" + "PATH": "$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:$env{HOMEBREW_PREFIX}/bin:$penv{PATH}" } }, { @@ -90,7 +90,7 @@ "VCPKG_HOST_TRIPLET": "x64-osx" }, "environment": { - "PATH": "$penv{PATH}:$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:/Applications/CMake.app/Contents/bin" + "PATH": "$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:$env{HOMEBREW_PREFIX}/bin:$penv{PATH}" } }, { @@ -111,7 +111,7 @@ "IOS": "TRUE" }, "environment": { - "PATH": "$penv{PATH}:$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:/Applications/CMake.app/Contents/bin" + "PATH": "$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:$env{HOMEBREW_PREFIX}/bin:$penv{PATH}" } }, { @@ -126,7 +126,7 @@ "ENABLE_TESTS": "TRUE" }, "environment": { - "PATH": "$penv{PATH}:$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:/Applications/CMake.app/Contents/bin" + "PATH": "$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:$env{HOMEBREW_PREFIX}/bin:$penv{PATH}" } }, { @@ -140,7 +140,7 @@ "VCPKG_TARGET_TRIPLET": "arm64-osx" }, "environment": { - "PATH": "$penv{PATH}:$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:/Applications/CMake.app/Contents/bin" + "PATH": "$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:$env{HOMEBREW_PREFIX}/bin:$penv{PATH}" } }, { @@ -155,7 +155,7 @@ "ENABLE_TESTS": "TRUE" }, "environment": { - "PATH": "$penv{PATH}:$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:/Applications/CMake.app/Contents/bin" + "PATH": "$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:$env{HOMEBREW_PREFIX}/bin:$penv{PATH}" } }, { @@ -169,7 +169,7 @@ "VCPKG_TARGET_TRIPLET": "x64-osx" }, "environment": { - "PATH": "$penv{PATH}:$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:/Applications/CMake.app/Contents/bin" + "PATH": "$env{HOMEBREW_PREFIX}/opt/flex/bin:$env{HOMEBREW_PREFIX}/opt/bison/bin:$env{HOMEBREW_PREFIX}/opt/gettext/bin:$env{HOMEBREW_PREFIX}/bin:$penv{PATH}" } }, { From ee731c9d4de3b12edd3c8fe7893cb3fab6eb6dcf Mon Sep 17 00:00:00 2001 From: Matej Bagar Date: Wed, 20 May 2026 17:37:30 +0200 Subject: [PATCH 2/2] Update android install docs --- INSTALL.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/INSTALL.md b/INSTALL.md index 534b6adc3..0aaf12476 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -229,6 +229,7 @@ For building ABIs see https://www.qt.io/blog/android-multi-abi-builds-are-back binary vcpkg cache. ``` + export ANDROID_NDK_HOME=/home//android/ndk/ export ANDROID_NDK_ROOT=/home//android/ndk/ export ANDROID_SDK_ROOT=/home//android export QT_ANDROID_KEYSTORE_ALIAS= @@ -262,6 +263,7 @@ For building ABIs see https://www.qt.io/blog/android-multi-abi-builds-are-back ``` PATH=+/Users//Projects/quick/build/vcpkg ANDROID_NDK_HOME=/Users//android/ndk/ + ANDROID_NDK_ROOT=/Users//android/ndk/ ANDROID_SDK_ROOT=/Users//android QT_ANDROID_KEYSTORE_ALIAS= QT_ANDROID_KEYSTORE_KEY_PASS= @@ -345,6 +347,7 @@ For building ABIs see https://www.qt.io/blog/android-multi-abi-builds-are-back ``` export ANDROID_NDK_HOME=/Users//android/ndk/ + export ANDROID_NDK_ROOT=/Users//android/ndk/ export ANDROID_SDK_ROOT=/Users//android export QT_ANDROID_KEYSTORE_ALIAS= export QT_ANDROID_KEYSTORE_KEY_PASS= @@ -379,6 +382,7 @@ For building ABIs see https://www.qt.io/blog/android-multi-abi-builds-are-back PATH=+/opt/homebrew/Cellar/bison/3.8.2/bin PATH=+/Users//Projects/quick/build/vcpkg ANDROID_NDK_HOME=/Users//android/ndk/ + ANDROID_NDK_ROOT=/Users//android/ndk/ ANDROID_SDK_ROOT=/Users//android QT_ANDROID_KEYSTORE_ALIAS= QT_ANDROID_KEYSTORE_KEY_PASS=