javageometryconvex-hulljts

Simplified outer convex hull of a set of points using JTS


I'd like to use JTS to calculate a simplified (fewer points) convex hull from a collection of points including inner and perimeter points, where the hull is allowed to be a bit larger in area in order to simplify, but must contain all of the points. I think that PolygonHullSimplifier.hullByAreaDelta is designed for that.

Code snippet here (same data as plots but in different units.) https://gist.github.com/joshgold22/11d39c85456271109b2421dd124be241

I tried creating a MultiPoint, calling convexHull() on it, and passing that to PolygonHullSimplifier.hullByAreaDelta(hull, true /*outer*/, 0.1 /*areaDeltaRatio*/)

There were two surprises for me. First, convexHull didn't seem to return a convexHull, though it did remove many points. Showing the original set and then the result of convexHull():

plot of all points

plot of convexHull result, but not convex

Second, no matter how I play with the areaDeltaRatio, the result of hullByAreaDelta was always identical to the basic (intermediate) convex hull Polygon. (It did differ when I tried an inner hull.)

I'll add one more picture, of a DouglasPeuckerSimplifier simplification, which does work, but which I understand would not be guaranteed to contain all of the original points.

Can you help clarify what I'm misunderstanding and show me how to do this?

Douglas Peucker simplification


Solution

  • What’s likely happening?

    What are your options?

    What might fix your code approach?

    Make sure the input to hullByAreaDelta is a polygon with multiple points and some detail, not just a convex hull polygon

    If you want a simplified convex polygon enclosing all points, try this:

    Compute the convex hull polygon from your MultiPoint.

    Then try a polygon simplifier that preserves convexity or implement a heuristic:

    For example, attempt to remove vertices one by one, checking if the polygon remains convex and contains all points.

    Unfortunately, JTS does not have a built-in simplified convex hull with tolerance.

    Quick test you can do in JTS

    MultiPoint multiPoint = ... // your points
    Geometry convexHull = multiPoint.convexHull();
    
    // convexHull is a Polygon
    System.out.println("Hull points: " + convexHull.getNumPoints());
    
    // Try simplifying with DouglasPeucker - might break containment
    Geometry simplified = DouglasPeuckerSimplifier.simplify(convexHull, tolerance);
    
    // Try hullByAreaDelta on the convex hull polygon (may not simplify)
    Geometry simplifiedByAreaDelta = PolygonHullSimplifier.hullByAreaDelta(convexHull, true, 0.1);
    
    // Print/plot and compare areas, number of points
    System.out.println("Original area: " + convexHull.getArea());
    System.out.println("Simplified area: " + simplified.getArea());
    System.out.println("Simplified by area delta area: " + simplifiedByAreaDelta.getArea());
    

    You will likely see no difference between convexHull and hullByAreaDelta output because convex polygons are minimal for convexity.

    Summary

    convexHull() returns the minimal convex polygon; it won’t simplify further or reduce points unless some are exactly collinear or duplicated.

    PolygonHullSimplifier.hullByAreaDelta simplifies polygons allowing area increase but won’t simplify the minimal convex polygon further.

    If you want a simplified hull that contains all points but is simpler than the convex hull, you need a polygon more complex than the convex hull (e.g., alpha shape), then simplify outward.

    Alternatively, create your own routine to reduce convex hull points under convexity and containment constraints.