/********************************************************************** * * GEOS - Geometry Engine Open Source * http://geos.osgeo.org * * Copyright (C) 2021 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 #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 LineString; class LinearRing; class Polygon; class CoordinateSequence; class Coordinate; } namespace operation { namespace buffer { class SegmentMCIndex; } } namespace index { namespace chain { class MonotoneChain; } } } using geos::geom::Geometry; using geos::geom::GeometryFactory; using geos::geom::LineString; using geos::geom::LinearRing; using geos::geom::Polygon; using geos::geom::CoordinateSequence; using geos::geom::Coordinate; namespace geos { namespace operation { namespace buffer { /** * Computes an offset curve from a geometry. * The offset curve is a linear geometry which is offset a specified distance * from the input. * If the offset distance is positive the curve lies on the left side of the input; * if it is negative the curve is on the right side. * * The offset curve of a line is a LineString which * The offset curve of a Point is an empty LineString. * The offset curve of a Polygon is the boundary of the polygon buffer (which * may be a MultiLineString. * For a collection the output is a MultiLineString of the element offset curves. * * The offset curve is computed as a single contiguous section of the geometry buffer boundary. * In some geometric situations this definition is ill-defined. * This algorithm provides a "best-effort" interpretation. * In particular: * * * For self-intersecting lines, the buffer boundary includes * offset lines for both left and right sides of the input line. * Only a single contiguous portion on the specified side is returned. * * If the offset corresponds to buffer holes, only the largest hole is used. * * Offset curves support setting the number of quadrant segments, * the join style, and the mitre limit (if applicable) via * the BufferParameters. * * @author Martin Davis */ class GEOS_DLL OffsetCurve { private: // Constants static constexpr int NEARNESS_FACTOR = 10000; // Members const Geometry& inputGeom; double distance; BufferParameters bufferParams; double matchDistance; const GeometryFactory* geomFactory; // Methods std::unique_ptr computeCurve(const LineString& lineGeom, double distance); std::unique_ptr offsetSegment(const CoordinateSequence* pts, double distance); static std::unique_ptr getBufferOriented(const LineString& geom, double distance, BufferParameters& bufParms); /** * Extracts the largest polygon by area from a geometry. * Used here to avoid issues with non-robust buffer results which have spurious extra polygons. * * @param geom a geometry * @return the polygon element of largest area */ static std::unique_ptr extractMaxAreaPolygon(const Geometry& geom); static std::unique_ptr extractLongestHole(const Polygon& poly); std::unique_ptr computeCurve( const CoordinateSequence* bufferPts, std::vector& rawOffsetList); int markMatchingSegments(const Coordinate& p0, const Coordinate& p1, SegmentMCIndex& segIndex, const CoordinateSequence* bufferPts, std::vector& isInCurve); static double subsegmentMatchFrac(const Coordinate& p0, const Coordinate& p1, const Coordinate& seg0, const Coordinate& seg1, double matchDistance); /** * Extracts a section of a ring of coordinates, starting at a given index, * and keeping coordinates which are flagged as being required. * * @param ring the ring of points * @param startIndex the index of the start coordinate * @param isExtracted flag indicating if coordinate is to be extracted * @return */ static void extractSection(const CoordinateSequence* ring, int iStartIndex, std::vector& isExtracted, std::vector& extractedPoints); static std::size_t next(std::size_t i, std::size_t size); /* private */ class MatchCurveSegmentAction : public index::chain::MonotoneChainSelectAction { private: const Coordinate& p0; const Coordinate& p1; const CoordinateSequence* bufferPts; double matchDistance; std::vector& isInCurve; double minFrac = -1; int minCurveIndex = -1; public: MatchCurveSegmentAction( const Coordinate& p_p0, const Coordinate& p_p1, const CoordinateSequence* p_bufferPts, double p_matchDistance, std::vector& p_isInCurve) : p0(p_p0) , p1(p_p1) , bufferPts(p_bufferPts) , matchDistance(p_matchDistance) , isInCurve(p_isInCurve) , minFrac(-1) , minCurveIndex(-1) {}; void select(const index::chain::MonotoneChain& mc, std::size_t segIndex) override; void select(const geom::LineSegment& seg) override { (void)seg; return; }; int getMinCurveIndex() { return minCurveIndex; } }; public: /** * Creates a new instance for computing an offset curve for a geometryat a given distance. * with default quadrant segments BufferParameters::DEFAULT_QUADRANT_SEGMENTS * and join style BufferParameters::JOIN_STYLE. * * @param geom the geometry to offset * @param dist the offset distance (positive = left, negative = right) * * \see BufferParameters */ OffsetCurve(const Geometry& geom, double dist) : inputGeom(geom) , distance(dist) , matchDistance(std::abs(dist)/NEARNESS_FACTOR) , geomFactory(geom.getFactory()) {}; /** * Creates a new instance for computing an offset curve for a geometry at a given distance. * allowing the quadrant segments and join style and mitre limit to be set * via BufferParameters. * * @param geom * @param dist * @param bp */ OffsetCurve(const Geometry& geom, double dist, BufferParameters& bp) : inputGeom(geom) , distance(dist) , bufferParams(bp) , matchDistance(std::abs(dist)/NEARNESS_FACTOR) , geomFactory(geom.getFactory()) {}; /** * Computes the offset curve of a geometry at a given distance, * and for a specified quadrant segments, join style and mitre limit. * * @param geom a geometry * @param dist the offset distance (positive = left, negative = right) * @param quadSegs the quadrant segments (-1 for default) * @param joinStyle the join style (-1 for default) * @param mitreLimit the mitre limit (-1 for default) * @return the offset curve */ static std::unique_ptr getCurve( const Geometry& geom, double dist, int quadSegs, BufferParameters::JoinStyle joinStyle, double mitreLimit); static std::unique_ptr getCurve(const Geometry& geom, double dist); std::unique_ptr getCurve(); /** * Gets the raw offset line. * The quadrant segments and join style and mitre limit to be set * via BufferParameters. * * The raw offset line may contain loops and other artifacts which are * not present in the true offset curve. * The raw offset line is matched to the buffer ring (which is clean) * to extract the offset curve. * * @param geom the linestring to offset * @param dist the offset distance * @param bufParams the buffer parameters to use * @param lineList the vector to populate with the return value */ static void rawOffset(const LineString& geom, double dist, BufferParameters& bufParams, std::vector& lineList); static void rawOffset(const LineString& geom, double dist, std::vector& lineList); }; } // namespace geos::operation::buffer } // namespace geos::operation } // namespace geos #ifdef _MSC_VER #pragma warning(pop) #endif