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/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/measurements/points_within_polygon.rst b/docs/source/joins/points_within_polygon.rst similarity index 96% rename from docs/source/measurements/points_within_polygon.rst rename to docs/source/joins/points_within_polygon.rst index 85c6eaa..8ed8261 100644 --- a/docs/source/measurements/points_within_polygon.rst +++ b/docs/source/joins/points_within_polygon.rst @@ -9,7 +9,7 @@ Interactive Example .. jupyter-execute:: from geojson import Feature, FeatureCollection, Point, Polygon - from turfpy.measurement import points_within_polygon + from turfpy.joins import points_within_polygon from ipyleaflet import Map, GeoJSON p1 = Feature(geometry=Point((-46.6318, -23.5523))) 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/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/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/joins.md b/joins.md new file mode 100644 index 0000000..6457c86 --- /dev/null +++ b/joins.md @@ -0,0 +1,40 @@ +## Joins 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 64b4535..af6076d 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 | @@ -421,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/tests/test_boolean.py b/tests/test_boolean.py index c63539e..783fef4 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 +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): @@ -22,7 +29,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 +46,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, ): @@ -53,7 +68,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, ): @@ -66,7 +85,11 @@ def test_true_fixtures(self): 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 +98,124 @@ 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) + + +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 ba6decd..ec38ecb 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"] 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_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_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_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 4b905e5..eab67df 100644 --- a/tests/test_measurement.py +++ b/tests/test_measurement.py @@ -1,3 +1,5 @@ +import json + from geojson import ( Feature, FeatureCollection, @@ -12,9 +14,9 @@ from turfpy.measurement import ( along, + area, bbox, bbox_polygon, - boolean_point_in_polygon, center, destination, envelope, @@ -22,13 +24,22 @@ midpoint, nearest_point, point_to_line_distance, - points_within_polygon, rhumb_bearing, rhumb_destination, rhumb_distance, 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)) @@ -253,20 +264,6 @@ def test_destination(): assert c1 == 39.9802 -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)],), - ] - ) - ) - bpp = boolean_point_in_polygon(point, polygon) - assert bpp == True - - def test_point_to_line_distance(): point = Feature(geometry=Point((0, 0))) linestring = Feature(geometry=LineString([(1, 1), (-1, 1)])) @@ -304,79 +301,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 == { - "features": [ - { - "geometry": {"coordinates": [-46.6318, -23.5523], "type": "Point"}, - "properties": {}, - "type": "Feature", - }, - { - "geometry": {"coordinates": [-46.643, -23.557], "type": "Point"}, - "properties": {}, - "type": "Feature", - }, - ], - "type": "FeatureCollection", - } - - 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_random.py b/tests/test_random.py index 2e53c23..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 diff --git a/tests/test_transformation.py b/tests/test_transformation.py index d92f4f1..811cd7f 100644 --- a/tests/test_transformation.py +++ b/tests/test_transformation.py @@ -325,7 +325,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/__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" diff --git a/turfpy/boolean.py b/turfpy/boolean.py index a09450f..725e55d 100644 --- a/turfpy/boolean.py +++ b/turfpy/boolean.py @@ -5,10 +5,18 @@ 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 from turfpy.meta import flatten_each from turfpy.misc import line_intersect @@ -28,7 +36,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 +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(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)) @@ -87,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(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(coord2, feature1): + if boolean_point_in_polygon( + Feature(geometry=Point(coordinates=coord2)), Feature(geometry=feature1) + ): return True do_lines_intersect = line_intersect( @@ -104,43 +120,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,14 +155,13 @@ 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"]) 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": @@ -195,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": @@ -277,3 +253,381 @@ def inner_check(flatten2, index2, feature_collection2): flatten_each(feature_1, check_intersection) return bool_result + + +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)) + for coord2 in multipoint2.geoms + ) + for coord1 in multipoint1.geoms + ) + + +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: + 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: + """ + 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 + + for coord in multipoint.geoms: + polygon_type = poly.geom_type + if polygon_type == "Polygon": + geojson_poly = Feature( + geometry=Polygon(coordinates=json.loads(to_geojson(poly))["coordinates"]) + ) + elif polygon_type == "MultiPolygon": + geojson_poly = Feature( + geometry=MultiPolygon( + coordinates=json.loads(to_geojson(poly))["coordinates"] + ) + ) + is_inside = boolean_point_in_polygon( + Feature(geometry=json.loads(to_geojson(coord))), geojson_poly + ) + + if not is_inside: + output = False + break + if not one_inside: + is_inside = boolean_point_in_polygon( + Feature(geometry=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: + """ + 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) + + 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: + """ + 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 + 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 __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 + 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..b1cad1f 100644 --- a/turfpy/dev_lib/earcut.py +++ b/turfpy/dev_lib/earcut.py @@ -393,7 +393,6 @@ 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): @@ -460,7 +459,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/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/joins.py b/turfpy/joins.py new file mode 100644 index 0000000..834cb7b --- /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: ListProxy): + """ + 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 ae7797f..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 ( @@ -29,7 +25,6 @@ feature_of, get_coord, get_coords, - get_geom, get_type, length_to_radians, radians_to_length, @@ -37,7 +32,6 @@ from turfpy.meta import ( coord_each, feature_each, - geom_each, geom_reduce, segment_each, segment_reduce, @@ -142,7 +136,7 @@ def area( MultiPolygon, Feature, FeatureCollection, - ] + ], ): """ This function calculates the area of the Geojson object given as input. @@ -354,10 +348,19 @@ 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])), units + Feature(geometry=Point(coords[0])), + Feature(geometry=Point(coords[1])), + units, ) return segment_reduce(geojson, _callback_segment_reduce, 0) @@ -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])), @@ -621,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) @@ -679,7 +683,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 @@ -709,95 +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, 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 not point: - raise Exception("point is required") - if not polygon: - raise Exception("polygon is required") - - pt = get_coord(point) - geom = get_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 -----------# @@ -1018,8 +933,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 @@ -1296,68 +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 - if boolean_point_in_polygon(point, current_geometry): - contained = True - - if contained: - nonlocal results - results.append(point) - - geom_each(polygons, __callback_geom_each) diff --git a/turfpy/meta.py b/turfpy/meta.py index 0aef4f2..4a7976a 100644 --- a/turfpy/meta.py +++ b/turfpy/meta.py @@ -1,156 +1,320 @@ from math import pi, sin -from geojson import Feature, LineString +from geojson import Feature, LineString, Point RADIUS = 6378137 -def geom_reduce(geojson, initial_value_param): - initial_value = initial_value_param - previous_value = initial_value_param +def coord_each(geojson_obj, callback, exclude_wrap_coord=False): + if geojson_obj is None: + return - def callback_geom_each( - current_geometry, - feature_index, - feature_properties, - feature_bbox, - feature_id, - ): - nonlocal initial_value + 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(stop_g): + multi_feature_index = 0 + geometry_index = 0 + geometry = ( + geometry_maybe_collection["geometries"][geom_index] + if is_geometry_collection + else geometry_maybe_collection + ) + if geometry is None: + continue + + coords = geometry["coordinates"] + geom_type = geometry["type"] + wrap_shrink = ( + 1 + if exclude_wrap_coord + and (geom_type == "Polygon" or geom_type == "MultiPolygon") + else 0 + ) + + if geom_type == "Point": + if ( + callback( + coords, + coord_index, + feature_index, + multi_feature_index, + geometry_index, + ) + 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, + feature_index, + multi_feature_index, + geometry_index, + ) + 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, + feature_index, + multi_feature_index, + geometry_index, + ) + is False + ): + return False + coord_index += 1 + if geom_type == "MultiLineString": + multi_feature_index += 1 + if geom_type == "Polygon": + 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][m], + coord_index, + feature_index, + multi_feature_index, + geometry_index, + ) + 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 coord_reduce(geojson_obj, callback, initial_value=None, exclude_wrap_coord=False): + previous_value = initial_value + + def _callback(*args): nonlocal previous_value + ( + 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_coord, + coord_index, + feature_index, + multi_feature_index, + geometry_index, + ) - def callback_geom_reduce(value, geom): - return value + calculate_area(geom) + coord_each(geojson_obj, _callback, exclude_wrap_coord) + return previous_value - if feature_index == 0 and initial_value: - previous_value = current_geometry + +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 + + 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_geom_reduce(previous_value, current_geometry) - return previous_value + previous_value = callback(previous_value, current_properties, feature_index) - geom_each(geojson, callback_geom_each) + prop_each(geojson_obj, _callback) return previous_value -def geom_each(geojson, callback): - """ - Iterate over each geometry in any GeoJSON object, similar to Array.forEach(). +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 - :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") + +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: - 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 + 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 ) - for g in range(0, stop_g): + for g in range(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, + if geometry is None: + if ( + callback( + None, + feature_index, + feature_properties, + feature_bbox, + feature_id, + ) + is False ): 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], + if geometry["type"] in [ + "Point", + "LineString", + "MultiPoint", + "Polygon", + "MultiLineString", + "MultiPolygon", + ]: + if ( + callback( + geometry, feature_index, feature_properties, - feature_b_box, + 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 - 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 + raise ValueError("Unknown Geometry Type") - 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])) + feature_index += 1 - return total +def rad(num: float): + return num * pi / 180 def ring_area(coords: list): total = 0.0 @@ -180,176 +344,123 @@ def ring_area(coords: list): return total -def rad(num: float): - return num * pi / 180 +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])) -def coord_each(geojson, callback, excludeWrapCoord=None): - """ - Iterate over coordinates in any GeoJSON object, similar to Array.forEach() - :return: - """ - if not geojson: - 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 + return total - if geometry_maybe_collection: - is_geometry_collection = ( - geometry_maybe_collection["type"] == "GeometryCollection" - ) + +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 + + def callback_geom_reduce(value, geom): + return value + calculate_area(geom) + + if feature_index == 0 and initial_value: + previous_value = current_geometry else: - is_geometry_collection = False + previous_value = callback_geom_reduce(previous_value, current_geometry) + return previous_value - stopg = ( - len(geometry_maybe_collection["geometries"]) if is_geometry_collection else 1 - ) + geom_each(geojson, callback_geom_each) + return previous_value - for geom_index in range(0, stopg): - multi_feature_index = 0 - geometry_index = 0 - geometry = ( - geometry_maybe_collection["geometries"][geom_index] - if is_geometry_collection - else geometry_maybe_collection - ) - if not geometry: - continue - coords = geometry["coordinates"] +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 - wrap_shrink = ( - 1 - if excludeWrapCoord - and (geom_type == "Polygon" or geom_type == "MultiPolygon") - else 0 - ) + geom_type_mapping = { + "MultiPoint": "Point", + "MultiLineString": "LineString", + "MultiPolygon": "Polygon", + } + geom_type = geom_type_mapping[geom_type] - if geom_type: - if geom_type == "Point": - # if not callback(coords): - # return False - callback( - coords, - coord_index, - feature_index, - 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 - callback( - coords[j], - coord_index, - feature_index, - 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 - callback( - coords[j][k], - coord_index, - feature_index, - 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 - 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 - callback( - coords[j][k][le], - 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 + 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) -def segment_reduce(geojson, callback, initial_value=None): +def flatten_reduce(geojson_obj, callback, initial_value=None): 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(current_feature, feature_index, multi_feature_index): nonlocal previous_value - if not started and (not initial_value and initial_value != 0): - previous_value = current_segment + 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_segment) - started = True - return True - - segment_each(geojson, callback_segment_each) + previous_value = callback( + previous_value, current_feature, feature_index, multi_feature_index + ) + flatten_each(geojson_obj, _callback) return previous_value -def segment_each(geojson, callback): - def callback_flatten_each(feature, feature_index, multi_feature_index): +def segment_each(geojson_obj, callback): + def _segment_callback(feature, feature_index, multi_feature_index): segment_index = 0 - - if not feature["geometry"]: + if feature["geometry"] is None: return - - type = feature["geometry"]["type"] - - if type == "Point" or type == "MultiPoint": + geom_type = feature["geometry"]["type"] + if geom_type in ["Point", "MultiPoint"]: return previous_coords = None @@ -357,7 +468,7 @@ 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, @@ -370,7 +481,7 @@ def callback_coord_each( nonlocal prev_geom_index nonlocal 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 +492,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..fad3f8b 100644 --- a/turfpy/misc.py +++ b/turfpy/misc.py @@ -96,7 +96,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 +186,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) diff --git a/turfpy/transformation.py b/turfpy/transformation.py index 3860918..02caabe 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