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

Add DiscreteFrechetDistance #764

Merged

Conversation

FObermaier
Copy link
Contributor

@FObermaier FObermaier commented Aug 4, 2021

Like Hausdorff distance the Fréchet distance is a measure of similarity between curves.
Its metric is better than Hausdorff's because it takes the flow of the curves into account.
It is possible that two curves have a small Hausdorff but a large Fréchet distance.

This implementation is based on the following optimized Fréchet distance algorithm:

Thomas Devogele, Maxence Esnault, Laurent Etienne. Distance discrète de Fréchet optimisée. Spatial Analysis and Geomatics (SAGEO), Nov 2016, Nice, France. hal-02110055

It attempts to compute only relevant coordinate distances for perfromance.
It provides a minimal MatrixStorage base class and implementations that trade off memory consumption and performance.

Signed-off-by: Felix Obermaier felix.obermaier@netcologne.de

Signed-off-by: Felix Obermaier <felix.obermaier@netcologne.de>
* Fix HausdorffSimilarityMeasure to handle equal `POINT` geometries by
  using fast exit if `DiscreteHausdorffDistance.distance` returns 0d.
* Add/extend unit tests
*
* @author Felix Obermaier
*/
public interface DistanceFunction {
Copy link
Contributor

Choose a reason for hiding this comment

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

The term "function" is fairly overloaded in the JTS codebase. Can this be changed to DistanceMetric?

@dr-jts
Copy link
Contributor

dr-jts commented Sep 2, 2021

Frechet Distance is a nice addition to JTS.

How do you see the DistanceFunction capability being used? I see the example of a Scaled distance metric in the unit test - is this a real-world scenario? Does it change the Frechet Distance result by more than the scale factor?

@dr-jts
Copy link
Contributor

dr-jts commented Sep 2, 2021

It is very useful to have distance algorithms also be able to return the two points at which the calculated distance is obtained. This is for confirmation and visualization purposes. (For example see the implementation in DiscreteHausdorffDistance)

Is it possible to add this capability to the Frechet Distance code?

@FObermaier
Copy link
Contributor Author

FObermaier commented Sep 3, 2021

How do you see the DistanceFunction capability being used?

I thought about an implementation using Haversine or Vincenty's formula for geographic coordinates.

@dr-jts
Copy link
Contributor

dr-jts commented Sep 3, 2021

I thought about an implementation using Haversine or Vincenty's formula for geographic coordinates.

In geodetic space the locus of boundary segments between vertices is a great circle arc, rather than a straight line. So usually it requires more than just using a geodetic distance metric to produce accurate results. Of course, geometries can be densified to mitigate this somewhat.

i have had some thoughts about bundling up the various pieces of metadata about the geometry coordinate system (including geodetic information and precision model) into a single object (perhaps called GeometryRealm or maybe CoordinateSystem. Still thinking about whether this should be provided on a per-geometry basis (perhaps as part of the GeometryFactory), or on a per-operation basis. Or both... The advantage to doing this is that it will avoid a proliferation of objects and function signatures in the API.

Given this, I'd prefer to defer introducing the DistanceMetric API unless there is a clear current reason for doing so.

@dr-jts
Copy link
Contributor

dr-jts commented Sep 17, 2021

@FObermaier is there a bug in this algorithm? What should the Frechet Distance be between the following:

A: LINESTRING (1 1, 2 2)
B: LINESTRING (1 4, 2 3)

The implementation gives the distance as 2.23606797749979. But since the distance between the two start points is 3, shouldn't that be the overall Frechet Distance?

A fix has been developed in #783.

@FObermaier
Copy link
Contributor Author

Yes, I'm working on it.

* Add getCoordinates() to DiscreteFrechetDistance
* Add DiscreteFrechetDistanceLinear for validation of unit tests
* Add unit test
* Add code documentation to PointPairDistance
* Add DiscreteFrechetDistanceLinear to tests for verification.
* Add MatrixStorage and implementations CsrMatrix, RectMatrix and HashMapMatrix
* Add unit tests for MatrixStorage
* Add unit tests
@FObermaier FObermaier force-pushed the enhancement/DiscreteFrechetDistance branch from 8db79ca to 9aaa5ba Compare September 21, 2021 21:39
@FObermaier
Copy link
Contributor Author

@dr-jts, I fixed the algorithm, sorry for doing PR too early.

* Add algorithm references
* Add code documentation
* Add references
* Restrict test verbosity
int max = Math.max(rows, cols);
// NOTE: these constraints need to be verified
if (max < 64)
return new HashMapMatrix(rows, cols, Double.POSITIVE_INFINITY);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this faster? If not, why not just use a RectMatrix, since storage isn't a problem for small dimensions?

/**
* Abstract base class for storing 2d matrix data
*/
private abstract static class MatrixStorage {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's unusual to duplicate implementation code in unit tests, since this doesn't actually test the code in the library?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did not see another way to test and verify that MatrixStorage implementations work correctly while keeping it private in jts-core.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, that's a bit of a limitation in the Java package model. I usually make things like this package-private, so they can be unit tested.

* remove code duplication in unit test
* remove constraint for HashMapMatrix
@dr-jts dr-jts merged commit e176732 into locationtech:master Oct 13, 2021
/**
* Linear Discrete Fréchet Distance computation
*/
public class DiscreteFrechetDistanceLinear {
Copy link
Contributor

Choose a reason for hiding this comment

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

What does this class do differently to DiscreteFrechetDistance? Are they both needed?

@dr-jts
Copy link
Contributor

dr-jts commented Oct 13, 2021

Computing the distance between the following geometries causes an ArrayIndexOutOfBoundsException.

A: LINESTRING (80 260, 170 180, 190 290, 310 350, 330 270, 360 280)
B: LINESTRING (120 90, 380 130)

It looks like the error occurs in the Bresenham diagonal generating method, when geometry B has fewer than half the vertices of geometry A.

*
* @param numCols the number of columns
* @param numRows the number of rows
* @return an array of column and row indices bitwise-or combined.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this correct? Or is the returned array a "flattened" set of X,Y indices?

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.

3 participants