[geos-commits] [SCM] GEOS branch main updated. c37391cf135ebe358a80a91d56c0daac4115214b

git at osgeo.org git at osgeo.org
Wed Jun 14 12:34:59 PDT 2023


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, main has been updated
       via  c37391cf135ebe358a80a91d56c0daac4115214b (commit)
      from  5611d6be4396678c777827cab71be49852b62cd4 (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 c37391cf135ebe358a80a91d56c0daac4115214b
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Wed Jun 14 11:55:57 2023 -0700

    Overlay with mixed dimension (#923)
    
    For overlay operations (union/difference/intersection/symdifference) support for GeometryCollection has been missing, at least for those cases of GeometryCollection that contain a mixture of types (point/line/polygon).
    
    This fix tests inputs to HeuristicOverlay (formerly a wide-ranging set of shims to work around issues with the old overlay code) and for those that are mixed-type collections, processes the component dimensions separately, with appropriate logic for the overlay operation being run. Fixes GH-797.

diff --git a/include/geos/geom/Geometry.h b/include/geos/geom/Geometry.h
index 8ba3587c1..38343cee2 100644
--- a/include/geos/geom/Geometry.h
+++ b/include/geos/geom/Geometry.h
@@ -364,6 +364,9 @@ public:
         return isDimensionStrict(Dimension::A);
     }
 
+    bool isMixedDimension() const;
+    bool isMixedDimension(Dimension::DimensionType* baseDim) const;
+
     bool isCollection() const {
         int t = getGeometryTypeId();
         return t == GEOS_GEOMETRYCOLLECTION ||
diff --git a/include/geos/geom/HeuristicOverlay.h b/include/geos/geom/HeuristicOverlay.h
index b6146bbfd..bc1217a78 100644
--- a/include/geos/geom/HeuristicOverlay.h
+++ b/include/geos/geom/HeuristicOverlay.h
@@ -20,16 +20,80 @@
 #pragma once
 
 #include <geos/export.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/Dimension.h>
+
+
 #include <memory> // for unique_ptr
+#include <vector>
 
-namespace geos {
-namespace geom { // geos::geom
 
+namespace geos {
+namespace geom {
 class Geometry;
+class GeometryFactory;
+}
+}
+
+
+namespace geos {
+namespace geom { // geos::geom
 
 std::unique_ptr<Geometry> GEOS_DLL
 HeuristicOverlay(const Geometry* g0, const Geometry* g1, int opCode);
 
+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);
+
+
+private:
+
+    const GeometryFactory* factory;
+    std::vector<const Geometry*> pts;
+    std::vector<const Geometry*> lines;
+    std::vector<const Geometry*> polys;
+    std::unique_ptr<Geometry> pt_union;
+    std::unique_ptr<Geometry> line_union;
+    std::unique_ptr<Geometry> poly_union;
+
+};
+
+
+
+
+
 } // namespace geos::geom
 } // namespace geos
 
diff --git a/src/geom/Geometry.cpp b/src/geom/Geometry.cpp
index d514e4d36..dfd37113e 100644
--- a/src/geom/Geometry.cpp
+++ b/src/geom/Geometry.cpp
@@ -160,6 +160,34 @@ Geometry::getCentroid() const
     return std::unique_ptr<Point>(getFactory()->createPoint(centPt));
 }
 
+/* public */
+bool
+Geometry::isMixedDimension() const
+{
+    Dimension::DimensionType baseDim = Dimension::DONTCARE;
+    return isMixedDimension(&baseDim);
+}
+
+/* public */
+bool
+Geometry::isMixedDimension(Dimension::DimensionType* baseDim) const
+{
+    if (isCollection()) {
+        for (std::size_t i = 0; i < getNumGeometries(); i++) {
+            if (getGeometryN(i)->isMixedDimension(baseDim))
+                return true;
+        }
+        return false;
+    }
+    else {
+        if (*baseDim == Dimension::DONTCARE) {
+            *baseDim = getDimension();
+            return false;
+        }
+        return *baseDim != getDimension();
+    }
+}
+
 /*public*/
 bool
 Geometry::getCentroid(CoordinateXY& ret) const
diff --git a/src/geom/HeuristicOverlay.cpp b/src/geom/HeuristicOverlay.cpp
index 05aa2b607..2d3d51304 100644
--- a/src/geom/HeuristicOverlay.cpp
+++ b/src/geom/HeuristicOverlay.cpp
@@ -25,6 +25,14 @@
 
 #include <geos/geom/HeuristicOverlay.h>
 #include <geos/operation/overlayng/OverlayNGRobust.h>
+#include <geos/util/IllegalArgumentException.h>
+#include <geos/geom/Dimension.h>
+#include <geos/geom/MultiPoint.h>
+#include <geos/geom/MultiLineString.h>
+#include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/GeometryCollection.h>
+#include <geos/io/WKTWriter.h>
 
 namespace geos {
 namespace geom { // geos::geom
@@ -37,48 +45,355 @@ HeuristicOverlay(const Geometry* g0, const Geometry* g1, int opCode)
 {
     std::unique_ptr<Geometry> ret;
 
-/**************************************************************************/
-
-/*
-* overlayng::OverlayNGRobust carries out the following steps
-*
-* 1. Perform overlay operation using PrecisionModel(float).
-*    If no exception return result.
-* 2. Perform overlay operation using SnappingNoder(tolerance), starting
-*    with a very very small tolerance and increasing it for 5 iterations.
-*    The SnappingNoder moves only nodes that are within tolerance of
-*    other nodes and lines, leaving all the rest undisturbed, for a very
-*    clean result, if it manages to create one.
-*    If a result is found with no exception, return.
-* 3. Perform overlay operation using a PrecisionModel(scale), which
-*    uses a SnapRoundingNoder. Every vertex will be noded to the snapping
-*    grid, resulting in a modified geometry. The SnapRoundingNoder approach
-*    reliably produces results, assuming valid inputs.
-*
-* Running overlayng::OverlayNGRobust at this stage should guarantee
-* that none of the other heuristics are ever needed.
-*/
-        if (g0 == nullptr && g1 == nullptr) {
-            return std::unique_ptr<Geometry>(nullptr);
+    /*
+    * overlayng::OverlayNGRobust does not currently handle
+    * GeometryCollection (collections of mixed dimension)
+    * so we handle that case here.
+    */
+    if ((g0->isMixedDimension() && !g0->isEmpty()) ||
+        (g1->isMixedDimension() && !g1->isEmpty()))
+    {
+        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);
         }
-        else if (g0 == nullptr) {
-            // Use a unary union for the one-parameter case, as the pairwise
-            // union with one parameter is very intolerant to invalid
-            // collections and multi-polygons.
-            ret = OverlayNGRobust::Union(g1);
+    }
+
+    /*
+    * overlayng::OverlayNGRobust carries out the following steps
+    *
+    * 1. Perform overlay operation using PrecisionModel(float).
+    *    If no exception return result.
+    * 2. Perform overlay operation using SnappingNoder(tolerance), starting
+    *    with a very very small tolerance and increasing it for 5 iterations.
+    *    The SnappingNoder moves only nodes that are within tolerance of
+    *    other nodes and lines, leaving all the rest undisturbed, for a very
+    *    clean result, if it manages to create one.
+    *    If a result is found with no exception, return.
+    * 3. Perform overlay operation using a PrecisionModel(scale), which
+    *    uses a SnapRoundingNoder. Every vertex will be noded to the snapping
+    *    grid, resulting in a modified geometry. The SnapRoundingNoder approach
+    *    reliably produces results, assuming valid inputs.
+    *
+    * Running overlayng::OverlayNGRobust at this stage should guarantee
+    * that none of the other heuristics are ever needed.
+    */
+    if (g0 == nullptr && g1 == nullptr) {
+        return std::unique_ptr<Geometry>(nullptr);
+    }
+    else if (g0 == nullptr) {
+        // Use a unary union for the one-parameter case, as the pairwise
+        // union with one parameter is very intolerant to invalid
+        // collections and multi-polygons.
+        ret = OverlayNGRobust::Union(g1);
+    }
+    else if (g1 == nullptr) {
+        // Use a unary union for the one-parameter case, as the pairwise
+        // union with one parameter is very intolerant to invalid
+        // collections and multi-polygons.
+        ret = OverlayNGRobust::Union(g0);
+    }
+    else {
+        ret = OverlayNGRobust::Overlay(g0, g1, opCode);
+    }
+
+    return ret;
+}
+
+/* public */
+void
+StructuredCollection::readCollection(const Geometry* g)
+{
+    if (!factory) factory = g->getFactory();
+    if (g->isCollection()) {
+        for (std::size_t i = 0; i < g->getNumGeometries(); i++) {
+            readCollection(g->getGeometryN(i));
         }
-        else if (g1 == nullptr) {
-            // Use a unary union for the one-parameter case, as the pairwise
-            // union with one parameter is very intolerant to invalid
-            // collections and multi-polygons.
-            ret = OverlayNGRobust::Union(g0);
+    }
+    else {
+        if (g->isEmpty()) return;
+        switch (g->getGeometryTypeId()) {
+            case GEOS_POINT:
+                pts.push_back(g);
+                break;
+            case GEOS_LINESTRING:
+                lines.push_back(g);
+                break;
+            case GEOS_POLYGON:
+                polys.push_back(g);
+                break;
+            default:
+                throw util::IllegalArgumentException("cannot process unexpected collection");
+        }
+    }
+}
+
+/* public static */
+void
+StructuredCollection::toVector(const Geometry* g, std::vector<const Geometry*>& v)
+{
+    if (!g || g->isEmpty()) return;
+    if (g->isCollection()) {
+        for (std::size_t i = 0; i < g->getNumGeometries(); i++) {
+            toVector(g->getGeometryN(i), v);
         }
-        else {
-            ret = OverlayNGRobust::Overlay(g0, g1, opCode);
+    }
+    else {
+        switch (g->getGeometryTypeId()) {
+            case GEOS_POINT:
+            case GEOS_LINESTRING:
+            case GEOS_POLYGON:
+                v.push_back(g);
+                break;
+            default:
+                return;
         }
+    }
+}
+
+
+/* public */
+void
+StructuredCollection::unionByDimension(void)
+{
+    /*
+    * Remove duplication within each dimension, so that there
+    * is only one object covering any particular space within
+    * that dimension.
+    * This makes reasoning about the collection-on-collection
+    * operations a little easier later on.
+    */
+    std::unique_ptr<MultiPoint> pt_col = factory->createMultiPoint(pts);
+    std::unique_ptr<MultiLineString> line_col = factory->createMultiLineString(lines);
+    std::unique_ptr<MultiPolygon> poly_col = factory->createMultiPolygon(polys);
+
+    pt_union = OverlayNGRobust::Union(static_cast<const Geometry*>(pt_col.get()));
+    line_union = OverlayNGRobust::Union(static_cast<const Geometry*>(line_col.get()));
+    poly_union = OverlayNGRobust::Union(static_cast<const Geometry*>(poly_col.get()));
+
+    // io::WKTWriter w;
+    // std::cout << "line_col " << w.write(*line_col) << std::endl;
+    // std::cout << "line_union " << w.write(*line_union) << std::endl;
+
+    if (! pt_union->isPuntal())
+        throw util::IllegalArgumentException("union of points not puntal");
+    if (! line_union->isLineal())
+        throw util::IllegalArgumentException("union of lines not lineal");
+    if (! poly_union->isPolygonal())
+        throw util::IllegalArgumentException("union of polygons not polygonal");
+}
+
+/* public */
+std::unique_ptr<Geometry>
+StructuredCollection::doUnaryUnion() const
+{
+    /*
+    * Before output, we clean up the components to remove spatial
+    * duplication. Points that lines pass through. Lines that are covered
+    * by polygonal areas already. Provides a "neater" output that still
+    * covers all the area it should.
+    */
+    std::unique_ptr<Geometry> pts_less_lines = OverlayNGRobust::Overlay(
+        pt_union.get(),
+        line_union.get(),
+        OverlayNG::DIFFERENCE);
+
+    std::unique_ptr<Geometry> pts_less_polys_lines = OverlayNGRobust::Overlay(
+        pts_less_lines.get(),
+        poly_union.get(),
+        OverlayNG::DIFFERENCE);
+
+    std::unique_ptr<Geometry> lines_less_polys = OverlayNGRobust::Overlay(
+        line_union.get(),
+        poly_union.get(),
+        OverlayNG::DIFFERENCE);
+
+    std::vector<const Geometry*> geoms;
+    toVector(pts_less_polys_lines.get(), geoms);
+    toVector(lines_less_polys.get(), geoms);
+    toVector(poly_union.get(), geoms);
+
+    return factory->buildGeometry(geoms.begin(), geoms.end());
+}
+
+
+/* public */
+std::unique_ptr<Geometry>
+StructuredCollection::doUnion(const StructuredCollection& a) const
+{
+
+    auto poly_union_poly = OverlayNGRobust::Overlay(
+        a.getPolyUnion(),
+        poly_union.get(),
+        OverlayNG::UNION);
+
+    auto line_union_line = OverlayNGRobust::Overlay(
+        a.getLineUnion(),
+        line_union.get(),
+        OverlayNG::UNION);
+
+    auto pt_union_pt = OverlayNGRobust::Overlay(
+        a.getPointUnion(),
+        pt_union.get(),
+        OverlayNG::UNION);
+
+    StructuredCollection c;
+    c.readCollection(poly_union_poly.get());
+    c.readCollection(line_union_line.get());
+    c.readCollection(pt_union_pt.get());
+    c.unionByDimension();
+    return c.doUnaryUnion();
+}
+
+
+std::unique_ptr<Geometry>
+StructuredCollection::doIntersection(const StructuredCollection& a) const
+{
+    std::unique_ptr<Geometry> poly_inter_poly = OverlayNGRobust::Overlay(
+        poly_union.get(),
+        a.getPolyUnion(),
+        OverlayNG::INTERSECTION);
+
+    std::unique_ptr<Geometry> poly_inter_line = OverlayNGRobust::Overlay(
+        poly_union.get(),
+        a.getLineUnion(),
+        OverlayNG::INTERSECTION);
+
+    std::unique_ptr<Geometry> poly_inter_pt = OverlayNGRobust::Overlay(
+        poly_union.get(),
+        a.getPointUnion(),
+        OverlayNG::INTERSECTION);
+
+    std::unique_ptr<Geometry> line_inter_poly = OverlayNGRobust::Overlay(
+        line_union.get(),
+        a.getPolyUnion(),
+        OverlayNG::INTERSECTION);
+
+    std::unique_ptr<Geometry> line_inter_line = OverlayNGRobust::Overlay(
+        line_union.get(),
+        a.getLineUnion(),
+        OverlayNG::INTERSECTION);
 
-        return ret;
+    std::unique_ptr<Geometry> line_inter_pt = OverlayNGRobust::Overlay(
+        line_union.get(),
+        a.getPointUnion(),
+        OverlayNG::INTERSECTION);
+
+    std::unique_ptr<Geometry> pt_inter_pt = OverlayNGRobust::Overlay(
+        pt_union.get(),
+        a.getPointUnion(),
+        OverlayNG::INTERSECTION);
+
+    std::unique_ptr<Geometry> pt_inter_line = OverlayNGRobust::Overlay(
+        pt_union.get(),
+        a.getLineUnion(),
+        OverlayNG::INTERSECTION);
+
+    std::unique_ptr<Geometry> pt_inter_poly = OverlayNGRobust::Overlay(
+        pt_union.get(),
+        a.getPolyUnion(),
+        OverlayNG::INTERSECTION);
+
+    // io::WKTWriter w;
+    // std::cout << "poly_inter_poly " << w.write(*poly_inter_poly) << std::endl;
+    // std::cout << "poly_union.get() " << w.write(poly_union.get()) << std::endl;
+    // std::cout << "a.getLineUnion() " << w.write(a.getLineUnion()) << std::endl;
+    // std::cout << "poly_inter_line " << w.write(*poly_inter_line) << std::endl;
+    // std::cout << "poly_inter_pt " << w.write(*poly_inter_pt) << std::endl;
+    // std::cout << "line_inter_line " << w.write(*line_inter_line) << std::endl;
+    // std::cout << "line_inter_pt " << w.write(*line_inter_pt) << std::endl;
+    // std::cout << "pt_inter_pt " << w.write(*pt_inter_pt) << std::endl;
+
+    StructuredCollection c;
+    c.readCollection(poly_inter_poly.get());
+    c.readCollection(poly_inter_line.get());
+    c.readCollection(poly_inter_pt.get());
+    c.readCollection(line_inter_poly.get());
+    c.readCollection(line_inter_line.get());
+    c.readCollection(line_inter_pt.get());
+    c.readCollection(pt_inter_poly.get());
+    c.readCollection(pt_inter_line.get());
+    c.readCollection(pt_inter_pt.get());
+    c.unionByDimension();
+    return c.doUnaryUnion();
 }
 
+
+std::unique_ptr<Geometry>
+StructuredCollection::doDifference(const StructuredCollection& a) const
+{
+    std::unique_ptr<Geometry> poly_diff_poly = OverlayNGRobust::Overlay(
+        poly_union.get(),
+        a.getPolyUnion(),
+        OverlayNG::DIFFERENCE);
+
+    std::unique_ptr<Geometry> line_diff_poly = OverlayNGRobust::Overlay(
+        line_union.get(),
+        a.getPolyUnion(),
+        OverlayNG::DIFFERENCE);
+
+    std::unique_ptr<Geometry> pt_diff_poly = OverlayNGRobust::Overlay(
+        pt_union.get(),
+        a.getPolyUnion(),
+        OverlayNG::DIFFERENCE);
+
+    std::unique_ptr<Geometry> line_diff_poly_line = OverlayNGRobust::Overlay(
+        line_diff_poly.get(),
+        a.getLineUnion(),
+        OverlayNG::DIFFERENCE);
+
+    std::unique_ptr<Geometry> pt_diff_poly_line = OverlayNGRobust::Overlay(
+        pt_diff_poly.get(),
+        line_diff_poly_line.get(),
+        OverlayNG::DIFFERENCE);
+
+    std::unique_ptr<Geometry> pt_diff_poly_line_pt = OverlayNGRobust::Overlay(
+        pt_diff_poly_line.get(),
+        a.getPointUnion(),
+        OverlayNG::DIFFERENCE);
+
+    StructuredCollection c;
+    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();
+}
+
+std::unique_ptr<Geometry>
+StructuredCollection::doSymDifference(const StructuredCollection& a) const
+{
+    std::unique_ptr<Geometry> poly_symdiff_poly = OverlayNGRobust::Overlay(
+        poly_union.get(),
+        a.getPolyUnion(),
+        OverlayNG::SYMDIFFERENCE);
+
+    std::unique_ptr<Geometry> line_symdiff_line = OverlayNGRobust::Overlay(
+        line_union.get(),
+        a.getLineUnion(),
+        OverlayNG::DIFFERENCE);
+
+    std::unique_ptr<Geometry> pt_symdiff_pt = OverlayNGRobust::Overlay(
+        pt_union.get(),
+        a.getPointUnion(),
+        OverlayNG::DIFFERENCE);
+
+    StructuredCollection c;
+    c.readCollection(poly_symdiff_poly.get());
+    c.readCollection(line_symdiff_line.get());
+    c.readCollection(pt_symdiff_pt.get());
+    c.unionByDimension();
+    return c.doUnaryUnion();
+}
+
+
 } // namespace geos::geom
 } // namespace geos
diff --git a/tests/unit/geom/DimensionTest.cpp b/tests/unit/geom/DimensionTest.cpp
index c37d54ff9..5ac000d16 100644
--- a/tests/unit/geom/DimensionTest.cpp
+++ b/tests/unit/geom/DimensionTest.cpp
@@ -5,6 +5,7 @@
 #include <tut/tut.hpp>
 // geos
 #include <geos/geom/Dimension.h>
+#include <geos/io/WKTReader.h>
 #include <geos/util/IllegalArgumentException.h>
 #include <geos/util.h>
 
@@ -137,5 +138,21 @@ void object::test<5>
     }
 }
 
+template<>
+template<>
+void object::test<6>
+()
+{
+    using geos::geom::Dimension;
+    geos::io::WKTReader reader;
+    auto geom = reader.read("GEOMETRYCOLLECTION(POINT(1 1), LINESTRING(2 2, 3 3))");
+    Dimension::DimensionType d = geom->getDimension();
+    // std::cout << d << std::endl;
+    // getDimension() finds the highest dimension in the collection
+    ensure(d == Dimension::L);
+}
+
+
+
 } // namespace tut
 
diff --git a/tests/unit/geom/HeuristicOverlayTest.cpp b/tests/unit/geom/HeuristicOverlayTest.cpp
new file mode 100644
index 000000000..c687e8a69
--- /dev/null
+++ b/tests/unit/geom/HeuristicOverlayTest.cpp
@@ -0,0 +1,207 @@
+//
+// Test Suite for geos::geom::HeuristicOverlay
+
+#include <tut/tut.hpp>
+
+// geos
+#include <geos/algorithm/hull/ConcaveHull.h>
+#include <geos/constants.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/HeuristicOverlay.h>
+#include <geos/io/WKTReader.h>
+#include <geos/io/WKTWriter.h>
+#include <geos/operation/overlayng/OverlayNG.h>
+#include <utility.h>
+
+// std
+#include <string>
+#include <memory>
+
+namespace tut {
+//
+// Test Group
+//
+
+using geos::operation::overlayng::OverlayNG;
+
+struct test_heuristic_data {
+
+    WKTReader reader_;
+    WKTWriter writer_;
+
+    test_heuristic_data() {}
+
+    void checkOverlay(
+        const std::string& wkt1,
+        const std::string& wkt2,
+        int opCode,
+        const std::string& wkt_expected)
+    {
+        std::unique_ptr<Geometry> g1 = reader_.read(wkt1);
+        std::unique_ptr<Geometry> g2 = reader_.read(wkt2);
+        std::unique_ptr<Geometry> expected = reader_.read(wkt_expected);
+        std::unique_ptr<Geometry> actual = HeuristicOverlay(g1.get(), g2.get(), opCode);
+
+        // std::cout << "expected:" << std::endl << writer_.write(*expected) << std::endl;
+        // std::cout << "actual:" << std::endl << writer_.write(*actual) << std::endl;
+
+        ensure_equals_geometry(expected.get(), actual.get());
+    }
+
+
+};
+
+typedef test_group<test_heuristic_data> group;
+typedef group::object object;
+
+group test_heuristic_data("geos::geom::HeuristicOverlay");
+
+//
+// Test Cases
+//
+
+//
+// These tests exercise the special cast code in HeuristicOverlay
+// for GeometryCollection in which the contents are "mixed dimension",
+// such as points and lines or lines and polygons in the same collection.
+// For those cases the result of the overlay might be a matter of
+// interpretation, depending on the inputs and the opinions of the
+// end user. The implementation just tries to generate a visually
+// defensible, simplified answer.
+//
+
+template<>
+template<>
+void object::test<1> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(1 1, 2 2))",
+        "GEOMETRYCOLLECTION(POINT(10 10), LINESTRING(11 11, 12 12))",
+        OverlayNG::UNION,
+        "GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(1 1, 2 2), POINT(10 10), LINESTRING(11 11, 12 12))"
+        );
+}
+
+template<>
+template<>
+void object::test<2> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(1 1, 2 2))",
+        "POLYGON((-10 -10, -10 10, 10 10, 10 -10, -10 -10))",
+        OverlayNG::UNION,
+        "POLYGON((-10 -10, -10 10, 10 10, 10 -10, -10 -10))"
+        );
+}
+
+template<>
+template<>
+void object::test<3> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POINT(0.5 0.5), LINESTRING(0 0, 2 2), POLYGON((0 0, 1 0, 1 1, 0 1, 0 0)))",
+        "GEOMETRYCOLLECTION(LINESTRING(0.5 0.5, 0.5 4), POINT(2 0))",
+        OverlayNG::UNION,
+        "GEOMETRYCOLLECTION (POINT (2 0), LINESTRING (0.5 1, 0.5 4), LINESTRING (1 1, 2 2), POLYGON ((0 1, 1 1, 1 0, 0 0, 0 1)))"
+        );
+}
+
+template<>
+template<>
+void object::test<4> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)), LINESTRING(20 20, 30 30))",
+        "GEOMETRYCOLLECTION(POLYGON((9 9, 21 9, 21 21, 9 21, 9 9)), POINT(5 5))",
+        OverlayNG::DIFFERENCE,
+        "GEOMETRYCOLLECTION (LINESTRING (21 21, 30 30), POLYGON ((10 0, 0 0, 0 10, 9 10, 9 9, 10 9, 10 0)))"
+        );
+}
+
+template<>
+template<>
+void object::test<5> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)), LINESTRING(20 20, 30 30))",
+        "GEOMETRYCOLLECTION(POLYGON((9 9, 21 9, 21 21, 9 21, 9 9)), POINT(5 5))",
+        OverlayNG::INTERSECTION,
+        "GEOMETRYCOLLECTION (POINT (5 5), LINESTRING(20 20, 21 21), POLYGON ((10 10, 10 9, 9 9, 9 10, 10 10)))"
+        );
+}
+
+template<>
+template<>
+void object::test<6> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)), LINESTRING(20 20, 30 30))",
+        "GEOMETRYCOLLECTION(POLYGON((9 9, 21 9, 21 21, 9 21, 9 9)), POINT(5 5))",
+        OverlayNG::SYMDIFFERENCE,
+        "GEOMETRYCOLLECTION (LINESTRING (21 21, 30 30), POLYGON ((0 0, 0 10, 9 10, 9 9, 10 9, 10 0, 0 0)), POLYGON ((9 10, 9 21, 21 21, 21 9, 10 9, 10 10, 9 10)))"
+        );
+}
+
+
+template<>
+template<>
+void object::test<7> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)))",
+        "GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)))",
+        OverlayNG::UNION,
+        "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))"
+        );
+}
+
+template<>
+template<>
+void object::test<8> ()
+{
+    checkOverlay(
+        "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"
+        );
+}
+
+template<>
+template<>
+void object::test<9> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)))",
+        "GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)))",
+        OverlayNG::INTERSECTION,
+        "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))"
+        );
+}
+
+
+template<>
+template<>
+void object::test<10> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)), POINT EMPTY, MULTIPOINT(4 4, 11 11), LINESTRING(5 5, 6 6))",
+        "GEOMETRYCOLLECTION(POLYGON((2 2, 12 2, 12 12, 2 12, 2 2)), LINESTRING EMPTY, MULTIPOINT(4 4, 11 11), LINESTRING(5 6, 6 5))",
+        OverlayNG::INTERSECTION,
+        "GEOMETRYCOLLECTION (POINT (11 11), POLYGON ((10 10, 10 2, 2 2, 2 10, 10 10)))"
+        );
+}
+
+template<>
+template<>
+void object::test<11> ()
+{
+    checkOverlay(
+        "GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)), POINT EMPTY, MULTIPOINT(4 4, 11 11), LINESTRING(5 5, 6 6))",
+        "GEOMETRYCOLLECTION(POLYGON((2 2, 12 2, 12 12, 2 12, 2 2)), LINESTRING EMPTY, MULTIPOINT(4 4, 11 11), LINESTRING(5 6, 6 5))",
+        OverlayNG::UNION,
+        "POLYGON ((2 12, 12 12, 12 2, 10 2, 10 0, 0 0, 0 10, 2 10, 2 12))"
+        );
+}
+
+} // namespace tut

-----------------------------------------------------------------------

Summary of changes:
 include/geos/geom/Geometry.h             |   3 +
 include/geos/geom/HeuristicOverlay.h     |  68 +++++-
 src/geom/Geometry.cpp                    |  28 +++
 src/geom/HeuristicOverlay.cpp            | 387 ++++++++++++++++++++++++++++---
 tests/unit/geom/DimensionTest.cpp        |  17 ++
 tests/unit/geom/HeuristicOverlayTest.cpp | 207 +++++++++++++++++
 6 files changed, 672 insertions(+), 38 deletions(-)
 create mode 100644 tests/unit/geom/HeuristicOverlayTest.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list