-
Notifications
You must be signed in to change notification settings - Fork 358
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* References qgis/QGIS#53165 * References #897
- Loading branch information
Showing
5 changed files
with
1,014 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,328 @@ | ||
/********************************************************************** | ||
* | ||
* GEOS - Geometry Engine Open Source | ||
* http://geos.osgeo.org | ||
* | ||
* Copyright (C) 2021 Paul Ramsey <pramsey@cleverelephant.ca> | ||
* | ||
* 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 <geos/export.h> | ||
|
||
#include <geos/operation/buffer/BufferParameters.h> | ||
#include <geos/geom/GeometryFactory.h> | ||
#include <geos/constants.h> | ||
|
||
// 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<Geometry> computeCurve( | ||
const LineString& lineGeom, double distance); | ||
|
||
std::vector<std::unique_ptr<OffsetCurveSection>> computeSections( | ||
const LineString& lineGeom, double distance); | ||
|
||
std::unique_ptr<LineString> offsetSegment( | ||
const CoordinateSequence* pts, double distance); | ||
|
||
static std::unique_ptr<Polygon> 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<std::unique_ptr<OffsetCurveSection>>& 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<double>& 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<double>& rawCurveLoc, | ||
std::size_t startIndex, | ||
std::vector<std::unique_ptr<OffsetCurveSection>>& sections); | ||
|
||
std::size_t findSectionStart( | ||
const std::vector<double>& loc, | ||
std::size_t end); | ||
|
||
std::size_t findSectionEnd( | ||
const std::vector<double>& 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<Geometry> getCurve( | ||
const Geometry& geom, | ||
double dist, | ||
int quadSegs, | ||
BufferParameters::JoinStyle joinStyle, | ||
double mitreLimit); | ||
|
||
static std::unique_ptr<Geometry> 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<Geometry> getCurveJoined( | ||
const Geometry& geom, double dist); | ||
|
||
/** | ||
* Gets the computed offset curve lines. | ||
* | ||
* @return the offset curve geometry | ||
*/ | ||
std::unique_ptr<Geometry> 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<CoordinateSequence> 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<CoordinateSequence> rawOffset( | ||
const LineString& line, | ||
double distance); | ||
|
||
}; | ||
|
||
} // namespace geos::operation::buffer | ||
} // namespace geos::operation | ||
} // namespace geos | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.