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

Enhance coverage simplification to handle one tolerance per geometry #1037

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

nstrahl
Copy link

@nstrahl nstrahl commented Mar 10, 2024

An enhancement has been made to the coverage simplification algorithm (simplify and simplifyInner). Currently, coverage simplification only allows one global tolerance value to be specified for the entire coverage. The proposed enhancement allows multiple tolerances to be passed in as a list, where each entry corresponds to a geometry. The geometries can thus be simplified with different tolerances while still ensuring topological correctness. At coinciding coverage edges the lowest available value is utilized.

This will be very useful for solving problems related to the boundary discretization of tessellated geometries.

The following figure shows an example where the boundary polygon is simplified with a tolerance of 1, and the other hole polygons with 1 and 0.5.

coverage1

Here is another one with tolerances of 50 and 10.

coverage2

… geometry)

Signed-off-by: N_Strahl <bowsher228@gmail.com>
* @param tolerances comma-separated string of simplification tolerances for each coverage
* @return the simplified polygons
*/
public static Geometry[] simplify(Geometry[] coverage, String tolerances) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Conversion from a string to an array of tolerances should be left up to client code, since it's highly usage-specific. It can be done in CoverageFunctions as well, to allow for easier testing and experimentation.

Copy link
Author

Choose a reason for hiding this comment

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

Ok, then I will be moving that method into the JTS test builder code

…ts-app

Signed-off-by: N_Strahl <bowsher228@gmail.com>
@dr-jts
Copy link
Contributor

dr-jts commented Mar 11, 2024

The class Javadoc needs some mention of the semantics when using tolerance-per-geometry. Specifically, there should be a comment like:

The simplifier allows specifying the simplification tolerance separately for each input geometry.  Shared edges are simplified using the lowest of the tolerances for the adjacent geometries (i.e. the least amount of simplification is performed). This allows specific geometries in a coverage to be simplified less than other geometries (or to force their boundary to be preserved without simplification).

Signed-off-by: N_Strahl <bowsher228@gmail.com>
@nstrahl nstrahl requested a review from dr-jts April 5, 2024 07:27
@nstrahl
Copy link
Author

nstrahl commented May 31, 2024

@dr-jts Any other suggestions for changes in this PR?

* @param tolerances the simplification tolerances for each coverage
* @return the simplified polygons
*/
public static Geometry[] simplify(Geometry[] coverage, List<Double> tolerances) {
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 tolerances a List and not an array? It seems more uniform for it to have the same structure as the geometries.

for (int i = 0 ; i < lines.getNumGeometries(); i++) {
LineString line = (LineString) lines.getGeometryN(i);
boolean isFree = isFreeRing == null ? false : isFreeRing.get(i);
double areaTolerance = (areaTolerances.isEmpty()) ? -1 : ((areaTolerances.size() == 1) ? areaTolerances.get(0) : areaTolerances.get(i));
Copy link
Contributor

Choose a reason for hiding this comment

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

Better to put this complex logic into a function

CoverageEdge edge;
LineSegment edgeKey = (end == start) ? CoverageEdge.key(ring) : CoverageEdge.key(ring, start, end);
if (uniqueEdgeMap.containsKey(edgeKey)) {
edge = uniqueEdgeMap.get(edgeKey);
if (!coverageTolerances.isEmpty()){
edge.setTolerance((edge.getTolerance() < coverageTolerances.get(coverageId)) ? edge.getTolerance() : coverageTolerances.get(coverageId));
Copy link
Contributor

Choose a reason for hiding this comment

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

can this logic be factored out into a function?

@dr-jts
Copy link
Contributor

dr-jts commented May 31, 2024

I'm about to land a relatively complex extension to CoverageSimplifier. But I'll get this one in first. I might need to refactor it however.

@dr-jts
Copy link
Contributor

dr-jts commented May 31, 2024

One of my changes involves adding another tolerance value. Passing all this information down the call chain is getting ugly. I might factor the action of getting a tolerance value out into a strategy class. That can also include the array of per-geom tolerances, which will make this transparent to the rest of the codebase.

@dr-jts
Copy link
Contributor

dr-jts commented May 31, 2024

Actually my changes impact quite a few method signatures. So I'll refactor mine to support a tolerance strategy class, and then that will make refactoring your code on top of that much easier.

@dr-jts
Copy link
Contributor

dr-jts commented May 31, 2024

One thing I'm adding is removing small rings below the tolerance area (of MultiPolygons, or holes). I suppose this can just use the area tolerance of the parent geometry. Does this make sense?

Signed-off-by: N_Strahl <bowsher228@gmail.com>
…erance

Signed-off-by: N_Strahl <bowsher228@gmail.com>
Signed-off-by: N_Strahl <bowsher228@gmail.com>
@nstrahl
Copy link
Author

nstrahl commented Jun 2, 2024

@dr-jts I hope these few changes help you out.

@nstrahl
Copy link
Author

nstrahl commented Jun 2, 2024

One thing I'm adding is removing small rings below the tolerance area (of MultiPolygons, or holes). I suppose this can just use the area tolerance of the parent geometry. Does this make sense?

I do think removing tiny polygons or holes is a good idea. This feature could even be activated via a user flag. This reminds me a lot of the "qgis:eliminateselectedpolygons" processing algorithm in QGIS, which actually works by forwarding the geometry checks to GEOS anyway. So it might be helpful to draw some inspiration from that algorithm.

It's nice to see this exciting new feature being improved. Is there maybe a publication describing the underlying algorithm? If so, could you forward me the DOI?

@dr-jts
Copy link
Contributor

dr-jts commented Jun 2, 2024

I do think removing tiny polygons or holes is a good idea. This feature could even be activated via a user flag.

I was not planning to make this optional, but could do so. Do you think it's useful to have both options?

It's nice to see this exciting new feature being improved. Is there maybe a publication describing the underlying algorithm? If so, could you forward me the DOI?

I'll be writing a blog post soon. It's a fairly simple extension to the current algorithm. When I have a PR ready I'll link to this one.

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

2 participants