Skip to content

Rework handling of panning to identified features - #4591

Open
uclaros wants to merge 7 commits into
masterfrom
bugfix/better-jump-to
Open

Rework handling of panning to identified features#4591
uclaros wants to merge 7 commits into
masterfrom
bugfix/better-jump-to

Conversation

@uclaros

@uclaros uclaros commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Plan A

This PR is a rework on how we jump to geometries. It was initially just a way to mitigate a GEOS crash when identifying self-overlapping multipart geometries, but quickly evolved!

Plan B

If this is too much for a last minute change, we can avoid hitting the geos bug by slightly modifying the current approach: Perform bounding box intersections instead of geometry intersections.

diff --git a/app/inpututils.cpp b/app/inpututils.cpp
index b5573eb3..02a874e8 100644
--- a/app/inpututils.cpp
+++ b/app/inpututils.cpp
@@ -330,13 +330,12 @@ QPointF InputUtils::relevantGeometryCenterToScreenCoordinates( const QgsGeometry
 
   const QgsRectangle currentExtent = mapSettings->mapSettings().visibleExtent();
 
-  // Cut the geometry to current extent
-  const QgsGeometry currentExtentAsGeom = QgsGeometry::fromRect( currentExtent );
-  const QgsGeometry intersectedGeom = geom.intersection( currentExtentAsGeom );
+  // Cut the geometry extent to current extent
+  const QgsRectangle intersectedExtent = currentExtent.intersect( geom.boundingBox() );
 
-  if ( !intersectedGeom.isEmpty() )
+  if ( !intersectedExtent.isEmpty() )
   {
-    target = QgsPoint( intersectedGeom.boundingBox().center() );
+    target = QgsPoint( intersectedExtent.center() );
   }
   else
   {

This will change the existing behavior, eg identifying C shaped geometry that is partially visible will not recenter the map canvas to the center of the visible part of the geometry, but to the visible part of the geom bbox.

Testing notes:

  • need to test all Geometries that we support (Points, Lines, Polygons and it's multi variants)

uclaros added 7 commits July 10, 2026 11:44
We will use it to jump to the highlighted feature once we know
the available area of the map that is not covered by the panel
- Use the renamed previewPanelHeight property instead of signal params
- Move all highlighting and form opening to a identifyFeature()
- Don't pan if whole feature is near visible map center
- Center feature to visible map if it fits
- Center clicked location to visible map if whole does not fit
- Add method to calculate the map extent required for a geometry to
  fit the visible part of the map canvas when covered by a drawer
- Use animated zoom when identifying features from a list and not on map
@Withalion

Copy link
Copy Markdown
Collaborator

might help resolve #3845 as well

@Withalion Withalion left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! It would be great to rebase on current master as well since some time passed.

{
rendererPrivate.freeze('jumpTo')

let oldExtent = mapRenderer.mapSettings.visibleExtent

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let oldExtent = mapRenderer.mapSettings.visibleExtent
const oldExtent = mapRenderer.mapSettings.visibleExtent

Comment on lines +154 to +157
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 )

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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 )
const tmpMinX = startMinX - percentage * 0.01 * ( startMinX - endMinX )
const tmpMinY = startMinY - percentage * 0.01 * ( startMinY - endMinY )
const tmpMaxX = startMaxX - percentage * 0.01 * ( startMaxX - endMaxX )
const tmpMaxY = startMaxY - percentage * 0.01 * ( startMaxY - endMaxY )

enabled: jumpExtentAnimator.enabled
}

onPercentageChanged: {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
onPercentageChanged: {
onPercentageChanged: () => {

either use lambda or anonymous function


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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it be better to just use null here instead, if the location is unknown?

// 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 )

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
signal featureIdentified( var pair, var point )
signal featureIdentified( FeatureLayerPair pair, qgsPoint clickedPoint )

It's about time we expose FeatureLayerPair & QgsPoint to QML properly


// 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 ) );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pan = mUtils->whereToPanWhenIdentifying( visiblePoint, &ms, bottomOffset, QPointF( 20, 40 ) );
pan = InputUtils::whereToPanWhenIdentifying( visiblePoint, &ms, bottomOffset, QPointF( 20, 40 ) );

}
}

void TestUtilsFunctions::testWhereToPanWhenIdentifying()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we check here that the scale has not changed as well

}
}

void TestUtilsFunctions::testDrawerCompensatedExtent()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As well here I would have case for:

  • when the map is just panned
  • when feature is bigger then current extent, so the map zooms out
  • when feature is smaller then current extent

// 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 );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
QgsRectangle extent = mUtils->drawerCompensatedExtent( point, &ms, bottomOffset );
QgsRectangle extent = InputUtils::drawerCompensatedExtent( point, &ms, bottomOffset );

// 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 );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
extent = mUtils->drawerCompensatedExtent( line, &ms, bottomOffset );
extent = InputUtils::drawerCompensatedExtent( line, &ms, bottomOffset );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants