[geos-commits] [SCM] GEOS branch 3.13 updated. b3844c03ee8761a0713c15931d72f2e242f0b44e
git at osgeo.org
git at osgeo.org
Fri Jan 17 15:07:23 PST 2025
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.13 has been updated
via b3844c03ee8761a0713c15931d72f2e242f0b44e (commit)
from 52c30fd0387e35148b7b5baaf67841d9643c0ee3 (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 b3844c03ee8761a0713c15931d72f2e242f0b44e
Author: Martin Davis <mtnclimb at gmail.com>
Date: Fri Jan 17 14:37:47 2025 -0800
Fix overlay heuristic for GeometryCollections with empty elements (#1229)
diff --git a/include/geos/geom/HeuristicOverlay.h b/include/geos/geom/HeuristicOverlay.h
index bc1217a78..e4eb8b74a 100644
--- a/include/geos/geom/HeuristicOverlay.h
+++ b/include/geos/geom/HeuristicOverlay.h
@@ -46,37 +46,7 @@ class StructuredCollection {
public:
- StructuredCollection(const Geometry* g)
- : factory(g->getFactory())
- , pt_union(nullptr)
- , line_union(nullptr)
- , poly_union(nullptr)
- {
- readCollection(g);
- unionByDimension();
- };
-
- StructuredCollection()
- : factory(nullptr)
- , pt_union(nullptr)
- , line_union(nullptr)
- , poly_union(nullptr)
- {};
-
- void readCollection(const Geometry* g);
- const Geometry* getPolyUnion() const { return poly_union.get(); }
- const Geometry* getLineUnion() const { return line_union.get(); }
- const Geometry* getPointUnion() const { return pt_union.get(); }
-
- std::unique_ptr<Geometry> doUnion(const StructuredCollection& a) const;
- std::unique_ptr<Geometry> doIntersection(const StructuredCollection& a) const;
- std::unique_ptr<Geometry> doSymDifference(const StructuredCollection& a) const;
- std::unique_ptr<Geometry> doDifference(const StructuredCollection& a) const;
- std::unique_ptr<Geometry> doUnaryUnion() const;
-
- static void toVector(const Geometry* g, std::vector<const Geometry*>& v);
- void unionByDimension(void);
-
+ static std::unique_ptr<Geometry> overlay(const Geometry* g0, const Geometry* g1, int opCode);
private:
@@ -87,7 +57,49 @@ private:
std::unique_ptr<Geometry> pt_union;
std::unique_ptr<Geometry> line_union;
std::unique_ptr<Geometry> poly_union;
+ Dimension::DimensionType dimension;
+ StructuredCollection(const Geometry* g)
+ : factory(g->getFactory())
+ , pt_union(nullptr)
+ , line_union(nullptr)
+ , poly_union(nullptr)
+ , dimension(Dimension::DONTCARE)
+ {
+ readCollection(g);
+ unionByDimension();
+ };
+
+ StructuredCollection()
+ : factory(nullptr)
+ , pt_union(nullptr)
+ , line_union(nullptr)
+ , poly_union(nullptr)
+ , dimension(Dimension::DONTCARE)
+ {};
+
+ Dimension::DimensionType getDimension() const
+ {
+ return dimension;
+ };
+
+ void addDimension(Dimension::DimensionType dim);
+ std::unique_ptr<Geometry> doUnaryUnion(int resultDim) const;
+ std::unique_ptr<Geometry> computeResult(StructuredCollection& coll, int opCode,
+ Dimension::DimensionType dimA, Dimension::DimensionType dimB) const;
+
+ void readCollection(const Geometry* g);
+ const Geometry* getPolyUnion() const { return poly_union.get(); }
+ const Geometry* getLineUnion() const { return line_union.get(); }
+ const Geometry* getPointUnion() const { return pt_union.get(); }
+
+ std::unique_ptr<Geometry> doUnion(const StructuredCollection& a) const;
+ std::unique_ptr<Geometry> doIntersection(const StructuredCollection& a) const;
+ std::unique_ptr<Geometry> doSymDifference(const StructuredCollection& a) const;
+ std::unique_ptr<Geometry> doDifference(const StructuredCollection& a) const;
+
+ static void toVector(const Geometry* g, std::vector<const Geometry*>& v);
+ void unionByDimension(void);
};
diff --git a/src/geom/Geometry.cpp b/src/geom/Geometry.cpp
index 433933587..a34c7bf35 100644
--- a/src/geom/Geometry.cpp
+++ b/src/geom/Geometry.cpp
@@ -619,43 +619,6 @@ Geometry::intersection(const Geometry* other) const
std::unique_ptr<Geometry>
Geometry::Union(const Geometry* other) const
{
-#ifdef SHORTCIRCUIT_PREDICATES
- // if envelopes are disjoint return a MULTI geom or
- // a geometrycollection
- if(! getEnvelopeInternal()->intersects(other->getEnvelopeInternal())) {
-//cerr<<"SHORTCIRCUITED-UNION engaged"<<endl;
- const GeometryCollection* coll;
-
- std::size_t ngeomsThis = getNumGeometries();
- std::size_t ngeomsOther = other->getNumGeometries();
-
- // Allocated for ownership transfer
- std::vector<std::unique_ptr<Geometry>> v;
- v.reserve(ngeomsThis + ngeomsOther);
-
-
- if(nullptr != (coll = dynamic_cast<const GeometryCollection*>(this))) {
- for(std::size_t i = 0; i < ngeomsThis; ++i) {
- v.push_back(coll->getGeometryN(i)->clone());
- }
- }
- else {
- v.push_back(this->clone());
- }
-
- if(nullptr != (coll = dynamic_cast<const GeometryCollection*>(other))) {
- for(std::size_t i = 0; i < ngeomsOther; ++i) {
- v.push_back(coll->getGeometryN(i)->clone());
- }
- }
- else {
- v.push_back(other->clone());
- }
-
- return _factory->buildGeometry(std::move(v));
- }
-#endif
-
return HeuristicOverlay(this, other, OverlayNG::UNION);
}
@@ -676,40 +639,6 @@ Geometry::difference(const Geometry* other) const
std::unique_ptr<Geometry>
Geometry::symDifference(const Geometry* other) const
{
- // if envelopes are disjoint return a MULTI geom or
- // a geometrycollection
- if(! getEnvelopeInternal()->intersects(other->getEnvelopeInternal()) && !(isEmpty() && other->isEmpty())) {
- const GeometryCollection* coll;
-
- std::size_t ngeomsThis = getNumGeometries();
- std::size_t ngeomsOther = other->getNumGeometries();
-
- // Allocated for ownership transfer
- std::vector<std::unique_ptr<Geometry>> v;
- v.reserve(ngeomsThis + ngeomsOther);
-
-
- if(nullptr != (coll = dynamic_cast<const GeometryCollection*>(this))) {
- for(std::size_t i = 0; i < ngeomsThis; ++i) {
- v.push_back(coll->getGeometryN(i)->clone());
- }
- }
- else {
- v.push_back(this->clone());
- }
-
- if(nullptr != (coll = dynamic_cast<const GeometryCollection*>(other))) {
- for(std::size_t i = 0; i < ngeomsOther; ++i) {
- v.push_back(coll->getGeometryN(i)->clone());
- }
- }
- else {
- v.push_back(other->clone());
- }
-
- return _factory->buildGeometry(std::move(v));
- }
-
return HeuristicOverlay(this, other, OverlayNG::SYMDIFFERENCE);
}
diff --git a/src/geom/HeuristicOverlay.cpp b/src/geom/HeuristicOverlay.cpp
index 2d3d51304..c164e00fb 100644
--- a/src/geom/HeuristicOverlay.cpp
+++ b/src/geom/HeuristicOverlay.cpp
@@ -20,11 +20,19 @@
* This file provides a single function, taking two
* const Geometry pointers, applying a binary operator to them
* and returning a result Geometry in an unique_ptr<>.
+ *
+ * It implements a "combine-disjoint" heuristic for optimizing some overlay cases.
+ * It also implements overlay for GeometryCollections, which is not (yet)
+ * provided by OverlayNG.
+ * Internal overlay usage should call OverlayNG directly
+ * if this behaviour is not needed (i.e. when the inputs are known to be simple
+ * or when full overlay is desired to be computed.)
*
**********************************************************************/
#include <geos/geom/HeuristicOverlay.h>
#include <geos/operation/overlayng/OverlayNGRobust.h>
+#include <geos/operation/overlayng/OverlayUtil.h>
#include <geos/util/IllegalArgumentException.h>
#include <geos/geom/Dimension.h>
#include <geos/geom/MultiPoint.h>
@@ -39,32 +47,109 @@ namespace geom { // geos::geom
using operation::overlayng::OverlayNG;
using operation::overlayng::OverlayNGRobust;
+using operation::overlayng::OverlayUtil;
+
+bool
+hasSingleNonEmptyElement(const Geometry* geom) {
+ if (geom->getGeometryTypeId() != GEOS_GEOMETRYCOLLECTION) {
+ //-- is a non-empty element
+ return ! geom->isEmpty();
+ }
+ //-- iterate over GC elements and determine if a single non-empty is present
+ //-- buffer num geoms, since can be expensive for nested collections
+ bool foundNonEmptyElement = false;
+ std::size_t numGeoms = geom->getNumGeometries();
+ for(std::size_t i = 0; i < numGeoms; ++i) {
+ if (hasSingleNonEmptyElement(geom->getGeometryN(i))) {
+ //-- already found an element, so more than one present
+ if (foundNonEmptyElement) {
+ return false;
+ }
+ foundNonEmptyElement = true;
+ }
+ }
+ return foundNonEmptyElement;
+}
+
+bool isCombinable(const Geometry* g0, const Geometry* g1)
+{
+ //-- if both empty use OverlayNG return-type logic
+ if (g0->isEmpty() && g1->isEmpty())
+ return false;
+
+ //-- not disjoint
+ if (g0->getEnvelopeInternal()->intersects(g1->getEnvelopeInternal()))
+ return false;
+
+ return hasSingleNonEmptyElement(g0)
+ && hasSingleNonEmptyElement(g1);
+}
+
+void
+extractElements(const Geometry* g, std::vector<std::unique_ptr<Geometry>>& v)
+{
+ if (const auto* coll = dynamic_cast<const GeometryCollection*>(g)) {
+ //-- buffer num geoms, since can be expensive for nested collections
+ std::size_t numGeoms = g->getNumGeometries();
+ for(std::size_t i = 0; i < numGeoms; ++i) {
+ //-- recurse to handle nested GCs
+ extractElements(coll->getGeometryN(i), v);
+ }
+ }
+ else if (g->isEmpty()) {
+ return;
+ }
+ else {
+ v.push_back(g->clone());
+ }
+}
+
+std::unique_ptr<Geometry>
+combineReduced(const Geometry* g0, const Geometry* g1)
+{
+ // Allocated for ownership transfer
+ std::vector<std::unique_ptr<Geometry>> v;
+ v.reserve(g0->getNumGeometries() + g1->getNumGeometries());
+ extractElements(g0, v);
+ extractElements(g1, v);
+ return g0->getFactory()->buildGeometry(std::move(v));
+}
+
+bool isHandledByOverlayNG(const Geometry* geom) {
+ if (geom->isMixedDimension() && ! geom->isEmpty())
+ return false;
+ //-- GCs with polygonals must be unioned
+ if (geom->getGeometryTypeId() == GEOS_GEOMETRYCOLLECTION
+ && geom->getDimension() == 2)
+ return false;
+ return true;
+}
std::unique_ptr<Geometry>
HeuristicOverlay(const Geometry* g0, const Geometry* g1, int opCode)
{
- std::unique_ptr<Geometry> ret;
+ /**
+ * If feasible, do fast combine instead of full overlay
+ *
+ * NOTE: does not node LineStrings, or merge elements of arg geoms.
+ * This is different behaviour to full overlay.
+ */
+ //TODO: could also handle difference and intersection?
+ if ( ( opCode == OverlayNG::UNION
+ || opCode == OverlayNG::SYMDIFFERENCE)
+ && isCombinable(g0, g1)) {
+ return combineReduced(g0, g1);
+ }
/*
* overlayng::OverlayNGRobust does not currently handle
- * GeometryCollection (collections of mixed dimension)
+ * non-empty GeometryCollections
* so we handle that case here.
*/
- if ((g0->isMixedDimension() && !g0->isEmpty()) ||
- (g1->isMixedDimension() && !g1->isEmpty()))
+ if (! isHandledByOverlayNG(g0) ||
+ ! isHandledByOverlayNG(g1))
{
- StructuredCollection s0(g0);
- StructuredCollection s1(g1);
- switch (opCode) {
- case OverlayNG::UNION:
- return s0.doUnion(s1);
- case OverlayNG::DIFFERENCE:
- return s0.doDifference(s1);
- case OverlayNG::SYMDIFFERENCE:
- return s0.doSymDifference(s1);
- case OverlayNG::INTERSECTION:
- return s0.doIntersection(s1);
- }
+ return StructuredCollection::overlay(g0, g1, opCode);
}
/*
@@ -86,6 +171,7 @@ HeuristicOverlay(const Geometry* g0, const Geometry* g1, int opCode)
* Running overlayng::OverlayNGRobust at this stage should guarantee
* that none of the other heuristics are ever needed.
*/
+ std::unique_ptr<Geometry> ret;
if (g0 == nullptr && g1 == nullptr) {
return std::unique_ptr<Geometry>(nullptr);
}
@@ -108,6 +194,34 @@ HeuristicOverlay(const Geometry* g0, const Geometry* g1, int opCode)
return ret;
}
+/* public static */
+std::unique_ptr<Geometry>
+StructuredCollection::overlay(const Geometry* g0, const Geometry* g1, int opCode)
+{
+ StructuredCollection s0(g0);
+ StructuredCollection s1(g1);
+ switch (opCode) {
+ case OverlayNG::UNION:
+ return s0.doUnion(s1);
+ case OverlayNG::DIFFERENCE:
+ return s0.doDifference(s1);
+ case OverlayNG::SYMDIFFERENCE:
+ return s0.doSymDifference(s1);
+ case OverlayNG::INTERSECTION:
+ return s0.doIntersection(s1);
+ default:
+ throw util::IllegalArgumentException("Invalid overlay opcode");
+ }
+}
+
+/* private */
+void
+StructuredCollection::addDimension(Dimension::DimensionType dim)
+{
+ if (dimension < dim)
+ dimension = dim;
+}
+
/* public */
void
StructuredCollection::readCollection(const Geometry* g)
@@ -123,12 +237,15 @@ StructuredCollection::readCollection(const Geometry* g)
switch (g->getGeometryTypeId()) {
case GEOS_POINT:
pts.push_back(g);
+ addDimension(Dimension::P);
break;
case GEOS_LINESTRING:
lines.push_back(g);
+ addDimension(Dimension::L);
break;
case GEOS_POLYGON:
polys.push_back(g);
+ addDimension(Dimension::A);
break;
default:
throw util::IllegalArgumentException("cannot process unexpected collection");
@@ -181,7 +298,9 @@ StructuredCollection::unionByDimension(void)
// io::WKTWriter w;
// std::cout << "line_col " << w.write(*line_col) << std::endl;
- // std::cout << "line_union " << w.write(*line_union) << std::endl;
+ // std::cout << "pt_union: " << w.write(*pt_union) << std::endl;
+ // std::cout << "line_union: " << w.write(*line_union) << std::endl;
+ // std::cout << "poly_union: " << w.write(*poly_union) << std::endl;
if (! pt_union->isPuntal())
throw util::IllegalArgumentException("union of points not puntal");
@@ -193,7 +312,7 @@ StructuredCollection::unionByDimension(void)
/* public */
std::unique_ptr<Geometry>
-StructuredCollection::doUnaryUnion() const
+StructuredCollection::doUnaryUnion(int resultDim) const
{
/*
* Before output, we clean up the components to remove spatial
@@ -221,15 +340,27 @@ StructuredCollection::doUnaryUnion() const
toVector(lines_less_polys.get(), geoms);
toVector(poly_union.get(), geoms);
+ if (geoms.size() == 0) {
+ return OverlayUtil::createEmptyResult(
+ resultDim, factory);
+ }
return factory->buildGeometry(geoms.begin(), geoms.end());
}
+/* public */
+std::unique_ptr<Geometry>
+StructuredCollection::computeResult(StructuredCollection& coll, int opCode,
+ Dimension::DimensionType dimA, Dimension::DimensionType dimB) const
+{
+ coll.unionByDimension();
+ int resultDim = OverlayUtil::resultDimension(opCode, dimA, dimB);
+ return coll.doUnaryUnion(resultDim);
+}
/* public */
std::unique_ptr<Geometry>
StructuredCollection::doUnion(const StructuredCollection& a) const
{
-
auto poly_union_poly = OverlayNGRobust::Overlay(
a.getPolyUnion(),
poly_union.get(),
@@ -249,8 +380,7 @@ StructuredCollection::doUnion(const StructuredCollection& a) const
c.readCollection(poly_union_poly.get());
c.readCollection(line_union_line.get());
c.readCollection(pt_union_pt.get());
- c.unionByDimension();
- return c.doUnaryUnion();
+ return computeResult(c, OverlayNG::UNION, getDimension(), a.getDimension());
}
@@ -322,8 +452,7 @@ StructuredCollection::doIntersection(const StructuredCollection& a) const
c.readCollection(pt_inter_poly.get());
c.readCollection(pt_inter_line.get());
c.readCollection(pt_inter_pt.get());
- c.unionByDimension();
- return c.doUnaryUnion();
+ return computeResult(c, OverlayNG::INTERSECTION, getDimension(), a.getDimension());
}
@@ -364,8 +493,7 @@ StructuredCollection::doDifference(const StructuredCollection& a) const
c.readCollection(poly_diff_poly.get());
c.readCollection(line_diff_poly_line.get());
c.readCollection(pt_diff_poly_line_pt.get());
- c.unionByDimension();
- return c.doUnaryUnion();
+ return computeResult(c, OverlayNG::DIFFERENCE, getDimension(), a.getDimension());
}
std::unique_ptr<Geometry>
@@ -390,8 +518,7 @@ StructuredCollection::doSymDifference(const StructuredCollection& a) const
c.readCollection(poly_symdiff_poly.get());
c.readCollection(line_symdiff_line.get());
c.readCollection(pt_symdiff_pt.get());
- c.unionByDimension();
- return c.doUnaryUnion();
+ return computeResult(c, OverlayNG::SYMDIFFERENCE, getDimension(), a.getDimension());
}
diff --git a/src/operation/union/CascadedPolygonUnion.cpp b/src/operation/union/CascadedPolygonUnion.cpp
index 2a4e736f4..82cd91bcc 100644
--- a/src/operation/union/CascadedPolygonUnion.cpp
+++ b/src/operation/union/CascadedPolygonUnion.cpp
@@ -20,11 +20,11 @@
#include <geos/geom/Geometry.h>
#include <geos/geom/GeometryFactory.h>
-#include <geos/geom/HeuristicOverlay.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/Polygon.h>
#include <geos/index/strtree/TemplateSTRtree.h>
#include <geos/operation/overlayng/OverlayNG.h>
+#include <geos/operation/overlayng/OverlayNGRobust.h>
#include <geos/operation/union/CascadedPolygonUnion.h>
#include <geos/operation/valid/IsValidOp.h>
#include <geos/operation/valid/IsSimpleOp.h>
@@ -201,10 +201,8 @@ CascadedPolygonUnion::restrictToPolygons(std::unique_ptr<geom::Geometry> g)
std::unique_ptr<geom::Geometry>
ClassicUnionStrategy::Union(const geom::Geometry* g0, const geom::Geometry* g1)
{
- // TODO make an rvalue overload for this that can consume its inputs.
- // At a minimum, a copy in the buffer fallback can be eliminated.
try {
- return geom::HeuristicOverlay(g0, g1, operation::overlayng::OverlayNG::UNION);
+ return operation::overlayng::OverlayNGRobust::Overlay(g0, g1, operation::overlayng::OverlayNG::UNION);
}
catch (const util::TopologyException &ex) {
::geos::ignore_unused_variable_warning(ex);
diff --git a/src/operation/valid/MakeValid.cpp b/src/operation/valid/MakeValid.cpp
index f54428390..4d00027c7 100644
--- a/src/operation/valid/MakeValid.cpp
+++ b/src/operation/valid/MakeValid.cpp
@@ -23,9 +23,9 @@
#include <geos/operation/valid/IsValidOp.h>
#include <geos/operation/overlayng/OverlayNG.h>
+#include <geos/operation/overlayng/OverlayNGRobust.h>
#include <geos/operation/polygonize/BuildArea.h>
#include <geos/operation/union/UnaryUnionOp.h>
-#include <geos/geom/HeuristicOverlay.h>
#include <geos/geom/Geometry.h>
#include <geos/geom/GeometryCollection.h>
#include <geos/geom/GeometryFactory.h>
@@ -51,6 +51,7 @@
using namespace geos::geom;
using geos::operation::overlayng::OverlayNG;
+using geos::operation::overlayng::OverlayNGRobust;
namespace geos {
namespace operation { // geos.operation
@@ -60,19 +61,19 @@ namespace valid { // geos.operation.valid
static std::unique_ptr<geom::Geometry>
makeValidSymDifference(const geom::Geometry* g0, const geom::Geometry* g1)
{
- return HeuristicOverlay(g0, g1, OverlayNG::SYMDIFFERENCE);
+ return OverlayNGRobust::Overlay(g0, g1, OverlayNG::SYMDIFFERENCE);
}
static std::unique_ptr<geom::Geometry>
makeValidDifference(const geom::Geometry* g0, const geom::Geometry* g1)
{
- return HeuristicOverlay(g0, g1, OverlayNG::DIFFERENCE);
+ return OverlayNGRobust::Overlay(g0, g1, OverlayNG::DIFFERENCE);
}
static std::unique_ptr<geom::Geometry>
makeValidUnion(const geom::Geometry* g0, const geom::Geometry* g1)
{
- return HeuristicOverlay(g0, g1, OverlayNG::UNION);
+ return OverlayNGRobust::Overlay(g0, g1, OverlayNG::UNION);
}
/*
diff --git a/tests/unit/geom/HeuristicOverlayTest.cpp b/tests/unit/geom/HeuristicOverlayTest.cpp
index c687e8a69..0f9a19717 100644
--- a/tests/unit/geom/HeuristicOverlayTest.cpp
+++ b/tests/unit/geom/HeuristicOverlayTest.cpp
@@ -163,7 +163,7 @@ void object::test<8> ()
"GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)))",
"GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)), POINT(20 20))",
OverlayNG::DIFFERENCE,
- "GEOMETRYCOLLECTION EMPTY"
+ "POLYGON EMPTY"
);
}
@@ -204,4 +204,16 @@ void object::test<11> ()
);
}
+template<>
+template<>
+void object::test<12> ()
+{
+ checkOverlay(
+ "GEOMETRYCOLLECTION (POLYGON ((1 5, 5 5, 5 1, 1 1, 1 5)), POLYGON ((9 5, 9 1, 5 1, 5 5, 9 5)))",
+ "POLYGON ((1 5, 9 5, 9 1, 1 1, 1 5))",
+ OverlayNG::DIFFERENCE,
+ "POLYGON EMPTY"
+ );
+}
+
} // namespace tut
diff --git a/tests/xmltester/BufferResultMatcher.cpp b/tests/xmltester/BufferResultMatcher.cpp
index dd260ca7c..7100673d1 100644
--- a/tests/xmltester/BufferResultMatcher.cpp
+++ b/tests/xmltester/BufferResultMatcher.cpp
@@ -19,7 +19,6 @@
#include "BufferResultMatcher.h"
#include <geos/geom/Geometry.h>
-#include <geos/geom/HeuristicOverlay.h>
#include <geos/operation/overlayng/OverlayNG.h>
#include <geos/algorithm/distance/DiscreteHausdorffDistance.h>
@@ -65,15 +64,8 @@ BufferResultMatcher::isSymDiffAreaInTolerance(
const geom::Geometry& actualBuffer,
const geom::Geometry& expectedBuffer)
{
- typedef std::unique_ptr<geom::Geometry> GeomPtr;
-
- using geos::geom::HeuristicOverlay;
-
double area = expectedBuffer.getArea();
- GeomPtr diff = HeuristicOverlay(&actualBuffer, &expectedBuffer,
- operation::overlayng::OverlayNG::SYMDIFFERENCE);
-
- double areaDiff = diff->getArea();
+ double areaDiff = actualBuffer.symDifference(&expectedBuffer)->getArea();
// can't get closer than difference area = 0 !
// This also handles case when symDiff is empty
diff --git a/tests/xmltester/XMLTester.cpp b/tests/xmltester/XMLTester.cpp
index 165fa199b..aa01c88da 100644
--- a/tests/xmltester/XMLTester.cpp
+++ b/tests/xmltester/XMLTester.cpp
@@ -178,6 +178,13 @@ tolower(std::string& str)
);
}
+void toupper(std::string& s)
+{
+ std::transform(s.begin(), s.end(), s.begin(),
+ [](char c){ return (char)std::toupper(c); }
+ );
+}
+
std::string
normalize_filename(const std::string& str)
{
@@ -875,11 +882,13 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)
tmp = opel->Attribute("arg1");
if(tmp) {
opArg1 = tmp;
+ toupper(opArg1);
}
tmp = opel->Attribute("arg2");
if(tmp) {
opArg2 = tmp;
+ toupper(opArg2);
}
tmp = opel->Attribute("arg3");
@@ -988,13 +997,15 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)
}
else if(opName == "intersection") {
+ geom::Geometry* g1 = opArg1 == "B" ? gB : gA;
+ geom::Geometry* g2 = opArg2 == "B" ? gB : gA;
GeomPtr gRes(parseGeometry(opRes, "expected"));
gRes->normalize();
profile.start();
- GeomPtr gRealRes(gA->intersection(gB));
+ GeomPtr gRealRes(g1->intersection(g2));
profile.stop();
@@ -1312,6 +1323,9 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)
else if(opName == "union") {
+ geom::Geometry* g1 = opArg1 == "B" ? gB : gA;
+ geom::Geometry* g2 = opArg2 == "B" ? gB : gA;
+
GeomPtr gRes(parseGeometry(opRes, "expected"));
gRes->normalize();
@@ -1319,10 +1333,10 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)
GeomPtr gRealRes;
if(gB) {
- gRealRes = gA->Union(gB);
+ gRealRes = g1->Union(g2);
}
else {
- gRealRes = gA->Union();
+ gRealRes = g1->Union();
}
profile.stop();
@@ -1339,11 +1353,13 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)
}
else if(opName == "difference") {
+ geom::Geometry* g1 = opArg1 == "B" ? gB : gA;
+ geom::Geometry* g2 = opArg2 == "B" ? gB : gA;
GeomPtr gRes(parseGeometry(opRes, "expected"));
gRes->normalize();
- GeomPtr gRealRes(gA->difference(gB));
+ GeomPtr gRealRes(g1->difference(g2));
gRealRes->normalize();
@@ -1360,10 +1376,13 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)
}
else if(opName == "symdifference") {
+ geom::Geometry* g1 = opArg1 == "B" ? gB : gA;
+ geom::Geometry* g2 = opArg2 == "B" ? gB : gA;
+
GeomPtr gRes(parseGeometry(opRes, "expected"));
gRes->normalize();
- GeomPtr gRealRes(gA->symDifference(gB));
+ GeomPtr gRealRes(g1->symDifference(g2));
gRealRes->normalize();
diff --git a/tests/xmltester/tests/general/TestOverlayEmpty.xml b/tests/xmltester/tests/general/TestOverlayEmpty.xml
index 9dd989672..2c17375c5 100644
--- a/tests/xmltester/tests/general/TestOverlayEmpty.xml
+++ b/tests/xmltester/tests/general/TestOverlayEmpty.xml
@@ -1016,6 +1016,55 @@
<test> <op name='symDifference' arg1='A' arg2='B'> POLYGON EMPTY </op> </test>
</case>
+<case>
+ <desc>PC - point and disjoint GC with empty elements</desc>
+ <a>
+POINT (1 1)
+ </a>
+ <b>
+GEOMETRYCOLLECTION (POLYGON EMPTY, POINT EMPTY, LINESTRING (2 2, 3 3))
+ </b>
+<test> <op name="intersection" arg1="A" arg2="B">
+POINT EMPTY
+ </op></test>
+<test> <op name="union" arg1="A" arg2="B">
+GEOMETRYCOLLECTION (LINESTRING (2 2, 3 3), POINT (1 1))
+ </op></test>
+<test> <op name="difference" arg1="A" arg2="B">
+POINT (1 1)
+ </op></test>
+<test> <op name="difference" arg1="B" arg2="A">
+LINESTRING (2 2, 3 3)
+ </op></test>
+<test> <op name="symdifference" arg1="A" arg2="B">
+GEOMETRYCOLLECTION (LINESTRING (2 2, 3 3), POINT (1 1))
+ </op></test>
+</case>
+
+<case>
+ <desc>PC - point and disjoint GC with nested empty elements</desc>
+ <a>
+POINT (1 1)
+ </a>
+ <b>
+GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POLYGON EMPTY, POINT EMPTY), GEOMETRYCOLLECTION(POLYGON EMPTY, LINESTRING (2 2, 3 3)))
+ </b>
+<test> <op name="intersection" arg1="A" arg2="B">
+POINT EMPTY
+ </op></test>
+<test> <op name="union" arg1="A" arg2="B">
+GEOMETRYCOLLECTION (LINESTRING (2 2, 3 3), POINT (1 1))
+ </op></test>
+<test> <op name="difference" arg1="A" arg2="B">
+POINT (1 1)
+ </op></test>
+<test> <op name="difference" arg1="B" arg2="A">
+LINESTRING (2 2, 3 3)
+ </op></test>
+<test> <op name="symdifference" arg1="A" arg2="B">
+GEOMETRYCOLLECTION (LINESTRING (2 2, 3 3), POINT (1 1))
+ </op></test>
+</case>
</run>
-----------------------------------------------------------------------
Summary of changes:
include/geos/geom/HeuristicOverlay.h | 38 +++--
src/geom/Geometry.cpp | 71 --------
src/geom/HeuristicOverlay.cpp | 181 ++++++++++++++++++---
src/operation/union/CascadedPolygonUnion.cpp | 6 +-
src/operation/valid/MakeValid.cpp | 9 +-
tests/unit/geom/HeuristicOverlayTest.cpp | 14 +-
tests/xmltester/BufferResultMatcher.cpp | 10 +-
tests/xmltester/XMLTester.cpp | 29 +++-
tests/xmltester/tests/general/TestOverlayEmpty.xml | 49 ++++++
9 files changed, 273 insertions(+), 134 deletions(-)
hooks/post-receive
--
GEOS
More information about the geos-commits
mailing list