1965 lines
72 KiB
Python
1965 lines
72 KiB
Python
import itertools
|
|
import math
|
|
import pickle
|
|
import subprocess
|
|
import sys
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
|
|
import numpy as np
|
|
import pytest
|
|
from numpy.testing import assert_array_equal
|
|
|
|
import shapely
|
|
from shapely import LineString, MultiPoint, Point, STRtree, box, geos_version
|
|
from shapely.errors import UnsupportedGEOSVersionError
|
|
from shapely.testing import assert_geometries_equal
|
|
from shapely.tests.common import (
|
|
empty,
|
|
empty_line_string,
|
|
empty_point,
|
|
ignore_invalid,
|
|
point,
|
|
)
|
|
|
|
# the distance between 2 points spaced at whole numbers along a diagonal
|
|
HALF_UNIT_DIAG = math.sqrt(2) / 2
|
|
EPS = 1e-9
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def tree():
|
|
geoms = shapely.points(np.arange(10), np.arange(10))
|
|
yield STRtree(geoms)
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def line_tree():
|
|
x = np.arange(10)
|
|
y = np.arange(10)
|
|
offset = 1
|
|
geoms = shapely.linestrings(np.array([[x, x + offset], [y, y + offset]]).T)
|
|
yield STRtree(geoms)
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def poly_tree():
|
|
# create buffers so that midpoint between two buffers intersects
|
|
# each buffer. NOTE: add EPS to help mitigate rounding errors at midpoint.
|
|
geoms = shapely.buffer(
|
|
shapely.points(np.arange(10), np.arange(10)), HALF_UNIT_DIAG + EPS, quad_segs=32
|
|
)
|
|
yield STRtree(geoms)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,count, hits",
|
|
[
|
|
# Empty array produces empty tree
|
|
([], 0, 0),
|
|
([point], 1, 1),
|
|
# None geometries are ignored when creating tree
|
|
([None], 0, 0),
|
|
([point, None], 1, 1),
|
|
# empty geometries are ignored when creating tree
|
|
([empty, empty_point, empty_line_string], 0, 0),
|
|
# only the valid geometry should have a hit
|
|
([empty, point, empty_point, empty_line_string], 1, 1),
|
|
],
|
|
)
|
|
def test_init(geometry, count, hits):
|
|
tree = STRtree(geometry)
|
|
assert len(tree) == count
|
|
assert tree.query(box(0, 0, 100, 100)).size == hits
|
|
|
|
|
|
def test_init_with_invalid_geometry():
|
|
with pytest.raises(TypeError):
|
|
STRtree(["Not a geometry"])
|
|
|
|
|
|
def test_references():
|
|
point1 = Point()
|
|
point2 = Point(0, 1)
|
|
|
|
geoms = [point1, point2]
|
|
tree = STRtree(geoms)
|
|
|
|
point1 = None
|
|
point2 = None
|
|
|
|
import gc
|
|
|
|
gc.collect()
|
|
|
|
# query after freeing geometries does not lead to segfault
|
|
assert tree.query(box(0, 0, 1, 1)).tolist() == [1]
|
|
|
|
|
|
def test_flush_geometries():
|
|
arr = shapely.points(np.arange(10), np.arange(10))
|
|
tree = STRtree(arr)
|
|
|
|
# Dereference geometries
|
|
arr[:] = None
|
|
import gc
|
|
|
|
gc.collect()
|
|
# Still it does not lead to a segfault
|
|
tree.query(point)
|
|
|
|
|
|
def test_geometries_property():
|
|
arr = np.array([point])
|
|
tree = STRtree(arr)
|
|
assert_geometries_equal(arr, tree.geometries)
|
|
|
|
# modifying elements of input should not modify tree.geometries
|
|
arr[0] = shapely.Point(0, 0)
|
|
assert_geometries_equal(point, tree.geometries[0])
|
|
|
|
|
|
def test_pickle_persistence(tmp_path):
|
|
# write the pickeled tree to another process; the process should not crash
|
|
tree = STRtree([Point(i, i).buffer(0.1) for i in range(3)])
|
|
|
|
pickled_strtree = pickle.dumps(tree)
|
|
unpickle_script = """
|
|
import pickle
|
|
import sys
|
|
|
|
from shapely import Point
|
|
|
|
pickled_strtree = sys.stdin.buffer.read()
|
|
print("received pickled strtree:", repr(pickled_strtree))
|
|
tree = pickle.loads(pickled_strtree)
|
|
|
|
tree.query(Point(0, 0))
|
|
tree.nearest(Point(0, 0))
|
|
print("done")
|
|
"""
|
|
|
|
filename = tmp_path / "unpickle-strtree.py"
|
|
with open(filename, "w") as out:
|
|
out.write(unpickle_script)
|
|
|
|
proc = subprocess.Popen(
|
|
[sys.executable, str(filename)],
|
|
stdin=subprocess.PIPE,
|
|
)
|
|
proc.communicate(input=pickled_strtree)
|
|
proc.wait()
|
|
assert proc.returncode == 0
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry",
|
|
[
|
|
"I am not a geometry",
|
|
["I am not a geometry"],
|
|
[Point(0, 0), "still not a geometry"],
|
|
[[], "in a mixed array", 1],
|
|
],
|
|
)
|
|
@pytest.mark.filterwarnings("ignore:Creating an ndarray from ragged nested sequences:")
|
|
def test_query_invalid_geometry(tree, geometry):
|
|
with pytest.raises((TypeError, ValueError)):
|
|
tree.query(geometry)
|
|
|
|
|
|
def test_query_invalid_dimension(tree):
|
|
with pytest.raises(TypeError, match="Array should be one dimensional"):
|
|
tree.query([[Point(0.5, 0.5)]])
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"tree_geometry, geometry,expected",
|
|
[
|
|
# Empty tree returns no results
|
|
([], point, []),
|
|
([], [point], [[], []]),
|
|
([], None, []),
|
|
([], [None], [[], []]),
|
|
# Tree with only None returns no results
|
|
([None], point, []),
|
|
([None], [point], [[], []]),
|
|
([None], None, []),
|
|
([None], [None], [[], []]),
|
|
# querying with None returns no results
|
|
([point], None, []),
|
|
([point], [None], [[], []]),
|
|
# Empty is included in the tree, but ignored when querying the tree
|
|
([empty], empty, []),
|
|
([empty], [empty], [[], []]),
|
|
([empty], point, []),
|
|
([empty], [point], [[], []]),
|
|
([point, empty], empty, []),
|
|
([point, empty], [empty], [[], []]),
|
|
# None and empty are ignored in the tree, but the index of the valid
|
|
# geometry should be retained.
|
|
([None, point], box(0, 0, 10, 10), [1]),
|
|
([None, point], [box(0, 0, 10, 10)], [[0], [1]]),
|
|
([None, empty, point], box(0, 0, 10, 10), [2]),
|
|
([point, None, point], box(0, 0, 10, 10), [0, 2]),
|
|
([point, None, point], [box(0, 0, 10, 10)], [[0, 0], [0, 2]]),
|
|
# Only the non-empty query geometry gets hits
|
|
([empty, point], [empty, point], [[1], [1]]),
|
|
(
|
|
[empty, empty_point, empty_line_string, point],
|
|
[empty, empty_point, empty_line_string, point],
|
|
[[3], [3]],
|
|
),
|
|
],
|
|
)
|
|
def test_query_with_none_and_empty(tree_geometry, geometry, expected):
|
|
tree = STRtree(tree_geometry)
|
|
assert_array_equal(tree.query(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points do not intersect
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# points intersect
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# first and last points intersect
|
|
(
|
|
[Point(1, 1), Point(-1, -1), Point(2, 2)],
|
|
[[0, 2], [1, 2]],
|
|
),
|
|
# box contains points
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
([box(0, 0, 1, 1)], [[0, 0], [0, 1]]),
|
|
# bigger box contains more points
|
|
(box(5, 5, 15, 15), [5, 6, 7, 8, 9]),
|
|
([box(5, 5, 15, 15)], [[0, 0, 0, 0, 0], [5, 6, 7, 8, 9]]),
|
|
# first and last boxes contains points
|
|
(
|
|
[box(0, 0, 1, 1), box(100, 100, 110, 110), box(5, 5, 15, 15)],
|
|
[[0, 0, 2, 2, 2, 2, 2], [0, 1, 5, 6, 7, 8, 9]],
|
|
),
|
|
# envelope of buffer contains points
|
|
(shapely.buffer(Point(3, 3), 1), [2, 3, 4]),
|
|
([shapely.buffer(Point(3, 3), 1)], [[0, 0, 0], [2, 3, 4]]),
|
|
# envelope of points contains points
|
|
(MultiPoint([[5, 7], [7, 5]]), [5, 6, 7]),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[0, 0, 0], [5, 6, 7]]),
|
|
],
|
|
)
|
|
def test_query_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point intersects first line
|
|
(Point(0, 0), [0]),
|
|
([Point(0, 0)], [[0], [0]]),
|
|
(Point(0.5, 0.5), [0]),
|
|
([Point(0.5, 0.5)], [[0], [0]]),
|
|
# point within envelope of first line
|
|
(Point(0, 0.5), [0]),
|
|
([Point(0, 0.5)], [[0], [0]]),
|
|
# point at shared vertex between 2 lines
|
|
(Point(1, 1), [0, 1]),
|
|
([Point(1, 1)], [[0, 0], [0, 1]]),
|
|
# box overlaps envelope of first 2 lines (touches edge of 1)
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
([box(0, 0, 1, 1)], [[0, 0], [0, 1]]),
|
|
# envelope of buffer overlaps envelope of 2 lines
|
|
(shapely.buffer(Point(3, 3), 0.5), [2, 3]),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[0, 0], [2, 3]]),
|
|
# envelope of points overlaps 5 lines (touches edge of 2 envelopes)
|
|
(MultiPoint([[5, 7], [7, 5]]), [4, 5, 6, 7]),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[0, 0, 0, 0], [4, 5, 6, 7]]),
|
|
],
|
|
)
|
|
def test_query_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point intersects edge of envelopes of 2 polygons
|
|
(Point(0.5, 0.5), [0, 1]),
|
|
([Point(0.5, 0.5)], [[0, 0], [0, 1]]),
|
|
# point intersects single polygon
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# box overlaps envelope of 2 polygons
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
([box(0, 0, 1, 1)], [[0, 0], [0, 1]]),
|
|
# larger box overlaps envelope of 3 polygons
|
|
(box(0, 0, 1.5, 1.5), [0, 1, 2]),
|
|
([box(0, 0, 1.5, 1.5)], [[0, 0, 0], [0, 1, 2]]),
|
|
# first and last boxes overlap envelope of 2 polyons
|
|
(
|
|
[box(0, 0, 1, 1), box(100, 100, 110, 110), box(2, 2, 3, 3)],
|
|
[[0, 0, 2, 2], [0, 1, 2, 3]],
|
|
),
|
|
# envelope of buffer overlaps envelope of 3 polygons
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), [2, 3, 4]),
|
|
(
|
|
[shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)],
|
|
[[0, 0, 0], [2, 3, 4]],
|
|
),
|
|
# envelope of larger buffer overlaps envelope of 6 polygons
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [1, 2, 3, 4, 5]),
|
|
(
|
|
[shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)],
|
|
[[0, 0, 0, 0, 0], [1, 2, 3, 4, 5]],
|
|
),
|
|
# envelope of points overlaps 3 polygons
|
|
(MultiPoint([[5, 7], [7, 5]]), [5, 6, 7]),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[0, 0, 0], [5, 6, 7]]),
|
|
],
|
|
)
|
|
def test_query_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"predicate",
|
|
[
|
|
"bad_predicate",
|
|
# disjoint is a valid GEOS binary predicate, but not supported for query
|
|
"disjoint",
|
|
],
|
|
)
|
|
def test_query_invalid_predicate(tree, predicate):
|
|
with pytest.raises(ValueError, match="is not a valid option"):
|
|
tree.query(Point(1, 1), predicate=predicate)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"predicate,expected",
|
|
[
|
|
("intersects", [0, 1, 2]),
|
|
("within", []),
|
|
("contains", [1]),
|
|
("overlaps", []),
|
|
("crosses", []),
|
|
("covers", [0, 1, 2]),
|
|
("covered_by", []),
|
|
("contains_properly", [1]),
|
|
],
|
|
)
|
|
def test_query_prepared_inputs(tree, predicate, expected):
|
|
geom = box(0, 0, 2, 2)
|
|
shapely.prepare(geom)
|
|
assert_array_equal(tree.query(geom, predicate=predicate), expected)
|
|
|
|
|
|
def test_query_with_partially_prepared_inputs(tree):
|
|
geom = np.array([box(0, 0, 1, 1), box(3, 3, 5, 5)])
|
|
expected = tree.query(geom, predicate="intersects")
|
|
|
|
# test with array of partially prepared geometries
|
|
shapely.prepare(geom[0])
|
|
assert_array_equal(expected, tree.query(geom, predicate="intersects"))
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"predicate,expected",
|
|
[
|
|
pytest.param(
|
|
"intersects",
|
|
[1],
|
|
marks=pytest.mark.xfail(geos_version < (3, 13, 0), reason="GEOS < 3.13"),
|
|
),
|
|
("within", []),
|
|
("contains", []),
|
|
("overlaps", []),
|
|
("crosses", [1]),
|
|
("touches", []),
|
|
("covers", []),
|
|
("covered_by", []),
|
|
("contains_properly", []),
|
|
],
|
|
)
|
|
def test_query_predicate_errors(tree, predicate, expected):
|
|
with ignore_invalid():
|
|
line_nan = shapely.linestrings([1, 1], [1, float("nan")])
|
|
if geos_version < (3, 13, 0):
|
|
with pytest.raises(shapely.GEOSException):
|
|
tree.query(line_nan, predicate=predicate)
|
|
else:
|
|
assert_array_equal(tree.query(line_nan, predicate=predicate), expected)
|
|
|
|
|
|
### predicate == 'intersects'
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points do not intersect
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# points intersect
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# box contains points
|
|
(box(3, 3, 6, 6), [3, 4, 5, 6]),
|
|
([box(3, 3, 6, 6)], [[0, 0, 0, 0], [3, 4, 5, 6]]),
|
|
# first and last boxes contain points
|
|
(
|
|
[box(0, 0, 1, 1), box(100, 100, 110, 110), box(3, 3, 6, 6)],
|
|
[[0, 0, 2, 2, 2, 2], [0, 1, 3, 4, 5, 6]],
|
|
),
|
|
# envelope of buffer contains more points than intersect buffer
|
|
# due to diagonal distance
|
|
(shapely.buffer(Point(3, 3), 1), [3]),
|
|
([shapely.buffer(Point(3, 3), 1)], [[0], [3]]),
|
|
# envelope of buffer with 1/2 distance between points should intersect
|
|
# same points as envelope
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [2, 3, 4]),
|
|
(
|
|
[shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)],
|
|
[[0, 0, 0], [2, 3, 4]],
|
|
),
|
|
# multipoints intersect
|
|
(
|
|
MultiPoint([[5, 5], [7, 7]]),
|
|
[5, 7],
|
|
),
|
|
(
|
|
[MultiPoint([[5, 5], [7, 7]])],
|
|
[[0, 0], [5, 7]],
|
|
),
|
|
# envelope of points contains points, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects
|
|
(
|
|
MultiPoint([[5, 7], [7, 7]]),
|
|
[7],
|
|
),
|
|
(
|
|
[MultiPoint([[5, 7], [7, 7]])],
|
|
[[0], [7]],
|
|
),
|
|
],
|
|
)
|
|
def test_query_intersects_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry, predicate="intersects"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point intersects first line
|
|
(Point(0, 0), [0]),
|
|
([Point(0, 0)], [[0], [0]]),
|
|
(Point(0.5, 0.5), [0]),
|
|
([Point(0.5, 0.5)], [[0], [0]]),
|
|
# point within envelope of first line but does not intersect
|
|
(Point(0, 0.5), []),
|
|
([Point(0, 0.5)], [[], []]),
|
|
# point at shared vertex between 2 lines
|
|
(Point(1, 1), [0, 1]),
|
|
([Point(1, 1)], [[0, 0], [0, 1]]),
|
|
# box overlaps envelope of first 2 lines (touches edge of 1)
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
([box(0, 0, 1, 1)], [[0, 0], [0, 1]]),
|
|
# first and last boxes overlap multiple lines each
|
|
(
|
|
[box(0, 0, 1, 1), box(100, 100, 110, 110), box(2, 2, 3, 3)],
|
|
[[0, 0, 2, 2, 2], [0, 1, 1, 2, 3]],
|
|
),
|
|
# buffer intersects 2 lines
|
|
(shapely.buffer(Point(3, 3), 0.5), [2, 3]),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[0, 0], [2, 3]]),
|
|
# buffer intersects midpoint of line at tangent
|
|
(shapely.buffer(Point(2, 1), HALF_UNIT_DIAG), [1]),
|
|
([shapely.buffer(Point(2, 1), HALF_UNIT_DIAG)], [[0], [1]]),
|
|
# envelope of points overlaps lines but intersects none
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects
|
|
(MultiPoint([[5, 7], [7, 7]]), [6, 7]),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[0, 0], [6, 7]]),
|
|
],
|
|
)
|
|
def test_query_intersects_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query(geometry, predicate="intersects"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point within first polygon
|
|
(Point(0, 0.5), [0]),
|
|
([Point(0, 0.5)], [[0], [0]]),
|
|
(Point(0.5, 0), [0]),
|
|
([Point(0.5, 0)], [[0], [0]]),
|
|
# midpoint between two polygons intersects both
|
|
(Point(0.5, 0.5), [0, 1]),
|
|
([Point(0.5, 0.5)], [[0, 0], [0, 1]]),
|
|
# point intersects single polygon
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# box overlaps envelope of 2 polygons
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
([box(0, 0, 1, 1)], [[0, 0], [0, 1]]),
|
|
# larger box intersects 3 polygons
|
|
(box(0, 0, 1.5, 1.5), [0, 1, 2]),
|
|
([box(0, 0, 1.5, 1.5)], [[0, 0, 0], [0, 1, 2]]),
|
|
# first and last boxes overlap
|
|
(
|
|
[box(0, 0, 1, 1), box(100, 100, 110, 110), box(2, 2, 3, 3)],
|
|
[[0, 0, 2, 2], [0, 1, 2, 3]],
|
|
),
|
|
# buffer overlaps 3 polygons
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), [2, 3, 4]),
|
|
(
|
|
[shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)],
|
|
[[0, 0, 0], [2, 3, 4]],
|
|
),
|
|
# larger buffer overlaps 6 polygons (touches midpoints)
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [1, 2, 3, 4, 5]),
|
|
(
|
|
[shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)],
|
|
[[0, 0, 0, 0, 0], [1, 2, 3, 4, 5]],
|
|
),
|
|
# envelope of points overlaps polygons, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint within polygon
|
|
(MultiPoint([[5, 7], [7, 7]]), [7]),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[0], [7]]),
|
|
],
|
|
)
|
|
def test_query_intersects_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query(geometry, predicate="intersects"), expected)
|
|
|
|
|
|
### predicate == 'within'
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points do not intersect
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# points intersect
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# box not within points
|
|
(box(3, 3, 6, 6), []),
|
|
([box(3, 3, 6, 6)], [[], []]),
|
|
# envelope of buffer not within points
|
|
(shapely.buffer(Point(3, 3), 1), []),
|
|
([shapely.buffer(Point(3, 3), 1)], [[], []]),
|
|
# multipoints intersect but are not within points in tree
|
|
(MultiPoint([[5, 5], [7, 7]]), []),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[], []]),
|
|
# only one point of multipoint intersects, but multipoints are not
|
|
# within any points in tree
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
# envelope of points contains points, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_within_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry, predicate="within"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# endpoint not within first line
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
# point within first line
|
|
(Point(0.5, 0.5), [0]),
|
|
([Point(0.5, 0.5)], [[0], [0]]),
|
|
# point within envelope of first line but does not intersect
|
|
(Point(0, 0.5), []),
|
|
([Point(0, 0.5)], [[], []]),
|
|
# point at shared vertex between 2 lines (but within neither)
|
|
(Point(1, 1), []),
|
|
([Point(1, 1)], [[], []]),
|
|
# box not within line
|
|
(box(0, 0, 1, 1), []),
|
|
([box(0, 0, 1, 1)], [[], []]),
|
|
# buffer intersects 2 lines but not within either
|
|
(shapely.buffer(Point(3, 3), 0.5), []),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[], []]),
|
|
# envelope of points overlaps lines but intersects none
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects, but both are not within line
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
(MultiPoint([[6.5, 6.5], [7, 7]]), [6]),
|
|
([MultiPoint([[6.5, 6.5], [7, 7]])], [[0], [6]]),
|
|
],
|
|
)
|
|
def test_query_within_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query(geometry, predicate="within"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point within first polygon
|
|
(Point(0, 0.5), [0]),
|
|
([Point(0, 0.5)], [[0], [0]]),
|
|
(Point(0.5, 0), [0]),
|
|
([Point(0.5, 0)], [[0], [0]]),
|
|
# midpoint between two polygons intersects both
|
|
(Point(0.5, 0.5), [0, 1]),
|
|
([Point(0.5, 0.5)], [[0, 0], [0, 1]]),
|
|
# point intersects single polygon
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# box overlaps envelope of 2 polygons but within neither
|
|
(box(0, 0, 1, 1), []),
|
|
([box(0, 0, 1, 1)], [[], []]),
|
|
# box within polygon
|
|
(box(0, 0, 0.5, 0.5), [0]),
|
|
([box(0, 0, 0.5, 0.5)], [[0], [0]]),
|
|
# larger box intersects 3 polygons but within none
|
|
(box(0, 0, 1.5, 1.5), []),
|
|
([box(0, 0, 1.5, 1.5)], [[], []]),
|
|
# buffer intersects 3 polygons but only within one
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), [3]),
|
|
([shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)], [[0], [3]]),
|
|
# larger buffer overlaps 6 polygons (touches midpoints) but within none
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), []),
|
|
([shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)], [[], []]),
|
|
# envelope of points overlaps polygons, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint within polygon
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
# both points in multipoint within polygon
|
|
(MultiPoint([[5.25, 5.5], [5.25, 5.0]]), [5]),
|
|
([MultiPoint([[5.25, 5.5], [5.25, 5.0]])], [[0], [5]]),
|
|
],
|
|
)
|
|
def test_query_within_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query(geometry, predicate="within"), expected)
|
|
|
|
|
|
### predicate == 'contains'
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points do not intersect
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# points intersect
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# box contains points (2 are at edges and not contained)
|
|
(box(3, 3, 6, 6), [4, 5]),
|
|
([box(3, 3, 6, 6)], [[0, 0], [4, 5]]),
|
|
# envelope of buffer contains more points than within buffer
|
|
# due to diagonal distance
|
|
(shapely.buffer(Point(3, 3), 1), [3]),
|
|
([shapely.buffer(Point(3, 3), 1)], [[0], [3]]),
|
|
# envelope of buffer with 1/2 distance between points should intersect
|
|
# same points as envelope
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [2, 3, 4]),
|
|
([shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)], [[0, 0, 0], [2, 3, 4]]),
|
|
# multipoints intersect
|
|
(MultiPoint([[5, 5], [7, 7]]), [5, 7]),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[0, 0], [5, 7]]),
|
|
# envelope of points contains points, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects
|
|
(MultiPoint([[5, 7], [7, 7]]), [7]),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[0], [7]]),
|
|
],
|
|
)
|
|
def test_query_contains_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry, predicate="contains"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point does not contain any lines (not valid relation)
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
# box contains first line (touches edge of 1 but does not contain it)
|
|
(box(0, 0, 1, 1), [0]),
|
|
([box(0, 0, 1, 1)], [[0], [0]]),
|
|
# buffer intersects 2 lines but contains neither
|
|
(shapely.buffer(Point(3, 3), 0.5), []),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[], []]),
|
|
# envelope of points overlaps lines but intersects none
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
# both points intersect but do not contain any lines (not valid relation)
|
|
(MultiPoint([[5, 5], [6, 6]]), []),
|
|
([MultiPoint([[5, 5], [6, 6]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_contains_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query(geometry, predicate="contains"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point does not contain any polygons (not valid relation)
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
# box overlaps envelope of 2 polygons but contains neither
|
|
(box(0, 0, 1, 1), []),
|
|
([box(0, 0, 1, 1)], [[], []]),
|
|
# larger box intersects 3 polygons but contains only one
|
|
(box(0, 0, 2, 2), [1]),
|
|
([box(0, 0, 2, 2)], [[0], [1]]),
|
|
# buffer overlaps 3 polygons but contains none
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), []),
|
|
([shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)], [[], []]),
|
|
# larger buffer overlaps 6 polygons (touches midpoints) but contains one
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [3]),
|
|
([shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)], [[0], [3]]),
|
|
# envelope of points overlaps polygons, but points do not intersect
|
|
# (not valid relation)
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_contains_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query(geometry, predicate="contains"), expected)
|
|
|
|
|
|
### predicate == 'overlaps'
|
|
# Overlaps only returns results where geometries are of same dimensions
|
|
# and do not completely contain each other.
|
|
# See: https://postgis.net/docs/ST_Overlaps.html
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points do not intersect
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# points intersect but do not overlap
|
|
(Point(1, 1), []),
|
|
([Point(1, 1)], [[], []]),
|
|
# box overlaps points including those at edge but does not overlap
|
|
# (completely contains all points)
|
|
(box(3, 3, 6, 6), []),
|
|
([box(3, 3, 6, 6)], [[], []]),
|
|
# envelope of buffer contains points, but does not overlap
|
|
(shapely.buffer(Point(3, 3), 1), []),
|
|
([shapely.buffer(Point(3, 3), 1)], [[], []]),
|
|
# multipoints intersect but do not overlap (both completely contain each other)
|
|
(MultiPoint([[5, 5], [7, 7]]), []),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[], []]),
|
|
# envelope of points contains points in tree, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects but does not overlap
|
|
# the intersecting point from multipoint completely contains point in tree
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_overlaps_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry, predicate="overlaps"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point intersects line but is completely contained by it
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
# box overlaps second line (contains first line)
|
|
# but of different dimensions so does not overlap
|
|
(box(0, 0, 1.5, 1.5), []),
|
|
([box(0, 0, 1.5, 1.5)], [[], []]),
|
|
# buffer intersects 2 lines but of different dimensions so does not overlap
|
|
(shapely.buffer(Point(3, 3), 0.5), []),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[], []]),
|
|
# envelope of points overlaps lines but intersects none
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
# both points intersect but different dimensions
|
|
(MultiPoint([[5, 5], [6, 6]]), []),
|
|
([MultiPoint([[5, 5], [6, 6]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_overlaps_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query(geometry, predicate="overlaps"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point does not overlap any polygons (different dimensions)
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
# box overlaps 2 polygons
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
([box(0, 0, 1, 1)], [[0, 0], [0, 1]]),
|
|
# larger box intersects 3 polygons and contains one
|
|
(box(0, 0, 2, 2), [0, 2]),
|
|
([box(0, 0, 2, 2)], [[0, 0], [0, 2]]),
|
|
# buffer overlaps 3 polygons and contains 1
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), [2, 4]),
|
|
([shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)], [[0, 0], [2, 4]]),
|
|
# larger buffer overlaps 6 polygons (touches midpoints) but contains one
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [1, 2, 4, 5]),
|
|
(
|
|
[shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)],
|
|
[[0, 0, 0, 0], [1, 2, 4, 5]],
|
|
),
|
|
# one of two points intersects but different dimensions
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_overlaps_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query(geometry, predicate="overlaps"), expected)
|
|
|
|
|
|
### predicate == 'crosses'
|
|
# Only valid for certain geometry combinations
|
|
# See: https://postgis.net/docs/ST_Crosses.html
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points intersect but not valid relation
|
|
(Point(1, 1), []),
|
|
# all points of result from tree are in common with box
|
|
(box(3, 3, 6, 6), []),
|
|
# all points of result from tree are in common with buffer
|
|
(shapely.buffer(Point(3, 3), 1), []),
|
|
# only one point of multipoint intersects but not valid relation
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
],
|
|
)
|
|
def test_query_crosses_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry, predicate="crosses"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point intersects first line but is completely in common with line
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
# box overlaps envelope of first 2 lines, contains first and crosses second
|
|
(box(0, 0, 1.5, 1.5), [1]),
|
|
([box(0, 0, 1.5, 1.5)], [[0], [1]]),
|
|
# buffer intersects 2 lines
|
|
(shapely.buffer(Point(3, 3), 0.5), [2, 3]),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[0, 0], [2, 3]]),
|
|
# line crosses line
|
|
(shapely.linestrings([(1, 0), (0, 1)]), [0]),
|
|
([shapely.linestrings([(1, 0), (0, 1)])], [[0], [0]]),
|
|
# envelope of points overlaps lines but intersects none
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects
|
|
(MultiPoint([[5, 7], [7, 7], [7, 8]]), []),
|
|
([MultiPoint([[5, 7], [7, 7], [7, 8]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_crosses_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query(geometry, predicate="crosses"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point within first polygon but not valid relation
|
|
(Point(0, 0.5), []),
|
|
([Point(0, 0.5)], [[], []]),
|
|
# box overlaps 2 polygons but not valid relation
|
|
(box(0, 0, 1.5, 1.5), []),
|
|
([box(0, 0, 1.5, 1.5)], [[], []]),
|
|
# buffer overlaps 3 polygons but not valid relation
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), []),
|
|
([shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)], [[], []]),
|
|
# only one point of multipoint within
|
|
(MultiPoint([[5, 7], [7, 7], [7, 8]]), [7]),
|
|
([MultiPoint([[5, 7], [7, 7], [7, 8]])], [[0], [7]]),
|
|
],
|
|
)
|
|
def test_query_crosses_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query(geometry, predicate="crosses"), expected)
|
|
|
|
|
|
### predicate == 'touches'
|
|
# See: https://postgis.net/docs/ST_Touches.html
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points do not intersect
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# points intersect but not valid relation
|
|
(Point(1, 1), []),
|
|
([Point(1, 1)], [[], []]),
|
|
# box contains points but touches only those at edges
|
|
(box(3, 3, 6, 6), [3, 6]),
|
|
([box(3, 3, 6, 6)], [[0, 0], [3, 6]]),
|
|
# polygon completely contains point in tree
|
|
(shapely.buffer(Point(3, 3), 1), []),
|
|
([shapely.buffer(Point(3, 3), 1)], [[], []]),
|
|
# linestring intersects 2 points but touches only one
|
|
(LineString([(-1, -1), (1, 1)]), [1]),
|
|
([LineString([(-1, -1), (1, 1)])], [[0], [1]]),
|
|
# multipoints intersect but not valid relation
|
|
(MultiPoint([[5, 5], [7, 7]]), []),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_touches_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry, predicate="touches"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point intersects first line
|
|
(Point(0, 0), [0]),
|
|
([Point(0, 0)], [[0], [0]]),
|
|
# point is within line
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# point at shared vertex between 2 lines
|
|
(Point(1, 1), [0, 1]),
|
|
([Point(1, 1)], [[0, 0], [0, 1]]),
|
|
# box overlaps envelope of first 2 lines (touches edge of 1)
|
|
(box(0, 0, 1, 1), [1]),
|
|
([box(0, 0, 1, 1)], [[0], [1]]),
|
|
# buffer intersects 2 lines but does not touch edges of either
|
|
(shapely.buffer(Point(3, 3), 0.5), []),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[], []]),
|
|
# buffer intersects midpoint of line at tangent but there is a little overlap
|
|
# due to precision issues
|
|
(shapely.buffer(Point(2, 1), HALF_UNIT_DIAG + 1e-7), []),
|
|
([shapely.buffer(Point(2, 1), HALF_UNIT_DIAG + 1e-7)], [[], []]),
|
|
# envelope of points overlaps lines but intersects none
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects at vertex between lines
|
|
(MultiPoint([[5, 7], [7, 7], [7, 8]]), [6, 7]),
|
|
([MultiPoint([[5, 7], [7, 7], [7, 8]])], [[0, 0], [6, 7]]),
|
|
],
|
|
)
|
|
def test_query_touches_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query(geometry, predicate="touches"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point within first polygon
|
|
(Point(0, 0.5), []),
|
|
([Point(0, 0.5)], [[], []]),
|
|
# point is at edge of first polygon
|
|
(Point(HALF_UNIT_DIAG + EPS, 0), [0]),
|
|
([Point(HALF_UNIT_DIAG + EPS, 0)], [[0], [0]]),
|
|
# box overlaps envelope of 2 polygons does not touch any at edge
|
|
(box(0, 0, 1, 1), []),
|
|
([box(0, 0, 1, 1)], [[], []]),
|
|
# box overlaps 2 polygons and touches edge of first
|
|
(box(HALF_UNIT_DIAG + EPS, 0, 2, 2), [0]),
|
|
([box(HALF_UNIT_DIAG + EPS, 0, 2, 2)], [[0], [0]]),
|
|
# buffer overlaps 3 polygons but does not touch any at edge
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG + EPS), []),
|
|
([shapely.buffer(Point(3, 3), HALF_UNIT_DIAG + EPS)], [[], []]),
|
|
# only one point of multipoint within polygon but does not touch
|
|
(MultiPoint([[0, 0], [7, 7], [7, 8]]), []),
|
|
([MultiPoint([[0, 0], [7, 7], [7, 8]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_touches_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query(geometry, predicate="touches"), expected)
|
|
|
|
|
|
### predicate == 'covers'
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points do not intersect
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# points intersect and thus no point is outside the other
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# box covers any points that intersect or are within
|
|
(box(3, 3, 6, 6), [3, 4, 5, 6]),
|
|
([box(3, 3, 6, 6)], [[0, 0, 0, 0], [3, 4, 5, 6]]),
|
|
# envelope of buffer covers more points than are covered by buffer
|
|
# due to diagonal distance
|
|
(shapely.buffer(Point(3, 3), 1), [3]),
|
|
([shapely.buffer(Point(3, 3), 1)], [[0], [3]]),
|
|
# envelope of buffer with 1/2 distance between points should intersect
|
|
# same points as envelope
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [2, 3, 4]),
|
|
([shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)], [[0, 0, 0], [2, 3, 4]]),
|
|
# multipoints intersect and thus no point is outside the other
|
|
(MultiPoint([[5, 5], [7, 7]]), [5, 7]),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[0, 0], [5, 7]]),
|
|
# envelope of points contains points, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects
|
|
(MultiPoint([[5, 7], [7, 7]]), [7]),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[0], [7]]),
|
|
],
|
|
)
|
|
def test_query_covers_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry, predicate="covers"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point does not cover any lines (not valid relation)
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
# box covers first line (intersects another does not contain it)
|
|
(box(0, 0, 1.5, 1.5), [0]),
|
|
([box(0, 0, 1.5, 1.5)], [[0], [0]]),
|
|
# box completely covers 2 lines (touches edges of 2 others)
|
|
(box(1, 1, 3, 3), [1, 2]),
|
|
([box(1, 1, 3, 3)], [[0, 0], [1, 2]]),
|
|
# buffer intersects 2 lines but does not completely cover either
|
|
(shapely.buffer(Point(3, 3), 0.5), []),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[], []]),
|
|
# envelope of points overlaps lines but intersects none
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects a line, but does not completely cover
|
|
# it
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
# both points intersect but do not cover any lines (not valid relation)
|
|
(MultiPoint([[5, 5], [6, 6]]), []),
|
|
([MultiPoint([[5, 5], [6, 6]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_covers_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query(geometry, predicate="covers"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point does not cover any polygons (not valid relation)
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
# box overlaps envelope of 2 polygons but does not completely cover either
|
|
(box(0, 0, 1, 1), []),
|
|
([box(0, 0, 1, 1)], [[], []]),
|
|
# larger box intersects 3 polygons but covers only one
|
|
(box(0, 0, 2, 2), [1]),
|
|
([box(0, 0, 2, 2)], [[0], [1]]),
|
|
# buffer overlaps 3 polygons but does not completely cover any
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), []),
|
|
([shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)], [[], []]),
|
|
# larger buffer overlaps 6 polygons (touches midpoints) but covers only one
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [3]),
|
|
([shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)], [[0], [3]]),
|
|
# envelope of points overlaps polygons, but points do not intersect
|
|
# (not valid relation)
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_covers_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query(geometry, predicate="covers"), expected)
|
|
|
|
|
|
### predicate == 'covered_by'
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points do not intersect
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# points intersect
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# box not covered by points
|
|
(box(3, 3, 6, 6), []),
|
|
([box(3, 3, 6, 6)], [[], []]),
|
|
# envelope of buffer not covered by points
|
|
(shapely.buffer(Point(3, 3), 1), []),
|
|
([shapely.buffer(Point(3, 3), 1)], [[], []]),
|
|
# multipoints intersect but are not covered by points in tree
|
|
(MultiPoint([[5, 5], [7, 7]]), []),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[], []]),
|
|
# only one point of multipoint intersects, but multipoints are not
|
|
# covered by any points in tree
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
# envelope of points overlaps points, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_covered_by_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry, predicate="covered_by"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# endpoint is covered by first line
|
|
(Point(0, 0), [0]),
|
|
([Point(0, 0)], [[0], [0]]),
|
|
# point covered by first line
|
|
(Point(0.5, 0.5), [0]),
|
|
([Point(0.5, 0.5)], [[0], [0]]),
|
|
# point within envelope of first line but does not intersect
|
|
(Point(0, 0.5), []),
|
|
([Point(0, 0.5)], [[], []]),
|
|
# point at shared vertex between 2 lines and is covered by both
|
|
(Point(1, 1), [0, 1]),
|
|
([Point(1, 1)], [[0, 0], [0, 1]]),
|
|
# line intersects 3 lines, but is covered by only one
|
|
(shapely.linestrings([[1, 1], [2, 2]]), [1]),
|
|
([shapely.linestrings([[1, 1], [2, 2]])], [[0], [1]]),
|
|
# line intersects 2 lines, but is covered by neither
|
|
(shapely.linestrings([[1.5, 1.5], [2.5, 2.5]]), []),
|
|
([shapely.linestrings([[1.5, 1.5], [2.5, 2.5]])], [[], []]),
|
|
# box not covered by line (not valid geometric relation)
|
|
(box(0, 0, 1, 1), []),
|
|
([box(0, 0, 1, 1)], [[], []]),
|
|
# buffer intersects 2 lines but not within either (not valid geometric relation)
|
|
(shapely.buffer(Point(3, 3), 0.5), []),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[], []]),
|
|
# envelope of points overlaps lines but intersects none
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects, but both are not covered by line
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
# both points are covered by a line
|
|
(MultiPoint([[6.5, 6.5], [7, 7]]), [6]),
|
|
([MultiPoint([[6.5, 6.5], [7, 7]])], [[0], [6]]),
|
|
],
|
|
)
|
|
def test_query_covered_by_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query(geometry, predicate="covered_by"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point covered by polygon
|
|
(Point(0, 0.5), [0]),
|
|
([Point(0, 0.5)], [[0], [0]]),
|
|
(Point(0.5, 0), [0]),
|
|
([Point(0.5, 0)], [[0], [0]]),
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# midpoint between two polygons is covered by both
|
|
(Point(0.5, 0.5), [0, 1]),
|
|
([Point(0.5, 0.5)], [[0, 0], [0, 1]]),
|
|
# line intersects multiple polygons but is not covered by any
|
|
(shapely.linestrings([[0, 0], [2, 2]]), []),
|
|
([shapely.linestrings([[0, 0], [2, 2]])], [[], []]),
|
|
# line intersects multiple polygons but is covered by only one
|
|
(shapely.linestrings([[1.5, 1.5], [2.5, 2.5]]), [2]),
|
|
([shapely.linestrings([[1.5, 1.5], [2.5, 2.5]])], [[0], [2]]),
|
|
# box overlaps envelope of 2 polygons but not covered by either
|
|
(box(0, 0, 1, 1), []),
|
|
([box(0, 0, 1, 1)], [[], []]),
|
|
# box covered by polygon
|
|
(box(0, 0, 0.5, 0.5), [0]),
|
|
([box(0, 0, 0.5, 0.5)], [[0], [0]]),
|
|
# larger box intersects 3 polygons but not covered by any
|
|
(box(0, 0, 1.5, 1.5), []),
|
|
([box(0, 0, 1.5, 1.5)], [[], []]),
|
|
# buffer intersects 3 polygons but only within one
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), [3]),
|
|
([shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)], [[0], [3]]),
|
|
# larger buffer overlaps 6 polygons (touches midpoints) but within none
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), []),
|
|
([shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)], [[], []]),
|
|
# envelope of points overlaps polygons, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint within polygon
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
# both points in multipoint within polygon
|
|
(MultiPoint([[5.25, 5.5], [5.25, 5.0]]), [5]),
|
|
([MultiPoint([[5.25, 5.5], [5.25, 5.0]])], [[0], [5]]),
|
|
],
|
|
)
|
|
def test_query_covered_by_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query(geometry, predicate="covered_by"), expected)
|
|
|
|
|
|
### predicate == 'contains_properly'
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# points do not intersect
|
|
(Point(0.5, 0.5), []),
|
|
([Point(0.5, 0.5)], [[], []]),
|
|
# points intersect
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# line contains every point that is not on its first or last coordinate
|
|
# these are on the "exterior" of the line
|
|
(shapely.linestrings([[0, 0], [2, 2]]), [1]),
|
|
([shapely.linestrings([[0, 0], [2, 2]])], [[0], [1]]),
|
|
# slightly longer line contains multiple points
|
|
(shapely.linestrings([[0.5, 0.5], [2.5, 2.5]]), [1, 2]),
|
|
([shapely.linestrings([[0.5, 0.5], [2.5, 2.5]])], [[0, 0], [1, 2]]),
|
|
# line intersects and contains one point
|
|
(shapely.linestrings([[0, 2], [2, 0]]), [1]),
|
|
([shapely.linestrings([[0, 2], [2, 0]])], [[0], [1]]),
|
|
# box contains points (2 are at edges and not contained)
|
|
(box(3, 3, 6, 6), [4, 5]),
|
|
([box(3, 3, 6, 6)], [[0, 0], [4, 5]]),
|
|
# envelope of buffer contains more points than within buffer
|
|
# due to diagonal distance
|
|
(shapely.buffer(Point(3, 3), 1), [3]),
|
|
([shapely.buffer(Point(3, 3), 1)], [[0], [3]]),
|
|
# envelope of buffer with 1/2 distance between points should intersect
|
|
# same points as envelope
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [2, 3, 4]),
|
|
([shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)], [[0, 0, 0], [2, 3, 4]]),
|
|
# multipoints intersect
|
|
(MultiPoint([[5, 5], [7, 7]]), [5, 7]),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[0, 0], [5, 7]]),
|
|
# envelope of points contains points, but points do not intersect
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
# only one point of multipoint intersects
|
|
(MultiPoint([[5, 7], [7, 7]]), [7]),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[0], [7]]),
|
|
],
|
|
)
|
|
def test_query_contains_properly_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query(geometry, predicate="contains_properly"), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# None of the following conditions satisfy the relation for linestrings
|
|
# because they have no interior:
|
|
# "a contains b if no points of b lie in the exterior of a, and at least one
|
|
# point of the interior of b lies in the interior of a"
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
(shapely.linestrings([[0, 0], [1, 1]]), []),
|
|
([shapely.linestrings([[0, 0], [1, 1]])], [[], []]),
|
|
(shapely.linestrings([[0, 0], [2, 2]]), []),
|
|
([shapely.linestrings([[0, 0], [2, 2]])], [[], []]),
|
|
(shapely.linestrings([[0, 2], [2, 0]]), []),
|
|
([shapely.linestrings([[0, 2], [2, 0]])], [[], []]),
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
(MultiPoint([[5, 7], [7, 7]]), []),
|
|
([MultiPoint([[5, 7], [7, 7]])], [[], []]),
|
|
(MultiPoint([[5, 5], [6, 6]]), []),
|
|
([MultiPoint([[5, 5], [6, 6]])], [[], []]),
|
|
(box(0, 0, 1, 1), []),
|
|
([box(0, 0, 1, 1)], [[], []]),
|
|
(box(0, 0, 2, 2), []),
|
|
([box(0, 0, 2, 2)], [[], []]),
|
|
(shapely.buffer(Point(3, 3), 0.5), []),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[], []]),
|
|
],
|
|
)
|
|
def test_query_contains_properly_lines(line_tree, geometry, expected):
|
|
assert_array_equal(
|
|
line_tree.query(geometry, predicate="contains_properly"), expected
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# point does not contain any polygons (not valid relation)
|
|
(Point(0, 0), []),
|
|
([Point(0, 0)], [[], []]),
|
|
# line intersects multiple polygons but does not contain any (not valid
|
|
# relation)
|
|
(shapely.linestrings([[0, 0], [2, 2]]), []),
|
|
([shapely.linestrings([[0, 0], [2, 2]])], [[], []]),
|
|
# box overlaps envelope of 2 polygons but contains neither
|
|
(box(0, 0, 1, 1), []),
|
|
([box(0, 0, 1, 1)], [[], []]),
|
|
# larger box intersects 3 polygons but contains only one
|
|
(box(0, 0, 2, 2), [1]),
|
|
([box(0, 0, 2, 2)], [[0], [1]]),
|
|
# buffer overlaps 3 polygons but contains none
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), []),
|
|
([shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)], [[], []]),
|
|
# larger buffer overlaps 6 polygons (touches midpoints) but contains one
|
|
(shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG), [3]),
|
|
([shapely.buffer(Point(3, 3), 3 * HALF_UNIT_DIAG)], [[0], [3]]),
|
|
# envelope of points overlaps polygons, but points do not intersect
|
|
# (not valid relation)
|
|
(MultiPoint([[5, 7], [7, 5]]), []),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[], []]),
|
|
],
|
|
)
|
|
def test_query_contains_properly_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(
|
|
poly_tree.query(geometry, predicate="contains_properly"), expected
|
|
)
|
|
|
|
|
|
### predicate = 'dwithin'
|
|
|
|
|
|
@pytest.mark.skipif(geos_version >= (3, 10, 0), reason="GEOS >= 3.10")
|
|
@pytest.mark.parametrize(
|
|
"geometry", [Point(0, 0), [Point(0, 0)], None, [None], empty, [empty]]
|
|
)
|
|
def test_query_dwithin_geos_version(tree, geometry):
|
|
with pytest.raises(UnsupportedGEOSVersionError, match="requires GEOS >= 3.10"):
|
|
tree.query(geometry, predicate="dwithin", distance=1)
|
|
|
|
|
|
@pytest.mark.skipif(geos_version < (3, 10, 0), reason="GEOS < 3.10")
|
|
@pytest.mark.parametrize(
|
|
"geometry,distance,match",
|
|
[
|
|
(Point(0, 0), None, "distance parameter must be provided"),
|
|
([Point(0, 0)], None, "distance parameter must be provided"),
|
|
(Point(0, 0), "foo", "could not convert string to float"),
|
|
([Point(0, 0)], "foo", "could not convert string to float"),
|
|
([Point(0, 0)], ["foo"], "could not convert string to float"),
|
|
(Point(0, 0), [0, 1], "Could not broadcast distance to match geometry"),
|
|
([Point(0, 0)], [0, 1], "Could not broadcast distance to match geometry"),
|
|
(Point(0, 0), [[1.0]], "should be one dimensional"),
|
|
([Point(0, 0)], [[1.0]], "should be one dimensional"),
|
|
],
|
|
)
|
|
def test_query_dwithin_invalid_distance(tree, geometry, distance, match):
|
|
with pytest.raises(ValueError, match=match):
|
|
tree.query(geometry, predicate="dwithin", distance=distance)
|
|
|
|
|
|
@pytest.mark.skipif(geos_version < (3, 10, 0), reason="GEOS < 3.10")
|
|
@pytest.mark.parametrize(
|
|
"geometry,distance,expected",
|
|
[
|
|
(None, 1.0, []),
|
|
([None], 1.0, [[], []]),
|
|
(Point(0.25, 0.25), 0, []),
|
|
([Point(0.25, 0.25)], 0, [[], []]),
|
|
(Point(0.25, 0.25), -1, []),
|
|
([Point(0.25, 0.25)], -1, [[], []]),
|
|
(Point(0.25, 0.25), np.nan, []),
|
|
([Point(0.25, 0.25)], np.nan, [[], []]),
|
|
(Point(), 1, []),
|
|
([Point()], 1, [[], []]),
|
|
(Point(0.25, 0.25), 0.5, [0]),
|
|
([Point(0.25, 0.25)], 0.5, [[0], [0]]),
|
|
(Point(0.25, 0.25), 2.5, [0, 1, 2]),
|
|
([Point(0.25, 0.25)], 2.5, [[0, 0, 0], [0, 1, 2]]),
|
|
(Point(3, 3), 1.5, [2, 3, 4]),
|
|
([Point(3, 3)], 1.5, [[0, 0, 0], [2, 3, 4]]),
|
|
# 2 equidistant points in tree
|
|
(Point(0.5, 0.5), 0.75, [0, 1]),
|
|
([Point(0.5, 0.5)], 0.75, [[0, 0], [0, 1]]),
|
|
(
|
|
[None, Point(0.5, 0.5)],
|
|
0.75,
|
|
[
|
|
[
|
|
1,
|
|
1,
|
|
],
|
|
[0, 1],
|
|
],
|
|
),
|
|
(
|
|
[Point(0.5, 0.5), Point(0.25, 0.25)],
|
|
0.75,
|
|
[[0, 0, 1], [0, 1, 0]],
|
|
),
|
|
(
|
|
[Point(0, 0.2), Point(1.75, 1.75)],
|
|
[0.25, 2],
|
|
[[0, 1, 1, 1], [0, 1, 2, 3]],
|
|
),
|
|
# all points intersect box
|
|
(box(0, 0, 3, 3), 0, [0, 1, 2, 3]),
|
|
([box(0, 0, 3, 3)], 0, [[0, 0, 0, 0], [0, 1, 2, 3]]),
|
|
(box(0, 0, 3, 3), 0.25, [0, 1, 2, 3]),
|
|
([box(0, 0, 3, 3)], 0.25, [[0, 0, 0, 0], [0, 1, 2, 3]]),
|
|
# intersecting and nearby points
|
|
(box(1, 1, 2, 2), 1.5, [0, 1, 2, 3]),
|
|
([box(1, 1, 2, 2)], 1.5, [[0, 0, 0, 0], [0, 1, 2, 3]]),
|
|
# # return nearest point in tree for each point in multipoint
|
|
(MultiPoint([[0.25, 0.25], [1.5, 1.5]]), 0.75, [0, 1, 2]),
|
|
([MultiPoint([[0.25, 0.25], [1.5, 1.5]])], 0.75, [[0, 0, 0], [0, 1, 2]]),
|
|
# 2 equidistant points per point in multipoint
|
|
(
|
|
MultiPoint([[0.5, 0.5], [3.5, 3.5]]),
|
|
0.75,
|
|
[0, 1, 3, 4],
|
|
),
|
|
(
|
|
[MultiPoint([[0.5, 0.5], [3.5, 3.5]])],
|
|
0.75,
|
|
[[0, 0, 0, 0], [0, 1, 3, 4]],
|
|
),
|
|
],
|
|
)
|
|
def test_query_dwithin_points(tree, geometry, distance, expected):
|
|
assert_array_equal(
|
|
tree.query(geometry, predicate="dwithin", distance=distance), expected
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(geos_version < (3, 10, 0), reason="GEOS < 3.10")
|
|
@pytest.mark.parametrize(
|
|
"geometry,distance,expected",
|
|
[
|
|
(None, 1.0, []),
|
|
([None], 1.0, [[], []]),
|
|
(Point(0.5, 0.5), 0, [0]),
|
|
([Point(0.5, 0.5)], 0, [[0], [0]]),
|
|
(Point(0.5, 0.5), 1.0, [0, 1]),
|
|
([Point(0.5, 0.5)], 1.0, [[0, 0], [0, 1]]),
|
|
(Point(2, 2), 0.5, [1, 2]),
|
|
([Point(2, 2)], 0.5, [[0, 0], [1, 2]]),
|
|
(box(0, 0, 1, 1), 0.5, [0, 1]),
|
|
([box(0, 0, 1, 1)], 0.5, [[0, 0], [0, 1]]),
|
|
(box(0.5, 0.5, 1.5, 1.5), 0.5, [0, 1]),
|
|
([box(0.5, 0.5, 1.5, 1.5)], 0.5, [[0, 0], [0, 1]]),
|
|
# multipoints at endpoints of 2 lines each
|
|
(MultiPoint([[5, 5], [7, 7]]), 0.5, [4, 5, 6, 7]),
|
|
([MultiPoint([[5, 5], [7, 7]])], 0.5, [[0, 0, 0, 0], [4, 5, 6, 7]]),
|
|
# multipoints are equidistant from 2 lines
|
|
(MultiPoint([[5, 7], [7, 5]]), 1.5, [5, 6]),
|
|
([MultiPoint([[5, 7], [7, 5]])], 1.5, [[0, 0], [5, 6]]),
|
|
],
|
|
)
|
|
def test_query_dwithin_lines(line_tree, geometry, distance, expected):
|
|
assert_array_equal(
|
|
line_tree.query(geometry, predicate="dwithin", distance=distance),
|
|
expected,
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(geos_version < (3, 10, 0), reason="GEOS < 3.10")
|
|
@pytest.mark.parametrize(
|
|
"geometry,distance,expected",
|
|
[
|
|
(Point(0, 0), 0, [0]),
|
|
([Point(0, 0)], 0, [[0], [0]]),
|
|
(Point(0, 0), 0.5, [0]),
|
|
([Point(0, 0)], 0.5, [[0], [0]]),
|
|
(Point(0, 0), 1.5, [0, 1]),
|
|
([Point(0, 0)], 1.5, [[0, 0], [0, 1]]),
|
|
(Point(0.5, 0.5), 1, [0, 1]),
|
|
([Point(0.5, 0.5)], 1, [[0, 0], [0, 1]]),
|
|
(Point(0.5, 0.5), 0.5, [0, 1]),
|
|
([Point(0.5, 0.5)], 0.5, [[0, 0], [0, 1]]),
|
|
(box(0, 0, 1, 1), 0, [0, 1]),
|
|
([box(0, 0, 1, 1)], 0, [[0, 0], [0, 1]]),
|
|
(box(0, 0, 1, 1), 2, [0, 1, 2]),
|
|
([box(0, 0, 1, 1)], 2, [[0, 0, 0], [0, 1, 2]]),
|
|
(MultiPoint([[5, 5], [7, 7]]), 0.5, [5, 7]),
|
|
([MultiPoint([[5, 5], [7, 7]])], 0.5, [[0, 0], [5, 7]]),
|
|
(
|
|
MultiPoint([[5, 5], [7, 7]]),
|
|
2.5,
|
|
[3, 4, 5, 6, 7, 8, 9],
|
|
),
|
|
(
|
|
[MultiPoint([[5, 5], [7, 7]])],
|
|
2.5,
|
|
[[0, 0, 0, 0, 0, 0, 0], [3, 4, 5, 6, 7, 8, 9]],
|
|
),
|
|
],
|
|
)
|
|
def test_query_dwithin_polygons(poly_tree, geometry, distance, expected):
|
|
assert_array_equal(
|
|
poly_tree.query(geometry, predicate="dwithin", distance=distance),
|
|
expected,
|
|
)
|
|
|
|
|
|
### STRtree nearest
|
|
|
|
|
|
def test_nearest_empty_tree():
|
|
tree = STRtree([])
|
|
assert tree.nearest(point) is None
|
|
|
|
|
|
@pytest.mark.parametrize("geometry", ["I am not a geometry"])
|
|
def test_nearest_invalid_geom(tree, geometry):
|
|
with pytest.raises(TypeError):
|
|
tree.nearest(geometry)
|
|
|
|
|
|
@pytest.mark.parametrize("geometry", [None, [None], [Point(1, 1), None]])
|
|
def test_nearest_none(tree, geometry):
|
|
with pytest.raises(ValueError):
|
|
tree.nearest(geometry)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry", [empty_point, [empty_point], [Point(1, 1), empty_point]]
|
|
)
|
|
def test_nearest_empty(tree, geometry):
|
|
with pytest.raises(ValueError):
|
|
tree.nearest(geometry)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
(Point(0.25, 0.25), 0),
|
|
(Point(0.75, 0.75), 1),
|
|
(Point(1, 1), 1),
|
|
([Point(1, 1), Point(0, 0)], [1, 0]),
|
|
([Point(1, 1), Point(0.25, 1)], [1, 1]),
|
|
([Point(-10, -10), Point(100, 100)], [0, 9]),
|
|
(box(0.5, 0.5, 0.75, 0.75), 1),
|
|
(shapely.buffer(Point(2.5, 2.5), HALF_UNIT_DIAG), 2),
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), 3),
|
|
(MultiPoint([[5.5, 5], [7, 7]]), 7),
|
|
(MultiPoint([[5, 7], [7, 5]]), 6),
|
|
],
|
|
)
|
|
def test_nearest_points(tree, geometry, expected):
|
|
assert_array_equal(tree.nearest(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# 2 equidistant points in tree
|
|
(Point(0.5, 0.5), [0, 1]),
|
|
# multiple points in box
|
|
(box(0, 0, 3, 3), [0, 1, 2, 3]),
|
|
# return nearest point in tree for each point in multipoint
|
|
(MultiPoint([[5, 5], [7, 7]]), [5, 7]),
|
|
],
|
|
)
|
|
def test_nearest_points_equidistant(tree, geometry, expected):
|
|
# results are returned in order they are traversed when searching the tree,
|
|
# which can vary between GEOS versions, so we test that one of the valid
|
|
# results is present
|
|
result = tree.nearest(geometry)
|
|
assert result in expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
(Point(0.5, 0.5), 0),
|
|
(Point(1.5, 0.5), 0),
|
|
(shapely.box(0.5, 1.5, 1, 2), 1),
|
|
(shapely.linestrings([[0, 0.5], [1, 2.5]]), 0),
|
|
],
|
|
)
|
|
def test_nearest_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.nearest(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# at junction between 2 lines
|
|
(Point(2, 2), [1, 2]),
|
|
# contains one line, intersects with another
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
# overlaps 2 lines
|
|
(box(0.5, 0.5, 1.5, 1.5), [0, 1]),
|
|
# box overlaps 2 lines and intersects endpoints of 2 more
|
|
(box(3, 3, 5, 5), [2, 3, 4, 5]),
|
|
(shapely.buffer(Point(2.5, 2.5), HALF_UNIT_DIAG), [1, 2]),
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), [2, 3]),
|
|
# multipoints at endpoints of 2 lines each
|
|
(MultiPoint([[5, 5], [7, 7]]), [4, 5, 6, 7]),
|
|
# second point in multipoint at endpoints of 2 lines
|
|
(MultiPoint([[5.5, 5], [7, 7]]), [6, 7]),
|
|
# multipoints are equidistant from 2 lines
|
|
(MultiPoint([[5, 7], [7, 5]]), [5, 6]),
|
|
],
|
|
)
|
|
def test_nearest_lines_equidistant(line_tree, geometry, expected):
|
|
# results are returned in order they are traversed when searching the tree,
|
|
# which can vary between GEOS versions, so we test that one of the valid
|
|
# results is present
|
|
result = line_tree.nearest(geometry)
|
|
assert result in expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
(Point(0, 0), 0),
|
|
(Point(2, 2), 2),
|
|
(shapely.box(0, 5, 1, 6), 3),
|
|
(MultiPoint([[5, 7], [7, 5]]), 6),
|
|
],
|
|
)
|
|
def test_nearest_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.nearest(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
# 2 polygons in tree overlap point
|
|
(Point(0.5, 0.5), [0, 1]),
|
|
# box overlaps multiple polygons
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
(box(0.5, 0.5, 1.5, 1.5), [0, 1, 2]),
|
|
(box(3, 3, 5, 5), [3, 4, 5]),
|
|
(shapely.buffer(Point(2.5, 2.5), HALF_UNIT_DIAG), [2, 3]),
|
|
# completely overlaps one polygon, touches 2 others
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), [2, 3, 4]),
|
|
# each point in multi point intersects a polygon in tree
|
|
(MultiPoint([[5, 5], [7, 7]]), [5, 7]),
|
|
(MultiPoint([[5.5, 5], [7, 7]]), [5, 7]),
|
|
],
|
|
)
|
|
def test_nearest_polygons_equidistant(poly_tree, geometry, expected):
|
|
# results are returned in order they are traversed when searching the tree,
|
|
# which can vary between GEOS versions, so we test that one of the valid
|
|
# results is present
|
|
result = poly_tree.nearest(geometry)
|
|
assert result in expected
|
|
|
|
|
|
def test_query_nearest_empty_tree():
|
|
tree = STRtree([])
|
|
assert_array_equal(tree.query_nearest(point), [])
|
|
assert_array_equal(tree.query_nearest([point]), [[], []])
|
|
|
|
|
|
@pytest.mark.parametrize("geometry", ["I am not a geometry", ["still not a geometry"]])
|
|
def test_query_nearest_invalid_geom(tree, geometry):
|
|
with pytest.raises(TypeError):
|
|
tree.query_nearest(geometry)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,return_distance,expected",
|
|
[
|
|
(None, False, []),
|
|
([None], False, [[], []]),
|
|
(None, True, ([], [])),
|
|
([None], True, ([[], []], [])),
|
|
],
|
|
)
|
|
def test_query_nearest_none(tree, geometry, return_distance, expected):
|
|
if return_distance:
|
|
index, distance = tree.query_nearest(geometry, return_distance=True)
|
|
assert_array_equal(index, expected[0])
|
|
assert_array_equal(distance, expected[1])
|
|
|
|
else:
|
|
assert_array_equal(tree.query_nearest(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[(empty, []), ([empty], [[], []]), ([empty, point], [[1, 1], [2, 3]])],
|
|
)
|
|
def test_query_nearest_empty_geom(tree, geometry, expected):
|
|
assert_array_equal(tree.query_nearest(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
(Point(0.25, 0.25), [0]),
|
|
([Point(0.25, 0.25)], [[0], [0]]),
|
|
(Point(0.75, 0.75), [1]),
|
|
([Point(0.75, 0.75)], [[0], [1]]),
|
|
(Point(1, 1), [1]),
|
|
([Point(1, 1)], [[0], [1]]),
|
|
# 2 equidistant points in tree
|
|
(Point(0.5, 0.5), [0, 1]),
|
|
([Point(0.5, 0.5)], [[0, 0], [0, 1]]),
|
|
([Point(1, 1), Point(0, 0)], [[0, 1], [1, 0]]),
|
|
([Point(1, 1), Point(0.25, 1)], [[0, 1], [1, 1]]),
|
|
([Point(-10, -10), Point(100, 100)], [[0, 1], [0, 9]]),
|
|
(box(0.5, 0.5, 0.75, 0.75), [1]),
|
|
([box(0.5, 0.5, 0.75, 0.75)], [[0], [1]]),
|
|
# multiple points in box
|
|
(box(0, 0, 3, 3), [0, 1, 2, 3]),
|
|
([box(0, 0, 3, 3)], [[0, 0, 0, 0], [0, 1, 2, 3]]),
|
|
(shapely.buffer(Point(2.5, 2.5), 1), [2, 3]),
|
|
([shapely.buffer(Point(2.5, 2.5), 1)], [[0, 0], [2, 3]]),
|
|
(shapely.buffer(Point(3, 3), 0.5), [3]),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[0], [3]]),
|
|
(MultiPoint([[5.5, 5], [7, 7]]), [7]),
|
|
([MultiPoint([[5.5, 5], [7, 7]])], [[0], [7]]),
|
|
(MultiPoint([[5, 7], [7, 5]]), [6]),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[0], [6]]),
|
|
# return nearest point in tree for each point in multipoint
|
|
(MultiPoint([[5, 5], [7, 7]]), [5, 7]),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[0, 0], [5, 7]]),
|
|
# 2 equidistant points per point in multipoint
|
|
(MultiPoint([[0.5, 0.5], [3.5, 3.5]]), [0, 1, 3, 4]),
|
|
([MultiPoint([[0.5, 0.5], [3.5, 3.5]])], [[0, 0, 0, 0], [0, 1, 3, 4]]),
|
|
],
|
|
)
|
|
def test_query_nearest_points(tree, geometry, expected):
|
|
assert_array_equal(tree.query_nearest(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
(Point(0.5, 0.5), [0]),
|
|
([Point(0.5, 0.5)], [[0], [0]]),
|
|
# at junction between 2 lines, will return both
|
|
(Point(2, 2), [1, 2]),
|
|
([Point(2, 2)], [[0, 0], [1, 2]]),
|
|
# contains one line, intersects with another
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
([box(0, 0, 1, 1)], [[0, 0], [0, 1]]),
|
|
# overlaps 2 lines
|
|
(box(0.5, 0.5, 1.5, 1.5), [0, 1]),
|
|
([box(0.5, 0.5, 1.5, 1.5)], [[0, 0], [0, 1]]),
|
|
# second box overlaps 2 lines and intersects endpoints of 2 more
|
|
([box(0, 0, 0.5, 0.5), box(3, 3, 5, 5)], [[0, 1, 1, 1, 1], [0, 2, 3, 4, 5]]),
|
|
(shapely.buffer(Point(2.5, 2.5), 1), [1, 2, 3]),
|
|
([shapely.buffer(Point(2.5, 2.5), 1)], [[0, 0, 0], [1, 2, 3]]),
|
|
(shapely.buffer(Point(3, 3), 0.5), [2, 3]),
|
|
([shapely.buffer(Point(3, 3), 0.5)], [[0, 0], [2, 3]]),
|
|
# multipoints at endpoints of 2 lines each
|
|
(MultiPoint([[5, 5], [7, 7]]), [4, 5, 6, 7]),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[0, 0, 0, 0], [4, 5, 6, 7]]),
|
|
# second point in multipoint at endpoints of 2 lines
|
|
(MultiPoint([[5.5, 5], [7, 7]]), [6, 7]),
|
|
([MultiPoint([[5.5, 5], [7, 7]])], [[0, 0], [6, 7]]),
|
|
# multipoints are equidistant from 2 lines
|
|
(MultiPoint([[5, 7], [7, 5]]), [5, 6]),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[0, 0], [5, 6]]),
|
|
],
|
|
)
|
|
def test_query_nearest_lines(line_tree, geometry, expected):
|
|
assert_array_equal(line_tree.query_nearest(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
(Point(0, 0), [0]),
|
|
([Point(0, 0)], [[0], [0]]),
|
|
(Point(2, 2), [2]),
|
|
([Point(2, 2)], [[0], [2]]),
|
|
# 2 polygons in tree overlap point
|
|
(Point(0.5, 0.5), [0, 1]),
|
|
([Point(0.5, 0.5)], [[0, 0], [0, 1]]),
|
|
# box overlaps multiple polygons
|
|
(box(0, 0, 1, 1), [0, 1]),
|
|
([box(0, 0, 1, 1)], [[0, 0], [0, 1]]),
|
|
(box(0.5, 0.5, 1.5, 1.5), [0, 1, 2]),
|
|
([box(0.5, 0.5, 1.5, 1.5)], [[0, 0, 0], [0, 1, 2]]),
|
|
([box(0, 0, 1, 1), box(3, 3, 5, 5)], [[0, 0, 1, 1, 1], [0, 1, 3, 4, 5]]),
|
|
(shapely.buffer(Point(2.5, 2.5), HALF_UNIT_DIAG), [2, 3]),
|
|
([shapely.buffer(Point(2.5, 2.5), HALF_UNIT_DIAG)], [[0, 0], [2, 3]]),
|
|
# completely overlaps one polygon, touches 2 others
|
|
(shapely.buffer(Point(3, 3), HALF_UNIT_DIAG), [2, 3, 4]),
|
|
([shapely.buffer(Point(3, 3), HALF_UNIT_DIAG)], [[0, 0, 0], [2, 3, 4]]),
|
|
# each point in multi point intersects a polygon in tree
|
|
(MultiPoint([[5, 5], [7, 7]]), [5, 7]),
|
|
([MultiPoint([[5, 5], [7, 7]])], [[0, 0], [5, 7]]),
|
|
(MultiPoint([[5.5, 5], [7, 7]]), [5, 7]),
|
|
([MultiPoint([[5.5, 5], [7, 7]])], [[0, 0], [5, 7]]),
|
|
(MultiPoint([[5, 7], [7, 5]]), [6]),
|
|
([MultiPoint([[5, 7], [7, 5]])], [[0], [6]]),
|
|
],
|
|
)
|
|
def test_query_nearest_polygons(poly_tree, geometry, expected):
|
|
assert_array_equal(poly_tree.query_nearest(geometry), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,max_distance,expected",
|
|
[
|
|
# using unset max_distance should return all nearest
|
|
(Point(0.5, 0.5), None, [0, 1]),
|
|
([Point(0.5, 0.5)], None, [[0, 0], [0, 1]]),
|
|
# using large max_distance should return all nearest
|
|
(Point(0.5, 0.5), 10, [0, 1]),
|
|
([Point(0.5, 0.5)], 10, [[0, 0], [0, 1]]),
|
|
# using small max_distance should return no results
|
|
(Point(0.5, 0.5), 0.1, []),
|
|
([Point(0.5, 0.5)], 0.1, [[], []]),
|
|
# using small max_distance should only return results in that distance
|
|
([Point(0.5, 0.5), Point(0, 0)], 0.1, [[1], [0]]),
|
|
],
|
|
)
|
|
def test_query_nearest_max_distance(tree, geometry, max_distance, expected):
|
|
assert_array_equal(
|
|
tree.query_nearest(geometry, max_distance=max_distance), expected
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,max_distance",
|
|
[
|
|
(Point(0.5, 0.5), 0),
|
|
([Point(0.5, 0.5)], 0),
|
|
(Point(0.5, 0.5), -1),
|
|
([Point(0.5, 0.5)], -1),
|
|
],
|
|
)
|
|
def test_query_nearest_invalid_max_distance(tree, geometry, max_distance):
|
|
with pytest.raises(ValueError, match="max_distance must be greater than 0"):
|
|
tree.query_nearest(geometry, max_distance=max_distance)
|
|
|
|
|
|
def test_query_nearest_nonscalar_max_distance(tree):
|
|
with pytest.raises(ValueError, match="parameter only accepts scalar values"):
|
|
tree.query_nearest(Point(0.5, 0.5), max_distance=[1])
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
(Point(0, 0), ([0], [0.0])),
|
|
([Point(0, 0)], ([[0], [0]], [0.0])),
|
|
(Point(0.5, 0.5), ([0, 1], [0.7071, 0.7071])),
|
|
([Point(0.5, 0.5)], ([[0, 0], [0, 1]], [0.7071, 0.7071])),
|
|
(box(0, 0, 1, 1), ([0, 1], [0.0, 0.0])),
|
|
([box(0, 0, 1, 1)], ([[0, 0], [0, 1]], [0.0, 0.0])),
|
|
],
|
|
)
|
|
def test_query_nearest_return_distance(tree, geometry, expected):
|
|
expected_indices, expected_dist = expected
|
|
|
|
actual_indices, actual_dist = tree.query_nearest(geometry, return_distance=True)
|
|
|
|
assert_array_equal(actual_indices, expected_indices)
|
|
assert_array_equal(np.round(actual_dist, 4), expected_dist)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,exclusive,expected",
|
|
[
|
|
(Point(1, 1), False, [1]),
|
|
([Point(1, 1)], False, [[0], [1]]),
|
|
(Point(1, 1), True, [0, 2]),
|
|
([Point(1, 1)], True, [[0, 0], [0, 2]]),
|
|
([Point(1, 1), Point(2, 2)], True, [[0, 0, 1, 1], [0, 2, 1, 3]]),
|
|
],
|
|
)
|
|
def test_query_nearest_exclusive(tree, geometry, exclusive, expected):
|
|
assert_array_equal(tree.query_nearest(geometry, exclusive=exclusive), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,expected",
|
|
[
|
|
(Point(1, 1), []),
|
|
([Point(1, 1)], [[], []]),
|
|
],
|
|
)
|
|
def test_query_nearest_exclusive_no_results(tree, geometry, expected):
|
|
tree = STRtree([Point(1, 1)])
|
|
assert_array_equal(tree.query_nearest(geometry, exclusive=True), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,exclusive",
|
|
[
|
|
(Point(1, 1), "invalid"),
|
|
# non-scalar exclusive parameter not allowed
|
|
(Point(1, 1), ["also invalid"]),
|
|
([Point(1, 1)], []),
|
|
([Point(1, 1)], [False]),
|
|
],
|
|
)
|
|
def test_query_nearest_invalid_exclusive(tree, geometry, exclusive):
|
|
with pytest.raises(ValueError):
|
|
tree.query_nearest(geometry, exclusive=exclusive)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry,all_matches",
|
|
[
|
|
(Point(1, 1), "invalid"),
|
|
# non-scalar all_matches parameter not allowed
|
|
(Point(1, 1), ["also invalid"]),
|
|
([Point(1, 1)], []),
|
|
([Point(1, 1)], [False]),
|
|
],
|
|
)
|
|
def test_query_nearest_invalid_all_matches(tree, geometry, all_matches):
|
|
with pytest.raises(ValueError):
|
|
tree.query_nearest(geometry, all_matches=all_matches)
|
|
|
|
|
|
def test_query_nearest_all_matches(tree):
|
|
point = Point(0.5, 0.5)
|
|
assert_array_equal(tree.query_nearest(point, all_matches=True), [0, 1])
|
|
|
|
indices = tree.query_nearest(point, all_matches=False)
|
|
# result is dependent on tree traversal order; may vary across test runs
|
|
assert np.array_equal(indices, [0]) or np.array_equal(indices, [1])
|
|
|
|
|
|
def test_strtree_threaded_query():
|
|
## Create data
|
|
polygons = shapely.polygons(np.random.randn(1000, 3, 2))
|
|
# needs to be big enough to trigger the segfault
|
|
N = 100_000
|
|
points = shapely.points(4 * np.random.random(N) - 2, 4 * np.random.random(N) - 2)
|
|
|
|
## Slice parts of the arrays -> 4x4 => 16 combinations
|
|
n = int(len(polygons) / 4)
|
|
polygons_parts = [
|
|
polygons[:n],
|
|
polygons[n : 2 * n],
|
|
polygons[2 * n : 3 * n],
|
|
polygons[3 * n :],
|
|
]
|
|
n = int(len(points) / 4)
|
|
points_parts = [
|
|
points[:n],
|
|
points[n : 2 * n],
|
|
points[2 * n : 3 * n],
|
|
points[3 * n :],
|
|
]
|
|
|
|
## Creating the trees in advance
|
|
trees = []
|
|
for i in range(4):
|
|
left = points_parts[i]
|
|
tree = STRtree(left)
|
|
trees.append(tree)
|
|
|
|
## The function querying the trees in parallel
|
|
|
|
def thread_func(idxs):
|
|
i, j = idxs
|
|
tree = trees[i]
|
|
right = polygons_parts[j]
|
|
return tree.query(right, predicate="contains")
|
|
|
|
with ThreadPoolExecutor() as pool:
|
|
list(pool.map(thread_func, itertools.product(range(4), range(4))))
|