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

[SEDONA-298] Implement ST_ClosestPoint #877

Merged
merged 11 commits into from
Jul 2, 2023
Merged

Conversation

yyy1000
Copy link
Contributor

@yyy1000 yyy1000 commented Jun 28, 2023

Did you read the Contributor Guide?

Is this PR related to a JIRA ticket?

What changes were proposed in this PR?

Add ST_ClosestPointfunction to sedona-common, sedona-sql, sedona-flink, and the dataframe/python API.

How was this patch tested?

  1. Comprehensive unit tests in sedona-common
  2. Integration unit tests in sedona-sql, sedona-flink, and python.

Did this PR include necessary documentation updates?

  • Yes, I have updated the documentation update.

assertEquals(expected2, actual2);
Point expectedPoint3 = GEOMETRY_FACTORY.createPoint(new Coordinate(125.75342465753425, 115.34246575342466));
Double expected3 = Functions.closestPoint(lineString2, point2).distance(expectedPoint3);
assertEquals(expected3, 0, 1e-3);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the tolerance level 1e-3 here? please standardize to 1e-6 or we can even go upto 1e-9.

@Test
public void closestPoint() {
Point point1 = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 1));
LineString lineString1 = GEOMETRY_FACTORY.createLineString(coordArray(1, 0, 1, 1, 2, 1, 2, 0, 1, 0));
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if one or both the geometries are empty? Please add unit tests testing that.

@@ -447,6 +448,12 @@ public static Geometry lineFromMultiPoint(Geometry geometry) {
return GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[0]));
}

public static Geometry closestPoint(Geometry left, Geometry right) {
DistanceOp distanceOp = new DistanceOp(left, right);
Coordinate[] closestPoints = distanceOp.nearestPoints();
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we're using JTS DistanceOp, what happens if the one or both the input geometries are 3D? Is the z ordinate ignored, or it is supported?
We will have to update documentation and tests accordingly.

Point expectedPoint4 = GEOMETRY_FACTORY.createPoint(new Coordinate(131.59149149528952, 101.89887534906197));
Double expected4 = Functions.closestPoint(polygonA, polygonB).distance(expectedPoint4);
assertEquals(expected4, 0, 1e-6);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a unit test case testing geometry collections

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, but I'm a little confused. Though it could work. I search it but can't find any usage about geometry collections. XD

Copy link
Contributor

Choose a reason for hiding this comment

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

There could be many possible use cases. Like given a geometry collection representing a neighborhood (polygon/multipolygon for houses), find closest point to a given external point.


Example1:
```sql
SELECT ST_AsText( ST_ClosestPoint(g1, g2)) As ptwkt;
Copy link
Contributor

Choose a reason for hiding this comment

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

This documentation needs to be updated about 3D geometries, based on testing JTS DistanceOp

Copy link
Contributor

Choose a reason for hiding this comment

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

Update documentation here to include the thrown exception if any of the geometry is empty

return GEOMETRY_FACTORY.createPoint(closestPoints[0]);
}
catch (Exception e) {
return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do not throw null here, throw a meaningful exception. If this is thrown if the geometry is empty, throw an IllegalArgumentException with an appropriate message

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should we throw an exception or just return a null value? I think postgis return an empty value:

postgres=# select  ST_AsText(ST_ClosestPoint(ST_GeomFromText('POINT EMPTY'), 
                                   ST_GeomFromText('LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, 5 0)')));
 st_astext 
-----------
 
(1 row)

Copy link
Contributor

Choose a reason for hiding this comment

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

It is better to throw an exception, in a case where multiple functions are stringed together, which is what happens usually, throwing null means user would see an unexplainable NullPointerException eventually.

By throwing an IllegalArgumentException with a proper message, we will end up helping the user.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Based on this, I tried to resolve all your comments! You can review again. :) Thanks! 😁

Point expectedPoint4 = GEOMETRY_FACTORY.createPoint(new Coordinate(131.59149149528952, 101.89887534906197));
Double expected4 = Functions.closestPoint(polygonA, polygonB).distance(expectedPoint4);
assertEquals(expected4, 0, 1e-6);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

There could be many possible use cases. Like given a geometry collection representing a neighborhood (polygon/multipolygon for houses), find closest point to a given external point.

assertNull(actual);

// Both objects are empty
Polygon emptyPolygon = GEOMETRY_FACTORY.createPolygon();
Copy link
Contributor

Choose a reason for hiding this comment

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

refactor these test cases to catch the meaningful exception.
There would be 2 asserts, 1 for the type of exception thrown, 1 for the message in the exception.
Check implementation and tests of nRings for reference


Example1:
```sql
SELECT ST_AsText( ST_ClosestPoint(g1, g2)) As ptwkt;
Copy link
Contributor

Choose a reason for hiding this comment

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

Update documentation here to include the thrown exception if any of the geometry is empty

## ST_ClosestPoint

Introduction: Returns the 2-dimensional point on geom1 that is closest to geom2. This is the first point of the shortest line between the geometries. If using 3D geometries, the Z coordinates will be ignored. If you have a 3D Geometry, you may prefer to use ST_3DClosestPoint.

Copy link
Contributor

Choose a reason for hiding this comment

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

Update documentation here to include the thrown exception if any of the geometry is empty

@jiayuasu jiayuasu merged commit 8a1c404 into apache:master Jul 2, 2023
37 of 39 checks passed
@yyy1000 yyy1000 deleted the closed-point branch July 3, 2023 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants