/********************************************************************** * * 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 // Forward declarations namespace geos { namespace geom { class Coordinate; class CoordinateSequence; class Geometry; class LineString; class Polygon; } namespace operation { namespace buffer { class OffsetCurveSection; class SegmentMCIndex; } } } using geos::geom::Coordinate; using geos::geom::CoordinateSequence; using geos::geom::Geometry; using geos::geom::GeometryFactory; using geos::geom::LineString; using geos::geom::Polygon; namespace geos { namespace operation { namespace buffer { /** * Computes an offset curve from a geometry. * An offset curve is a linear geometry which is offset a given 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 curve(s) have the same direction as the input line(s). * * The offset curve is based on the boundary of the buffer for the geometry * at the offset distance (see BufferOp). * The normal mode of operation is to return the sections of the buffer boundary * which lie on the raw offset curve * (obtained via rawOffset(LineString, double). * The offset curve will contain multiple sections * if the input self-intersects or has close approaches. * The computed sections are ordered along the raw offset curve. * Sections are disjoint. They never self-intersect, but may be rings. * * * For a LineString the offset curve is a linear geometry * (LineString or MultiLineString). * * For a Point or MultiPoint the offset curve is an empty LineString. * * For a Polygon the offset curve is the boundary of the polygon buffer (which * may be a MultiLineString. * * For a collection the output is a MultiLineString containing * the offset curves of the elements. * * In "joined" mode (see setJoined(bool)) * the sections computed for each input line are joined into a single offset curve line. * The joined curve may self-intersect. * At larger offset distances the curve may contain "flat-line" artifacts * in places where the input self-intersects. * * 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: // Members const Geometry& inputGeom; double distance; bool isJoined = false; BufferParameters bufferParams; double matchDistance; const GeometryFactory* geomFactory; // Methods std::unique_ptr computeCurve( const LineString& lineGeom, double distance); std::vector> computeSections( 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& bufParams); /** * 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 const Polygon* extractMaxAreaPolygon(const Geometry* geom); void computeCurveSections( const CoordinateSequence* bufferRingPts, const CoordinateSequence& rawCurve, std::vector>& sections); /** * Matches the segments in a buffer ring to the raw offset curve * to obtain their match positions (if any). * * @param raw0 a raw curve segment start point * @param raw1 a raw curve segment end point * @param rawCurveIndex the index of the raw curve segment * @param bufferSegIndex the spatial index of the buffer ring segments * @param bufferPts the points of the buffer ring * @param rawCurvePos the raw curve positions of the buffer ring segments * @return the index of the minimum matched buffer segment */ std::size_t matchSegments( const Coordinate& raw0, const Coordinate& raw1, std::size_t rawCurveIndex, SegmentMCIndex& bufferSegIndex, const CoordinateSequence* bufferPts, std::vector& rawCurvePos); static double segmentMatchFrac( const Coordinate& p0, const Coordinate& p1, const Coordinate& seg0, const Coordinate& seg1, double matchDistance); /** * This is only called when there is at least one ring segment matched * (so rawCurvePos has at least one entry != NOT_IN_CURVE). * The start index of the first section must be provided. * This is intended to be the section with lowest position * along the raw curve. * @param ringPts the points in a buffer ring * @param rawCurveLoc the position of buffer ring segments along the raw curve * @param startIndex the index of the start of a section * @param sections the list of extracted offset curve sections */ void extractSections( const CoordinateSequence* ringPts, std::vector& rawCurveLoc, std::size_t startIndex, std::vector>& sections); std::size_t findSectionStart( const std::vector& loc, std::size_t end); std::size_t findSectionEnd( const std::vector& loc, std::size_t start, std::size_t firstStartIndex); static std::size_t nextIndex(std::size_t i, std::size_t size); static std::size_t prevIndex(std::size_t i, std::size_t size); public: // Constants static constexpr int MATCH_DISTANCE_FACTOR = 10000; /** * A QuadSegs minimum value that will prevent generating * unwanted offset curve artifacts near end caps. */ static constexpr int MIN_QUADRANT_SEGMENTS = 8; /** * Creates a new instance for computing an offset curve for a geometry at 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 for left, negative for right) * * @see BufferParameters */ OffsetCurve(const Geometry& geom, double dist) : inputGeom(geom) , distance(dist) , matchDistance(std::abs(dist)/MATCH_DISTANCE_FACTOR) , geomFactory(geom.getFactory()) { if (!std::isfinite(dist)) { throw util::IllegalArgumentException("OffsetCurve distance must be a finite value"); } }; /** * Creates a new instance for computing an offset curve for a geometry at a given distance. * setting the quadrant segments and join style and mitre limit * via {@link BufferParameters}. * * @param geom the geometry to offset * @param dist the offset distance (positive for left, negative for right) * @param bp the buffer parameters to use */ OffsetCurve(const Geometry& geom, double dist, BufferParameters& bp) : inputGeom(geom) , distance(dist) , matchDistance(std::abs(dist)/MATCH_DISTANCE_FACTOR) , geomFactory(geom.getFactory()) { if (!std::isfinite(dist)) { throw util::IllegalArgumentException("OffsetCurve distance must be a finite value"); } //-- set buffer params, leaving cap style as the default CAP_ROUND /** * Prevent using a very small QuadSegs value, to avoid * offset curve artifacts near the end caps. */ int quadSegs = bp.getQuadrantSegments(); if (quadSegs < MIN_QUADRANT_SEGMENTS) { quadSegs = MIN_QUADRANT_SEGMENTS; } bufferParams.setQuadrantSegments(quadSegs); bufferParams.setJoinStyle( bp.getJoinStyle()); bufferParams.setMitreLimit( bp.getMitreLimit()); }; /** * Computes a single curve line for each input linear component, * by joining curve sections in order along the raw offset curve. * The default mode is to compute separate curve sections. * * @param pIsJoined true if joined mode should be used. */ void setJoined(bool pIsJoined); 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); /** * Computes the offset curve of a geometry at a given distance, * joining curve sections into a single line for each input line. * * @param geom a geometry * @param dist the offset distance (positive for left, negative for right) * @return the joined offset curve */ static std::unique_ptr getCurveJoined( const Geometry& geom, double dist); /** * Gets the computed offset curve lines. * * @return the offset curve geometry */ std::unique_ptr getCurve(); /** * Gets the raw offset curve for a line at a given distance. * The quadrant segments, join style and mitre limit can be specified * via BufferParameters. * * The raw offset line may contain loops and other artifacts which are * not present in the true offset curve. * * @param line the line to offset * @param distance the offset distance (positive for left, negative for right) * @param bufParams the buffer parameters to use * @return the raw offset curve points */ static std::unique_ptr rawOffsetCurve( const LineString& line, double distance, BufferParameters& bufParams); /** * Gets the raw offset curve for a line at a given distance, * with default buffer parameters. * * @param line the line to offset * @param distance the offset distance (positive for left, negative for right) * @return the raw offset curve points */ static std::unique_ptr rawOffset( const LineString& line, double distance); }; } // namespace geos::operation::buffer } // namespace geos::operation } // namespace geos