diff --git a/CHANGELOG.md b/CHANGELOG.md index b51839e..204893e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [0.11.0](https://github.com/brick/geo/releases/tag/0.11.0) - 2024-06-05 + +💥 **Breaking changes** + +- interface `GeometryEngine` has a new method: `split()` + +✨ **New features** + +- New engine method: `GeometryEngine::split()` + ## [0.10.0](https://github.com/brick/geo/releases/tag/0.10.0) - 2024-01-23 💥 **Breaking changes** diff --git a/README.md b/README.md index 2378498..b543402 100644 --- a/README.md +++ b/README.md @@ -272,46 +272,47 @@ Here is a list of all exceptions: - `UnexpectedGeometryException` is thrown when a geometry is not an instance of the expected sub-type, for example when calling `Point::fromText()` with a `LineString` WKT. -Spatial Function Reference --------------------------- +GeometryEngine methods reference +-------------------------------- -This is a list of all functions which are currently implemented in the geo project. Some functions are only available +This is a list of all methods available in the `GeometryEngine` interface. Some methods are only available if you use a specific geometry engine, sometimes with a minimum version. This table also shows which functions are part of the OpenGIS standard. | Function Name | GEOS | PostGIS | MySQL | MariaDB | SpatiaLite | OpenGIS standard | |------------------|------|---------|--------|---------|------------|------------------| -| `area` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `azimuth` | | ✓ | | | ✓ | | -| `boundary` | ✓ | ✓ | | | ✓ | ✓ | -| `buffer` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `centroid` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `contains` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `convexHull` | ✓ | ✓ | 5.7.6 | | ✓ | ✓ | -| `crosses` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `difference` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `disjoint` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `distance` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `envelope` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `equals` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `intersects` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `intersection` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `isSimple` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `isValid` | ✓ | ✓ | 5.7.6 | | ✓ | | -| `length` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `locateAlong` | | ✓ | | | ✓ | | -| `locateBetween` | | ✓ | | | ✓ | | -| `makeValid` | | ✓ | | | ✓ | | -| `maxDistance` | | ✓ | | | ✓ | | -| `overlaps` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `pointOnSurface` | ✓ | ✓ | | | ✓ | ✓ | -| `relate` | ✓ | ✓ | | | ✓ | ✓ | -| `simplify` | ✓ | ✓ | 5.7.6 | | 4.1.0 | | -| `snapToGrid` | | ✓ | | | ✓ | | -| `symDifference` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `touches` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `union` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| `within` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `area` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `azimuth` | | ✓ | | | ✓ | | +| `boundary` | ✓ | ✓ | | | ✓ | ✓ | +| `buffer` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `centroid` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `contains` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `convexHull` | ✓ | ✓ | 5.7.6 | | ✓ | ✓ | +| `crosses` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `difference` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `disjoint` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `distance` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `envelope` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `equals` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `intersection` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `intersects` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `isSimple` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `isValid` | ✓ | ✓ | 5.7.6 | | ✓ | | +| `length` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `locateAlong` | | ✓ | | | ✓ | | +| `locateBetween` | | ✓ | | | ✓ | | +| `makeValid` | | ✓ | | | ✓ | | +| `maxDistance` | | ✓ | | | ✓ | | +| `overlaps` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `pointOnSurface` | ✓ | ✓ | | | ✓ | ✓ | +| `relate` | ✓ | ✓ | | | ✓ | ✓ | +| `simplify` | ✓ | ✓ | 5.7.6 | | 4.1.0 | | +| `snapToGrid` | | ✓ | | | ✓ | | +| `split` | | ✓ | | | ✓ | | +| `symDifference` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `touches` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `union` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| `within` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Importing and exporting geometries ---------------------------------- diff --git a/src/Engine/DatabaseEngine.php b/src/Engine/DatabaseEngine.php index 08227ef..fc3796c 100644 --- a/src/Engine/DatabaseEngine.php +++ b/src/Engine/DatabaseEngine.php @@ -433,4 +433,9 @@ public function transform(Geometry $g, int $srid) : Geometry { return $this->queryGeometry('ST_Transform', $g, $srid); } + + public function split(Geometry $g, Geometry $blade) : Geometry + { + return $this->queryGeometry('ST_Split', $g, $blade); + } } diff --git a/src/Engine/GEOSEngine.php b/src/Engine/GEOSEngine.php index 8665ffd..b747df5 100644 --- a/src/Engine/GEOSEngine.php +++ b/src/Engine/GEOSEngine.php @@ -375,4 +375,9 @@ public function transform(Geometry $g, int $srid) : Geometry { throw GeometryEngineException::unimplementedMethod(__METHOD__); } + + public function split(Geometry $g, Geometry $blade) : Geometry + { + throw GeometryEngineException::unimplementedMethod(__METHOD__); + } } diff --git a/src/Engine/GeometryEngine.php b/src/Engine/GeometryEngine.php index ed721ea..60aa034 100644 --- a/src/Engine/GeometryEngine.php +++ b/src/Engine/GeometryEngine.php @@ -486,4 +486,9 @@ public function boundingPolygons(Polygon $p) : MultiPolygon; * Returns a new geometry with its coordinates transformed to a different spatial reference system. */ public function transform(Geometry $g, int $srid) : Geometry; + + /** + * Splits a geometry into several geometries using a blade. + */ + public function split(Geometry $g, Geometry $blade) : Geometry; } diff --git a/tests/GeometryEngineTest.php b/tests/GeometryEngineTest.php index 1bb2261..ac0a64e 100644 --- a/tests/GeometryEngineTest.php +++ b/tests/GeometryEngineTest.php @@ -323,10 +323,10 @@ public static function providerPointOnSurface() : array /** * @param string $geometry The WKT of the geometry to test. - * @param string $boundary The WKT of the expected boundary. + * @param string|string[] $boundary The WKT of the expected boundary. If multiple possible results, an array. */ #[DataProvider('providerBoundary')] - public function testBoundary(string $geometry, string $boundary) : void + public function testBoundary(string $geometry, string|array $boundary) : void { $geometryEngine = $this->getGeometryEngine(); @@ -344,16 +344,20 @@ public function testBoundary(string $geometry, string $boundary) : void $this->expectException(GeometryEngineException::class); } - self::assertSame($boundary, $geometryEngine->boundary($geometry)->asText()); + if (is_array($boundary)) { + self::assertContains($geometryEngine->boundary($geometry)->asText(), $boundary); + } else { + self::assertSame($boundary, $geometryEngine->boundary($geometry)->asText()); + } } public static function providerBoundary() : array { return [ - ['POINT (1 2)', 'GEOMETRYCOLLECTION EMPTY'], - ['POINT Z (2 3 4)', 'GEOMETRYCOLLECTION EMPTY'], - ['POINT M (3 4 5)', 'GEOMETRYCOLLECTION EMPTY'], - ['POINT ZM (4 5 6 7)', 'GEOMETRYCOLLECTION EMPTY'], + ['POINT (1 2)', ['POINT EMPTY', 'GEOMETRYCOLLECTION EMPTY']], + ['POINT Z (2 3 4)', ['POINT Z EMPTY', 'GEOMETRYCOLLECTION EMPTY']], + ['POINT M (3 4 5)', ['POINT M EMPTY', 'GEOMETRYCOLLECTION EMPTY']], + ['POINT ZM (4 5 6 7)', ['POINT ZM EMPTY', 'GEOMETRYCOLLECTION EMPTY']], ['LINESTRING (1 1, 0 0, -1 1)', 'MULTIPOINT (1 1, -1 1)'], ['POLYGON ((1 1, 0 0, -1 1, 1 1))', 'LINESTRING (1 1, 0 0, -1 1, 1 1)'], ]; @@ -1158,6 +1162,33 @@ public static function providerTransform() : array ]; } + #[DataProvider('providerSplit')] + public function testSplit(string $originalWKT, string $bladeWKT, string $expectedWKT) : void + { + $geometryEngine = $this->getGeometryEngine(); + + if (! $this->isPostGIS()) { + self::markTestSkipped('This test currently runs on PostGIS only.'); + } + + $originalGeometry = Geometry::fromText($originalWKT); + $bladeGeometry = Geometry::fromText($bladeWKT); + + $splitGeometry = $geometryEngine->split($originalGeometry, $bladeGeometry); + + $this->assertSame($expectedWKT, $splitGeometry->asText()); + } + + public static function providerSplit() : array + { + return [ + ['LINESTRING (1 1, 3 3)', 'POINT (2 2)', 'GEOMETRYCOLLECTION (LINESTRING (1 1, 2 2), LINESTRING (2 2, 3 3))'], + ['LINESTRING (1 1, 1 2, 2 2, 2 1, 1 1)', 'LINESTRING (0 0, 3 3)', 'GEOMETRYCOLLECTION (LINESTRING (1 1, 1 2, 2 2), LINESTRING (2 2, 2 1, 1 1))'], + ['POLYGON ((1 1, 1 2, 2 2, 2 1, 1 1))', 'LINESTRING (0 0, 3 3)', 'GEOMETRYCOLLECTION (POLYGON ((1 1, 1 2, 2 2, 1 1)), POLYGON ((2 2, 2 1, 1 1, 2 2)))'], + ['POLYGON ((1 1, 1 2, 3 2, 3 1, 1 1))', 'LINESTRING (1 1, 2 2, 3 1)', 'GEOMETRYCOLLECTION (POLYGON ((1 1, 1 2, 2 2, 1 1)), POLYGON ((1 1, 2 2, 3 1, 1 1)), POLYGON ((3 1, 2 2, 3 2, 3 1)))'], + ]; + } + private function getGeometryEngine(): GeometryEngine { if (! isset($GLOBALS['GEOMETRY_ENGINE'])) {