diff --git a/NEWS.md b/NEWS.md index b137b3e8f6..375dfbcaae 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,6 +12,7 @@ - GEOSClipByRect: Fix case with POINT EMPTY (GH-913, Mike Taves) - Remove undefined behaviour in use of null PrecisionModel (GH-931, Jeff Walton) - Fix InteriorPointPoint to handle empty elements (GH-977, Martin Davis) + - PreparedLineStringIntersects: Fix incorrect result with mixed-dim collection with points (GH-774, Dan Baston) ## Changes in 3.11.2 2023-03-16 diff --git a/include/geos/geom/Geometry.h b/include/geos/geom/Geometry.h index 807ecbbe22..f225a802de 100644 --- a/include/geos/geom/Geometry.h +++ b/include/geos/geom/Geometry.h @@ -342,6 +342,11 @@ class GEOS_DLL Geometry { /// Returns the dimension of this Geometry (0=point, 1=line, 2=surface) virtual Dimension::DimensionType getDimension() const = 0; //Abstract + /// Checks whether any component of this geometry has dimension d + virtual bool hasDimension(Dimension::DimensionType d) const { + return getDimension() == d; + } + /// Checks whether this Geometry consists only of components having dimension d. virtual bool isDimensionStrict(Dimension::DimensionType d) const { return d == getDimension(); diff --git a/include/geos/geom/GeometryCollection.h b/include/geos/geom/GeometryCollection.h index b6cf4df8d4..59c146018d 100644 --- a/include/geos/geom/GeometryCollection.h +++ b/include/geos/geom/GeometryCollection.h @@ -109,6 +109,8 @@ class GEOS_DLL GeometryCollection : public Geometry { */ Dimension::DimensionType getDimension() const override; + bool hasDimension(Dimension::DimensionType d) const override; + bool isDimensionStrict(Dimension::DimensionType d) const override; /// Returns coordinate dimension. diff --git a/include/geos/geom/MultiLineString.h b/include/geos/geom/MultiLineString.h index ec04b0745b..81aa0805f9 100644 --- a/include/geos/geom/MultiLineString.h +++ b/include/geos/geom/MultiLineString.h @@ -58,6 +58,10 @@ class GEOS_DLL MultiLineString: public GeometryCollection { /// Returns line dimension (1) Dimension::DimensionType getDimension() const override; + bool hasDimension(Dimension::DimensionType d) const override { + return d == Dimension::L; + } + bool isDimensionStrict(Dimension::DimensionType d) const override { return d == Dimension::L; } diff --git a/include/geos/geom/MultiPoint.h b/include/geos/geom/MultiPoint.h index 8bfaf9386e..c586ca76b0 100644 --- a/include/geos/geom/MultiPoint.h +++ b/include/geos/geom/MultiPoint.h @@ -63,6 +63,10 @@ class GEOS_DLL MultiPoint: public GeometryCollection { return d == Dimension::P; } + bool hasDimension(Dimension::DimensionType d) const override { + return d == Dimension::P; + } + /// Returns Dimension::False (Point has no boundary) int getBoundaryDimension() const override; diff --git a/include/geos/geom/MultiPolygon.h b/include/geos/geom/MultiPolygon.h index 365a4da50a..6cdc0eb15c 100644 --- a/include/geos/geom/MultiPolygon.h +++ b/include/geos/geom/MultiPolygon.h @@ -66,6 +66,10 @@ class GEOS_DLL MultiPolygon: public GeometryCollection { /// Returns surface dimension (2) Dimension::DimensionType getDimension() const override; + bool hasDimension(Dimension::DimensionType d) const override { + return d == Dimension::A; + } + bool isDimensionStrict(Dimension::DimensionType d) const override { return d == Dimension::A; } diff --git a/src/geom/GeometryCollection.cpp b/src/geom/GeometryCollection.cpp index c278eea995..8675cef366 100644 --- a/src/geom/GeometryCollection.cpp +++ b/src/geom/GeometryCollection.cpp @@ -137,6 +137,15 @@ GeometryCollection::isDimensionStrict(Dimension::DimensionType d) const { }); } +bool +GeometryCollection::hasDimension(Dimension::DimensionType d) const { + return std::any_of(geometries.begin(), + geometries.end(), + [&d](const std::unique_ptr& g) { + return g->hasDimension(d); + }); +} + int GeometryCollection::getBoundaryDimension() const { diff --git a/src/geom/prep/PreparedLineStringIntersects.cpp b/src/geom/prep/PreparedLineStringIntersects.cpp index 810bb5f61e..8933cf7d56 100644 --- a/src/geom/prep/PreparedLineStringIntersects.cpp +++ b/src/geom/prep/PreparedLineStringIntersects.cpp @@ -70,11 +70,6 @@ PreparedLineStringIntersects::intersects(const geom::Geometry* g) const return true; } - // For L/L case we are done - if(g->getDimension() == 1) { - return false; - } - // For L/A case, need to check for proper inclusion of the target in the test if(g->getDimension() == 2 && prepLine.isAnyTargetComponentInTest(g)) { @@ -82,7 +77,7 @@ PreparedLineStringIntersects::intersects(const geom::Geometry* g) const } // For L/P case, need to check if any points lie on line(s) - if(g->getDimension() == 0) { + if(g->hasDimension(Dimension::P)) { return isAnyTestPointInTarget(g); } diff --git a/tests/unit/geom/GeometryCollectionTest.cpp b/tests/unit/geom/GeometryCollectionTest.cpp index 934af2d304..41477bb2ff 100644 --- a/tests/unit/geom/GeometryCollectionTest.cpp +++ b/tests/unit/geom/GeometryCollectionTest.cpp @@ -154,4 +154,31 @@ void object::test<7>() ensure_equals(components.size(), 2u); } +// Test of hasDimension() +template<> +template<> +void object::test<8> +() +{ + auto gc = readWKT("GEOMETRYCOLLECTION(POINT (1 1), LINESTRING (1 1, 2 2))"); + + ensure(gc->hasDimension(geos::geom::Dimension::P)); + ensure(gc->hasDimension(geos::geom::Dimension::L)); + ensure(!gc->hasDimension(geos::geom::Dimension::A)); +} + + +// Test of hasDimension() for nested collection +template<> +template<> +void object::test<9> +() +{ + auto gc = readWKT("GEOMETRYCOLLECTION(POINT (1 1), GEOMETRYCOLLECTION(LINESTRING (1 1, 2 2), POLYGON((0 0, 0 1, 1 1, 0 0))))"); + + ensure(gc->hasDimension(geos::geom::Dimension::P)); + ensure(gc->hasDimension(geos::geom::Dimension::L)); + ensure(gc->hasDimension(geos::geom::Dimension::A)); +} + } // namespace tut diff --git a/tests/unit/geom/LineStringTest.cpp b/tests/unit/geom/LineStringTest.cpp index 9921887621..f94c37c039 100644 --- a/tests/unit/geom/LineStringTest.cpp +++ b/tests/unit/geom/LineStringTest.cpp @@ -573,6 +573,18 @@ void object::test<31> } +// Test of hasDimension() +template<> +template<> +void object::test<32> +() +{ + auto line = reader_.read("LINESTRING (0 0, 10 10)"); + + ensure(!line->hasDimension(geos::geom::Dimension::P)); + ensure(line->hasDimension(geos::geom::Dimension::L)); + ensure(!line->hasDimension(geos::geom::Dimension::A)); +} } // namespace tut diff --git a/tests/unit/geom/MultiLineStringTest.cpp b/tests/unit/geom/MultiLineStringTest.cpp index 9e238be253..7e7433a9db 100644 --- a/tests/unit/geom/MultiLineStringTest.cpp +++ b/tests/unit/geom/MultiLineStringTest.cpp @@ -63,5 +63,18 @@ void object::test<3> ensure(!mls_->isDimensionStrict(geos::geom::Dimension::A)); } + +// Test of hasDimension() +template<> +template<> +void object::test<4> +() +{ + ensure(!mls_->hasDimension(geos::geom::Dimension::P)); + ensure(mls_->hasDimension(geos::geom::Dimension::L)); + ensure(!mls_->hasDimension(geos::geom::Dimension::A)); +} + + } // namespace tut diff --git a/tests/unit/geom/MultiPointTest.cpp b/tests/unit/geom/MultiPointTest.cpp index 24fae37f8b..3f5c10373c 100644 --- a/tests/unit/geom/MultiPointTest.cpp +++ b/tests/unit/geom/MultiPointTest.cpp @@ -417,5 +417,16 @@ void object::test<31> ensure(!empty_mp_->isDimensionStrict(geos::geom::Dimension::L)); } +// Test of hasDimension() +template<> +template<> +void object::test<32> +() +{ + ensure(mp_->hasDimension(geos::geom::Dimension::P)); + ensure(!mp_->hasDimension(geos::geom::Dimension::L)); + ensure(!mp_->hasDimension(geos::geom::Dimension::A)); +} + } // namespace tut diff --git a/tests/unit/geom/MultiPolygonTest.cpp b/tests/unit/geom/MultiPolygonTest.cpp index 1ff9f8cd30..efe46919cf 100644 --- a/tests/unit/geom/MultiPolygonTest.cpp +++ b/tests/unit/geom/MultiPolygonTest.cpp @@ -62,4 +62,15 @@ void object::test<3> ensure(!empty_mp_->isDimensionStrict(geos::geom::Dimension::L)); } +// Test of hasDimension() +template<> +template<> +void object::test<4> +() +{ + ensure(!mp_->hasDimension(geos::geom::Dimension::P)); + ensure(!mp_->hasDimension(geos::geom::Dimension::L)); + ensure(mp_->hasDimension(geos::geom::Dimension::A)); +} + } // namespace tut diff --git a/tests/unit/geom/PointTest.cpp b/tests/unit/geom/PointTest.cpp index 75ed26259e..140cfed578 100644 --- a/tests/unit/geom/PointTest.cpp +++ b/tests/unit/geom/PointTest.cpp @@ -17,6 +17,7 @@ #include #include +constexpr int MAX_TESTS = 100; namespace tut { // @@ -58,7 +59,7 @@ struct test_point_data { } }; -typedef test_group group; +typedef test_group group; typedef group::object object; group test_point_group("geos::geom::Point"); @@ -600,5 +601,16 @@ void object::test<46> ensure("point->getCoordinateDimension() == 2", point->getCoordinateDimension() == 2); } +// Test of hasDimension() +template<> +template<> +void object::test<47> +() +{ + ensure(point_->hasDimension(geos::geom::Dimension::P)); + ensure(!point_->hasDimension(geos::geom::Dimension::L)); + ensure(!point_->hasDimension(geos::geom::Dimension::A)); +} + } // namespace tut diff --git a/tests/unit/geom/PolygonTest.cpp b/tests/unit/geom/PolygonTest.cpp index f3b4ae20bf..ab1fdc5014 100644 --- a/tests/unit/geom/PolygonTest.cpp +++ b/tests/unit/geom/PolygonTest.cpp @@ -652,4 +652,15 @@ void object::test<44>() ensure_equals(holes.size(), 1u); } +// Test of hasDimension() +template<> +template<> +void object::test<45> +() +{ + ensure(!poly_->hasDimension(geos::geom::Dimension::P)); + ensure(!poly_->hasDimension(geos::geom::Dimension::L)); + ensure(poly_->hasDimension(geos::geom::Dimension::A)); +} + } // namespace tut diff --git a/tests/xmltester/tests/general/TestPreparedPredicatesWithGeometryCollection.xml b/tests/xmltester/tests/general/TestPreparedPredicatesWithGeometryCollection.xml index 9ea3821932..a1092a0da8 100644 --- a/tests/xmltester/tests/general/TestPreparedPredicatesWithGeometryCollection.xml +++ b/tests/xmltester/tests/general/TestPreparedPredicatesWithGeometryCollection.xml @@ -76,6 +76,17 @@ true + + LineString against GC, with point at endpoint + + + LINESTRING (0 0, 1 1) + + + GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (2 2, 3 3)) + + true +