[geos-commits] [SCM] GEOS branch 3.11 updated. 9e4fb70897a2abcd70a1c8831d2ddedd65f7c5d8
git at osgeo.org
git at osgeo.org
Tue Oct 22 14:00:42 PDT 2024
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GEOS".
The branch, 3.11 has been updated
via 9e4fb70897a2abcd70a1c8831d2ddedd65f7c5d8 (commit)
via 5297e1e7d87a58708c2389a4bcce3e8b4f1f57e5 (commit)
from 246ec0eabea9779c81bd9aca7a00752d948d7e21 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 9e4fb70897a2abcd70a1c8831d2ddedd65f7c5d8
Merge: 5297e1e7d 246ec0eab
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Tue Oct 22 14:00:10 2024 -0700
Merge branch '3.11' of github.com:libgeos/geos into 3.11
diff --cc NEWS.md
index 9ac8bfc49,fbd9f9095..f9b07564b
--- a/NEWS.md
+++ b/NEWS.md
@@@ -5,7 -5,7 +5,8 @@@
- Centroid: Fix crash on polygons with empty holes (GH-1075, Dan Baston)
- MinimumClearance: Fix crash on NaN inputs (GH-1082, Dan Baston)
- GEOSRelatePatternMatch: Fix crash on invalid DE-9IM pattern (GH-1089, Dan Baston)
+ - Port TopologyPreservingSimplifier fixes (GH-986, GH-1107, GH-857, GH-784, GH-1070, Paul Ramsey)
+ - Fix ConcaveHullOfPolygons nested shell handling (GH-1169, Martin Davis)
## Changes in 3.11.4
2024-06-05
commit 5297e1e7d87a58708c2389a4bcce3e8b4f1f57e5
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Tue Oct 22 13:59:03 2024 -0700
Port TopologyPreservingSimplifier fixes (GH-986, GH-1107, GH-857, GH-784, GH-1070)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 132ddef3e..01eee4602 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -121,7 +121,7 @@ jobs:
- name: 'Upload Valgrind Log'
if: failure()
- uses: actions/upload-artifact at v2
+ uses: actions/upload-artifact at v3
with:
name: valgrind-log
path: build.cmake/Testing/Temporary/MemoryChecker.**.log
diff --git a/NEWS.md b/NEWS.md
index 3e2bf5ee1..9ac8bfc49 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -5,6 +5,7 @@
- Centroid: Fix crash on polygons with empty holes (GH-1075, Dan Baston)
- MinimumClearance: Fix crash on NaN inputs (GH-1082, Dan Baston)
- GEOSRelatePatternMatch: Fix crash on invalid DE-9IM pattern (GH-1089, Dan Baston)
+ - Port TopologyPreservingSimplifier fixes (GH-986, GH-1107, GH-857, GH-784, GH-1070, Paul Ramsey)
## Changes in 3.11.4
2024-06-05
diff --git a/include/geos/algorithm/RayCrossingCounter.h b/include/geos/algorithm/RayCrossingCounter.h
index cc35b3647..f16d52d59 100644
--- a/include/geos/algorithm/RayCrossingCounter.h
+++ b/include/geos/algorithm/RayCrossingCounter.h
@@ -65,7 +65,7 @@ class GEOS_DLL RayCrossingCounter {
private:
const geom::Coordinate& point;
- int crossingCount;
+ std::size_t crossingCount;
// true if the test point lies on an input segment
bool isPointOnSegment;
@@ -144,6 +144,8 @@ public:
*/
bool isPointInPolygon() const;
+ std::size_t getCount() const { return crossingCount; };
+
};
} // geos::algorithm
diff --git a/include/geos/simplify/ComponentJumpChecker.h b/include/geos/simplify/ComponentJumpChecker.h
new file mode 100644
index 000000000..59fdd2ecb
--- /dev/null
+++ b/include/geos/simplify/ComponentJumpChecker.h
@@ -0,0 +1,120 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://libgeos.org
+ *
+ * Copyright (C) 2006 Refractions Research Inc.
+ * Copyright (C) 2023 Martin Davis <mtnclimb at gmail.com>
+ * Copyright (C) 2023 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#pragma once
+
+#include <geos/export.h>
+#include <vector>
+#include <memory>
+
+
+// Forward declarations
+namespace geos {
+namespace geom {
+class Coordinate;
+class CoordinateSequence;
+class Envelope;
+class LineSegment;
+}
+namespace simplify {
+class TaggedLineString;
+}
+}
+
+using geos::geom::Coordinate;
+using geos::geom::Envelope;
+using geos::geom::LineSegment;
+
+namespace geos {
+namespace simplify { // geos::simplify
+
+
+class GEOS_DLL ComponentJumpChecker {
+
+private:
+
+ const std::vector<TaggedLineString*>& components;
+
+ static bool hasJumpAtComponent(
+ const Coordinate& compPt,
+ const TaggedLineString* line,
+ std::size_t start, std::size_t end,
+ const LineSegment& seg);
+
+ static bool hasJumpAtComponent(
+ const Coordinate& compPt,
+ const LineSegment* seg1, const LineSegment* seg2,
+ const LineSegment& seg);
+
+ static std::size_t crossingCount(
+ const Coordinate& compPt,
+ const LineSegment& seg);
+
+ static std::size_t crossingCount(
+ const Coordinate& compPt,
+ const LineSegment* seg1, const LineSegment* seg2);
+
+ std::size_t static crossingCount(
+ const Coordinate& compPt,
+ const TaggedLineString* line,
+ std::size_t start, std::size_t end);
+
+ static Envelope computeEnvelope(
+ const LineSegment* seg1, const LineSegment* seg2);
+
+ static Envelope computeEnvelope(
+ const TaggedLineString* line,
+ std::size_t start, std::size_t end);
+
+
+public:
+
+ ComponentJumpChecker(const std::vector<TaggedLineString*>& taggedLines)
+ : components(taggedLines)
+ {}
+
+ bool hasJump(
+ const TaggedLineString* line,
+ std::size_t start, std::size_t end,
+ const LineSegment& seg) const;
+
+ /**
+ * Checks if two consecutive segments jumps a component if flattened.
+ * The segments are assumed to be consecutive.
+ * (so the seg1.p1 = seg2.p0).
+ * The flattening segment must be the segment between seg1.p0 and seg2.p1.
+ *
+ * @param line the line containing the section being flattened
+ * @param seg1 the first replaced segment
+ * @param seg2 the next replaced segment
+ * @param seg the flattening segment
+ * @return true if the flattened segment jumps a component
+ */
+ bool hasJump(
+ const TaggedLineString* line,
+ const LineSegment* seg1,
+ const LineSegment* seg2,
+ const LineSegment& seg) const;
+
+};
+
+} // namespace geos::simplify
+} // namespace geos
+
+
+
+
+
diff --git a/include/geos/simplify/TaggedLineString.h b/include/geos/simplify/TaggedLineString.h
index 70ffff8b3..b23f3a400 100644
--- a/include/geos/simplify/TaggedLineString.h
+++ b/include/geos/simplify/TaggedLineString.h
@@ -47,6 +47,9 @@ class TaggedLineSegment;
}
}
+using geos::geom::Coordinate;
+using geos::geom::CoordinateSequence;
+
namespace geos {
namespace simplify { // geos::simplify
@@ -58,27 +61,36 @@ class GEOS_DLL TaggedLineString {
public:
- typedef std::vector<geom::Coordinate> CoordVect;
+ typedef std::vector<Coordinate> CoordVect;
typedef std::unique_ptr<CoordVect> CoordVectPtr;
- typedef geom::CoordinateSequence CoordSeq;
+ typedef CoordinateSequence CoordSeq;
- typedef std::unique_ptr<geom::CoordinateSequence> CoordSeqPtr;
+ typedef std::unique_ptr<CoordinateSequence> CoordSeqPtr;
TaggedLineString(const geom::LineString* nParentLine,
- std::size_t minimumSize = 2);
+ std::size_t minimumSize,
+ bool bIsRing);
~TaggedLineString();
std::size_t getMinimumSize() const;
+ bool isRing() const;
+
const geom::LineString* getParent() const;
const CoordSeq* getParentCoordinates() const;
CoordSeqPtr getResultCoordinates() const;
+ const Coordinate& getCoordinate(std::size_t i) const;
+
+ std::size_t size() const;
+
+ const Coordinate& getComponentPoint() const;
+
std::size_t getResultSize() const;
TaggedLineSegment* getSegment(std::size_t i);
@@ -89,8 +101,12 @@ public:
const std::vector<TaggedLineSegment*>& getSegments() const;
+ const std::vector<TaggedLineSegment*>& getResultSegments() const;
+
void addToResult(std::unique_ptr<TaggedLineSegment> seg);
+ const TaggedLineSegment* removeRingEndpoint();
+
std::unique_ptr<geom::Geometry> asLineString() const;
std::unique_ptr<geom::Geometry> asLinearRing() const;
@@ -107,9 +123,11 @@ private:
std::size_t minimumSize;
+ bool m_isRing;
+
void init();
- static CoordVectPtr extractCoordinates(
+ static std::unique_ptr<CoordinateSequence> extractCoordinates(
const std::vector<TaggedLineSegment*>& segs);
// Copying is turned off
diff --git a/include/geos/simplify/TaggedLineStringSimplifier.h b/include/geos/simplify/TaggedLineStringSimplifier.h
index 79e9e1293..cd3096f4d 100644
--- a/include/geos/simplify/TaggedLineStringSimplifier.h
+++ b/include/geos/simplify/TaggedLineStringSimplifier.h
@@ -40,15 +40,22 @@ class LineIntersector;
}
namespace geom {
class CoordinateSequence;
+class Coordinate;
class LineSegment;
}
namespace simplify {
class TaggedLineSegment;
class TaggedLineString;
class LineSegmentIndex;
+class ComponentJumpChecker;
}
}
+using geos::geom::CoordinateSequence;
+using geos::geom::Coordinate;
+using geos::geom::LineSegment;
+
+
namespace geos {
namespace simplify { // geos::simplify
@@ -64,25 +71,17 @@ class GEOS_DLL TaggedLineStringSimplifier {
public:
TaggedLineStringSimplifier(LineSegmentIndex* inputIndex,
- LineSegmentIndex* outputIndex);
-
- /** \brief
- * Sets the distance tolerance for the simplification.
- *
- * All vertices in the simplified geometry will be within this
- * distance of the original geometry.
- *
- * @param d the approximation tolerance to use
- */
- void setDistanceTolerance(double d);
+ LineSegmentIndex* outputIndex,
+ const ComponentJumpChecker* jumpChecker);
/**
* Simplifies the given {@link TaggedLineString}
* using the distance tolerance specified.
*
* @param line the linestring to simplify
+ * @param distanceTolerance simplification tolerance
*/
- void simplify(TaggedLineString* line);
+ void simplify(TaggedLineString* line, double distanceTolerance);
private:
@@ -93,35 +92,48 @@ private:
// externally owned
LineSegmentIndex* outputIndex;
+ const ComponentJumpChecker* jumpChecker;
+
std::unique_ptr<algorithm::LineIntersector> li;
/// non-const as segments are possibly added to it
TaggedLineString* line;
- const geom::CoordinateSequence* linePts;
+ const CoordinateSequence* linePts;
- double distanceTolerance;
+ void simplifySection(std::size_t i, std::size_t j, std::size_t depth, double distanceTolerance);
- void simplifySection(std::size_t i, std::size_t j,
- std::size_t depth);
+ void simplifyRingEndpoint(double distanceTolerance);
static std::size_t findFurthestPoint(
- const geom::CoordinateSequence* pts,
+ const CoordinateSequence* pts,
std::size_t i, std::size_t j,
double& maxDistance);
- bool hasBadIntersection(const TaggedLineString* parentLine,
- const std::pair<std::size_t, std::size_t>& sectionIndex,
- const geom::LineSegment& candidateSeg);
+ bool isTopologyValid(
+ const TaggedLineString* lineIn,
+ std::size_t sectionStart, std::size_t sectionEnd,
+ const LineSegment& flatSeg);
- bool hasBadInputIntersection(const TaggedLineString* parentLine,
- const std::pair<std::size_t, std::size_t>& sectionIndex,
- const geom::LineSegment& candidateSeg);
+ bool isTopologyValid(
+ const TaggedLineString* lineIn,
+ const LineSegment* seg1, const LineSegment* seg2,
+ const LineSegment& flatSeg);
- bool hasBadOutputIntersection(const geom::LineSegment& candidateSeg);
+ bool hasInputIntersection(const LineSegment& flatSeg);
- bool hasInteriorIntersection(const geom::LineSegment& seg0,
- const geom::LineSegment& seg1) const;
+ bool hasInputIntersection(
+ const TaggedLineString* lineIn,
+ std::size_t excludeStart, std::size_t excludeEnd,
+ const LineSegment& flatSeg);
+
+ bool isCollinear(const Coordinate& pt, const LineSegment& seg) const;
+
+ bool hasOutputIntersection(const LineSegment& flatSeg);
+
+ bool hasInvalidIntersection(
+ const LineSegment& seg0,
+ const LineSegment& seg1) const;
std::unique_ptr<TaggedLineSegment> flatten(
std::size_t start, std::size_t end);
@@ -129,14 +141,14 @@ private:
/** \brief
* Tests whether a segment is in a section of a TaggedLineString
*
- * @param parentLine
- * @param sectionIndex
- * @param seg
+ * @param line line to be checked for the presence of `seg`
+ * @param sectionIndex start and end indices of the section to check
+ * @param seg segment to look for in `line`
* @return
*/
static bool isInLineSection(
- const TaggedLineString* parentLine,
- const std::pair<std::size_t, std::size_t>& sectionIndex,
+ const TaggedLineString* line,
+ const std::size_t excludeStart, const std::size_t excludeEnd,
const TaggedLineSegment* seg);
/** \brief
@@ -152,11 +164,6 @@ private:
};
-inline void
-TaggedLineStringSimplifier::setDistanceTolerance(double d)
-{
- distanceTolerance = d;
-}
} // namespace geos::simplify
} // namespace geos
diff --git a/include/geos/simplify/TaggedLinesSimplifier.h b/include/geos/simplify/TaggedLinesSimplifier.h
index f1f1efdd7..12e8baa0b 100644
--- a/include/geos/simplify/TaggedLinesSimplifier.h
+++ b/include/geos/simplify/TaggedLinesSimplifier.h
@@ -68,44 +68,15 @@ public:
*/
void setDistanceTolerance(double tolerance);
- /** \brief
- * Simplify a set of {@link TaggedLineString}s
- *
- * @tparam iterator_type an iterator, must support assignment, increment,
- * inequality and dereference operators. Dereference
- * operator must return a `TaggedLineString*`.
- * @param begin iterator to the first element to be simplified.
- * @param end an iterator to one-past-last element to be simplified.
- */
- template <class iterator_type>
- void
- simplify(
- iterator_type begin,
- iterator_type end)
- {
- // add lines to the index
- for(iterator_type it = begin; it != end; ++it) {
- assert(*it);
- inputIndex->add(*(*it));
- }
-
- // Simplify lines
- for(iterator_type it = begin; it != end; ++it) {
- assert(*it);
- simplify(*(*it));
- }
- }
-
+ void simplify(std::vector<TaggedLineString*>& tlsVector);
private:
- void simplify(TaggedLineString& line);
-
std::unique_ptr<LineSegmentIndex> inputIndex;
std::unique_ptr<LineSegmentIndex> outputIndex;
- std::unique_ptr<TaggedLineStringSimplifier> taggedlineSimplifier;
+ double distanceTolerance;
};
} // namespace geos::simplify
diff --git a/src/simplify/ComponentJumpChecker.cpp b/src/simplify/ComponentJumpChecker.cpp
new file mode 100644
index 000000000..c09a5f9ab
--- /dev/null
+++ b/src/simplify/ComponentJumpChecker.cpp
@@ -0,0 +1,202 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2006 Refractions Research Inc.
+ * Copyright (C) 2023 Martin Davis <mtnclimb at gmail.com>
+ * Copyright (C) 2023 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <geos/simplify/ComponentJumpChecker.h>
+#include <geos/simplify/TaggedLineString.h>
+
+#include <geos/algorithm/RayCrossingCounter.h>
+#include <geos/geom/Coordinate.h>
+#include <geos/geom/CoordinateSequence.h>
+#include <geos/geom/Envelope.h>
+#include <geos/geom/LineSegment.h>
+#include <geos/util.h>
+
+using geos::algorithm::RayCrossingCounter;
+using geos::geom::Coordinate;
+using geos::geom::CoordinateSequence;
+using geos::geom::Envelope;
+using geos::geom::LineSegment;
+
+
+namespace geos {
+namespace simplify { // geos::simplify
+
+/**
+* Checks if a line section jumps a component if flattened.
+*
+* Assumes start <= end.
+*
+* @param line the line containing the section being flattened
+* @param start start index of the section
+* @param end end index of the section
+* @param seg the flattening segment
+* @return true if the flattened section jumps a component
+*/
+/*public*/
+bool
+ComponentJumpChecker::hasJump(
+ const TaggedLineString* line,
+ std::size_t start, std::size_t end,
+ const LineSegment& seg) const
+{
+ Envelope sectionEnv = computeEnvelope(line, start, end);
+ for (TaggedLineString* comp : components) {
+ //-- don't test component against itself
+ if (comp == line)
+ continue;
+
+ const Coordinate& compPt = comp->getComponentPoint();
+ if (sectionEnv.intersects(compPt)) {
+ if (hasJumpAtComponent(compPt, line, start, end, seg)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+/**
+* Checks if two consecutive segments jumps a component if flattened.
+* The segments are assumed to be consecutive.
+* (so the seg1.p1 = seg2.p0).
+* The flattening segment must be the segment between seg1.p0 and seg2.p1.
+*
+* @param line the line containing the section being flattened
+* @param seg1 the first replaced segment
+* @param seg2 the next replaced segment
+* @param seg the flattening segment
+* @return true if the flattened segment jumps a component
+*/
+/* public */
+bool
+ComponentJumpChecker::hasJump(
+ const TaggedLineString* line,
+ const LineSegment* seg1,
+ const LineSegment* seg2,
+ const LineSegment& seg) const
+{
+ Envelope sectionEnv = computeEnvelope(seg1, seg2);
+ for (TaggedLineString* comp : components) {
+ //-- don't test component against itself
+ if (comp == line)
+ continue;
+
+ const Coordinate& compPt = comp->getComponentPoint();
+ if (sectionEnv.intersects(compPt)) {
+ if (hasJumpAtComponent(compPt, seg1, seg2, seg)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+/*private static*/
+bool
+ComponentJumpChecker::hasJumpAtComponent(
+ const Coordinate& compPt,
+ const TaggedLineString* line,
+ std::size_t start, std::size_t end,
+ const LineSegment& seg)
+{
+ std::size_t sectionCount = crossingCount(compPt, line, start, end);
+ std::size_t segCount = crossingCount(compPt, seg);
+ bool hasJump = sectionCount % 2 != segCount % 2;
+ return hasJump;
+}
+
+/*private static*/
+bool
+ComponentJumpChecker::hasJumpAtComponent(
+ const Coordinate& compPt,
+ const LineSegment* seg1, const LineSegment* seg2,
+ const LineSegment& seg)
+{
+ std::size_t sectionCount = crossingCount(compPt, seg1, seg2);
+ std::size_t segCount = crossingCount(compPt, seg);
+ bool hasJump = sectionCount % 2 != segCount % 2;
+ return hasJump;
+}
+
+/*private static*/
+std::size_t
+ComponentJumpChecker::crossingCount(
+ const Coordinate& compPt,
+ const LineSegment& seg)
+{
+ RayCrossingCounter rcc(compPt);
+ rcc.countSegment(seg.p0, seg.p1);
+ return rcc.getCount();
+}
+
+/*private static*/
+std::size_t
+ComponentJumpChecker::crossingCount(
+ const Coordinate& compPt,
+ const LineSegment* seg1, const LineSegment* seg2)
+{
+ RayCrossingCounter rcc(compPt);
+ rcc.countSegment(seg1->p0, seg1->p1);
+ rcc.countSegment(seg2->p0, seg2->p1);
+ return rcc.getCount();
+}
+
+/*private static*/
+std::size_t
+ComponentJumpChecker::crossingCount(
+ const Coordinate& compPt,
+ const TaggedLineString* line,
+ std::size_t start, std::size_t end)
+{
+ RayCrossingCounter rcc(compPt);
+ for (std::size_t i = start; i < end; i++) {
+ rcc.countSegment(line->getCoordinate(i), line->getCoordinate(i + 1));
+ }
+ return rcc.getCount();
+}
+
+/*private static*/
+Envelope
+ComponentJumpChecker::computeEnvelope(
+ const LineSegment* seg1, const LineSegment* seg2)
+{
+ Envelope env;
+ env.expandToInclude(seg1->p0);
+ env.expandToInclude(seg1->p1);
+ env.expandToInclude(seg2->p0);
+ env.expandToInclude(seg2->p1);
+ return env;
+}
+
+/*private static*/
+Envelope
+ComponentJumpChecker::computeEnvelope(
+ const TaggedLineString* line,
+ std::size_t start, std::size_t end)
+{
+ Envelope env;
+ for (std::size_t i = start; i <= end; i++) {
+ env.expandToInclude(line->getCoordinate(i));
+ }
+ return env;
+}
+
+
+
+} // namespace geos::simplify
+} // namespace geos
diff --git a/src/simplify/TaggedLineString.cpp b/src/simplify/TaggedLineString.cpp
index fc64e825d..c953217ff 100644
--- a/src/simplify/TaggedLineString.cpp
+++ b/src/simplify/TaggedLineString.cpp
@@ -23,7 +23,7 @@
#include <geos/geom/LinearRing.h>
#include <geos/geom/Geometry.h> // for unique_ptr destructor
#include <geos/geom/GeometryFactory.h>
-#include <geos/geom/CoordinateSequenceFactory.h>
+#include <geos/geom/CoordinateArraySequence.h>
#include <cassert>
#include <memory>
@@ -44,10 +44,11 @@ namespace simplify { // geos::simplify
/*public*/
TaggedLineString::TaggedLineString(const geom::LineString* nParentLine,
- std::size_t nMinimumSize)
- :
- parentLine(nParentLine),
- minimumSize(nMinimumSize)
+ std::size_t nMinimumSize,
+ bool bIsRing)
+ : parentLine(nParentLine)
+ , minimumSize(nMinimumSize)
+ , m_isRing(bIsRing)
{
init();
}
@@ -111,6 +112,13 @@ TaggedLineString::getMinimumSize() const
return minimumSize;
}
+/*public*/
+bool
+TaggedLineString::isRing() const
+{
+ return m_isRing;
+}
+
/*public*/
const geom::LineString*
TaggedLineString::getParent() const
@@ -136,25 +144,22 @@ TaggedLineString::getResultCoordinates() const
<< resultSegs.size() << std::endl;
#endif
- CoordVectPtr pts = extractCoordinates(resultSegs);
+ auto pts = extractCoordinates(resultSegs);
#if GEOS_DEBUG
std::cerr << __FUNCTION__ << " extracted Coords.size: "
<< pts->size() << std::endl;
#endif
-
- CoordVect* v = pts.release();
- return CoordinateSequence::Ptr(parentLine->getFactory()->getCoordinateSequenceFactory()->create(v));
-
+ return pts;
}
/*private static*/
-TaggedLineString::CoordVectPtr
+std::unique_ptr<CoordinateSequence>
TaggedLineString::extractCoordinates(
const std::vector<TaggedLineSegment*>& segs)
{
- CoordVectPtr pts(new CoordVect());
+ std::vector<Coordinate> pts;
#if GEOS_DEBUG
std::cerr << __FUNCTION__ << " segs.size: " << segs.size() << std::endl;
@@ -166,16 +171,41 @@ TaggedLineString::extractCoordinates(
for(std::size_t i = 0; i < size; i++) {
TaggedLineSegment* seg = segs[i];
assert(seg);
- pts->push_back(seg->p0);
+ pts.push_back(seg->p0);
}
// add last point
- pts->push_back(segs[size - 1]->p1);
+ pts.push_back(segs[size - 1]->p1);
}
- return pts;
+ std::unique_ptr<CoordinateSequence> cs(new CoordinateArraySequence(std::move(pts)));
+ return cs;
}
+
+const Coordinate&
+TaggedLineString::getCoordinate(std::size_t i) const
+{
+
+ return parentLine->getCoordinateN(i);
+}
+
+std::size_t
+TaggedLineString::size() const
+{
+ return parentLine->getNumPoints();
+}
+
+const Coordinate&
+TaggedLineString::getComponentPoint() const
+{
+ return getParentCoordinates()->getAt(1);
+}
+
+
+
+
+
/*public*/
std::size_t
TaggedLineString::getResultSize() const
@@ -202,7 +232,6 @@ TaggedLineString::getSegment(std::size_t i) const
std::vector<TaggedLineSegment*>&
TaggedLineString::getSegments()
{
- assert(0);
return segs;
}
@@ -213,6 +242,13 @@ TaggedLineString::getSegments() const
return segs;
}
+/*public*/
+const std::vector<TaggedLineSegment*>&
+TaggedLineString::getResultSegments() const
+{
+ return resultSegs;
+}
+
/*public*/
std::unique_ptr<Geometry>
TaggedLineString::asLineString() const
@@ -246,5 +282,17 @@ TaggedLineString::addToResult(std::unique_ptr<TaggedLineSegment> seg)
#endif
}
+const TaggedLineSegment*
+TaggedLineString::removeRingEndpoint()
+{
+ auto* firstSeg = resultSegs.front();
+ auto* lastSeg = resultSegs.back();
+
+ firstSeg->p0 = lastSeg->p0;
+ resultSegs.pop_back();
+ delete lastSeg;
+ return firstSeg;
+}
+
} // namespace geos::simplify
} // namespace geos
diff --git a/src/simplify/TaggedLineStringSimplifier.cpp b/src/simplify/TaggedLineStringSimplifier.cpp
index d6bda5362..2cb64ccf9 100644
--- a/src/simplify/TaggedLineStringSimplifier.cpp
+++ b/src/simplify/TaggedLineStringSimplifier.cpp
@@ -16,16 +16,16 @@
*
**********************************************************************/
-#include <geos/simplify/TaggedLineStringSimplifier.h>
-#include <geos/simplify/LineSegmentIndex.h>
#include <geos/algorithm/LineIntersector.h>
-#include <geos/simplify/TaggedLineString.h>
-#include <geos/simplify/TaggedLineSegment.h>
+#include <geos/algorithm/Orientation.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/LineString.h>
-//#include <geos/geom/Geometry.h> // for std::unique_ptr destructor
-//#include <geos/geom/GeometryFactory.h>
-//#include <geos/geom/CoordinateSequenceFactory.h>
+#include <geos/simplify/ComponentJumpChecker.h>
+#include <geos/simplify/LineSegmentIndex.h>
+#include <geos/simplify/TaggedLineStringSimplifier.h>
+#include <geos/simplify/TaggedLineString.h>
+#include <geos/simplify/TaggedLineSegment.h>
+#include <geos/util.h>
#include <algorithm>
#include <cassert>
@@ -39,10 +39,11 @@
#include <iostream>
#endif
-using namespace geos::geom;
-using std::pair;
-using std::unique_ptr;
-using std::vector;
+using geos::geom::LineSegment;
+using geos::geom::Coordinate;
+using geos::geom::LineString;
+using geos::algorithm::LineIntersector;
+using geos::algorithm::Orientation;
namespace geos {
namespace simplify { // geos::simplify
@@ -50,20 +51,20 @@ namespace simplify { // geos::simplify
/*public*/
TaggedLineStringSimplifier::TaggedLineStringSimplifier(
LineSegmentIndex* nInputIndex,
- LineSegmentIndex* nOutputIndex)
- :
- inputIndex(nInputIndex),
- outputIndex(nOutputIndex),
- li(new algorithm::LineIntersector()),
- line(nullptr),
- linePts(nullptr),
- distanceTolerance(0.0)
-{
-}
+ LineSegmentIndex* nOutputIndex,
+ const ComponentJumpChecker* crossChecker)
+ : inputIndex(nInputIndex)
+ , outputIndex(nOutputIndex)
+ , jumpChecker(crossChecker)
+ , li(new LineIntersector())
+ , line(nullptr)
+ , linePts(nullptr)
+{}
+
/*public*/
void
-TaggedLineStringSimplifier::simplify(TaggedLineString* nLine)
+TaggedLineStringSimplifier::simplify(TaggedLineString* nLine, double distanceTolerance)
{
assert(nLine);
line = nLine;
@@ -81,15 +82,17 @@ TaggedLineStringSimplifier::simplify(TaggedLineString* nLine)
if(linePts->isEmpty()) {
return;
}
- simplifySection(0, linePts->size() - 1, 0);
+ simplifySection(0, linePts->size() - 1, 0, distanceTolerance);
+ if(line->isRing() && linePts->isRing()) {
+ simplifyRingEndpoint(distanceTolerance);
+ }
}
-
/*private*/
void
TaggedLineStringSimplifier::simplifySection(std::size_t i,
- std::size_t j, std::size_t depth)
+ std::size_t j, std::size_t depth, double distanceTolerance)
{
depth += 1;
@@ -141,16 +144,26 @@ TaggedLineStringSimplifier::simplifySection(std::size_t i,
<< std::endl;
#endif
+ if (distance < 0) {
+ // negative distance indicates that we could not compute distance to the
+ // farthest point, probably because of infinite or large-magnitude coordinates.
+ // avoid trying to simplify this section.
+ for (std::size_t k = i; k < j; k++) {
+ auto newSeg = detail::make_unique<TaggedLineSegment>(*(line->getSegment(k)));
+ line->addToResult(std::move(newSeg));
+ }
+ return;
+ }
+
// flattening must be less than distanceTolerance
if(distance > distanceTolerance) {
isValidToSimplify = false;
}
- // test if flattened section would cause intersection
- LineSegment candidateSeg(linePts->getAt(i), linePts->getAt(j));
-
- if(hasBadIntersection(line, std::make_pair(i, j), candidateSeg)) {
- isValidToSimplify = false;
+ if (isValidToSimplify) {
+ // test if flattened section would cause intersection or jump
+ LineSegment flatSeg(linePts->getAt(i), linePts->getAt(j));
+ isValidToSimplify = isTopologyValid(line, i, j, flatSeg);
}
if(isValidToSimplify) {
@@ -168,11 +181,35 @@ TaggedLineStringSimplifier::simplifySection(std::size_t i,
return;
}
- simplifySection(i, furthestPtIndex, depth);
- simplifySection(furthestPtIndex, j, depth);
-
+ simplifySection(i, furthestPtIndex, depth, distanceTolerance);
+ simplifySection(furthestPtIndex, j, depth, distanceTolerance);
}
+/*private*/
+void
+TaggedLineStringSimplifier::simplifyRingEndpoint(double distanceTolerance)
+{
+ if (line->getResultSize() > line->getMinimumSize()) {
+ const auto* firstSeg = static_cast<LineSegment*>(line->getResultSegments().front());
+ const auto* lastSeg = static_cast<LineSegment*>(line->getResultSegments().back());
+
+ LineSegment simpSeg(lastSeg->p0, firstSeg->p1);
+ const Coordinate& endPt = firstSeg->p0;
+ if (simpSeg.distance(endPt) <= distanceTolerance &&
+ isTopologyValid(line, firstSeg, lastSeg, simpSeg))
+ {
+ //-- don't know if segments are original or new, so remove from all indexes
+ inputIndex->remove(firstSeg);
+ inputIndex->remove(lastSeg);
+ outputIndex->remove(firstSeg);
+ outputIndex->remove(lastSeg);
+
+ const TaggedLineSegment* flatSeg = line->removeRingEndpoint();
+ //-- removed endpoint alters an existing result edge
+ outputIndex->add(flatSeg);
+ }
+ }
+}
/*private*/
std::unique_ptr<TaggedLineSegment>
@@ -183,93 +220,140 @@ TaggedLineStringSimplifier::flatten(std::size_t start, std::size_t end)
const Coordinate& p1 = linePts->getAt(end);
std::unique_ptr<TaggedLineSegment> newSeg(new TaggedLineSegment(p0, p1));
// update the indexes
- remove(line, start, end);
outputIndex->add(newSeg.get());
+ remove(line, start, end);
return newSeg;
}
/*private*/
bool
-TaggedLineStringSimplifier::hasBadIntersection(
- const TaggedLineString* parentLine,
- const pair<size_t, size_t>& sectionIndex,
- const LineSegment& candidateSeg)
+TaggedLineStringSimplifier::isTopologyValid(
+ const TaggedLineString* lineIn,
+ std::size_t sectionStart, std::size_t sectionEnd,
+ const LineSegment& flatSeg)
{
- if(hasBadOutputIntersection(candidateSeg)) {
- return true;
- }
-
- if(hasBadInputIntersection(parentLine, sectionIndex, candidateSeg)) {
- return true;
- }
-
- return false;
+ if (hasOutputIntersection(flatSeg))
+ return false;
+ if (hasInputIntersection(lineIn, sectionStart, sectionEnd, flatSeg))
+ return false;
+ if (jumpChecker->hasJump(lineIn, sectionStart, sectionEnd, flatSeg))
+ return false;
+ return true;
}
/*private*/
bool
-TaggedLineStringSimplifier::hasBadOutputIntersection(
- const LineSegment& candidateSeg)
+TaggedLineStringSimplifier::isTopologyValid(
+ const TaggedLineString* lineIn,
+ const LineSegment* seg1, const LineSegment* seg2,
+ const LineSegment& flatSeg)
{
- std::unique_ptr< std::vector<LineSegment*> > querySegs =
- outputIndex->query(&candidateSeg);
+ //-- if segments are already flat, topology is unchanged and so is valid
+ //-- (otherwise, output and/or input intersection test would report false positive)
+ if (isCollinear(seg1->p0, flatSeg))
+ return true;
+ if (hasOutputIntersection(flatSeg))
+ return false;
+ if (hasInputIntersection(flatSeg))
+ return false;
+ if (jumpChecker->hasJump(lineIn, seg1, seg2, flatSeg))
+ return false;
+ return true;
+}
+
+/*private*/
+bool
+TaggedLineStringSimplifier::isCollinear(
+ const Coordinate& pt,
+ const LineSegment& seg) const
+{
+ return Orientation::COLLINEAR == seg.orientationIndex(pt);
+}
+
+/*private*/
+bool
+TaggedLineStringSimplifier::hasOutputIntersection(
+ const LineSegment& flatSeg)
+{
+ //std::unique_ptr<std::vector<LineSegment*>>
+ auto querySegs = outputIndex->query(&flatSeg);
for(const LineSegment* querySeg : *querySegs) {
- if(hasInteriorIntersection(*querySeg, candidateSeg)) {
+ if(hasInvalidIntersection(*querySeg, flatSeg)) {
return true;
}
}
-
return false;
}
/*private*/
bool
-TaggedLineStringSimplifier::hasInteriorIntersection(
+TaggedLineStringSimplifier::hasInvalidIntersection(
const LineSegment& seg0,
const LineSegment& seg1) const
{
+ if(seg0.equalsTopo(seg1))
+ return true;
li->computeIntersection(seg0.p0, seg0.p1, seg1.p0, seg1.p1);
return li->isInteriorIntersection();
}
/*private*/
bool
-TaggedLineStringSimplifier::hasBadInputIntersection(
- const TaggedLineString* parentLine,
- const pair<std::size_t, std::size_t>& sectionIndex,
- const LineSegment& candidateSeg)
+TaggedLineStringSimplifier::hasInputIntersection(const LineSegment& flatSeg)
{
- std::unique_ptr< std::vector<LineSegment*> > querySegs =
- inputIndex->query(&candidateSeg);
+ return hasInputIntersection(nullptr, 0, 0, flatSeg);
+}
+
+/*private*/
+bool
+TaggedLineStringSimplifier::hasInputIntersection(
+ const TaggedLineString* parentLine,
+ const std::size_t excludeStart, const std::size_t excludeEnd,
+ const LineSegment& flatSeg)
+{
+ const auto& querySegs = inputIndex->query(&flatSeg);
for(const LineSegment* ls : *querySegs) {
const TaggedLineSegment* querySeg = static_cast<const TaggedLineSegment*>(ls);
- if(!isInLineSection(parentLine, sectionIndex, querySeg) && hasInteriorIntersection(*querySeg, candidateSeg)) {
-
+ if (hasInvalidIntersection(*ls, flatSeg)) {
+ /**
+ * Ignore the intersection if the intersecting segment is part of the section being collapsed
+ * to the candidate segment
+ */
+ if (parentLine != nullptr &&
+ isInLineSection(line, excludeStart, excludeEnd, querySeg)) {
+ continue;
+ }
return true;
}
}
-
return false;
}
/*static private*/
bool
TaggedLineStringSimplifier::isInLineSection(
- const TaggedLineString* line,
- const pair<size_t, size_t>& sectionIndex,
+ const TaggedLineString* lineIn,
+ const std::size_t excludeStart, const std::size_t excludeEnd,
const TaggedLineSegment* seg)
{
// not in this line
- if(seg->getParent() != line->getParent()) {
+ if (seg->getParent() != lineIn->getParent()) {
return false;
}
std::size_t segIndex = seg->getIndex();
- if(segIndex >= sectionIndex.first && segIndex < sectionIndex.second) {
- return true;
+ if (excludeStart <= excludeEnd) {
+ //-- section is contiguous
+ if (segIndex >= excludeStart && segIndex < excludeEnd)
+ return true;
+ }
+ else {
+ //-- section wraps around the end of a ring
+ if (segIndex >= excludeStart || segIndex <= excludeEnd)
+ return true;
}
return false;
@@ -323,8 +407,8 @@ TaggedLineStringSimplifier::findFurthestPoint(
}
maxDistance = maxDist;
return maxIndex;
-
}
+
} // namespace geos::simplify
} // namespace geos
diff --git a/src/simplify/TaggedLinesSimplifier.cpp b/src/simplify/TaggedLinesSimplifier.cpp
index d360c3aee..af75f35af 100644
--- a/src/simplify/TaggedLinesSimplifier.cpp
+++ b/src/simplify/TaggedLinesSimplifier.cpp
@@ -16,8 +16,9 @@
*
**********************************************************************/
-#include <geos/simplify/TaggedLinesSimplifier.h>
+#include <geos/simplify/ComponentJumpChecker.h>
#include <geos/simplify/LineSegmentIndex.h>
+#include <geos/simplify/TaggedLinesSimplifier.h>
#include <geos/simplify/TaggedLineStringSimplifier.h>
#include <geos/algorithm/LineIntersector.h>
@@ -41,27 +42,36 @@ namespace simplify { // geos::simplify
/*public*/
TaggedLinesSimplifier::TaggedLinesSimplifier()
- :
- inputIndex(new LineSegmentIndex()),
- outputIndex(new LineSegmentIndex()),
- taggedlineSimplifier(new TaggedLineStringSimplifier(inputIndex.get(),
- outputIndex.get()))
-{
-}
+ : inputIndex(new LineSegmentIndex())
+ , outputIndex(new LineSegmentIndex())
+ , distanceTolerance(0.0)
+{}
+
/*public*/
void
TaggedLinesSimplifier::setDistanceTolerance(double d)
{
- taggedlineSimplifier->setDistanceTolerance(d);
+ distanceTolerance = d;
}
-/*private*/
+
+/*public*/
void
-TaggedLinesSimplifier::simplify(TaggedLineString& tls)
+TaggedLinesSimplifier::simplify(std::vector<TaggedLineString*>& taggedLines)
{
- taggedlineSimplifier->simplify(&tls);
+ ComponentJumpChecker jumpChecker(taggedLines);
+
+ for (auto* tls : taggedLines) {
+ inputIndex->add(*tls);
+ }
+
+ for (auto* tls : taggedLines) {
+ TaggedLineStringSimplifier tlss(inputIndex.get(), outputIndex.get(), &jumpChecker);
+ tlss.simplify(tls, distanceTolerance);
+ }
}
+
} // namespace geos::simplify
} // namespace geos
diff --git a/src/simplify/TopologyPreservingSimplifier.cpp b/src/simplify/TopologyPreservingSimplifier.cpp
index 1e76cd20f..85e39e1d5 100644
--- a/src/simplify/TopologyPreservingSimplifier.cpp
+++ b/src/simplify/TopologyPreservingSimplifier.cpp
@@ -92,6 +92,8 @@ LineStringTransformer::transformCoordinates(
std::cerr << __FUNCTION__ << ": parent: " << parent
<< std::endl;
#endif
+ if (coords->size() == 0) return nullptr;
+
if(dynamic_cast<const LineString*>(parent)) {
LinesMap::iterator it = linestringMap.find(parent);
assert(it != linestringMap.end());
@@ -134,9 +136,6 @@ class LineStringMapBuilderFilter: public geom::GeometryComponentFilter {
public:
- // no more needed
- //friend class TopologyPreservingSimplifier;
-
/**
* Filters linear geometries.
*
@@ -172,17 +171,21 @@ LineStringMapBuilderFilter::LineStringMapBuilderFilter(LinesMap& nMap, std::vect
void
LineStringMapBuilderFilter::filter_ro(const Geometry* geom)
{
- TaggedLineString* taggedLine;
+ auto typ = geom->getGeometryTypeId();
+ bool isRing = false;
- if(const LineString* ls =
- dynamic_cast<const LineString*>(geom)) {
- std::size_t minSize = ls->isClosed() ? 4 : 2;
- taggedLine = new TaggedLineString(ls, minSize);
- }
- else {
+ if (geom->isEmpty()) return;
+
+ if (typ == GEOS_LINEARRING) {
+ isRing = true;
+ } else if (typ != GEOS_LINESTRING) {
return;
}
+ auto ls = static_cast<const LineString*>(geom);
+ std::size_t minSize = ls->isClosed() ? 4 : 2;
+ TaggedLineString* taggedLine = new TaggedLineString(ls, minSize, isRing);
+
// Duplicated Geometry pointers shouldn't happen
if(! linestringMap.insert(std::make_pair(geom, taggedLine)).second) {
delete taggedLine;
@@ -253,7 +256,7 @@ TopologyPreservingSimplifier::getResultGeometry()
<< linestringMap.size() << " elements\n";
#endif
- lineSimplifier->simplify(tlsVector.begin(), tlsVector.end());
+ lineSimplifier->simplify(tlsVector);
#if GEOS_DEBUG
std::cerr << "all TaggedLineString simplified\n";
diff --git a/tests/unit/simplify/TopologyPreservingSimplifierTest.cpp b/tests/unit/simplify/TopologyPreservingSimplifierTest.cpp
index ce31d13f3..e30a74562 100644
--- a/tests/unit/simplify/TopologyPreservingSimplifierTest.cpp
+++ b/tests/unit/simplify/TopologyPreservingSimplifierTest.cpp
@@ -23,17 +23,30 @@ struct test_tpsimp_data {
typedef geos::geom::GeometryFactory GeometryFactory;
GeometryFactory::Ptr gf;
geos::io::WKTReader wktreader;
- geos::io::WKTWriter wktwriter;
typedef geos::geom::Geometry::Ptr GeomPtr;
test_tpsimp_data()
- : pm(1.0)
+ : pm()
, gf(GeometryFactory::create(&pm))
, wktreader(gf.get())
- , wktwriter()
{
- //wktwriter.setTrim(1);
+ }
+
+ void
+ checkTPS(const std::string& wkt, double tolerance, const std::string& wkt_expected)
+ {
+ GeomPtr g(wktreader.read(wkt));
+ GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), tolerance);
+ GeomPtr exp(wktreader.read(wkt_expected));
+ ensure_equals_geometry(exp.get(), simplified.get(),0.00001);
+ ensure("Simplified geometry is invalid!", simplified->isValid());
+ }
+
+ void
+ checkTPSNoChange(const std::string& wkt, double tolerance)
+ {
+ checkTPS(wkt, tolerance, wkt);
}
};
@@ -46,315 +59,398 @@ group test_tpsimp_group("geos::simplify::TopologyPreservingSimplifier");
// Test Cases
//
-// EmptyPolygon
+// testPoint
template<>
template<>
-void object::test<1>
-()
+void object::test<0>()
{
- std::string wkt("POLYGON EMPTY");
-
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(g.get(), simplified.get());
+ checkTPSNoChange("POINT (10 10)", 1);
}
-// Point
+// testPolygonEmpty
template<>
template<>
-void object::test<2>
-()
+void object::test<1>()
{
- std::string wkt("POINT (10 10)");
-
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(g.get(), simplified.get());
+ checkTPSNoChange("POLYGON(EMPTY)", 1);
}
-#if 0 // Fails with JTS too !
-// MultiPolygonWithSmallComponents
-// Test is from http://postgis.refractions.net/pipermail/postgis-users/2008-April/019327.html
+// testPolygonFlatVertices
template<>
template<>
-void object::test<3>
-()
+void object::test<2>()
{
- std::string wkt(
- "MULTIPOLYGON(((13.73095 51.024734,13.7309323 51.0247668,13.7306959 51.0247959,13.7292724 51.0249742,13.7280216 51.0251252,13.7266598 51.0252998,13.7259617 51.0254072,13.7258854 51.0254201,13.7253253 51.0255144,13.725276 51.025492,13.724538 51.025631,13.7230288 51.0259021,13.7223529 51.0260273,13.7223299 51.0260863,13.7222292 51.026391,13.7220002 51.0273366,13.7217875 51.0282094,13.721746 51.028243,13.7217693 51.0282803,13.7215512 51.0291967,13.721513 51.029222,13.7215203 51.0292567,13.7212713 51.0295967,13.7222258 51.0299532,13.722234 51.03,13.7222931 51.0299823,13.7232514 51.0303187,13.7242514 51.0306715,13.724263 51.030714,13.7243024 51.0306951,13.7249934 51.0309315,13.7265097 51.0314552,13.7266116 51.0313952,13.7267988 51.0313334,13.7269952 51.0313243,13.72703 51.0314107,13.7271637 51.0313254,13.7272524 51.0313839,13.72739 51.031449,13.7276768 51.0313074,13.7283793 51.0309944,13.7296654 51.0304157,13.7297572 51.0303637,13.729845 51.0303139,13.7299557 51.0301763,13.730096
4 51.0300176,13.730252 51.0298919,13.7304615 51.0297932,13.730668 51.0297363,13.730743 51.029783,13.7307859 51.0298398,13.7307094 51.0301388,13.730624 51.030263,13.7306955 51.0303267,13.7301182 51.0325594,13.7300528 51.0325663,13.7301114 51.0327342,13.7301645 51.0329094,13.7300035 51.0327693,13.7299669 51.0327351,13.7299445 51.0327211,13.7298934 51.032814,13.7298539 51.0328585,13.7297737 51.0328321,13.7288526 51.0325639,13.7288201 51.0324367,13.7284426 51.0324383,13.7276461 51.032179,13.7274569 51.0321976,13.7272787 51.0322421,13.7271265 51.0322903,13.7267034 51.0322495,13.7265364 51.0322161,13.7259018 51.0324269,13.7258649 51.03242,13.725733 51.0326646,13.7251933 51.0328876,13.7247918 51.0331374,13.7244439 51.0331106,13.7242967 51.0334273,13.7239131 51.0337529,13.7237035 51.0338511,13.7235429 51.033967,13.7233375 51.0339148,13.7232064 51.0339347,13.7231786 51.0339863,13.7228848 51.0340776,13.7224481 51.0341888,13.7220471 51.0342483,13.7217493 51.0343198,13.721552 51.0343861,13.7214
718 51.0344095,13.7215108 51.034534,13.7205032 51.0349932,13.7197657 51.0352983,13.7195764 51.0352291,13.7195934 51.0352797,13.7182451 51.0359157,13.7181108 51.0359003,13.7181657 51.0359571,13.717622 51.0361956,13.7159749 51.0369683,13.7159057 51.0369284,13.7158604 51.0370288,13.7157161 51.0370124,13.7157523 51.0370733,13.7153708 51.0372801,13.7150274 51.0374899,13.7144074 51.0379192,13.7138287 51.0383899,13.7137514 51.0383857,13.7137492 51.0384566,13.7134249 51.0387269,13.7130179 51.0390385,13.7125791 51.0393343,13.7120736 51.039611,13.7115839 51.0398558,13.7112945 51.0399894,13.7114637 51.0402313,13.7123153 51.041449,13.7126333 51.0417033,13.713371 51.0421453,13.7138861 51.0424061,13.7142518 51.0425683,13.7164587 51.0435668,13.7167995 51.0437957,13.7170883 51.0439897,13.7190694 51.0451663,13.7196131 51.0458277,13.7197562 51.0461521,13.7198262 51.0464192,13.7198377 51.0467389,13.7205681 51.0455573,13.7210009 51.0450379,13.7214987 51.0445401,13.7220306 51.0442859,13.7227215 51.04395
58,13.7237962 51.0434514,13.723979 51.0435278,13.7241448 51.0435041,13.7241052 51.0436042,13.7247987 51.0438896,13.7250186 51.0439093,13.7250579 51.0440386,13.7257225 51.0443545,13.7259312 51.0443456,13.725955 51.0443813,13.7260235 51.0443873,13.7260682 51.0445303,13.7282191 51.0455848,13.7290532 51.045927,13.7292643 51.0458591,13.7292228 51.0459969,13.729706 51.0461854,13.7303185 51.046393,13.7309107 51.0465601,13.731546 51.0466841,13.7321939 51.0467752,13.7332896 51.0468999,13.7333733 51.0469094,13.7334778 51.0468127,13.7335706 51.0469078,13.733651 51.0470684,13.7338458 51.0471508,13.7346109 51.0472333,13.7346367 51.0471474,13.7346922 51.0470697,13.7346666 51.0470056,13.7346564 51.0468714,13.7345552 51.0467095,13.7336001 51.0465496,13.733427 51.046454,13.7335317 51.0464255,13.7347225 51.0465948,13.7348421 51.0466562,13.7349123 51.0466203,13.736811 51.0468537,13.7382043 51.0469796,13.7383487 51.0469803,13.7394909 51.0469005,13.7400899 51.0467949,13.7405051 51.0464739,13.7408331 51.
0462204,13.7412027 51.0463256,13.741053 51.0466451,13.7407291 51.0469007,13.7405095 51.0469726,13.7400888 51.0470337,13.7393051 51.0471049,13.7393014 51.0472015,13.7393088 51.0473019,13.7395556 51.0473056,13.7404944 51.0472245,13.740932 51.0470192,13.7414421 51.0465652,13.7414893 51.0465576,13.7416494 51.0464916,13.7416003 51.0466074,13.7416246 51.04663,13.741668 51.0466443,13.7417272 51.0467159,13.7417503 51.0466716,13.7423587 51.0468732,13.7426958 51.0470246,13.7429143 51.0471813,13.74318 51.04726,13.7430363 51.0472995,13.7433021 51.047588,13.7434678 51.0475916,13.7433805 51.0477019,13.7436362 51.0479981,13.7446308 51.0491622,13.7447961 51.0491827,13.744722 51.0492509,13.7448536 51.0494078,13.745056 51.0494766,13.7450313 51.0496901,13.7453573 51.0500052,13.7465317 51.0512807,13.7466999 51.0513722,13.746638 51.0514149,13.7468683 51.0516781,13.7470071 51.051777,13.7469985 51.0518746,13.7470732 51.0519866,13.7471316 51.0520528,13.7472989 51.0523089,13.7472368 51.0523858,13.7473063 51
.0524932,13.7473468 51.0527412,13.7473392 51.0531614,13.7472987 51.0533157,13.7473919 51.0534224,13.7472684 51.0534549,13.7472134 51.0536926,13.7472913 51.0537784,13.7473216 51.053725,13.7474649 51.0537575,13.7474492 51.053833,13.7475625 51.0537839,13.7497379 51.0544435,13.7515333 51.0551019,13.7527693 51.0555438,13.7549766 51.0564993,13.7550622 51.0565364,13.755105 51.0566612,13.7552745 51.0566237,13.7558661 51.0560648,13.7559318 51.0560101,13.755908 51.055897,13.7559252 51.0558292,13.7559566 51.0557055,13.7564494 51.0551377,13.7564124 51.0550457,13.7573213 51.0539813,13.7575007 51.0539933,13.757856 51.0540047,13.7580394 51.054028,13.7580896 51.053984,13.7580949 51.0539463,13.7579963 51.0538534,13.7581294 51.0537147,13.7582346 51.0535957,13.758354 51.053433,13.758363 51.053392,13.7583656 51.0533457,13.758359 51.0532095,13.7583338 51.0530937,13.7582902 51.0529647,13.7580365 51.0522637,13.7577683 51.051463,13.7573182 51.0501993,13.7571595 51.0497164,13.7567579 51.0490095,13.7563383 5
1.0482979,13.7557757 51.0473383,13.7557095 51.0472522,13.7555771 51.0471199,13.7554448 51.0470471,13.7548596 51.0462612,13.7547097 51.046054,13.7549127 51.0460086,13.7548633 51.0459174,13.7548127 51.0458413,13.7547176 51.0457237,13.7538293 51.0449222,13.7530218 51.0441346,13.7526711 51.0437838,13.752446 51.0435522,13.7522297 51.0433547,13.751704 51.042833,13.7513058 51.0424448,13.7505766 51.0417281,13.7499967 51.0411283,13.7497695 51.0408943,13.7493849 51.0405205,13.7486222 51.0397896,13.7478209 51.0390261,13.7477474 51.0389532,13.7477041 51.0389189,13.7476277 51.0388729,13.7475781 51.0388513,13.7472699 51.038726,13.747131 51.0386506,13.7469329 51.0385052,13.7468562 51.0384284,13.7466683 51.0383483,13.7467998 51.038236,13.7473841 51.0380129,13.747838 51.0378277,13.7481801 51.0376558,13.7489728 51.0370285,13.7491313 51.0368016,13.7492665 51.0363477,13.7493166 51.0359389,13.7492966 51.0358087,13.7493888 51.0356942,13.7492867 51.0357016,13.7492855 51.0354359,13.7492829 51.034867,13.749
2723 51.0348311,13.7492455 51.0347398,13.7493034 51.0346612,13.7491987 51.0346142,13.748866 51.034723,13.748791 51.034201,13.748335 51.034159,13.748294 51.034034,13.748205 51.033764,13.7488691 51.0333037,13.748962 51.033245,13.7486777 51.0332252,13.7483008 51.032683,13.7484397 51.0324582,13.7469913 51.0327817,13.7466998 51.0326205,13.7459997 51.0314852,13.7460996 51.0313569,13.745967 51.0314864,13.7449355 51.0317377,13.7447301 51.0316513,13.7446705 51.0318463,13.7420262 51.0323659,13.7419131 51.0322884,13.7418636 51.0322552,13.7416501 51.0321425,13.7415567 51.0317708,13.7414972 51.0314666,13.741484 51.0311492,13.741923 51.031003,13.7418649 51.030884,13.74209 51.0304134,13.7422077 51.0300143,13.7421975 51.0299222,13.742286 51.029835,13.7421463 51.0297533,13.7420951 51.0296254,13.7415933 51.0288452,13.7414906 51.0286855,13.7414437 51.0286127,13.7413482 51.0284642,13.7410545 51.0280777,13.7407158 51.0277229,13.7401513 51.0273842,13.7392803 51.0270293,13.7382744 51.0267844,13.737321 51.
0267454,13.7365929 51.0267541,13.736556 51.026812,13.7364715 51.026754,13.7357088 51.0268017,13.7353967 51.02678,13.73534 51.02685,13.7352667 51.0267757,13.734907 51.0267324,13.734824 51.02679,13.7347684 51.0267064,13.7342093 51.0266674,13.73409 51.026725,13.7340359 51.0266283,13.7335072 51.0265633,13.733407 51.02663,13.7333208 51.0265373,13.7317087 51.0263813,13.7317173 51.0263119,13.73167 51.026241,13.7317563 51.0261602,13.7318473 51.0258395,13.7318647 51.0254971,13.73183 51.0253281,13.7317736 51.0252414,13.731663 51.025181,13.7316826 51.0251114,13.7310803 51.0247604,13.73095 51.024734)),((13.7368533 51.0470386,13.7368426 51.0471226,13.7368067 51.0472669,13.7368255 51.0473828,13.7369099 51.0474154,13.7376695 51.0474677,13.7382756 51.0474245,13.738513 51.0474297,13.7386105 51.0474065,13.738705 51.0473737,13.7385856 51.0473757,13.7385618 51.0473751,13.7385263 51.0473743,13.7384706 51.0473744,13.7383071 51.0473734,13.7383822 51.0473564,13.7390821 51.047287,13.7390933 51.047209,13.739
0933 51.0471421,13.7368533 51.0470386)),((13.7367293 51.0470057,13.7346615 51.0466892,13.7347551 51.0468411,13.7347754 51.0470359,13.7347106 51.0471899,13.7356421 51.0472919,13.7366963 51.0474074,13.736705 51.047249,13.7367293 51.0470057)))"
- );
-
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 0.0057);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
-
+ checkTPS("POLYGON ((20 220, 40 220, 60 220, 80 220, 100 220, 120 220, 140 220, 140 180, 100 180, 60 180, 20 180, 20 220))",
+ 10,
+ "POLYGON ((20 220, 140 220, 140 180, 20 180, 20 220))");
}
-#endif // fails with JTS too
-// PolygonWithSpike
+// testPolygonNoReduction
template<>
template<>
-void object::test<4>
-()
+void object::test<3>()
{
- std::string wkt("POLYGON ((3312459.605 6646878.353, \
- 3312460.524 6646875.969, 3312459.427 6646878.421, \
- 3312460.014 6646886.391, 3312465.889 6646887.398, \
- 3312470.827 6646884.839, 3312475.4 6646878.027, \
- 3312477.289 6646871.694, 3312472.748 6646869.547, \
- 3312468.253 6646874.01, 3312463.52 6646875.779, \
- 3312459.605 6646878.353))");
+ checkTPSNoChange("POLYGON ((20 220, 140 220, 140 180, 20 180, 20 220))",
+ 10);
+}
- std::string wkt_expected("POLYGON (( \
- 3312459.605 6646878.353, \
- 3312460.524 6646875.969, \
- 3312459.427 6646878.421, \
- 3312460.014 6646886.391, \
- 3312465.889 6646887.398, \
- 3312470.827 6646884.839, \
- 3312477.289 6646871.694, \
- 3312472.748 6646869.547, \
- 3312459.605 6646878.353))");
+// testPolygonNoReductionWithConflicts
+template<>
+template<>
+void object::test<4>()
+{
+ checkTPSNoChange("POLYGON ((40 240, 160 241, 280 240, 280 160, 160 240, 40 140, 40 240))",
+ 10);
+}
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 2.0);
+// testPolygonWithTouchingHole
+template<>
+template<>
+void object::test<5>()
+{
+ checkTPS("POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200), (120 120, 220 120, 180 199, 160 200, 140 199, 120 120))",
+ 10,
+ "POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200), (120 120, 220 120, 180 199, 160 200, 140 199, 120 120))");
+}
- ensure("Simplified geometry is invalid!", simplified->isValid());
+// testFlattishPolygon
+template<>
+template<>
+void object::test<6>()
+{
+ checkTPS("POLYGON ((0 0, 50 0, 53 0, 55 0, 100 0, 70 1, 60 1, 50 1, 40 1, 0 0))",
+ 10,
+ "POLYGON ((0 0, 50 0, 100 0, 70 1, 0 0))");
+}
- GeomPtr g_expected(wktreader.read(wkt_expected));
- ensure(g_expected->equalsExact(simplified.get()));
+// testPolygonWithFlattishHole
+template<>
+template<>
+void object::test<7>()
+{
+ checkTPS("POLYGON ((0 0, 0 200, 200 200, 200 0, 0 0), (140 40, 90 95, 40 160, 95 100, 140 40))",
+ 20,
+ "POLYGON ((0 0, 0 200, 200 200, 200 0, 0 0), (140 40, 90 95, 40 160, 95 100, 140 40))");
+}
+
+// testTinySquare
+template<>
+template<>
+void object::test<8>()
+{
+ checkTPS("POLYGON ((0 5, 5 5, 5 0, 0 0, 0 1, 0 5))",
+ 10,
+ "POLYGON ((0 0, 5 5, 5 0, 0 0))");
+}
+
+// testTinyLineString
+template<>
+template<>
+void object::test<9>()
+{
+ checkTPS("LINESTRING (0 5, 1 5, 2 5, 5 5)",
+ 10,
+ "LINESTRING (0 5, 5 5)");
+}
+
+// testTinyClosedLineString
+template<>
+template<>
+void object::test<10>()
+{
+ checkTPSNoChange("LINESTRING (0 0, 5 0, 5 5, 0 0)",
+ 10);
+}
+
+// testMultiPoint
+template<>
+template<>
+void object::test<11>()
+{
+ checkTPSNoChange("MULTIPOINT(80 200, 240 200, 240 60, 80 60, 80 200, 140 199, 120 120)",
+ 10);
+}
+
+// testMultiLineString
+template<>
+template<>
+void object::test<12>()
+{
+ checkTPS("MULTILINESTRING( (0 0, 50 0, 70 0, 80 0, 100 0), (0 0, 50 1, 60 1, 100 0) )",
+ 10,
+ "MULTILINESTRING ((0 0, 50 1, 100 0), (0 0, 100 0))");
}
-// PolygonNoReduction
+// testMultiLineStringWithEmpty
template<>
template<>
-void object::test<5>
-()
+void object::test<13>()
{
- std::string wkt("POLYGON((20 220, 40 220, 60 220, 80 220, \
- 100 220, 120 220, 140 220, 140 180, 100 180, \
- 60 180, 20 180, 20 220))");
- GeomPtr g(wktreader.read(wkt));
-
- std::string wkt_exp("POLYGON ((20 220, 140 220, 140 180, 20 180, 20 220))");
- GeomPtr exp(wktreader.read(wkt_exp));
-
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure("Simplified and original geometry inequal", simplified->equals(g.get()));
- ensure_equals_geometry(exp.get(), simplified.get());
+ checkTPS("MULTILINESTRING(EMPTY, (0 0, 50 0, 70 0, 80 0, 100 0), (0 0, 50 1, 60 1, 100 0) )",
+ 10,
+ "MULTILINESTRING ((0 0, 100 0), (0 0, 50 1, 100 0))");
}
-// PolygonNoReductionWithConflicts
+// testMultiPolygonWithEmpty
template<>
template<>
-void object::test<6>
-()
+void object::test<14>()
{
- std::string wkt("POLYGON ((40 240, 160 241, 280 240, 280 160, \
- 160 240, 40 140, 40 240))");
-
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure("Topology has been changed by simplification!", simplified->equals(g.get()));
- ensure_equals_geometry(g.get(), simplified.get());
+ checkTPS("MULTIPOLYGON (EMPTY, ((10 90, 10 10, 90 10, 50 60, 10 90)), ((70 90, 90 90, 90 70, 70 70, 70 90)))",
+ 10,
+ "MULTIPOLYGON (((10 90, 10 10, 90 10, 50 60, 10 90)), ((70 90, 90 90, 90 70, 70 70, 70 90)))");
}
-// PolygonWithTouchingHole
+// testGeometryCollection
template<>
template<>
-void object::test<7>
-()
+void object::test<15>()
{
- std::string wkt("POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200), \
- (120 120, 220 120, 180 199, 160 200, 140 199, 120 120))");
-
- std::string wkt_expected("POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200), \
- (120 120, 220 120, 180 199, 160 200, 140 199, 120 120))");
-
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(g.get(), simplified.get());
-
- GeomPtr g_expected(wktreader.read(wkt_expected));
-
- ensure(g_expected->equalsExact(simplified.get()));
+ checkTPSNoChange("GEOMETRYCOLLECTION (MULTIPOINT (80 200, 240 200, 240 60, 80 60, 80 200, 140 199, 120 120), POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200)), LINESTRING (80 200, 240 200, 240 60, 80 60, 80 200, 140 199, 120 120))",
+ 10);
}
-// FlattishPolygon
+
+// testNoCollapse_mL
template<>
template<>
-void object::test<8>
-()
+void object::test<16>()
{
- std::string wkt("POLYGON ((0 0, 50 0, 53 0, 55 0, 100 0, 70 1, 60 1, 50 1, 40 1, 0 0))");
- GeomPtr g(wktreader.read(wkt));
-
- std::string wkt_exp("POLYGON ((0 0, 50 0, 100 0, 70 1, 0 0))");
- GeomPtr exp(wktreader.read(wkt_exp));
-
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(exp.get(), simplified.get());
+ checkTPS(
+ "MULTILINESTRING ((0 0, 100 0), (0 0, 60 1, 100 0))",
+ 10.0,
+ "MULTILINESTRING ((0 0, 100 0), (0 0, 60 1, 100 0))"
+ );
}
-// PolygonWithFlattishHole
+// testNoCollapseMany_mL
template<>
template<>
-void object::test<9>
-()
+void object::test<17>()
{
- std::string wkt("POLYGON ((0 0, 0 200, 200 200, 200 0, 0 0), \
- (140 40, 90 95, 40 160, 95 100, 140 40))");
-
- GeomPtr g(wktreader.read(wkt));
- GeomPtr g_expected(wktreader.read(wkt));
-
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(g.get(), simplified.get());
- ensure(g_expected->equalsExact(simplified.get()));
+ checkTPS(
+ "MULTILINESTRING ((0 100, 400 100), (0 100, 105 122, 245 116, 280 110, 330 120, 400 100), (0 100, 155 79, 270 90, 350 70, 400 100), (0 100, 110 130, 205 138, 330 130, 400 100))",
+ 100.0,
+ "MULTILINESTRING ((0 100, 400 100), (0 100, 105 122, 400 100), (0 100, 350 70, 400 100), (0 100, 110 130, 205 138, 400 100))"
+ );
}
-// TinySquare
+
+// testNoCollapseSmallSquare
template<>
template<>
-void object::test<10>
-()
+void object::test<18>()
{
- std::string wkt("POLYGON ((0 5, 5 5, 5 0, 0 0, 0 1, 0 5))");
- GeomPtr g(wktreader.read(wkt));
-
- std::string wkt_exp("POLYGON ((0 5, 5 5, 5 0, 0 0, 0 5))");
- GeomPtr exp(wktreader.read(wkt_exp));
-
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(exp.get(), simplified.get());
+ checkTPS(
+ "POLYGON ((0 5, 5 5, 5 0, 0 0, 0 1, 0 5))",
+ 100,
+ "POLYGON ((0 0, 5 5, 5 0, 0 0))"
+ );
}
-// TinyLineString
+
+// testPolygonRemoveEndpoint
template<>
template<>
-void object::test<11>
-()
-{
- std::string wkt("LINESTRING (0 5, 1 5, 2 5, 5 5)");
- GeomPtr g(wktreader.read(wkt));
-
- std::string wkt_exp("LINESTRING (0 5, 5 5)");
- GeomPtr exp(wktreader.read(wkt_exp));
-
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(exp.get(), simplified.get());
+void object::test<19>()
+ {
+ checkTPS(
+ "POLYGON ((220 180, 261 175, 380 220, 300 40, 140 30, 30 220, 176 176, 220 180))",
+ 40,
+ "POLYGON ((30 220, 380 220, 300 40, 140 30, 30 220))"
+ );
}
-// TinyClosedLineString
+// testLinearRingRemoveEndpoint
template<>
template<>
-void object::test<12>
-()
-{
- std::string wkt("LINESTRING (0 0, 5 0, 5 5, 0 0)");
-
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(g.get(), simplified.get());
+void object::test<20>()
+ {
+ checkTPS(
+ "LINEARRING (220 180, 261 175, 380 220, 300 40, 140 30, 30 220, 176 176, 220 180)",
+ 40,
+ "LINEARRING (30 220, 380 220, 300 40, 140 30, 30 220)"
+ );
}
-// MultiPoint
+
+// testPolygonKeepFlatEndpointWithTouch
+// Test that flat ring endpoint is not removed if it touches another ring
template<>
template<>
-void object::test<13>
-()
+void object::test<21>()
{
- std::string wkt("MULTIPOINT(80 200, 240 200, 240 60, \
- 80 60, 80 200, 140 199, 120 120)");
-
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
-
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(g.get(), simplified.get());
+ checkTPSNoChange(
+ "POLYGON ((0 0, 5 2.05, 10 0, 10 10, 0 10, 0 0),"
+ "(5 2.1, 6 2, 6 4, 4 4, 4 2, 5 2.1))",
+ 0.1);
}
-// MultiLineString
+// Test for no use-after-free triggered by ring endpoint removal handling
+// See https://github.com/libgeos/geos/issues/1107, fix https://github.com/libgeos/geos/pull/1110
template<>
template<>
-void object::test<14>
-()
+void object::test<43>()
{
- std::string wkt("MULTILINESTRING((0 0, 50 0, 70 0, 80 0, 100 0), (0 0, 50 1, 60 1, 100 0))");
- GeomPtr g(wktreader.read(wkt));
-
- //TODO: investigate why TPS does not prevent lines from collapsing (JTS has same behaviour)
- //std::string wkt_exp("MULTILINESTRING ((0 0, 100 0), (0 0, 50 1, 100 0))");
- std::string wkt_exp("MULTILINESTRING ((0 0, 100 0), (0 0, 100 0))");
- GeomPtr exp(wktreader.read(wkt_exp));
-
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
- // std::cout << "expected " << *exp << std::endl;
- // std::cout << "result " << *simplified << std::endl;
- ensure("Simplified geometry is invalid!", simplified->isValid());
- /* Temporarily disable this component of the test only for MSVC
- See https://trac.osgeo.org/geos/ticket/1081 */
-#ifndef _MSC_VER
- ensure_equals_geometry(exp.get(), simplified.get());
-#endif
+ checkTPS("POLYGON ((-222601.33094265286 6299915.50260568, -222599.13611514607 6299917.747821213, -222599.09754554977 6299925.149899498, -222599.07870256738 6299925.234615005, -222595.52372420163 6299932.934861557, -222510 6300300, -221720.85158014414 6300132.680680807, -222448.77936063593 6299647.669870703, -222618.41756525903 6299886.966175825, -222618.40178141624 6299887.020684309, -222616.09648739762 6299892.144482262, -222601.33094265286 6299915.50260568), (-222456.68914400978 6299947.489843342, -222455.07603815367 6299939.772017001, -222455.07542453965 6299939.691595431, -222456.57057590774 6299931.950053182, -222462.75368034307 6299916.87367865, -222465.9575074078 6299911.582542387, -222465.99782740293 6299911.534645676, -222483.5296791599 6299864.079888968, -222484.09789251382 6299852.105440594, -222485.11401620077 6299846.692173677, -222485.13170293145 6299846.639453617, -222487.58585740798 6299841.7086228, -222490.56062790897 6299837.359974515, -222415.1394852571 629992
6.6972160805, -222421.19226152284 6299963.389132584, -222467.0202338936 6299970.185860572, -222465.71145777934 6299968.200784476, -222465.69955208438 6299968.180154371, -222464.63560265294 6299966.053788407, -222456.68914400978 6299947.489843342))",
+ 20,
+ "POLYGON ((-222618.41756525903 6299886.966175825, -222510 6300300, -221720.85158014414 6300132.680680807, -222448.77936063593 6299647.669870703, -222618.41756525903 6299886.966175825), (-222467.0202338936 6299970.185860572, -222456.57057590774 6299931.950053182, -222490.56062790897 6299837.359974515, -222415.1394852571 6299926.6972160805, -222421.19226152284 6299963.389132584, -222467.0202338936 6299970.185860572))");
}
-// GeometryCollection
+// testPolygonKeepEndpointWithCross
+// Test that endpoint is not simplified if it breaks topology
template<>
template<>
-void object::test<15>
-()
+void object::test<22>()
{
- std::string wkt("GEOMETRYCOLLECTION ( \
- MULTIPOINT (80 200, 240 200, 240 60, 80 60, 80 200, 140 199, 120 120), \
- POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200)), \
- LINESTRING (80 200, 240 200, 240 60, 80 60, 80 200, 140 199, 120 120))");
+ checkTPS(
+ "POLYGON ((50 52, 60 50, 90 60, 90 10, 10 10, 10 90, 60 90, 50 55, 40 80, 20 60, 40 50, 50 52))",
+ 10,
+ "POLYGON ((50 52, 90 60, 90 10, 10 10, 10 90, 60 90, 50 55, 40 80, 20 60, 50 52))"
+ );
+}
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simplified = TopologyPreservingSimplifier::simplify(g.get(), 10.0);
+// see https://trac.osgeo.org/geos/ticket/1064
+// testPolygonRemoveFlatEndpoint
+template<>
+template<>
+void object::test<23>()
+{
+ checkTPS(
+ "POLYGON ((42 42, 0 42, 0 100, 42 100, 100 42, 42 42))",
+ 1,
+ "POLYGON ((100 42, 0 42, 0 100, 42 100, 100 42))"
+ );
+}
- ensure("Simplified geometry is invalid!", simplified->isValid());
- ensure_equals_geometry(g.get(), simplified.get());
+// testPolygonManyFlatSegments
+template<>
+template<>
+void object::test<24>()
+{
+ checkTPS(
+ "POLYGON ((5 5, 7 5, 9 5, 9 1, 1 1, 1 5, 3 5, 5 5))",
+ 1,
+ "POLYGON ((9 5, 9 1, 1 1, 1 5, 9 5))"
+ );
+}
+
+//-- vertex is not removed due to overly-restrictive heuristic result length calculation?
+// testPolygonSize5NotSimplfied
+template<>
+template<>
+void object::test<25>()
+{
+ checkTPS(
+ "POLYGON ((10 90, 10 10, 90 10, 47 57, 10 90))",
+ 10,
+ "POLYGON ((10 90, 10 10, 90 10, 47 57, 10 90))"
+ );
+}
+
+/**
+* Test is from http://postgis.refractions.net/pipermail/postgis-users/2008-April/019327.html
+* Exhibits the issue where simplified polygon shells can "jump" across
+* holes, causing invalid topology.
+*
+* @throws Exception
+*/
+// testMultiPolygonWithSmallComponents
+template<>
+template<>
+void object::test<26>()
+{
+ checkTPS("MULTIPOLYGON(((13.73095 51.024734,13.7309323 51.0247668,13.7306959 51.0247959,13.7292724 51.0249742,13.7280216 51.0251252,13.7266598 51.0252998,13.7259617 51.0254072,13.7258854 51.0254201,13.7253253 51.0255144,13.725276 51.025492,13.724538 51.025631,13.7230288 51.0259021,13.7223529 51.0260273,13.7223299 51.0260863,13.7222292 51.026391,13.7220002 51.0273366,13.7217875 51.0282094,13.721746 51.028243,13.7217693 51.0282803,13.7215512 51.0291967,13.721513 51.029222,13.7215203 51.0292567,13.7212713 51.0295967,13.7222258 51.0299532,13.722234 51.03,13.7222931 51.0299823,13.7232514 51.0303187,13.7242514 51.0306715,13.724263 51.030714,13.7243024 51.0306951,13.7249934 51.0309315,13.7265097 51.0314552,13.7266116 51.0313952,13.7267988 51.0313334,13.7269952 51.0313243,13.72703 51.0314107,13.7271637 51.0313254,13.7272524 51.0313839,13.72739 51.031449,13.7276768 51.0313074,13.7283793 51.0309944,13.7296654 51.0304157,13.7297572 51.0303637,13.729845 51.0303139,13.7299557 51.0301763,13.7
300964 51.0300176,13.730252 51.0298919,13.7304615 51.0297932,13.730668 51.0297363,13.730743 51.029783,13.7307859 51.0298398,13.7307094 51.0301388,13.730624 51.030263,13.7306955 51.0303267,13.7301182 51.0325594,13.7300528 51.0325663,13.7301114 51.0327342,13.7301645 51.0329094,13.7300035 51.0327693,13.7299669 51.0327351,13.7299445 51.0327211,13.7298934 51.032814,13.7298539 51.0328585,13.7297737 51.0328321,13.7288526 51.0325639,13.7288201 51.0324367,13.7284426 51.0324383,13.7276461 51.032179,13.7274569 51.0321976,13.7272787 51.0322421,13.7271265 51.0322903,13.7267034 51.0322495,13.7265364 51.0322161,13.7259018 51.0324269,13.7258649 51.03242,13.725733 51.0326646,13.7251933 51.0328876,13.7247918 51.0331374,13.7244439 51.0331106,13.7242967 51.0334273,13.7239131 51.0337529,13.7237035 51.0338511,13.7235429 51.033967,13.7233375 51.0339148,13.7232064 51.0339347,13.7231786 51.0339863,13.7228848 51.0340776,13.7224481 51.0341888,13.7220471 51.0342483,13.7217493 51.0343198,13.721552 51.0343861,13
.7214718 51.0344095,13.7215108 51.034534,13.7205032 51.0349932,13.7197657 51.0352983,13.7195764 51.0352291,13.7195934 51.0352797,13.7182451 51.0359157,13.7181108 51.0359003,13.7181657 51.0359571,13.717622 51.0361956,13.7159749 51.0369683,13.7159057 51.0369284,13.7158604 51.0370288,13.7157161 51.0370124,13.7157523 51.0370733,13.7153708 51.0372801,13.7150274 51.0374899,13.7144074 51.0379192,13.7138287 51.0383899,13.7137514 51.0383857,13.7137492 51.0384566,13.7134249 51.0387269,13.7130179 51.0390385,13.7125791 51.0393343,13.7120736 51.039611,13.7115839 51.0398558,13.7112945 51.0399894,13.7114637 51.0402313,13.7123153 51.041449,13.7126333 51.0417033,13.713371 51.0421453,13.7138861 51.0424061,13.7142518 51.0425683,13.7164587 51.0435668,13.7167995 51.0437957,13.7170883 51.0439897,13.7190694 51.0451663,13.7196131 51.0458277,13.7197562 51.0461521,13.7198262 51.0464192,13.7198377 51.0467389,13.7205681 51.0455573,13.7210009 51.0450379,13.7214987 51.0445401,13.7220306 51.0442859,13.7227215 51.
0439558,13.7237962 51.0434514,13.723979 51.0435278,13.7241448 51.0435041,13.7241052 51.0436042,13.7247987 51.0438896,13.7250186 51.0439093,13.7250579 51.0440386,13.7257225 51.0443545,13.7259312 51.0443456,13.725955 51.0443813,13.7260235 51.0443873,13.7260682 51.0445303,13.7282191 51.0455848,13.7290532 51.045927,13.7292643 51.0458591,13.7292228 51.0459969,13.729706 51.0461854,13.7303185 51.046393,13.7309107 51.0465601,13.731546 51.0466841,13.7321939 51.0467752,13.7332896 51.0468999,13.7333733 51.0469094,13.7334778 51.0468127,13.7335706 51.0469078,13.733651 51.0470684,13.7338458 51.0471508,13.7346109 51.0472333,13.7346367 51.0471474,13.7346922 51.0470697,13.7346666 51.0470056,13.7346564 51.0468714,13.7345552 51.0467095,13.7336001 51.0465496,13.733427 51.046454,13.7335317 51.0464255,13.7347225 51.0465948,13.7348421 51.0466562,13.7349123 51.0466203,13.736811 51.0468537,13.7382043 51.0469796,13.7383487 51.0469803,13.7394909 51.0469005,13.7400899 51.0467949,13.7405051 51.0464739,13.740833
1 51.0462204,13.7412027 51.0463256,13.741053 51.0466451,13.7407291 51.0469007,13.7405095 51.0469726,13.7400888 51.0470337,13.7393051 51.0471049,13.7393014 51.0472015,13.7393088 51.0473019,13.7395556 51.0473056,13.7404944 51.0472245,13.740932 51.0470192,13.7414421 51.0465652,13.7414893 51.0465576,13.7416494 51.0464916,13.7416003 51.0466074,13.7416246 51.04663,13.741668 51.0466443,13.7417272 51.0467159,13.7417503 51.0466716,13.7423587 51.0468732,13.7426958 51.0470246,13.7429143 51.0471813,13.74318 51.04726,13.7430363 51.0472995,13.7433021 51.047588,13.7434678 51.0475916,13.7433805 51.0477019,13.7436362 51.0479981,13.7446308 51.0491622,13.7447961 51.0491827,13.744722 51.0492509,13.7448536 51.0494078,13.745056 51.0494766,13.7450313 51.0496901,13.7453573 51.0500052,13.7465317 51.0512807,13.7466999 51.0513722,13.746638 51.0514149,13.7468683 51.0516781,13.7470071 51.051777,13.7469985 51.0518746,13.7470732 51.0519866,13.7471316 51.0520528,13.7472989 51.0523089,13.7472368 51.0523858,13.74730
63 51.0524932,13.7473468 51.0527412,13.7473392 51.0531614,13.7472987 51.0533157,13.7473919 51.0534224,13.7472684 51.0534549,13.7472134 51.0536926,13.7472913 51.0537784,13.7473216 51.053725,13.7474649 51.0537575,13.7474492 51.053833,13.7475625 51.0537839,13.7497379 51.0544435,13.7515333 51.0551019,13.7527693 51.0555438,13.7549766 51.0564993,13.7550622 51.0565364,13.755105 51.0566612,13.7552745 51.0566237,13.7558661 51.0560648,13.7559318 51.0560101,13.755908 51.055897,13.7559252 51.0558292,13.7559566 51.0557055,13.7564494 51.0551377,13.7564124 51.0550457,13.7573213 51.0539813,13.7575007 51.0539933,13.757856 51.0540047,13.7580394 51.054028,13.7580896 51.053984,13.7580949 51.0539463,13.7579963 51.0538534,13.7581294 51.0537147,13.7582346 51.0535957,13.758354 51.053433,13.758363 51.053392,13.7583656 51.0533457,13.758359 51.0532095,13.7583338 51.0530937,13.7582902 51.0529647,13.7580365 51.0522637,13.7577683 51.051463,13.7573182 51.0501993,13.7571595 51.0497164,13.7567579 51.0490095,13.7563
383 51.0482979,13.7557757 51.0473383,13.7557095 51.0472522,13.7555771 51.0471199,13.7554448 51.0470471,13.7548596 51.0462612,13.7547097 51.046054,13.7549127 51.0460086,13.7548633 51.0459174,13.7548127 51.0458413,13.7547176 51.0457237,13.7538293 51.0449222,13.7530218 51.0441346,13.7526711 51.0437838,13.752446 51.0435522,13.7522297 51.0433547,13.751704 51.042833,13.7513058 51.0424448,13.7505766 51.0417281,13.7499967 51.0411283,13.7497695 51.0408943,13.7493849 51.0405205,13.7486222 51.0397896,13.7478209 51.0390261,13.7477474 51.0389532,13.7477041 51.0389189,13.7476277 51.0388729,13.7475781 51.0388513,13.7472699 51.038726,13.747131 51.0386506,13.7469329 51.0385052,13.7468562 51.0384284,13.7466683 51.0383483,13.7467998 51.038236,13.7473841 51.0380129,13.747838 51.0378277,13.7481801 51.0376558,13.7489728 51.0370285,13.7491313 51.0368016,13.7492665 51.0363477,13.7493166 51.0359389,13.7492966 51.0358087,13.7493888 51.0356942,13.7492867 51.0357016,13.7492855 51.0354359,13.7492829 51.034867,1
3.7492723 51.0348311,13.7492455 51.0347398,13.7493034 51.0346612,13.7491987 51.0346142,13.748866 51.034723,13.748791 51.034201,13.748335 51.034159,13.748294 51.034034,13.748205 51.033764,13.7488691 51.0333037,13.748962 51.033245,13.7486777 51.0332252,13.7483008 51.032683,13.7484397 51.0324582,13.7469913 51.0327817,13.7466998 51.0326205,13.7459997 51.0314852,13.7460996 51.0313569,13.745967 51.0314864,13.7449355 51.0317377,13.7447301 51.0316513,13.7446705 51.0318463,13.7420262 51.0323659,13.7419131 51.0322884,13.7418636 51.0322552,13.7416501 51.0321425,13.7415567 51.0317708,13.7414972 51.0314666,13.741484 51.0311492,13.741923 51.031003,13.7418649 51.030884,13.74209 51.0304134,13.7422077 51.0300143,13.7421975 51.0299222,13.742286 51.029835,13.7421463 51.0297533,13.7420951 51.0296254,13.7415933 51.0288452,13.7414906 51.0286855,13.7414437 51.0286127,13.7413482 51.0284642,13.7410545 51.0280777,13.7407158 51.0277229,13.7401513 51.0273842,13.7392803 51.0270293,13.7382744 51.0267844,13.73732
1 51.0267454,13.7365929 51.0267541,13.736556 51.026812,13.7364715 51.026754,13.7357088 51.0268017,13.7353967 51.02678,13.73534 51.02685,13.7352667 51.0267757,13.734907 51.0267324,13.734824 51.02679,13.7347684 51.0267064,13.7342093 51.0266674,13.73409 51.026725,13.7340359 51.0266283,13.7335072 51.0265633,13.733407 51.02663,13.7333208 51.0265373,13.7317087 51.0263813,13.7317173 51.0263119,13.73167 51.026241,13.7317563 51.0261602,13.7318473 51.0258395,13.7318647 51.0254971,13.73183 51.0253281,13.7317736 51.0252414,13.731663 51.025181,13.7316826 51.0251114,13.7310803 51.0247604,13.73095 51.024734)),((13.7368533 51.0470386,13.7368426 51.0471226,13.7368067 51.0472669,13.7368255 51.0473828,13.7369099 51.0474154,13.7376695 51.0474677,13.7382756 51.0474245,13.738513 51.0474297,13.7386105 51.0474065,13.738705 51.0473737,13.7385856 51.0473757,13.7385618 51.0473751,13.7385263 51.0473743,13.7384706 51.0473744,13.7383071 51.0473734,13.7383822 51.0473564,13.7390821 51.047287,13.7390933 51.047209,1
3.7390933 51.0471421,13.7368533 51.0470386)),((13.7367293 51.0470057,13.7346615 51.0466892,13.7347551 51.0468411,13.7347754 51.0470359,13.7347106 51.0471899,13.7356421 51.0472919,13.7366963 51.0474074,13.736705 51.047249,13.7367293 51.0470057)))",
+ 0.0057,
+ "MULTIPOLYGON (((13.73095 51.024734, 13.7123153 51.041449, 13.7412027 51.0463256, 13.7552745 51.0566237, 13.7484397 51.0324582, 13.73095 51.024734)), ((13.7390933 51.0471421, 13.7369099 51.0474154, 13.7390933 51.047209, 13.7390933 51.0471421)), ((13.7367293 51.0470057, 13.7346615 51.0466892, 13.7347106 51.0471899, 13.7367293 51.0470057)))");
+}
+
+/**
+* Test is from http://lists.jump-project.org/pipermail/jts-devel/2008-February/002350.html
+* @throws Exception
+*/
+// testPolygonWithSpike
+template<>
+template<>
+void object::test<27>()
+{
+ checkTPS("POLYGON ((3312459.605 6646878.353, 3312460.524 6646875.969, 3312459.427 6646878.421, 3312460.014 6646886.391, 3312465.889 6646887.398, 3312470.827 6646884.839, 3312475.4 6646878.027, 3312477.289 6646871.694, 3312472.748 6646869.547, 3312468.253 6646874.01, 3312463.52 6646875.779, 3312459.605 6646878.353))",
+ 2,
+ "POLYGON ((3312459.605 6646878.353, 3312460.524 6646875.969, 3312459.427 6646878.421, 3312460.014 6646886.391, 3312465.889 6646887.398, 3312470.827 6646884.839, 3312477.289 6646871.694, 3312472.748 6646869.547, 3312459.605 6646878.353))");
+}
+
+// testLineComponentCross
+template<>
+template<>
+void object::test<28>()
+{
+ checkTPS("MULTILINESTRING ((0 0, 10 2, 20 0), (9 1, 11 1))",
+ 4,
+ "MULTILINESTRING ((0 0, 10 2, 20 0), (9 1, 11 1))");
+}
+
+// testPolygonComponentCrossAtEndpoint
+template<>
+template<>
+void object::test<29>()
+{
+ checkTPS("MULTIPOLYGON (((50 40, 40 60, 80 40, 0 0, 30 70, 50 40)), ((40 56, 40 57, 41 56, 40 56)))",
+ 30,
+ "MULTIPOLYGON (((50 40, 80 40, 0 0, 30 70, 50 40)), ((40 56, 40 57, 41 56, 40 56)))");
+}
+
+// testPolygonIntersectingSegments
+template<>
+template<>
+void object::test<30>()
+{
+ checkTPS("MULTIPOLYGON (((0.63 0.2, 0.35 0, 0.73 0.66, 0.63 0.2)), ((1.42 4.01, 3.45 0.7, 1.79 1.47, 0 0.57, 1.42 4.01)))",
+ 10,
+ "MULTIPOLYGON (((0.63 0.2, 0.35 0, 0.73 0.66, 0.63 0.2)), ((1.42 4.01, 3.45 0.7, 1.79 1.47, 0 0.57, 1.42 4.01)))");
}
// GeometryCollection with empty elements
// See http://trac.osgeo.org/geos/ticket/519
template<>
template<>
-void object::test<16>
-()
+void object::test<31>()
{
- std::string wkt("GEOMETRYCOLLECTION ( \
- LINESTRING (0 0, 10 0), POLYGON EMPTY)");
-
- GeomPtr g(wktreader.read(wkt));
- GeomPtr simp = TopologyPreservingSimplifier::simplify(g.get(), 1);
-
- ensure("Simplified geometry is invalid!", simp->isValid());
- ensure_equals(wktwriter.write(simp.get()),
- "GEOMETRYCOLLECTION (LINESTRING (0 0, 10 0))");
+ checkTPS("GEOMETRYCOLLECTION ( LINESTRING (0 0, 10 0), POLYGON EMPTY)",
+ 1,
+ "GEOMETRYCOLLECTION (LINESTRING (0 0, 10 0))");
}
+// Test that start point of a closed LineString is not changed
+template<>
+template<>
+void object::test<32>()
+{
+ checkTPSNoChange("LINESTRING (1 0, 2 0, 2 2, 0 2, 0 0, 1 0)", 0);
+}
+
+// Test for no use-after-free triggered by ring endpoint removal handling
+// See https://github.com/libgeos/geos/issues/1107, fix https://github.com/libgeos/geos/pull/1110
+template<>
+template<>
+void object::test<33>()
+{
+ checkTPS("POLYGON ((-222601.33094265286 6299915.50260568, -222599.13611514607 6299917.747821213, -222599.09754554977 6299925.149899498, -222599.07870256738 6299925.234615005, -222595.52372420163 6299932.934861557, -222510 6300300, -221720.85158014414 6300132.680680807, -222448.77936063593 6299647.669870703, -222618.41756525903 6299886.966175825, -222618.40178141624 6299887.020684309, -222616.09648739762 6299892.144482262, -222601.33094265286 6299915.50260568), (-222456.68914400978 6299947.489843342, -222455.07603815367 6299939.772017001, -222455.07542453965 6299939.691595431, -222456.57057590774 6299931.950053182, -222462.75368034307 6299916.87367865, -222465.9575074078 6299911.582542387, -222465.99782740293 6299911.534645676, -222483.5296791599 6299864.079888968, -222484.09789251382 6299852.105440594, -222485.11401620077 6299846.692173677, -222485.13170293145 6299846.639453617, -222487.58585740798 6299841.7086228, -222490.56062790897 6299837.359974515, -222415.1394852571 629992
6.6972160805, -222421.19226152284 6299963.389132584, -222467.0202338936 6299970.185860572, -222465.71145777934 6299968.200784476, -222465.69955208438 6299968.180154371, -222464.63560265294 6299966.053788407, -222456.68914400978 6299947.489843342))",
+ 20,
+ "POLYGON ((-222618.41756525903 6299886.966175825, -222510 6300300, -221720.85158014414 6300132.680680807, -222448.77936063593 6299647.669870703, -222618.41756525903 6299886.966175825), (-222467.0202338936 6299970.185860572, -222456.57057590774 6299931.950053182, -222490.56062790897 6299837.359974515, -222415.1394852571 6299926.6972160805, -222421.19226152284 6299963.389132584, -222467.0202338936 6299970.185860572))");
+}
+
+// https://github.com/libgeos/geos/issues/1070
+template<>
+template<>
+void object::test<34>()
+{
+ auto input = wktreader.read("MULTILINESTRING((0 0, 1 0001211111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111, 1 00015111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111, 1 0001511111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111, 1 0, 10 01, 0 0))");
+
+ auto simplified = TopologyPreservingSimplifier::simplify(input.get(), 2.0);
+
+ ensure(simplified != nullptr); // no crash
+}
+
+
} // namespace tut
-----------------------------------------------------------------------
Summary of changes:
NEWS.md | 1 +
include/geos/algorithm/RayCrossingCounter.h | 4 +-
include/geos/simplify/ComponentJumpChecker.h | 120 +++++
include/geos/simplify/TaggedLineString.h | 28 +-
include/geos/simplify/TaggedLineStringSimplifier.h | 79 ++--
include/geos/simplify/TaggedLinesSimplifier.h | 33 +-
src/simplify/ComponentJumpChecker.cpp | 202 ++++++++
src/simplify/TaggedLineString.cpp | 80 +++-
src/simplify/TaggedLineStringSimplifier.cpp | 216 ++++++---
src/simplify/TaggedLinesSimplifier.cpp | 34 +-
src/simplify/TopologyPreservingSimplifier.cpp | 25 +-
.../simplify/TopologyPreservingSimplifierTest.cpp | 514 ++++++++++++---------
12 files changed, 949 insertions(+), 387 deletions(-)
create mode 100644 include/geos/simplify/ComponentJumpChecker.h
create mode 100644 src/simplify/ComponentJumpChecker.cpp
hooks/post-receive
--
GEOS
More information about the geos-commits
mailing list