/********************************************************************** * * GEOS - Geometry Engine Open Source * http://geos.osgeo.org * * Copyright (C) 2011 Sandro Santilli * Copyright (C) 2006 Refractions Research Inc. * * 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. * ********************************************************************** * * Last port: operation/buffer/BufferCurveSetBuilder.java 4c343e79f (JTS-1.19) * **********************************************************************/ #pragma once #include #include #include #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class #endif // Forward declarations namespace geos { namespace geom { class Geometry; class CoordinateSequence; class PrecisionModel; class GeometryCollection; class Point; class LineString; class LinearRing; class Polygon; } namespace geomgraph { class Label; } namespace noding { class SegmentString; } namespace operation { namespace buffer { class BufferParameters; } } } namespace geos { namespace operation { // geos.operation namespace buffer { // geos.operation.buffer /** * \class BufferCurveSetBuilder * * \brief * Creates all the raw offset curves for a buffer of a Geometry. * * Raw curves need to be noded together and polygonized to form the * final buffer area. * */ class GEOS_DLL BufferCurveSetBuilder { private: static constexpr int MAX_INVERTED_RING_SIZE = 9; static constexpr int INVERTED_CURVE_VERTEX_FACTOR = 4; static constexpr double NEARNESS_FACTOR = 0.99; // To keep track of newly-created Labels. // Labels will be released by object dtor std::vector newLabels; const geom::Geometry& inputGeom; double distance; OffsetCurveBuilder curveBuilder; /// The raw offset curves computed. /// This class holds ownership of std::vector elements. /// std::vector curveList; bool isInvertOrientation = false; /** * Creates a noding::SegmentString for a coordinate list which is a raw * offset curve, and adds it to the list of buffer curves. * The noding::SegmentString is tagged with a geomgraph::Label * giving the topology of the curve. * The curve may be oriented in either direction. * If the curve is oriented CW, the locations will be: * - Left: Location.EXTERIOR * - Right: Location.INTERIOR * * @param coord is raw offset curve, ownership transferred here */ void addCurve(geom::CoordinateSequence* coord, geom::Location leftLoc, geom::Location rightLoc); void add(const geom::Geometry& g); void addCollection(const geom::GeometryCollection* gc); /** * Add a Point to the graph. */ void addPoint(const geom::Point* p); void addLineString(const geom::LineString* line); void addPolygon(const geom::Polygon* p); void addRingBothSides(const geom::CoordinateSequence* coord, double p_distance); /** * Add an offset curve for a polygon ring. * The side and left and right topological location arguments * assume that the ring is oriented CW. * If the ring is in the opposite orientation, * the left and right locations must be interchanged and the side * flipped. * * @param coord the coordinates of the ring (must not contain * repeated points) * @param offsetDistance the distance at which to create the buffer * @param side the side of the ring on which to construct the buffer * line * @param cwLeftLoc the location on the L side of the ring * (if it is CW) * @param cwRightLoc the location on the R side of the ring * (if it is CW) */ void addRingSide(const geom::CoordinateSequence* coord, double offsetDistance, int side, geom::Location cwLeftLoc, geom::Location cwRightLoc); /** * Tests whether the offset curve for a ring is fully inverted. * An inverted ("inside-out") curve occurs in some specific situations * involving a buffer distance which should result in a fully-eroded (empty) buffer. * It can happen that the sides of a small, convex polygon * produce offset segments which all cross one another to form * a curve with inverted orientation. * This happens at buffer distances slightly greater than the distance at * which the buffer should disappear. * The inverted curve will produce an incorrect non-empty buffer (for a shell) * or an incorrect hole (for a hole). * It must be discarded from the set of offset curves used in the buffer. * Heuristics are used to reduce the number of cases which area checked, * for efficiency and correctness. *

* See https://github.com/locationtech/jts/issues/472 * * @param inputPts the input ring * @param distance the buffer distance * @param curvePts the generated offset curve * @return true if the offset curve is inverted */ static bool isRingCurveInverted( const geom::CoordinateSequence* inputPts, double dist, const geom::CoordinateSequence* curvePts); /** * Computes the maximum distance out of a set of points to a linestring. * * @param pts the points * @param line the linestring vertices * @return the maximum distance */ static double maxDistance( const geom::CoordinateSequence* pts, const geom::CoordinateSequence* line); /** * The ringCoord is assumed to contain no repeated points. * It may be degenerate (i.e. contain only 1, 2, or 3 points). * In this case it has no area, and hence has a minimum diameter of 0. * * @param ringCoord * @param bufferDistance * @return */ bool isErodedCompletely(const geom::LinearRing* ringCoord, double bufferDistance); /** * Tests whether a triangular ring would be eroded completely by * the given buffer distance. * This is a precise test. It uses the fact that the inner buffer * of a triangle converges on the inCentre of the triangle (the * point equidistant from all sides). If the buffer distance is * greater than the distance of the inCentre from a side, the * triangle will be eroded completely. * * This test is important, since it removes a problematic case where * the buffer distance is slightly larger than the inCentre distance. * In this case the triangle buffer curve "inverts" with incorrect * topology, producing an incorrect hole in the buffer. * * @param triCoord * @param bufferDistance * @return */ bool isTriangleErodedCompletely(const geom::CoordinateSequence* triCoords, double bufferDistance); // Declare type as noncopyable BufferCurveSetBuilder(const BufferCurveSetBuilder& other) = delete; BufferCurveSetBuilder& operator=(const BufferCurveSetBuilder& rhs) = delete; /** * Computes orientation of a ring using a signed-area orientation test. * For invalid (self-crossing) rings this ensures the largest enclosed area * is taken to be the interior of the ring. * This produces a more sensible result when * used for repairing polygonal geometry via buffer-by-zero. * For buffer, using the lower robustness of orientation-by-area * doesn't matter, since narrow or flat rings * produce an acceptable offset curve for either orientation. * * @param coord the ring coordinates * @return true if the ring is CCW */ bool isRingCCW(const geom::CoordinateSequence* coords) const; public: /// Constructor BufferCurveSetBuilder( const geom::Geometry& newInputGeom, double newDistance, const geom::PrecisionModel* newPm, const BufferParameters& newBufParams) : inputGeom(newInputGeom) , distance(newDistance) , curveBuilder(newPm, newBufParams) , curveList() , isInvertOrientation(false) {}; /// Destructor ~BufferCurveSetBuilder(); /** \brief * Computes the set of raw offset curves for the buffer. * * Each offset curve has an attached {@link geomgraph::Label} indicating * its left and right location. * * @return a Collection of SegmentStrings representing the raw buffer curves */ std::vector& getCurves(); /// \brief Add raw curves for a set of CoordinateSequences. /// /// @param lineList is a list of CoordinateSequence, ownership /// of which is transferred here /// @param leftLoc left location /// @param rightLoc right location /// void addCurves(const std::vector& lineList, geom::Location leftLoc, geom::Location rightLoc); /** * Sets whether the offset curve is generated * using the inverted orientation of input rings. * This allows generating a buffer(0) polygon from the smaller lobes * of self-crossing rings. * * @param p_isInvertOrientation true if input ring orientation should be inverted */ void setInvertOrientation(bool p_isInvertOrientation) { isInvertOrientation = p_isInvertOrientation; } }; } // namespace geos::operation::buffer } // namespace geos::operation } // namespace geos #ifdef _MSC_VER #pragma warning(pop) #endif