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

Correctness issues with Contains #513

Closed
michaelkirk opened this issue Sep 21, 2020 · 9 comments
Closed

Correctness issues with Contains #513

michaelkirk opened this issue Sep 21, 2020 · 9 comments

Comments

@michaelkirk
Copy link
Member

michaelkirk commented Sep 21, 2020

There are some correctness issues with our Contains implementation - discussed in #511

We should fix them.

I can find time to fix these this week, unless @rmanoka was already planning to do the work (I'm happy either way, so long as it gets fixed).

@rmanoka
Copy link
Contributor

rmanoka commented Sep 21, 2020

@michaelkirk Please go ahead! I suggest that we turn this into a tracking issue for completing both the Intersects and Contains traits (as these two are interrelated and use each other in the impls.). Here are my thoughts on what we should have eventually. We currently have 12 geometry types. Ideally, we should have an Intersects and Contains implementation for all the combinations.

  1. Coordinate
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  2. Point
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  3. Line
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  4. LineString
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  5. Polygon
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  6. MultiPoint
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  7. MultiLineString
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  8. MultiPolygon
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  9. Rect
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  10. Triangle
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  11. Geometry
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection
  12. GeometryCollection
    1. Coordinate
    2. Point
    3. Line
    4. LineString
    5. Polygon
    6. MultiPoint
    7. MultiLineString
    8. MultiPolygon
    9. Rect
    10. Triangle
    11. Geometry
    12. GeometryCollection

Some of these (especially in the case of Intersects trait) can be solved via a blanket implementation based on another implementation:

  1. Point<T>: Intersects<G> from Coordinate<T>: Intersects<G>
  2. MultiPoint<T>: Intersects<G> from Point<T>: Intersects<G>
  3. MultiLineString from LineString
  4. MultiPolygon from Polygon

We could check off (or add comments on what is done / left) as and when we ensure correctness of specific pairs of geometries. Please let me know if you wish to discuss any specific combination.

@michaelkirk
Copy link
Member Author

Sounds good!

@michaelkirk
Copy link
Member Author

michaelkirk commented Sep 23, 2020

Seems like JTS's approach (and thus probably GOES/PostGIS/GDAL's) is to actually generate an explicit dimensional matrix to compare to the DE-9IM definition for the general case, with some short-circuit optimizations where possible.

Sounds like a lot of work, but maybe the way to go. I'm going to do some more research.

Geometry#contains:
https://github.com/locationtech/jts/blob/master/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java#L877

calculate intersection matrix:
https://github.com/locationtech/jts/blob/master/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateComputer.java#L73

@michaelkirk
Copy link
Member Author

michaelkirk commented Sep 23, 2020

After looking into more, I think we should explicitly calculate an explicit DE-9IM style intersection matrix.

As well as fixing contains, this would give us default implementations for all the other DE-9IM relations "for free".

you can see the JTS implementation:
https://github.com/locationtech/jts/tree/5c78e723cc4a9dcd8cbbc74615305690203293df/modules/core/src/main/java/org/locationtech/jts/operation/relate

It's ~1k lines of code/comments by itself:

[15:36:39] ~/src/georust/jts/modules/core/src/main/java/org/locationtech/jts/operation/relate (master)
$ wc -l *
     150 EdgeEndBuilder.java
     192 EdgeEndBundle.java
      69 EdgeEndBundleStar.java
     389 RelateComputer.java
      52 RelateNode.java
      33 RelateNodeFactory.java
     135 RelateNodeGraph.java
     115 RelateOp.java
      52 package.html
    1187 total

And of course it depends on lots of other things, maybe most notably JTS's own geomgraph module, which seems to coordinate the bulk of the topological calculations. We'd have to build a corollary for this - unless someone knows of an existing crate with this functionality - I couldn't find anything.

$ find . -name \*.java | xargs wc -l
     154 ./Node.java
     201 ./Label.java
     151 ./EdgeIntersectionList.java
      87 ./EdgeIntersection.java
      84 ./EdgeNodingValidator.java
     338 ./EdgeEndStar.java
     114 ./EdgeList.java
     185 ./TopologyLocation.java
     137 ./Depth.java
     231 ./EdgeRing.java
     387 ./DirectedEdgeStar.java
     120 ./NodeMap.java
      84 ./GraphComponent.java
      40 ./Position.java
     144 ./index/SimpleSweepLineIntersector.java
     117 ./index/MonotoneChainIndexer.java
     206 ./index/SegmentIntersector.java
      57 ./index/EdgeSetIntersector.java
      91 ./index/SweepLineEvent.java
      34 ./index/MonotoneChain.java
      82 ./index/SimpleEdgeSetIntersector.java
     142 ./index/MonotoneChainEdge.java
     151 ./index/SimpleMCSweepLineIntersector.java
      53 ./index/SweepLineSegment.java
     219 ./DirectedEdge.java
     238 ./PlanarGraph.java
     139 ./Quadrant.java
     475 ./GeometryGraph.java
      29 ./NodeFactory.java
     129 ./EdgeEnd.java
     270 ./Edge.java
    4889 total

Line count is admittedly a crude metric, I didn't strip out comments and we can probably make some simplifying assumptions, e.g. maybe we can skip having pluggable boundaryDeterminationRules, but I'm trying to impress that it's a large undertaking, and I wanted to get some input about the approach before investing a lot of time/code into it.

As well as getting a wider array of (theoretically correct) implementations for some of the relation booleans, JTS (et al) also use the geomgraph stuff as a dependency of building buffers and some other operations, so it might pay some future dividends.

What do you think @georust/core - is there interest in merging several-thousand lines to do explicit DE-9IM calculations?

@urschrei
Copy link
Member

What do you think @georust/core - is there interest in merging several-thousand lines to do explicit DE-9IM calculations?

Yes very much so – I think having a full DE-9IM implementation is incredibly valuable for geo as a project. It's not an insurmountable amount of work, but as you say, it's not a 200-line PR, either.

Let's create a tracking issue and discuss ideas for what concretely needs to be done, and who is available/interested in helping?

@rmanoka
Copy link
Contributor

rmanoka commented Sep 24, 2020

I'm in favour of adding the functionality to compute the full matrix. Also agree with @urschrei in creating a tracking issue, and incrementally adding this functionality.

That said, we should still aim to keep the existing traits as efficient as we can (but also fully correct ofc), and not compute the whole matrix before answering the Intersects or Contains traits. May be provide traits for each entry of the matrix, and for calculating boundary of a geometry. Then the current traits can use those, but exit early if it is not needed to compute all the entries.

How about working on a couple of RFCs to brainstorm the design of the traits / planning the implementation?

On a related note, could we enable GitHub pages on a gh-pages branch for this repo? The long list of check boxes above could be nicely made into a table, but GitHub doesn't like css (for security reasons) in it's markdown. I also would also like to see / add some proofs of correctness of our implementations which we can maintain there as pandoc documents (it has excellent math support via LaTeX in it's markdown).

@urschrei
Copy link
Member

How about working on a couple of RFCs to brainstorm the design of the traits / planning the implementation?

Great idea. I've also enabled "projects" for this repo – may be useful.

On a related note, could we enable GitHub pages on a gh-pages branch for this repo?

Yep.

I also would also like to see / add some proofs of correctness of our implementations which we can maintain there as pandoc documents (it has excellent math support via LaTeX in it's markdown).

💓

@rmanoka
Copy link
Contributor

rmanoka commented Oct 2, 2020

I've put together a basic table for the Intersects trait here. The aim would be to fill in the values there as and when we verify our existing impls and add new ones. Thoughts?

bors bot added a commit that referenced this issue Apr 13, 2021
639: Introduce the geomgraph module for DE-9IM Relate trait r=michaelkirk a=michaelkirk

- [x] I agree to follow the project's [code of conduct](https://github.com/georust/geo/blob/master/CODE_OF_CONDUCT.md).
- [x] I added an entry to `CHANGES.md` if knowledge of this change could be valuable to users.
---

Fixes #513, #515

(I'm sorry it's so large)

~~I'm going to leave it as a draft (edit: 🤦 I failed to actually open the PR as a draft) while I wait to merge #636 and #638 and then do some rebasing, but I don't anticipate doing other large changes before review.~~ *update: ready for review!*

Here's some of the earlier work in pursuit of this:

#514
#516
#523
#524 
#538
#552
#561
#611
#628
#629
#636 

Primarily, this introduces the geomgraph module for a DE-9IM `Relate` trait.

geomgraph implements a topology graph largely inspired by JTS's module of the same name:
https://github.com/locationtech/jts/tree/jts-1.18.1/modules/core/src/main/java/org/locationtech/jts/geomgraph

You can see some of the reference code if you omit the "REMOVE JTS COMMENTS" commit. In some places the implementation is quite close to the JTS source. 

The overall "flow" is pretty similar to that of JTS, but in the small, there were some divergences. It's not easy (or desirable) to literally translate a Java codebase making heavy use of inheritance and pointers to rust. Additionally, I chose to take advantage of `Option` and rust's enums with associated data to make some case analysis more explicit.

There is a corresponding PR in our [jts-test-runner](georust/jts-test-runner#6) crate which includes the bulk of the tests for the new Relate trait.

## Algorithm Overview 

This functionality is accessed on geometries, via the `Relate` trait, e.g. `line.relate(point)` which returns a DE-9IM [`IntersectionMatrix`](https://en.wikipedia.org/wiki/DE-9IM#Matrix_model).

The `Relate` trait is driven by the `RelateOperation`. The `RelateOperation` builds a `GeometryGraph` for each of the two geometries being related. 

A `GeometryGraph` is a systematic way to organize the "interesting" parts of a geometry's structure - e.g. where its vertices, lines, and areas lie relative to one another.

Once the `RelateOperation` has built the two `GeometryGraph`s, it uses them to efficiently compare the two Geometries's structures, outputting the `IntesectionMatrix`.





Co-authored-by: Michael Kirk <[email protected]>
Co-authored-by: bors[bot] <26634292+bors[bot]@users.noreply.github.com>
bors bot added a commit that referenced this issue Apr 13, 2021
639: Introduce the geomgraph module for DE-9IM Relate trait r=frewsxcv,rmanoka a=michaelkirk

- [x] I agree to follow the project's [code of conduct](https://github.com/georust/geo/blob/master/CODE_OF_CONDUCT.md).
- [x] I added an entry to `CHANGES.md` if knowledge of this change could be valuable to users.
---

Fixes #513, #515

(I'm sorry it's so large)

~~I'm going to leave it as a draft (edit: 🤦 I failed to actually open the PR as a draft) while I wait to merge #636 and #638 and then do some rebasing, but I don't anticipate doing other large changes before review.~~ *update: ready for review!*

Here's some of the earlier work in pursuit of this:

#514
#516
#523
#524 
#538
#552
#561
#611
#628
#629
#636 

Primarily, this introduces the geomgraph module for a DE-9IM `Relate` trait.

geomgraph implements a topology graph largely inspired by JTS's module of the same name:
https://github.com/locationtech/jts/tree/jts-1.18.1/modules/core/src/main/java/org/locationtech/jts/geomgraph

You can see some of the reference code if you omit the "REMOVE JTS COMMENTS" commit. In some places the implementation is quite close to the JTS source. 

The overall "flow" is pretty similar to that of JTS, but in the small, there were some divergences. It's not easy (or desirable) to literally translate a Java codebase making heavy use of inheritance and pointers to rust. Additionally, I chose to take advantage of `Option` and rust's enums with associated data to make some case analysis more explicit.

There is a corresponding PR in our [jts-test-runner](georust/jts-test-runner#6) crate which includes the bulk of the tests for the new Relate trait.

## Algorithm Overview 

This functionality is accessed on geometries, via the `Relate` trait, e.g. `line.relate(point)` which returns a DE-9IM [`IntersectionMatrix`](https://en.wikipedia.org/wiki/DE-9IM#Matrix_model).

The `Relate` trait is driven by the `RelateOperation`. The `RelateOperation` builds a `GeometryGraph` for each of the two geometries being related. 

A `GeometryGraph` is a systematic way to organize the "interesting" parts of a geometry's structure - e.g. where its vertices, lines, and areas lie relative to one another.

Once the `RelateOperation` has built the two `GeometryGraph`s, it uses them to efficiently compare the two Geometries's structures, outputting the `IntesectionMatrix`.





Co-authored-by: Michael Kirk <[email protected]>
Co-authored-by: bors[bot] <26634292+bors[bot]@users.noreply.github.com>
@michaelkirk
Copy link
Member Author

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

No branches or pull requests

3 participants