Is there a way to convert a self intersecting polygon to a multipolygon in JTS?

前端 未结 1 1254
野性不改
野性不改 2021-01-30 13:53

Take the invalid polygon POLYGON((0 100, 100 100, 0 0, 100 0, 0 100)) - an egg timer shape with an undeclared point of intersection

相关标签:
1条回答
  • 2021-01-30 14:42

    JTS seems to offer the behaviour I require, though I had to do a little legwork in my own code. The validate function I wrote breaks down a polygon/multipolygon into a collection of non self intersecting linestrings, and then uses the Polygonizer class to build polygons from the result. I have tested it on the following (limited) set of inputs, and it seems to behave the way I require:

        POLYGON((0 100, 100 100, 0 0, 100 0, 0 100))
        POLYGON((0 0, 0 100, 100 100, 100 0, 0 0))
        MULTIPOLYGON(((0 0, 0 100, 100 100, 100 0, 0 0)),((50 50, 50 150, 150 150, 150 50, 50 50)))
        POLYGON((0 0, 50 50, 100 0, 150 0, 200 50, 250 0, 0 0))
    

    Code:

    /**
     * Get / create a valid version of the geometry given. If the geometry is a polygon or multi polygon, self intersections /
     * inconsistencies are fixed. Otherwise the geometry is returned.
     * 
     * @param geom
     * @return a geometry 
     */
    public static Geometry validate(Geometry geom){
        if(geom instanceof Polygon){
            if(geom.isValid()){
                geom.normalize(); // validate does not pick up rings in the wrong order - this will fix that
                return geom; // If the polygon is valid just return it
            }
            Polygonizer polygonizer = new Polygonizer();
            addPolygon((Polygon)geom, polygonizer);
            return toPolygonGeometry(polygonizer.getPolygons(), geom.getFactory());
        }else if(geom instanceof MultiPolygon){
            if(geom.isValid()){
                geom.normalize(); // validate does not pick up rings in the wrong order - this will fix that
                return geom; // If the multipolygon is valid just return it
            }
            Polygonizer polygonizer = new Polygonizer();
            for(int n = geom.getNumGeometries(); n-- > 0;){
                addPolygon((Polygon)geom.getGeometryN(n), polygonizer);
            }
            return toPolygonGeometry(polygonizer.getPolygons(), geom.getFactory());
        }else{
            return geom; // In my case, I only care about polygon / multipolygon geometries
        }
    }
    
    /**
     * Add all line strings from the polygon given to the polygonizer given
     * 
     * @param polygon polygon from which to extract line strings
     * @param polygonizer polygonizer
     */
    static void addPolygon(Polygon polygon, Polygonizer polygonizer){
        addLineString(polygon.getExteriorRing(), polygonizer);
        for(int n = polygon.getNumInteriorRing(); n-- > 0;){
            addLineString(polygon.getInteriorRingN(n), polygonizer);
        }
    }
    
    /**
     * Add the linestring given to the polygonizer
     * 
     * @param linestring line string
     * @param polygonizer polygonizer
     */
    static void addLineString(LineString lineString, Polygonizer polygonizer){
    
        if(lineString instanceof LinearRing){ // LinearRings are treated differently to line strings : we need a LineString NOT a LinearRing
            lineString = lineString.getFactory().createLineString(lineString.getCoordinateSequence());
        }
    
        // unioning the linestring with the point makes any self intersections explicit.
        Point point = lineString.getFactory().createPoint(lineString.getCoordinateN(0));
        Geometry toAdd = lineString.union(point); 
    
        //Add result to polygonizer
        polygonizer.add(toAdd);
    }
    
    /**
     * Get a geometry from a collection of polygons.
     * 
     * @param polygons collection
     * @param factory factory to generate MultiPolygon if required
     * @return null if there were no polygons, the polygon if there was only one, or a MultiPolygon containing all polygons otherwise
     */
    static Geometry toPolygonGeometry(Collection<Polygon> polygons, GeometryFactory factory){
        switch(polygons.size()){
            case 0:
                return null; // No valid polygons!
            case 1:
                return polygons.iterator().next(); // single polygon - no need to wrap
            default:
                //polygons may still overlap! Need to sym difference them
                Iterator<Polygon> iter = polygons.iterator();
                Geometry ret = iter.next();
                while(iter.hasNext()){
                    ret = ret.symDifference(iter.next());
                }
                return ret;
        }
    }
    
    0 讨论(0)
提交回复
热议问题