diff --git a/python/core/auto_generated/geometry/qgsrectangle.sip.in b/python/core/auto_generated/geometry/qgsrectangle.sip.in index 1dae45d63e5..a7ab241c917 100644 --- a/python/core/auto_generated/geometry/qgsrectangle.sip.in +++ b/python/core/auto_generated/geometry/qgsrectangle.sip.in @@ -183,6 +183,13 @@ Scale the rectangle around its center point. void scale( double scaleFactor, double centerX, double centerY ); %Docstring Scale the rectangle around its center point. +%End + + QgsRectangle scaled( double scaleFactor, const QgsPointXY *center = 0 ) const; +%Docstring +Scale the rectangle around its ``center`` point. + +.. versionadded:: 3.4 %End void grow( double delta ); diff --git a/src/core/geometry/qgsrectangle.cpp b/src/core/geometry/qgsrectangle.cpp index a5c7884b1e7..cd54b82def1 100644 --- a/src/core/geometry/qgsrectangle.cpp +++ b/src/core/geometry/qgsrectangle.cpp @@ -58,6 +58,13 @@ QgsRectangle QgsRectangle::fromCenterAndSize( QgsPointXY center, double width, d return QgsRectangle( xMin, yMin, xMax, yMax ); } +QgsRectangle QgsRectangle::scaled( double scaleFactor, const QgsPointXY *center ) const +{ + QgsRectangle scaledRect = QgsRectangle( *this ); + scaledRect.scale( scaleFactor, center ); + return scaledRect; +} + QgsRectangle QgsRectangle::operator-( const QgsVector v ) const { double xmin = mXmin - v.x(); diff --git a/src/core/geometry/qgsrectangle.h b/src/core/geometry/qgsrectangle.h index 81202f6c4dd..61db526e12e 100644 --- a/src/core/geometry/qgsrectangle.h +++ b/src/core/geometry/qgsrectangle.h @@ -261,6 +261,12 @@ class CORE_EXPORT QgsRectangle mYmax = centerY + newHeight / 2.0; } + /** + * Scale the rectangle around its \a center point. + * \since QGIS 3.4 + */ + QgsRectangle scaled( double scaleFactor, const QgsPointXY *center = nullptr ) const; + /** * Grows the rectangle in place by the specified amount. * \see buffered() diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index 0210e04a895..caf69850656 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -996,8 +996,40 @@ void QgsMapCanvas::zoomToSelected( QgsVectorLayer *layer ) } rect = mapSettings().layerExtentToOutputExtent( layer, rect ); + + // zoom in if point cannot be distinguished from others + // also check that rect is empty, as it might not in case of multi points + if ( layer->geometryType() == QgsWkbTypes::PointGeometry && rect.isEmpty() ) + { + int scaleFactor = 5; + QgsPointXY center = mSettings.mapToLayerCoordinates( layer, rect.center() ); + QgsRectangle extentRect = mSettings.mapToLayerCoordinates( layer, extent() ).scaled( 1.0 / scaleFactor, ¢er ); + QgsFeatureRequest req = QgsFeatureRequest().setFilterRect( extentRect ).setLimit( 1000 ).setNoAttributes(); + QgsFeatureIterator fit = layer->getFeatures( req ); + QgsFeature f; + QgsPointXY closestPoint; + double closestSquaredDistance = extentRect.width() + extentRect.height(); + bool pointFound = false; + while ( fit.nextFeature( f ) ) + { + QgsPointXY point = f.geometry().asPoint(); + double sqrDist = point.sqrDist( center ); + if ( sqrDist > closestSquaredDistance || sqrDist < 4 * std::numeric_limits::epsilon() ) + continue; + pointFound = true; + closestPoint = point; + closestSquaredDistance = sqrDist; + } + if ( pointFound ) + { + // combine selected point with closest point and scale this rect + rect.combineExtentWith( mSettings.layerToMapCoordinates( layer, closestPoint ) ); + rect.scale( scaleFactor, ¢er ); + } + } + zoomToFeatureExtent( rect ); -} // zoomToSelected +} void QgsMapCanvas::zoomToFeatureExtent( QgsRectangle &rect ) { diff --git a/tests/src/core/testqgsrectangle.cpp b/tests/src/core/testqgsrectangle.cpp index 4ace39739cb..3d43aba2422 100644 --- a/tests/src/core/testqgsrectangle.cpp +++ b/tests/src/core/testqgsrectangle.cpp @@ -40,6 +40,7 @@ class TestQgsRectangle: public QObject void isFinite(); void combine(); void dataStream(); + void scale(); }; void TestQgsRectangle::isEmpty() @@ -348,5 +349,22 @@ void TestQgsRectangle::dataStream() QCOMPARE( result, original ); } +void TestQgsRectangle::scale() +{ + QgsRectangle rect( 10, 20, 30, 60 ); + rect.scale( 2 ); + QCOMPARE( rect, QgsRectangle( 0, 0, 40, 80 ) ); + rect.scale( .5 ); + QCOMPARE( rect, QgsRectangle( 10, 20, 30, 60 ) ); + QgsPointXY center( 10, 20 ); + + // with center + rect.scale( 2, ¢er ); + QCOMPARE( rect, QgsRectangle( -10, -20, 30, 60 ) ); + + // scaled + QCOMPARE( rect, QgsRectangle( 10, 20, 30, 60 ).scaled( 2, ¢er ) ); +} + QGSTEST_MAIN( TestQgsRectangle ) #include "testqgsrectangle.moc"