/********************************************************************** * * GEOS - Geometry Engine Open Source * http://geos.osgeo.org * * Copyright (C) 2022 Paul Ramsey * * This is free software; you can redistribute and/or modify it under * the terms of the GNU Lesser General Public Licence as published * by the Free Software Foundation. * See the COPYING file for more information. * **********************************************************************/ #pragma once #include #include #include #include namespace geos { namespace geom { class Coordinate; class CoordinateSequence; class Envelope; class Geometry; class GeometryCollection; class GeometryFactory; class LinearRing; class Polygon; } namespace triangulate { namespace tri { class Tri; } } } #include using geos::geom::Coordinate; using geos::geom::CoordinateSequence; using geos::geom::Envelope; using geos::geom::Geometry; using geos::geom::GeometryCollection; using geos::geom::GeometryFactory; using geos::geom::LinearRing; using geos::geom::Polygon; using geos::triangulate::tri::Tri; using geos::triangulate::tri::TriList; namespace geos { namespace algorithm { // geos::algorithm namespace hull { // geos::algorithm::hull /** * Constructs a concave hull of a set of polygons, respecting * the polygons as constraints. * A concave hull is a possibly non-convex polygon containing all the input polygons. * A given set of polygons has a sequence of hulls of increasing concaveness, * determined by a numeric target parameter. * The computed hull "fills the gap" between the polygons, * and does not intersect their interior. * * The concave hull is constructed by removing the longest outer edges * of the Delaunay Triangulation of the space between the polygons, * until the target criterion parameter is reached. * * The target criteria are: * * Maximum Edge Length - the length of the longest edge between the polygons is no larger * than this value. * * Maximum Edge Length Ratio - determine the Maximum Edge Length * as a fraction of the difference between the longest and shortest edge lengths * between the polygons. This normalizes the Maximum Edge Length to be scale-free. * A value of 1 produces the convex hull; a value of 0 produces the original polygons. * * The preferred criterion is the Maximum Edge Length Ratio, since it is * scale-free and local (so that no assumption needs to be made about the * total amount of concaveness present). * * Optionally the concave hull can be allowed to contain holes, via setHolesAllowed(). * * The hull can be specified as being "tight", which means it follows the outer boundaries * of the input polygons. * * The input polygons must form a valid MultiPolygon * (i.e. they must be non-overlapping). * * \author Martin Davis * */ class GEOS_DLL ConcaveHullOfPolygons { private: /* Members */ static constexpr int FRAME_EXPAND_FACTOR = 4; const Geometry* inputPolygons; const GeometryFactory* geomFactory; double maxEdgeLength; double maxEdgeLengthRatio; bool isHolesAllowed; bool isTight; std::set hullTris; std::deque borderTriQue; std::vector polygonRings; TriList triList; /** * Records the edge index of the longest border edge for border tris, * so it can be tested for length and possible removal. */ std::map borderEdgeMap; /* Methods */ std::unique_ptr createEmptyHull(); static void extractShellRings( const Geometry* polygons, std::vector& rings); void buildHullTris(); /** * Creates a rectangular "frame" around the input polygons, * with the input polygons as holes in it. * The frame is large enough that the constrained Delaunay triangulation * of it should contain the convex hull of the input as edges. * The frame corner triangles can be removed to produce a * triangulation of the space around and between the input polygons. * * @param polygonsEnv * @return the frame polygon */ std::unique_ptr createFrame( const Envelope* polygonsEnv); double computeTargetEdgeLength( TriList& triList, const CoordinateSequence* frameCorners, double edgeLengthRatio) const; bool isFrameTri( const Tri* tri, const CoordinateSequence* frameCorners) const; void removeFrameCornerTris( TriList& tris, const CoordinateSequence* frameCorners); /** * Get the tri vertex index of some point in a list, * or -1 if none are vertices. * * @param tri the tri to test for containing a point * @param pts the points to test * @return the vertex index of a point, or -1 */ TriIndex vertexIndex( const Tri* tri, const CoordinateSequence* pts) const; void removeBorderTris(); void removeHoleTris(); Tri* findHoleSeedTri() const; bool isHoleSeedTri(const Tri* tri) const; bool isBorderTri(const Tri* tri) const; bool isRemovable(const Tri* tri) const; /** * Tests whether a triangle touches a single polygon at all vertices. * If so, it is a candidate for removal if the hull polygon * is being kept tight to the outer boundary of the input polygons. * Tris which touch more than one polygon are called "bridging". * * @param tri * @return true if the tri touches a single polygon */ bool isTouchingSinglePolygon(const Tri* tri) const; void addBorderTris(Tri* tri); /** * Adds an adjacent tri to the current border. * The adjacent edge is recorded as the border edge for the tri. * Note that only edges adjacent to another tri can become border edges. * Since constraint-adjacent edges do not have an adjacent tri, * they can never be on the border and thus will not be removed * due to being shorter than the length threshold. * The tri containing them may still be removed via another edge, however. * * @param tri the tri adjacent to the tri to be added to the border * @param index the index of the adjacent tri */ void addBorderTri(Tri* tri, TriIndex index); void removeBorderTri(Tri* tri); bool hasAllVertices(const LinearRing* ring, const Tri* tri) const; bool hasVertex(const LinearRing* ring, const Coordinate& v) const; void envelope(const Tri* tri, Envelope& env) const; std::unique_ptr createHullGeometry(bool isIncludeInput); public: /** * Computes a concave hull of set of polygons * using the target criterion of maximum edge length. * * @param polygons the input polygons * @param maxLength the target maximum edge length * @return the concave hull */ static std::unique_ptr concaveHullByLength(const Geometry* polygons, double maxLength); /** * Computes a concave hull of set of polygons * using the target criterion of maximum edge length, * and allowing control over whether the hull boundary is tight * and can contain holes. * * @param polygons the input polygons * @param maxLength the target maximum edge length * @param isTight true if the hull should be tight to the outside of the polygons * @param isHolesAllowed true if holes are allowed in the hull polygon * @return the concave hull */ static std::unique_ptr concaveHullByLength( const Geometry* polygons, double maxLength, bool isTight, bool isHolesAllowed); /** * Computes a concave hull of set of polygons * using the target criterion of maximum edge length ratio. * * @param polygons the input polygons * @param lengthRatio the target maximum edge length ratio * @return the concave hull */ static std::unique_ptr concaveHullByLengthRatio(const Geometry* polygons, double lengthRatio); /** * Computes a concave hull of set of polygons * using the target criterion of maximum edge length ratio, * and allowing control over whether the hull boundary is tight * and can contain holes. * * @param polygons the input polygons * @param lengthRatio the target maximum edge length ratio * @param isTight true if the hull should be tight to the outside of the polygons * @param isHolesAllowed true if holes are allowed in the hull polygon * @return the concave hull */ static std::unique_ptr concaveHullByLengthRatio( const Geometry* polygons, double lengthRatio, bool isTight, bool isHolesAllowed); /** * Computes a concave fill area between a set of polygons, * using the target criterion of maximum edge length. * * @param polygons the input polygons * @param maxLength the target maximum edge length * @return the concave fill */ static std::unique_ptr concaveFillByLength(const Geometry* polygons, double maxLength); /** * Computes a concave fill area between a set of polygons, * using the target criterion of maximum edge length ratio. * * @param polygons the input polygons * @param lengthRatio the target maximum edge length ratio * @return the concave fill */ static std::unique_ptr concaveFillByLengthRatio(const Geometry* polygons, double lengthRatio); /** * Creates a new instance for a given geometry. * * @param geom the input geometry */ ConcaveHullOfPolygons(const Geometry* geom); /** * Sets the target maximum edge length for the concave hull. * The length value must be zero or greater. * * * The value 0.0 produces the input polygons. * * Larger values produce less concave results. * Above a certain large value the result is the convex hull of the input. * * The edge length ratio provides a scale-free parameter which * is intended to produce similar concave results for a variety of inputs. * * @param edgeLength a non-negative length */ void setMaximumEdgeLength(double edgeLength); /** * Sets the target maximum edge length ratio for the concave hull. * The edge length ratio is a fraction of the difference * between the longest and shortest edge lengths * in the Delaunay Triangulation of the area between the input polygons. * (Roughly speaking, it is a fraction of the difference between * the shortest and longest distances between the input polygons.) * It is a value in the range 0 to 1. * * * The value 0.0 produces the original input polygons. * * The value 1.0 produces the convex hull. * * @param edgeLengthRatio a length factor value between 0 and 1 */ void setMaximumEdgeLengthRatio(double edgeLengthRatio); /** * Sets whether holes are allowed in the concave hull polygon. * * @param p_isHolesAllowed true if holes are allowed in the result */ void setHolesAllowed(bool p_isHolesAllowed); /** * Sets whether the boundary of the hull polygon is kept * tight to the outer edges of the input polygons. * * @param p_isTight true if the boundary is kept tight */ void setTight(bool p_isTight); /** * Gets the computed concave hull. * * @return the concave hull */ std::unique_ptr getHull(); /** * Gets the concave fill, which is the area between the input polygons, * subject to the concaveness control parameter. * * @return the concave fill */ std::unique_ptr getFill(); }; } // geos::algorithm::hull } // geos::algorithm } // geos