From 1fa566cdb5a1a32c2ff56da81c9cf5a4edcf9bcf Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Wed, 13 Nov 2024 06:18:01 -0600 Subject: [PATCH 1/7] feat: initial changes for boolean within --- tests/test_boolean.py | 72 +- tests/test_feature_conversion.py | 7 +- .../LineString/LineString-LineString.geojson | 0 .../Point/LineString-Point-1.geojson | 0 .../Point/LineString-Point-2.geojson | 0 .../Polygon/LineString-In-Polygon.geojson | 0 .../Polygon/LineString-Polygon.geojson | 0 .../LineString/MultiPoint-LineString.geojson | 0 .../MultiPoint/MultiPoint-MultiPoint.geojson | 0 .../Polygon/MultiPoint-Polygon.geojson | 0 .../Polygon/MultiPolygon-Polygon.geojson | 0 .../LineString/Point-LineString-1.geojson | 0 .../LineString/Point-LineString-2.geojson | 0 .../LineString/Point-LineString-3.geojson | 0 .../LineString/Point-LineString-4.geojson | 0 .../Point/MultiPoint/Point-MultiPoint.geojson | 0 .../false/Point/Point/Point-Point.geojson | 0 .../Point/Polygon/Point-Polygon-1.geojson | 0 .../Point/Polygon/Point-Polygon-2.geojson | 0 .../Polygon-Containing-Linestring.geojson | 0 .../LineString/Polygon-LineString.geojson | 0 .../MultiPolygon/Polygon-MultiPolygon.geojson | 0 .../false/Polygon/Point/Polygon-Point.geojson | 0 .../Polygon/Large-Inside-Small.geojson | 0 .../Polygon/Polygon/Polygon-Polygon.geojson | 0 .../Polygon/Small-Inside-Large.geojson | 0 .../false/Polygon/Polygon/issue-1216.geojson | 0 .../LineString/LineString-LineString.geojson | 0 .../LineString/Point/LineString-Point.geojson | 0 .../Polygon/LineString-Polygon.geojson | 0 .../LineString/MultiPoint-LineString.geojson | 0 .../MultiPoint/MultiPoint-MultiPoint.geojson | 0 .../MultiPoint/Point/MultiPoint-Point.geojson | 0 .../Polygon/MultiPoint-Polygon.geojson | 0 .../Polygon/MultiPolygon-Polygon.geojson | 0 .../Point/LineString/Point-LineString.geojson | 0 .../Point/MultiPoint/Point-Multipoint.geojson | 0 .../true/Point/Point/Point-Point.geojson | 0 .../true/Point/Polygon/Point-Polygon.geojson | 0 .../LineString/Polygon-LineString.geojson | 0 .../MultiPolygon/Polygon-MultiPolygon.geojson | 0 .../true/Polygon/Point/Polygon-Point.geojson | 0 .../Polygon/Polygon/Polygon-Polygon.geojson | 0 .../LineString/LineString-LineString.geojson | 0 .../LineString/Point/LineString-Point.geojson | 0 .../Polygon/LineString-Polygon.geojson | 0 .../LineString/MultiPoint-LineString.geojson | 0 .../MultiPoint/MultiPoint-MultiPoint.geojson | 0 .../MultiPoint/Point/MultiPoint-Point.geojson | 0 .../Polygon/MultiPoint-Polygon.geojson | 0 .../Polygon/MultiPolygon-Polygon.geojson | 0 .../Point/LineString/Point-LineString.geojson | 0 .../Point/MultiPoint/Point-Multipoint.geojson | 0 .../false/Point/Point/Point-Point.geojson | 0 .../false/Point/Polygon/Point-Polygon.geojson | 0 .../LineString/Polygon-LineString.geojson | 0 .../MultiPolygon/Polygon-MultiPolygon.geojson | 0 .../false/Polygon/Point/Polygon-Point.geojson | 0 .../Polygon/Polygon/Polygon-Polygon.geojson | 0 .../LineString/LineString-LineString.geojson | 0 .../Point/LineString-Point-1.geojson | 0 .../Point/LineString-Point-2.geojson | 0 .../Polygon/LineString-In-Polygon.geojson | 0 .../Polygon/LineString-Polygon.geojson | 0 .../LineString/MultiPoint-LineString.geojson | 0 .../MultiPoint/MultiPoint-MultiPoint.geojson | 0 .../Polygon/MultiPoint-Polygon.geojson | 0 .../Polygon/MultiPolygon-Polygon.geojson | 0 .../LineString/Point-LineString-1.geojson | 0 .../LineString/Point-LineString-2.geojson | 0 .../LineString/Point-LineString-3.geojson | 0 .../LineString/Point-LineString-4.geojson | 0 .../Point/MultiPoint/Point-MultiPoint.geojson | 0 .../true/Point/Point/Point-Point.geojson | 0 .../Point/Polygon/Point-Polygon-1.geojson | 0 .../Point/Polygon/Point-Polygon-2.geojson | 0 .../Polygon-Containing-Linestring.geojson | 0 .../LineString/Polygon-LineString.geojson | 0 .../MultiPolygon/Polygon-MultiPolygon.geojson | 0 .../true/Polygon/Point/Polygon-Point.geojson | 0 .../Polygon/Large-Inside-Small.geojson | 0 .../Polygon/Polygon/Polygon-Polygon.geojson | 0 .../Polygon/Small-Inside-Large.geojson | 0 .../true/Polygon/Polygon/issue-1216.geojson | 0 .../LineString/LineIsNotWithinLine.geojson | 30 + .../Polygon/LineIsNotWIthinPolygon.geojson | 33 + .../LineIsNotWIthinPolygonBoundary.geojson | 33 + .../MultiPointsIsNotWIthinLine.geojson | 30 + ...ltiPointsOnLineEndsIsNotWIthinLine.geojson | 29 + .../MultiPointIsNotWithinMultiPoint.geojson | 28 + ...multipoint-not-within-multipolygon.geojson | 43 + ...intAllOnBoundaryIsNotWithinPolygon.geojson | 32 + .../MultiPointIsNotWithinPolygon.geojson | 33 + .../LineString/PointIsNotWithinLine.geojson | 26 + .../PointIsNotWithinLineBecauseOnEnd.geojson | 26 + .../PointOnEndIsWithinLinestring.geojson | 26 + .../PointIsNotWithinMultiPoint.geojson | 24 + .../point-not-within-multipolygon.geojson | 40 + .../Polygon/PointIsNotWithinPolygon.geojson | 29 + .../Polygon/PointOnPolygonBoundary.geojson | 29 + .../polygon-not-within-multipolygon.geojson | 48 + .../Polygon/Polygon/Polygon-Polygon.geojson | 37 + .../LineString/LineIsWithinLine.geojson | 30 + .../LineString/LinesExactSame.geojson | 31 + .../Polygon/LineIsContainedByPolygon.geojson | 33 + ...nedByPolygonWithNoInternalVertices.geojson | 32 + .../MultipointsIsWithinLine.geojson | 29 + .../MultiPointsWithinMultiPoints.geojson | 29 + .../multipoint-within-multipolygon.geojson | 43 + .../Polygon/MultiPointIsWithinPolygon.geojson | 32 + .../MultiPointSingleIsWithinPolygon.geojson | 29 + .../LineString/PointIsWithinLine.geojson | 26 + .../PointIsWithinMultiPoint.geojson | 24 + .../point-within-multipolygon.geojson | 40 + .../Polygon/PointIsWithinPolygon.geojson | 29 + .../polygon-within-multipolygon.geojson | 48 + .../Polygon/PolygonIsWIthinPolygon.geojson | 36 + .../Polygon/PolygonsExactSameShape.geojson | 41 + .../in/geometry-polygon.geojson | 0 .../in/multi-polygon-outer-doughnut.geojson | 0 .../in/multi-polygon-with-holes.geojson | 0 .../in/multi-polygon.geojson | 0 .../in/polygon-with-hole.geojson | 0 .../in/polygon.geojson | 0 .../out/geometry-polygon.geojson | 0 .../out/multi-polygon-outer-doughnut.geojson | 0 .../out/multi-polygon-with-holes.geojson | 0 .../out/multi-polygon.geojson | 0 .../out/polygon-with-hole.geojson | 0 .../out/polygon.geojson | 0 tests/test_helper.py | 4 +- tests/test_measurement.py | 37 +- tests/test_random.py | 8 +- tests/test_transformation.py | 9 +- turfpy/__init__.py | 3 +- turfpy/boolean.py | 266 ++++- turfpy/dev_lib/earcut.py | 6 +- turfpy/dev_lib/spline.py | 6 +- turfpy/helper.py | 5 +- turfpy/measurement.py | 66 +- turfpy/meta.py | 944 +++++++++++------- turfpy/misc.py | 19 +- turfpy/random.py | 4 +- turfpy/transformation.py | 27 +- 144 files changed, 2118 insertions(+), 473 deletions(-) rename tests/{ => test_files}/boolean_disjoint_test/false/LineString/LineString/LineString-LineString.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/LineString/Point/LineString-Point-1.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/LineString/Point/LineString-Point-2.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/LineString/Polygon/LineString-In-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/LineString/Polygon/LineString-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Point/LineString/Point-LineString-1.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Point/LineString/Point-LineString-2.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Point/LineString/Point-LineString-3.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Point/LineString/Point-LineString-4.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Point/MultiPoint/Point-MultiPoint.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Point/Point/Point-Point.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-1.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-2.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Polygon/LineString/Polygon-Containing-Linestring.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Polygon/LineString/Polygon-LineString.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Polygon/Point/Polygon-Point.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Polygon/Polygon/Large-Inside-Small.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Polygon/Polygon/Polygon-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Polygon/Polygon/Small-Inside-Large.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/false/Polygon/Polygon/issue-1216.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/LineString/LineString/LineString-LineString.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/LineString/Point/LineString-Point.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/LineString/Polygon/LineString-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/MultiPoint/Point/MultiPoint-Point.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/Point/LineString/Point-LineString.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/Point/MultiPoint/Point-Multipoint.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/Point/Point/Point-Point.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/Point/Polygon/Point-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/Polygon/LineString/Polygon-LineString.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/Polygon/Point/Polygon-Point.geojson (100%) rename tests/{ => test_files}/boolean_disjoint_test/true/Polygon/Polygon/Polygon-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/LineString/LineString/LineString-LineString.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/LineString/Point/LineString-Point.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/LineString/Polygon/LineString-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/MultiPoint/Point/MultiPoint-Point.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/Point/LineString/Point-LineString.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/Point/MultiPoint/Point-Multipoint.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/Point/Point/Point-Point.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/Point/Polygon/Point-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/Polygon/LineString/Polygon-LineString.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/Polygon/Point/Polygon-Point.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/false/Polygon/Polygon/Polygon-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/LineString/LineString/LineString-LineString.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/LineString/Point/LineString-Point-1.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/LineString/Point/LineString-Point-2.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/LineString/Polygon/LineString-In-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/LineString/Polygon/LineString-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Point/LineString/Point-LineString-1.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Point/LineString/Point-LineString-2.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Point/LineString/Point-LineString-3.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Point/LineString/Point-LineString-4.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Point/MultiPoint/Point-MultiPoint.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Point/Point/Point-Point.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Point/Polygon/Point-Polygon-1.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Point/Polygon/Point-Polygon-2.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Polygon/LineString/Polygon-Containing-Linestring.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Polygon/LineString/Polygon-LineString.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Polygon/Point/Polygon-Point.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Polygon/Polygon/Large-Inside-Small.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Polygon/Polygon/Polygon-Polygon.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Polygon/Polygon/Small-Inside-Large.geojson (100%) rename tests/{ => test_files}/boolean_intersects_test/true/Polygon/Polygon/issue-1216.geojson (100%) create mode 100644 tests/test_files/boolean_within_test/false/LineString/LineString/LineIsNotWithinLine.geojson create mode 100644 tests/test_files/boolean_within_test/false/LineString/Polygon/LineIsNotWIthinPolygon.geojson create mode 100644 tests/test_files/boolean_within_test/false/LineString/Polygon/LineIsNotWIthinPolygonBoundary.geojson create mode 100644 tests/test_files/boolean_within_test/false/MultiPoint/LineString/MultiPointsIsNotWIthinLine.geojson create mode 100644 tests/test_files/boolean_within_test/false/MultiPoint/LineString/MultiPointsOnLineEndsIsNotWIthinLine.geojson create mode 100644 tests/test_files/boolean_within_test/false/MultiPoint/MultiPoint/MultiPointIsNotWithinMultiPoint.geojson create mode 100644 tests/test_files/boolean_within_test/false/MultiPoint/MultiPolygon/multipoint-not-within-multipolygon.geojson create mode 100644 tests/test_files/boolean_within_test/false/MultiPoint/Polygon/MultiPointAllOnBoundaryIsNotWithinPolygon.geojson create mode 100644 tests/test_files/boolean_within_test/false/MultiPoint/Polygon/MultiPointIsNotWithinPolygon.geojson create mode 100644 tests/test_files/boolean_within_test/false/Point/LineString/PointIsNotWithinLine.geojson create mode 100644 tests/test_files/boolean_within_test/false/Point/LineString/PointIsNotWithinLineBecauseOnEnd.geojson create mode 100644 tests/test_files/boolean_within_test/false/Point/LineString/PointOnEndIsWithinLinestring.geojson create mode 100644 tests/test_files/boolean_within_test/false/Point/MultiPoint/PointIsNotWithinMultiPoint.geojson create mode 100644 tests/test_files/boolean_within_test/false/Point/MultiPolygon/point-not-within-multipolygon.geojson create mode 100644 tests/test_files/boolean_within_test/false/Point/Polygon/PointIsNotWithinPolygon.geojson create mode 100644 tests/test_files/boolean_within_test/false/Point/Polygon/PointOnPolygonBoundary.geojson create mode 100644 tests/test_files/boolean_within_test/false/Polygon/MultiPolygon/polygon-not-within-multipolygon.geojson create mode 100644 tests/test_files/boolean_within_test/false/Polygon/Polygon/Polygon-Polygon.geojson create mode 100644 tests/test_files/boolean_within_test/true/LineString/LineString/LineIsWithinLine.geojson create mode 100644 tests/test_files/boolean_within_test/true/LineString/LineString/LinesExactSame.geojson create mode 100644 tests/test_files/boolean_within_test/true/LineString/Polygon/LineIsContainedByPolygon.geojson create mode 100644 tests/test_files/boolean_within_test/true/LineString/Polygon/LineIsContainedByPolygonWithNoInternalVertices.geojson create mode 100644 tests/test_files/boolean_within_test/true/MultiPoint/LineString/MultipointsIsWithinLine.geojson create mode 100644 tests/test_files/boolean_within_test/true/MultiPoint/MultiPoint/MultiPointsWithinMultiPoints.geojson create mode 100644 tests/test_files/boolean_within_test/true/MultiPoint/MultiPolygon/multipoint-within-multipolygon.geojson create mode 100644 tests/test_files/boolean_within_test/true/MultiPoint/Polygon/MultiPointIsWithinPolygon.geojson create mode 100644 tests/test_files/boolean_within_test/true/MultiPoint/Polygon/MultiPointSingleIsWithinPolygon.geojson create mode 100644 tests/test_files/boolean_within_test/true/Point/LineString/PointIsWithinLine.geojson create mode 100644 tests/test_files/boolean_within_test/true/Point/MultiPoint/PointIsWithinMultiPoint.geojson create mode 100644 tests/test_files/boolean_within_test/true/Point/MultiPolygon/point-within-multipolygon.geojson create mode 100644 tests/test_files/boolean_within_test/true/Point/Polygon/PointIsWithinPolygon.geojson create mode 100644 tests/test_files/boolean_within_test/true/Polygon/MultiPolygon/polygon-within-multipolygon.geojson create mode 100644 tests/test_files/boolean_within_test/true/Polygon/Polygon/PolygonIsWIthinPolygon.geojson create mode 100644 tests/test_files/boolean_within_test/true/Polygon/Polygon/PolygonsExactSameShape.geojson rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/in/geometry-polygon.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/in/multi-polygon-outer-doughnut.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/in/multi-polygon-with-holes.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/in/multi-polygon.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/in/polygon-with-hole.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/in/polygon.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/out/geometry-polygon.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/out/multi-polygon-outer-doughnut.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/out/multi-polygon-with-holes.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/out/multi-polygon.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/out/polygon-with-hole.geojson (100%) rename tests/{ => test_files}/feature_conversion_polygon_to_line_test/out/polygon.geojson (100%) diff --git a/tests/test_boolean.py b/tests/test_boolean.py index c63539e..1601c17 100644 --- a/tests/test_boolean.py +++ b/tests/test_boolean.py @@ -7,7 +7,7 @@ import os import unittest -from turfpy.boolean import boolean_disjoint, boolean_intersects +from turfpy.boolean import boolean_disjoint, boolean_intersects, boolean_within def load_json_file_sync(filepath): @@ -22,7 +22,11 @@ def setUp(self): def test_true_fixtures(self): for filepath in glob.glob( os.path.join( - self.dirname, "boolean_disjoint_test", "true", "**", "*.geojson" + self.dirname, + "test_files/boolean_disjoint_test", + "true", + "**", + "*.geojson", ), recursive=True, ): @@ -35,7 +39,11 @@ def test_true_fixtures(self): def test_false_fixtures(self): for filepath in glob.glob( os.path.join( - self.dirname, "boolean_disjoint_test", "false", "**", "*.geojson" + self.dirname, + "test_files/boolean_disjoint_test", + "false", + "**", + "*.geojson", ), recursive=True, ): @@ -43,6 +51,9 @@ def test_false_fixtures(self): feature1 = geojson["features"][0] feature2 = geojson["features"][1] result = boolean_disjoint(feature1, feature2) + if result is True: + print(json.dumps(feature1)) + print(json.dumps(feature2)) self.assertFalse(result, False) @@ -53,7 +64,11 @@ def setUp(self): def test_true_fixtures(self): for filepath in glob.glob( os.path.join( - self.dirname, "boolean_intersects_test", "true", "**", "*.geojson" + self.dirname, + "test_files/boolean_intersects_test", + "true", + "**", + "*.geojson", ), recursive=True, ): @@ -61,12 +76,19 @@ def test_true_fixtures(self): feature1 = geojson["features"][0] feature2 = geojson["features"][1] result = boolean_intersects(feature1, feature2) + if result is False: + print(json.dumps(feature1)) + print(json.dumps(feature2)) self.assertTrue(result, True) def test_false_fixtures(self): for filepath in glob.glob( os.path.join( - self.dirname, "boolean_intersects_test", "false", "**", "*.geojson" + self.dirname, + "test_files/boolean_intersects_test", + "false", + "**", + "*.geojson", ), recursive=True, ): @@ -75,3 +97,43 @@ def test_false_fixtures(self): feature2 = geojson["features"][1] result = boolean_intersects(feature1, feature2) self.assertFalse(result, False) + + +class TestTurfBooleanWithin(unittest.TestCase): + def setUp(self): + self.dirname = os.path.dirname(os.path.abspath(__file__)) + + def test_true_fixtures(self): + for filepath in glob.glob( + os.path.join( + self.dirname, + "test_files/boolean_within_test", + "true", + "**", + "*.geojson", + ), + recursive=True, + ): + geojson = load_json_file_sync(filepath) + + feature1 = geojson["features"][0] + feature2 = geojson["features"][1] + result = boolean_within(feature1, feature2) + self.assertTrue(result, True) + + def test_false_fixtures(self): + for filepath in glob.glob( + os.path.join( + self.dirname, + "test_files/boolean_within_test", + "false", + "**", + "*.geojson", + ), + recursive=True, + ): + geojson = load_json_file_sync(filepath) + feature1 = geojson["features"][0] + feature2 = geojson["features"][1] + result = boolean_within(feature1, feature2) + self.assertFalse(result, False) \ No newline at end of file diff --git a/tests/test_feature_conversion.py b/tests/test_feature_conversion.py index ba6decd..835fd06 100644 --- a/tests/test_feature_conversion.py +++ b/tests/test_feature_conversion.py @@ -12,8 +12,8 @@ # Define directories current_dir = Path(__file__).resolve().parent directories = { - "in": current_dir / "feature_conversion_polygon_to_line_test" / "in", - "out": current_dir / "feature_conversion_polygon_to_line_test" / "out", + "in": current_dir / "test_files/feature_conversion_polygon_to_line_test" / "in", + "out": current_dir / "test_files/feature_conversion_polygon_to_line_test" / "out", } # Load fixtures @@ -32,7 +32,6 @@ class TestPolygonToLine(unittest.TestCase): - def test_polygon_to_linestring(self): for fixture in fixtures: name = fixture["name"] @@ -47,4 +46,4 @@ def test_polygon_to_linestring(self): expected_results = json.load(file) # Assert the results are as expected - self.assertEqual(results, expected_results, name) + self.assertEqual(results, expected_results, name) \ No newline at end of file diff --git a/tests/boolean_disjoint_test/false/LineString/LineString/LineString-LineString.geojson b/tests/test_files/boolean_disjoint_test/false/LineString/LineString/LineString-LineString.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/LineString/LineString/LineString-LineString.geojson rename to tests/test_files/boolean_disjoint_test/false/LineString/LineString/LineString-LineString.geojson diff --git a/tests/boolean_disjoint_test/false/LineString/Point/LineString-Point-1.geojson b/tests/test_files/boolean_disjoint_test/false/LineString/Point/LineString-Point-1.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/LineString/Point/LineString-Point-1.geojson rename to tests/test_files/boolean_disjoint_test/false/LineString/Point/LineString-Point-1.geojson diff --git a/tests/boolean_disjoint_test/false/LineString/Point/LineString-Point-2.geojson b/tests/test_files/boolean_disjoint_test/false/LineString/Point/LineString-Point-2.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/LineString/Point/LineString-Point-2.geojson rename to tests/test_files/boolean_disjoint_test/false/LineString/Point/LineString-Point-2.geojson diff --git a/tests/boolean_disjoint_test/false/LineString/Polygon/LineString-In-Polygon.geojson b/tests/test_files/boolean_disjoint_test/false/LineString/Polygon/LineString-In-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/LineString/Polygon/LineString-In-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/false/LineString/Polygon/LineString-In-Polygon.geojson diff --git a/tests/boolean_disjoint_test/false/LineString/Polygon/LineString-Polygon.geojson b/tests/test_files/boolean_disjoint_test/false/LineString/Polygon/LineString-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/LineString/Polygon/LineString-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/false/LineString/Polygon/LineString-Polygon.geojson diff --git a/tests/boolean_disjoint_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson b/tests/test_files/boolean_disjoint_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson rename to tests/test_files/boolean_disjoint_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson diff --git a/tests/boolean_disjoint_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson b/tests/test_files/boolean_disjoint_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson rename to tests/test_files/boolean_disjoint_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson diff --git a/tests/boolean_disjoint_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson b/tests/test_files/boolean_disjoint_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson diff --git a/tests/boolean_disjoint_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson b/tests/test_files/boolean_disjoint_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson diff --git a/tests/boolean_disjoint_test/false/Point/LineString/Point-LineString-1.geojson b/tests/test_files/boolean_disjoint_test/false/Point/LineString/Point-LineString-1.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Point/LineString/Point-LineString-1.geojson rename to tests/test_files/boolean_disjoint_test/false/Point/LineString/Point-LineString-1.geojson diff --git a/tests/boolean_disjoint_test/false/Point/LineString/Point-LineString-2.geojson b/tests/test_files/boolean_disjoint_test/false/Point/LineString/Point-LineString-2.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Point/LineString/Point-LineString-2.geojson rename to tests/test_files/boolean_disjoint_test/false/Point/LineString/Point-LineString-2.geojson diff --git a/tests/boolean_disjoint_test/false/Point/LineString/Point-LineString-3.geojson b/tests/test_files/boolean_disjoint_test/false/Point/LineString/Point-LineString-3.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Point/LineString/Point-LineString-3.geojson rename to tests/test_files/boolean_disjoint_test/false/Point/LineString/Point-LineString-3.geojson diff --git a/tests/boolean_disjoint_test/false/Point/LineString/Point-LineString-4.geojson b/tests/test_files/boolean_disjoint_test/false/Point/LineString/Point-LineString-4.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Point/LineString/Point-LineString-4.geojson rename to tests/test_files/boolean_disjoint_test/false/Point/LineString/Point-LineString-4.geojson diff --git a/tests/boolean_disjoint_test/false/Point/MultiPoint/Point-MultiPoint.geojson b/tests/test_files/boolean_disjoint_test/false/Point/MultiPoint/Point-MultiPoint.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Point/MultiPoint/Point-MultiPoint.geojson rename to tests/test_files/boolean_disjoint_test/false/Point/MultiPoint/Point-MultiPoint.geojson diff --git a/tests/boolean_disjoint_test/false/Point/Point/Point-Point.geojson b/tests/test_files/boolean_disjoint_test/false/Point/Point/Point-Point.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Point/Point/Point-Point.geojson rename to tests/test_files/boolean_disjoint_test/false/Point/Point/Point-Point.geojson diff --git a/tests/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-1.geojson b/tests/test_files/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-1.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-1.geojson rename to tests/test_files/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-1.geojson diff --git a/tests/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-2.geojson b/tests/test_files/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-2.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-2.geojson rename to tests/test_files/boolean_disjoint_test/false/Point/Polygon/Point-Polygon-2.geojson diff --git a/tests/boolean_disjoint_test/false/Polygon/LineString/Polygon-Containing-Linestring.geojson b/tests/test_files/boolean_disjoint_test/false/Polygon/LineString/Polygon-Containing-Linestring.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Polygon/LineString/Polygon-Containing-Linestring.geojson rename to tests/test_files/boolean_disjoint_test/false/Polygon/LineString/Polygon-Containing-Linestring.geojson diff --git a/tests/boolean_disjoint_test/false/Polygon/LineString/Polygon-LineString.geojson b/tests/test_files/boolean_disjoint_test/false/Polygon/LineString/Polygon-LineString.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Polygon/LineString/Polygon-LineString.geojson rename to tests/test_files/boolean_disjoint_test/false/Polygon/LineString/Polygon-LineString.geojson diff --git a/tests/boolean_disjoint_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson b/tests/test_files/boolean_disjoint_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson rename to tests/test_files/boolean_disjoint_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson diff --git a/tests/boolean_disjoint_test/false/Polygon/Point/Polygon-Point.geojson b/tests/test_files/boolean_disjoint_test/false/Polygon/Point/Polygon-Point.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Polygon/Point/Polygon-Point.geojson rename to tests/test_files/boolean_disjoint_test/false/Polygon/Point/Polygon-Point.geojson diff --git a/tests/boolean_disjoint_test/false/Polygon/Polygon/Large-Inside-Small.geojson b/tests/test_files/boolean_disjoint_test/false/Polygon/Polygon/Large-Inside-Small.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Polygon/Polygon/Large-Inside-Small.geojson rename to tests/test_files/boolean_disjoint_test/false/Polygon/Polygon/Large-Inside-Small.geojson diff --git a/tests/boolean_disjoint_test/false/Polygon/Polygon/Polygon-Polygon.geojson b/tests/test_files/boolean_disjoint_test/false/Polygon/Polygon/Polygon-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Polygon/Polygon/Polygon-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/false/Polygon/Polygon/Polygon-Polygon.geojson diff --git a/tests/boolean_disjoint_test/false/Polygon/Polygon/Small-Inside-Large.geojson b/tests/test_files/boolean_disjoint_test/false/Polygon/Polygon/Small-Inside-Large.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Polygon/Polygon/Small-Inside-Large.geojson rename to tests/test_files/boolean_disjoint_test/false/Polygon/Polygon/Small-Inside-Large.geojson diff --git a/tests/boolean_disjoint_test/false/Polygon/Polygon/issue-1216.geojson b/tests/test_files/boolean_disjoint_test/false/Polygon/Polygon/issue-1216.geojson similarity index 100% rename from tests/boolean_disjoint_test/false/Polygon/Polygon/issue-1216.geojson rename to tests/test_files/boolean_disjoint_test/false/Polygon/Polygon/issue-1216.geojson diff --git a/tests/boolean_disjoint_test/true/LineString/LineString/LineString-LineString.geojson b/tests/test_files/boolean_disjoint_test/true/LineString/LineString/LineString-LineString.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/LineString/LineString/LineString-LineString.geojson rename to tests/test_files/boolean_disjoint_test/true/LineString/LineString/LineString-LineString.geojson diff --git a/tests/boolean_disjoint_test/true/LineString/Point/LineString-Point.geojson b/tests/test_files/boolean_disjoint_test/true/LineString/Point/LineString-Point.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/LineString/Point/LineString-Point.geojson rename to tests/test_files/boolean_disjoint_test/true/LineString/Point/LineString-Point.geojson diff --git a/tests/boolean_disjoint_test/true/LineString/Polygon/LineString-Polygon.geojson b/tests/test_files/boolean_disjoint_test/true/LineString/Polygon/LineString-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/LineString/Polygon/LineString-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/true/LineString/Polygon/LineString-Polygon.geojson diff --git a/tests/boolean_disjoint_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson b/tests/test_files/boolean_disjoint_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson rename to tests/test_files/boolean_disjoint_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson diff --git a/tests/boolean_disjoint_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson b/tests/test_files/boolean_disjoint_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson rename to tests/test_files/boolean_disjoint_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson diff --git a/tests/boolean_disjoint_test/true/MultiPoint/Point/MultiPoint-Point.geojson b/tests/test_files/boolean_disjoint_test/true/MultiPoint/Point/MultiPoint-Point.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/MultiPoint/Point/MultiPoint-Point.geojson rename to tests/test_files/boolean_disjoint_test/true/MultiPoint/Point/MultiPoint-Point.geojson diff --git a/tests/boolean_disjoint_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson b/tests/test_files/boolean_disjoint_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson diff --git a/tests/boolean_disjoint_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson b/tests/test_files/boolean_disjoint_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson diff --git a/tests/boolean_disjoint_test/true/Point/LineString/Point-LineString.geojson b/tests/test_files/boolean_disjoint_test/true/Point/LineString/Point-LineString.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/Point/LineString/Point-LineString.geojson rename to tests/test_files/boolean_disjoint_test/true/Point/LineString/Point-LineString.geojson diff --git a/tests/boolean_disjoint_test/true/Point/MultiPoint/Point-Multipoint.geojson b/tests/test_files/boolean_disjoint_test/true/Point/MultiPoint/Point-Multipoint.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/Point/MultiPoint/Point-Multipoint.geojson rename to tests/test_files/boolean_disjoint_test/true/Point/MultiPoint/Point-Multipoint.geojson diff --git a/tests/boolean_disjoint_test/true/Point/Point/Point-Point.geojson b/tests/test_files/boolean_disjoint_test/true/Point/Point/Point-Point.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/Point/Point/Point-Point.geojson rename to tests/test_files/boolean_disjoint_test/true/Point/Point/Point-Point.geojson diff --git a/tests/boolean_disjoint_test/true/Point/Polygon/Point-Polygon.geojson b/tests/test_files/boolean_disjoint_test/true/Point/Polygon/Point-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/Point/Polygon/Point-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/true/Point/Polygon/Point-Polygon.geojson diff --git a/tests/boolean_disjoint_test/true/Polygon/LineString/Polygon-LineString.geojson b/tests/test_files/boolean_disjoint_test/true/Polygon/LineString/Polygon-LineString.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/Polygon/LineString/Polygon-LineString.geojson rename to tests/test_files/boolean_disjoint_test/true/Polygon/LineString/Polygon-LineString.geojson diff --git a/tests/boolean_disjoint_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson b/tests/test_files/boolean_disjoint_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson rename to tests/test_files/boolean_disjoint_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson diff --git a/tests/boolean_disjoint_test/true/Polygon/Point/Polygon-Point.geojson b/tests/test_files/boolean_disjoint_test/true/Polygon/Point/Polygon-Point.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/Polygon/Point/Polygon-Point.geojson rename to tests/test_files/boolean_disjoint_test/true/Polygon/Point/Polygon-Point.geojson diff --git a/tests/boolean_disjoint_test/true/Polygon/Polygon/Polygon-Polygon.geojson b/tests/test_files/boolean_disjoint_test/true/Polygon/Polygon/Polygon-Polygon.geojson similarity index 100% rename from tests/boolean_disjoint_test/true/Polygon/Polygon/Polygon-Polygon.geojson rename to tests/test_files/boolean_disjoint_test/true/Polygon/Polygon/Polygon-Polygon.geojson diff --git a/tests/boolean_intersects_test/false/LineString/LineString/LineString-LineString.geojson b/tests/test_files/boolean_intersects_test/false/LineString/LineString/LineString-LineString.geojson similarity index 100% rename from tests/boolean_intersects_test/false/LineString/LineString/LineString-LineString.geojson rename to tests/test_files/boolean_intersects_test/false/LineString/LineString/LineString-LineString.geojson diff --git a/tests/boolean_intersects_test/false/LineString/Point/LineString-Point.geojson b/tests/test_files/boolean_intersects_test/false/LineString/Point/LineString-Point.geojson similarity index 100% rename from tests/boolean_intersects_test/false/LineString/Point/LineString-Point.geojson rename to tests/test_files/boolean_intersects_test/false/LineString/Point/LineString-Point.geojson diff --git a/tests/boolean_intersects_test/false/LineString/Polygon/LineString-Polygon.geojson b/tests/test_files/boolean_intersects_test/false/LineString/Polygon/LineString-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/false/LineString/Polygon/LineString-Polygon.geojson rename to tests/test_files/boolean_intersects_test/false/LineString/Polygon/LineString-Polygon.geojson diff --git a/tests/boolean_intersects_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson b/tests/test_files/boolean_intersects_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson similarity index 100% rename from tests/boolean_intersects_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson rename to tests/test_files/boolean_intersects_test/false/MultiPoint/LineString/MultiPoint-LineString.geojson diff --git a/tests/boolean_intersects_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson b/tests/test_files/boolean_intersects_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson similarity index 100% rename from tests/boolean_intersects_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson rename to tests/test_files/boolean_intersects_test/false/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson diff --git a/tests/boolean_intersects_test/false/MultiPoint/Point/MultiPoint-Point.geojson b/tests/test_files/boolean_intersects_test/false/MultiPoint/Point/MultiPoint-Point.geojson similarity index 100% rename from tests/boolean_intersects_test/false/MultiPoint/Point/MultiPoint-Point.geojson rename to tests/test_files/boolean_intersects_test/false/MultiPoint/Point/MultiPoint-Point.geojson diff --git a/tests/boolean_intersects_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson b/tests/test_files/boolean_intersects_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson rename to tests/test_files/boolean_intersects_test/false/MultiPoint/Polygon/MultiPoint-Polygon.geojson diff --git a/tests/boolean_intersects_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson b/tests/test_files/boolean_intersects_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson rename to tests/test_files/boolean_intersects_test/false/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson diff --git a/tests/boolean_intersects_test/false/Point/LineString/Point-LineString.geojson b/tests/test_files/boolean_intersects_test/false/Point/LineString/Point-LineString.geojson similarity index 100% rename from tests/boolean_intersects_test/false/Point/LineString/Point-LineString.geojson rename to tests/test_files/boolean_intersects_test/false/Point/LineString/Point-LineString.geojson diff --git a/tests/boolean_intersects_test/false/Point/MultiPoint/Point-Multipoint.geojson b/tests/test_files/boolean_intersects_test/false/Point/MultiPoint/Point-Multipoint.geojson similarity index 100% rename from tests/boolean_intersects_test/false/Point/MultiPoint/Point-Multipoint.geojson rename to tests/test_files/boolean_intersects_test/false/Point/MultiPoint/Point-Multipoint.geojson diff --git a/tests/boolean_intersects_test/false/Point/Point/Point-Point.geojson b/tests/test_files/boolean_intersects_test/false/Point/Point/Point-Point.geojson similarity index 100% rename from tests/boolean_intersects_test/false/Point/Point/Point-Point.geojson rename to tests/test_files/boolean_intersects_test/false/Point/Point/Point-Point.geojson diff --git a/tests/boolean_intersects_test/false/Point/Polygon/Point-Polygon.geojson b/tests/test_files/boolean_intersects_test/false/Point/Polygon/Point-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/false/Point/Polygon/Point-Polygon.geojson rename to tests/test_files/boolean_intersects_test/false/Point/Polygon/Point-Polygon.geojson diff --git a/tests/boolean_intersects_test/false/Polygon/LineString/Polygon-LineString.geojson b/tests/test_files/boolean_intersects_test/false/Polygon/LineString/Polygon-LineString.geojson similarity index 100% rename from tests/boolean_intersects_test/false/Polygon/LineString/Polygon-LineString.geojson rename to tests/test_files/boolean_intersects_test/false/Polygon/LineString/Polygon-LineString.geojson diff --git a/tests/boolean_intersects_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson b/tests/test_files/boolean_intersects_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson similarity index 100% rename from tests/boolean_intersects_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson rename to tests/test_files/boolean_intersects_test/false/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson diff --git a/tests/boolean_intersects_test/false/Polygon/Point/Polygon-Point.geojson b/tests/test_files/boolean_intersects_test/false/Polygon/Point/Polygon-Point.geojson similarity index 100% rename from tests/boolean_intersects_test/false/Polygon/Point/Polygon-Point.geojson rename to tests/test_files/boolean_intersects_test/false/Polygon/Point/Polygon-Point.geojson diff --git a/tests/boolean_intersects_test/false/Polygon/Polygon/Polygon-Polygon.geojson b/tests/test_files/boolean_intersects_test/false/Polygon/Polygon/Polygon-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/false/Polygon/Polygon/Polygon-Polygon.geojson rename to tests/test_files/boolean_intersects_test/false/Polygon/Polygon/Polygon-Polygon.geojson diff --git a/tests/boolean_intersects_test/true/LineString/LineString/LineString-LineString.geojson b/tests/test_files/boolean_intersects_test/true/LineString/LineString/LineString-LineString.geojson similarity index 100% rename from tests/boolean_intersects_test/true/LineString/LineString/LineString-LineString.geojson rename to tests/test_files/boolean_intersects_test/true/LineString/LineString/LineString-LineString.geojson diff --git a/tests/boolean_intersects_test/true/LineString/Point/LineString-Point-1.geojson b/tests/test_files/boolean_intersects_test/true/LineString/Point/LineString-Point-1.geojson similarity index 100% rename from tests/boolean_intersects_test/true/LineString/Point/LineString-Point-1.geojson rename to tests/test_files/boolean_intersects_test/true/LineString/Point/LineString-Point-1.geojson diff --git a/tests/boolean_intersects_test/true/LineString/Point/LineString-Point-2.geojson b/tests/test_files/boolean_intersects_test/true/LineString/Point/LineString-Point-2.geojson similarity index 100% rename from tests/boolean_intersects_test/true/LineString/Point/LineString-Point-2.geojson rename to tests/test_files/boolean_intersects_test/true/LineString/Point/LineString-Point-2.geojson diff --git a/tests/boolean_intersects_test/true/LineString/Polygon/LineString-In-Polygon.geojson b/tests/test_files/boolean_intersects_test/true/LineString/Polygon/LineString-In-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/true/LineString/Polygon/LineString-In-Polygon.geojson rename to tests/test_files/boolean_intersects_test/true/LineString/Polygon/LineString-In-Polygon.geojson diff --git a/tests/boolean_intersects_test/true/LineString/Polygon/LineString-Polygon.geojson b/tests/test_files/boolean_intersects_test/true/LineString/Polygon/LineString-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/true/LineString/Polygon/LineString-Polygon.geojson rename to tests/test_files/boolean_intersects_test/true/LineString/Polygon/LineString-Polygon.geojson diff --git a/tests/boolean_intersects_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson b/tests/test_files/boolean_intersects_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson similarity index 100% rename from tests/boolean_intersects_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson rename to tests/test_files/boolean_intersects_test/true/MultiPoint/LineString/MultiPoint-LineString.geojson diff --git a/tests/boolean_intersects_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson b/tests/test_files/boolean_intersects_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson similarity index 100% rename from tests/boolean_intersects_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson rename to tests/test_files/boolean_intersects_test/true/MultiPoint/MultiPoint/MultiPoint-MultiPoint.geojson diff --git a/tests/boolean_intersects_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson b/tests/test_files/boolean_intersects_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson rename to tests/test_files/boolean_intersects_test/true/MultiPoint/Polygon/MultiPoint-Polygon.geojson diff --git a/tests/boolean_intersects_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson b/tests/test_files/boolean_intersects_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson rename to tests/test_files/boolean_intersects_test/true/MultiPolygon/Polygon/MultiPolygon-Polygon.geojson diff --git a/tests/boolean_intersects_test/true/Point/LineString/Point-LineString-1.geojson b/tests/test_files/boolean_intersects_test/true/Point/LineString/Point-LineString-1.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Point/LineString/Point-LineString-1.geojson rename to tests/test_files/boolean_intersects_test/true/Point/LineString/Point-LineString-1.geojson diff --git a/tests/boolean_intersects_test/true/Point/LineString/Point-LineString-2.geojson b/tests/test_files/boolean_intersects_test/true/Point/LineString/Point-LineString-2.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Point/LineString/Point-LineString-2.geojson rename to tests/test_files/boolean_intersects_test/true/Point/LineString/Point-LineString-2.geojson diff --git a/tests/boolean_intersects_test/true/Point/LineString/Point-LineString-3.geojson b/tests/test_files/boolean_intersects_test/true/Point/LineString/Point-LineString-3.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Point/LineString/Point-LineString-3.geojson rename to tests/test_files/boolean_intersects_test/true/Point/LineString/Point-LineString-3.geojson diff --git a/tests/boolean_intersects_test/true/Point/LineString/Point-LineString-4.geojson b/tests/test_files/boolean_intersects_test/true/Point/LineString/Point-LineString-4.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Point/LineString/Point-LineString-4.geojson rename to tests/test_files/boolean_intersects_test/true/Point/LineString/Point-LineString-4.geojson diff --git a/tests/boolean_intersects_test/true/Point/MultiPoint/Point-MultiPoint.geojson b/tests/test_files/boolean_intersects_test/true/Point/MultiPoint/Point-MultiPoint.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Point/MultiPoint/Point-MultiPoint.geojson rename to tests/test_files/boolean_intersects_test/true/Point/MultiPoint/Point-MultiPoint.geojson diff --git a/tests/boolean_intersects_test/true/Point/Point/Point-Point.geojson b/tests/test_files/boolean_intersects_test/true/Point/Point/Point-Point.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Point/Point/Point-Point.geojson rename to tests/test_files/boolean_intersects_test/true/Point/Point/Point-Point.geojson diff --git a/tests/boolean_intersects_test/true/Point/Polygon/Point-Polygon-1.geojson b/tests/test_files/boolean_intersects_test/true/Point/Polygon/Point-Polygon-1.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Point/Polygon/Point-Polygon-1.geojson rename to tests/test_files/boolean_intersects_test/true/Point/Polygon/Point-Polygon-1.geojson diff --git a/tests/boolean_intersects_test/true/Point/Polygon/Point-Polygon-2.geojson b/tests/test_files/boolean_intersects_test/true/Point/Polygon/Point-Polygon-2.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Point/Polygon/Point-Polygon-2.geojson rename to tests/test_files/boolean_intersects_test/true/Point/Polygon/Point-Polygon-2.geojson diff --git a/tests/boolean_intersects_test/true/Polygon/LineString/Polygon-Containing-Linestring.geojson b/tests/test_files/boolean_intersects_test/true/Polygon/LineString/Polygon-Containing-Linestring.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Polygon/LineString/Polygon-Containing-Linestring.geojson rename to tests/test_files/boolean_intersects_test/true/Polygon/LineString/Polygon-Containing-Linestring.geojson diff --git a/tests/boolean_intersects_test/true/Polygon/LineString/Polygon-LineString.geojson b/tests/test_files/boolean_intersects_test/true/Polygon/LineString/Polygon-LineString.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Polygon/LineString/Polygon-LineString.geojson rename to tests/test_files/boolean_intersects_test/true/Polygon/LineString/Polygon-LineString.geojson diff --git a/tests/boolean_intersects_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson b/tests/test_files/boolean_intersects_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson rename to tests/test_files/boolean_intersects_test/true/Polygon/MultiPolygon/Polygon-MultiPolygon.geojson diff --git a/tests/boolean_intersects_test/true/Polygon/Point/Polygon-Point.geojson b/tests/test_files/boolean_intersects_test/true/Polygon/Point/Polygon-Point.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Polygon/Point/Polygon-Point.geojson rename to tests/test_files/boolean_intersects_test/true/Polygon/Point/Polygon-Point.geojson diff --git a/tests/boolean_intersects_test/true/Polygon/Polygon/Large-Inside-Small.geojson b/tests/test_files/boolean_intersects_test/true/Polygon/Polygon/Large-Inside-Small.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Polygon/Polygon/Large-Inside-Small.geojson rename to tests/test_files/boolean_intersects_test/true/Polygon/Polygon/Large-Inside-Small.geojson diff --git a/tests/boolean_intersects_test/true/Polygon/Polygon/Polygon-Polygon.geojson b/tests/test_files/boolean_intersects_test/true/Polygon/Polygon/Polygon-Polygon.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Polygon/Polygon/Polygon-Polygon.geojson rename to tests/test_files/boolean_intersects_test/true/Polygon/Polygon/Polygon-Polygon.geojson diff --git a/tests/boolean_intersects_test/true/Polygon/Polygon/Small-Inside-Large.geojson b/tests/test_files/boolean_intersects_test/true/Polygon/Polygon/Small-Inside-Large.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Polygon/Polygon/Small-Inside-Large.geojson rename to tests/test_files/boolean_intersects_test/true/Polygon/Polygon/Small-Inside-Large.geojson diff --git a/tests/boolean_intersects_test/true/Polygon/Polygon/issue-1216.geojson b/tests/test_files/boolean_intersects_test/true/Polygon/Polygon/issue-1216.geojson similarity index 100% rename from tests/boolean_intersects_test/true/Polygon/Polygon/issue-1216.geojson rename to tests/test_files/boolean_intersects_test/true/Polygon/Polygon/issue-1216.geojson diff --git a/tests/test_files/boolean_within_test/false/LineString/LineString/LineIsNotWithinLine.geojson b/tests/test_files/boolean_within_test/false/LineString/LineString/LineIsNotWithinLine.geojson new file mode 100644 index 0000000..28c41a8 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/LineString/LineString/LineIsNotWithinLine.geojson @@ -0,0 +1,30 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 2], + [1, 3], + [1, 15.5] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/LineString/Polygon/LineIsNotWIthinPolygon.geojson b/tests/test_files/boolean_within_test/false/LineString/Polygon/LineIsNotWIthinPolygon.geojson new file mode 100644 index 0000000..c70523d --- /dev/null +++ b/tests/test_files/boolean_within_test/false/LineString/Polygon/LineIsNotWIthinPolygon.geojson @@ -0,0 +1,33 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 2], + [1, 3], + [1, 15.5] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/LineString/Polygon/LineIsNotWIthinPolygonBoundary.geojson b/tests/test_files/boolean_within_test/false/LineString/Polygon/LineIsNotWIthinPolygonBoundary.geojson new file mode 100644 index 0000000..d0125aa --- /dev/null +++ b/tests/test_files/boolean_within_test/false/LineString/Polygon/LineIsNotWIthinPolygonBoundary.geojson @@ -0,0 +1,33 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 2], + [1, 3], + [1, 3.5] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/MultiPoint/LineString/MultiPointsIsNotWIthinLine.geojson b/tests/test_files/boolean_within_test/false/MultiPoint/LineString/MultiPointsIsNotWIthinLine.geojson new file mode 100644 index 0000000..83c5ecc --- /dev/null +++ b/tests/test_files/boolean_within_test/false/MultiPoint/LineString/MultiPointsIsNotWIthinLine.geojson @@ -0,0 +1,30 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [12, 12], + [15, 15] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/MultiPoint/LineString/MultiPointsOnLineEndsIsNotWIthinLine.geojson b/tests/test_files/boolean_within_test/false/MultiPoint/LineString/MultiPointsOnLineEndsIsNotWIthinLine.geojson new file mode 100644 index 0000000..a99fcbc --- /dev/null +++ b/tests/test_files/boolean_within_test/false/MultiPoint/LineString/MultiPointsOnLineEndsIsNotWIthinLine.geojson @@ -0,0 +1,29 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [1, 4] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/MultiPoint/MultiPoint/MultiPointIsNotWithinMultiPoint.geojson b/tests/test_files/boolean_within_test/false/MultiPoint/MultiPoint/MultiPointIsNotWithinMultiPoint.geojson new file mode 100644 index 0000000..d00c24a --- /dev/null +++ b/tests/test_files/boolean_within_test/false/MultiPoint/MultiPoint/MultiPointIsNotWithinMultiPoint.geojson @@ -0,0 +1,28 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [1, 1.5] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [12, 12], + [15, 15] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/MultiPoint/MultiPolygon/multipoint-not-within-multipolygon.geojson b/tests/test_files/boolean_within_test/false/MultiPoint/MultiPolygon/multipoint-not-within-multipolygon.geojson new file mode 100644 index 0000000..7cd0e75 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/MultiPoint/MultiPolygon/multipoint-not-within-multipolygon.geojson @@ -0,0 +1,43 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [4, 4], + [-14, -14] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ], + [ + [ + [-1, -1], + [-1, -10], + [-10, -10], + [-10, -1], + [-1, -1] + ] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/MultiPoint/Polygon/MultiPointAllOnBoundaryIsNotWithinPolygon.geojson b/tests/test_files/boolean_within_test/false/MultiPoint/Polygon/MultiPointAllOnBoundaryIsNotWithinPolygon.geojson new file mode 100644 index 0000000..d69eb20 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/MultiPoint/Polygon/MultiPointAllOnBoundaryIsNotWithinPolygon.geojson @@ -0,0 +1,32 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [1, 10] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/MultiPoint/Polygon/MultiPointIsNotWithinPolygon.geojson b/tests/test_files/boolean_within_test/false/MultiPoint/Polygon/MultiPointIsNotWithinPolygon.geojson new file mode 100644 index 0000000..81743c7 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/MultiPoint/Polygon/MultiPointIsNotWithinPolygon.geojson @@ -0,0 +1,33 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [12, 12], + [15, 15] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/Point/LineString/PointIsNotWithinLine.geojson b/tests/test_files/boolean_within_test/false/Point/LineString/PointIsNotWithinLine.geojson new file mode 100644 index 0000000..e759552 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/Point/LineString/PointIsNotWithinLine.geojson @@ -0,0 +1,26 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [2, 2] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/Point/LineString/PointIsNotWithinLineBecauseOnEnd.geojson b/tests/test_files/boolean_within_test/false/Point/LineString/PointIsNotWithinLineBecauseOnEnd.geojson new file mode 100644 index 0000000..b30df47 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/Point/LineString/PointIsNotWithinLineBecauseOnEnd.geojson @@ -0,0 +1,26 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [1, 4] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/Point/LineString/PointOnEndIsWithinLinestring.geojson b/tests/test_files/boolean_within_test/false/Point/LineString/PointOnEndIsWithinLinestring.geojson new file mode 100644 index 0000000..b84728a --- /dev/null +++ b/tests/test_files/boolean_within_test/false/Point/LineString/PointOnEndIsWithinLinestring.geojson @@ -0,0 +1,26 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [1, 1] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/Point/MultiPoint/PointIsNotWithinMultiPoint.geojson b/tests/test_files/boolean_within_test/false/Point/MultiPoint/PointIsNotWithinMultiPoint.geojson new file mode 100644 index 0000000..ad262c6 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/Point/MultiPoint/PointIsNotWithinMultiPoint.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [1, 4] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [12, 12] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/Point/MultiPolygon/point-not-within-multipolygon.geojson b/tests/test_files/boolean_within_test/false/Point/MultiPolygon/point-not-within-multipolygon.geojson new file mode 100644 index 0000000..30c016b --- /dev/null +++ b/tests/test_files/boolean_within_test/false/Point/MultiPolygon/point-not-within-multipolygon.geojson @@ -0,0 +1,40 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [14, 14] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ], + [ + [ + [-1, -1], + [-1, -10], + [-10, -10], + [-10, -1], + [-1, -1] + ] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/Point/Polygon/PointIsNotWithinPolygon.geojson b/tests/test_files/boolean_within_test/false/Point/Polygon/PointIsNotWithinPolygon.geojson new file mode 100644 index 0000000..3bc2028 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/Point/Polygon/PointIsNotWithinPolygon.geojson @@ -0,0 +1,29 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [14, 14] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/Point/Polygon/PointOnPolygonBoundary.geojson b/tests/test_files/boolean_within_test/false/Point/Polygon/PointOnPolygonBoundary.geojson new file mode 100644 index 0000000..d24f78b --- /dev/null +++ b/tests/test_files/boolean_within_test/false/Point/Polygon/PointOnPolygonBoundary.geojson @@ -0,0 +1,29 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [1, 1] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/Polygon/MultiPolygon/polygon-not-within-multipolygon.geojson b/tests/test_files/boolean_within_test/false/Polygon/MultiPolygon/polygon-not-within-multipolygon.geojson new file mode 100644 index 0000000..fb07e04 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/Polygon/MultiPolygon/polygon-not-within-multipolygon.geojson @@ -0,0 +1,48 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-12, -12], + [-12, -19], + [-19, -19], + [-19, -12], + [-12, -12] + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ], + [ + [ + [-1, -1], + [-1, -10], + [-10, -10], + [-10, -1], + [-1, -1] + ] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/false/Polygon/Polygon/Polygon-Polygon.geojson b/tests/test_files/boolean_within_test/false/Polygon/Polygon/Polygon-Polygon.geojson new file mode 100644 index 0000000..5a1b425 --- /dev/null +++ b/tests/test_files/boolean_within_test/false/Polygon/Polygon/Polygon-Polygon.geojson @@ -0,0 +1,37 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 20], + [1, 3], + [1, 4], + [1, 1] + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/LineString/LineString/LineIsWithinLine.geojson b/tests/test_files/boolean_within_test/true/LineString/LineString/LineIsWithinLine.geojson new file mode 100644 index 0000000..dcf5cb1 --- /dev/null +++ b/tests/test_files/boolean_within_test/true/LineString/LineString/LineIsWithinLine.geojson @@ -0,0 +1,30 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 2], + [1, 3], + [1, 3.5] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/LineString/LineString/LinesExactSame.geojson b/tests/test_files/boolean_within_test/true/LineString/LineString/LinesExactSame.geojson new file mode 100644 index 0000000..034737b --- /dev/null +++ b/tests/test_files/boolean_within_test/true/LineString/LineString/LinesExactSame.geojson @@ -0,0 +1,31 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/LineString/Polygon/LineIsContainedByPolygon.geojson b/tests/test_files/boolean_within_test/true/LineString/Polygon/LineIsContainedByPolygon.geojson new file mode 100644 index 0000000..eba012a --- /dev/null +++ b/tests/test_files/boolean_within_test/true/LineString/Polygon/LineIsContainedByPolygon.geojson @@ -0,0 +1,33 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [2, 3], + [2, 3.5] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/LineString/Polygon/LineIsContainedByPolygonWithNoInternalVertices.geojson b/tests/test_files/boolean_within_test/true/LineString/Polygon/LineIsContainedByPolygonWithNoInternalVertices.geojson new file mode 100644 index 0000000..4d4f566 --- /dev/null +++ b/tests/test_files/boolean_within_test/true/LineString/Polygon/LineIsContainedByPolygonWithNoInternalVertices.geojson @@ -0,0 +1,32 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [10, 10] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/MultiPoint/LineString/MultipointsIsWithinLine.geojson b/tests/test_files/boolean_within_test/true/MultiPoint/LineString/MultipointsIsWithinLine.geojson new file mode 100644 index 0000000..cdf0856 --- /dev/null +++ b/tests/test_files/boolean_within_test/true/MultiPoint/LineString/MultipointsIsWithinLine.geojson @@ -0,0 +1,29 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [1, 1.5] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/MultiPoint/MultiPoint/MultiPointsWithinMultiPoints.geojson b/tests/test_files/boolean_within_test/true/MultiPoint/MultiPoint/MultiPointsWithinMultiPoints.geojson new file mode 100644 index 0000000..8a0585f --- /dev/null +++ b/tests/test_files/boolean_within_test/true/MultiPoint/MultiPoint/MultiPointsWithinMultiPoints.geojson @@ -0,0 +1,29 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [12, 12], + [15, 15] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [12, 12], + [15, 15] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/MultiPoint/MultiPolygon/multipoint-within-multipolygon.geojson b/tests/test_files/boolean_within_test/true/MultiPoint/MultiPolygon/multipoint-within-multipolygon.geojson new file mode 100644 index 0000000..9d78da4 --- /dev/null +++ b/tests/test_files/boolean_within_test/true/MultiPoint/MultiPolygon/multipoint-within-multipolygon.geojson @@ -0,0 +1,43 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [4, 4], + [-4, -4] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ], + [ + [ + [-1, -1], + [-1, -10], + [-10, -10], + [-10, -1], + [-1, -1] + ] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/MultiPoint/Polygon/MultiPointIsWithinPolygon.geojson b/tests/test_files/boolean_within_test/true/MultiPoint/Polygon/MultiPointIsWithinPolygon.geojson new file mode 100644 index 0000000..81f544d --- /dev/null +++ b/tests/test_files/boolean_within_test/true/MultiPoint/Polygon/MultiPointIsWithinPolygon.geojson @@ -0,0 +1,32 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [4, 4] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/MultiPoint/Polygon/MultiPointSingleIsWithinPolygon.geojson b/tests/test_files/boolean_within_test/true/MultiPoint/Polygon/MultiPointSingleIsWithinPolygon.geojson new file mode 100644 index 0000000..f4645c5 --- /dev/null +++ b/tests/test_files/boolean_within_test/true/MultiPoint/Polygon/MultiPointSingleIsWithinPolygon.geojson @@ -0,0 +1,29 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [[2, 2]] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/Point/LineString/PointIsWithinLine.geojson b/tests/test_files/boolean_within_test/true/Point/LineString/PointIsWithinLine.geojson new file mode 100644 index 0000000..8cb3cc4 --- /dev/null +++ b/tests/test_files/boolean_within_test/true/Point/LineString/PointIsWithinLine.geojson @@ -0,0 +1,26 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [1, 2] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [1, 3], + [1, 4] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/Point/MultiPoint/PointIsWithinMultiPoint.geojson b/tests/test_files/boolean_within_test/true/Point/MultiPoint/PointIsWithinMultiPoint.geojson new file mode 100644 index 0000000..f958a47 --- /dev/null +++ b/tests/test_files/boolean_within_test/true/Point/MultiPoint/PointIsWithinMultiPoint.geojson @@ -0,0 +1,24 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [1, 1] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [1, 1], + [12, 12] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/Point/MultiPolygon/point-within-multipolygon.geojson b/tests/test_files/boolean_within_test/true/Point/MultiPolygon/point-within-multipolygon.geojson new file mode 100644 index 0000000..84d378b --- /dev/null +++ b/tests/test_files/boolean_within_test/true/Point/MultiPolygon/point-within-multipolygon.geojson @@ -0,0 +1,40 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [-4, -4] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ], + [ + [ + [-1, -1], + [-1, -10], + [-10, -10], + [-10, -1], + [-1, -1] + ] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/Point/Polygon/PointIsWithinPolygon.geojson b/tests/test_files/boolean_within_test/true/Point/Polygon/PointIsWithinPolygon.geojson new file mode 100644 index 0000000..8e3ea36 --- /dev/null +++ b/tests/test_files/boolean_within_test/true/Point/Polygon/PointIsWithinPolygon.geojson @@ -0,0 +1,29 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [4, 4] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/Polygon/MultiPolygon/polygon-within-multipolygon.geojson b/tests/test_files/boolean_within_test/true/Polygon/MultiPolygon/polygon-within-multipolygon.geojson new file mode 100644 index 0000000..2819e2b --- /dev/null +++ b/tests/test_files/boolean_within_test/true/Polygon/MultiPolygon/polygon-within-multipolygon.geojson @@ -0,0 +1,48 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-2, -2], + [-2, -9], + [-9, -9], + [-9, -2], + [-2, -2] + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ], + [ + [ + [-1, -1], + [-1, -10], + [-10, -10], + [-10, -1], + [-1, -1] + ] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/Polygon/Polygon/PolygonIsWIthinPolygon.geojson b/tests/test_files/boolean_within_test/true/Polygon/Polygon/PolygonIsWIthinPolygon.geojson new file mode 100644 index 0000000..65d6525 --- /dev/null +++ b/tests/test_files/boolean_within_test/true/Polygon/Polygon/PolygonIsWIthinPolygon.geojson @@ -0,0 +1,36 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [2, 2], + [3, 2], + [1, 1] + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/tests/test_files/boolean_within_test/true/Polygon/Polygon/PolygonsExactSameShape.geojson b/tests/test_files/boolean_within_test/true/Polygon/Polygon/PolygonsExactSameShape.geojson new file mode 100644 index 0000000..8bfd83d --- /dev/null +++ b/tests/test_files/boolean_within_test/true/Polygon/Polygon/PolygonsExactSameShape.geojson @@ -0,0 +1,41 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "fill": "#F00" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-12.65625, 36.87962060502676], + [35.419921875, 36.38591277287651], + [37.79296875, 56.897003921272606], + [-12.12890625, 57.040729838360875], + [-12.65625, 36.87962060502676] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "fill": "#F00" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-12.65625, 36.87962060502676], + [35.419921875, 36.38591277287651], + [37.79296875, 56.897003921272606], + [-12.12890625, 57.040729838360875], + [-12.65625, 36.87962060502676] + ] + ] + } + } + ] +} diff --git a/tests/feature_conversion_polygon_to_line_test/in/geometry-polygon.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/in/geometry-polygon.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/in/geometry-polygon.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/in/geometry-polygon.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/in/multi-polygon-outer-doughnut.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/in/multi-polygon-outer-doughnut.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/in/multi-polygon-outer-doughnut.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/in/multi-polygon-outer-doughnut.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/in/multi-polygon-with-holes.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/in/multi-polygon-with-holes.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/in/multi-polygon-with-holes.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/in/multi-polygon-with-holes.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/in/multi-polygon.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/in/multi-polygon.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/in/multi-polygon.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/in/multi-polygon.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/in/polygon-with-hole.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/in/polygon-with-hole.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/in/polygon-with-hole.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/in/polygon-with-hole.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/in/polygon.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/in/polygon.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/in/polygon.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/in/polygon.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/out/geometry-polygon.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/out/geometry-polygon.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/out/geometry-polygon.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/out/geometry-polygon.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/out/multi-polygon-outer-doughnut.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/out/multi-polygon-outer-doughnut.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/out/multi-polygon-outer-doughnut.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/out/multi-polygon-outer-doughnut.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/out/multi-polygon-with-holes.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/out/multi-polygon-with-holes.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/out/multi-polygon-with-holes.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/out/multi-polygon-with-holes.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/out/multi-polygon.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/out/multi-polygon.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/out/multi-polygon.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/out/multi-polygon.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/out/polygon-with-hole.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/out/polygon-with-hole.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/out/polygon-with-hole.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/out/polygon-with-hole.geojson diff --git a/tests/feature_conversion_polygon_to_line_test/out/polygon.geojson b/tests/test_files/feature_conversion_polygon_to_line_test/out/polygon.geojson similarity index 100% rename from tests/feature_conversion_polygon_to_line_test/out/polygon.geojson rename to tests/test_files/feature_conversion_polygon_to_line_test/out/polygon.geojson diff --git a/tests/test_helper.py b/tests/test_helper.py index 3a86d11..0396f31 100644 --- a/tests/test_helper.py +++ b/tests/test_helper.py @@ -42,6 +42,8 @@ def __init__(self, node: str, point: Point): def test_get_coords(): """Test get_coords function""" - poly = Polygon([[(2.38, 57.322), (23.194, -20.28), (-120.43, 19.15), (2.38, 57.322)]]) + poly = Polygon( + [[(2.38, 57.322), (23.194, -20.28), (-120.43, 19.15), (2.38, 57.322)]] + ) coords = get_coords(poly) assert coords == poly.coordinates diff --git a/tests/test_measurement.py b/tests/test_measurement.py index 4b905e5..878c7e9 100644 --- a/tests/test_measurement.py +++ b/tests/test_measurement.py @@ -28,6 +28,7 @@ rhumb_distance, square, ) +import json def test_bbox_point(): @@ -174,7 +175,9 @@ def test_envelope(): def test_rhumb_destination(): - start = Feature(geometry=Point((-75.343, 39.984)), properties={"marker-color": "F00"}) + start = Feature( + geometry=Point((-75.343, 39.984)), properties={"marker-color": "F00"} + ) distance = 50 bearing = 90 dest = rhumb_destination( @@ -254,17 +257,16 @@ def test_destination(): def test_boolean_point_in_polygon(): - point = Feature(geometry=Point((-77, 44))) - polygon = Feature( - geometry=MultiPolygon( - [ - ([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), - ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],), - ] - ) + point = Point((-77, 44)) + polygon = MultiPolygon( + [ + ([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), + ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],), + ] ) + bpp = boolean_point_in_polygon(point, polygon) - assert bpp == True + assert bpp is True def test_point_to_line_distance(): @@ -346,19 +348,24 @@ def test_points_within_polygon(): fc = FeatureCollection([fpoly, fpoly2]) result = points_within_polygon(points, fc) assert result == { + "type": "FeatureCollection", "features": [ { - "geometry": {"coordinates": [-46.6318, -23.5523], "type": "Point"}, - "properties": {}, "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-46.6318, -23.5523]}, + "properties": {}, }, { - "geometry": {"coordinates": [-46.643, -23.557], "type": "Point"}, + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-46.663, -23.554]}, "properties": {}, + }, + { "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-46.643, -23.557]}, + "properties": {}, }, ], - "type": "FeatureCollection", } multi_polygon = Feature( @@ -379,4 +386,4 @@ def test_points_within_polygon(): } ], "type": "FeatureCollection", - } + } \ No newline at end of file diff --git a/tests/test_random.py b/tests/test_random.py index 2e53c23..8355465 100644 --- a/tests/test_random.py +++ b/tests/test_random.py @@ -28,8 +28,8 @@ def test_random_position(): pos = random_position(bbox=bbox(data)) assert len(pos) == 2 - pos = Feature(geometry=Point(pos)) - assert boolean_point_in_polygon(point=pos, polygon=data) + pos = Point(pos) + assert boolean_point_in_polygon(point=pos, polygon=data["geometry"]) def test_random_points(): @@ -53,4 +53,6 @@ def test_random_points(): pos = random_points(count=3, bbox=bbox(data)) assert len(pos["features"]) == 3 for point in pos["features"]: - assert boolean_point_in_polygon(point=point, polygon=data) + assert boolean_point_in_polygon( + point=point["geometry"], polygon=data["geometry"] + ) \ No newline at end of file diff --git a/tests/test_transformation.py b/tests/test_transformation.py index d92f4f1..a491cc5 100644 --- a/tests/test_transformation.py +++ b/tests/test_transformation.py @@ -287,12 +287,16 @@ def test_dissolve(): def test_difference(): f1 = Feature( - geometry=Polygon([[[128, -26], [141, -26], [141, -21], [128, -21], [128, -26]]]), + geometry=Polygon( + [[[128, -26], [141, -26], [141, -21], [128, -21], [128, -26]]] + ), properties={"combine": "yes", "fill": "#00f"}, ) f2 = Feature( - geometry=Polygon([[[126, -28], [140, -28], [140, -20], [126, -20], [126, -28]]]), + geometry=Polygon( + [[[126, -28], [140, -28], [140, -20], [126, -20], [126, -28]]] + ), properties={"combine": "yes"}, ) @@ -325,7 +329,6 @@ def test_transform_rotate(): def test_transform_translate(): - f = Feature(geometry=Polygon([[[0, 29], [3.5, 29], [2.5, 32], [0, 29]]])) translate_feature = transform_translate(f, 100, 35, mutate=True) diff --git a/turfpy/__init__.py b/turfpy/__init__.py index 020cef4..5783c91 100644 --- a/turfpy/__init__.py +++ b/turfpy/__init__.py @@ -1,4 +1,3 @@ -"""A Python library for performing geospatial data analysis which reimplements turf.js -""" +"""A Python library for performing geospatial data analysis which reimplements turf.js""" from .__version__ import __version__ # noqa F401 diff --git a/turfpy/boolean.py b/turfpy/boolean.py index a09450f..669569f 100644 --- a/turfpy/boolean.py +++ b/turfpy/boolean.py @@ -5,7 +5,16 @@ link: http://turfjs.org/ """ -from geojson import Feature, LineString, Point, Polygon +import json +from typing import Optional, Tuple + +from geojson import Feature, LineString, MultiPolygon, Point, Polygon +from shapely import to_geojson +from shapely.geometry import LineString as ShapelyLineString +from shapely.geometry import MultiPoint as ShapelyMultiPoint +from shapely.geometry import Point as ShapelyPoint +from shapely.geometry import Polygon as ShapelyPolygon +from shapely.geometry import shape from turfpy.feature_conversion import polygon_to_line from turfpy.measurement import boolean_point_in_polygon @@ -28,7 +37,9 @@ def __is_point_on_line(line_string: LineString, point: Point) -> bool: pt_coordinates = point["coordinates"] for i in range(len(coordinates) - 1): - if __is_point_on_line_segment(coordinates[i], coordinates[i + 1], pt_coordinates): + if __is_point_on_line_segment( + coordinates[i], coordinates[i + 1], pt_coordinates, False, None, None + ): return True return False @@ -64,7 +75,7 @@ def __is_line_in_poly(polygon: Polygon, line_string: LineString) -> bool: :rtype: bool """ for coord in line_string["coordinates"]: - if boolean_point_in_polygon(coord, polygon): + if boolean_point_in_polygon(Point(coordinates=coord), polygon): return True do_lines_intersect = line_intersect(line_string, polygon_to_line(polygon)) @@ -87,11 +98,11 @@ def __is_poly_in_poly(feature1: Polygon, feature2: Polygon) -> bool: :rtype: bool """ for coord1 in feature1["coordinates"][0]: - if boolean_point_in_polygon(coord1, feature2): + if boolean_point_in_polygon(Point(coordinates=coord1), feature2): return True for coord2 in feature2["coordinates"][0]: - if boolean_point_in_polygon(coord2, feature1): + if boolean_point_in_polygon(Point(coordinates=coord2), feature1): return True do_lines_intersect = line_intersect( @@ -104,43 +115,6 @@ def __is_poly_in_poly(feature1: Polygon, feature2: Polygon) -> bool: return False -def __is_point_on_line_segment( - line_segment_start: list, line_segment_end: list, point: list -) -> bool: - """ - Determine if point is on a line segment - - :param line_segment_start: start of line segment - :type line_segment_start: list - :param line_segment_end: end of line segment - :type line_segment_end: list - :param point: point to check - :type point: list - :returns: True if point is on the line segment - :rtype: bool - """ - - dxc = point[0] - line_segment_start[0] - dyc = point[1] - line_segment_start[1] - dxl = line_segment_end[0] - line_segment_start[0] - dyl = line_segment_end[1] - line_segment_start[1] - cross = dxc * dyl - dyc * dxl - - if cross != 0: - return False - - if abs(dxl) >= abs(dyl): - if dxl > 0: - return line_segment_start[0] <= point[0] <= line_segment_end[0] - else: - return line_segment_end[0] <= point[0] <= line_segment_start[0] - else: - if dyl > 0: - return line_segment_start[1] <= point[1] <= line_segment_end[1] - else: - return line_segment_end[1] <= point[1] <= line_segment_start[1] - - def __compare_coords(pair1: list, pair2: list) -> bool: """ Compare coordinates to see if they match @@ -176,7 +150,8 @@ def __disjoint(feature_1: Feature, feature_2: Feature) -> bool: if geom1_type == "Point": if geom2_type == "Point": return not __compare_coords( - feature_1["geometry"]["coordinates"], feature_2["geometry"]["coordinates"] + feature_1["geometry"]["coordinates"], + feature_2["geometry"]["coordinates"], ) elif geom2_type == "LineString": return not __is_point_on_line(feature_2["geometry"], feature_1["geometry"]) @@ -270,10 +245,213 @@ def inner_check(flatten2, index2, feature_collection2): nonlocal bool_result if bool_result: return True - bool_result = not boolean_disjoint(flatten1["geometry"], flatten2["geometry"]) + bool_result = not boolean_disjoint( + flatten1["geometry"], flatten2["geometry"] + ) flatten_each(feature_2, inner_check) flatten_each(feature_1, check_intersection) return bool_result + + +def __is_point_in_multipoint( + point: ShapelyPoint, multipoint: ShapelyMultiPoint +) -> bool: + return any(point.equals(ShapelyPoint(coord)) for coord in multipoint.geoms) + + +def __is_multipoint_in_multipoint( + multipoint1: ShapelyMultiPoint, multipoint2: ShapelyMultiPoint +) -> bool: + return all( + any( + ShapelyPoint(coord1).equals(ShapelyPoint(coord2)) + for coord2 in multipoint2.geoms + ) + for coord1 in multipoint1.geoms + ) + + +def __is_multipoint_on_line( + multipoint: ShapelyMultiPoint, line: ShapelyLineString +) -> bool: + found_inside_point = False + + for point in multipoint.geoms: + if not boolean_point_on_line(point, line): + return False + if not found_inside_point: + found_inside_point = boolean_point_on_line( + point, line, ignore_end_vertices=True + ) + + return found_inside_point + + +def __is_multipoint_in_poly( + multipoint: ShapelyMultiPoint, poly: ShapelyPolygon +) -> bool: + output = True + one_inside = False + is_inside = False + + for coord in multipoint.geoms: + polygon_type = poly.geom_type + if polygon_type == "Polygon": + geojson_poly = Polygon( + coordinates=json.loads(to_geojson(poly))["coordinates"] + ) + elif polygon_type == "MultiPolygon": + geojson_poly = MultiPolygon( + coordinates=json.loads(to_geojson(poly))["coordinates"] + ) + is_inside = boolean_point_in_polygon( + json.loads(to_geojson(coord)), geojson_poly + ) + + if not is_inside: + output = False + break + if not one_inside: + is_inside = boolean_point_in_polygon( + json.loads(to_geojson(coord)), + geojson_poly, + ignore_boundary=True, + ) + return output and is_inside + + +def boolean_point_on_line( + pt: Point, + line: LineString, + ignore_end_vertices: bool = False, + epsilon: Optional[float] = None, +) -> bool: + pt_coords = pt.coords[0] + line_coords = list(line.coords) + + for i in range(len(line_coords) - 1): + ignore_boundary = False + ignore_boundary_type = None + if ignore_end_vertices: + if i == 0: + ignore_boundary = True + ignore_boundary_type = "start" + if i == len(line_coords) - 2: + ignore_boundary = True + ignore_boundary_type = "end" + if i == 0 and i + 1 == len(line_coords) - 1: + ignore_boundary = True + ignore_boundary_type = "both" + + if __is_point_on_line_segment( + line_coords[i], + line_coords[i + 1], + pt_coords, + ignore_boundary, + ignore_boundary_type, + epsilon, + ): + return True + + return False + + +def __is_point_on_line_segment( + line_segment_start: Tuple[float, float], + line_segment_end: Tuple[float, float], + pt: Tuple[float, float], + exclude_boundary: Optional[bool], + exclude_boundary_type: Optional[str], + epsilon: Optional[float], +) -> bool: + x, y = pt + x1, y1 = line_segment_start + x2, y2 = line_segment_end + dxc = x - x1 + dyc = y - y1 + dxl = x2 - x1 + dyl = y2 - y1 + cross = dxc * dyl - dyc * dxl + + if epsilon is not None: + if abs(cross) > epsilon: + return False + elif cross != 0: + return False + + if not exclude_boundary: + if abs(dxl) >= abs(dyl): + return dxl > 0 if x1 <= x <= x2 else x2 <= x <= x1 + return dyl > 0 if y1 <= y <= y2 else y2 <= y <= y1 + elif exclude_boundary and exclude_boundary_type == "start": + if abs(dxl) >= abs(dyl): + return dxl > 0 if x1 < x <= x2 else x2 <= x < x1 + return dyl > 0 if y1 < y <= y2 else y2 <= y < y1 + elif exclude_boundary and exclude_boundary_type == "end": + if abs(dxl) >= abs(dyl): + return dxl > 0 if x1 <= x < x2 else x2 < x <= x1 + return dyl > 0 if y1 <= y < y2 else y2 < y <= y1 + elif exclude_boundary and exclude_boundary_type == "both": + if abs(dxl) >= abs(dyl): + return dxl > 0 if x1 < x < x2 else x2 < x < x1 + return dyl > 0 if y1 < y < y2 else y2 < y < y1 + + return False + + +def boolean_within(feature_1: Feature, feature_2: Feature) -> bool: + """ + Boolean-within returns (TRUE) if the first geometry is + completely within the second geometry. The interiors + of both geometries must intersect and, the interior + and boundary of the primary (geometry a) must not + intersect the exterior of the secondary (geometry b). + + :param feature_1: GeoJSON Feature or Geometry + :type feature_1: Feature + :param feature_2: GeoJSON Feature or Geometry + :type feature_2: Feature + :returns: True if the first geometry is + completely within the second geometry. + :rtype: bool + """ + geom1 = shape(feature_1["geometry"]) + geom2 = shape(feature_2["geometry"]) + type1 = geom1.geom_type + type2 = geom2.geom_type + + if type1 == "Point": + if type2 == "MultiPoint": + return __is_point_in_multipoint(geom1, geom2) + elif type2 == "LineString": + return geom1.within(geom2) + elif type2 in ["Polygon", "MultiPolygon"]: + return geom1.within(geom2) + else: + raise ValueError(f"feature2 {type2} geometry not supported") + elif type1 == "MultiPoint": + if type2 == "MultiPoint": + return __is_multipoint_in_multipoint(geom1, geom2) + elif type2 == "LineString": + return __is_multipoint_on_line(geom1, geom2) + elif type2 in ["Polygon", "MultiPolygon"]: + return __is_multipoint_in_poly(geom1, geom2) + else: + raise ValueError(f"feature2 {type2} geometry not supported") + elif type1 == "LineString": + if type2 == "LineString": + return geom1.within(geom2) + elif type2 in ["Polygon", "MultiPolygon"]: + return geom1.within(geom2) + else: + raise ValueError(f"feature2 {type2} geometry not supported") + elif type1 == "Polygon": + if type2 in ["Polygon", "MultiPolygon"]: + return geom1.within(geom2) + else: + raise ValueError(f"feature2 {type2} geometry not supported") + else: + raise ValueError(f"feature1 {type1} geometry not supported") diff --git a/turfpy/dev_lib/earcut.py b/turfpy/dev_lib/earcut.py index d58157d..9727dba 100644 --- a/turfpy/dev_lib/earcut.py +++ b/turfpy/dev_lib/earcut.py @@ -393,10 +393,11 @@ def __find_hole_bridge(hole, outer_node): and p.x >= mx and __point_in_triangle(hx_or_qx, hy, mx, my, qx_or_hx, hy, p.x, p.y) ): - tan = abs(hy - p.y) / (hx - p.x) # tangential - if (tan < tanMin or (tan == tanMin and p.x > m.x)) and locallyInside(p, hole): + if (tan < tanMin or (tan == tanMin and p.x > m.x)) and locallyInside( + p, hole + ): m = p tanMin = tan @@ -460,7 +461,6 @@ def __sort_linked(_list): qSize = inSize while pSize > 0 or (qSize > 0 and q): - if pSize == 0: e = q q = q.nextZ diff --git a/turfpy/dev_lib/spline.py b/turfpy/dev_lib/spline.py index 8939be7..bdff9b9 100644 --- a/turfpy/dev_lib/spline.py +++ b/turfpy/dev_lib/spline.py @@ -56,11 +56,9 @@ def __init__(self, points_data=[], resolution=10000, sharpness=0.85): }, # noqa: E501 { "x": (1.0 - self.sharpness) * self.points[i + 1]["x"] - + self.sharpness - * (self.centers[i + 1]["x"] + dx), # noqa: E501 # noqa: E126 + + self.sharpness * (self.centers[i + 1]["x"] + dx), # noqa: E501 # noqa: E126 "y": (1.0 - self.sharpness) * self.points[i + 1]["y"] - + self.sharpness - * (self.centers[i + 1]["y"] + dy), # noqa: E501 # noqa: E126 + + self.sharpness * (self.centers[i + 1]["y"] + dy), # noqa: E501 # noqa: E126 "z": (1.0 - self.sharpness) * self.points[i + 1]["z"] + self.sharpness * (self.centers[i + 1]["z"] + dz), # noqa: E501 }, diff --git a/turfpy/helper.py b/turfpy/helper.py index 1eb1ea8..1da9228 100644 --- a/turfpy/helper.py +++ b/turfpy/helper.py @@ -1,8 +1,9 @@ -import math - """ This module will have common utilities. """ + +import math + from geojson import Feature, Point from geojson.geometry import Geometry diff --git a/turfpy/measurement.py b/turfpy/measurement.py index ae7797f..70de291 100644 --- a/turfpy/measurement.py +++ b/turfpy/measurement.py @@ -23,13 +23,13 @@ Polygon, ) + from turfpy.helper import ( avg_earth_radius_km, convert_length, feature_of, get_coord, get_coords, - get_geom, get_type, length_to_radians, radians_to_length, @@ -142,7 +142,7 @@ def area( MultiPolygon, Feature, FeatureCollection, - ] + ], ): """ This function calculates the area of the Geojson object given as input. @@ -357,7 +357,9 @@ def length(geojson, units: str = "km"): def _callback_segment_reduce(previous_value, segment): coords = segment["geometry"]["coordinates"] return previous_value + distance( - Feature(geometry=Point(coords[0])), Feature(geometry=Point(coords[1])), units + Feature(geometry=Point(coords[0])), + Feature(geometry=Point(coords[1])), + units, ) return segment_reduce(geojson, _callback_segment_reduce, 0) @@ -403,7 +405,8 @@ def destination( radian = length_to_radians(distance) latitude2 = asin( - (sin(latitude1) * cos(radian)) + (cos(latitude1) * sin(radian) * cos(bearingRad)) + (sin(latitude1) * cos(radian)) + + (cos(latitude1) * sin(radian) * cos(bearingRad)) ) longitude2 = longitude1 + atan2( sin(bearingRad) * sin(radian) * cos(latitude1), @@ -509,7 +512,6 @@ def along(line: Feature, dist, unit: str = "km") -> Feature: ) return interpolated else: - travelled += distance( Feature(geometry=Point(coords[i])), Feature(geometry=Point(coords[i + 1])), @@ -679,7 +681,7 @@ def point_on_feature(geojson) -> Feature: k += 1 j += 1 elif geom["type"] == "Polygon" or geom["type"] == "MultiPolygon": - if boolean_point_in_polygon(cent, geom): + if boolean_point_in_polygon(cent, fc["features"][i]): on_surface = True i += 1 @@ -714,7 +716,7 @@ def _point_on_segment(x, y, x1, y1, x2, y2): # ------------ boolean point in polygon ----------------# -def boolean_point_in_polygon(point, polygon, ignore_boundary=False): +def boolean_point_in_polygon(point: Point, polygon: Polygon, ignore_boundary=False): """ Takes a Point or a Point Feature and Polygon or Polygon Feature as input and returns True if Point is in given Feature. @@ -735,13 +737,14 @@ def boolean_point_in_polygon(point, polygon, ignore_boundary=False): >>> ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],)])) >>> boolean_point_in_polygon(point, polygon) """ - if not point: + + if "type" not in point: raise Exception("point is required") - if not polygon: + if "type" not in polygon: raise Exception("polygon is required") - pt = get_coord(point) - geom = get_geom(polygon) + pt = point["coordinates"] + geom = polygon geo_type = geom["type"] bbox = polygon.get("bbox", None) polys = geom["coordinates"] @@ -952,9 +955,9 @@ def _is_below(point1, point2, point3): def _is_left(point1, point2, point3): - return (point2[0] - point1[0]) * (point3[1] - point1[1]) - (point3[0] - point1[0]) * ( - point2[1] - point1[1] - ) + return (point2[0] - point1[0]) * (point3[1] - point1[1]) - ( + point3[0] - point1[0] + ) * (point2[1] - point1[1]) # -------------------------------# @@ -962,7 +965,9 @@ def _is_left(point1, point2, point3): # ------------ point to line distance -----------# -def point_to_line_distance(point: Feature, line: Feature, units="km", method="geodesic"): +def point_to_line_distance( + point: Feature, line: Feature, units="km", method="geodesic" +): """ Returns the minimum distance between a Point and any segment of the LineString. @@ -982,7 +987,9 @@ def point_to_line_distance(point: Feature, line: Feature, units="km", method="ge >>> point_to_line_distance(point, linestring) """ if method != "geodesic" and method != "planar": - raise Exception("method name is incorrect ot should be either geodesic or planar") + raise Exception( + "method name is incorrect ot should be either geodesic or planar" + ) options = {"units": units, "method": method} @@ -1018,8 +1025,8 @@ def _callback_segment_each( segment_index, ): nonlocal options, distance - a = current_segment["geometry"]["coordinates"][0] - b = current_segment["geometry"]["coordinates"][1] + a = current_segment["coordinates"][0] + b = current_segment["coordinates"][1] d = distance_to_segment(p, a, b, options) if d < distance: distance = d @@ -1035,10 +1042,14 @@ def distance_to_segment(p, a, b, options): c1 = _dot(w, v) if c1 <= 0: - return _calc_distance(p, a, {"method": options.get("method", ""), "units": "deg"}) + return _calc_distance( + p, a, {"method": options.get("method", ""), "units": "deg"} + ) c2 = _dot(v, v) if c2 <= c1: - return _calc_distance(p, b, {"method": options.get("method", ""), "units": "deg"}) + return _calc_distance( + p, b, {"method": options.get("method", ""), "units": "deg"} + ) b2 = c1 / c2 Pb = [a[0] + (b2 * v[0]), a[1] + (b2 * v[1])] @@ -1149,7 +1160,9 @@ def rhumb_destination(origin, distance, bearing, options: dict = {}) -> Feature: if was_negative_distance: distance_in_meters = -1 * (abs(distance_in_meters)) coords = get_coord(origin) - destination_point = _calculate_rhumb_destination(coords, distance_in_meters, bearing) + destination_point = _calculate_rhumb_destination( + coords, distance_in_meters, bearing + ) return Feature( geometry=Point(destination_point), properties=options.get("properties", ""), @@ -1353,11 +1366,12 @@ def __callback_geom_each( current_geometry, feature_index, feature_properties, feature_bbox, feature_id ): contained = False - if boolean_point_in_polygon(point, current_geometry): - contained = True + nonlocal results + if point not in results: + if boolean_point_in_polygon(point["geometry"], current_geometry): + contained = True - if contained: - nonlocal results - results.append(point) + if contained: + results.append(point) geom_each(polygons, __callback_geom_each) diff --git a/turfpy/meta.py b/turfpy/meta.py index 0aef4f2..d63fd70 100644 --- a/turfpy/meta.py +++ b/turfpy/meta.py @@ -1,222 +1,40 @@ -from math import pi, sin +from geojson import ( + Feature, + Point, + LineString, +) -from geojson import Feature, LineString -RADIUS = 6378137 - - -def geom_reduce(geojson, initial_value_param): - initial_value = initial_value_param - previous_value = initial_value_param - - def callback_geom_each( - current_geometry, - feature_index, - feature_properties, - feature_bbox, - feature_id, - ): - nonlocal initial_value - nonlocal previous_value - - def callback_geom_reduce(value, geom): - return value + calculate_area(geom) - - if feature_index == 0 and initial_value: - previous_value = current_geometry - else: - previous_value = callback_geom_reduce(previous_value, current_geometry) - return previous_value - - geom_each(geojson, callback_geom_each) - return previous_value - - -def geom_each(geojson, callback): - """ - Iterate over each geometry in any GeoJSON object, similar to Array.forEach(). - - :param geojson: Point|Polygon|MultiPolygon|MultiPoint|LineString|MultiLineString| - FeatureCollection|Feature geojson any GeoJSON object - :param callback: - :return: - """ - feature_index = 0 - is_feature_collection = geojson["type"] == "FeatureCollection" - is_feature = geojson["type"] == "Feature" - stop = len(geojson["features"]) if is_feature_collection else 1 - - for i in range(0, stop): - if is_feature_collection: - geometry_maybe_collection = geojson["features"][i]["geometry"] - feature_properties = geojson["features"][i].get("properties") - feature_b_box = geojson["features"][i].get("bbox") - feature_id = geojson["features"][i].get("id") - elif is_feature: - geometry_maybe_collection = geojson["geometry"] - feature_properties = geojson.get("properties") - feature_b_box = geojson.get("bbox") - feature_id = geojson.get("id") - else: - geometry_maybe_collection = geojson - feature_properties = {} - feature_b_box = None - feature_id = None - - if geometry_maybe_collection: - if geometry_maybe_collection["type"] == "GeometryCollection": - is_geometry_collection = True - else: - is_geometry_collection = False - stop_g = ( - len(geometry_maybe_collection["geometries"]) if is_geometry_collection else 1 - ) - - for g in range(0, stop_g): - geometry = ( - geometry_maybe_collection["geometries"][g] - if is_geometry_collection - else geometry_maybe_collection - ) - - if not geometry: - if not callback( - None, - feature_index, - feature_properties, - feature_b_box, - feature_id, - ): - return False - continue - - if ( - geometry["type"] == "Point" - or geometry["type"] == "LineString" - or geometry["type"] == "MultiPoint" - or geometry["type"] == "Polygon" - or geometry["type"] == "MultiLineString" - or geometry["type"] == "MultiPolygon" - ): - if not callback( - geometry, - feature_index, - feature_properties, - feature_b_box, - feature_id, - ): - return False - break - elif geometry["type"] == "GeometryCollection": - for j in range(0, len(geometry["geometries"])): - if not callback( - geometry["geometries"][j], - feature_index, - feature_properties, - feature_b_box, - feature_id, - ): - return False - break - else: - raise Exception("Unknown Geometry Type") - feature_index += feature_index + 1 - - -def calculate_area(geom) -> float: - total = 0.0 - if geom["type"] == "Polygon": - return polygon_area(geom["coordinates"]) - elif geom["type"] == "MultiPolygon": - for coords in geom["coordinates"]: - total += polygon_area(coords) - return total - elif ( - geom["type"] == "Point" - or geom["type"] == "MultiPoint" - or geom["type"] == "LineString" - or geom["type"] == "MultiLineString" - ): - return 0 - return 0 - - -def polygon_area(coords: list) -> float: - total = 0 - - if coords and len(coords) > 0: - total += abs(ring_area(coords[0])) - for i in range(1, len(coords)): - total -= abs(ring_area(coords[i])) - - return total - - -def ring_area(coords: list): - total = 0.0 - coords_length = len(coords) - - if coords_length > 2: - for i in range(0, coords_length): - if i == coords_length - 2: - lower_index = coords_length - 2 - middle_index = coords_length - 1 - upper_index = 0 - elif i == coords_length - 1: - lower_index = coords_length - 1 - middle_index = 0 - upper_index = 1 - else: - lower_index = i - middle_index = i + 1 - upper_index = i + 2 - - p1 = coords[lower_index] - p2 = coords[middle_index] - p3 = coords[upper_index] - total += (rad(p3[0]) - rad(p1[0])) * sin(rad(p2[1])) - - total = total * RADIUS * RADIUS / 2 - return total - - -def rad(num: float): - return num * pi / 180 - - -def coord_each(geojson, callback, excludeWrapCoord=None): - """ - Iterate over coordinates in any GeoJSON object, similar to Array.forEach() - :return: - """ - if not geojson: +def coord_each(geojson_obj, callback, exclude_wrap_coord=False): + if geojson_obj is None: return - coord_index = 0 - type = geojson["type"] - is_feature_collection = type == "FeatureCollection" - is_feature = type == "Feature" - stop = len(geojson["features"]) if is_feature_collection else 1 - - for feature_index in range(0, stop): - if is_feature_collection: - geometry_maybe_collection = geojson["features"][feature_index]["geometry"] - elif is_feature: - geometry_maybe_collection = geojson["geometry"] - else: - geometry_maybe_collection = geojson - if geometry_maybe_collection: - is_geometry_collection = ( - geometry_maybe_collection["type"] == "GeometryCollection" - ) - else: - is_geometry_collection = False - - stopg = ( - len(geometry_maybe_collection["geometries"]) if is_geometry_collection else 1 + coord_index = 0 + is_feature_collection = geojson_obj["type"] == "FeatureCollection" + is_feature = geojson_obj["type"] == "Feature" + + stop = len(geojson_obj["features"]) if is_feature_collection else 1 + + for feature_index in range(stop): + geometry_maybe_collection = ( + geojson_obj["features"][feature_index]["geometry"] + if is_feature_collection + else geojson_obj["geometry"] + if is_feature + else geojson_obj + ) + is_geometry_collection = ( + geometry_maybe_collection["type"] == "GeometryCollection" + if geometry_maybe_collection + else False + ) + stop_g = ( + len(geometry_maybe_collection["geometries"]) + if is_geometry_collection + else 1 ) - for geom_index in range(0, stopg): + for geom_index in range(stop_g): multi_feature_index = 0 geometry_index = 0 geometry = ( @@ -224,23 +42,20 @@ def coord_each(geojson, callback, excludeWrapCoord=None): if is_geometry_collection else geometry_maybe_collection ) - - if not geometry: + if geometry is None: continue + coords = geometry["coordinates"] geom_type = geometry["type"] - wrap_shrink = ( 1 - if excludeWrapCoord + if exclude_wrap_coord and (geom_type == "Polygon" or geom_type == "MultiPolygon") else 0 ) - if geom_type: - if geom_type == "Point": - # if not callback(coords): - # return False + if geom_type == "Point": + if ( callback( coords, coord_index, @@ -248,12 +63,14 @@ def coord_each(geojson, callback, excludeWrapCoord=None): multi_feature_index, geometry_index, ) - coord_index += coord_index + 1 - multi_feature_index += multi_feature_index + 1 - elif geom_type == "LineString" or geom_type == "MultiPoint": - for j in range(0, len(coords)): - # if not callback(coords[j]): - # return False + is False + ): + return False + coord_index += 1 + multi_feature_index += 1 + elif geom_type in ["LineString", "MultiPoint"]: + for j in range(len(coords)): + if ( callback( coords[j], coord_index, @@ -261,16 +78,18 @@ def coord_each(geojson, callback, excludeWrapCoord=None): multi_feature_index, geometry_index, ) - coord_index += coord_index + 1 - if geom_type == "MultiPoint": - multi_feature_index += multi_feature_index + 1 - if geom_type == "LineString": - multi_feature_index += multi_feature_index + 1 - elif geom_type == "Polygon" or geom_type == "MultiLineString": - for j in range(0, len(coords)): - for k in range(0, len(coords[j]) - wrap_shrink): - # if not callback(coords[j][k]): - # return False + is False + ): + return False + coord_index += 1 + if geom_type == "MultiPoint": + multi_feature_index += 1 + if geom_type == "LineString": + multi_feature_index += 1 + elif geom_type in ["Polygon", "MultiLineString"]: + for j in range(len(coords)): + for k in range(len(coords[j]) - wrap_shrink): + if ( callback( coords[j][k], coord_index, @@ -278,78 +97,323 @@ def coord_each(geojson, callback, excludeWrapCoord=None): multi_feature_index, geometry_index, ) - coord_index += coord_index + 1 - if geom_type == "MultiLineString": - multi_feature_index += multi_feature_index + 1 - if geom_type == "Polygon": - geometry_index += geometry_index + 1 + is False + ): + return False + coord_index += 1 + if geom_type == "MultiLineString": + multi_feature_index += 1 if geom_type == "Polygon": - multi_feature_index += multi_feature_index + 1 - elif geom_type == "MultiPolygon": - for j in range(0, len(coords)): - geometry_index = 0 - for k in range(0, len(coords[j])): - for le in range(0, len(coords[j][k]) - wrap_shrink): - # if not callback(coords[j][k][l]): - # return False + geometry_index += 1 + if geom_type == "Polygon": + multi_feature_index += 1 + elif geom_type == "MultiPolygon": + for j in range(len(coords)): + geometry_index = 0 + for k in range(len(coords[j])): + for m in range(len(coords[j][k]) - wrap_shrink): + if ( callback( - coords[j][k][le], + coords[j][k][m], coord_index, feature_index, multi_feature_index, geometry_index, ) - coord_index += coord_index + 1 - geometry_index += geometry_index + 1 - multi_feature_index += multi_feature_index + 1 - elif geom_type == "GeometryCollection": - for j in range(0, len(geometry["geometries"])): - if not coord_each( - geometry["geometries"][j], - callback, - excludeWrapCoord, - ): - return False - else: - raise Exception("Unknown Geometry Type") - return True + is False + ): + return False + coord_index += 1 + geometry_index += 1 + multi_feature_index += 1 + elif geom_type == "GeometryCollection": + for j in range(len(geometry["geometries"])): + if ( + coord_each( + geometry["geometries"][j], callback, exclude_wrap_coord + ) + is False + ): + return False + else: + raise ValueError("Unknown Geometry Type") -def segment_reduce(geojson, callback, initial_value=None): +def coord_reduce(geojson_obj, callback, initial_value=None, exclude_wrap_coord=False): previous_value = initial_value - started = False - def callback_segment_each( - current_segment, - feature_index, - multi_feature_index, - geometry_index, - segment_index, - ): - nonlocal started + def _callback(*args): nonlocal previous_value - if not started and (not initial_value and initial_value != 0): - previous_value = current_segment + ( + current_coord, + coord_index, + feature_index, + multi_feature_index, + geometry_index, + ) = args + if coord_index == 0 and initial_value is None: + previous_value = current_coord else: - previous_value = callback(previous_value, current_segment) - started = True - return True + previous_value = callback( + previous_value, + current_coord, + coord_index, + feature_index, + multi_feature_index, + geometry_index, + ) + + coord_each(geojson_obj, _callback, exclude_wrap_coord) + return previous_value + + +def prop_each(geojson_obj, callback): + if geojson_obj["type"] == "FeatureCollection": + for i, feature in enumerate(geojson_obj["features"]): + if callback(feature["properties"], i) is False: + break + elif geojson_obj["type"] == "Feature": + callback(geojson_obj["properties"], 0) + + +def prop_reduce(geojson_obj, callback, initial_value=None): + previous_value = initial_value - segment_each(geojson, callback_segment_each) + def _callback(current_properties, feature_index): + nonlocal previous_value + if feature_index == 0 and initial_value is None: + previous_value = current_properties + else: + previous_value = callback(previous_value, current_properties, feature_index) + prop_each(geojson_obj, _callback) return previous_value -def segment_each(geojson, callback): - def callback_flatten_each(feature, feature_index, multi_feature_index): - segment_index = 0 +def feature_each(geojson_obj, callback): + if geojson_obj["type"] == "Feature": + callback(geojson_obj, 0) + elif geojson_obj["type"] == "FeatureCollection": + for i, feature in enumerate(geojson_obj["features"]): + if callback(feature, i) is False: + break + + +def feature_reduce(geojson_obj, callback, initial_value=None): + previous_value = initial_value + + def _callback(current_feature, feature_index): + nonlocal previous_value + if feature_index == 0 and initial_value is None: + previous_value = current_feature + else: + previous_value = callback(previous_value, current_feature, feature_index) + + feature_each(geojson_obj, _callback) + return previous_value + + +def coord_all(geojson_obj): + coords = [] + + def _callback(coord, *args): + coords.append(coord) + + coord_each(geojson_obj, _callback) + return coords + + +def geom_each(geojson_obj, callback): + feature_index = 0 + is_feature_collection = geojson_obj["type"] == "FeatureCollection" + is_feature = geojson_obj["type"] == "Feature" + stop = len(geojson_obj["features"]) if is_feature_collection else 1 + + for i in range(stop): + geometry_maybe_collection = ( + geojson_obj["features"][i]["geometry"] + if is_feature_collection + else geojson_obj["geometry"] + if is_feature + else geojson_obj + ) + feature_properties = ( + geojson_obj["features"][i]["properties"] + if is_feature_collection + else geojson_obj["properties"] + if is_feature + else {} + ) + feature_bbox = ( + geojson_obj["features"][i].get("bbox") + if is_feature_collection + else geojson_obj.get("bbox") + if is_feature + else None + ) + feature_id = ( + geojson_obj["features"][i].get("id") + if is_feature_collection + else geojson_obj.get("id") + if is_feature + else None + ) + is_geometry_collection = ( + geometry_maybe_collection["type"] == "GeometryCollection" + if geometry_maybe_collection + else False + ) + stop_g = ( + len(geometry_maybe_collection["geometries"]) + if is_geometry_collection + else 1 + ) - if not feature["geometry"]: + for g in range(stop_g): + geometry = ( + geometry_maybe_collection["geometries"][g] + if is_geometry_collection + else geometry_maybe_collection + ) + if geometry is None: + if ( + callback( + None, + feature_index, + feature_properties, + feature_bbox, + feature_id, + ) + is False + ): + return False + continue + + if geometry["type"] in [ + "Point", + "LineString", + "MultiPoint", + "Polygon", + "MultiLineString", + "MultiPolygon", + ]: + if ( + callback( + geometry, + feature_index, + feature_properties, + feature_bbox, + feature_id, + ) + is False + ): + return False + elif geometry["type"] == "GeometryCollection": + for geom in geometry["geometries"]: + if ( + callback( + geom, + feature_index, + feature_properties, + feature_bbox, + feature_id, + ) + is False + ): + return False + else: + raise ValueError("Unknown Geometry Type") + + feature_index += 1 + + +def geom_reduce(geojson_obj, callback, initial_value=None): + previous_value = initial_value + + def _callback( + current_geometry, feature_index, feature_properties, feature_bbox, feature_id + ): + nonlocal previous_value + if feature_index == 0 and initial_value is None: + previous_value = current_geometry + else: + previous_value = callback( + previous_value, + current_geometry, + feature_index, + feature_properties, + feature_bbox, + feature_id, + ) + + geom_each(geojson_obj, _callback) + return previous_value + + +def flatten_each(geojson_obj, callback): + def _flatten_callback(geometry, feature_index, properties, bbox, id_): + geom_type = None + if geometry is not None: + geom_type = geometry["type"] + if geom_type in [None, "Point", "LineString", "Polygon"]: + if ( + callback( + Feature( + geometry=geometry, properties=properties, bbox=bbox, id=id_ + ), + feature_index, + 0, + ) + is False + ): + return False return - type = feature["geometry"]["type"] + geom_type_mapping = { + "MultiPoint": "Point", + "MultiLineString": "LineString", + "MultiPolygon": "Polygon", + } + geom_type = geom_type_mapping[geom_type] + + for multi_feature_index, coordinates in enumerate(geometry["coordinates"]): + geom = {"type": geom_type, "coordinates": coordinates} + if ( + callback( + Feature(geometry=geom, properties=properties), + feature_index, + multi_feature_index, + ) + is False + ): + return False + + geom_each(geojson_obj, _flatten_callback) + - if type == "Point" or type == "MultiPoint": +def flatten_reduce(geojson_obj, callback, initial_value=None): + previous_value = initial_value + + def _callback(current_feature, feature_index, multi_feature_index): + nonlocal previous_value + if feature_index == 0 and multi_feature_index == 0 and initial_value is None: + previous_value = current_feature + else: + previous_value = callback( + previous_value, current_feature, feature_index, multi_feature_index + ) + + flatten_each(geojson_obj, _callback) + return previous_value + + +def segment_each(geojson_obj, callback): + def _segment_callback(feature, feature_index, multi_feature_index): + segment_index = 0 + if feature["geometry"] is None: + return + geom_type = feature["geometry"]["type"] + if geom_type in ["Point", "MultiPoint"]: return previous_coords = None @@ -357,20 +421,21 @@ def callback_flatten_each(feature, feature_index, multi_feature_index): previous_multi_index = 0 prev_geom_index = 0 - def callback_coord_each( + def _coord_callback( current_coord, coord_index, feature_index_coord, multi_part_index_coord, geometry_index, ): - nonlocal previous_coords - nonlocal previous_feature_index - nonlocal previous_multi_index - nonlocal prev_geom_index - nonlocal segment_index + nonlocal \ + previous_coords, \ + previous_feature_index, \ + previous_multi_index, \ + prev_geom_index, \ + segment_index if ( - not previous_coords + previous_coords is None or feature_index > previous_feature_index or multi_part_index_coord > previous_multi_index or geometry_index > prev_geom_index @@ -381,72 +446,261 @@ def callback_coord_each( prev_geom_index = geometry_index segment_index = 0 return - ls = LineString([previous_coords, current_coord]) - current_segment = Feature( - geometry=ls, - properties=feature["properties"] if feature["properties"] else {}, + + current_segment = LineString( + [previous_coords, current_coord], properties=feature["properties"] ) - if not callback( - current_segment, - feature_index, - multi_feature_index, - geometry_index, - segment_index, + if ( + callback( + current_segment, + feature_index, + multi_feature_index, + geometry_index, + segment_index, + ) + is False ): return False - segment_index = segment_index + 1 + segment_index += 1 previous_coords = current_coord - if not coord_each(feature, callback_coord_each): - return False + coord_each(feature, _coord_callback) - return True + flatten_each(geojson_obj, _segment_callback) - flatten_each(geojson, callback_flatten_each) +def segment_reduce(geojson_obj, callback, initial_value=None): + previous_value = initial_value + started = False -def flatten_each(geojson, callback): - def callback_geom_each(geometry, feature_index, properties, bbox, id): - if not geometry: - type = None + def _callback( + current_segment, + feature_index, + multi_feature_index, + geometry_index, + segment_index, + ): + nonlocal previous_value, started + if not started and initial_value is None: + previous_value = current_segment else: - type = geometry["type"] + previous_value = callback( + previous_value, + current_segment, + feature_index, + multi_feature_index, + geometry_index, + segment_index, + ) + started = True - if not type or type == "Point" or type == "LineString" or type == "Polygon": - if not callback( - Feature(geometry=geometry, bbox=bbox, id=id), feature_index, 0 - ): + segment_each(geojson_obj, _callback) + return previous_value + + +def line_each(geojson_obj, callback): + if not geojson_obj: + raise ValueError("geojson is required") + + def _line_callback(feature, feature_index, multi_feature_index): + if feature["geometry"] is None: + return + geom_type = feature["geometry"]["type"] + coords = feature["geometry"]["coordinates"] + + if geom_type == "LineString": + if callback(feature, feature_index, multi_feature_index, 0, 0) is False: return False - return True - - geom_type = "" - - if type == "MultiPoint": - geom_type = "Point" - elif type == "MultiLineString": - geom_type = "LineString" - elif type == "MultiPolygon": - geom_type = "Polygon" - - for multi_feature_index in range(0, len(geometry["coordinates"])): - coordinate = geometry["coordinates"][multi_feature_index] - geom = {"type": geom_type, "coordinates": coordinate} - if not callback( - Feature(geometry=geom, properties=properties), + elif geom_type == "Polygon": + for geometry_index, line_coords in enumerate(coords): + if ( + callback( + LineString(line_coords, properties=feature["properties"]), + feature_index, + multi_feature_index, + geometry_index, + ) + is False + ): + return False + + flatten_each(geojson_obj, _line_callback) + + +def line_reduce(geojson_obj, callback, initial_value=None): + previous_value = initial_value + + def _callback(current_line, feature_index, multi_feature_index, geometry_index): + nonlocal previous_value + if feature_index == 0 and initial_value is None: + previous_value = current_line + else: + previous_value = callback( + previous_value, + current_line, feature_index, multi_feature_index, - ): - return False + geometry_index, + ) - return True + line_each(geojson_obj, _callback) + return previous_value - geom_each(geojson, callback_geom_each) +def find_segment(geojson_obj, options): + if not isinstance(options, dict): + raise ValueError("options is invalid") + + feature_index = options.get("featureIndex", 0) + multi_feature_index = options.get("multiFeatureIndex", 0) + geometry_index = options.get("geometryIndex", 0) + segment_index = options.get("segmentIndex", 0) + properties = options.get("properties") + + geometry = None + if geojson_obj["type"] == "FeatureCollection": + if feature_index < 0: + feature_index = len(geojson_obj["features"]) + feature_index + properties = properties or geojson_obj["features"][feature_index]["properties"] + geometry = geojson_obj["features"][feature_index]["geometry"] + elif geojson_obj["type"] == "Feature": + properties = properties or geojson_obj["properties"] + geometry = geojson_obj["geometry"] + elif geojson_obj["type"] in ["Point", "MultiPoint"]: + return None + elif geojson_obj["type"] in [ + "LineString", + "Polygon", + "MultiLineString", + "MultiPolygon", + ]: + geometry = geojson_obj + else: + raise ValueError("geojson is invalid") + + if geometry is None: + return None + + coords = geometry["coordinates"] + if geometry["type"] == "Point" or geometry["type"] == "MultiPoint": + return None + elif geometry["type"] == "LineString": + if segment_index < 0: + segment_index = len(coords) + segment_index - 1 + return LineString( + [coords[segment_index], coords[segment_index + 1]], properties=properties + ) + elif geometry["type"] == "Polygon": + if geometry_index < 0: + geometry_index = len(coords) + geometry_index + if segment_index < 0: + segment_index = len(coords[geometry_index]) + segment_index - 1 + return LineString( + [ + coords[geometry_index][segment_index], + coords[geometry_index][segment_index + 1], + ], + properties=properties, + ) + elif geometry["type"] == "MultiLineString": + if multi_feature_index < 0: + multi_feature_index = len(coords) + multi_feature_index + if segment_index < 0: + segment_index = len(coords[multi_feature_index]) + segment_index - 1 + return LineString( + [ + coords[multi_feature_index][segment_index], + coords[multi_feature_index][segment_index + 1], + ], + properties=properties, + ) + elif geometry["type"] == "MultiPolygon": + if multi_feature_index < 0: + multi_feature_index = len(coords) + multi_feature_index + if geometry_index < 0: + geometry_index = len(coords[multi_feature_index]) + geometry_index + if segment_index < 0: + segment_index = ( + len(coords[multi_feature_index][geometry_index]) - segment_index - 1 + ) + return LineString( + [ + coords[multi_feature_index][geometry_index][segment_index], + coords[multi_feature_index][geometry_index][segment_index + 1], + ], + properties=properties, + ) + else: + raise ValueError("geojson is invalid") + + +def find_point(geojson_obj, options): + if not isinstance(options, dict): + raise ValueError("options is invalid") + + feature_index = options.get("featureIndex", 0) + multi_feature_index = options.get("multiFeatureIndex", 0) + geometry_index = options.get("geometryIndex", 0) + coord_index = options.get("coordIndex", 0) + properties = options.get("properties") + + geometry = None + if geojson_obj["type"] == "FeatureCollection": + if feature_index < 0: + feature_index = len(geojson_obj["features"]) + feature_index + properties = properties or geojson_obj["features"][feature_index]["properties"] + geometry = geojson_obj["features"][feature_index]["geometry"] + elif geojson_obj["type"] == "Feature": + properties = properties or geojson_obj["properties"] + geometry = geojson_obj["geometry"] + elif geojson_obj["type"] in ["Point", "MultiPoint"]: + return None + elif geojson_obj["type"] in [ + "LineString", + "Polygon", + "MultiLineString", + "MultiPolygon", + ]: + geometry = geojson_obj + else: + raise ValueError("geojson is invalid") + + if geometry is None: + return None + + coords = geometry["coordinates"] + if geometry["type"] == "Point": + return Point(coords, properties=properties) + elif geometry["type"] == "MultiPoint": + if multi_feature_index < 0: + multi_feature_index = len(coords) + multi_feature_index + return Point(coords[multi_feature_index], properties=properties) + elif geometry["type"] == "LineString": + if coord_index < 0: + coord_index = len(coords) + coord_index + return Point(coords[coord_index], properties=properties) + elif geometry["type"] == "Polygon": + if geometry_index < 0: + geometry_index = len(coords) + geometry_index + if coord_index < 0: + coord_index = len(coords[geometry_index]) + coord_index + return Point(coords[geometry_index][coord_index], properties=properties) + elif geometry["type"] == "MultiLineString": + if multi_feature_index < 0: + multi_feature_index = len(coords) + multi_feature_index + if coord_index < 0: + coord_index = len(coords[multi_feature_index]) + coord_index + return Point(coords[multi_feature_index][coord_index], properties=properties) + elif geometry["type"] == "MultiPolygon": + if multi_feature_index < 0: + multi_feature_index = len(coords) + multi_feature_index + if geometry_index < 0: + geometry_index = len(coords[multi_feature_index]) + geometry_index + if coord_index < 0: + coord_index = len(coords[multi_feature_index][geometry_index]) + coord_index + return Point( + coords[multi_feature_index][geometry_index][coord_index], + properties=properties, + ) -def feature_each(geojson, callback): - if geojson["type"] == "Feature": - callback(geojson, 0) - elif geojson["type"] == "FeatureCollection": - for i in range(0, len(geojson["features"])): - if not callback(geojson["features"][i], i): - break + raise ValueError("geojson is invalid") diff --git a/turfpy/misc.py b/turfpy/misc.py index 6e0dbe6..836d54b 100644 --- a/turfpy/misc.py +++ b/turfpy/misc.py @@ -86,7 +86,9 @@ def line_intersect( if not precise_matches.empty: for index, row in precise_matches.iterrows(): # intersect = intersects(mapping(row["geometry"]), segment) - intersection = Feature(geometry=mapping(row["geometry"].intersection(s))) + intersection = Feature( + geometry=mapping(row["geometry"].intersection(s)) + ) key = ",".join(map(str, get_coords(intersection))) if key not in unique: unique.add(key) @@ -96,7 +98,7 @@ def line_intersect( def line_segment( - geojson: Union[LineString, Polygon, MultiLineString, MultiPolygon, Feature] + geojson: Union[LineString, Polygon, MultiLineString, MultiPolygon, Feature], ) -> FeatureCollection: """ Creates a FeatureCollection of 2-vertex LineString segments from a @@ -186,7 +188,8 @@ def create_segments(coords, properties): def callback(current_coords, previous_coords): segment = Feature( - geometry=LineString([previous_coords, current_coords]), properties=properties + geometry=LineString([previous_coords, current_coords]), + properties=properties, ) segment.bbox = bbox(previous_coords, current_coords) segments.append(segment) @@ -364,7 +367,9 @@ def line_slice( ) clip_coords.append(get_coord(ends[1])) - return Feature(geometry=LineString(clip_coords), properties=line["properties"].copy()) + return Feature( + geometry=LineString(clip_coords), properties=line["properties"].copy() + ) def line_arc( @@ -435,9 +440,9 @@ def line_arc( if alfa > arc_end_degree: coordinates.append( - destination(center, radius, arc_end_degree, {"steps": steps, "units": units})[ - "geometry" - ]["coordinates"] + destination( + center, radius, arc_end_degree, {"steps": steps, "units": units} + )["geometry"]["coordinates"] ) return Feature(geometry=LineString(coordinates, properties=properties)) diff --git a/turfpy/random.py b/turfpy/random.py index 6ae2cc0..fc4028e 100644 --- a/turfpy/random.py +++ b/turfpy/random.py @@ -53,7 +53,9 @@ def coord_in_bbox(bbox: list): ] -def random_points(count: int = 1, bbox: Optional[list[Any]] = None) -> FeatureCollection: +def random_points( + count: int = 1, bbox: Optional[list[Any]] = None +) -> FeatureCollection: """ Generates geojson random points, if bbox provided then the generated points will be in the bbox. diff --git a/turfpy/transformation.py b/turfpy/transformation.py index 3860918..b29b8e0 100644 --- a/turfpy/transformation.py +++ b/turfpy/transformation.py @@ -250,7 +250,7 @@ def merge_dict(dicts: list): def union( - features: Union[List[Feature], FeatureCollection] + features: Union[List[Feature], FeatureCollection], ) -> Union[Feature, FeatureCollection]: """ Given list of features or ``FeatureCollection`` return union of those. @@ -394,7 +394,7 @@ def get_points(features): def get_ext_points(geom, points): - if geom.type == "Point": + if geom.geom_type == "Point": for p in geom.coords: points.append(Point(p)) elif geom.type == "MultiPoint": @@ -473,7 +473,8 @@ def convex(features: Union[Feature, FeatureCollection]): def dissolve( - features: Union[List[Feature], FeatureCollection], property_name: Optional[str] = None + features: Union[List[Feature], FeatureCollection], + property_name: Optional[str] = None, ) -> FeatureCollection: """ Take FeatureCollection or list of features to dissolve based on @@ -528,7 +529,9 @@ def dissolve( else: return union(features) if "properties" in features.keys(): - return FeatureCollection(dissolve_feature_list, properties=features["properties"]) + return FeatureCollection( + dissolve_feature_list, properties=features["properties"] + ) else: return FeatureCollection(dissolve_feature_list) @@ -630,7 +633,9 @@ def _callback_coord_each( initial_angle = rhumb_bearing(GeoPoint(pivot), GeoPoint(coord)) final_angle = initial_angle + angle distance = rhumb_distance(GeoPoint(pivot), GeoPoint(coord)) - new_coords = get_coord(rhumb_destination(GeoPoint(pivot), distance, final_angle)) + new_coords = get_coord( + rhumb_destination(GeoPoint(pivot), distance, final_angle) + ) coord[0] = new_coords[0] coord[1] = new_coords[1] @@ -769,7 +774,9 @@ def _callback_coord_each( original_distance = rhumb_distance(GeoPoint(origin), GeoPoint(coord)) bearing = rhumb_bearing(GeoPoint(origin), GeoPoint(coord)) new_distance = original_distance * factor - new_coord = get_coord(rhumb_destination(GeoPoint(origin), new_distance, bearing)) + new_coord = get_coord( + rhumb_destination(GeoPoint(origin), new_distance, bearing) + ) coord[0] = new_coord[0] coord[1] = new_coord[1] if len(coord) == 3: @@ -948,7 +955,9 @@ def line_offset_feature(line, distance, units): for index, current_coords in enumerate(coords): if index != len(coords) - 1: - segment = _process_segment(current_coords, coords[index + 1], offset_degrees) + segment = _process_segment( + current_coords, coords[index + 1], offset_degrees + ) segments.append(segment) if index > 0: seg2_coords = segments[index - 1] @@ -1079,7 +1088,9 @@ def voronoi( ] convex_hull = MultiPoint([Point(i) for i in points]).convex_hull.buffer(2) - result = MultiPolygon([poly.intersection(convex_hull) for poly in polygonize(lines)]) + result = MultiPolygon( + [poly.intersection(convex_hull) for poly in polygonize(lines)] + ) result = MultiPolygon( [p for p in result.geoms] + [p for p in convex_hull.difference(unary_union(result)).geoms] From 4035225469302062dd324f16ef58c7c1e4f4e797 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 13 Dec 2024 07:43:23 -0600 Subject: [PATCH 2/7] feat: further updates towards adding boolean_within --- docs/source/index.rst | 6 + docs/source/joins/points_within_polygon.rst | 18 ++ docs/source/measurements/point_in_polygon.rst | 56 ----- examples/boolean.ipynb | 34 +++ examples/joins.ipynb | 57 +++++ examples/measurements.ipynb | 22 -- measurements.md | 22 -- tests/test_boolean.py | 92 ++++++- tests/test_feature_conversion.py | 2 +- .../in/multipoly-with-hole.geojson | 38 +++ .../in/poly-with-hole.geojson | 27 +++ tests/test_helper.py | 4 +- tests/test_joins.py | 65 +++++ tests/test_measurement.py | 103 +------- tests/test_random.py | 11 +- tests/test_transformation.py | 8 +- turfpy/boolean.py | 226 ++++++++++++++++-- turfpy/dev_lib/earcut.py | 4 +- turfpy/dev_lib/spline.py | 6 +- turfpy/joins.py | 93 +++++++ turfpy/measurement.py | 204 ++-------------- turfpy/meta.py | 49 ++-- turfpy/misc.py | 14 +- turfpy/random.py | 4 +- turfpy/transformation.py | 20 +- 25 files changed, 691 insertions(+), 494 deletions(-) create mode 100644 docs/source/joins/points_within_polygon.rst delete mode 100644 docs/source/measurements/point_in_polygon.rst create mode 100644 examples/joins.ipynb create mode 100644 tests/test_files/boolean_point_in_polygon_test/in/multipoly-with-hole.geojson create mode 100644 tests/test_files/boolean_point_in_polygon_test/in/poly-with-hole.geojson create mode 100644 tests/test_joins.py create mode 100644 turfpy/joins.py diff --git a/docs/source/index.rst b/docs/source/index.rst index ca988fe..be2af1b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -70,6 +70,12 @@ A Python library for performing geospatial data analysis which reimplements `tur Boolean Disjoint Boolean Intersects + .. toctree:: + :maxdepth: 1 + :caption: Joins + + Points Within Polygon + .. toctree:: :maxdepth: 1 :caption: Feature Conversion diff --git a/docs/source/joins/points_within_polygon.rst b/docs/source/joins/points_within_polygon.rst new file mode 100644 index 0000000..d1e6469 --- /dev/null +++ b/docs/source/joins/points_within_polygon.rst @@ -0,0 +1,18 @@ +Points Within Polygon +===================== +Finds Points or MultiPoint coordinate positions that fall within (Multi)Polygon(s). + +Example +------- + +.. jupyter-execute:: + + from turfpy.joins import points_within_polygon + from geojson import Point, MultiPolygon, Feature + + point = Feature(geometry=Point([-77, 44])) + polygon = Feature(geometry=MultiPolygon([([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), + ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],)])) + + points_within_polygon(point, polygon) + diff --git a/docs/source/measurements/point_in_polygon.rst b/docs/source/measurements/point_in_polygon.rst deleted file mode 100644 index 79ca57f..0000000 --- a/docs/source/measurements/point_in_polygon.rst +++ /dev/null @@ -1,56 +0,0 @@ -Point In Polygon -================ -Takes a Point or a Point Feature and Polygon or Polygon Feature as input and returns True if Point is in given Feature. - - -Example -------- - -.. jupyter-execute:: - - from turfpy.measurement import boolean_point_in_polygon - from geojson import Point, MultiPolygon, Feature - point = Feature(geometry=Point([-77, 44])) - polygon = Feature(geometry=MultiPolygon([([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), - ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],)])) - boolean_point_in_polygon(point, polygon) - - - - -Interactive Example -------------------- - -.. jupyter-execute:: - - from turfpy.measurement import boolean_point_in_polygon - from geojson import Point, MultiPolygon, Feature - from ipyleaflet import Map, GeoJSON, LayersControl - - point = Feature(geometry=Point([-77, 44])) - polygon = Feature( - geometry=MultiPolygon( - [ - ([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), - ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],), - ] - ) - ) - boolean_point_in_polygon(point, polygon) - - m = Map(center=[46.57868671298067, -40.91583251953126], zoom=2) - - geo_json = GeoJSON(name="MultiPolygon Feature", data=polygon) - - m.add_layer(geo_json) - - point_geojson = GeoJSON(name="Point in Polygon", data=point) - - m.add_layer(point_geojson) - - control = LayersControl(position="topright") - m.add_control(control) - - m - - diff --git a/examples/boolean.ipynb b/examples/boolean.ipynb index 9da8fee..a145dd0 100644 --- a/examples/boolean.ipynb +++ b/examples/boolean.ipynb @@ -51,6 +51,40 @@ "feature_2 = Feature(geometry=Point((29.0760, 72.8777)))\n", "boolean_intersects(feature_1, feature_2)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Boolean Within\n", + "Boolean-within returns true if the first geometry is completely within the second geometry. The interiors of both geometries must intersect and, the interior and boundary of the primary (geometry a) must not intersect the exterior of the secondary (geometry b)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from geojson import Feature, Point, Polygon\n", + "from turfpy.boolean import boolean_within\n", + "\n", + "poly = Polygon(\n", + " [\n", + " [\n", + " (1, 1),\n", + " (1, 10),\n", + " (10, 10),\n", + " (10, 1),\n", + " (1, 1)\n", + " ]\n", + " ]\n", + ")\n", + "\n", + "feature_1 = Feature(geometry=Point((4, 4)))\n", + "feature_2 = Feature(geometry=poly)\n", + "boolean_within(feature_1, feature_2)" + ] } ], "metadata": { diff --git a/examples/joins.ipynb b/examples/joins.ipynb new file mode 100644 index 0000000..5dbdf26 --- /dev/null +++ b/examples/joins.ipynb @@ -0,0 +1,57 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Joins\n", + "This notebook demonstrates all the examples of joins" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Points within Polygon\n", + "Finds Points or MultiPoint coordinate positions that fall within (Multi)Polygon(s)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from turfpy.joins import points_within_polygon\n", + "from geojson import Point, MultiPolygon, Feature\n", + "\n", + "point = Feature(geometry=Point([-77, 44]))\n", + "polygon = Feature(geometry=MultiPolygon([([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],),\n", + "([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],)]))\n", + "\n", + "points_within_polygon(point, polygon)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/measurements.ipynb b/examples/measurements.ipynb index f1f9663..a624e18 100644 --- a/examples/measurements.ipynb +++ b/examples/measurements.ipynb @@ -316,28 +316,6 @@ "point_on_feature(feature)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Point In Polygon\n", - "Takes a Point or a Point Feature and Polygon or Polygon Feature as input and returns True if Point is in given Feature." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from turfpy.measurement import boolean_point_in_polygon\n", - "from geojson import Point, MultiPolygon, Feature\n", - "point = Feature(geometry=Point([-77, 44]))\n", - "polygon = Feature(geometry=MultiPolygon([([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],),\n", - "([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],)]))\n", - "boolean_point_in_polygon(point, polygon)" - ] - }, { "cell_type": "markdown", "metadata": {}, diff --git a/measurements.md b/measurements.md index 64b4535..eb5bb3c 100644 --- a/measurements.md +++ b/measurements.md @@ -281,28 +281,6 @@ feature = Feature(geometry=point) point_on_feature(feature) ``` -* Point In Polygon : Takes a Point or a Point Feature and Polygon or Polygon Feature as input and returns - True if Point is in given Feature. - -| Argument | Type | Description | -| ------- | ------ | ----------- | -| `point` | Feature | Point or Point Feature | -| `polygon` | Polygon | Polygon or Polygon Feature | -| `ignore_boundary` | boolean(Optional) | Default value is False, specify whether to exclude boundary of the given polygon or not | - -| Return | Type | Description | -| ------- | ------ | ----------- | -| `result` | boolean | True if the given Point is in Polygons else False | - -```python -from turfpy.measurement import boolean_point_in_polygon -from geojson import Point, MultiPolygon, Feature -point = Feature(geometry=Point([-77, 44])) -polygon = Feature(geometry=MultiPolygon([([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), -([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],)])) -boolean_point_in_polygon(point, polygon) -``` - * Tangent To Polygon : Finds the tangents of a (Multi)Polygon from a Point. | Argument | Type | Description | diff --git a/tests/test_boolean.py b/tests/test_boolean.py index 1601c17..50af399 100644 --- a/tests/test_boolean.py +++ b/tests/test_boolean.py @@ -7,7 +7,14 @@ import os import unittest -from turfpy.boolean import boolean_disjoint, boolean_intersects, boolean_within +from geojson import Feature, Point, Polygon + +from turfpy.boolean import ( + boolean_disjoint, + boolean_intersects, + boolean_point_in_polygon, + boolean_within, +) def load_json_file_sync(filepath): @@ -136,4 +143,85 @@ def test_false_fixtures(self): feature1 = geojson["features"][0] feature2 = geojson["features"][1] result = boolean_within(feature1, feature2) - self.assertFalse(result, False) \ No newline at end of file + self.assertFalse(result, False) + + +class TestTurfBooleanPointInPolygonFeatureCollection(unittest.TestCase): + def test_simple_polygon(self): + poly = Polygon([[[0, 0], [0, 100], [100, 100], [100, 0], [0, 0]]]) + pt_in = Point([50, 50]) + pt_out = Point([140, 150]) + + self.assertTrue( + boolean_point_in_polygon(Feature(geometry=pt_in), Feature(geometry=poly)), + "point inside simple polygon", + ) + self.assertFalse( + boolean_point_in_polygon(Feature(geometry=pt_out), Feature(geometry=poly)), + "point outside simple polygon", + ) + + def test_concave_polygon(self): + concave_poly = Polygon( + [[[0, 0], [50, 50], [0, 100], [100, 100], [100, 0], [0, 0]]] + ) + pt_concave_in = Point([75, 75]) + pt_concave_out = Point([25, 50]) + + self.assertTrue( + boolean_point_in_polygon( + Feature(geometry=pt_concave_in), Feature(geometry=concave_poly) + ), + "point inside concave polygon", + ) + self.assertFalse( + boolean_point_in_polygon( + Feature(geometry=pt_concave_out), Feature(geometry=concave_poly) + ), + "point outside concave polygon", + ) + + def test_poly_with_hole(self): + pt_in_hole = Point([-86.69208526611328, 36.20373274711739]) + pt_in_poly = Point([-86.72229766845702, 36.20258997094334]) + pt_outside_poly = Point([-86.75079345703125, 36.18527313913089]) + + with open( + f"{os.path.dirname(os.path.abspath(__file__))}/test_files/boolean_point_in_polygon_test/in/poly-with-hole.geojson" + ) as f: + poly_hole = json.load(f) + + self.assertFalse( + boolean_point_in_polygon(Feature(geometry=pt_in_hole), poly_hole) + ) + self.assertTrue(boolean_point_in_polygon(Feature(geometry=pt_in_poly), poly_hole)) + self.assertFalse( + boolean_point_in_polygon(Feature(geometry=pt_outside_poly), poly_hole) + ) + + def test_multipolygon_with_hole(self): + pt_in_hole = Point([-86.69208526611328, 36.20373274711739]) + pt_in_poly = Point([-86.72229766845702, 36.20258997094334]) + pt_in_poly2 = Point([-86.75079345703125, 36.18527313913089]) + pt_outside_poly = Point([-86.75302505493164, 36.23015046460186]) + + with open( + f"{os.path.dirname(os.path.abspath(__file__))}/test_files/boolean_point_in_polygon_test/in/multipoly-with-hole.geojson" + ) as f: + multi_poly_hole = json.load(f) + + self.assertFalse( + boolean_point_in_polygon(Feature(geometry=pt_in_hole), multi_poly_hole) + ) + self.assertTrue( + boolean_point_in_polygon(Feature(geometry=pt_in_poly), multi_poly_hole) + ) + self.assertTrue( + boolean_point_in_polygon(Feature(geometry=pt_in_poly2), multi_poly_hole) + ) + self.assertTrue( + boolean_point_in_polygon(Feature(geometry=pt_in_poly), multi_poly_hole) + ) + self.assertFalse( + boolean_point_in_polygon(Feature(geometry=pt_outside_poly), multi_poly_hole) + ) diff --git a/tests/test_feature_conversion.py b/tests/test_feature_conversion.py index 835fd06..ec38ecb 100644 --- a/tests/test_feature_conversion.py +++ b/tests/test_feature_conversion.py @@ -46,4 +46,4 @@ def test_polygon_to_linestring(self): expected_results = json.load(file) # Assert the results are as expected - self.assertEqual(results, expected_results, name) \ No newline at end of file + self.assertEqual(results, expected_results, name) diff --git a/tests/test_files/boolean_point_in_polygon_test/in/multipoly-with-hole.geojson b/tests/test_files/boolean_point_in_polygon_test/in/multipoly-with-hole.geojson new file mode 100644 index 0000000..505f460 --- /dev/null +++ b/tests/test_files/boolean_point_in_polygon_test/in/multipoly-with-hole.geojson @@ -0,0 +1,38 @@ +{ + "type": "Feature", + "properties": {}, + "bbox": [-86.77362442016602, 36.170862616662134, -86.67303085327148, 36.23084281427824], + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [-86.76624298095703, 36.171278341935434], + [-86.77362442016602, 36.2014818084173], + [-86.74100875854492, 36.19607929145354], + [-86.74238204956055, 36.170862616662134], + [-86.76624298095703, 36.171278341935434] + ] + ], + [ + [ + [-86.70478820800781, 36.23084281427824], + [-86.73980712890625, 36.21062368007896], + [-86.71371459960938, 36.173495506147], + [-86.67526245117186, 36.17709826419592], + [-86.67303085327148, 36.20910010895552], + [-86.68041229248047, 36.230427405208005], + [-86.70478820800781, 36.23084281427824] + ], + [ + [-86.6934585571289, 36.217271643303604], + [-86.71268463134766, 36.20771501855801], + [-86.70238494873047, 36.19067640168397], + [-86.68487548828125, 36.19691047217554], + [-86.68264389038086, 36.20993115142727], + [-86.6934585571289, 36.217271643303604] + ] + ] + ] + } +} diff --git a/tests/test_files/boolean_point_in_polygon_test/in/poly-with-hole.geojson b/tests/test_files/boolean_point_in_polygon_test/in/poly-with-hole.geojson new file mode 100644 index 0000000..87ad41c --- /dev/null +++ b/tests/test_files/boolean_point_in_polygon_test/in/poly-with-hole.geojson @@ -0,0 +1,27 @@ +{ + "type": "Feature", + "properties": {}, + "bbox": [-86.73980712890625, 36.173495506147, -86.67303085327148, 36.23084281427824], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-86.70478820800781, 36.23084281427824], + [-86.73980712890625, 36.21062368007896], + [-86.71371459960938, 36.173495506147], + [-86.67526245117186, 36.17709826419592], + [-86.67303085327148, 36.20910010895552], + [-86.68041229248047, 36.230427405208005], + [-86.70478820800781, 36.23084281427824] + ], + [ + [-86.6934585571289, 36.217271643303604], + [-86.71268463134766, 36.20771501855801], + [-86.70238494873047, 36.19067640168397], + [-86.68487548828125, 36.19691047217554], + [-86.68264389038086, 36.20993115142727], + [-86.6934585571289, 36.217271643303604] + ] + ] + } +} diff --git a/tests/test_helper.py b/tests/test_helper.py index 0396f31..3a86d11 100644 --- a/tests/test_helper.py +++ b/tests/test_helper.py @@ -42,8 +42,6 @@ def __init__(self, node: str, point: Point): def test_get_coords(): """Test get_coords function""" - poly = Polygon( - [[(2.38, 57.322), (23.194, -20.28), (-120.43, 19.15), (2.38, 57.322)]] - ) + poly = Polygon([[(2.38, 57.322), (23.194, -20.28), (-120.43, 19.15), (2.38, 57.322)]]) coords = get_coords(poly) assert coords == poly.coordinates diff --git a/tests/test_joins.py b/tests/test_joins.py new file mode 100644 index 0000000..c56fbda --- /dev/null +++ b/tests/test_joins.py @@ -0,0 +1,65 @@ +from geojson import Feature, FeatureCollection, MultiPolygon, Point, Polygon + +from turfpy.joins import points_within_polygon + + +def test_points_within_polygon(): + f1 = Feature(geometry=Point((-46.6318, -23.5523))) + f2 = Feature(geometry=Point((-46.6246, -23.5325))) + f3 = Feature(geometry=Point((-46.6062, -23.5513))) + f4 = Feature(geometry=Point((-46.663, -23.554))) + f5 = Feature(geometry=Point((-46.643, -23.557))) + f6 = Feature(geometry=Point((-73, 45))) + f7 = Feature(geometry=Point((36, 71))) + points = FeatureCollection([f1, f2, f3, f4, f5, f6, f7]) + poly = Polygon( + [ + [ + (-46.653, -23.543), + (-46.634, -23.5346), + (-46.613, -23.543), + (-46.614, -23.559), + (-46.631, -23.567), + (-46.653, -23.560), + (-46.653, -23.543), + ] + ] + ) + fpoly = Feature(geometry=poly) + poly2 = Polygon( + [ + [ + (-76.653, -11.543), + (-46.634, -23.5346), + (-46.613, -23.543), + (-46.614, -23.559), + (-46.631, -23.567), + (-46.653, -23.560), + (-76.653, -11.543), + ] + ] + ) + fpoly2 = Feature(geometry=poly2) + fc = FeatureCollection([fpoly, fpoly2]) + result = points_within_polygon(points, fc) + assert len(result["features"]) == 3 + + multi_polygon = Feature( + geometry=MultiPolygon( + [ + ([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), + ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],), + ] + ) + ) + result2 = points_within_polygon(f6, multi_polygon) + assert result2 == { + "features": [ + { + "geometry": {"coordinates": [-73, 45], "type": "Point"}, + "properties": {}, + "type": "Feature", + } + ], + "type": "FeatureCollection", + } diff --git a/tests/test_measurement.py b/tests/test_measurement.py index 878c7e9..ab8d03f 100644 --- a/tests/test_measurement.py +++ b/tests/test_measurement.py @@ -1,3 +1,5 @@ +import json + from geojson import ( Feature, FeatureCollection, @@ -14,7 +16,6 @@ along, bbox, bbox_polygon, - boolean_point_in_polygon, center, destination, envelope, @@ -22,13 +23,11 @@ midpoint, nearest_point, point_to_line_distance, - points_within_polygon, rhumb_bearing, rhumb_destination, rhumb_distance, square, ) -import json def test_bbox_point(): @@ -175,9 +174,7 @@ def test_envelope(): def test_rhumb_destination(): - start = Feature( - geometry=Point((-75.343, 39.984)), properties={"marker-color": "F00"} - ) + start = Feature(geometry=Point((-75.343, 39.984)), properties={"marker-color": "F00"}) distance = 50 bearing = 90 dest = rhumb_destination( @@ -256,19 +253,6 @@ def test_destination(): assert c1 == 39.9802 -def test_boolean_point_in_polygon(): - point = Point((-77, 44)) - polygon = MultiPolygon( - [ - ([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), - ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],), - ] - ) - - bpp = boolean_point_in_polygon(point, polygon) - assert bpp is True - - def test_point_to_line_distance(): point = Feature(geometry=Point((0, 0))) linestring = Feature(geometry=LineString([(1, 1), (-1, 1)])) @@ -306,84 +290,3 @@ def test_rhumb_bearing(): # c0, c1 = cen["geometry"]["coordinates"] # # assert c0 == 82 # assert c1 == 36 - - -def test_points_within_polygon(): - f1 = Feature(geometry=Point((-46.6318, -23.5523))) - f2 = Feature(geometry=Point((-46.6246, -23.5325))) - f3 = Feature(geometry=Point((-46.6062, -23.5513))) - f4 = Feature(geometry=Point((-46.663, -23.554))) - f5 = Feature(geometry=Point((-46.643, -23.557))) - f6 = Feature(geometry=Point((-73, 45))) - f7 = Feature(geometry=Point((36, 71))) - points = FeatureCollection([f1, f2, f3, f4, f5, f6, f7]) - poly = Polygon( - [ - [ - (-46.653, -23.543), - (-46.634, -23.5346), - (-46.613, -23.543), - (-46.614, -23.559), - (-46.631, -23.567), - (-46.653, -23.560), - (-46.653, -23.543), - ] - ] - ) - fpoly = Feature(geometry=poly) - poly2 = Polygon( - [ - [ - (-76.653, -11.543), - (-46.634, -23.5346), - (-46.613, -23.543), - (-46.614, -23.559), - (-46.631, -23.567), - (-46.653, -23.560), - (-76.653, -11.543), - ] - ] - ) - fpoly2 = Feature(geometry=poly2) - fc = FeatureCollection([fpoly, fpoly2]) - result = points_within_polygon(points, fc) - assert result == { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-46.6318, -23.5523]}, - "properties": {}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-46.663, -23.554]}, - "properties": {}, - }, - { - "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-46.643, -23.557]}, - "properties": {}, - }, - ], - } - - multi_polygon = Feature( - geometry=MultiPolygon( - [ - ([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), - ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],), - ] - ) - ) - result2 = points_within_polygon(f6, multi_polygon) - assert result2 == { - "features": [ - { - "geometry": {"coordinates": [-73, 45], "type": "Point"}, - "properties": {}, - "type": "Feature", - } - ], - "type": "FeatureCollection", - } \ No newline at end of file diff --git a/tests/test_random.py b/tests/test_random.py index 8355465..05558c5 100644 --- a/tests/test_random.py +++ b/tests/test_random.py @@ -4,7 +4,8 @@ from geojson import Feature, Point -from turfpy.measurement import bbox, boolean_point_in_polygon +from turfpy.boolean import boolean_point_in_polygon +from turfpy.measurement import bbox from turfpy.random import random_points, random_position @@ -28,8 +29,8 @@ def test_random_position(): pos = random_position(bbox=bbox(data)) assert len(pos) == 2 - pos = Point(pos) - assert boolean_point_in_polygon(point=pos, polygon=data["geometry"]) + pos = Feature(geometry=Point(pos)) + assert boolean_point_in_polygon(point=pos, polygon=data) def test_random_points(): @@ -53,6 +54,4 @@ def test_random_points(): pos = random_points(count=3, bbox=bbox(data)) assert len(pos["features"]) == 3 for point in pos["features"]: - assert boolean_point_in_polygon( - point=point["geometry"], polygon=data["geometry"] - ) \ No newline at end of file + assert boolean_point_in_polygon(point=point, polygon=data) diff --git a/tests/test_transformation.py b/tests/test_transformation.py index a491cc5..811cd7f 100644 --- a/tests/test_transformation.py +++ b/tests/test_transformation.py @@ -287,16 +287,12 @@ def test_dissolve(): def test_difference(): f1 = Feature( - geometry=Polygon( - [[[128, -26], [141, -26], [141, -21], [128, -21], [128, -26]]] - ), + geometry=Polygon([[[128, -26], [141, -26], [141, -21], [128, -21], [128, -26]]]), properties={"combine": "yes", "fill": "#00f"}, ) f2 = Feature( - geometry=Polygon( - [[[126, -28], [140, -28], [140, -20], [126, -20], [126, -28]]] - ), + geometry=Polygon([[[126, -28], [140, -28], [140, -20], [126, -20], [126, -28]]]), properties={"combine": "yes"}, ) diff --git a/turfpy/boolean.py b/turfpy/boolean.py index 669569f..725e55d 100644 --- a/turfpy/boolean.py +++ b/turfpy/boolean.py @@ -17,7 +17,6 @@ from shapely.geometry import shape from turfpy.feature_conversion import polygon_to_line -from turfpy.measurement import boolean_point_in_polygon from turfpy.meta import flatten_each from turfpy.misc import line_intersect @@ -75,7 +74,9 @@ def __is_line_in_poly(polygon: Polygon, line_string: LineString) -> bool: :rtype: bool """ for coord in line_string["coordinates"]: - if boolean_point_in_polygon(Point(coordinates=coord), polygon): + if boolean_point_in_polygon( + Feature(geometry=Point(coordinates=coord)), Feature(geometry=polygon) + ): return True do_lines_intersect = line_intersect(line_string, polygon_to_line(polygon)) @@ -98,11 +99,15 @@ def __is_poly_in_poly(feature1: Polygon, feature2: Polygon) -> bool: :rtype: bool """ for coord1 in feature1["coordinates"][0]: - if boolean_point_in_polygon(Point(coordinates=coord1), feature2): + if boolean_point_in_polygon( + Feature(geometry=Point(coordinates=coord1)), Feature(geometry=feature2) + ): return True for coord2 in feature2["coordinates"][0]: - if boolean_point_in_polygon(Point(coordinates=coord2), feature1): + if boolean_point_in_polygon( + Feature(geometry=Point(coordinates=coord2)), Feature(geometry=feature1) + ): return True do_lines_intersect = line_intersect( @@ -156,9 +161,7 @@ def __disjoint(feature_1: Feature, feature_2: Feature) -> bool: elif geom2_type == "LineString": return not __is_point_on_line(feature_2["geometry"], feature_1["geometry"]) elif geom2_type == "Polygon": - return not boolean_point_in_polygon( - feature_1["geometry"], feature_2["geometry"] - ) + return not boolean_point_in_polygon(feature_1, feature_2) elif geom1_type == "LineString": if geom2_type == "Point": @@ -170,9 +173,7 @@ def __disjoint(feature_1: Feature, feature_2: Feature) -> bool: elif geom1_type == "Polygon": if geom2_type == "Point": - return not boolean_point_in_polygon( - feature_2["geometry"], feature_1["geometry"] - ) + return not boolean_point_in_polygon(feature_2, feature_1) elif geom2_type == "LineString": return not __is_line_in_poly(feature_1["geometry"], feature_2["geometry"]) elif geom2_type == "Polygon": @@ -245,9 +246,7 @@ def inner_check(flatten2, index2, feature_collection2): nonlocal bool_result if bool_result: return True - bool_result = not boolean_disjoint( - flatten1["geometry"], flatten2["geometry"] - ) + bool_result = not boolean_disjoint(flatten1["geometry"], flatten2["geometry"]) flatten_each(feature_2, inner_check) @@ -256,15 +255,33 @@ def inner_check(flatten2, index2, feature_collection2): return bool_result -def __is_point_in_multipoint( - point: ShapelyPoint, multipoint: ShapelyMultiPoint -) -> bool: +def __is_point_in_multipoint(point: ShapelyPoint, multipoint: ShapelyMultiPoint) -> bool: + """ + Determine if a point is in a multipoint. + + :param point: Shapely Point + :type point: Point + :param multipoint: Shapely MultiPoint + :type multipoint: MultiPoint + :returns: True if the point is in the multipoint + :rtype: bool + """ return any(point.equals(ShapelyPoint(coord)) for coord in multipoint.geoms) def __is_multipoint_in_multipoint( multipoint1: ShapelyMultiPoint, multipoint2: ShapelyMultiPoint ) -> bool: + """ + Determine if a multipoint is in another multipoint + + :param multipoint1: Shapely MultiPoint + :type multipoint1: MultiPoint + :param multipoint2: Shapely MultiPoint + :type multipoint2: MultiPoint + :returns: True if the multipoint is in the other multipoint + :rtype: bool + """ return all( any( ShapelyPoint(coord1).equals(ShapelyPoint(coord2)) @@ -277,6 +294,16 @@ def __is_multipoint_in_multipoint( def __is_multipoint_on_line( multipoint: ShapelyMultiPoint, line: ShapelyLineString ) -> bool: + """ + Determine if a multipoint is on a line + + :param multipoint: Shapely MultiPoint + :type multipoint: MultiPoint + :param line: Shapely LineString + :type line: LineString + :returns: True if the multipoint is on the line + :rtype: bool + """ found_inside_point = False for point in multipoint.geoms: @@ -290,9 +317,17 @@ def __is_multipoint_on_line( return found_inside_point -def __is_multipoint_in_poly( - multipoint: ShapelyMultiPoint, poly: ShapelyPolygon -) -> bool: +def __is_multipoint_in_poly(multipoint: ShapelyMultiPoint, poly: ShapelyPolygon) -> bool: + """ + Determine if a multipoint is in a polygon + + :param multipoint: Shapely MultiPoint + :type multipoint: MultiPoint + :param poly: Shapely Polygon + :type poly: Polygon + :returns: True if the multipoint is in the polygon + :rtype: bool + """ output = True one_inside = False is_inside = False @@ -300,15 +335,17 @@ def __is_multipoint_in_poly( for coord in multipoint.geoms: polygon_type = poly.geom_type if polygon_type == "Polygon": - geojson_poly = Polygon( - coordinates=json.loads(to_geojson(poly))["coordinates"] + geojson_poly = Feature( + geometry=Polygon(coordinates=json.loads(to_geojson(poly))["coordinates"]) ) elif polygon_type == "MultiPolygon": - geojson_poly = MultiPolygon( - coordinates=json.loads(to_geojson(poly))["coordinates"] + geojson_poly = Feature( + geometry=MultiPolygon( + coordinates=json.loads(to_geojson(poly))["coordinates"] + ) ) is_inside = boolean_point_in_polygon( - json.loads(to_geojson(coord)), geojson_poly + Feature(geometry=json.loads(to_geojson(coord))), geojson_poly ) if not is_inside: @@ -316,7 +353,7 @@ def __is_multipoint_in_poly( break if not one_inside: is_inside = boolean_point_in_polygon( - json.loads(to_geojson(coord)), + Feature(geometry=json.loads(to_geojson(coord))), geojson_poly, ignore_boundary=True, ) @@ -329,6 +366,22 @@ def boolean_point_on_line( ignore_end_vertices: bool = False, epsilon: Optional[float] = None, ) -> bool: + """ + Determine if a point is on a line + + :param pt: GeoJSON Point + :type pt: Point + :param line: GeoJSON LineString + :type line: LineString + :param ignore_end_vertices: Whether to ignore the end vertices + :type ignore_end_vertices: bool + :param epsilon: Epsilon value for floating point comparison + :type epsilon: Optional[float] + :returns: True if the point is on the line + :rtype: bool + """ + pt = shape(pt) + line pt_coords = pt.coords[0] line_coords = list(line.coords) @@ -367,6 +420,24 @@ def __is_point_on_line_segment( exclude_boundary_type: Optional[str], epsilon: Optional[float], ) -> bool: + """ + Determine if a point is on a line segment + + :param line_segment_start: Start of the line segment + :type line_segment_start: Tuple[float, float] + :param line_segment_end: End of the line segment + :type line_segment_end: Tuple[float, float] + :param pt: Point to check + :type pt: Tuple[float, float] + :param exclude_boundary: Whether to exclude the boundary + :type exclude_boundary: Optional[bool] + :param exclude_boundary_type: Type of boundary to exclude + :type exclude_boundary_type: Optional[str] + :param epsilon: Epsilon value for floating point comparison + :type epsilon: Optional[float] + :returns: True if the point is on the line segment + :rtype: bool + """ x, y = pt x1, y1 = line_segment_start x2, y2 = line_segment_end @@ -402,6 +473,111 @@ def __is_point_on_line_segment( return False +def __in_ring(pt: list, ring: list, ignore_boundary: bool) -> bool: + """ + Determine if a point is in a ring. + + :param pt: Point to check + :type pt: list + :param ring: Ring + :type ring: list + :param ignore_boundary: Whether to ignore the boundary + :type ignore_boundary: bool + :returns: True if the point is in the ring + :rtype: bool + """ + is_inside = False + if ring[0][0] == ring[len(ring) - 1][0] and ring[0][1] == ring[len(ring) - 1][1]: + ring = ring[0 : len(ring) - 1] + j = len(ring) - 1 + for i in range(0, len(ring)): + xi = ring[i][0] + yi = ring[i][1] + xj = ring[j][0] + yj = ring[j][1] + on_boundary = ( + (pt[1] * (xi - xj) + yi * (xj - pt[0]) + yj * (pt[0] - xi) == 0) + and ((xi - pt[0]) * (xj - pt[0]) <= 0) + and ((yi - pt[1]) * (yj - pt[1]) <= 0) + ) + if on_boundary: + return not ignore_boundary + intersect = ((yi > pt[1]) != (yj > pt[1])) and ( + pt[0] < (xj - xi) * (pt[1] - yi) / (yj - yi) + xi + ) + if intersect: + is_inside = not is_inside + j = i + return is_inside + + +def __in_bbox(pt: list, bbox: list) -> bool: + """ + Determine if a point is in a bounding box. + + :param pt: Point to check + :type pt: list + :param bbox: Bounding box + :type bbox: list + :returns: True if the point is in the bounding box + :rtype: bool + """ + return bbox[0] <= pt[0] <= bbox[2] and bbox[1] <= pt[1] <= bbox[3] + + +def boolean_point_in_polygon(point: Feature, polygon: Feature, ignore_boundary=False): + """ + Boolean Point In Polygon takes a Point and a Polygon or MultiPolygon + and determines if the point resides inside the polygon. + The polygon can be convex or concave. The function accounts for holes. + + :param point: GeoJSON Point + :type point: Point + :param polygon: GeoJSON Polygon or MultiPolygon + :type polygon: Polygon + :param ignore_boundary: Whether to ignore the boundary + :type ignore_boundary: bool + :returns: True if the point resides inside the polygon + :rtype: bool + """ + # Check if point feature is a point + if point["type"] != "Feature" and point["geometry"]["type"] != "Point": + raise Exception("point is required") + # Check if polygon feature is a polygon + if polygon["type"] != "Feature" and ( + polygon["geometry"]["type"] != "Polygon" + and polygon["geometry"]["type"] != "MultiPolygon" + ): + raise Exception("polygon is required") + + pt = point["geometry"]["coordinates"] + geom = polygon + geo_type = geom["geometry"]["type"] + bbox = polygon.get("bbox", None) + polys = geom["geometry"]["coordinates"] + + if bbox and not __in_bbox(pt, bbox): + return False + + if geo_type == "Polygon": + polys = [polys] + + inside_poly = False + + for i in range(0, len(polys)): + if __in_ring(pt, polys[i][0], ignore_boundary): + in_hole = False + k = 1 + while k < len(polys[i]) and not in_hole: + if __in_ring(pt, polys[i][k], not ignore_boundary): + in_hole = True + k += 1 + if not in_hole: + inside_poly = True + + return inside_poly + + def boolean_within(feature_1: Feature, feature_2: Feature) -> bool: """ Boolean-within returns (TRUE) if the first geometry is diff --git a/turfpy/dev_lib/earcut.py b/turfpy/dev_lib/earcut.py index 9727dba..b1cad1f 100644 --- a/turfpy/dev_lib/earcut.py +++ b/turfpy/dev_lib/earcut.py @@ -395,9 +395,7 @@ def __find_hole_bridge(hole, outer_node): ): tan = abs(hy - p.y) / (hx - p.x) # tangential - if (tan < tanMin or (tan == tanMin and p.x > m.x)) and locallyInside( - p, hole - ): + if (tan < tanMin or (tan == tanMin and p.x > m.x)) and locallyInside(p, hole): m = p tanMin = tan diff --git a/turfpy/dev_lib/spline.py b/turfpy/dev_lib/spline.py index bdff9b9..8939be7 100644 --- a/turfpy/dev_lib/spline.py +++ b/turfpy/dev_lib/spline.py @@ -56,9 +56,11 @@ def __init__(self, points_data=[], resolution=10000, sharpness=0.85): }, # noqa: E501 { "x": (1.0 - self.sharpness) * self.points[i + 1]["x"] - + self.sharpness * (self.centers[i + 1]["x"] + dx), # noqa: E501 # noqa: E126 + + self.sharpness + * (self.centers[i + 1]["x"] + dx), # noqa: E501 # noqa: E126 "y": (1.0 - self.sharpness) * self.points[i + 1]["y"] - + self.sharpness * (self.centers[i + 1]["y"] + dy), # noqa: E501 # noqa: E126 + + self.sharpness + * (self.centers[i + 1]["y"] + dy), # noqa: E501 # noqa: E126 "z": (1.0 - self.sharpness) * self.points[i + 1]["z"] + self.sharpness * (self.centers[i + 1]["z"] + dz), # noqa: E501 }, diff --git a/turfpy/joins.py b/turfpy/joins.py new file mode 100644 index 0000000..d7613a2 --- /dev/null +++ b/turfpy/joins.py @@ -0,0 +1,93 @@ +""" +This module implements some of the spatial analysis techniques and processes used to +understand the patterns and relationships of geographic features. +This is mainly inspired by turf.js. +link: http://turfjs.org/ +""" + +import concurrent.futures +from functools import partial +from multiprocessing import Manager +from multiprocessing.managers import ListProxy +from typing import Union + +from geojson import Feature, FeatureCollection, Point, Polygon + +from turfpy.boolean import boolean_point_in_polygon +from turfpy.meta import geom_each + + +def points_within_polygon( + points: Union[Feature, FeatureCollection], + polygons: Union[Feature, FeatureCollection], + chunk_size: int = 1, +) -> FeatureCollection: + """Find Point(s) that fall within (Multi)Polygon(s). + + This function takes two inputs GeoJSON Feature :class:`geojson.Point` or + :class:`geojson.FeatureCollection` of Points and GeoJSON Feature + :class:`geojson.Polygon` or Feature :class:`geojson.MultiPolygon` or + FeatureCollection of :class:`geojson.Polygon` or Feature + :class:`geojson.MultiPolygon`. and returns all points with in in those + Polygon(s) or (Multi)Polygon(s). + + :param points: A single GeoJSON ``Point`` feature or FeatureCollection of Points. + :param polygons: A Single GeoJSON Polygon/MultiPolygon or FeatureCollection of + Polygons/MultiPolygons. + :param chunk_size: Number of chunks each process to handle. The default value is + 1, for a large number of features please use `chunk_size` greater than 1 + to get better results in terms of performance. + :return: A :class:`geojson.FeatureCollection` of Points. + """ + if not points: + raise Exception("Points cannot be empty") + + if points["type"] == "Point": + points = FeatureCollection([Feature(geometry=points)]) + + if points["type"] == "Feature": + points = FeatureCollection([points]) + + manager = Manager() + results: ListProxy[dict] = manager.list() + + part_func = partial( + check_each_point, + polygons=polygons, + results=results, + ) + + with concurrent.futures.ProcessPoolExecutor() as executor: + for _ in executor.map(part_func, points["features"], chunksize=chunk_size): + pass + + return FeatureCollection(list(results)) + + +def check_each_point(point: Point, polygons: list[Polygon], results: list): + """ + Check each point with in the polygon(s) and append the point to the results list + + :param point: A single GeoJSON Point feature. + :type point: dict + :param polygons: A list of GeoJSON Polygon/MultiPolygon. + :type polygons: list + :param results: A list of GeoJSON Point features. + :type results: list + """ + + def __callback_geom_each( + current_geometry, feature_index, feature_properties, feature_bbox, feature_id + ): + contained = False + if boolean_point_in_polygon( + Feature(geometry=point["geometry"]), Feature(geometry=current_geometry) + ): + contained = True + + if contained: + nonlocal results + if point not in results: + results.append(point) + + geom_each(polygons, __callback_geom_each) diff --git a/turfpy/measurement.py b/turfpy/measurement.py index 70de291..dc7138a 100644 --- a/turfpy/measurement.py +++ b/turfpy/measurement.py @@ -5,11 +5,7 @@ link: http://turfjs.org/ """ -import concurrent.futures -from functools import partial from math import asin, atan2, cos, degrees, log, pi, pow, radians, sin, sqrt, tan -from multiprocessing import Manager -from multiprocessing.managers import ListProxy from typing import Optional, Union from geojson import ( @@ -23,7 +19,6 @@ Polygon, ) - from turfpy.helper import ( avg_earth_radius_km, convert_length, @@ -37,7 +32,6 @@ from turfpy.meta import ( coord_each, feature_each, - geom_each, geom_reduce, segment_each, segment_reduce, @@ -354,8 +348,15 @@ def length(geojson, units: str = "km"): >>> length(ls) """ - def _callback_segment_reduce(previous_value, segment): - coords = segment["geometry"]["coordinates"] + def _callback_segment_reduce( + previous_value, + segment, + feature_index, + multi_feature_index, + geometry_index, + segment_index, + ): + coords = segment["coordinates"] return previous_value + distance( Feature(geometry=Point(coords[0])), Feature(geometry=Point(coords[1])), @@ -405,8 +406,7 @@ def destination( radian = length_to_radians(distance) latitude2 = asin( - (sin(latitude1) * cos(radian)) - + (cos(latitude1) * sin(radian) * cos(bearingRad)) + (sin(latitude1) * cos(radian)) + (cos(latitude1) * sin(radian) * cos(bearingRad)) ) longitude2 = longitude1 + atan2( sin(bearingRad) * sin(radian) * cos(latitude1), @@ -623,6 +623,8 @@ def point_on_feature(geojson) -> Feature: >>> feature = Feature(geometry=point) >>> point_on_feature(feature) """ + from turfpy.boolean import boolean_point_in_polygon + fc = _normalize(geojson) cent = centroid(fc) @@ -711,96 +713,6 @@ def _point_on_segment(x, y, x1, y1, x2, y2): return ab == (ap + pb) -# -------------------------------# - -# ------------ boolean point in polygon ----------------# - - -def boolean_point_in_polygon(point: Point, polygon: Polygon, ignore_boundary=False): - """ - Takes a Point or a Point Feature and Polygon or Polygon Feature as input and returns - True if Point is in given Feature. - - :param point: Point or Point Feature. - :param polygon: Polygon or Polygon Feature. - :param ignore_boundary: [Optional] default value is False, specify whether to exclude - boundary of the given polygon or not. - :return: True if the given Point is in Polygons else False - - Example: - - >>> from turfpy.measurement import boolean_point_in_polygon - >>> from geojson import Point, MultiPolygon, Feature - >>> point = Feature(geometry=Point((-77, 44))) - >>> polygon = Feature(geometry=MultiPolygon([([(-81, 41), (-81, 47), (-72, 47), - (-72, 41), (-81, 41)],), - >>> ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],)])) - >>> boolean_point_in_polygon(point, polygon) - """ - - if "type" not in point: - raise Exception("point is required") - if "type" not in polygon: - raise Exception("polygon is required") - - pt = point["coordinates"] - geom = polygon - geo_type = geom["type"] - bbox = polygon.get("bbox", None) - polys = geom["coordinates"] - - if bbox and not in_bbox(pt, bbox): - return False - - if geo_type == "Polygon": - polys = [polys] - - inside_poly = False - - for i in range(0, len(polys)): - if in_ring(pt, polys[i][0], ignore_boundary): - in_hole = False - k = 1 - while k < len(polys[i]) and not in_hole: - if in_ring(pt, polys[i][k], not ignore_boundary): - in_hole = True - k += 1 - if not in_hole: - inside_poly = True - - return inside_poly - - -def in_ring(pt, ring, ignore_boundary): - is_inside = False - if ring[0][0] == ring[len(ring) - 1][0] and ring[0][1] == ring[len(ring) - 1][1]: - ring = ring[0 : len(ring) - 1] - j = len(ring) - 1 - for i in range(0, len(ring)): - xi = ring[i][0] - yi = ring[i][1] - xj = ring[j][0] - yj = ring[j][1] - on_boundary = ( - (pt[1] * (xi - xj) + yi * (xj - pt[0]) + yj * (pt[0] - xi) == 0) - and ((xi - pt[0]) * (xj - pt[0]) <= 0) - and ((yi - pt[1]) * (yj - pt[1]) <= 0) - ) - if on_boundary: - return not ignore_boundary - intersect = ((yi > pt[1]) != (yj > pt[1])) and ( - pt[0] < (xj - xi) * (pt[1] - yi) / (yj - yi) + xi - ) - if intersect: - is_inside = not is_inside - j = i - return is_inside - - -def in_bbox(pt, bbox): - return bbox[0] <= pt[0] <= bbox[2] and bbox[1] <= pt[1] <= bbox[3] - - # -------------------------------# # ------------ Explode -----------# @@ -955,9 +867,9 @@ def _is_below(point1, point2, point3): def _is_left(point1, point2, point3): - return (point2[0] - point1[0]) * (point3[1] - point1[1]) - ( - point3[0] - point1[0] - ) * (point2[1] - point1[1]) + return (point2[0] - point1[0]) * (point3[1] - point1[1]) - (point3[0] - point1[0]) * ( + point2[1] - point1[1] + ) # -------------------------------# @@ -965,9 +877,7 @@ def _is_left(point1, point2, point3): # ------------ point to line distance -----------# -def point_to_line_distance( - point: Feature, line: Feature, units="km", method="geodesic" -): +def point_to_line_distance(point: Feature, line: Feature, units="km", method="geodesic"): """ Returns the minimum distance between a Point and any segment of the LineString. @@ -987,9 +897,7 @@ def point_to_line_distance( >>> point_to_line_distance(point, linestring) """ if method != "geodesic" and method != "planar": - raise Exception( - "method name is incorrect ot should be either geodesic or planar" - ) + raise Exception("method name is incorrect ot should be either geodesic or planar") options = {"units": units, "method": method} @@ -1042,14 +950,10 @@ def distance_to_segment(p, a, b, options): c1 = _dot(w, v) if c1 <= 0: - return _calc_distance( - p, a, {"method": options.get("method", ""), "units": "deg"} - ) + return _calc_distance(p, a, {"method": options.get("method", ""), "units": "deg"}) c2 = _dot(v, v) if c2 <= c1: - return _calc_distance( - p, b, {"method": options.get("method", ""), "units": "deg"} - ) + return _calc_distance(p, b, {"method": options.get("method", ""), "units": "deg"}) b2 = c1 / c2 Pb = [a[0] + (b2 * v[0]), a[1] + (b2 * v[1])] @@ -1160,9 +1064,7 @@ def rhumb_destination(origin, distance, bearing, options: dict = {}) -> Feature: if was_negative_distance: distance_in_meters = -1 * (abs(distance_in_meters)) coords = get_coord(origin) - destination_point = _calculate_rhumb_destination( - coords, distance_in_meters, bearing - ) + destination_point = _calculate_rhumb_destination(coords, distance_in_meters, bearing) return Feature( geometry=Point(destination_point), properties=options.get("properties", ""), @@ -1309,69 +1211,3 @@ def square(bbox: list): horizontal_midpoint + ((north - south) / 2), north, ] - - -# -------------------------------# - - -def points_within_polygon( - points: Union[Feature, FeatureCollection], - polygons: Union[Feature, FeatureCollection], - chunk_size: int = 1, -) -> FeatureCollection: - """Find Point(s) that fall within (Multi)Polygon(s). - - This function takes two inputs GeoJSON Feature :class:`geojson.Point` or - :class:`geojson.FeatureCollection` of Points and GeoJSON Feature - :class:`geojson.Polygon` or Feature :class:`geojson.MultiPolygon` or - FeatureCollection of :class:`geojson.Polygon` or Feature - :class:`geojson.MultiPolygon`. and returns all points with in in those - Polygon(s) or (Multi)Polygon(s). - - :param points: A single GeoJSON ``Point`` feature or FeatureCollection of Points. - :param polygons: A Single GeoJSON Polygon/MultiPolygon or FeatureCollection of - Polygons/MultiPolygons. - :param chunk_size: Number of chunks each process to handle. The default value is - 1, for a large number of features please use `chunk_size` greater than 1 - to get better results in terms of performance. - :return: A :class:`geojson.FeatureCollection` of Points. - """ - if not points: - raise Exception("Points cannot be empty") - - if points["type"] == "Point": - points = FeatureCollection([Feature(geometry=points)]) - - if points["type"] == "Feature": - points = FeatureCollection([points]) - - manager = Manager() - results: ListProxy[dict] = manager.list() - - part_func = partial( - check_each_point, - polygons=polygons, - results=results, - ) - - with concurrent.futures.ProcessPoolExecutor() as executor: - for _ in executor.map(part_func, points["features"], chunksize=chunk_size): - pass - - return FeatureCollection(list(results)) - - -def check_each_point(point, polygons, results): - def __callback_geom_each( - current_geometry, feature_index, feature_properties, feature_bbox, feature_id - ): - contained = False - nonlocal results - if point not in results: - if boolean_point_in_polygon(point["geometry"], current_geometry): - contained = True - - if contained: - results.append(point) - - geom_each(polygons, __callback_geom_each) diff --git a/turfpy/meta.py b/turfpy/meta.py index d63fd70..aec853e 100644 --- a/turfpy/meta.py +++ b/turfpy/meta.py @@ -1,8 +1,4 @@ -from geojson import ( - Feature, - Point, - LineString, -) +from geojson import Feature, LineString, Point def coord_each(geojson_obj, callback, exclude_wrap_coord=False): @@ -19,9 +15,7 @@ def coord_each(geojson_obj, callback, exclude_wrap_coord=False): geometry_maybe_collection = ( geojson_obj["features"][feature_index]["geometry"] if is_feature_collection - else geojson_obj["geometry"] - if is_feature - else geojson_obj + else geojson_obj["geometry"] if is_feature else geojson_obj ) is_geometry_collection = ( geometry_maybe_collection["type"] == "GeometryCollection" @@ -29,9 +23,7 @@ def coord_each(geojson_obj, callback, exclude_wrap_coord=False): else False ) stop_g = ( - len(geometry_maybe_collection["geometries"]) - if is_geometry_collection - else 1 + len(geometry_maybe_collection["geometries"]) if is_geometry_collection else 1 ) for geom_index in range(stop_g): @@ -233,30 +225,22 @@ def geom_each(geojson_obj, callback): geometry_maybe_collection = ( geojson_obj["features"][i]["geometry"] if is_feature_collection - else geojson_obj["geometry"] - if is_feature - else geojson_obj + else geojson_obj["geometry"] if is_feature else geojson_obj ) feature_properties = ( geojson_obj["features"][i]["properties"] if is_feature_collection - else geojson_obj["properties"] - if is_feature - else {} + else geojson_obj["properties"] if is_feature else {} ) feature_bbox = ( geojson_obj["features"][i].get("bbox") if is_feature_collection - else geojson_obj.get("bbox") - if is_feature - else None + else geojson_obj.get("bbox") if is_feature else None ) feature_id = ( geojson_obj["features"][i].get("id") if is_feature_collection - else geojson_obj.get("id") - if is_feature - else None + else geojson_obj.get("id") if is_feature else None ) is_geometry_collection = ( geometry_maybe_collection["type"] == "GeometryCollection" @@ -264,9 +248,7 @@ def geom_each(geojson_obj, callback): else False ) stop_g = ( - len(geometry_maybe_collection["geometries"]) - if is_geometry_collection - else 1 + len(geometry_maybe_collection["geometries"]) if is_geometry_collection else 1 ) for g in range(stop_g): @@ -358,9 +340,7 @@ def _flatten_callback(geometry, feature_index, properties, bbox, id_): if geom_type in [None, "Point", "LineString", "Polygon"]: if ( callback( - Feature( - geometry=geometry, properties=properties, bbox=bbox, id=id_ - ), + Feature(geometry=geometry, properties=properties, bbox=bbox, id=id_), feature_index, 0, ) @@ -428,12 +408,11 @@ def _coord_callback( multi_part_index_coord, geometry_index, ): - nonlocal \ - previous_coords, \ - previous_feature_index, \ - previous_multi_index, \ - prev_geom_index, \ - segment_index + nonlocal previous_coords + nonlocal previous_feature_index + nonlocal previous_multi_index + nonlocal prev_geom_index + nonlocal segment_index if ( previous_coords is None or feature_index > previous_feature_index diff --git a/turfpy/misc.py b/turfpy/misc.py index 836d54b..fad3f8b 100644 --- a/turfpy/misc.py +++ b/turfpy/misc.py @@ -86,9 +86,7 @@ def line_intersect( if not precise_matches.empty: for index, row in precise_matches.iterrows(): # intersect = intersects(mapping(row["geometry"]), segment) - intersection = Feature( - geometry=mapping(row["geometry"].intersection(s)) - ) + intersection = Feature(geometry=mapping(row["geometry"].intersection(s))) key = ",".join(map(str, get_coords(intersection))) if key not in unique: unique.add(key) @@ -367,9 +365,7 @@ def line_slice( ) clip_coords.append(get_coord(ends[1])) - return Feature( - geometry=LineString(clip_coords), properties=line["properties"].copy() - ) + return Feature(geometry=LineString(clip_coords), properties=line["properties"].copy()) def line_arc( @@ -440,9 +436,9 @@ def line_arc( if alfa > arc_end_degree: coordinates.append( - destination( - center, radius, arc_end_degree, {"steps": steps, "units": units} - )["geometry"]["coordinates"] + destination(center, radius, arc_end_degree, {"steps": steps, "units": units})[ + "geometry" + ]["coordinates"] ) return Feature(geometry=LineString(coordinates, properties=properties)) diff --git a/turfpy/random.py b/turfpy/random.py index fc4028e..6ae2cc0 100644 --- a/turfpy/random.py +++ b/turfpy/random.py @@ -53,9 +53,7 @@ def coord_in_bbox(bbox: list): ] -def random_points( - count: int = 1, bbox: Optional[list[Any]] = None -) -> FeatureCollection: +def random_points(count: int = 1, bbox: Optional[list[Any]] = None) -> FeatureCollection: """ Generates geojson random points, if bbox provided then the generated points will be in the bbox. diff --git a/turfpy/transformation.py b/turfpy/transformation.py index b29b8e0..02caabe 100644 --- a/turfpy/transformation.py +++ b/turfpy/transformation.py @@ -529,9 +529,7 @@ def dissolve( else: return union(features) if "properties" in features.keys(): - return FeatureCollection( - dissolve_feature_list, properties=features["properties"] - ) + return FeatureCollection(dissolve_feature_list, properties=features["properties"]) else: return FeatureCollection(dissolve_feature_list) @@ -633,9 +631,7 @@ def _callback_coord_each( initial_angle = rhumb_bearing(GeoPoint(pivot), GeoPoint(coord)) final_angle = initial_angle + angle distance = rhumb_distance(GeoPoint(pivot), GeoPoint(coord)) - new_coords = get_coord( - rhumb_destination(GeoPoint(pivot), distance, final_angle) - ) + new_coords = get_coord(rhumb_destination(GeoPoint(pivot), distance, final_angle)) coord[0] = new_coords[0] coord[1] = new_coords[1] @@ -774,9 +770,7 @@ def _callback_coord_each( original_distance = rhumb_distance(GeoPoint(origin), GeoPoint(coord)) bearing = rhumb_bearing(GeoPoint(origin), GeoPoint(coord)) new_distance = original_distance * factor - new_coord = get_coord( - rhumb_destination(GeoPoint(origin), new_distance, bearing) - ) + new_coord = get_coord(rhumb_destination(GeoPoint(origin), new_distance, bearing)) coord[0] = new_coord[0] coord[1] = new_coord[1] if len(coord) == 3: @@ -955,9 +949,7 @@ def line_offset_feature(line, distance, units): for index, current_coords in enumerate(coords): if index != len(coords) - 1: - segment = _process_segment( - current_coords, coords[index + 1], offset_degrees - ) + segment = _process_segment(current_coords, coords[index + 1], offset_degrees) segments.append(segment) if index > 0: seg2_coords = segments[index - 1] @@ -1088,9 +1080,7 @@ def voronoi( ] convex_hull = MultiPoint([Point(i) for i in points]).convex_hull.buffer(2) - result = MultiPolygon( - [poly.intersection(convex_hull) for poly in polygonize(lines)] - ) + result = MultiPolygon([poly.intersection(convex_hull) for poly in polygonize(lines)]) result = MultiPolygon( [p for p in result.geoms] + [p for p in convex_hull.difference(unary_union(result)).geoms] From 7743617cb4bdc1a0f0d37dd4a31ec06d867e662b Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 13 Dec 2024 07:44:44 -0600 Subject: [PATCH 3/7] ci: run tests --- .github/workflows/main.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8e264ec..b07e663 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,12 +1,12 @@ name: Tests -on: - push: - branches: - - master - pull_request: - branches: - - master +# on: +# push: +# branches: +# - master +# pull_request: +# branches: +# - master jobs: run: From 6b9a051d3658e90b8ae7313007fda7437cd961c5 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 13 Dec 2024 07:46:37 -0600 Subject: [PATCH 4/7] ci: update logic --- .github/workflows/main.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b07e663..71f84a4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,12 +1,6 @@ name: Tests -# on: -# push: -# branches: -# - master -# pull_request: -# branches: -# - master +on: [push] jobs: run: From ccad7beefa4d6d8b9355a5f316a00a96a535a064 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 13 Dec 2024 07:55:25 -0600 Subject: [PATCH 5/7] fix: update results type --- turfpy/joins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/turfpy/joins.py b/turfpy/joins.py index d7613a2..834cb7b 100644 --- a/turfpy/joins.py +++ b/turfpy/joins.py @@ -64,7 +64,7 @@ def points_within_polygon( return FeatureCollection(list(results)) -def check_each_point(point: Point, polygons: list[Polygon], results: list): +def check_each_point(point: Point, polygons: list[Polygon], results: ListProxy): """ Check each point with in the polygon(s) and append the point to the results list From 2fe6d8b39347e2683c555f253b9b2fc405068fe2 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 13 Dec 2024 08:10:52 -0600 Subject: [PATCH 6/7] fix: issue with calculating area --- README.md | 2 + boolean.md | 32 +++++++ docs/source/boolean/boolean_within.rst | 28 ++++++ docs/source/joins/points_within_polygon.rst | 60 ++++++++++-- .../measurements/points_within_polygon.rst | 60 ------------ docs/source/turfpy.joins.rst | 8 ++ joins.md | 40 ++++++++ measurements.md | 38 -------- test.py | 10 ++ tests/test_measurement.py | 11 +++ turfpy/meta.py | 95 ++++++++++++++++--- 11 files changed, 263 insertions(+), 121 deletions(-) create mode 100644 docs/source/boolean/boolean_within.rst delete mode 100644 docs/source/measurements/points_within_polygon.rst create mode 100644 docs/source/turfpy.joins.rst create mode 100644 joins.md create mode 100644 test.py diff --git a/README.md b/README.md index 598a40e..0a4286c 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ It supports below features: - [Boolean](https://github.com/omanges/turfpy/blob/master/boolean.md) +- [Joins](https://github.com/omanges/turfpy/blob/master/joins.md) + ## Documentation Documentation can be found at: [docs](https://turfpy.readthedocs.io/en/latest/) diff --git a/boolean.md b/boolean.md index 6305aea..acfbd14 100644 --- a/boolean.md +++ b/boolean.md @@ -38,4 +38,36 @@ from turfpy.boolean import boolean_intersects feature_1 = Feature(geometry=Point((19.0760, 72.8777))) feature_2 = Feature(geometry=Point((29.0760, 72.8777))) boolean_intersects(feature_1, feature_2) +``` + +* boolean_within : Takes two features and returns (TRUE) if the intersection of the two geometries is NOT an empty set. + +| Argument| Type | Description| +| ------- |------ | ----------- | +| `feature_1` |Feature | Feature 1 | +| `feature_2` |Feature | Feature 2 | + +| Return | Type | Description | +| ------- | ------ | ----------- | +| `bool` | bool | Return true or false | + +```python +from geojson import Feature, Point, Polygon +from turfpy.boolean import boolean_within + +poly = Polygon( + [ + [ + (1, 1), + (1, 10), + (10, 10), + (10, 1), + (1, 1) + ] + ] +) + +feature_1 = Feature(geometry=Point((4, 4))) +feature_2 = Feature(geometry=poly) +boolean_within(feature_1, feature_2) ``` \ No newline at end of file diff --git a/docs/source/boolean/boolean_within.rst b/docs/source/boolean/boolean_within.rst new file mode 100644 index 0000000..403d680 --- /dev/null +++ b/docs/source/boolean/boolean_within.rst @@ -0,0 +1,28 @@ +Boolean Within +============== +Boolean-within returns true if the first geometry is completely within the second geometry. The interiors of both geometries must intersect and, the interior and boundary of the primary (geometry a) must not intersect the exterior of the secondary (geometry b). + +Example +------- + +.. jupyter-execute:: + + from geojson import Feature, Point, Polygon + from turfpy.boolean import boolean_within + + poly = Polygon( + [ + [ + (1, 1), + (1, 10), + (10, 10), + (10, 1), + (1, 1) + ] + ] + ) + + feature_1 = Feature(geometry=Point((4, 4))) + feature_2 = Feature(geometry=poly) + boolean_within(feature_1, feature_2) + diff --git a/docs/source/joins/points_within_polygon.rst b/docs/source/joins/points_within_polygon.rst index d1e6469..8ed8261 100644 --- a/docs/source/joins/points_within_polygon.rst +++ b/docs/source/joins/points_within_polygon.rst @@ -1,18 +1,60 @@ -Points Within Polygon +Points within Polygon ===================== -Finds Points or MultiPoint coordinate positions that fall within (Multi)Polygon(s). +Takes two inputs Point/Points and Polygon(s)/MultiPolygon(s) and returns all the Points with in Polygon(s)/MultiPolygon(s). -Example -------- + +Interactive Example +------------------- .. jupyter-execute:: + from geojson import Feature, FeatureCollection, Point, Polygon from turfpy.joins import points_within_polygon - from geojson import Point, MultiPolygon, Feature + from ipyleaflet import Map, GeoJSON + + p1 = Feature(geometry=Point((-46.6318, -23.5523))) + p2 = Feature(geometry=Point((-46.6246, -23.5325))) + p3 = Feature(geometry=Point((-46.6062, -23.5513))) + p4 = Feature(geometry=Point((-46.663, -23.554))) + p5 = Feature(geometry=Point((-46.643, -23.557))) + + points = FeatureCollection([p1, p2, p3, p4, p5]) + + poly = Polygon( + [ + [ + (-46.653, -23.543), + (-46.634, -23.5346), + (-46.613, -23.543), + (-46.614, -23.559), + (-46.631, -23.567), + (-46.653, -23.560), + (-46.653, -23.543), + ] + ] + ) + + m = Map(center=(-23.5523, -46.6318), zoom=13) + fc = FeatureCollection([p1, p2, p3, p4, p5, poly]) + + geo_json = GeoJSON( + data=fc, + style={"opacity": 1, "dashArray": "9", "fillOpacity": 0.3, "weight": 1}, + hover_style={"color": "green", "dashArray": "0", "fillOpacity": 0.5}, + ) + m.add_layer(geo_json) + + result = points_within_polygon(points, poly) - point = Feature(geometry=Point([-77, 44])) - polygon = Feature(geometry=MultiPolygon([([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), - ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],)])) + data = result.copy() + data["features"].append(Feature(geometry=poly)) - points_within_polygon(point, polygon) + m = Map(center=(-23.5523, -46.6318), zoom=13) + geo_json2 = GeoJSON( + data=data, + style={"opacity": 1, "dashArray": "9", "fillOpacity": 0.3, "weight": 1}, + hover_style={"color": "green", "dashArray": "0", "fillOpacity": 0.5}, + ) + m.add_layer(geo_json2) + m \ No newline at end of file diff --git a/docs/source/measurements/points_within_polygon.rst b/docs/source/measurements/points_within_polygon.rst deleted file mode 100644 index 85c6eaa..0000000 --- a/docs/source/measurements/points_within_polygon.rst +++ /dev/null @@ -1,60 +0,0 @@ -Points within Polygon -===================== -Takes two inputs Point/Points and Polygon(s)/MultiPolygon(s) and returns all the Points with in Polygon(s)/MultiPolygon(s). - - -Interactive Example -------------------- - -.. jupyter-execute:: - - from geojson import Feature, FeatureCollection, Point, Polygon - from turfpy.measurement import points_within_polygon - from ipyleaflet import Map, GeoJSON - - p1 = Feature(geometry=Point((-46.6318, -23.5523))) - p2 = Feature(geometry=Point((-46.6246, -23.5325))) - p3 = Feature(geometry=Point((-46.6062, -23.5513))) - p4 = Feature(geometry=Point((-46.663, -23.554))) - p5 = Feature(geometry=Point((-46.643, -23.557))) - - points = FeatureCollection([p1, p2, p3, p4, p5]) - - poly = Polygon( - [ - [ - (-46.653, -23.543), - (-46.634, -23.5346), - (-46.613, -23.543), - (-46.614, -23.559), - (-46.631, -23.567), - (-46.653, -23.560), - (-46.653, -23.543), - ] - ] - ) - - m = Map(center=(-23.5523, -46.6318), zoom=13) - fc = FeatureCollection([p1, p2, p3, p4, p5, poly]) - - geo_json = GeoJSON( - data=fc, - style={"opacity": 1, "dashArray": "9", "fillOpacity": 0.3, "weight": 1}, - hover_style={"color": "green", "dashArray": "0", "fillOpacity": 0.5}, - ) - m.add_layer(geo_json) - - result = points_within_polygon(points, poly) - - data = result.copy() - data["features"].append(Feature(geometry=poly)) - - m = Map(center=(-23.5523, -46.6318), zoom=13) - - geo_json2 = GeoJSON( - data=data, - style={"opacity": 1, "dashArray": "9", "fillOpacity": 0.3, "weight": 1}, - hover_style={"color": "green", "dashArray": "0", "fillOpacity": 0.5}, - ) - m.add_layer(geo_json2) - m \ No newline at end of file diff --git a/docs/source/turfpy.joins.rst b/docs/source/turfpy.joins.rst new file mode 100644 index 0000000..633ea2f --- /dev/null +++ b/docs/source/turfpy.joins.rst @@ -0,0 +1,8 @@ +turfpy.joins module +========================= + +.. automodule:: turfpy.joins + :members: + :undoc-members: + :show-inheritance: + :private-members: diff --git a/joins.md b/joins.md new file mode 100644 index 0000000..f8b9580 --- /dev/null +++ b/joins.md @@ -0,0 +1,40 @@ +## Join Examples : +* points_within_polygon : Find Point(s) that fall within (Multi)Polygon(s). + +| Argument | Type | Description | +| ------- | ---------------------------------------------------------- | ---------------------------------------------- | +| `points` | Feature/FeatureCollection of Points | FeatureCollection of Points to find | +| `polygons` | Feature/FeatureCollection of Polygon(s)/MultiPolygon(s) | FeatureCollection of Polygon(s)/MultiPolygon(s)| +| `chunk_size` | int | Number of chunks each process to handle. The default value is 1, for a large number of features please use `chunk_size` greater than 1 to get better results in terms of performance.| + +| Return | Type | Description | +| ----------- | ------------------ | ----------------------------------------------------------------- | +| `points` | FeatureCollection | A FeatureCollection of Points in given Polygon(s)/MultiPolygon(s) | + +```python +from geojson import Feature, FeatureCollection, Point, Polygon +from turfpy.joins import points_within_polygon + +p1 = Feature(geometry=Point((-46.6318, -23.5523))) +p2 = Feature(geometry=Point((-46.6246, -23.5325))) +p3 = Feature(geometry=Point((-46.6062, -23.5513))) +p4 = Feature(geometry=Point((-46.663, -23.554))) +p5 = Feature(geometry=Point((-46.643, -23.557))) + +points = FeatureCollection([p1, p2, p3, p4, p5]) + +poly = Polygon( + [ + [ + (-46.653, -23.543), + (-46.634, -23.5346), + (-46.613, -23.543), + (-46.614, -23.559), + (-46.631, -23.567), + (-46.653, -23.560), + (-46.653, -23.543), + ] + ] +) +result = points_within_polygon(points, FeatureCollection([poly])) +``` \ No newline at end of file diff --git a/measurements.md b/measurements.md index eb5bb3c..af6076d 100644 --- a/measurements.md +++ b/measurements.md @@ -399,45 +399,7 @@ bbox = [-20, -20, -15, 0] square(bbox) ``` -* points_within_polygon : Find Point(s) that fall within (Multi)Polygon(s). -| Argument | Type | Description | -| ------- | ---------------------------------------------------------- | ---------------------------------------------- | -| `points` | Feature/FeatureCollection of Points | FeatureCollection of Points to find | -| `polygons` | Feature/FeatureCollection of Polygon(s)/MultiPolygon(s) | FeatureCollection of Polygon(s)/MultiPolygon(s)| -| `chunk_size` | int | Number of chunks each process to handle. The default value is 1, for a large number of features please use `chunk_size` greater than 1 to get better results in terms of performance.| - -| Return | Type | Description | -| ----------- | ------------------ | ----------------------------------------------------------------- | -| `points` | FeatureCollection | A FeatureCollection of Points in given Polygon(s)/MultiPolygon(s) | - -```python -from geojson import Feature, FeatureCollection, Point, Polygon -from turfpy.measurement import points_within_polygon - -p1 = Feature(geometry=Point((-46.6318, -23.5523))) -p2 = Feature(geometry=Point((-46.6246, -23.5325))) -p3 = Feature(geometry=Point((-46.6062, -23.5513))) -p4 = Feature(geometry=Point((-46.663, -23.554))) -p5 = Feature(geometry=Point((-46.643, -23.557))) - -points = FeatureCollection([p1, p2, p3, p4, p5]) - -poly = Polygon( - [ - [ - (-46.653, -23.543), - (-46.634, -23.5346), - (-46.613, -23.543), - (-46.614, -23.559), - (-46.631, -23.567), - (-46.653, -23.560), - (-46.653, -23.543), - ] - ] -) -result = points_within_polygon(points, FeatureCollection([poly])) -``` ## Units Type Some functionalities support `units` as a parameter, default values of `units` is `kilometers` for the functionalities that have units are parameters. The values for it are: ```text diff --git a/test.py b/test.py new file mode 100644 index 0000000..ba795cb --- /dev/null +++ b/test.py @@ -0,0 +1,10 @@ +from turfpy.measurement import area +from geojson import Feature, FeatureCollection + +geometry_1 = {"coordinates": [[[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]], "type": "Polygon"}; +geometry_2 = {"coordinates": [[[2.38, 57.322], [23.194, -20.28], [-120.43, 19.15], [2.38, 57.322]]], "type": "Polygon"}; +feature_1 = Feature(geometry=geometry_1) +feature_2 = Feature(geometry=geometry_2) +feature_collection = FeatureCollection([feature_1, feature_2]) + +print(area(feature_collection)) \ No newline at end of file diff --git a/tests/test_measurement.py b/tests/test_measurement.py index ab8d03f..eab67df 100644 --- a/tests/test_measurement.py +++ b/tests/test_measurement.py @@ -14,6 +14,7 @@ from turfpy.measurement import ( along, + area, bbox, bbox_polygon, center, @@ -29,6 +30,16 @@ square, ) +def test_area(): + """Test area function.""" + geometry_1 = {"coordinates": [[[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]], "type": "Polygon"}; + geometry_2 = {"coordinates": [[[2.38, 57.322], [23.194, -20.28], [-120.43, 19.15], [2.38, 57.322]]], "type": "Polygon"}; + feature_1 = Feature(geometry=geometry_1) + feature_2 = Feature(geometry=geometry_2) + feature_collection = FeatureCollection([feature_1, feature_2]) + + assert area(feature_collection) == 56837434206665.02 + def test_bbox_point(): p = Point((-75.343, 39.984)) diff --git a/turfpy/meta.py b/turfpy/meta.py index aec853e..4a7976a 100644 --- a/turfpy/meta.py +++ b/turfpy/meta.py @@ -1,5 +1,9 @@ +from math import pi, sin + from geojson import Feature, LineString, Point +RADIUS = 6378137 + def coord_each(geojson_obj, callback, exclude_wrap_coord=False): if geojson_obj is None: @@ -309,26 +313,89 @@ def geom_each(geojson_obj, callback): feature_index += 1 -def geom_reduce(geojson_obj, callback, initial_value=None): - previous_value = initial_value +def rad(num: float): + return num * pi / 180 - def _callback( - current_geometry, feature_index, feature_properties, feature_bbox, feature_id +def ring_area(coords: list): + total = 0.0 + coords_length = len(coords) + + if coords_length > 2: + for i in range(0, coords_length): + if i == coords_length - 2: + lower_index = coords_length - 2 + middle_index = coords_length - 1 + upper_index = 0 + elif i == coords_length - 1: + lower_index = coords_length - 1 + middle_index = 0 + upper_index = 1 + else: + lower_index = i + middle_index = i + 1 + upper_index = i + 2 + + p1 = coords[lower_index] + p2 = coords[middle_index] + p3 = coords[upper_index] + total += (rad(p3[0]) - rad(p1[0])) * sin(rad(p2[1])) + + total = total * RADIUS * RADIUS / 2 + return total + + +def polygon_area(coords: list) -> float: + total = 0 + + if coords and len(coords) > 0: + total += abs(ring_area(coords[0])) + for i in range(1, len(coords)): + total -= abs(ring_area(coords[i])) + + return total + + +def calculate_area(geom) -> float: + total = 0.0 + if geom["type"] == "Polygon": + return polygon_area(geom["coordinates"]) + elif geom["type"] == "MultiPolygon": + for coords in geom["coordinates"]: + total += polygon_area(coords) + return total + elif ( + geom["type"] == "Point" + or geom["type"] == "MultiPoint" + or geom["type"] == "LineString" + or geom["type"] == "MultiLineString" ): + return 0 + return 0 + +def geom_reduce(geojson, initial_value_param): + initial_value = initial_value_param + previous_value = initial_value_param + + def callback_geom_each( + current_geometry, + feature_index, + feature_properties, + feature_bbox, + feature_id, + ): + nonlocal initial_value nonlocal previous_value - if feature_index == 0 and initial_value is None: + + def callback_geom_reduce(value, geom): + return value + calculate_area(geom) + + if feature_index == 0 and initial_value: previous_value = current_geometry else: - previous_value = callback( - previous_value, - current_geometry, - feature_index, - feature_properties, - feature_bbox, - feature_id, - ) + previous_value = callback_geom_reduce(previous_value, current_geometry) + return previous_value - geom_each(geojson_obj, _callback) + geom_each(geojson, callback_geom_each) return previous_value From 592ead1d2e6c9521be6a80b9015c84f3e4be5022 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 13 Dec 2024 08:34:19 -0600 Subject: [PATCH 7/7] fix: final updates --- .github/workflows/main.yml | 8 +++++++- joins.md | 2 +- test.py | 10 ---------- tests/test_boolean.py | 6 ------ turfpy/__version__.py | 2 +- 5 files changed, 9 insertions(+), 19 deletions(-) delete mode 100644 test.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 71f84a4..8e264ec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,12 @@ name: Tests -on: [push] +on: + push: + branches: + - master + pull_request: + branches: + - master jobs: run: diff --git a/joins.md b/joins.md index f8b9580..6457c86 100644 --- a/joins.md +++ b/joins.md @@ -1,4 +1,4 @@ -## Join Examples : +## Joins Examples : * points_within_polygon : Find Point(s) that fall within (Multi)Polygon(s). | Argument | Type | Description | diff --git a/test.py b/test.py deleted file mode 100644 index ba795cb..0000000 --- a/test.py +++ /dev/null @@ -1,10 +0,0 @@ -from turfpy.measurement import area -from geojson import Feature, FeatureCollection - -geometry_1 = {"coordinates": [[[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]], "type": "Polygon"}; -geometry_2 = {"coordinates": [[[2.38, 57.322], [23.194, -20.28], [-120.43, 19.15], [2.38, 57.322]]], "type": "Polygon"}; -feature_1 = Feature(geometry=geometry_1) -feature_2 = Feature(geometry=geometry_2) -feature_collection = FeatureCollection([feature_1, feature_2]) - -print(area(feature_collection)) \ No newline at end of file diff --git a/tests/test_boolean.py b/tests/test_boolean.py index 50af399..783fef4 100644 --- a/tests/test_boolean.py +++ b/tests/test_boolean.py @@ -58,9 +58,6 @@ def test_false_fixtures(self): feature1 = geojson["features"][0] feature2 = geojson["features"][1] result = boolean_disjoint(feature1, feature2) - if result is True: - print(json.dumps(feature1)) - print(json.dumps(feature2)) self.assertFalse(result, False) @@ -83,9 +80,6 @@ def test_true_fixtures(self): feature1 = geojson["features"][0] feature2 = geojson["features"][1] result = boolean_intersects(feature1, feature2) - if result is False: - print(json.dumps(feature1)) - print(json.dumps(feature2)) self.assertTrue(result, True) def test_false_fixtures(self): diff --git a/turfpy/__version__.py b/turfpy/__version__.py index 4f8ca05..e2e5afb 100644 --- a/turfpy/__version__.py +++ b/turfpy/__version__.py @@ -1,3 +1,3 @@ """Project version information.""" -__version__ = "0.0.8" +__version__ = "1.0.0"