Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue with vertical lines not visible in the elevation profile tool #58959

Merged
merged 3 commits into from
Oct 16, 2024

Conversation

ptitjano
Copy link
Contributor

@ptitjano ptitjano commented Oct 3, 2024

Description

This PR reverts commit ce96dee.

This commit was introduced to gain some performance when the
tolerance is set by using request.setDistanceWithin instead of
request.setFilterRect. However, in that case, the providers either
use GEOS::distance or GEOS::distanceWithin which is not able to
handle the Z component of a Geometry.
For example, a purely vertical line, i.e.: LineString(X Y Z1, X Y Z2) will always be to an infinite distance of any geometry according
to GEOS.

In practice, this means that a purely vertical line will never be
visible in the elevation profile when the tolerance is set. Indeed,
the provider will always discard such geometry because of the usage
of setDistanceWithin. This issue is fixed by always using
setFilterRect. This might introduce a performance penalty but this
ensures that the result is always correct.

Update October 10

`GEOSPreparedDistance_r` is not able to properly handle a purely
vertical line (LineString Z((X Y Z1, X Y Z2, ..., X Y Zn))). In that
case, it always `inf`. However, `GEOSDistance_r` works.

This issue is fixed by checking if one of the geometry is vertical 3d
line by introducing `isZVerticalLine`. This function checks if a
geometry is a 3d line. If that's the case, it loops through the points
of the line. If two points do not have the same X Y corrdinates, it
means that the line is not purely vertical.

If `geom` is a vertical line, it is replaced by its first point.
It the prepared geometry is a vertical line, fallback to the non
prepared case.

@github-actions github-actions bot added this to the 3.40.0 milestone Oct 3, 2024
@ptitjano ptitjano self-assigned this Oct 3, 2024
@ptitjano ptitjano added Bug Either a bug report, or a bug fix. Let's hope for the latter! Profile tool labels Oct 3, 2024
Copy link

github-actions bot commented Oct 3, 2024

🪟 Windows builds ready!

Windows builds of this PR are available for testing here. Debug symbols for this build are available here.

(Built from commit 2e5df52)

@ptitjano ptitjano force-pushed the elevation-profile-fix-vertical branch from 42d217c to 2e5df52 Compare October 3, 2024 20:32
@nyalldawson
Copy link
Collaborator

However, in that case, the providers either
use GEOS::distance or GEOS::distanceWithin which is not able to
handle the Z component of a Geometry.
For example, a purely vertical line, i.e.: LineString(X Y Z1, X Y Z2) will always be to an infinite distance of any geometry according
to GEOS.

I think this special case should be handled in the iterator (or geometry) classes then, not specifically the elevation profile code

@ptitjano
Copy link
Contributor Author

ptitjano commented Oct 4, 2024

However, in that case, the providers either
use GEOS::distance or GEOS::distanceWithin which is not able to
handle the Z component of a Geometry.
For example, a purely vertical line, i.e.: LineString(X Y Z1, X Y Z2) will always be to an infinite distance of any geometry according
to GEOS.

I think this special case should be handled in the iterator (or geometry) classes then, not specifically the elevation profile code

Thanks for you message. However, I don't understand it. Do you mean that the iterator code of the providers should be changed? Or something else?

@nyalldawson
Copy link
Collaborator

. Do you mean that the iterator code of the providers should be changed?

I mean if the setDistanceWithin method for QgsFeatureRequest can't handle this situation, then it needs to be fixed in the iterator code. Otherwise other users of setDistanceWithin will remain broken.

So maybe the fix needs to apply in QgsGeos::distance / QgsGeos::distanceWithin instead, eg by detecting a vertical line and using a GEOS point geometry there instead...

@ptitjano ptitjano force-pushed the elevation-profile-fix-vertical branch from 2e5df52 to 9c5aaae Compare October 10, 2024 15:12
@ptitjano
Copy link
Contributor Author

. Do you mean that the iterator code of the providers should be changed?

I mean if the setDistanceWithin method for QgsFeatureRequest can't handle this situation, then it needs to be fixed in the iterator code. Otherwise other users of setDistanceWithin will remain broken.

So maybe the fix needs to apply in QgsGeos::distance / QgsGeos::distanceWithin instead, eg by detecting a vertical line and using a GEOS point geometry there instead...

Thanks for the suggestion. I have completely rewritten the PR to handle this is in QgsGeos.

Copy link

github-actions bot commented Oct 10, 2024

🪟 Windows builds

Download Windows builds of this PR for testing.
Debug symbols for this build are available here.
(Built from commit 9245434)

🪟 Windows Qt6 builds

Download Windows Qt6 builds of this PR for testing.
(Built from commit 9245434)

src/core/geometry/qgsgeos.cpp Outdated Show resolved Hide resolved
src/core/geometry/qgsgeos.cpp Outdated Show resolved Hide resolved
src/core/geometry/qgsgeos.cpp Outdated Show resolved Hide resolved
src/core/geometry/qgsgeos.cpp Outdated Show resolved Hide resolved
src/core/geometry/qgsgeos.cpp Outdated Show resolved Hide resolved
src/core/geometry/qgsgeos.cpp Outdated Show resolved Hide resolved
@nyalldawson
Copy link
Collaborator

Thanks @ptitjano , this approach looks much better to me! Just be aware of introducing any inefficiencies in these classes (like clones which aren't 100% required), as this is all performance critical code which needs to be as optimised as possible

@ptitjano ptitjano force-pushed the elevation-profile-fix-vertical branch from 9c5aaae to b2f4c2e Compare October 11, 2024 15:05
@ptitjano
Copy link
Contributor Author

Thanks @ptitjano , this approach looks much better to me! Just be aware of introducing any inefficiencies in these classes (like clones which aren't 100% required), as this is all performance critical code which needs to be as optimised as possible

Thanks. This should be good now.

`GEOSPreparedDistance_r` is not able to properly handle a purely
vertical line (LineString Z((X Y Z1, X Y Z2, ..., X Y Zn))). In that
case, it always `inf`. However, `GEOSDistance_r` works.

This issue is fixed by checking if one of the geometry is vertical 3d
line by introducing `isZVerticalLine`. This function checks if a
geometry is a 3d line. If that's the case, it loops through the points
of the line. If two points do not have the same X Y corrdinates, it
means that the line is not purely vertical.

If `geom` is a vertical line, it is replaced by its first point.
It the prepared geometry is a vertical line, fallback to the non
prepared case.
This is similar to the change done on `QgsGeos::distance` in the
previous commit.
@ptitjano ptitjano force-pushed the elevation-profile-fix-vertical branch from b2f4c2e to 9245434 Compare October 14, 2024 08:31
@nyalldawson nyalldawson reopened this Oct 15, 2024
@nyalldawson nyalldawson merged commit 19b1f8a into qgis:master Oct 16, 2024
63 of 64 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Either a bug report, or a bug fix. Let's hope for the latter! Profile tool
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants