| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964 |
- 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))))
|