From 65f496c300e1af91b9dd7c736b85220e9b2e1b9f Mon Sep 17 00:00:00 2001 From: ncclementi Date: Fri, 20 Oct 2023 13:32:00 -0400 Subject: [PATCH] feat(duckdb): add initial support for geospatial functions --- ibis/backends/duckdb/registry.py | 34 +++ ibis/backends/duckdb/tests/conftest.py | 31 +++ ibis/backends/duckdb/tests/test_geospatial.py | 228 ++++++++++++++++++ poetry.lock | 133 +++++----- pyproject.toml | 3 + 5 files changed, 365 insertions(+), 64 deletions(-) create mode 100644 ibis/backends/duckdb/tests/test_geospatial.py diff --git a/ibis/backends/duckdb/registry.py b/ibis/backends/duckdb/registry.py index f2b7b3c54276..9b46d81b3160 100644 --- a/ibis/backends/duckdb/registry.py +++ b/ibis/backends/duckdb/registry.py @@ -498,6 +498,40 @@ def _to_json_collection(t, op): ops.ToJSONArray: _to_json_collection, ops.ArrayFlatten: unary(sa.func.flatten), ops.IntegerRange: fixed_arity(sa.func.range, 3), + # geospatial + ops.GeoPoint: fixed_arity(sa.func.ST_Point, 2), + ops.GeoAsText: unary(sa.func.ST_AsText), + ops.GeoArea: unary(sa.func.ST_Area), + # ops.GeoBuffer: fixed_arity(sa.func.ST_Buffer, 2), duckdb sup 2 or 3? + ops.GeoCentroid: unary(sa.func.ST_Centroid), + ops.GeoContains: fixed_arity(sa.func.ST_Contains, 2), + # ops.GeoContainsProperly: fixed_arity(sa.func.ST_Contains, 2), ? + ops.GeoCovers: fixed_arity(sa.func.ST_Covers, 2), + ops.GeoCoveredBy: fixed_arity(sa.func.ST_CoveredBy, 2), + ops.GeoCrosses: fixed_arity(sa.func.ST_Crosses, 2), + ops.GeoDifference: fixed_arity(sa.func.ST_Difference, 2), + ops.GeoDisjoint: fixed_arity(sa.func.ST_Disjoint, 2), + ops.GeoDistance: fixed_arity(sa.func.ST_Distance, 2), + # ops.GeoDWithin: fixed_arity(sa.func.ST_DWithin, 3), see # https://github.com/ibis-project/ibis/issues/7427 + ops.GeoEndPoint: unary(sa.func.ST_EndPoint), + ops.GeoEnvelope: unary(sa.func.ST_Envelope), + ops.GeoEquals: fixed_arity(sa.func.ST_Equals, 2), + ops.GeoGeometryType: unary(sa.func.ST_GeometryType), + ops.GeoIntersection: fixed_arity(sa.func.ST_Intersection, 2), + ops.GeoIntersects: fixed_arity(sa.func.ST_Intersects, 2), + ops.GeoIsValid: unary(sa.func.ST_IsValid), + ops.GeoLength: unary(sa.func.ST_Length), + ops.GeoNPoints: unary(sa.func.ST_NPoints), + ops.GeoOverlaps: fixed_arity(sa.func.ST_Overlaps, 2), + # ops.GeoSimplify fixed_arity(sa.func.ST_Simplify, 3) # ? ST_Simplify and ST_SimplifyPreserveTopology + ops.GeoStartPoint: unary(sa.func.ST_StartPoint), + ops.GeoTouches: fixed_arity(sa.func.ST_Touches, 2), + # ops.GeoTransform fixed_arity(sa.func.ST_Transform, 2) # ? ST_Transform(GEOMETRY, VARCHAR, VARCHAR) + # ops.GeoUnaryUnion unary(sa.func.ST_Union) # ? ST_Union_Agg + ops.GeoUnion: fixed_arity(sa.func.ST_Union, 2), + ops.GeoWithin: fixed_arity(sa.func.ST_Within, 2), + ops.GeoX: unary(sa.func.ST_X), + ops.GeoY: unary(sa.func.ST_Y), } ) diff --git a/ibis/backends/duckdb/tests/conftest.py b/ibis/backends/duckdb/tests/conftest.py index bc62b94fe034..2c96dd8ca852 100644 --- a/ibis/backends/duckdb/tests/conftest.py +++ b/ibis/backends/duckdb/tests/conftest.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING +import geopandas as gpd import pytest import ibis @@ -14,6 +15,19 @@ from ibis.backends.base import BaseBackend +TEST_TABLES_GEO = { + "zones": ibis.schema( + { + "zone": "string", + "LocationID": "int32", + "borough": "string", + "geom": "geometry", + "x_cent": "float32", + "y_cent": "float32", + } + ) +} + class TestConf(BackendTest): supports_map = True @@ -30,6 +44,7 @@ def preload(self): @property def ddl_script(self) -> Iterator[str]: parquet_dir = self.data_dir / "parquet" + geojson_dir = self.data_dir / "geojson" for table in TEST_TABLES: yield ( f""" @@ -37,6 +52,15 @@ def ddl_script(self) -> Iterator[str]: SELECT * FROM read_parquet('{parquet_dir / f'{table}.parquet'}') """ ) + for table in TEST_TABLES_GEO: + yield ( + f""" + INSTALL spatial; + LOAD spatial; + CREATE OR REPLACE TABLE {table} AS + SELECT * FROM st_read('{geojson_dir / f'{table}.geojson'}') + """ + ) yield from super().ddl_script @staticmethod @@ -55,3 +79,10 @@ def load_tpch(self) -> None: @pytest.fixture(scope="session") def con(data_dir, tmp_path_factory, worker_id): return TestConf.load_data(data_dir, tmp_path_factory, worker_id).connection + + +@pytest.fixture(scope="session") +def zones_gdf(data_dir): + # pending merge https://github.com/ibis-project/testing-data/pull/5 + gdf = gpd.read_file(data_dir / "geojson" / "zones.geojson") + return gdf diff --git a/ibis/backends/duckdb/tests/test_geospatial.py b/ibis/backends/duckdb/tests/test_geospatial.py new file mode 100644 index 000000000000..d01494d6e442 --- /dev/null +++ b/ibis/backends/duckdb/tests/test_geospatial.py @@ -0,0 +1,228 @@ +from __future__ import annotations + +import pyarrow as pa +import pytest +import sqlalchemy as sa + +# import ibis + +# how to load geo extension here, do I need to create a con +# to_sql = ibis.duckdb.compile + + +@pytest.mark.xfail(raises=sa.exc.ProgrammingError, reason="ST_AsEWKB") +def test_geospatial_point(con): + zones = con.tables.zones + coord = zones.x_cent.point(zones.y_cent).name("coord") + coord.to_pandas() + + +def test_geospatial_as_text(con): + # there is no geopandas available to convert to text + # do snapshot test + zones = con.tables.zones + at = zones.geom.as_text().name("as_text") + at.to_pandas() + # ASK HERE + # t = ibis.table([("geom", "geometry")], name="t") + # expr = t.geom.as_text().name("tmp") + # snapshot.assert_match(to_sql(expr), "out.sql") + + +def test_geospatial_area(con, zones_gdf): + zones = con.tables.zones + gp_area = zones_gdf.geometry.area + area = zones.geom.area().name("area") + + assert all(area.to_pandas() == gp_area) + + +# def test_geospatial_buffer() +# GeoBUffer in alchemy supports 2 arguments, but duckdb is a unary? +# actually docs are wrong, it takes 2 or 3 args +# looks like we have fixed_arity(sa.func.ST_Buffer, 2) + + +@pytest.mark.xfail(raises=sa.exc.ProgrammingError, reason="ST_AsEWKB") +def test_geospatial_centroid(con): + zones = con.tables.zones + cen = zones.geom.centroid().name("centroid") + cen.to_pandas() + + +## ???? +def test_geospatial_contains(con, zones_gdf): + zones = con.tables.zones + # using same geom because of issue to generate geojason + # with 2 geom cols on duckdb + gp_cont = zones_gdf.geometry.contains(zones_gdf.geometry) + cont = zones.geom.contains(zones.geom).name("contains") + cont.to_pandas() + assert all(cont.to_pandas() == gp_cont) + + +def test_geospatial_covers(con): + # Note that ST_Covers(A, B) = ST_CoveredBy(B, A) + zones = con.tables.zones + # using same geom because of issue to generate geojason with 2 geom cols on duckdb + covers = zones.geom.covers(zones.geom).name("covers") + covers.to_pandas() + + +def test_geospatial_covered_by(con): + # Note that ST_CoveredBy(A, B) = ST_Covers(B,A) + zones = con.tables.zones + # using same geom because of issue to generate geojason with 2 geom cols on duckdb + coverby = zones.geom.covered_by(zones.geom).name("coverby") + coverby.to_pandas() + + +def test_geospatial_crosses(con): + zones = con.tables.zones + # using same geom because of issue to generate geojason with 2 geom cols on duckdb + crosses = zones.geom.crosses(zones.geom).name("crosses") + crosses.to_pandas() + + +@pytest.mark.xfail(raises=sa.exc.ProgrammingError, reason="ST_AsEWKB") +def test_geospatial_diff(con): + zones = con.tables.zones + # using same geom because of issue to generate geojason with 2 geom cols on duckdb + diff = zones.geom.difference(zones.geom).name("diff") + diff.to_pandas() + + +def test_geospatial_disjoint(con): + zones = con.tables.zones + # using same geom because of issue to generate geojason with 2 geom cols on duckdb + disj = zones.geom.disjoint(zones.geom).name("disj") + disj.to_pandas() + + +def test_geospatial_distance(con): + zones = con.tables.zones + # using same geom because of issue to generate geojason with 2 geom cols on duckdb + dist = zones.geom.distance(zones.geom).name("dist") + dist.to_pandas() + + +# #try this one after I merge master. +# def test_geospatial_dwithin(con): +# zones = con.tables.zones +# #using same geom because of issue to generate geojason with 2 geom cols on duckdb +# dwithin = zones.geom.d_within(zones.geom, 3.0).name("dwithin") +# dwithin.to_pandas() + + +@pytest.mark.xfail(raises=sa.exc.ProgrammingError, reason="ST_AsEWKB") +def test_geospatial_end_point(con): + zones = con.tables.zones + epoint = zones.geom.end_point().name("end_point") + epoint.to_pandas() + + +@pytest.mark.xfail(raises=sa.exc.ProgrammingError, reason="ST_AsEWKB") +def test_geospatial_envelope(con): + zones = con.tables.zones + envelope = zones.geom.envelope().name("envelope") + envelope.to_pandas() + + +def test_geospatial_equals(con): + zones = con.tables.zones + eqs = zones.geom.geo_equals(zones.geom).name("geo_eq") + eqs.to_pandas() + + +@pytest.mark.xfail(raises=pa.lib.ArrowTypeError) +def test_geospatial_geometry_type(con): + zones = con.tables.zones + geom_type = zones.geom.geometry_type().name("geom_type") + geom_type.to_pandas() # breaks here + + +@pytest.mark.xfail(raises=sa.exc.ProgrammingError, reason="ST_AsEWKB") +def test_geospatial_intersection(con): + zones = con.tables.zones + intersection = zones.geom.intersection(zones.geom).name("intersection") + intersection.to_pandas() + + +def test_geospatial_intersects(con): + zones = con.tables.zones + intersects = zones.geom.intersects(zones.geom).name("intersects") + intersects.to_pandas() + + +def test_geospatial_is_valid(con): + zones = con.tables.zones + is_valid = zones.geom.is_valid().name("is_valid") + is_valid.to_pandas() + + +def test_geospatial_length(con): + zones = con.tables.zones + length = zones.geom.length().name("length") + length.to_pandas() + + +def test_geospatial_npoints(con): + zones = con.tables.zones + npoints = zones.geom.n_points() + npoints.to_pandas() + + +def test_geospatial_overlaps(con): + zones = con.tables.zones + overlaps = zones.geom.overlaps(zones.geom).name("overlaps") + overlaps.to_pandas() + + +@pytest.mark.xfail(raises=sa.exc.ProgrammingError, reason="ST_AsEWKB") +def test_geospatial_start_point(con): + zones = con.tables.zones + start_point = zones.geom.start_point().name("start_point") + start_point.to_pandas() + + +def test_geospatial_touches(con): + zones = con.tables.zones + touches = zones.geom.touches(zones.geom).name("touches") + touches.to_pandas() + + +@pytest.mark.xfail(raises=sa.exc.ProgrammingError, reason="ST_AsEWKB") +def test_geospatial_union(con): + zones = con.tables.zones + union = zones.geom.union(zones.geom).name("union") + union.to_pandas() + + +def test_geospatial_within(con): + zones = con.tables.zones + within = zones.geom.within(zones.geom).name("within") + within.to_pandas() + + +def test_geospatial_x(con): + # we need to have a point type column to test this, need to figure geojson problem + # duckdb.duckdb.InvalidInputException: Invalid Input Error: ST_X/ST_Y only supports POINT geometries + # try to create it with geojson only accepts one geometry column + zones = con.tables.zones + # work around: get centroids to get Point() like things + cen = zones.geom.centroid().name("centroid") + # Get x coord + x = cen.x().name("x") + x.to_pandas() + + +def test_geospatial_y(con): + # we need to have a point type column to test this, need to figure geojson problem + # duckdb.duckdb.InvalidInputException: Invalid Input Error: ST_X/ST_Y only supports POINT geometries + # try to create it with geojson only accepts one geometry column + zones = con.tables.zones + # work around: get centroids to get Point() like things + cen = zones.geom.centroid().name("centroid") + # Get x coord + y = cen.y().name("y") + y.to_pandas() diff --git a/poetry.lock b/poetry.lock index 3377159d12cf..ca8bb641e638 100644 --- a/poetry.lock +++ b/poetry.lock @@ -686,7 +686,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "click-plugins" version = "1.1.1" description = "An extension module for click to enable registering CLI commands via setuptools entry-points." -optional = true +optional = false python-versions = "*" files = [ {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, @@ -809,7 +809,7 @@ sqlalchemy = ["sqlalchemy (>1.3.21,<2.0)"] name = "cligj" version = "0.7.2" description = "Click params for commmand line interfaces to GeoJSON" -optional = true +optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <4" files = [ {file = "cligj-0.7.2-py3-none-any.whl", hash = "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df"}, @@ -1364,7 +1364,7 @@ typing = ["typing-extensions (>=4.8)"] name = "fiona" version = "1.9.5" description = "Fiona reads and writes spatial data files" -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "fiona-1.9.5-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:5f40a40529ecfca5294260316cf987a0420c77a2f0cf0849f529d1afbccd093e"}, @@ -1625,7 +1625,7 @@ shapely = ["Shapely (>=1.7)"] name = "geopandas" version = "0.14.1" description = "Geographic pandas extensions" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "geopandas-0.14.1-py3-none-any.whl", hash = "sha256:ed5a7cae7874bfc3238fb05e0501cc1760e1b7b11e5b76ecad29da644ca305da"}, @@ -1947,63 +1947,68 @@ description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63"}, - {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e"}, - {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846"}, - {file = "greenlet-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9"}, - {file = "greenlet-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234"}, - {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884"}, - {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94"}, - {file = "greenlet-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c"}, - {file = "greenlet-3.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5"}, - {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d"}, - {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445"}, - {file = "greenlet-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de"}, - {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166"}, - {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36"}, - {file = "greenlet-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1"}, - {file = "greenlet-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8"}, - {file = "greenlet-3.0.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9"}, - {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e"}, - {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a"}, - {file = "greenlet-3.0.1-cp38-cp38-win32.whl", hash = "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd"}, - {file = "greenlet-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6"}, - {file = "greenlet-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d"}, - {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8"}, - {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546"}, - {file = "greenlet-3.0.1-cp39-cp39-win32.whl", hash = "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57"}, - {file = "greenlet-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619"}, - {file = "greenlet-3.0.1.tar.gz", hash = "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b"}, + {file = "greenlet-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e09dea87cc91aea5500262993cbd484b41edf8af74f976719dd83fe724644cd6"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47932c434a3c8d3c86d865443fadc1fbf574e9b11d6650b656e602b1797908a"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a68d670c8f89ff65c82b936275369e532772eebc027c3be68c6b87ad05ca695"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ad562a104cd41e9d4644f46ea37167b93190c6d5e4048fcc4b80d34ecb278f"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02a807b2a58d5cdebb07050efe3d7deaf915468d112dfcf5e426d0564aa3aa4a"}, + {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b1660a15a446206c8545edc292ab5c48b91ff732f91b3d3b30d9a915d5ec4779"}, + {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:813720bd57e193391dfe26f4871186cf460848b83df7e23e6bef698a7624b4c9"}, + {file = "greenlet-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:aa15a2ec737cb609ed48902b45c5e4ff6044feb5dcdfcf6fa8482379190330d7"}, + {file = "greenlet-3.0.0-cp310-universal2-macosx_11_0_x86_64.whl", hash = "sha256:7709fd7bb02b31908dc8fd35bfd0a29fc24681d5cc9ac1d64ad07f8d2b7db62f"}, + {file = "greenlet-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6512592cc49b2c6d9b19fbaa0312124cd4c4c8a90d28473f86f92685cc5fef8e"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:871b0a8835f9e9d461b7fdaa1b57e3492dd45398e87324c047469ce2fc9f516c"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b505fcfc26f4148551826a96f7317e02c400665fa0883fe505d4fcaab1dabfdd"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96d9ea57292f636ec851a9bb961a5cc0f9976900e16e5d5647f19aa36ba6366b"}, + {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b72b802496cccbd9b31acea72b6f87e7771ccfd7f7927437d592e5c92ed703c"}, + {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362"}, + {file = "greenlet-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:37f60b3a42d8b5499be910d1267b24355c495064f271cfe74bf28b17b099133c"}, + {file = "greenlet-3.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383"}, + {file = "greenlet-3.0.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73b2f1922a39d5d59cc0e597987300df3396b148a9bd10b76a058a2f2772fc04"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1e22c22f7826096ad503e9bb681b05b8c1f5a8138469b255eb91f26a76634f2"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:334ef6ed8337bd0b58bb0ae4f7f2dcc84c9f116e474bb4ec250a8bb9bd797a66"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6672fdde0fd1a60b44fb1751a7779c6db487e42b0cc65e7caa6aa686874e79fb"}, + {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:952256c2bc5b4ee8df8dfc54fc4de330970bf5d79253c863fb5e6761f00dda35"}, + {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:269d06fa0f9624455ce08ae0179430eea61085e3cf6457f05982b37fd2cefe17"}, + {file = "greenlet-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9adbd8ecf097e34ada8efde9b6fec4dd2a903b1e98037adf72d12993a1c80b51"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf94aa539e97a8411b5ea52fc6ccd8371be9550c4041011a091eb8b3ca1d810"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80dcd3c938cbcac986c5c92779db8e8ce51a89a849c135172c88ecbdc8c056b7"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52a712c38e5fb4fd68e00dc3caf00b60cb65634d50e32281a9d6431b33b4af1"}, + {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5539f6da3418c3dc002739cb2bb8d169056aa66e0c83f6bacae0cd3ac26b423"}, + {file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:343675e0da2f3c69d3fb1e894ba0a1acf58f481f3b9372ce1eb465ef93cf6fed"}, + {file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:abe1ef3d780de56defd0c77c5ba95e152f4e4c4e12d7e11dd8447d338b85a625"}, + {file = "greenlet-3.0.0-cp37-cp37m-win32.whl", hash = "sha256:e693e759e172fa1c2c90d35dea4acbdd1d609b6936115d3739148d5e4cd11947"}, + {file = "greenlet-3.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705"}, + {file = "greenlet-3.0.0-cp37-universal2-macosx_11_0_x86_64.whl", hash = "sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353"}, + {file = "greenlet-3.0.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b822fab253ac0f330ee807e7485769e3ac85d5eef827ca224feaaefa462dc0d0"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8060b32d8586e912a7b7dac2d15b28dbbd63a174ab32f5bc6d107a1c4143f40b"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365"}, + {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb36985f606a7c49916eff74ab99399cdfd09241c375d5a820bb855dfb4af9f"}, + {file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d"}, + {file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f351479a6914fd81a55c8e68963609f792d9b067fb8a60a042c585a621e0de4f"}, + {file = "greenlet-3.0.0-cp38-cp38-win32.whl", hash = "sha256:9de687479faec7db5b198cc365bc34addd256b0028956501f4d4d5e9ca2e240a"}, + {file = "greenlet-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:3fd2b18432e7298fcbec3d39e1a0aa91ae9ea1c93356ec089421fabc3651572b"}, + {file = "greenlet-3.0.0-cp38-universal2-macosx_11_0_x86_64.whl", hash = "sha256:3c0d36f5adc6e6100aedbc976d7428a9f7194ea79911aa4bf471f44ee13a9464"}, + {file = "greenlet-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5b2d4cdaf1c71057ff823a19d850ed5c6c2d3686cb71f73ae4d6382aaa7a06"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e7dcdfad252f2ca83c685b0fa9fba00e4d8f243b73839229d56ee3d9d219314"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6fb737e46b8bd63156b8f59ba6cdef46fe2b7db0c5804388a2d0519b8ddb99"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d55db1db455c59b46f794346efce896e754b8942817f46a1bada2d29446e305a"}, + {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692"}, + {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a812224a5fb17a538207e8cf8e86f517df2080c8ee0f8c1ed2bdaccd18f38f4"}, + {file = "greenlet-3.0.0-cp39-cp39-win32.whl", hash = "sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9"}, + {file = "greenlet-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:831d6f35037cf18ca5e80a737a27d822d87cd922521d18ed3dbc8a6967be50ce"}, + {file = "greenlet-3.0.0-cp39-universal2-macosx_11_0_x86_64.whl", hash = "sha256:a048293392d4e058298710a54dfaefcefdf49d287cd33fb1f7d63d55426e4355"}, + {file = "greenlet-3.0.0.tar.gz", hash = "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b"}, ] [package.extras] @@ -4209,7 +4214,7 @@ diagrams = ["jinja2", "railroad-diagrams"] name = "pyproj" version = "3.6.1" description = "Python interface to PROJ (cartographic projections and coordinate transformations library)" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "pyproj-3.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab7aa4d9ff3c3acf60d4b285ccec134167a948df02347585fdd934ebad8811b4"}, @@ -5118,7 +5123,7 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar name = "shapely" version = "2.0.2" description = "Manipulation and analysis of geometric objects" -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "shapely-2.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6ca8cffbe84ddde8f52b297b53f8e0687bd31141abb2c373fd8a9f032df415d6"}, @@ -6094,4 +6099,4 @@ visualization = ["graphviz"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "9e51fbe59d7314865111ee050024c97ba145e5074c14b2bfc6c2e8aaf47cea30" +content-hash = "16724d5f4f18a846e2729d8e1962dc3fabad1e29fbd5b71a320b80bda3e8650b" diff --git a/pyproject.toml b/pyproject.toml index a4ac63034d3d..45156dcb6750 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,6 +129,7 @@ pytest-snapshot = ">=0.9.0,<1" pytest-xdist = ">=2.3.0,<4" requests = ">=2,<3" sqlalchemy = ">=1.4,<3" +geopandas = ">=0.14.0,<1" [tool.poetry.group.docs.dependencies] altair = { version = ">=5.0.1,<6", python = ">=3.10,<3.13" } @@ -324,6 +325,8 @@ filterwarnings = [ "ignore:the imp module is deprecated in favour of importlib:DeprecationWarning", # pytest raises a syntax error when encountering this from *any* module, including third party modules "ignore:invalid escape sequence:DeprecationWarning", + # geopandas raises usr warning on geometry column + "ignore:Geometry is in a geographic CRS", # `is_sparse` deprecation was addressed in pyarrow 13.0.0 (see https://github.com/apache/arrow/pull/35366), # but flink requires apache-beam<2.49, which caps its pyarrow dependency (see https://github.com/apache/beam/blob/v2.48.0/sdks/python/setup.py#L144) "ignore:is_sparse is deprecated and will be removed in a future version:FutureWarning",