diff --git a/app/inpututils.cpp b/app/inpututils.cpp index b5573eb39..732a9d8e4 100644 --- a/app/inpututils.cpp +++ b/app/inpututils.cpp @@ -321,34 +321,84 @@ void InputUtils::setExtentToGeom( const QgsGeometry &geom, InputMapSettings *map mapSettings->setExtent( currentExtent ); } -QPointF InputUtils::relevantGeometryCenterToScreenCoordinates( const QgsGeometry &geom, InputMapSettings *mapSettings ) +QPointF InputUtils::whereToPanWhenIdentifying( const QgsGeometry &geom, InputMapSettings *mapSettings, double bottomOffset, const QPointF &identifyLocation ) { - QPointF screenPoint; - QgsPoint target; - if ( !mapSettings || geom.isNull() || !geom.constGet() ) - return screenPoint; + QgsRectangle effectiveExtent( mapSettings->visibleExtent() ); + + // canvas size in logical pixels; bottomOffset is in logical pixels too and + // screenToCoordinate() expects logical pixel coordinates + const double canvasWidth = mapSettings->outputSize().width() / mapSettings->devicePixelRatio(); + const double canvasHeight = mapSettings->outputSize().height() / mapSettings->devicePixelRatio(); + const QPointF bottomPoint( canvasWidth / 2.0, canvasHeight - bottomOffset ); - const QgsRectangle currentExtent = mapSettings->mapSettings().visibleExtent(); + const QgsPoint bottomPointMap = mapSettings->screenToCoordinate( bottomPoint ); - // Cut the geometry to current extent - const QgsGeometry currentExtentAsGeom = QgsGeometry::fromRect( currentExtent ); - const QgsGeometry intersectedGeom = geom.intersection( currentExtentAsGeom ); + effectiveExtent.setYMinimum( bottomPointMap.y() ); - if ( !intersectedGeom.isEmpty() ) + // Now we calculate a safeEffectiveExtent, slightly smaller, so that we don't allow geometries too close to the screen borders + constexpr double EXTENT_BUFFER_SCALE = 0.82; + const QgsRectangle safeEffectiveExtent( effectiveExtent.scaled( EXTENT_BUFFER_SCALE ) ); + + QPointF screenPoint; + QgsPoint target; + if ( safeEffectiveExtent.contains( geom.boundingBox() ) ) { - target = QgsPoint( intersectedGeom.boundingBox().center() ); + // If the whole geometry is visible, don't move map + return { std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() }; } - else + else if ( effectiveExtent.width() >= geom.boundingBox().width() && + effectiveExtent.height() >= geom.boundingBox().height() ) { - // The geometry is outside the current viewed extent - setExtentToGeom( geom, mapSettings ); + // if the whole geometry would fit without changing scale, center it target = QgsPoint( geom.boundingBox().center() ); } + else + { + // the geometry is big, let's pan to the point the user clicked on the map + target = QgsPoint( identifyLocation ); + } screenPoint = mapSettings->coordinateToScreen( target ); + screenPoint.ry() += bottomOffset / 2; + return screenPoint; } +QgsRectangle InputUtils::drawerCompensatedExtent( const QgsGeometry &geom, InputMapSettings *mapSettings, double bottomOffset ) +{ + const QgsRectangle bbox = geom.boundingBox(); + QgsRectangle currentExtent = mapSettings->mapSettings().visibleExtent(); + + // canvas size in logical pixels; the bottom bottomOffset of the canvas is covered by another + // component (e.g. preview drawer), so we center the geometry in the remaining visible part + const double canvasWidth = mapSettings->outputSize().width() / mapSettings->devicePixelRatio(); + const double canvasHeight = mapSettings->outputSize().height() / mapSettings->devicePixelRatio(); + const double visibleHeight = std::max( canvasHeight - bottomOffset, 1.0 ); + + if ( bbox.isEmpty() ) // Deal with an empty bouding box e.g : a point + { + const QgsPointXY center( bbox.center().x(), bbox.center().y() - bottomOffset / 2.0 * mapSettings->mapUnitsPerPoint() ); + const QgsVector offset = currentExtent.center() - center; + currentExtent -= offset; + } + else + { + QgsRectangle paddedBbox = bbox; + + // Add a offset to encompass handles etc.. + // This number is based on what feel confortable for the user + constexpr double SCALE_FACTOR = 1.18; + paddedBbox.scale( SCALE_FACTOR ); + + const double mapUnitsPerPoint = std::max( paddedBbox.width() / canvasWidth, paddedBbox.height() / visibleHeight ); + const double cx = bbox.center().x(); + const double cy = bbox.center().y() - bottomOffset / 2.0 * mapUnitsPerPoint; + currentExtent = QgsRectangle( cx - canvasWidth * mapUnitsPerPoint / 2, cy - canvasHeight * mapUnitsPerPoint / 2, + cx + canvasWidth * mapUnitsPerPoint / 2, cy + canvasHeight * mapUnitsPerPoint / 2 ); + } + return currentExtent; +} + double InputUtils::convertCoordinateString( const QString &rationalValue ) { QStringList values = rationalValue.split( "," ); @@ -2146,6 +2196,11 @@ QString InputUtils::getUniqueString( const QString &newString, const QStringList return uniqueString; } +QgsRectangle InputUtils::extentFromMinMax( double xMin, double yMin, double xMax, double yMax ) +{ + return QgsRectangle( xMin, yMin, xMax, yMax ); +} + bool InputUtils::rescaleImage( const QString &path, QgsProject *activeProject ) { int quality = activeProject->readNumEntry( QStringLiteral( "Mergin" ), QStringLiteral( "PhotoQuality" ), 0 ); diff --git a/app/inpututils.h b/app/inpututils.h index 122661a0f..b14e91cd7 100644 --- a/app/inpututils.h +++ b/app/inpututils.h @@ -92,13 +92,29 @@ class InputUtils: public QObject Q_INVOKABLE void setExtentToGeom( const QgsGeometry &geom, InputMapSettings *mapSettings ); /** - * Returns the center point of the \a geom currently displayed on screen + * Returns the screen point where the map should jump to when identifying features. + * If the map should not pan, a QPointF with NaNs is returned. * - * Fall back to \see setExtentToGeom in case we don't find any geometry relevant + * \param geom The geometry identified + * \param mapSettings Current map settings + * \param bottomOffset Pixel size (height) of drawer that may be covering the map canvas, eg preview panel + * \param identifyLocation The map coords of the location the user clicked for identification * * Nota Bene: Assume geometry and map canvas CRS are the same */ - Q_INVOKABLE QPointF relevantGeometryCenterToScreenCoordinates( const QgsGeometry &geom, InputMapSettings *mapSettings ); + Q_INVOKABLE static QPointF whereToPanWhenIdentifying( const QgsGeometry &geom, InputMapSettings *mapSettings, double bottomOffset, const QPointF &identifyLocation ); + + /** + * Returns the extent that the map needs to zoom to so that the \a geom fits (with a safe buffer) the visible part + * of the map canvas when the canvas lower part is covered by a drawer of \a bottomOffset pixels high. + * + * \param geom The geometry identified + * \param mapSettings Current map settings + * \param bottomOffset Pixel size (height) of drawer that may be covering the map canvas, eg preview panel + * + * Nota Bene: Assume geometry and map canvas CRS are the same + */ + Q_INVOKABLE static QgsRectangle drawerCompensatedExtent( const QgsGeometry &geom, InputMapSettings *mapSettings, double bottomOffset ); // utility functions to extract information from map settings // (in theory this data should be directly available from .MapTransform @@ -681,6 +697,9 @@ class InputUtils: public QObject */ static QString getUniqueString( const QString &newString, const QStringList &existingStrings ); + //! Trivial constructor for QgsRectangle since the actual constructor is not invokable + Q_INVOKABLE static QgsRectangle extentFromMinMax( double xMin, double yMin, double xMax, double yMax ); + public slots: void onQgsLogMessageReceived( const QString &message, const QString &tag, Qgis::MessageLevel level ); diff --git a/app/qml/form/MMFormController.qml b/app/qml/form/MMFormController.qml index 6b098ac52..56ea6a3f9 100644 --- a/app/qml/form/MMFormController.qml +++ b/app/qml/form/MMFormController.qml @@ -37,7 +37,7 @@ Item { property bool layerIsReadOnly: featureLayerPair?.layer?.readOnly ?? false property bool layerIsSpatial: featureLayerPair ? __inputUtils.isSpatialLayer( featureLayerPair.layer ) : false - property real drawerHeight: drawer.height + property real previewPanelHeight: previewPanel.implicitHeight signal closed() signal saveRequested() @@ -46,7 +46,7 @@ Item { signal createLinkedFeature( var targetLayer, var parentPair ) signal multiSelectFeature( var feature ) signal stakeoutFeature( var feature ) - signal previewPanelChanged( var panelHeight ) + signal previewPanelChanged() function openDrawer() { root.panelState = "form" @@ -142,11 +142,6 @@ Item { edge: Qt.BottomEdge closePolicy: Popup.CloseOnEscape // prevents the drawer closing while moving canvas - onOpened: { - if ( panelState === "preview" ) - previewPanelChanged( previewPanel.implicitHeight ) - } - onClosed: { if ( statesManager.state !== "hidden" ) statesManager.state = "closed" @@ -179,8 +174,9 @@ Item { onCloseClicked: drawer.close() - onImplicitHeightChanged: { - previewPanelChanged( previewPanel.implicitHeight ) + onDoneLoading: { + if ( root.panelState === "preview" ) + previewPanelChanged() } } @@ -243,9 +239,4 @@ Item { } } } - - onFeatureLayerPairChanged: { - if ( panelState === "preview" ) - previewPanelChanged( previewPanel.implicitHeight ) - } } diff --git a/app/qml/form/MMFormStackController.qml b/app/qml/form/MMFormStackController.qml index 134b796e1..384079e0a 100644 --- a/app/qml/form/MMFormStackController.qml +++ b/app/qml/form/MMFormStackController.qml @@ -39,8 +39,8 @@ Item { return root.height // form is the highest always } else if ( form.panelState === "preview" ) { - if ( form.drawerHeight > maxHeight ) { - maxHeight = form.drawerHeight + if ( form.previewPanelHeight > maxHeight ) { + maxHeight = form.previewPanelHeight } } } @@ -54,7 +54,7 @@ Item { signal createLinkedFeatureRequested( var targetLayer, var parentPair ) signal multiSelectFeature( var feature ) signal stakeoutFeature( var feature ) - signal previewPanelChanged( var panelHeight ) + signal previewPanelChanged() function openForm( pair, formState, panelState ) { if ( formsStack.depth === 0 ) @@ -308,8 +308,8 @@ Item { formsStack.syncWhenFormCloses = true } - onPreviewPanelChanged: function( panelHeight ) { - root.previewPanelChanged( panelHeight ) + onPreviewPanelChanged: { + root.previewPanelChanged() } onEditGeometry: function( pair ) { diff --git a/app/qml/form/MMPreviewDrawer.qml b/app/qml/form/MMPreviewDrawer.qml index 5c0891313..789a27932 100644 --- a/app/qml/form/MMPreviewDrawer.qml +++ b/app/qml/form/MMPreviewDrawer.qml @@ -35,6 +35,8 @@ Item { signal selectMoreClicked( var feature ) signal stakeoutClicked( var feature ) signal closeClicked() + // this signal fires once after content-layout has settled when the feature changes + signal doneLoading() MouseArea { anchors.fill: parent @@ -271,6 +273,23 @@ Item { } } + Timer { + id: heightDebounceTimer + interval: 100 + onTriggered: root.doneLoading() + } + + onImplicitHeightChanged: { + heightDebounceTimer.restart() + } + + Connections { + target: controller + function onFeatureLayerPairChanged() { + heightDebounceTimer.start() + } + } + QtObject { id: internal diff --git a/app/qml/main.qml b/app/qml/main.qml index f1f75837a..0acdea64e 100644 --- a/app/qml/main.qml +++ b/app/qml/main.qml @@ -104,15 +104,16 @@ ApplicationWindow { projDialog.open() } - function identifyFeature( pair ) { - let hasNullGeometry = pair.feature.geometry.isNull + function identifyFeature( pair, point = Qt.point(NaN, NaN) ) { + map.identifyLocation = point - if ( hasNullGeometry ) { + let skipPreview = __inputUtils.isEmptyGeometry( pair.feature.geometry ) + if ( skipPreview ) { formsStackManager.openForm( pair, "readOnly", "form" ) } else if ( pair.valid ) { map.highlightPair( pair ) - formsStackManager.openForm( pair, "readOnly", "preview") + formsStackManager.openForm( pair, "readOnly", "preview" ) } } @@ -180,8 +181,8 @@ ApplicationWindow { return 0 } - onFeatureIdentified: function( pair ) { - formsStackManager.openForm( pair, "readOnly", "preview" ); + onFeatureIdentified: function( pair, point ) { + window.identifyFeature( pair, point ) } onFeaturesIdentified: function( pairs ) { @@ -190,6 +191,7 @@ ApplicationWindow { } onNothingIdentified: { + map.hideHighlight() formsStackManager.closeDrawer() } @@ -860,8 +862,8 @@ ApplicationWindow { closeDrawer() } - onPreviewPanelChanged: function( panelHeight ) { - map.jumpToHighlighted( panelHeight - mapToolbar.height ) + onPreviewPanelChanged: { + map.jumpToHighlighted() } } @@ -957,10 +959,8 @@ ApplicationWindow { secondaryText: model.LayerName leftContent: MMIcon { source: model.LayerIcon } onClicked: { - let pair = model.FeaturePair featurePairSelection.close() - map.highlightPair( pair ) - formsStackManager.openForm( pair, "readOnly", "preview" ); + window.identifyFeature( model.FeaturePair ); } } diff --git a/app/qml/map/MMMapCanvas.qml b/app/qml/map/MMMapCanvas.qml index 262a74685..6e29f90ce 100644 --- a/app/qml/map/MMMapCanvas.qml +++ b/app/qml/map/MMMapCanvas.qml @@ -66,6 +66,29 @@ Item { rendererPrivate.unfreeze('jumpTo') } + function jumpToExtent( newExtent ) + { + rendererPrivate.freeze('jumpTo') + + let oldExtent = mapRenderer.mapSettings.visibleExtent + + // Disable animation until initial position is set + jumpExtentAnimator.enabled = false + jumpExtentAnimator.startMinX = oldExtent.xMinimum + jumpExtentAnimator.startMinY = oldExtent.yMinimum + jumpExtentAnimator.startMaxX = oldExtent.xMaximum + jumpExtentAnimator.startMaxY = oldExtent.yMaximum + jumpExtentAnimator.endMinX = newExtent.xMinimum + jumpExtentAnimator.endMinY = newExtent.yMinimum + jumpExtentAnimator.endMaxX = newExtent.xMaximum + jumpExtentAnimator.endMaxY = newExtent.yMaximum + + jumpExtentAnimator.percentage = 0 + jumpExtentAnimator.enabled = true + jumpExtentAnimator.percentage = 100 + rendererPrivate.unfreeze('jumpTo') + } + function zoom( center, scale ) { mapRenderer.zoom( center, scale ) @@ -105,6 +128,39 @@ Item { } } + Item { + id: jumpExtentAnimator + + property double startMinX + property double startMinY + property double startMaxX + property double startMaxY + property double endMinX + property double endMinY + property double endMaxX + property double endMaxY + property double percentage: 0 + + Behavior on percentage { + NumberAnimation { + easing.type: Easing.OutQuart + duration: 500 + } + enabled: jumpExtentAnimator.enabled + } + + onPercentageChanged: { + if ( enabled ) { + let tmpMinX = startMinX - percentage * 0.01 * ( startMinX - endMinX ) + let tmpMinY = startMinY - percentage * 0.01 * ( startMinY - endMinY ) + let tmpMaxX = startMaxX - percentage * 0.01 * ( startMaxX - endMaxX ) + let tmpMaxY = startMaxY - percentage * 0.01 * ( startMaxY - endMaxY ) + + mapRenderer.mapSettings.extent = __inputUtils.extentFromMinMax( tmpMinX, tmpMinY, tmpMaxX, tmpMaxY ) + } + } + } + MM.MapCanvasMap { id: mapRenderer diff --git a/app/qml/map/MMMapController.qml b/app/qml/map/MMMapController.qml index be9f06451..2c6839176 100644 --- a/app/qml/map/MMMapController.qml +++ b/app/qml/map/MMMapController.qml @@ -46,7 +46,10 @@ Item { property MM.MapSketchingController sketchingController: sketchesLoader.item?.controller ?? null - signal featureIdentified( var pair ) + // Holds the map coordinates of the point the user identified. NaN if identify was triggered from list of features + property point identifyLocation: Qt.point(NaN, NaN) + + signal featureIdentified( var pair, var point ) signal featuresIdentified( var pairs ) signal nothingIdentified() @@ -261,12 +264,11 @@ Item { } else if ( pair.valid ) // root.state === "view" { - root.highlightPair( pair ) - root.featureIdentified( pair ) + let mapPoint = mapCanvas.mapSettings.screenToCoordinate( screenPoint ) + root.featureIdentified( pair, mapPoint ) } else { - root.hideHighlight() root.nothingIdentified() } } @@ -1419,13 +1421,25 @@ Item { __inputUtils.setExtentToFeature( pair, mapCanvas.mapSettings ) } - function jumpToHighlighted( mapOffset ) { - if ( identifyHighlight.geometry.isNull ) + function jumpToHighlighted() { + if ( __inputUtils.isEmptyGeometry( identifyHighlight.geometry ) ) return - let screenPt = __inputUtils.relevantGeometryCenterToScreenCoordinates( identifyHighlight.geometry, mapCanvas.mapSettings ) - screenPt.y += mapOffset / 2 - mapCanvas.jumpTo( screenPt ) + if ( !isNaN( root.identifyLocation.x ) && !isNaN( root.identifyLocation.y ) ) + { + // when identifying with a point on the map, we preserve scale and may only pan + let screenPt = __inputUtils.whereToPanWhenIdentifying( identifyHighlight.geometry, mapCanvas.mapSettings, root.mapExtentOffset, root.identifyLocation ) + if ( isNaN( screenPt.x ) || isNaN( screenPt.y ) ) + return + + mapCanvas.jumpTo( screenPt ) + } + else + { + // when identifying by picking from feature list, we zoom to the geometry + let extent = __inputUtils.drawerCompensatedExtent( identifyHighlight.geometry, mapCanvas.mapSettings, root.mapExtentOffset ) + mapCanvas.jumpToExtent( extent ) + } } function highlightPair( pair ) { diff --git a/app/test/testutils.h b/app/test/testutils.h index cd906ef4e..e98ebb2d0 100644 --- a/app/test/testutils.h +++ b/app/test/testutils.h @@ -100,7 +100,7 @@ namespace TestUtils #define COMPARENEAR(actual, expected, epsilon) \ do {\ - if (!QTest::compare_helper((qAbs(actual - expected) <= epsilon), \ + if (!QTest::compare_helper((qAbs((actual) - (expected)) <= (epsilon)), \ QString{"Compared values are not the same in respect to epsilon %1"} \ .arg(epsilon).toLocal8Bit().constData(), \ QTest::toString(actual), \ diff --git a/app/test/testutilsfunctions.cpp b/app/test/testutilsfunctions.cpp index 7ac5d6302..84bb49e1b 100644 --- a/app/test/testutilsfunctions.cpp +++ b/app/test/testutilsfunctions.cpp @@ -473,6 +473,72 @@ void TestUtilsFunctions::testStakeoutPathExtent() } } +void TestUtilsFunctions::testWhereToPanWhenIdentifying() +{ + // bottomOffset and the returned screen point are in logical pixels, + // so the result must not depend on the device pixel ratio + const QVector dprs = { 1.0, 2.0 }; + for ( const qreal dpr : dprs ) + { + InputMapSettings ms; + ms.setDestinationCrs( QgsCoordinateReferenceSystem::fromEpsgId( 4326 ) ); + ms.setDevicePixelRatio( dpr ); + ms.setOutputSize( QSize( 400, 620 ) ); + ms.setExtent( QgsRectangle( 0, 0, 40, 62 ) ); // 0.1 map units per logical pixel + + const double bottomOffset = 200; // drawer covers screen y in [420, 620] ~ map y in [0, 20] + + // point hidden under the drawer -> map should pan so that the point + // ends up centered in the unobstructed part of the canvas + const QgsGeometry hiddenPoint = QgsGeometry::fromPointXY( QgsPointXY( 20, 10 ) ); + QPointF pan = mUtils->whereToPanWhenIdentifying( hiddenPoint, &ms, bottomOffset, QPointF( 20, 10 ) ); + COMPARENEAR( pan.x(), 200.0, 1e-4 ); + COMPARENEAR( pan.y(), 620.0, 1e-4 ); + + // point well inside the unobstructed area -> no pan needed + const QgsGeometry visiblePoint = QgsGeometry::fromPointXY( QgsPointXY( 20, 40 ) ); + pan = mUtils->whereToPanWhenIdentifying( visiblePoint, &ms, bottomOffset, QPointF( 20, 40 ) ); + QVERIFY( std::isnan( pan.x() ) ); + QVERIFY( std::isnan( pan.y() ) ); + } +} + +void TestUtilsFunctions::testDrawerCompensatedExtent() +{ + // bottomOffset is in logical pixels, so the result must not depend on the device pixel ratio + const QVector dprs = { 1.0, 2.0 }; + for ( const qreal dpr : dprs ) + { + InputMapSettings ms; + ms.setDestinationCrs( QgsCoordinateReferenceSystem::fromEpsgId( 4326 ) ); + ms.setDevicePixelRatio( dpr ); + ms.setOutputSize( QSize( 400, 620 ) ); + ms.setExtent( QgsRectangle( 0, 0, 40, 62 ) ); // 0.1 map units per logical pixel + + const double bottomOffset = 200; // drawer covers screen y in [420, 620] + + // point geometry -> scale is kept, center shifts down by bottomOffset / 2 + // in screen space so the point is centered in the unobstructed part + const QgsGeometry point = QgsGeometry::fromPointXY( QgsPointXY( 20, 10 ) ); + QgsRectangle extent = mUtils->drawerCompensatedExtent( point, &ms, bottomOffset ); + COMPARENEAR( extent.width(), 40.0, 1e-4 ); + COMPARENEAR( extent.height(), 62.0, 1e-4 ); + COMPARENEAR( extent.center().x(), 20.0, 1e-4 ); + COMPARENEAR( extent.center().y(), 0.0, 1e-4 ); + + // non-empty bounding box -> zoom so the padded bbox fits the part of the + // canvas not covered by the drawer, centered in it + const QgsGeometry line = QgsGeometry::fromPolylineXY( { QgsPointXY( 10, 10 ), QgsPointXY( 30, 20 ) } ); + extent = mUtils->drawerCompensatedExtent( line, &ms, bottomOffset ); + + // padded bbox is 23.6 x 11.8 -> 0.059 map units per logical pixel to fit 400 x 420 + COMPARENEAR( extent.width(), 400 * 0.059, 1e-4 ); + COMPARENEAR( extent.height(), 620 * 0.059, 1e-4 ); + COMPARENEAR( extent.center().x(), 20.0, 1e-4 ); + COMPARENEAR( extent.center().y(), 15.0 - bottomOffset / 2.0 * 0.059, 1e-4 ); + } +} + void TestUtilsFunctions::testDistanceBetweenGpsAndFeature() { QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromEpsgId( 4326 ); @@ -954,23 +1020,6 @@ void TestUtilsFunctions::testFormatAreaInProjectUnit() QVERIFY( area2str == "1.7 ac" ); } -void TestUtilsFunctions::testRelevantGeometryCenterToScreenCoordinates() -{ - InputMapSettings ms; - ms.setDestinationCrs( QgsCoordinateReferenceSystem::fromEpsgId( 3857 ) ); - ms.setOutputSize( QSize( 436, 690 ) ); - QgsGeometry geom = QgsGeometry::fromWkt( "LineString (605540.02427726075984538 5422974.88796170614659786, 618450.11232534842565656 5430064.85434877127408981, 631042.73919192561879754 5418953.71299590915441513, 652418.458746955730021 5431228.87868097703903913)" ); - double epsilon = 0.1; - - // Case when the geometry can fully be contained within the extent - ms.setExtent( QgsRectangle( 595290, 5.35402e+06, 661796, 5.45927e+06 ) ); - QCOMPARE( InputUtils::equals( mUtils->relevantGeometryCenterToScreenCoordinates( geom, &ms ), QPointF( 220.861, 224.065 ), epsilon ), true ); - - // Case when we cut the geometry to current extent - ms.setExtent( QgsRectangle( 599032, 5.40671e+06, 619818, 5.43961e+06 ) ); - QCOMPARE( InputUtils::equals( mUtils->relevantGeometryCenterToScreenCoordinates( geom, &ms ), QPointF( 286.257, 274.5 ), epsilon ), true ); -} - void TestUtilsFunctions::testIsValidEmail() { // valid emails diff --git a/app/test/testutilsfunctions.h b/app/test/testutilsfunctions.h index 09839fecb..e55d06abb 100644 --- a/app/test/testutilsfunctions.h +++ b/app/test/testutilsfunctions.h @@ -37,6 +37,8 @@ class TestUtilsFunctions: public QObject void resolveTargetDir(); void testExtractPointFromFeature(); void testStakeoutPathExtent(); + void testWhereToPanWhenIdentifying(); + void testDrawerCompensatedExtent(); void testDistanceBetweenGpsAndFeature(); void testAngleBetweenGpsAndFeature(); void testIsPointLayerFeature(); @@ -49,7 +51,6 @@ class TestUtilsFunctions: public QObject void testParsePositionUpdates(); void testFormatDistanceInProjectUnit(); void testFormatAreaInProjectUnit(); - void testRelevantGeometryCenterToScreenCoordinates(); void testIsValidEmail(); void testSanitizePath(); void testUniqueString();