329 lines
11 KiB
C
329 lines
11 KiB
C
|
/**********************************************************************
|
||
|
*
|
||
|
* 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
|
||
|
|
||
|
|
||
|
|