diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 82be3c7b0..6a6449e7e 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -10,6 +10,8 @@ "${workspaceFolder}/deps/libgdal/gdal/apps", "${workspaceFolder}/deps/libgdal/arch/unix", "${workspaceFolder}/deps/libproj/proj/src", + "${workspaceFolder}/deps/libgeos/include", + "${workspaceFolder}/deps/libgeos/geos/include", "${workspaceFolder}/node_modules/nan" ] }, @@ -48,4 +50,4 @@ } ], "version": 4 -} \ No newline at end of file +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 231f54ed6..234e5d6bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - GDAL 3.10.0 + - GEOS 3.13.0 - Apple ARM support on macOS 15 and later - Node.js 23 support - Implement RFC101 support, see [`ASYNCIO.md`](https://github.com/mmomtchev/node-gdal-async/blob/main/ASYNCIO.md) for more information diff --git a/deps/libgeos.sh b/deps/libgeos.sh index 0a10db110..e2c62d701 100755 --- a/deps/libgeos.sh +++ b/deps/libgeos.sh @@ -5,7 +5,7 @@ set -eu DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "$DIR/libgeos" -GEOS_VERSION=3.12.1 +GEOS_VERSION=3.13.0 dir_geos=./geos # diff --git a/deps/libgeos/geos/.astylerc b/deps/libgeos/geos/.astylerc deleted file mode 100644 index fb05c5047..000000000 --- a/deps/libgeos/geos/.astylerc +++ /dev/null @@ -1,12 +0,0 @@ ---style=stroustrup ---indent=spaces=4 ---max-code-length=120 ---lineend=linux ---unpad-paren ---pad-oper ---align-pointer=type ---align-reference=type ---break-closing-braces ---add-braces ---break-return-type ---break-after-logical diff --git a/deps/libgeos/geos/.mailmap b/deps/libgeos/geos/.mailmap deleted file mode 100644 index b9a4b890a..000000000 --- a/deps/libgeos/geos/.mailmap +++ /dev/null @@ -1,16 +0,0 @@ -Brendan Ward -Casper van der Wel -Daniel Baston -Daniel Baston -Daniel Baston -Howard Butler -Martin Davis -Martin Davis -Mateusz Łoskot -Paul Ramsey -Paul Ramsey -Paul van der Linden -Raul Marin -Regina Obe -Sandro Santilli -Vicky Vergara diff --git a/deps/libgeos/geos/DEVELOPER-NOTES.md b/deps/libgeos/geos/DEVELOPER-NOTES.md index 77a021416..fd0ea8b96 100644 --- a/deps/libgeos/geos/DEVELOPER-NOTES.md +++ b/deps/libgeos/geos/DEVELOPER-NOTES.md @@ -5,11 +5,11 @@ * This is just history, JTS was written first and GEOS was a slavish port. * Being memory managed, JTS is an easier language to prototype in. * Having various visual tooling, JTS is an easier platform to debug spatial algorithms in. - * Being Java, JTS has less language legacy than GEOS, which was originally ported when STL was still not part of the standard, and therefor reflects a mix of styles and eras. - -* Ideally, new algorithms will be implemented in JTS and then ported to GEOS. -* Smaller performance optimizations in GEOS can travel back to JTS. + * Being Java, JTS has less language legacy than GEOS, which was originally ported when STL was not part of the standard, and therefore reflects a mix of styles and eras. +* Ideally, new algorithms are implemented in JTS and then ported to GEOS. + * Sometimes GEOS gains new functionality; ideally this is backported to JTS +* Performance optimizations in GEOS can backport to JTS. * Short circuits, indexes, other non-language optimizations, should be ticketed in JTS when they are added to GEOS. ### Follow JTS as Much as Possible @@ -20,10 +20,17 @@ * Method names * Variable names * Class members + * If class members are shadowed by local variables, use the `m_` prefix convention for the member. - * Yes, we know in your last job you were taught all member variables are prefixed with `m_`, but please don't. +## C/C++ Guidelines +C++ is a large, complex language, with many patterns that have evolved over the years. +The GEOS codebase has also evolved over the years, but parts still exhibit obsolete language +and project patterns. +When porting or adding code, follow the style of the most recently written code (use the commit history to find this). +In general, we follow the [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines). +The following summarizes some of the key patterns used in GEOS. ### Manage Lifecycles @@ -58,6 +65,16 @@ public: * You can pass pointers to the object to other methods using `std::unique_ptr<>.get()`. +### Resource Management +[Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r-resource-management). +* [A raw pointer (a T*) is non-owning](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-ptr) +* [A raw reference (a T&) is non-owning](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-ref) + +### Function calling conventions + + +* Prefer T* over T& when “no argument” is a valid option + ### Avoid Many Small Heap Allocations * Heap allocations (objects created using `new`) are more expensive than stack allocations, but they can show up in batchs in JTS in places where structures are built, like index trees, or graphs. diff --git a/deps/libgeos/geos/NEWS.md b/deps/libgeos/geos/NEWS.md index a944ff1b0..9cfbd2b55 100644 --- a/deps/libgeos/geos/NEWS.md +++ b/deps/libgeos/geos/NEWS.md @@ -1,19 +1,58 @@ +## Changes in 3.13.0 +2024-09-06 -## Changes in 3.12.1 -2023-11-11 +- New things: + - Add Angle::sinCosSnap to avoid small errors, e.g. with buffer operations (GH-978, Mike Taves) + - Add classes for curved geometry types: CircularString, CompoundCurve, CurvedPolygon, MultiCurve, + MultiSurface (GH-1046, Dan Baston/German QGIS users group/Canton of Basel-Landschaft/Canton of Zug) + - Support curved geometry types in WKT/WKB readers/writers (GH-1046, GH-1104, GH-1106, Dan Baston) + - 3D read and write support for GeoJSON (GH-1150, Oreilles) + - Port of RelateNG https://github.com/locationtech/jts/pull/1052 (Martin Davis, Paul Ramsey) + - Rewrite of boolean predicates and relate matrix calculations + - "Prepared" mode now available for all predicates and relate matrix + - CAPI functions GEOSPreparedRelate and GEOSPreparedRelatePattern expose new functionality + - CAPI implementations of GEOSPreparedTouches, etc, that were previously defaulting + into non-prepared implementations now default into the RelateNG prepared implementation + - Prepared implementations for Intersects, Covers, still use the older implementations + - https://lin-ear-th-inking.blogspot.com/2024/05/jts-topological-relationships-next.html + - https://lin-ear-th-inking.blogspot.com/2024/05/relateng-performance.html + +- Breaking Changes + - Zero-length linestrings (eg LINESTRING(1 1, 1 1)) are now treated as equivalent to points (POINT(1 1)) in boolean predicates + - CMake 3.15 or later is requried (GH-1143, Mike Taves) -- Fixes: - - Remove undefined behaviour in use of null PrecisionModel (GH-931, Jeff Walton) - - Explicitly set endianness for some tests so that output matches expected (GH-934, Paul Ramsey) +- Fixes/Improvements: + - WKTReader: Points with all-NaN coordinates are not considered empty anymore (GH-927, Casper van der Wel) + - WKTWriter: Points with all-NaN coordinates are written as such (GH-927, Casper van der Wel) + - ConvexHull: Performance improvement for larger geometries (JTS-985, Martin Davis) + - Distance: Improve performance, especially for point-point distance (GH-1067, Dan Baston) + - Intersection: change to using DoubleDouble computation to improve robustness (GH-937, Martin Davis) + - Fix LargestEmptyCircle to respect polygonal obstacles (GH-939, Martin Davis) + - Fix WKTWriter to emit EMPTY elements in multi-geometries (GH-952, Mike Taves) - Fix IncrementalDelaunayTriangulator to ensure triangulation boundary is convex (GH-953, Martin Davis) - - Improve scale handling for PrecisionModel (GH-956, Martin Davis) - Fix PreparedLineStringDistance for lines within envelope and polygons (GH-959, Martin Davis) + - Improve scale handling for PrecisionModel (GH-956, Martin Davis) - Fix error in CoordinateSequence::add when disallowing repeated points (GH-963, Dan Baston) + - Fix WKTWriter::writeTrimmedNumber for big and small values (GH-973, Mike Taves) - Fix InteriorPointPoint to handle empty elements (GH-977, Martin Davis) - - Skip over testing empty distances for mixed collections. (GH-979, Paul Ramsey) - Fix TopologyPreservingSimplifier endpoint handling to avoid self-intersections (GH-986, Martin Davis) - - Build warnings with Visual Studio (GH-929, Jeff Mckenna, Paul Ramsey) - - Fix CMake on Windows with Visual Studio (GH-945, Aaron Barany) + - Fix spatial predicates for MultiPoint with EMPTY (GH-989, Martin Davis) + - Fix DiscreteHausdorffDistance for LinearRing (GH-1000, Martin Davis) + - Fix IsSimpleOp for MultiPoint with empty element (GH-1005, Martin Davis) + - Fix PreparedPolygonContains for GC with MultiPoint (GH-1008, Martin Davis) + - Fix reading WKT with EMPTY token with white space (GH-1025, Mike Taves) + - Fix buffer Inverted Ring Removal check (GH-1056, Martin Davis) + - Add PointLocation.isOnSegment and remove LineIntersector point methods (GH-1083, Martin Davis) + - Densify: Interpolate Z coordinates (GH-1094) + - GEOSLineSubstring: Fix crash on NaN length fractions (GH-1088, Dan Baston) + - MinimumClearance: Fix crash on NaN inputs (GH-1082, Dan Baston) + - Centroid: Fix crash on polygons with empty holes (GH-1075, Dan Baston) + - GEOSRelatePatternMatch: Fix crash on invalid DE-9IM pattern (GH-1089, Dan Baston) + - CoveragePolygonValidator: add section performance optimization (GH-1099, Martin Davis) + - TopologyPreservingSimplifier: fix to remove ring endpoints safely (GH-1110, Martin Davis) + - TopologyPreservingSimplifier: fix stack overflow on degenerate inputs (GH-1113, Dan Baston) + - DouglasPeuckerSimplifier: fix stack overflow on NaN tolerance (GH-1114, Dan Baston) + - GEOSConcaveHullOfPolygons: Avoid crash on zero-area input (GH-1076, Dan Baston) ## Changes in 3.12.0 2023-06-27 @@ -82,13 +121,13 @@ - MaximumInscribedCircle: Fix infinite loop with non-finite coordinates (GH-843, Dan Baston) - DistanceOp: Fix crash on collection containing empty point (GH-842, Dan Baston) - OffsetCurve: improve behaviour and add Joined mode (JTS-956, Martin Davis) - - GeometryPrecisionReducer: preserve input collection types (GH-846, Paul Ramsey) - OffsetCurve: handle zero-distance offsets (GH-850, Martin Davis) + - OffsetCurve: fix EndCap parameter handling (GH-899, Martin Davis) + - GeometryPrecisionReducer: preserve input collection types (GH-846, Paul Ramsey) - Tri: add exceptions for invalid indexes (GH-853, Martin Davis) - LargestEmptyCircle: enhance boundary to allow any polygonal geometry (GH-859, Martin Davis) - Fix MaximumInscribedCircle and LargestEmptyCircle performance and memory issues (GH-883, Martin Davis) - GEOSHasZ: Fix handling with empty geometries (GH-887, Mike Taves) - - OffsetCurve: fix EndCap parameter handling (GH-899, Martin Davis) - Reduce artifacts in single-sided Buffers: (GH-665 #810 and #712, Sandro Santilli) - GeoJSONReader: Fix 2D empty geometry creation (GH-909, Mike Taves) - GEOSClipByRect: Fix case with POINT EMPTY (GH-913, Mike Taves) @@ -141,6 +180,8 @@ - Fix BufferOp inverted ring check (JTS-878, Martin Davis) - Fix OverlayNG geomunion to avoid lines in result (Martin Davis) +- Changes: + - Offset curves now have the same direction as the input line, for both positive and negative offsets. ## Changes in 3.10.0 2021-10-20 @@ -189,7 +230,7 @@ - Autoconf build system is dropped in favour of CMake See README.md and INSTALL.md for examples of usage - Libtool is no longer used for in favour of CMake - Therefor libgeos.la is no longer built/installed + Therefore libgeos.la is no longer built/installed - #1094, #1090: Drop inlines.cpp to address duplicate symbols on many platforms (Regina Obe) - GH475: Do not return NaN from GEOSProjectNormalized_r (Paul Ramsey) @@ -214,7 +255,7 @@ - Update geos-config tool for consistency and escape paths (https://git.osgeo.org/gitea/geos/geos/pulls/99) changes mostly affect CMake MSVC builds (#1015, Mike Taves) - - Testing on Rasberry Pi 32-bit (berrie) (#1017, Bruce Rindahl, Regina Obe) + - Testing on Raspberry Pi 32-bit (berrie) (#1017, Bruce Rindahl, Regina Obe) - Replace ttmath with JTS DD double-double implementation (Paul Ramsey) - Fix bug in DistanceOp for geometries with empty components (#1026, Paul Ramsey) - Remove undefined behaviour in CAPI (#1021, Greg Troxel) @@ -466,7 +507,7 @@ See 3.7.0 notes - Fix area boundary return from GEOSPointOnSurface (#623) - Speedup GEOSWKBReader_read (#621) - Fix RobustLineIntersector handling of invalid intersection points (#622) - - Reduce likelyhood of invalid output from snap operation (#629, #501) + - Reduce likelihood of invalid output from snap operation (#629, #501) - Reduce memory fragmentation of prepared Polygon/Point intersection op - Fix mingw64 compile (#630) - Fix bug in HotPixel constructor (#635) @@ -497,7 +538,7 @@ See 3.7.0 notes 2012-11-15 -- that's Post-GIS day ! - Bug fixes / improvements - - Add support for testing with phpunit 3.6 (not loosing support for 3.4) + - Add support for testing with phpunit 3.6 (not losing support for 3.4) - Segfault from intersection (#586, #598, #599) ## Changes in 3.3.5 @@ -578,7 +619,7 @@ See 3.7.0 notes GEOSGeom_createEmptyPolygon, GEOSGeom_createEmptyCollection - CAPI: GEOSGeom_extractUniquePoints - CAPI: GEOSGetGeometryN support for single geometries - - CAPI: GEOSPolygonize_full to return all informations computed by + - CAPI: GEOSPolygonize_full to return all information computed by the polygonizer - CAPI: GEOSOrientationIndex - CAPI: GEOSSharedPaths to find shared paths and their orientation @@ -671,7 +712,7 @@ See 3.7.0 notes - New operation/predicate package - Added CGAlgorithms::isPointInRing() version working with Coordinate::ConstVect type (faster!) - - Added getAt(int pos, Coordinate &to) funtion to CoordinateSequence + - Added getAt(int pos, Coordinate &to) function to CoordinateSequence class. - Moved GetNumGeometries() and GetGeometryN() interfaces from GeometryCollection to Geometry class. @@ -743,12 +784,12 @@ See 3.7.0 notes - Namespaces mapping JTS packages - Renamed classes after JTS names (namespaces use made this possible w/out name clashes) - - Splitted headers, for build speedup and possible API reduction. + - Split headers, for build speedup and possible API reduction. - Moved source/bigtest and source/test to tests/bigtest and test/xmltester - Moved C-API in it's own top-level dir capi/ - Reworked automake scripts to produce a static lib for each subdir - and then link all subsystem's libs togheter + and then link all subsystem's libs together - Renamed DefaultCoordinateSequence to CoordinateArraySequence. - Renamed OverlayOp opcodes by prepending the 'op' prefix, and given the enum a name (OpCode) for type-safety. @@ -784,7 +825,7 @@ See 3.7.0 notes use unsigned int rather then int - Changed EdgeEndStar to maintain a single container for EdgeEnds - Changed PlanarGraph::addEdges to take a const vector by ref - rathern then a non-const vector by pointer + rather then a non-const vector by pointer - Changed EdgeList::addAll to take a const vector by ref rather then a non-const vector by pointer - Added apply_rw(CoordinateFilter *) and apply_ro(CoordinateFilter *) diff --git a/deps/libgeos/geos/Version.txt b/deps/libgeos/geos/Version.txt index b256a437f..a486d43ef 100644 --- a/deps/libgeos/geos/Version.txt +++ b/deps/libgeos/geos/Version.txt @@ -1,8 +1,8 @@ # GEOS Versions GEOS_VERSION_MAJOR=3 -GEOS_VERSION_MINOR=12 -GEOS_VERSION_PATCH=1 +GEOS_VERSION_MINOR=13 +GEOS_VERSION_PATCH=0 # OPTIONS: "", "dev", "rc1" etc. GEOS_PATCH_WORD= @@ -15,9 +15,9 @@ GEOS_PATCH_WORD= # - Deleting interfaces / compatibility issues - bump CURRENT, others to zero # ( THIS MUST BE CAREFULLY AVOIDED ) # -CAPI_INTERFACE_CURRENT=19 -CAPI_INTERFACE_REVISION=1 -CAPI_INTERFACE_AGE=18 +CAPI_INTERFACE_CURRENT=20 +CAPI_INTERFACE_REVISION=0 +CAPI_INTERFACE_AGE=19 # JTS Port JTS_PORT=1.18.0 diff --git a/deps/libgeos/geos/capi/geos_c.cpp b/deps/libgeos/geos/capi/geos_c.cpp index 04e6c4fd3..834f3ef51 100644 --- a/deps/libgeos/geos/capi/geos_c.cpp +++ b/deps/libgeos/geos/capi/geos_c.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -32,10 +33,15 @@ #pragma warning(disable : 4099) #endif -// Some extra magic to make type declarations in geos_c.h work - for cross-checking of types in header. +// Some extra magic to make type declarations in geos_c.h work - +// for cross-checking of types in header. +// NOTE: the below defines or struct definition must be kept in exact +// sync between geos_c.cpp and geos_ts_c.cpp to avoid C++ One Definition Rule +// violations. #define GEOSGeometry geos::geom::Geometry #define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry #define GEOSCoordSequence geos::geom::CoordinateSequence +#define GEOSBufferParams geos::operation::buffer::BufferParameters #define GEOSSTRtree geos::index::strtree::TemplateSTRtree #define GEOSWKTReader geos::io::WKTReader #define GEOSWKTWriter geos::io::WKTWriter @@ -43,8 +49,12 @@ #define GEOSWKBWriter geos::io::WKBWriter #define GEOSGeoJSONReader geos::io::GeoJSONReader #define GEOSGeoJSONWriter geos::io::GeoJSONWriter -typedef struct GEOSBufParams_t GEOSBufferParams; -typedef struct GEOSMakeValidParams_t GEOSMakeValidParams; + +// Implementation struct for the GEOSMakeValidParams object +typedef struct { + int method; + int keepCollapsed; +} GEOSMakeValidParams; #include "geos_c.h" @@ -136,7 +146,7 @@ extern "C" { /**************************************************************** ** relate()-related functions - ** return 0 = false, 1 = true, 2 = error occured + ** return 0 = false, 1 = true, 2 = error occurred ** */ char @@ -203,15 +213,15 @@ extern "C" { //------------------------------------------------------------------ char - GEOSRelatePattern(const Geometry* g1, const Geometry* g2, const char* pat) + GEOSRelatePattern(const Geometry* g1, const Geometry* g2, const char* imPattern) { - return GEOSRelatePattern_r(handle, g1, g2, pat); + return GEOSRelatePattern_r(handle, g1, g2, imPattern); } char - GEOSRelatePatternMatch(const char* mat, const char* pat) + GEOSRelatePatternMatch(const char* intMatrix, const char* imPattern) { - return GEOSRelatePatternMatch_r(handle, mat, pat); + return GEOSRelatePatternMatch_r(handle, intMatrix, imPattern); } char* @@ -1194,6 +1204,24 @@ extern "C" { return GEOSGeom_createPolygon_r(handle, shell, holes, nholes); } + Geometry* + GEOSGeom_createCircularString(CoordinateSequence* cs) + { + return GEOSGeom_createCircularString_r(handle, cs); + } + + Geometry* + GEOSGeom_createCompoundCurve(Geometry** curves, unsigned int ngeoms) + { + return GEOSGeom_createCompoundCurve_r(handle, curves, ngeoms); + } + + Geometry* + GEOSGeom_createCurvePolygon(Geometry* shell, Geometry** holes, unsigned int nholes) + { + return GEOSGeom_createCurvePolygon_r(handle, shell, holes, nholes); + } + Geometry* GEOSGeom_clone(const Geometry* g) { @@ -1443,6 +1471,11 @@ extern "C" { GEOSWKBWriter_setIncludeSRID_r(handle, writer, newIncludeSRID); } + int + GEOS_printDouble(double d, unsigned int precision, char *result) { + return WKTWriter::writeTrimmedNumber(d, precision, result); + } + /* GeoJSON Reader */ GeoJSONReader* GEOSGeoJSONReader_create() @@ -1570,6 +1603,18 @@ extern "C" { return GEOSPreparedWithin_r(handle, pg1, g2); } + char * + GEOSPreparedRelate(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedRelate_r(handle, pg1, g2); + } + + char + GEOSPreparedRelatePattern(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2, const char* imPattern) + { + return GEOSPreparedRelatePattern_r(handle, pg1, g2, imPattern); + } + CoordinateSequence* GEOSPreparedNearestPoints(const geos::geom::prep::PreparedGeometry* g1, const Geometry* g2) { @@ -1713,6 +1758,24 @@ extern "C" { return GEOSGeom_createEmptyPolygon_r(handle); } + geos::geom::Geometry* + GEOSGeom_createEmptyCircularString() + { + return GEOSGeom_createEmptyCircularString_r(handle); + } + + geos::geom::Geometry* + GEOSGeom_createEmptyCompoundCurve() + { + return GEOSGeom_createEmptyCompoundCurve_r(handle); + } + + geos::geom::Geometry* + GEOSGeom_createEmptyCurvePolygon() + { + return GEOSGeom_createEmptyCurvePolygon_r(handle); + } + geos::geom::Geometry* GEOSGeom_createRectangle(double xmin, double ymin, double xmax, double ymax) diff --git a/deps/libgeos/geos/capi/geos_ts_c.cpp b/deps/libgeos/geos/capi/geos_ts_c.cpp index 3af41d44d..ef238229a 100644 --- a/deps/libgeos/geos/capi/geos_ts_c.cpp +++ b/deps/libgeos/geos/capi/geos_ts_c.cpp @@ -31,8 +31,12 @@ #include #include #include +#include +#include #include #include +#include +#include #include #include #include @@ -41,12 +45,15 @@ #include #include #include +#include #include #include #include +#include #include #include #include +#include #include #include #include @@ -85,6 +92,9 @@ #include #include #include + +#include + #include #include #include @@ -119,6 +129,9 @@ // Some extra magic to make type declarations in geos_c.h work - // for cross-checking of types in header. +// NOTE: the below defines or struct definition must be kept in exact +// sync between geos_c.cpp and geos_ts_c.cpp to avoid C++ One Definition Rule +// violations. #define GEOSGeometry geos::geom::Geometry #define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry #define GEOSCoordSequence geos::geom::CoordinateSequence @@ -159,17 +172,21 @@ using geos::geom::CoordinateXY; using geos::geom::CoordinateXYM; using geos::geom::CoordinateXYZM; using geos::geom::CoordinateSequence; +using geos::geom::Curve; using geos::geom::Envelope; using geos::geom::Geometry; using geos::geom::GeometryCollection; using geos::geom::GeometryFactory; using geos::geom::LineString; using geos::geom::LinearRing; +using geos::geom::MultiCurve; using geos::geom::MultiLineString; using geos::geom::MultiPolygon; using geos::geom::Point; using geos::geom::Polygon; using geos::geom::PrecisionModel; +using geos::geom::SimpleCurve; +using geos::geom::Surface; using geos::io::WKTReader; using geos::io::WKTWriter; @@ -191,6 +208,7 @@ using geos::operation::geounion::CascadedPolygonUnion; using geos::operation::overlayng::OverlayNG; using geos::operation::overlayng::UnaryUnionNG; using geos::operation::overlayng::OverlayNGRobust; +using geos::operation::relateng::RelateNG; using geos::operation::valid::TopologyValidationError; using geos::precision::GeometryPrecisionReducer; @@ -279,7 +297,7 @@ typedef struct GEOSContextHandle_HS { } void - NOTICE_MESSAGE(const char *fmt, ...) + NOTICE_MESSAGE(GEOS_PRINTF_FORMAT const char *fmt, ...) GEOS_PRINTF_FORMAT_ATTR(2, 3) { if(nullptr == noticeMessageOld && nullptr == noticeMessageNew) { return; @@ -287,7 +305,14 @@ typedef struct GEOSContextHandle_HS { va_list args; va_start(args, fmt); + #ifdef __MINGW32__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wsuggest-attribute=format" + #endif int result = vsnprintf(msgBuffer, sizeof(msgBuffer) - 1, fmt, args); + #ifdef __MINGW32__ + #pragma GCC diagnostic pop + #endif va_end(args); if(result > 0) { @@ -301,7 +326,7 @@ typedef struct GEOSContextHandle_HS { } void - ERROR_MESSAGE(const char *fmt, ...) + ERROR_MESSAGE(GEOS_PRINTF_FORMAT const char *fmt, ...) GEOS_PRINTF_FORMAT_ATTR(2, 3) { if(nullptr == errorMessageOld && nullptr == errorMessageNew) { return; @@ -309,7 +334,14 @@ typedef struct GEOSContextHandle_HS { va_list args; va_start(args, fmt); + #ifdef __MINGW32__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wsuggest-attribute=format" + #endif int result = vsnprintf(msgBuffer, sizeof(msgBuffer) - 1, fmt, args); + #ifdef __MINGW32__ + #pragma GCC diagnostic pop + #endif va_end(args); if(result > 0) { @@ -617,29 +649,37 @@ extern "C" { }); } + char + GEOSEquals_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->equals(g2); + }); + } + //------------------------------------------------------------------- // low-level relate functions //------------------------------------------------------------------ char - GEOSRelatePattern_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, const char* pat) + GEOSRelatePattern_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, const char* imPattern) { return execute(extHandle, 2, [&]() { - std::string s(pat); + std::string s(imPattern); return g1->relate(g2, s); }); } char - GEOSRelatePatternMatch_r(GEOSContextHandle_t extHandle, const char* mat, - const char* pat) + GEOSRelatePatternMatch_r(GEOSContextHandle_t extHandle, const char* intMatrix, + const char* imPattern) { return execute(extHandle, 2, [&]() { using geos::geom::IntersectionMatrix; - std::string m(mat); - std::string p(pat); + std::string m(intMatrix); + std::string p(imPattern); IntersectionMatrix im(m); return im.matches(p); @@ -673,19 +713,19 @@ extern "C" { switch (bnr) { case GEOSRELATE_BNR_MOD2: /* same as OGC */ - im = RelateOp::relate(g1, g2, + im = RelateNG::relate(g1, g2, BoundaryNodeRule::getBoundaryRuleMod2()); break; case GEOSRELATE_BNR_ENDPOINT: - im = RelateOp::relate(g1, g2, + im = RelateNG::relate(g1, g2, BoundaryNodeRule::getBoundaryEndPoint()); break; case GEOSRELATE_BNR_MULTIVALENT_ENDPOINT: - im = RelateOp::relate(g1, g2, + im = RelateNG::relate(g1, g2, BoundaryNodeRule::getBoundaryMultivalentEndPoint()); break; case GEOSRELATE_BNR_MONOVALENT_ENDPOINT: - im = RelateOp::relate(g1, g2, + im = RelateNG::relate(g1, g2, BoundaryNodeRule::getBoundaryMonovalentEndPoint()); break; default: @@ -799,14 +839,6 @@ extern "C" { // general purpose //----------------------------------------------------------------- - char - GEOSEquals_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) - { - return execute(extHandle, 2, [&]() { - return g1->equals(g2); - }); - } - char GEOSEqualsExact_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double tolerance) { @@ -1045,8 +1077,7 @@ extern "C" { GEOSisRing_r(GEOSContextHandle_t extHandle, const Geometry* g) { return execute(extHandle, 2, [&]() { - // both LineString* and LinearRing* can cast to LineString* - const LineString* ls = dynamic_cast(g); + const Curve* ls = dynamic_cast(g); if(ls) { return ls->isRing(); } @@ -1184,7 +1215,7 @@ extern "C" { { return execute(extHandle, [&]() { BufferParameters bp; - //-- use default cap style ROUND + //-- use default cap style ROUND bp.setQuadrantSegments(quadsegs); if(joinStyle > BufferParameters::JOIN_BEVEL) { @@ -1598,13 +1629,13 @@ extern "C" { Geometry* GEOSGeom_transformXY_r(GEOSContextHandle_t handle, const GEOSGeometry* g, GEOSTransformXYCallback callback, void* userdata) { - struct TransformFilter : public geos::geom::CoordinateFilter { + struct TransformFilter final: public geos::geom::CoordinateFilter { TransformFilter(GEOSTransformXYCallback p_callback, void* p_userdata) : m_callback(p_callback), m_userdata(p_userdata) {} - void filter_rw(CoordinateXY* c) const final { + void filter_rw(CoordinateXY* c) const override { if (!m_callback(&(c->x), &(c->y), m_userdata)) { throw std::runtime_error(std::string("Failed to transform coordinates.")); } @@ -1689,6 +1720,8 @@ extern "C" { if (g->getGeometryTypeId() == geos::geom::GeometryTypeId::GEOS_POLYGON) { auto p = geos::detail::down_cast(g); p->orientRings(exteriorCW); + } else if (g->getGeometryTypeId() == geos::geom::GeometryTypeId::GEOS_CURVEPOLYGON) { + throw geos::util::UnsupportedOperationException("Curved geometries not supported."); } } @@ -1707,9 +1740,9 @@ extern "C" { GEOSGetNumInteriorRings_r(GEOSContextHandle_t extHandle, const Geometry* g1) { return execute(extHandle, -1, [&]() { - const Polygon* p = dynamic_cast(g1); + const Surface* p = dynamic_cast(g1); if(!p) { - throw IllegalArgumentException("Argument is not a Polygon"); + throw IllegalArgumentException("Argument is not a Surface"); } return static_cast(p->getNumInteriorRing()); }); @@ -1799,7 +1832,7 @@ extern "C" { GEOSisClosed_r(GEOSContextHandle_t extHandle, const Geometry* g1) { return execute(extHandle, 2, [&]() { - const LineString* ls = dynamic_cast(g1); + const Curve* ls = dynamic_cast(g1); if(ls) { return ls->isClosed(); } @@ -1809,7 +1842,12 @@ extern "C" { return mls->isClosed(); } - throw IllegalArgumentException("Argument is not a LineString or MultiLineString"); + const MultiCurve* mc = dynamic_cast(g1); + if(mc) { + return mc->isClosed(); + } + + throw IllegalArgumentException("Argument is not a Curve, MultiLineString, or MultiCurve"); }); } @@ -1837,9 +1875,9 @@ extern "C" { GEOSGeomGetNumPoints_r(GEOSContextHandle_t extHandle, const Geometry* g1) { return execute(extHandle, -1, [&]() { - const LineString* ls = dynamic_cast(g1); + const SimpleCurve* ls = dynamic_cast(g1); if(!ls) { - throw IllegalArgumentException("Argument is not a LineString"); + throw IllegalArgumentException("Argument is not a SimpleCurve"); } return static_cast(ls->getNumPoints()); }); @@ -1923,9 +1961,9 @@ extern "C" { GEOSGetExteriorRing_r(GEOSContextHandle_t extHandle, const Geometry* g1) { return execute(extHandle, [&]() { - const Polygon* p = dynamic_cast(g1); + const Surface* p = dynamic_cast(g1); if(!p) { - throw IllegalArgumentException("Invalid argument (must be a Polygon)"); + throw IllegalArgumentException("Invalid argument (must be a Surface)"); } return p->getExteriorRing(); }); @@ -1939,9 +1977,9 @@ extern "C" { GEOSGetInteriorRingN_r(GEOSContextHandle_t extHandle, const Geometry* g1, int n) { return execute(extHandle, [&]() { - const Polygon* p = dynamic_cast(g1); + const Surface* p = dynamic_cast(g1); if(!p) { - throw IllegalArgumentException("Invalid argument (must be a Polygon)"); + throw IllegalArgumentException("Invalid argument (must be a Surface)"); } if(n < 0) { throw IllegalArgumentException("Index must be non-negative."); @@ -2048,6 +2086,12 @@ extern "C" { case GEOS_MULTIPOLYGON: g = gf->createMultiPolygon(std::move(vgeoms)); break; + case GEOS_MULTICURVE: + g = gf->createMultiCurve(std::move(vgeoms)); + break; + case GEOS_MULTISURFACE: + g = gf->createMultiSurface(std::move(vgeoms)); + break; default: handle->ERROR_MESSAGE("Unsupported type request for GEOSGeom_createCollection_r"); } @@ -2069,10 +2113,11 @@ extern "C" { GeometryCollection *col = dynamic_cast(collection); if (!col) { handle->ERROR_MESSAGE("Parameter collection of GEOSGeom_releaseCollection_r must not be a collection"); + } else { + *ngeoms = static_cast(col->getNumGeometries()); } // Early exit on empty/null input - *ngeoms = static_cast(col->getNumGeometries()); if (!col || *ngeoms == 0) { return static_cast(nullptr); } @@ -2809,7 +2854,7 @@ extern "C" { GEOSGeom_getCoordSeq_r(GEOSContextHandle_t extHandle, const Geometry* g) { return execute(extHandle, [&]() { - const LineString* ls = dynamic_cast(g); + const SimpleCurve* ls = dynamic_cast(g); if(ls) { return ls->getCoordinatesRO(); } @@ -2960,6 +3005,113 @@ extern "C" { }); } + Geometry* + GEOSGeom_createCircularString_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createCircularString(std::unique_ptr(cs)).release(); + }); + } + + Geometry* + GEOSGeom_createEmptyCircularString_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createCircularString(false, false).release(); + }); + } + + Geometry* + GEOSGeom_createCompoundCurve_r(GEOSContextHandle_t extHandle, Geometry** geoms, unsigned int ngeoms) + { + return execute(extHandle, [&]() -> Geometry* { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + bool invalid_input = false; + std::vector> geom_vec(ngeoms); + for (std::size_t i = 0; i < ngeoms; i++) { + if (SimpleCurve* c = dynamic_cast(geoms[i])) { + geom_vec[i].reset(c); + } else { + delete geoms[i]; + invalid_input = true; + } + } + + if (invalid_input) { + throw IllegalArgumentException("Input is not a SimpleCurve"); + } + + return gf->createCompoundCurve(std::move(geom_vec)).release(); + }); + } + + Geometry* + GEOSGeom_createEmptyCompoundCurve_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createCompoundCurve().release(); + }); + } + + Geometry* + GEOSGeom_createCurvePolygon_r(GEOSContextHandle_t extHandle, Geometry* p_shell, Geometry** p_holes, unsigned int nholes) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + bool good_holes = true, good_shell = true; + + std::unique_ptr shell; + std::vector> holes(nholes); + + if (Curve* c = dynamic_cast(p_shell)) { + shell.reset(c); + } else { + good_shell = false; + delete p_shell; + } + + for (std::size_t i = 0; i < nholes; i++) { + if (Curve* c = dynamic_cast(p_holes[i])) { + holes[i].reset(c); + } else { + good_shell = false; + delete p_holes[i]; + } + } + + if (good_shell && good_holes) { + return gf->createCurvePolygon(std::move(shell), std::move(holes)).release(); + } else if (!good_shell) { + throw IllegalArgumentException("Shell is not a Curve"); + } else { + throw IllegalArgumentException("Hole is not a Curve"); + } + }); + } + + + Geometry* + GEOSGeom_createEmptyCurvePolygon_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + return gf->createCurvePolygon(false, false).release(); + }); + } + Geometry* GEOSGeom_clone_r(GEOSContextHandle_t extHandle, const Geometry* g) { @@ -3596,6 +3748,24 @@ extern "C" { }); } + char * + GEOSPreparedRelate_r(GEOSContextHandle_t extHandle, + const geos::geom::prep::PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, [&]() -> char * { + return gstrdup(pg->relate(g)->toString()); + }); + } + + char + GEOSPreparedRelatePattern_r(GEOSContextHandle_t extHandle, + const geos::geom::prep::PreparedGeometry* pg, const Geometry* g, const char* imPattern) + { + return execute(extHandle, 2, [&]() { + return pg->relate(g, std::string(imPattern)); + }); + } + CoordinateSequence* GEOSPreparedNearestPoints_r(GEOSContextHandle_t extHandle, const geos::geom::prep::PreparedGeometry* pg, const Geometry* g) @@ -3785,7 +3955,7 @@ extern "C" { geos::linearref::LengthIndexedLine lil(g); geos::geom::Coordinate coord = lil.extractPoint(d); const GeometryFactory* gf = handle->geomFactory; - auto point = gf->createPoint(coord); + auto point = coord.isNull() ? gf->createPoint(g->getCoordinateDimension()) : gf->createPoint(coord); point->setSRID(g->getSRID()); return point.release(); }); diff --git a/deps/libgeos/geos/doxygen/check_doxygen_errors.cmake b/deps/libgeos/geos/doxygen/check_doxygen_errors.cmake index 78a0c74c2..0d4bf385e 100644 --- a/deps/libgeos/geos/doxygen/check_doxygen_errors.cmake +++ b/deps/libgeos/geos/doxygen/check_doxygen_errors.cmake @@ -32,7 +32,7 @@ if(EXISTS "${DOXYGEN_LOGFILE}") foreach(LINE ${LOGFILE}) string(REGEX MATCH - ".*(not documented|ignoring unsupported tag).*" IGNORE ${LINE}) + ".*(not documented|ignoring unsupported tag|tag without matching).*" IGNORE ${LINE}) if("${IGNORE}" STREQUAL "") list(APPEND ERRORS ${LINE}) endif() diff --git a/deps/libgeos/geos/include/geos/algorithm/Angle.h b/deps/libgeos/geos/include/geos/algorithm/Angle.h index ddcdedb7b..a2ea53a48 100644 --- a/deps/libgeos/geos/include/geos/algorithm/Angle.h +++ b/deps/libgeos/geos/include/geos/algorithm/Angle.h @@ -216,6 +216,28 @@ class GEOS_DLL Angle { /// (in range [0, Pi] ) /// static double diff(double ang1, double ang2); + + /// \brief + /// Computes both sin and cos of an angle, snapping near-zero values + /// to zero. + /// + /// The angle does not need to be normalized. Unlike std::sin + /// and std::cos, this method will snap near-zero values to zero + /// for (e.g.) sin(pi) and cos(pi/2). + /// + /// @param ang the input angle (in radians) + /// @param rSin the result of sin(ang) + /// @param rCos the result of cos(ang) + /// + static inline void sinCosSnap(const double ang, double& rSin, double& rCos) { + // calculate both; may be optimized with FSINCOS instruction + rSin = std::sin(ang); + rCos = std::cos(ang); + // snap near-zero values + if (std::fabs(rSin) < 5e-16) rSin = 0.0; + if (std::fabs(rCos) < 5e-16) rCos = 0.0; + } + }; diff --git a/deps/libgeos/geos/include/geos/algorithm/Area.h b/deps/libgeos/geos/include/geos/algorithm/Area.h index f53938c1a..c3c799f87 100644 --- a/deps/libgeos/geos/include/geos/algorithm/Area.h +++ b/deps/libgeos/geos/include/geos/algorithm/Area.h @@ -23,12 +23,19 @@ #include namespace geos { + +namespace geom { +class Curve; +} + namespace algorithm { // geos::algorithm class GEOS_DLL Area { public: + static double ofClosedCurve(const geom::Curve& ring); + /** * Computes the area for a ring. * diff --git a/deps/libgeos/geos/include/geos/algorithm/BoundaryNodeRule.h b/deps/libgeos/geos/include/geos/algorithm/BoundaryNodeRule.h index 07c38265f..19cd6e111 100644 --- a/deps/libgeos/geos/include/geos/algorithm/BoundaryNodeRule.h +++ b/deps/libgeos/geos/include/geos/algorithm/BoundaryNodeRule.h @@ -18,6 +18,8 @@ #pragma once +#include + #include // Forward declarations @@ -55,6 +57,8 @@ class GEOS_DLL BoundaryNodeRule { virtual ~BoundaryNodeRule() {} + virtual std::string toString() const = 0; + /** \brief * Tests whether a point that lies in `boundaryCount` * geometry component boundaries is considered to form part of diff --git a/deps/libgeos/geos/include/geos/algorithm/CGAlgorithmsDD.h b/deps/libgeos/geos/include/geos/algorithm/CGAlgorithmsDD.h index cfb62aa80..ef251c3ee 100644 --- a/deps/libgeos/geos/include/geos/algorithm/CGAlgorithmsDD.h +++ b/deps/libgeos/geos/include/geos/algorithm/CGAlgorithmsDD.h @@ -87,7 +87,7 @@ class GEOS_DLL CGAlgorithmsDD { * * Uses an approach due to Jonathan Shewchuk, which is in the public domain. */ - static int orientationIndexFilter( + static inline int orientationIndexFilter( double pax, double pay, double pbx, double pby, double pcx, double pcy) diff --git a/deps/libgeos/geos/include/geos/algorithm/CircularArcs.h b/deps/libgeos/geos/include/geos/algorithm/CircularArcs.h new file mode 100644 index 000000000..54f0a9b7d --- /dev/null +++ b/deps/libgeos/geos/include/geos/algorithm/CircularArcs.h @@ -0,0 +1,37 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace algorithm { + +class GEOS_DLL CircularArcs { +public: + + /// Return the circle center of an arc defined by three points + static geom::CoordinateXY getCenter(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2); + + /// Expand an envelope to include an arc defined by three points + static void expandEnvelope(geom::Envelope& e, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2); +}; + +} +} diff --git a/deps/libgeos/geos/include/geos/algorithm/ConvexHull.h b/deps/libgeos/geos/include/geos/algorithm/ConvexHull.h index a3dfb7a8a..e8eba463e 100644 --- a/deps/libgeos/geos/include/geos/algorithm/ConvexHull.h +++ b/deps/libgeos/geos/include/geos/algorithm/ConvexHull.h @@ -25,12 +25,15 @@ #include #include -// FIXME: avoid using Cordinate:: typedefs to avoid full include +// FIXME: avoid using Coordinate:: typedefs to avoid full include #include #include #include #include #include +#include + +#include "geos/util.h" #ifdef _MSC_VER #pragma warning(push) @@ -60,27 +63,26 @@ namespace algorithm { // geos::algorithm */ class GEOS_DLL ConvexHull { private: + + static constexpr std::size_t TUNING_REDUCE_SIZE = 50; + + const geom::Geometry* inputGeom; const geom::GeometryFactory* geomFactory; geom::Coordinate::ConstVect inputPts; - void - extractCoordinates(const geom::Geometry* geom) - { - util::UniqueCoordinateArrayFilter filter(inputPts); - geom->apply_ro(&filter); - } - /// Create a CoordinateSequence from the Coordinate::ConstVect /// This is needed to construct the geometries. /// Here coordinate copies happen /// The returned object is newly allocated !NO EXCEPTION SAFE! std::unique_ptr toCoordinateSequence(geom::Coordinate::ConstVect& cv); - void computeOctPts(const geom::Coordinate::ConstVect& src, - geom::Coordinate::ConstVect& tgt); + void computeInnerOctolateralPts( + const geom::Coordinate::ConstVect& src, + geom::Coordinate::ConstVect& tgt); - bool computeOctRing(const geom::Coordinate::ConstVect& src, - geom::Coordinate::ConstVect& tgt); + bool computeInnerOctolateralRing( + const geom::Coordinate::ConstVect& src, + geom::Coordinate::ConstVect& tgt); /** * Uses a heuristic to reduce the number of points scanned @@ -132,7 +134,8 @@ class GEOS_DLL ConvexHull { * equal to or greater than q */ int polarCompare(const geom::Coordinate& o, - const geom::Coordinate& p, const geom::Coordinate& q); + const geom::Coordinate& p, + const geom::Coordinate& q); void grahamScan(const geom::Coordinate::ConstVect& c, geom::Coordinate::ConstVect& ps); @@ -150,7 +153,7 @@ class GEOS_DLL ConvexHull { /** * Write in 'cleaned' a version of 'input' with collinear - * vertexes removed. + * vertices removed. */ void cleanRing(const geom::Coordinate::ConstVect& input, geom::Coordinate::ConstVect& cleaned); @@ -159,7 +162,15 @@ class GEOS_DLL ConvexHull { * @return whether the three coordinates are collinear * and c2 lies between c1 and c3 inclusive */ - bool isBetween(const geom::Coordinate& c1, const geom::Coordinate& c2, const geom::Coordinate& c3); + bool isBetween( + const geom::Coordinate& c1, + const geom::Coordinate& c2, + const geom::Coordinate& c3); + + bool extractUnique(geom::Coordinate::ConstVect& pts, std::size_t maxPts); + std::unique_ptr createFewPointsResult(); + + public: @@ -167,9 +178,10 @@ class GEOS_DLL ConvexHull { * Create a new convex hull construction for the input Geometry. */ ConvexHull(const geom::Geometry* newGeometry) - : geomFactory(newGeometry->getFactory()) + : inputGeom(newGeometry) + , geomFactory(newGeometry->getFactory()) { - extractCoordinates(newGeometry); + util::ensureNoCurvedComponents(inputGeom); }; ~ConvexHull() {}; diff --git a/deps/libgeos/geos/include/geos/algorithm/HCoordinate.h b/deps/libgeos/geos/include/geos/algorithm/HCoordinate.h index c7c328bc3..294981c2d 100644 --- a/deps/libgeos/geos/include/geos/algorithm/HCoordinate.h +++ b/deps/libgeos/geos/include/geos/algorithm/HCoordinate.h @@ -72,7 +72,7 @@ class GEOS_DLL HCoordinate { /** \brief * Constructs a homogeneous coordinate which is the intersection - * of the lines define by the homogenous coordinates represented + * of the lines define by the homogeneous coordinates represented * by two [Coordinates](@ref geom::Coordinate). * * @param p1 diff --git a/deps/libgeos/geos/include/geos/algorithm/Intersection.h b/deps/libgeos/geos/include/geos/algorithm/Intersection.h index c4a38fb4f..de413d4a1 100644 --- a/deps/libgeos/geos/include/geos/algorithm/Intersection.h +++ b/deps/libgeos/geos/include/geos/algorithm/Intersection.h @@ -19,19 +19,28 @@ #include namespace geos { -namespace algorithm { // geos::algorithm +namespace algorithm { + + +/** \brief + * Functions to compute intersection points between lines and line segments. + * + * In general it is not possible to compute + * the intersection point of two lines exactly, due to numerical roundoff. + * This is particularly true when the lines are nearly parallel. + * These routines uses numerical conditioning on the input values + * to ensure that the computed value is very close to the correct value. + * + * The Z-ordinate is ignored, and not populated. + */ +class GEOS_DLL Intersection { + +public: /** \brief * Computes the intersection point of two lines. * If the lines are parallel or collinear this case is detected * and null is returned. - *

- * In general it is not possible to accurately compute - * the intersection point of two lines, due to - * numerical roundoff. - * This is particularly true when the input lines are nearly parallel. - * This routine uses numerical conditioning on the input values - * to ensure that the computed value should be very close to the correct value. * * @param p1 an endpoint of line 1 * @param p2 an endpoint of line 1 @@ -42,13 +51,31 @@ namespace algorithm { // geos::algorithm * * @see CGAlgorithmsDD#intersection(Coordinate, Coordinate, Coordinate, Coordinate) */ -class GEOS_DLL Intersection { - -public: - static geom::CoordinateXY intersection(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2, const geom::CoordinateXY& q1, const geom::CoordinateXY& q2); +/** +* Computes the intersection point of a line and a line segment (if any). +* There will be no intersection point if: +* +* * the segment does not intersect the line +* * the line or the segment are degenerate (have zero length) +* +* If the segment is collinear with the line the first segment endpoint is returned. +* +* @param line1 a point on the line +* @param line2 a point on the line +* @param seg1 an endpoint of the line segment +* @param seg2 an endpoint of the line segment +* @return the intersection point, or null if it is not possible to find an intersection +*/ +static geom::CoordinateXY intersectionLineSegment( + const geom::CoordinateXY& line1, + const geom::CoordinateXY& line2, + const geom::CoordinateXY& seg1, + const geom::CoordinateXY& seg2); + + }; diff --git a/deps/libgeos/geos/include/geos/algorithm/LineIntersector.h b/deps/libgeos/geos/include/geos/algorithm/LineIntersector.h index e4a5c010d..ebf676b10 100644 --- a/deps/libgeos/geos/include/geos/algorithm/LineIntersector.h +++ b/deps/libgeos/geos/include/geos/algorithm/LineIntersector.h @@ -134,17 +134,6 @@ class GEOS_DLL LineIntersector { precisionModel = newPM; } - /// Compute the intersection of a point p and the line p1-p2. - /// - /// This function computes the boolean value of the hasIntersection test. - /// The actual value of the intersection (if there is one) - /// is equal to the value of p. - /// - void computeIntersection(const geom::CoordinateXY& p, const geom::CoordinateXY& p1, const geom::CoordinateXY& p2); - - /// Same as above but doesn't compute intersection point. Faster. - static bool hasIntersection(const geom::CoordinateXY& p, const geom::CoordinateXY& p1, const geom::CoordinateXY& p2); - enum intersection_type : uint8_t { /// Indicates that line segments do not intersect NO_INTERSECTION = 0, @@ -600,8 +589,20 @@ class GEOS_DLL LineIntersector { { geom::CoordinateXYZM ptInt(Intersection::intersection(p1, p2, q1, q2)); if (ptInt.isNull()) { - // FIXME need to cast to correct type in mixed-dimensionality case - ptInt = static_cast(nearestEndpoint(p1, p2, q1, q2)); + const geom::CoordinateXY& nearest = nearestEndpoint(p1, p2, q1, q2); +#if __cplusplus >= 201703L + if constexpr (std::is_same::value) { +#else + if (std::is_same::value) { +#endif + ptInt = static_cast(nearest); + } else { + if (&nearest == static_cast(&p1) || &nearest == static_cast(&p2)) { + ptInt = static_cast(nearest); + } else { + ptInt = static_cast(nearest); + } + } } return ptInt; } diff --git a/deps/libgeos/geos/include/geos/algorithm/PointLocation.h b/deps/libgeos/geos/include/geos/algorithm/PointLocation.h index b68010df6..6c3f74b1d 100644 --- a/deps/libgeos/geos/include/geos/algorithm/PointLocation.h +++ b/deps/libgeos/geos/include/geos/algorithm/PointLocation.h @@ -19,11 +19,18 @@ #pragma once #include -#include -#include #include +#include namespace geos { + +namespace geom { +class Coordinate; +class CoordinateXY; +class CoordinateSequence; +class Curve; +} + namespace algorithm { // geos::algorithm /** \brief @@ -36,6 +43,16 @@ namespace algorithm { // geos::algorithm class GEOS_DLL PointLocation { public: + /** \brief + * Tests whether a point lies on a line segment. + * + * @param p the point to test + * @param p0 a point of the line segment + * @param p1 a point of the line segment + * @return true if the point lies on the line segment + */ + static bool isOnSegment(const geom::CoordinateXY& p, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1); + /** \brief * Tests whether a point lies on the line defined by a * [CoordinateSequence](@ref geom::CoordinateSequence). @@ -80,6 +97,7 @@ class GEOS_DLL PointLocation { */ static geom::Location locateInRing(const geom::CoordinateXY& p, const std::vector& ring); static geom::Location locateInRing(const geom::CoordinateXY& p, const geom::CoordinateSequence& ring); + static geom::Location locateInRing(const geom::CoordinateXY& p, const geom::Curve& ring); }; diff --git a/deps/libgeos/geos/include/geos/algorithm/PolygonNodeTopology.h b/deps/libgeos/geos/include/geos/algorithm/PolygonNodeTopology.h index a1987e3f5..ca3de04f2 100644 --- a/deps/libgeos/geos/include/geos/algorithm/PolygonNodeTopology.h +++ b/deps/libgeos/geos/include/geos/algorithm/PolygonNodeTopology.h @@ -76,6 +76,21 @@ class GEOS_DLL PolygonNodeTopology { static bool isInteriorSegment(const CoordinateXY* nodePt, const CoordinateXY* a0, const CoordinateXY* a1, const CoordinateXY* b); + /** + * Compares the angles of two vectors + * relative to the positive X-axis at their origin. + * Angles increase CCW from the X-axis. + * + * @param origin the origin of the vectors + * @param p the endpoint of the vector P + * @param q the endpoint of the vector Q + * @return a negative integer, zero, or a positive integer as this vector P has angle less than, equal to, or greater than vector Q + */ + static int compareAngle( + const CoordinateXY* origin, + const CoordinateXY* p, + const CoordinateXY* q); + private: @@ -95,6 +110,23 @@ class GEOS_DLL PolygonNodeTopology { const CoordinateXY* p, const CoordinateXY* e0, const CoordinateXY* e1); + /** + * Compares whether an edge p is between or outside the edges e0 and e1, + * where the edges all originate at a common origin. + * The "inside" of e0 and e1 is the arc which does not include + * the positive X-axis at the origin. + * If p is collinear with an edge 0 is returned. + * + * @param origin the origin + * @param p the destination point of edge p + * @param e0 the destination point of edge e0 + * @param e1 the destination point of edge e1 + * @return a negative integer, zero or positive integer as the vector P lies outside, collinear with, or inside the vectors E0 and E1 + */ + static int compareBetween(const CoordinateXY* origin, const CoordinateXY* p, + const CoordinateXY* e0, const CoordinateXY* e1); + + /** * Tests if the angle with the origin of a vector P is greater than that of the * vector Q. diff --git a/deps/libgeos/geos/include/geos/algorithm/RayCrossingCounter.h b/deps/libgeos/geos/include/geos/algorithm/RayCrossingCounter.h index 1806dfad3..2120e2358 100644 --- a/deps/libgeos/geos/include/geos/algorithm/RayCrossingCounter.h +++ b/deps/libgeos/geos/include/geos/algorithm/RayCrossingCounter.h @@ -22,6 +22,7 @@ #include #include +#include #include // forward declarations @@ -30,6 +31,8 @@ namespace geom { class Coordinate; class CoordinateXY; class CoordinateSequence; +class CircularArc; +class Curve; } } @@ -66,7 +69,7 @@ class GEOS_DLL RayCrossingCounter { private: const geom::CoordinateXY& point; - int crossingCount; + std::size_t crossingCount; // true if the test point lies on an input segment bool isPointOnSegment; @@ -92,6 +95,9 @@ class GEOS_DLL RayCrossingCounter { static geom::Location locatePointInRing(const geom::CoordinateXY& p, const std::vector& ring); + static geom::Location locatePointInRing(const geom::CoordinateXY& p, + const geom::Curve& ring); + RayCrossingCounter(const geom::CoordinateXY& p_point) : point(p_point), crossingCount(0), @@ -107,6 +113,15 @@ class GEOS_DLL RayCrossingCounter { void countSegment(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2); + void countArc(const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2, + const geom::CoordinateXY& p3); + + /** \brief + * Counts all segments or arcs in the sequence + */ + void processSequence(const geom::CoordinateSequence& seq, bool isLinear); + /** \brief * Reports whether the point lies exactly on one of the supplied segments. * @@ -145,6 +160,13 @@ class GEOS_DLL RayCrossingCounter { */ bool isPointInPolygon() const; + std::size_t getCount() const { return crossingCount; }; + + static bool shouldCountCrossing(const geom::CircularArc& arc, const geom::CoordinateXY& q); + + static std::array + pointsIntersectingHorizontalRay(const geom::CircularArc& arc, const geom::CoordinateXY& origin); + }; } // geos::algorithm diff --git a/deps/libgeos/geos/include/geos/algorithm/construct/IndexedDistanceToPoint.h b/deps/libgeos/geos/include/geos/algorithm/construct/IndexedDistanceToPoint.h new file mode 100644 index 000000000..68d845245 --- /dev/null +++ b/deps/libgeos/geos/include/geos/algorithm/construct/IndexedDistanceToPoint.h @@ -0,0 +1,88 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/construct/IndexedDistanceToPoint.java + * https://github.com/locationtech/jts/commit/d92f783163d9440fcc10c729143787bf7b9fe8f9 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +using geos::geom::Geometry; +using geos::geom::Point; +using geos::operation::distance::IndexedFacetDistance; + +namespace geos { +namespace algorithm { // geos::algorithm +namespace construct { // geos::algorithm::construct + +/** + * \brief Computes the distance between a point and a geometry + * (which may be a collection containing any type of geometry). + * + * Also computes the pair of points containing the input + * point and the nearest point on the geometry. + * + * \author Martin Davis + */ +class GEOS_DLL IndexedDistanceToPoint { + +public: + /** + * \brief Creates an instance to find the distance from points to a geometry. + * + * \param geom the geometry to compute distances to + */ + IndexedDistanceToPoint(const Geometry& geom); + + /** + * \brief Computes the distance from the base geometry to the given point. + * + * \param pt the point to compute the distance to + * + * \return the computed distance + */ + double distance(const Point& pt); + + /** + * \brief Computes the nearest point on the geometry to the point. + * + * The first location lies on the geometry, + * and the second location is the provided point. + * + * \param pt the point to compute the nearest point for + * + * \return the points that are nearest + */ + std::unique_ptr nearestPoints(const Point& pt); + +private: + void init(); + + bool isInArea(const Point& pt); + + //-- members + const Geometry& targetGeometry; + std::unique_ptr facetDistance; + std::unique_ptr ptLocator; + +}; + +}}} \ No newline at end of file diff --git a/deps/libgeos/geos/include/geos/algorithm/construct/IndexedPointInPolygonsLocator.h b/deps/libgeos/geos/include/geos/algorithm/construct/IndexedPointInPolygonsLocator.h new file mode 100644 index 000000000..dd82d55a4 --- /dev/null +++ b/deps/libgeos/geos/include/geos/algorithm/construct/IndexedPointInPolygonsLocator.h @@ -0,0 +1,79 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/construct/IndexedDistanceToPoint.java + * https://github.com/locationtech/jts/commit/d92f783163d9440fcc10c729143787bf7b9fe8f9 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +using geos::geom::Geometry; +using geos::geom::CoordinateXY; +using geos::geom::Location; +using geos::index::strtree::TemplateSTRtree; +using geos::algorithm::locate::IndexedPointInAreaLocator; + +namespace geos { +namespace algorithm { // geos::algorithm +namespace construct { // geos::algorithm::construct + +/** + * \brief Determines the location of a point in the polygonal elements of a geometry. + * + * Uses spatial indexing to provide efficient performance. + * + * \author Martin Davis + */ +class GEOS_DLL IndexedPointInPolygonsLocator { + +public: + /** + * \brief Creates an instance to locate a point in polygonal elements. + * + * \param geom the geometry to locate in + */ + IndexedPointInPolygonsLocator(const Geometry& geom); + + /** \brief + * Determines the [Location](@ref geom::Location) of a point in + * the polygonal elements of a + * [Geometry](@ref geom::Geometry). + * + * @param p the point to test + * @return the location of the point in the geometry + */ + Location locate(const CoordinateXY* /*const*/ p); + +private: + void init(); + + // Declare type as noncopyable + IndexedPointInPolygonsLocator(const IndexedPointInPolygonsLocator& other) = delete; + IndexedPointInPolygonsLocator& operator=(const IndexedPointInPolygonsLocator& rhs) = delete; + + //-- members + const Geometry& geom; + bool isInitialized; + TemplateSTRtree index; + std::vector> locators; +}; + +}}} \ No newline at end of file diff --git a/deps/libgeos/geos/include/geos/algorithm/construct/LargestEmptyCircle.h b/deps/libgeos/geos/include/geos/algorithm/construct/LargestEmptyCircle.h index b84ccb3f4..f19931488 100644 --- a/deps/libgeos/geos/include/geos/algorithm/construct/LargestEmptyCircle.h +++ b/deps/libgeos/geos/include/geos/algorithm/construct/LargestEmptyCircle.h @@ -22,14 +22,13 @@ #include #include #include +#include #include #include #include #include - - namespace geos { namespace geom { class Coordinate; @@ -39,13 +38,9 @@ class GeometryFactory; class LineString; class Point; } -namespace operation { -namespace distance { -class IndexedFacetDistance; -} -} } +using geos::operation::distance::IndexedFacetDistance; namespace geos { namespace algorithm { // geos::algorithm @@ -53,15 +48,23 @@ namespace construct { // geos::algorithm::construct /** * Constructs the Largest Empty Circle for a set of obstacle geometries, -* up to a specified tolerance. The obstacles are point and line geometries. +* up to a specified tolerance. +* The obstacles may be any combination of point, linear and polygonal geometries. * -* The Largest Empty Circle is the largest circle which has its center -* in the convex hull of the obstacles (the boundary), and whose -* interior does not intersect with any obstacle. The circle center -* is the point in the interior of the boundary which has the -* farthest distance from the obstacles (up to tolerance). -* The circle is determined by the center point and a point lying -* on an obstacle indicating the circle radius. +* The Largest Empty Circle (LEC) is the largest circle +* whose interior does not intersect with any obstacle +* and whose center lies within a polygonal boundary. +* The circle center is the point in the interior of the boundary +* which has the farthest distance from the obstacles +* (up to the accuracy of the distance tolerance). +* The circle itself is determined by the center point +* and a point lying on an obstacle determining the circle radius. +* +* The polygonal boundary may be supplied explicitly. +* If it is not specified the convex hull of the obstacles is used as the boundary. +* +* To compute an LEC which lies wholly within +* a polygonal boundary, include the boundary of the polygon(s) as an obstacle. * * The implementation uses a successive-approximation technique * over a grid of square cells covering the obstacles and boundary. @@ -77,19 +80,36 @@ class GEOS_DLL LargestEmptyCircle { /** * Creates a new instance of a Largest Empty Circle construction. + * The obstacles may be any collection of points, lines and polygons. + * The constructed circle center lies within the convex hull of the obstacles. * - * @param p_obstacles a geometry representing the obstacles (points and lines) + * @param p_obstacles a geometry representing the obstacles * @param p_tolerance the distance tolerance for computing the circle center point */ LargestEmptyCircle(const geom::Geometry* p_obstacles, double p_tolerance); + + /** + * Creates a new instance of a Largest Empty Circle construction, + * interior-disjoint to a set of obstacle geometries + * and having its center within a polygonal boundary. + * The obstacles may be any collection of points, lines and polygons. + * If the boundary is null or empty the convex hull + * of the obstacles is used as the boundary. + * + * @param p_obstacles a geometry representing the obstacles + * @param p_boundary a polygonal geometry to contain the LEC center + * @param p_tolerance the distance tolerance for computing the circle center point + */ LargestEmptyCircle(const geom::Geometry* p_obstacles, const geom::Geometry* p_boundary, double p_tolerance); + ~LargestEmptyCircle() = default; /** * Computes the center point of the Largest Empty Circle * within a set of obstacles, up to a given tolerance distance. + * The obstacles may be any collection of points, lines and polygons. * - * @param p_obstacles a geometry representing the obstacles (points and lines) + * @param p_obstacles a geometry representing the obstacles * @param p_tolerance the distance tolerance for computing the center point * @return the center point of the Largest Empty Circle */ @@ -98,8 +118,9 @@ class GEOS_DLL LargestEmptyCircle { /** * Computes a radius line of the Largest Empty Circle * within a set of obstacles, up to a given distance tolerance. + * The obstacles may be any collection of points, lines and polygons. * - * @param p_obstacles a geometry representing the obstacles (points and lines) + * @param p_obstacles a geometry representing the obstacles * @param p_tolerance the distance tolerance for computing the center point * @return a line from the center of the circle to a point on the edge */ @@ -118,10 +139,10 @@ class GEOS_DLL LargestEmptyCircle { std::unique_ptr boundary; const geom::GeometryFactory* factory; geom::Envelope gridEnv; - operation::distance::IndexedFacetDistance obstacleDistance; bool done; - std::unique_ptr ptLocator; - std::unique_ptr boundaryDistance; + std::unique_ptr boundaryPtLocater; + IndexedDistanceToPoint obstacleDistance; + std::unique_ptr boundaryDistance; geom::CoordinateXY centerPt; geom::CoordinateXY radiusPt; diff --git a/deps/libgeos/geos/include/geos/algorithm/distance/DiscreteHausdorffDistance.h b/deps/libgeos/geos/include/geos/algorithm/distance/DiscreteHausdorffDistance.h index 5dc1dd0a6..be136edee 100644 --- a/deps/libgeos/geos/include/geos/algorithm/distance/DiscreteHausdorffDistance.h +++ b/deps/libgeos/geos/include/geos/algorithm/distance/DiscreteHausdorffDistance.h @@ -36,19 +36,11 @@ #endif namespace geos { -namespace algorithm { -//class RayCrossingCounter; -} namespace geom { class Geometry; class Coordinate; //class CoordinateSequence; } -namespace index { -namespace intervalrtree { -//class SortedPackedIntervalRTree; -} -} } namespace geos { diff --git a/deps/libgeos/geos/include/geos/algorithm/hull/ConcaveHull.h b/deps/libgeos/geos/include/geos/algorithm/hull/ConcaveHull.h index 1420e1f0a..6201e21b5 100644 --- a/deps/libgeos/geos/include/geos/algorithm/hull/ConcaveHull.h +++ b/deps/libgeos/geos/include/geos/algorithm/hull/ConcaveHull.h @@ -96,15 +96,7 @@ class GEOS_DLL ConcaveHull { public: - ConcaveHull(const Geometry* geom) - : inputGeometry(geom) - , maxEdgeLengthRatio(-1.0) - , alpha(-1) - , isHolesAllowed(false) - , criteriaType(PARAM_EDGE_LENGTH) - , maxSizeInHull(0.0) - , geomFactory(geom->getFactory()) - {}; + ConcaveHull(const Geometry* geom); /** * Computes the approximate edge length of diff --git a/deps/libgeos/geos/include/geos/algorithm/hull/HullTriangulation.h b/deps/libgeos/geos/include/geos/algorithm/hull/HullTriangulation.h index f6db2eac8..05808c842 100644 --- a/deps/libgeos/geos/include/geos/algorithm/hull/HullTriangulation.h +++ b/deps/libgeos/geos/include/geos/algorithm/hull/HullTriangulation.h @@ -124,7 +124,7 @@ class HullTriangulation : triList(p_triList) {}; - void visit(std::array& triEdges); + void visit(std::array& triEdges) override; }; // HullTriVisitor diff --git a/deps/libgeos/geos/include/geos/algorithm/locate/IndexedPointInAreaLocator.h b/deps/libgeos/geos/include/geos/algorithm/locate/IndexedPointInAreaLocator.h index 44ddf88a1..ac327fe8d 100644 --- a/deps/libgeos/geos/include/geos/algorithm/locate/IndexedPointInAreaLocator.h +++ b/deps/libgeos/geos/include/geos/algorithm/locate/IndexedPointInAreaLocator.h @@ -60,7 +60,7 @@ class GEOS_DLL IndexedPointInAreaLocator : public PointOnGeometryLocator { // p1 follows p0 in a CoordinateSequence, we know that the address // of p1 is 16, 24, or 32 bytes greater than the address of p0. // By packing this offset into the least significant bits of p0, - // we can retrieve both p0 and p1 while only using 8 byytes. + // we can retrieve both p0 and p1 while only using 8 bytes. std::size_t os = static_cast(reinterpret_cast(p1) - reinterpret_cast(p0)) - 2u; m_p0 = reinterpret_cast(p0) | os; diff --git a/deps/libgeos/geos/include/geos/algorithm/locate/SimplePointInAreaLocator.h b/deps/libgeos/geos/include/geos/algorithm/locate/SimplePointInAreaLocator.h index 1149c9cdd..be77ff9d6 100644 --- a/deps/libgeos/geos/include/geos/algorithm/locate/SimplePointInAreaLocator.h +++ b/deps/libgeos/geos/include/geos/algorithm/locate/SimplePointInAreaLocator.h @@ -22,7 +22,7 @@ namespace geos { namespace geom { class Geometry; class Coordinate; -class Polygon; +class Surface; } } @@ -51,7 +51,7 @@ class GEOS_DLL SimplePointInAreaLocator : public PointOnGeometryLocator { const geom::Geometry* geom); /** \brief - * Determines the Location of a point in a [Polygon](@ref geom::Polygon). + * Determines the Location of a point in a [Surface](@ref geom::Surface). * * The return value is one of: * @@ -69,8 +69,8 @@ class GEOS_DLL SimplePointInAreaLocator : public PointOnGeometryLocator { * @param poly the geometry to test * @return the Location of the point in the polygon */ - static geom::Location locatePointInPolygon(const geom::CoordinateXY& p, - const geom::Polygon* poly); + static geom::Location locatePointInSurface(const geom::CoordinateXY& p, + const geom::Surface& poly); /** \brief * Determines whether a point is contained in a [Geometry](@ref geom::Geometry), diff --git a/deps/libgeos/geos/include/geos/constants.h b/deps/libgeos/geos/include/geos/constants.h index 7d58ca3d5..7be692144 100644 --- a/deps/libgeos/geos/include/geos/constants.h +++ b/deps/libgeos/geos/include/geos/constants.h @@ -29,6 +29,7 @@ typedef __int64 int64; #include #include #include +#include // for std::size_t namespace geos { @@ -42,6 +43,7 @@ constexpr double DoubleNegInfinity = (-(std::numeric_limits::infinity)() constexpr double DoubleEpsilon = std::numeric_limits::epsilon(); constexpr std::size_t NO_COORD_INDEX = std::numeric_limits::max(); +constexpr std::size_t INDEX_UNKNOWN = std::numeric_limits::max(); } // namespace geos diff --git a/deps/libgeos/geos/include/geos/coverage/CoveragePolygon.h b/deps/libgeos/geos/include/geos/coverage/CoveragePolygon.h new file mode 100644 index 000000000..e56668c45 --- /dev/null +++ b/deps/libgeos/geos/include/geos/coverage/CoveragePolygon.h @@ -0,0 +1,57 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Envelope; +class Polygon; +} +} + +using geos::geom::CoordinateXY; +using geos::geom::Envelope; +using geos::geom::Polygon; +using geos::algorithm::locate::IndexedPointInAreaLocator; + +namespace geos { // geos +namespace coverage { // geos::coverage + +class GEOS_DLL CoveragePolygon { + + // Members + const Polygon* m_polygon; + Envelope polyEnv; + mutable std::unique_ptr m_locator; + +public: + CoveragePolygon(const Polygon* poly); + + bool intersectsEnv(const Envelope& env) const; + bool intersectsEnv(const CoordinateXY& p) const; + bool contains(const CoordinateXY& p) const; + +private: + IndexedPointInAreaLocator& getLocator() const; + +}; + +} // namespace geos::coverage +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/coverage/CoveragePolygonValidator.h b/deps/libgeos/geos/include/geos/coverage/CoveragePolygonValidator.h index c0150c895..8f635de38 100644 --- a/deps/libgeos/geos/include/geos/coverage/CoveragePolygonValidator.h +++ b/deps/libgeos/geos/include/geos/coverage/CoveragePolygonValidator.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -195,11 +196,10 @@ class GEOS_DLL CoveragePolygonValidator { // Members const Geometry* targetGeom; std::vector adjGeoms; - std::vector m_adjPolygons; + //std::vector m_adjPolygons; const GeometryFactory* geomFactory; double gapWidth = 0.0; - std::map> adjPolygonLocators; - // std::vector> coverageRingStore; + std::vector> m_adjCovPolygons; std::deque coverageRingStore; std::vector> localCoordinateSequences; std::deque coverageRingSegmentStore; @@ -273,6 +273,8 @@ class GEOS_DLL CoveragePolygonValidator { private: + static std::vector> + toCoveragePolygons(const std::vector polygons); static std::vector extractPolygons(std::vector& geoms); /* private */ @@ -336,34 +338,26 @@ class GEOS_DLL CoveragePolygonValidator { * to an adjacent polygon. * * @param targetRings the rings with segments to test - * @param adjPolygons the adjacent polygons + * @param adjCovPolygons the adjacent polygons */ void markInvalidInteriorSegments( std::vector& targetRings, - std::vector& adjPolygons); + std::vector>& adjCovPolygons); + + void markInvalidInteriorSection( + CoverageRing& ring, + std::size_t iStart, + std::size_t iEnd, + std::vector>& adjCovPolygons ); + + void markInvalidInteriorSegment( + CoverageRing& ring, std::size_t i, CoveragePolygon* adjPoly); void checkTargetRings( std::vector& targetRings, std::vector& adjRngs, const Envelope& targetEnv); - /** - * Tests if a coordinate is in the interior of some adjacent polygon. - * Uses the cached Point-In-Polygon indexed locators, for performance. - * - * @param p the coordinate to test - * @param adjPolygons the list of polygons - * @return true if the point is in the interior - */ - bool isInteriorVertex(const Coordinate& p, - std::vector& adjPolygons); - - - bool polygonContainsPoint(std::size_t index, - const Polygon* poly, const Coordinate& pt); - - IndexedPointInAreaLocator* getLocator(std::size_t index, const Polygon* poly); - std::unique_ptr createInvalidLines(std::vector& rings); std::vector createRings(const Geometry* geom); diff --git a/deps/libgeos/geos/include/geos/coverage/CoverageRing.h b/deps/libgeos/geos/include/geos/coverage/CoverageRing.h index 45a348e9f..76bd44aaf 100644 --- a/deps/libgeos/geos/include/geos/coverage/CoverageRing.h +++ b/deps/libgeos/geos/include/geos/coverage/CoverageRing.h @@ -83,6 +83,8 @@ class GEOS_DLL CoverageRing : public noding::BasicSegmentString { CoverageRing(const LinearRing* ring, bool isShell); + geom::Envelope getEnvelope(std::size_t start, std::size_t end); + /** * Tests if all rings have known status (matched or invalid) * for all segments. diff --git a/deps/libgeos/geos/include/geos/coverage/CoverageRingEdges.h b/deps/libgeos/geos/include/geos/coverage/CoverageRingEdges.h index d43279310..ec2809852 100644 --- a/deps/libgeos/geos/include/geos/coverage/CoverageRingEdges.h +++ b/deps/libgeos/geos/include/geos/coverage/CoverageRingEdges.h @@ -17,6 +17,7 @@ #include #include +#include // to materialize CoverageEdge #include #include @@ -61,7 +62,7 @@ class GEOS_DLL CoverageRingEdges { private: // Members - std::vector& m_coverage; + const std::vector& m_coverage; std::map> m_ringEdgesMap; std::vector m_edges; std::vector> m_edgeStore; @@ -72,7 +73,7 @@ class GEOS_DLL CoverageRingEdges { public: - CoverageRingEdges(std::vector& coverage) + CoverageRingEdges(const std::vector& coverage) : m_coverage(coverage) { build(); @@ -140,7 +141,7 @@ class GEOS_DLL CoverageRingEdges { const CoordinateSequence& ring); Coordinate::UnorderedSet findMultiRingNodes( - std::vector& coverage); + const std::vector& coverage); Coordinate::UnorderedSet findBoundaryNodes( LineSegment::UnorderedSet& lineSegments); diff --git a/deps/libgeos/geos/include/geos/coverage/CoverageSimplifier.h b/deps/libgeos/geos/include/geos/coverage/CoverageSimplifier.h index 11e8fa3d1..5e23028d3 100644 --- a/deps/libgeos/geos/include/geos/coverage/CoverageSimplifier.h +++ b/deps/libgeos/geos/include/geos/coverage/CoverageSimplifier.h @@ -83,7 +83,7 @@ class GEOS_DLL CoverageSimplifier { * * @param coverage a set of polygonal geometries forming a coverage */ - CoverageSimplifier(std::vector& coverage); + CoverageSimplifier(const std::vector& coverage); /** * Simplifies the boundaries of a set of polygonal geometries forming a coverage, @@ -142,7 +142,7 @@ class GEOS_DLL CoverageSimplifier { private: // Members - std::vector& m_input; // TODO? make this const + const std::vector& m_input; const GeometryFactory* m_geomFactory; // Methods diff --git a/deps/libgeos/geos/include/geos/coverage/VertexRingCounter.h b/deps/libgeos/geos/include/geos/coverage/VertexRingCounter.h index 3d1151cd4..a8899ae91 100644 --- a/deps/libgeos/geos/include/geos/coverage/VertexRingCounter.h +++ b/deps/libgeos/geos/include/geos/coverage/VertexRingCounter.h @@ -56,7 +56,7 @@ class VertexRingCounter : public CoordinateSequenceFilter void filter_ro(const CoordinateSequence& seq, std::size_t i) override; static void count( - std::vector& geoms, + const std::vector& geoms, std::map& counts); private: diff --git a/deps/libgeos/geos/include/geos/export.h b/deps/libgeos/geos/include/geos/export.h index 997fb4c30..d1baff31d 100644 --- a/deps/libgeos/geos/include/geos/export.h +++ b/deps/libgeos/geos/include/geos/export.h @@ -31,3 +31,21 @@ #if defined(_MSC_VER) # pragma warning(disable: 4251) // identifier : class type needs to have dll-interface to be used by clients of class type2 #endif + + +/********************************************************************** + * Portability macros + **********************************************************************/ + +#ifdef _MSC_VER +#include +#define GEOS_PRINTF_FORMAT _Printf_format_string_ +#define GEOS_PRINTF_FORMAT_ATTR(format_param, dots_param) /**/ +#elif __GNUC__ +#define GEOS_PRINTF_FORMAT /**/ +#define GEOS_PRINTF_FORMAT_ATTR(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#else +#define GEOS_PRINTF_FORMAT /**/ +#define GEOS_PRINTF_FORMAT_ATTR(format_param, dots_param) /**/ +#endif diff --git a/deps/libgeos/geos/include/geos/geom/CircularArc.h b/deps/libgeos/geos/include/geos/geom/CircularArc.h new file mode 100644 index 000000000..283eaf3b0 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/CircularArc.h @@ -0,0 +1,273 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace geos { +namespace geom { + +/// A CircularArc is a reference to three points that define a circular arc. +/// It provides for the lazy calculation of various arc properties such as the center, radius, and orientation +class GEOS_DLL CircularArc { +public: + + using CoordinateXY = geom::CoordinateXY; + + CircularArc(const CoordinateXY& q0, const CoordinateXY& q1, const CoordinateXY& q2) + : p0(q0) + , p1(q1) + , p2(q2) + , m_center_known(false) + , m_radius_known(false) + , m_orientation_known(false) + {} + + const CoordinateXY& p0; + const CoordinateXY& p1; + const CoordinateXY& p2; + + /// Return the orientation of the arc as one of: + /// - algorithm::Orientation::CLOCKWISE, + /// - algorithm::Orientation::COUNTERCLOCKWISE + /// - algorithm::Orientation::COLLINEAR + int orientation() const { + if (!m_orientation_known) { + m_orientation = algorithm::Orientation::index(p0, p1, p2); + m_orientation_known = true; + } + return m_orientation; + } + + /// Return the center point of the circle associated with this arc + const CoordinateXY& getCenter() const { + if (!m_center_known) { + m_center = algorithm::CircularArcs::getCenter(p0, p1, p2); + m_center_known = true; + } + + return m_center; + } + + /// Return the radius of the circle associated with this arc + double getRadius() const { + if (!m_radius_known) { + m_radius = getCenter().distance(p0); + m_radius_known = true; + } + + return m_radius; + } + + /// Return whether this arc forms a complete circle + bool isCircle() const { + return p0.equals(p2); + } + + /// Returns whether this arc forms a straight line (p0, p1, and p2 are collinear) + bool isLinear() const { + return std::isnan(getRadius()); + } + + /// Return the inner angle of the sector associated with this arc + double getAngle() const { + if (isCircle()) { + return 2*MATH_PI; + } + + /// Even Rouault: + /// potential optimization?: using crossproduct(p0 - center, p2 - center) = radius * radius * sin(angle) + /// could yield the result by just doing a single asin(), instead of 2 atan2() + /// actually one should also likely compute dotproduct(p0 - center, p2 - center) = radius * radius * cos(angle), + /// and thus angle = atan2(crossproduct(p0 - center, p2 - center) , dotproduct(p0 - center, p2 - center) ) + auto t0 = theta0(); + auto t2 = theta2(); + + if (orientation() == algorithm::Orientation::COUNTERCLOCKWISE) { + std::swap(t0, t2); + } + + if (t0 < t2) { + t0 += 2*MATH_PI; + } + + auto diff = t0-t2; + + return diff; + } + + /// Return the length of the arc + double getLength() const { + if (isLinear()) { + return p0.distance(p2); + } + + return getAngle()*getRadius(); + } + + /// Return the area enclosed by the arc p0-p1-p2 and the line segment p2-p0 + double getArea() const { + if (isLinear()) { + return 0; + } + + auto R = getRadius(); + auto theta = getAngle(); + return R*R/2*(theta - std::sin(theta)); + } + + /// Return the angle of p0 + double theta0() const { + return std::atan2(p0.y - getCenter().y, p0.x - getCenter().x); + } + + /// Return the angle of p2 + double theta2() const { + return std::atan2(p2.y - getCenter().y, p2.x - getCenter().x); + } + + /// Check to see if a coordinate lies on the arc + /// Only the angle is checked, so it is assumed that the point lies on + /// the circle of which this arc is a part. + bool containsPointOnCircle(const CoordinateXY& q) const { + double theta = std::atan2(q.y - getCenter().y, q.x - getCenter().x); + return containsAngle(theta); + } + + /// Check to see if a coordinate lies on the arc, after testing whether + /// it lies on the circle. + bool containsPoint(const CoordinateXY& q) { + if (q == p0 || q == p1 || q == p2) { + return true; + } + + auto dist = std::abs(q.distance(getCenter()) - getRadius()); + + if (dist > 1e-8) { + return false; + } + + if (triangulate::quadedge::TrianglePredicate::isInCircleNormalized(p0, p1, p2, q) != geom::Location::BOUNDARY) { + return false; + } + + return containsPointOnCircle(q); + } + + /// Check to see if a given angle lies on this arc + bool containsAngle(double theta) const { + auto t0 = theta0(); + auto t2 = theta2(); + + if (theta == t0 || theta == t2) { + return true; + } + + if (orientation() == algorithm::Orientation::COUNTERCLOCKWISE) { + std::swap(t0, t2); + } + + t2 -= t0; + theta -= t0; + + if (t2 < 0) { + t2 += 2*MATH_PI; + } + if (theta < 0) { + theta += 2*MATH_PI; + } + + return theta >= t2; + } + + /// Return true if the arc is pointing positive in the y direction + /// at the location of a specified point. The point is assumed to + /// be on the arc. + bool isUpwardAtPoint(const CoordinateXY& q) const { + auto quad = geom::Quadrant::quadrant(getCenter(), q); + bool isUpward; + + if (orientation() == algorithm::Orientation::CLOCKWISE) { + isUpward = (quad == geom::Quadrant::SW || quad == geom::Quadrant::NW); + } else { + isUpward = (quad == geom::Quadrant::SE || quad == geom::Quadrant::NE); + } + + return isUpward; + } + + class Iterator { + public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = geom::CoordinateXY; + using pointer = const geom::CoordinateXY*; + using reference = const geom::CoordinateXY&; + + Iterator(const CircularArc& arc, int i) : m_arc(arc), m_i(i) {} + + reference operator*() const { + return m_i == 0 ? m_arc.p0 : (m_i == 1 ? m_arc.p1 : m_arc.p2); + } + + Iterator& operator++() { + m_i++; + return *this; + } + + Iterator operator++(int) { + Iterator ret = *this; + m_i++; + return ret; + } + + bool operator==(const Iterator& other) const { + return m_i == other.m_i; + } + + bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + private: + const CircularArc& m_arc; + int m_i; + + }; + + Iterator begin() const { + return Iterator(*this, 0); + } + + Iterator end() const { + return Iterator(*this, 3); + } + +private: + mutable CoordinateXY m_center; + mutable double m_radius; + mutable int m_orientation; + mutable bool m_center_known = false; + mutable bool m_radius_known = false; + mutable bool m_orientation_known = false; +}; + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/CircularString.h b/deps/libgeos/geos/include/geos/geom/CircularString.h new file mode 100644 index 000000000..85dc3014e --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/CircularString.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { + +class GEOS_DLL CircularString : public SimpleCurve { + +public: + using SimpleCurve::SimpleCurve; + + friend class GeometryFactory; + + ~CircularString() override; + + std::unique_ptr clone() const; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + double getLength() const override; + + bool hasCurvedComponents() const override + { + return true; + } + + bool isCurved() const override { + return true; + } + + std::unique_ptr reverse() const + { + return std::unique_ptr(reverseImpl()); + } + +protected: + + /// \brief + /// Constructs a CircularString taking ownership the + /// given CoordinateSequence. + CircularString(std::unique_ptr&& pts, + const GeometryFactory& newFactory); + + CircularString* cloneImpl() const override + { + return new CircularString(*this); + } + + void geometryChangedAction() override + { + envelope = computeEnvelopeInternal(false); + } + + int + getSortIndex() const override + { + return SORTINDEX_LINESTRING; + }; + + CircularString* reverseImpl() const override; + + void validateConstruction(); + +}; + + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/CompoundCurve.h b/deps/libgeos/geos/include/geos/geom/CompoundCurve.h new file mode 100644 index 000000000..1dbdf0f23 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/CompoundCurve.h @@ -0,0 +1,121 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace geom { + +class GEOS_DLL CompoundCurve : public Curve { + friend class GeometryFactory; + +public: + using Curve::apply_ro; + using Curve::apply_rw; + + void apply_ro(CoordinateFilter* filter) const override; + + void apply_ro(CoordinateSequenceFilter& filter) const override; + + void apply_rw(CoordinateSequenceFilter& filter) override; + + void apply_rw(const CoordinateFilter* filter) override; + + int compareToSameClass(const Geometry* geom) const override; + + std::unique_ptr clone() const; + + bool equalsExact(const Geometry* other, double tolerance = 0) + const override; + + bool equalsIdentical(const Geometry* other) const override; + + std::unique_ptr getBoundary() const override; + + const CoordinateXY* getCoordinate() const override; + + uint8_t getCoordinateDimension() const override; + + std::unique_ptr getCoordinates() const override; + + /// Returns the nth section of the CompoundCurve + const SimpleCurve* getCurveN(std::size_t) const override; + + const Envelope* getEnvelopeInternal() const override + { + return &envelope; + } + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + double getLength() const override; + + /// Returns the number of sections in the CompoundCurve + std::size_t getNumCurves() const override; + + std::size_t getNumPoints() const override; + + bool hasCurvedComponents() const override; + + bool hasM() const override; + + bool hasZ() const override; + + bool isClosed() const override; + + bool isEmpty() const override; + + void normalize() override; + + std::unique_ptr reverse() const; + +protected: + /// Construct a CompoundCurve, taking ownership of the + /// provided CoordinateSequence + CompoundCurve(std::vector>&&, + const GeometryFactory&); + + CompoundCurve(const CompoundCurve&); + + CompoundCurve& operator=(const CompoundCurve&); + + CompoundCurve* cloneImpl() const override; + + Envelope computeEnvelopeInternal() const; + + void geometryChangedAction() override + { + envelope = computeEnvelopeInternal(); + } + + int getSortIndex() const override + { + return SORTINDEX_COMPOUNDCURVE; + } + + CompoundCurve* reverseImpl() const override; + +private: + std::vector> curves; + Envelope envelope; +}; + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/Coordinate.h b/deps/libgeos/geos/include/geos/geom/Coordinate.h index 50763083c..0d8bced0f 100644 --- a/deps/libgeos/geos/include/geos/geom/Coordinate.h +++ b/deps/libgeos/geos/include/geos/geom/Coordinate.h @@ -22,6 +22,7 @@ #include // for typedefs #include #include +#include #ifdef _MSC_VER #pragma warning(push) @@ -222,6 +223,7 @@ class GEOS_DLL Coordinate : public CoordinateXY { public: /// A set of const Coordinate pointers typedef std::set ConstSet; + typedef std::set ConstXYSet; /// A vector of const Coordinate pointers typedef std::vector ConstVect; @@ -232,6 +234,9 @@ class GEOS_DLL Coordinate : public CoordinateXY { /// A vector of Coordinate objects (real object, not pointers) typedef std::vector Vect; + /// A map of const Coordinate pointers to integers + typedef std::map ConstIntMap; + /// z-coordinate double z; diff --git a/deps/libgeos/geos/include/geos/geom/CoordinateFilter.h b/deps/libgeos/geos/include/geos/geom/CoordinateFilter.h index 47b30d806..2555a79b2 100644 --- a/deps/libgeos/geos/include/geos/geom/CoordinateFilter.h +++ b/deps/libgeos/geos/include/geos/geom/CoordinateFilter.h @@ -45,6 +45,11 @@ class GEOS_DLL CoordinateFilter { virtual ~CoordinateFilter() {} + virtual bool isDone() const + { + return false; + } + /** \brief * Performs an operation on `coord`. * diff --git a/deps/libgeos/geos/include/geos/geom/CoordinateList.h b/deps/libgeos/geos/include/geos/geom/CoordinateList.h index 9e3b46510..532fd3df3 100644 --- a/deps/libgeos/geos/include/geos/geom/CoordinateList.h +++ b/deps/libgeos/geos/include/geos/geom/CoordinateList.h @@ -46,7 +46,7 @@ namespace geom { // geos::geom /** \brief * A list of {@link Coordinate}s, which may - * be set to prevent repeated coordinates from occuring in the list. + * be set to prevent repeated coordinates from occurring in the list. * * Use this class when fast insertions and removal at arbitrary * position is needed. diff --git a/deps/libgeos/geos/include/geos/geom/CoordinateSequence.h b/deps/libgeos/geos/include/geos/geom/CoordinateSequence.h index 3070ec834..b49b8693f 100644 --- a/deps/libgeos/geos/include/geos/geom/CoordinateSequence.h +++ b/deps/libgeos/geos/include/geos/geom/CoordinateSequence.h @@ -197,20 +197,6 @@ class GEOS_DLL CoordinateSequence { return m_vect.empty(); } - /// Returns true if there is 1 coordinate and if it is null. - bool isNullPoint() const { - if (size() != 1) { - return false; - } - switch(getCoordinateType()) { - case CoordinateType::XY: return getAt(0).isNull(); - case CoordinateType::XYZ: return getAt(0).isNull(); - case CoordinateType::XYZM: return getAt(0).isNull(); - case CoordinateType::XYM: return getAt(0).isNull(); - default: return false; - } - } - /** \brief * Tests whether an a {@link CoordinateSequence} forms a ring, * by checking length and closure. Self-intersection is not checked. @@ -635,10 +621,30 @@ class GEOS_DLL CoordinateSequence { template void apply_rw(const Filter* filter) { switch(getCoordinateType()) { - case CoordinateType::XY: for (auto& c : items()) { filter->filter_rw(&c); } break; - case CoordinateType::XYZ: for (auto& c : items()) { filter->filter_rw(&c); } break; - case CoordinateType::XYM: for (auto& c : items()) { filter->filter_rw(&c); } break; - case CoordinateType::XYZM: for (auto& c : items()) { filter->filter_rw(&c); } break; + case CoordinateType::XY: + for (auto& c : items()) { + if (filter->isDone()) break; + filter->filter_rw(&c); + } + break; + case CoordinateType::XYZ: + for (auto& c : items()) { + if (filter->isDone()) break; + filter->filter_rw(&c); + } + break; + case CoordinateType::XYM: + for (auto& c : items()) { + if (filter->isDone()) break; + filter->filter_rw(&c); + } + break; + case CoordinateType::XYZM: + for (auto& c : items()) { + if (filter->isDone()) break; + filter->filter_rw(&c); + } + break; } m_hasdim = m_hasz = false; // re-check (see http://trac.osgeo.org/geos/ticket/435) } @@ -646,10 +652,30 @@ class GEOS_DLL CoordinateSequence { template void apply_ro(Filter* filter) const { switch(getCoordinateType()) { - case CoordinateType::XY: for (const auto& c : items()) { filter->filter_ro(&c); } break; - case CoordinateType::XYZ: for (const auto& c : items()) { filter->filter_ro(&c); } break; - case CoordinateType::XYM: for (const auto& c : items()) { filter->filter_ro(&c); } break; - case CoordinateType::XYZM: for (const auto& c : items()) { filter->filter_ro(&c); } break; + case CoordinateType::XY: + for (const auto& c : items()) { + if (filter->isDone()) break; + filter->filter_ro(&c); + } + break; + case CoordinateType::XYZ: + for (const auto& c : items()) { + if (filter->isDone()) break; + filter->filter_ro(&c); + } + break; + case CoordinateType::XYM: + for (const auto& c : items()) { + if (filter->isDone()) break; + filter->filter_ro(&c); + } + break; + case CoordinateType::XYZM: + for (const auto& c : items()) { + if (filter->isDone()) break; + filter->filter_ro(&c); + } + break; } } diff --git a/deps/libgeos/geos/include/geos/geom/CoordinateSequences.h b/deps/libgeos/geos/include/geos/geom/CoordinateSequences.h index 72d9d3fca..93595be53 100644 --- a/deps/libgeos/geos/include/geos/geom/CoordinateSequences.h +++ b/deps/libgeos/geos/include/geos/geom/CoordinateSequences.h @@ -37,7 +37,7 @@ class CoordinateSequences { public: /// /// \brief binaryDispatch calls a functor template, explicitly providing the backing types of two CoordinateSequences. The - /// CoordinateSequences are not provided to the functor as arguments but can be provided along with any other arugments + /// CoordinateSequences are not provided to the functor as arguments but can be provided along with any other arguments /// through the `args` argument. template static void binaryDispatch(const CoordinateSequence& seq1, const CoordinateSequence& seq2, F& fun, Args... args) diff --git a/deps/libgeos/geos/include/geos/geom/Curve.h b/deps/libgeos/geos/include/geos/geom/Curve.h new file mode 100644 index 000000000..352dff661 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/Curve.h @@ -0,0 +1,71 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { + +class SimpleCurve; + +class GEOS_DLL Curve : public Geometry { + +public: + using Geometry::apply_ro; + using Geometry::apply_rw; + + void apply_ro(GeometryComponentFilter* filter) const override; + + void apply_ro(GeometryFilter* filter) const override; + + void apply_rw(GeometryComponentFilter* filter) override; + + void apply_rw(GeometryFilter* filter) override; + + /** + * \brief + * Returns Dimension::False for a closed Curve, + * 0 otherwise (Curve boundary is a MultiPoint) + */ + int + getBoundaryDimension() const override + { + return isClosed() ? Dimension::False : 0; + } + + /// Returns line dimension (1) + Dimension::DimensionType getDimension() const override + { + return Dimension::L; // line + } + + /// Returns true if the first and last coordinate in the Curve are the same + virtual bool isClosed() const = 0; + + /// Returns true if the curve is closed and simple + bool isRing() const; + + virtual std::size_t getNumCurves() const = 0; + + virtual const SimpleCurve* getCurveN(std::size_t) const = 0; + +protected: + Curve(const GeometryFactory& factory) : Geometry(&factory) {} + +}; + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/CurvePolygon.h b/deps/libgeos/geos/include/geos/geom/CurvePolygon.h new file mode 100644 index 000000000..1a12b4837 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/CurvePolygon.h @@ -0,0 +1,58 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { + +class GEOS_DLL CurvePolygon : public SurfaceImpl { + friend class GeometryFactory; + +public: + ~CurvePolygon() override = default; + + double getArea() const override; + + std::unique_ptr getBoundary() const override; + + std::unique_ptr getCoordinates() const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + bool hasCurvedComponents() const override; + + void normalize() override; + +protected: + using SurfaceImpl::SurfaceImpl; + + Geometry* cloneImpl() const override; + + int + getSortIndex() const override + { + return SORTINDEX_CURVEPOLYGON; + } + + Geometry* reverseImpl() const override; +}; + + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/Envelope.h b/deps/libgeos/geos/include/geos/geom/Envelope.h index 784e79d61..823a0dc04 100644 --- a/deps/libgeos/geos/include/geos/geom/Envelope.h +++ b/deps/libgeos/geos/include/geos/geom/Envelope.h @@ -48,7 +48,8 @@ class Coordinate; * It is often used to represent the bounding box of a Geometry, * e.g. the minimum and maximum x and y values of the Coordinates. * - * Note that Envelopes support infinite or half-infinite regions, by using + * Envelopes allow null values, which are represented with NaN values for ordinates. + * Envelopes support infinite or half-infinite regions, by using * the values of `Double_POSITIVE_INFINITY` and `Double_NEGATIVE_INFINITY`. * * When Envelope objects are created or initialized, the supplies extent @@ -301,8 +302,8 @@ class GEOS_DLL Envelope { } /** \brief - * Returns the Envelope maximum y-value. `min y > max y` indicates - * that this is a null Envelope. + * Returns the Envelope maximum y-value. + * Null envelopes do not have maximum values. */ double getMaxY() const { @@ -311,8 +312,8 @@ class GEOS_DLL Envelope { }; /** \brief - * Returns the Envelope maximum x-value. `min x > max x` indicates - * that this is a null Envelope. + * Returns the Envelope maximum x-value. + * Null envelopes do not have maximum values. */ double getMaxX() const { @@ -321,8 +322,8 @@ class GEOS_DLL Envelope { }; /** \brief - * Returns the Envelope minimum y-value. `min y > max y` indicates - * that this is a null Envelope. + * Returns the Envelope minimum y-value. + * Null envelopes do not have maximum values. */ double getMinY() const { @@ -331,8 +332,8 @@ class GEOS_DLL Envelope { }; /** \brief - * Returns the Envelope minimum x-value. `min x > max x` indicates - * that this is a null Envelope. + * Returns the Envelope minimum x-value. + * Null envelopes do not have maximum values. */ double getMinX() const { @@ -371,7 +372,7 @@ class GEOS_DLL Envelope { * @param result the envelope representing the intersection of * the envelopes (this will be the null envelope * if either argument is null, or they do not intersect) - * @return false if not intersection is found + * @return false if no intersection is found */ bool intersection(const Envelope& env, Envelope& result) const; diff --git a/deps/libgeos/geos/include/geos/geom/Geometry.h b/deps/libgeos/geos/include/geos/geom/Geometry.h index 38343cee2..c2378f4c1 100644 --- a/deps/libgeos/geos/include/geos/geom/Geometry.h +++ b/deps/libgeos/geos/include/geos/geom/Geometry.h @@ -34,6 +34,7 @@ #include #include // for Dimension::DimensionType #include // for inheritance +#include // to materialize CoordinateSequence #include #include @@ -70,7 +71,7 @@ namespace geos { // geos namespace geom { // geos::geom /// Geometry types -enum GeometryTypeId { +enum GeometryTypeId : int { /// a point GEOS_POINT, /// a linestring @@ -86,7 +87,12 @@ enum GeometryTypeId { /// a collection of polygons GEOS_MULTIPOLYGON, /// a collection of heterogeneus geometries - GEOS_GEOMETRYCOLLECTION + GEOS_GEOMETRYCOLLECTION, + GEOS_CIRCULARSTRING, + GEOS_COMPOUNDCURVE, + GEOS_CURVEPOLYGON, + GEOS_MULTICURVE, + GEOS_MULTISURFACE, }; enum GeometrySortIndex { @@ -97,7 +103,12 @@ enum GeometrySortIndex { SORTINDEX_MULTILINESTRING = 4, SORTINDEX_POLYGON = 5, SORTINDEX_MULTIPOLYGON = 6, - SORTINDEX_GEOMETRYCOLLECTION = 7 + SORTINDEX_GEOMETRYCOLLECTION = 7, + SORTINDEX_CIRCULARSTRING = 8, + SORTINDEX_COMPOUNDCURVE = 9, + SORTINDEX_CURVEPOLYGON = 10, + SORTINDEX_MULTICURVE = 11, + SORTINDEX_MULTISURFACE = 12, }; /** @@ -299,11 +310,19 @@ class GEOS_DLL Geometry { /// Return a string representation of this Geometry type virtual std::string getGeometryType() const = 0; //Abstract + /// Returns whether the Geometry contains curved components + virtual bool hasCurvedComponents() const; + /// Return an integer representation of this Geometry type virtual GeometryTypeId getGeometryTypeId() const = 0; //Abstract - /// Returns the number of geometries in this collection - /// (or 1 if this is not a collection) + /** + * \brief Returns the number of geometries in this collection, + * or 1 if this is not a collection. + * + * Empty collection or multi-geometry types return 0, + * and empty simple geometry types return 1. + */ virtual std::size_t getNumGeometries() const { @@ -593,11 +612,7 @@ class GEOS_DLL Geometry { * @see Geometry#within * @see Geometry#covers */ - bool - coveredBy(const Geometry* g) const - { - return g->covers(this); - } + bool coveredBy(const Geometry* g) const; /// Returns the Well-known Text representation of this Geometry. @@ -914,11 +929,34 @@ class GEOS_DLL Geometry { virtual int compareToSameClass(const Geometry* geom) const = 0; //Abstract - int compare(std::vector a, std::vector b) const; + template + static int compare(const T& a, const T& b) + { + std::size_t i = 0; + std::size_t j = 0; + while(i < a.size() && j < b.size()) { + const auto& aGeom = *a[i]; + const auto& bGeom = *b[j]; + + int comparison = aGeom.compareTo(&bGeom); + if(comparison != 0) { + return comparison; + } + + i++; + j++; + } - int compare(std::vector a, std::vector b) const; + if(i < a.size()) { + return 1; + } - int compare(const std::vector> & a, const std::vector> & b) const; + if(j < b.size()) { + return -1; + } + + return 0; + } bool equal(const CoordinateXY& a, const CoordinateXY& b, double tolerance) const; diff --git a/deps/libgeos/geos/include/geos/geom/GeometryCollection.h b/deps/libgeos/geos/include/geos/geom/GeometryCollection.h index 7e0428c1f..9437f6af3 100644 --- a/deps/libgeos/geos/include/geos/geom/GeometryCollection.h +++ b/deps/libgeos/geos/include/geos/geom/GeometryCollection.h @@ -246,6 +246,9 @@ class GEOS_DLL GeometryCollection : public Geometry { int compareToSameClass(const Geometry* gc) const override; + bool hasCurvedComponents() const override; + + }; } // namespace geos::geom diff --git a/deps/libgeos/geos/include/geos/geom/GeometryFactory.h b/deps/libgeos/geos/include/geos/geom/GeometryFactory.h index ce2be87ae..10c1e867f 100644 --- a/deps/libgeos/geos/include/geos/geom/GeometryFactory.h +++ b/deps/libgeos/geos/include/geos/geom/GeometryFactory.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,8 @@ namespace geos { namespace geom { class Coordinate; +class CircularString; +class CompoundCurve; class CoordinateSequence; class Envelope; class Geometry; @@ -43,9 +46,11 @@ class GeometryCollection; class LineString; class LinearRing; class MultiLineString; +class MultiCurve; class MultiPoint; class MultiPolygon; -class Polygon; +class MultiSurface; +class CurvePolygon; } } @@ -121,7 +126,7 @@ class GEOS_DLL GeometryFactory { static const GeometryFactory* getDefaultInstance(); -//Skipped a lot of list to array convertors +//Skipped a lot of list to array converters static std::unique_ptr createPointFromInternalCoord(const Coordinate* coord, const Geometry* exemplar); @@ -142,6 +147,7 @@ class GEOS_DLL GeometryFactory { /// Creates an EMPTY Point std::unique_ptr createPoint(std::size_t coordinateDimension = 2) const; + std::unique_ptr createPoint(bool hasZ, bool hasM) const; /// Creates a Point using the given Coordinate std::unique_ptr createPoint(const Coordinate& coordinate) const; @@ -159,7 +165,7 @@ class GEOS_DLL GeometryFactory { std::unique_ptr createGeometryCollection() const; /// Construct the EMPTY Geometry - std::unique_ptr createEmptyGeometry() const; + std::unique_ptr createEmptyGeometry(GeometryTypeId type = GEOS_GEOMETRYCOLLECTION, bool hasZ=false, bool hasM=false) const; /// Construct a GeometryCollection taking ownership of given arguments template @@ -187,6 +193,16 @@ class GEOS_DLL GeometryFactory { std::unique_ptr createMultiLineString( std::vector> && fromLines) const; + /// Construct an EMPTY MultiCurve + std::unique_ptr createMultiCurve() const; + + /// Construct a MultiCurve taking ownership of given arguments + std::unique_ptr createMultiCurve( + std::vector> && fromCurves) const; + + std::unique_ptr createMultiCurve( + std::vector> && fromCurves) const; + /// Construct an EMPTY MultiPolygon std::unique_ptr createMultiPolygon() const; @@ -201,8 +217,19 @@ class GEOS_DLL GeometryFactory { std::unique_ptr createMultiPolygon( std::vector> && fromPolys) const; + /// Construct an EMPTY MultiSurface + std::unique_ptr createMultiSurface() const; + + /// Construct a MultiSurface taking ownership of given arguments + std::unique_ptr createMultiSurface( + std::vector> && from) const; + + std::unique_ptr createMultiSurface( + std::vector> && from) const; + /// Construct an EMPTY LinearRing std::unique_ptr createLinearRing(std::size_t coordinateDimension = 2) const; + std::unique_ptr createLinearRing(bool hasZ, bool hasM) const; /// Construct a LinearRing taking ownership of given arguments std::unique_ptr createLinearRing( @@ -244,6 +271,7 @@ class GEOS_DLL GeometryFactory { /// Construct an EMPTY Polygon std::unique_ptr createPolygon(std::size_t coordinateDimension = 2) const; + std::unique_ptr createPolygon(bool hasZ, bool hasM) const; /// Construct a Polygon taking ownership of given arguments std::unique_ptr createPolygon(std::unique_ptr && shell) const; @@ -258,8 +286,19 @@ class GEOS_DLL GeometryFactory { Polygon* createPolygon(const LinearRing& shell, const std::vector& holes) const; + + /// Construct an EMPTY CurvePolygon + std::unique_ptr createCurvePolygon(bool hasZ, bool hasM) const; + + /// Construct a CurvePolygon taking ownership of given arguments + std::unique_ptr createCurvePolygon(std::unique_ptr&& shell) const; + + std::unique_ptr createCurvePolygon(std::unique_ptr&& shell, + std::vector> && holes) const; + /// Construct an EMPTY LineString std::unique_ptr createLineString(std::size_t coordinateDimension = 2) const; + std::unique_ptr createLineString(bool hasZ, bool hasM) const; /// Copy a LineString std::unique_ptr createLineString(const LineString& ls) const; @@ -272,6 +311,26 @@ class GEOS_DLL GeometryFactory { std::unique_ptr createLineString( const CoordinateSequence& coordinates) const; + /// Construct an EMPTY CircularString + std::unique_ptr createCircularString(bool hasZ, bool hasM) const; + + /// Copy a CircularString + std::unique_ptr createCircularString(const CircularString& ls) const; + + /// Construct a CircularString taking ownership of given argument + std::unique_ptr createCircularString( + std::unique_ptr && coordinates) const; + + /// Construct a CircularString with a deep-copy of given argument + std::unique_ptr createCircularString( + const CoordinateSequence& coordinates) const; + + /// Construct an EMPTY CompoundCurve + std::unique_ptr createCompoundCurve() const; + + /// Construct a CompoundCurve taking ownership of given argument + std::unique_ptr createCompoundCurve(std::vector>&&) const; + /** * Creates an empty atomic geometry of the given dimension. * If passed a dimension of -1 will create an empty {@link GeometryCollection}. @@ -330,7 +389,7 @@ class GEOS_DLL GeometryFactory { /// See buildGeometry(std::vector&) for semantics // - /// Will clone the geometries accessible trough the iterator. + /// Will clone the geometries accessible through the iterator. /// /// @tparam T an iterator yielding something which casts to const Geometry* /// @param from start iterator diff --git a/deps/libgeos/geos/include/geos/geom/GeometryTypeName.h b/deps/libgeos/geos/include/geos/geom/GeometryTypeName.h new file mode 100644 index 000000000..9807e5a24 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/GeometryTypeName.h @@ -0,0 +1,110 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +namespace geos { +namespace geom { + +class Curve; +class CurvePolygon; +class GeometryCollection; +class LineString; +class LinearRing; +class MultiCurve; +class MultiLineString; +class MultiPoint; +class MultiPolygon; +class MultiSurface; +class Point; +class Polygon; +class SimpleCurve; +class Surface; + +// These structures allow templates to have compile-time access to a type's human-readable name. +template +struct GeometryTypeName {}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "Curve"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "CurvePolygon"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "GeometryCollection"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "LineString"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "LinearRing"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiCurve"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiLineString"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiPoint"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiPolygon"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiSurface"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "Point"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "Polygon"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "SimpleCurve"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "Surface"; +}; + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/IntersectionMatrix.h b/deps/libgeos/geos/include/geos/geom/IntersectionMatrix.h index 76823abbd..c7ad506c5 100644 --- a/deps/libgeos/geos/include/geos/geom/IntersectionMatrix.h +++ b/deps/libgeos/geos/include/geos/geom/IntersectionMatrix.h @@ -61,7 +61,7 @@ class GEOS_DLL IntersectionMatrix { IntersectionMatrix(); /** \brief - * Overriden constructor. + * Overridden constructor. * * Creates an IntersectionMatrix with the given dimension symbols. * @@ -226,7 +226,7 @@ class GEOS_DLL IntersectionMatrix { * * @return the dimension value at the given matrix position. */ - int get(geom::Location row, geom::Location column) const { + int get(Location row, Location column) const { return matrix[static_cast(row)][static_cast(column)]; } @@ -363,6 +363,7 @@ class GEOS_DLL IntersectionMatrix { */ std::string toString() const; + private: static const int firstDim; // = 3; diff --git a/deps/libgeos/geos/include/geos/geom/LineSegment.h b/deps/libgeos/geos/include/geos/geom/LineSegment.h index 714c123d9..aea0fefd8 100644 --- a/deps/libgeos/geos/include/geos/geom/LineSegment.h +++ b/deps/libgeos/geos/include/geos/geom/LineSegment.h @@ -246,17 +246,23 @@ class GEOS_DLL LineSegment { return std::atan2(p1.y - p0.y, p1.x - p0.x); }; - /// Computes the midpoint of the segment - // - /// @param ret will be set to the midpoint of the segment - /// - void midPoint(Coordinate& ret) const + /** \brief + * Computes the midpoint of the segment + * + * @return the midpoint of the segment + */ + CoordinateXY midPoint() const { - ret = Coordinate( - (p0.x + p1.x) / 2, - (p0.y + p1.y) / 2); + return midPoint(p0, p1); }; + static CoordinateXY midPoint(const CoordinateXY& pt0, const CoordinateXY& pt1) + { + return CoordinateXY( + (pt0.x + pt1.x) / 2, + (pt0.y + pt1.y) / 2); + } + /// Computes the distance between this line segment and another one. double distance(const LineSegment& ls) const { @@ -316,7 +322,8 @@ class GEOS_DLL LineSegment { { ret = Coordinate( p0.x + segmentLengthFraction * (p1.x - p0.x), - p0.y + segmentLengthFraction * (p1.y - p0.y)); + p0.y + segmentLengthFraction * (p1.y - p0.y), + p0.z + segmentLengthFraction * (p1.z - p0.z)); }; /** \brief diff --git a/deps/libgeos/geos/include/geos/geom/LineString.h b/deps/libgeos/geos/include/geos/geom/LineString.h index 7e70ca5e5..478970874 100644 --- a/deps/libgeos/geos/include/geos/geom/LineString.h +++ b/deps/libgeos/geos/include/geos/geom/LineString.h @@ -25,6 +25,7 @@ #include // for proper use of unique_ptr<> #include // for proper use of unique_ptr<> #include // for Dimension::DimensionType +#include #include #include @@ -62,7 +63,7 @@ namespace geom { // geos::geom * If these conditions are not met, the constructors throw * an {@link util::IllegalArgumentException}. */ -class GEOS_DLL LineString: public Geometry { +class GEOS_DLL LineString: public SimpleCurve { public: @@ -85,111 +86,16 @@ class GEOS_DLL LineString: public Geometry { return std::unique_ptr(cloneImpl()); } - std::unique_ptr getCoordinates() const override; - - /// Returns a read-only pointer to internal CoordinateSequence - const CoordinateSequence* getCoordinatesRO() const; - - virtual const Coordinate& getCoordinateN(std::size_t n) const; - - /** - * \brief - * Take ownership of the CoordinateSequence managed by this geometry. - * After releasing the coordinates, the geometry should be considered - * in a moved-from state and should not be accessed. - * @return this Geometry's CoordinateSequence. - */ - std::unique_ptr releaseCoordinates(); - - /// Returns line dimension (1) - Dimension::DimensionType getDimension() const override; - - /** - * \brief - * Returns Dimension::False for a closed LineString, - * 0 otherwise (LineString boundary is a MultiPoint) - */ - int getBoundaryDimension() const override; - - /// Returns coordinate dimension. - uint8_t getCoordinateDimension() const override; - - bool hasM() const override; - - bool hasZ() const override; - - /** - * \brief - * Returns a MultiPoint. - * Empty for closed LineString, a Point for each vertex otherwise. - */ - std::unique_ptr getBoundary() const override; - - bool isEmpty() const override; - - std::size_t getNumPoints() const override; - - virtual std::unique_ptr getPointN(std::size_t n) const; - - /// \brief - /// Return the start point of the LineString - /// or NULL if this is an EMPTY LineString. - /// - virtual std::unique_ptr getStartPoint() const; - - /// \brief - /// Return the end point of the LineString - /// or NULL if this is an EMPTY LineString. - /// - virtual std::unique_ptr getEndPoint() const; - - virtual bool isClosed() const; - - virtual bool isRing() const; - std::string getGeometryType() const override; GeometryTypeId getGeometryTypeId() const override; - virtual bool isCoordinate(Coordinate& pt) const; - - bool equalsExact(const Geometry* other, double tolerance = 0) - const override; - - bool equalsIdentical(const Geometry* other) const override; - - void apply_rw(const CoordinateFilter* filter) override; - - void apply_ro(CoordinateFilter* filter) const override; - - void apply_rw(GeometryFilter* filter) override; - - void apply_ro(GeometryFilter* filter) const override; - - void apply_rw(GeometryComponentFilter* filter) override; - - void apply_ro(GeometryComponentFilter* filter) const override; - - void apply_rw(CoordinateSequenceFilter& filter) override; - - void apply_ro(CoordinateSequenceFilter& filter) const override; - - /** \brief - * Normalizes a LineString. - * - * A normalized linestring - * has the first point which is not equal to its reflected point - * less than the reflected point. - */ - void normalize() override; - - //was protected - int compareToSameClass(const Geometry* ls) const override; - - const CoordinateXY* getCoordinate() const override; - double getLength() const override; + bool isCurved() const override { + return false; + } + /** * Creates a LineString whose coordinates are in the reverse * order of this object's @@ -198,10 +104,6 @@ class GEOS_DLL LineString: public Geometry { */ std::unique_ptr reverse() const { return std::unique_ptr(reverseImpl()); } - const Envelope* getEnvelopeInternal() const override { - return &envelope; - } - protected: LineString(const LineString& ls); @@ -216,28 +118,20 @@ class GEOS_DLL LineString: public Geometry { LineString* reverseImpl() const override; - Envelope computeEnvelopeInternal() const; - - CoordinateSequence::Ptr points; - - mutable Envelope envelope; - int getSortIndex() const override { return SORTINDEX_LINESTRING; }; - void geometryChangedAction() override { - envelope = computeEnvelopeInternal(); + void geometryChangedAction() override + { + envelope = computeEnvelopeInternal(true); } private: void validateConstruction(); - void normalizeClosed(); - - }; struct GEOS_DLL LineStringLT { diff --git a/deps/libgeos/geos/include/geos/geom/MultiCurve.h b/deps/libgeos/geos/include/geos/geom/MultiCurve.h new file mode 100644 index 000000000..5505f5d96 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/MultiCurve.h @@ -0,0 +1,126 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace geom { + +class GEOS_DLL MultiCurve : public GeometryCollection { + friend class GeometryFactory; + +public: + ~MultiCurve() override = default; + + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + }; + + /// Returns a (possibly empty) [MultiPoint](@ref geom::MultiPoint) + std::unique_ptr getBoundary() const override; + + /** + * \brief + * Returns Dimension::False if all [Curves](@ref geom::Curve) in the collection + * are closed, 0 otherwise. + */ + int getBoundaryDimension() const override; + + /// Returns line dimension (1) + Dimension::DimensionType getDimension() const override; + + const Curve* getGeometryN(std::size_t n) const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + bool hasDimension(Dimension::DimensionType d) const override + { + return d == Dimension::L; + } + + /// Returns true if the MultiCurve is not empty, and every included + /// Curve is also closed. + bool isClosed() const; + + bool isDimensionStrict(Dimension::DimensionType d) const override + { + return d == Dimension::L; + } + + /** + * Creates a MultiCurve in the reverse + * order to this object. + * Both the order of the component Curves + * and the order of their coordinate sequences + * are reversed. + * + * @return a MultiCurve in the reverse order + */ + std::unique_ptr reverse() const + { + return std::unique_ptr(reverseImpl()); + } + +protected: + + /** + * \brief Constructs a MultiCurve. + * + * @param newLines The [Curves](@ref geom::Curve) for this + * MultiCurve, or `null` + * or an empty array to create the empty geometry. + * Elements may be empty Curve, + * but not `null`s. + * + * @param newFactory The GeometryFactory used to create this geometry. + * Caller must keep the factory alive for the life-time + * of the constructed MultiCurve. + * + * @note Constructed object will take ownership of + * the vector and its elements. + * + */ + MultiCurve(std::vector>&& newLines, + const GeometryFactory& newFactory); + + MultiCurve(std::vector>&& newLines, + const GeometryFactory& newFactory); + + MultiCurve(const MultiCurve& mp) + : GeometryCollection(mp) + {} + + MultiCurve* cloneImpl() const override + { + return new MultiCurve(*this); + } + + MultiCurve* reverseImpl() const override; + + int + getSortIndex() const override + { + return SORTINDEX_MULTICURVE; + }; + +}; + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/MultiLineString.h b/deps/libgeos/geos/include/geos/geom/MultiLineString.h index 47f2abaf9..2b6cc76fd 100644 --- a/deps/libgeos/geos/include/geos/geom/MultiLineString.h +++ b/deps/libgeos/geos/include/geos/geom/MultiLineString.h @@ -138,6 +138,11 @@ class GEOS_DLL MultiLineString: public GeometryCollection { return SORTINDEX_MULTILINESTRING; }; + bool hasCurvedComponents() const override + { + return false; + } + }; diff --git a/deps/libgeos/geos/include/geos/geom/MultiPoint.h b/deps/libgeos/geos/include/geos/geom/MultiPoint.h index 84863681e..0e1d564d1 100644 --- a/deps/libgeos/geos/include/geos/geom/MultiPoint.h +++ b/deps/libgeos/geos/include/geos/geom/MultiPoint.h @@ -134,6 +134,11 @@ class GEOS_DLL MultiPoint: public GeometryCollection { return SORTINDEX_MULTIPOINT; }; + bool hasCurvedComponents() const override + { + return false; + } + }; #ifdef _MSC_VER diff --git a/deps/libgeos/geos/include/geos/geom/MultiPolygon.h b/deps/libgeos/geos/include/geos/geom/MultiPolygon.h index 391b71240..f4cca3a38 100644 --- a/deps/libgeos/geos/include/geos/geom/MultiPolygon.h +++ b/deps/libgeos/geos/include/geos/geom/MultiPolygon.h @@ -26,7 +26,6 @@ #include // for inheritance #include // for inheritance #include // for Dimension::DimensionType -#include #include @@ -35,6 +34,7 @@ namespace geos { namespace geom { // geos::geom class Coordinate; class MultiPoint; +class Polygon; } } @@ -140,6 +140,11 @@ class GEOS_DLL MultiPolygon: public GeometryCollection { return SORTINDEX_MULTIPOLYGON; }; + bool hasCurvedComponents() const override + { + return false; + } + }; #ifdef _MSC_VER diff --git a/deps/libgeos/geos/include/geos/geom/MultiSurface.h b/deps/libgeos/geos/include/geos/geom/MultiSurface.h new file mode 100644 index 000000000..73871e4f0 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/MultiSurface.h @@ -0,0 +1,94 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace geom { +class GEOS_DLL MultiSurface : public GeometryCollection { + friend class GeometryFactory; + +public: + + ~MultiSurface() override; + + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + }; + + /** \brief + * Computes the boundary of this geometry + * + * @return a lineal geometry (which may be empty) + * @see Geometry#getBoundary + */ + std::unique_ptr getBoundary() const override; + + /// Returns 1 (MultiSurface boundary is MultiCurve) + int getBoundaryDimension() const override; + + /// Returns surface dimension (2) + Dimension::DimensionType getDimension() const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + bool hasDimension(Dimension::DimensionType d) const override + { + return d == Dimension::A; + } + + bool isDimensionStrict(Dimension::DimensionType d) const override + { + return d == Dimension::A; + } + + std::unique_ptr reverse() const + { + return std::unique_ptr(reverseImpl()); + } + +protected: + + MultiSurface(std::vector>&& newPolys, + const GeometryFactory& newFactory); + + MultiSurface(std::vector>&& newPolys, + const GeometryFactory& newFactory); + + MultiSurface(const MultiSurface& mp) + : GeometryCollection(mp) + {}; + + MultiSurface* cloneImpl() const override + { + return new MultiSurface(*this); + } + + int + getSortIndex() const override + { + return SORTINDEX_MULTISURFACE; + }; + + MultiSurface* reverseImpl() const override; + +}; +} +} diff --git a/deps/libgeos/geos/include/geos/geom/Polygon.h b/deps/libgeos/geos/include/geos/geom/Polygon.h index abd5f100f..aabc4d866 100644 --- a/deps/libgeos/geos/include/geos/geom/Polygon.h +++ b/deps/libgeos/geos/include/geos/geom/Polygon.h @@ -27,6 +27,7 @@ #include // for proper use of unique_ptr<> #include #include // for Dimension::DimensionType +#include #include // for unique_ptr @@ -57,7 +58,7 @@ namespace geom { // geos::geom * Specification for SQL . * */ -class GEOS_DLL Polygon: public Geometry { +class GEOS_DLL Polygon: public SurfaceImpl { public: @@ -68,6 +69,9 @@ class GEOS_DLL Polygon: public Geometry { ~Polygon() override = default; + std::unique_ptr + getCoordinates() const override; + /** * Creates and returns a full copy of this {@link Polygon} object. * (including all coordinates contained by it). @@ -79,23 +83,6 @@ class GEOS_DLL Polygon: public Geometry { return std::unique_ptr(cloneImpl()); } - std::unique_ptr getCoordinates() const override; - - std::size_t getNumPoints() const override; - - /// Returns surface dimension (2) - Dimension::DimensionType getDimension() const override; - - /// Returns coordinate dimension. - uint8_t getCoordinateDimension() const override; - - bool hasM() const override; - - bool hasZ() const override; - - /// Returns 1 (Polygon boundary is a MultiLineString) - int getBoundaryDimension() const override; - /** \brief * Computes the boundary of this geometry * @@ -104,121 +91,35 @@ class GEOS_DLL Polygon: public Geometry { */ std::unique_ptr getBoundary() const override; - bool isEmpty() const override; - - /// Returns the exterior ring (shell) - const LinearRing* getExteriorRing() const; - - /** - * \brief - * Take ownership of this Polygon's exterior ring. - * After releasing the exterior ring, the Polygon should be - * considered in a moved-from state and should not be accessed, - * except to release the interior rings (if desired.) - * @return exterior ring - */ - std::unique_ptr releaseExteriorRing(); - - /// Returns number of interior rings (hole) - std::size_t getNumInteriorRing() const; - - /// Get nth interior ring (hole) - const LinearRing* getInteriorRingN(std::size_t n) const; - - /** - * \brief - * Take ownership of this Polygon's interior rings. - * After releasing the rings, the Polygon should be - * considered in a moved-from state and should not be accessed, - * except to release the exterior ring (if desired.) - * @return vector of rings (may be empty) - */ - std::vector> releaseInteriorRings(); - std::string getGeometryType() const override; GeometryTypeId getGeometryTypeId() const override; - bool equalsExact(const Geometry* other, double tolerance = 0) const override; - bool equalsIdentical(const Geometry* other) const override; - void apply_rw(const CoordinateFilter* filter) override; - void apply_ro(CoordinateFilter* filter) const override; - void apply_rw(GeometryFilter* filter) override; - void apply_ro(GeometryFilter* filter) const override; - void apply_rw(CoordinateSequenceFilter& filter) override; - void apply_ro(CoordinateSequenceFilter& filter) const override; - void apply_rw(GeometryComponentFilter* filter) override; - void apply_ro(GeometryComponentFilter* filter) const override; - - std::unique_ptr convexHull() const override; void normalize() override; - /** - * \brief - * Apply a ring ordering convention to this polygon, with - * interior rings having an opposite orientation to the - * specified exterior orientation. - * - * \param exteriorCW should exterior ring be clockwise? - */ - void orientRings(bool exteriorCW); - std::unique_ptr reverse() const { return std::unique_ptr(reverseImpl()); } - const CoordinateXY* getCoordinate() const override; - double getArea() const override; - /// Returns the perimeter of this Polygon - double getLength() const override; - bool isRectangle() const override; - const Envelope* getEnvelopeInternal() const override { - return shell->getEnvelopeInternal(); - } + /** + * \brief + * Apply a ring ordering convention to this polygon, with + * interior rings having an opposite orientation to the + * specified exterior orientation. + * + * \param exteriorCW should exterior ring be clockwise? + */ + void orientRings(bool exteriorCW); protected: - - Polygon(const Polygon& p); - - int compareToSameClass(const Geometry* p) const override; - - /** - * Constructs a Polygon with the given exterior - * and interior boundaries. - * - * @param newShell the outer boundary of the new Polygon, - * or null or an empty - * LinearRing if the empty geometry - * is to be created. - * - * @param newHoles the LinearRings defining the inner - * boundaries of the new Polygon, or - * null or empty LinearRing - * if the empty geometry is to be created. - * - * @param newFactory the GeometryFactory used to create this geometry - * - * Polygon will take ownership of Shell and Holes LinearRings - */ - Polygon(std::unique_ptr && newShell, - std::vector> && newHoles, - const GeometryFactory& newFactory); - - Polygon(std::unique_ptr && newShell, - const GeometryFactory& newFactory); + using SurfaceImpl::SurfaceImpl; Polygon* cloneImpl() const override { return new Polygon(*this); } Polygon* reverseImpl() const override; - std::unique_ptr shell; - - std::vector> holes; - - void geometryChangedAction() override {} - int getSortIndex() const override { diff --git a/deps/libgeos/geos/include/geos/geom/PrecisionModel.h b/deps/libgeos/geos/include/geos/geom/PrecisionModel.h index f97fa22f4..2228067c5 100644 --- a/deps/libgeos/geos/include/geos/geom/PrecisionModel.h +++ b/deps/libgeos/geos/include/geos/geom/PrecisionModel.h @@ -236,7 +236,7 @@ class GEOS_DLL PrecisionModel { /** * Computes the grid size for a fixed precision model. * This is equal to the reciprocal of the scale factor. - * If the grid size has been set explicity (via a negative scale factor) + * If the grid size has been set explicitly (via a negative scale factor) * it will be returned. * * @return the grid size at a fixed precision scale. diff --git a/deps/libgeos/geos/include/geos/geom/SimpleCurve.h b/deps/libgeos/geos/include/geos/geom/SimpleCurve.h new file mode 100644 index 000000000..1bf674f69 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/SimpleCurve.h @@ -0,0 +1,142 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace geom { + +class GEOS_DLL SimpleCurve : public Curve { +public: + + using Curve::apply_ro; + using Curve::apply_rw; + + void apply_ro(CoordinateFilter* filter) const override; + + void apply_ro(CoordinateSequenceFilter& filter) const override; + + void apply_rw(CoordinateSequenceFilter& filter) override; + + void apply_rw(const CoordinateFilter* filter) override; + + bool equalsExact(const Geometry* other, double tolerance = 0) + const override; + + bool equalsIdentical(const Geometry* other) const override; + + /** + * \brief + * Returns a MultiPoint. + * Empty for closed Curve, a Point for each vertex otherwise. + */ + std::unique_ptr getBoundary() const override; + + const CoordinateXY* getCoordinate() const override; + + /// Returns coordinate dimension. + uint8_t getCoordinateDimension() const override; + + virtual const Coordinate& getCoordinateN(std::size_t n) const; + + std::unique_ptr getCoordinates() const override; + + /// Returns a read-only pointer to internal CoordinateSequence + const CoordinateSequence* getCoordinatesRO() const; + + const SimpleCurve* getCurveN(std::size_t) const override; + + /// \brief + /// Return the end point of the LineString + /// or NULL if this is an EMPTY LineString. + /// + virtual std::unique_ptr getEndPoint() const; + + const Envelope* getEnvelopeInternal() const override + { + return &envelope; + } + + std::size_t getNumCurves() const override; + + std::size_t getNumPoints() const override; + + virtual std::unique_ptr getPointN(std::size_t n) const; + + /// \brief + /// Return the start point of the LineString + /// or NULL if this is an EMPTY LineString. + /// + virtual std::unique_ptr getStartPoint() const; + + bool hasM() const override; + + bool hasZ() const override; + + bool isClosed() const override; + + virtual bool isCoordinate(CoordinateXY& pt) const; + + virtual bool isCurved() const = 0; + + bool isEmpty() const override; + + /** \brief + * Normalizes a SimpleCurve. + * + * A normalized simple curve + * has the first point which is not equal to its reflected point + * less than the reflected point. + */ + void normalize() override; + + /** + * \brief + * Take ownership of the CoordinateSequence managed by this geometry. + * After releasing the coordinates, the geometry should be considered + * in a moved-from state and should not be accessed. + * @return this Geometry's CoordinateSequence. + */ + std::unique_ptr releaseCoordinates(); + +protected: + + SimpleCurve(const SimpleCurve& other); + + SimpleCurve(std::unique_ptr&& newCoords, + bool isLinear, + const GeometryFactory& factory); + + int compareToSameClass(const Geometry* ls) const override; + + Envelope computeEnvelopeInternal(bool isLinear) const; + + // TODO: hold value or shared_ptr instead of unique_ptr? + std::unique_ptr points; + mutable Envelope envelope; + + +private: + + void normalizeClosed(); +}; + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/Surface.h b/deps/libgeos/geos/include/geos/geom/Surface.h new file mode 100644 index 000000000..7b1bb6ff3 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/Surface.h @@ -0,0 +1,115 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { + +class Curve; + +/// A Surface is an abstract class representing a Geometry of dimension 2. +/// It is extended by Polygon, which represents a Surface with linear edges, +/// and by CurvePolygon, whose edges may include circular arcs. +class GEOS_DLL Surface : public Geometry { + +private: + +protected: + using Geometry::Geometry; + +public: + + void apply_ro(CoordinateFilter* filter) const override; + + void apply_ro(CoordinateSequenceFilter& filter) const override; + + void apply_ro(GeometryComponentFilter* filter) const override; + + void apply_ro(GeometryFilter* filter) const override; + + void apply_rw(CoordinateSequenceFilter& filter) override; + + void apply_rw(GeometryComponentFilter* filter) override; + + void apply_rw(GeometryFilter* filter) override; + + void apply_rw(const CoordinateFilter* filter) override; + + std::unique_ptr convexHull() const override; + + bool + equalsExact(const Geometry* other, double tolerance = 0.0) const override; + + bool + equalsIdentical(const Geometry* other) const override; + + int + getBoundaryDimension() const override + { + return 1; + } + + const CoordinateXY* getCoordinate() const override; + + uint8_t getCoordinateDimension() const override; + + Dimension::DimensionType + getDimension() const override + { + return Dimension::A; // area + } + + const Envelope* getEnvelopeInternal() const override; + + /// Returns the exterior ring (shell) + virtual const Curve* getExteriorRing() const = 0; + + /// Get nth interior ring (hole) + virtual const Curve* getInteriorRingN(std::size_t n) const = 0; + + /// Returns the perimeter of this Surface + double getLength() const override; + + /// Returns number of interior rings (holes) + virtual size_t getNumInteriorRing() const = 0; + + size_t getNumPoints() const override; + + bool hasM() const override; + + bool hasZ() const override; + + bool isEmpty() const override; + +protected: + + int + compareToSameClass(const Geometry* g) const override; + + // Helper method allowing PolygonImpl to use GeometryFactory without cirular imports + static std::unique_ptr createEmptyRing(const GeometryFactory&); + + virtual Curve* getExteriorRing() = 0; + + virtual Curve* getInteriorRingN(std::size_t i) = 0; + + void geometryChangedAction() override {} + +}; + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/SurfaceImpl.h b/deps/libgeos/geos/include/geos/geom/SurfaceImpl.h new file mode 100644 index 000000000..60da401ef --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/SurfaceImpl.h @@ -0,0 +1,159 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace geos { +namespace geom { + +template +class SurfaceImpl : public Surface { + +protected: + + SurfaceImpl(const SurfaceImpl& p) + : + Surface(p), + shell(static_cast(p.shell->clone().release())), + holes(p.holes.size()) + { + for (std::size_t i = 0; i < holes.size(); ++i) { + holes[i].reset(static_cast(p.holes[i]->clone().release())); + } + } + + /** + * Constructs a Surface with the given exterior + * and interior boundaries. + * + * @param newShell the outer boundary of the new Polygon, + * or null or an empty + * Curve if the empty geometry + * is to be created. + * + * @param newHoles the rings defining the inner + * boundaries of the new Surface, or + * null or empty Curve + * if the empty geometry is to be created. + * + * @param newFactory the GeometryFactory used to create this geometry + * + * Polygon will take ownership of shell and hole curves + */ + SurfaceImpl(std::unique_ptr&& newShell, + const GeometryFactory& newFactory) : + Surface(&newFactory), + shell(std::move(newShell)) + { + if (shell == nullptr) { + shell.reset(static_cast(createEmptyRing(newFactory).release())); + } + } + + SurfaceImpl(std::unique_ptr&& newShell, + std::vector>&& newHoles, + const GeometryFactory& newFactory) : + Surface(&newFactory), + shell(std::move(newShell)), + holes(std::move(newHoles)) + { + if (shell == nullptr) { + shell.reset(static_cast(createEmptyRing(newFactory).release())); + } + + if(shell->isEmpty() && hasNonEmptyElements(&holes)) { + throw geos::util::IllegalArgumentException("shell is empty but holes are not"); + } + if (hasNullElements(&holes)) { + throw geos::util::IllegalArgumentException("holes must not contain null elements"); + } + } + +public: + + const RingType* + getExteriorRing() const override + { + return shell.get(); + } + + RingType* + getExteriorRing() override + { + return shell.get(); + } + + const RingType* + getInteriorRingN(std::size_t n) const override + { + return holes[n].get(); + } + + RingType* + getInteriorRingN(std::size_t n) override + { + return holes[n].get(); + } + + size_t getNumInteriorRing() const override + { + return holes.size(); + } + + /** + * \brief + * Take ownership of this Surface's exterior ring. + * After releasing the exterior ring, the Surface should be + * considered in a moved-from state and should not be accessed, + * except to release the interior rings (if desired.) + * @return exterior ring + */ + std::unique_ptr + releaseExteriorRing() + { + return std::move(shell); + } + + /** + * \brief + * Take ownership of this Surfaces's interior rings. + * After releasing the rings, the Surface should be + * considered in a moved-from state and should not be accessed, + * except to release the exterior ring (if desired.) + * @return vector of rings (may be empty) + */ + std::vector> releaseInteriorRings() + { + return std::move(holes); + } + +protected: + std::unique_ptr shell; + std::vector> holes; + +}; + +} +} diff --git a/deps/libgeos/geos/include/geos/geom/prep/BasicPreparedGeometry.h b/deps/libgeos/geos/include/geos/geom/prep/BasicPreparedGeometry.h index 62f8621fa..f061de451 100644 --- a/deps/libgeos/geos/include/geos/geom/prep/BasicPreparedGeometry.h +++ b/deps/libgeos/geos/include/geos/geom/prep/BasicPreparedGeometry.h @@ -20,10 +20,8 @@ #pragma once #include // for inheritance -//#include -//#include #include -//#include +#include #include #include @@ -40,6 +38,8 @@ namespace geos { namespace geom { // geos::geom namespace prep { // geos::geom::prep +using geos::operation::relateng::RelateNG; + // * \class BasicPreparedGeometry /** @@ -59,6 +59,15 @@ class BasicPreparedGeometry: public PreparedGeometry { private: const geom::Geometry* baseGeom; std::vector representativePts; + mutable std::unique_ptr relate_ng; + + RelateNG& getRelateNG() const + { + if (relate_ng == nullptr) + relate_ng = RelateNG::prepare(baseGeom); + + return *relate_ng; + } protected: /** @@ -119,6 +128,11 @@ class BasicPreparedGeometry: public PreparedGeometry { */ bool isAnyTargetComponentInTest(const geom::Geometry* testGeom) const; + /** + * Default implementation. + */ + bool within(const geom::Geometry* g) const override; + /** * Default implementation. */ @@ -168,7 +182,12 @@ class BasicPreparedGeometry: public PreparedGeometry { /** * Default implementation. */ - bool within(const geom::Geometry* g) const override; + std::unique_ptr relate(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool relate(const geom::Geometry* g, const std::string& pat) const override; /** * Default implementation. diff --git a/deps/libgeos/geos/include/geos/geom/prep/PreparedGeometry.h b/deps/libgeos/geos/include/geos/geom/prep/PreparedGeometry.h index d0cf2379b..6c7751390 100644 --- a/deps/libgeos/geos/include/geos/geom/prep/PreparedGeometry.h +++ b/deps/libgeos/geos/include/geos/geom/prep/PreparedGeometry.h @@ -20,6 +20,7 @@ #include #include +#include #include // Forward declarations @@ -28,6 +29,7 @@ namespace geos { class Geometry; class Coordinate; class CoordinateSequence; + class IntersectionMatrix; } } @@ -227,6 +229,28 @@ class GEOS_DLL PreparedGeometry { * */ virtual bool isWithinDistance(const geom::Geometry* geom, double dist) const = 0; + + /** \brief + * Compares the prepared geometry to the given geometry + * and returns the DE9IM intersection matrix as a string. + * + * @param geom the Geometry to test the + * @return the DE9IM matrix + */ + virtual std::unique_ptr relate(const geom::Geometry* geom) const = 0; + + /** \brief + * Compares the prepared geometry to the given geometry + * and the provided DE9IM pattern, and returns true if the + * pattern is consistent with the relationship between the + * prepared and provided geometries. + * + * @param geom the Geometry to test the distance to + * @param pat the DE9IM pattern + * @return true if the patterns are consistent + */ + virtual bool relate(const geom::Geometry* geom, const std::string& pat) const = 0; + }; diff --git a/deps/libgeos/geos/include/geos/geom/util/GeometryEditor.h b/deps/libgeos/geos/include/geos/geom/util/GeometryEditor.h index 86e1a3226..294aad3c8 100644 --- a/deps/libgeos/geos/include/geos/geom/util/GeometryEditor.h +++ b/deps/libgeos/geos/include/geos/geom/util/GeometryEditor.h @@ -55,7 +55,7 @@ namespace util { // geos.geom.util * this is not checked by the GeometryEditor * - the coordinate lists may be changed * (e.g. by adding or deleting coordinates). - * The modifed coordinate lists must be consistent with their original + * The modified coordinate lists must be consistent with their original * parent component * (e.g. a LinearRing must always have at least 4 coordinates, and the * first and last coordinate must be equal) diff --git a/deps/libgeos/geos/include/geos/geom/util/GeometryLister.h b/deps/libgeos/geos/include/geos/geom/util/GeometryLister.h new file mode 100644 index 000000000..912518ef1 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/util/GeometryLister.h @@ -0,0 +1,92 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/util/GeometryExtracter.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * Extracts all the components of a collection, or just echoes back a + * pointers to singletons. + */ +class GEOS_DLL GeometryLister { + +public: + + /** + * Extracts the components from a {@link Geometry} + * and adds them to the provided container. + * + * Useful for iterating over components of a collection. + * + * @param geom the geometry from which to extract + * @param lst the list to add the extracted elements to + */ + static void + list(const Geometry* geom, std::vector& lst) + { + if(geom->isCollection()) { + GeometryLister::Lister lister(lst); + geom->apply_ro(&lister); + } + else { + lst.push_back(geom); + } + } + +private: + + struct Lister : public GeometryFilter { + + /** + * Constructs a filter with a list in which to store the elements found. + * + * @param comps the container to extract into (will push_back to it) + */ + Lister(std::vector& p_geoms) : geoms(p_geoms) {} + + std::vector& geoms; + + void + filter_ro(const Geometry* geom) override + { + if(!geom->isCollection()) { + geoms.push_back(geom); + } + } + + // // Declare type as noncopyable + // Lister(const Lister& other); + // Lister& operator=(const Lister& rhs); + }; +}; + + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/geom/util/GeometryTransformer.h b/deps/libgeos/geos/include/geos/geom/util/GeometryTransformer.h index ed49566c6..7cbe0c35f 100644 --- a/deps/libgeos/geos/include/geos/geom/util/GeometryTransformer.h +++ b/deps/libgeos/geos/include/geos/geom/util/GeometryTransformer.h @@ -148,7 +148,7 @@ class GEOS_DLL GeometryTransformer { bool pruneEmptyGeometry; /** - * `true` if a homogenous collection result + * `true` if a homogeneous collection result * from a {@link GeometryCollection} should still * be a general GeometryCollection */ diff --git a/deps/libgeos/geos/include/geos/geom/util/PolygonalExtracter.h b/deps/libgeos/geos/include/geos/geom/util/PolygonalExtracter.h new file mode 100644 index 000000000..9dfba65d5 --- /dev/null +++ b/deps/libgeos/geos/include/geos/geom/util/PolygonalExtracter.h @@ -0,0 +1,54 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * \brief Extracts the polygonal (Polygon and MultiPolygon) elements from a Geometry. + */ +class GEOS_DLL PolygonalExtracter { + +public: + + /** + * Pushes the polygonal (Polygon and MultiPolygon) elements from a geometry into + * the provided vector. + * + * @param geom the geometry to extract from + * @param polys the vector to add the polygonal elements to + */ + static void getPolygonals(const Geometry& geom, std::vector& polys); + +private: + + static void getPolygonals(const Geometry* geom, std::vector& polys); + + // Declare type as noncopyable + PolygonalExtracter(const PolygonalExtracter& other) = delete; + PolygonalExtracter& operator=(const PolygonalExtracter& rhs) = delete; +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/geomgraph/DirectedEdge.h b/deps/libgeos/geos/include/geos/geomgraph/DirectedEdge.h index 8b4d8f8e0..ab88f6234 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/DirectedEdge.h +++ b/deps/libgeos/geos/include/geos/geomgraph/DirectedEdge.h @@ -39,7 +39,7 @@ namespace geos { namespace geomgraph { // geos.geomgraph /// A directed EdgeEnd -class GEOS_DLL DirectedEdge: public EdgeEnd { +class GEOS_DLL DirectedEdge final: public EdgeEnd { public: @@ -159,7 +159,7 @@ class GEOS_DLL DirectedEdge: public EdgeEnd { }; /** \brief - * Tells wheter this edge is a Line + * Tells whether this edge is a Line * * This edge is a line edge if * - at least one of the labels is a line label @@ -169,7 +169,7 @@ class GEOS_DLL DirectedEdge: public EdgeEnd { bool isLineEdge(); /** \brief - * Tells wheter this edge is an Area + * Tells whether this edge is an Area * * This is an interior Area edge if * - its label is an Area label for both Geometries diff --git a/deps/libgeos/geos/include/geos/geomgraph/DirectedEdgeStar.h b/deps/libgeos/geos/include/geos/geomgraph/DirectedEdgeStar.h index 3e70ada97..4400daa46 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/DirectedEdgeStar.h +++ b/deps/libgeos/geos/include/geos/geomgraph/DirectedEdgeStar.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -81,7 +82,7 @@ class GEOS_DLL DirectedEdgeStar: public EdgeEndStar { /** \brief * Compute the labelling for all dirEdges in this star, as well as the overall labelling */ - void computeLabelling(std::vector* geom) override; // throw(TopologyException *); + void computeLabelling(const std::vector>&geom) override; // throw(TopologyException *); /** \brief * For each dirEdge in the star, merge the label from the sym dirEdge into the label @@ -116,7 +117,7 @@ class GEOS_DLL DirectedEdgeStar: public EdgeEndStar { void linkAllDirectedEdges(); /** \brief - * Traverse the star of edges, maintaing the current location in the result + * Traverse the star of edges, maintaining the current location in the result * area at this node (if any). * * If any L edges are found in the interior of the result, mark them as covered. diff --git a/deps/libgeos/geos/include/geos/geomgraph/Edge.h b/deps/libgeos/geos/include/geos/geomgraph/Edge.h index 100ae71e7..52a9f57bc 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/Edge.h +++ b/deps/libgeos/geos/include/geos/geomgraph/Edge.h @@ -60,7 +60,7 @@ namespace geos { namespace geomgraph { // geos.geomgraph /** The edge component of a geometry graph */ -class GEOS_DLL Edge: public GraphComponent { +class GEOS_DLL Edge final: public GraphComponent { using GraphComponent::updateIM; private: @@ -102,27 +102,27 @@ class GEOS_DLL Edge: public GraphComponent { ~Edge() override; - virtual size_t + size_t getNumPoints() const { return pts->getSize(); } - virtual const geom::CoordinateSequence* + const geom::CoordinateSequence* getCoordinates() const { testInvariant(); return pts.get(); } - virtual const geom::Coordinate& + const geom::Coordinate& getCoordinate(std::size_t i) const { testInvariant(); return pts->getAt(i); } - virtual const geom::Coordinate& + const geom::Coordinate& getCoordinate() const { testInvariant(); @@ -130,8 +130,8 @@ class GEOS_DLL Edge: public GraphComponent { } - virtual Depth& - getDepth() + const Depth& + getDepth() const { testInvariant(); return depth; @@ -142,41 +142,48 @@ class GEOS_DLL Edge: public GraphComponent { * * @return the change in depth as the edge is crossed from R to L */ - virtual int + int getDepthDelta() const { testInvariant(); return depthDelta; } - virtual void + void setDepthDelta(int newDepthDelta) { depthDelta = newDepthDelta; testInvariant(); } - virtual size_t + size_t getMaximumSegmentIndex() const { testInvariant(); return getNumPoints() - 1; } - virtual EdgeIntersectionList& + EdgeIntersectionList& getEdgeIntersectionList() { testInvariant(); return eiList; } + const EdgeIntersectionList& + getEdgeIntersectionList() const + { + testInvariant(); + return eiList; + } + /// \brief /// Return this Edge's index::MonotoneChainEdge, /// ownership is retained by this object. /// - virtual index::MonotoneChainEdge* getMonotoneChainEdge(); + index::MonotoneChainEdge* getMonotoneChainEdge(); - virtual bool + bool isClosed() const { testInvariant(); @@ -187,11 +194,11 @@ class GEOS_DLL Edge: public GraphComponent { * An Edge is collapsed if it is an Area edge and it consists of * two segments which are equal and opposite (eg a zero-width V). */ - virtual bool isCollapsed() const; + bool isCollapsed() const; - virtual Edge* getCollapsedEdge(); + Edge* getCollapsedEdge(); - virtual void + void setIsolated(bool newIsIsolated) { isIsolatedVar = newIsIsolated; @@ -209,7 +216,7 @@ class GEOS_DLL Edge: public GraphComponent { * Adds EdgeIntersections for one or both * intersections found for a segment of an edge to the edge intersection list. */ - virtual void addIntersections(algorithm::LineIntersector* li, std::size_t segmentIndex, + void addIntersections(algorithm::LineIntersector* li, std::size_t segmentIndex, std::size_t geomIndex); /// Add an EdgeIntersection for intersection intIndex. @@ -217,7 +224,7 @@ class GEOS_DLL Edge: public GraphComponent { /// An intersection that falls exactly on a vertex of the edge is normalized /// to use the higher of the two possible segmentIndexes /// - virtual void addIntersection(algorithm::LineIntersector* li, std::size_t segmentIndex, + void addIntersection(algorithm::LineIntersector* li, std::size_t segmentIndex, std::size_t geomIndex, std::size_t intIndex); /// Update the IM with the contribution for this component. @@ -233,11 +240,11 @@ class GEOS_DLL Edge: public GraphComponent { } /// return true if the coordinate sequences of the Edges are identical - virtual bool isPointwiseEqual(const Edge* e) const; + bool isPointwiseEqual(const Edge* e) const; - virtual std::string print() const; + std::string print() const; - virtual std::string printReverse() const; + std::string printReverse() const; /** * equals is defined to be: @@ -246,16 +253,16 @@ class GEOS_DLL Edge: public GraphComponent { * iff * the coordinates of e1 are the same or the reverse of the coordinates in e2 */ - virtual bool equals(const Edge& e) const; + bool equals(const Edge& e) const; - virtual bool + bool equals(const Edge* e) const { assert(e); return equals(*e); } - virtual const geom::Envelope* getEnvelope(); + const geom::Envelope* getEnvelope(); }; diff --git a/deps/libgeos/geos/include/geos/geomgraph/EdgeEnd.h b/deps/libgeos/geos/include/geos/geomgraph/EdgeEnd.h index f497d94e3..86ac11a87 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/EdgeEnd.h +++ b/deps/libgeos/geos/include/geos/geomgraph/EdgeEnd.h @@ -51,7 +51,7 @@ namespace geomgraph { // geos.geomgraph * "a has a greater angle with the x-axis than b". * This ordering is used to sort EdgeEnds around a node. */ -class GEOS_DLL EdgeEnd { +class GEOS_DLL EdgeEnd /* non-final */ { public: @@ -101,7 +101,7 @@ class GEOS_DLL EdgeEnd { return label; } - virtual geom::Coordinate& getCoordinate() { + geom::Coordinate& getCoordinate() { return p0; } @@ -111,19 +111,19 @@ class GEOS_DLL EdgeEnd { return p0; } - virtual geom::Coordinate& getDirectedCoordinate(); + geom::Coordinate& getDirectedCoordinate(); - virtual int getQuadrant(); + int getQuadrant(); - virtual double getDx(); + double getDx(); - virtual double getDy(); + double getDy(); - virtual void setNode(Node* newNode); + void setNode(Node* newNode); - virtual Node* getNode(); + Node* getNode(); - virtual int compareTo(const EdgeEnd* e) const; + int compareTo(const EdgeEnd* e) const; /** * Implements the total order relation: @@ -141,7 +141,7 @@ class GEOS_DLL EdgeEnd { * computeOrientation function can be used to decide * the relative orientation of the vectors. */ - virtual int compareDirection(const EdgeEnd* e) const; + int compareDirection(const EdgeEnd* e) const; virtual void computeLabel(const algorithm::BoundaryNodeRule& bnr); @@ -155,7 +155,7 @@ class GEOS_DLL EdgeEnd { EdgeEnd(Edge* newEdge); - virtual void init(const geom::Coordinate& newP0, + void init(const geom::Coordinate& newP0, const geom::Coordinate& newP1); private: diff --git a/deps/libgeos/geos/include/geos/geomgraph/EdgeEndStar.h b/deps/libgeos/geos/include/geos/geomgraph/EdgeEndStar.h index 2982703a8..b249b60d5 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/EdgeEndStar.h +++ b/deps/libgeos/geos/include/geos/geomgraph/EdgeEndStar.h @@ -27,6 +27,7 @@ #include // for p0,p1 #include +#include #include #include #include @@ -59,7 +60,7 @@ namespace geomgraph { // geos.geomgraph * * @version 1.4 */ -class GEOS_DLL EdgeEndStar { +class GEOS_DLL EdgeEndStar /* non-final */ { public: typedef std::set container; @@ -85,46 +86,46 @@ class GEOS_DLL EdgeEndStar { * a Coordinate owned by the specific EdgeEnd happening * to be the first in the star (ordered CCW) */ - virtual geom::Coordinate& getCoordinate(); + geom::Coordinate& getCoordinate(); const geom::Coordinate& getCoordinate() const; - virtual std::size_t getDegree(); + std::size_t getDegree(); - virtual iterator begin(); + iterator begin(); - virtual iterator end(); + iterator end(); - virtual reverse_iterator rbegin(); + reverse_iterator rbegin(); - virtual reverse_iterator rend(); + reverse_iterator rend(); - virtual const_iterator + const_iterator begin() const { return edgeMap.begin(); } - virtual const_iterator + const_iterator end() const { return edgeMap.end(); } - virtual container& getEdges(); + container& getEdges(); - virtual EdgeEnd* getNextCW(EdgeEnd* ee); + EdgeEnd* getNextCW(EdgeEnd* ee); - virtual void computeLabelling(std::vector* geomGraph); + virtual void computeLabelling(const std::vector>&geomGraph); // throw(TopologyException *); - virtual bool isAreaLabelsConsistent(const GeometryGraph& geomGraph); + bool isAreaLabelsConsistent(const GeometryGraph& geomGraph); - virtual void propagateSideLabels(uint32_t geomIndex); + void propagateSideLabels(uint32_t geomIndex); // throw(TopologyException *); //virtual int findIndex(EdgeEnd *eSearch); - virtual iterator find(EdgeEnd* eSearch); + iterator find(EdgeEnd* eSearch); virtual std::string print() const; @@ -139,7 +140,7 @@ class GEOS_DLL EdgeEndStar { /** \brief * Insert an EdgeEnd into the map. */ - virtual void + void insertEdgeEnd(EdgeEnd* e) { edgeMap.insert(e); @@ -147,9 +148,9 @@ class GEOS_DLL EdgeEndStar { private: - virtual geom::Location getLocation(uint32_t geomIndex, - const geom::Coordinate& p, - std::vector* geom); + geom::Location getLocation(uint32_t geomIndex, + const geom::Coordinate&p, + const std::vector>&geom); /** \brief * The location of the point for this star in @@ -157,9 +158,9 @@ class GEOS_DLL EdgeEndStar { */ std::array ptInAreaLocation; - virtual void computeEdgeEndLabels(const algorithm::BoundaryNodeRule&); + void computeEdgeEndLabels(const algorithm::BoundaryNodeRule&); - virtual bool checkAreaLabelsConsistent(uint32_t geomIndex); + bool checkAreaLabelsConsistent(uint32_t geomIndex); }; diff --git a/deps/libgeos/geos/include/geos/geomgraph/EdgeIntersection.h b/deps/libgeos/geos/include/geos/geomgraph/EdgeIntersection.h index 7e43c910a..6a65131cf 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/EdgeIntersection.h +++ b/deps/libgeos/geos/include/geos/geomgraph/EdgeIntersection.h @@ -39,7 +39,7 @@ namespace geomgraph { // geos.geomgraph * The intersection point must be precise. * */ -class GEOS_DLL EdgeIntersection { +class GEOS_DLL EdgeIntersection final { public: // the point of intersection diff --git a/deps/libgeos/geos/include/geos/geomgraph/EdgeIntersectionList.h b/deps/libgeos/geos/include/geos/geomgraph/EdgeIntersectionList.h index 428bbd490..7c8bdba89 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/EdgeIntersectionList.h +++ b/deps/libgeos/geos/include/geos/geomgraph/EdgeIntersectionList.h @@ -54,7 +54,7 @@ namespace geomgraph { // geos.geomgraph * Implements splitting an edge with intersections * into multiple resultant edges. */ -class GEOS_DLL EdgeIntersectionList { +class GEOS_DLL EdgeIntersectionList final { public: // Instead of storing edge intersections in a set, as JTS does, we store them // in a vector and then sort the vector if needed before iterating among the diff --git a/deps/libgeos/geos/include/geos/geomgraph/EdgeList.h b/deps/libgeos/geos/include/geos/geomgraph/EdgeList.h index 01e6675eb..56fd225c6 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/EdgeList.h +++ b/deps/libgeos/geos/include/geos/geomgraph/EdgeList.h @@ -52,7 +52,7 @@ namespace geomgraph { // geos.geomgraph * It supports locating edges * that are pointwise equals to a target edge. */ -class GEOS_DLL EdgeList { +class GEOS_DLL EdgeList final { private: @@ -85,7 +85,7 @@ class GEOS_DLL EdgeList { ocaMap() {} - virtual ~EdgeList() = default; + ~EdgeList() = default; /** * Insert an edge unless it is already in the list diff --git a/deps/libgeos/geos/include/geos/geomgraph/EdgeNodingValidator.h b/deps/libgeos/geos/include/geos/geomgraph/EdgeNodingValidator.h index 287bf678a..6f844d1e0 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/EdgeNodingValidator.h +++ b/deps/libgeos/geos/include/geos/geomgraph/EdgeNodingValidator.h @@ -51,7 +51,7 @@ namespace geomgraph { // geos.geomgraph * * Throws an appropriate exception if an noding error is found. */ -class GEOS_DLL EdgeNodingValidator { +class GEOS_DLL EdgeNodingValidator final { private: std::vector& toSegmentStrings(std::vector& edges); diff --git a/deps/libgeos/geos/include/geos/geomgraph/EdgeRing.h b/deps/libgeos/geos/include/geos/geomgraph/EdgeRing.h index 00f5d9a25..257453c81 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/EdgeRing.h +++ b/deps/libgeos/geos/include/geos/geomgraph/EdgeRing.h @@ -54,7 +54,7 @@ namespace geos { namespace geomgraph { // geos.geomgraph /** EdgeRing */ -class GEOS_DLL EdgeRing { +class GEOS_DLL EdgeRing /* non-final */ { public: friend std::ostream& operator<< (std::ostream& os, const EdgeRing& er); diff --git a/deps/libgeos/geos/include/geos/geomgraph/GeometryGraph.h b/deps/libgeos/geos/include/geos/geomgraph/GeometryGraph.h index 322bb3fc8..78976cd68 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/GeometryGraph.h +++ b/deps/libgeos/geos/include/geos/geomgraph/GeometryGraph.h @@ -68,7 +68,7 @@ namespace geomgraph { // geos.geomgraph /** \brief * A GeometryGraph is a graph that models a given Geometry. */ -class GEOS_DLL GeometryGraph: public PlanarGraph { +class GEOS_DLL GeometryGraph final: public PlanarGraph { using PlanarGraph::add; using PlanarGraph::findEdge; diff --git a/deps/libgeos/geos/include/geos/geomgraph/GraphComponent.h b/deps/libgeos/geos/include/geos/geomgraph/GraphComponent.h index 44ba7b71a..2ad8058fd 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/GraphComponent.h +++ b/deps/libgeos/geos/include/geos/geomgraph/GraphComponent.h @@ -42,7 +42,7 @@ namespace geomgraph { // geos.geomgraph * * Each GraphComponent can carry a Label. */ -class GEOS_DLL GraphComponent { +class GEOS_DLL GraphComponent /* non-final */ { public: GraphComponent(); @@ -69,39 +69,39 @@ class GEOS_DLL GraphComponent { label = newLabel; } - virtual void + void setInResult(bool p_isInResult) { isInResultVar = p_isInResult; } - virtual bool + bool isInResult() const { return isInResultVar; } - virtual void setCovered(bool isCovered); - virtual bool + void setCovered(bool isCovered); + bool isCovered() const { return isCoveredVar; } - virtual bool + bool isCoveredSet() const { return isCoveredSetVar; } - virtual bool + bool isVisited() const { return isVisitedVar; } - virtual void + void setVisited(bool p_isVisited) { isVisitedVar = p_isVisited; } virtual bool isIsolated() const = 0; - virtual void updateIM(geom::IntersectionMatrix& im); + void updateIM(geom::IntersectionMatrix& im); protected: Label label; virtual void computeIM(geom::IntersectionMatrix& im) = 0; diff --git a/deps/libgeos/geos/include/geos/geomgraph/Label.h b/deps/libgeos/geos/include/geos/geomgraph/Label.h index 025e454ab..773b822ad 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/Label.h +++ b/deps/libgeos/geos/include/geos/geomgraph/Label.h @@ -54,7 +54,7 @@ namespace geomgraph { // geos.geomgraph * with specific geometries. * */ -class GEOS_DLL Label { +class GEOS_DLL Label final { public: diff --git a/deps/libgeos/geos/include/geos/geomgraph/Node.h b/deps/libgeos/geos/include/geos/geomgraph/Node.h index 72991be0c..3f63f6426 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/Node.h +++ b/deps/libgeos/geos/include/geos/geomgraph/Node.h @@ -56,7 +56,7 @@ namespace geos { namespace geomgraph { // geos.geomgraph /** \brief The node component of a geometry graph. */ -class GEOS_DLL Node: public GraphComponent { +class GEOS_DLL Node /* non-final */: public GraphComponent { using GraphComponent::setLabel; public: @@ -67,18 +67,18 @@ class GEOS_DLL Node: public GraphComponent { ~Node() override; - virtual const geom::Coordinate& getCoordinate() const; + const geom::Coordinate& getCoordinate() const; - virtual EdgeEndStar* getEdges(); + EdgeEndStar* getEdges(); bool isIsolated() const override; /** \brief * Add the edge to the list of edges at this node */ - virtual void add(EdgeEnd* e); + void add(EdgeEnd* e); - virtual void mergeLabel(const Node& n); + void mergeLabel(const Node& n); /** \brief * To merge labels for two nodes, @@ -87,15 +87,15 @@ class GEOS_DLL Node: public GraphComponent { * The location for the corresponding node LabelElement is set * to the result, as long as the location is non-null. */ - virtual void mergeLabel(const Label& label2); + void mergeLabel(const Label& label2); - virtual void setLabel(uint8_t argIndex, geom::Location onLocation); + void setLabel(uint8_t argIndex, geom::Location onLocation); /** \brief * Updates the label of a node to BOUNDARY, * obeying the mod-2 boundaryDetermination rule. */ - virtual void setLabelBoundary(uint8_t argIndex); + void setLabelBoundary(uint8_t argIndex); /** * The location for a given eltIndex for a node will be one @@ -105,13 +105,13 @@ class GEOS_DLL Node: public GraphComponent { * in the boundary. * The merged location is the maximum of the two input values. */ - virtual geom::Location computeMergedLocation(const Label& label2, uint8_t eltIndex); + geom::Location computeMergedLocation(const Label& label2, uint8_t eltIndex); - virtual std::string print() const; + std::string print() const; - virtual const std::vector& getZ() const; + const std::vector& getZ() const; - virtual void addZ(double); + void addZ(double); /** \brief * Tests whether any incident edge is flagged as @@ -124,7 +124,7 @@ class GEOS_DLL Node: public GraphComponent { * @return true if any indicident edge in the in * the result */ - virtual bool isIncidentEdgeInResult() const; + bool isIncidentEdgeInResult() const; protected: diff --git a/deps/libgeos/geos/include/geos/geomgraph/NodeFactory.h b/deps/libgeos/geos/include/geos/geomgraph/NodeFactory.h index 641de8bc7..fe577b610 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/NodeFactory.h +++ b/deps/libgeos/geos/include/geos/geomgraph/NodeFactory.h @@ -35,7 +35,7 @@ class Node; namespace geos { namespace geomgraph { // geos.geomgraph -class GEOS_DLL NodeFactory { +class GEOS_DLL NodeFactory /* non-final */ { public: virtual Node* createNode(const geom::Coordinate& coord) const; static const NodeFactory& instance(); diff --git a/deps/libgeos/geos/include/geos/geomgraph/NodeMap.h b/deps/libgeos/geos/include/geos/geomgraph/NodeMap.h index e30969a79..a3a769e11 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/NodeMap.h +++ b/deps/libgeos/geos/include/geos/geomgraph/NodeMap.h @@ -47,7 +47,7 @@ class NodeFactory; namespace geos { namespace geomgraph { // geos.geomgraph -class GEOS_DLL NodeMap { +class GEOS_DLL NodeMap final { public: typedef std::map, geom::CoordinateLessThan> container; @@ -65,8 +65,6 @@ class GEOS_DLL NodeMap { /// keep it alive for the whole NodeMap lifetime NodeMap(const NodeFactory& newNodeFact); - virtual ~NodeMap(); - Node* addNode(const geom::Coordinate& coord); Node* addNode(Node* n); diff --git a/deps/libgeos/geos/include/geos/geomgraph/PlanarGraph.h b/deps/libgeos/geos/include/geos/geomgraph/PlanarGraph.h index 8ba0c8c63..8684d3b7a 100644 --- a/deps/libgeos/geos/include/geos/geomgraph/PlanarGraph.h +++ b/deps/libgeos/geos/include/geos/geomgraph/PlanarGraph.h @@ -69,7 +69,7 @@ namespace geomgraph { // geos.geomgraph * different graphs * */ -class GEOS_DLL PlanarGraph { +class GEOS_DLL PlanarGraph /* non-final */ { public: /** \brief @@ -105,36 +105,36 @@ class GEOS_DLL PlanarGraph { virtual ~PlanarGraph(); - virtual std::vector::iterator getEdgeIterator(); + std::vector::iterator getEdgeIterator(); - virtual std::vector* getEdgeEnds(); + std::vector* getEdgeEnds(); - virtual bool isBoundaryNode(uint8_t geomIndex, const geom::Coordinate& coord); + bool isBoundaryNode(uint8_t geomIndex, const geom::Coordinate& coord); - virtual void add(EdgeEnd* e); + void add(EdgeEnd* e); - virtual NodeMap::iterator getNodeIterator(); + NodeMap::iterator getNodeIterator(); - virtual void getNodes(std::vector&); + void getNodes(std::vector&); - virtual Node* addNode(Node* node); + Node* addNode(Node* node); - virtual Node* addNode(const geom::Coordinate& coord); + Node* addNode(const geom::Coordinate& coord); /** * @return the node if found; null otherwise */ - virtual Node* find(geom::Coordinate& coord); + Node* find(geom::Coordinate& coord); /** \brief * Add a set of edges to the graph. For each edge two DirectedEdges * will be created. DirectedEdges are NOT linked by this method. */ - virtual void addEdges(const std::vector& edgesToAdd); + void addEdges(const std::vector& edgesToAdd); - virtual void linkResultDirectedEdges(); + void linkResultDirectedEdges(); - virtual void linkAllDirectedEdges(); + void linkAllDirectedEdges(); /** \brief * Returns the EdgeEnd which has edge e as its base edge @@ -143,7 +143,7 @@ class GEOS_DLL PlanarGraph { * @return the edge, if found * null if the edge was not found */ - virtual EdgeEnd* findEdgeEnd(Edge* e); + EdgeEnd* findEdgeEnd(Edge* e); /** \brief * Returns the edge whose first two coordinates are p0 and p1 @@ -151,7 +151,7 @@ class GEOS_DLL PlanarGraph { * @return the edge, if found * null if the edge was not found */ - virtual Edge* findEdge(const geom::Coordinate& p0, + Edge* findEdge(const geom::Coordinate& p0, const geom::Coordinate& p1); /** \brief @@ -161,12 +161,12 @@ class GEOS_DLL PlanarGraph { * @return the edge, if found * null if the edge was not found */ - virtual Edge* findEdgeInSameDirection(const geom::Coordinate& p0, + Edge* findEdgeInSameDirection(const geom::Coordinate& p0, const geom::Coordinate& p1); - virtual std::string printEdges(); + std::string printEdges(); - virtual NodeMap* getNodeMap(); + NodeMap* getNodeMap(); protected: @@ -176,7 +176,7 @@ class GEOS_DLL PlanarGraph { std::vector* edgeEndList; - virtual void insertEdge(Edge* e); + void insertEdge(Edge* e); private: diff --git a/deps/libgeos/geos/include/geos/index/bintree/Bintree.h b/deps/libgeos/geos/include/geos/index/bintree/Bintree.h index b0d897cbe..4c414761a 100644 --- a/deps/libgeos/geos/include/geos/index/bintree/Bintree.h +++ b/deps/libgeos/geos/include/geos/index/bintree/Bintree.h @@ -44,7 +44,7 @@ namespace bintree { // geos::index::bintree * may be a single point). * * This implementation does not require specifying the extent of the inserted - * items beforehand. It will automatically expand to accomodate any extent + * items beforehand. It will automatically expand to accommodate any extent * of dataset. * * This index is different to the "Interval Tree of Edelsbrunner" diff --git a/deps/libgeos/geos/include/geos/index/chain/MonotoneChain.h b/deps/libgeos/geos/include/geos/index/chain/MonotoneChain.h index 4b0dbb427..a5172f9d7 100644 --- a/deps/libgeos/geos/include/geos/index/chain/MonotoneChain.h +++ b/deps/libgeos/geos/include/geos/index/chain/MonotoneChain.h @@ -197,6 +197,7 @@ class GEOS_DLL MonotoneChain { /// Owned by this class mutable geom::Envelope env; + }; } // namespace geos::index::chain diff --git a/deps/libgeos/geos/include/geos/index/quadtree/Quadtree.h b/deps/libgeos/geos/include/geos/index/quadtree/Quadtree.h index aa9fcf7f1..9f92f609e 100644 --- a/deps/libgeos/geos/include/geos/index/quadtree/Quadtree.h +++ b/deps/libgeos/geos/include/geos/index/quadtree/Quadtree.h @@ -61,7 +61,7 @@ namespace quadtree { // geos::index::quadtree * intersection, such as testing other kinds of spatial relationships. * * This implementation does not require specifying the extent of the inserted - * items beforehand. It will automatically expand to accomodate any extent + * items beforehand. It will automatically expand to accommodate any extent * of dataset. * * This data structure is also known as an MX-CIF quadtree diff --git a/deps/libgeos/geos/include/geos/index/strtree/TemplateSTRtree.h b/deps/libgeos/geos/include/geos/index/strtree/TemplateSTRtree.h index 32263289b..baae429d0 100644 --- a/deps/libgeos/geos/include/geos/index/strtree/TemplateSTRtree.h +++ b/deps/libgeos/geos/include/geos/index/strtree/TemplateSTRtree.h @@ -203,10 +203,9 @@ class TemplateSTRtreeImpl { return nearestNeighbour(*this, distance); } - /** Determine the two closest items in the tree using distance metric `distance`. */ + /** Determine the two closest items in the tree using distance metric `ItemDistance`. */ template std::pair nearestNeighbour() { - ItemDistance id; return nearestNeighbour(*this); } @@ -222,7 +221,7 @@ class TemplateSTRtreeImpl { return td.nearestNeighbour(*root, *other.root); } - /** Determine the two closest items this tree and `other` tree using distance metric `distance`. */ + /** Determine the two closest items this tree and `other` tree using distance metric `ItemDistance`. */ template std::pair nearestNeighbour(TemplateSTRtreeImpl& other) { ItemDistance id; diff --git a/deps/libgeos/geos/include/geos/io/GeoJSON.h b/deps/libgeos/geos/include/geos/io/GeoJSON.h index 0fd045bdb..8281786bb 100644 --- a/deps/libgeos/geos/include/geos/io/GeoJSON.h +++ b/deps/libgeos/geos/include/geos/io/GeoJSON.h @@ -98,6 +98,13 @@ class GEOS_DLL GeoJSONFeature { GeoJSONFeature(std::unique_ptr g, std::map&& p); + GeoJSONFeature(std::unique_ptr g, + const std::map& p, + const std::string& id); + + GeoJSONFeature(std::unique_ptr g, + std::map&& p, std::string id); + GeoJSONFeature(GeoJSONFeature const& other); GeoJSONFeature(GeoJSONFeature&& other); @@ -110,12 +117,16 @@ class GEOS_DLL GeoJSONFeature { const std::map& getProperties() const; + const std::string& getId() const; + private: std::unique_ptr geometry; std::map properties; + std::string id; + }; class GEOS_DLL GeoJSONFeatureCollection { diff --git a/deps/libgeos/geos/include/geos/io/GeoJSONReader.h b/deps/libgeos/geos/include/geos/io/GeoJSONReader.h index c47555502..41834f1dd 100644 --- a/deps/libgeos/geos/include/geos/io/GeoJSONReader.h +++ b/deps/libgeos/geos/include/geos/io/GeoJSONReader.h @@ -50,7 +50,7 @@ class GEOS_DLL GeoJSONReader { public: /** - * \brief Inizialize parser with given GeometryFactory. + * \brief Initialize parser with given GeometryFactory. * * Note that all Geometry objects created by the * parser will contain a pointer to the given factory @@ -60,7 +60,7 @@ class GEOS_DLL GeoJSONReader { GeoJSONReader(const geom::GeometryFactory& gf); /** - * \brief Inizialize parser with default GeometryFactory. + * \brief Initialize parser with default GeometryFactory. * */ GeoJSONReader(); diff --git a/deps/libgeos/geos/include/geos/io/GeoJSONWriter.h b/deps/libgeos/geos/include/geos/io/GeoJSONWriter.h index 8bb7e663b..002a6f501 100644 --- a/deps/libgeos/geos/include/geos/io/GeoJSONWriter.h +++ b/deps/libgeos/geos/include/geos/io/GeoJSONWriter.h @@ -74,11 +74,33 @@ class GEOS_DLL GeoJSONWriter { std::string write(const GeoJSONFeatureCollection& features); + /* + * \brief + * Returns the output dimension used by the + * GeoJSONWriter. + */ + int + getOutputDimension() const + { + return defaultOutputDimension; + } + + /* + * Sets the output dimension used by the GeoJSONWriter. + * + * @param newOutputDimension Supported values are 2 or 3. + * Default since GEOS 3.12 is 3. + * Note that 3 indicates up to 3 dimensions will be + * written but 2D GeoJSON is still produced for 2D geometries. + */ + void setOutputDimension(uint8_t newOutputDimension); + private: + uint8_t defaultOutputDimension = 3; - std::pair convertCoordinate(const geom::CoordinateXY* c); + std::vector convertCoordinate(const geom::Coordinate* c); - std::vector> convertCoordinateSequence(const geom::CoordinateSequence* c); + std::vector> convertCoordinateSequence(const geom::CoordinateSequence* c); void encode(const geom::Geometry* g, GeoJSONType type, geos_nlohmann::ordered_json& j); diff --git a/deps/libgeos/geos/include/geos/io/WKBConstants.h b/deps/libgeos/geos/include/geos/io/WKBConstants.h index 51d9615f4..438574841 100644 --- a/deps/libgeos/geos/include/geos/io/WKBConstants.h +++ b/deps/libgeos/geos/include/geos/io/WKBConstants.h @@ -37,7 +37,12 @@ namespace WKBConstants { wkbMultiPoint = 4, wkbMultiLineString = 5, wkbMultiPolygon = 6, - wkbGeometryCollection = 7 + wkbGeometryCollection = 7, + wkbCircularString = 8, + wkbCompoundCurve = 9, + wkbCurvePolygon = 10, + wkbMultiCurve = 11, + wkbMultiSurface = 12, }; enum wkbFlavour { diff --git a/deps/libgeos/geos/include/geos/io/WKBReader.h b/deps/libgeos/geos/include/geos/io/WKBReader.h index c050f26ac..9d410c250 100644 --- a/deps/libgeos/geos/include/geos/io/WKBReader.h +++ b/deps/libgeos/geos/include/geos/io/WKBReader.h @@ -20,7 +20,8 @@ #pragma once #include - +#include +#include #include // for composition #include // ostream, istream @@ -28,8 +29,6 @@ // #include #include -#define BAD_GEOM_TYPE_MSG "Bad geometry type encountered in" - #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class @@ -41,17 +40,24 @@ namespace geom { class GeometryFactory; class Coordinate; +class CircularString; +class CompoundCurve; +class CurvePolygon; class Geometry; +enum GeometryTypeId : int; class GeometryCollection; class Point; class LineString; class LinearRing; class Polygon; +class MultiCurve; class MultiPoint; class MultiLineString; class MultiPolygon; +class MultiSurface; class PrecisionModel; class CoordinateSequence; +class SimpleCurve; } // namespace geom } // namespace geos @@ -82,7 +88,7 @@ class GEOS_DLL WKBReader { WKBReader(geom::GeometryFactory const& f); - /// Inizialize parser with default GeometryFactory. + /// Initialize parser with default GeometryFactory. WKBReader(); void setFixStructure(bool doFixStructure); @@ -148,22 +154,42 @@ class GEOS_DLL WKBReader { std::unique_ptr readLinearRing(); + std::unique_ptr readCircularString(); + + std::unique_ptr readCompoundCurve(); + std::unique_ptr readPolygon(); + std::unique_ptr readCurvePolygon(); + std::unique_ptr readMultiPoint(); std::unique_ptr readMultiLineString(); + std::unique_ptr readMultiCurve(); + std::unique_ptr readMultiPolygon(); + std::unique_ptr readMultiSurface(); + std::unique_ptr readGeometryCollection(); std::unique_ptr readCoordinateSequence(unsigned int); // throws IOException - void minMemSize(int geomType, uint64_t size); + void minMemSize(geom::GeometryTypeId geomType, uint64_t size) const; void readCoordinate(); // throws IOException + template + std::unique_ptr readChild() + { + auto g = readGeometry(); + if (dynamic_cast(g.get())) { + return std::unique_ptr(static_cast(g.release())); + } + throw io::ParseException(std::string("Expected ") + geom::GeometryTypeName::name + " but got " + g->getGeometryType()); + } + // Declare type as noncopyable WKBReader(const WKBReader& other) = delete; WKBReader& operator=(const WKBReader& rhs) = delete; diff --git a/deps/libgeos/geos/include/geos/io/WKBWriter.h b/deps/libgeos/geos/include/geos/io/WKBWriter.h index 0910f52f5..90b2fbc67 100644 --- a/deps/libgeos/geos/include/geos/io/WKBWriter.h +++ b/deps/libgeos/geos/include/geos/io/WKBWriter.h @@ -33,6 +33,8 @@ namespace geos { namespace geom { class CoordinateSequence; +class CompoundCurve; +class CurvePolygon; class Geometry; class GeometryCollection; class Point; @@ -43,6 +45,7 @@ class MultiPoint; class MultiLineString; class MultiPolygon; class PrecisionModel; +class SimpleCurve; } // namespace geom } // namespace geos @@ -194,6 +197,8 @@ class GEOS_DLL WKBWriter { void writeHEX(const geom::Geometry& g, std::ostream& os); // throws IOException, ParseException + static int getWkbType(const geom::Geometry&); + private: // 2, 3, or 4 @@ -215,13 +220,17 @@ class GEOS_DLL WKBWriter { void writePointEmpty(const geom::Point& p); // throws IOException - void writeLineString(const geom::LineString& ls); + void writeSimpleCurve(const geom::SimpleCurve& ls); // throws IOException + void writeCompoundCurve(const geom::CompoundCurve& curve); + void writePolygon(const geom::Polygon& p); // throws IOException - void writeGeometryCollection(const geom::GeometryCollection& c, int wkbtype); + void writeCurvePolygon(const geom::CurvePolygon& p); + + void writeGeometryCollection(const geom::GeometryCollection& gc); // throws IOException, ParseException void writeCoordinateSequence(const geom::CoordinateSequence& cs, bool sized); diff --git a/deps/libgeos/geos/include/geos/io/WKTReader.h b/deps/libgeos/geos/include/geos/io/WKTReader.h index 93249bd71..7294d64f5 100644 --- a/deps/libgeos/geos/include/geos/io/WKTReader.h +++ b/deps/libgeos/geos/include/geos/io/WKTReader.h @@ -40,10 +40,15 @@ class GeometryCollection; class Point; class LineString; class LinearRing; +class CircularString; +class CompoundCurve; class Polygon; +class CurvePolygon; class MultiPoint; class MultiLineString; +class MultiCurve; class MultiPolygon; +class MultiSurface; class PrecisionModel; } } @@ -118,7 +123,8 @@ class GEOS_DLL WKTReader { static std::string getNextCloserOrComma(io::StringTokenizer* tokenizer); static std::string getNextCloser(io::StringTokenizer* tokenizer); static std::string getNextWord(io::StringTokenizer* tokenizer); - std::unique_ptr readGeometryTaggedText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readGeometryTaggedText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags, const geom::GeometryTypeId* emptyType = nullptr) const; + std::unique_ptr readPointText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; std::unique_ptr readLineStringText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; std::unique_ptr readLinearRingText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; @@ -127,6 +133,17 @@ class GEOS_DLL WKTReader { std::unique_ptr readMultiLineStringText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; std::unique_ptr readMultiPolygonText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; std::unique_ptr readGeometryCollectionText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readCircularStringText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readCompoundCurveText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readCurvePolygonText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readMultiCurveText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readMultiSurfaceText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + + /// Read the contents of a LINEARRING, LINESTRING, CIRCULARSTRING, or COMPOUNDCURVE + std::unique_ptr readCurveText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + + /// Read the contents of a POLYGON or a CURVEPOLYGON + std::unique_ptr readSurfaceText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; private: const geom::GeometryFactory* geometryFactory; const geom::PrecisionModel* precisionModel; diff --git a/deps/libgeos/geos/include/geos/io/WKTWriter.h b/deps/libgeos/geos/include/geos/io/WKTWriter.h index 09f6184ef..105b63739 100644 --- a/deps/libgeos/geos/include/geos/io/WKTWriter.h +++ b/deps/libgeos/geos/include/geos/io/WKTWriter.h @@ -39,6 +39,8 @@ class Coordinate; class CoordinateXY; class CoordinateXYZM; class CoordinateSequence; +class Curve; +class CompoundCurve; class Geometry; class GeometryCollection; class Point; @@ -49,6 +51,8 @@ class MultiPoint; class MultiLineString; class MultiPolygon; class PrecisionModel; +class SimpleCurve; +class Surface; } namespace io { class Writer; @@ -117,7 +121,7 @@ class GEOS_DLL WKTWriter { * * @return the WKT */ - static std::string toLineString(const geom::Coordinate& p0, const geom::Coordinate& p1); + static std::string toLineString(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1); /** * Generates the WKT for a Point. @@ -195,6 +199,9 @@ class GEOS_DLL WKTWriter { */ void setOutputDimension(uint8_t newOutputDimension); + static std::string writeNumber(double d, bool trim, uint32_t precision); + static int writeTrimmedNumber(double d, uint32_t precision, char* buf); + protected: int decimalPlaces; @@ -205,23 +212,28 @@ class GEOS_DLL WKTWriter { int level, Writer& writer) const; + void appendTag( + const geom::Geometry& geometry, + OrdinateSet outputOrdinates, + Writer& writer) const; + void appendPointTaggedText( const geom::Point& point, OrdinateSet outputOrdinates, int level, Writer& writer) const; - void appendLineStringTaggedText( - const geom::LineString& lineString, + void appendSimpleCurveTaggedText( + const geom::SimpleCurve& lineString, OrdinateSet outputOrdinates, int level, Writer& writer) const; - void appendLinearRingTaggedText( - const geom::LinearRing& lineString, + void appendCompoundCurveTaggedText( + const geom::CompoundCurve& lineString, OrdinateSet outputOrdinates, int level, Writer& writer) const; - void appendPolygonTaggedText( - const geom::Polygon& polygon, + void appendSurfaceTaggedText( + const geom::Surface& polygon, OrdinateSet outputOrdinates, int level, Writer& writer) const; @@ -230,13 +242,13 @@ class GEOS_DLL WKTWriter { OrdinateSet outputOrdinates, int level, Writer& writer) const; - void appendMultiLineStringTaggedText( - const geom::MultiLineString& multiLineString, + void appendMultiCurveTaggedText( + const geom::GeometryCollection& multiCurve, OrdinateSet outputOrdinates, int level, Writer& writer) const; - void appendMultiPolygonTaggedText( - const geom::MultiPolygon& multiPolygon, + void appendMultiSurfaceTaggedText( + const geom::GeometryCollection& multiSurface, OrdinateSet outputOrdinates, int level, Writer& writer) const; @@ -260,13 +272,18 @@ class GEOS_DLL WKTWriter { std::string writeNumber(double d) const; - void appendLineStringText( - const geom::LineString& lineString, + void appendCurveText( + const geom::Curve& lineString, OrdinateSet outputOrdinates, int level, bool doIndent, Writer& writer) const; - void appendPolygonText( - const geom::Polygon& polygon, + void appendSimpleCurveText( + const geom::SimpleCurve& lineString, + OrdinateSet outputOrdinates, + int level, bool doIndent, Writer& writer) const; + + void appendSurfaceText( + const geom::Surface& polygon, OrdinateSet outputOrdinates, int level, bool indentFirst, Writer& writer) const; @@ -275,13 +292,13 @@ class GEOS_DLL WKTWriter { OrdinateSet outputOrdinates, int level, Writer& writer) const; - void appendMultiLineStringText( - const geom::MultiLineString& multiLineString, + void appendMultiCurveText( + const geom::GeometryCollection& multiCurve, OrdinateSet outputOrdinates, int level, bool indentFirst, Writer& writer) const; - void appendMultiPolygonText( - const geom::MultiPolygon& multiPolygon, + void appendMultiSurfaceText( + const geom::GeometryCollection& multiSurface, OrdinateSet outputOrdinates, int level, Writer& writer) const; @@ -296,8 +313,6 @@ class GEOS_DLL WKTWriter { INDENT = 2 }; -// static const int INDENT = 2; - bool isFormatted; int roundingPrecision; @@ -306,8 +321,6 @@ class GEOS_DLL WKTWriter { bool removeEmptyDimensions = false; - int level; - static constexpr int coordsPerLine = 10; uint8_t defaultOutputDimension; diff --git a/deps/libgeos/geos/include/geos/linearref/LengthIndexedLine.h b/deps/libgeos/geos/include/geos/linearref/LengthIndexedLine.h index 462957153..d5c823768 100644 --- a/deps/libgeos/geos/include/geos/linearref/LengthIndexedLine.h +++ b/deps/libgeos/geos/include/geos/linearref/LengthIndexedLine.h @@ -157,7 +157,7 @@ class GEOS_DLL LengthIndexedLine { * * (The subline must **conform** to the line; that is, * all vertices in the subline (except possibly the first and last) - * must be vertices of the line and occcur in the same order). + * must be vertices of the line and occur in the same order). * * @param subLine a subLine of the line * @return a pair of indices for the start and end of the subline. diff --git a/deps/libgeos/geos/include/geos/math/DD.h b/deps/libgeos/geos/include/geos/math/DD.h index 9f2ea979c..61e3a08a7 100644 --- a/deps/libgeos/geos/include/geos/math/DD.h +++ b/deps/libgeos/geos/include/geos/math/DD.h @@ -93,6 +93,7 @@ #pragma once #include +#include namespace geos { namespace math { // geos.math diff --git a/deps/libgeos/geos/include/geos/namespaces.h b/deps/libgeos/geos/include/geos/namespaces.h index dd71a2105..61132b857 100644 --- a/deps/libgeos/geos/include/geos/namespaces.h +++ b/deps/libgeos/geos/include/geos/namespaces.h @@ -143,7 +143,7 @@ namespace index { // geos.index /// formats. /// /// The Java Topology Suite (JTS) is a Java API that implements a core set of -/// spatial data operations usin g an explicit precision model and robust +/// spatial data operations using an explicit precision model and robust /// geometric algorithms. JTS is intended to be used in the devel opment of /// applications that support the validation, cleaning, integration and /// querying of spatial data sets. diff --git a/deps/libgeos/geos/include/geos/noding/MCIndexSegmentSetMutualIntersector.h b/deps/libgeos/geos/include/geos/noding/MCIndexSegmentSetMutualIntersector.h index abc687919..770784bb8 100644 --- a/deps/libgeos/geos/include/geos/noding/MCIndexSegmentSetMutualIntersector.h +++ b/deps/libgeos/geos/include/geos/noding/MCIndexSegmentSetMutualIntersector.h @@ -20,12 +20,15 @@ #include // inherited namespace geos { +namespace geom { + class Envelope; +} namespace index { -class SpatialIndex; + class SpatialIndex; } namespace noding { -class SegmentString; -class SegmentIntersector; + class SegmentString; + class SegmentIntersector; } } @@ -41,7 +44,7 @@ namespace noding { // geos::noding * * @version 1.7 */ -class MCIndexSegmentSetMutualIntersector : public SegmentSetMutualIntersector { +class GEOS_DLL MCIndexSegmentSetMutualIntersector : public SegmentSetMutualIntersector { public: MCIndexSegmentSetMutualIntersector(double p_tolerance) @@ -51,6 +54,17 @@ class MCIndexSegmentSetMutualIntersector : public SegmentSetMutualIntersector { , nOverlaps(0) , overlapTolerance(p_tolerance) , indexBuilt(false) + , envelope(nullptr) + {} + + MCIndexSegmentSetMutualIntersector(const geom::Envelope* p_envelope) + : monoChains() + , indexCounter(0) + , processCounter(0) + , nOverlaps(0) + , overlapTolerance(0.0) + , indexBuilt(false) + , envelope(p_envelope) {} MCIndexSegmentSetMutualIntersector() @@ -117,6 +131,7 @@ class MCIndexSegmentSetMutualIntersector : public SegmentSetMutualIntersector { */ bool indexBuilt; MonoChains indexChains; + const geom::Envelope* envelope; void addToIndex(SegmentString* segStr); diff --git a/deps/libgeos/geos/include/geos/noding/Octant.h b/deps/libgeos/geos/include/geos/noding/Octant.h index 2202131ed..7b96c0af1 100644 --- a/deps/libgeos/geos/include/geos/noding/Octant.h +++ b/deps/libgeos/geos/include/geos/noding/Octant.h @@ -46,7 +46,7 @@ namespace noding { // geos.noding */ class GEOS_DLL Octant { private: - Octant() {} // Can't instanciate it + Octant() {} // Can't instantiate it public: /** diff --git a/deps/libgeos/geos/include/geos/noding/SegmentSetMutualIntersector.h b/deps/libgeos/geos/include/geos/noding/SegmentSetMutualIntersector.h index f5fda95a9..cdd99b21e 100644 --- a/deps/libgeos/geos/include/geos/noding/SegmentSetMutualIntersector.h +++ b/deps/libgeos/geos/include/geos/noding/SegmentSetMutualIntersector.h @@ -33,7 +33,7 @@ namespace noding { // geos::noding * @author Martin Davis * @version 1.10 */ -class SegmentSetMutualIntersector { +class GEOS_DLL SegmentSetMutualIntersector { public: SegmentSetMutualIntersector() diff --git a/deps/libgeos/geos/include/geos/noding/SegmentString.h b/deps/libgeos/geos/include/geos/noding/SegmentString.h index 3d71f009a..af7cee2de 100644 --- a/deps/libgeos/geos/include/geos/noding/SegmentString.h +++ b/deps/libgeos/geos/include/geos/noding/SegmentString.h @@ -131,6 +131,42 @@ class GEOS_DLL SegmentString { return ss.getSegmentOctant(index); } + /** + * Gets the next vertex in a ring from a vertex index. + * + * @param index the vertex index + * @return the next vertex in the ring + * + * @see isClosed + */ + const geom::CoordinateXY& nextInRing(std::size_t index) const + { + std::size_t nextIndex = index + 1; + if (nextIndex > size() - 1) { + nextIndex = 1; + } + return getCoordinate(nextIndex); + } + + /** + * Gets the previous vertex in a ring from a vertex index. + * + * @param index the vertex index + * @return the previous vertex in the ring + * + * @see isClosed + */ + const geom::CoordinateXY& prevInRing(std::size_t index) const + { + std::size_t prevIndex; + if (index == 0) + prevIndex = size() - 2; + else + prevIndex = index - 1; + return getCoordinate( prevIndex ); + } + + bool isClosed() const { return seq->front().equals(seq->back()); } diff --git a/deps/libgeos/geos/include/geos/operation/GeometryGraphOperation.h b/deps/libgeos/geos/include/geos/operation/GeometryGraphOperation.h index a088bccdf..0886e65b3 100644 --- a/deps/libgeos/geos/include/geos/operation/GeometryGraphOperation.h +++ b/deps/libgeos/geos/include/geos/operation/GeometryGraphOperation.h @@ -74,9 +74,13 @@ class GEOS_DLL GeometryGraphOperation { /** \brief * The operation args into an array so they can be accessed by index */ - std::vector arg; + std::vector> arg; void setComputationPrecision(const geom::PrecisionModel* pm); + + // Declare type as noncopyable + GeometryGraphOperation(const GeometryGraphOperation& other) = delete; + GeometryGraphOperation& operator=(const GeometryGraphOperation& rhs) = delete; }; } // namespace geos.operation diff --git a/deps/libgeos/geos/include/geos/operation/buffer/BufferCurveSetBuilder.h b/deps/libgeos/geos/include/geos/operation/buffer/BufferCurveSetBuilder.h index 2c0ddc061..e731840bf 100644 --- a/deps/libgeos/geos/include/geos/operation/buffer/BufferCurveSetBuilder.h +++ b/deps/libgeos/geos/include/geos/operation/buffer/BufferCurveSetBuilder.h @@ -169,14 +169,19 @@ class GEOS_DLL BufferCurveSetBuilder { const geom::CoordinateSequence* curvePts); /** - * Computes the maximum distance out of a set of points to a linestring. - * - * @param pts the points - * @param line the linestring vertices - * @return the maximum distance + * Tests if there are points on the raw offset curve which may + * lie on the final buffer curve + * (i.e. they are (approximately) at the buffer distance from the input ring). + * For efficiency this only tests a limited set of points on the curve. + * + * @param inputRing + * @param distance + * @param curveRing + * @return true if the curve contains points lying at the required buffer distance */ - static double maxDistance( - const geom::CoordinateSequence* pts, const geom::CoordinateSequence* line); + static bool hasPointOnBuffer( + const CoordinateSequence* inputRing, double dist, + const CoordinateSequence* curveRing); /** * The ringCoord is assumed to contain no repeated points. diff --git a/deps/libgeos/geos/include/geos/operation/buffer/BufferOp.h b/deps/libgeos/geos/include/geos/operation/buffer/BufferOp.h index 9d2b18a1b..6339ca895 100644 --- a/deps/libgeos/geos/include/geos/operation/buffer/BufferOp.h +++ b/deps/libgeos/geos/include/geos/operation/buffer/BufferOp.h @@ -25,6 +25,7 @@ #include #include // for enum values +#include // to materialize Geometry #include // for composition diff --git a/deps/libgeos/geos/include/geos/operation/buffer/OffsetCurveSection.h b/deps/libgeos/geos/include/geos/operation/buffer/OffsetCurveSection.h index fe8d9035e..e73d759ce 100644 --- a/deps/libgeos/geos/include/geos/operation/buffer/OffsetCurveSection.h +++ b/deps/libgeos/geos/include/geos/operation/buffer/OffsetCurveSection.h @@ -16,6 +16,7 @@ #pragma once #include +#include // to materialize CoordinateSequence #include #include diff --git a/deps/libgeos/geos/include/geos/operation/distance/ConnectedElementLocationFilter.h b/deps/libgeos/geos/include/geos/operation/distance/ConnectedElementLocationFilter.h index 0c6ab18e2..82e4261ea 100644 --- a/deps/libgeos/geos/include/geos/operation/distance/ConnectedElementLocationFilter.h +++ b/deps/libgeos/geos/include/geos/operation/distance/ConnectedElementLocationFilter.h @@ -50,7 +50,7 @@ namespace distance { // geos::operation::distance class GEOS_DLL ConnectedElementLocationFilter: public geom::GeometryFilter { private: - std::vector> locations; + std::vector locations; ConnectedElementLocationFilter() = default; ConnectedElementLocationFilter(const ConnectedElementLocationFilter&) = delete; ConnectedElementLocationFilter& operator=(const ConnectedElementLocationFilter&) = delete; @@ -63,7 +63,7 @@ class GEOS_DLL ConnectedElementLocationFilter: public geom::GeometryFilter { * an empty list will be returned. The elements of the list * are [GeometryLocations](@ref operation::distance::GeometryLocation). */ - static std::vector> getLocations(const geom::Geometry* geom); + static std::vector getLocations(const geom::Geometry* geom); void filter_ro(const geom::Geometry* geom) override; void filter_rw(geom::Geometry* geom) override; diff --git a/deps/libgeos/geos/include/geos/operation/distance/DistanceOp.h b/deps/libgeos/geos/include/geos/operation/distance/DistanceOp.h index da13ab130..b4d6fed8b 100644 --- a/deps/libgeos/geos/include/geos/operation/distance/DistanceOp.h +++ b/deps/libgeos/geos/include/geos/operation/distance/DistanceOp.h @@ -172,19 +172,19 @@ class GEOS_DLL DistanceOp { // working algorithm::PointLocator ptLocator; - std::array, 2> minDistanceLocation; + std::array minDistanceLocation; double minDistance; bool computed = false; - void updateMinDistance(std::array, 2> & locGeom, bool flip); + void updateMinDistance(std::array & locGeom, bool flip); void computeMinDistance(); void computeContainmentDistance(); - void computeInside(std::vector> & locs, + void computeInside(std::vector & locs, const std::vector& polys, - std::array, 2> & locPtPoly); + std::array & locPtPoly); /** @@ -196,25 +196,25 @@ class GEOS_DLL DistanceOp { void computeMinDistanceLines( const std::vector& lines0, const std::vector& lines1, - std::array, 2> & locGeom); + std::array & locGeom); void computeMinDistancePoints( const std::vector& points0, const std::vector& points1, - std::array, 2> & locGeom); + std::array & locGeom); void computeMinDistanceLinesPoints( const std::vector& lines0, const std::vector& points1, - std::array, 2> & locGeom); + std::array & locGeom); void computeMinDistance(const geom::LineString* line0, const geom::LineString* line1, - std::array, 2> & locGeom); + std::array & locGeom); void computeMinDistance(const geom::LineString* line, const geom::Point* pt, - std::array, 2> & locGeom); + std::array & locGeom); }; diff --git a/deps/libgeos/geos/include/geos/operation/distance/GeometryLocation.h b/deps/libgeos/geos/include/geos/operation/distance/GeometryLocation.h index 764c6e7b0..1fb443762 100644 --- a/deps/libgeos/geos/include/geos/operation/distance/GeometryLocation.h +++ b/deps/libgeos/geos/include/geos/operation/distance/GeometryLocation.h @@ -62,6 +62,13 @@ class GEOS_DLL GeometryLocation { */ static const int INSIDE_AREA = -1; + GeometryLocation() : + component(nullptr), + segIndex(0), + inside_area(false), + pt() + {} + /** \brief * Constructs a GeometryLocation specifying a point on a geometry, * as well as the segment that the point is on (or INSIDE_AREA if diff --git a/deps/libgeos/geos/include/geos/operation/overlayng/EdgeNodingBuilder.h b/deps/libgeos/geos/include/geos/operation/overlayng/EdgeNodingBuilder.h index c04124e3d..aec4d7b41 100644 --- a/deps/libgeos/geos/include/geos/operation/overlayng/EdgeNodingBuilder.h +++ b/deps/libgeos/geos/include/geos/operation/overlayng/EdgeNodingBuilder.h @@ -110,7 +110,7 @@ class GEOS_DLL EdgeNodingBuilder { * This is one of: * * - Fixed precision: a snap-rounding noder (which should be fully robust) - * - Floating precision: a conventional nodel (which may be non-robust). + * - Floating precision: a conventional model (which may be non-robust). * In this case, a validation step is applied to the output from the noder. */ Noder* getNoder(); diff --git a/deps/libgeos/geos/include/geos/operation/polygonize/BuildArea.h b/deps/libgeos/geos/include/geos/operation/polygonize/BuildArea.h index 0dd7d6486..679698a27 100644 --- a/deps/libgeos/geos/include/geos/operation/polygonize/BuildArea.h +++ b/deps/libgeos/geos/include/geos/operation/polygonize/BuildArea.h @@ -62,7 +62,7 @@ class GEOS_DLL BuildArea { ~BuildArea() = default; - /** \brief Return the area built fromthe constituent linework of the input geometry. */ + /** \brief Return the area built from the constituent linework of the input geometry. */ std::unique_ptr build(const geom::Geometry* geom); }; diff --git a/deps/libgeos/geos/include/geos/operation/polygonize/PolygonizeGraph.h b/deps/libgeos/geos/include/geos/operation/polygonize/PolygonizeGraph.h index b7b7af500..694f8fe02 100644 --- a/deps/libgeos/geos/include/geos/operation/polygonize/PolygonizeGraph.h +++ b/deps/libgeos/geos/include/geos/operation/polygonize/PolygonizeGraph.h @@ -196,7 +196,7 @@ class GEOS_DLL PolygonizeGraph: public planargraph::PlanarGraph { EdgeRing* findEdgeRing(PolygonizeDirectedEdge* startDE); - /* Tese are for memory management */ + /* These are for memory management */ std::vector newEdges; std::vector newDirEdges; std::vector newNodes; diff --git a/deps/libgeos/geos/include/geos/operation/polygonize/Polygonizer.h b/deps/libgeos/geos/include/geos/operation/polygonize/Polygonizer.h index 50fce1f7c..1c21a3a34 100644 --- a/deps/libgeos/geos/include/geos/operation/polygonize/Polygonizer.h +++ b/deps/libgeos/geos/include/geos/operation/polygonize/Polygonizer.h @@ -192,16 +192,6 @@ class GEOS_DLL Polygonizer { */ void add(std::vector* geomList); - /** - * Add a geometry to the linework to be polygonized. - * May be called multiple times. - * Any dimension of Geometry may be added; - * the constituent linework will be extracted and used - * - * @param g a Geometry with linework to be polygonized - */ - void add(geom::Geometry* g); - /** * Add a geometry to the linework to be polygonized. * May be called multiple times. diff --git a/deps/libgeos/geos/include/geos/operation/relate/EdgeEndBundle.h b/deps/libgeos/geos/include/geos/operation/relate/EdgeEndBundle.h index 9098ae25b..291d49e22 100644 --- a/deps/libgeos/geos/include/geos/operation/relate/EdgeEndBundle.h +++ b/deps/libgeos/geos/include/geos/operation/relate/EdgeEndBundle.h @@ -81,7 +81,7 @@ class GEOS_DLL EdgeEndBundle: public geomgraph::EdgeEnd { * on the boundary and in the interior (e.g. a LineString segment * lying on * top of a Polygon edge.) In this case the Boundary is - * given precendence. + * given precedence. * * These observations result in the following rules for computing * the ON location: diff --git a/deps/libgeos/geos/include/geos/operation/relate/RelateComputer.h b/deps/libgeos/geos/include/geos/operation/relate/RelateComputer.h index a232534d2..6a9d9288d 100644 --- a/deps/libgeos/geos/include/geos/operation/relate/RelateComputer.h +++ b/deps/libgeos/geos/include/geos/operation/relate/RelateComputer.h @@ -76,7 +76,7 @@ namespace relate { // geos::operation::relate */ class GEOS_DLL RelateComputer { public: - RelateComputer(std::vector* newArg); + RelateComputer(std::vector>& newArg); ~RelateComputer() = default; std::unique_ptr computeIM(); @@ -87,7 +87,7 @@ class GEOS_DLL RelateComputer { algorithm::PointLocator ptLocator; /// the arg(s) of the operation - std::vector* arg; + const std::vector>& arg; geomgraph::NodeMap nodes; diff --git a/deps/libgeos/geos/include/geos/operation/relate/RelateNode.h b/deps/libgeos/geos/include/geos/operation/relate/RelateNode.h index 5f7727dd1..047533d3f 100644 --- a/deps/libgeos/geos/include/geos/operation/relate/RelateNode.h +++ b/deps/libgeos/geos/include/geos/operation/relate/RelateNode.h @@ -42,7 +42,7 @@ namespace relate { // geos::operation::relate * Represents a node in the topological graph used to compute spatial * relationships. */ -class GEOS_DLL RelateNode: public geomgraph::Node { +class GEOS_DLL RelateNode final: public geomgraph::Node { public: diff --git a/deps/libgeos/geos/include/geos/operation/relateng/AdjacentEdgeLocator.h b/deps/libgeos/geos/include/geos/operation/relateng/AdjacentEdgeLocator.h new file mode 100644 index 000000000..d6e576985 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/AdjacentEdgeLocator.h @@ -0,0 +1,124 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { + class NodeSections; + class NodeSection; +} +} +namespace geom { + class CoordinateXY; + class Geometry; + class LinearRing; + class Polygon; +} +} + + +using geos::geom::CoordinateXY; +using geos::geom::CoordinateSequence; +using geos::geom::Geometry; +using geos::geom::LinearRing; +using geos::geom::Polygon; +using geos::geom::Location; + + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +/** + * Determines the location for a point which is known to lie + * on at least one edge of a set of polygons. + * This provides the union-semantics for determining + * point location in a GeometryCollection, which may + * have polygons with adjacent edges which are effectively + * in the interior of the geometry. + * Note that it is also possible to have adjacent edges which + * lie on the boundary of the geometry + * (e.g. a polygon contained within another polygon with adjacent edges). + * + * @author mdavis + * + */ +class GEOS_DLL AdjacentEdgeLocator { + +public: + + AdjacentEdgeLocator(const Geometry* geom) + { + init(geom); + } + + Location locate(const CoordinateXY* p); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + AdjacentEdgeLocator(const AdjacentEdgeLocator&) = delete; + AdjacentEdgeLocator& operator=(const AdjacentEdgeLocator&) = delete; + + +private: + + // Members + + std::vector ringList; + + /* + * When we have to reorient rings, we end up allocating new + * rings, since we cannot reorient the rings of the input + * geometry, so this is where we store those "local" rings. + */ + std::vector> localRingList; + + + // Methods + + void addSections( + const CoordinateXY* p, + const CoordinateSequence* ring, + NodeSections& sections); + + NodeSection* createSection( + const CoordinateXY* p, + const CoordinateXY* prev, + const CoordinateXY* next); + + void init(const Geometry* geom); + + void addRings(const Geometry* geom); + + void addRing(const LinearRing* ring, bool requireCW); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/BasicPredicate.h b/deps/libgeos/geos/include/geos/operation/relateng/BasicPredicate.h new file mode 100644 index 000000000..2ed3f0539 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/BasicPredicate.h @@ -0,0 +1,105 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + + +using geos::geom::Envelope; +using geos::geom::Location; + + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL BasicPredicate : public TopologyPredicate { + +private: + + static constexpr int UNKNOWN = -1; + static constexpr int FALSE = 0; + static constexpr int TRUE = 1; + + int m_value = UNKNOWN; + + static bool isKnown(int val); + + static bool toBoolean(int val); + + static int toValue(bool val); + + +protected: + + /** + * Updates the predicate value to the given state + * if it is currently unknown. + * + * @param val the predicate value to update + */ + void setValue(bool val); + + void setValue(int val); + + void setValueIf(bool val, bool cond); + + void require(bool cond); + + using TopologyPredicate::requireCovers; + void requireCovers(const Envelope& a, const Envelope& b); + + +public: + + /** + * Tests if two geometries intersect + * based on an interaction at given locations. + * + * @param locA the location on geometry A + * @param locB the location on geometry B + * @return true if the geometries intersect + */ + static bool isIntersection(Location locA, Location locB); + + std::string name() const override = 0; + + void finish() override = 0; + + bool isKnown() const override; + + bool value() const override; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/DimensionLocation.h b/deps/libgeos/geos/include/geos/operation/relateng/DimensionLocation.h new file mode 100644 index 000000000..6d3d87050 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/DimensionLocation.h @@ -0,0 +1,60 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + + +using geos::geom::Location; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL DimensionLocation { + +public: + + enum DimensionLocationType { + EXTERIOR = 2, // == Location.EXTERIOR + POINT_INTERIOR = 103, + LINE_INTERIOR = 110, + LINE_BOUNDARY = 111, + AREA_INTERIOR = 120, + AREA_BOUNDARY = 121 + }; + + static int locationArea(Location loc); + + static int locationLine(Location loc); + + static int locationPoint(Location loc); + + static Location location(int dimLoc); + + static int dimension(int dimLoc); + + static int dimension(int dimLoc, int exteriorDim); + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/EdgeSegmentIntersector.h b/deps/libgeos/geos/include/geos/operation/relateng/EdgeSegmentIntersector.h new file mode 100644 index 000000000..1b05b6f13 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/EdgeSegmentIntersector.h @@ -0,0 +1,80 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace noding { + class SegmentString; +} +namespace operation { +namespace relateng { + class RelateSegmentString; + class TopologyComputer; +} +} +} + + +using geos::noding::SegmentIntersector; +using geos::noding::SegmentString; +using geos::algorithm::LineIntersector; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +class GEOS_DLL EdgeSegmentIntersector : public SegmentIntersector { + +private: + + // Members + LineIntersector li; + TopologyComputer& topoComputer; + + // Methods + + + void addIntersections( + RelateSegmentString* ssA, std::size_t segIndexA, + RelateSegmentString* ssB, std::size_t segIndexB); + + +public: + + EdgeSegmentIntersector(TopologyComputer& p_topoComputer) + : topoComputer(p_topoComputer) + {}; + + void processIntersections( + SegmentString* ss0, std::size_t segIndex0, + SegmentString* ss1, std::size_t segIndex1) override; + + bool isDone() const override; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/EdgeSegmentOverlapAction.h b/deps/libgeos/geos/include/geos/operation/relateng/EdgeSegmentOverlapAction.h new file mode 100644 index 000000000..7f84ec797 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/EdgeSegmentOverlapAction.h @@ -0,0 +1,75 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/chain/MonotoneChainOverlapAction.java rev. 1.6 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace index { +namespace chain { + class MonotoneChain; +} +} +namespace noding { + class SegmentIntersector; +} +} + + +using geos::index::chain::MonotoneChain; +using geos::index::chain::MonotoneChainOverlapAction; +using geos::noding::SegmentIntersector; + + +namespace geos { +namespace operation { // geos::operation +namespace relateng { // geos::operation::relateng + +/** \brief + * The action for the internal iterator for performing + * overlap queries on a MonotoneChain. + */ +class GEOS_DLL EdgeSegmentOverlapAction : public MonotoneChainOverlapAction { + +private: + + SegmentIntersector& si; + + +public: + + EdgeSegmentOverlapAction(SegmentIntersector& p_si) + : si(p_si) + {} + + void overlap( + const MonotoneChain& mc1, std::size_t start1, + const MonotoneChain& mc2, std::size_t start2) override; + + +}; + +} // namespace geos::index::chain +} // namespace geos::index +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/EdgeSetIntersector.h b/deps/libgeos/geos/include/geos/operation/relateng/EdgeSetIntersector.h new file mode 100644 index 000000000..35028b5bd --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/EdgeSetIntersector.h @@ -0,0 +1,94 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { + class Geometry; + class Envelope; +} +namespace noding { + class SegmentString; +} +namespace operation { +namespace relateng { + class RelateSegmentString; + class EdgeSegmentIntersector; +} +} +} + + +using geos::geom::Envelope; +using geos::geom::Geometry; +using geos::index::strtree::TemplateSTRtree; +using geos::index::chain::MonotoneChain; +using geos::operation::relateng::EdgeSegmentIntersector; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +class GEOS_DLL EdgeSetIntersector { + +private: + + // Members + TemplateSTRtree index; + // HPRtree index = new HPRtree(); + const Envelope* envelope = nullptr; + std::deque monoChains; + std::size_t overlapCounter = 0; + + + // Methods + + void addToIndex(const SegmentString* segStr); + + void addEdges(std::vector& segStrings); + + +public: + + EdgeSetIntersector( + std::vector& edgesA, + std::vector& edgesB, + const Envelope* env) + : envelope(env) + { + addEdges(edgesA); + addEdges(edgesB); + // build index to ensure thread-safety + // index.build(); + }; + + void process(EdgeSegmentIntersector& intersector); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/IMPatternMatcher.h b/deps/libgeos/geos/include/geos/operation/relateng/IMPatternMatcher.h new file mode 100644 index 000000000..be8486c57 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/IMPatternMatcher.h @@ -0,0 +1,87 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + + +using geos::geom::Envelope; +using geos::geom::Location; +using geos::geom::Dimension; +using geos::geom::IntersectionMatrix; + + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL IMPatternMatcher : public IMPredicate { + + +private: + + std::string imPattern; + IntersectionMatrix patternMatrix; + + static bool requireInteraction(const IntersectionMatrix& im); + + static bool isInteraction(int imDim); + + +public: + + IMPatternMatcher(std::string p_imPattern) + : imPattern(p_imPattern) + , patternMatrix(p_imPattern) + {}; + + std::string name() const override; + + using IMPredicate::init; + void init(const Envelope& envA, const Envelope& envB) override; + + bool requireInteraction() const override; + + bool isDetermined() const override; + + bool valueIM() override; + + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& os, const IMPatternMatcher& imp); + +}; + + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/IMPredicate.h b/deps/libgeos/geos/include/geos/operation/relateng/IMPredicate.h new file mode 100644 index 000000000..6cf9f9b10 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/IMPredicate.h @@ -0,0 +1,131 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + + +using geos::geom::Envelope; +using geos::geom::Location; +using geos::geom::Dimension; +using geos::geom::IntersectionMatrix; + + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL IMPredicate : public BasicPredicate { + +private: + + + +protected: + + static constexpr int DIM_UNKNOWN = Dimension::DONTCARE; + + int dimA; + int dimB; + IntersectionMatrix intMatrix; + + /** + * Gets the value of the predicate according to the current + * intersection matrix state. + * + * @return the current predicate value + */ + virtual bool valueIM() = 0; + + /** + * Tests whether predicate evaluation can be short-circuited + * due to the current state of the matrix providing + * enough information to determine the predicate value. + * + * If this value is true then valueIM() + * must provide the correct result of the predicate. + * + * @return true if the predicate value is determined + */ + virtual bool isDetermined() const = 0; + + /** + * Tests whether the exterior of the specified input geometry + * is intersected by any part of the other input. + * + * @param isA the input geometry + * @return true if the input geometry exterior is intersected + */ + bool intersectsExteriorOf(bool isA) const; + + bool isIntersects(Location locA, Location locB) const; + + +public: + + IMPredicate() + { + // intMatrix = new IntersectionMatrix(); + //-- E/E is always dim = 2 + intMatrix.set(Location::EXTERIOR, Location::EXTERIOR, Dimension::A); + } + + static bool isDimsCompatibleWithCovers(int dim0, int dim1); + + void init(int dA, int dB) override; + + void updateDimension(Location locA, Location locB, int dimension) override; + + bool isDimChanged(Location locA, Location locB, int dimension) const; + + using TopologyPredicate::isKnown; + bool isKnown(Location locA, Location locB) const; + + bool isDimension(Location locA, Location locB, int dimension) const; + + int getDimension(Location locA, Location locB) const; + + /** + * Sets the final value based on the state of the IM. + */ + void finish() override; + + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& os, const IMPredicate& imp); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/IntersectionMatrixPattern.h b/deps/libgeos/geos/include/geos/operation/relateng/IntersectionMatrixPattern.h new file mode 100644 index 000000000..9e7f2f504 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/IntersectionMatrixPattern.h @@ -0,0 +1,65 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL IntersectionMatrixPattern { + +private: + + /** + * Cannot be instantiated. + */ + IntersectionMatrixPattern() {}; + + +public: + + /** + * A DE-9IM pattern to detect whether two polygonal geometries are adjacent along + * an edge, but do not overlap. + */ + static constexpr const char* ADJACENT = "F***1****"; + + /** + * A DE-9IM pattern to detect a geometry which properly contains another + * geometry (i.e. which lies entirely in the interior of the first geometry). + */ + static constexpr const char* CONTAINS_PROPERLY = "T**FF*FF*"; + + /** + * A DE-9IM pattern to detect if two geometries intersect in their interiors. + * This can be used to determine if a polygonal coverage contains any overlaps + * (although not whether they are correctly noded). + */ + static constexpr const char* INTERIOR_INTERSECTS = "T********"; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/LineStringExtracter.h b/deps/libgeos/geos/include/geos/operation/relateng/LineStringExtracter.h new file mode 100644 index 000000000..fc7298f88 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/LineStringExtracter.h @@ -0,0 +1,77 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class LineString; + class Geometry; +} +} + + +using geos::geom::LineString; +using geos::geom::Geometry; +using geos::geom::GeometryFilter; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL LineStringExtracter : public GeometryFilter { + +private: + + std::vector& comps; + + +public: + + LineStringExtracter(std::vector& p_comps) + : comps(p_comps) + {} + + void filter_ro(const geom::Geometry* geom) override; + + static void getLines(const Geometry* geom, std::vector& lines); + + static std::vector getLines(const Geometry* geom); + + /** + * Extracts the {@link LineString} elements from a single {@link Geometry} + * and returns them as either a {@link LineString} or {@link MultiLineString}. + * + * @param geom the geometry from which to extract + * @return a linear geometry + */ + // static std::unique_ptr getGeometry(const Geometry* geom); + +}; + + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/LinearBoundary.h b/deps/libgeos/geos/include/geos/operation/relateng/LinearBoundary.h new file mode 100644 index 000000000..18032663b --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/LinearBoundary.h @@ -0,0 +1,88 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace algorithm { + class BoundaryNodeRule; +} +namespace geom { + class CoordinateXY; + class LineString; +} +} + + +using geos::algorithm::BoundaryNodeRule; +using geos::geom::Coordinate; +using geos::geom::CoordinateXY; +using geos::geom::LineString; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +class GEOS_DLL LinearBoundary { + +private: + + // Members + + Coordinate::ConstIntMap m_vertexDegree; + bool m_hasBoundary; + const BoundaryNodeRule& m_boundaryNodeRule; + + +public: + + // Constructors + + LinearBoundary(std::vector& lines, const BoundaryNodeRule& bnRule); + + bool hasBoundary() const; + + bool isBoundary(const CoordinateXY* pt) const; + + +private: + + // Methods + + bool checkBoundary(Coordinate::ConstIntMap& vertexDegree) const; + + static void computeBoundaryPoints( + std::vector& lines, + Coordinate::ConstIntMap& vertexDegree); + + static void addEndpoint( + const CoordinateXY *p, + Coordinate::ConstIntMap& vertexDegree); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/NodeSection.h b/deps/libgeos/geos/include/geos/operation/relateng/NodeSection.h new file mode 100644 index 000000000..8fe00870e --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/NodeSection.h @@ -0,0 +1,166 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Geometry; +} +} + + +using geos::geom::CoordinateXY; +using geos::geom::Geometry; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +/** + * Represents a computed node along with the incident edges on either side of + * it (if they exist). + * This captures the information about a node in a geometry component + * required to determine the component's contribution to the node topology. + * A node in an area geometry always has edges on both sides of the node. + * A node in a linear geometry may have one or other incident edge missing, if + * the node occurs at an endpoint of the line. + * The edges of an area node are assumed to be provided + * with CW-shell orientation (as per JTS norm). + * This must be enforced by the caller. + * + * @author Martin Davis + * + */ +class GEOS_DLL NodeSection { + +private: + + // Members + bool m_isA; + int m_dim; + int m_id; + int m_ringId; + const Geometry* m_poly; + bool m_isNodeAtVertex; + const CoordinateXY* m_v0; + const CoordinateXY m_nodePt; + const CoordinateXY* m_v1; + + // Methods + + static int compareWithNull(const CoordinateXY* v0, const CoordinateXY* v1); + + static int compare(int a, int b); + +public: + + NodeSection( + bool isA, + int dim, + int id, + int ringId, + const Geometry* poly, + bool isNodeAtVertex, + const CoordinateXY* v0, + const CoordinateXY nodePt, + const CoordinateXY* v1) + : m_isA(isA) + , m_dim(dim) + , m_id(id) + , m_ringId(ringId) + , m_poly(poly) + , m_isNodeAtVertex(isNodeAtVertex) + , m_v0(v0) + , m_nodePt(nodePt) + , m_v1(v1) + {}; + + NodeSection(const NodeSection* ns) + : m_isA(ns->isA()) + , m_dim(ns->dimension()) + , m_id(ns->id()) + , m_ringId(ns->ringId()) + , m_poly(ns->getPolygonal()) + , m_isNodeAtVertex(ns->isNodeAtVertex()) + , m_v0(ns->getVertex(0)) + , m_nodePt(ns->nodePt()) + , m_v1(ns->getVertex(1)) + {}; + + const CoordinateXY* getVertex(int i) const; + + const CoordinateXY& nodePt() const; + + int dimension() const; + + int id() const; + + int ringId() const; + + /** + * Gets the polygon this section is part of. + * Will be null if section is not on a polygon boundary. + * + * @return the associated polygon, or null + */ + const Geometry* getPolygonal() const; + + bool isShell() const; + + bool isArea() const; + + static bool isAreaArea(const NodeSection& a, const NodeSection& b); + + bool isA() const; + + bool isSameGeometry(const NodeSection& ns) const; + + bool isSamePolygon(const NodeSection& ns) const; + + bool isNodeAtVertex() const; + + bool isProper() const; + + static bool isProper(const NodeSection& a, const NodeSection& b); + + std::string toString() const; + + static std::string edgeRep(const CoordinateXY* p0, const CoordinateXY* p1); + + friend std::ostream& operator<<(std::ostream& os, const NodeSection& ns); + + /** + * Compare node sections by parent geometry, dimension, element id and ring id, + * and edge vertices. + * Sections are assumed to be at the same node point. + */ + int compareTo(const NodeSection& o) const; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/NodeSections.h b/deps/libgeos/geos/include/geos/operation/relateng/NodeSections.h new file mode 100644 index 000000000..d7d08b342 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/NodeSections.h @@ -0,0 +1,102 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { +class RelateNode; +// class NodeSection; +} +} +namespace geom { + class CoordinateXY; + class Geometry; +} +} + + +using geos::geom::CoordinateXY; +using geos::geom::Geometry; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL NodeSections { + +private: + + // Members + const CoordinateXY* nodePt; + std::vector> sections; + + // Methods + + /** + * Sorts the sections so that: + * * lines are before areas + * * edges from the same polygon are contiguous + */ + void prepareSections(); + + static bool hasMultiplePolygonSections( + std::vector>& sections, + std::size_t i); + + static std::vector collectPolygonSections( + std::vector>& sections, + std::size_t i); + + +public: + + NodeSections(const CoordinateXY* pt) + : nodePt(pt) + {}; + + const CoordinateXY* getCoordinate() const; + + void addNodeSection(NodeSection* e); + + bool hasInteractionAB() const; + + const Geometry* getPolygonal(bool isA) const; + + std::unique_ptr createNode(); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + NodeSections(const NodeSections&) = delete; + NodeSections& operator=(const NodeSections&) = delete; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/PolygonNodeConverter.h b/deps/libgeos/geos/include/geos/operation/relateng/PolygonNodeConverter.h new file mode 100644 index 000000000..0b1ddce35 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/PolygonNodeConverter.h @@ -0,0 +1,112 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { +// class NodeSection; +} +} +} + + +// using geos::geom::CoordinateXY; +// using geos::geom::Geometry; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +/** + * Converts the node sections at a polygon node where + * a shell and one or more holes touch, or two or more holes touch. + * This converts the node topological structure from + * the OGC "touching-rings" (AKA "minimal-ring") model to the equivalent "self-touch" + * (AKA "inverted/exverted ring" or "maximal ring") model. + * In the "self-touch" model the converted NodeSection corners enclose areas + * which all lies inside the polygon + * (i.e. they does not enclose hole edges). + * This allows RelateNode to use simple area-additive semantics + * for adding edges and propagating edge locations. + * + * The input node sections are assumed to have canonical orientation + * (CW shells and CCW holes). + * The arrangement of shells and holes must be topologically valid. + * Specifically, the node sections must not cross or be collinear. + * + * This supports multiple shell-shell touches + * (including ones containing holes), and hole-hole touches, + * This generalizes the relate algorithm to support + * both the OGC model and the self-touch model. + * + * @author Martin Davis + * @see RelateNode + */ +class GEOS_DLL PolygonNodeConverter { + +public: + + /** + * Converts a list of sections of valid polygon rings + * to have "self-touching" structure. + * There are the same number of output sections as input ones. + * + * @param polySections the original sections + * @return the converted sections + */ + static std::vector> convert( + std::vector& polySections); + + +private: + + static std::size_t convertShellAndHoles( + std::vector& sections, + std::size_t shellIndex, + std::vector>& convertedSections); + + static std::vector> convertHoles( + std::vector& sections); + + static NodeSection* createSection( + const NodeSection* ns, + const CoordinateXY* v0, + const CoordinateXY* v1); + + static std::vector extractUnique( + std::vector& sections); + + static std::size_t next( + std::vector& ns, std::size_t i); + + static std::size_t findShell( + std::vector& polySections); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/RelateEdge.h b/deps/libgeos/geos/include/geos/operation/relateng/RelateEdge.h new file mode 100644 index 000000000..96afc244c --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/RelateEdge.h @@ -0,0 +1,176 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { +class RelateNode; +} +} +namespace geom { + class CoordinateXY; + class Geometry; +} +} + + +using geos::geom::CoordinateXY; +using geos::geom::Geometry; +using geos::geom::Location; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL RelateEdge { + +private: + + /** + * Indicates that the location is currently unknown + */ + static constexpr Location LOC_UNKNOWN = Location::NONE; + + // Members + const RelateNode* node; + const CoordinateXY* dirPt; + + int aDim = DIM_UNKNOWN; + Location aLocLeft = LOC_UNKNOWN; + Location aLocRight = LOC_UNKNOWN; + Location aLocLine = LOC_UNKNOWN; + + int bDim = DIM_UNKNOWN; + Location bLocLeft = LOC_UNKNOWN; + Location bLocRight = LOC_UNKNOWN; + Location bLocLine = LOC_UNKNOWN; + + +public: + + // Constants + static constexpr bool IS_FORWARD = true; + static constexpr bool IS_REVERSE = false; + static constexpr int DIM_UNKNOWN = -1; + + // Constructors + RelateEdge( + const RelateNode* node, const CoordinateXY* pt, + bool isA, bool isForward); + + RelateEdge( + const RelateNode* node, const CoordinateXY* pt, + bool isA); + + RelateEdge( + const RelateNode* node, const CoordinateXY* pt, + bool isA, Location locLeft, Location locRight, Location locLine); + + // Methods + static RelateEdge* create( + const RelateNode* node, + const CoordinateXY* dirPt, + bool isA, int dim, bool isForward); + + static std::size_t findKnownEdgeIndex( + std::vector>& edges, + bool isA); + + static void setAreaInterior( + std::vector>& edges, + bool isA); + + bool isInterior(bool isA, int position) const; + + Location location(bool isA, int position) const; + + int compareToEdge(const CoordinateXY* edgeDirPt) const; + + void setDimLocations(bool isA, int dim, Location loc); + + void setAreaInterior(bool isA); + + void setLocation(bool isA, int pos, Location loc); + + void setAllLocations(bool isA, Location loc); + + void setUnknownLocations(bool isA, Location loc); + + void merge(bool isA, int dim, bool isForward); + + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& os, const RelateEdge& re); + + +private: + + // Methods + void mergeSideLocation(bool isA, int pos, Location loc); + + /** + * Area edges override Line edges. + * Merging edges of same dimension is a no-op for + * the dimension and on location. + * But merging an area edge into a line edge + * sets the dimension to A and the location to BOUNDARY. + * + * @param isA + * @param locEdge + */ + void mergeDimEdgeLoc(bool isA, Location locEdge); + + void setDimension(bool isA, int dimension); + + void setLeft(bool isA, Location loc); + + void setRight(bool isA, Location loc); + + void setOn(bool isA, Location loc); + + int dimension(bool isA) const; + + bool isKnown(bool isA) const; + + bool isKnown(bool isA, int pos) const; + + void setLocations(bool isA, Location locLeft, Location locRight, Location locLine); + + void setLocationsLine(bool isA); + + void setLocationsArea(bool isA, bool isForward); + + std::string labelString() const; + + std::string locationString(bool isA) const; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/RelateGeometry.h b/deps/libgeos/geos/include/geos/operation/relateng/RelateGeometry.h new file mode 100644 index 000000000..53adf01b1 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/RelateGeometry.h @@ -0,0 +1,272 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { + class CoordinateSequence; + class Envelope; + class Geometry; + class LinearRing; + class LineString; + class MultiPolygon; + class Point; +} +namespace noding { + class SegmentString; +} +} + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +using namespace geos::geom; +using geos::algorithm::BoundaryNodeRule; +using geos::noding::SegmentString; + + +class GEOS_DLL RelateGeometry { + +private: + + // Members + + const Geometry* geom; + bool m_isPrepared = false; + const Envelope* geomEnv; + const BoundaryNodeRule& boundaryNodeRule; + int geomDim = Dimension::False; + bool isLineZeroLen = false; + bool isGeomEmpty = false; + + Coordinate::ConstXYSet uniquePoints; + std::unique_ptr locator; + int elementId = 0; + bool hasPoints = false; + bool hasLines = false; + bool hasAreas = false; + + /* + * Memory contexts for lower level allocations + */ + std::vector> segStringTempStore; + std::vector> segStringPermStore; + std::vector> csStore; + + + // Methods + + void analyzeDimensions(); + + /** + * Tests if all geometry linear elements are zero-length. + * For efficiency the test avoids computing actual length. + * + * @param geom + * @return + */ + static bool isZeroLength(const Geometry* geom); + + static bool isZeroLength(const LineString* line); + + bool isZeroLengthLine(const Geometry* g) const { + // avoid expensive zero-length calculation if not linear + if (getDimension() != Dimension::L) + return false; + return isZeroLength(g); + }; + + RelatePointLocator* getLocator(); + + Coordinate::ConstXYSet createUniquePoints(); + + void extractSegmentStringsFromAtomic(bool isA, + const Geometry* geom, const MultiPolygon* parentPolygonal, + const Envelope* env, + std::vector& segStrings, + std::vector>& segStore); + + void extractRingToSegmentString(bool isA, + const LinearRing* ring, int ringId, const Envelope* env, + const Geometry* parentPoly, + std::vector& segStrings, + std::vector>& segStore); + + void extractSegmentStrings(bool isA, + const Envelope* env, const Geometry* geom, + std::vector& segStrings, + std::vector>& segStore); + + const CoordinateSequence* orientAndRemoveRepeated( + const CoordinateSequence* cs, bool orientCW); + + const CoordinateSequence* removeRepeated( + const CoordinateSequence* cs); + +public: + + static constexpr bool GEOM_A = true; + static constexpr bool GEOM_B = false; + + RelateGeometry(const Geometry* input) + : RelateGeometry(input, false, BoundaryNodeRule::getBoundaryRuleMod2()) + {}; + + RelateGeometry(const Geometry* input, const BoundaryNodeRule& bnRule) + : RelateGeometry(input, false, bnRule) + {}; + + RelateGeometry(const Geometry* input, bool p_isPrepared, const BoundaryNodeRule& bnRule); + + static std::string name(bool isA); + + const Geometry* getGeometry() const { + return geom; + } + + bool isPrepared() const { + return m_isPrepared; + } + + const Envelope* getEnvelope() const { + return geomEnv; + } + + inline int getDimension() const { + return geomDim; + } + + bool hasDimension(int dim) const { + switch (dim) { + case Dimension::P: return hasPoints; + case Dimension::L: return hasLines; + case Dimension::A: return hasAreas; + } + return false; + } + + /** + * Gets the actual non-empty dimension of the geometry. + * Zero-length LineStrings are treated as Points. + * + * @return the real (non-empty) dimension + */ + int getDimensionReal() const; + + bool hasEdges() const; + + bool isNodeInArea(const CoordinateXY* nodePt, const Geometry* parentPolygonal); + + int locateLineEndWithDim(const CoordinateXY* p); + + /** + * Locates a vertex of a polygon. + * A vertex of a Polygon or MultiPolygon is on + * the {@link Location#BOUNDARY}. + * But a vertex of an overlapped polygon in a GeometryCollection + * may be in the {@link Location#INTERIOR}. + * + * @param pt the polygon vertex + * @return the location of the vertex + */ + Location locateAreaVertex(const CoordinateXY* pt); + + Location locateNode(const CoordinateXY* pt, const Geometry* parentPolygonal); + + int locateWithDim(const CoordinateXY* pt); + + /** + * Indicates whether the geometry requires self-noding + * for correct evaluation of specific spatial predicates. + * Self-noding is required for geometries which may self-cross + * - i.e. lines, and overlapping elements in GeometryCollections. + * Self-noding is not required for polygonal geometries, + * since they can only touch at vertices. + * This ensures that the coordinates of nodes created by + * crossing segments are computed explicitly. + * This ensures that node locations match in situations + * where a self-crossing and mutual crossing occur at the same logical location. + * E.g. a self-crossing line tested against a single segment + * identical to one of the crossed segments. + * + * @return true if self-noding is required for this geometry + */ + bool isSelfNodingRequired() const; + + /** + * Tests whether the geometry has polygonal topology. + * This is not the case if it is a GeometryCollection + * containing more than one polygon (since they may overlap + * or be adjacent). + * The significance is that polygonal topology allows more assumptions + * about the location of boundary vertices. + * + * @return true if the geometry has polygonal topology + */ + bool isPolygonal() const; + + bool isEmpty() const; + + bool hasBoundary(); + + Coordinate::ConstXYSet& getUniquePoints(); + + std::vector getEffectivePoints(); + + /** + * Extract RSegmentStrings from the geometry which + * intersect a given envelope. + * If the envelope is null all edges are extracted. + * @param geomA + * + * @param env the envelope to extract around (may be null) + * @return a list of SegmentStrings + */ + std::vector extractSegmentStrings(bool isA, const Envelope* env); + + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& os, const RelateGeometry& rg); + + /** + * Disable copy construction and assignment. Needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + * Classes with members that are vector<> of unique_ptr<> need this. + */ + RelateGeometry(const RelateGeometry&) = delete; + RelateGeometry& operator=(const RelateGeometry&) = delete; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/RelateMatrixPredicate.h b/deps/libgeos/geos/include/geos/operation/relateng/RelateMatrixPredicate.h new file mode 100644 index 000000000..b54ec2c3b --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/RelateMatrixPredicate.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + + +using geos::geom::Envelope; +using geos::geom::Location; +using geos::geom::Dimension; +using geos::geom::IntersectionMatrix; + + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL RelateMatrixPredicate : public IMPredicate { + +public: + + RelateMatrixPredicate() {}; + + std::string name() const override { + return "relateMatrix"; + }; + + bool requireInteraction() const override { + //-- ensure entire matrix is computed + return false; + }; + + bool isDetermined() const override { + //-- ensure entire matrix is computed + return false; + }; + + bool valueIM() override { + //-- indicates full matrix is being evaluated + return false; + }; + + /** + * Gets the current state of the IM matrix (which may only be partially complete). + * + * @return the IM matrix + */ + std::unique_ptr getIM() { + return std::unique_ptr(new IntersectionMatrix(intMatrix)); + } + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/RelateNG.h b/deps/libgeos/geos/include/geos/operation/relateng/RelateNG.h new file mode 100644 index 000000000..b55f2ddfc --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/RelateNG.h @@ -0,0 +1,295 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace algorithm { + class BoundaryNodeRule; + +} +namespace geom { + class Geometry; +} +namespace noding { +} +namespace operation { +namespace relateng { + class TopologyPredicate; + class TopologyComputer; + class EdgeSegmentIntersector; +} +} +} + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +using geos::geom::CoordinateXY; +using geos::geom::Geometry; +using geos::algorithm::BoundaryNodeRule; +using geos::noding::MCIndexSegmentSetMutualIntersector; + + +/** + * Computes the value of topological predicates between two geometries based on the + * Dimensionally-Extended 9-Intersection Model (DE-9IM). + * Standard and custom topological predicates are provided by RelatePredicate. + * + * The RelateNG algorithm has the following capabilities: + * + * * Efficient short-circuited evaluation of topological predicates + * (including matching custom DE-9IM matrix patterns) + * * Optimized repeated evaluation of predicates against a single geometry + * via cached spatial indexes (AKA "prepared mode") + * * Robust computation (only point-local topology is required, + * so invalid geometry topology does not cause failures) + * * GeometryCollection inputs containing mixed types and overlapping polygons + * are supported, using union semantics. + * * Zero-length LineStrings are treated as being topologically identical to Points. + * * Support for BoundaryNodeRule. + * + * See IntersectionMatrixPattern for a description of DE-9IM patterns. + * + * If not specified, the standard BoundaryNodeRule::MOD2_BOUNDARY_RULE is used. + * + * RelateNG operates in 2D only; it ignores any Z ordinates. + * + * This implementation replaces RelateOp and PreparedGeometry. + * + * FUTURE WORK + * + * * Support for a distance tolerance to provide "approximate" predicate evaluation + * + * @author Martin Davis + * + * @see RelateOp + * @see PreparedGeometry + */ +class GEOS_DLL RelateNG { + +private: + + // Members + const BoundaryNodeRule& boundaryNodeRule; + RelateGeometry geomA; + std::unique_ptr edgeMutualInt = nullptr; + + // Methods + + RelateNG(const Geometry* inputA, bool isPrepared, const BoundaryNodeRule& bnRule) + : boundaryNodeRule(bnRule) + , geomA(inputA, isPrepared, bnRule) + {} + + RelateNG(const Geometry* inputA, bool isPrepared) + : RelateNG(inputA, isPrepared, BoundaryNodeRule::getBoundaryRuleMod2()) + {} + + bool hasRequiredEnvelopeInteraction(const Geometry* b, TopologyPredicate& predicate); + + bool finishValue(TopologyPredicate& predicate); + + void computePP(RelateGeometry& geomB, TopologyComputer& topoComputer); + + void computeAtPoints(RelateGeometry& geom, bool isA, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + bool computePoints(RelateGeometry& geom, bool isA, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + void computePoint(bool isA, const CoordinateXY* pt, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + bool computeLineEnds(RelateGeometry& geom, bool isA, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + + /** + * Compute the topology of a line endpoint. + * Also reports if the line end is in the exterior of the target geometry, + * to optimize testing multiple exterior endpoints. + * + * @param geom + * @param isA + * @param pt + * @param geomTarget + * @param topoComputer + * @return true if the line endpoint is in the exterior of the target + */ + bool computeLineEnd(RelateGeometry& geom, bool isA, const CoordinateXY* pt, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + bool computeAreaVertex(RelateGeometry& geom, bool isA, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + bool computeAreaVertex(RelateGeometry& geom, bool isA, const LinearRing* ring, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + void computeAtEdges(RelateGeometry& geomB, TopologyComputer& topoComputer); + + void computeEdgesAll(std::vector& edgesB, const Envelope* envInt, EdgeSegmentIntersector& intersector); + + void computeEdgesMutual(std::vector& edgesB, const Envelope* envInt, EdgeSegmentIntersector& intersector); + + + +public: + + /** + * Tests whether the topological relationship between two geometries + * satisfies a topological predicate. + * + * @param a the A input geometry + * @param b the B input geometry + * @param pred the topological predicate + * @return true if the topological relationship is satisfied + */ + static bool relate(const Geometry* a, const Geometry* b, TopologyPredicate& pred); + + /** + * Tests whether the topological relationship between two geometries + * satisfies a topological predicate, + * using a given BoundaryNodeRule. + * + * @param a the A input geometry + * @param b the B input geometry + * @param pred the topological predicate + * @param bnRule the Boundary Node Rule to use + * @return true if the topological relationship is satisfied + */ + static bool relate(const Geometry* a, const Geometry* b, TopologyPredicate& pred, const BoundaryNodeRule& bnRule); + + /** + * Tests whether the topological relationship to a geometry + * matches a DE-9IM matrix pattern. + * + * @param a the A input geometry + * @param b the B input geometry + * @param imPattern the DE-9IM pattern to match + * @return true if the geometries relationship matches the DE-9IM pattern + * + * @see IntersectionMatrixPattern + */ + static bool relate(const Geometry* a, const Geometry* b, const std::string& imPattern); + + /** + * Computes the DE-9IM matrix + * for the topological relationship between two geometries. + * + * @param a the A input geometry + * @param b the B input geometry + * @return the DE-9IM matrix for the topological relationship + */ + static std::unique_ptr relate(const Geometry* a, const Geometry* b); + + /** + * Computes the DE-9IM matrix + * for the topological relationship between two geometries. + * + * @param a the A input geometry + * @param b the B input geometry + * @param bnRule the Boundary Node Rule to use + * @return the DE-9IM matrix for the relationship + */ + static std::unique_ptr relate(const Geometry* a, const Geometry* b, const BoundaryNodeRule& bnRule); + + /** + * Creates a prepared RelateNG instance to optimize the + * evaluation of relationships against a single geometry. + * + * @param a the A input geometry + * @return a prepared instance + */ + static std::unique_ptr prepare(const Geometry* a); + + /** + * Creates a prepared RelateNG instance to optimize the + * computation of predicates against a single geometry, + * using a given BoundaryNodeRule. + * + * @param a the A input geometry + * @param bnRule the required BoundaryNodeRule + * @return a prepared instance + */ + static std::unique_ptr prepare(const Geometry* a, const BoundaryNodeRule& bnRule); + + + /** + * Computes the DE-9IM matrix for the topological relationship to a geometry. + * + * @param b the B geometry to test against + * @return the DE-9IM matrix + */ + std::unique_ptr evaluate(const Geometry* b); + + + /** + * Tests whether the topological relationship to a geometry + * matches a DE-9IM matrix pattern. + * + * @param b the B geometry to test against + * @param imPattern the DE-9IM pattern to match + * @return true if the geometries' topological relationship matches the DE-9IM pattern + * + * @see IntersectionMatrixPattern + */ + bool evaluate(const Geometry* b, const std::string& imPattern); + + /** + * Tests whether the topological relationship to a geometry + * satisfies a topology predicate. + * + * @param b the B geometry to test against + * @param predicate the topological predicate + * @return true if the predicate is satisfied + */ + bool evaluate(const Geometry* b, TopologyPredicate& predicate); + + static bool intersects(const Geometry* a, const Geometry* b); + static bool crosses(const Geometry* a, const Geometry* b); + static bool disjoint(const Geometry* a, const Geometry* b); + static bool touches(const Geometry* a, const Geometry* b); + static bool within(const Geometry* a, const Geometry* b); + static bool contains(const Geometry* a, const Geometry* b); + static bool overlaps(const Geometry* a, const Geometry* b); + static bool covers(const Geometry* a, const Geometry* b); + static bool coveredBy(const Geometry* a, const Geometry* b); + static bool equalsTopo(const Geometry* a, const Geometry* b); + + bool intersects(const Geometry* a); + bool crosses(const Geometry* a); + bool disjoint(const Geometry* a); + bool touches(const Geometry* a); + bool within(const Geometry* a); + bool contains(const Geometry* a); + bool overlaps(const Geometry* a); + bool covers(const Geometry* a); + bool coveredBy(const Geometry* a); + bool equalsTopo(const Geometry* a); + bool relate(const Geometry* a, const std::string& pat); + std::unique_ptr relate(const Geometry* a); + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/RelateNode.h b/deps/libgeos/geos/include/geos/operation/relateng/RelateNode.h new file mode 100644 index 000000000..581eec359 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/RelateNode.h @@ -0,0 +1,146 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +#include + + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { + class NodeSection; +} +} +namespace geom { + class CoordinateXY; + class Geometry; +} +} + + +using geos::geom::CoordinateXY; +using geos::geom::Geometry; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL RelateNode { + +private: + + // Members + + /** + * A list of the edges around the node in CCW order, + * ordered by their CCW angle with the positive X-axis. + */ + std::vector> edges; + + const CoordinateXY* nodePt; + + + // Methods + + void updateEdgesInArea(bool isA, std::size_t indexFrom, std::size_t indexTo); + + void updateIfAreaPrev(bool isA, std::size_t index); + + void updateIfAreaNext(bool isA, std::size_t index); + + const RelateEdge* addLineEdge(bool isA, const CoordinateXY* dirPt); + + const RelateEdge* addAreaEdge(bool isA, const CoordinateXY* dirPt, bool isForward); + + /** + * Adds or merges an edge to the node. + * + * @param isA + * @param dirPt + * @param dim dimension of the geometry element containing the edge + * @param isForward the direction of the edge + * + * @return the created or merged edge for this point + */ + const RelateEdge* addEdge(bool isA, const CoordinateXY* dirPt, int dim, bool isForward); + + void finishNode(bool isA, bool isAreaInterior); + + void propagateSideLocations(bool isA, std::size_t startIndex); + + static std::size_t prevIndex(std::vector>& list, std::size_t index); + + static std::size_t nextIndex(std::vector>& list, std::size_t i); + + std::size_t indexOf( + const std::vector>& edges, + const RelateEdge* edge) const; + + +public: + + RelateNode(const CoordinateXY* pt) + : nodePt(pt) + {}; + + const CoordinateXY* getCoordinate() const; + + const std::vector>& getEdges() const; + + void addEdges(std::vector& nss); + void addEdges(std::vector>& nss); + + void addEdges(const NodeSection* ns); + + /** + * Computes the final topology for the edges around this node. + * Although nodes lie on the boundary of areas or the interior of lines, + * in a mixed GC they may also lie in the interior of an area. + * This changes the locations of the sides and line to Interior. + * + * @param isAreaInteriorA true if the node is in the interior of A + * @param isAreaInteriorB true if the node is in the interior of B + */ + void finish(bool isAreaInteriorA, bool isAreaInteriorB); + + std::string toString() const; + + bool hasExteriorEdge(bool isA); + + friend std::ostream& operator<<(std::ostream& os, const RelateNode& ns); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + RelateNode(const RelateNode&) = delete; + RelateNode& operator=(const RelateNode&) = delete; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/RelatePointLocator.h b/deps/libgeos/geos/include/geos/operation/relateng/RelatePointLocator.h new file mode 100644 index 000000000..3ac03da64 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/RelatePointLocator.h @@ -0,0 +1,213 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace algorithm { + namespace locate { + // class PointOnGeometryLocator; + } +} +namespace operation { + namespace relateng { + // class LinearBoundary; + // class AdjacentEdgeLocator; + } +} +namespace geom { + class CoordinateXY; + class Geometry; + class LineString; + class Point; +} +} + + +using geos::algorithm::BoundaryNodeRule; +using geos::algorithm::locate::PointOnGeometryLocator; +using geos::geom::Coordinate; +using geos::geom::CoordinateXY; +using geos::geom::Geometry; +using geos::geom::LineString; +using geos::geom::Point; +using geos::geom::Location; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +/** + * Locates a point on a geometry, including mixed-type collections. + * The dimension of the containing geometry element is also determined. + * GeometryCollections are handled with union semantics; + * i.e. the location of a point is that location of that point + * on the union of the elements of the collection. + * + * Union semantics for GeometryCollections has the following behaviours: + * + * * For a mixed-dimension (heterogeneous) collection + * a point may lie on two geometry elements with different dimensions. + * In this case the location on the largest-dimension element is reported. + * * For a collection with overlapping or adjacent polygons, + * points on polygon element boundaries may lie in the effective interior + * of the collection geometry. + * + * Prepared mode is supported via cached spatial indexes. + * + * @author Martin Davis + */ +class GEOS_DLL RelatePointLocator { + +private: + + // Members + + const Geometry* geom; + bool isPrepared = false; + const BoundaryNodeRule& boundaryRule; + std::unique_ptr adjEdgeLocator; + Coordinate::ConstXYSet points; + std::vector lines; + std::vector polygons; + std::vector> polyLocator; + std::unique_ptr lineBoundary; + bool isEmpty; + + +public: + + // Constructors + + RelatePointLocator(const Geometry* p_geom) + : RelatePointLocator(p_geom, false, BoundaryNodeRule::getBoundaryRuleMod2()) + {}; + + RelatePointLocator(const Geometry* p_geom, bool p_isPrepared, const BoundaryNodeRule& p_bnRule) + : geom(p_geom) + , isPrepared(p_isPrepared) + , boundaryRule(p_bnRule) + { + init(geom); + }; + + void init(const Geometry* p_geom); + + bool hasBoundary() const; + + void extractElements(const Geometry* geom); + + void addPoint(const Point* pt); + + void addLine(const LineString* line); + + void addPolygonal(const Geometry* polygonal); + + Location locate(const CoordinateXY* p); + + int locateLineEndWithDim(const CoordinateXY* p); + + /* + * Locates a point which is known to be a node of the geometry + * (i.e. a vertex or on an edge). + * + * @param p the node point to locate + * @param parentPolygonal the polygon the point is a node of + * @return the location of the node point + */ + Location locateNode(const CoordinateXY* p, const Geometry* parentPolygonal); + + /** + * Locates a point which is known to be a node of the geometry, + * as a DimensionLocation. + * + * @param p the point to locate + * @param parentPolygonal the polygon the point is a node of + * @return the dimension and location of the point + */ + int locateNodeWithDim(const CoordinateXY* p, const Geometry* parentPolygonal); + + /** + * Computes the topological location ( Location) of a single point + * in a Geometry, as well as the dimension of the geometry element the point + * is located in (if not in the Exterior). + * It handles both single-element and multi-element Geometries. + * The algorithm for multi-part Geometries + * takes into account the SFS Boundary Determination Rule. + * + * @param p the point to locate + * @return the Location of the point relative to the input Geometry + */ + int locateWithDim(const CoordinateXY* p); + + +private: + + // Methods + + /** + * Computes the topological location (Location) of a single point + * in a Geometry, as well as the dimension of the geometry element the point + * is located in (if not in the Exterior). + * It handles both single-element and multi-element Geometries. + * The algorithm for multi-part Geometries + * takes into account the SFS Boundary Determination Rule. + * + * @param p the coordinate to locate + * @param isNode whether the coordinate is a node (on an edge) of the geometry + * @param polygon + * @return the Location of the point relative to the input Geometry + */ + int locateWithDim(const CoordinateXY* p, bool isNode, const Geometry* parentPolygonal); + + int computeDimLocation(const CoordinateXY* p, bool isNode, const Geometry* parentPolygonal); + + Location locateOnPoints(const CoordinateXY* p) const; + + Location locateOnLines(const CoordinateXY* p, bool isNode); + + Location locateOnLine(const CoordinateXY* p, /*bool isNode,*/ const LineString* l); + + Location locateOnPolygons(const CoordinateXY* p, bool isNode, const Geometry* parentPolygonal); + + Location locateOnPolygonal(const CoordinateXY* p, + bool isNode, + const Geometry* parentPolygonal, + std::size_t index); + + PointOnGeometryLocator * getLocator(std::size_t index); + + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/RelatePredicate.h b/deps/libgeos/geos/include/geos/operation/relateng/RelatePredicate.h new file mode 100644 index 000000000..ca1db412f --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/RelatePredicate.h @@ -0,0 +1,652 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + + +using geos::geom::Envelope; +using geos::geom::Location; + + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL RelatePredicate { + +public: + +/************************************************************************ + * + * Creates a predicate to determine whether two geometries intersect. + * + * The intersects predicate has the following equivalent definitions: + * + * * The two geometries have at least one point in common + * * The DE-9IM Intersection Matrix for the two geometries matches + * at least one of the patterns + * + * [T********] + * [*T*******] + * [***T*****] + * [****T****] + * + * disjoint() = false + * (intersects is the inverse of disjoint) + * + * @return the predicate instance + * + * @see disjoint() + */ +class IntersectsPredicate : public BasicPredicate { + +public: + + std::string name() const override { + return std::string("intersects"); + } + + bool requireSelfNoding() const override { + //-- self-noding is not required to check for a simple interaction + return false; + } + + bool requireExteriorCheck(bool isSourceA) const override { + (void)isSourceA; + //-- intersects only requires testing interaction + return false; + } + + void init(const Envelope& envA, const Envelope& envB) override { + require(envA.intersects(envB)); + } + + void updateDimension(Location locA, Location locB, int dimension) override { + (void)dimension; + setValueIf(true, isIntersection(locA, locB)); + } + + void finish() override { + //-- if no intersecting locations were found + setValue(false); + } + +}; + +static std::unique_ptr intersects(); + +/************************************************************************ + * + * Creates a predicate to determine whether two geometries are disjoint. + * + * The disjoint predicate has the following equivalent definitions: + * + * * The two geometries have no point in common + * * The DE-9IM Intersection Matrix for the two geometries matches + * [FF*FF****] + * * intersects() = false + * (disjoint is the inverse of intersects) + * + * @return the predicate instance + * + * @see intersects() + */ +class DisjointPredicate : public BasicPredicate { + + std::string name() const override { + return std::string("disjoint"); + } + + bool requireSelfNoding() const override { + //-- self-noding is not required to check for a simple interaction + return false; + } + + bool requireInteraction() const override { + //-- ensure entire matrix is computed + return false; + } + + bool requireExteriorCheck(bool isSourceA) const override { + (void)isSourceA; + //-- intersects only requires testing interaction + return false; + } + + void init(const Envelope& envA, const Envelope& envB) override { + setValueIf(true, envA.disjoint(envB)); + } + + void updateDimension(Location locA, Location locB, int dimension) override { + (void)dimension; + setValueIf(false, isIntersection(locA, locB)); + } + + void finish() override { + //-- if no intersecting locations were found + setValue(true); + } +}; + +static std::unique_ptr disjoint(); + +/************************************************************************ + * Creates a predicate to determine whether a geometry contains another geometry. + * + * The contains predicate has the following equivalent definitions: + * + * * Every point of the other geometry is a point of this geometry, + * and the interiors of the two geometries have at least one point in common. + * * The DE-9IM Intersection Matrix for the two geometries matches + * the pattern + * [T*****FF*] + * * within(B, A) = true + * (contains is the converse of within) + * + * An implication of the definition is that "Geometries do not + * contain their boundary". In other words, if a geometry A is a subset of + * the points in the boundary of a geometry B, B.contains(A) = false. + * (As a concrete example, take A to be a LineString which lies in the boundary of a Polygon B.) + * For a predicate with similar behavior but avoiding + * this subtle limitation, see covers(). + * + * @return the predicate instance + * + * @see within() + */ +class ContainsPredicate : public IMPredicate { + + std::string name() const override { + return std::string("contains"); + } + + bool requireCovers(bool isSourceA) override { + return isSourceA == RelateGeometry::GEOM_A; + } + + bool requireExteriorCheck(bool isSourceA) const override { + //-- only need to check B against Exterior of A + return isSourceA == RelateGeometry::GEOM_B; + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(isDimsCompatibleWithCovers(dimA, dimB)); + } + + void init(const Envelope& envA, const Envelope& envB) override { + BasicPredicate::requireCovers(envA, envB); + } + + bool isDetermined() const override { + return intersectsExteriorOf(RelateGeometry::GEOM_A); + } + + bool valueIM() override { + return intMatrix.isContains(); + } +}; + +static std::unique_ptr contains(); + + + +/************************************************************************ + * Creates a predicate to determine whether a geometry is within another geometry. + * + * The within predicate has the following equivalent definitions: + * + * * Every point of this geometry is a point of the other geometry, + * and the interiors of the two geometries have at least one point in common. + * * The DE-9IM Intersection Matrix for the two geometries matches + * [T*F**F***] + * * contains(B, A) = true + * (within is the converse of contains()) + * + * An implication of the definition is that + * "The boundary of a Geometry is not within the Geometry". + * In other words, if a geometry A is a subset of + * the points in the boundary of a geometry B, within(B, A) = false + * (As a concrete example, take A to be a LineString which lies in the boundary of a Polygon B.) + * For a predicate with similar behavior but avoiding + * this subtle limitation, see coveredimBy(). + * + * @return the predicate instance + * + * @see #contains() + */ +class WithinPredicate : public IMPredicate { + + std::string name() const override { + return std::string("within"); + } + + bool requireCovers(bool isSourceA) override { + return isSourceA == RelateGeometry::GEOM_B; + } + + bool requireExteriorCheck(bool isSourceA) const override { + //-- only need to check B against Exterior of A + return isSourceA == RelateGeometry::GEOM_A; + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(isDimsCompatibleWithCovers(dimB, dimA)); + } + + void init(const Envelope& envA, const Envelope& envB) override { + BasicPredicate::requireCovers(envB, envA); + } + + bool isDetermined() const override { + return intersectsExteriorOf(RelateGeometry::GEOM_B); + } + + bool valueIM() override { + return intMatrix.isWithin(); + } +}; + +static std::unique_ptr within(); + + + +/************************************************************************ + * Creates a predicate to determine whether a geometry covers another geometry. + * + * The covers predicate has the following equivalent definitions: + * + * Every point of the other geometry is a point of this geometry. + * The DE-9IM Intersection Matrix for the two geometries matches + * at least one of the following patterns: + * + * * [T*****FF*] + * * [*T****FF*] + * * [***T**FF*] + * * [****T*FF*] + * + * coveredimBy(b, a) = true + * (covers is the converse of coveredimBy()) + * + * If either geometry is empty, the value of this predicate is false. + * + * This predicate is similar to contains(), + * but is more inclusive (i.e. returns true for more cases). + * In particular, unlike contains it does not distinguish between + * points in the boundary and in the interior of geometries. + * For most cases, covers should be used in preference to contains. + * As an added benefit, covers is more amenable to optimization, + * and hence should be more performant. + * + * @return the predicate instance + * + * @see #coveredimBy() + */ +class CoversPredicate : public IMPredicate { + + std::string name() const override { + return std::string("covers"); + } + + bool requireCovers(bool isSourceA) override { + return isSourceA == RelateGeometry::GEOM_A; + } + + bool requireExteriorCheck(bool isSourceA) const override { + //-- only need to check B against Exterior of A + return isSourceA == RelateGeometry::GEOM_B; + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(isDimsCompatibleWithCovers(dimA, dimB)); + } + + void init(const Envelope& envA, const Envelope& envB) override { + BasicPredicate::requireCovers(envA, envB); + + } + + bool isDetermined() const override { + return intersectsExteriorOf(RelateGeometry::GEOM_A); + } + + bool valueIM() override { + return intMatrix.isCovers(); + } +}; + +static std::unique_ptr covers(); + + +/************************************************************************ +* Creates a predicate to determine whether a geometry is covered +* by another geometry. +* +* The coveredimBy predicate has the following equivalent definitions: +* +* Every point of this geometry is a point of the other geometry. +* The DE-9IM Intersection Matrix for the two geometries matches +* at least one of the following patterns: +* +* [T*F**F***] +* [*TF**F***] +* [**FT*F***] +* [**F*TF***] +* +* covers(B, A) = true +* (coveredimBy is the converse of covers()) +* +* If either geometry is empty, the value of this predicate is false. +* +* This predicate is similar to within(), +* but is more inclusive (i.e. returns true for more cases). +* +* @return the predicate instance +* +* @see #covers() +*/ +class CoveredByPredicate : public IMPredicate { + + std::string name() const override { + return std::string("coveredBy"); + } + + bool requireCovers(bool isSourceA) override { + return isSourceA == RelateGeometry::GEOM_B; + } + + bool requireExteriorCheck(bool isSourceA) const override { + //-- only need to check B against Exterior of A + return isSourceA == RelateGeometry::GEOM_A; + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(isDimsCompatibleWithCovers(dimB, dimA)); + } + + void init(const Envelope& envA, const Envelope& envB) override { + BasicPredicate::requireCovers(envB, envA); + } + + bool isDetermined() const override { + return intersectsExteriorOf(RelateGeometry::GEOM_B); + } + + bool valueIM() override { + return intMatrix.isCoveredBy(); + } + +}; + +static std::unique_ptr coveredBy(); + + +/************************************************************************ +* Creates a predicate to determine whether a geometry crosses another geometry. +* +* The crosses predicate has the following equivalent definitions: +* +* The geometries have some but not all interior points in common. +* The DE-9IM Intersection Matrix for the two geometries matches +* one of the following patterns: +* +* [T*T******] (for P/L, P/A, and L/A cases) +* [T*****T**] (for L/P, A/P, and A/L cases) +* [0********] (for L/L cases) +* +* +* For the A/A and P/P cases this predicate returns false. +* +* The SFS defined this predicate only for P/L, P/A, L/L, and L/A cases. +* To make the relation symmetric +* JTS extends the definition to apply to L/P, A/P and A/L cases as well. +* +* @return the predicate instance +*/ + +class CrossesPredicate : public IMPredicate { + + std::string name() const override { + return std::string("crosses"); + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + bool isBothPointsOrAreas = + (dimA == Dimension::P && dimB == Dimension::P) || + (dimA == Dimension::A && dimB == Dimension::A); + require(!isBothPointsOrAreas); + } + + bool isDetermined() const override { + if (dimA == Dimension::L && dimB == Dimension::L) { + //-- L/L interaction can only be dim = P + if (getDimension(Location::INTERIOR, Location::INTERIOR) > Dimension::P) + return true; + } + else if (dimA < dimB) { + if (isIntersects(Location::INTERIOR, Location::INTERIOR) && + isIntersects(Location::INTERIOR, Location::EXTERIOR)) { + return true; + } + } + else if (dimA > dimB) { + if (isIntersects(Location::INTERIOR, Location::INTERIOR) && + isIntersects(Location::EXTERIOR, Location::INTERIOR)) { + return true; + } + } + return false; + } + + bool valueIM() override { + return intMatrix.isCrosses(dimA, dimB); + } +}; + +static std::unique_ptr crosses(); + + +/************************************************************************ +* Creates a predicate to determine whether two geometries are +* topologically equal. +* +* The equals predicate has the following equivalent definitions: +* +* The two geometries have at least one point in common, +* and no point of either geometry lies in the exterior of the other geometry. +* The DE-9IM Intersection Matrix for the two geometries matches +* the pattern T*F**FFF* +* +* @return the predicate instance +*/ +class EqualsTopoPredicate : public IMPredicate { + + std::string name() const override { + return std::string("equals"); + } + + bool requireInteraction() const override { + //-- allow EMPTY = EMPTY + return false; + }; + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + //-- don't require equal dims, because EMPTY = EMPTY for all dims + } + + void init(const Envelope& envA, const Envelope& envB) override { + //-- handle EMPTY = EMPTY cases + setValueIf(true, envA.isNull() && envB.isNull()); + + require(envA.equals(&envB)); + } + + bool isDetermined() const override { + bool isEitherExteriorIntersects = + isIntersects(Location::INTERIOR, Location::EXTERIOR) || + isIntersects(Location::BOUNDARY, Location::EXTERIOR) || + isIntersects(Location::EXTERIOR, Location::INTERIOR) || + isIntersects(Location::EXTERIOR, Location::BOUNDARY); + + return isEitherExteriorIntersects; + } + + bool valueIM() override { + return intMatrix.isEquals(dimA, dimB); + } + +}; + +static std::unique_ptr equalsTopo(); + + +/************************************************************************ + * Creates a predicate to determine whether a geometry overlaps another geometry. + * + * The overlaps predicate has the following equivalent definitions: + * + * The geometries have at least one point each not shared by the other + * (or equivalently neither covers the other), + * they have the same dimension, + * and the intersection of the interiors of the two geometries has + * the same dimension as the geometries themselves. + * The DE-9IM Intersection Matrix for the two geometries matches + * [T*T***T**] (for P/P and A/A cases) + * or [1*T***T**] (for L/L cases) + * + * If the geometries are of different dimension this predicate returns false. + * This predicate is symmetric. + * + * @return the predicate instance + */ +class OverlapsPredicate : public IMPredicate { + + std::string name() const override { + return std::string("overlaps"); + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(dimA == dimB); + } + + bool isDetermined() const override { + if (dimA == Dimension::A || dimA == Dimension::P) { + if (isIntersects(Location::INTERIOR, Location::INTERIOR) && + isIntersects(Location::INTERIOR, Location::EXTERIOR) && + isIntersects(Location::EXTERIOR, Location::INTERIOR)) + return true; + } + if (dimA == Dimension::L) { + if (isDimension(Location::INTERIOR, Location::INTERIOR, Dimension::L) && + isIntersects(Location::INTERIOR, Location::EXTERIOR) && + isIntersects(Location::EXTERIOR, Location::INTERIOR)) + return true; + } + return false; + } + + bool valueIM() override { + return intMatrix.isOverlaps(dimA, dimB); + } +}; + +static std::unique_ptr overlaps(); + + + + + +/************************************************************************ +* Creates a predicate to determine whether a geometry touches another geometry. +* +* The touches predicate has the following equivalent definitions: +* +* The geometries have at least one point in common, +* but their interiors do not intersect. +* The DE-9IM Intersection Matrix for the two geometries matches +* at least one of the following patterns +* +* [FT*******] +* [F**T*****] +* [F***T****] +* +* +* If both geometries have dimension 0, the predicate returns false, +* since points have only interiors. +* This predicate is symmetric. +* +* @return the predicate instance +*/ +class TouchesPredicate : public IMPredicate { + + std::string name() const override { + return std::string("touches"); + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + bool isBothPoints = (dimA == 0 && dimB == 0); + require(! isBothPoints); + } + + bool isDetermined() const override { + bool isInteriorsIntersects = isIntersects(Location::INTERIOR, Location::INTERIOR); + return isInteriorsIntersects; + } + + bool valueIM() override { + return intMatrix.isTouches(dimA, dimB); + } +}; + +static std::unique_ptr touches(); + +/** + * Creates a predicate that matches a DE-9IM matrix pattern. + * + * @param imPattern the pattern to match + * @return a predicate that matches the pattern + * + * @see IntersectionMatrixPattern + */ +static std::unique_ptr matches(const std::string& imPattern) +{ + return std::unique_ptr(new IMPatternMatcher(imPattern)); +} + + + +}; // !RelatePredicate + + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/RelateSegmentString.h b/deps/libgeos/geos/include/geos/operation/relateng/RelateSegmentString.h new file mode 100644 index 000000000..f2e6caff4 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/RelateSegmentString.h @@ -0,0 +1,161 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + + +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { + class CoordinateXY; + class CoordinateSequence; + class Geometry; +} +namespace operation { +namespace relateng { + class RelateGeometry; + class NodeSection; +} +} +} + + +using geos::noding::BasicSegmentString; +using geos::geom::Geometry; +using geos::geom::CoordinateXY; +using geos::geom::CoordinateSequence; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + + +class GEOS_DLL RelateSegmentString : public BasicSegmentString { + +private: + + // Members + bool m_isA; + int m_dimension; + int m_id; + int m_ringId; + const RelateGeometry* m_inputGeom; + const Geometry* m_parentPolygonal = nullptr; + + // Constructor + RelateSegmentString( + const CoordinateSequence* pts, + bool isA, + int dimension, + int id, + int ringId, + const Geometry* poly, + const RelateGeometry* inputGeom) + : BasicSegmentString(const_cast(pts), nullptr) + , m_isA(isA) + , m_dimension(dimension) + , m_id(id) + , m_ringId(ringId) + , m_inputGeom(inputGeom) + , m_parentPolygonal(poly) + {} + + + // Methods + + static const RelateSegmentString* createSegmentString( + const CoordinateSequence* pts, + bool isA, int dim, int elementId, int ringId, + const Geometry* poly, const RelateGeometry* parent); + + /** + * + * @param ss + * @param segIndex + * @param pt + * @return the previous vertex, or null if none exists + */ + const CoordinateXY* prevVertex( + std::size_t segIndex, + const CoordinateXY* pt) const; + + /** + * @param ss + * @param segIndex + * @param pt + * @return the next vertex, or null if none exists + */ + const CoordinateXY* nextVertex( + std::size_t segIndex, + const CoordinateXY* pt) const; + + +public: + + static const RelateSegmentString* createLine( + const CoordinateSequence* pts, + bool isA, int elementId, + const RelateGeometry* parent); + + static const RelateSegmentString* createRing( + const CoordinateSequence* pts, + bool isA, int elementId, int ringId, + const Geometry* poly, const RelateGeometry* parent); + + inline bool isA() const { + return m_isA; + } + + inline const RelateGeometry* getGeometry() const { + return m_inputGeom; + } + + inline const Geometry* getPolygonal() const { + return m_parentPolygonal; + } + + NodeSection* createNodeSection(std::size_t segIndex, const CoordinateXY intPt) const; + + /** + * Tests if a segment intersection point has that segment as its + * canonical containing segment. + * Segments are half-closed, and contain their start point but not the endpoint, + * except for the final segment in a non-closed segment string, which contains + * its endpoint as well. + * This test ensures that vertices are assigned to a unique segment in a segment string. + * In particular, this avoids double-counting intersections which lie exactly + * at segment endpoints. + * + * @param segIndex the segment the point may lie on + * @param pt the point + * @return true if the segment contains the point + */ + bool isContainingSegment(std::size_t segIndex, const CoordinateXY* pt) const; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/TopologyComputer.h b/deps/libgeos/geos/include/geos/operation/relateng/TopologyComputer.h new file mode 100644 index 000000000..4b52598b3 --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/TopologyComputer.h @@ -0,0 +1,236 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { + class NodeSection; + class RelateGeometry; + class RelateNode; + class TopologyPredicate; +} +} +} + + +using geos::geom::CoordinateXY; +using geos::geom::Location; +using geos::operation::relateng::NodeSection; +using geos::operation::relateng::NodeSections; +using geos::operation::relateng::RelateGeometry; +using geos::operation::relateng::RelateNode; +using geos::operation::relateng::TopologyPredicate; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL TopologyComputer { + +private: + + // Members + TopologyPredicate& predicate; + RelateGeometry& geomA; + RelateGeometry& geomB; + std::map nodeMap; + std::deque> nodeSectionsStore; + + // Methods + + /** + * Determine a priori partial EXTERIOR topology based on dimensions. + */ + void initExteriorDims(); + + void initExteriorEmpty(bool geomNonEmpty); + + inline RelateGeometry& getGeometry(bool isA) const { + return isA ? geomA : geomB; + }; + + void updateDim(Location locA, Location locB, int dimension); + + void updateDim(bool isAB, Location loc1, Location loc2, int dimension); + + /** + * Update topology for an intersection between A and B. + * + * @param a the section for geometry A + * @param b the section for geometry B + */ + void updateIntersectionAB(const NodeSection* a, const NodeSection* b); + + /** + * Updates topology for an AB Area-Area crossing node. + * Sections cross at a node if (a) the intersection is proper + * (i.e. in the interior of two segments) + * or (b) if non-proper then whether the linework crosses + * is determined by the geometry of the segments on either side of the node. + * In these situations the area geometry interiors intersect (in dimension 2). + * + * @param a the section for geometry A + * @param b the section for geometry B + */ + void updateAreaAreaCross(const NodeSection* a, const NodeSection* b); + + /** + * Updates topology for a node at an AB edge intersection. + * + * @param a the section for geometry A + * @param b the section for geometry B + */ + void updateNodeLocation(const NodeSection* a, const NodeSection* b); + + void addNodeSections(NodeSection* ns0, NodeSection* ns1); + + void addLineEndOnLine(bool isLineA, Location locLineEnd, Location locLine, const CoordinateXY* pt); + + void addLineEndOnArea(bool isLineA, Location locLineEnd, Location locArea, const CoordinateXY* pt); + + /** + * Updates topology for an area vertex (in Interior or on Boundary) + * intersecting a point. + * Note that because the largest dimension of intersecting target is determined, + * the intersecting point is not part of any other target geometry, + * and hence its neighbourhood is in the Exterior of the target. + * + * @param isAreaA whether the area is the A input + * @param locArea the location of the vertex in the area + * @param pt the point at which topology is being updated + */ + void addAreaVertexOnPoint(bool isAreaA, Location locArea, const CoordinateXY* pt); + + void addAreaVertexOnLine(bool isAreaA, Location locArea, Location locTarget, const CoordinateXY* pt); + + void evaluateNode(NodeSections* nodeSections); + + void evaluateNodeEdges(const RelateNode* node); + + NodeSections* getNodeSections(const CoordinateXY& nodePt); + + + +public: + + TopologyComputer( + TopologyPredicate& p_predicate, + RelateGeometry& p_geomA, + RelateGeometry& p_geomB) + : predicate(p_predicate) + , geomA(p_geomA) + , geomB(p_geomB) + { + initExteriorDims(); + }; + + int getDimension(bool isA) const; + + bool isAreaArea() const; + + /** + * Indicates whether the input geometries require self-noding + * for correct evaluation of specific spatial predicates. + * Self-noding is required for geometries which may + * have self-crossing linework. + * This causes the coordinates of nodes created by + * crossing segments to be computed explicitly. + * This ensures that node locations match in situations + * where a self-crossing and mutual crossing occur at the same logical location. + * The canonical example is a self-crossing line tested against a single segment * identical to one of the crossed segments. + * + * @return true if self-noding is required + */ + bool isSelfNodingRequired() const; + + bool isExteriorCheckRequired(bool isA) const; + + bool isResultKnown() const; + + bool getResult() const; + + /** + * Finalize the evaluation. + */ + void finish(); + + void addIntersection(NodeSection* a, NodeSection* b); + + void addPointOnPointInterior(const CoordinateXY* pt); + + void addPointOnPointExterior(bool isGeomA, const CoordinateXY* pt); + + void addPointOnGeometry(bool isA, Location locTarget, int dimTarget, const CoordinateXY* pt); + + /** + * Add topology for a line end. + * The line end point must be "significant"; + * i.e. not contained in an area if the source is a mixed-dimension GC. + * + * @param isLineA the input containing the line end + * @param locLineEnd the location of the line end (Interior or Boundary) + * @param locTarget the location on the target geometry + * @param dimTarget the dimension of the interacting target geometry element, + * (if any), or the dimension of the target + * @param pt the line end coordinate + */ + void addLineEndOnGeometry(bool isLineA, Location locLineEnd, Location locTarget, int dimTarget, const CoordinateXY* pt); + + /** + * Adds topology for an area vertex interaction with a target geometry element. + * Assumes the target geometry element has highest dimension + * (i.e. if the point lies on two elements of different dimension, + * the location on the higher dimension element is provided. + * This is the semantic provided by {@link RelatePointLocator}. + * + * Note that in a GeometryCollection containing overlapping or adjacent polygons, + * the area vertex location may be INTERIOR instead of BOUNDARY. + * + * @param isAreaA the input that is the area + * @param locArea the location on the area + * @param locTarget the location on the target geometry element + * @param dimTarget the dimension of the target geometry element + * @param pt the point of interaction + */ + void addAreaVertex(bool isAreaA, Location locArea, Location locTarget, int dimTarget, const CoordinateXY* pt); + + void addAreaVertexOnArea(bool isAreaA, Location locArea, Location locTarget, const CoordinateXY* pt); + + void evaluateNodes(); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + TopologyComputer(const TopologyComputer&) = delete; + TopologyComputer& operator=(const TopologyComputer&) = delete; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/relateng/TopologyPredicate.h b/deps/libgeos/geos/include/geos/operation/relateng/TopologyPredicate.h new file mode 100644 index 000000000..6b594dbca --- /dev/null +++ b/deps/libgeos/geos/include/geos/operation/relateng/TopologyPredicate.h @@ -0,0 +1,206 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + + +using geos::geom::Envelope; +using geos::geom::Location; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL TopologyPredicate { + +public: + + /* Virtual destructor to ensure proper cleanup of derived classes */ + virtual ~TopologyPredicate() {}; + + /** + * Gets the name of the predicate. + * + * @return the predicate name + */ + virtual std::string name() const = 0; + + /** + * Indicates that the value of the predicate can be finalized + * based on its current state. + */ + virtual void finish() = 0; + + /** + * Tests if the predicate value is known. + * + * @return true if the result is known + */ + virtual bool isKnown() const = 0; + + /** + * Gets the current value of the predicate result. + * The value is only valid if isKnown() is true. + * + * @return the predicate result value + */ + virtual bool value() const = 0; + + /** + * Reports whether this predicate requires self-noding for + * geometries which contain crossing edges + * (for example, LineString, or GeometryCollection + * containing lines or polygons which may self-intersect). + * Self-noding ensures that intersections are computed consistently + * in cases which contain self-crossings and mutual crossings. + * + * Most predicates require this, but it can + * be avoided for simple intersection detection + * (such as in RelatePredicate#intersects() + * and RelatePredicate#disjoint(). + * Avoiding self-noding improves performance for polygonal inputs. + * + * @return true if self-noding is required. + */ + virtual bool requireSelfNoding() const { + return true; + }; + + /** + * Reports whether this predicate requires interaction between + * the input geometries. + * This is the case if + * + * IM[I, I] >= 0 or IM[I, B] >= 0 or IM[B, I] >= 0 or IM[B, B] >= 0 + * + * This allows a fast result if + * the envelopes of the geometries are disjoint. + * + * @return true if the geometries must interact + */ + virtual bool requireInteraction() const { + return true; + }; + + /** + * Reports whether this predicate requires that the source + * cover the target. + * This is the case if + * + * IM[Ext(Src), Int(Tgt)] = F and IM[Ext(Src), Bdy(Tgt)] = F + * + * If true, this allows a fast result if + * the source envelope does not cover the target envelope. + * + * @param isSourceA indicates the source input geometry + * @return true if the predicate requires checking whether the source covers the target + */ + virtual bool requireCovers(bool isSourceA) { + (void)isSourceA; + return false; + } + + /** + * Reports whether this predicate requires checking if the source input intersects + * the Exterior of the target input. + * This is the case if: + * + * IM[Int(Src), Ext(Tgt)] >= 0 or IM[Bdy(Src), Ext(Tgt)] >= 0 + * + * If false, this may permit a faster result in some geometric situations. + * + * @param isSourceA indicates the source input geometry + * @return true if the predicate requires checking whether the source intersects the target exterior + */ + virtual bool requireExteriorCheck(bool isSourceA) const { + (void)isSourceA; + return true; + } + + /** + * Initializes the predicate for a specific geometric case. + * This may allow the predicate result to become known + * if it can be inferred from the dimensions. + * + * @param dimA the dimension of geometry A + * @param dimB the dimension of geometry B + * + * @see Dimension + */ + virtual void init(int dimA, int dimB) { + (void)dimA; + (void)dimB; + }; + + /** + * Initializes the predicate for a specific geometric case. + * This may allow the predicate result to become known + * if it can be inferred from the envelopes. + * + * @param envA the envelope of geometry A + * @param envB the envelope of geometry B + */ + virtual void init(const Envelope& envA, const Envelope& envB) + { + //-- default if envelopes provide no information + (void)envA; + (void)envB; + }; + + /** + * Updates the entry in the DE-9IM intersection matrix + * for given Location in the input geometries. + * + * If this method is called with a {@link Dimension} value + * which is less than the current value for the matrix entry, + * the implementing class should avoid changing the entry + * if this would cause information loss. + * + * @param locA the location on the A axis of the matrix + * @param locB the location on the B axis of the matrix + * @param dimension the dimension value for the entry + * + * @see Dimension + * @see Location + */ + virtual void updateDimension(Location locA, Location locB, int dimension) = 0; + + + friend std::ostream& + operator<<(std::ostream& os, const TopologyPredicate& ns) + { + os << ns.name(); + return os; + } + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/deps/libgeos/geos/include/geos/operation/sharedpaths/SharedPathsOp.h b/deps/libgeos/geos/include/geos/operation/sharedpaths/SharedPathsOp.h index 62a421885..dfd75f69a 100644 --- a/deps/libgeos/geos/include/geos/operation/sharedpaths/SharedPathsOp.h +++ b/deps/libgeos/geos/include/geos/operation/sharedpaths/SharedPathsOp.h @@ -79,12 +79,12 @@ class GEOS_DLL SharedPathsOp { /// @param sameDirection /// Shared edges having the same direction are pushed /// onto this vector. They'll be of type LineString. - /// Ownership of the edges is tranferred. + /// Ownership of the edges is transferred. /// /// @param oppositeDirection /// Shared edges having the opposite direction are pushed /// onto this vector. They'll be of type geom::LineString. - /// Ownership of the edges is tranferred. + /// Ownership of the edges is transferred. /// static void sharedPathsOp(const geom::Geometry& g1, const geom::Geometry& g2, @@ -106,12 +106,12 @@ class GEOS_DLL SharedPathsOp { /// @param sameDirection /// Shared edges having the same direction are pushed /// onto this vector. They'll be of type geom::LineString. - /// Ownership of the edges is tranferred. + /// Ownership of the edges is transferred. /// /// @param oppositeDirection /// Shared edges having the opposite direction are pushed /// onto this vector. They'll be of type geom::LineString. - /// Ownership of the edges is tranferred. + /// Ownership of the edges is transferred. /// void getSharedPaths(PathList& sameDirection, PathList& oppositeDirection); diff --git a/deps/libgeos/geos/include/geos/operation/union/CascadedPolygonUnion.h b/deps/libgeos/geos/include/geos/operation/union/CascadedPolygonUnion.h index 0bcf39426..a89e6fa1c 100644 --- a/deps/libgeos/geos/include/geos/operation/union/CascadedPolygonUnion.h +++ b/deps/libgeos/geos/include/geos/operation/union/CascadedPolygonUnion.h @@ -73,7 +73,7 @@ class GEOS_DLL ClassicUnionStrategy : public UnionStrategy { /** * An alternative way of unioning polygonal geometries - * by using bufer(0). + * by using buffer(0). * Only worth using if regular overlay union fails. */ std::unique_ptr unionPolygonsByBuffer(const geom::Geometry* g0, const geom::Geometry* g1); diff --git a/deps/libgeos/geos/include/geos/operation/union/UnaryUnionOp.h b/deps/libgeos/geos/include/geos/operation/union/UnaryUnionOp.h index 35354ba61..3fed7e32b 100644 --- a/deps/libgeos/geos/include/geos/operation/union/UnaryUnionOp.h +++ b/deps/libgeos/geos/include/geos/operation/union/UnaryUnionOp.h @@ -29,6 +29,8 @@ #include #include +#include + #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class @@ -169,6 +171,8 @@ class GEOS_DLL UnaryUnionOp { void extract(const geom::Geometry& geom) { + util::ensureNoCurvedComponents(geom); + using namespace geom::util; if(! geomFact) { diff --git a/deps/libgeos/geos/include/geos/operation/valid/IndexedNestedPolygonTester.h b/deps/libgeos/geos/include/geos/operation/valid/IndexedNestedPolygonTester.h index 17beb1ea8..9ae9dd020 100644 --- a/deps/libgeos/geos/include/geos/operation/valid/IndexedNestedPolygonTester.h +++ b/deps/libgeos/geos/include/geos/operation/valid/IndexedNestedPolygonTester.h @@ -70,7 +70,7 @@ class GEOS_DLL IndexedNestedPolygonTester { * * @param the shell to test * @param the polygon to test against - * @param coordNested return parametr for found coordinate + * @param coordNested return parameter for found coordinate * @return an interior segment point, or null if the shell is nested correctly */ static bool findIncidentSegmentNestedPoint( diff --git a/deps/libgeos/geos/include/geos/operation/valid/IsSimpleOp.h b/deps/libgeos/geos/include/geos/operation/valid/IsSimpleOp.h index b26d6d53f..1f2abfa68 100644 --- a/deps/libgeos/geos/include/geos/operation/valid/IsSimpleOp.h +++ b/deps/libgeos/geos/include/geos/operation/valid/IsSimpleOp.h @@ -29,9 +29,9 @@ namespace noding { class SegmentString; class BasicSegmentString; } -namespace algorithm { -class BoundaryNodeRule; -} +// namespace algorithm { +// class BoundaryNodeRule; +// } namespace geom { class LineString; class LinearRing; diff --git a/deps/libgeos/geos/include/geos/planargraph/DirectedEdgeStar.h b/deps/libgeos/geos/include/geos/planargraph/DirectedEdgeStar.h index dae69f5a8..4ad9e9c3b 100644 --- a/deps/libgeos/geos/include/geos/planargraph/DirectedEdgeStar.h +++ b/deps/libgeos/geos/include/geos/planargraph/DirectedEdgeStar.h @@ -101,7 +101,7 @@ class GEOS_DLL DirectedEdgeStar { } /** - * \brief Returns the coordinate for the node at wich this + * \brief Returns the coordinate for the node at which this * star is based */ geom::Coordinate& getCoordinate() const; diff --git a/deps/libgeos/geos/include/geos/planargraph/Subgraph.h b/deps/libgeos/geos/include/geos/planargraph/Subgraph.h index c3fa2e6d8..18ee0d593 100644 --- a/deps/libgeos/geos/include/geos/planargraph/Subgraph.h +++ b/deps/libgeos/geos/include/geos/planargraph/Subgraph.h @@ -83,7 +83,7 @@ class GEOS_DLL Subgraph { * * @return a pair with first element being an iterator * to the Edge in set and second element - * being a boolean value indicating wheter + * being a boolean value indicating whether * the Edge has been inserted now or was * already in the set. */ diff --git a/deps/libgeos/geos/include/geos/precision/CommonBitsOp.h b/deps/libgeos/geos/include/geos/precision/CommonBitsOp.h index 69e565be6..9216411fc 100644 --- a/deps/libgeos/geos/include/geos/precision/CommonBitsOp.h +++ b/deps/libgeos/geos/include/geos/precision/CommonBitsOp.h @@ -149,7 +149,7 @@ class GEOS_DLL CommonBitsOp { double distance); /** \brief - * If required, returning the result to the orginal precision + * If required, returning the result to the original precision * if required. * * In this current implementation, no rounding is performed on the diff --git a/deps/libgeos/geos/include/geos/precision/GeometryPrecisionReducer.h b/deps/libgeos/geos/include/geos/precision/GeometryPrecisionReducer.h index 5aca042af..cb0cb9a82 100644 --- a/deps/libgeos/geos/include/geos/precision/GeometryPrecisionReducer.h +++ b/deps/libgeos/geos/include/geos/precision/GeometryPrecisionReducer.h @@ -45,7 +45,7 @@ namespace precision { // geos.precision * It can be forced to be reduced pointwise by using setPointwise(boolean). * Note that in this case the result geometry may be invalid. * Linear and point geometry is always reduced pointwise (i.e. without further change to - * its topology or stucture), since this does not change validity. + * its topology or structure), since this does not change validity. * * By default the geometry precision model is not changed. * This can be overridden by usingsetChangePrecisionModel(boolean). @@ -193,6 +193,8 @@ class GEOS_DLL GeometryPrecisionReducer { std::unique_ptr reduce(const geom::Geometry& geom); + + }; } // namespace geos.precision diff --git a/deps/libgeos/geos/include/geos/precision/PointwisePrecisionReducerTransformer.h b/deps/libgeos/geos/include/geos/precision/PointwisePrecisionReducerTransformer.h index 1b5df1db4..9ea06717f 100644 --- a/deps/libgeos/geos/include/geos/precision/PointwisePrecisionReducerTransformer.h +++ b/deps/libgeos/geos/include/geos/precision/PointwisePrecisionReducerTransformer.h @@ -63,7 +63,7 @@ class GEOS_DLL PointwisePrecisionReducerTransformer : public geom::util::Geometr std::unique_ptr transformCoordinates( const geom::CoordinateSequence* coords, - const geom::Geometry* parent); + const geom::Geometry* parent) override; }; diff --git a/deps/libgeos/geos/include/geos/simplify/ComponentJumpChecker.h b/deps/libgeos/geos/include/geos/simplify/ComponentJumpChecker.h new file mode 100644 index 000000000..59fdd2ecb --- /dev/null +++ b/deps/libgeos/geos/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 + * Copyright (C) 2023 Paul Ramsey + * + * 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 +#include +#include + + +// 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& 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& 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/deps/libgeos/geos/include/geos/simplify/LineSegmentIndex.h b/deps/libgeos/geos/include/geos/simplify/LineSegmentIndex.h index 136d7bedb..3a8de122d 100644 --- a/deps/libgeos/geos/include/geos/simplify/LineSegmentIndex.h +++ b/deps/libgeos/geos/include/geos/simplify/LineSegmentIndex.h @@ -60,7 +60,7 @@ class GEOS_DLL LineSegmentIndex { void remove(const geom::LineSegment* seg); - std::unique_ptr< std::vector > + std::vector query(const geom::LineSegment* seg); diff --git a/deps/libgeos/geos/include/geos/simplify/TaggedLineSegment.h b/deps/libgeos/geos/include/geos/simplify/TaggedLineSegment.h index a6a5394d1..98b341c8d 100644 --- a/deps/libgeos/geos/include/geos/simplify/TaggedLineSegment.h +++ b/deps/libgeos/geos/include/geos/simplify/TaggedLineSegment.h @@ -20,7 +20,7 @@ * makes it useless for a TaggedLineSegment to store copies * of coordinates. Using pointers would be good enough here. * We don't do it to avoid having to break inheritance from - * LineSegment, which has copies intead. Wheter LineSegment + * LineSegment, which has copies instead. Whether LineSegment * itself should be refactored can be discussed. * --strk 2006-04-12 * diff --git a/deps/libgeos/geos/include/geos/simplify/TaggedLineString.h b/deps/libgeos/geos/include/geos/simplify/TaggedLineString.h index fea0a7f46..b23f3a400 100644 --- a/deps/libgeos/geos/include/geos/simplify/TaggedLineString.h +++ b/deps/libgeos/geos/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,23 +61,23 @@ class GEOS_DLL TaggedLineString { public: - typedef std::vector CoordVect; + typedef std::vector CoordVect; typedef std::unique_ptr CoordVectPtr; - typedef geom::CoordinateSequence CoordSeq; + typedef CoordinateSequence CoordSeq; - typedef std::unique_ptr CoordSeqPtr; + typedef std::unique_ptr CoordSeqPtr; TaggedLineString(const geom::LineString* nParentLine, std::size_t minimumSize, - bool preserveEndpoint); + bool bIsRing); ~TaggedLineString(); std::size_t getMinimumSize() const; - bool getPreserveEndpoint() const; + bool isRing() const; const geom::LineString* getParent() const; @@ -82,6 +85,12 @@ class GEOS_DLL TaggedLineString { 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); @@ -96,7 +105,7 @@ class GEOS_DLL TaggedLineString { void addToResult(std::unique_ptr seg); - void removeRingEndpoint(); + const TaggedLineSegment* removeRingEndpoint(); std::unique_ptr asLineString() const; @@ -114,11 +123,11 @@ class GEOS_DLL TaggedLineString { std::size_t minimumSize; - bool preserveEndpoint; + bool m_isRing; void init(); - static std::unique_ptr extractCoordinates( + static std::unique_ptr extractCoordinates( const std::vector& segs); // Copying is turned off diff --git a/deps/libgeos/geos/include/geos/simplify/TaggedLineStringSimplifier.h b/deps/libgeos/geos/include/geos/simplify/TaggedLineStringSimplifier.h index 5ed98e766..3fba7e0d5 100644 --- a/deps/libgeos/geos/include/geos/simplify/TaggedLineStringSimplifier.h +++ b/deps/libgeos/geos/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,37 +92,49 @@ class GEOS_DLL TaggedLineStringSimplifier { // externally owned LineSegmentIndex* outputIndex; + const ComponentJumpChecker* jumpChecker; + std::unique_ptr li; /// non-const as segments are possibly added to it TaggedLineString* line; - const geom::CoordinateSequence* linePts; - - double distanceTolerance; + const CoordinateSequence* linePts; - void simplifySection(std::size_t i, std::size_t j, - std::size_t depth); + void simplifySection(std::size_t i, std::size_t j, std::size_t depth, double distanceTolerance); - void simplifyRingEndpoint(); + 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 size_t excludeStart, const size_t excludeEnd, - const geom::LineSegment& candidateSeg); + bool isTopologyValid( + const TaggedLineString* lineIn, + std::size_t sectionStart, std::size_t sectionEnd, + const LineSegment& flatSeg); + + bool isTopologyValid( + const TaggedLineString* lineIn, + const LineSegment* seg1, const LineSegment* seg2, + const LineSegment& flatSeg); + + bool hasInputIntersection(const LineSegment& flatSeg); - bool hasBadInputIntersection(const TaggedLineString* parentLine, - const size_t excludeStart, const size_t excludeEnd, - const geom::LineSegment& candidateSeg); + bool hasInputIntersection( + const TaggedLineString* lineIn, + std::size_t excludeStart, std::size_t excludeEnd, + const LineSegment& flatSeg); - bool hasBadOutputIntersection(const geom::LineSegment& candidateSeg); + bool isCollinear(const Coordinate& pt, const LineSegment& seg) const; + + bool hasOutputIntersection(const LineSegment& flatSeg); + + bool hasInvalidIntersection( + const LineSegment& seg0, + const LineSegment& seg1) const; - bool hasInteriorIntersection(const geom::LineSegment& seg0, - const geom::LineSegment& seg1) const; std::unique_ptr flatten( std::size_t start, std::size_t end); @@ -142,7 +153,7 @@ class GEOS_DLL TaggedLineStringSimplifier { */ static bool isInLineSection( const TaggedLineString* line, - const size_t excludeStart, const size_t excludeEnd, + const std::size_t excludeStart, const std::size_t excludeEnd, const TaggedLineSegment* seg); /** \brief @@ -158,11 +169,6 @@ class GEOS_DLL TaggedLineStringSimplifier { }; -inline void -TaggedLineStringSimplifier::setDistanceTolerance(double d) -{ - distanceTolerance = d; -} } // namespace geos::simplify } // namespace geos diff --git a/deps/libgeos/geos/include/geos/simplify/TaggedLinesSimplifier.h b/deps/libgeos/geos/include/geos/simplify/TaggedLinesSimplifier.h index f1f1efdd7..12e8baa0b 100644 --- a/deps/libgeos/geos/include/geos/simplify/TaggedLinesSimplifier.h +++ b/deps/libgeos/geos/include/geos/simplify/TaggedLinesSimplifier.h @@ -68,44 +68,15 @@ class GEOS_DLL TaggedLinesSimplifier { */ 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 - 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& tlsVector); private: - void simplify(TaggedLineString& line); - std::unique_ptr inputIndex; std::unique_ptr outputIndex; - std::unique_ptr taggedlineSimplifier; + double distanceTolerance; }; } // namespace geos::simplify diff --git a/deps/libgeos/geos/include/geos/triangulate/polygon/ConstrainedDelaunayTriangulator.h b/deps/libgeos/geos/include/geos/triangulate/polygon/ConstrainedDelaunayTriangulator.h index c8cc8ac33..23c385d21 100644 --- a/deps/libgeos/geos/include/geos/triangulate/polygon/ConstrainedDelaunayTriangulator.h +++ b/deps/libgeos/geos/include/geos/triangulate/polygon/ConstrainedDelaunayTriangulator.h @@ -55,7 +55,7 @@ class GEOS_DLL ConstrainedDelaunayTriangulator { const Geometry* inputGeom; const GeometryFactory* geomFact; - std::unique_ptr compute(); + std::unique_ptr compute() const; static std::unique_ptr toGeometry( const geom::GeometryFactory* geomFact, diff --git a/deps/libgeos/geos/include/geos/triangulate/quadedge/QuadEdgeSubdivision.h b/deps/libgeos/geos/include/geos/triangulate/quadedge/QuadEdgeSubdivision.h index 24492d51c..fd4a94f8d 100644 --- a/deps/libgeos/geos/include/geos/triangulate/quadedge/QuadEdgeSubdivision.h +++ b/deps/libgeos/geos/include/geos/triangulate/quadedge/QuadEdgeSubdivision.h @@ -110,7 +110,7 @@ class GEOS_DLL QuadEdgeSubdivision { * that encloses a supplied bounding box. * A new super-bounding box that contains the triangle is computed and stored. * - * @param env the bouding box to surround + * @param env the bounding box to surround * @param tolerance the tolerance value for determining if two sites are equal */ QuadEdgeSubdivision(const geom::Envelope& env, double tolerance); diff --git a/deps/libgeos/geos/include/geos/triangulate/quadedge/TrianglePredicate.h b/deps/libgeos/geos/include/geos/triangulate/quadedge/TrianglePredicate.h index f38015950..c356928ce 100644 --- a/deps/libgeos/geos/include/geos/triangulate/quadedge/TrianglePredicate.h +++ b/deps/libgeos/geos/include/geos/triangulate/quadedge/TrianglePredicate.h @@ -19,15 +19,14 @@ #pragma once #include +#include namespace geos { namespace geom { -class Coordinate; +class CoordinateXY; } } -using geos::geom::Coordinate; - namespace geos { namespace triangulate { namespace quadedge { @@ -49,6 +48,8 @@ namespace quadedge { */ class GEOS_DLL TrianglePredicate { public: + using CoordinateXY = geos::geom::CoordinateXY; + /** * Tests if a point is inside the circle defined by * the triangle with vertices a, b, c (oriented counter-clockwise). @@ -61,9 +62,9 @@ class GEOS_DLL TrianglePredicate { * @param p the point to test * @return true if this point is inside the circle defined by the points a, b, c */ - static bool isInCircleNonRobust( - const Coordinate& a, const Coordinate& b, const Coordinate& c, - const Coordinate& p); + static geom::Location isInCircleNonRobust( + const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c, + const CoordinateXY& p); /** * Tests if a point is inside the circle defined by @@ -82,9 +83,9 @@ class GEOS_DLL TrianglePredicate { * @param p the point to test * @return true if this point is inside the circle defined by the points a, b, c */ - static bool isInCircleNormalized( - const Coordinate& a, const Coordinate& b, const Coordinate& c, - const Coordinate& p); + static geom::Location isInCircleNormalized( + const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c, + const CoordinateXY& p); private: /** @@ -95,8 +96,8 @@ class GEOS_DLL TrianglePredicate { * @param b a vertex of the triangle * @param c a vertex of the triangle */ - static double triArea(const Coordinate& a, - const Coordinate& b, const Coordinate& c); + static double triArea(const CoordinateXY& a, + const CoordinateXY& b, const CoordinateXY& c); public: /** @@ -110,9 +111,9 @@ class GEOS_DLL TrianglePredicate { * @param p the point to test * @return true if this point is inside the circle defined by the points a, b, c */ - static bool isInCircleRobust( - const Coordinate& a, const Coordinate& b, const Coordinate& c, - const Coordinate& p); + static geom::Location isInCircleRobust( + const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c, + const CoordinateXY& p); } ; diff --git a/deps/libgeos/geos/include/geos/triangulate/quadedge/Vertex.h b/deps/libgeos/geos/include/geos/triangulate/quadedge/Vertex.h index ce621baa2..abb7bdcfb 100644 --- a/deps/libgeos/geos/include/geos/triangulate/quadedge/Vertex.h +++ b/deps/libgeos/geos/include/geos/triangulate/quadedge/Vertex.h @@ -205,7 +205,7 @@ class GEOS_DLL Vertex { * @return true if this vertex is in the circumcircle of (a,b,c) */ bool isInCircle(const Vertex& a, const Vertex& b, const Vertex& c) const { - return triangulate::quadedge::TrianglePredicate::isInCircleRobust(a.p, b.p, c.p, this->p); + return triangulate::quadedge::TrianglePredicate::isInCircleRobust(a.p, b.p, c.p, this->p) == geom::Location::INTERIOR; } /** diff --git a/deps/libgeos/geos/include/geos/util.h b/deps/libgeos/geos/include/geos/util.h index 661dff451..fe0825b85 100644 --- a/deps/libgeos/geos/include/geos/util.h +++ b/deps/libgeos/geos/include/geos/util.h @@ -24,6 +24,7 @@ #include #include #include +#include // // Private macros definition @@ -59,6 +60,27 @@ template inline To down_cast(From* f) } } // namespace detail + +namespace util { + +template +void ensureNoCurvedComponents(const T& geom) +{ + if (geom.hasCurvedComponents()) { + throw UnsupportedOperationException("Curved geometry types are not supported."); + } +} + +template +void ensureNoCurvedComponents(const T* geom) +{ + ensureNoCurvedComponents(*geom); +} + + +} + + } // namespace geos #endif // GEOS_UTIL_H diff --git a/deps/libgeos/geos/include/geos/util/CoordinateArrayFilter.h b/deps/libgeos/geos/include/geos/util/CoordinateArrayFilter.h index 9ef7e4242..0bed5c406 100644 --- a/deps/libgeos/geos/include/geos/util/CoordinateArrayFilter.h +++ b/deps/libgeos/geos/include/geos/util/CoordinateArrayFilter.h @@ -17,46 +17,69 @@ #include #include +#include +#include #include #include #include +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + namespace geos { namespace util { // geos::util -/** \brief - * A CoordinateFilter that adds read-only pointers - * to every Coordinate in a Geometry to a given - * vector. - * - * Last port: util/CoordinateArrayFilter.java rev. 1.15 +/* + * A CoordinateFilter that fills a vector of Coordinate const pointers. */ -class GEOS_DLL CoordinateArrayFilter: public geom::CoordinateFilter { -private: - geom::Coordinate::ConstVect& pts; // target vector reference +class GEOS_DLL CoordinateArrayFilter : public geom::CoordinateInspector { public: - /** \brief - * Constructs a CoordinateArrayFilter. - * - * @param target The destination vector. - */ - CoordinateArrayFilter(geom::Coordinate::ConstVect& target) - : - pts(target) + + CoordinateArrayFilter(std::vector& target) + : pts(target) {} - virtual - ~CoordinateArrayFilter() {} + /** + * Destructor. + * Virtual dctor promises appropriate behaviour when someone will + * delete a derived-class object via a base-class pointer. + * http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7 + */ + ~CoordinateArrayFilter() override {} - virtual void - filter_ro(const geom::Coordinate* coord) + template + void filter(const CoordType* coord) { pts.push_back(coord); } + + void filter(const geom::CoordinateXY*) { + assert(0); // not supported + } + + void filter(const geom::CoordinateXYM*) { + assert(0); // not supported + } + +private: + std::vector& pts; // target set reference + + // Declare type as noncopyable + CoordinateArrayFilter(const CoordinateArrayFilter& other) = delete; + CoordinateArrayFilter& operator=(const CoordinateArrayFilter& rhs) = delete; }; -} // namespace geos.util + + + +} // namespace geos::util } // namespace geos +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/deps/libgeos/geos/include/geos/util/UniqueCoordinateArrayFilter.h b/deps/libgeos/geos/include/geos/util/UniqueCoordinateArrayFilter.h index 17371169a..5b0afb025 100644 --- a/deps/libgeos/geos/include/geos/util/UniqueCoordinateArrayFilter.h +++ b/deps/libgeos/geos/include/geos/util/UniqueCoordinateArrayFilter.h @@ -47,6 +47,12 @@ class GEOS_DLL UniqueCoordinateArrayFilter : public geom::CoordinateInspector& target) : pts(target) + , maxUnique(NO_COORD_INDEX) + {} + + UniqueCoordinateArrayFilter(std::vector& target, std::size_t p_maxUnique) + : pts(target) + , maxUnique(p_maxUnique) {} /** @@ -69,6 +75,9 @@ class GEOS_DLL UniqueCoordinateArrayFilter : public geom::CoordinateInspector maxUnique) { + done = true; + } } void filter(const geom::CoordinateXY*) { @@ -80,15 +89,22 @@ class GEOS_DLL UniqueCoordinateArrayFilter : public geom::CoordinateInspector& pts; // target set reference std::set uniqPts; // unique points set + std::size_t maxUnique; // stop visiting when we have this many unique coordinates + bool done = false; // Declare type as noncopyable UniqueCoordinateArrayFilter(const UniqueCoordinateArrayFilter& other) = delete; UniqueCoordinateArrayFilter& operator=(const UniqueCoordinateArrayFilter& rhs) = delete; }; + } // namespace geos::util } // namespace geos diff --git a/deps/libgeos/geos/include/geos/util/string.h b/deps/libgeos/geos/include/geos/util/string.h index 32375e0ca..374dfa03b 100644 --- a/deps/libgeos/geos/include/geos/util/string.h +++ b/deps/libgeos/geos/include/geos/util/string.h @@ -25,5 +25,7 @@ bool endsWith(const std::string & s, char suffix); bool startsWith(const std::string & s, const std::string & prefix); bool startsWith(const std::string & s, char prefix); +void toUpper(std::string& s); + +} } -} \ No newline at end of file diff --git a/deps/libgeos/geos/include/geos/vend/json.hpp b/deps/libgeos/geos/include/geos/vend/json.hpp index 492118a5f..c81c5c510 100644 --- a/deps/libgeos/geos/include/geos/vend/json.hpp +++ b/deps/libgeos/geos/include/geos/vend/json.hpp @@ -5255,7 +5255,7 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) } // This class only handles inputs of input_buffer_adapter type. -// It's required so that expressions like {ptr, len} can be implicitely casted +// It's required so that expressions like {ptr, len} can be implicitly casted // to the correct adapter. class span_input_adapter { @@ -10030,7 +10030,7 @@ class binary_reader @return whether conversion completed - @note This function needs to respect the system's endianess, because + @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ @@ -10202,7 +10202,7 @@ class binary_reader /// the number of characters read std::size_t chars_read = 0; - /// whether we can assume little endianess + /// whether we can assume little endianness const bool is_little_endian = little_endianess(); /// the SAX parser @@ -14345,7 +14345,7 @@ class binary_writer @tparam OutputIsLittleEndian Set to true if output data is required to be little endian - @note This function needs to respect the system's endianess, because bytes + @note This function needs to respect the system's endianness, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ @@ -14428,7 +14428,7 @@ class binary_writer } private: - /// whether we can assume little endianess + /// whether we can assume little endianness const bool is_little_endian = little_endianess(); /// the output @@ -17528,7 +17528,7 @@ class basic_json - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. - The subtype is then added as singed 8-bit integer. + The subtype is then added as signed 8-bit integer. - If no subtype is given, the bin family (bin8, bin16, bin32) is used. - BSON - If a subtype is given, it is used and added as unsigned 8-bit integer. @@ -21482,7 +21482,7 @@ class basic_json `key()` returns an empty string. @warning Using `items()` on temporary objects is dangerous. Make sure the - object's lifetime exeeds the iteration. See + object's lifetime exceeds the iteration. See for more information. diff --git a/deps/libgeos/geos/include/geos/version.h b/deps/libgeos/geos/include/geos/version.h index c3d3335a6..e9ff92cf7 100644 --- a/deps/libgeos/geos/include/geos/version.h +++ b/deps/libgeos/geos/include/geos/version.h @@ -19,15 +19,15 @@ #endif #ifndef GEOS_VERSION_MINOR -#define GEOS_VERSION_MINOR 10 +#define GEOS_VERSION_MINOR 12 #endif #ifndef GEOS_VERSION_PATCH -#define GEOS_VERSION_PATCH 2 +#define GEOS_VERSION_PATCH 1 #endif #ifndef GEOS_VERSION -#define GEOS_VERSION "3.10.2" +#define GEOS_VERSION "3.12.1" #endif #ifndef GEOS_JTS_PORT diff --git a/deps/libgeos/geos/release.md b/deps/libgeos/geos/release.md index 5e8bb37b7..f4080b742 100644 --- a/deps/libgeos/geos/release.md +++ b/deps/libgeos/geos/release.md @@ -1,16 +1,55 @@ +2024-09-06 -2023-11-11 +- New things: + - Add Angle::sinCosSnap to avoid small errors, e.g. with buffer operations (GH-978, Mike Taves) + - Add classes for curved geometry types: CircularString, CompoundCurve, CurvedPolygon, MultiCurve, + MultiSurface (GH-1046, Dan Baston/German QGIS users group/Canton of Basel-Landschaft/Canton of Zug) + - Support curved geometry types in WKT/WKB readers/writers (GH-1046, GH-1104, GH-1106, Dan Baston) + - 3D read and write support for GeoJSON (GH-1150, Oreilles) + - Port of RelateNG https://github.com/locationtech/jts/pull/1052 (Martin Davis, Paul Ramsey) + - Rewrite of boolean predicates and relate matrix calculations + - "Prepared" mode now available for all predicates and relate matrix + - CAPI functions GEOSPreparedRelate and GEOSPreparedRelatePattern expose new functionality + - CAPI implementations of GEOSPreparedTouches, etc, that were previously defaulting + into non-prepared implementations now default into the RelateNG prepared implementation + - Prepared implementations for Intersects, Covers, still use the older implementations + - https://lin-ear-th-inking.blogspot.com/2024/05/jts-topological-relationships-next.html + - https://lin-ear-th-inking.blogspot.com/2024/05/relateng-performance.html -- Fixes: - - Remove undefined behaviour in use of null PrecisionModel (GH-931, Jeff Walton) - - Explicitly set endianness for some tests so that output matches expected (GH-934, Paul Ramsey) +- Breaking Changes + - Zero-length linestrings (eg LINESTRING(1 1, 1 1)) are now treated as equivalent to points (POINT(1 1)) in boolean predicates + - CMake 3.15 or later is requried (GH-1143, Mike Taves) + +- Fixes/Improvements: + - WKTReader: Points with all-NaN coordinates are not considered empty anymore (GH-927, Casper van der Wel) + - WKTWriter: Points with all-NaN coordinates are written as such (GH-927, Casper van der Wel) + - ConvexHull: Performance improvement for larger geometries (JTS-985, Martin Davis) + - Distance: Improve performance, especially for point-point distance (GH-1067, Dan Baston) + - Intersection: change to using DoubleDouble computation to improve robustness (GH-937, Martin Davis) + - Fix LargestEmptyCircle to respect polygonal obstacles (GH-939, Martin Davis) + - Fix WKTWriter to emit EMPTY elements in multi-geometries (GH-952, Mike Taves) - Fix IncrementalDelaunayTriangulator to ensure triangulation boundary is convex (GH-953, Martin Davis) - - Improve scale handling for PrecisionModel (GH-956, Martin Davis) - Fix PreparedLineStringDistance for lines within envelope and polygons (GH-959, Martin Davis) + - Improve scale handling for PrecisionModel (GH-956, Martin Davis) - Fix error in CoordinateSequence::add when disallowing repeated points (GH-963, Dan Baston) + - Fix WKTWriter::writeTrimmedNumber for big and small values (GH-973, Mike Taves) - Fix InteriorPointPoint to handle empty elements (GH-977, Martin Davis) - - Skip over testing empty distances for mixed collections. (GH-979, Paul Ramsey) - Fix TopologyPreservingSimplifier endpoint handling to avoid self-intersections (GH-986, Martin Davis) - - Build warnings with Visual Studio (GH-929, Jeff Mckenna, Paul Ramsey) - - Fix CMake on Windows with Visual Studio (GH-945, Aaron Barany) + - Fix spatial predicates for MultiPoint with EMPTY (GH-989, Martin Davis) + - Fix DiscreteHausdorffDistance for LinearRing (GH-1000, Martin Davis) + - Fix IsSimpleOp for MultiPoint with empty element (GH-1005, Martin Davis) + - Fix PreparedPolygonContains for GC with MultiPoint (GH-1008, Martin Davis) + - Fix reading WKT with EMPTY token with white space (GH-1025, Mike Taves) + - Fix buffer Inverted Ring Removal check (GH-1056, Martin Davis) + - Add PointLocation.isOnSegment and remove LineIntersector point methods (GH-1083, Martin Davis) + - Densify: Interpolate Z coordinates (GH-1094) + - GEOSLineSubstring: Fix crash on NaN length fractions (GH-1088, Dan Baston) + - MinimumClearance: Fix crash on NaN inputs (GH-1082, Dan Baston) + - Centroid: Fix crash on polygons with empty holes (GH-1075, Dan Baston) + - GEOSRelatePatternMatch: Fix crash on invalid DE-9IM pattern (GH-1089, Dan Baston) + - CoveragePolygonValidator: add section performance optimization (GH-1099, Martin Davis) + - TopologyPreservingSimplifier: fix to remove ring endpoints safely (GH-1110, Martin Davis) + - TopologyPreservingSimplifier: fix stack overflow on degenerate inputs (GH-1113, Dan Baston) + - DouglasPeuckerSimplifier: fix stack overflow on NaN tolerance (GH-1114, Dan Baston) + - GEOSConcaveHullOfPolygons: Avoid crash on zero-area input (GH-1076, Dan Baston) diff --git a/deps/libgeos/geos/src/algorithm/Angle.cpp b/deps/libgeos/geos/src/algorithm/Angle.cpp index 3b0aebf41..7069607f5 100644 --- a/deps/libgeos/geos/src/algorithm/Angle.cpp +++ b/deps/libgeos/geos/src/algorithm/Angle.cpp @@ -195,7 +195,7 @@ Angle::diff(double ang1, double ang2) } if(delAngle > MATH_PI) { - delAngle = (2 * MATH_PI) - delAngle; + delAngle = PI_TIMES_2 - delAngle; } return delAngle; diff --git a/deps/libgeos/geos/src/algorithm/Area.cpp b/deps/libgeos/geos/src/algorithm/Area.cpp index e51954dbc..3d2375c95 100644 --- a/deps/libgeos/geos/src/algorithm/Area.cpp +++ b/deps/libgeos/geos/src/algorithm/Area.cpp @@ -20,6 +20,10 @@ #include #include +#include +#include +#include +#include using geos::geom::CoordinateXY; @@ -93,7 +97,58 @@ Area::ofRingSigned(const geom::CoordinateSequence* ring) return sum / 2.0; } +double +Area::ofClosedCurve(const geom::Curve& ring) { + if (!ring.isClosed()) { + throw util::IllegalArgumentException("Argument is not closed"); + } + + double sum = 0; + + for (std::size_t i = 0; i < ring.getNumCurves(); i++) { + const geom::SimpleCurve& section = *ring.getCurveN(i); + + if (section.isEmpty()) { + continue; + } + + const geom::CoordinateSequence& coords = *section.getCoordinatesRO(); + + if (section.isCurved()) { + for (std::size_t j = 2; j < coords.size(); j += 2) { + const CoordinateXY& p0 = coords.getAt(j-2); + const CoordinateXY& p1 = coords.getAt(j-1); + const CoordinateXY& p2 = coords.getAt(j); + double triangleArea = 0.5*(p0.x*p2.y - p2.x*p0.y); + sum += triangleArea; + + geom::CircularArc arc(p0, p1, p2); + if (arc.isLinear()) { + continue; + } + + double circularSegmentArea = arc.getArea(); + + if (algorithm::Orientation::index(p0, p2, p1) == algorithm::Orientation::CLOCKWISE) { + sum += circularSegmentArea; + } else { + sum -= circularSegmentArea; + } + } + } else { + for (std::size_t j = 1; j < coords.size(); j++) { + const CoordinateXY& p0 = coords.getAt(j-1); + const CoordinateXY& p1 = coords.getAt(j); + + double triangleArea = 0.5*(p0.x*p1.y - p1.x*p0.y); + sum += triangleArea; + } + } + } + + return std::abs(sum); +} } // namespace geos.algorithm } //namespace geos diff --git a/deps/libgeos/geos/src/algorithm/BoundaryNodeRule.cpp b/deps/libgeos/geos/src/algorithm/BoundaryNodeRule.cpp index c7417b9d5..393ca5e87 100644 --- a/deps/libgeos/geos/src/algorithm/BoundaryNodeRule.cpp +++ b/deps/libgeos/geos/src/algorithm/BoundaryNodeRule.cpp @@ -48,7 +48,11 @@ class Mod2BoundaryNodeRule : public BoundaryNodeRule { { // the "Mod-2 Rule" return boundaryCount % 2 == 1; - } + }; + std::string + toString() const override { + return "Mod2BoundaryNodeRule"; + }; }; @@ -84,7 +88,11 @@ class EndPointBoundaryNodeRule : public BoundaryNodeRule { isInBoundary(int boundaryCount) const override { return boundaryCount > 0; - } + }; + std::string + toString() const override { + return "EndPointBoundaryNodeRule"; + }; }; /** @@ -103,6 +111,10 @@ class MultiValentEndPointBoundaryNodeRule : public BoundaryNodeRule { { return boundaryCount > 1; } + std::string + toString() const override { + return "MultiValentEndPointBoundaryNodeRule"; + }; }; /** @@ -120,6 +132,10 @@ class MonoValentEndPointBoundaryNodeRule : public BoundaryNodeRule { { return boundaryCount == 1; } + std::string + toString() const override { + return "MonoValentEndPointBoundaryNodeRule"; + }; }; Mod2BoundaryNodeRule mod2Rule; diff --git a/deps/libgeos/geos/src/algorithm/CGAlgorithmsDD.cpp b/deps/libgeos/geos/src/algorithm/CGAlgorithmsDD.cpp index b58acfe65..ccfef602e 100644 --- a/deps/libgeos/geos/src/algorithm/CGAlgorithmsDD.cpp +++ b/deps/libgeos/geos/src/algorithm/CGAlgorithmsDD.cpp @@ -80,7 +80,7 @@ CGAlgorithmsDD::orientationIndex(double p1x, double p1y, } -// inlining this method worsened performance slighly +// inlining this method worsened performance slightly int CGAlgorithmsDD::orientationIndex(const CoordinateXY& p1, const CoordinateXY& p2, diff --git a/deps/libgeos/geos/src/algorithm/Centroid.cpp b/deps/libgeos/geos/src/algorithm/Centroid.cpp index fa9355421..727bb282a 100644 --- a/deps/libgeos/geos/src/algorithm/Centroid.cpp +++ b/deps/libgeos/geos/src/algorithm/Centroid.cpp @@ -28,6 +28,8 @@ #include // for std::abs +#include "geos/util.h" + using namespace geos::geom; namespace geos { @@ -68,6 +70,8 @@ Centroid::getCentroid(CoordinateXY& cent) const void Centroid::add(const Geometry& geom) { + util::ensureNoCurvedComponents(geom); + if(geom.isEmpty()) { return; } @@ -124,6 +128,10 @@ Centroid::addShell(const CoordinateSequence& pts) void Centroid::addHole(const CoordinateSequence& pts) { + if (pts.isEmpty()) { + return; + } + bool isPositiveArea = Orientation::isCCW(&pts); for(std::size_t i = 0, e = pts.size() - 1; i < e; ++i) { addTriangle(*areaBasePt, pts.getAt(i), pts.getAt(i + 1), isPositiveArea); diff --git a/deps/libgeos/geos/src/algorithm/CircularArcs.cpp b/deps/libgeos/geos/src/algorithm/CircularArcs.cpp new file mode 100644 index 000000000..949f18509 --- /dev/null +++ b/deps/libgeos/geos/src/algorithm/CircularArcs.cpp @@ -0,0 +1,124 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include +#include +#include +#include + +using geos::geom::CoordinateXY; + +namespace geos { +namespace algorithm { + + +CoordinateXY +CircularArcs::getCenter(const CoordinateXY& p0, const CoordinateXY& p1, const CoordinateXY& p2) +{ + if (p0.equals2D(p2)) { + // Closed circle + return { 0.5*(p0.x + p1.x), 0.5*(p0.y + p1.y) }; + } + + // Circumcenter formulas from Graphics Gems III + CoordinateXY a{p1.x - p2.x, p1.y - p2.y}; + CoordinateXY b{p2.x - p0.x, p2.y - p0.y}; + CoordinateXY c{p0.x - p1.x, p0.y - p1.y}; + + double d1 = -(b.x*c.x + b.y*c.y); + double d2 = -(c.x*a.x + c.y*a.y); + double d3 = -(a.x*b.x + a.y*b.y); + + double e1 = d2*d3; + double e2 = d3*d1; + double e3 = d1*d2; + double e = e1 + e2 + e3; + + CoordinateXY G3{p0.x + p1.x + p2.x, p0.y + p1.y + p2.y}; + CoordinateXY H {(e1*p0.x + e2*p1.x + e3*p2.x) / e, (e1*p0.y + e2*p1.y + e3*p2.y) / e}; + + CoordinateXY center = {0.5*(G3.x - H.x), 0.5*(G3.y - H.y)}; + + return center; +} + +void +CircularArcs::expandEnvelope(geom::Envelope& e, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2) +{ + e.expandToInclude(p0); + e.expandToInclude(p1); + e.expandToInclude(p2); + + CoordinateXY center = getCenter(p0, p1, p2); + + // zero-length arc + if (center.equals2D(p0) || center.equals2D(p1)) { + return; + } + + // collinear + if (std::isnan(center.x)) { + return; + } + + auto orientation = Orientation::index(center, p0, p1); + + //* 1 | 0 + //* --+-- + //* 2 | 3 + + using geom::Quadrant; + + auto q0 = geom::Quadrant::quadrant(center, p0); + auto q2 = geom::Quadrant::quadrant(center, p2); + double R = center.distance(p1); + + if (q0 == q2) { + // Start and end quadrants are the same. Either the arc crosses all of + // the axes, or none of the axes. + if (Orientation::index(center, p1, p2) != orientation) { + e.expandToInclude({center.x, center.y + R}); + e.expandToInclude({center.x - R, center.y}); + e.expandToInclude({center.x, center.y - R}); + e.expandToInclude({center.x + R, center.y}); + } + + return; + } + + if (orientation == Orientation::CLOCKWISE) { + std::swap(q0, q2); + } + + for (auto q = q0 + 1; (q % 4) != ((q2+1) % 4); q++) { + switch (q % 4) { + case Quadrant::NW: + e.expandToInclude({center.x, center.y + R}); + break; + case Quadrant::SW: + e.expandToInclude({center.x - R, center.y}); + break; + case Quadrant::SE: + e.expandToInclude({center.x, center.y - R}); + break; + case Quadrant::NE: + e.expandToInclude({center.x + R, center.y}); + break; + } + } +} + +} +} diff --git a/deps/libgeos/geos/src/algorithm/ConvexHull.cpp b/deps/libgeos/geos/src/algorithm/ConvexHull.cpp index 205ca1ad4..7d8e34374 100644 --- a/deps/libgeos/geos/src/algorithm/ConvexHull.cpp +++ b/deps/libgeos/geos/src/algorithm/ConvexHull.cpp @@ -126,7 +126,7 @@ ConvexHull::toCoordinateSequence(Coordinate::ConstVect& cv) /* private */ void -ConvexHull::computeOctPts(const Coordinate::ConstVect& p_inputPts, +ConvexHull::computeInnerOctolateralPts(const Coordinate::ConstVect& p_inputPts, Coordinate::ConstVect& pts) { // Initialize all slots with first input coordinate @@ -164,10 +164,10 @@ ConvexHull::computeOctPts(const Coordinate::ConstVect& p_inputPts, /* private */ bool -ConvexHull::computeOctRing(const Coordinate::ConstVect& p_inputPts, +ConvexHull::computeInnerOctolateralRing(const Coordinate::ConstVect& p_inputPts, Coordinate::ConstVect& dest) { - computeOctPts(p_inputPts, dest); + computeInnerOctolateralPts(p_inputPts, dest); // Remove consecutive equal Coordinates // unique() returns an iterator to the end of the resulting @@ -191,7 +191,7 @@ ConvexHull::reduce(Coordinate::ConstVect& pts) { Coordinate::ConstVect polyPts; - if(! computeOctRing(pts, polyPts)) { + if(! computeInnerOctolateralRing(pts, polyPts)) { // unable to compute interior polygon for some reason return; } @@ -234,27 +234,23 @@ ConvexHull::padArray3(geom::Coordinate::ConstVect& pts) std::unique_ptr ConvexHull::getConvexHull() { - std::size_t nInputPts = inputPts.size(); - - if(nInputPts == 0) { // Return an empty geometry - return geomFactory->createEmptyGeometry(); - } + std::unique_ptr fewPointsGeom = createFewPointsResult(); + if (fewPointsGeom != nullptr) + return fewPointsGeom; - if(nInputPts == 1) { // Return a Point - // Copy the Coordinate from the ConstVect - return std::unique_ptr(geomFactory->createPoint(*(inputPts[0]))); - } + util::CoordinateArrayFilter filter(inputPts); + inputGeom->apply_ro(&filter); - if(nInputPts == 2) { // Return a LineString - // Copy all Coordinates from the ConstVect - auto cs = toCoordinateSequence(inputPts); - return geomFactory->createLineString(std::move(cs)); - } + std::size_t nInputPts = inputPts.size(); // use heuristic to reduce points, if large - if(nInputPts > 50) { + if(nInputPts > TUNING_REDUCE_SIZE) { reduce(inputPts); } + else { + inputPts.clear(); + extractUnique(inputPts, NO_COORD_INDEX); + } GEOS_CHECK_FOR_INTERRUPTS(); @@ -399,5 +395,45 @@ ConvexHull::cleanRing(const Coordinate::ConstVect& original, } +/* +* Returns true if the extraction is stopped +* by the maxPts restriction. That is, if there +* are yet more points to be processed. +*/ +/* private */ +bool +ConvexHull::extractUnique(Coordinate::ConstVect& pts, std::size_t maxPts) +{ + util::UniqueCoordinateArrayFilter filter(pts, maxPts); + inputGeom->apply_ro(&filter); + return filter.isDone(); +} + + +/* private */ +std::unique_ptr +ConvexHull::createFewPointsResult() +{ + Coordinate::ConstVect uniquePts; + bool ok = extractUnique(uniquePts, 2); + + if (ok) { + return nullptr; + } + + if(uniquePts.size() == 0) { // Return an empty geometry + return geomFactory->createEmptyGeometry(); + } + else if(uniquePts.size() == 1) { // Return a Point + // Copy the Coordinate from the ConstVect + return std::unique_ptr(geomFactory->createPoint(*(uniquePts[0]))); + } + else { // Return a LineString + auto cs = toCoordinateSequence(uniquePts); + return geomFactory->createLineString(std::move(cs)); + } +} + + } // namespace geos.algorithm } // namespace geos diff --git a/deps/libgeos/geos/src/algorithm/InteriorPointLine.cpp b/deps/libgeos/geos/src/algorithm/InteriorPointLine.cpp index 85d6bd459..5d791d8d2 100644 --- a/deps/libgeos/geos/src/algorithm/InteriorPointLine.cpp +++ b/deps/libgeos/geos/src/algorithm/InteriorPointLine.cpp @@ -65,6 +65,7 @@ InteriorPointLine::addInterior(const Geometry* geom) { const LineString* ls = dynamic_cast(geom); if (ls) { + if (ls->isEmpty()) return; addInterior(ls->getCoordinatesRO()); return; } diff --git a/deps/libgeos/geos/src/algorithm/Intersection.cpp b/deps/libgeos/geos/src/algorithm/Intersection.cpp index 6a69596de..7970f6f8f 100644 --- a/deps/libgeos/geos/src/algorithm/Intersection.cpp +++ b/deps/libgeos/geos/src/algorithm/Intersection.cpp @@ -15,7 +15,10 @@ #include #include +#include +#include #include +#include namespace geos { namespace algorithm { // geos.algorithm @@ -26,59 +29,66 @@ geom::CoordinateXY Intersection::intersection(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2, const geom::CoordinateXY& q1, const geom::CoordinateXY& q2) { - double minX0 = p1.x < p2.x ? p1.x : p2.x; - double minY0 = p1.y < p2.y ? p1.y : p2.y; - double maxX0 = p1.x > p2.x ? p1.x : p2.x; - double maxY0 = p1.y > p2.y ? p1.y : p2.y; - - double minX1 = q1.x < q2.x ? q1.x : q2.x; - double minY1 = q1.y < q2.y ? q1.y : q2.y; - double maxX1 = q1.x > q2.x ? q1.x : q2.x; - double maxY1 = q1.y > q2.y ? q1.y : q2.y; - - double intMinX = minX0 > minX1 ? minX0 : minX1; - double intMaxX = maxX0 < maxX1 ? maxX0 : maxX1; - double intMinY = minY0 > minY1 ? minY0 : minY1; - double intMaxY = maxY0 < maxY1 ? maxY0 : maxY1; - - double midx = (intMinX + intMaxX) / 2.0; - double midy = (intMinY + intMaxY) / 2.0; - - // condition ordinate values by subtracting midpoint - double p1x = p1.x - midx; - double p1y = p1.y - midy; - double p2x = p2.x - midx; - double p2y = p2.y - midy; - double q1x = q1.x - midx; - double q1y = q1.y - midy; - double q2x = q2.x - midx; - double q2y = q2.y - midy; - - // unrolled computation using homogeneous coordinates eqn - double px = p1y - p2y; - double py = p2x - p1x; - double pw = p1x * p2y - p2x * p1y; - - double qx = q1y - q2y; - double qy = q2x - q1x; - double qw = q1x * q2y - q2x * q1y; + return CGAlgorithmsDD::intersection(p1, p2, q1, q2); +} - double x = py * qw - qy * pw; - double y = qx * pw - px * qw; - double w = px * qy - qx * py; - double xInt = x/w; - double yInt = y/w; - geom::CoordinateXY rv; - // check for parallel lines - if (!std::isfinite(xInt) || !std::isfinite(yInt)) { - rv.setNull(); - return rv; +/** +* Computes the intersection point of a line and a line segment (if any). +* There will be no intersection point if: +* +* * the segment does not intersect the line +* * the line or the segment are degenerate (have zero length) +* +* If the segment is collinear with the line the first segment endpoint is returned. +* +* @param line1 a point on the line +* @param line2 a point on the line +* @param seg1 an endpoint of the line segment +* @param seg2 an endpoint of the line segment +* @return the intersection point, or null if it is not possible to find an intersection +*/ +/* public static */ +geom::CoordinateXY +Intersection::intersectionLineSegment( + const geom::CoordinateXY& line1, + const geom::CoordinateXY& line2, + const geom::CoordinateXY& seg1, + const geom::CoordinateXY& seg2) +{ + int orientS1 = Orientation::index(line1, line2, seg1); + if (orientS1 == 0) + return seg1; + + int orientS2 = Orientation::index(line1, line2, seg2); + if (orientS2 == 0) + return seg2; + + /** + * If segment lies completely on one side of the line, it does not intersect + */ + if ((orientS1 > 0 && orientS2 > 0) || (orientS1 < 0 && orientS2 < 0)) { + return geom::CoordinateXY::getNull(); } - // de-condition intersection point - rv.x = xInt + midx; - rv.y = yInt + midy; - return rv; + + /** + * The segment intersects the line. + * The full line-line intersection is used to compute the intersection point. + */ + geom::CoordinateXY intPt = intersection(line1, line2, seg1, seg2); + if (!intPt.isNull()) + return intPt; + + /** + * Due to robustness failure it is possible the intersection computation will return null. + * In this case choose the closest point + */ + double dist1 = Distance::pointToLinePerpendicular(seg1, line1, line2); + double dist2 = Distance::pointToLinePerpendicular(seg2, line1, line2); + if (dist1 < dist2) + return seg1; + else + return seg2; } diff --git a/deps/libgeos/geos/src/algorithm/LineIntersector.cpp b/deps/libgeos/geos/src/algorithm/LineIntersector.cpp index 61ae787d5..b3901b9b3 100644 --- a/deps/libgeos/geos/src/algorithm/LineIntersector.cpp +++ b/deps/libgeos/geos/src/algorithm/LineIntersector.cpp @@ -212,40 +212,6 @@ LineIntersector::computeIntersection(const CoordinateSequence& p, std::size_t p0 CoordinateSequences::binaryDispatch(p, q, dis); } -/*public*/ -void -LineIntersector::computeIntersection(const CoordinateXY& p, const CoordinateXY& p1, const CoordinateXY& p2) -{ - isProperVar = false; - - // do between check first, since it is faster than the orientation test - if(Envelope::intersects(p1, p2, p)) { - if((Orientation::index(p1, p2, p) == 0) && - (Orientation::index(p2, p1, p) == 0)) { - isProperVar = true; - if((p == p1) || (p == p2)) { // 2d only test - isProperVar = false; - } - result = POINT_INTERSECTION; - return; - } - } - result = NO_INTERSECTION; -} - -/* public static */ -bool -LineIntersector::hasIntersection(const CoordinateXY& p, const CoordinateXY& p1, const CoordinateXY& p2) -{ - if(Envelope::intersects(p1, p2, p)) { - if((Orientation::index(p1, p2, p) == 0) && - (Orientation::index(p2, p1, p) == 0)) { - return true; - } - } - return false; -} - /* private static */ const CoordinateXY& LineIntersector::nearestEndpoint(const CoordinateXY& p1, const CoordinateXY& p2, diff --git a/deps/libgeos/geos/src/algorithm/PointLocation.cpp b/deps/libgeos/geos/src/algorithm/PointLocation.cpp index 5ceb6bd53..054ea334b 100644 --- a/deps/libgeos/geos/src/algorithm/PointLocation.cpp +++ b/deps/libgeos/geos/src/algorithm/PointLocation.cpp @@ -20,16 +20,32 @@ #include #include +#include #include #include #include #include +#include #include #include namespace geos { namespace algorithm { // geos.algorithm +/* public static */ +bool +PointLocation::isOnSegment(const geom::CoordinateXY& p, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) +{ + //-- test envelope first since it's faster + if (! geom::Envelope::intersects(p0, p1, p)) + return false; + //-- handle zero-length segments + if (p.equals2D(p0)) + return true; + bool isOnLine = Orientation::COLLINEAR == Orientation::index(p0, p1, p); + return isOnLine; +} + /* public static */ bool PointLocation::isOnLine(const geom::CoordinateXY& p, const geom::CoordinateSequence* pt) @@ -39,13 +55,12 @@ PointLocation::isOnLine(const geom::CoordinateXY& p, const geom::CoordinateSeque return false; } - const geom::CoordinateXY* pp = &(pt->getAt(0)); for(std::size_t i = 1; i < ptsize; ++i) { - const geom::CoordinateXY& p1 = pt->getAt(i); - if(LineIntersector::hasIntersection(p, *pp, p1)) { + if(isOnSegment(p, + pt->getAt(i - 1), + pt->getAt(i))) { return true; } - pp = &p1; } return false; } @@ -82,6 +97,12 @@ PointLocation::locateInRing(const geom::CoordinateXY& p, return RayCrossingCounter::locatePointInRing(p, ring); } +geom::Location +PointLocation::locateInRing(const geom::CoordinateXY& p, + const geom::Curve& ring) { + return RayCrossingCounter::locatePointInRing(p, ring); +} + } // namespace geos.algorithm } // namespace geos diff --git a/deps/libgeos/geos/src/algorithm/PointLocator.cpp b/deps/libgeos/geos/src/algorithm/PointLocator.cpp index 04fbe31ea..7fa7dd7e3 100644 --- a/deps/libgeos/geos/src/algorithm/PointLocator.cpp +++ b/deps/libgeos/geos/src/algorithm/PointLocator.cpp @@ -74,7 +74,11 @@ PointLocator::locate(const CoordinateXY& p, const Geometry* geom) void PointLocator::computeLocation(const CoordinateXY& p, const Geometry* geom) { - + //-- handle empty elements + if (geom->isEmpty()) { + return; + } + GeometryTypeId geomTypeId = geom->getGeometryTypeId(); switch (geomTypeId) { diff --git a/deps/libgeos/geos/src/algorithm/PolygonNodeTopology.cpp b/deps/libgeos/geos/src/algorithm/PolygonNodeTopology.cpp index 588cbdf9f..8449944dd 100644 --- a/deps/libgeos/geos/src/algorithm/PolygonNodeTopology.cpp +++ b/deps/libgeos/geos/src/algorithm/PolygonNodeTopology.cpp @@ -24,6 +24,7 @@ using geos::geom::Quadrant; namespace geos { namespace algorithm { // geos.algorithm + /* public static */ bool PolygonNodeTopology::isCrossing(const CoordinateXY* nodePt, @@ -36,16 +37,24 @@ PolygonNodeTopology::isCrossing(const CoordinateXY* nodePt, aLo = a1; aHi = a0; } + + // bool bBetween0 = isBetween(nodePt, b0, aLo, aHi); + // bool bBetween1 = isBetween(nodePt, b1, aLo, aHi); + // return bBetween0 != bBetween1; + /** * Find positions of b0 and b1. * If they are the same they do not cross the other edge */ - bool bBetween0 = isBetween(nodePt, b0, aLo, aHi); - bool bBetween1 = isBetween(nodePt, b1, aLo, aHi); + int compBetween0 = compareBetween(nodePt, b0, aLo, aHi); + if (compBetween0 == 0) return false; + int compBetween1 = compareBetween(nodePt, b1, aLo, aHi); + if (compBetween1 == 0) return false; - return bBetween0 != bBetween1; + return compBetween0 != compBetween1; } + /* public static */ bool PolygonNodeTopology::isInteriorSegment(const CoordinateXY* nodePt, @@ -78,6 +87,52 @@ PolygonNodeTopology::isBetween(const CoordinateXY* origin, } +/* private static */ +int +PolygonNodeTopology::compareBetween( + const CoordinateXY* origin, const CoordinateXY* p, + const CoordinateXY* e0, const CoordinateXY* e1) +{ + int comp0 = compareAngle(origin, p, e0); + if (comp0 == 0) return 0; + int comp1 = compareAngle(origin, p, e1); + if (comp1 == 0) return 0; + if (comp0 > 0 && comp1 < 0) return 1; + return -1; +} + + +/* public static */ +int +PolygonNodeTopology::compareAngle( + const CoordinateXY* origin, + const CoordinateXY* p, + const CoordinateXY* q) +{ + int quadrantP = quadrant(origin, p); + int quadrantQ = quadrant(origin, q); + + /** + * If the vectors are in different quadrants, + * that determines the ordering + */ + if (quadrantP > quadrantQ) return 1; + if (quadrantP < quadrantQ) return -1; + + //--- vectors are in the same quadrant + // Check relative orientation of vectors + // P > Q if it is CCW of Q + int orient = Orientation::index(*origin, *q, *p); + switch (orient) { + case Orientation::COUNTERCLOCKWISE: + return 1; + case Orientation::CLOCKWISE: + return -1; + default: + return 0; + } +} + /* private static */ bool PolygonNodeTopology::isAngleGreater(const CoordinateXY* origin, diff --git a/deps/libgeos/geos/src/algorithm/RayCrossingCounter.cpp b/deps/libgeos/geos/src/algorithm/RayCrossingCounter.cpp index 40af81a4c..9e25deba8 100644 --- a/deps/libgeos/geos/src/algorithm/RayCrossingCounter.cpp +++ b/deps/libgeos/geos/src/algorithm/RayCrossingCounter.cpp @@ -18,11 +18,15 @@ #include #include -#include -#include +#include #include #include +#include +#include +#include +#include +using geos::geom::CoordinateXY; namespace geos { namespace algorithm { @@ -75,6 +79,53 @@ RayCrossingCounter::locatePointInRing(const geom::CoordinateXY& point, return rcc.getLocation(); } +geom::Location +RayCrossingCounter::locatePointInRing(const geom::CoordinateXY& point, + const geom::Curve& ring) +{ + RayCrossingCounter rcc(point); + + for (std::size_t i = 0; i < ring.getNumCurves(); i++) { + const geom::SimpleCurve* curve = ring.getCurveN(i); + rcc.processSequence(*curve->getCoordinatesRO(), !curve->hasCurvedComponents()); + } + + return rcc.getLocation(); +} + +void +RayCrossingCounter::processSequence(const geom::CoordinateSequence& seq, bool isLinear) +{ + if (isOnSegment()) { + return; + } + + if (isLinear) { + for(std::size_t i = 1; i < seq.size(); i++) { + const geom::CoordinateXY& p1 = seq.getAt(i-1);; + const geom::CoordinateXY& p2 = seq.getAt(i); + + countSegment(p1, p2); + + if (isOnSegment()) { + return; + } + } + } else { + for (std::size_t i = 2; i < seq.size(); i += 2) { + const geom::CoordinateXY& p1 = seq.getAt(i-2); + const geom::CoordinateXY& p2 = seq.getAt(i-1); + const geom::CoordinateXY& p3 = seq.getAt(i); + + countArc(p1, p2, p3); + + if (isOnSegment()) { + return; + } + } + } +} + void RayCrossingCounter::countSegment(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2) @@ -119,8 +170,8 @@ RayCrossingCounter::countSegment(const geom::CoordinateXY& p1, // final endpoint // - a downward edge excludes its starting endpoint, and includes its // final endpoint - if(((p1.y > point.y) && (p2.y <= point.y)) || - ((p2.y > point.y) && (p1.y <= point.y))) { + + if((p1.y > point.y && p2.y <= point.y) || (p2.y > point.y && p1.y <= point.y)) { // For an upward edge, orientationIndex will be positive when p1->p2 // crosses ray. Conversely, downward edges should have negative sign. int sign = CGAlgorithmsDD::orientationIndex(p1, p2, point); @@ -140,6 +191,107 @@ RayCrossingCounter::countSegment(const geom::CoordinateXY& p1, } } +bool +RayCrossingCounter::shouldCountCrossing(const geom::CircularArc& arc, const geom::CoordinateXY& q) { + // To avoid double-counting shared vertices, we count an intersection point if + // a) is in the interior of the arc + // b) is at the starting point of the arc, and the arc is directed upward at that point + // c) is at the ending point of the arc is directed downward at that point + if (q.equals2D(arc.p0)) { + return arc.isUpwardAtPoint(q); + } else if (q.equals2D(arc.p2)) { + return !arc.isUpwardAtPoint(q); + } else { + return true; + } +} + +/// Return an array of 0-2 intersection points between an arc and a horizontal +/// ray extending righward from a point. If fewer than 2 intersection points exist, +/// some Coordinates in the returned array will be equal to CoordinateXY::getNull(). +std::array +RayCrossingCounter::pointsIntersectingHorizontalRay(const geom::CircularArc& arc, const geom::CoordinateXY& origin) { + const auto& c = arc.getCenter(); + const auto& R = arc.getRadius(); + + auto dx = std::sqrt(R*R - std::pow(origin.y - c.y, 2) ); + + // Find two points where the horizontal line intersects the circle + // that is coincident with this arc. + // Problem: because of floating-point errors, these + // constructed points may not actually like on the circle. + CoordinateXY intPt1{c.x - dx, origin.y}; + CoordinateXY intPt2{c.x + dx, origin.y}; + + // Solution (best we have for now) + // Snap computed points to points that define the arc + double eps = 1e-14; + + for (const CoordinateXY& pt : arc ) { + if (origin.y == pt.y) { + if (intPt1.distance(pt) < eps) { + intPt1 = pt; + } + if (intPt2.distance(pt) < eps) { + intPt2 = pt; + } + } + } + + std::array ret { CoordinateXY::getNull(), CoordinateXY::getNull() }; + + std::size_t pos = 0; + if (intPt1.x >= origin.x && arc.containsPointOnCircle(intPt1)) { + ret[pos++] = intPt1; + } + if (intPt2.x >= origin.x && arc.containsPointOnCircle(intPt2)) { + ret[pos++] = intPt2; + } + + return ret; +} + +void +RayCrossingCounter::countArc(const CoordinateXY& p1, + const CoordinateXY& p2, + const CoordinateXY& p3) +{ + // For each arc, check if it crosses + // a horizontal ray running from the test point in + // the positive x direction. + geom::CircularArc arc(p1, p2, p3); + + // If the arc is degenerate, process it is two line segments + if (arc.isLinear()) { + countSegment(p1, p2); + countSegment(p2, p3); + return; + } + + // Check if the arc is strictly to the left of the test point + geom::Envelope arcEnvelope; + CircularArcs::expandEnvelope(arcEnvelope, p1, p2, p3); + + if (arcEnvelope.getMaxX() < point.x) { + return; + } + + // Evaluate all arcs whose enveleope is to the right of the test point. + if (arcEnvelope.getMaxY() >= point.y && arcEnvelope.getMinY() <= point.y) { + if (arc.containsPoint(point)) { + isPointOnSegment = true; + return; + } + + auto intPts = pointsIntersectingHorizontalRay(arc, point); + if (!intPts[0].isNull() && shouldCountCrossing(arc, intPts[0])) { + crossingCount++; + } + if (!intPts[1].isNull() && shouldCountCrossing(arc, intPts[1])) { + crossingCount++; + } + } +} geom::Location RayCrossingCounter::getLocation() const diff --git a/deps/libgeos/geos/src/algorithm/RobustDeterminant.cpp b/deps/libgeos/geos/src/algorithm/RobustDeterminant.cpp index 0b5cf66d4..33b93714b 100644 --- a/deps/libgeos/geos/src/algorithm/RobustDeterminant.cpp +++ b/deps/libgeos/geos/src/algorithm/RobustDeterminant.cpp @@ -52,7 +52,7 @@ RobustDeterminant::signOfDet2x2(double x1, double y1, double x2, double y2) { // returns -1 if the determinant is negative, // returns 1 if the determinant is positive, - // retunrs 0 if the determinant is null. + // returns 0 if the determinant is null. int sign = 1; double swap; diff --git a/deps/libgeos/geos/src/algorithm/construct/IndexedDistanceToPoint.cpp b/deps/libgeos/geos/src/algorithm/construct/IndexedDistanceToPoint.cpp new file mode 100644 index 000000000..e1fd58571 --- /dev/null +++ b/deps/libgeos/geos/src/algorithm/construct/IndexedDistanceToPoint.cpp @@ -0,0 +1,69 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/construct/IndexedDistanceToPoint.java + * https://github.com/locationtech/jts/commit/d92f783163d9440fcc10c729143787bf7b9fe8f9 + * + **********************************************************************/ + +#include +#include + +//using namespace geos::geom; +using geos::geom::Location; + +namespace geos { +namespace algorithm { // geos.algorithm +namespace construct { // geos.algorithm.construct + +IndexedDistanceToPoint::IndexedDistanceToPoint(const Geometry& geom) + : targetGeometry(geom) +{ +} + +/* private */ +void IndexedDistanceToPoint::init() +{ + if (facetDistance != nullptr) + return; + ptLocator.reset(new IndexedPointInPolygonsLocator(targetGeometry)); + facetDistance.reset(new IndexedFacetDistance(&targetGeometry)); +} + +/* public */ +double IndexedDistanceToPoint::distance(const Point& pt) +{ + init(); + //-- distance is 0 if point is inside a target polygon + if (isInArea(pt)) { + return 0; + } + return facetDistance->distance(&pt); +} + +/* private */ +bool IndexedDistanceToPoint::isInArea(const Point& pt) +{ + return Location::EXTERIOR != ptLocator->locate(pt.getCoordinate()); +} + +/* public */ +std::unique_ptr +IndexedDistanceToPoint::nearestPoints(const Point& pt) +{ + init(); + return facetDistance->nearestPoints(&pt); +} + +}}} diff --git a/deps/libgeos/geos/src/algorithm/construct/IndexedPointInPolygonsLocator.cpp b/deps/libgeos/geos/src/algorithm/construct/IndexedPointInPolygonsLocator.cpp new file mode 100644 index 000000000..bb9e51abe --- /dev/null +++ b/deps/libgeos/geos/src/algorithm/construct/IndexedPointInPolygonsLocator.cpp @@ -0,0 +1,70 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/construct/IndexedDistanceToPoint.java + * https://github.com/locationtech/jts/commit/d92f783163d9440fcc10c729143787bf7b9fe8f9 + * + **********************************************************************/ + +#include +#include +#include +#include + +using geos::algorithm::locate::IndexedPointInAreaLocator; +using geos::geom::Envelope; +using geos::geom::util::PolygonalExtracter; + +namespace geos { +namespace algorithm { // geos.algorithm +namespace construct { // geos.algorithm.construct + +/* public */ +IndexedPointInPolygonsLocator::IndexedPointInPolygonsLocator(const Geometry& g) + : geom(g), isInitialized(false) +{ +} + +/* private */ +void IndexedPointInPolygonsLocator::init() +{ + if (isInitialized) { + return; + } + isInitialized = true; + std::vector polys; + PolygonalExtracter::getPolygonals(geom, polys); + for (const Geometry* poly : polys) { + IndexedPointInAreaLocator* ptLocator = new IndexedPointInAreaLocator(*poly); + locators.emplace_back(ptLocator); + index.insert(poly->getEnvelopeInternal(), ptLocator); + } +} + +/* public */ +Location IndexedPointInPolygonsLocator::locate(const CoordinateXY* /*const*/ pt) +{ + init(); + Envelope queryEnv(*pt); + std::vector result; + index.query(queryEnv, result); + for (IndexedPointInAreaLocator* ptLocater : result) { + Location loc = ptLocater->locate(pt); + if (loc != Location::EXTERIOR) + return loc; + } + return Location::EXTERIOR; +} + +}}} \ No newline at end of file diff --git a/deps/libgeos/geos/src/algorithm/construct/LargestEmptyCircle.cpp b/deps/libgeos/geos/src/algorithm/construct/LargestEmptyCircle.cpp index 68f50223a..c4fc40f09 100644 --- a/deps/libgeos/geos/src/algorithm/construct/LargestEmptyCircle.cpp +++ b/deps/libgeos/geos/src/algorithm/construct/LargestEmptyCircle.cpp @@ -49,8 +49,8 @@ LargestEmptyCircle::LargestEmptyCircle(const Geometry* p_obstacles, const Geomet : tolerance(p_tolerance) , obstacles(p_obstacles) , factory(p_obstacles->getFactory()) - , obstacleDistance(p_obstacles) , done(false) + , obstacleDistance(*p_obstacles) { if (obstacles->isEmpty()) { throw util::IllegalArgumentException("Empty obstacles geometry is not supported"); @@ -164,14 +164,14 @@ LargestEmptyCircle::mayContainCircleCenter(const Cell& cell, const Cell& farthes double LargestEmptyCircle::distanceToConstraints(const Coordinate& c) { - bool isOutside = ptLocator && (Location::EXTERIOR == ptLocator->locate(&c)); + bool isOutside = boundaryPtLocater && (Location::EXTERIOR == boundaryPtLocater->locate(&c)); std::unique_ptr pt(factory->createPoint(c)); if (isOutside) { double boundaryDist = boundaryDistance->distance(pt.get()); return -boundaryDist; } - double dist = obstacleDistance.distance(pt.get()); + double dist = obstacleDistance.distance(*(pt.get())); return dist; } @@ -200,8 +200,8 @@ LargestEmptyCircle::initBoundary() gridEnv = *(boundary->getEnvelopeInternal()); // if boundary does not enclose an area cannot create a ptLocator if (boundary->getDimension() >= 2) { - ptLocator.reset(new algorithm::locate::IndexedPointInAreaLocator(*(boundary.get()))); - boundaryDistance.reset(new operation::distance::IndexedFacetDistance(boundary.get())); + boundaryPtLocater = detail::make_unique(*(boundary.get())); + boundaryDistance = detail::make_unique(boundary.get()); } } @@ -214,7 +214,7 @@ LargestEmptyCircle::compute() initBoundary(); // if ptLocator is not present then result is degenerate (represented as zero-radius circle) - if (!ptLocator) { + if (!boundaryPtLocater) { const CoordinateXY* pt = obstacles->getCoordinate(); centerPt = *pt; radiusPt = *pt; @@ -273,7 +273,7 @@ LargestEmptyCircle::compute() // compute radius point std::unique_ptr centerPoint(factory->createPoint(centerPt)); - const auto& nearestPts = obstacleDistance.nearestPoints(centerPoint.get()); + const auto& nearestPts = obstacleDistance.nearestPoints(*(centerPoint.get())); radiusPt = nearestPts->getAt(0); // flag computation diff --git a/deps/libgeos/geos/src/algorithm/construct/MaximumInscribedCircle.cpp b/deps/libgeos/geos/src/algorithm/construct/MaximumInscribedCircle.cpp index b20d14a08..c3a471078 100644 --- a/deps/libgeos/geos/src/algorithm/construct/MaximumInscribedCircle.cpp +++ b/deps/libgeos/geos/src/algorithm/construct/MaximumInscribedCircle.cpp @@ -85,9 +85,9 @@ MaximumInscribedCircle::computeMaximumIterations(const Geometry* geom, double to double diam = geom->getEnvelopeInternal()->getDiameter(); double ncells = diam / toleranceDist; //-- Using log of ncells allows control over number of iterations - std::size_t factor = (std::size_t) std::log(ncells); + int factor = (int) std::log(ncells); if (factor < 1) factor = 1; - return 2000 + 2000 * factor; + return (std::size_t) (2000 + 2000 * factor); } /* public */ diff --git a/deps/libgeos/geos/src/algorithm/distance/DiscreteFrechetDistance.cpp b/deps/libgeos/geos/src/algorithm/distance/DiscreteFrechetDistance.cpp index a54dd8146..99a650f19 100644 --- a/deps/libgeos/geos/src/algorithm/distance/DiscreteFrechetDistance.cpp +++ b/deps/libgeos/geos/src/algorithm/distance/DiscreteFrechetDistance.cpp @@ -28,6 +28,8 @@ #include #include #include + +#include "geos/util.h" using namespace geos::geom; namespace geos { @@ -142,6 +144,9 @@ DiscreteFrechetDistance::compute( throw util::IllegalArgumentException("DiscreteFrechetDistance called with empty inputs."); } + util::ensureNoCurvedComponents(discreteGeom); + util::ensureNoCurvedComponents(geom); + auto lp = discreteGeom.getCoordinates(); auto lq = geom.getCoordinates(); std::size_t pSize, qSize; diff --git a/deps/libgeos/geos/src/algorithm/distance/DiscreteHausdorffDistance.cpp b/deps/libgeos/geos/src/algorithm/distance/DiscreteHausdorffDistance.cpp index cffc7b6a7..3712b0a40 100644 --- a/deps/libgeos/geos/src/algorithm/distance/DiscreteHausdorffDistance.cpp +++ b/deps/libgeos/geos/src/algorithm/distance/DiscreteHausdorffDistance.cpp @@ -23,6 +23,8 @@ #include #include +#include "geos/util.h" + using namespace geos::geom; namespace geos { @@ -101,6 +103,9 @@ DiscreteHausdorffDistance::computeOrientedDistance( const geom::Geometry& geom, PointPairDistance& p_ptDist) { + util::ensureNoCurvedComponents(discreteGeom); + util::ensureNoCurvedComponents(geom); + // can't calculate distance with empty if (discreteGeom.isEmpty() || geom.isEmpty()) return; diff --git a/deps/libgeos/geos/src/algorithm/distance/DistanceToPoint.cpp b/deps/libgeos/geos/src/algorithm/distance/DistanceToPoint.cpp index 1891b63f8..b98343229 100644 --- a/deps/libgeos/geos/src/algorithm/distance/DistanceToPoint.cpp +++ b/deps/libgeos/geos/src/algorithm/distance/DistanceToPoint.cpp @@ -45,7 +45,7 @@ DistanceToPoint::computeDistance(const geom::Geometry& geom, return; } - if(geom.getGeometryTypeId() == GEOS_LINESTRING) { + if(geom.getGeometryTypeId() == GEOS_LINESTRING || geom.getGeometryTypeId() == GEOS_LINEARRING) { const LineString* ls = static_cast(&geom); computeDistance(*ls, pt, ptDist); } diff --git a/deps/libgeos/geos/src/algorithm/hull/ConcaveHull.cpp b/deps/libgeos/geos/src/algorithm/hull/ConcaveHull.cpp index bbee04bad..d70a80d46 100644 --- a/deps/libgeos/geos/src/algorithm/hull/ConcaveHull.cpp +++ b/deps/libgeos/geos/src/algorithm/hull/ConcaveHull.cpp @@ -28,6 +28,7 @@ #include #include #include +#include using geos::geom::Coordinate; @@ -48,6 +49,17 @@ namespace geos { namespace algorithm { // geos.algorithm namespace hull { // geos.algorithm.hulll +ConcaveHull::ConcaveHull(const Geometry* geom) + : inputGeometry(geom) + , maxEdgeLengthRatio(-1.0) + , alpha(-1) + , isHolesAllowed(false) + , criteriaType(PARAM_EDGE_LENGTH) + , maxSizeInHull(0.0) + , geomFactory(geom->getFactory()) +{ + util::ensureNoCurvedComponents(geom); +} /* public static */ double diff --git a/deps/libgeos/geos/src/algorithm/hull/ConcaveHullOfPolygons.cpp b/deps/libgeos/geos/src/algorithm/hull/ConcaveHullOfPolygons.cpp index a774cf6f0..6ed54fb0a 100644 --- a/deps/libgeos/geos/src/algorithm/hull/ConcaveHullOfPolygons.cpp +++ b/deps/libgeos/geos/src/algorithm/hull/ConcaveHullOfPolygons.cpp @@ -26,6 +26,8 @@ #include #include +#include "geos/util.h" + using geos::geom::Coordinate; using geos::geom::Geometry; @@ -111,6 +113,7 @@ ConcaveHullOfPolygons::ConcaveHullOfPolygons(const Geometry* geom) , isHolesAllowed(false) , isTight(false) { + util::ensureNoCurvedComponents(geom); if (! geom->isPolygonal()) { throw util::IllegalArgumentException("Input must be polygonal"); } @@ -153,7 +156,7 @@ ConcaveHullOfPolygons::setTight(bool p_isTight) std::unique_ptr ConcaveHullOfPolygons::getHull() { - if (inputPolygons->isEmpty()) { + if (inputPolygons->isEmpty() || inputPolygons->getArea() == 0) { return createEmptyHull(); } buildHullTris(); diff --git a/deps/libgeos/geos/src/algorithm/locate/IndexedPointInAreaLocator.cpp b/deps/libgeos/geos/src/algorithm/locate/IndexedPointInAreaLocator.cpp index 0382c5deb..ce9ac78e4 100644 --- a/deps/libgeos/geos/src/algorithm/locate/IndexedPointInAreaLocator.cpp +++ b/deps/libgeos/geos/src/algorithm/locate/IndexedPointInAreaLocator.cpp @@ -30,7 +30,6 @@ #include #include -#include using geos::geom::CoordinateXY; diff --git a/deps/libgeos/geos/src/algorithm/locate/SimplePointInAreaLocator.cpp b/deps/libgeos/geos/src/algorithm/locate/SimplePointInAreaLocator.cpp index 5f1550a83..0a7b8e343 100644 --- a/deps/libgeos/geos/src/algorithm/locate/SimplePointInAreaLocator.cpp +++ b/deps/libgeos/geos/src/algorithm/locate/SimplePointInAreaLocator.cpp @@ -23,9 +23,6 @@ #include #include -#include -#include - using namespace geos::geom; namespace geos { @@ -63,9 +60,10 @@ SimplePointInAreaLocator::locateInGeometry(const CoordinateXY& p, const Geometry } if (geom->getNumGeometries() == 1) { - if (geom->getGeometryTypeId() == GEOS_POLYGON) { - auto poly = static_cast(geom); - return locatePointInPolygon(p, poly); + auto typ = geom->getGeometryTypeId(); + if (typ == GEOS_POLYGON || typ == GEOS_CURVEPOLYGON) { + auto surface = static_cast(geom); + return locatePointInSurface(p, *surface); } } for (std::size_t i = 0; i < geom->getNumGeometries(); i++) { @@ -80,28 +78,25 @@ SimplePointInAreaLocator::locateInGeometry(const CoordinateXY& p, const Geometry } geom::Location -SimplePointInAreaLocator::locatePointInPolygon(const CoordinateXY& p, const Polygon* poly) +SimplePointInAreaLocator::locatePointInSurface(const CoordinateXY& p, const Surface& surface) { - if(poly->isEmpty()) { + if(surface.isEmpty()) { return Location::EXTERIOR; } - if(!poly->getEnvelopeInternal()->contains(p)) { + if(!surface.getEnvelopeInternal()->contains(p)) { return Location::EXTERIOR; } - const LineString* shell = poly->getExteriorRing(); - const CoordinateSequence* cl; - cl = shell->getCoordinatesRO(); - Location shellLoc = PointLocation::locateInRing(p, *cl); + const Curve& shell = *surface.getExteriorRing(); + Location shellLoc = PointLocation::locateInRing(p, shell); if(shellLoc != Location::INTERIOR) { return shellLoc; } // now test if the point lies in or on the holes - for(std::size_t i = 0, n = poly->getNumInteriorRing(); i < n; i++) { - const LineString* hole = poly->getInteriorRingN(i); - if(hole->getEnvelopeInternal()->contains(p)) { - cl = hole->getCoordinatesRO(); - Location holeLoc = RayCrossingCounter::locatePointInRing(p, *cl); + for(std::size_t i = 0; i < surface.getNumInteriorRing(); i++) { + const Curve& hole = *surface.getInteriorRingN(i); + if(hole.getEnvelopeInternal()->contains(p)) { + Location holeLoc = RayCrossingCounter::locatePointInRing(p, hole); if(holeLoc == Location::BOUNDARY) { return Location::BOUNDARY; } diff --git a/deps/libgeos/geos/src/coverage/CoveragePolygon.cpp b/deps/libgeos/geos/src/coverage/CoveragePolygon.cpp new file mode 100644 index 000000000..70ba4684f --- /dev/null +++ b/deps/libgeos/geos/src/coverage/CoveragePolygon.cpp @@ -0,0 +1,76 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include + +#include +#include +#include +#include + +using geos::algorithm::locate::IndexedPointInAreaLocator; +using geos::geom::CoordinateXY; +using geos::geom::Envelope; +using geos::geom::Location; +using geos::geom::Polygon; + +namespace geos { // geos +namespace coverage { // geos.coverage + +/* public */ +CoveragePolygon::CoveragePolygon(const Polygon* poly) + : m_polygon(poly) +{ + //-- cache polygon envelope for maximum performance + polyEnv = *(poly->getEnvelopeInternal()); +} + +/* public */ +bool +CoveragePolygon::intersectsEnv(const Envelope& env) const +{ + return polyEnv.intersects(env); +} + +/* public */ +bool +CoveragePolygon::intersectsEnv(const CoordinateXY& p) const +{ + return polyEnv.intersects(p); +} + +/* public */ +bool +CoveragePolygon::contains(const CoordinateXY& p) const +{ + if (! intersectsEnv(p)) + return false; + auto& pia = getLocator(); + return Location::INTERIOR == pia.locate(&p); +} + +/* private */ +IndexedPointInAreaLocator& +CoveragePolygon::getLocator() const +{ + if (m_locator == nullptr) { + m_locator = std::make_unique(*m_polygon); + } + return *m_locator; +} + +} // namespace geos.coverage +} // namespace geos + + diff --git a/deps/libgeos/geos/src/coverage/CoveragePolygonValidator.cpp b/deps/libgeos/geos/src/coverage/CoveragePolygonValidator.cpp index a99643903..d0161c097 100644 --- a/deps/libgeos/geos/src/coverage/CoveragePolygonValidator.cpp +++ b/deps/libgeos/geos/src/coverage/CoveragePolygonValidator.cpp @@ -13,7 +13,9 @@ **********************************************************************/ #include + #include +#include #include #include @@ -31,7 +33,7 @@ using geos::algorithm::locate::IndexedPointInAreaLocator; using geos::algorithm::Orientation; -using geos::geom::Coordinate; +using geos::geom::CoordinateXY; using geos::geom::Envelope; using geos::geom::Geometry; using geos::geom::GeometryFactory; @@ -88,10 +90,10 @@ CoveragePolygonValidator::setGapWidth(double p_gapWidth) std::unique_ptr CoveragePolygonValidator::validate() { - m_adjPolygons = extractPolygons(adjGeoms); - + std::vector adjPolygons = extractPolygons(adjGeoms); + m_adjCovPolygons = toCoveragePolygons(adjPolygons); std::vector targetRings = createRings(targetGeom); - std::vector adjRings = createRings(m_adjPolygons); + std::vector adjRings = createRings(adjPolygons); /** * Mark matching segments first. @@ -106,6 +108,15 @@ CoveragePolygonValidator::validate() return createInvalidLines(targetRings); } +/* private static */ +std::vector> +CoveragePolygonValidator::toCoveragePolygons(const std::vector polygons) { + std::vector> covPolys; + for (const Polygon* poly : polygons) { + covPolys.push_back( std::make_unique(poly) ); + } + return covPolys; +} /* private */ void @@ -132,10 +143,9 @@ CoveragePolygonValidator::checkTargetRings( * Do further checks to see if any of them are are invalid. */ markInvalidInteractingSegments(targetRings, adjRings, gapWidth); - markInvalidInteriorSegments(targetRings, m_adjPolygons); + markInvalidInteriorSegments(targetRings, m_adjCovPolygons); } - /* private static */ std::vector CoveragePolygonValidator::extractPolygons(std::vector& geoms) @@ -246,85 +256,63 @@ CoveragePolygonValidator::markInvalidInteractingSegments( void CoveragePolygonValidator::markInvalidInteriorSegments( std::vector& targetRings, - std::vector& adjPolygons) + std::vector>& adjCovPolygons ) { for (CoverageRing* ring : targetRings) { - for (std::size_t i = 0; i < ring->size() - 1; i++) { - //-- skip check for segments with known state. - if (ring->isKnown(i)) - continue; - - /** - * Check if vertex is in interior of an adjacent polygon. - * If so, the segments on either side are in the interior. - * Mark them invalid, unless they are already matched. - */ - const Coordinate& p = ring->getCoordinate(i); - if (isInteriorVertex(p, adjPolygons)) { - ring->markInvalid(i); - //-- previous segment may be interior (but may also be matched) - std::size_t iPrev = i == 0 ? ring->size()-2 : i-1; - if (! ring->isKnown(iPrev)) - ring->markInvalid(iPrev); - } + std::size_t stride = 1000; //-- RING_SECTION_STRIDE; + for (std::size_t i = 0; i < ring->size() - 1; i += stride) { + std::size_t iEnd = i + stride; + if (iEnd >= ring->size()) + iEnd = ring->size() - 1; + markInvalidInteriorSection(*ring, i, iEnd, adjCovPolygons); } } } - /* private */ -bool -CoveragePolygonValidator::isInteriorVertex( - const Coordinate& p, - std::vector& adjPolygons) +void +CoveragePolygonValidator::markInvalidInteriorSection( + CoverageRing& ring, + std::size_t iStart, + std::size_t iEnd, + std::vector>& adjCovPolygons ) { - /** - * There should not be too many adjacent polygons, - * and hopefully not too many segments with unknown status - * so a linear scan should not be too inefficient - */ - //TODO: try a spatial index? - for (std::size_t i = 0; i < adjPolygons.size(); i++) { - const Polygon* adjPoly = adjPolygons[i]; - if (polygonContainsPoint(i, adjPoly, p)) - return true; + Envelope sectionEnv = ring.getEnvelope(iStart, iEnd); + //TODO: is it worth indexing polygons? + for (auto& adjPoly : adjCovPolygons) { + if (adjPoly->intersectsEnv(sectionEnv)) { + //-- test vertices in section + for (auto i = iStart; i < iEnd; i++) { + markInvalidInteriorSegment(ring, i, adjPoly.get()); + } + } } - return false; } - /* private */ -bool -CoveragePolygonValidator::polygonContainsPoint(std::size_t index, - const Polygon* poly, const Coordinate& pt) +void +CoveragePolygonValidator::markInvalidInteriorSegment( + CoverageRing& ring, std::size_t i, CoveragePolygon* adjPoly) { - if (! poly->getEnvelopeInternal()->intersects(pt)) - return false; - IndexedPointInAreaLocator* pia = getLocator(index, poly); - return Location::INTERIOR == pia->locate(&pt); -} - + //-- skip check for segments with known state. + if (ring.isKnown(i)) + return; -/* private */ -IndexedPointInAreaLocator* -CoveragePolygonValidator::getLocator(std::size_t index, const Polygon* poly) -{ - auto it = adjPolygonLocators.find(index); - // found locator already constructed - if (it != adjPolygonLocators.end()) { - return (it->second).get(); - } - // construct new locator for this polygon - else { - IndexedPointInAreaLocator* ipia = new IndexedPointInAreaLocator(*poly); - adjPolygonLocators.emplace(std::piecewise_construct, - std::forward_as_tuple(index), - std::forward_as_tuple(ipia)); - return ipia; + /** + * Check if vertex is in interior of an adjacent polygon. + * If so, the segments on either side are in the interior. + * Mark them invalid, unless they are already matched. + */ + const CoordinateXY& p = ring.getCoordinate(i); + if (adjPoly->contains(p)) { + ring.markInvalid(i); + //-- previous segment may be interior (but may also be matched) + auto iPrev = i == 0 ? ring.size()-2 : i-1; + if (! ring.isKnown(iPrev)) + ring.markInvalid(iPrev); } } - /* private */ std::unique_ptr CoveragePolygonValidator::createInvalidLines(std::vector& rings) diff --git a/deps/libgeos/geos/src/coverage/CoverageRing.cpp b/deps/libgeos/geos/src/coverage/CoverageRing.cpp index 8f258d9d5..9a331291f 100644 --- a/deps/libgeos/geos/src/coverage/CoverageRing.cpp +++ b/deps/libgeos/geos/src/coverage/CoverageRing.cpp @@ -73,6 +73,16 @@ CoverageRing::CoverageRing(const LinearRing* ring, bool isShell) algorithm::Orientation::isCCW(ring->getCoordinatesRO()) != isShell) {} +/* public */ +geom::Envelope CoverageRing::getEnvelope(std::size_t start, std::size_t end) +{ + geom::Envelope env; + for (std::size_t i = start; i < end; i++) { + env.expandToInclude(getCoordinate(i)); + } + return env; +} + /* public */ bool diff --git a/deps/libgeos/geos/src/coverage/CoverageRingEdges.cpp b/deps/libgeos/geos/src/coverage/CoverageRingEdges.cpp index c334e47f5..e6ec619b7 100644 --- a/deps/libgeos/geos/src/coverage/CoverageRingEdges.cpp +++ b/deps/libgeos/geos/src/coverage/CoverageRingEdges.cpp @@ -71,6 +71,8 @@ CoverageRingEdges::build() std::map uniqueEdgeMap; for (const Geometry* geom : m_coverage) { for (std::size_t ipoly = 0; ipoly < geom->getNumGeometries(); ipoly++) { + util::ensureNoCurvedComponents(geom->getGeometryN(ipoly)); + const Polygon* poly = static_cast(geom->getGeometryN(ipoly)); //-- skip empty elements. Missing elements are copied in result @@ -252,7 +254,7 @@ CoverageRingEdges::next(std::size_t index, const CoordinateSequence& ring) /* private */ Coordinate::UnorderedSet -CoverageRingEdges::findMultiRingNodes(std::vector& coverage) +CoverageRingEdges::findMultiRingNodes(const std::vector& coverage) { std::map vertexRingCount; VertexRingCounter::count(coverage, vertexRingCount); diff --git a/deps/libgeos/geos/src/coverage/CoverageSimplifier.cpp b/deps/libgeos/geos/src/coverage/CoverageSimplifier.cpp index 73b5a5e44..dbbd9022a 100644 --- a/deps/libgeos/geos/src/coverage/CoverageSimplifier.cpp +++ b/deps/libgeos/geos/src/coverage/CoverageSimplifier.cpp @@ -20,6 +20,7 @@ #include #include #include +#include using geos::geom::Geometry; @@ -80,10 +81,15 @@ CoverageSimplifier::simplifyInner( /* public */ -CoverageSimplifier::CoverageSimplifier(std::vector& coverage) +CoverageSimplifier::CoverageSimplifier(const std::vector& coverage) : m_input(coverage) , m_geomFactory(coverage.empty() ? nullptr : coverage[0]->getFactory()) - {} + { + for (const Geometry* g: m_input) { + if (!g->isPolygonal()) + throw util::IllegalArgumentException("Argument is non-polygonal"); + } + } /* public */ std::vector> diff --git a/deps/libgeos/geos/src/coverage/CoverageValidator.cpp b/deps/libgeos/geos/src/coverage/CoverageValidator.cpp index 719c7f134..644558334 100644 --- a/deps/libgeos/geos/src/coverage/CoverageValidator.cpp +++ b/deps/libgeos/geos/src/coverage/CoverageValidator.cpp @@ -74,6 +74,8 @@ CoverageValidator::validate() TemplateSTRtree index; std::vector> invalidLines; for (auto* geom : m_coverage) { + util::ensureNoCurvedComponents(geom); + index.insert(geom); invalidLines.emplace_back(nullptr); } diff --git a/deps/libgeos/geos/src/coverage/VertexRingCounter.cpp b/deps/libgeos/geos/src/coverage/VertexRingCounter.cpp index c0d79cab9..fe742889f 100644 --- a/deps/libgeos/geos/src/coverage/VertexRingCounter.cpp +++ b/deps/libgeos/geos/src/coverage/VertexRingCounter.cpp @@ -33,7 +33,7 @@ namespace coverage { // geos.coverage /* public static */ void VertexRingCounter::count( - std::vector& geoms, + const std::vector& geoms, std::map& counts) { VertexRingCounter vertextCounter(counts); diff --git a/deps/libgeos/geos/src/geom/CircularString.cpp b/deps/libgeos/geos/src/geom/CircularString.cpp new file mode 100644 index 000000000..b666c8eb1 --- /dev/null +++ b/deps/libgeos/geos/src/geom/CircularString.cpp @@ -0,0 +1,98 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include +#include +#include +#include +#include + +namespace geos { +namespace geom { + +/*public*/ +CircularString::CircularString(std::unique_ptr&& newCoords, + const GeometryFactory& factory) + : + SimpleCurve(std::move(newCoords), false, factory) +{ + validateConstruction(); +} + +CircularString::~CircularString() = default; + +std::unique_ptr +CircularString::clone() const +{ + return std::unique_ptr(cloneImpl()); +} + +std::string +CircularString::getGeometryType() const +{ + return "CircularString"; +} + +GeometryTypeId +CircularString::getGeometryTypeId() const +{ + return GEOS_CIRCULARSTRING; +} + +double +CircularString::getLength() const +{ + if (isEmpty()) { + return 0; + } + + const CoordinateSequence& coords = *getCoordinatesRO(); + + double tot = 0; + for (std::size_t i = 2; i < coords.size(); i += 2) { + auto len = CircularArc(coords[i-2], coords[i-1], coords[i]).getLength(); + tot += len; + } + return tot; +} + +CircularString* +CircularString::reverseImpl() const +{ + if (isEmpty()) { + return clone().release(); + } + + assert(points.get()); + auto seq = points->clone(); + seq->reverse(); + assert(getFactory()); + return getFactory()->createCircularString(std::move(seq)).release(); +} + +void +CircularString::validateConstruction() +{ + if (points.get() == nullptr) { + points = std::make_unique(); + return; + } + + if (points->size() == 2) { + throw util::IllegalArgumentException("point array must contain 0 or >2 elements\n"); + } +} + +} +} diff --git a/deps/libgeos/geos/src/geom/CompoundCurve.cpp b/deps/libgeos/geos/src/geom/CompoundCurve.cpp new file mode 100644 index 000000000..2175c7ed2 --- /dev/null +++ b/deps/libgeos/geos/src/geom/CompoundCurve.cpp @@ -0,0 +1,311 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include +#include +#include +#include +#include + +namespace geos { +namespace geom { + +CompoundCurve::CompoundCurve(std::vector>&& p_curves, + const GeometryFactory& gf) + : Curve(gf), + curves(std::move(p_curves)), + envelope(computeEnvelopeInternal()) {} + +CompoundCurve::CompoundCurve(const CompoundCurve& other) + : Curve(other), + curves(other.curves.size()), + envelope(other.envelope) +{ + for (std::size_t i = 0; i < curves.size(); i++) { + curves[i].reset(static_cast(other.curves[i]->clone().release())); + } +} + +CompoundCurve& +CompoundCurve::operator=(const CompoundCurve& other) +{ + curves.resize(other.curves.size()); + envelope = other.envelope; + + for (std::size_t i =0; i < curves.size(); i++) { + curves[i].reset(static_cast(other.curves[i]->clone().release())); + } + + return *this; +} + +void +CompoundCurve::apply_ro(CoordinateFilter* cf) const +{ + for (const auto& curve : curves) { + curve->apply_ro(cf); + } +} + +void +CompoundCurve::apply_ro(CoordinateSequenceFilter& csf) const +{ + for (const auto& curve : curves) { + const auto& seq = *curve->getCoordinatesRO(); + for (std::size_t i = 0; i < seq.size(); i++) { + if (csf.isDone()) { + return; + } + csf.filter_ro(seq, i); + } + } +} + +void +CompoundCurve::apply_rw(const CoordinateFilter* cf) +{ + for (auto& curve : curves) { + curve->apply_rw(cf); + } +} + +void +CompoundCurve::apply_rw(CoordinateSequenceFilter&) +{ + throw util::UnsupportedOperationException(); +} + +std::unique_ptr +CompoundCurve::clone() const +{ + return std::unique_ptr(cloneImpl()); +} + +CompoundCurve* +CompoundCurve::cloneImpl() const +{ + return new CompoundCurve(*this); +} + +int +CompoundCurve::compareToSameClass(const Geometry* g) const +{ + const CompoundCurve* curve = detail::down_cast(g); + return compare(curves, curve->curves); +} + +Envelope +CompoundCurve::computeEnvelopeInternal() const +{ + Envelope e; + for (const auto& curve : curves) { + e.expandToInclude(curve->getEnvelopeInternal()); + } + return e; +} + +bool +CompoundCurve::equalsExact(const Geometry* other, double tolerance) const +{ + if (!isEquivalentClass(other)) { + return false; + } + + const CompoundCurve* otherCurve = static_cast(other); + if (curves.size() != otherCurve->curves.size()) { + return false; + } + + for (std::size_t i = 0; i < otherCurve->curves.size(); i++) { + if (!curves[i]->equalsExact(otherCurve->curves[i].get(), tolerance)) { + return false; + } + } + + return true; +} + +bool +CompoundCurve::equalsIdentical(const Geometry* other) const +{ + if (!isEquivalentClass(other)) { + return false; + } + + const CompoundCurve* otherCurve = static_cast(other); + if (curves.size() != otherCurve->curves.size()) { + return false; + } + + for (std::size_t i = 0; i < otherCurve->curves.size(); i++) { + if (!curves[i]->equalsIdentical(otherCurve->curves[i].get())) { + return false; + } + } + + return true; +} + +std::unique_ptr +CompoundCurve::getBoundary() const +{ + operation::BoundaryOp bop(*this); + return bop.getBoundary(); +} + +const CoordinateXY* +CompoundCurve::getCoordinate() const +{ + for (const auto& curve : curves) { + if (!curve->isEmpty()) { + return curve->getCoordinate(); + } + } + + return nullptr; +} + +uint8_t +CompoundCurve::getCoordinateDimension() const +{ + return static_cast(2 + hasZ() + hasM()); +} + +std::unique_ptr +CompoundCurve::getCoordinates() const +{ + auto ret = std::make_unique(0, hasZ(), hasM()); + for (const auto& curve : curves) { + ret->add(*curve->getCoordinatesRO()); + } + return ret; +} + +const SimpleCurve* +CompoundCurve::getCurveN(std::size_t i) const +{ + return curves[i].get(); +} + +std::string +CompoundCurve::getGeometryType() const +{ + return "CompoundCurve"; +} + +GeometryTypeId +CompoundCurve::getGeometryTypeId() const +{ + return GEOS_COMPOUNDCURVE; +} + +double +CompoundCurve::getLength() const +{ + double sum = 0; + for (const auto& curve : curves) { + sum += curve->getLength(); + } + return sum; +} + +std::size_t +CompoundCurve::getNumCurves() const +{ + return curves.size(); +} + +std::size_t +CompoundCurve::getNumPoints() const +{ + std::size_t n =0; + for (const auto& curve : curves) { + n += curve->getNumPoints(); + } + return n; +} + +bool +CompoundCurve::hasZ() const +{ + return std::any_of(curves.begin(), curves.end(), [](const auto& curve) { + return curve->hasZ(); + }); +} + +bool +CompoundCurve::hasM() const +{ + return std::any_of(curves.begin(), curves.end(), [](const auto& curve) { + return curve->hasM(); + }); +} + +bool +CompoundCurve::hasCurvedComponents() const +{ + for (const auto& curve : curves) { + if (curve->hasCurvedComponents()) { + return true; + } + } + return false; +} + +bool +CompoundCurve::isClosed() const +{ + if (isEmpty()) { + return false; + } + + const SimpleCurve& first = *curves.front(); + const SimpleCurve& last = *curves.back(); + + return first.getCoordinateN(0) == last.getCoordinateN(last.getNumPoints() - 1); +} + +bool +CompoundCurve::isEmpty() const +{ + return !std::any_of(curves.begin(), curves.end(), [](const auto& curve) { + return !curve->isEmpty(); + }); +} + +void +CompoundCurve::normalize() +{ + throw util::UnsupportedOperationException(); +} + +std::unique_ptr +CompoundCurve::reverse() const +{ + return std::unique_ptr(reverseImpl()); +} + +CompoundCurve* +CompoundCurve::reverseImpl() const +{ + std::vector> reversed(curves.size()); + std::transform(curves.rbegin(), curves.rend(), reversed.begin(), [](const auto& curve) { + return std::unique_ptr(static_cast(curve->reverse().release())); + }); + + return getFactory()->createCompoundCurve(std::move(reversed)).release(); +} + +} +} diff --git a/deps/libgeos/geos/src/geom/CoordinateSequence.cpp b/deps/libgeos/geos/src/geom/CoordinateSequence.cpp index fc36b4bb3..599ca6ffb 100644 --- a/deps/libgeos/geos/src/geom/CoordinateSequence.cpp +++ b/deps/libgeos/geos/src/geom/CoordinateSequence.cpp @@ -43,7 +43,7 @@ static Profiler* profiler = Profiler::instance(); // - XYM sequences will be stored as XYZM // This prevents incorrect results when an XYZ Coordinate is read from // a sequence storing XY or XYM. When GEOS is changed to check -// coordinate types throughout the libary, this can be undefined to +// coordinate types throughout the library, this can be undefined to // store coordinates efficiently. #define GEOS_COORDSEQ_PADZ @@ -151,7 +151,7 @@ CoordinateSequence::initialize() void CoordinateSequence::add(const CoordinateSequence& cs, std::size_t from, std::size_t to) { - if (cs.stride() == stride() && cs.hasM() == cs.hasM()) { + if (cs.stride() == stride() && cs.hasM() == hasM()) { m_vect.insert(m_vect.end(), std::next(cs.m_vect.cbegin(), static_cast(from * stride())), std::next(cs.m_vect.cbegin(), static_cast((to + 1u)*stride()))); diff --git a/deps/libgeos/geos/src/geom/Curve.cpp b/deps/libgeos/geos/src/geom/Curve.cpp new file mode 100644 index 000000000..91fd60da2 --- /dev/null +++ b/deps/libgeos/geos/src/geom/Curve.cpp @@ -0,0 +1,59 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include +#include + +namespace geos { +namespace geom { + +void +Curve::apply_ro(GeometryComponentFilter* filter) const +{ + assert(filter); + filter->filter_ro(this); +} + +void +Curve::apply_ro(GeometryFilter* filter) const +{ + assert(filter); + filter->filter_ro(this); +} + +void +Curve::apply_rw(GeometryComponentFilter* filter) +{ + assert(filter); + filter->filter_rw(this); +} + +void +Curve::apply_rw(GeometryFilter* filter) +{ + assert(filter); + filter->filter_rw(this); +} + +bool +Curve::isRing() const +{ + return isClosed() && isSimple(); +} + + +} +} diff --git a/deps/libgeos/geos/src/geom/CurvePolygon.cpp b/deps/libgeos/geos/src/geom/CurvePolygon.cpp new file mode 100644 index 000000000..c5213602c --- /dev/null +++ b/deps/libgeos/geos/src/geom/CurvePolygon.cpp @@ -0,0 +1,92 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include +#include +#include +#include +#include + +namespace geos { +namespace geom { + + std::unique_ptr + CurvePolygon::getCoordinates() const + { + auto coordinates = shell->getCoordinates(); + for (const auto& hole : holes) { + if (auto simpleHole = dynamic_cast(hole.get())) { + coordinates->add(*simpleHole->getCoordinatesRO()); + } else { + coordinates->add(*hole->getCoordinates()); + } + } + return coordinates; + } + + std::string CurvePolygon::getGeometryType() const { + return "CurvePolygon"; + } + + GeometryTypeId CurvePolygon::getGeometryTypeId() const { + return GEOS_CURVEPOLYGON; + } + + std::unique_ptr + CurvePolygon::getBoundary() const { + throw util::UnsupportedOperationException(); + } + + void + CurvePolygon::normalize() { + throw util::UnsupportedOperationException(); + } + + double CurvePolygon::getArea() const { + double sum = algorithm::Area::ofClosedCurve(*shell); + for (const auto& hole : holes) { + sum -= algorithm::Area::ofClosedCurve(*hole); + } + return sum; + } + + bool CurvePolygon::hasCurvedComponents() const { + if (shell->hasCurvedComponents()) { + return true; + } + for (const auto& hole : holes) { + if (hole->hasCurvedComponents()) { + return true; + } + } + return false; + } + + Geometry* + CurvePolygon::cloneImpl() const { + return new CurvePolygon(*this); + } + + Geometry* + CurvePolygon::reverseImpl() const { + std::unique_ptr revShell(static_cast(shell->reverse().release())); + std::vector> revHoles(holes.size()); + for (std::size_t i = 0; i < revHoles.size(); i++) { + revHoles[i].reset(static_cast(holes[i]->reverse().release())); + } + return new CurvePolygon(std::move(revShell), std::move(revHoles), *getFactory()); + } + +} +} diff --git a/deps/libgeos/geos/src/geom/Geometry.cpp b/deps/libgeos/geos/src/geom/Geometry.cpp index dfd37113e..433933587 100644 --- a/deps/libgeos/geos/src/geom/Geometry.cpp +++ b/deps/libgeos/geos/src/geom/Geometry.cpp @@ -39,12 +39,15 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -71,6 +74,7 @@ #define SHORTCIRCUIT_PREDICATES 1 //#define USE_RECTANGLE_INTERSECTION 1 +#define USE_RELATENG 1 using namespace geos::algorithm; using namespace geos::operation::valid; @@ -205,6 +209,8 @@ Geometry::getCentroid(CoordinateXY& ret) const std::unique_ptr Geometry::getInteriorPoint() const { + geos::util::ensureNoCurvedComponents(this); + Coordinate interiorPt; int dim = getDimension(); if(dim == 0) { @@ -255,7 +261,11 @@ Geometry::getEnvelope() const bool Geometry::disjoint(const Geometry* g) const { +#if USE_RELATENG + return operation::relateng::RelateNG::disjoint(this, g); +#else return !intersects(g); +#endif } bool @@ -267,9 +277,14 @@ Geometry::touches(const Geometry* g) const return false; } #endif + +#if USE_RELATENG + return operation::relateng::RelateNG::touches(this, g); +#else std::unique_ptr im(relate(g)); bool res = im->isTouches(getDimension(), g->getDimension()); return res; +#endif } bool @@ -308,19 +323,35 @@ Geometry::intersects(const Geometry* g) const return predicate::RectangleIntersects::intersects(*p, *this); } - if (getGeometryTypeId() == GEOS_GEOMETRYCOLLECTION) { + auto typ = getGeometryTypeId(); + if (typ == GEOS_CURVEPOLYGON && g->getGeometryTypeId() == GEOS_POINT) { + auto loc = locate::SimplePointInAreaLocator::locatePointInSurface(*g->getCoordinate(), *detail::down_cast(this)); + return loc != Location::EXTERIOR; + } else if (typ == GEOS_POINT && g->getGeometryTypeId() == GEOS_CURVEPOLYGON) { + auto loc = locate::SimplePointInAreaLocator::locatePointInSurface(*getCoordinate(), *detail::down_cast(g)); + return loc != Location::EXTERIOR; + } + +#if USE_RELATENG + return operation::relateng::RelateNG::intersects(this, g); +#else + if (typ == GEOS_GEOMETRYCOLLECTION) { auto im = relate(g); bool res = im->isIntersects(); return res; } else { return prep::PreparedGeometryFactory::prepare(this)->intersects(g); } +#endif } /*public*/ bool Geometry::covers(const Geometry* g) const { +#if USE_RELATENG + return operation::relateng::RelateNG::covers(this, g); +#else // optimization - lower dimension cannot cover areas if(g->getDimension() == 2 && getDimension() < 2) { return false; @@ -348,12 +379,27 @@ Geometry::covers(const Geometry* g) const std::unique_ptr im(relate(g)); return im->isCovers(); +#endif } +/*public*/ +bool +Geometry::coveredBy(const Geometry* g) const +{ +#if USE_RELATENG + return operation::relateng::RelateNG::coveredBy(this, g); +#else + return covers(g, this); +#endif +} bool Geometry::crosses(const Geometry* g) const { +#if USE_RELATENG + return operation::relateng::RelateNG::crosses(this, g); +#else + #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->intersects(g->getEnvelopeInternal())) { @@ -363,17 +409,27 @@ Geometry::crosses(const Geometry* g) const std::unique_ptr im(relate(g)); bool res = im->isCrosses(getDimension(), g->getDimension()); return res; + +#endif } bool Geometry::within(const Geometry* g) const { +#if USE_RELATENG + return operation::relateng::RelateNG::within(this, g); +#else return g->contains(this); +#endif } bool Geometry::contains(const Geometry* g) const { +#if USE_RELATENG + return operation::relateng::RelateNG::contains(this, g); +#else + // optimization - lower dimension cannot contain areas if(g->getDimension() == 2 && getDimension() < 2) { return false; @@ -406,11 +462,16 @@ Geometry::contains(const Geometry* g) const std::unique_ptr im(relate(g)); bool res = im->isContains(); return res; +#endif } bool Geometry::overlaps(const Geometry* g) const { +#if USE_RELATENG + return operation::relateng::RelateNG::overlaps(this, g); +#else + #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->intersects(g->getEnvelopeInternal())) { @@ -420,19 +481,29 @@ Geometry::overlaps(const Geometry* g) const std::unique_ptr im(relate(g)); bool res = im->isOverlaps(getDimension(), g->getDimension()); return res; + +#endif } bool Geometry::relate(const Geometry* g, const std::string& intersectionPattern) const { +#if USE_RELATENG + return operation::relateng::RelateNG::relate(this, g, intersectionPattern); +#else std::unique_ptr im(relate(g)); bool res = im->matches(intersectionPattern); return res; +#endif } bool Geometry::equals(const Geometry* g) const { +#if USE_RELATENG + return operation::relateng::RelateNG::equalsTopo(this, g); +#else + #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->equals(g->getEnvelopeInternal())) { @@ -450,12 +521,17 @@ Geometry::equals(const Geometry* g) const std::unique_ptr im(relate(g)); bool res = im->isEquals(getDimension(), g->getDimension()); return res; +#endif } std::unique_ptr Geometry::relate(const Geometry* other) const { +#if USE_RELATENG + return operation::relateng::RelateNG::relate(this, other); +#else return RelateOp::relate(this, other); +#endif } std::unique_ptr @@ -689,78 +765,6 @@ Geometry::GeometryChangedFilter::filter_rw(Geometry* geom) geom->geometryChangedAction(); } -int -Geometry::compare(std::vector a, std::vector b) const -{ - std::size_t i = 0; - std::size_t j = 0; - while(i < a.size() && j < b.size()) { - Coordinate& aCoord = a[i]; - Coordinate& bCoord = b[j]; - int comparison = aCoord.compareTo(bCoord); - if(comparison != 0) { - return comparison; - } - i++; - j++; - } - if(i < a.size()) { - return 1; - } - if(j < b.size()) { - return -1; - } - return 0; -} - -int -Geometry::compare(std::vector a, std::vector b) const -{ - std::size_t i = 0; - std::size_t j = 0; - while(i < a.size() && j < b.size()) { - Geometry* aGeom = a[i]; - Geometry* bGeom = b[j]; - int comparison = aGeom->compareTo(bGeom); - if(comparison != 0) { - return comparison; - } - i++; - j++; - } - if(i < a.size()) { - return 1; - } - if(j < b.size()) { - return -1; - } - return 0; -} - -int -Geometry::compare(const std::vector> & a, - const std::vector> & b) const -{ - std::size_t i = 0; - std::size_t j = 0; - while(i < a.size() && j < b.size()) { - Geometry* aGeom = a[i].get(); - Geometry* bGeom = b[j].get(); - int comparison = aGeom->compareTo(bGeom); - if(comparison != 0) { - return comparison; - } - i++; - j++; - } - if(i < a.size()) { - return 1; - } - if(j < b.size()) { - return -1; - } - return 0; -} /** * Returns the minimum distance between this Geometry @@ -868,6 +872,11 @@ Geometry::getPrecisionModel() const return _factory->getPrecisionModel(); } +bool +Geometry::hasCurvedComponents() const { + return false; +} + } // namespace geos::geom } // namespace geos diff --git a/deps/libgeos/geos/src/geom/GeometryCollection.cpp b/deps/libgeos/geos/src/geom/GeometryCollection.cpp index 107b3b8bd..80ecbccae 100644 --- a/deps/libgeos/geos/src/geom/GeometryCollection.cpp +++ b/deps/libgeos/geos/src/geom/GeometryCollection.cpp @@ -333,6 +333,15 @@ GeometryCollection::compareToSameClass(const Geometry* g) const return compare(geometries, gc->geometries); } +bool GeometryCollection::hasCurvedComponents() const { + for (const auto& g : geometries) { + if (g->hasCurvedComponents()) { + return true; + } + } + return false; +} + const CoordinateXY* GeometryCollection::getCoordinate() const { @@ -447,5 +456,7 @@ GeometryCollection::reverseImpl() const return getFactory()->createGeometryCollection(std::move(reversed)).release(); } + + } // namespace geos::geom } // namespace geos diff --git a/deps/libgeos/geos/src/geom/GeometryFactory.cpp b/deps/libgeos/geos/src/geom/GeometryFactory.cpp index d30ee4906..4e97f85cd 100644 --- a/deps/libgeos/geos/src/geom/GeometryFactory.cpp +++ b/deps/libgeos/geos/src/geom/GeometryFactory.cpp @@ -20,14 +20,19 @@ #include #include +#include +#include +#include #include #include #include #include #include +#include #include #include #include +#include #include #include #include @@ -220,14 +225,20 @@ GeometryFactory::createPoint(std::size_t coordinateDimension) const return std::unique_ptr(new Point(std::move(seq), this)); } +/*public*/ +std::unique_ptr +GeometryFactory::createPoint(bool hasZ, bool hasM) const +{ + CoordinateSequence seq(0u, hasZ, hasM); + return std::unique_ptr(new Point(std::move(seq), this)); +} + /*public*/ std::unique_ptr GeometryFactory::createPoint(std::unique_ptr&& coords) const { if (!coords) { return createPoint(); - } else if ((*coords).isNullPoint()) { - return createPoint((*coords).getDimension()); } return std::unique_ptr(new Point(std::move(*coords), this)); } @@ -235,55 +246,32 @@ GeometryFactory::createPoint(std::unique_ptr&& coords) const std::unique_ptr GeometryFactory::createPoint(const CoordinateXY& coordinate) const { - if(coordinate.isNull()) { - return createPoint(2); - } - else { - return std::unique_ptr(new Point(coordinate, this)); - } + return std::unique_ptr(new Point(coordinate, this)); } /*public*/ std::unique_ptr GeometryFactory::createPoint(const Coordinate& coordinate) const { - if(coordinate.isNull()) { - return createPoint(3); - } - else { - return std::unique_ptr(new Point(coordinate, this)); - } + return std::unique_ptr(new Point(coordinate, this)); } std::unique_ptr GeometryFactory::createPoint(const CoordinateXYM& coordinate) const { - if(coordinate.isNull()) { - return createPoint(4); // can't do XYM! - } - else { - return std::unique_ptr(new Point(coordinate, this)); - } + return std::unique_ptr(new Point(coordinate, this)); } std::unique_ptr GeometryFactory::createPoint(const CoordinateXYZM& coordinate) const { - if(coordinate.isNull()) { - return createPoint(4); - } - else { - return std::unique_ptr(new Point(coordinate, this)); - } + return std::unique_ptr(new Point(coordinate, this)); } /*public*/ std::unique_ptr GeometryFactory::createPoint(const CoordinateSequence& fromCoords) const { - if (fromCoords.isNullPoint()) { - return createPoint(fromCoords.getDimension()); - } CoordinateSequence newCoords(fromCoords); return std::unique_ptr(new Point(std::move(newCoords), this)); @@ -328,6 +316,23 @@ GeometryFactory::createMultiLineString(std::vector> && return std::unique_ptr(new MultiLineString(std::move(fromLines), *this)); } +std::unique_ptr +GeometryFactory::createMultiCurve() const { + return createMultiCurve(std::vector>()); +} + +std::unique_ptr +GeometryFactory::createMultiCurve(std::vector> && fromCurves) const { + // Can't use make_unique because constructor is protected + return std::unique_ptr(new MultiCurve(std::move(fromCurves), *this)); +} + +std::unique_ptr +GeometryFactory::createMultiCurve(std::vector> && fromCurves) const { + // Can't use make_unique because constructor is protected + return std::unique_ptr(new MultiCurve(std::move(fromCurves), *this)); +} + /*public*/ std::unique_ptr GeometryFactory::createGeometryCollection() const @@ -337,9 +342,26 @@ GeometryFactory::createGeometryCollection() const /*public*/ std::unique_ptr -GeometryFactory::createEmptyGeometry() const +GeometryFactory::createEmptyGeometry(GeometryTypeId type, bool hasZ, bool hasM) const { - return createGeometryCollection(); + switch (type) { + case GEOS_POINT: return createPoint(hasZ, hasM); + case GEOS_LINESTRING: return createLineString(hasZ, hasM); + case GEOS_LINEARRING: return createLinearRing(hasZ, hasM); + case GEOS_POLYGON: return createPolygon(hasZ, hasM); + case GEOS_MULTIPOINT: return createMultiPoint(); + case GEOS_MULTILINESTRING: return createMultiLineString(); + case GEOS_MULTIPOLYGON: return createMultiPolygon(); + case GEOS_GEOMETRYCOLLECTION: return createGeometryCollection(); + case GEOS_CIRCULARSTRING: return createCircularString(hasZ, hasM); + case GEOS_COMPOUNDCURVE: return createCompoundCurve(); + case GEOS_CURVEPOLYGON: return createCurvePolygon(hasZ, hasM); + case GEOS_MULTICURVE: return createMultiCurve(); + case GEOS_MULTISURFACE: return createMultiSurface(); + default: + throw geos::util::IllegalArgumentException("Unexpected GeometryTypeId"); + + } } /*public*/ @@ -390,6 +412,27 @@ GeometryFactory::createMultiPolygon(const std::vector& fromPoly return createMultiPolygon(std::move(newGeoms)); } +std::unique_ptr +GeometryFactory::createMultiSurface() const +{ + // Can't use make_unique because constructor is protected + return std::unique_ptr(new MultiSurface(std::vector>(), *this)); +} + +std::unique_ptr +GeometryFactory::createMultiSurface(std::vector> && newSurfaces) const +{ + // Can't use make_unique because constructor is protected + return std::unique_ptr(new MultiSurface(std::move(newSurfaces), *this)); +} + +std::unique_ptr +GeometryFactory::createMultiSurface(std::vector> && newSurfaces) const +{ + // Can't use make_unique because constructor is protected + return std::unique_ptr(new MultiSurface(std::move(newSurfaces), *this)); +} + /*public*/ std::unique_ptr GeometryFactory::createLinearRing(std::size_t coordinateDimension) const @@ -399,6 +442,15 @@ GeometryFactory::createLinearRing(std::size_t coordinateDimension) const return std::unique_ptr(new LinearRing(std::move(cs), *this)); } +/*public*/ +std::unique_ptr +GeometryFactory::createLinearRing(bool hasZ, bool hasM) const +{ + // Can't use make_unique with protected constructor + auto cs = detail::make_unique(0u, hasZ, hasM); + return std::unique_ptr(new LinearRing(std::move(cs), *this)); +} + std::unique_ptr GeometryFactory::createLinearRing(CoordinateSequence::Ptr && newCoords) const { @@ -469,6 +521,15 @@ GeometryFactory::createPolygon(std::size_t coordinateDimension) const return createPolygon(std::move(lr)); } +/*public*/ +std::unique_ptr +GeometryFactory::createPolygon(bool hasZ, bool hasM) const +{ + auto cs = detail::make_unique(0u, hasZ, hasM); + auto lr = createLinearRing(std::move(cs)); + return createPolygon(std::move(lr)); +} + std::unique_ptr GeometryFactory::createPolygon(std::unique_ptr && shell) const @@ -513,6 +574,33 @@ const return new Polygon(std::move(newRing), std::move(newHoles), *this); } +/* public */ +std::unique_ptr +GeometryFactory::createCurvePolygon(bool hasZ, bool hasM) +const +{ + // Can't use make_unique with protected constructor + return std::unique_ptr(new CurvePolygon(createLinearRing(hasZ, hasM), *this)); +} + +/* public */ +std::unique_ptr +GeometryFactory::createCurvePolygon(std::unique_ptr && shell) +const +{ + // Can't use make_unique with protected constructor + return std::unique_ptr(new CurvePolygon(std::move(shell), *this)); +} + +/* public */ +std::unique_ptr +GeometryFactory::createCurvePolygon(std::unique_ptr && shell, std::vector> && holes) +const +{ + // Can't use make_unique with protected constructor + return std::unique_ptr(new CurvePolygon(std::move(shell), std::move(holes), *this)); +} + /*public*/ std::unique_ptr GeometryFactory::createLineString(std::size_t coordinateDimension) const @@ -521,6 +609,22 @@ GeometryFactory::createLineString(std::size_t coordinateDimension) const return createLineString(std::move(cs)); } +/*public*/ +std::unique_ptr +GeometryFactory::createLineString(bool hasZ, bool hasM) const +{ + auto cs = detail::make_unique(0u, hasZ, hasM); + return createLineString(std::move(cs)); +} + +/*public*/ +std::unique_ptr +GeometryFactory::createCircularString(bool hasZ, bool hasM) const +{ + auto cs = detail::make_unique(0u, hasZ, hasM); + return createCircularString(std::move(cs)); +} + /*public*/ std::unique_ptr GeometryFactory::createLineString(const LineString& ls) const @@ -529,15 +633,53 @@ GeometryFactory::createLineString(const LineString& ls) const return std::unique_ptr(new LineString(ls)); } +/*public*/ +std::unique_ptr +GeometryFactory::createCircularString(const CircularString& ls) const +{ + // Can't use make_unique with protected constructor + return std::unique_ptr(new CircularString(ls)); +} + /*public*/ std::unique_ptr GeometryFactory::createLineString(CoordinateSequence::Ptr && newCoords) const { + if (!newCoords) + return createLineString(); // Can't use make_unique with protected constructor return std::unique_ptr(new LineString(std::move(newCoords), *this)); } +/*public*/ +std::unique_ptr +GeometryFactory::createCircularString(CoordinateSequence::Ptr && newCoords) +const +{ + if (!newCoords) + return createCircularString(false, false); + // Can't use make_unique with protected constructor + return std::unique_ptr(new CircularString(std::move(newCoords), *this)); +} + +/*public*/ +std::unique_ptr +GeometryFactory::createCompoundCurve() +const +{ + std::vector> curves; + return createCompoundCurve(std::move(curves)); +} + +/*public*/ +std::unique_ptr +GeometryFactory::createCompoundCurve(std::vector>&& curves) +const +{ + return std::unique_ptr(new CompoundCurve(std::move(curves), *this)); +} + /*public*/ std::unique_ptr GeometryFactory::createLineString(const CoordinateSequence& fromCoords) @@ -547,6 +689,15 @@ const return std::unique_ptr(new LineString(fromCoords.clone(), *this)); } +/*public*/ +std::unique_ptr +GeometryFactory::createCircularString(const CoordinateSequence& fromCoords) +const +{ + // Can't use make_unique with protected constructor + return std::unique_ptr(new CircularString(fromCoords.clone(), *this)); +} + /*public*/ std::unique_ptr GeometryFactory::createEmpty(int dimension) const diff --git a/deps/libgeos/geos/src/geom/IntersectionMatrix.cpp b/deps/libgeos/geos/src/geom/IntersectionMatrix.cpp index 82acfcf4e..f7ace8a9e 100644 --- a/deps/libgeos/geos/src/geom/IntersectionMatrix.cpp +++ b/deps/libgeos/geos/src/geom/IntersectionMatrix.cpp @@ -144,7 +144,7 @@ IntersectionMatrix::set(Location row, Location col, int dimensionValue) void IntersectionMatrix::set(const std::string& dimensionSymbols) { - auto limit = dimensionSymbols.length(); + auto limit = std::min(dimensionSymbols.length(), static_cast(9)); for(std::size_t i = 0; i < limit; i++) { auto row = i / firstDim; diff --git a/deps/libgeos/geos/src/geom/LineString.cpp b/deps/libgeos/geos/src/geom/LineString.cpp index e3c84c3dc..90e37479f 100644 --- a/deps/libgeos/geos/src/geom/LineString.cpp +++ b/deps/libgeos/geos/src/geom/LineString.cpp @@ -50,10 +50,7 @@ LineString::~LineString(){} /*protected*/ LineString::LineString(const LineString& ls) - : - Geometry(ls), - points(ls.points->clone()), - envelope(ls.envelope) + : SimpleCurve(ls) { } @@ -61,9 +58,7 @@ LineString::LineString(const LineString& ls) LineString::LineString(CoordinateSequence::Ptr && newCoords, const GeometryFactory& factory) : - Geometry(&factory), - points(newCoords ? std::move(newCoords) : detail::make_unique()), - envelope(computeEnvelopeInternal()) + SimpleCurve(std::move(newCoords), true, factory) { validateConstruction(); } @@ -88,7 +83,7 @@ void LineString::validateConstruction() { if(points.get() == nullptr) { - points = detail::make_unique(); + points = std::make_unique(); return; } @@ -97,126 +92,6 @@ LineString::validateConstruction() } } -std::unique_ptr -LineString::getCoordinates() const -{ - assert(points.get()); - return points->clone(); - //return points; -} - -const CoordinateSequence* -LineString::getCoordinatesRO() const -{ - assert(nullptr != points.get()); - return points.get(); -} - -std::unique_ptr -LineString::releaseCoordinates() -{ - auto newPts = detail::make_unique(0u, points->hasZ(), points->hasM()); - auto ret = std::move(points); - points = std::move(newPts); - geometryChanged(); - return ret; -} - -const Coordinate& -LineString::getCoordinateN(std::size_t n) const -{ - assert(points.get()); - return points->getAt(n); -} - -Dimension::DimensionType -LineString::getDimension() const -{ - return Dimension::L; // line -} - -uint8_t -LineString::getCoordinateDimension() const -{ - return (uint8_t) points->getDimension(); -} - -bool -LineString::hasM() const -{ - return points->hasM(); -} - -bool -LineString::hasZ() const -{ - return points->hasZ(); -} - -int -LineString::getBoundaryDimension() const -{ - if(isClosed()) { - return Dimension::False; - } - return 0; -} - -bool -LineString::isEmpty() const -{ - assert(points.get()); - return points->isEmpty(); -} - -std::size_t -LineString::getNumPoints() const -{ - assert(points.get()); - return points->getSize(); -} - -std::unique_ptr -LineString::getPointN(std::size_t n) const -{ - assert(getFactory()); - assert(points.get()); - return std::unique_ptr(getFactory()->createPoint(points->getAt(n))); -} - -std::unique_ptr -LineString::getStartPoint() const -{ - if(isEmpty()) { - return nullptr; - } - return getPointN(0); -} - -std::unique_ptr -LineString::getEndPoint() const -{ - if(isEmpty()) { - return nullptr; - } - return getPointN(getNumPoints() - 1); -} - -bool -LineString::isClosed() const -{ - if(isEmpty()) { - return false; - } - - return points->front().equals2D(points->back()); -} - -bool -LineString::isRing() const -{ - return isClosed() && isSimple(); -} std::string LineString::getGeometryType() const @@ -224,182 +99,6 @@ LineString::getGeometryType() const return "LineString"; } -std::unique_ptr -LineString::getBoundary() const -{ - operation::BoundaryOp bop(*this); - return bop.getBoundary(); -} - -bool -LineString::isCoordinate(Coordinate& pt) const -{ - assert(points.get()); - std::size_t npts = points->getSize(); - for(std::size_t i = 0; i < npts; i++) { - if(points->getAt(i) == pt) { - return true; - } - } - return false; -} - -/*protected*/ -Envelope -LineString::computeEnvelopeInternal() const -{ - if(isEmpty()) { - return Envelope(); - } - - return points->getEnvelope(); -} - -bool -LineString::equalsExact(const Geometry* other, double tolerance) const -{ - if(!isEquivalentClass(other)) { - return false; - } - - const LineString* otherLineString = detail::down_cast(other); - std::size_t npts = points->getSize(); - if(npts != otherLineString->points->getSize()) { - return false; - } - for(std::size_t i = 0; i < npts; ++i) { - if(!equal(points->getAt(i), otherLineString->points->getAt(i), tolerance)) { - return false; - } - } - return true; -} - -bool -LineString::equalsIdentical(const Geometry* other_g) const -{ - if(!isEquivalentClass(other_g)) { - return false; - } - - const auto& other = static_cast(*other_g); - - if (envelope != other.envelope) { - return false; - } - - return getCoordinatesRO()->equalsIdentical(*other.getCoordinatesRO()); -} - -void -LineString::apply_rw(const CoordinateFilter* filter) -{ - assert(points.get()); - points->apply_rw(filter); -} - -void -LineString::apply_ro(CoordinateFilter* filter) const -{ - assert(points.get()); - points->apply_ro(filter); -} - -void -LineString::apply_rw(GeometryFilter* filter) -{ - assert(filter); - filter->filter_rw(this); -} - -void -LineString::apply_ro(GeometryFilter* filter) const -{ - assert(filter); - filter->filter_ro(this); -} - -/*private*/ -void -LineString::normalizeClosed() -{ - if(isEmpty()) { - return; - } - - const auto& ringCoords = getCoordinatesRO(); - - auto coords = detail::make_unique(0u, ringCoords->hasZ(), ringCoords->hasM()); - coords->reserve(ringCoords->size()); - // exclude last point (repeated) - coords->add(*ringCoords, 0, ringCoords->size() - 2); - - const CoordinateXY* minCoordinate = coords->minCoordinate(); - - CoordinateSequence::scroll(coords.get(), minCoordinate); - coords->closeRing(true); - - if(coords->size() >= 4 && algorithm::Orientation::isCCW(coords.get())) { - coords->reverse(); - } - - points = std::move(coords); -} - -/*public*/ -void -LineString::normalize() -{ - if (isEmpty()) return; - assert(points.get()); - if (isClosed()) { - normalizeClosed(); - return; - } - std::size_t npts = points->getSize(); - std::size_t n = npts / 2; - for(std::size_t i = 0; i < n; i++) { - std::size_t j = npts - 1 - i; - if(!(points->getAt(i) == points->getAt(j))) { - if(points->getAt(i).compareTo(points->getAt(j)) > 0) { - points->reverse(); - } - return; - } - } -} - -int -LineString::compareToSameClass(const Geometry* ls) const -{ - const LineString* line = detail::down_cast(ls); - - // MD - optimized implementation - std::size_t mynpts = points->getSize(); - std::size_t othnpts = line->points->getSize(); - if(mynpts > othnpts) { - return 1; - } - if(mynpts < othnpts) { - return -1; - } - for(std::size_t i = 0; i < mynpts; i++) { - int cmp = points->getAt(i).compareTo(line->points->getAt(i)); - if(cmp) { - return cmp; - } - } - return 0; -} - -const CoordinateXY* -LineString::getCoordinate() const -{ - if(isEmpty()) { - return nullptr; - } - return &(points->getAt(0)); -} double LineString::getLength() const @@ -407,53 +106,6 @@ LineString::getLength() const return Length::ofLine(points.get()); } -void -LineString::apply_rw(GeometryComponentFilter* filter) -{ - assert(filter); - filter->filter_rw(this); -} - -void -LineString::apply_ro(GeometryComponentFilter* filter) const -{ - assert(filter); - filter->filter_ro(this); -} - -void -LineString::apply_rw(CoordinateSequenceFilter& filter) -{ - std::size_t npts = points->size(); - if(!npts) { - return; - } - for(std::size_t i = 0; i < npts; ++i) { - filter.filter_rw(*points, i); - if(filter.isDone()) { - break; - } - } - if(filter.isGeometryChanged()) { - geometryChanged(); - } -} - -void -LineString::apply_ro(CoordinateSequenceFilter& filter) const -{ - std::size_t npts = points->size(); - if(!npts) { - return; - } - for(std::size_t i = 0; i < npts; ++i) { - filter.filter_ro(*points, i); - if(filter.isDone()) { - break; - } - } - //if (filter.isGeometryChanged()) geometryChanged(); -} GeometryTypeId LineString::getGeometryTypeId() const diff --git a/deps/libgeos/geos/src/geom/MultiCurve.cpp b/deps/libgeos/geos/src/geom/MultiCurve.cpp new file mode 100644 index 000000000..31e13eb2c --- /dev/null +++ b/deps/libgeos/geos/src/geom/MultiCurve.cpp @@ -0,0 +1,116 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include +#include +#include +#include + +namespace geos { +namespace geom { + +MultiCurve::MultiCurve(std::vector>&& newLines, + const GeometryFactory& factory) + : GeometryCollection(std::move(newLines), factory) +{ + for (const auto& geom : geometries) { + if (!dynamic_cast(geom.get())) { + throw util::IllegalArgumentException("All elements of MultiCurve must be a Curve"); + } + } +} + +MultiCurve::MultiCurve(std::vector>&& newLines, + const GeometryFactory& factory) + : GeometryCollection(std::move(newLines), factory) +{} + +std::unique_ptr +MultiCurve::getBoundary() const +{ + operation::BoundaryOp bop(*this); + return bop.getBoundary(); +} + +int +MultiCurve::getBoundaryDimension() const +{ + if (isClosed()) { + return Dimension::False; + } + return 0; +} + +Dimension::DimensionType +MultiCurve::getDimension() const +{ + return Dimension::L; // line +} + +const Curve* +MultiCurve::getGeometryN(std::size_t i) const +{ + return static_cast(geometries[i].get()); +} + +std::string +MultiCurve::getGeometryType() const +{ + return "MultiCurve"; +} + +GeometryTypeId +MultiCurve::getGeometryTypeId() const +{ + return GEOS_MULTICURVE; +} + +bool +MultiCurve::isClosed() const +{ + if (isEmpty()) { + return false; + } + for (const auto& g : geometries) { + const Curve* ls = detail::down_cast(g.get()); + if (! ls->isClosed()) { + return false; + } + } + return true; +} + +MultiCurve* +MultiCurve::reverseImpl() const +{ + if (isEmpty()) { + return clone().release(); + } + + std::vector> reversed(geometries.size()); + + std::transform(geometries.begin(), + geometries.end(), + reversed.begin(), + [](const std::unique_ptr& g) { + return g->reverse(); + }); + + return getFactory()->createMultiCurve(std::move(reversed)).release(); +} + +} +} diff --git a/deps/libgeos/geos/src/geom/MultiSurface.cpp b/deps/libgeos/geos/src/geom/MultiSurface.cpp new file mode 100644 index 000000000..b8e299f6a --- /dev/null +++ b/deps/libgeos/geos/src/geom/MultiSurface.cpp @@ -0,0 +1,109 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include +#include +#include + +namespace geos { +namespace geom { + +MultiSurface::MultiSurface(std::vector>&& newPolys, const GeometryFactory& factory) + : GeometryCollection(std::move(newPolys), factory) +{ + for (const auto& geom : geometries) { + if (!dynamic_cast(geom.get())) { + throw util::IllegalArgumentException("All elements of MultiSurface must be a Surface"); + } + } +} + +MultiSurface::MultiSurface(std::vector>&& newPolys, const GeometryFactory& factory) + : GeometryCollection(std::move(newPolys), factory) +{ +} + +MultiSurface::~MultiSurface() {} + +std::unique_ptr +MultiSurface::getBoundary() const +{ + if (isEmpty()) { + return std::unique_ptr(getFactory()->createMultiCurve()); + } + + std::vector> allRings; + for (const auto& pg : geometries) { + auto g = pg->getBoundary(); + + if (g->getNumGeometries() == 1) { + allRings.push_back(std::move(g)); + } + else { + for (auto& gi : (static_cast(*g)).releaseGeometries()) { + allRings.push_back(std::move(gi)); + } + } + } + + return getFactory()->createMultiCurve(std::move(allRings)); +} + +int +MultiSurface::getBoundaryDimension() const +{ + return 1; +} + +Dimension::DimensionType +MultiSurface::getDimension() const +{ + return Dimension::A; // area +} + +std::string +MultiSurface::getGeometryType() const +{ + return "MultiSurface"; +} + +GeometryTypeId +MultiSurface::getGeometryTypeId() const +{ + return GEOS_MULTISURFACE; +} + +MultiSurface* +MultiSurface::reverseImpl() const +{ + if (isEmpty()) { + return clone().release(); + } + + std::vector> reversed(geometries.size()); + + std::transform(geometries.begin(), + geometries.end(), + reversed.begin(), + [](const std::unique_ptr& g) { + return g->reverse(); + }); + + return getFactory()->createMultiSurface(std::move(reversed)).release(); +} + +} +} diff --git a/deps/libgeos/geos/src/geom/Polygon.cpp b/deps/libgeos/geos/src/geom/Polygon.cpp index 515275d51..3163e8543 100644 --- a/deps/libgeos/geos/src/geom/Polygon.cpp +++ b/deps/libgeos/geos/src/geom/Polygon.cpp @@ -28,9 +28,6 @@ #include #include #include -#include -#include -#include #include #include @@ -48,57 +45,14 @@ namespace geos { namespace geom { // geos::geom -/*protected*/ -Polygon::Polygon(const Polygon& p) - : - Geometry(p), - shell(detail::make_unique(*p.shell)), - holes(p.holes.size()) -{ - for(std::size_t i = 0; i < holes.size(); ++i) { - holes[i] = detail::make_unique(*p.holes[i]); - } -} - -Polygon::Polygon(std::unique_ptr && newShell, - const GeometryFactory& newFactory) : - Geometry(&newFactory), - shell(std::move(newShell)) -{ - if(shell == nullptr) { - shell = getFactory()->createLinearRing(); - } -} - -Polygon::Polygon(std::unique_ptr && newShell, - std::vector> && newHoles, - const GeometryFactory& newFactory) : - Geometry(&newFactory), - shell(std::move(newShell)), - holes(std::move(newHoles)) -{ - if(shell == nullptr) { - shell = getFactory()->createLinearRing(); - } - - // TODO move into validateConstruction() method - if(shell->isEmpty() && hasNonEmptyElements(&holes)) { - throw util::IllegalArgumentException("shell is empty but holes are not"); - } - if (hasNullElements(&holes)) { - throw util::IllegalArgumentException("holes must not contain null elements"); - } -} - - std::unique_ptr Polygon::getCoordinates() const { if(isEmpty()) { - return detail::make_unique(0u, hasZ(), hasM()); + return std::make_unique(0u, hasZ(), hasM()); } - auto cl = detail::make_unique(0u, hasZ(), hasM()); + auto cl = std::make_unique(0u, hasZ(), hasM()); cl->reserve(getNumPoints()); // Add shell points @@ -112,110 +66,6 @@ Polygon::getCoordinates() const return cl; } -size_t -Polygon::getNumPoints() const -{ - std::size_t numPoints = shell->getNumPoints(); - for(const auto& lr : holes) { - numPoints += lr->getNumPoints(); - } - return numPoints; -} - -Dimension::DimensionType -Polygon::getDimension() const -{ - return Dimension::A; // area -} - -uint8_t -Polygon::getCoordinateDimension() const -{ - uint8_t dimension = 2; - - if(shell != nullptr) { - dimension = std::max(dimension, shell->getCoordinateDimension()); - } - - for(const auto& hole : holes) { - dimension = std::max(dimension, hole->getCoordinateDimension()); - } - - return dimension; -} - -bool -Polygon::hasM() const { - if (shell->getCoordinatesRO()->hasM()) { - return true; - } - - for (const auto& hole : holes) { - if (hole->getCoordinatesRO()->hasM()) { - return true; - } - } - - return false; -} - -bool -Polygon::hasZ() const { - if (shell->getCoordinatesRO()->hasZ()) { - return true; - } - - for (const auto& hole : holes) { - if (hole->getCoordinatesRO()->hasZ()) { - return true; - } - } - - return false; -} - -int -Polygon::getBoundaryDimension() const -{ - return 1; -} - -bool -Polygon::isEmpty() const -{ - return shell->isEmpty(); -} - -const LinearRing* -Polygon::getExteriorRing() const -{ - return shell.get(); -} - -std::unique_ptr -Polygon::releaseExteriorRing() -{ - return std::move(shell); -} - -size_t -Polygon::getNumInteriorRing() const -{ - return holes.size(); -} - -const LinearRing* -Polygon::getInteriorRingN(std::size_t n) const -{ - return holes[n].get(); -} - -std::vector> -Polygon::releaseInteriorRings() -{ - return std::move(holes); -} - std::string Polygon::getGeometryType() const { @@ -255,151 +105,6 @@ Polygon::getBoundary() const return getFactory()->createMultiLineString(std::move(rings)); } -bool -Polygon::equalsExact(const Geometry* other, double tolerance) const -{ - if(!isEquivalentClass(other)) { - return false; - } - - const Polygon* otherPolygon = detail::down_cast(other); - if(! otherPolygon) { - return false; - } - - if(!shell->equalsExact(otherPolygon->shell.get(), tolerance)) { - return false; - } - - std::size_t nholes = holes.size(); - - if(nholes != otherPolygon->holes.size()) { - return false; - } - - for(std::size_t i = 0; i < nholes; i++) { - const LinearRing* hole = holes[i].get(); - const LinearRing* otherhole = otherPolygon->holes[i].get(); - if(!hole->equalsExact(otherhole, tolerance)) { - return false; - } - } - - return true; -} - -bool -Polygon::equalsIdentical(const Geometry* other_g) const -{ - if(!isEquivalentClass(other_g)) { - return false; - } - - const auto& other = static_cast(*other_g); - - if (getNumInteriorRing() != other.getNumInteriorRing()) { - return false; - } - - if (!getExteriorRing()->equalsIdentical(other.getExteriorRing())) { - return false; - } - - for (std::size_t i = 0; i < getNumInteriorRing(); i++) { - if (!getInteriorRingN(i)->equalsIdentical(other.getInteriorRingN(i))) { - return false; - } - } - - return true; -} - -void -Polygon::apply_ro(CoordinateFilter* filter) const -{ - shell->apply_ro(filter); - for(const auto& lr : holes) { - lr->apply_ro(filter); - } -} - -void -Polygon::apply_rw(const CoordinateFilter* filter) -{ - shell->apply_rw(filter); - for(auto& lr : holes) { - lr->apply_rw(filter); - } -} - -void -Polygon::apply_rw(GeometryFilter* filter) -{ - filter->filter_rw(this); -} - -void -Polygon::apply_ro(GeometryFilter* filter) const -{ - filter->filter_ro(this); -} - -std::unique_ptr -Polygon::convexHull() const -{ - return getExteriorRing()->convexHull(); -} - - -void -Polygon::normalize() -{ - normalize(shell.get(), true); - for(auto& lr : holes) { - normalize(lr.get(), false); - } - std::sort(holes.begin(), holes.end(), [](const std::unique_ptr & a, const std::unique_ptr & b) { - return a->compareTo(b.get()) > 0; - }); -} - -void -Polygon::orientRings(bool exteriorCW) -{ - shell->orient(exteriorCW); - for (auto& hole : holes) { - hole->orient(!exteriorCW); - } -} - -int -Polygon::compareToSameClass(const Geometry* g) const -{ - const Polygon* p = detail::down_cast(g); - int shellComp = shell->compareToSameClass(p->shell.get()); - if (shellComp != 0) { - return shellComp; - } - - size_t nHole1 = getNumInteriorRing(); - size_t nHole2 = p->getNumInteriorRing(); - if (nHole1 < nHole2) { - return -1; - } - if (nHole1 > nHole2) { - return 1; - } - - for (size_t i=0; i < nHole1; i++) { - const LinearRing *lr = p->getInteriorRingN(i); - const int holeComp = getInteriorRingN(i)->compareToSameClass(lr); - if (holeComp != 0) { - return holeComp; - } - } - - return 0; -} /* * TODO: check this function, there should be CoordinateSequence copy @@ -430,11 +135,6 @@ Polygon::normalize(LinearRing* ring, bool clockwise) ring->setPoints(&coords); } -const CoordinateXY* -Polygon::getCoordinate() const -{ - return shell->getCoordinate(); -} /* * Returns the area of this Polygon @@ -453,76 +153,6 @@ Polygon::getArea() const return area; } -/** - * Returns the perimeter of this Polygon - * - * @return the perimeter of the polygon - */ -double -Polygon::getLength() const -{ - double len = 0.0; - len += shell->getLength(); - for(const auto& hole : holes) { - len += hole->getLength(); - } - return len; -} - -void -Polygon::apply_ro(GeometryComponentFilter* filter) const -{ - filter->filter_ro(this); - shell->apply_ro(filter); - for(std::size_t i = 0, n = holes.size(); i < n && !filter->isDone(); ++i) { - holes[i]->apply_ro(filter); - } -} - -void -Polygon::apply_rw(GeometryComponentFilter* filter) -{ - filter->filter_rw(this); - shell->apply_rw(filter); - for(std::size_t i = 0, n = holes.size(); i < n && !filter->isDone(); ++i) { - holes[i]->apply_rw(filter); - } -} - -void -Polygon::apply_rw(CoordinateSequenceFilter& filter) -{ - shell->apply_rw(filter); - - if(! filter.isDone()) { - for(std::size_t i = 0, n = holes.size(); i < n; ++i) { - holes[i]->apply_rw(filter); - if(filter.isDone()) { - break; - } - } - } - if(filter.isGeometryChanged()) { - geometryChanged(); - } -} - -void -Polygon::apply_ro(CoordinateSequenceFilter& filter) const -{ - shell->apply_ro(filter); - - if(! filter.isDone()) { - for(std::size_t i = 0, n = holes.size(); i < n; ++i) { - holes[i]->apply_ro(filter); - if(filter.isDone()) { - break; - } - } - } - //if (filter.isGeometryChanged()) geometryChanged(); -} - GeometryTypeId Polygon::getGeometryTypeId() const { @@ -572,6 +202,16 @@ Polygon::isRectangle() const return true; } +void +Polygon::orientRings(bool exteriorCW) +{ + shell->orient(exteriorCW); + for (auto& hole : holes) { + hole->orient(!exteriorCW); + } +} + + Polygon* Polygon::reverseImpl() const { @@ -591,5 +231,17 @@ Polygon::reverseImpl() const return getFactory()->createPolygon(shell->reverse(), std::move(interiorRingsReversed)).release(); } +void +Polygon::normalize() +{ + normalize(shell.get(), true); + for(auto& lr : holes) { + normalize(lr.get(), false); + } + std::sort(holes.begin(), holes.end(), [](const auto& a, const auto& b) { + return a->compareTo(b.get()) > 0; + }); +} + } // namespace geos::geom } // namespace geos diff --git a/deps/libgeos/geos/src/geom/SimpleCurve.cpp b/deps/libgeos/geos/src/geom/SimpleCurve.cpp new file mode 100644 index 000000000..be9b50b44 --- /dev/null +++ b/deps/libgeos/geos/src/geom/SimpleCurve.cpp @@ -0,0 +1,371 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace geos { +namespace geom { + +SimpleCurve::SimpleCurve(const SimpleCurve& other) + : Curve(other), + points(other.points->clone()), + envelope(other.envelope) +{ +} + +SimpleCurve::SimpleCurve(std::unique_ptr&& newCoords, + bool isLinear, + const GeometryFactory& factory) + : Curve(factory), + points(newCoords ? std::move(newCoords) : std::make_unique()), + envelope(computeEnvelopeInternal(isLinear)) +{ +} + +void +SimpleCurve::apply_ro(CoordinateFilter* filter) const +{ + assert(points.get()); + points->apply_ro(filter); +} + +void +SimpleCurve::apply_ro(CoordinateSequenceFilter& filter) const +{ + std::size_t npts = points->size(); + if (!npts) { + return; + } + for (std::size_t i = 0; i < npts; ++i) { + filter.filter_ro(*points, i); + if (filter.isDone()) { + break; + } + } +} + +void +SimpleCurve::apply_rw(const CoordinateFilter* filter) +{ + assert(points.get()); + points->apply_rw(filter); +} + +void +SimpleCurve::apply_rw(CoordinateSequenceFilter& filter) +{ + std::size_t npts = points->size(); + if (!npts) { + return; + } + for (std::size_t i = 0; i < npts; ++i) { + filter.filter_rw(*points, i); + if (filter.isDone()) { + break; + } + } + if (filter.isGeometryChanged()) { + geometryChanged(); + } +} + +int +SimpleCurve::compareToSameClass(const Geometry* ls) const +{ + const SimpleCurve* line = detail::down_cast(ls); + + // MD - optimized implementation + std::size_t mynpts = points->getSize(); + std::size_t othnpts = line->points->getSize(); + if (mynpts > othnpts) { + return 1; + } + if (mynpts < othnpts) { + return -1; + } + for (std::size_t i = 0; i < mynpts; i++) { + int cmp = points->getAt(i).compareTo(line->points->getAt(i)); + if (cmp) { + return cmp; + } + } + return 0; +} + +Envelope +SimpleCurve::computeEnvelopeInternal(bool isLinear) const +{ + if (isEmpty()) { + return Envelope(); + } + + if (isLinear) { + return points->getEnvelope(); + } + else { + Envelope e; + for (std::size_t i = 2; i < points->size(); i++) { + algorithm::CircularArcs::expandEnvelope(e, + points->getAt(i-2), + points->getAt(i-1), + points->getAt(i)); + } + return e; + } +} + +bool +SimpleCurve::equalsExact(const Geometry* other, double tolerance) const +{ + if (!isEquivalentClass(other)) { + return false; + } + + const SimpleCurve* otherCurve = detail::down_cast(other); + std::size_t npts = points->getSize(); + if (npts != otherCurve->points->getSize()) { + return false; + } + for (std::size_t i = 0; i < npts; ++i) { + if (!equal(points->getAt(i), otherCurve->points->getAt(i), tolerance)) { + return false; + } + } + return true; +} + +bool +SimpleCurve::equalsIdentical(const Geometry* other_g) const +{ + if (!isEquivalentClass(other_g)) { + return false; + } + + const auto& other = static_cast(*other_g); + + if (envelope != other.envelope) { + return false; + } + + return getCoordinatesRO()->equalsIdentical(*other.getCoordinatesRO()); +} + +std::unique_ptr +SimpleCurve::getBoundary() const +{ + operation::BoundaryOp bop(*this); + return bop.getBoundary(); +} + +const CoordinateXY* +SimpleCurve::getCoordinate() const +{ + if (isEmpty()) { + return nullptr; + } + return &(points->getAt(0)); +} + +uint8_t +SimpleCurve::getCoordinateDimension() const +{ + return (uint8_t) points->getDimension(); +} + +const Coordinate& +SimpleCurve::getCoordinateN(std::size_t n) const +{ + assert(points.get()); + return points->getAt(n); +} + +std::unique_ptr +SimpleCurve::getCoordinates() const +{ + assert(points.get()); + return points->clone(); +} + +const CoordinateSequence* +SimpleCurve::getCoordinatesRO() const +{ + assert(nullptr != points.get()); + return points.get(); +} + +const SimpleCurve* +SimpleCurve::getCurveN(std::size_t) const +{ + return this; +} + +std::unique_ptr +SimpleCurve::getEndPoint() const +{ + if (isEmpty()) { + return nullptr; + } + return getPointN(getNumPoints() - 1); +} + +std::size_t +SimpleCurve::getNumCurves() const +{ + return 1; +} + +std::size_t +SimpleCurve::getNumPoints() const +{ + assert(points.get()); + return points->getSize(); +} + +std::unique_ptr +SimpleCurve::getPointN(std::size_t n) const +{ + assert(getFactory()); + assert(points.get()); + return std::unique_ptr(getFactory()->createPoint(points->getAt(n))); +} + +std::unique_ptr +SimpleCurve::getStartPoint() const +{ + if (isEmpty()) { + return nullptr; + } + return getPointN(0); +} + +bool +SimpleCurve::hasM() const +{ + return points->hasM(); +} + +bool +SimpleCurve::hasZ() const +{ + return points->hasZ(); +} + +bool +SimpleCurve::isClosed() const +{ + if (isEmpty()) { + return false; + } + + return points->front().equals2D(points->back()); +} + +bool +SimpleCurve::isCoordinate(CoordinateXY& pt) const +{ + assert(points.get()); + std::size_t npts = points->getSize(); + for (std::size_t i = 0; i < npts; i++) { + if (points->getAt(i) == pt) { + return true; + } + } + return false; +} + +bool +SimpleCurve::isEmpty() const +{ + assert(points.get()); + return points->isEmpty(); +} + +/*public*/ +void +SimpleCurve::normalize() +{ + util::ensureNoCurvedComponents(*this); + + if (isEmpty()) return; + assert(points.get()); + if (isClosed()) { + normalizeClosed(); + return; + } + std::size_t npts = points->getSize(); + std::size_t n = npts / 2; + for (std::size_t i = 0; i < n; i++) { + std::size_t j = npts - 1 - i; + if (!(points->getAt(i) == points->getAt(j))) { + if (points->getAt(i).compareTo(points->getAt(j)) > 0) { + points->reverse(); + } + return; + } + } +} + +/*private*/ +void +SimpleCurve::normalizeClosed() +{ + if (isEmpty()) { + return; + } + + const auto& ringCoords = getCoordinatesRO(); + + auto coords = detail::make_unique(0u, ringCoords->hasZ(), ringCoords->hasM()); + coords->reserve(ringCoords->size()); + // exclude last point (repeated) + coords->add(*ringCoords, 0, ringCoords->size() - 2); + + const CoordinateXY* minCoordinate = coords->minCoordinate(); + + CoordinateSequence::scroll(coords.get(), minCoordinate); + coords->closeRing(true); + + if (coords->size() >= 4 && algorithm::Orientation::isCCW(coords.get())) { + coords->reverse(); + } + + points = std::move(coords); +} + +std::unique_ptr +SimpleCurve::releaseCoordinates() +{ + auto newPts = std::make_unique(0u, points->hasZ(), points->hasM()); + auto ret = std::move(points); + points = std::move(newPts); + geometryChanged(); + return ret; +} + +} // namespace geos::geom +} // namespace geos diff --git a/deps/libgeos/geos/src/geom/Surface.cpp b/deps/libgeos/geos/src/geom/Surface.cpp new file mode 100644 index 000000000..34bb5c601 --- /dev/null +++ b/deps/libgeos/geos/src/geom/Surface.cpp @@ -0,0 +1,286 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences LLC + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/Polygon.java r320 (JTS-1.12) + * + **********************************************************************/ + +#include +#include +#include +#include + +namespace geos { +namespace geom { + +void +Surface::apply_ro(CoordinateFilter* filter) const +{ + getExteriorRing()->apply_ro(filter); + for (std::size_t i = 0; i < getNumInteriorRing(); i++) { + getInteriorRingN(i)->apply_ro(filter); + } +} + +void +Surface::apply_ro(CoordinateSequenceFilter& filter) const +{ + getExteriorRing()->apply_ro(filter); + + for (std::size_t i = 0; !filter.isDone() && i < getNumInteriorRing(); i++) { + getInteriorRingN(i)->apply_ro(filter); + } +} + +void +Surface::apply_ro(GeometryComponentFilter* filter) const +{ + filter->filter_ro(this); + getExteriorRing()->apply_ro(filter); + for (std::size_t i = 0; !filter->isDone() && i < getNumInteriorRing(); i++) { + getInteriorRingN(i)->apply_ro(filter); + } +} + +void +Surface::apply_ro(GeometryFilter* filter) const +{ + filter->filter_ro(this); +} + +void +Surface::apply_rw(const CoordinateFilter* filter) +{ + getExteriorRing()->apply_rw(filter); + for (std::size_t i = 0; i < getNumInteriorRing(); i++) { + getInteriorRingN(i)->apply_rw(filter); + } +} + +void +Surface::apply_rw(CoordinateSequenceFilter& filter) +{ + getExteriorRing()->apply_rw(filter); + + for (std::size_t i = 0; !filter.isDone() && i < getNumInteriorRing(); i++) { + getInteriorRingN(i)->apply_rw(filter); + } + + if (filter.isGeometryChanged()) { + geometryChanged(); + } +} + +void +Surface::apply_rw(GeometryComponentFilter* filter) +{ + filter->filter_rw(this); + getExteriorRing()->apply_rw(filter); + for (std::size_t i = 0; !filter->isDone() && i < getNumInteriorRing(); i++) { + getInteriorRingN(i)->apply_rw(filter); + } +} + +void +Surface::apply_rw(GeometryFilter* filter) +{ + filter->filter_rw(this); +} + +int +Surface::compareToSameClass(const Geometry* g) const +{ + const Surface* p = detail::down_cast(g); + int shellComp = getExteriorRing()->compareTo(p->getExteriorRing()); + if (shellComp != 0) { + return shellComp; + } + + size_t nHole1 = getNumInteriorRing(); + size_t nHole2 = p->getNumInteriorRing(); + if (nHole1 < nHole2) { + return -1; + } + if (nHole1 > nHole2) { + return 1; + } + + for (size_t i=0; i < nHole1; i++) { + const Curve* lr = p->getInteriorRingN(i); + const int holeComp = getInteriorRingN(i)->compareTo(lr); + if (holeComp != 0) { + return holeComp; + } + } + + return 0; +} + +std::unique_ptr +Surface::convexHull() const +{ + return getExteriorRing()->convexHull(); +} + +std::unique_ptr +Surface::createEmptyRing(const GeometryFactory& factory) +{ + return factory.createLinearRing(); +} + +bool +Surface::equalsExact(const Geometry* other, double tolerance) const +{ + if (!isEquivalentClass(other)) { + return false; + } + + const Surface* otherPolygon = detail::down_cast(other); + if (! otherPolygon) { + return false; + } + + if (!getExteriorRing()->equalsExact(otherPolygon->getExteriorRing(), tolerance)) { + return false; + } + + if (getNumInteriorRing() != otherPolygon->getNumInteriorRing()) { + return false; + } + + for (std::size_t i = 0; i < getNumInteriorRing(); i++) { + const Curve* hole = getInteriorRingN(i); + const Curve* otherhole = otherPolygon->getInteriorRingN(i); + if (!hole->equalsExact(otherhole, tolerance)) { + return false; + } + } + + return true; +} + +bool +Surface::equalsIdentical(const Geometry* other_g) const +{ + if (!isEquivalentClass(other_g)) { + return false; + } + + const auto& other = static_cast(*other_g); + + if (getNumInteriorRing() != other.getNumInteriorRing()) { + return false; + } + + if (!getExteriorRing()->equalsIdentical(other.getExteriorRing())) { + return false; + } + + for (std::size_t i = 0; i < getNumInteriorRing(); i++) { + if (!getInteriorRingN(i)->equalsIdentical(other.getInteriorRingN(i))) { + return false; + } + } + + return true; +} + +const CoordinateXY* +Surface::getCoordinate() const +{ + return getExteriorRing()->getCoordinate(); +} + +uint8_t +Surface::getCoordinateDimension() const +{ + uint8_t dimension = 2; + + if (getExteriorRing() != nullptr) { + dimension = std::max(dimension, getExteriorRing()->getCoordinateDimension()); + } + + for (std::size_t i = 0; i < getNumInteriorRing(); i++) { + dimension = std::max(dimension, getInteriorRingN(i)->getCoordinateDimension()); + } + + return dimension; +} + +const Envelope* +Surface::getEnvelopeInternal() const +{ + return getExteriorRing()->getEnvelopeInternal(); +} + +double +Surface::getLength() const +{ + double len = 0.0; + len += getExteriorRing()->getLength(); + for (std::size_t i = 0; i < getNumInteriorRing(); i++) { + len += getInteriorRingN(i)->getLength(); + } + return len; +} + +size_t +Surface::getNumPoints() const +{ + std::size_t numPoints = getExteriorRing()->getNumPoints(); + for (std::size_t i = 0; i < getNumInteriorRing(); i++) { + numPoints += getInteriorRingN(i)->getNumPoints(); + } + return numPoints; +} + +bool +Surface::hasM() const +{ + if (getExteriorRing()->hasM()) { + return true; + } + for (std::size_t i = 0 ; i < getNumInteriorRing(); i++) { + if (getInteriorRingN(i)->hasM()) { + return true; + } + } + return false; +} + +bool +Surface::hasZ() const +{ + if (getExteriorRing()->hasZ()) { + return true; + } + for (std::size_t i = 0 ; i < getNumInteriorRing(); i++) { + if (getInteriorRingN(i)->hasZ()) { + return true; + } + } + return false; +} + +bool +Surface::isEmpty() const +{ + return getExteriorRing()->isEmpty(); +} + +} +} diff --git a/deps/libgeos/geos/src/geom/prep/AbstractPreparedPolygonContains.cpp b/deps/libgeos/geos/src/geom/prep/AbstractPreparedPolygonContains.cpp index cfc87436c..f0dbdeacc 100644 --- a/deps/libgeos/geos/src/geom/prep/AbstractPreparedPolygonContains.cpp +++ b/deps/libgeos/geos/src/geom/prep/AbstractPreparedPolygonContains.cpp @@ -186,7 +186,6 @@ bool AbstractPreparedPolygonContains::evalPointTestGeom(const Geometry *geom, Lo if (outermostLoc == Location::EXTERIOR) { return false; } - // For the Covers predicate, we can return true // since no Points are on the exterior of the target // geometry. @@ -195,19 +194,18 @@ bool AbstractPreparedPolygonContains::evalPointTestGeom(const Geometry *geom, Lo } // For the Contains predicate, we need to test if any - // of those points lie in the interior of the target + // of the test points lie in the interior of the target // geometry. if (outermostLoc == Location::INTERIOR) { return true; } - if (geom->getNumGeometries() > 1) { - // for MultiPoint, try to find at least one point - // in interior - return isAnyTestComponentInTargetInterior(geom); + // a single point must not be in interior + if (geom->getNumPoints() <= 1) { + return false; } - - return false; + // for multiple points have to check all + return isAnyTestComponentInTargetInterior(geom); } // diff --git a/deps/libgeos/geos/src/geom/prep/BasicPreparedGeometry.cpp b/deps/libgeos/geos/src/geom/prep/BasicPreparedGeometry.cpp index efde4ce2a..0718a37b0 100644 --- a/deps/libgeos/geos/src/geom/prep/BasicPreparedGeometry.cpp +++ b/deps/libgeos/geos/src/geom/prep/BasicPreparedGeometry.cpp @@ -19,10 +19,13 @@ #include #include +#include #include #include #include +#include "geos/util.h" + namespace geos { namespace geom { // geos.geom namespace prep { // geos.geom.prep @@ -87,74 +90,79 @@ BasicPreparedGeometry::isAnyTargetComponentInTest(const geom::Geometry* testGeom return false; } +bool +BasicPreparedGeometry::within(const geom::Geometry* g) const +{ + return getRelateNG().within(g); +} + bool BasicPreparedGeometry::contains(const geom::Geometry* g) const { - return baseGeom->contains(g); + return getRelateNG().contains(g); } bool BasicPreparedGeometry::containsProperly(const geom::Geometry* g) const { - // since raw relate is used, provide some optimizations - - // short-circuit test - if(! baseGeom->getEnvelopeInternal()->contains(g->getEnvelopeInternal())) { - return false; - } - - // otherwise, compute using relate mask - return baseGeom->relate(g, "T**FF*FF*"); + return getRelateNG().relate(g, "T**FF*FF*"); } bool BasicPreparedGeometry::coveredBy(const geom::Geometry* g) const { - return baseGeom->coveredBy(g); + return getRelateNG().coveredBy(g); } bool BasicPreparedGeometry::covers(const geom::Geometry* g) const { - return baseGeom->covers(g); + return getRelateNG().covers(g); } bool BasicPreparedGeometry::crosses(const geom::Geometry* g) const { - return baseGeom->crosses(g); + return getRelateNG().crosses(g); } bool BasicPreparedGeometry::disjoint(const geom::Geometry* g) const { - return ! intersects(g); + return getRelateNG().disjoint(g); } bool BasicPreparedGeometry::intersects(const geom::Geometry* g) const { - return baseGeom->intersects(g); + return getRelateNG().intersects(g); } bool BasicPreparedGeometry::overlaps(const geom::Geometry* g) const { - return baseGeom->overlaps(g); + return getRelateNG().overlaps(g); } bool BasicPreparedGeometry::touches(const geom::Geometry* g) const { - return baseGeom->touches(g); + return getRelateNG().touches(g); } bool -BasicPreparedGeometry::within(const geom::Geometry* g) const +BasicPreparedGeometry::relate(const geom::Geometry* g, const std::string& pat) const { - return baseGeom->within(g); + return getRelateNG().relate(g, pat); } +std::unique_ptr +BasicPreparedGeometry::relate(const geom::Geometry* g) const +{ + return getRelateNG().relate(g); +} + + std::unique_ptr BasicPreparedGeometry::nearestPoints(const geom::Geometry* g) const { diff --git a/deps/libgeos/geos/src/geom/prep/PreparedGeometryFactory.cpp b/deps/libgeos/geos/src/geom/prep/PreparedGeometryFactory.cpp index 899eff9a3..844596935 100644 --- a/deps/libgeos/geos/src/geom/prep/PreparedGeometryFactory.cpp +++ b/deps/libgeos/geos/src/geom/prep/PreparedGeometryFactory.cpp @@ -45,6 +45,8 @@ PreparedGeometryFactory::create(const geom::Geometry* g) const throw util::IllegalArgumentException("PreparedGeometry constructed with null Geometry object"); } + util::ensureNoCurvedComponents(g); + std::unique_ptr pg; switch(g->getGeometryTypeId()) { diff --git a/deps/libgeos/geos/src/geom/prep/PreparedLineString.cpp b/deps/libgeos/geos/src/geom/prep/PreparedLineString.cpp index 819a42d6c..4ae7f0ae8 100644 --- a/deps/libgeos/geos/src/geom/prep/PreparedLineString.cpp +++ b/deps/libgeos/geos/src/geom/prep/PreparedLineString.cpp @@ -57,6 +57,8 @@ PreparedLineString::getIntersectionFinder() bool PreparedLineString::intersects(const geom::Geometry* g) const { + geos::util::ensureNoCurvedComponents(g); + if(! envelopesIntersect(g)) { return false; } diff --git a/deps/libgeos/geos/src/geom/prep/PreparedPoint.cpp b/deps/libgeos/geos/src/geom/prep/PreparedPoint.cpp index 8fa3f94f5..5ddef6602 100644 --- a/deps/libgeos/geos/src/geom/prep/PreparedPoint.cpp +++ b/deps/libgeos/geos/src/geom/prep/PreparedPoint.cpp @@ -17,8 +17,11 @@ **********************************************************************/ +#include #include +#include "geos/util.h" + namespace geos { namespace geom { // geos.geom namespace prep { // geos.geom.prep @@ -26,6 +29,8 @@ namespace prep { // geos.geom.prep bool PreparedPoint::intersects(const geom::Geometry* g) const { + util::ensureNoCurvedComponents(g); + if(! envelopesIntersect(g)) { return false; } diff --git a/deps/libgeos/geos/src/geom/prep/PreparedPolygon.cpp b/deps/libgeos/geos/src/geom/prep/PreparedPolygon.cpp index 332c4798e..cf2243217 100644 --- a/deps/libgeos/geos/src/geom/prep/PreparedPolygon.cpp +++ b/deps/libgeos/geos/src/geom/prep/PreparedPolygon.cpp @@ -136,6 +136,8 @@ bool PreparedPolygon:: intersects(const geom::Geometry* g) const { + geos::util::ensureNoCurvedComponents(g); + // envelope test if(!envelopesIntersect(g)) { return false; diff --git a/deps/libgeos/geos/src/geom/util/GeometryEditor.cpp b/deps/libgeos/geos/src/geom/util/GeometryEditor.cpp index b2d66b947..acba50712 100644 --- a/deps/libgeos/geos/src/geom/util/GeometryEditor.cpp +++ b/deps/libgeos/geos/src/geom/util/GeometryEditor.cpp @@ -74,6 +74,8 @@ GeometryEditor::GeometryEditor(const GeometryFactory* newFactory) std::unique_ptr GeometryEditor::edit(const Geometry* geometry, GeometryEditorOperation* operation) { + geos::util::ensureNoCurvedComponents(geometry); + // if client did not supply a GeometryFactory, use the one from the input Geometry if(factory == nullptr) { factory = geometry->getFactory(); diff --git a/deps/libgeos/geos/src/geom/util/LinearComponentExtracter.cpp b/deps/libgeos/geos/src/geom/util/LinearComponentExtracter.cpp index 6217ff6f7..2dbc76444 100644 --- a/deps/libgeos/geos/src/geom/util/LinearComponentExtracter.cpp +++ b/deps/libgeos/geos/src/geom/util/LinearComponentExtracter.cpp @@ -32,6 +32,10 @@ LinearComponentExtracter::LinearComponentExtracter(std::vector& ret) { + if (geom.getDimension() == Dimension::P) { + return; + } + LinearComponentExtracter lce(ret); geom.apply_ro(&lce); } diff --git a/deps/libgeos/geos/src/geom/util/PointExtracter.cpp b/deps/libgeos/geos/src/geom/util/PointExtracter.cpp index 79f615487..70375adc1 100644 --- a/deps/libgeos/geos/src/geom/util/PointExtracter.cpp +++ b/deps/libgeos/geos/src/geom/util/PointExtracter.cpp @@ -28,6 +28,10 @@ namespace util { // geos.geom.util void PointExtracter::getPoints(const Geometry& geom, Point::ConstVect& ret) { + if (!geom.hasDimension(Dimension::P)) { + return; + } + PointExtracter pe(ret); geom.apply_ro(&pe); } @@ -44,16 +48,16 @@ PointExtracter::PointExtracter(Point::ConstVect& newComps) void PointExtracter::filter_rw(Geometry* geom) { - if(const Point* p = dynamic_cast(geom)) { - comps.push_back(p); + if (geom->getGeometryTypeId() == GEOS_POINT) { + comps.push_back(static_cast(geom)); } } void PointExtracter::filter_ro(const Geometry* geom) { - if(const Point* p = dynamic_cast(geom)) { - comps.push_back(p); + if (geom->getGeometryTypeId() == GEOS_POINT) { + comps.push_back(static_cast(geom)); } } } diff --git a/deps/libgeos/geos/src/geom/util/PolygonExtracter.cpp b/deps/libgeos/geos/src/geom/util/PolygonExtracter.cpp index 5e4ae6bc6..879e913c5 100644 --- a/deps/libgeos/geos/src/geom/util/PolygonExtracter.cpp +++ b/deps/libgeos/geos/src/geom/util/PolygonExtracter.cpp @@ -27,6 +27,10 @@ namespace util { // geos.geom.util void PolygonExtracter::getPolygons(const Geometry& geom, std::vector& ret) { + if (!geom.hasDimension(Dimension::A)) { + return; + } + PolygonExtracter pe(ret); geom.apply_ro(&pe); } diff --git a/deps/libgeos/geos/src/geom/util/PolygonalExtracter.cpp b/deps/libgeos/geos/src/geom/util/PolygonalExtracter.cpp new file mode 100644 index 000000000..009b10969 --- /dev/null +++ b/deps/libgeos/geos/src/geom/util/PolygonalExtracter.cpp @@ -0,0 +1,49 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#include +#include +#include +#include + +#include + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +void +PolygonalExtracter::getPolygonals(const Geometry& geom, std::vector& polys) +{ + getPolygonals(&geom, polys); +} + +void +PolygonalExtracter::getPolygonals(const Geometry* geom, std::vector& polys) +{ + if (dynamic_cast(geom) != nullptr + || dynamic_cast(geom) != nullptr ) { + polys.push_back(geom); + } + else if (dynamic_cast(geom) != nullptr) { + for (std::size_t i = 0; i < geom->getNumGeometries(); i++) { + getPolygonals(geom->getGeometryN(i), polys); + } + } +} + +} +} +} diff --git a/deps/libgeos/geos/src/geomgraph/DirectedEdgeStar_geomgraph.cpp b/deps/libgeos/geos/src/geomgraph/DirectedEdgeStar_geomgraph.cpp index 118c98ebe..44b0ded2a 100644 --- a/deps/libgeos/geos/src/geomgraph/DirectedEdgeStar_geomgraph.cpp +++ b/deps/libgeos/geos/src/geomgraph/DirectedEdgeStar_geomgraph.cpp @@ -130,7 +130,7 @@ DirectedEdgeStar::getRightmostEdge() /*public*/ void -DirectedEdgeStar::computeLabelling(std::vector* geom) +DirectedEdgeStar::computeLabelling(const std::vector>&geom) //throw(TopologyException *) { // this call can throw a TopologyException diff --git a/deps/libgeos/geos/src/geomgraph/EdgeEndStar.cpp b/deps/libgeos/geos/src/geomgraph/EdgeEndStar.cpp index da3df4a2b..44fcc4f4b 100644 --- a/deps/libgeos/geos/src/geomgraph/EdgeEndStar.cpp +++ b/deps/libgeos/geos/src/geomgraph/EdgeEndStar.cpp @@ -91,10 +91,10 @@ EdgeEndStar::getNextCW(EdgeEnd* ee) /*public*/ void -EdgeEndStar::computeLabelling(std::vector* geomGraph) +EdgeEndStar::computeLabelling(const std::vector>& geomGraph) //throw(TopologyException *) { - computeEdgeEndLabels((*geomGraph)[0]->getBoundaryNodeRule()); + computeEdgeEndLabels(geomGraph[0]->getBoundaryNodeRule()); // Propagate side labels around the edges in the star // for each parent Geometry @@ -185,12 +185,12 @@ EdgeEndStar::computeEdgeEndLabels( /*public*/ Location EdgeEndStar::getLocation(uint32_t geomIndex, - const Coordinate& p, std::vector* geom) + const Coordinate& p, const std::vector>& geom) { // compute location only on demand if(ptInAreaLocation[geomIndex] == Location::NONE) { ptInAreaLocation[geomIndex] = algorithm::locate::SimplePointInAreaLocator::locate(p, - (*geom)[geomIndex]->getGeometry()); + geom[geomIndex]->getGeometry()); } return ptInAreaLocation[geomIndex]; } diff --git a/deps/libgeos/geos/src/geomgraph/EdgeList.cpp b/deps/libgeos/geos/src/geomgraph/EdgeList.cpp index 41a6f55f6..d32f51930 100644 --- a/deps/libgeos/geos/src/geomgraph/EdgeList.cpp +++ b/deps/libgeos/geos/src/geomgraph/EdgeList.cpp @@ -140,7 +140,7 @@ operator<< (std::ostream& os, const EdgeList& el) { os << "EdgeList: " << std::endl; for(std::size_t j = 0, s = el.edges.size(); j < s; ++j) { - Edge* e = el.edges[j]; + const Edge* e = el.edges[j]; os << " " << *e << std::endl; } return os; diff --git a/deps/libgeos/geos/src/geomgraph/GeometryGraph.cpp b/deps/libgeos/geos/src/geomgraph/GeometryGraph.cpp index a18b65b72..48e5f6de2 100644 --- a/deps/libgeos/geos/src/geomgraph/GeometryGraph.cpp +++ b/deps/libgeos/geos/src/geomgraph/GeometryGraph.cpp @@ -169,6 +169,8 @@ void GeometryGraph::add(const Geometry* g) //throw (UnsupportedOperationException *) { + util::ensureNoCurvedComponents(g); + if(g->isEmpty()) { return; } @@ -208,8 +210,7 @@ void GeometryGraph::addCollection(const GeometryCollection* gc) { for(std::size_t i = 0, n = gc->getNumGeometries(); i < n; ++i) { - const Geometry* g = gc->getGeometryN(i); - add(g); + add(gc->getGeometryN(i)); } } diff --git a/deps/libgeos/geos/src/geomgraph/NodeMap_geomgraph.cpp b/deps/libgeos/geos/src/geomgraph/NodeMap_geomgraph.cpp index 331fbaa98..3624cc83e 100644 --- a/deps/libgeos/geos/src/geomgraph/NodeMap_geomgraph.cpp +++ b/deps/libgeos/geos/src/geomgraph/NodeMap_geomgraph.cpp @@ -46,8 +46,6 @@ NodeMap::NodeMap(const NodeFactory& newNodeFact) #endif } -NodeMap::~NodeMap() = default; - Node* NodeMap::addNode(const Coordinate& coord) { diff --git a/deps/libgeos/geos/src/io/GeoJSON.cpp b/deps/libgeos/geos/src/io/GeoJSON.cpp index cfb28ea13..32e0f6c33 100644 --- a/deps/libgeos/geos/src/io/GeoJSON.cpp +++ b/deps/libgeos/geos/src/io/GeoJSON.cpp @@ -224,16 +224,26 @@ bool GeoJSONValue::isArray() const // GeoJSONFeature GeoJSONFeature::GeoJSONFeature(std::unique_ptr g, - const std::map& p) : geometry(std::move(g)), properties(p) {} + const std::map& p) : geometry(std::move(g)), properties(p), id("") {} GeoJSONFeature::GeoJSONFeature(std::unique_ptr g, - std::map&& p) : geometry(std::move(g)), properties(std::move(p)) {} + std::map&& p) : geometry(std::move(g)), properties(std::move(p)), id("") {} + +GeoJSONFeature::GeoJSONFeature(std::unique_ptr g, + const std::map& p, + const std::string& i) + : geometry(std::move(g)), properties(p), id(i) {} + +GeoJSONFeature::GeoJSONFeature(std::unique_ptr g, + std::map&& p, + std::string i) + : geometry(std::move(g)), properties(std::move(p)), id(std::move(i)) {} GeoJSONFeature::GeoJSONFeature(GeoJSONFeature const& other) : geometry(other.geometry->clone()), - properties(other.properties) {} + properties(other.properties), id(other.id) {} GeoJSONFeature::GeoJSONFeature(GeoJSONFeature&& other) : geometry(std::move(other.geometry)), - properties(std::move(other.properties)) {} + properties(std::move(other.properties)), id(std::move(other.id)) {} GeoJSONFeature& GeoJSONFeature::operator=(const GeoJSONFeature& other) { @@ -262,6 +272,8 @@ const std::map& GeoJSONFeature::getProperties() const return properties; } +const std::string& GeoJSONFeature::getId() const { return id; } + // GeoJSONFeatureCollection GeoJSONFeatureCollection::GeoJSONFeatureCollection(const std::vector& f) : features(f) {} diff --git a/deps/libgeos/geos/src/io/GeoJSONReader.cpp b/deps/libgeos/geos/src/io/GeoJSONReader.cpp index 444905b0c..0f0817e32 100644 --- a/deps/libgeos/geos/src/io/GeoJSONReader.cpp +++ b/deps/libgeos/geos/src/io/GeoJSONReader.cpp @@ -98,7 +98,14 @@ GeoJSONFeature GeoJSONReader::readFeature(const geos_nlohmann::json& j) const { const auto& geometryJson = j.at("geometry"); const auto& properties = j.at("properties"); - return GeoJSONFeature{readGeometry(geometryJson), readProperties(properties)}; + + std::string id = ""; + if (j.contains("id") && !j.at("id").is_null()) { + if (j.at("id").is_string()) id = j.at("id").get(); + if (j.at("id").is_number()) id = j.at("id").dump(); + } + + return GeoJSONFeature{readGeometry(geometryJson), readProperties(properties), id}; } std::map GeoJSONReader::readProperties( @@ -203,13 +210,16 @@ geom::Coordinate GeoJSONReader::readCoordinate( const std::vector& coords) const { if (coords.size() == 1) { - throw ParseException("Expected two coordinates found one"); + throw ParseException("Expected two or three coordinates found one"); } - else if (coords.size() > 2) { - throw ParseException("Expected two coordinates found more than two"); + else if (coords.size() == 2) { + return geom::Coordinate { coords[0], coords[1] }; + } + else if (coords.size() == 3) { + return geom::Coordinate { coords[0], coords[1], coords[2] }; } else { - return geom::Coordinate {coords[0], coords[1]}; + throw ParseException("Expected two or three coordinates found more than three"); } } @@ -218,7 +228,7 @@ std::unique_ptr GeoJSONReader::readPoint( { const auto& coords = j.at("coordinates").get>(); if (coords.size() == 1) { - throw ParseException("Expected two coordinates found one"); + throw ParseException("Expected two or three coordinates found one"); } else if (coords.size() < 2) { return geometryFactory.createPoint(2); @@ -233,7 +243,8 @@ std::unique_ptr GeoJSONReader::readLineString( const geos_nlohmann::json& j) const { const auto& coords = j.at("coordinates").get>>(); - auto coordinates = detail::make_unique(0u, 2u); + bool has_z = std::any_of(coords.begin(), coords.end(), [](auto v) { return v.size() > 2; }); + auto coordinates = detail::make_unique(0u, has_z, false); coordinates->reserve(coords.size()); for (const auto& coord : coords) { const geom::Coordinate& c = readCoordinate(coord); @@ -256,7 +267,8 @@ std::unique_ptr GeoJSONReader::readPolygon( std::vector> rings; rings.reserve(polygonCoords.size()); for (const auto& ring : polygonCoords) { - auto coordinates = detail::make_unique(0u, 2u); + bool has_z = std::any_of(ring.begin(), ring.end(), [](auto v) { return v.size() > 2; }); + auto coordinates = detail::make_unique(0u, has_z, false); coordinates->reserve(ring.size()); for (const auto& coord : ring) { const geom::Coordinate& c = readCoordinate(coord); @@ -300,11 +312,12 @@ std::unique_ptr GeoJSONReader::readMultiLineString( std::vector> lines; lines.reserve(listOfCoords.size()); for (const auto& coords : listOfCoords) { - auto coordinates = detail::make_unique(0u, 2u); + bool has_z = std::any_of(coords.begin(), coords.end(), [](auto v) { return v.size() > 2; }); + auto coordinates = detail::make_unique(0u, has_z, false); coordinates->reserve(coords.size()); for (const auto& coord : coords) { const geom::Coordinate& c = readCoordinate(coord); - coordinates->add(geom::Coordinate{c.x, c.y}); + coordinates->add(c); } lines.push_back(geometryFactory.createLineString(std::move(coordinates))); } diff --git a/deps/libgeos/geos/src/io/GeoJSONWriter.cpp b/deps/libgeos/geos/src/io/GeoJSONWriter.cpp index 6e7b196b8..bf3826d49 100644 --- a/deps/libgeos/geos/src/io/GeoJSONWriter.cpp +++ b/deps/libgeos/geos/src/io/GeoJSONWriter.cpp @@ -29,6 +29,9 @@ #include #include #include +#include + +#include "geos/util.h" #define GEOS_COMPILATION @@ -38,6 +41,17 @@ using json = geos_nlohmann::ordered_json; namespace geos { namespace io { // geos.io + +/* public */ +void +GeoJSONWriter::setOutputDimension(uint8_t dims) +{ + if(dims < 2 || dims > 3) { + throw util::IllegalArgumentException("GeoJSON output dimension must be 2 or 3"); + } + defaultOutputDimension = dims; +} + std::string GeoJSONWriter::write(const geom::Geometry* geometry, GeoJSONType type) { json j; @@ -125,9 +139,13 @@ std::string GeoJSONWriter::write(const GeoJSONFeatureCollection& features) void GeoJSONWriter::encodeFeature(const GeoJSONFeature& feature, geos_nlohmann::ordered_json& j) { j["type"] = "Feature"; + + if (feature.getId().size() > 0) j["id"] = feature.getId(); + json geometryJson; encodeGeometry(feature.getGeometry(), geometryJson); j["geometry"] = geometryJson; + json propertiesJson = json::object(); for (auto const& property : feature.getProperties()) { std::string key = property.first; @@ -170,6 +188,8 @@ void GeoJSONWriter::encodeFeatureCollection(const geom::Geometry* g, geos_nlohma void GeoJSONWriter::encodeGeometry(const geom::Geometry* geometry, geos_nlohmann::ordered_json& j) { + util::ensureNoCurvedComponents(geometry); + auto type = geometry->getGeometryTypeId(); if (type == GEOS_POINT) { auto point = static_cast(geometry); @@ -209,7 +229,8 @@ void GeoJSONWriter::encodePoint(const geom::Point* point, geos_nlohmann::ordered { j["type"] = "Point"; if (!point->isEmpty()) { - j["coordinates"] = convertCoordinate(point->getCoordinate()); + auto as_coord = Coordinate { point->getX(), point->getY(), point->getZ()}; + j["coordinates"] = convertCoordinate(&as_coord); } else { j["coordinates"] = j.array(); @@ -225,7 +246,7 @@ void GeoJSONWriter::encodeLineString(const geom::LineString* line, geos_nlohmann void GeoJSONWriter::encodePolygon(const geom::Polygon* poly, geos_nlohmann::ordered_json& j) { j["type"] = "Polygon"; - std::vector>> rings; + std::vector>> rings; auto ring = poly->getExteriorRing(); rings.reserve(poly->getNumInteriorRing()+1); rings.push_back(convertCoordinateSequence(ring->getCoordinates().get())); @@ -244,7 +265,7 @@ void GeoJSONWriter::encodeMultiPoint(const geom::MultiPoint* multiPoint, geos_nl void GeoJSONWriter::encodeMultiLineString(const geom::MultiLineString* multiLineString, geos_nlohmann::ordered_json& j) { j["type"] = "MultiLineString"; - std::vector>> lines; + std::vector>> lines; lines.reserve(multiLineString->getNumGeometries()); for (size_t i = 0; i < multiLineString->getNumGeometries(); i++) { lines.push_back(convertCoordinateSequence(multiLineString->getGeometryN(i)->getCoordinates().get())); @@ -255,11 +276,11 @@ void GeoJSONWriter::encodeMultiLineString(const geom::MultiLineString* multiLine void GeoJSONWriter::encodeMultiPolygon(const geom::MultiPolygon* multiPolygon, geos_nlohmann::ordered_json& json) { json["type"] = "MultiPolygon"; - std::vector>>> polygons; + std::vector>>> polygons; polygons.reserve(multiPolygon->getNumGeometries()); for (size_t i = 0; i < multiPolygon->getNumGeometries(); i++) { const Polygon* polygon = multiPolygon->getGeometryN(i); - std::vector>> rings; + std::vector>> rings; auto ring = polygon->getExteriorRing(); rings.reserve(polygon->getNumInteriorRing() + 1); rings.push_back(convertCoordinateSequence(ring->getCoordinates().get())); @@ -283,15 +304,18 @@ void GeoJSONWriter::encodeGeometryCollection(const geom::GeometryCollection* g, j["geometries"] = geometryArray; } -std::pair GeoJSONWriter::convertCoordinate(const CoordinateXY* c) +std::vector GeoJSONWriter::convertCoordinate(const Coordinate* c) { - return std::make_pair(c->x, c->y); + if (std::isnan(c->z) || defaultOutputDimension == 2) { + return std::vector { c->x, c->y }; + } + return std::vector { c->x, c->y, c->z }; } -std::vector> GeoJSONWriter::convertCoordinateSequence(const CoordinateSequence* +std::vector> GeoJSONWriter::convertCoordinateSequence(const CoordinateSequence* coordinateSequence) { - std::vector> coordinates; + std::vector> coordinates; coordinates.reserve(coordinateSequence->size()); for (size_t i = 0; isize(); i++) { const geom::Coordinate& c = coordinateSequence->getAt(i); diff --git a/deps/libgeos/geos/src/io/StringTokenizer.cpp b/deps/libgeos/geos/src/io/StringTokenizer.cpp index 5735913cc..e016299eb 100644 --- a/deps/libgeos/geos/src/io/StringTokenizer.cpp +++ b/deps/libgeos/geos/src/io/StringTokenizer.cpp @@ -154,10 +154,8 @@ StringTokenizer::peekNextToken() return str[pos]; } - // It's either a Number or a Word, let's - // see when it ends - - pos = str.find_first_of("\n\r\t() ,", static_cast(iter - str.begin())); + // It's either a Number or a Word, let's see when it ends + pos = str.find_first_of("\n\r\t() ,", pos + 1); if(pos == string::npos) { if(iter != str.end()) { diff --git a/deps/libgeos/geos/src/io/WKBReader.cpp b/deps/libgeos/geos/src/io/WKBReader.cpp index 0aa9c38e9..5410ab0c7 100644 --- a/deps/libgeos/geos/src/io/WKBReader.cpp +++ b/deps/libgeos/geos/src/io/WKBReader.cpp @@ -20,15 +20,21 @@ #include #include #include +#include +#include +#include +#include #include #include #include #include #include #include +#include #include #include #include +#include #include #include #include @@ -58,6 +64,7 @@ WKBReader::WKBReader() : WKBReader(*(GeometryFactory::getDefaultInstance())) {} + void WKBReader::setFixStructure(bool doFixStructure) { @@ -178,7 +185,7 @@ WKBReader::readHEX(std::istream& is) } void -WKBReader::minMemSize(int geomType, uint64_t size) +WKBReader::minMemSize(geom::GeometryTypeId geomType, uint64_t size) const { uint64_t minSize = 0; constexpr uint64_t minCoordSize = 2 * sizeof(double); @@ -191,18 +198,24 @@ WKBReader::minMemSize(int geomType, uint64_t size) switch(geomType) { case GEOS_LINESTRING: case GEOS_LINEARRING: + case GEOS_CIRCULARSTRING: + case GEOS_COMPOUNDCURVE: + case GEOS_POINT: minSize = size * minCoordSize; break; case GEOS_POLYGON: + case GEOS_CURVEPOLYGON: minSize = size * minRingSize; break; case GEOS_MULTIPOINT: minSize = size * minPtSize; break; case GEOS_MULTILINESTRING: + case GEOS_MULTICURVE: minSize = size * minLineSize; break; case GEOS_MULTIPOLYGON: + case GEOS_MULTISURFACE: minSize = size * minPolySize; break; case GEOS_GEOMETRYCOLLECTION: @@ -309,9 +322,18 @@ WKBReader::readGeometry() case WKBConstants::wkbLineString : result = readLineString(); break; + case WKBConstants::wkbCircularString : + result = readCircularString(); + break; + case WKBConstants::wkbCompoundCurve : + result = readCompoundCurve(); + break; case WKBConstants::wkbPolygon : result = readPolygon(); break; + case WKBConstants::wkbCurvePolygon : + result = readCurvePolygon(); + break; case WKBConstants::wkbMultiPoint : result = readMultiPoint(); break; @@ -324,6 +346,12 @@ WKBReader::readGeometry() case WKBConstants::wkbGeometryCollection : result = readGeometryCollection(); break; + case WKBConstants::wkbMultiCurve : + result = readMultiCurve(); + break; + case WKBConstants::wkbMultiSurface : + result = readMultiSurface(); + break; default: std::stringstream err; err << "Unknown WKB type " << geometryType; @@ -376,6 +404,30 @@ WKBReader::readLinearRing() return factory.createLinearRing(std::move(pts)); } +std::unique_ptr +WKBReader::readCircularString() +{ + uint32_t size = dis.readUnsigned(); + minMemSize(GEOS_CIRCULARSTRING, size); + auto pts = readCoordinateSequence(size); + return factory.createCircularString(std::move(pts)); +} + +std::unique_ptr +WKBReader::readCompoundCurve() +{ + auto numCurves = dis.readUnsigned(); + minMemSize(GEOS_COMPOUNDCURVE, numCurves); + + std::vector> curves(numCurves); + + for (std::uint32_t i = 0; i < numCurves; i++) { + curves[i] = readChild(); + } + + return factory.createCompoundCurve(std::move(curves)); +} + std::unique_ptr WKBReader::readPolygon() { @@ -402,9 +454,38 @@ WKBReader::readPolygon() return factory.createPolygon(std::move(shell), std::move(holes)); } + return factory.createPolygon(std::move(shell)); } +std::unique_ptr +WKBReader::readCurvePolygon() +{ + uint32_t numRings = dis.readUnsigned(); + minMemSize(GEOS_POLYGON, numRings); + +#if DEBUG_WKB_READER + std::size_t << "WKB numRings: " << numRings << std::endl; +#endif + + if (numRings == 0) { + return factory.createCurvePolygon(hasZ, hasM); + } + + auto shell = readChild(); + + if(numRings > 1) { + std::vector> holes(numRings - 1); + for(uint32_t i = 0; i < numRings - 1; i++) { + holes[i] = readChild(); + } + + return factory.createCurvePolygon(std::move(shell), std::move(holes)); + } + + return factory.createCurvePolygon(std::move(shell)); +} + std::unique_ptr WKBReader::readMultiPoint() { @@ -413,12 +494,7 @@ WKBReader::readMultiPoint() std::vector> geoms(numGeoms); for(uint32_t i = 0; i < numGeoms; i++) { - geoms[i] = readGeometry(); - if(!dynamic_cast(geoms[i].get())) { - std::stringstream err; - err << BAD_GEOM_TYPE_MSG << " MultiPoint"; - throw ParseException(err.str()); - } + geoms[i] = readChild(); } return factory.createMultiPoint(std::move(geoms)); @@ -432,12 +508,7 @@ WKBReader::readMultiLineString() std::vector> geoms(numGeoms); for(uint32_t i = 0; i < numGeoms; i++) { - geoms[i] = readGeometry(); - if(!dynamic_cast(geoms[i].get())) { - std::stringstream err; - err << BAD_GEOM_TYPE_MSG << " LineString"; - throw ParseException(err.str()); - } + geoms[i] = readChild(); } return factory.createMultiLineString(std::move(geoms)); @@ -451,17 +522,40 @@ WKBReader::readMultiPolygon() std::vector> geoms(numGeoms); for(uint32_t i = 0; i < numGeoms; i++) { - geoms[i] = readGeometry(); - if(!dynamic_cast(geoms[i].get())) { - std::stringstream err; - err << BAD_GEOM_TYPE_MSG << " Polygon"; - throw ParseException(err.str()); - } + geoms[i] = readChild(); } return factory.createMultiPolygon(std::move(geoms)); } +std::unique_ptr +WKBReader::readMultiCurve() +{ + uint32_t numGeoms = dis.readUnsigned(); + minMemSize(GEOS_MULTICURVE, numGeoms); + std::vector> geoms(numGeoms); + + for(uint32_t i = 0; i < numGeoms; i++) { + geoms[i] = readChild(); + } + + return factory.createMultiCurve(std::move(geoms)); +} + +std::unique_ptr +WKBReader::readMultiSurface() +{ + uint32_t numGeoms = dis.readUnsigned(); + minMemSize(GEOS_MULTISURFACE, numGeoms); + std::vector> geoms(numGeoms); + + for(uint32_t i = 0; i < numGeoms; i++) { + geoms[i] = readChild(); + } + + return factory.createMultiSurface(std::move(geoms)); +} + std::unique_ptr WKBReader::readGeometryCollection() { diff --git a/deps/libgeos/geos/src/io/WKBWriter.cpp b/deps/libgeos/geos/src/io/WKBWriter.cpp index 43ad801a0..f7c83d38d 100644 --- a/deps/libgeos/geos/src/io/WKBWriter.cpp +++ b/deps/libgeos/geos/src/io/WKBWriter.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,6 +38,8 @@ #include #include +#include "geos/util.h" + #undef DEBUG_WKB_WRITER @@ -104,36 +108,21 @@ WKBWriter::write(const Geometry& g, std::ostream& os) outStream = &os; - if (const Point* x = dynamic_cast(&g)) { - return writePoint(*x); - } - - if (const LineString* x = dynamic_cast(&g)) { - return writeLineString(*x); - } - - if (const Polygon* x = dynamic_cast(&g)) { - return writePolygon(*x); - } - - if (const MultiPoint* x = dynamic_cast(&g)) { - return writeGeometryCollection(*x, WKBConstants::wkbMultiPoint); + switch(g.getGeometryTypeId()) { + case GEOS_POINT: writePoint(static_cast(g)); break; + case GEOS_LINESTRING: + case GEOS_LINEARRING: + case GEOS_CIRCULARSTRING: writeSimpleCurve(static_cast(g)); break; + case GEOS_COMPOUNDCURVE: writeCompoundCurve(static_cast(g)); break; + case GEOS_POLYGON: writePolygon(static_cast(g)); break; + case GEOS_CURVEPOLYGON: writeCurvePolygon(static_cast(g)); break; + case GEOS_MULTIPOINT: + case GEOS_MULTILINESTRING: + case GEOS_MULTIPOLYGON: + case GEOS_MULTICURVE: + case GEOS_MULTISURFACE: + case GEOS_GEOMETRYCOLLECTION: writeGeometryCollection(static_cast(g)); break; } - - if (const MultiLineString* x = dynamic_cast(&g)) { - return writeGeometryCollection(*x, WKBConstants::wkbMultiLineString); - } - - if (const MultiPolygon* x = dynamic_cast(&g)) { - return writeGeometryCollection(*x, WKBConstants::wkbMultiPolygon); - } - - if (const GeometryCollection* x = - dynamic_cast(&g)) { - return writeGeometryCollection(*x, WKBConstants::wkbGeometryCollection); - } - - assert(0); // Unknown Geometry type } void @@ -168,11 +157,11 @@ WKBWriter::writePoint(const Point& g) } void -WKBWriter::writeLineString(const LineString& g) +WKBWriter::writeSimpleCurve(const SimpleCurve& g) { writeByteOrder(); - writeGeometryType(WKBConstants::wkbLineString, g.getSRID()); + writeGeometryType(getWkbType(g), g.getSRID()); writeSRID(g.getSRID()); const CoordinateSequence* cs = g.getCoordinatesRO(); @@ -180,12 +169,33 @@ WKBWriter::writeLineString(const LineString& g) writeCoordinateSequence(*cs, true); } +void +WKBWriter::writeCompoundCurve(const CompoundCurve& g) +{ + writeByteOrder(); + + writeGeometryType(getWkbType(g), g.getSRID()); + writeSRID(g.getSRID()); + + writeInt(static_cast(g.getNumCurves())); + + auto orig_includeSRID = includeSRID; + includeSRID = false; + + for (std::size_t i = 0; i < g.getNumCurves(); i++) { + const SimpleCurve& section = *g.getCurveN(i); + writeSimpleCurve(section); + } + + includeSRID = orig_includeSRID; +} + void WKBWriter::writePolygon(const Polygon& g) { writeByteOrder(); - writeGeometryType(WKBConstants::wkbPolygon, g.getSRID()); + writeGeometryType(getWkbType(g), g.getSRID()); writeSRID(g.getSRID()); if (g.isEmpty()) { @@ -215,12 +225,42 @@ WKBWriter::writePolygon(const Polygon& g) } void -WKBWriter::writeGeometryCollection(const GeometryCollection& g, - int wkbtype) +WKBWriter::writeCurvePolygon(const CurvePolygon& g) { + // Why not combine this with writePolygon? + // A CurvePolygon differs from a Polygon in that its rings + // can one of three different types. Therefore, the type + // information is written for each ring, unlike a Polygon. + writeByteOrder(); - writeGeometryType(wkbtype, g.getSRID()); + writeGeometryType(getWkbType(g), g.getSRID()); + + writeSRID(g.getSRID()); + + if (g.isEmpty()) { + writeInt(0); + return; + } + + std::size_t nholes = g.getNumInteriorRing(); + writeInt(static_cast(nholes + 1)); + + const Curve* ring = g.getExteriorRing(); + write(*ring, *outStream); + + for(std::size_t i = 0; i < nholes; i++) { + ring = g.getInteriorRingN(i); + write(*ring, *outStream); + } +} + +void +WKBWriter::writeGeometryCollection(const GeometryCollection& g) +{ + writeByteOrder(); + + writeGeometryType(getWkbType(g), g.getSRID()); writeSRID(g.getSRID()); auto ngeoms = g.getNumGeometries(); @@ -377,6 +417,28 @@ WKBWriter::getOutputOrdinates(OrdinateSet ordinates) return newOrdinates; } +int +WKBWriter::getWkbType(const Geometry& g) { + switch(g.getGeometryTypeId()) { + case GEOS_POINT: return WKBConstants::wkbPoint; + case GEOS_LINESTRING: + case GEOS_LINEARRING: return WKBConstants::wkbLineString; + case GEOS_CIRCULARSTRING: return WKBConstants::wkbCircularString; + case GEOS_COMPOUNDCURVE: return WKBConstants::wkbCompoundCurve; + case GEOS_POLYGON: return WKBConstants::wkbPolygon; + case GEOS_CURVEPOLYGON: return WKBConstants::wkbCurvePolygon; + case GEOS_MULTIPOINT: return WKBConstants::wkbMultiPoint; + case GEOS_MULTILINESTRING: return WKBConstants::wkbMultiLineString; + case GEOS_MULTICURVE: return WKBConstants::wkbMultiCurve; + case GEOS_MULTIPOLYGON: return WKBConstants::wkbMultiPolygon; + case GEOS_MULTISURFACE: return WKBConstants::wkbMultiSurface; + case GEOS_GEOMETRYCOLLECTION: return WKBConstants::wkbGeometryCollection; + } + + // Avoid -Wreturn-type warning + throw util::IllegalArgumentException("Invalid geometry type."); +} + } // namespace geos.io } // namespace geos diff --git a/deps/libgeos/geos/src/io/WKTReader.cpp b/deps/libgeos/geos/src/io/WKTReader.cpp index 3ed640630..fc850d220 100644 --- a/deps/libgeos/geos/src/io/WKTReader.cpp +++ b/deps/libgeos/geos/src/io/WKTReader.cpp @@ -22,14 +22,20 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include +#include #include +#include #include +#include #include #include @@ -259,14 +265,19 @@ WKTReader::getNextWord(StringTokenizer* tokenizer) } std::unique_ptr -WKTReader::readGeometryTaggedText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const +WKTReader::readGeometryTaggedText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags, const GeometryTypeId* emptyType) const { std::string type = getNextWord(tokenizer); std::unique_ptr geom; OrdinateSet origFlags = ordinateFlags; + OrdinateSet newFlags = OrdinateSet::createXY(); - readOrdinateFlags(type, newFlags); + if (type == "EMPTY") { + newFlags = origFlags; + } else { + readOrdinateFlags(type, newFlags); + } if(isTypeName(type, "POINT")) { geom = readPointText(tokenizer, newFlags); @@ -277,20 +288,37 @@ WKTReader::readGeometryTaggedText(StringTokenizer* tokenizer, OrdinateSet& ordin else if(isTypeName(type, "LINEARRING")) { geom = readLinearRingText(tokenizer, newFlags); } + else if(isTypeName(type, "CIRCULARSTRING")) { + geom = readCircularStringText(tokenizer, newFlags); + } + else if(isTypeName(type, "COMPOUNDCURVE")) { + geom = readCompoundCurveText(tokenizer, newFlags); + } else if(isTypeName(type, "POLYGON")) { geom = readPolygonText(tokenizer, newFlags); } + else if(isTypeName(type, "CURVEPOLYGON")) { + geom = readCurvePolygonText(tokenizer, newFlags); + } else if(isTypeName(type, "MULTIPOINT")) { geom = readMultiPointText(tokenizer, newFlags); } else if(isTypeName(type, "MULTILINESTRING")) { geom = readMultiLineStringText(tokenizer, newFlags); } + else if(isTypeName(type, "MULTICURVE")) { + geom = readMultiCurveText(tokenizer, newFlags); + } else if(isTypeName(type, "MULTIPOLYGON")) { geom = readMultiPolygonText(tokenizer, newFlags); } + else if(isTypeName(type, "MULTISURFACE")) { + geom = readMultiSurfaceText(tokenizer, newFlags); + } else if(isTypeName(type, "GEOMETRYCOLLECTION")) { geom = readGeometryCollectionText(tokenizer, newFlags); + } else if (type == "EMPTY" && emptyType != nullptr) { + return geometryFactory->createEmptyGeometry(*emptyType, newFlags.hasZ(), newFlags.hasM()); } else { throw ParseException("Unknown type", type); } @@ -306,7 +334,7 @@ WKTReader::readGeometryTaggedText(StringTokenizer* tokenizer, OrdinateSet& ordin std::unique_ptr WKTReader::readPointText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const { - auto coords = getCoordinates(tokenizer, ordinateFlags); + auto&& coords = getCoordinates(tokenizer, ordinateFlags); return geometryFactory->createPoint(std::move(coords)); } @@ -327,6 +355,69 @@ WKTReader::readLinearRingText(StringTokenizer* tokenizer, OrdinateSet& ordinateF return geometryFactory->createLinearRing(std::move(coords)); } +std::unique_ptr +WKTReader::readCircularStringText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const +{ + auto&& coords = getCoordinates(tokenizer, ordinateFlags); + return geometryFactory->createCircularString(std::move(coords)); +} + +std::unique_ptr +WKTReader::readCurveText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const +{ + int type = tokenizer->peekNextToken(); + if (type == '(') { + return readLineStringText(tokenizer, ordinateFlags); + } + + GeometryTypeId defaultType = GEOS_LINESTRING; + auto component = readGeometryTaggedText(tokenizer, ordinateFlags, &defaultType); + if (dynamic_cast(component.get())) { + return std::unique_ptr(static_cast(component.release())); + } + + throw ParseException("Expected LINESTRING/CIRCULARSTRING/COMPOUNDCURVE but got " + component->getGeometryType()); +} + +std::unique_ptr +WKTReader::readSurfaceText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const +{ + int type = tokenizer->peekNextToken(); + if (type == '(') { + return readPolygonText(tokenizer, ordinateFlags); + } + + GeometryTypeId defaultType = GEOS_POLYGON; + auto component = readGeometryTaggedText(tokenizer, ordinateFlags, &defaultType); + if (dynamic_cast(component.get())) { + return component; + } + + throw ParseException("Expected POLYGON or CURVEPOLYGON but got " + component->getGeometryType()); +} + +std::unique_ptr +WKTReader::readCompoundCurveText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const +{ + std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); + if (nextToken == "EMPTY") { + return geometryFactory->createCompoundCurve(); + } + + std::vector> curves; + do { + auto curve = readCurveText(tokenizer, ordinateFlags); + if (dynamic_cast(curve.get())) { + curves.emplace_back(static_cast(curve.release())); + } else { + throw ParseException("Expected LINESTRING or CIRCULARSTRING but got " + curve->getGeometryType()); + } + nextToken = getNextCloserOrComma(tokenizer); + } while (nextToken == ","); + + return geometryFactory->createCompoundCurve(std::move(curves)); +} + std::unique_ptr WKTReader::readMultiPointText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const { @@ -418,6 +509,27 @@ WKTReader::readPolygonText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlag return geometryFactory->createPolygon(std::move(shell), std::move(holes)); } +std::unique_ptr +WKTReader::readCurvePolygonText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const +{ + std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); + if(nextToken == "EMPTY") { + auto coords = detail::make_unique(0u, ordinateFlags.hasZ(), ordinateFlags.hasM()); + std::unique_ptr ring = geometryFactory->createLinearRing(std::move(coords)); + return geometryFactory->createCurvePolygon(std::move(ring)); + } + + std::vector> holes; + auto shell = readCurveText(tokenizer, ordinateFlags); + nextToken = getNextCloserOrComma(tokenizer); + while(nextToken == ",") { + holes.push_back(readCurveText(tokenizer, ordinateFlags)); + nextToken = getNextCloserOrComma(tokenizer); + } + + return geometryFactory->createCurvePolygon(std::move(shell), std::move(holes)); +} + std::unique_ptr WKTReader::readMultiLineStringText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const { @@ -435,6 +547,23 @@ WKTReader::readMultiLineStringText(StringTokenizer* tokenizer, OrdinateSet& ordi return geometryFactory->createMultiLineString(std::move(lineStrings)); } +std::unique_ptr +WKTReader::readMultiCurveText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const +{ + std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); + if(nextToken == "EMPTY") { + return geometryFactory->createMultiCurve(); + } + + std::vector> curves; + do { + curves.push_back(readCurveText(tokenizer, ordinateFlags)); + nextToken = getNextCloserOrComma(tokenizer); + } while(nextToken == ","); + + return geometryFactory->createMultiCurve(std::move(curves)); +} + std::unique_ptr WKTReader::readMultiPolygonText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const { @@ -452,6 +581,23 @@ WKTReader::readMultiPolygonText(StringTokenizer* tokenizer, OrdinateSet& ordinat return geometryFactory->createMultiPolygon(std::move(polygons)); } +std::unique_ptr +WKTReader::readMultiSurfaceText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const +{ + std::string nextToken = getNextEmptyOrOpener(tokenizer, ordinateFlags); + if(nextToken == "EMPTY") { + return geometryFactory->createMultiSurface(); + } + + std::vector> surfaces; + do { + surfaces.push_back(readSurfaceText(tokenizer, ordinateFlags)); + nextToken = getNextCloserOrComma(tokenizer); + } while(nextToken == ","); + + return geometryFactory->createMultiSurface(std::move(surfaces)); +} + std::unique_ptr WKTReader::readGeometryCollectionText(StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const { diff --git a/deps/libgeos/geos/src/io/WKTWriter.cpp b/deps/libgeos/geos/src/io/WKTWriter.cpp index 77d4b20dc..53fa14269 100644 --- a/deps/libgeos/geos/src/io/WKTWriter.cpp +++ b/deps/libgeos/geos/src/io/WKTWriter.cpp @@ -22,8 +22,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -31,9 +33,12 @@ #include #include #include +#include +#include #include #include #include +#include #include #include @@ -58,7 +63,6 @@ WKTWriter::WKTWriter(): isFormatted(false), roundingPrecision(-1), trim(true), - level(0), defaultOutputDimension(4), old3D(false) { @@ -103,17 +107,11 @@ WKTWriter::toLineString(const CoordinateSequence& seq) /*static*/ std::string -WKTWriter::toLineString(const Coordinate& p0, const Coordinate& p1) +WKTWriter::toLineString(const CoordinateXY& p0, const CoordinateXY& p1) { std::stringstream ret(std::ios_base::in | std::ios_base::out); ret << "LINESTRING (" << p0.x << " " << p0.y; -#if PRINT_Z - ret << " " << p0.z; -#endif ret << ", " << p1.x << " " << p1.y; -#if PRINT_Z - ret << " " << p1.z; -#endif ret << ")"; return ret.str(); @@ -208,7 +206,7 @@ WKTWriter::writeFormatted(const Geometry* geometry, bool p_isFormatted, void WKTWriter::appendGeometryTaggedText(const Geometry& geometry, OrdinateSet checkOrdinates, - int p_level, + int level, Writer& writer) const { OrdinateSet outputOrdinates = OrdinateSet::createXY(); @@ -235,19 +233,33 @@ WKTWriter::appendGeometryTaggedText(const Geometry& geometry, } } - indent(p_level, &writer); + indent(level, &writer); switch(geometry.getGeometryTypeId()) { - case GEOS_POINT: appendPointTaggedText(static_cast(geometry), outputOrdinates, p_level, writer); break; - case GEOS_LINESTRING: appendLineStringTaggedText(static_cast(geometry), outputOrdinates, p_level, writer); break; - case GEOS_LINEARRING: appendLinearRingTaggedText(static_cast(geometry), outputOrdinates, p_level, writer); break; - case GEOS_POLYGON: appendPolygonTaggedText(static_cast(geometry), outputOrdinates, p_level, writer); break; - case GEOS_MULTIPOINT: appendMultiPointTaggedText(static_cast(geometry), outputOrdinates, p_level, writer); break; - case GEOS_MULTILINESTRING: appendMultiLineStringTaggedText(static_cast(geometry), outputOrdinates, p_level, writer); break; - case GEOS_MULTIPOLYGON: appendMultiPolygonTaggedText(static_cast(geometry), outputOrdinates, p_level, writer); break; - case GEOS_GEOMETRYCOLLECTION: appendGeometryCollectionTaggedText(static_cast(geometry), outputOrdinates, p_level, writer); break; + case GEOS_POINT: appendPointTaggedText(static_cast(geometry), outputOrdinates, level, writer); break; + case GEOS_LINESTRING: + case GEOS_LINEARRING: + case GEOS_CIRCULARSTRING: appendSimpleCurveTaggedText(static_cast(geometry), outputOrdinates, level, writer); break; + case GEOS_COMPOUNDCURVE: appendCompoundCurveTaggedText(static_cast(geometry), outputOrdinates, level, writer); break; + case GEOS_CURVEPOLYGON: + case GEOS_POLYGON: appendSurfaceTaggedText(static_cast(geometry), outputOrdinates, level, writer); break; + case GEOS_MULTIPOINT: appendMultiPointTaggedText(static_cast(geometry), outputOrdinates, level, writer); break; + case GEOS_MULTICURVE: + case GEOS_MULTILINESTRING: appendMultiCurveTaggedText(static_cast(geometry), outputOrdinates, level, writer); break; + case GEOS_MULTISURFACE: + case GEOS_MULTIPOLYGON: appendMultiSurfaceTaggedText(static_cast(geometry), outputOrdinates, level, writer); break; + case GEOS_GEOMETRYCOLLECTION: appendGeometryCollectionTaggedText(static_cast(geometry), outputOrdinates, level, writer); break; } } +void WKTWriter::appendTag(const Geometry& geometry, OrdinateSet outputOrdinates, Writer& writer) const +{ + std::string type = geometry.getGeometryType(); + util::toUpper(type); + writer.write(type); + writer.write(" "); + appendOrdinateText(outputOrdinates, writer); +} + /*protected*/ void WKTWriter::appendOrdinateText(OrdinateSet outputOrdinates, Writer& writer) const @@ -274,7 +286,7 @@ WKTWriter::appendOrdinateText(OrdinateSet outputOrdinates, Writer& writer) const } void -WKTWriter::appendPointTaggedText(const Point& point, OrdinateSet outputOrdinates, int p_level, +WKTWriter::appendPointTaggedText(const Point& point, OrdinateSet outputOrdinates, int level, Writer& writer) const { writer.write("POINT "); @@ -284,73 +296,103 @@ WKTWriter::appendPointTaggedText(const Point& point, OrdinateSet outputOrdinates if (coord == nullptr) { writer.write("EMPTY"); } else { - appendSequenceText(*point.getCoordinatesRO(), outputOrdinates, p_level, false, writer); + appendSequenceText(*point.getCoordinatesRO(), outputOrdinates, level, false, writer); } } void -WKTWriter::appendLineStringTaggedText(const LineString& lineString, OrdinateSet outputOrdinates, int p_level, - Writer& writer) const +WKTWriter::appendSimpleCurveTaggedText(const SimpleCurve& curve, OrdinateSet outputOrdinates, int level, Writer& writer) const { - writer.write("LINESTRING "); - appendOrdinateText(outputOrdinates, writer); - appendSequenceText(*lineString.getCoordinatesRO(), outputOrdinates, p_level, false, writer); + appendTag(curve, outputOrdinates, writer); + appendSequenceText(*curve.getCoordinatesRO(), outputOrdinates, level, false, writer); } -/** - * Converts a `LinearRing` to \ - * format, then appends it to the writer. - * - * @param linearRing the `LinearRing` to process - * @param writer the output writer to append to - */ void -WKTWriter::appendLinearRingTaggedText(const LinearRing& linearRing, OrdinateSet outputOrdinates, int p_level, Writer& writer) const +WKTWriter::appendCurveText(const Curve& curve, OrdinateSet outputOrdinates, int level, bool doIndent, Writer& writer) const { + if (doIndent) { + indent(level, &writer); + } + + if (curve.getGeometryTypeId() == GEOS_COMPOUNDCURVE) { + appendCompoundCurveTaggedText(static_cast(curve), outputOrdinates, level, writer); + } else { + appendSimpleCurveText(static_cast(curve), outputOrdinates, level, false, writer); + } +} + +void +WKTWriter::appendSimpleCurveText(const SimpleCurve& curve, OrdinateSet outputOrdinates, int level, bool doIndent, Writer& writer) const { + if (doIndent) { + indent(level, &writer); + } + + if (curve.getGeometryTypeId() == GEOS_CIRCULARSTRING) { + appendSimpleCurveTaggedText(curve, outputOrdinates, level, writer); + } else { + appendSequenceText(*curve.getCoordinatesRO(), outputOrdinates, level, false, writer); + } +} + + +void +WKTWriter::appendCompoundCurveTaggedText(const CompoundCurve& curve, OrdinateSet outputOrdinates, int level, Writer& writer) const { - writer.write("LINEARRING "); + writer.write("COMPOUNDCURVE "); appendOrdinateText(outputOrdinates, writer); - appendSequenceText(*linearRing.getCoordinatesRO(), outputOrdinates, p_level, false, writer); + + if (curve.isEmpty()) { + writer.write("EMPTY"); + } else { + writer.write("("); + bool indentFirst = false; + for (std::size_t i = 0; i < curve.getNumCurves(); i++) { + if (i > 0) { + writer.write(", "); + indentFirst = true; + } + + appendSimpleCurveText(*curve.getCurveN(i), outputOrdinates, level + (i > 0), indentFirst, writer); + } + writer.write(")"); + } } void -WKTWriter::appendPolygonTaggedText(const Polygon& polygon, OrdinateSet outputOrdinates, int p_level, Writer& writer) const +WKTWriter::appendSurfaceTaggedText(const Surface& surface, OrdinateSet outputOrdinates, int level, Writer& writer) const { - writer.write("POLYGON "); - appendOrdinateText(outputOrdinates, writer); - appendPolygonText(polygon, outputOrdinates, p_level, false, writer); + appendTag(surface, outputOrdinates, writer); + appendSurfaceText(surface, outputOrdinates, level, false, writer); } void -WKTWriter::appendMultiPointTaggedText(const MultiPoint& multipoint, OrdinateSet outputOrdinates, int p_level, Writer& writer) const +WKTWriter::appendMultiPointTaggedText(const MultiPoint& multipoint, OrdinateSet outputOrdinates, int level, Writer& writer) const { writer.write("MULTIPOINT "); appendOrdinateText(outputOrdinates, writer); - appendMultiPointText(multipoint, outputOrdinates, p_level, writer); + appendMultiPointText(multipoint, outputOrdinates, level, writer); } void -WKTWriter::appendMultiLineStringTaggedText(const MultiLineString& multiLineString, OrdinateSet outputOrdinates, int p_level, Writer& writer) const +WKTWriter::appendMultiCurveTaggedText(const GeometryCollection& multiCurve, OrdinateSet outputOrdinates, int level, Writer& writer) const { - writer.write("MULTILINESTRING "); - appendOrdinateText(outputOrdinates, writer); - appendMultiLineStringText(multiLineString, outputOrdinates, p_level, false, writer); + appendTag(multiCurve, outputOrdinates, writer); + appendMultiCurveText(multiCurve, outputOrdinates, level, false, writer); } void -WKTWriter::appendMultiPolygonTaggedText(const MultiPolygon& multiPolygon, OrdinateSet outputOrdinates, int p_level, Writer& writer) const +WKTWriter::appendMultiSurfaceTaggedText(const GeometryCollection& multiPolygon, OrdinateSet outputOrdinates, int level, Writer& writer) const { - writer.write("MULTIPOLYGON "); - appendOrdinateText(outputOrdinates, writer); - appendMultiPolygonText(multiPolygon, outputOrdinates, p_level, writer); + appendTag(multiPolygon, outputOrdinates, writer); + appendMultiSurfaceText(multiPolygon, outputOrdinates, level, writer); } void -WKTWriter::appendGeometryCollectionTaggedText(const GeometryCollection& geometryCollection, OrdinateSet outputOrdinates, int p_level, +WKTWriter::appendGeometryCollectionTaggedText(const GeometryCollection& geometryCollection, OrdinateSet outputOrdinates, int level, Writer& writer) const { writer.write("GEOMETRYCOLLECTION "); appendOrdinateText(outputOrdinates, writer); - appendGeometryCollectionText(geometryCollection, outputOrdinates, p_level, writer); + appendGeometryCollectionText(geometryCollection, outputOrdinates, level, writer); } /* protected */ @@ -377,7 +419,7 @@ WKTWriter::appendCoordinate(const CoordinateXYZM& coordinate, void WKTWriter::appendSequenceText(const CoordinateSequence& seq, OrdinateSet outputOrdinates, - int p_level, + int level, bool doIndent, Writer& writer) const { @@ -386,7 +428,7 @@ WKTWriter::appendSequenceText(const CoordinateSequence& seq, } else { if(doIndent) { - indent(p_level, &writer); + indent(level, &writer); } writer.write("("); CoordinateXYZM c; @@ -394,7 +436,7 @@ WKTWriter::appendSequenceText(const CoordinateSequence& seq, if(i > 0) { writer.write(", "); if(coordsPerLine > 0 && i % coordsPerLine == 0) { - indent(p_level + 2, &writer); + indent(level + 2, &writer); } } seq.getAt(i, c); @@ -404,18 +446,35 @@ WKTWriter::appendSequenceText(const CoordinateSequence& seq, } } -/* protected */ -std::string -WKTWriter::writeNumber(double d) const +int +WKTWriter::writeTrimmedNumber(double d, uint32_t precision, char* buf) { - uint32_t precision = decimalPlaces >= 0 ? static_cast(decimalPlaces) : 0; + const auto da = std::fabs(d); + if ( !std::isfinite(d) || (da == 0.0) ) + // non-finite or exactly zero + return geos_d2sfixed_buffered_n(d, precision, buf); + else if ( (da >= 1e+17) || (da < 1e-4) ) + // very large or small numbers, use scientific notation + return geos_d2sexp_buffered_n(d, precision, buf); + else { + // most real-world coordinates, use positional notation + if ( (precision < 4) && (da < 1.0) ) { + // adjust precision to avoid rounding to zero + precision = static_cast(-floor(log10(da))); + } + return geos_d2sfixed_buffered_n(d, precision, buf); + } +} + +std::string +WKTWriter::writeNumber(double d, bool trim, uint32_t precision) { /* * For a "trimmed" result, with no trailing zeros we use * the ryu library. */ if (trim) { - char buf[128]; - int len = geos_d2sfixed_buffered_n(d, precision, buf); + char buf[28]; + int len = writeTrimmedNumber(d, precision, buf); buf[len] = '\0'; std::string s(buf); return s; @@ -433,15 +492,16 @@ WKTWriter::writeNumber(double d) const } } -void -WKTWriter::appendLineStringText(const LineString& lineString, OrdinateSet outputOrdinates, int p_level, - bool doIndent, Writer& writer) const +/* protected */ +std::string +WKTWriter::writeNumber(double d) const { - appendSequenceText(*lineString.getCoordinatesRO(), outputOrdinates, p_level, doIndent, writer); + uint32_t precision = decimalPlaces >= 0 ? static_cast(decimalPlaces) : 0; + return writeNumber(d, trim, precision); } void -WKTWriter::appendPolygonText(const Polygon& polygon, OrdinateSet outputOrdinates, int /*level*/, +WKTWriter::appendSurfaceText(const Surface& polygon, OrdinateSet outputOrdinates, int level, bool indentFirst, Writer& writer) const { if(polygon.isEmpty()) { @@ -452,11 +512,15 @@ WKTWriter::appendPolygonText(const Polygon& polygon, OrdinateSet outputOrdinates indent(level, &writer); } writer.write("("); - appendLineStringText(*polygon.getExteriorRing(), outputOrdinates, level, false, writer); + + auto ring = polygon.getExteriorRing(); + appendCurveText(*ring, outputOrdinates, level, false, writer); + for(std::size_t i = 0, n = polygon.getNumInteriorRing(); i < n; ++i) { writer.write(", "); - const LineString* ls = polygon.getInteriorRingN(i); - appendLineStringText(*ls, outputOrdinates, level + 1, true, writer); + + auto hole = polygon.getInteriorRingN(i); + appendCurveText(*hole, outputOrdinates, level + 1, true, writer); } writer.write(")"); } @@ -466,12 +530,13 @@ void WKTWriter::appendMultiPointText(const MultiPoint& multiPoint, OrdinateSet outputOrdinates, int /*level*/, Writer& writer) const { - if(multiPoint.isEmpty()) { + const std::size_t n = multiPoint.getNumGeometries(); + if(n == 0) { writer.write("EMPTY"); } else { writer.write("("); - for(std::size_t i = 0, n = multiPoint.getNumGeometries(); i < n; ++i) { + for(std::size_t i = 0; i < n; ++i) { if(i > 0) { writer.write(", "); } @@ -492,49 +557,55 @@ WKTWriter::appendMultiPointText(const MultiPoint& multiPoint, OrdinateSet output } void -WKTWriter::appendMultiLineStringText(const MultiLineString& multiLineString, OrdinateSet outputOrdinates, int p_level, bool indentFirst, +WKTWriter::appendMultiCurveText(const GeometryCollection& multiCurve, OrdinateSet outputOrdinates, int level, bool indentFirst, Writer& writer) const { - if(multiLineString.isEmpty()) { + const std::size_t n = multiCurve.getNumGeometries(); + if(n == 0) { writer.write("EMPTY"); } else { - int level2 = p_level; + int level2 = level; bool doIndent = indentFirst; writer.write("("); - for(std::size_t i = 0, n = multiLineString.getNumGeometries(); - i < n; ++i) { + for(std::size_t i = 0; i < n; ++i) { if(i > 0) { writer.write(", "); - level2 = p_level + 1; + level2 = level + 1; doIndent = true; } - const LineString* ls = multiLineString.getGeometryN(i); - appendLineStringText(*ls, outputOrdinates, level2, doIndent, writer); + + const Curve* g = static_cast(multiCurve.getGeometryN(i)); + appendCurveText(*g, outputOrdinates, level2, doIndent, writer); } writer.write(")"); } } void -WKTWriter::appendMultiPolygonText(const MultiPolygon& multiPolygon, OrdinateSet outputOrdinates, int p_level, Writer& writer) const +WKTWriter::appendMultiSurfaceText(const GeometryCollection& multiSurface, OrdinateSet outputOrdinates, int level, Writer& writer) const { - if(multiPolygon.isEmpty()) { + const std::size_t n = multiSurface.getNumGeometries(); + if(n == 0) { writer.write("EMPTY"); } else { - int level2 = p_level; + int level2 = level; bool doIndent = false; writer.write("("); - for(std::size_t i = 0, n = multiPolygon.getNumGeometries(); - i < n; ++i) { + for(std::size_t i = 0; i < n; ++i) { if(i > 0) { writer.write(", "); - level2 = p_level + 1; + level2 = level + 1; doIndent = true; } - const Polygon* p = multiPolygon.getGeometryN(i); - appendPolygonText(*p, outputOrdinates, level2, doIndent, writer); + const Surface* p = static_cast(multiSurface.getGeometryN(i)); + if (p->getGeometryTypeId() == GEOS_POLYGON) { + appendSurfaceText(*p, outputOrdinates, level2, doIndent, writer); + } else { + // FIXME indent + appendSurfaceTaggedText(*p, outputOrdinates, level2, writer); + } } writer.write(")"); } @@ -544,35 +615,35 @@ void WKTWriter::appendGeometryCollectionText( const GeometryCollection& geometryCollection, OrdinateSet outputOrdinates, - int p_level, + int level, Writer& writer) const { - if(geometryCollection.getNumGeometries() > 0) { - int level2 = p_level; + const std::size_t n = geometryCollection.getNumGeometries(); + if(n == 0) { + writer.write("EMPTY"); + } + else { + int level2 = level; writer.write("("); - for(std::size_t i = 0, n = geometryCollection.getNumGeometries(); - i < n; ++i) { + for(std::size_t i = 0; i < n; ++i) { if(i > 0) { writer.write(", "); - level2 = p_level + 1; + level2 = level + 1; } appendGeometryTaggedText(*geometryCollection.getGeometryN(i), outputOrdinates, level2, writer); } writer.write(")"); } - else { - writer.write("EMPTY"); - } } void -WKTWriter::indent(int p_level, Writer* writer) const +WKTWriter::indent(int level, Writer* writer) const { - if(!isFormatted || p_level <= 0) { + if(!isFormatted || level <= 0) { return; } writer->write("\n"); - writer->write(std::string(INDENT * static_cast(p_level), ' ')); + writer->write(std::string(INDENT * static_cast(level), ' ')); } } // namespace geos.io diff --git a/deps/libgeos/geos/src/linearref/LengthIndexedLine.cpp b/deps/libgeos/geos/src/linearref/LengthIndexedLine.cpp index 012a9dc43..5522eb32c 100644 --- a/deps/libgeos/geos/src/linearref/LengthIndexedLine.cpp +++ b/deps/libgeos/geos/src/linearref/LengthIndexedLine.cpp @@ -57,6 +57,13 @@ LengthIndexedLine::extractPoint(double index, double offsetDistance) const std::unique_ptr LengthIndexedLine::extractLine(double startIndex, double endIndex) const { + if (std::isnan(startIndex)) { + throw util::IllegalArgumentException("startIndex is NaN"); + } + if (std::isnan(endIndex)) { + throw util::IllegalArgumentException("endIndex is NaN"); + } + const LocationIndexedLine lil(linearGeom); const double startIndex2 = clampIndex(startIndex); const double endIndex2 = clampIndex(endIndex); diff --git a/deps/libgeos/geos/src/noding/GeometryNoder.cpp b/deps/libgeos/geos/src/noding/GeometryNoder.cpp index 144d1f306..71ad91ea9 100644 --- a/deps/libgeos/geos/src/noding/GeometryNoder.cpp +++ b/deps/libgeos/geos/src/noding/GeometryNoder.cpp @@ -92,6 +92,7 @@ GeometryNoder::GeometryNoder(const geom::Geometry& g) : argGeom(g) { + util::ensureNoCurvedComponents(argGeom); } /* private */ diff --git a/deps/libgeos/geos/src/noding/MCIndexSegmentSetMutualIntersector.cpp b/deps/libgeos/geos/src/noding/MCIndexSegmentSetMutualIntersector.cpp index 148ace410..b5b5e4d49 100644 --- a/deps/libgeos/geos/src/noding/MCIndexSegmentSetMutualIntersector.cpp +++ b/deps/libgeos/geos/src/noding/MCIndexSegmentSetMutualIntersector.cpp @@ -12,6 +12,7 @@ * **********************************************************************/ +#include #include #include #include @@ -47,8 +48,14 @@ MCIndexSegmentSetMutualIntersector::addToMonoChains(SegmentString* segStr) { if (segStr->size() == 0) return; + MonoChains segChains; MonotoneChainBuilder::getChains(segStr->getCoordinates(), - segStr, monoChains); + segStr, segChains); + for (auto& mc : segChains) { + if (envelope == nullptr || envelope->intersects(mc.getEnvelope())) { + monoChains.push_back(mc); + } + } } @@ -89,7 +96,9 @@ MCIndexSegmentSetMutualIntersector::process(SegmentString::ConstVect* segStrings { if (!indexBuilt) { for (auto& mc: indexChains) { - index.insert(&(mc.getEnvelope(overlapTolerance)), &mc); + if (envelope == nullptr || envelope->intersects(mc.getEnvelope())) { + index.insert(&(mc.getEnvelope(overlapTolerance)), &mc); + } } indexBuilt = true; } diff --git a/deps/libgeos/geos/src/operation/BoundaryOp.cpp b/deps/libgeos/geos/src/operation/BoundaryOp.cpp index 504861784..b407a2a41 100644 --- a/deps/libgeos/geos/src/operation/BoundaryOp.cpp +++ b/deps/libgeos/geos/src/operation/BoundaryOp.cpp @@ -52,6 +52,8 @@ BoundaryOp::BoundaryOp(const geom::Geometry& geom, const algorithm::BoundaryNode std::unique_ptr BoundaryOp::getBoundary() { + util::ensureNoCurvedComponents(m_geom); + if (auto ls = dynamic_cast(&m_geom)) { return boundaryLineString(*ls); } diff --git a/deps/libgeos/geos/src/operation/GeometryGraphOperation.cpp b/deps/libgeos/geos/src/operation/GeometryGraphOperation.cpp index 42d157bd0..65b5c298c 100644 --- a/deps/libgeos/geos/src/operation/GeometryGraphOperation.cpp +++ b/deps/libgeos/geos/src/operation/GeometryGraphOperation.cpp @@ -54,9 +54,9 @@ GeometryGraphOperation::GeometryGraphOperation(const Geometry* g0, setComputationPrecision(pm1); } - arg[0] = new GeometryGraph(0, g0, + arg[0] = std::make_unique(0, g0, algorithm::BoundaryNodeRule::getBoundaryOGCSFS()); - arg[1] = new GeometryGraph(1, g1, + arg[1] = std::make_unique(1, g1, algorithm::BoundaryNodeRule::getBoundaryOGCSFS()); } @@ -80,8 +80,8 @@ GeometryGraphOperation::GeometryGraphOperation(const Geometry* g0, setComputationPrecision(pm1); } - arg[0] = new GeometryGraph(0, g0, boundaryNodeRule); - arg[1] = new GeometryGraph(1, g1, boundaryNodeRule); + arg[0] = std::make_unique(0, g0, boundaryNodeRule); + arg[1] = std::make_unique(1, g1, boundaryNodeRule); } @@ -93,7 +93,7 @@ GeometryGraphOperation::GeometryGraphOperation(const Geometry* g0): setComputationPrecision(pm0); - arg[0] = new GeometryGraph(0, g0); + arg[0] = std::make_unique(0, g0); } const Geometry* @@ -114,9 +114,6 @@ GeometryGraphOperation::setComputationPrecision(const PrecisionModel* pm) GeometryGraphOperation::~GeometryGraphOperation() { - for(unsigned int i = 0; i < arg.size(); ++i) { - delete arg[i]; - } } } // namespace geos.operation diff --git a/deps/libgeos/geos/src/operation/buffer/BufferBuilder.cpp b/deps/libgeos/geos/src/operation/buffer/BufferBuilder.cpp index bb2f17377..716215b71 100644 --- a/deps/libgeos/geos/src/operation/buffer/BufferBuilder.cpp +++ b/deps/libgeos/geos/src/operation/buffer/BufferBuilder.cpp @@ -539,7 +539,7 @@ BufferBuilder::buffer(const Geometry* g, double distance) std::vector> polys = plgnzr.getPolygons(); #if GEOS_DEBUG - std::cerr << "Polygonization of noded linework returend: " << polys.size() << " polygons" << std::endl; + std::cerr << "Polygonization of noded linework returned: " << polys.size() << " polygons" << std::endl; #endif if ( polys.size() > 1 ) diff --git a/deps/libgeos/geos/src/operation/buffer/BufferCurveSetBuilder.cpp b/deps/libgeos/geos/src/operation/buffer/BufferCurveSetBuilder.cpp index 239b28a8d..41038d74e 100644 --- a/deps/libgeos/geos/src/operation/buffer/BufferCurveSetBuilder.cpp +++ b/deps/libgeos/geos/src/operation/buffer/BufferCurveSetBuilder.cpp @@ -38,10 +38,12 @@ #include #include #include +#include #include // for min #include #include +#include #include #include #include @@ -337,7 +339,7 @@ BufferCurveSetBuilder::addRingSide(const CoordinateSequence* coord, } std::vector lineList; curveBuilder.getRingCurve(coord, side, offsetDistance, lineList); - // ASSERT: lineList contains exactly 1 curve (this is teh JTS semantics) + // ASSERT: lineList contains exactly 1 curve (this is the JTS semantics) if (lineList.size() > 0) { const CoordinateSequence* curve = lineList[0]; /** @@ -357,56 +359,67 @@ BufferCurveSetBuilder::addRingSide(const CoordinateSequence* coord, /* private static*/ bool BufferCurveSetBuilder::isRingCurveInverted( - const CoordinateSequence* inputPts, double dist, - const CoordinateSequence* curvePts) + const CoordinateSequence* inputRing, double dist, + const CoordinateSequence* curveRing) { if (dist == 0.0) return false; /** * Only proper rings can invert. */ - if (inputPts->size() <= 3) return false; + if (inputRing->size() <= 3) return false; /** * Heuristic based on low chance that a ring with many vertices will invert. * This low limit ensures this test is fairly efficient. */ - if (inputPts->size() >= MAX_INVERTED_RING_SIZE) return false; + if (inputRing->size() >= MAX_INVERTED_RING_SIZE) return false; /** * An inverted curve has no more points than the input ring. * This also eliminates concave inputs (which will produce fillet arcs) */ - if (curvePts->size() > INVERTED_CURVE_VERTEX_FACTOR * inputPts->size()) return false; + if (curveRing->size() > INVERTED_CURVE_VERTEX_FACTOR * inputRing->size()) return false; /** - * Check if the curve vertices are all closer to the input ring - * than the buffer distance. - * If so, the curve is NOT a valid buffer curve. + * If curve contains points which are on the buffer, + * it is not inverted and can be included in the raw curves. */ - double distTol = NEARNESS_FACTOR * fabs(dist); - double maxDist = maxDistance(curvePts, inputPts); - bool isCurveTooClose = maxDist < distTol; - return isCurveTooClose; + if (hasPointOnBuffer(inputRing, dist, curveRing)) + return false; + + //-- curve is inverted, so discard it + return true; +//std::cout << std::setprecision(10) << io::WKTWriter::toLineString(*curveRing) << std::endl; +//std::cout << "isRingCurveInverted: " << isCurveTooClose << " maxDist = " << maxDist << std::endl; } -/** - * Computes the maximum distance out of a set of points to a linestring. - * - * @param pts the points - * @param line the linestring vertices - * @return the maximum distance - */ -/* private static */ -double -BufferCurveSetBuilder::maxDistance(const CoordinateSequence* pts, const CoordinateSequence* line) { - double maxDistance = 0; - for (std::size_t i = 0; i < pts->size(); i++) { - const Coordinate& p = pts->getAt(i); - double dist = Distance::pointToSegmentString(p, line); - if (dist > maxDistance) { - maxDistance = dist; +/* private static*/ +bool +BufferCurveSetBuilder::hasPointOnBuffer( + const CoordinateSequence* inputRing, double dist, + const CoordinateSequence* curveRing) +{ + double distTol = NEARNESS_FACTOR * fabs(dist); + + for (std::size_t i = 0; i < curveRing->size(); i++) { + const CoordinateXY& v = curveRing->getAt(i); + + //-- check curve vertices + double distVertex = Distance::pointToSegmentString(v, inputRing); + if (distVertex > distTol) { + return true; + } + + //-- check curve segment midpoints + std::size_t iNext = (i < curveRing->size() - 1) ? i + 1 : 0; + const CoordinateXY& vnext = curveRing->getAt(iNext); + CoordinateXY midPt = LineSegment::midPoint(v, vnext); + + double distMid = Distance::pointToSegmentString(midPt, inputRing); + if (distMid > distTol) { + return true; } } - return maxDistance; + return false; } /*private*/ diff --git a/deps/libgeos/geos/src/operation/buffer/BufferParameters.cpp b/deps/libgeos/geos/src/operation/buffer/BufferParameters.cpp index 8c97a51c3..76c6c13e9 100644 --- a/deps/libgeos/geos/src/operation/buffer/BufferParameters.cpp +++ b/deps/libgeos/geos/src/operation/buffer/BufferParameters.cpp @@ -19,6 +19,7 @@ #include // for std::abs() #include // for cos +#include #include #include @@ -95,7 +96,7 @@ BufferParameters::setQuadrantSegments(int quadSegs) double BufferParameters::bufferDistanceError(int quadSegs) { - double alpha = MATH_PI / 2.0 / quadSegs; + double alpha = algorithm::Angle::PI_OVER_2 / quadSegs; return 1 - cos(alpha / 2.0); } diff --git a/deps/libgeos/geos/src/operation/buffer/BufferSubgraph.cpp b/deps/libgeos/geos/src/operation/buffer/BufferSubgraph.cpp index 5c0211dc6..f43eb165a 100644 --- a/deps/libgeos/geos/src/operation/buffer/BufferSubgraph.cpp +++ b/deps/libgeos/geos/src/operation/buffer/BufferSubgraph.cpp @@ -71,7 +71,7 @@ BufferSubgraph::create(Node* node) addReachable(node); // We are assuming that dirEdgeList - // contains *at leas* ONE forward DirectedEdge + // contains *at least* ONE forward DirectedEdge finder.findEdge(&dirEdgeList); rightMostCoord = &(finder.getCoordinate()); diff --git a/deps/libgeos/geos/src/operation/buffer/OffsetCurve.cpp b/deps/libgeos/geos/src/operation/buffer/OffsetCurve.cpp index d68c0545f..f524ccdc1 100644 --- a/deps/libgeos/geos/src/operation/buffer/OffsetCurve.cpp +++ b/deps/libgeos/geos/src/operation/buffer/OffsetCurve.cpp @@ -332,13 +332,13 @@ OffsetCurve::matchSegments( } void select(const geom::LineSegment& seg) override { - (void)seg; // quiet ununsed variable warning + (void)seg; // quiet unused variable warning return; } void select(const MonotoneChain& mc, std::size_t segIndex) override { - (void)mc; // quiet ununsed variable warning + (void)mc; // quiet unused variable warning /** * A curveRingPt segment may match all or only a portion of a single raw segment. * There may be multiple curve ring segs that match along the raw segment. @@ -467,7 +467,7 @@ OffsetCurve::findSectionEnd( /** * End section at gap in raw curve. * Only needed for joined curve, since otherwise - * contigous buffer segments can be in same section + * contiguous buffer segments can be in same section */ double locDelta = std::abs(loc[next] - loc[end]); if (locDelta > 1) diff --git a/deps/libgeos/geos/src/operation/buffer/OffsetSegmentGenerator.cpp b/deps/libgeos/geos/src/operation/buffer/OffsetSegmentGenerator.cpp index 32ab310f0..4537f9b3d 100644 --- a/deps/libgeos/geos/src/operation/buffer/OffsetSegmentGenerator.cpp +++ b/deps/libgeos/geos/src/operation/buffer/OffsetSegmentGenerator.cpp @@ -32,13 +32,9 @@ #include #include #include -#include +#include #include -#ifndef GEOS_DEBUG -#define GEOS_DEBUG 0 -#endif - using namespace geos::algorithm; using namespace geos::geom; @@ -79,11 +75,11 @@ OffsetSegmentGenerator::OffsetSegmentGenerator( { // compute intersections in full precision, to provide accuracy // the points are rounded as they are inserted into the curve line - filletAngleQuantum = MATH_PI / 2.0 / bufParams.getQuadrantSegments(); + filletAngleQuantum = Angle::PI_OVER_2 / bufParams.getQuadrantSegments(); int quadSegs = bufParams.getQuadrantSegments(); if (quadSegs < 1) quadSegs = 1; - filletAngleQuantum = MATH_PI / 2.0 / quadSegs; + filletAngleQuantum = Angle::PI_OVER_2 / quadSegs; /* * Non-round joins cause issues with short closing segments, @@ -208,7 +204,7 @@ OffsetSegmentGenerator::addLineEndCap(const Coordinate& p0, const Coordinate& p1 case BufferParameters::CAP_ROUND: // add offset seg points with a fillet between them segList.addPt(offsetL.p1); - addDirectedFillet(p1, angle + MATH_PI / 2.0, angle - MATH_PI / 2.0, + addDirectedFillet(p1, angle + Angle::PI_OVER_2, angle - Angle::PI_OVER_2, Orientation::CLOCKWISE, distance); segList.addPt(offsetR.p1); break; @@ -221,8 +217,10 @@ OffsetSegmentGenerator::addLineEndCap(const Coordinate& p0, const Coordinate& p1 // add a square defined by extensions of the offset // segment endpoints Coordinate squareCapSideOffset; - squareCapSideOffset.x = fabs(distance) * cos(angle); - squareCapSideOffset.y = fabs(distance) * sin(angle); + double sinangle, cosangle; + Angle::sinCosSnap(angle, sinangle, cosangle); + squareCapSideOffset.x = fabs(distance) * cosangle; + squareCapSideOffset.y = fabs(distance) * sinangle; Coordinate squareCapLOffset( offsetL.p1.x + squareCapSideOffset.x, @@ -250,12 +248,12 @@ OffsetSegmentGenerator::addDirectedFillet(const Coordinate& p, const Coordinate& if(direction == Orientation::CLOCKWISE) { if(startAngle <= endAngle) { - startAngle += 2.0 * MATH_PI; + startAngle += Angle::PI_TIMES_2; } } else { // direction==COUNTERCLOCKWISE if(startAngle >= endAngle) { - startAngle -= 2.0 * MATH_PI; + startAngle -= Angle::PI_TIMES_2; } } @@ -277,13 +275,13 @@ OffsetSegmentGenerator::addDirectedFillet(const Coordinate& p, double startAngle // no segments because angle is less than increment-nothing to do! if(nSegs < 1) return; - // double initAngle, currAngleInc; double angleInc = totalAngle / nSegs; + double sinangle, cosangle; Coordinate pt; for (int i = 0; i < nSegs; i++) { - double angle = startAngle + directionFactor * i * angleInc; - pt.x = p.x + radius * cos(angle); - pt.y = p.y + radius * sin(angle); + Angle::sinCosSnap(startAngle + directionFactor * i * angleInc, sinangle, cosangle); + pt.x = p.x + radius * cosangle; + pt.y = p.y + radius * sinangle; segList.addPt(pt); } } @@ -295,7 +293,7 @@ OffsetSegmentGenerator::createCircle(const Coordinate& p, double p_distance) // add start point Coordinate pt(p.x + p_distance, p.y); segList.addPt(pt); - addDirectedFillet(p, 0.0, 2.0 * MATH_PI, -1, p_distance); + addDirectedFillet(p, 0.0, Angle::PI_TIMES_2, -1, p_distance); segList.closeRing(); } @@ -483,7 +481,7 @@ OffsetSegmentGenerator::addMitreJoin(const geom::Coordinate& cornerPt, * However, this situation should have been eliminated earlier by the check * for whether the offset segment endpoints are almost coincident */ - CoordinateXY intPt = algorithm::Intersection::intersection(p_offset0.p0, p_offset0.p1, p_offset1.p0, p_offset1.p1); + CoordinateXY intPt = algorithm::CGAlgorithmsDD::intersection(p_offset0.p0, p_offset0.p1, p_offset1.p0, p_offset1.p1); if (!intPt.isNull() && intPt.distance(cornerPt) <= mitreLimitDistance) { segList.addPt(Coordinate(intPt)); @@ -531,20 +529,14 @@ OffsetSegmentGenerator::addLimitedMitreJoin( Coordinate bevelMidPt = project(cornerPt, p_mitreLimitDistance, dirBisectorOut); // slope angle of bevel segment - double dirBevel = Angle::normalize(dirBisectorOut + MATH_PI/2.0); + double dirBevel = Angle::normalize(dirBisectorOut + Angle::PI_OVER_2); // compute the candidate bevel segment by projecting both sides of the midpoint Coordinate bevel0 = project(bevelMidPt, p_distance, dirBevel); Coordinate bevel1 = project(bevelMidPt, p_distance, dirBevel + MATH_PI); - LineSegment bevel(bevel0, bevel1); - - //-- compute intersections with extended offset segments - double extendLen = p_mitreLimitDistance < p_distance ? p_distance : p_mitreLimitDistance; - LineSegment extend0 = extend(p_offset0, 2 * extendLen); - LineSegment extend1 = extend(p_offset1, -2 * extendLen); - Coordinate bevelInt0 = bevel.intersection(extend0); - Coordinate bevelInt1 = bevel.intersection(extend1); + Coordinate bevelInt0(Intersection::intersectionLineSegment(p_offset0.p0, p_offset0.p1, bevel0, bevel1)); + Coordinate bevelInt1(Intersection::intersectionLineSegment(p_offset1.p0, p_offset1.p1, bevel0, bevel1)); //-- add the limited bevel, if it intersects the offsets if (!bevelInt0.isNull() && !bevelInt1.isNull()) { @@ -561,27 +553,14 @@ OffsetSegmentGenerator::addLimitedMitreJoin( } -/* private static */ -LineSegment -OffsetSegmentGenerator::extend(const LineSegment& seg, double dist) -{ - double distFrac = std::abs(dist) / seg.getLength(); - double segFrac = dist >= 0 ? 1 + distFrac : 0 - distFrac; - Coordinate extendPt; - seg.pointAlong(segFrac, extendPt); - if (dist > 0) - return LineSegment(seg.p0, extendPt); - else - return LineSegment(extendPt, seg.p1); -} - - /* private static */ Coordinate OffsetSegmentGenerator::project(const Coordinate& pt, double d, double dir) { - double x = pt.x + d * std::cos(dir); - double y = pt.y + d * std::sin(dir); + double sindir, cosdir; + Angle::sinCosSnap(dir, sindir, cosdir); + double x = pt.x + d * cosdir; + double y = pt.y + d * sindir; return Coordinate(x, y); } diff --git a/deps/libgeos/geos/src/operation/buffer/SubgraphDepthLocater.cpp b/deps/libgeos/geos/src/operation/buffer/SubgraphDepthLocater.cpp index 7fa3c90eb..4fc9d578a 100644 --- a/deps/libgeos/geos/src/operation/buffer/SubgraphDepthLocater.cpp +++ b/deps/libgeos/geos/src/operation/buffer/SubgraphDepthLocater.cpp @@ -70,7 +70,7 @@ class DepthSegment { } /** - * Defines a comparision operation on DepthSegments + * Defines a comparison operation on DepthSegments * which orders them left to right * *

diff --git a/deps/libgeos/geos/src/operation/distance/ConnectedElementLocationFilter.cpp b/deps/libgeos/geos/src/operation/distance/ConnectedElementLocationFilter.cpp
index 71feb2801..b117627b1 100644
--- a/deps/libgeos/geos/src/operation/distance/ConnectedElementLocationFilter.cpp
+++ b/deps/libgeos/geos/src/operation/distance/ConnectedElementLocationFilter.cpp
@@ -35,7 +35,7 @@ namespace operation { // geos.operation
 namespace distance { // geos.operation.distance
 
 /*public*/
-std::vector>
+std::vector
 ConnectedElementLocationFilter::getLocations(const Geometry* geom)
 {
     ConnectedElementLocationFilter c;
@@ -51,7 +51,7 @@ ConnectedElementLocationFilter::filter_ro(const Geometry* geom)
             (typeid(*geom) == typeid(LineString)) ||
             (typeid(*geom) == typeid(LinearRing)) ||
             (typeid(*geom) == typeid(Polygon))) {
-        locations.emplace_back(new GeometryLocation(geom, 0, *(geom->getCoordinate())));
+        locations.emplace_back(geom, 0, *(geom->getCoordinate()));
     }
 }
 
@@ -64,7 +64,7 @@ ConnectedElementLocationFilter::filter_rw(Geometry* geom)
             (typeid(*geom) == typeid(LineString)) ||
             (typeid(*geom) == typeid(LinearRing)) ||
             (typeid(*geom) == typeid(Polygon))) {
-        locations.emplace_back(new GeometryLocation(geom, 0, *(geom->getCoordinate())));
+        locations.emplace_back(geom, 0, *(geom->getCoordinate()));
     }
 }
 
diff --git a/deps/libgeos/geos/src/operation/distance/DistanceOp.cpp b/deps/libgeos/geos/src/operation/distance/DistanceOp.cpp
index 952c68d96..18ae46c35 100644
--- a/deps/libgeos/geos/src/operation/distance/DistanceOp.cpp
+++ b/deps/libgeos/geos/src/operation/distance/DistanceOp.cpp
@@ -104,12 +104,19 @@ DistanceOp::distance()
 {
     using geos::util::IllegalArgumentException;
 
+    util::ensureNoCurvedComponents(geom[0]);
+    util::ensureNoCurvedComponents(geom[1]);
+
     if(geom[0] == nullptr || geom[1] == nullptr) {
         throw IllegalArgumentException("null geometries are not supported");
     }
     if(geom[0]->isEmpty() || geom[1]->isEmpty()) {
         return 0.0;
     }
+    if(geom[0]->getGeometryTypeId() == GEOS_POINT && geom[1]->getGeometryTypeId() == GEOS_POINT) {
+        return static_cast(geom[0])->getCoordinate()->distance(*static_cast(geom[1])->getCoordinate());
+    }
+
     computeMinDistance();
     return minDistance;
 }
@@ -124,26 +131,26 @@ DistanceOp::nearestPoints()
     auto& locs = minDistanceLocation;
 
     // Empty input geometries result in this behaviour
-    if(locs[0] == nullptr || locs[1] == nullptr) {
+    if(locs[0].getGeometryComponent() == nullptr || locs[1].getGeometryComponent() == nullptr) {
         // either both or none are set..
-        assert(locs[0] == nullptr && locs[1] == nullptr);
+        assert(locs[0].getGeometryComponent() == nullptr && locs[1].getGeometryComponent() == nullptr);
 
         return nullptr;
     }
 
     auto nearestPts = detail::make_unique(2u);
-    nearestPts->setAt(locs[0]->getCoordinate(), 0);
-    nearestPts->setAt(locs[1]->getCoordinate(), 1);
+    nearestPts->setAt(locs[0].getCoordinate(), 0);
+    nearestPts->setAt(locs[1].getCoordinate(), 1);
 
     return nearestPts;
 }
 
 void
-DistanceOp::updateMinDistance(std::array, 2> & locGeom, bool flip)
+DistanceOp::updateMinDistance(std::array & locGeom, bool flip)
 {
     // if not set then don't update
-    if(locGeom[0] == nullptr) {
-        assert(locGeom[1] == nullptr);
+    if(locGeom[0].getGeometryComponent() == nullptr) {
+        assert(locGeom[1].getGeometryComponent() == nullptr);
 #if GEOS_DEBUG
         std::cerr << "updateMinDistance called with loc[0] == null and loc[1] == null" << std::endl;
 #endif
@@ -206,15 +213,15 @@ DistanceOp::computeContainmentDistance()
     // Expected to fill minDistanceLocation items
     // if minDistance <= terminateDistance
 
-    std::array, 2> locPtPoly;
+    std::array locPtPoly;
     // test if either geometry has a vertex inside the other
     if(! polys1.empty()) {
         auto insideLocs0 = ConnectedElementLocationFilter::getLocations(geom[0]);
         computeInside(insideLocs0, polys1, locPtPoly);
 
         if(minDistance <= terminateDistance) {
-            assert(locPtPoly[0]);
-            assert(locPtPoly[1]);
+            assert(locPtPoly[0].getGeometryComponent());
+            assert(locPtPoly[1].getGeometryComponent());
 
             minDistanceLocation[0] = std::move(locPtPoly[0]);
             minDistanceLocation[1] = std::move(locPtPoly[1]);
@@ -236,8 +243,8 @@ DistanceOp::computeContainmentDistance()
         computeInside(insideLocs1, polys0, locPtPoly);
         if(minDistance <= terminateDistance) {
             // flip locations, since we are testing geom 1 VS geom 0
-            assert(locPtPoly[0]);
-            assert(locPtPoly[1]);
+            assert(locPtPoly[0].getGeometryComponent());
+            assert(locPtPoly[1].getGeometryComponent());
 
             minDistanceLocation[0] = std::move(locPtPoly[1]);
             minDistanceLocation[1] = std::move(locPtPoly[0]);
@@ -250,18 +257,18 @@ DistanceOp::computeContainmentDistance()
 
 /*private*/
 void
-DistanceOp::computeInside(std::vector> & locs,
+DistanceOp::computeInside(std::vector & locs,
                           const Polygon::ConstVect& polys,
-                          std::array, 2> & locPtPoly)
+                          std::array & locPtPoly)
 {
     for(auto& loc : locs) {
         for(const auto& poly : polys) {
-            const auto& pt = loc->getCoordinate();
+            const auto& pt = loc.getCoordinate();
 
 			if (Location::EXTERIOR != ptLocator.locate(pt, static_cast(poly))) {
 				minDistance = 0.0;
 				locPtPoly[0] = std::move(loc);
-				locPtPoly[1].reset(new GeometryLocation(poly, pt));
+                locPtPoly[1] = GeometryLocation(poly, pt);
 				return;
 			}
         }
@@ -275,7 +282,7 @@ DistanceOp::computeFacetDistance()
     using geom::util::LinearComponentExtracter;
     using geom::util::PointExtracter;
 
-    std::array, 2> locGeom;
+    std::array locGeom;
 
     /*
      * Geometries are not wholly inside, so compute distance from lines
@@ -310,8 +317,8 @@ DistanceOp::computeFacetDistance()
     std::cerr << "PointExtracter found " << pts1.size() << " points in geometry 2" << std::endl;
 #endif
 
-    locGeom[0] = nullptr;
-    locGeom[1] = nullptr;
+    locGeom[0] = GeometryLocation();
+    locGeom[1] = GeometryLocation();
     computeMinDistanceLinesPoints(lines0, pts1, locGeom);
     updateMinDistance(locGeom, false);
     if(minDistance <= terminateDistance) {
@@ -328,8 +335,8 @@ DistanceOp::computeFacetDistance()
     std::cerr << "PointExtracter found " << pts0.size() << " points in geometry 1" << std::endl;
 #endif
 
-    locGeom[0] = nullptr;
-    locGeom[1] = nullptr;
+    locGeom[0] = GeometryLocation();
+    locGeom[1] = GeometryLocation();
     computeMinDistanceLinesPoints(lines1, pts0, locGeom);
     updateMinDistance(locGeom, true);
     if(minDistance <= terminateDistance) {
@@ -339,8 +346,8 @@ DistanceOp::computeFacetDistance()
         return;
     }
 
-    locGeom[0] = nullptr;
-    locGeom[1] = nullptr;
+    locGeom[0] = GeometryLocation();
+    locGeom[1] = GeometryLocation();
     computeMinDistancePoints(pts0, pts1, locGeom);
     updateMinDistance(locGeom, false);
 
@@ -354,7 +361,7 @@ void
 DistanceOp::computeMinDistanceLines(
     const LineString::ConstVect& lines0,
     const LineString::ConstVect& lines1,
-    std::array, 2> & locGeom)
+    std::array & locGeom)
 {
     for(const LineString* line0 : lines0) {
         for(const LineString* line1 : lines1) {
@@ -375,7 +382,7 @@ void
 DistanceOp::computeMinDistancePoints(
     const Point::ConstVect& points0,
     const Point::ConstVect& points1,
-    std::array, 2> & locGeom)
+    std::array & locGeom)
 {
     for(const Point* pt0 : points0) {
         for(const Point* pt1 : points1) {
@@ -396,8 +403,8 @@ DistanceOp::computeMinDistancePoints(
             if(dist < minDistance) {
                 minDistance = dist;
                 // this is wrong - need to determine closest points on both segments!!!
-                locGeom[0].reset(new GeometryLocation(pt0, 0, *(pt0->getCoordinate())));
-                locGeom[1].reset(new GeometryLocation(pt1, 0, *(pt1->getCoordinate())));
+                locGeom[0] = GeometryLocation(pt0, 0, *(pt0->getCoordinate()));
+                locGeom[1] = GeometryLocation(pt1, 0, *(pt1->getCoordinate()));
             }
 
             if(minDistance <= terminateDistance) {
@@ -412,7 +419,7 @@ void
 DistanceOp::computeMinDistanceLinesPoints(
     const LineString::ConstVect& lines,
     const Point::ConstVect& points,
-    std::array, 2> & locGeom)
+    std::array & locGeom)
 {
     for(const LineString* line : lines) {
         for(const Point* pt : points) {
@@ -433,7 +440,7 @@ void
 DistanceOp::computeMinDistance(
     const LineString* line0,
     const LineString* line1,
-    std::array, 2> & locGeom)
+    std::array & locGeom)
 {
     using geos::algorithm::Distance;
 
@@ -480,8 +487,8 @@ DistanceOp::computeMinDistance(
                 LineSegment seg1{Coordinate(p10), Coordinate(p11)};
                 auto closestPt = seg0.closestPoints(seg1);
 
-                locGeom[0].reset(new GeometryLocation(line0, i, closestPt[0]));
-                locGeom[1].reset(new GeometryLocation(line1, j, closestPt[1]));
+                locGeom[0] = GeometryLocation(line0, i, closestPt[0]);
+                locGeom[1] = GeometryLocation(line1, j, closestPt[1]);
             }
             if(minDistance <= terminateDistance) {
                 return;
@@ -494,7 +501,7 @@ DistanceOp::computeMinDistance(
 void
 DistanceOp::computeMinDistance(const LineString* line,
                                const Point* pt,
-                               std::array, 2> & locGeom)
+                               std::array & locGeom)
 {
     using geos::algorithm::Distance;
 
@@ -520,8 +527,8 @@ DistanceOp::computeMinDistance(const LineString* line,
             Coordinate segClosestPoint;
             seg.closestPoint(*coord, segClosestPoint);
 
-            locGeom[0].reset(new GeometryLocation(line, i, segClosestPoint));
-            locGeom[1].reset(new GeometryLocation(pt, 0, *coord));
+            locGeom[0] = GeometryLocation(line, i, segClosestPoint);
+            locGeom[1] = GeometryLocation(pt, 0, *coord);
         }
         if(minDistance <= terminateDistance) {
             return;
diff --git a/deps/libgeos/geos/src/operation/intersection/RectangleIntersection.cpp b/deps/libgeos/geos/src/operation/intersection/RectangleIntersection.cpp
index e9ddc3142..e3f7750af 100644
--- a/deps/libgeos/geos/src/operation/intersection/RectangleIntersection.cpp
+++ b/deps/libgeos/geos/src/operation/intersection/RectangleIntersection.cpp
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -34,6 +35,7 @@
 
 using geos::operation::intersection::Rectangle;
 using geos::operation::intersection::RectangleIntersectionBuilder;
+using geos::operation::overlayng::ElevationModel;
 using namespace geos::geom;
 using namespace geos::algorithm;
 namespace geos {
@@ -62,15 +64,18 @@ different(double x1, double y1, double x2, double y2)
 
 inline
 void
-clip_one_edge(double& x1, double& y1, double x2, double y2, double limit)
+clip_one_edge(double& x1, double& y1, double& z1, double x2, double y2, double z2, double limit)
 {
     if(x2 == limit) {
         y1 = y2;
         x1 = x2;
+        z1 = z2;
     }
 
     if(x1 != x2) {
-        y1 += (y2 - y1) * (limit - x1) / (x2 - x1);
+        double fraction = (limit - x1) / (x2 - x1);
+        y1 += (y2 - y1) * fraction;
+        z1 += (z2 - z1) * fraction;
         x1 = limit;
     }
 }
@@ -86,22 +91,22 @@ clip_one_edge(double& x1, double& y1, double x2, double y2, double limit)
  */
 
 void
-clip_to_edges(double& x1, double& y1,
-              double x2, double y2,
+clip_to_edges(double& x1, double& y1, double& z1,
+              double x2, double y2, double z2,
               const Rectangle& rect)
 {
     if(x1 < rect.xmin()) {
-        clip_one_edge(x1, y1, x2, y2, rect.xmin());
+        clip_one_edge(x1, y1, z1, x2, y2, z2, rect.xmin());
     }
     else if(x1 > rect.xmax()) {
-        clip_one_edge(x1, y1, x2, y2, rect.xmax());
+        clip_one_edge(x1, y1, z1, x2, y2, z2, rect.xmax());
     }
 
     if(y1 < rect.ymin()) {
-        clip_one_edge(y1, x1, y2, x2, rect.ymin());
+        clip_one_edge(y1, x1, z1, y2, x2, z2, rect.ymin());
     }
     else if(y1 > rect.ymax()) {
-        clip_one_edge(y1, x1, y2, x2, rect.ymax());
+        clip_one_edge(y1, x1, z1, y2, x2, z2, rect.ymax());
     }
 }
 
@@ -153,6 +158,7 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
 
     double x0 = 0;
     double y0 = 0;
+    double z0 = 0;
     bool add_start = false;
 
     // Start iterating
@@ -164,6 +170,7 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
 
         double x = cs[i].x;
         double y = cs[i].y;
+        double z = cs[i].z;
         Rectangle::Position pos = rect.position(x, y);
 
         if(pos == Rectangle::Outside) {
@@ -199,12 +206,14 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
             // Establish new position
             x = cs[i].x;
             y = cs[i].y;
+            z = cs[i].z;
             pos = rect.position(x, y);
 
             // Handle all possible cases
             x0 = cs[i - 1].x;
             y0 = cs[i - 1].y;
-            clip_to_edges(x0, y0, x, y, rect);
+            z0 = cs[i - 1].z;
+            clip_to_edges(x0, y0, z0, x, y, z, rect);
 
             if(pos == Rectangle::Inside) {
                 add_start = true;	// x0,y0 must have clipped the rectangle
@@ -218,7 +227,7 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
                 // which will then enter the Outside section.
 
                 // Clip the other end too
-                clip_to_edges(x, y, x0, y0, rect);
+                clip_to_edges(x, y, z, x0, y0, z0, rect);
 
                 Rectangle::Position prev_pos = rect.position(x0, y0);
                 pos = rect.position(x, y);
@@ -229,8 +238,8 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
                         !Rectangle::onSameEdge(prev_pos, pos)	// discard if travels along edge
                   ) {
                     auto coords = detail::make_unique(2u);
-                    coords->setAt(Coordinate(x0, y0), 0);
-                    coords->setAt(Coordinate(x, y), 1);
+                    coords->setAt(Coordinate(x0, y0, z0), 0);
+                    coords->setAt(Coordinate(x, y, z), 1);
                     auto line = _gf->createLineString(std::move(coords));
                     parts.add(line.release());
                 }
@@ -267,6 +276,7 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
             while(!go_outside && ++i < n) {
                 x = cs[i].x;
                 y = cs[i].y;
+                z = cs[i].z;
 
                 Rectangle::Position prev_pos = pos;
                 pos = rect.position(x, y);
@@ -278,7 +288,7 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
                     go_outside = true;
 
                     // Clip the outside point to edges
-                    clip_to_edges(x, y, cs[i - 1].x, cs[i - 1].y, rect);
+                    clip_to_edges(x, y, z, cs[i - 1].x, cs[i - 1].y, cs[i - 1].z, rect);
                     pos = rect.position(x, y);
 
                     // Does the line exit through the inside of the box?
@@ -291,7 +301,7 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
                     if(start_index < i - 1 || add_start || through_box) {
                         auto coords = detail::make_unique();
                         if(add_start) {
-                            coords->add(Coordinate(x0, y0));
+                            coords->add(Coordinate(x0, y0, z0));
                             add_start = false;
                         }
                         //line->addSubLineString(&g, start_index, i-1);
@@ -299,7 +309,7 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
                                     cs.begin() + static_cast(i));
 
                         if(through_box) {
-                            coords->add(Coordinate(x, y));
+                            coords->add(Coordinate(x, y, z));
                         }
 
                         auto line = _gf->createLineString(std::move(coords));
@@ -316,7 +326,7 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
                             //geom::LineString * line = new geom::LineString();
                             if(add_start) {
                                 //line->addPoint(x0,y0);
-                                coords->add(Coordinate(x0, y0));
+                                coords->add(Coordinate(x0, y0, z0));
                                 add_start = false;
                             }
                             //line->addSubLineString(&g, start_index, i-1);
@@ -349,7 +359,7 @@ RectangleIntersection::clip_linestring_parts(const geom::LineString* gi,
                 //geom::LineString * line = new geom::LineString();
                 if(add_start) {
                     //line->addPoint(x0,y0);
-                    coords->add(Coordinate(x0, y0));
+                    coords->add(Coordinate(x0, y0, z0));
                     add_start = false;
                 }
                 //line->addSubLineString(&g, start_index, i-1);
@@ -521,7 +531,6 @@ RectangleIntersection::clip_polygon_to_polygons(const geom::Polygon* g,
 
     parts.reconnectPolygons(rect);
     parts.release(toParts);
-
 }
 
 /**
@@ -682,7 +691,14 @@ std::unique_ptr
 RectangleIntersection::clip(const geom::Geometry& g, const Rectangle& rect)
 {
     RectangleIntersection ri(g, rect);
-    return ri.clip();
+    std::unique_ptr result = ri.clip();
+
+    if (g.hasZ()) {
+        std::unique_ptr elevModel = ElevationModel::create(g);
+        elevModel->populateZ(*result);
+    }
+
+    return result;
 }
 
 std::unique_ptr
diff --git a/deps/libgeos/geos/src/operation/linemerge/LineMerger.cpp b/deps/libgeos/geos/src/operation/linemerge/LineMerger.cpp
index 8163c9a62..e26aab299 100644
--- a/deps/libgeos/geos/src/operation/linemerge/LineMerger.cpp
+++ b/deps/libgeos/geos/src/operation/linemerge/LineMerger.cpp
@@ -94,6 +94,8 @@ struct LMGeometryComponentFilter: public GeometryComponentFilter {
 void
 LineMerger::add(const Geometry* geometry)
 {
+    util::ensureNoCurvedComponents(geometry);
+
     LMGeometryComponentFilter lmgcf(this);
     geometry->apply_ro(&lmgcf);
 }
diff --git a/deps/libgeos/geos/src/operation/linemerge/LineSequencer.cpp b/deps/libgeos/geos/src/operation/linemerge/LineSequencer.cpp
index cef634079..12b826695 100644
--- a/deps/libgeos/geos/src/operation/linemerge/LineSequencer.cpp
+++ b/deps/libgeos/geos/src/operation/linemerge/LineSequencer.cpp
@@ -309,7 +309,7 @@ LineSequencer::addReverseSubpath(const planargraph::DirectedEdge* de,
     if(expectedClosed) {
         // the path should end at the toNode of this de,
         // otherwise we have an error
-        util::Assert::isTrue(fromNode == endNode, "path not contiguos");
+        util::Assert::isTrue(fromNode == endNode, "path not contiguous");
         //assert(fromNode == endNode);
     }
 
diff --git a/deps/libgeos/geos/src/operation/overlayng/OverlayGraph.cpp b/deps/libgeos/geos/src/operation/overlayng/OverlayGraph.cpp
index 9bf076447..01ce395b0 100644
--- a/deps/libgeos/geos/src/operation/overlayng/OverlayGraph.cpp
+++ b/deps/libgeos/geos/src/operation/overlayng/OverlayGraph.cpp
@@ -130,7 +130,7 @@ OverlayGraph::createOverlayEdge(const CoordinateSequence* pts, OverlayLabel* lbl
 OverlayLabel*
 OverlayGraph::createOverlayLabel(const Edge* edge)
 {
-    // Instantate OverlayLabel on the std::deque
+    // Instantiate OverlayLabel on the std::deque
     ovLabelQue.emplace_back();
     // Read back a reference
     OverlayLabel& ovl = ovLabelQue.back();
diff --git a/deps/libgeos/geos/src/operation/overlayng/OverlayNGRobust.cpp b/deps/libgeos/geos/src/operation/overlayng/OverlayNGRobust.cpp
index 016eeb3df..e9af20896 100644
--- a/deps/libgeos/geos/src/operation/overlayng/OverlayNGRobust.cpp
+++ b/deps/libgeos/geos/src/operation/overlayng/OverlayNGRobust.cpp
@@ -83,6 +83,9 @@ OverlayNGRobust::Union(const Geometry* a)
 std::unique_ptr
 OverlayNGRobust::Overlay(const Geometry* geom0, const Geometry* geom1, int opCode)
 {
+    geos::util::ensureNoCurvedComponents(geom0);
+    geos::util::ensureNoCurvedComponents(geom1);
+
     std::unique_ptr result;
     std::runtime_error exOriginal("");
 
@@ -106,7 +109,9 @@ OverlayNGRobust::Overlay(const Geometry* geom0, const Geometry* geom1, int opCod
      */
     try {
         geom::PrecisionModel PM_FLOAT;
-        // std::cerr << "Using floating point overlay." << std::endl;
+#if GEOS_DEBUG
+        std::cerr << "Using floating point overlay." << std::endl;
+#endif
         result = OverlayNG::overlay(geom0, geom1, opCode, &PM_FLOAT);
 
         // Simple noding with no validation
diff --git a/deps/libgeos/geos/src/operation/overlayng/OverlayPoints.cpp b/deps/libgeos/geos/src/operation/overlayng/OverlayPoints.cpp
index c1c37799c..6afe7a211 100644
--- a/deps/libgeos/geos/src/operation/overlayng/OverlayPoints.cpp
+++ b/deps/libgeos/geos/src/operation/overlayng/OverlayPoints.cpp
@@ -27,14 +27,14 @@ namespace geos {      // geos
 namespace operation { // geos.operation
 namespace overlayng { // geos.operation.overlayng
 
-struct PointExtractingFilter: public GeometryComponentFilter {
+struct PointExtractingFilter final: public GeometryComponentFilter {
 
     PointExtractingFilter(std::map>& p_ptMap, const PrecisionModel* p_pm)
         : ptMap(p_ptMap), pm(p_pm)
     {}
 
     void
-    filter_ro(const Geometry* geom)
+    filter_ro(const Geometry* geom) override
     {
         if (geom->getGeometryTypeId() != GEOS_POINT) return;
 
diff --git a/deps/libgeos/geos/src/operation/polygonize/BuildArea.cpp b/deps/libgeos/geos/src/operation/polygonize/BuildArea.cpp
index 3956c735d..37ccd371e 100644
--- a/deps/libgeos/geos/src/operation/polygonize/BuildArea.cpp
+++ b/deps/libgeos/geos/src/operation/polygonize/BuildArea.cpp
@@ -98,7 +98,7 @@ static bool ringsEqualAnyDirection(const LinearRing* r1, const LinearRing* r2)
 
     bool equal = true;
 
-    /* Check equals forward (skip first point, we checked it alread) */
+    /* Check equals forward (skip first point, we checked it already) */
     for (size_t i=1; i* geomList)
     }
 }
 
-/*
- * Add a geometry to the linework to be polygonized.
- * May be called multiple times.
- * Any dimension of Geometry may be added;
- * the constituent linework will be extracted and used
- *
- * @param g a Geometry with linework to be polygonized
- */
-void
-Polygonizer::add(Geometry* g)
-{
-    g->apply_ro(&lineStringAdder);
-}
-
 /*
  * Add a geometry to the linework to be polygonized.
  * May be called multiple times.
@@ -128,6 +114,7 @@ Polygonizer::add(Geometry* g)
 void
 Polygonizer::add(const Geometry* g)
 {
+    util::ensureNoCurvedComponents(g);
     g->apply_ro(&lineStringAdder);
 }
 
diff --git a/deps/libgeos/geos/src/operation/predicate/RectangleIntersects.cpp b/deps/libgeos/geos/src/operation/predicate/RectangleIntersects.cpp
index a2feea7e6..c31fe8ce9 100644
--- a/deps/libgeos/geos/src/operation/predicate/RectangleIntersects.cpp
+++ b/deps/libgeos/geos/src/operation/predicate/RectangleIntersects.cpp
@@ -166,7 +166,7 @@ class ContainsPointVisitor: public geom::util::ShortCircuitedGeometryVisitor {
 
             // check rect point in poly (rect is known not to
             // touch polygon at this point)
-            if(SimplePointInAreaLocator::locatePointInPolygon(rectPt, poly) != geom::Location::EXTERIOR) {
+            if(SimplePointInAreaLocator::locatePointInSurface(rectPt, *poly) != geom::Location::EXTERIOR) {
                 containsPointVar = true;
                 return;
             }
diff --git a/deps/libgeos/geos/src/operation/relate/RelateComputer.cpp b/deps/libgeos/geos/src/operation/relate/RelateComputer.cpp
index bbdd255fe..90a18ed44 100644
--- a/deps/libgeos/geos/src/operation/relate/RelateComputer.cpp
+++ b/deps/libgeos/geos/src/operation/relate/RelateComputer.cpp
@@ -61,7 +61,7 @@ namespace geos {
 namespace operation { // geos.operation
 namespace relate { // geos.operation.relate
 
-RelateComputer::RelateComputer(std::vector* newArg):
+RelateComputer::RelateComputer(std::vector>& newArg):
     arg(newArg),
     nodes(RelateNodeFactory::instance()),
     im(new IntersectionMatrix())
@@ -74,10 +74,10 @@ RelateComputer::computeIM()
     // since Geometries are finite and embedded in a 2-D space, the EE element must always be 2
     im->set(Location::EXTERIOR, Location::EXTERIOR, 2);
     // if the Geometries don't overlap there is nothing to do
-    const Envelope* e1 = (*arg)[0]->getGeometry()->getEnvelopeInternal();
-    const Envelope* e2 = (*arg)[1]->getGeometry()->getEnvelopeInternal();
+    const Envelope* e1 = arg[0]->getGeometry()->getEnvelopeInternal();
+    const Envelope* e2 = arg[1]->getGeometry()->getEnvelopeInternal();
     if(!e1->intersects(e2)) {
-        computeDisjointIM(im.get(), (*arg)[0]->getBoundaryNodeRule());
+        computeDisjointIM(im.get(), arg[0]->getBoundaryNodeRule());
         return std::move(im);
     }
 
@@ -88,7 +88,7 @@ RelateComputer::computeIM()
 #endif
 
     std::unique_ptr si1(
-        (*arg)[0]->computeSelfNodes(&li, false)
+        arg[0]->computeSelfNodes(&li, false)
     );
 
     GEOS_CHECK_FOR_INTERRUPTS();
@@ -100,7 +100,7 @@ RelateComputer::computeIM()
 #endif
 
     std::unique_ptr si2(
-        (*arg)[1]->computeSelfNodes(&li, false)
+        arg[1]->computeSelfNodes(&li, false)
     );
 
     GEOS_CHECK_FOR_INTERRUPTS();
@@ -113,7 +113,7 @@ RelateComputer::computeIM()
 
     // compute intersections between edges of the two input geometries
     std::unique_ptr< SegmentIntersector> intersector(
-        (*arg)[0]->computeEdgeIntersections((*arg)[1], &li, false)
+        arg[0]->computeEdgeIntersections(arg[1].get(), &li, false)
     );
 
     GEOS_CHECK_FOR_INTERRUPTS();
@@ -188,9 +188,9 @@ RelateComputer::computeIM()
      */
     // build EdgeEnds for all intersections
     EdgeEndBuilder eeBuilder;
-    auto&& ee0 = eeBuilder.computeEdgeEnds((*arg)[0]->getEdges());
+    auto&& ee0 = eeBuilder.computeEdgeEnds(arg[0]->getEdges());
     insertEdgeEnds(ee0);
-    auto&& ee1 = eeBuilder.computeEdgeEnds((*arg)[1]->getEdges());
+    auto&& ee1 = eeBuilder.computeEdgeEnds(arg[1]->getEdges());
 
 #if GEOS_DEBUG
     std::cerr << "RelateComputer::computeIM: "
@@ -246,8 +246,8 @@ void
 RelateComputer::computeProperIntersectionIM(SegmentIntersector* intersector, IntersectionMatrix* imX)
 {
     // If a proper intersection is found, we can set a lower bound on the IM.
-    int dimA = (*arg)[0]->getGeometry()->getDimension();
-    int dimB = (*arg)[1]->getGeometry()->getDimension();
+    int dimA = arg[0]->getGeometry()->getDimension();
+    int dimB = arg[1]->getGeometry()->getDimension();
     bool hasProper = intersector->hasProperIntersection();
     bool hasProperInterior = intersector->hasProperInteriorIntersection();
     // For Geometry's of dim 0 there can never be proper intersections.
@@ -311,7 +311,7 @@ RelateComputer::computeProperIntersectionIM(SegmentIntersector* intersector, Int
 void
 RelateComputer::copyNodesAndLabels(uint8_t argIndex)
 {
-    const NodeMap* nm = (*arg)[argIndex]->getNodeMap();
+    const NodeMap* nm = arg[argIndex]->getNodeMap();
     for(const auto& it: *nm) {
         const Node* graphNode = it.second.get();
         Node* newNode = nodes.addNode(graphNode->getCoordinate());
@@ -333,7 +333,7 @@ RelateComputer::copyNodesAndLabels(uint8_t argIndex)
 void
 RelateComputer::computeIntersectionNodes(uint8_t argIndex)
 {
-    std::vector* edges = (*arg)[argIndex]->getEdges();
+    std::vector* edges = arg[argIndex]->getEdges();
     for(Edge* e: *edges) {
         Location eLoc = e->getLabel().getLocation(argIndex);
         const EdgeIntersectionList& eiL = e->getEdgeIntersectionList();
@@ -361,10 +361,10 @@ RelateComputer::computeIntersectionNodes(uint8_t argIndex)
 void
 RelateComputer::labelIntersectionNodes(uint8_t argIndex)
 {
-    std::vector* edges = (*arg)[argIndex]->getEdges();
-    for(Edge* e: *edges) {
+    const std::vector* edges = arg[argIndex]->getEdges();
+    for(const Edge* e: *edges) {
         Location eLoc = e->getLabel().getLocation(argIndex);
-        EdgeIntersectionList& eiL = e->getEdgeIntersectionList();
+        const EdgeIntersectionList& eiL = e->getEdgeIntersectionList();
 
         for(const EdgeIntersection& ei : eiL) {
             RelateNode* n = static_cast(nodes.find(ei.coord));
@@ -384,12 +384,12 @@ RelateComputer::labelIntersectionNodes(uint8_t argIndex)
 void
 RelateComputer::computeDisjointIM(IntersectionMatrix* imX, const algorithm::BoundaryNodeRule& boundaryNodeRule)
 {
-    const Geometry* ga = (*arg)[0]->getGeometry();
+    const Geometry* ga = arg[0]->getGeometry();
     if(!ga->isEmpty()) {
         imX->set(Location::INTERIOR, Location::EXTERIOR, ga->getDimension());
         imX->set(Location::BOUNDARY, Location::EXTERIOR, getBoundaryDim(*ga, boundaryNodeRule));
     }
-    const Geometry* gb = (*arg)[1]->getGeometry();
+    const Geometry* gb = arg[1]->getGeometry();
     if(!gb->isEmpty()) {
         imX->set(Location::EXTERIOR, Location::INTERIOR, gb->getDimension());
         imX->set(Location::EXTERIOR, Location::BOUNDARY, getBoundaryDim(*gb, boundaryNodeRule));
@@ -453,10 +453,10 @@ RelateComputer::updateIM(IntersectionMatrix& imX)
 void
 RelateComputer::labelIsolatedEdges(uint8_t thisIndex, uint8_t targetIndex)
 {
-    std::vector* edges = (*arg)[thisIndex]->getEdges();
+    std::vector* edges = arg[thisIndex]->getEdges();
     for(Edge* e: *edges) {
         if(e->isIsolated()) {
-            labelIsolatedEdge(e, targetIndex, (*arg)[targetIndex]->getGeometry());
+            labelIsolatedEdge(e, targetIndex, arg[targetIndex]->getGeometry());
             isolatedEdges.push_back(e);
         }
     }
@@ -505,7 +505,7 @@ void
 RelateComputer::labelIsolatedNode(Node* n, uint8_t targetIndex)
 {
     Location loc = ptLocator.locate(n->getCoordinate(),
-                               (*arg)[targetIndex]->getGeometry());
+                               arg[targetIndex]->getGeometry());
     n->getLabel().setAllLocations(targetIndex, loc);
     //debugPrintln(n.getLabel());
 }
diff --git a/deps/libgeos/geos/src/operation/relate/RelateOp.cpp b/deps/libgeos/geos/src/operation/relate/RelateOp.cpp
index 719cddb7f..739b6d4cc 100644
--- a/deps/libgeos/geos/src/operation/relate/RelateOp.cpp
+++ b/deps/libgeos/geos/src/operation/relate/RelateOp.cpp
@@ -52,7 +52,7 @@ RelateOp::relate(const Geometry* a, const Geometry* b,
 
 RelateOp::RelateOp(const Geometry* g0, const Geometry* g1):
     GeometryGraphOperation(g0, g1),
-    relateComp(&arg)
+    relateComp(arg)
 {
 }
 
@@ -60,7 +60,7 @@ RelateOp::RelateOp(const Geometry* g0, const Geometry* g1,
                    const algorithm::BoundaryNodeRule& boundaryNodeRule)
     :
     GeometryGraphOperation(g0, g1, boundaryNodeRule),
-    relateComp(&arg)
+    relateComp(arg)
 {
 }
 
diff --git a/deps/libgeos/geos/src/operation/relateng/AdjacentEdgeLocator.cpp b/deps/libgeos/geos/src/operation/relateng/AdjacentEdgeLocator.cpp
new file mode 100644
index 000000000..aeb5f8084
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/AdjacentEdgeLocator.cpp
@@ -0,0 +1,159 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+using geos::algorithm::Orientation;
+using geos::algorithm::PointLocation;
+using geos::geom::CoordinateXY;
+using geos::geom::Dimension;
+using geos::geom::Geometry;
+using geos::geom::LinearRing;
+using geos::geom::Location;
+using geos::geom::Polygon;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public */
+Location
+AdjacentEdgeLocator::locate(const CoordinateXY* p)
+{
+    NodeSections sections(p);
+    for (const CoordinateSequence* ring : ringList) {
+        addSections(p, ring, sections);
+    }
+    std::unique_ptr node = sections.createNode();
+
+    return node->hasExteriorEdge(true) ? Location::BOUNDARY : Location::INTERIOR;
+}
+
+
+/* private */
+void
+AdjacentEdgeLocator::addSections(
+    const CoordinateXY* p,
+    const CoordinateSequence* ring,
+    NodeSections& sections)
+{
+    for (std::size_t i = 0; i < ring->getSize() - 1; i++) {
+        const CoordinateXY& p0 = ring->getAt(i);
+        const CoordinateXY& pnext = ring->getAt(i+1);
+        if (p->equals2D(pnext)) {
+            //-- segment final point is assigned to next segment
+            continue;
+        }
+        else if (p->equals2D(p0)) {
+            std::size_t iprev = i > 0 ? i - 1 : ring->getSize() - 2;
+            const CoordinateXY& pprev = ring->getAt(iprev);
+            NodeSection *ns = createSection(p, &pprev, &pnext);
+            sections.addNodeSection(ns);
+        }
+        else if (PointLocation::isOnSegment(*p, p0, pnext)) {
+            NodeSection *ns = createSection(p, &p0, &pnext);
+            sections.addNodeSection(ns);
+        }
+    }
+}
+
+
+/* private */
+NodeSection*
+AdjacentEdgeLocator::createSection(const CoordinateXY* p,
+    const CoordinateXY* prev,
+    const CoordinateXY* next)
+{
+    if (prev->distance(*p) == 0 || next->distance(*p) == 0) {
+        //System.out.println("Found zero-length section segment");
+    };
+    return new NodeSection(true, Dimension::A, 1, 0, nullptr, false, prev, *p, next);
+}
+
+
+/* private */
+void
+AdjacentEdgeLocator::init(const Geometry* geom)
+{
+    if (geom->isEmpty())
+        return;
+    addRings(geom);
+}
+
+
+/* private */
+void
+AdjacentEdgeLocator::addRings(const Geometry* geom)
+{
+    if (const Polygon* poly = dynamic_cast(geom)) {
+        const LinearRing* shell = poly->getExteriorRing();
+        addRing(shell, true);
+        for (std::size_t i = 0; i < poly->getNumInteriorRing(); i++) {
+            const LinearRing* hole = poly->getInteriorRingN(i);
+            addRing(hole, false);
+        }
+    }
+    else if (geom->isCollection()) {
+        //-- recurse through collections
+        for (std::size_t i = 0; i < geom->getNumGeometries(); i++) {
+            addRings(geom->getGeometryN(i));
+        }
+    }
+}
+
+
+/* private */
+void
+AdjacentEdgeLocator::addRing(const LinearRing* ring, bool requireCW)
+{
+    //TODO: remove repeated points?
+    const CoordinateSequence* pts = ring->getCoordinatesRO();
+    bool isFlipped = requireCW == Orientation::isCCW(pts);
+    /*
+     * In case of flipped rings, we need to keep a local copy
+     * since we cannot mutate the const geometry we are fed
+     * in the constructor.
+     */
+    if (isFlipped) {
+        std::unique_ptr localPts = pts->clone();
+        localPts->reverse();
+        ringList.push_back(localPts.get());
+        localRingList.push_back(std::move(localPts));
+    }
+    else {
+        ringList.push_back(pts);
+    }
+    return;
+}
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
diff --git a/deps/libgeos/geos/src/operation/relateng/BasicPredicate.cpp b/deps/libgeos/geos/src/operation/relateng/BasicPredicate.cpp
new file mode 100644
index 000000000..c225de6fe
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/BasicPredicate.cpp
@@ -0,0 +1,137 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+
+using geos::geom::Envelope;
+using geos::geom::Location;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+
+/* private static */
+bool
+BasicPredicate::isKnown(int val)
+{
+     return val > UNKNOWN;
+}
+
+/* private static */
+bool
+BasicPredicate::toBoolean(int val)
+{
+    return val == TRUE;
+}
+
+/* private static */
+int
+BasicPredicate::toValue(bool val)
+{
+    return val ? TRUE : FALSE;
+}
+
+
+/* public static */
+bool
+BasicPredicate::isIntersection(Location locA, Location locB)
+{
+    //-- i.e. some location on both geometries intersects
+    return locA != Location::EXTERIOR && locB != Location::EXTERIOR;
+}
+
+
+// /* public */
+// bool isSelfNodingRequired() {
+//     return false;
+//   }
+
+
+/* public override */
+bool
+BasicPredicate::isKnown() const
+{
+    return isKnown(m_value);
+}
+
+/* public override */
+bool
+BasicPredicate::value() const
+{
+    return toBoolean(m_value);
+}
+
+
+/* protected */
+void
+BasicPredicate::setValue(bool val)
+{
+    //-- don't change already-known value
+    if (isKnown())
+        return;
+    m_value = toValue(val);
+}
+
+/* protected */
+void
+BasicPredicate::setValue(int val)
+{
+    //-- don't change already-known value
+    if (isKnown())
+        return;
+    m_value = val;
+}
+
+
+/* protected */
+void
+BasicPredicate::setValueIf(bool val, bool cond)
+{
+    if (cond)
+        setValue(val);
+}
+
+/* protected */
+void
+BasicPredicate::require(bool cond)
+{
+    if (! cond)
+        setValue(false);
+}
+
+/* protected */
+void
+BasicPredicate::requireCovers(const Envelope& a, const Envelope& b)
+{
+    require(a.covers(b));
+}
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/DimensionLocation.cpp b/deps/libgeos/geos/src/operation/relateng/DimensionLocation.cpp
new file mode 100644
index 000000000..50a3457d8
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/DimensionLocation.cpp
@@ -0,0 +1,125 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+
+
+using geos::geom::Location;
+using geos::geom::Dimension;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public static */
+int
+DimensionLocation::locationArea(Location loc)
+{
+    switch (loc) {
+        case Location::INTERIOR: return AREA_INTERIOR;
+        case Location::BOUNDARY: return AREA_BOUNDARY;
+        default:
+            return EXTERIOR;
+    }
+}
+
+
+/* public static */
+int
+DimensionLocation::locationLine(Location loc)
+{
+    switch (loc) {
+        case Location::INTERIOR: return LINE_INTERIOR;
+        case Location::BOUNDARY: return LINE_BOUNDARY;
+        default:
+            return EXTERIOR;
+    }
+}
+
+
+/* public static */
+int
+DimensionLocation::locationPoint(Location loc)
+{
+    switch (loc) {
+        case Location::INTERIOR: return POINT_INTERIOR;
+        default:
+            return EXTERIOR;
+    }
+}
+
+
+/* public static */
+Location
+DimensionLocation::location(int dimLoc)
+{
+    switch (dimLoc) {
+        case POINT_INTERIOR:
+        case LINE_INTERIOR:
+        case AREA_INTERIOR:
+            return Location::INTERIOR;
+        case LINE_BOUNDARY:
+        case AREA_BOUNDARY:
+            return Location::BOUNDARY;
+        default:
+            return Location::EXTERIOR;
+    }
+}
+
+
+/* public static */
+int
+DimensionLocation::dimension(int dimLoc)
+{
+    switch (dimLoc) {
+        case POINT_INTERIOR:
+            return Dimension::P;
+        case LINE_INTERIOR:
+        case LINE_BOUNDARY:
+            return Dimension::L;
+        case AREA_INTERIOR:
+        case AREA_BOUNDARY:
+            return Dimension::A;
+        default:
+            return Dimension::False;
+    }
+}
+
+
+/* public static */
+int
+DimensionLocation::dimension(int dimLoc, int exteriorDim)
+{
+    if (dimLoc == EXTERIOR)
+        return exteriorDim;
+    else
+        return dimension(dimLoc);
+}
+
+
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/EdgeSegmentIntersector.cpp b/deps/libgeos/geos/src/operation/relateng/EdgeSegmentIntersector.cpp
new file mode 100644
index 000000000..8ea3c5205
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/EdgeSegmentIntersector.cpp
@@ -0,0 +1,114 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+using geos::geom::CoordinateXYZM;
+using geos::geom::CoordinateXY;
+using geos::geom::Geometry;
+using geos::noding::SegmentString;
+using geos::index::chain::MonotoneChain;
+using geos::index::chain::MonotoneChainBuilder;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public override */
+bool
+EdgeSegmentIntersector::isDone() const
+{
+    return topoComputer.isResultKnown();
+}
+
+
+/* public override */
+void
+EdgeSegmentIntersector::processIntersections(
+    SegmentString* ss0, std::size_t segIndex0,
+    SegmentString* ss1, std::size_t segIndex1)
+{
+    // don't intersect a segment with itself
+    if (ss0 == ss1 && segIndex0 == segIndex1)
+        return;
+
+    RelateSegmentString* rss0 = static_cast(ss0);
+    RelateSegmentString* rss1 = static_cast(ss1);
+    //TODO: move this ordering logic to TopologyBuilder
+    if (rss0->isA()) {
+        addIntersections(rss0, segIndex0, rss1, segIndex1);
+    }
+    else {
+        addIntersections(rss1, segIndex1, rss0, segIndex0);
+    }
+}
+
+
+/* private */
+void
+EdgeSegmentIntersector::addIntersections(
+    RelateSegmentString* ssA, std::size_t segIndexA,
+    RelateSegmentString* ssB, std::size_t segIndexB)
+{
+
+    const CoordinateXY& a0 = ssA->getCoordinate(segIndexA);
+    const CoordinateXY& a1 = ssA->getCoordinate(segIndexA + 1);
+    const CoordinateXY& b0 = ssB->getCoordinate(segIndexB);
+    const CoordinateXY& b1 = ssB->getCoordinate(segIndexB + 1);
+
+    li.computeIntersection(a0, a1, b0, b1);
+
+    if (! li.hasIntersection())
+        return;
+
+    for (uint32_t i = 0; i < li.getIntersectionNum(); i++)
+    {
+        const CoordinateXYZM& intPtXYZM = li.getIntersection(i);
+        CoordinateXY intPt(intPtXYZM.x, intPtXYZM.y);
+        /**
+         * Ensure endpoint intersections are added once only, for their canonical segments.
+         * Proper intersections lie on a unique segment so do not need to be checked.
+         * And it is important that the Containing Segment check not be used,
+         * since due to intersection computation roundoff,
+         * it is not reliable in that situation.
+         */
+        if (li.isProper() ||
+            (ssA->isContainingSegment(segIndexA, &intPt) &&
+             ssB->isContainingSegment(segIndexB, &intPt)))
+        {
+            NodeSection* nsa = ssA->createNodeSection(segIndexA, intPt);
+            NodeSection* nsb = ssB->createNodeSection(segIndexB, intPt);
+            topoComputer.addIntersection(nsa, nsb);
+        }
+    }
+}
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/EdgeSegmentOverlapAction.cpp b/deps/libgeos/geos/src/operation/relateng/EdgeSegmentOverlapAction.cpp
new file mode 100644
index 000000000..cd32bca78
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/EdgeSegmentOverlapAction.cpp
@@ -0,0 +1,53 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2001-2002 Vivid Solutions Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************
+ *
+ * Last port: index/chain/MonotoneChainOverlapAction.java rev. 1.6 (JTS-1.10)
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+//#include 
+
+using geos::index::chain::MonotoneChain;
+using geos::noding::SegmentString;
+using geos::noding::SegmentIntersector;
+
+namespace geos {
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public override */
+void
+EdgeSegmentOverlapAction::overlap(
+    const MonotoneChain& mc1, std::size_t start1,
+    const MonotoneChain& mc2, std::size_t start2)
+{
+    SegmentString* ss1 = static_cast(mc1.getContext());
+    SegmentString* ss2 = static_cast(mc2.getContext());
+    si.processIntersections(ss1, start1, ss2, start2);
+}
+
+
+} // namespace geos.index.chain
+} // namespace geos.index
+} // namespace geos
diff --git a/deps/libgeos/geos/src/operation/relateng/EdgeSetIntersector.cpp b/deps/libgeos/geos/src/operation/relateng/EdgeSetIntersector.cpp
new file mode 100644
index 000000000..5a33ef387
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/EdgeSetIntersector.cpp
@@ -0,0 +1,92 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+using geos::geom::Geometry;
+using geos::geom::Envelope;
+using geos::noding::SegmentString;
+using geos::index::chain::MonotoneChain;
+using geos::index::chain::MonotoneChainBuilder;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* private */
+void
+EdgeSetIntersector::addEdges(std::vector& segStrings)
+{
+    for (const SegmentString* ss : segStrings) {
+        addToIndex(ss);
+    }
+}
+
+/* private */
+void
+EdgeSetIntersector::addToIndex(const SegmentString* segStr)
+{
+    std::vector segChains;
+    MonotoneChainBuilder::getChains(segStr->getCoordinates(), const_cast(segStr), segChains);
+
+    for (MonotoneChain& mc : segChains) {
+        if (envelope == nullptr || envelope->intersects(mc.getEnvelope())) {
+            // mc.setId(idCounter++);
+            monoChains.push_back(mc);
+            MonotoneChain* mcPtr = &(monoChains.back());
+            index.insert(mcPtr->getEnvelope(), mcPtr);
+        }
+    }
+}
+
+/* public */
+void
+EdgeSetIntersector::process(EdgeSegmentIntersector& intersector)
+{
+    EdgeSegmentOverlapAction overlapAction(intersector);
+
+    // Replaces JTS implementation that manually iterates on the
+    // monoChains with the automatic queryPairs method in TemplateSTRTree
+    index.queryPairs([this, &overlapAction, &intersector](const MonotoneChain* queryChain, const MonotoneChain* testChain) {
+
+        if (overlapCounter++ % 100000 == 0)
+            GEOS_CHECK_FOR_INTERRUPTS();
+
+        testChain->computeOverlaps(queryChain, &overlapAction);
+
+        return !intersector.isDone();
+    });
+
+}
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/IMPatternMatcher.cpp b/deps/libgeos/geos/src/operation/relateng/IMPatternMatcher.cpp
new file mode 100644
index 000000000..d70c88044
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/IMPatternMatcher.cpp
@@ -0,0 +1,149 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+
+#include 
+
+
+using geos::geom::Envelope;
+using geos::geom::Location;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public */
+std::string
+IMPatternMatcher::name() const
+{
+    return "IMPattern";
+}
+
+
+/* public */
+void
+IMPatternMatcher::init(const Envelope& envA, const Envelope& envB)
+{
+    IMPredicate::init(dimA, dimB);
+    //-- if pattern specifies any non-E/non-E interaction, envelopes must not be disjoint
+    bool requiresInteraction = requireInteraction(patternMatrix);
+    bool isDisjoint = envA.disjoint(&envB);
+    setValueIf(false, requiresInteraction && isDisjoint);
+}
+
+
+/* public */
+bool
+IMPatternMatcher::requireInteraction() const
+{
+    return requireInteraction(patternMatrix);
+}
+
+
+/* private static */
+bool
+IMPatternMatcher::requireInteraction(const IntersectionMatrix& im)
+{
+    bool requiresInteraction =
+        isInteraction(im.get(Location::INTERIOR, Location::INTERIOR)) ||
+        isInteraction(im.get(Location::INTERIOR, Location::BOUNDARY)) ||
+        isInteraction(im.get(Location::BOUNDARY, Location::INTERIOR)) ||
+        isInteraction(im.get(Location::BOUNDARY, Location::BOUNDARY));
+    return requiresInteraction;
+}
+
+
+/* private static */
+bool
+IMPatternMatcher::isInteraction(int imDim)
+{
+    return imDim == Dimension::True || imDim >= Dimension::P;
+}
+
+
+/* public */
+bool
+IMPatternMatcher::isDetermined() const
+{
+    /**
+     * Matrix entries only increase in dimension as topology is computed.
+     * The predicate can be short-circuited (as false) if
+     * any computed entry is greater than the mask value.
+     */
+    std::array locs = {
+        Location::INTERIOR, Location::BOUNDARY, Location::EXTERIOR};
+
+    for (Location i : locs) {
+        for (Location j : locs) {
+            int patternEntry = patternMatrix.get(i, j);
+
+            if (patternEntry == Dimension::DONTCARE)
+                continue;
+
+            int matrixVal = getDimension(i, j);
+
+            //-- mask entry TRUE requires a known matrix entry
+            if (patternEntry == Dimension::True) {
+                if (matrixVal < 0)
+                    return false;
+            }
+            //-- result is known (false) if matrix entry has exceeded mask
+            else if (matrixVal > patternEntry)
+                return true;
+        }
+    }
+    return false;
+}
+
+
+/* public */
+bool
+IMPatternMatcher::valueIM()
+{
+    bool val = intMatrix.matches(imPattern);
+    return val;
+}
+
+
+/* public */
+std::string
+IMPatternMatcher::toString() const
+{
+    return name() + "(" + imPattern + ")";
+}
+
+
+/* public friend */
+std::ostream&
+operator<<(std::ostream& os, const IMPatternMatcher& imp)
+{
+    os << imp.toString();
+    return os;
+}
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/IMPredicate.cpp b/deps/libgeos/geos/src/operation/relateng/IMPredicate.cpp
new file mode 100644
index 000000000..3b4411d73
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/IMPredicate.cpp
@@ -0,0 +1,155 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+
+using geos::geom::Envelope;
+using geos::geom::Location;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public static */
+bool
+IMPredicate::isDimsCompatibleWithCovers(int dim0, int dim1)
+{
+    //- allow Points coveredBy zero-length Lines
+    if (dim0 == Dimension::P && dim1 == Dimension::L)
+        return true;
+    return dim0 >= dim1;
+}
+
+
+/* public */
+void
+IMPredicate::init(int dA, int dB)
+{
+    dimA = dA;
+    dimB = dB;
+}
+
+
+/* public */
+void
+IMPredicate::updateDimension(Location locA, Location locB, int dimension)
+{
+    //-- only record an increased dimension value
+    if (isDimChanged(locA, locB, dimension)) {
+        intMatrix.set(locA, locB, dimension);
+        //-- set value if predicate value can be known
+        if (isDetermined()) {
+            setValue(valueIM());
+        }
+    }
+}
+
+
+/* public */
+bool
+IMPredicate::isDimChanged(Location locA, Location locB, int dimension) const
+{
+    return dimension > intMatrix.get(locA, locB);
+}
+
+
+/* protected */
+bool
+IMPredicate::intersectsExteriorOf(bool isA) const
+{
+    if (isA) {
+        return isIntersects(Location::EXTERIOR, Location::INTERIOR)
+            || isIntersects(Location::EXTERIOR, Location::BOUNDARY);
+    }
+    else {
+        return isIntersects(Location::INTERIOR, Location::EXTERIOR)
+            || isIntersects(Location::BOUNDARY, Location::EXTERIOR);
+    }
+}
+
+
+/* protected */
+bool
+IMPredicate::isIntersects(Location locA, Location locB) const
+{
+    return intMatrix.get(locA, locB) >= Dimension::P;
+}
+
+
+/* public */
+bool
+IMPredicate::isKnown(Location locA, Location locB) const
+{
+    return intMatrix.get(locA, locB) != DIM_UNKNOWN;
+}
+
+
+/* public */
+bool
+IMPredicate::isDimension(Location locA, Location locB, int dimension) const
+{
+    return intMatrix.get(locA, locB) == dimension;
+}
+
+
+/* public */
+int
+IMPredicate::getDimension(Location locA, Location locB) const
+{
+    return intMatrix.get(locA, locB);
+}
+
+
+/* public */
+void
+IMPredicate::finish()
+{
+    setValue(valueIM());
+}
+
+
+/* public */
+std::string
+IMPredicate::toString() const
+{
+    return name() + ": " + intMatrix.toString();
+}
+
+
+/* public friend */
+std::ostream&
+operator<<(std::ostream& os, const IMPredicate& imp)
+{
+    os << imp.toString() << " " << imp.intMatrix;
+    return os;
+}
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/LineStringExtracter.cpp b/deps/libgeos/geos/src/operation/relateng/LineStringExtracter.cpp
new file mode 100644
index 000000000..b1c229cba
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/LineStringExtracter.cpp
@@ -0,0 +1,87 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+using geos::geom::Geometry;
+using geos::geom::GeometryFactory;
+using geos::geom::LineString;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public static */
+void
+LineStringExtracter::getLines(const Geometry* geom, std::vector& lines)
+{
+    if (geom->getGeometryTypeId() == geom::GEOS_LINESTRING) {
+        lines.push_back(static_cast(geom));
+    }
+    else if (geom->isCollection()) {
+        LineStringExtracter lse(lines);
+        geom->apply_ro(&lse);
+    }
+    // skip non-LineString elemental geometries
+
+    return;
+}
+
+
+/* public static */
+std::vector
+LineStringExtracter::getLines(const Geometry* geom)
+{
+    std::vector lines;
+    getLines(geom, lines);
+    return lines;
+}
+
+
+/* public static */
+// std::unique_ptr
+// LineStringExtracter::getGeometry(const Geometry* geom)
+// {
+//     std::vector lines;
+//     getLines(geom, lines);
+//     return geom->getFactory()->buildGeometry(lines);
+// }
+
+
+/* public */
+void
+LineStringExtracter::filter_ro(const Geometry* geom)
+{
+    if (geom->getGeometryTypeId() == geom::GEOS_LINESTRING)
+        comps.push_back(static_cast(geom));
+}
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/LinearBoundary.cpp b/deps/libgeos/geos/src/operation/relateng/LinearBoundary.cpp
new file mode 100644
index 000000000..c97d6b84b
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/LinearBoundary.cpp
@@ -0,0 +1,114 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+using geos::algorithm::BoundaryNodeRule;
+using geos::geom::CoordinateXY;
+using geos::geom::CoordinateSequence;
+using geos::geom::LineString;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+LinearBoundary::LinearBoundary(std::vector& lines, const BoundaryNodeRule& bnRule)
+    : m_boundaryNodeRule(bnRule)
+{
+    //assert: dim(geom) == 1
+    computeBoundaryPoints(lines, m_vertexDegree);
+    m_hasBoundary = checkBoundary(m_vertexDegree);
+}
+
+/* private */
+bool
+LinearBoundary::checkBoundary(Coordinate::ConstIntMap& vertexDegree) const
+{
+    // Iterate over the map and test the values
+    for (const auto& pair : vertexDegree) {
+        int degree = pair.second;
+        if (m_boundaryNodeRule.isInBoundary(degree)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/* public */
+bool
+LinearBoundary::hasBoundary() const
+{
+    return m_hasBoundary;
+}
+
+/* public */
+bool
+LinearBoundary::isBoundary(const CoordinateXY* pt) const
+{
+    auto it = m_vertexDegree.find(pt);
+    if (it == m_vertexDegree.end())
+        return false;
+
+    int degree = it->second;
+    return m_boundaryNodeRule.isInBoundary(degree);
+}
+
+/* private static */
+void
+LinearBoundary::computeBoundaryPoints(std::vector& lines, Coordinate::ConstIntMap& vertexDegree)
+{
+    for (const LineString* line : lines) {
+        if (line->isEmpty())
+            continue;
+        const CoordinateSequence* cs = line->getCoordinatesRO();
+        const Coordinate& cs0 = cs->getAt(0);
+        const Coordinate& csn = cs->getAt(line->getNumPoints() - 1);
+        addEndpoint(&cs0, vertexDegree);
+        addEndpoint(&csn, vertexDegree);
+    }
+}
+
+/* private static */
+void
+LinearBoundary::addEndpoint(const CoordinateXY *p, Coordinate::ConstIntMap& vertexDegree)
+{
+    int dim = 0;
+    auto it = vertexDegree.find(p);
+
+    if (it != vertexDegree.end()) {
+        dim = it->second;
+    }
+    vertexDegree[p] = dim + 1;
+}
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/NodeSection.cpp b/deps/libgeos/geos/src/operation/relateng/NodeSection.cpp
new file mode 100644
index 000000000..d8d48f544
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/NodeSection.cpp
@@ -0,0 +1,253 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+using geos::geom::Coordinate;
+using geos::geom::CoordinateXY;
+using geos::geom::Geometry;
+using geos::geom::Dimension;
+using geos::io::WKTWriter;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public */
+const CoordinateXY *
+NodeSection::getVertex(int i) const
+{
+    return i == 0 ? m_v0 : m_v1;
+}
+
+
+/* public */
+const CoordinateXY &
+NodeSection::nodePt() const
+{
+    return m_nodePt;
+}
+
+
+/* public */
+int
+NodeSection::dimension() const
+{
+    return m_dim;
+}
+
+
+/* public */
+int
+NodeSection::id() const
+{
+    return m_id;
+}
+
+
+/* public */
+int
+NodeSection::ringId() const
+{
+    return m_ringId;
+}
+
+
+/* public */
+const Geometry *
+NodeSection::getPolygonal() const
+{
+    return m_poly;
+}
+
+
+/* public */
+bool
+NodeSection::isShell() const
+{
+    return m_ringId == 0;
+}
+
+
+/* public */
+bool
+NodeSection::isArea() const
+{
+    return m_dim == Dimension::A;
+}
+
+
+/* public static */
+bool
+NodeSection::isAreaArea(const NodeSection& a, const NodeSection& b)
+{
+    return a.dimension() == Dimension::A && b.dimension() == Dimension::A;
+}
+
+
+/* public */
+bool
+NodeSection::isA() const
+{
+     return m_isA;
+}
+
+
+/* public */
+bool
+NodeSection::isSameGeometry(const NodeSection& ns) const
+{
+    return isA() == ns.isA();
+}
+
+
+/* public */
+bool
+NodeSection::isSamePolygon(const NodeSection& ns) const
+{
+    return isA() == ns.isA() && id() == ns.id();
+}
+
+
+/* public */
+bool
+NodeSection::isNodeAtVertex() const
+{
+    return m_isNodeAtVertex;
+}
+
+
+/* public */
+bool
+NodeSection::isProper() const
+{
+    return ! m_isNodeAtVertex;
+}
+
+
+/* public static */
+bool
+NodeSection::isProper(const NodeSection& a, const NodeSection& b)
+{
+    return a.isProper() && b.isProper();
+}
+
+
+/* public static */
+std::string
+NodeSection::edgeRep(const CoordinateXY* p0, const CoordinateXY* p1)
+{
+    if (p0 == nullptr || p1 == nullptr)
+        return "null";
+    return WKTWriter::toLineString(*p0, *p1);
+}
+
+
+/* private */
+int
+NodeSection::compare(int a, int b)
+{
+    if (a < b) return -1;
+    if (a > b) return 1;
+    return 0;
+}
+
+
+/* public */
+int
+NodeSection::compareTo(const NodeSection& o) const
+{
+    // Assert: nodePt.equals2D(o.nodePt())
+
+    // sort A before B
+    if (m_isA != o.m_isA) {
+        if (m_isA) return -1;
+        return 1;
+    }
+    //-- sort on dimensions
+    int compDim = compare(m_dim,  o.m_dim);
+    if (compDim != 0) return compDim;
+
+    //-- sort on id and ring id
+    int compId = compare(m_id, o.m_id);
+    if (compId != 0) return compId;
+
+    int compRingId = compare(m_ringId, o.m_ringId);
+    if (compRingId != 0) return compRingId;
+
+    //-- sort on edge coordinates
+    int compV0 = compareWithNull(m_v0, o.m_v0);
+    if (compV0 != 0) return compV0;
+
+    return compareWithNull(m_v1, o.m_v1);
+}
+
+
+/* private static */
+int
+NodeSection::compareWithNull(const CoordinateXY* v0, const CoordinateXY* v1)
+{
+    if (v0 == nullptr) {
+        if (v1 == nullptr)
+            return 0;
+        //-- null is lower than non-null
+        return -1;
+    }
+    // v0 is non-null
+    if (v1 == nullptr)
+        return 1;
+    return v0->compareTo(*v1);
+}
+
+
+/* public */
+std::string
+NodeSection::toString() const
+{
+    // TODO, port RelateGeometry
+    // os << RelateGeometry::name(m_isA);
+    std::stringstream ss;
+    ss << m_dim;
+    if (m_id >= 0) {
+        ss << "[" << m_id << ":" << m_ringId << "]";
+    }
+    ss << ": " << edgeRep(m_v0, &m_nodePt);
+    ss << (m_isNodeAtVertex ? "-V-" : "---");
+    ss << " " << edgeRep(&m_nodePt, m_v1);
+    return ss.str();
+}
+
+/* public friend */
+std::ostream&
+operator<<(std::ostream& os, const NodeSection& ns)
+{
+    os << ns.toString();
+    return os;
+}
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/NodeSections.cpp b/deps/libgeos/geos/src/operation/relateng/NodeSections.cpp
new file mode 100644
index 000000000..e02433d32
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/NodeSections.cpp
@@ -0,0 +1,163 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+
+using geos::geom::CoordinateXY;
+using geos::geom::Geometry;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+/* public */
+const CoordinateXY*
+NodeSections::getCoordinate() const
+{
+    return nodePt;
+}
+
+
+/* public */
+void
+NodeSections::addNodeSection(NodeSection* e)
+{
+    //System.out.println(e);
+    sections.emplace_back(e);
+}
+
+
+/* public */
+bool
+NodeSections::hasInteractionAB() const
+{
+    bool isA = false;
+    bool isB = false;
+    for (const std::unique_ptr& ns : sections) {
+        if (ns->isA())
+            isA = true;
+        else
+            isB = true;
+
+        if (isA && isB)
+            return true;
+    }
+    return false;
+}
+
+
+/* public */
+const Geometry*
+NodeSections::getPolygonal(bool isA) const
+{
+    for (const std::unique_ptr& ns : sections) {
+        if (ns->isA() == isA) {
+            const Geometry* poly = ns->getPolygonal();
+            if (poly != nullptr)
+                return poly;
+        }
+    }
+    return nullptr;
+}
+
+
+/* public */
+std::unique_ptr
+NodeSections::createNode()
+{
+    prepareSections();
+
+    std::unique_ptr node(new RelateNode(nodePt));
+    std::size_t i = 0;
+    while (i < sections.size()) {
+        const std::unique_ptr& ns = sections[i];
+        //-- if there multiple polygon sections incident at node convert them to maximal-ring structure
+        if (ns->isArea() && hasMultiplePolygonSections(sections, i)) {
+            std::vector polySections = collectPolygonSections(sections, i);
+            std::vector> nsConvert = PolygonNodeConverter::convert(polySections);
+            node->addEdges(nsConvert);
+            i += polySections.size();
+        }
+        else {
+            //-- the most common case is a line or a single polygon ring section
+            node->addEdges(ns.get());
+            i += 1;
+        }
+    }
+    return node;
+}
+
+
+/* private */
+void
+NodeSections::prepareSections()
+{
+    // Comparator lambda for sort support
+    auto comparator = [](
+        const std::unique_ptr& a,
+        const std::unique_ptr& b)
+    {
+        return a->compareTo(*b) < 0;
+    };
+
+    std::sort(sections.begin(), sections.end(), comparator);
+    //TODO: remove duplicate sections
+}
+
+
+/* private static */
+bool
+NodeSections::hasMultiplePolygonSections(
+    std::vector>& sections,
+    std::size_t i)
+{
+    //-- if last section can only be one
+    if (i >= sections.size() - 1)
+        return false;
+    //-- check if there are at least two sections for same polygon
+    std::unique_ptr& ns = sections[i];
+    std::unique_ptr& nsNext = sections[i + 1];
+    return ns->isSamePolygon(*nsNext);
+}
+
+
+/* private static */
+std::vector
+NodeSections::collectPolygonSections(
+    std::vector>& sections,
+    std::size_t i)
+{
+    std::vector polySections;
+    //-- note ids are only unique to a geometry
+    std::unique_ptr& polySection = sections[i];
+    while (i < sections.size() &&
+        polySection->isSamePolygon(*(sections[i])))
+    {
+        polySections.push_back(sections[i].get());
+        i++;
+    }
+    return polySections;
+}
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/PolygonNodeConverter.cpp b/deps/libgeos/geos/src/operation/relateng/PolygonNodeConverter.cpp
new file mode 100644
index 000000000..edc582c32
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/PolygonNodeConverter.cpp
@@ -0,0 +1,193 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+
+using geos::algorithm::PolygonNodeTopology;
+using geos::geom::CoordinateXY;
+using geos::geom::Dimension;
+// using geos::geom::Location;
+// using geos::geom::Position;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public static */
+std::vector>
+PolygonNodeConverter::convert(std::vector& polySections)
+{
+    auto comparator = [](
+        const NodeSection* ns1,
+        const NodeSection* ns2)
+    {
+        int comp = PolygonNodeTopology::compareAngle(
+            &(ns1->nodePt()),
+            ns1->getVertex(0),
+            ns2->getVertex(0));
+        return comp < 0;
+    };
+
+    std::sort(polySections.begin(), polySections.end(), comparator);
+    // polySections.sort(new NodeSection.EdgeAngleComparator());
+
+    //TODO: move uniquing up to caller
+    std::vector sections = extractUnique(polySections);
+    if (sections.size() == 1) {
+        std::vector> nss;
+        nss.emplace_back(new NodeSection(sections[0]));
+        return nss;
+    }
+
+    //-- find shell section index
+    std::size_t shellIndex = findShell(sections);
+    if (shellIndex == INDEX_UNKNOWN) {
+        return convertHoles(sections);
+    }
+    //-- at least one shell is present.  Handle multiple ones if present
+    std::vector> convertedSections;
+    std::size_t nextShellIndex = shellIndex;
+    do {
+        nextShellIndex = convertShellAndHoles(
+            sections, nextShellIndex, convertedSections);
+    } while (nextShellIndex != shellIndex);
+
+    return convertedSections;
+}
+
+
+/* private static */
+std::size_t
+PolygonNodeConverter::convertShellAndHoles(
+    std::vector& sections,
+    std::size_t shellIndex,
+    std::vector>& convertedSections)
+{
+    const NodeSection* shellSection = sections[shellIndex];
+    const CoordinateXY* inVertex = shellSection->getVertex(0);
+    std::size_t i = next(sections, shellIndex);
+    const NodeSection* holeSection = nullptr;
+    while (! sections[i]->isShell()) {
+        holeSection = sections[i];
+        // Assert: holeSection.isShell() = false
+        const CoordinateXY* outVertex = holeSection->getVertex(1);
+        NodeSection* ns = createSection(shellSection, inVertex, outVertex);
+        convertedSections.emplace_back(ns);
+        inVertex = holeSection->getVertex(0);
+        i = next(sections, i);
+    }
+    //-- create final section for corner from last hole to shell
+    const CoordinateXY* outVertex = shellSection->getVertex(1);
+    NodeSection* ns = createSection(shellSection, inVertex, outVertex);
+    convertedSections.emplace_back(ns);
+    return i;
+}
+
+
+/* private static */
+std::vector>
+PolygonNodeConverter::convertHoles(std::vector& sections)
+{
+    std::vector> convertedSections;
+    const NodeSection* copySection = sections[0];
+    for (std::size_t i = 0; i < sections.size(); i++) {
+        std::size_t inext = next(sections, i);
+        const CoordinateXY* inVertex = sections[i]->getVertex(0);
+        const CoordinateXY* outVertex = sections[inext]->getVertex(1);
+        NodeSection* ns = createSection(copySection, inVertex, outVertex);
+        convertedSections.emplace_back(ns);
+    }
+    return convertedSections;
+}
+
+
+/* private static */
+NodeSection*
+PolygonNodeConverter::createSection(
+    const NodeSection* ns,
+    const CoordinateXY* v0,
+    const CoordinateXY* v1)
+{
+    return new NodeSection(
+        ns->isA(),
+        Dimension::A,
+        ns->id(), 0,
+        ns->getPolygonal(),
+        ns->isNodeAtVertex(),
+        v0, ns->nodePt(), v1);
+}
+
+
+
+/* private static */
+std::vector
+PolygonNodeConverter::extractUnique(std::vector& sections)
+{
+    std::vector uniqueSections;
+    const NodeSection* lastUnique = sections[0];
+    uniqueSections.push_back(lastUnique);
+    for (const NodeSection* ns : sections) {
+        if (0 != lastUnique->compareTo(ns)) {
+            uniqueSections.push_back(ns);
+            lastUnique = ns;
+        }
+    }
+    return uniqueSections;
+}
+
+
+/* private static */
+std::size_t
+PolygonNodeConverter::next(std::vector& ns, std::size_t i)
+{
+    std::size_t nxt = i;
+    if (nxt == INDEX_UNKNOWN)
+        nxt = 0;
+    else
+        nxt = i + 1;
+
+    if (nxt >= ns.size())
+        nxt = 0;
+
+    return nxt;
+}
+
+
+/* private static */
+std::size_t
+PolygonNodeConverter::findShell(std::vector& polySections)
+{
+    for (std::size_t i = 0; i < polySections.size(); i++) {
+        if (polySections[i]->isShell())
+            return i;
+    }
+    return INDEX_UNKNOWN;
+}
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
diff --git a/deps/libgeos/geos/src/operation/relateng/RelateEdge.cpp b/deps/libgeos/geos/src/operation/relateng/RelateEdge.cpp
new file mode 100644
index 000000000..4670b058b
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/RelateEdge.cpp
@@ -0,0 +1,475 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+using geos::algorithm::PolygonNodeTopology;
+using geos::geom::CoordinateXY;
+using geos::geom::Dimension;
+using geos::geom::Position;
+using geos::io::WKTWriter;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public */
+RelateEdge::RelateEdge(const RelateNode* rNode, const CoordinateXY* pt, bool isA, bool isForward)
+    : node(rNode)
+    , dirPt(pt)
+{
+    setLocationsArea(isA, isForward);
+}
+
+
+/* public */
+RelateEdge::RelateEdge(const RelateNode* rNode, const CoordinateXY* pt, bool isA)
+    : node(rNode)
+    , dirPt(pt)
+{
+    setLocationsLine(isA);
+}
+
+
+/* public */
+RelateEdge::RelateEdge(const RelateNode* rNode, const CoordinateXY* pt,
+    bool isA, Location locLeft, Location locRight, Location locLine)
+    : node(rNode)
+    , dirPt(pt)
+{
+    setLocations(isA, locLeft, locRight, locLine);
+}
+
+
+/* public static */
+RelateEdge *
+RelateEdge::create(const RelateNode* node, const CoordinateXY* dirPt, bool isA, int dim, bool isForward)
+{
+    if (dim == Dimension::A)
+        //-- create an area edge
+        return new RelateEdge(node, dirPt, isA, isForward);
+    //-- create line edge
+    return new RelateEdge(node, dirPt, isA);
+}
+
+
+/* public static */
+std::size_t
+RelateEdge::findKnownEdgeIndex(
+    std::vector>& edges,
+    bool isA)
+{
+    for (std::size_t i = 0; i < edges.size(); i++) {
+        auto& e = edges[i];
+        if (e->isKnown(isA))
+            return i;
+    }
+    return INDEX_UNKNOWN;
+}
+
+
+/* public static */
+void
+RelateEdge::setAreaInterior(
+    std::vector>& edges,
+    bool isA)
+{
+    for (auto& e : edges) {
+        e->setAreaInterior(isA);
+    }
+}
+
+
+/* private */
+void
+RelateEdge::setLocations(bool isA, Location locLeft, Location locRight, Location locLine)
+{
+    if (isA) {
+        aDim = 2;
+        aLocLeft = locLeft;
+        aLocRight = locRight;
+        aLocLine = locLine;
+    }
+    else {
+        bDim = 2;
+        bLocLeft = locLeft;
+        bLocRight = locRight;
+        bLocLine = locLine;
+    }
+}
+
+
+/* private */
+void
+RelateEdge::setLocationsLine(bool isA)
+{
+    if (isA) {
+        aDim = 1;
+        aLocLeft = Location::EXTERIOR;
+        aLocRight = Location::EXTERIOR;
+        aLocLine = Location::INTERIOR;
+    }
+    else {
+        bDim = 1;
+        bLocLeft = Location::EXTERIOR;
+        bLocRight = Location::EXTERIOR;
+        bLocLine = Location::INTERIOR;
+    }
+}
+
+
+/* private */
+void
+RelateEdge::setLocationsArea(bool isA, bool isForward)
+{
+    Location locLeft = isForward ? Location::EXTERIOR : Location::INTERIOR;
+    Location locRight = isForward ? Location::INTERIOR : Location::EXTERIOR;
+    if (isA) {
+        aDim = 2;
+        aLocLeft = locLeft;
+        aLocRight = locRight;
+        aLocLine = Location::BOUNDARY;
+    }
+    else {
+        bDim = 2;
+        bLocLeft = locLeft;
+        bLocRight = locRight;
+        bLocLine = Location::BOUNDARY;
+    }
+}
+
+
+/* public */
+int
+RelateEdge::compareToEdge(const CoordinateXY* edgeDirPt) const
+{
+    return PolygonNodeTopology::compareAngle(
+        node->getCoordinate(),
+        dirPt,
+        edgeDirPt);
+}
+
+
+/* public */
+void
+RelateEdge::merge(bool isA, int dim, bool isForward)
+{
+    Location locEdge = Location::INTERIOR;
+    Location locLeft = Location::EXTERIOR;
+    Location locRight = Location::EXTERIOR;
+    if (dim == Dimension::A) {
+        locEdge = Location::BOUNDARY;
+        locLeft = isForward ? Location::EXTERIOR : Location::INTERIOR;
+        locRight = isForward ? Location::INTERIOR : Location::EXTERIOR;
+    }
+
+    if (! isKnown(isA)) {
+        setDimension(isA, dim);
+        setOn(isA, locEdge);
+        setLeft(isA, locLeft);
+        setRight(isA, locRight);
+        return;
+    }
+
+    // Assert: node-dirpt is collinear with node-pt
+    mergeDimEdgeLoc(isA, locEdge);
+    mergeSideLocation(isA, Position::LEFT, locLeft);
+    mergeSideLocation(isA, Position::RIGHT, locRight);
+}
+
+/**
+* Area edges override Line edges.
+* Merging edges of same dimension is a no-op for
+* the dimension and on location.
+* But merging an area edge into a line edge
+* sets the dimension to A and the location to BOUNDARY.
+*
+* @param isA
+* @param locEdge
+*/
+/* private */
+void
+RelateEdge::mergeDimEdgeLoc(bool isA, Location locEdge)
+{
+    //TODO: this logic needs work - ie handling A edges marked as Interior
+    int dim = (locEdge == Location::BOUNDARY) ? Dimension::A : Dimension::L;
+    if (dim == Dimension::A && dimension(isA) == Dimension::L) {
+        setDimension(isA, dim);
+        setOn(isA, Location::BOUNDARY);
+    }
+}
+
+
+/* private */
+void
+RelateEdge::mergeSideLocation(bool isA, int pos, Location loc)
+{
+    Location currLoc = location(isA, pos);
+    //-- INTERIOR takes precedence over EXTERIOR
+    if (currLoc != Location::INTERIOR) {
+        setLocation(isA, pos, loc);
+    }
+}
+
+
+/* private */
+void
+RelateEdge::setDimension(bool isA, int dimension)
+{
+    if (isA) {
+        aDim = dimension;
+    }
+    else {
+        bDim = dimension;
+    }
+}
+
+
+/* public */
+void
+RelateEdge::setLocation(bool isA, int pos, Location loc)
+{
+    switch (pos) {
+    case Position::LEFT:
+        setLeft(isA, loc);
+        break;
+    case Position::RIGHT:
+        setRight(isA, loc);
+        break;
+    case Position::ON:
+        setOn(isA, loc);
+        break;
+    }
+}
+
+
+/* public */
+void
+RelateEdge::setAllLocations(bool isA, Location loc)
+{
+    setLeft(isA, loc);
+    setRight(isA, loc);
+    setOn(isA, loc);
+}
+
+
+/* public */
+void
+RelateEdge::setUnknownLocations(bool isA, Location loc)
+{
+    if (! isKnown(isA, Position::LEFT)) {
+        setLocation(isA, Position::LEFT, loc);
+    }
+    if (! isKnown(isA, Position::RIGHT)) {
+        setLocation(isA, Position::RIGHT, loc);
+    }
+    if (! isKnown(isA, Position::ON)) {
+        setLocation(isA, Position::ON, loc);
+    }
+}
+
+
+/* private */
+void
+RelateEdge::setLeft(bool isA, Location loc)
+{
+    if (isA) {
+        aLocLeft = loc;
+    }
+    else {
+        bLocLeft = loc;
+    }
+}
+
+
+/* private */
+void
+RelateEdge::setRight(bool isA, Location loc)
+{
+    if (isA) {
+        aLocRight = loc;
+    }
+    else {
+        bLocRight = loc;
+    }
+}
+
+
+/* private */
+void
+RelateEdge::setOn(bool isA, Location loc)
+{
+    if (isA) {
+        aLocLine = loc;
+    }
+    else {
+        bLocLine = loc;
+    }
+}
+
+
+/* public */
+Location
+RelateEdge::location(bool isA, int position) const
+{
+    if (isA) {
+        switch (position) {
+            case Position::LEFT: return aLocLeft;
+            case Position::RIGHT: return aLocRight;
+            case Position::ON: return aLocLine;
+        }
+    }
+    else {
+        switch (position) {
+            case Position::LEFT: return bLocLeft;
+            case Position::RIGHT: return bLocRight;
+            case Position::ON: return bLocLine;
+        }
+    }
+    assert(false && "never get here");
+    return LOC_UNKNOWN;
+}
+
+/* private */
+int
+RelateEdge::dimension(bool isA) const
+{
+    return isA ? aDim : bDim;
+}
+
+/* private */
+bool
+RelateEdge::isKnown(bool isA) const
+{
+    if (isA)
+        return aDim != DIM_UNKNOWN;
+    return bDim != DIM_UNKNOWN;
+}
+
+/* private */
+bool
+RelateEdge::isKnown(bool isA, int pos) const
+{
+    return location(isA, pos) != LOC_UNKNOWN;
+}
+
+
+/* public */
+bool
+RelateEdge::isInterior(bool isA, int position) const
+{
+    return location(isA, position) == Location::INTERIOR;
+}
+
+
+/* public */
+void
+RelateEdge::setDimLocations(bool isA, int dim, Location loc)
+{
+    if (isA) {
+        aDim = dim;
+        aLocLeft = loc;
+        aLocRight = loc;
+        aLocLine = loc;
+    }
+    else {
+        bDim = dim;
+        bLocLeft = loc;
+        bLocRight = loc;
+        bLocLine = loc;
+    }
+}
+
+
+/* public */
+void
+RelateEdge::setAreaInterior(bool isA)
+{
+    if (isA) {
+        aLocLeft = Location::INTERIOR;
+        aLocRight = Location::INTERIOR;
+        aLocLine = Location::INTERIOR;
+    }
+    else {
+        bLocLeft = Location::INTERIOR;
+        bLocRight = Location::INTERIOR;
+        bLocLine = Location::INTERIOR;
+    }
+}
+
+
+/* public */
+std::string
+RelateEdge::toString() const
+{
+    std::stringstream ss;
+    ss << WKTWriter::toLineString(*(node->getCoordinate()), *dirPt);
+    ss << " - " << labelString();
+    return ss.str();
+}
+
+
+/* private */
+std::string
+RelateEdge::labelString() const
+{
+    std::stringstream ss;
+    ss << "A:";
+    ss << locationString(RelateGeometry::GEOM_A);
+    ss << "/B:";
+    ss << locationString(RelateGeometry::GEOM_B);
+    return ss.str();
+}
+
+
+/* private */
+std::string
+RelateEdge::locationString(bool isA) const
+{
+    std::stringstream ss;
+    ss << location(isA, Position::LEFT);
+    ss << location(isA, Position::ON);
+    ss << location(isA, Position::RIGHT);
+    return ss.str();
+}
+
+
+/* public friend */
+std::ostream&
+operator<<(std::ostream& os, const RelateEdge& re)
+{
+    os << re.toString();
+    return os;
+}
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/RelateGeometry.cpp b/deps/libgeos/geos/src/operation/relateng/RelateGeometry.cpp
new file mode 100644
index 000000000..668968eff
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/RelateGeometry.cpp
@@ -0,0 +1,521 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+
+using geos::algorithm::BoundaryNodeRule;
+using geos::algorithm::Orientation;
+using namespace geos::geom;
+using geos::geom::util::ComponentCoordinateExtracter;
+using geos::geom::util::GeometryLister;
+using geos::geom::util::PointExtracter;
+using geos::operation::valid::RepeatedPointRemover;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+RelateGeometry::RelateGeometry(const Geometry* input, bool isPrepared, const BoundaryNodeRule& bnRule)
+    : geom(input)
+    , m_isPrepared(isPrepared)
+    , geomEnv(input->getEnvelopeInternal())
+    , boundaryNodeRule(bnRule)
+    , geomDim(input->getDimension())
+    , isLineZeroLen(isZeroLengthLine(input))
+    , isGeomEmpty(input->isEmpty())
+{
+    analyzeDimensions();
+}
+
+
+/* public static */
+std::string
+RelateGeometry::name(bool isA)
+{
+    return isA ? "A" : "B";
+}
+
+
+/* private */
+void
+RelateGeometry::analyzeDimensions()
+{
+    if (isGeomEmpty) {
+        return;
+    }
+    GeometryTypeId typeId = geom->getGeometryTypeId();
+    if (typeId == GEOS_POINT || typeId == GEOS_MULTIPOINT) {
+        hasPoints = true;
+        geomDim = Dimension::P;
+        return;
+    }
+    if (typeId == GEOS_LINESTRING || typeId == GEOS_LINEARRING || typeId == GEOS_MULTILINESTRING) {
+        hasLines = true;
+        geomDim = Dimension::L;
+        return;
+    }
+    if (typeId == GEOS_POLYGON || typeId == GEOS_MULTIPOLYGON) {
+        hasAreas = true;
+        geomDim = Dimension::A;
+        return;
+    }
+    //-- analyze a (possibly mixed type) collection
+    std::vector elems;
+    GeometryLister::list(geom, elems);
+    for (const Geometry* elem : elems)
+    {
+        if (elem->isEmpty())
+            continue;
+        if (elem->getGeometryTypeId() == GEOS_POINT) {
+            hasPoints = true;
+            if (geomDim < Dimension::P) geomDim = Dimension::P;
+        }
+        if (elem->getGeometryTypeId() == GEOS_LINESTRING ||
+            elem->getGeometryTypeId() == GEOS_LINEARRING) {
+            hasLines = true;
+            if (geomDim < Dimension::L) geomDim = Dimension::L;
+        }
+        if (elem->getGeometryTypeId() == GEOS_POLYGON) {
+            hasAreas = true;
+            if (geomDim < Dimension::A) geomDim = Dimension::A;
+        }
+    }
+}
+
+
+/* private static */
+bool
+RelateGeometry::isZeroLength(const Geometry* geom)
+{
+    std::vector elems;
+    GeometryLister::list(geom, elems);
+    for (const Geometry* elem : elems) {
+        if (elem->getGeometryTypeId() == GEOS_LINESTRING ||
+            elem->getGeometryTypeId() == GEOS_LINEARRING ) {
+            if (! isZeroLength(static_cast(elem))) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+/* private static */
+bool
+RelateGeometry::isZeroLength(const LineString* line) {
+    if (line->getNumPoints() >= 2) {
+        const CoordinateXY& p0 = line->getCoordinateN(0);
+        for (std::size_t i = 1; i < line->getNumPoints(); i++) {
+            // NOTE !!! CHANGE FROM JTS, original below
+            // const CoordinateXY& pi = line.getCoordinateN(1);
+            const CoordinateXY& pi = line->getCoordinateN(i);
+            //-- most non-zero-len lines will trigger this right away
+            if (! p0.equals2D(pi)) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+
+/* public */
+int
+RelateGeometry::getDimensionReal() const
+{
+    if (isGeomEmpty)
+        return Dimension::False;
+    if (getDimension() == Dimension::L && isLineZeroLen)
+        return Dimension::P;
+    if (hasAreas)
+        return Dimension::A;
+    if (hasLines)
+        return Dimension::L;
+    return Dimension::P;
+}
+
+
+/* public */
+bool
+RelateGeometry::hasEdges() const
+{
+    return hasLines || hasAreas;
+}
+
+/* private */
+RelatePointLocator*
+RelateGeometry::getLocator()
+{
+    if (locator == nullptr)
+        locator.reset(new RelatePointLocator(geom, m_isPrepared, boundaryNodeRule));
+    return locator.get();
+}
+
+
+/* public */
+bool
+RelateGeometry::isNodeInArea(const CoordinateXY* nodePt, const Geometry* parentPolygonal)
+{
+    int dimLoc = getLocator()->locateNodeWithDim(nodePt, parentPolygonal);
+    return dimLoc == DimensionLocation::AREA_INTERIOR;
+}
+
+
+/* public */
+int
+RelateGeometry::locateLineEndWithDim(const CoordinateXY* p)
+{
+    return getLocator()->locateLineEndWithDim(p);
+}
+
+
+/* public */
+Location
+RelateGeometry::locateAreaVertex(const CoordinateXY* pt)
+{
+    /**
+     * Can pass a null polygon, because the point is an exact vertex,
+     * which will be detected as being on the boundary of its polygon
+     */
+    return locateNode(pt, nullptr);
+}
+
+
+/* public */
+Location
+RelateGeometry::locateNode(const CoordinateXY* pt, const Geometry* parentPolygonal)
+{
+    return getLocator()->locateNode(pt, parentPolygonal);
+}
+
+
+/* public */
+int
+RelateGeometry::locateWithDim(const CoordinateXY* pt)
+{
+    int loc = getLocator()->locateWithDim(pt);
+    return loc;
+}
+
+
+/* public */
+bool
+RelateGeometry::isSelfNodingRequired() const
+{
+    GeometryTypeId typeId = geom->getGeometryTypeId();
+    if (typeId == GEOS_POINT
+        || typeId == GEOS_MULTIPOINT
+        || typeId == GEOS_POLYGON
+        || typeId == GEOS_MULTIPOLYGON)
+    {
+         return false;
+     }
+    //-- a GC with a single polygon does not need noding
+    if (hasAreas && geom->getNumGeometries() == 1)
+        return false;
+    return true;
+}
+
+
+/* public */
+bool
+RelateGeometry::isPolygonal() const
+{
+    //TODO: also true for a GC containing one polygonal element (and possibly some lower-dimension elements)
+    GeometryTypeId typeId = geom->getGeometryTypeId();
+    return typeId == GEOS_POLYGON
+        || typeId == GEOS_MULTIPOLYGON;
+}
+
+
+/* public */
+bool
+RelateGeometry::isEmpty() const
+{
+    return isGeomEmpty;
+}
+
+
+/* public */
+bool
+RelateGeometry::hasBoundary()
+{
+    return getLocator()->hasBoundary();
+}
+
+
+/* public */
+Coordinate::ConstXYSet&
+RelateGeometry::getUniquePoints()
+{
+    if (uniquePoints.empty()) {
+        uniquePoints = createUniquePoints();
+    }
+    return uniquePoints;
+}
+
+
+/* private */
+Coordinate::ConstXYSet
+RelateGeometry::createUniquePoints()
+{
+    //-- only called on P geometries
+    std::vector pts;
+    ComponentCoordinateExtracter::getCoordinates(*geom, pts);
+    Coordinate::ConstXYSet set(pts.begin(), pts.end());
+    return set;
+}
+
+
+/* public */
+std::vector
+RelateGeometry::getEffectivePoints()
+{
+    std::vector ptListAll;
+    geom::util::PointExtracter::getPoints(*geom, ptListAll);
+
+    if (getDimensionReal() <= Dimension::P)
+        return ptListAll;
+
+    //-- only return Points not covered by another element
+    std::vector ptList;
+    for (const Point* p : ptListAll) {
+        if (p->isEmpty())
+            continue;
+        int locDim = locateWithDim(p->getCoordinate());
+        if (DimensionLocation::dimension(locDim) == Dimension::P) {
+            ptList.push_back(p);
+        }
+    }
+    return ptList;
+}
+
+
+/* public */
+std::vector
+RelateGeometry::extractSegmentStrings(bool isA, const Envelope* env)
+{
+    std::vector segStrings;
+
+    // When we get called in the context of a prepared geometry
+    // geomA might already have segments extracted and stored,
+    // so check and reuse them if possible
+    if (isA && isPrepared() && env == nullptr) {
+        if (segStringPermStore.empty()) {
+            extractSegmentStrings(isA, env, geom, segStrings, segStringPermStore);
+        }
+        else {
+            for (auto& ss : segStringPermStore) {
+                segStrings.push_back(ss.get());
+            }
+        }
+    }
+    // In the context of geomB we always extract for each call,
+    // and same goes for geomA when not in prepared mode, or when
+    // using an envelope filter.
+    else {
+        segStringTempStore.clear();
+        extractSegmentStrings(isA, env, geom, segStrings, segStringTempStore);
+    }
+    return segStrings;
+}
+
+
+/* private */
+void
+RelateGeometry::extractSegmentStrings(bool isA,
+    const Envelope* env, const Geometry* p_geom,
+    std::vector& segStrings,
+    std::vector>& segStore)
+{
+    //-- record if parent is MultiPolygon
+    const MultiPolygon* parentPolygonal = nullptr;
+    if (p_geom->getGeometryTypeId() == GEOS_MULTIPOLYGON) {
+        parentPolygonal = static_cast(p_geom);
+    }
+
+    for (std::size_t i = 0; i < p_geom->getNumGeometries(); i++) {
+        const Geometry* g = p_geom->getGeometryN(i);
+        // if (g->getGeometryTypeId() == GEOS_GEOMETRYCOLLECTION) {
+        if (g->isCollection()) {
+            extractSegmentStrings(isA, env, g, segStrings, segStore);
+        }
+        else {
+            extractSegmentStringsFromAtomic(isA, g, parentPolygonal, env, segStrings, segStore);
+        }
+    }
+}
+
+
+/* private */
+void
+RelateGeometry::extractSegmentStringsFromAtomic(bool isA,
+    const Geometry* p_geom, const MultiPolygon* parentPolygonal,
+    const Envelope* env,
+    std::vector& segStrings,
+    std::vector>& segStore)
+{
+    if (p_geom->isEmpty())
+        return;
+
+    bool doExtract = (env == nullptr) || env->intersects(p_geom->getEnvelopeInternal());
+    if (! doExtract)
+        return;
+
+    elementId++;
+    if (p_geom->getGeometryTypeId() == GEOS_LINESTRING ||
+        p_geom->getGeometryTypeId() == GEOS_LINEARRING) {
+        const LineString* line = static_cast(p_geom);
+        /*
+         * Condition the input Coordinate sequence so that it has no repeated points.
+         * This requires taking a copy which removeRepeated does behind the scenes and stores in csStore.
+         */
+        const CoordinateSequence* cs = removeRepeated(line->getCoordinatesRO());
+        auto ss = RelateSegmentString::createLine(cs, isA, elementId, this);
+        segStore.emplace_back(ss);
+        segStrings.push_back(ss);
+    }
+    else if (p_geom->getGeometryTypeId() == GEOS_POLYGON) {
+        const Polygon* poly = static_cast(p_geom);
+        const Geometry* parentPoly;
+        if (parentPolygonal != nullptr)
+            parentPoly = static_cast(parentPolygonal);
+        else
+            parentPoly = static_cast(poly);
+        extractRingToSegmentString(isA, poly->getExteriorRing(), 0, env, parentPoly, segStrings, segStore);
+        for (uint32_t i = 0; i < poly->getNumInteriorRing(); i++) {
+            extractRingToSegmentString(isA, poly->getInteriorRingN(i), static_cast(i+1), env, parentPoly, segStrings, segStore);
+        }
+    }
+}
+
+
+/* private */
+void
+RelateGeometry::extractRingToSegmentString(bool isA,
+    const LinearRing* ring, int ringId, const Envelope* env,
+    const Geometry* parentPoly,
+    std::vector& segStrings,
+    std::vector>& segStore)
+{
+    if (ring->isEmpty())
+        return;
+    if (env != nullptr && ! env->intersects(ring->getEnvelopeInternal()))
+        return;
+
+    /*
+     * Condition the input Coordinate sequence so that it has no repeated points
+     * and is oriented in a deterministic way. This requires taking a copy which
+     * orientAndRemoveRepeated does behind the scenes and stores in csStore.
+     */
+    bool requireCW = (ringId == 0);
+    const CoordinateSequence* cs = orientAndRemoveRepeated(ring->getCoordinatesRO(), requireCW);
+    auto ss = RelateSegmentString::createRing(cs, isA, elementId, ringId, parentPoly, this);
+    segStore.emplace_back(ss);
+    segStrings.push_back(ss);
+}
+
+
+/* public */
+std::string
+RelateGeometry::toString() const
+{
+    return geom->toString();
+}
+
+
+/* public friend */
+std::ostream&
+operator<<(std::ostream& os, const RelateGeometry& rg)
+{
+    os << rg.toString();
+    return os;
+}
+
+
+
+/* private */
+const CoordinateSequence *
+RelateGeometry::orientAndRemoveRepeated(const CoordinateSequence *seq, bool orientCW)
+{
+    bool isFlipped = (orientCW == Orientation::isCCW(seq));
+    bool hasRepeated = seq->hasRepeatedPoints();
+    /* Already conditioned */
+    if (!isFlipped && !hasRepeated) {
+        return seq;
+    }
+
+    if (hasRepeated) {
+        auto deduped = RepeatedPointRemover::removeRepeatedPoints(seq);
+        if (isFlipped)
+            deduped->reverse();
+        CoordinateSequence* cs = deduped.release();
+        csStore.emplace_back(cs);
+        return cs;
+    }
+
+    if (isFlipped) {
+        auto reversed = seq->clone();
+        reversed->reverse();
+        CoordinateSequence* cs = reversed.release();
+        csStore.emplace_back(cs);
+        return cs;
+    }
+
+    return seq;
+}
+
+/* private */
+const CoordinateSequence *
+RelateGeometry::removeRepeated(const CoordinateSequence *seq)
+{
+    bool hasRepeated = seq->hasRepeatedPoints();
+    if (!hasRepeated)
+        return seq;
+    auto deduped = RepeatedPointRemover::removeRepeatedPoints(seq);
+    CoordinateSequence* cs = deduped.release();
+    csStore.emplace_back(cs);
+    return cs;
+}
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/RelateNG.cpp b/deps/libgeos/geos/src/operation/relateng/RelateNG.cpp
new file mode 100644
index 000000000..9338123ff
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/RelateNG.cpp
@@ -0,0 +1,703 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+
+using namespace geos::geom;
+using geos::algorithm::BoundaryNodeRule;
+using geos::noding::MCIndexSegmentSetMutualIntersector;
+using geos::noding::SegmentString;
+using geos::geom::util::GeometryLister;
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+#define GEOM_A RelateGeometry::GEOM_A
+#define GEOM_B RelateGeometry::GEOM_B
+
+
+/************************************************************************/
+
+/* public static */
+bool
+RelateNG::intersects(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::IntersectsPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::crosses(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::CrossesPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::disjoint(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::DisjointPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::touches(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::TouchesPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::within(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::WithinPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::contains(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::ContainsPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::overlaps(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::OverlapsPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::covers(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::CoversPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::coveredBy(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::CoveredByPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::equalsTopo(const Geometry* a, const Geometry* b)
+{
+    RelatePredicate::EqualsTopoPredicate pred;
+    return RelateNG::relate(a, b, pred);
+}
+
+/* public static */
+bool
+RelateNG::relate(const Geometry* a, const Geometry* b, TopologyPredicate& pred)
+{
+    RelateNG rng(a, false);
+    return rng.evaluate(b, pred);
+}
+
+/* public static */
+bool
+RelateNG::relate(const Geometry* a, const Geometry* b, TopologyPredicate& pred, const BoundaryNodeRule& bnRule)
+{
+    RelateNG rng(a, false, bnRule);
+    return rng.evaluate(b, pred);
+}
+
+/* public static */
+bool
+RelateNG::relate(const Geometry* a, const Geometry* b, const std::string& imPattern)
+{
+    RelateNG rng(a, false);
+    return rng.evaluate(b, imPattern);
+}
+
+/* public static */
+std::unique_ptr
+RelateNG::relate(const Geometry* a, const Geometry* b)
+{
+    RelateNG rng(a, false);
+    return rng.evaluate(b);
+}
+
+/* public static */
+std::unique_ptr
+RelateNG::relate(const Geometry* a, const Geometry* b, const BoundaryNodeRule& bnRule)
+{
+    RelateNG rng(a, false, bnRule);
+    return rng.evaluate(b);
+}
+
+/************************************************************************/
+
+/* public */
+bool
+RelateNG::intersects(const Geometry* b)
+{
+    RelatePredicate::IntersectsPredicate pred;
+    return evaluate(b, pred);
+}
+
+/* public */
+bool
+RelateNG::crosses(const Geometry* b)
+{
+    RelatePredicate::CrossesPredicate pred;
+    return evaluate(b, pred);
+}
+
+/* public */
+bool
+RelateNG::disjoint(const Geometry* b)
+{
+    RelatePredicate::DisjointPredicate pred;
+    return evaluate(b, pred);
+}
+
+/* public */
+bool
+RelateNG::touches(const Geometry* b)
+{
+    RelatePredicate::TouchesPredicate pred;
+    return evaluate(b, pred);
+}
+
+/* public */
+bool
+RelateNG::within(const Geometry* a)
+{
+    RelatePredicate::WithinPredicate pred;
+    return evaluate(a, pred);
+}
+
+/* public */
+bool
+RelateNG::contains(const Geometry* b)
+{
+    RelatePredicate::ContainsPredicate pred;
+    return evaluate(b, pred);
+}
+
+/* public */
+bool
+RelateNG::overlaps(const Geometry* b)
+{
+    RelatePredicate::OverlapsPredicate pred;
+    return evaluate(b, pred);
+}
+
+/* public */
+bool
+RelateNG::covers(const Geometry* b)
+{
+    RelatePredicate::CoversPredicate pred;
+    return evaluate(b, pred);
+}
+
+/* public */
+bool
+RelateNG::coveredBy(const Geometry* b)
+{
+    RelatePredicate::CoveredByPredicate pred;
+    return evaluate(b, pred);
+}
+
+/* public */
+bool
+RelateNG::equalsTopo(const Geometry* b)
+{
+    RelatePredicate::EqualsTopoPredicate pred;
+    return evaluate(b, pred);
+}
+
+/* public */
+bool
+RelateNG::relate(const Geometry* b, const std::string& imPattern)
+{
+    return evaluate(b, imPattern);
+}
+
+/* public */
+std::unique_ptr
+RelateNG::relate(const Geometry* b)
+{
+    return evaluate(b);
+}
+
+/************************************************************************/
+
+/* public static */
+std::unique_ptr
+RelateNG::prepare(const Geometry* a)
+{
+    return std::unique_ptr(new RelateNG(a, true));
+}
+
+
+/* public static */
+std::unique_ptr
+RelateNG::prepare(const Geometry* a, const BoundaryNodeRule& bnRule)
+{
+    return std::unique_ptr(new RelateNG(a, true, bnRule));
+}
+
+/************************************************************************/
+
+/* public */
+std::unique_ptr
+RelateNG::evaluate(const Geometry* b)
+{
+    RelateMatrixPredicate rel;
+    evaluate(b, rel);
+    return rel.getIM();
+}
+
+
+/* public */
+bool
+RelateNG::evaluate(const Geometry* b, const std::string& imPattern)
+{
+    auto predicate = RelatePredicate::matches(imPattern);
+    return evaluate(b, *predicate);
+}
+
+
+/* public */
+bool
+RelateNG::evaluate(const Geometry* b, TopologyPredicate& predicate)
+{
+    //-- fast envelope checks
+    if (! hasRequiredEnvelopeInteraction(b, predicate)) {
+        return false;
+    }
+
+    geos::util::ensureNoCurvedComponents(geomA.getGeometry());
+    geos::util::ensureNoCurvedComponents(b);
+    
+    RelateGeometry geomB(b, boundaryNodeRule);
+
+    int dimA = geomA.getDimensionReal();
+    int dimB = geomB.getDimensionReal();
+
+    //-- check if predicate is determined by dimension or envelope
+    predicate.init(dimA, dimB);
+    if (predicate.isKnown())
+        return finishValue(predicate);
+
+    predicate.init(*(geomA.getEnvelope()), *(geomB.getEnvelope()));
+    if (predicate.isKnown())
+        return finishValue(predicate);
+
+    TopologyComputer topoComputer(predicate, geomA, geomB);
+
+    //-- optimized P/P evaluation
+    if (dimA == Dimension::P && dimB == Dimension::P) {
+        computePP(geomB, topoComputer);
+        topoComputer.finish();
+        return topoComputer.getResult();
+    }
+
+    //-- test points against (potentially) indexed geometry first
+    computeAtPoints(geomB, GEOM_B, geomA, topoComputer);
+    if (topoComputer.isResultKnown()) {
+        return topoComputer.getResult();
+    }
+    computeAtPoints(geomA, GEOM_A, geomB, topoComputer);
+    if (topoComputer.isResultKnown()) {
+        return topoComputer.getResult();
+    }
+
+    if (geomA.hasEdges() && geomB.hasEdges()) {
+        computeAtEdges(geomB, topoComputer);
+    }
+
+    //-- after all processing, set remaining unknown values in IM
+    topoComputer.finish();
+    return topoComputer.getResult();
+}
+
+
+/* private */
+bool
+RelateNG::hasRequiredEnvelopeInteraction(const Geometry* b, TopologyPredicate& predicate)
+{
+    const Envelope* envB = b->getEnvelopeInternal();
+    bool isInteracts = false;
+    if (predicate.requireCovers(GEOM_A)) {
+        if (! geomA.getEnvelope()->covers(envB)) {
+            return false;
+        }
+        isInteracts = true;
+    }
+    else if (predicate.requireCovers(GEOM_B)) {
+        if (! envB->covers(geomA.getEnvelope())) {
+            return false;
+        }
+        isInteracts = true;
+    }
+    if (! isInteracts
+        && predicate.requireInteraction()
+        && ! geomA.getEnvelope()->intersects(envB)) {
+        return false;
+    }
+    return true;
+}
+
+/* private */
+bool
+RelateNG::finishValue(TopologyPredicate& predicate)
+{
+    predicate.finish();
+    return predicate.value();
+}
+
+
+/* private */
+void
+RelateNG::computePP(RelateGeometry& geomB, TopologyComputer& topoComputer)
+{
+    Coordinate::ConstXYSet& ptsA = geomA.getUniquePoints();
+    //TODO: only query points in interaction extent?
+    Coordinate::ConstXYSet& ptsB = geomB.getUniquePoints();
+
+    uint32_t numBinA = 0;
+    for (const CoordinateXY* ptB : ptsB) {
+        auto it = ptsA.find(ptB);
+        bool found = (it != ptsA.end());
+        if (found) {
+            numBinA++;
+            topoComputer.addPointOnPointInterior(ptB);
+        }
+        else {
+            topoComputer.addPointOnPointExterior(GEOM_B, ptB);
+        }
+        if (topoComputer.isResultKnown()) {
+            return;
+        }
+    }
+    /**
+     * If number of matched B points is less than size of A,
+     * there must be at least one A point in the exterior of B
+     */
+    if (numBinA < ptsA.size()) {
+        //TODO: determine actual exterior point?
+        topoComputer.addPointOnPointExterior(GEOM_A, nullptr);
+    }
+}
+
+
+/* private */
+void
+RelateNG::computeAtPoints(
+    RelateGeometry& geom, bool isA,
+    RelateGeometry& geomTarget, TopologyComputer& topoComputer)
+{
+    bool isResultKnown = false;
+    isResultKnown = computePoints(geom, isA, geomTarget, topoComputer);
+    if (isResultKnown)
+        return;
+
+    /**
+     * Performance optimization: only check points against target
+     * if it has areas OR if the predicate requires checking for
+     * exterior interaction.
+     * In particular, this avoids testing line ends against lines
+     * for the intersects predicate (since these are checked
+     * during segment/segment intersection checking anyway).
+     * Checking points against areas is necessary, since the input
+     * linework is disjoint if one input lies wholly inside an area,
+     * so segment intersection checking is not sufficient.
+     */
+    bool checkDisjointPoints = geomTarget.hasDimension(Dimension::A)
+                                || topoComputer.isExteriorCheckRequired(isA);
+    if (! checkDisjointPoints)
+        return;
+
+    isResultKnown = computeLineEnds(geom, isA, geomTarget, topoComputer);
+    if (isResultKnown)
+        return;
+
+    computeAreaVertex(geom, isA, geomTarget, topoComputer);
+}
+
+
+/* private */
+bool
+RelateNG::computePoints(
+    RelateGeometry& geom, bool isA,
+    RelateGeometry& geomTarget, TopologyComputer& topoComputer)
+{
+    if (! geom.hasDimension(Dimension::P)) {
+        return false;
+    }
+
+    std::vector points = geom.getEffectivePoints();
+    for (const Point* point : points) {
+        //TODO: exit when all possible target locations (E,I,B) have been found?
+        if (point->isEmpty())
+            continue;
+
+        const CoordinateXY* pt = point->getCoordinate();
+        computePoint(isA, pt, geomTarget, topoComputer);
+        if (topoComputer.isResultKnown()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+
+/* private */
+void
+RelateNG::computePoint(bool isA, const CoordinateXY* pt, RelateGeometry& geomTarget, TopologyComputer& topoComputer)
+{
+      int locDimTarget = geomTarget.locateWithDim(pt);
+      Location locTarget = DimensionLocation::location(locDimTarget);
+      int dimTarget = DimensionLocation::dimension(locDimTarget, topoComputer.getDimension(! isA));
+      topoComputer.addPointOnGeometry(isA, locTarget, dimTarget, pt);
+}
+
+
+/* private */
+bool
+RelateNG::computeLineEnds(
+    RelateGeometry& geom, bool isA,
+    RelateGeometry& geomTarget, TopologyComputer& topoComputer)
+{
+    if (! geom.hasDimension(Dimension::L)) {
+        return false;
+    }
+
+    bool hasExteriorIntersection = false;
+    std::vector elems;
+    GeometryLister::list(geom.getGeometry(), elems);
+    for (const Geometry* elem : elems) {
+        if (elem->isEmpty())
+            continue;
+
+        if (elem->getGeometryTypeId() == GEOS_LINESTRING ||
+            elem->getGeometryTypeId() == GEOS_LINEARRING) {
+            //-- once an intersection with target exterior is recorded, skip further known-exterior points
+            if (hasExteriorIntersection
+                && elem->getEnvelopeInternal()->disjoint(geomTarget.getEnvelope()))
+                continue;
+
+            const LineString* line = static_cast(elem);
+            //TODO: add optimzation to skip disjoint elements once exterior point found
+            const CoordinateXY& e0 = line->getCoordinatesRO()->getAt(0);
+            hasExteriorIntersection |= computeLineEnd(geom, isA, &e0, geomTarget, topoComputer);
+            if (topoComputer.isResultKnown()) {
+                return true;
+            }
+
+            if (! line->isClosed()) {
+                const CoordinateXY& e1 = line->getCoordinatesRO()->getAt(line->getNumPoints() - 1);
+                hasExteriorIntersection |= computeLineEnd(geom, isA, &e1, geomTarget, topoComputer);
+                if (topoComputer.isResultKnown()) {
+                    return true;
+                }
+            }
+        //TODO: break when all possible locations have been found?
+        }
+    }
+    return false;
+}
+
+
+/* private */
+bool
+RelateNG::computeLineEnd(RelateGeometry& geom, bool isA, const CoordinateXY* pt,
+      RelateGeometry& geomTarget, TopologyComputer& topoComputer)
+{
+    int locDimLineEnd = geom.locateLineEndWithDim(pt);
+    int dimLineEnd = DimensionLocation::dimension(locDimLineEnd, topoComputer.getDimension(isA));
+    //-- skip line ends which are in a GC area
+    if (dimLineEnd != Dimension::L)
+        return false;
+    Location locLineEnd = DimensionLocation::location(locDimLineEnd);
+    int locDimTarget = geomTarget.locateWithDim(pt);
+    Location locTarget = DimensionLocation::location(locDimTarget);
+    int dimTarget = DimensionLocation::dimension(locDimTarget, topoComputer.getDimension(! isA));
+    topoComputer.addLineEndOnGeometry(isA, locLineEnd, locTarget, dimTarget, pt);
+    return locTarget == Location::EXTERIOR;
+}
+
+/* private */
+bool
+RelateNG::computeAreaVertex(RelateGeometry& geom, bool isA, RelateGeometry& geomTarget, TopologyComputer& topoComputer)
+{
+    if (! geom.hasDimension(Dimension::A)) {
+        return false;
+    }
+    //-- evaluate for line and area targets only, since points are handled in the reverse direction
+    if (geomTarget.getDimension() < Dimension::L)
+        return false;
+
+    bool hasExteriorIntersection = false;
+
+    std::vector elems;
+    GeometryLister::list(geom.getGeometry(), elems);
+    for (const Geometry* elem : elems) {
+        if (elem->isEmpty())
+            continue;
+
+        if (elem->getGeometryTypeId() == GEOS_POLYGON) {
+            //-- once an intersection with target exterior is recorded, skip further known-exterior points
+            if (hasExteriorIntersection && elem->getEnvelopeInternal()->disjoint(geomTarget.getEnvelope()))
+                continue;
+
+            const Polygon* poly = static_cast(elem);
+            hasExteriorIntersection |= computeAreaVertex(geom, isA, poly->getExteriorRing(), geomTarget, topoComputer);
+            if (topoComputer.isResultKnown()) {
+                return true;
+            }
+            for (std::size_t j = 0; j < poly->getNumInteriorRing(); j++) {
+                hasExteriorIntersection |= computeAreaVertex(geom, isA, poly->getInteriorRingN(j), geomTarget, topoComputer);
+                if (topoComputer.isResultKnown()) {
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+
+/* private */
+bool
+RelateNG::computeAreaVertex(RelateGeometry& geom, bool isA, const LinearRing* ring, RelateGeometry& geomTarget, TopologyComputer& topoComputer)
+{
+    //TODO: use extremal (highest) point to ensure one is on boundary of polygon cluster
+    const CoordinateXY* pt = ring->getCoordinate();
+
+    Location locArea = geom.locateAreaVertex(pt);
+    int locDimTarget = geomTarget.locateWithDim(pt);
+    Location locTarget = DimensionLocation::location(locDimTarget);
+    int dimTarget = DimensionLocation::dimension(locDimTarget, topoComputer.getDimension(! isA));
+    topoComputer.addAreaVertex(isA, locArea, locTarget, dimTarget, pt);
+    return locTarget == Location::EXTERIOR;
+}
+
+
+/* private */
+void
+RelateNG::computeAtEdges(RelateGeometry& geomB, TopologyComputer& topoComputer)
+{
+    Envelope envInt;
+    geomA.getEnvelope()->intersection(*(geomB.getEnvelope()), envInt);
+    if (envInt.isNull())
+        return;
+
+    std::vector edgesB = geomB.extractSegmentStrings(GEOM_B, &envInt);
+    EdgeSegmentIntersector intersector(topoComputer);
+
+    if (topoComputer.isSelfNodingRequired()) {
+        computeEdgesAll(edgesB, &envInt, intersector);
+    }
+    else {
+        computeEdgesMutual(edgesB, &envInt, intersector);
+    }
+    if (topoComputer.isResultKnown()) {
+        return;
+    }
+
+    topoComputer.evaluateNodes();
+}
+
+
+/* private */
+void
+RelateNG::computeEdgesAll(std::vector& edgesB, const Envelope* envInt, EdgeSegmentIntersector& intersector)
+{
+    //TODO: find a way to reuse prepared index?
+    std::vector edgesA = geomA.extractSegmentStrings(GEOM_A, envInt);
+
+    EdgeSetIntersector edgeInt(edgesA, edgesB, envInt);
+    edgeInt.process(intersector);
+}
+
+
+/* private */
+void
+RelateNG::computeEdgesMutual(std::vector& edgesB, const Envelope* envInt, EdgeSegmentIntersector& intersector)
+{
+    //-- in prepared mode the A edge index is reused
+    if (edgeMutualInt == nullptr) {
+        const Envelope* envExtract = geomA.isPrepared() ? nullptr : envInt;
+        std::vector edgesA = geomA.extractSegmentStrings(GEOM_A, envExtract);
+        edgeMutualInt.reset(new MCIndexSegmentSetMutualIntersector(envExtract));
+        edgeMutualInt->setBaseSegments(&edgesA);
+
+    }
+
+    edgeMutualInt->setSegmentIntersector(&intersector);
+    edgeMutualInt->process(&edgesB);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/RelateNode.cpp b/deps/libgeos/geos/src/operation/relateng/RelateNode.cpp
new file mode 100644
index 000000000..0ec3045a6
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/RelateNode.cpp
@@ -0,0 +1,323 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+using geos::geom::CoordinateXY;
+using geos::geom::Geometry;
+using geos::geom::Dimension;
+using geos::geom::Position;
+using geos::io::WKTWriter;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public */
+const CoordinateXY*
+RelateNode::getCoordinate() const
+{
+    return nodePt;
+}
+
+
+/* public */
+const std::vector>&
+RelateNode::getEdges() const
+{
+    return edges;
+}
+
+
+/* public */
+void
+RelateNode::addEdges(std::vector& nss)
+{
+    for (auto* ns : nss) {
+        addEdges(ns);
+    }
+}
+
+/* public */
+void
+RelateNode::addEdges(std::vector>& nss)
+{
+    for (auto& ns : nss) {
+        addEdges(ns.get());
+    }
+}
+
+/* private */
+std::size_t
+RelateNode::indexOf(
+    const std::vector>& vEdges,
+    const RelateEdge* edge) const
+{
+    for (std::size_t i = 0; i < vEdges.size(); i++)
+    {
+        const std::unique_ptr& e = vEdges[i];
+        if (e.get() == edge)
+            return i;
+    }
+    return INDEX_UNKNOWN;
+}
+
+
+/* public */
+void
+RelateNode::addEdges(const NodeSection* ns)
+{
+  //Debug.println("Adding NS: " + ns);
+    switch (ns->dimension()) {
+    case Dimension::L:
+        addLineEdge(ns->isA(), ns->getVertex(0));
+        addLineEdge(ns->isA(), ns->getVertex(1));
+        break;
+    case Dimension::A:
+        //-- assumes node edges have CW orientation (as per JTS norm)
+        //-- entering edge - interior on L
+        const RelateEdge* e0 = addAreaEdge(ns->isA(), ns->getVertex(0), false);
+        //-- exiting edge - interior on R
+        const RelateEdge* e1 = addAreaEdge(ns->isA(), ns->getVertex(1), true);
+
+        std::size_t index0 = indexOf(edges, e0);
+        std::size_t index1 = indexOf(edges, e1);
+        updateEdgesInArea(ns->isA(), index0, index1);
+        updateIfAreaPrev(ns->isA(), index0);
+        updateIfAreaNext(ns->isA(), index1);
+    }
+}
+
+
+/* private */
+void
+RelateNode::updateEdgesInArea(bool isA, std::size_t indexFrom, std::size_t indexTo)
+{
+    std::size_t index = nextIndex(edges, indexFrom);
+    while (index != indexTo) {
+        auto& edge = edges[index];
+        edge->setAreaInterior(isA);
+        index = nextIndex(edges, index);
+    }
+}
+
+
+/* private */
+void
+RelateNode::updateIfAreaPrev(bool isA, std::size_t index)
+{
+    std::size_t indexPrev = prevIndex(edges, index);
+    auto& edgePrev = edges[indexPrev];
+    if (edgePrev->isInterior(isA, Position::LEFT)) {
+        std::unique_ptr& edge = edges[index];
+        edge->setAreaInterior(isA);
+    }
+}
+
+
+/* private */
+void
+RelateNode::updateIfAreaNext(bool isA, std::size_t index)
+{
+    std::size_t indexNext = nextIndex(edges, index);
+    auto& edgeNext = edges[indexNext];
+    if (edgeNext->isInterior(isA, Position::RIGHT)) {
+        auto& edge = edges[index];
+        edge->setAreaInterior(isA);
+    }
+}
+
+
+/* private */
+const RelateEdge*
+RelateNode::addLineEdge(bool isA, const CoordinateXY* dirPt)
+{
+    return addEdge(isA, dirPt, Dimension::L, false);
+}
+
+
+/* private */
+const RelateEdge*
+RelateNode::addAreaEdge(bool isA, const CoordinateXY* dirPt, bool isForward)
+{
+    return addEdge(isA, dirPt, Dimension::A, isForward);
+}
+
+
+/* private */
+const RelateEdge*
+RelateNode::addEdge(bool isA, const CoordinateXY* dirPt, int dim, bool isForward)
+{
+    //-- check for well-formed edge - skip null or zero-len input
+    if (dirPt == nullptr)
+        return nullptr;
+    if (nodePt->equals2D(*dirPt))
+        return nullptr;
+
+    std::size_t insertIndex = INDEX_UNKNOWN;
+    for (std::size_t i = 0; i < edges.size(); i++) {
+        auto* e = edges[i].get();
+        int comp = e->compareToEdge(dirPt);
+        if (comp == 0) {
+            e->merge(isA, dim, isForward);
+            return e;
+        }
+        if (comp == 1) {
+            //-- found further edge, so insert a new edge at this position
+            insertIndex = i;
+            break;
+        }
+    }
+    //-- add a new edge
+    RelateEdge* e = RelateEdge::create(this, dirPt, isA, dim, isForward);
+    if (insertIndex == INDEX_UNKNOWN) {
+        //-- add edge at end of list
+        edges.emplace_back(e);
+    }
+    else {
+        //-- add edge before higher edge found
+        std::unique_ptr re(e);
+        edges.insert(
+            edges.begin() + static_cast(insertIndex),
+            std::move(re));
+    }
+    return e;
+}
+
+
+/* public */
+void
+RelateNode::finish(bool isAreaInteriorA, bool isAreaInteriorB)
+{
+    //Debug.println("finish Node.");
+    //Debug.println("Before: " + this);
+    finishNode(RelateGeometry::GEOM_A, isAreaInteriorA);
+    finishNode(RelateGeometry::GEOM_B, isAreaInteriorB);
+    //Debug.println("After: " + this);
+}
+
+
+/* private */
+void
+RelateNode::finishNode(bool isA, bool isAreaInterior)
+{
+    if (isAreaInterior) {
+        RelateEdge::setAreaInterior(edges, isA);
+    }
+    else {
+        std::size_t startIndex = RelateEdge::findKnownEdgeIndex(edges, isA);
+        //-- only interacting nodes are finished, so this should never happen
+        //Assert.isTrue(startIndex >= 0l, "Node at "+ nodePt + "does not have AB interaction");
+        propagateSideLocations(isA, startIndex);
+    }
+}
+
+
+/* private */
+void
+RelateNode::propagateSideLocations(bool isA, std::size_t startIndex)
+{
+    Location currLoc = edges[startIndex]->location(isA, Position::LEFT);
+    //-- edges are stored in CCW order
+    std::size_t index = nextIndex(edges, startIndex);
+    while (index != startIndex) {
+        const auto& e = edges[index];
+        e->setUnknownLocations(isA, currLoc);
+        currLoc = e->location(isA, Position::LEFT);
+        index = nextIndex(edges, index);
+    }
+}
+
+
+/* private static */
+std::size_t
+RelateNode::prevIndex(
+    std::vector>& list,
+    std::size_t index)
+{
+    if (index > 0 && index != INDEX_UNKNOWN) {
+        return index - 1;
+    }
+    //-- index == 0
+    return list.size() - 1;
+}
+
+
+/* private static */
+std::size_t
+RelateNode::nextIndex(
+    std::vector>& list,
+    std::size_t index)
+{
+    if (index >= list.size() - 1 || index == INDEX_UNKNOWN) {
+        return 0;
+    }
+    return index + 1;
+}
+
+
+/* public */
+bool
+RelateNode::hasExteriorEdge(bool isA)
+{
+    for (auto& e : edges) {
+        if (Location::EXTERIOR == e->location(isA, Position::LEFT) ||
+            Location::EXTERIOR == e->location(isA, Position::RIGHT))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+
+/* public */
+std::string
+RelateNode::toString() const
+{
+    std::stringstream ss;
+    ss << "Node[" << WKTWriter::toPoint(*nodePt) << "]:" << std::endl;
+    for (auto& e : edges) {
+        ss << e->toString() << std::endl;
+    }
+    return ss.str();
+}
+
+
+/* public friend */
+std::ostream&
+operator<<(std::ostream& os, const RelateNode& rn)
+{
+    os << rn.toString();
+    return os;
+}
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/RelatePointLocator.cpp b/deps/libgeos/geos/src/operation/relateng/RelatePointLocator.cpp
new file mode 100644
index 000000000..f727bc089
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/RelatePointLocator.cpp
@@ -0,0 +1,338 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+using geos::algorithm::PointLocation;
+using geos::algorithm::locate::IndexedPointInAreaLocator;
+using geos::algorithm::locate::PointOnGeometryLocator;
+using geos::algorithm::locate::SimplePointInAreaLocator;
+using namespace geos::geom;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* private */
+void
+RelatePointLocator::init(const Geometry* p_geom)
+{
+    //-- cache empty status, since may be checked many times
+    isEmpty = p_geom->isEmpty();
+    extractElements(p_geom);
+
+    if (!lines.empty()) {
+        lineBoundary.reset(new LinearBoundary(lines, boundaryRule));
+    }
+
+    if (!polygons.empty()) {
+        polyLocator.resize(polygons.size());
+    }
+}
+
+
+/* public */
+bool
+RelatePointLocator::hasBoundary() const
+{
+    return lineBoundary->hasBoundary();
+}
+
+
+/* private */
+void
+RelatePointLocator::extractElements(const Geometry* p_geom)
+{
+    if (p_geom->isEmpty())
+        return;
+
+    if (p_geom->getGeometryTypeId() == GEOS_POINT) {
+        addPoint(static_cast(p_geom));
+    }
+    else if (p_geom->getGeometryTypeId() == GEOS_LINESTRING ||
+             p_geom->getGeometryTypeId() == GEOS_LINEARRING) {
+        addLine(static_cast(p_geom));
+    }
+    else if (p_geom->getGeometryTypeId() == GEOS_POLYGON ||
+             p_geom->getGeometryTypeId() == GEOS_MULTIPOLYGON)
+    {
+        addPolygonal(p_geom);
+    }
+    else if (p_geom->isCollection()) {
+        for (std::size_t i = 0; i < p_geom->getNumGeometries(); i++) {
+            const Geometry* g = p_geom->getGeometryN(i);
+            extractElements(g);
+        }
+    }
+}
+
+
+/* private */
+void
+RelatePointLocator::addPoint(const Point* pt)
+{
+    points.insert(pt->getCoordinate());
+}
+
+
+/* private */
+void
+RelatePointLocator::addLine(const LineString* line)
+{
+    lines.push_back(line);
+}
+
+
+/* private */
+void
+RelatePointLocator::addPolygonal(const Geometry* polygonal)
+{
+    polygons.push_back(polygonal);
+}
+
+
+/* public */
+Location
+RelatePointLocator::locate(const CoordinateXY* p)
+{
+    return DimensionLocation::location(locateWithDim(p));
+}
+
+
+/* public */
+int
+RelatePointLocator::locateLineEndWithDim(const CoordinateXY* p)
+{
+    if (!polygons.empty()) {
+        Location locPoly = locateOnPolygons(p, false, nullptr);
+        if (locPoly != Location::EXTERIOR)
+            return DimensionLocation::locationArea(locPoly);
+    }
+    return lineBoundary->isBoundary(p)
+        ? DimensionLocation::LINE_BOUNDARY
+        : DimensionLocation::LINE_INTERIOR;
+}
+
+
+/* public */
+Location
+RelatePointLocator::locateNode(const CoordinateXY* p, const Geometry* parentPolygonal)
+{
+    return DimensionLocation::location(locateNodeWithDim(p, parentPolygonal));
+}
+
+
+/* public */
+int
+RelatePointLocator::locateNodeWithDim(const CoordinateXY* p, const Geometry* parentPolygonal)
+{
+    return locateWithDim(p, true, parentPolygonal);
+}
+
+
+/* public */
+int
+RelatePointLocator::locateWithDim(const CoordinateXY* p)
+{
+    return locateWithDim(p, false, nullptr);
+}
+
+
+/* private */
+int
+RelatePointLocator::locateWithDim(const CoordinateXY* p, bool isNode, const Geometry* parentPolygonal)
+{
+    if (isEmpty) return DimensionLocation::EXTERIOR;
+
+    /**
+     * In a polygonal geometry a node must be on the boundary.
+     * (This is not the case for a mixed collection, since
+     * the node may be in the interior of a polygon.)
+     */
+    GeometryTypeId geomType = geom->getGeometryTypeId();
+    if (isNode && (geomType == GEOS_POLYGON || geomType == GEOS_MULTIPOLYGON))
+        return DimensionLocation::AREA_BOUNDARY;
+
+    int dimLoc = computeDimLocation(p, isNode, parentPolygonal);
+    return dimLoc;
+}
+
+
+/* private */
+int
+RelatePointLocator::computeDimLocation(const CoordinateXY* p, bool isNode, const Geometry* parentPolygonal)
+{
+    //-- check dimensions in order of precedence
+    if (!polygons.empty()) {
+        Location locPoly = locateOnPolygons(p, isNode, parentPolygonal);
+        if (locPoly != Location::EXTERIOR)
+            return DimensionLocation::locationArea(locPoly);
+    }
+    if (!lines.empty()) {
+        Location locLine = locateOnLines(p, isNode);
+        if (locLine != Location::EXTERIOR)
+            return DimensionLocation::locationLine(locLine);
+    }
+    if (!points.empty()) {
+        Location locPt = locateOnPoints(p);
+        if (locPt != Location::EXTERIOR)
+            return DimensionLocation::locationPoint(locPt);
+    }
+    return DimensionLocation::EXTERIOR;
+}
+
+
+/* private */
+Location
+RelatePointLocator::locateOnPoints(const CoordinateXY* p) const
+{
+    auto search = points.find(p);
+    if (search != points.end())
+        return Location::INTERIOR;
+    else
+        return Location::EXTERIOR;
+}
+
+
+/* private */
+Location
+RelatePointLocator::locateOnLines(const CoordinateXY* p, bool isNode)
+{
+    if (lineBoundary != nullptr && lineBoundary->isBoundary(p)) {
+        return Location::BOUNDARY;
+    }
+    //-- must be on line, in interior
+    if (isNode)
+        return Location::INTERIOR;
+
+    //TODO: index the lines
+    for (const LineString* line : lines) {
+        //-- have to check every line, since any/all may contain point
+        Location loc = locateOnLine(p, /*isNode,*/ line);
+        if (loc != Location::EXTERIOR)
+            return loc;
+        //TODO: minor optimization - some BoundaryNodeRules can short-circuit
+    }
+    return Location::EXTERIOR;
+}
+
+
+/* private */
+Location
+RelatePointLocator::locateOnLine(const CoordinateXY* p, /*bool isNode,*/ const LineString* l)
+{
+    // bounding-box check
+    if (! l->getEnvelopeInternal()->intersects(*p))
+        return Location::EXTERIOR;
+
+    const CoordinateSequence* seq = l->getCoordinatesRO();
+    if (PointLocation::isOnLine(*p, seq)) {
+        return Location::INTERIOR;
+    }
+    return Location::EXTERIOR;
+}
+
+
+/* private */
+Location
+RelatePointLocator::locateOnPolygons(const CoordinateXY* p, bool isNode, const Geometry* parentPolygonal)
+{
+    int numBdy = 0;
+    //TODO: use a spatial index on the polygons
+    for (std::size_t i = 0; i < polygons.size(); i++) {
+        Location loc = locateOnPolygonal(p, isNode, parentPolygonal, i);
+        if (loc == Location::INTERIOR) {
+            return Location::INTERIOR;
+        }
+        if (loc == Location::BOUNDARY) {
+            numBdy += 1;
+        }
+    }
+    if (numBdy == 1) {
+        return Location::BOUNDARY;
+    }
+    //-- check for point lying on adjacent boundaries
+    else if (numBdy > 1) {
+        if (adjEdgeLocator == nullptr) {
+            adjEdgeLocator.reset(new AdjacentEdgeLocator(geom));
+        }
+        return adjEdgeLocator->locate(p);
+    }
+    return Location::EXTERIOR;
+}
+
+
+/* private */
+Location
+RelatePointLocator::locateOnPolygonal(const CoordinateXY* p,
+    bool isNode,
+    const Geometry* parentPolygonal,
+    std::size_t index)
+{
+    const Geometry* polygonal = polygons[index];
+    if (isNode && parentPolygonal == polygonal) {
+        return Location::BOUNDARY;
+    }
+    PointOnGeometryLocator* locator = getLocator(index);
+    return locator->locate(p);
+}
+
+
+/* private */
+PointOnGeometryLocator *
+RelatePointLocator::getLocator(std::size_t index)
+{
+    std::unique_ptr& locator = polyLocator[index];
+    if (locator == nullptr) {
+        const Geometry* polygonal = polygons[index];
+        if (isPrepared) {
+            locator.reset(new IndexedPointInAreaLocator(*polygonal));
+        }
+        else {
+            locator.reset(new SimplePointInAreaLocator(*polygonal));
+        }
+    }
+    return locator.get();
+}
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/RelatePredicate.cpp b/deps/libgeos/geos/src/operation/relateng/RelatePredicate.cpp
new file mode 100644
index 000000000..3efe5537f
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/RelatePredicate.cpp
@@ -0,0 +1,109 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+// #include 
+
+#include 
+
+
+// using geos::geom::Envelope;
+// using geos::geom::Location;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public static */
+std::unique_ptr
+RelatePredicate::intersects()
+{
+    return std::unique_ptr(new IntersectsPredicate());
+}
+
+/* public static */
+std::unique_ptr
+RelatePredicate::disjoint()
+{
+    return std::unique_ptr(new DisjointPredicate());
+}
+
+/* public static */
+std::unique_ptr
+RelatePredicate::contains()
+{
+    return std::unique_ptr(new ContainsPredicate());
+}
+
+/* public static */
+std::unique_ptr
+RelatePredicate::within()
+{
+    return std::unique_ptr(new WithinPredicate());
+}
+
+/* public static */
+std::unique_ptr
+RelatePredicate::covers()
+{
+    return std::unique_ptr(new CoversPredicate());
+}
+
+/* public static */
+std::unique_ptr
+RelatePredicate::coveredBy()
+{
+    return std::unique_ptr(new CoveredByPredicate());
+}
+
+/* public static */
+std::unique_ptr
+RelatePredicate::crosses()
+{
+    return std::unique_ptr(new CrossesPredicate());
+}
+
+
+/* public static */
+std::unique_ptr
+RelatePredicate::equalsTopo()
+{
+    return std::unique_ptr(new EqualsTopoPredicate());
+}
+
+
+/* public static */
+std::unique_ptr
+RelatePredicate::overlaps()
+{
+    return std::unique_ptr(new OverlapsPredicate());
+}
+
+/* public static */
+std::unique_ptr
+RelatePredicate::touches()
+{
+    return std::unique_ptr(new TouchesPredicate());
+}
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/RelateSegmentString.cpp b/deps/libgeos/geos/src/operation/relateng/RelateSegmentString.cpp
new file mode 100644
index 000000000..41c0c4bbf
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/RelateSegmentString.cpp
@@ -0,0 +1,161 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+using geos::geom::Coordinate;
+using geos::geom::CoordinateXY;
+using geos::geom::Dimension;
+using geos::geom::Geometry;
+using geos::algorithm::Orientation;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* public static */
+const RelateSegmentString*
+RelateSegmentString::createLine(
+    const CoordinateSequence* pts,
+    bool isA, int elementId,
+    const RelateGeometry* parent)
+{
+    return createSegmentString(pts, isA, Dimension::L, elementId, -1, nullptr, parent);
+}
+
+
+/* public static */
+const RelateSegmentString*
+RelateSegmentString::createRing(
+    const CoordinateSequence* pts,
+    bool isA, int elementId, int ringId,
+    const Geometry* poly, const RelateGeometry* parent)
+{
+    return createSegmentString(pts, isA, Dimension::A, elementId, ringId, poly, parent);
+}
+
+
+/* private static */
+const RelateSegmentString*
+RelateSegmentString::createSegmentString(
+    const CoordinateSequence* pts,
+    bool isA, int dim, int elementId, int ringId,
+    const Geometry* poly, const RelateGeometry* parent)
+{
+    return new RelateSegmentString(pts, isA, dim, elementId, ringId, poly, parent);
+}
+
+
+/* public */
+NodeSection*
+RelateSegmentString::createNodeSection(std::size_t segIndex, const CoordinateXY intPt) const
+{
+    const CoordinateXY& c0 = getCoordinate(segIndex);
+    const CoordinateXY& c1 = getCoordinate(segIndex + 1);
+    bool isNodeAtVertex = intPt.equals2D(c0) || intPt.equals2D(c1);
+    const CoordinateXY* prev = prevVertex(segIndex, &intPt);
+    const CoordinateXY* next = nextVertex(segIndex, &intPt);
+    NodeSection* a = new NodeSection(m_isA, m_dimension, m_id, m_ringId, m_parentPolygonal, isNodeAtVertex, prev, intPt, next);
+    return a;
+}
+
+
+/* private */
+const CoordinateXY*
+RelateSegmentString::prevVertex(std::size_t segIndex, const CoordinateXY* pt) const
+{
+    const CoordinateXY& segStart = getCoordinate(segIndex);
+    if (! segStart.equals2D(*pt))
+        return &segStart;
+
+    //-- pt is at segment start, so get previous vertex
+    if (segIndex > 0) {
+        const CoordinateXY& seg = getCoordinate(segIndex - 1);
+        return &seg;
+    }
+
+    if (isClosed())
+        return &(prevInRing(segIndex));
+
+    return nullptr;
+}
+
+
+/* private */
+const CoordinateXY*
+RelateSegmentString::nextVertex(std::size_t segIndex, const CoordinateXY* pt) const
+{
+    const CoordinateXY& segEnd = getCoordinate(segIndex + 1);
+    if (! segEnd.equals2D(*pt))
+        return &segEnd;
+
+    //-- pt is at seg end, so get next vertex
+    if (size() == 2 && segIndex == INDEX_UNKNOWN) {
+        const CoordinateXY& seg = getCoordinate(0);
+        return &seg;
+    }
+
+    if (segIndex < size() - 2) {
+        const CoordinateXY& seg = getCoordinate(segIndex + 2);
+        return &seg;
+    }
+
+    if (isClosed())
+        return &(SegmentString::nextInRing(segIndex + 1));
+
+    //-- segstring is not closed, so there is no next segment
+    return nullptr;
+}
+
+
+/* public */
+bool
+RelateSegmentString::isContainingSegment(std::size_t segIndex, const CoordinateXY* pt) const
+{
+    //-- intersection is at segment start vertex - process it
+    const CoordinateXY& c0 = getCoordinate(segIndex);
+    if (pt->equals2D(c0))
+        return true;
+    const CoordinateXY& c1 = getCoordinate(segIndex+1);
+    if (pt->equals2D(c1)) {
+        bool isFinalSegment = segIndex == size() - 2;
+        if (isClosed() || ! isFinalSegment)
+            return false;
+        //-- for final segment, process intersections with final endpoint
+        return true;
+    }
+    //-- intersection is interior - process it
+    return true;
+}
+
+
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
diff --git a/deps/libgeos/geos/src/operation/relateng/TopologyComputer.cpp b/deps/libgeos/geos/src/operation/relateng/TopologyComputer.cpp
new file mode 100644
index 000000000..1e1c64683
--- /dev/null
+++ b/deps/libgeos/geos/src/operation/relateng/TopologyComputer.cpp
@@ -0,0 +1,568 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2024 Martin Davis
+ * Copyright (C) 2024 Paul Ramsey 
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+using geos::algorithm::PolygonNodeTopology;
+using geos::geom::Coordinate;
+using geos::geom::CoordinateXY;
+using geos::geom::Geometry;
+using geos::geom::Dimension;
+using geos::geom::Location;
+using geos::geom::Position;
+using geos::util::IllegalStateException;
+
+
+namespace geos {      // geos
+namespace operation { // geos.operation
+namespace relateng {  // geos.operation.relateng
+
+
+/* private */
+void
+TopologyComputer::initExteriorDims()
+{
+    int dimRealA = geomA.getDimensionReal();
+    int dimRealB = geomB.getDimensionReal();
+
+    /**
+     * For P/L case, P exterior intersects L interior
+     */
+    if (dimRealA == Dimension::P && dimRealB == Dimension::L) {
+        updateDim(Location::EXTERIOR, Location::INTERIOR, Dimension::L);
+    }
+    else if (dimRealA == Dimension::L && dimRealB == Dimension::P) {
+        updateDim(Location::INTERIOR, Location::EXTERIOR, Dimension::L);
+    }
+    /**
+     * For P/A case, the Area Int and Bdy intersect the Point exterior.
+     */
+    else if (dimRealA == Dimension::P && dimRealB == Dimension::A) {
+        updateDim(Location::EXTERIOR, Location::INTERIOR, Dimension::A);
+        updateDim(Location::EXTERIOR, Location::BOUNDARY, Dimension::L);
+    }
+    else if (dimRealA == Dimension::A && dimRealB == Dimension::P) {
+        updateDim(Location::INTERIOR, Location::EXTERIOR, Dimension::A);
+        updateDim(Location::BOUNDARY, Location::EXTERIOR, Dimension::L);
+    }
+    else if (dimRealA == Dimension::L && dimRealB == Dimension::A) {
+        updateDim(Location::EXTERIOR, Location::INTERIOR, Dimension::A);
+     }
+    else if (dimRealA == Dimension::A && dimRealB == Dimension::L) {
+        updateDim(Location::INTERIOR, Location::EXTERIOR, Dimension::A);
+    }
+    //-- cases where one geom is EMPTY
+    else if (dimRealA == Dimension::False || dimRealB == Dimension::False) {
+        if (dimRealA != Dimension::False) {
+            initExteriorEmpty(RelateGeometry::GEOM_A);
+        }
+        if (dimRealB != Dimension::False) {
+            initExteriorEmpty(RelateGeometry::GEOM_B);
+        }
+    }
+}
+
+
+/* private */
+void
+TopologyComputer::initExteriorEmpty(bool geomNonEmpty)
+{
+    int dimNonEmpty = getDimension(geomNonEmpty);
+    switch (dimNonEmpty) {
+        case Dimension::P:
+            updateDim(geomNonEmpty, Location::INTERIOR, Location::EXTERIOR, Dimension::P);
+            break;
+        case Dimension::L:
+            if (getGeometry(geomNonEmpty).hasBoundary()) {
+                updateDim(geomNonEmpty, Location::BOUNDARY, Location::EXTERIOR, Dimension::P);
+            }
+            updateDim(geomNonEmpty, Location::INTERIOR, Location::EXTERIOR, Dimension::L);
+            break;
+        case Dimension::A:
+            updateDim(geomNonEmpty, Location::BOUNDARY, Location::EXTERIOR, Dimension::L);
+            updateDim(geomNonEmpty, Location::INTERIOR, Location::EXTERIOR, Dimension::A);
+            break;
+    }
+}
+
+
+/* public */
+bool
+TopologyComputer::isAreaArea() const
+{
+    return getDimension(RelateGeometry::GEOM_A) == Dimension::A
+        && getDimension(RelateGeometry::GEOM_B) == Dimension::A;
+}
+
+
+/* public */
+int
+TopologyComputer::getDimension(bool isA) const
+{
+    return getGeometry(isA).getDimension();
+}
+
+
+/* public */
+bool
+TopologyComputer::isSelfNodingRequired() const
+{
+    if (predicate.requireSelfNoding()) {
+        if (geomA.isSelfNodingRequired() ||
+            geomB.isSelfNodingRequired())
+        return true;
+    }
+    return false;
+}
+
+
+/* public */
+bool
+TopologyComputer::isExteriorCheckRequired(bool isA) const
+{
+    return predicate.requireExteriorCheck(isA);
+}
+
+// static char
+// toSymbol(Location loc) {
+//     switch (loc) {
+//         case Location::NONE: return '-';
+//         case Location::INTERIOR: return 'I';
+//         case Location::BOUNDARY: return 'B';
+//         case Location::EXTERIOR: return 'E';
+//     }
+//     return ' ';
+// }
+
+/* private */
+void
+TopologyComputer::updateDim(Location locA, Location locB, int dimension)
+{
+    //std::cout << toSymbol(locA) << toSymbol(locB) << " <- " << dimension << std::endl;
+    predicate.updateDimension(locA, locB, dimension);
+}
+
+/* private */
+void
+TopologyComputer::updateDim(bool isAB, Location loc1, Location loc2, int dimension)
+{
+    if (isAB) {
+        updateDim(loc1, loc2, dimension);
+    }
+    else {
+        // is ordered BA
+        updateDim(loc2, loc1, dimension);
+    }
+}
+
+
+/* public */
+bool
+TopologyComputer::isResultKnown() const
+{
+    return predicate.isKnown();
+}
+
+
+/* public */
+bool
+TopologyComputer::getResult() const
+{
+    return predicate.value();
+}
+
+
+/* public */
+void
+TopologyComputer::finish()
+{
+    predicate.finish();
+}
+
+
+/* private */
+NodeSections *
+TopologyComputer::getNodeSections(const CoordinateXY& nodePt)
+{
+    NodeSections* ns;
+    auto result = nodeMap.find(nodePt);
+    if (result == nodeMap.end()) {
+        ns = new NodeSections(&nodePt);
+        nodeSectionsStore.emplace_back(ns);
+        nodeMap[nodePt] = ns;
+    }
+    else {
+        ns = result->second;
+    }
+    return ns;
+}
+
+
+/* public */
+void
+TopologyComputer::addIntersection(NodeSection* a, NodeSection* b)
+{
+    // add edges to node to allow full topology evaluation later
+    // we run this first (unlike JTS) in case the subsequent test throws
+    // an exception and the NodeSection pointers are not correctly
+    // saved in the memory managed store on the NodeSections, causing
+    // a small memeory leak
+    addNodeSections(a, b);
+
+    if (! a->isSameGeometry(b)) {
+        updateIntersectionAB(a, b);
+    }
+}
+
+
+/* private */
+void
+TopologyComputer::updateIntersectionAB(const NodeSection* a, const NodeSection* b)
+{
+    if (NodeSection::isAreaArea(*a, *b)) {
+        updateAreaAreaCross(a, b);
+    }
+    updateNodeLocation(a, b);
+}
+
+
+/* private */
+void
+TopologyComputer::updateAreaAreaCross(const NodeSection* a, const NodeSection* b)
+{
+    bool isProper = NodeSection::isProper(*a, *b);
+    if (isProper || PolygonNodeTopology::isCrossing(&(a->nodePt()),
+        a->getVertex(0), a->getVertex(1),
+        b->getVertex(0), b->getVertex(1)))
+    {
+        updateDim(Location::INTERIOR, Location::INTERIOR, Dimension::A);
+    }
+}
+
+
+/* private */
+void
+TopologyComputer::updateNodeLocation(const NodeSection* a, const NodeSection* b)
+{
+    const CoordinateXY& pt = a->nodePt();
+    Location locA = geomA.locateNode(&pt, a->getPolygonal());
+    Location locB = geomB.locateNode(&pt, b->getPolygonal());
+    updateDim(locA, locB, Dimension::P);
+}
+
+/* private */
+void
+TopologyComputer::addNodeSections(NodeSection* ns0, NodeSection* ns1)
+{
+    NodeSections* sections = getNodeSections(ns0->nodePt());
+    sections->addNodeSection(ns0);
+    sections->addNodeSection(ns1);
+}
+
+/* public */
+void
+TopologyComputer::addPointOnPointInterior(const CoordinateXY* pt)
+{
+    (void)pt;
+    updateDim(Location::INTERIOR, Location::INTERIOR, Dimension::P);
+}
+
+/* public */
+void
+TopologyComputer::addPointOnPointExterior(bool isGeomA, const CoordinateXY* pt)
+{
+    (void)pt;
+    updateDim(isGeomA, Location::INTERIOR, Location::EXTERIOR, Dimension::P);
+}
+
+/* public */
+void
+TopologyComputer::addPointOnGeometry(bool isA, Location locTarget, int dimTarget, const CoordinateXY* pt)
+{
+    (void)pt;
+    updateDim(isA, Location::INTERIOR, locTarget, Dimension::P);
+    switch (dimTarget) {
+    case Dimension::P:
+        return;
+    case Dimension::L:
+        /**
+         * Because zero-length lines are handled,
+         * a point lying in the exterior of the line target
+         * may imply either P or L for the Exterior interaction
+         */
+        //TODO: determine if effective dimension of linear target is L?
+        //updateDim(isGeomA, Location::EXTERIOR, locTarget, Dimension::P);
+        return;
+    case Dimension::A:
+        /**
+         * If a point intersects an area target, then the area interior and boundary
+         * must extend beyond the point and thus interact with its exterior.
+         */
+        updateDim(isA, Location::EXTERIOR, Location::INTERIOR, Dimension::A);
+        updateDim(isA, Location::EXTERIOR, Location::BOUNDARY, Dimension::L);
+        return;
+    }
+    throw IllegalStateException("Unknown target dimension: " + std::to_string(dimTarget));
+}
+
+/* public */
+void
+TopologyComputer::addLineEndOnGeometry(bool isLineA, Location locLineEnd, Location locTarget, int dimTarget, const CoordinateXY* pt)
+{
+    (void)pt;
+
+    //-- record topology at line end point
+    updateDim(isLineA, locLineEnd, locTarget, Dimension::P);
+
+    //-- Line and Area targets may have additional topology
+    switch (dimTarget) {
+    case Dimension::P:
+        return;
+    case Dimension::L:
+        addLineEndOnLine(isLineA, locLineEnd, locTarget, pt);
+        return;
+    case Dimension::A:
+        addLineEndOnArea(isLineA, locLineEnd, locTarget, pt);
+        return;
+    }
+    throw IllegalStateException("Unknown target dimension: " + std::to_string(dimTarget));
+}
+
+
+/* private */
+void
+TopologyComputer::addLineEndOnLine(bool isLineA, Location locLineEnd, Location locLine, const CoordinateXY* pt)
+{
+    (void)pt;
+    (void)locLineEnd;
+    /**
+     * When a line end is in the EXTERIOR of a Line,
+     * some length of the source Line INTERIOR
+     * is also in the target Line EXTERIOR.
+     * This works for zero-length lines as well.
+     */
+    if (locLine == Location::EXTERIOR) {
+        updateDim(isLineA, Location::INTERIOR, Location::EXTERIOR, Dimension::L);
+    }
+}
+
+
+/* private */
+void
+TopologyComputer::addLineEndOnArea(bool isLineA, Location locLineEnd, Location locArea, const CoordinateXY* pt)
+{
+    (void)pt;
+    (void)locLineEnd;
+    if (locArea != Location::BOUNDARY) {
+        /**
+         * When a line end is in an Area INTERIOR or EXTERIOR
+         * some length of the source Line Interior
+         * AND the Exterior of the line
+         * is also in that location of the target.
+         * NOTE: this assumes the line end is NOT also in an Area of a mixed-dim GC
+         */
+        //TODO: handle zero-length lines?
+        updateDim(isLineA, Location::INTERIOR, locArea, Dimension::L);
+        updateDim(isLineA, Location::EXTERIOR, locArea, Dimension::A);
+    }
+}
+
+
+/* public */
+void
+TopologyComputer::addAreaVertex(bool isAreaA, Location locArea, Location locTarget, int dimTarget, const CoordinateXY* pt)
+{
+    (void)pt;
+    if (locTarget == Location::EXTERIOR) {
+        updateDim(isAreaA, Location::INTERIOR, Location::EXTERIOR, Dimension::A);
+        /**
+         * If area vertex is on Boundary further topology can be deduced
+         * from the neighbourhood around the boundary vertex.
+         * This is always the case for polygonal geometries.
+         * For GCs, the vertex may be either on boundary or in interior
+         * (i.e. of overlapping or adjacent polygons)
+         */
+        if (locArea == Location::BOUNDARY) {
+            updateDim(isAreaA, Location::BOUNDARY, Location::EXTERIOR, Dimension::L);
+            updateDim(isAreaA, Location::EXTERIOR, Location::EXTERIOR, Dimension::A);
+        }
+        return;
+    }
+
+    switch (dimTarget) {
+        case Dimension::P:
+            addAreaVertexOnPoint(isAreaA, locArea, pt);
+            return;
+        case Dimension::L:
+            addAreaVertexOnLine(isAreaA, locArea, locTarget, pt);
+            return;
+        case Dimension::A:
+            addAreaVertexOnArea(isAreaA, locArea, locTarget, pt);
+            return;
+    }
+    throw IllegalStateException("Unknown target dimension: " + std::to_string(dimTarget));
+}
+
+
+/* private */
+void
+TopologyComputer::addAreaVertexOnPoint(bool isAreaA, Location locArea, const CoordinateXY* pt)
+{
+    (void)pt;
+    //-- Assert: locArea != EXTERIOR
+    //-- Assert: locTarget == INTERIOR
+    /**
+     * The vertex location intersects the Point.
+     */
+    updateDim(isAreaA, locArea, Location::INTERIOR, Dimension::P);
+    /**
+     * The area interior intersects the point's exterior neighbourhood.
+     */
+    updateDim(isAreaA, Location::INTERIOR, Location::EXTERIOR, Dimension::A);
+    /**
+     * If the area vertex is on the boundary,
+     * the area boundary and exterior intersect the point's exterior neighbourhood
+     */
+    if (locArea == Location::BOUNDARY) {
+        updateDim(isAreaA, Location::BOUNDARY, Location::EXTERIOR, Dimension::L);
+        updateDim(isAreaA, Location::EXTERIOR, Location::EXTERIOR, Dimension::A);
+    }
+}
+
+
+/* private */
+void
+TopologyComputer::addAreaVertexOnLine(bool isAreaA, Location locArea, Location locTarget, const CoordinateXY* pt)
+{
+    (void)pt;
+    //-- Assert: locArea != EXTERIOR
+    /**
+     * If an area vertex intersects a line, all we know is the
+     * intersection at that point.
+     * e.g. the line may or may not be collinear with the area boundary,
+     * and the line may or may not intersect the area interior.
+     * Full topology is determined later by node analysis
+     */
+    updateDim(isAreaA, locArea, locTarget, Dimension::P);
+    if (locArea == Location::INTERIOR) {
+        /**
+         * The area interior intersects the line's exterior neighbourhood.
+         */
+        updateDim(isAreaA, Location::INTERIOR, Location::EXTERIOR, Dimension::A);
+    }
+}
+
+
+/* public */
+void
+TopologyComputer::addAreaVertexOnArea(bool isAreaA, Location locArea, Location locTarget, const CoordinateXY* pt)
+{
+    (void)pt;
+    if (locTarget == Location::BOUNDARY) {
+        if (locArea == Location::BOUNDARY) {
+            //-- B/B topology is fully computed later by node analysis
+            updateDim(isAreaA, Location::BOUNDARY, Location::BOUNDARY, Dimension::P);
+        }
+        else {
+            // locArea == INTERIOR
+            updateDim(isAreaA, Location::INTERIOR, Location::INTERIOR, Dimension::A);
+            updateDim(isAreaA, Location::INTERIOR, Location::BOUNDARY, Dimension::L);
+            updateDim(isAreaA, Location::INTERIOR, Location::EXTERIOR, Dimension::A);
+        }
+    }
+    else {
+        //-- locTarget is INTERIOR or EXTERIOR`
+        updateDim(isAreaA, Location::INTERIOR, locTarget, Dimension::A);
+        /**
+         * If area vertex is on Boundary further topology can be deduced
+         * from the neighbourhood around the boundary vertex.
+         * This is always the case for polygonal geometries.
+         * For GCs, the vertex may be either on boundary or in interior
+         * (i.e. of overlapping or adjacent polygons)
+         */
+        if (locArea == Location::BOUNDARY) {
+            updateDim(isAreaA, Location::BOUNDARY, locTarget, Dimension::L);
+            updateDim(isAreaA, Location::EXTERIOR, locTarget, Dimension::A);
+        }
+    }
+}
+
+
+/* public */
+void
+TopologyComputer::evaluateNodes()
+{
+    for (auto& kv : nodeMap) {
+        NodeSections* nodeSections = kv.second;
+        if (nodeSections->hasInteractionAB()) {
+            evaluateNode(nodeSections);
+            if (isResultKnown())
+                return;
+        }
+    }
+}
+
+
+/* private */
+void
+TopologyComputer::evaluateNode(NodeSections* nodeSections)
+{
+    const CoordinateXY* p = nodeSections->getCoordinate();
+    std::unique_ptr node = nodeSections->createNode();
+    //-- Node must have edges for geom, but may also be in interior of a overlapping GC
+    bool isAreaInteriorA = geomA.isNodeInArea(p, nodeSections->getPolygonal(RelateGeometry::GEOM_A));
+    bool isAreaInteriorB = geomB.isNodeInArea(p, nodeSections->getPolygonal(RelateGeometry::GEOM_B));
+    node->finish(isAreaInteriorA, isAreaInteriorB);
+    evaluateNodeEdges(node.get());
+}
+
+
+/* private */
+void
+TopologyComputer::evaluateNodeEdges(const RelateNode* node)
+{
+    //TODO: collect distinct dim settings by using temporary matrix?
+    for (const std::unique_ptr& e : node->getEdges()) {
+        //-- An optimization to avoid updates for cases with a linear geometry
+        if (isAreaArea()) {
+            updateDim(e->location(RelateGeometry::GEOM_A, Position::LEFT),
+                      e->location(RelateGeometry::GEOM_B, Position::LEFT), Dimension::A);
+            updateDim(e->location(RelateGeometry::GEOM_A, Position::RIGHT),
+                      e->location(RelateGeometry::GEOM_B, Position::RIGHT), Dimension::A);
+        }
+        updateDim(e->location(RelateGeometry::GEOM_A, Position::ON),
+                  e->location(RelateGeometry::GEOM_B, Position::ON), Dimension::L);
+    }
+}
+
+
+} // namespace geos.operation.overlayng
+} // namespace geos.operation
+} // namespace geos
+
+
diff --git a/deps/libgeos/geos/src/operation/sharedpaths/SharedPathsOp.cpp b/deps/libgeos/geos/src/operation/sharedpaths/SharedPathsOp.cpp
index 80bfeef25..807881fe1 100644
--- a/deps/libgeos/geos/src/operation/sharedpaths/SharedPathsOp.cpp
+++ b/deps/libgeos/geos/src/operation/sharedpaths/SharedPathsOp.cpp
@@ -108,7 +108,7 @@ SharedPathsOp::findLinearIntersections(PathList& to)
 
     auto full = _g1.intersection(&_g2);
 
-    // NOTE: intersection of equal lines yields splitted lines,
+    // NOTE: intersection of equal lines yields split lines,
     //       should we sew them back ?
 
     for(std::size_t i = 0, n = full->getNumGeometries(); i < n; ++i) {
diff --git a/deps/libgeos/geos/src/operation/union/UnaryUnionOp.cpp b/deps/libgeos/geos/src/operation/union/UnaryUnionOp.cpp
index 8bd96b847..a801e5658 100644
--- a/deps/libgeos/geos/src/operation/union/UnaryUnionOp.cpp
+++ b/deps/libgeos/geos/src/operation/union/UnaryUnionOp.cpp
@@ -35,6 +35,8 @@
 #include 
 #include 
 
+#include "geos/util.h"
+
 namespace geos {
 namespace operation { // geos::operation
 namespace geounion {  // geos::operation::geounion
diff --git a/deps/libgeos/geos/src/operation/valid/IsSimpleOp.cpp b/deps/libgeos/geos/src/operation/valid/IsSimpleOp.cpp
index e072f99fd..065cd18d8 100644
--- a/deps/libgeos/geos/src/operation/valid/IsSimpleOp.cpp
+++ b/deps/libgeos/geos/src/operation/valid/IsSimpleOp.cpp
@@ -115,24 +115,27 @@ IsSimpleOp::computeSimple(const Geometry& geom)
 {
     if (geom.isEmpty()) return true;
     switch(geom.getGeometryTypeId()) {
+        case GEOS_POINT:
+            return true;
         case GEOS_MULTIPOINT:
             return isSimpleMultiPoint(dynamic_cast(geom));
         case GEOS_LINESTRING:
-            return isSimpleLinearGeometry(geom);
         case GEOS_MULTILINESTRING:
             return isSimpleLinearGeometry(geom);
         case GEOS_LINEARRING:
-            return isSimplePolygonal(geom);
         case GEOS_POLYGON:
-            return isSimplePolygonal(geom);
         case GEOS_MULTIPOLYGON:
             return isSimplePolygonal(geom);
         case GEOS_GEOMETRYCOLLECTION:
             return isSimpleGeometryCollection(geom);
-        // all other geometry types are simple by definition
-        default:
-            return true;
+        case GEOS_CIRCULARSTRING:
+        case GEOS_COMPOUNDCURVE:
+        case GEOS_MULTICURVE:
+        case GEOS_CURVEPOLYGON:
+        case GEOS_MULTISURFACE:
+            throw util::UnsupportedOperationException("Curved types not supported in IsSimpleOp.");
     }
+    throw util::UnsupportedOperationException("Unexpected type.");
 }
 
 /* private */
@@ -145,6 +148,8 @@ IsSimpleOp::isSimpleMultiPoint(const MultiPoint& mp)
 
     for (std::size_t i = 0; i < mp.getNumGeometries(); i++) {
         const Point* pt = mp.getGeometryN(i);
+        if (pt->isEmpty())
+            continue;
         const CoordinateXY* p = pt->getCoordinate();
         if (points.find(*p) != points.end()) {
             nonSimplePts.push_back(*p);
diff --git a/deps/libgeos/geos/src/operation/valid/IsValidOp.cpp b/deps/libgeos/geos/src/operation/valid/IsValidOp.cpp
index 39571cc58..454d9a18c 100644
--- a/deps/libgeos/geos/src/operation/valid/IsValidOp.cpp
+++ b/deps/libgeos/geos/src/operation/valid/IsValidOp.cpp
@@ -102,6 +102,12 @@ IsValidOp::isValidGeometry(const Geometry* g)
             return isValid(static_cast(g));
         case GEOS_GEOMETRYCOLLECTION:
             return isValid(static_cast(g));
+        case GEOS_CIRCULARSTRING:
+        case GEOS_COMPOUNDCURVE:
+        case GEOS_CURVEPOLYGON:
+        case GEOS_MULTICURVE:
+        case GEOS_MULTISURFACE:
+            throw util::UnsupportedOperationException("Curved types not supported in IsValidOp.");
     }
 
     // geometry type not known
diff --git a/deps/libgeos/geos/src/operation/valid/PolygonTopologyAnalyzer.cpp b/deps/libgeos/geos/src/operation/valid/PolygonTopologyAnalyzer.cpp
index 39b672b1d..c2b9d0a39 100644
--- a/deps/libgeos/geos/src/operation/valid/PolygonTopologyAnalyzer.cpp
+++ b/deps/libgeos/geos/src/operation/valid/PolygonTopologyAnalyzer.cpp
@@ -180,8 +180,7 @@ PolygonTopologyAnalyzer::intersectingSegIndex(const CoordinateSequence* ringPts,
 {
     algorithm::LineIntersector li;
     for (std::size_t i = 0; i < ringPts->size() - 1; i++) {
-      li.computeIntersection(*pt, ringPts->getAt(i), ringPts->getAt(i+1));
-      if (li.hasIntersection()) {
+      if ( algorithm::PointLocation::isOnSegment(*pt, ringPts->getAt(i), ringPts->getAt(i+1)) ) {
         //-- check if pt is the start point of the next segment
         if (pt->equals2D(ringPts->getAt(i + 1))) {
           return i + 1;
diff --git a/deps/libgeos/geos/src/precision/GeometryPrecisionReducer.cpp b/deps/libgeos/geos/src/precision/GeometryPrecisionReducer.cpp
index 9936ef455..a4d6a3d9c 100644
--- a/deps/libgeos/geos/src/precision/GeometryPrecisionReducer.cpp
+++ b/deps/libgeos/geos/src/precision/GeometryPrecisionReducer.cpp
@@ -41,37 +41,6 @@ namespace geos {
 namespace precision { // geos.precision
 
 
-/* public */
-std::unique_ptr
-GeometryPrecisionReducer::reduce(const Geometry& geom)
-{
-    std::unique_ptr reduced;
-    if (isPointwise) {
-        reduced = PointwisePrecisionReducerTransformer::reduce(geom, targetPM);
-    }
-    else {
-        reduced = PrecisionReducerTransformer::reduce(geom, targetPM, removeCollapsed);
-    }
-
-    // Match the collection level of the output to the input
-    // if necessary
-    if (geom.isCollection()
-        && ! reduced->isCollection()
-        && (geom.getCoordinateDimension() == reduced->getCoordinateDimension()))
-    {
-        reduced = geom.getFactory()->createMulti(std::move(reduced));
-    }
-
-    // TODO: incorporate this in the Transformer above
-    if (changePrecisionModel &&
-        (&targetPM != geom.getFactory()->getPrecisionModel()))
-    {
-         return changePM(reduced.get(), targetPM);
-    }
-
-    return reduced;
-}
-
 
 /* private */
 std::unique_ptr
@@ -156,6 +125,36 @@ GeometryPrecisionReducer::createFactory(const GeometryFactory& oldGF,
     return p_newFactory;
 }
 
+/* public */
+std::unique_ptr
+GeometryPrecisionReducer::reduce(const Geometry& geom)
+{
+    std::unique_ptr reduced;
+    if (isPointwise) {
+        reduced = PointwisePrecisionReducerTransformer::reduce(geom, targetPM);
+    }
+    else {
+        reduced = PrecisionReducerTransformer::reduce(geom, targetPM, removeCollapsed);
+    }
+
+    // Match the collection level of the output to the input
+    // if necessary
+    if (geom.isCollection()
+        && ! reduced->isCollection()
+        && (geom.getCoordinateDimension() == reduced->getCoordinateDimension()))
+    {
+        reduced = geom.getFactory()->createMulti(std::move(reduced));
+    }
+
+    // TODO: incorporate this in the Transformer above
+    if (changePrecisionModel &&
+        (&targetPM != geom.getFactory()->getPrecisionModel()))
+    {
+         return changePM(reduced.get(), targetPM);
+    }
+
+    return reduced;
+}
 
 
 } // namespace geos.precision
diff --git a/deps/libgeos/geos/src/precision/MinimumClearance.cpp b/deps/libgeos/geos/src/precision/MinimumClearance.cpp
index cc6b293d0..0a5e9e823 100644
--- a/deps/libgeos/geos/src/precision/MinimumClearance.cpp
+++ b/deps/libgeos/geos/src/precision/MinimumClearance.cpp
@@ -176,6 +176,10 @@ MinimumClearance::compute()
     MinClearanceDistance mcd;
     auto nearest = tree->nearestNeighbour(mcd);
 
+    if (nearest.first == nullptr || nearest.second == nullptr) {
+        throw util::GEOSException("Failed to find nearest items");
+    }
+
     minClearance = mcd.distance(nearest.first, nearest.second);
 
     const std::vector* minClearancePtsVec = mcd.getCoordinates();
diff --git a/deps/libgeos/geos/src/simplify/ComponentJumpChecker.cpp b/deps/libgeos/geos/src/simplify/ComponentJumpChecker.cpp
new file mode 100644
index 000000000..c09a5f9ab
--- /dev/null
+++ b/deps/libgeos/geos/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 
+ * Copyright (C) 2023 Paul Ramsey 
+ *
+ * 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 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+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/deps/libgeos/geos/src/simplify/DouglasPeuckerLineSimplifier.cpp b/deps/libgeos/geos/src/simplify/DouglasPeuckerLineSimplifier.cpp
index 6a2a456a5..49e040e0b 100644
--- a/deps/libgeos/geos/src/simplify/DouglasPeuckerLineSimplifier.cpp
+++ b/deps/libgeos/geos/src/simplify/DouglasPeuckerLineSimplifier.cpp
@@ -21,6 +21,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include  // for unique_ptr
@@ -58,6 +59,9 @@ void
 DouglasPeuckerLineSimplifier::setDistanceTolerance(
     double nDistanceTolerance)
 {
+    if (std::isnan(nDistanceTolerance)) {
+        throw util::IllegalArgumentException("Tolerance must not be NaN");
+    }
     distanceTolerance = nDistanceTolerance;
 }
 
@@ -110,6 +114,7 @@ DouglasPeuckerLineSimplifier::simplifySection(
     std::size_t j)
 {
     if((i + 1) == j) {
+
         return;
     }
 
diff --git a/deps/libgeos/geos/src/simplify/LineSegmentIndex.cpp b/deps/libgeos/geos/src/simplify/LineSegmentIndex.cpp
index 42b99e18a..7de458c1f 100644
--- a/deps/libgeos/geos/src/simplify/LineSegmentIndex.cpp
+++ b/deps/libgeos/geos/src/simplify/LineSegmentIndex.cpp
@@ -54,15 +54,14 @@ class LineSegmentVisitor: public index::ItemVisitor {
 
     const LineSegment* querySeg;
 
-    std::unique_ptr< std::vector > items;
+    std::vector items;
 
 public:
 
     LineSegmentVisitor(const LineSegment* s)
         :
         ItemVisitor(),
-        querySeg(s),
-        items(new std::vector())
+        querySeg(s)
     {}
 
     ~LineSegmentVisitor() override
@@ -70,43 +69,23 @@ class LineSegmentVisitor: public index::ItemVisitor {
         // nothing to do, LineSegments are not owned by us
     }
 
-    LineSegmentVisitor(const LineSegmentVisitor& o)
-        :
-        ItemVisitor(),
-        querySeg(o.querySeg),
-        items(new std::vector(*(o.items.get())))
-    {
-    }
-
-    LineSegmentVisitor&
-    operator=(const LineSegmentVisitor& o)
-    {
-        if(this == &o) {
-            return *this;
-        }
-        querySeg = o.querySeg;
-        items.reset(new std::vector(*(o.items.get())));
-        return *this;
-    }
-
     void
     visitItem(void* item) override
     {
-        LineSegment* seg = static_cast(item);
+        const LineSegment* seg = static_cast(item);
         if(Envelope::intersects(seg->p0, seg->p1,
                                 querySeg->p0, querySeg->p1)) {
-            items->push_back(seg);
+            items.push_back(seg);
         }
     }
 
-    std::unique_ptr< std::vector >
+    std::vector
     getItems()
     {
         // NOTE: Apparently, this is 'source' method giving up the object resource.
         return std::move(items);
     }
 
-
 };
 
 
@@ -144,7 +123,7 @@ LineSegmentIndex::remove(const LineSegment* seg)
 }
 
 /*public*/
-std::unique_ptr< std::vector >
+std::vector
 LineSegmentIndex::query(const LineSegment* querySeg)
 {
     Envelope env(querySeg->p0, querySeg->p1);
@@ -152,7 +131,7 @@ LineSegmentIndex::query(const LineSegment* querySeg)
     LineSegmentVisitor visitor(querySeg);
     index.query(&env, visitor);
 
-    std::unique_ptr< std::vector > itemsFound = visitor.getItems();
+    auto itemsFound = visitor.getItems();
 
     return itemsFound;
 }
diff --git a/deps/libgeos/geos/src/simplify/LinkedLine.cpp b/deps/libgeos/geos/src/simplify/LinkedLine.cpp
index 798769b5a..684e493e9 100644
--- a/deps/libgeos/geos/src/simplify/LinkedLine.cpp
+++ b/deps/libgeos/geos/src/simplify/LinkedLine.cpp
@@ -128,7 +128,7 @@ LinkedLine::nextCoordinate(std::size_t index) const
 bool
 LinkedLine::hasCoordinate(std::size_t index) const
 {
-    //-- if not a ring, endpoints are alway present
+    //-- if not a ring, endpoints are always present
     if (! m_isRing && (index == 0 || index == m_coord.size() - 1))
         return true;
 
diff --git a/deps/libgeos/geos/src/simplify/TaggedLineString.cpp b/deps/libgeos/geos/src/simplify/TaggedLineString.cpp
index bb4912047..e0dc40ddb 100644
--- a/deps/libgeos/geos/src/simplify/TaggedLineString.cpp
+++ b/deps/libgeos/geos/src/simplify/TaggedLineString.cpp
@@ -44,12 +44,11 @@ namespace simplify { // geos::simplify
 
 /*public*/
 TaggedLineString::TaggedLineString(const geom::LineString* nParentLine,
-                                   std::size_t nMinimumSize,
-                                   bool bPreserveEndpoint)
-    :
-    parentLine(nParentLine),
-    minimumSize(nMinimumSize),
-    preserveEndpoint(bPreserveEndpoint)
+    std::size_t nMinimumSize,
+    bool bIsRing)
+    : parentLine(nParentLine)
+    , minimumSize(nMinimumSize)
+    , m_isRing(bIsRing)
 {
     init();
 }
@@ -115,9 +114,9 @@ TaggedLineString::getMinimumSize() const
 
 /*public*/
 bool
-TaggedLineString::getPreserveEndpoint() const
+TaggedLineString::isRing() const
 {
-    return preserveEndpoint;
+    return m_isRing;
 }
 
 /*public*/
@@ -182,6 +181,30 @@ TaggedLineString::extractCoordinates(
     return pts;
 }
 
+
+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
@@ -258,15 +281,16 @@ TaggedLineString::addToResult(std::unique_ptr seg)
 #endif
 }
 
-void
+const TaggedLineSegment*
 TaggedLineString::removeRingEndpoint()
 {
     auto* firstSeg = resultSegs.front();
     auto* lastSeg = resultSegs.back();
 
     firstSeg->p0 = lastSeg->p0;
-    delete lastSeg;
     resultSegs.pop_back();
+    delete lastSeg;
+    return firstSeg;
 }
 
 } // namespace geos::simplify
diff --git a/deps/libgeos/geos/src/simplify/TaggedLineStringSimplifier.cpp b/deps/libgeos/geos/src/simplify/TaggedLineStringSimplifier.cpp
index 185218f4b..842710ae8 100644
--- a/deps/libgeos/geos/src/simplify/TaggedLineStringSimplifier.cpp
+++ b/deps/libgeos/geos/src/simplify/TaggedLineStringSimplifier.cpp
@@ -16,13 +16,15 @@
  *
  **********************************************************************/
 
-#include 
-#include 
 #include 
-#include 
-#include 
+#include 
 #include 
 #include 
+#include 
+#include 
+#include 
+#include 
+#include 
 #include 
 
 #include 
@@ -37,9 +39,11 @@
 #include 
 #endif
 
-using namespace geos::geom;
-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
@@ -47,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;
@@ -78,17 +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->getPreserveEndpoint() && linePts->isRing()) {
-        simplifyRingEndpoint();
+    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;
 
@@ -140,16 +144,27 @@ 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 = std::make_unique(*(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, 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) {
@@ -167,23 +182,32 @@ 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()
+TaggedLineStringSimplifier::simplifyRingEndpoint(double distanceTolerance)
 {
     if (line->getResultSize() > line->getMinimumSize()) {
-        const auto* firstSeg = line->getResultSegments().front();
-        const auto* lastSeg = line->getResultSegments().back();
-
-        LineSegment candidateSeg(lastSeg->p0, firstSeg->p1);
-        if (candidateSeg.distance(firstSeg->p0) <= distanceTolerance &&
-                ! hasBadIntersection(line, line->getSegments().size() - 2, 0, candidateSeg)) {
-            //auto newSeg = detail::make_unique(candidateSeg.p0, candidateSeg.p1);
-            line->removeRingEndpoint();
+        const auto* firstSeg = static_cast(line->getResultSegments().front());
+        const auto* lastSeg = static_cast(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);
         }
     }
 }
@@ -197,39 +221,66 @@ TaggedLineStringSimplifier::flatten(std::size_t start, std::size_t end)
     const Coordinate& p1 = linePts->getAt(end);
     std::unique_ptr 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 size_t excludeStart, const size_t excludeEnd,
-    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 (hasOutputIntersection(flatSeg))
+        return false;
+    if (hasInputIntersection(lineIn, sectionStart, sectionEnd, flatSeg))
+        return false;
+    if (jumpChecker->hasJump(lineIn, sectionStart, sectionEnd, flatSeg))
+        return false;
+    return true;
+}
 
-    if(hasBadInputIntersection(parentLine, excludeStart, excludeEnd, candidateSeg)) {
+/*private*/
+bool
+TaggedLineStringSimplifier::isTopologyValid(
+    const TaggedLineString* lineIn,
+    const LineSegment* seg1, const LineSegment* seg2,
+    const LineSegment& flatSeg)
+{
+    //-- 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;
+}
 
-    return false;
+/*private*/
+bool
+TaggedLineStringSimplifier::isCollinear(
+    const Coordinate& pt,
+    const LineSegment& seg) const
+{
+    return Orientation::COLLINEAR == seg.orientationIndex(pt);
 }
 
 /*private*/
 bool
-TaggedLineStringSimplifier::hasBadOutputIntersection(
-    const LineSegment& candidateSeg)
+TaggedLineStringSimplifier::hasOutputIntersection(
+    const LineSegment& flatSeg)
 {
-    std::unique_ptr< std::vector > querySegs =
-        outputIndex->query(&candidateSeg);
+    //std::unique_ptr>
+    auto querySegs = outputIndex->query(&flatSeg);
 
-    for(const LineSegment* querySeg : *querySegs) {
-        if(hasInteriorIntersection(*querySeg, candidateSeg)) {
+    for(const LineSegment* querySeg : querySegs) {
+        if(hasInvalidIntersection(*querySeg, flatSeg)) {
             return true;
         }
     }
@@ -239,27 +290,44 @@ TaggedLineStringSimplifier::hasBadOutputIntersection(
 
 /*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 size_t excludeStart, const size_t excludeEnd,
-    const LineSegment& candidateSeg)
+TaggedLineStringSimplifier::hasInputIntersection(const LineSegment& flatSeg)
 {
-    const auto& foundSegs = inputIndex->query(&candidateSeg);
-
-    for(const LineSegment* ls : *foundSegs) {
-        const TaggedLineSegment* foundSeg = static_cast(ls);
+    return hasInputIntersection(nullptr, 0, 0, flatSeg);
+}
 
-        if(!isInLineSection(parentLine, excludeStart, excludeEnd, foundSeg) && hasInteriorIntersection(*foundSeg, candidateSeg)) {
+/*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(ls);
+
+        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;
         }
     }
@@ -270,25 +338,25 @@ TaggedLineStringSimplifier::hasBadInputIntersection(
 /*static private*/
 bool
 TaggedLineStringSimplifier::isInLineSection(
-    const TaggedLineString* line,
-    const size_t excludeStart, const size_t excludeEnd,
+    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 (excludeStart <= excludeEnd) {
-      //-- section is contiguous
-      if (segIndex >= excludeStart && segIndex < excludeEnd)
-        return true;
+        //-- 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;
+        //-- section wraps around the end of a ring
+        if (segIndex >= excludeStart || segIndex <= excludeEnd)
+            return true;
     }
     return false;
 }
@@ -341,8 +409,8 @@ TaggedLineStringSimplifier::findFurthestPoint(
     }
     maxDistance = maxDist;
     return maxIndex;
-
 }
 
+
 } // namespace geos::simplify
 } // namespace geos
diff --git a/deps/libgeos/geos/src/simplify/TaggedLinesSimplifier.cpp b/deps/libgeos/geos/src/simplify/TaggedLinesSimplifier.cpp
index d360c3aee..af75f35af 100644
--- a/deps/libgeos/geos/src/simplify/TaggedLinesSimplifier.cpp
+++ b/deps/libgeos/geos/src/simplify/TaggedLinesSimplifier.cpp
@@ -16,8 +16,9 @@
  *
  **********************************************************************/
 
-#include 
+#include 
 #include 
+#include 
 #include 
 #include 
 
@@ -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& 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/deps/libgeos/geos/src/simplify/TopologyPreservingSimplifier.cpp b/deps/libgeos/geos/src/simplify/TopologyPreservingSimplifier.cpp
index a9a9bd075..85e39e1d5 100644
--- a/deps/libgeos/geos/src/simplify/TopologyPreservingSimplifier.cpp
+++ b/deps/libgeos/geos/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(parent)) {
         LinesMap::iterator it = linestringMap.find(parent);
         assert(it != linestringMap.end());
@@ -170,18 +172,19 @@ void
 LineStringMapBuilderFilter::filter_ro(const Geometry* geom)
 {
     auto typ = geom->getGeometryTypeId();
-    bool preserveEndpoint = true;
+    bool isRing = false;
+
+    if (geom->isEmpty()) return;
 
     if (typ == GEOS_LINEARRING) {
-        preserveEndpoint = false;
+        isRing = true;
     } else if (typ != GEOS_LINESTRING) {
         return;
     }
 
-
     auto ls = static_cast(geom);
     std::size_t minSize = ls->isClosed() ? 4 : 2;
-    TaggedLineString* taggedLine = new TaggedLineString(ls, minSize, preserveEndpoint);
+    TaggedLineString* taggedLine = new TaggedLineString(ls, minSize, isRing);
 
     // Duplicated Geometry pointers shouldn't happen
     if(! linestringMap.insert(std::make_pair(geom, taggedLine)).second) {
@@ -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/deps/libgeos/geos/src/triangulate/DelaunayTriangulationBuilder.cpp b/deps/libgeos/geos/src/triangulate/DelaunayTriangulationBuilder.cpp
index 424de2a13..cd80ecdcd 100644
--- a/deps/libgeos/geos/src/triangulate/DelaunayTriangulationBuilder.cpp
+++ b/deps/libgeos/geos/src/triangulate/DelaunayTriangulationBuilder.cpp
@@ -77,6 +77,8 @@ DelaunayTriangulationBuilder::DelaunayTriangulationBuilder() :
 void
 DelaunayTriangulationBuilder::setSites(const Geometry& geom)
 {
+    util::ensureNoCurvedComponents(geom);
+
     // remove any duplicate points (they will cause the triangulation to fail)
     siteCoords = extractUniqueCoordinates(geom);
 }
diff --git a/deps/libgeos/geos/src/triangulate/IncrementalDelaunayTriangulator.cpp b/deps/libgeos/geos/src/triangulate/IncrementalDelaunayTriangulator.cpp
index 36acaf88f..1b6c545d3 100644
--- a/deps/libgeos/geos/src/triangulate/IncrementalDelaunayTriangulator.cpp
+++ b/deps/libgeos/geos/src/triangulate/IncrementalDelaunayTriangulator.cpp
@@ -23,6 +23,8 @@
 #include 
 #include 
 
+using geos::geom::Coordinate;
+
 namespace geos {
 namespace triangulate { //geos.triangulate
 
diff --git a/deps/libgeos/geos/src/triangulate/VoronoiDiagramBuilder.cpp b/deps/libgeos/geos/src/triangulate/VoronoiDiagramBuilder.cpp
index d4b09c8c5..eeae7f064 100644
--- a/deps/libgeos/geos/src/triangulate/VoronoiDiagramBuilder.cpp
+++ b/deps/libgeos/geos/src/triangulate/VoronoiDiagramBuilder.cpp
@@ -51,6 +51,7 @@ VoronoiDiagramBuilder::VoronoiDiagramBuilder() :
 void
 VoronoiDiagramBuilder::setSites(const geom::Geometry& geom)
 {
+    util::ensureNoCurvedComponents(geom);
     siteCoords = DelaunayTriangulationBuilder::extractUniqueCoordinates(geom);
     inputGeom = &geom;
 }
diff --git a/deps/libgeos/geos/src/triangulate/polygon/ConstrainedDelaunayTriangulator.cpp b/deps/libgeos/geos/src/triangulate/polygon/ConstrainedDelaunayTriangulator.cpp
index c52f85548..5f56b12c2 100644
--- a/deps/libgeos/geos/src/triangulate/polygon/ConstrainedDelaunayTriangulator.cpp
+++ b/deps/libgeos/geos/src/triangulate/polygon/ConstrainedDelaunayTriangulator.cpp
@@ -39,8 +39,10 @@ ConstrainedDelaunayTriangulator::triangulate(const Geometry* geom)
 
 /* private */
 std::unique_ptr
-ConstrainedDelaunayTriangulator::compute()
+ConstrainedDelaunayTriangulator::compute() const
 {
+    util::ensureNoCurvedComponents(inputGeom);
+
     // short circuit empty input case
     if(inputGeom->isEmpty()) {
         auto gf = inputGeom->getFactory();
diff --git a/deps/libgeos/geos/src/triangulate/polygon/TriDelaunayImprover.cpp b/deps/libgeos/geos/src/triangulate/polygon/TriDelaunayImprover.cpp
index 2a6f9ac4c..21bae66f9 100644
--- a/deps/libgeos/geos/src/triangulate/polygon/TriDelaunayImprover.cpp
+++ b/deps/libgeos/geos/src/triangulate/polygon/TriDelaunayImprover.cpp
@@ -138,7 +138,7 @@ TriDelaunayImprover::isInCircle(
     const Coordinate& a, const Coordinate& b,
     const Coordinate& c, const Coordinate& p)
 {
-    return triangulate::quadedge::TrianglePredicate::isInCircleRobust(a, c, b, p);
+    return triangulate::quadedge::TrianglePredicate::isInCircleRobust(a, c, b, p) == geom::Location::INTERIOR;
 }
 
 
diff --git a/deps/libgeos/geos/src/triangulate/quadedge/TrianglePredicate.cpp b/deps/libgeos/geos/src/triangulate/quadedge/TrianglePredicate.cpp
index 4c8da959f..09a606e87 100644
--- a/deps/libgeos/geos/src/triangulate/quadedge/TrianglePredicate.cpp
+++ b/deps/libgeos/geos/src/triangulate/quadedge/TrianglePredicate.cpp
@@ -19,6 +19,7 @@
 #include 
 
 #include 
+#include 
 
 using geos::geom::Coordinate;
 
@@ -26,24 +27,24 @@ namespace geos {
 namespace triangulate {
 namespace quadedge {
 
-bool
+geom::Location
 TrianglePredicate::isInCircleNonRobust(
-    const Coordinate& a, const Coordinate& b, const Coordinate& c,
-    const Coordinate& p)
+    const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c,
+    const CoordinateXY& p)
 {
-    bool isInCircle =
+    auto det =
         (a.x * a.x + a.y * a.y) * triArea(b, c, p)
         - (b.x * b.x + b.y * b.y) * triArea(a, c, p)
         + (c.x * c.x + c.y * c.y) * triArea(a, b, p)
-        - (p.x * p.x + p.y * p.y) * triArea(a, b, c)
-        > 0;
-    return isInCircle;
+        - (p.x * p.x + p.y * p.y) * triArea(a, b, c);
+
+    return det > 0 ? geom::Location::EXTERIOR : (det < 0 ? geom::Location::INTERIOR : geom::Location::BOUNDARY);
 }
 
-bool
+geom::Location
 TrianglePredicate::isInCircleNormalized(
-    const Coordinate& a, const Coordinate& b, const Coordinate& c,
-    const Coordinate& p)
+    const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c,
+    const CoordinateXY& p)
 {
     // Unfortunately this implementation is not robust either. For robust one see:
     // https://www.cs.cmu.edu/~quake/robust.html
@@ -67,22 +68,31 @@ TrianglePredicate::isInCircleNormalized(
     long double adxbdy = adx * bdy;
     long double bdxady = bdx * ady;
     long double clift = cdx * cdx + cdy * cdy;
-    return (alift * bdxcdy + blift * cdxady + clift * adxbdy) >
-           (alift * cdxbdy + blift * adxcdy + clift * bdxady);
+
+    long double A = (alift * bdxcdy + blift * cdxady + clift * adxbdy);
+    long double B = (alift * cdxbdy + blift * adxcdy + clift * bdxady);
+
+    if (A < B) {
+        return geom::Location::EXTERIOR;
+    } else if (A == B) {
+        return geom::Location::BOUNDARY;
+    } else {
+        return geom::Location::INTERIOR;
+    }
 }
 
 double
-TrianglePredicate::triArea(const Coordinate& a,
-                           const Coordinate& b, const Coordinate& c)
+TrianglePredicate::triArea(const CoordinateXY& a,
+                           const CoordinateXY& b, const CoordinateXY& c)
 {
     return (b.x - a.x) * (c.y - a.y)
            - (b.y - a.y) * (c.x - a.x);
 }
 
-bool
+geom::Location
 TrianglePredicate::isInCircleRobust(
-    const Coordinate& a, const Coordinate& b, const Coordinate& c,
-    const Coordinate& p)
+    const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c,
+    const CoordinateXY& p)
 {
     // This implementation is not robust, name is ported from JTS.
     return isInCircleNormalized(a, b, c, p);
diff --git a/deps/libgeos/geos/src/util/GeometricShapeFactory.cpp b/deps/libgeos/geos/src/util/GeometricShapeFactory.cpp
index bfe485460..141ae5939 100644
--- a/deps/libgeos/geos/src/util/GeometricShapeFactory.cpp
+++ b/deps/libgeos/geos/src/util/GeometricShapeFactory.cpp
@@ -17,6 +17,7 @@
  *
  **********************************************************************/
 
+#include 
 #include 
 #include 
 #include 
@@ -31,6 +32,7 @@
 #include 
 
 
+using namespace geos::algorithm;
 using namespace geos::geom;
 
 namespace geos {
@@ -134,10 +136,11 @@ GeometricShapeFactory::createCircle()
 
     auto pts = detail::make_unique(nPts + 1);
     uint32_t iPt = 0;
+    double sinang, cosang;
     for(uint32_t i = 0; i < nPts; i++) {
-        double ang = i * (2 * 3.14159265358979 / nPts);
-        double x = xRadius * cos(ang) + centreX;
-        double y = yRadius * sin(ang) + centreY;
+        Angle::sinCosSnap(i * Angle::PI_TIMES_2 / nPts, sinang, cosang);
+        double x = xRadius * cosang + centreX;
+        double y = yRadius * sinang + centreY;
         (*pts)[iPt++] = coord(x, y);
     }
     (*pts)[iPt++] = (*pts)[0];
@@ -158,17 +161,18 @@ GeometricShapeFactory::createArc(double startAng, double angExtent)
     env.reset();
 
     double angSize = angExtent;
-    if(angSize <= 0.0 || angSize > 2 * MATH_PI) {
-        angSize = 2 * MATH_PI;
+    if(angSize <= 0.0 || angSize > Angle::PI_TIMES_2) {
+        angSize = Angle::PI_TIMES_2;
     }
     double angInc = angSize / (nPts - 1);
 
     auto pts = detail::make_unique(nPts);
     uint32_t iPt = 0;
+    double sinang, cosang;
     for(uint32_t i = 0; i < nPts; i++) {
-        double ang = startAng + i * angInc;
-        double x = xRadius * cos(ang) + centreX;
-        double y = yRadius * sin(ang) + centreY;
+        Angle::sinCosSnap(startAng + i * angInc, sinang, cosang);
+        double x = xRadius * cosang + centreX;
+        double y = yRadius * sinang + centreY;
         (*pts)[iPt++] = coord(x, y);
     }
     auto line = geomFact->createLineString(std::move(pts));
@@ -187,18 +191,19 @@ GeometricShapeFactory::createArcPolygon(double startAng, double angExtent)
     env.reset();
 
     double angSize = angExtent;
-    if(angSize <= 0.0 || angSize > 2 * MATH_PI) {
-        angSize = 2 * MATH_PI;
+    if(angSize <= 0.0 || angSize > Angle::PI_TIMES_2) {
+        angSize = Angle::PI_TIMES_2;
     }
     double angInc = angSize / (nPts - 1);
 
     auto pts = detail::make_unique(nPts + 2);
     uint32_t iPt = 0;
     (*pts)[iPt++] = coord(centreX, centreY);
+    double sinang, cosang;
     for(uint32_t i = 0; i < nPts; i++) {
-        double ang = startAng + i * angInc;
-        double x = xRadius * cos(ang) + centreX;
-        double y = yRadius * sin(ang) + centreY;
+        Angle::sinCosSnap(startAng + i * angInc, sinang, cosang);
+        double x = xRadius * cosang + centreX;
+        double y = yRadius * sinang + centreY;
         (*pts)[iPt++] = coord(x, y);
     }
     (*pts)[iPt++] = coord(centreX, centreY);
diff --git a/deps/libgeos/geos/src/util/string.cpp b/deps/libgeos/geos/src/util/string.cpp
index b28575a15..b61b147c1 100644
--- a/deps/libgeos/geos/src/util/string.cpp
+++ b/deps/libgeos/geos/src/util/string.cpp
@@ -12,6 +12,7 @@
  *
  **********************************************************************/
 
+#include 
 #include 
 
 namespace geos {
@@ -53,6 +54,11 @@ bool startsWith(const std::string & s, char prefix) {
     return s[0] == prefix;
 }
 
+void toUpper(std::string& s)
+{
+    std::transform(s.begin(), s.end(), s.begin(), ::toupper);
+}
+
 
 }
-}
\ No newline at end of file
+}
diff --git a/deps/libgeos/geos/util/geosop/GeometryOp.cpp b/deps/libgeos/geos/util/geosop/GeometryOp.cpp
index 59c01b644..22909ba9b 100644
--- a/deps/libgeos/geos/util/geosop/GeometryOp.cpp
+++ b/deps/libgeos/geos/util/geosop/GeometryOp.cpp
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -69,6 +70,13 @@
 #include 
 #include 
 
+// Quiet MSVC warning we cannot fix without
+// a bunch of function renaming,
+// see https://github.com/libgeos/geos/issues/929
+#ifdef _MSC_VER
+#pragma warning (disable : 4573)
+#endif
+
 using geos::operation::overlayng::OverlayNG;
 using geos::algorithm::distance::DiscreteFrechetDistance;
 using geos::operation::relate::RelateOp;
@@ -372,13 +380,22 @@ std::vector opRegistry {
 }},
 {"largestEmptyCircle", [](std::string name) { return GeometryOp::create(name,
     catConst,
-    "compute radius line of largest empty circle of geometry up to a distance tolerance",
+    "compute radius line of largest empty circle between obstacles, up to a distance tolerance",
     [](const std::unique_ptr& geom, double d) {
         geos::algorithm::construct::LargestEmptyCircle lec( geom.get(), d );
         std::unique_ptr res = lec.getRadiusLine();
         return new Result( std::move(res) );
     });
 }},
+{"largestEmptyCircleBdy", [](std::string name) { return GeometryOp::create(name,
+    catConst,
+    "compute radius line of largest empty circle between obstacles with center in a boundary, up to a distance tolerance",
+    [](const std::unique_ptr& geom, const std::unique_ptr& geom2, double d) {
+        geos::algorithm::construct::LargestEmptyCircle lec( geom.get(), geom2.get(), d );
+        std::unique_ptr res = lec.getRadiusLine();
+        return new Result( std::move(res) );
+    });
+}},
 {"maxInscribedCircle", [](std::string name) { return GeometryOp::create(name,
     catConst,
     "compute maximum inscribed circle radius of Polygon up to a distance tolerance",
@@ -388,6 +405,14 @@ std::vector opRegistry {
         return new Result( std::move(res) );
     });
     }},
+{"minAreaRectangle", [](std::string name) { return GeometryOp::create(name,
+    catConst,
+    "compute minimum-area rectangle enclosing geometry",
+    [](const std::unique_ptr& geom) {
+        std::unique_ptr res = geos::algorithm::MinimumAreaRectangle::getMinimumRectangle(geom.get());
+        return new Result( std::move(res) );
+    });
+    }},
 {"minBoundingCircle", [](std::string name) { return GeometryOp::create(name,
     catConst,
     "compute minimum bounding circle of geometry",
@@ -608,7 +633,7 @@ std::vector opRegistry {
     catRel, "test if geometry A covers geometry B",
     Result::typeBool,
     [](const std::unique_ptr& geom, const std::unique_ptr& geomB) {
-        return new Result( geom->contains( geomB.get() ) );
+        return new Result( geom->covers( geomB.get() ) );
     });
 }},
 { "crosses", [](std::string name) { return GeometryOp::create(name,
@@ -1057,6 +1082,18 @@ Result::isGeometry() {
     return typeCode == typeGeometry;
 }
 
+bool
+Result::isBool() {
+    return typeCode == typeBool;
+}
+
+bool
+Result::toBool() {
+    if (typeCode == typeBool)
+        return valBool;
+    return false;
+}
+
 bool
 Result::isGeometryList() {
     return typeCode == typeGeomList;
@@ -1108,7 +1145,7 @@ Result::metadata() {
     case typeGeomList:
         return "Geometry[" + std::to_string( valGeomList.size()) + "]";
     }
-    return "Unknonwn type";
+    return "Unknown type";
 }
 
 std::string
diff --git a/deps/libgeos/geos/util/geosop/GeometryOp.h b/deps/libgeos/geos/util/geosop/GeometryOp.h
index 1dad1016a..9ffc7f853 100644
--- a/deps/libgeos/geos/util/geosop/GeometryOp.h
+++ b/deps/libgeos/geos/util/geosop/GeometryOp.h
@@ -46,6 +46,9 @@ class Result {
 
     static std::string code(int typeCode);
 
+    bool isBool();
+    bool toBool();
+
     bool isGeometry();
     bool isGeometryList();
     std::string metadata();
diff --git a/deps/libgeos/geos/util/geosop/GeosOp.cpp b/deps/libgeos/geos/util/geosop/GeosOp.cpp
index 45aefbc66..43a453b3a 100644
--- a/deps/libgeos/geos/util/geosop/GeosOp.cpp
+++ b/deps/libgeos/geos/util/geosop/GeosOp.cpp
@@ -89,6 +89,8 @@ int main(int argc, char** argv) {
         ("p,precision", "Set number of decimal places in output coordinates", cxxopts::value( cmdArgs.precision ) )
         ("q,quiet", "Disable result output", cxxopts::value( cmdArgs.isQuiet ) )
         ("r,repeat", "Repeat operation N times", cxxopts::value( cmdArgs.repeatNum ) )
+        ("select", "Select geometries where op result is true", cxxopts::value( cmdArgs.isSelect ) )
+        ("selectNot", "Select geometries where op result is false", cxxopts::value( cmdArgs.isSelectNot ) )
         ("t,time", "Print execution time", cxxopts::value( cmdArgs.isShowTime ) )
         ("v,verbose", "Verbose output", cxxopts::value( cmdArgs.isVerbose )->default_value("false"))
         ("h,help", "Print help")
@@ -130,7 +132,7 @@ int main(int argc, char** argv) {
         auto fmt = result["format"].as();
         // Use lowercase matching
         std::transform(fmt.begin(), fmt.end(), fmt.begin(),
-            [](unsigned char c){ return std::tolower(c); });
+            [](unsigned char c){ return (unsigned char)std::tolower(c); });
         if (fmt == "txt" || fmt == "wkt" ) {
             cmdArgs.format = GeosOpArgs::fmtText;
         }
@@ -187,8 +189,8 @@ class comma_numpunct : public std::numpunct
       :m_thousands_sep(p_thousands_sep),
        m_grouping(p_grouping){}
 protected:
-   char do_thousands_sep() const {return m_thousands_sep;}
-   std::string do_grouping() const {return m_grouping;}
+   char do_thousands_sep() const override {return m_thousands_sep;}
+   std::string do_grouping() const override {return m_grouping;}
 private:
    char m_thousands_sep;
    std::string m_grouping;
@@ -214,7 +216,12 @@ std::vector> collect( std::vector(toupper(static_cast(c)));
+    if ( lastWord.compare(" EMPTY") == 0 ) return true;
 
     // assume if string contains a ( it is WKT
     auto numLParen = std::count(s.begin(), s.end(), '(');
@@ -434,8 +441,7 @@ void GeosOp::executeUnary(GeometryOp * op, OpArguments& opArgs) {
     for (unsigned i = 0; i < geomA.size(); i++) {
         vertexCount += geomA[i]->getNumPoints();
         Result* result = executeOpRepeat(op, i, geomA[i], 0, nullptr, opArgs);
-
-        output(result);
+        output(result, geomA[i].get());
         delete result;
     }
 }
@@ -447,7 +453,7 @@ void GeosOp::executeBinary(GeometryOp * op, OpArguments& opArgs) {
             vertexCount += geomB[ib]->getNumPoints();
             Result* result = executeOpRepeat(op, ia, geomA[ia], ib, geomB[ib], opArgs);
 
-            output(result);
+            output(result, geomA[ia].get());
             delete result;
         }
     }
@@ -507,7 +513,7 @@ Result* GeosOp::executeOp(GeometryOp * op,
     return result;
 }
 
-void GeosOp::output(Result* result) {
+void GeosOp::output(Result* result, Geometry* geom) {
     //---- print result if format specified
     if (args.isQuiet)
         return;
@@ -523,6 +529,18 @@ void GeosOp::output(Result* result) {
     else if (result->isGeometryList() ) {
         outputGeometryList( result->valGeomList );
     }
+    else if (result->isBool() ) {
+        if (args.isSelect || args.isSelectNot) {
+            bool isSelected = (args.isSelect && result->toBool())
+                    || (args.isSelectNot && ! result->toBool());
+            if (isSelected) {
+                outputGeometry( geom );
+            }
+        }
+        else {
+            std::cout << result->toString() << std::endl;
+        }
+    }
     else {
         // output as text/WKT
         std::cout << result->toString() << std::endl;
diff --git a/deps/libgeos/geos/util/geosop/GeosOp.h b/deps/libgeos/geos/util/geosop/GeosOp.h
index 3fe373700..3bfd16cb1 100644
--- a/deps/libgeos/geos/util/geosop/GeosOp.h
+++ b/deps/libgeos/geos/util/geosop/GeosOp.h
@@ -50,6 +50,8 @@ class GeosOpArgs {
     int offsetA = -1;
     bool isCollect = true;
     bool isExplode = false;
+    bool isSelect = false;
+    bool isSelectNot = false;
 
     std::string srcB;
 
@@ -91,7 +93,7 @@ class GeosOp {
         unsigned int indexA, const  std::unique_ptr& geomA,
         unsigned int indexB, const  std::unique_ptr& geomB,
         OpArguments& opArgs);
-    void output(Result* result);
+    void output(Result* result, Geometry* geom);
     void outputExplode(std::unique_ptr& geom);
     void outputGeometry( const Geometry* geom);
     void outputGeometryList(std::vector> & val);
diff --git a/deps/libgeos/geos/util/geosop/cxxopts.hpp b/deps/libgeos/geos/util/geosop/cxxopts.hpp
index 6ec7998a1..1acfb88c2 100644
--- a/deps/libgeos/geos/util/geosop/cxxopts.hpp
+++ b/deps/libgeos/geos/util/geosop/cxxopts.hpp
@@ -677,12 +677,14 @@ namespace cxxopts
       integer_parser(text, value);
     }
 
+    #ifndef __illumos__
     inline
     void
     parse_value(const std::string& text, int8_t& value)
     {
       integer_parser(text, value);
     }
+    #endif
 
     inline
     void
diff --git a/deps/libgeos/include/geos_c.h b/deps/libgeos/include/geos_c.h
index a3c1c63f3..f3d1433a7 100644
--- a/deps/libgeos/include/geos_c.h
+++ b/deps/libgeos/include/geos_c.h
@@ -61,22 +61,22 @@ extern "C" {
 #define GEOS_VERSION_MAJOR 3
 #endif
 #ifndef GEOS_VERSION_MINOR
-#define GEOS_VERSION_MINOR 12
+#define GEOS_VERSION_MINOR 13
 #endif
 #ifndef GEOS_VERSION_PATCH
-#define GEOS_VERSION_PATCH 1
+#define GEOS_VERSION_PATCH 0
 #endif
 #ifndef GEOS_VERSION
-#define GEOS_VERSION "3.12.1"
+#define GEOS_VERSION "3.13.0"
 #endif
 #ifndef GEOS_JTS_PORT
 #define GEOS_JTS_PORT "1.18.0"
 #endif
 
 #define GEOS_CAPI_VERSION_MAJOR 1
-#define GEOS_CAPI_VERSION_MINOR 18
-#define GEOS_CAPI_VERSION_PATCH 1
-#define GEOS_CAPI_VERSION "3.12.1-CAPI-1.18.1"
+#define GEOS_CAPI_VERSION_MINOR 19
+#define GEOS_CAPI_VERSION_PATCH 0
+#define GEOS_CAPI_VERSION "3.13.0-CAPI-1.19.0"
 
 #define GEOS_CAPI_FIRST_INTERFACE GEOS_CAPI_VERSION_MAJOR
 #define GEOS_CAPI_LAST_INTERFACE (GEOS_CAPI_VERSION_MAJOR+GEOS_CAPI_VERSION_MINOR)
@@ -102,7 +102,8 @@ typedef struct GEOSContextHandle_HS *GEOSContextHandle_t;
 *
 * \param fmt the message format template
 */
-typedef void (*GEOSMessageHandler)(const char *fmt, ...);
+typedef void (*GEOSMessageHandler)(GEOS_PRINTF_FORMAT const char *fmt, ...)
+    GEOS_PRINTF_FORMAT_ATTR(1, 2);
 
 /**
 * A GEOS message handler function.
@@ -210,7 +211,12 @@ enum GEOSGeomTypes {
     /** Multipolygon, a homogeneous collection of polygons */
     GEOS_MULTIPOLYGON,
     /** Geometry collection, a heterogeneous collection of geometry */
-    GEOS_GEOMETRYCOLLECTION
+    GEOS_GEOMETRYCOLLECTION,
+    GEOS_CIRCULARSTRING,
+    GEOS_COMPOUNDCURVE,
+    GEOS_CURVEPOLYGON,
+    GEOS_MULTICURVE,
+    GEOS_MULTISURFACE,
 };
 
 /**
@@ -757,6 +763,36 @@ extern GEOSGeometry GEOS_DLL *GEOSGeom_clone_r(
     GEOSContextHandle_t handle,
     const GEOSGeometry* g);
 
+/** \see GEOSGeom_createCircularString */
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createCircularString_r(
+    GEOSContextHandle_t handle,
+    GEOSCoordSequence* s);
+
+/** \see GEOSGeom_createEmptyCircularString */
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createEmptyCircularString_r(
+    GEOSContextHandle_t handle);
+
+/** \see GEOSGeom_createCompoundCurve */
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createCompoundCurve_r(
+    GEOSContextHandle_t handle,
+    GEOSGeometry** curves,
+    unsigned int ncurves);
+
+/** \see GEOSGeom_createEmptyCompoundCurve */
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createEmptyCompoundCurve_r(
+    GEOSContextHandle_t handle);
+
+/** \see GEOSGeom_createCurvePolygon */
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createCurvePolygon_r(
+    GEOSContextHandle_t handle,
+    GEOSGeometry* shell,
+    GEOSGeometry** holes,
+    unsigned int nholes);
+
+/** \see GEOSGeom_createEmptyCurvePolygon */
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createEmptyCurvePolygon_r(
+    GEOSContextHandle_t handle);
+
 /* ========= Memory management ========= */
 
 /** \see GEOSGeom_destroy */
@@ -1245,6 +1281,19 @@ extern char GEOS_DLL GEOSPreparedWithin_r(
     const GEOSPreparedGeometry* pg1,
     const GEOSGeometry* g2);
 
+/** \see GEOSPreparedRelate */
+extern char GEOS_DLL * GEOSPreparedRelate_r(
+    GEOSContextHandle_t handle,
+    const GEOSPreparedGeometry* pg1,
+    const GEOSGeometry* g2);
+
+/** \see GEOSPreparedRelatePattern */
+extern char GEOS_DLL GEOSPreparedRelatePattern_r(
+    GEOSContextHandle_t handle,
+    const GEOSPreparedGeometry* pg1,
+    const GEOSGeometry* g2,
+    const char* im);
+
 /** \see GEOSPreparedNearestPoints */
 extern GEOSCoordSequence GEOS_DLL *GEOSPreparedNearestPoints_r(
     GEOSContextHandle_t handle,
@@ -1381,7 +1430,7 @@ extern char GEOS_DLL GEOSRelatePattern_r(
     GEOSContextHandle_t handle,
     const GEOSGeometry* g1,
     const GEOSGeometry* g2,
-    const char *pat);
+    const char *imPattern);
 
 /** \see GEOSRelate */
 extern char GEOS_DLL *GEOSRelate_r(
@@ -1392,8 +1441,8 @@ extern char GEOS_DLL *GEOSRelate_r(
 /** \see GEOSRelatePatternMatch */
 extern char GEOS_DLL GEOSRelatePatternMatch_r(
     GEOSContextHandle_t handle,
-    const char *mat,
-    const char *pat);
+    const char *intMatrix,
+    const char *imPattern);
 
 /** \see GEOSRelateBoundaryNodeRule */
 extern char GEOS_DLL *GEOSRelateBoundaryNodeRule_r(
@@ -1898,6 +1947,21 @@ extern void GEOS_DLL GEOSWKTWriter_setOld3D_r(
     GEOSWKTWriter *writer,
     int useOld3D);
 
+/** Print the shortest representation of a double. Non-zero absolute values
+ * that are <1e-4 and >=1e+17 are formatted using scientific notation, and
+ * other values are formatted with positional notation with precision used for
+ * the max digits after decimal point.
+ * \param d The number to format.
+ * \param precision The desired precision.
+ * \param result The buffer to write the result to, with a suggested size 28.
+ * \return the length of the written string.
+ */
+extern int GEOS_DLL GEOS_printDouble(
+    double d,
+    unsigned int precision,
+    char *result
+);
+
 /* ========== WKB Reader ========== */
 
 /** \see GEOSWKBReader_create */
@@ -2085,7 +2149,9 @@ extern void GEOS_DLL finishGEOS(void);
 * Free strings and byte buffers returned by functions such
 * as GEOSWKBWriter_write(),
 * GEOSWKBWriter_writeHEX() and GEOSWKTWriter_write(), etc.
-* \param buffer The memory to free
+* If passed a null pointer the function does nothing.
+
+* \param buffer The memory to free (may be null)
 * \since 3.1
 */
 extern void GEOS_DLL GEOSFree(void *buffer);
@@ -2435,6 +2501,68 @@ extern GEOSGeometry GEOS_DLL *GEOSGeom_createPolygon(
     GEOSGeometry** holes,
     unsigned int nholes);
 
+/**
+* Creates a CircularString geometry.
+* \param s Input coordinate sequence, ownership passes to the geometry
+* \return A newly allocated CircularString geometry. NULL on exception.
+* Caller is responsible for freeing with GEOSGeom_destroy().
+* \since 3.13
+*/
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createCircularString(GEOSCoordSequence* s);
+
+/**
+* Creates an empty CircularString geometry.
+* \return A newly allocated CircularString geometry. NULL on exception.
+* Caller is responsible for freeing with GEOSGeom_destroy().
+* \since 3.13
+*/
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createEmptyCircularString();
+
+/**
+* Creates a CompoundCurve geometry.
+* \param curves A list of geometries that will form the CompoundCurve
+* \param ncurves The number of geometries in the curves list
+* \return A newly allocated CompoundCurve geometry. NULL on exception.
+* Caller is responsible for freeing with GEOSGeom_destroy().
+* \since 3.13
+*/
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createCompoundCurve(GEOSGeometry** curves,
+                                                           unsigned int ncurves);
+
+/**
+* Creates an empty CompoundCurve geometry.
+* \return A newly allocated CompoundCurve geometry. NULL on exception.
+* Caller is responsible for freeing with GEOSGeom_destroy().
+* \since 3.13
+*/
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createEmptyCompoundCurve();
+
+/**
+* Creates a CurvePolygon geometry from ring geometries.
+* \param shell A ring that is the exterior ring of the polygon.
+* \param holes An array of rings that are the holes.
+* \param nholes The number of rings in the holes array.
+* \return A newly allocated geometry. NULL on exception.
+* Caller is responsible for freeing with GEOSGeom_destroy().
+* \note The holes argument is an array of GEOSGeometry* objects.
+*       The caller **retains ownership** of the containing array,
+*       but the ownership of the pointed-to objects is transferred
+*       to the returned \ref GEOSGeometry.
+* \since 3.13
+*/
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createCurvePolygon(
+    GEOSGeometry* shell,
+    GEOSGeometry** holes,
+    unsigned int nholes);
+
+/**
+* Creates an empty CurvePolygon geometry.
+* \return A newly allocated CurvePolygon geometry. NULL on exception.
+* Caller is responsible for freeing with GEOSGeom_destroy().
+* \since 3.13
+*/
+extern GEOSGeometry GEOS_DLL *GEOSGeom_createEmptyCurvePolygon();
+
 /**
 * Create a geometry collection.
 * \param type The geometry type, enumerated by \ref GEOSGeomTypes
@@ -2467,7 +2595,7 @@ extern GEOSGeometry GEOS_DLL *GEOSGeom_createCollection(
 *       with GEOSFree() and all the elements with GEOSGeom_destroy().
 *       If called with an empty collection, null will be returned
 *       and ngeoms set to zero.
-* \since 3.4
+* \since 3.12
 */
 extern GEOSGeometry GEOS_DLL ** GEOSGeom_releaseCollection(
     GEOSGeometry * collection,
@@ -2551,7 +2679,7 @@ extern int GEOS_DLL GEOSGetSRID(const GEOSGeometry* g);
 /**
 * Return the anonymous "user data" for this geometry.
 * User data must be managed by the caller, and is not freed when
-* the geometry is destoryed.
+* the geometry is destroyed.
 * \param g Input geometry
 * \return A void* to the user data, caller is responsible for
 *         casting to the appropriate type.
@@ -2565,6 +2693,8 @@ extern void GEOS_DLL *GEOSGeom_getUserData(const GEOSGeometry* g);
 * multi-geometry or collection or 1 for a simple geometry.
 * For nested collections, remember to check if returned
 * sub-geometries are **themselves** also collections.
+* Empty collection or multi-geometry types return 0,
+* and empty simple geometry types return 1.
 * \param g Input geometry
 * \return Number of direct children in this collection
 * \warning For GEOS < 3.2 this function may crash when fed simple geometries
@@ -2930,6 +3060,7 @@ extern int GEOS_DLL GEOSNormalize(GEOSGeometry* g);
 * \param exteriorCW if 1, exterior rings will be clockwise and interior rings
 *                         will be counter-clockwise
 * \return 0 on success or -1 on exception
+* \since 3.12
 */
 extern int GEOS_DLL GEOSOrientPolygons(GEOSGeometry* g,
                                        int exteriorCW);
@@ -3942,7 +4073,7 @@ extern GEOSGeometry GEOS_DLL *GEOSConcaveHull(
 * The concave hull is constructed by removing the longest outer edges
 * of the Delaunay Triangulation of the space between the polygons,
 * until the specified maximm edge length is reached.
-* A large value produces the convex hull, 0 produces the hull of maximim concaveness.
+* A large value produces the convex hull, 0 produces the hull of maximum concaveness.
 *
 * \param g The input geometry
 * \param length The maximum edge length (0 or greater)
@@ -4116,8 +4247,7 @@ extern GEOSGeometry GEOS_DLL *GEOSMaximumInscribedCircle(
 * Constructs the "largest empty circle" (LEC) for a set of obstacle geometries
 * and within a polygonal boundary,
 * with accuracy to to a specified distance tolerance.
-* The obstacles are point and line geometries.
-* Polygonal obstacles are treated as linear features.
+* The obstacles may be any collection of points, lines and polygons.
 * The LEC is the largest circle whose interior does not intersect with any obstacle.
 * and which has its **center** inside the given boundary.
 * If no boundary is provided, the
@@ -4126,15 +4256,21 @@ extern GEOSGeometry GEOS_DLL *GEOSMaximumInscribedCircle(
 * the obstacles (up to the given distance tolerance).
 * The LEC is determined by the center point and a point indicating the circle radius
 * (which will lie on an obstacle).
+* \n
+* To compute an LEC which lies **wholly** within a polygonal boundary,
+* include the boundary of the polygon(s) as a linear obstacle.
+* \n
 * The implementation uses a successive-approximation technique over a grid of square cells covering the obstacles and boundary.
 * The grid is refined using a branch-and-bound algorithm.  Point containment and distance are computed in a performant
 * way by using spatial indexes.
+* \n
 * Returns the LEC radius as a two-point linestring, with the start point at the center of the inscribed circle and the end
 * on the boundary of the circle.
+*
 * \param obstacles The geometries that the LEC must not cross
 * \param boundary The area to contain the LEC center (may be null or empty)
 * \param tolerance Stop the algorithm when the search area is smaller than this tolerance
-* \return A newly allocated geometry of the LEC radius. NULL on exception.
+* \return A newly allocated geometry of the LEC radius line. NULL on exception.
 * Caller is responsible for freeing with GEOSGeom_destroy().
 * \see geos::algorithm::construct::LargestEmptyCircle
 *
@@ -4336,9 +4472,9 @@ extern GEOSGeometry GEOS_DLL *GEOSPolygonize(
     unsigned int ngeoms);
 
 /**
-* Has the same polygonizing behavior as GEOSPolygonize(), 
+* Has the same polygonizing behavior as GEOSPolygonize(),
 * but returns a result which is a valid polygonal geometry.
-* The result will not contain any edge-adjacent elements. 
+* The result will not contain any edge-adjacent elements.
 *
 * \param geoms Array of linear geometries to polygons. Caller retains ownersihp of both array container and objects.
 * \param ngeoms Size of the geoms array.
@@ -4372,7 +4508,7 @@ extern GEOSGeometry GEOS_DLL *GEOSPolygonizer_getCutEdges(
 
 /**
 * Perform the polygonization as GEOSPolygonize() and return the
-* polygonal result as well as all extra ouputs.
+* polygonal result as well as all extra outputs.
 *
 * \param[in] input A single geometry with all the input lines to polygonize.
 * \param[out] cuts Pointer to hold "cut edges", connected on both ends but not part of output. Caller must free.
@@ -4429,10 +4565,12 @@ extern GEOSGeometry GEOS_DLL *GEOSDensify(
     double tolerance);
 
 /**
-* Sews together a set of fully noded LineStrings
-* removing any cardinality 2 nodes in the linework.
+* Merges a set of LineStrings,
+* joining them at nodes which have cardinality 2.
+* Lines may have their direction reversed.
+
 * \param g The input linework
-* \return The merged linework
+* \return The merged linework.
 * Caller is responsible for freeing with GEOSGeom_destroy().
 * \see geos::operation::linemerge::LineMerger
 * \since 2.2
@@ -4440,11 +4578,13 @@ extern GEOSGeometry GEOS_DLL *GEOSDensify(
 extern GEOSGeometry GEOS_DLL *GEOSLineMerge(const GEOSGeometry* g);
 
 /**
-* Sews together a set of fully noded LineStrings
-* removing any cardinality 2 nodes in the linework
-* only if possible without changing order of points.
+* Merges a set of LineStrings,
+* joining them at nodes which have cardinality 2.
+* and where the lines have the same direction.
+* This means that lines do not have their direction reversed.
+
 * \param g The input linework
-* \return The merged linework
+* \return The merged linework.
 * Caller is responsible for freeing with GEOSGeom_destroy().
 * \see geos::operation::linemerge::LineMerger
 *
@@ -4588,7 +4728,7 @@ extern GEOSGeometry GEOS_DLL *GEOSGeom_transformXY(
 * \param input An input geometry
 * \param snap_target A geometry to snap the input to
 * \param tolerance Snapping tolerance
-* \return The snapped verion of the input. NULL on exception.
+* \return The snapped version of the input. NULL on exception.
 * Caller is responsible for freeing with GEOSGeom_destroy().
 *
 * \since 3.3
@@ -4787,25 +4927,29 @@ extern char GEOS_DLL GEOSEqualsIdentical(
         const GEOSGeometry* g2);
 
 /**
-* Calculate the DE9IM pattern for this geometry pair
-* and compare against the provided pattern to check for
-* consistency. If the result and pattern are consistent
-* return true. The pattern may include glob "*" characters
-* for portions that are allowed to match any value.
+* Calculate the [DE9IM](https://en.wikipedia.org/wiki/DE-9IM) string for a geometry pair
+* and compare against a DE9IM pattern to check for
+* consistency. 
+* If the result matches the pattern return true.
+* The pattern is a 9-character string 
+* containing symbols in the set "012TF*".
+* "012F" match the corresponding dimension symbol;
+* "T" matches any non-empty dimension; "*" matches any dimension.
 * \see geos::geom::Geometry::relate
 * \param g1 First geometry in pair
 * \param g2 Second geometry in pair
-* \param pat DE9IM pattern to check
+* \param imPattern DE9IM pattern to match
 * \return 1 on true, 0 on false, 2 on exception
 * \since 2.2
 */
 extern char GEOS_DLL GEOSRelatePattern(
     const GEOSGeometry* g1,
     const GEOSGeometry* g2,
-    const char *pat);
+    const char *imPattern);
 
 /**
-* Calculate and return the DE9IM pattern for this geometry pair.
+* Calculate the [DE9IM](https://en.wikipedia.org/wiki/DE-9IM) string for this geometry pair.
+* The result is a 9-character string containing dimension symbols in the set "012F".
 * \see geos::geom::Geometry::relate
 * \param g1 First geometry in pair
 * \param g2 Second geometry in pair
@@ -4818,21 +4962,21 @@ extern char GEOS_DLL *GEOSRelate(
     const GEOSGeometry* g2);
 
 /**
-* Compare two DE9IM patterns and return true if they
+* Compare a [DE9IM](https://en.wikipedia.org/wiki/DE-9IM) string to a pattern and return true if they
 * are consistent.
-* \param mat Complete DE9IM string (does not have "*")
-* \param pat Pattern to match to (may contain "*")
+* \param intMatrix DE9IM string (contains symbols "012F")
+* \param imPattern Pattern to match to (may also contain symbols "T" and "*")
 * \return 1 on true, 0 on false, 2 on exception
 *
 * \since 3.3
 */
 extern char GEOS_DLL GEOSRelatePatternMatch(
-    const char *mat,
-    const char *pat);
+    const char *intMatrix,
+    const char *imPattern);
 
 /**
-* Calculate and return the DE9IM pattern for this geometry pair.
-* Apply the supplied \ref GEOSRelateBoundaryNodeRules.
+* Calculate the [DE9IM](https://en.wikipedia.org/wiki/DE-9IM) string for this geometry pair,
+* using the supplied \ref GEOSRelateBoundaryNodeRules.
 * \see geos::geom::Geometry::relate
 * \see geos::algorithm::BoundaryNodeRule
 * \param g1 First geometry in pair
@@ -5061,6 +5205,44 @@ extern char GEOS_DLL GEOSPreparedWithin(
     const GEOSPreparedGeometry* pg1,
     const GEOSGeometry* g2);
 
+/**
+* Use a \ref GEOSPreparedGeometry do a high-performance
+* calculation of the [DE9IM](https://en.wikipedia.org/wiki/DE-9IM) relationship between the
+* prepared and provided geometry.
+* \param pg1 The prepared geometry
+* \param g2 The geometry to test
+* \returns The DE9IM string
+* \see GEOSPrepare
+* \see GEOSRelate
+* \see GEOSPreparedRelatePattern
+*
+* \since 3.13
+*/
+extern char GEOS_DLL * GEOSPreparedRelate(
+    const GEOSPreparedGeometry* pg1,
+    const GEOSGeometry* g2);
+
+/**
+* Use a \ref GEOSPreparedGeometry do a high-performance
+* calculation of the [DE9IM](https://en.wikipedia.org/wiki/DE-9IM) relationship between the
+* prepared and provided geometry, and compare the
+* relationship to the provided DE9IM pattern.
+* Returns true if the patterns are consistent and false otherwise.
+* \param pg1 The prepared geometry
+* \param g2 The geometry to test
+* \param imPattern The DE9IM pattern to test
+* \returns 1 on true, 0 on false, 2 on exception
+* \see GEOSPrepare
+* \see GEOSRelatePattern
+* \see GEOSPreparedRelate
+*
+* \since 3.13
+*/
+extern char GEOS_DLL GEOSPreparedRelatePattern(
+    const GEOSPreparedGeometry* pg1,
+    const GEOSGeometry* g2,
+    const char* imPattern);
+
 /**
 * Use a \ref GEOSPreparedGeometry do a high performance
 * calculation to find the nearest points between the
@@ -5141,9 +5323,10 @@ extern GEOSSTRtree GEOS_DLL *GEOSSTRtree_create(size_t nodeCapacity);
 * Construct an STRtree from items that have been inserted. Once constructed,
 * no more items may be inserted into the tree. Functions that require a
 * constructed tree will build it automatically, so there is no need to call
-* `GEOSSTRtree_build` unless it is desired to explicity construct the tree
+* `GEOSSTRtree_build` unless it is desired to explicitly construct the tree
 * in a certain section of code or using a certain thread.
 *
+* \param tree the \ref GEOSSTRtree to apply the build to
 * \return 1 on success, 0 on error
 *
 * \since 3.12
@@ -5272,6 +5455,8 @@ extern char GEOS_DLL GEOSSTRtree_remove(
 * GEOSSTRtree_insert() are not owned by the tree, and are
 * still left to the caller to manage.
 *
+* \param tree the \ref GEOSSTRtree to destroy
+*
 * \since 3.2
 */
 extern void GEOS_DLL GEOSSTRtree_destroy(GEOSSTRtree *tree);
@@ -5410,7 +5595,10 @@ extern char GEOS_DLL *GEOSWKTWriter_write(
 /**
 * Sets the number trimming option on a \ref GEOSWKTWriter.
 * With trim set to 1, the writer will strip trailing 0's from
-* the output coordinates. With 0, all coordinates will be
+* the output coordinates. With 1 (trimming enabled), big and small
+* absolute coordinates will use scientific notation, otherwise
+* positional notation is used; see \ref GEOS_printDouble for details.
+* With 0 (trimming disabled), all coordinates will be
 * padded with 0's out to the rounding precision.
 * Default since GEOS 3.12 is with trim set to 1 for 'on'.
 * \param writer A \ref GEOSWKTWriter.
diff --git a/deps/libgeos/include/version.h b/deps/libgeos/include/version.h
index e9ff92cf7..af549170b 100644
--- a/deps/libgeos/include/version.h
+++ b/deps/libgeos/include/version.h
@@ -19,15 +19,15 @@
 #endif
 
 #ifndef GEOS_VERSION_MINOR
-#define GEOS_VERSION_MINOR 12
+#define GEOS_VERSION_MINOR 13
 #endif
 
 #ifndef GEOS_VERSION_PATCH
-#define GEOS_VERSION_PATCH 1
+#define GEOS_VERSION_PATCH 0
 #endif
 
 #ifndef GEOS_VERSION
-#define GEOS_VERSION "3.12.1"
+#define GEOS_VERSION "3.13.0"
 #endif
 
 #ifndef GEOS_JTS_PORT