From a6f59ac524f8b913526d41ed771c5c45d051eb24 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 24 Sep 2023 14:44:24 -0400 Subject: [PATCH 01/10] folder structure --- geoarrow/shapely/README.md | 3 + geoarrow/shapely/geoarrow/shapely/__init__.py | 0 geoarrow/shapely/poetry.lock | 84 +++++++++++++++++++ geoarrow/shapely/pyproject.toml | 19 +++++ 4 files changed, 106 insertions(+) create mode 100644 geoarrow/shapely/README.md create mode 100644 geoarrow/shapely/geoarrow/shapely/__init__.py create mode 100644 geoarrow/shapely/poetry.lock create mode 100644 geoarrow/shapely/pyproject.toml diff --git a/geoarrow/shapely/README.md b/geoarrow/shapely/README.md new file mode 100644 index 0000000..15f7b7d --- /dev/null +++ b/geoarrow/shapely/README.md @@ -0,0 +1,3 @@ +# geoarrow.shapely + +Interop between Shapely and GeoArrow in Python diff --git a/geoarrow/shapely/geoarrow/shapely/__init__.py b/geoarrow/shapely/geoarrow/shapely/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/geoarrow/shapely/poetry.lock b/geoarrow/shapely/poetry.lock new file mode 100644 index 0000000..c5e3912 --- /dev/null +++ b/geoarrow/shapely/poetry.lock @@ -0,0 +1,84 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "numpy" +version = "1.24.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, +] + +[[package]] +name = "pyarrow" +version = "13.0.0" +description = "Python library for Apache Arrow" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyarrow-13.0.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:1afcc2c33f31f6fb25c92d50a86b7a9f076d38acbcb6f9e74349636109550148"}, + {file = "pyarrow-13.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:70fa38cdc66b2fc1349a082987f2b499d51d072faaa6b600f71931150de2e0e3"}, + {file = "pyarrow-13.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd57b13a6466822498238877892a9b287b0a58c2e81e4bdb0b596dbb151cbb73"}, + {file = "pyarrow-13.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ce69f7bf01de2e2764e14df45b8404fc6f1a5ed9871e8e08a12169f87b7a26"}, + {file = "pyarrow-13.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:588f0d2da6cf1b1680974d63be09a6530fd1bd825dc87f76e162404779a157dc"}, + {file = "pyarrow-13.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6241afd72b628787b4abea39e238e3ff9f34165273fad306c7acf780dd850956"}, + {file = "pyarrow-13.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:fda7857e35993673fcda603c07d43889fca60a5b254052a462653f8656c64f44"}, + {file = "pyarrow-13.0.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:aac0ae0146a9bfa5e12d87dda89d9ef7c57a96210b899459fc2f785303dcbb67"}, + {file = "pyarrow-13.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d7759994217c86c161c6a8060509cfdf782b952163569606bb373828afdd82e8"}, + {file = "pyarrow-13.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:868a073fd0ff6468ae7d869b5fc1f54de5c4255b37f44fb890385eb68b68f95d"}, + {file = "pyarrow-13.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51be67e29f3cfcde263a113c28e96aa04362ed8229cb7c6e5f5c719003659d33"}, + {file = "pyarrow-13.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d1b4e7176443d12610874bb84d0060bf080f000ea9ed7c84b2801df851320295"}, + {file = "pyarrow-13.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:69b6f9a089d116a82c3ed819eea8fe67dae6105f0d81eaf0fdd5e60d0c6e0944"}, + {file = "pyarrow-13.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:ab1268db81aeb241200e321e220e7cd769762f386f92f61b898352dd27e402ce"}, + {file = "pyarrow-13.0.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:ee7490f0f3f16a6c38f8c680949551053c8194e68de5046e6c288e396dccee80"}, + {file = "pyarrow-13.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3ad79455c197a36eefbd90ad4aa832bece7f830a64396c15c61a0985e337287"}, + {file = "pyarrow-13.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68fcd2dc1b7d9310b29a15949cdd0cb9bc34b6de767aff979ebf546020bf0ba0"}, + {file = "pyarrow-13.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc6fd330fd574c51d10638e63c0d00ab456498fc804c9d01f2a61b9264f2c5b2"}, + {file = "pyarrow-13.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:e66442e084979a97bb66939e18f7b8709e4ac5f887e636aba29486ffbf373763"}, + {file = "pyarrow-13.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:0f6eff839a9e40e9c5610d3ff8c5bdd2f10303408312caf4c8003285d0b49565"}, + {file = "pyarrow-13.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b30a27f1cddf5c6efcb67e598d7823a1e253d743d92ac32ec1eb4b6a1417867"}, + {file = "pyarrow-13.0.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:09552dad5cf3de2dc0aba1c7c4b470754c69bd821f5faafc3d774bedc3b04bb7"}, + {file = "pyarrow-13.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3896ae6c205d73ad192d2fc1489cd0edfab9f12867c85b4c277af4d37383c18c"}, + {file = "pyarrow-13.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6647444b21cb5e68b593b970b2a9a07748dd74ea457c7dadaa15fd469c48ada1"}, + {file = "pyarrow-13.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47663efc9c395e31d09c6aacfa860f4473815ad6804311c5433f7085415d62a7"}, + {file = "pyarrow-13.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:b9ba6b6d34bd2563345488cf444510588ea42ad5613df3b3509f48eb80250afd"}, + {file = "pyarrow-13.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:d00d374a5625beeb448a7fa23060df79adb596074beb3ddc1838adb647b6ef09"}, + {file = "pyarrow-13.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:c51afd87c35c8331b56f796eff954b9c7f8d4b7fef5903daf4e05fcf017d23a8"}, + {file = "pyarrow-13.0.0.tar.gz", hash = "sha256:83333726e83ed44b0ac94d8d7a21bbdee4a05029c3b1e8db58a863eec8fd8a33"}, +] + +[package.dependencies] +numpy = ">=1.16.6" + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "23170c9f47585417860090b165381fbcbab62c728a3fda98ca87dc75631b5a2d" diff --git a/geoarrow/shapely/pyproject.toml b/geoarrow/shapely/pyproject.toml new file mode 100644 index 0000000..b2b8ceb --- /dev/null +++ b/geoarrow/shapely/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "geoarrow.shapely" +version = "0.1" +description = "Interop between GeoArrow and Shapely geometries in Python" +authors = [] +readme = "README.md" +packages = [ + { include = "shapely", from = "geoarrow" } +] + +[tool.poetry.dependencies] +python = "^3.8" +pyarrow = "^13" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" From 98cb11720e48c05c69ce0bcfed7d5d9a8efac57e Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 24 Sep 2023 16:38:49 -0400 Subject: [PATCH 02/10] implement initial types --- geoarrow/shapely/geoarrow/shapely/__init__.py | 9 + .../geoarrow/shapely/extension_array.py | 31 + .../geoarrow/shapely/extension_types.py | 382 ++++++ geoarrow/shapely/poetry.lock | 1044 ++++++++++++++++- geoarrow/shapely/pyproject.toml | 9 +- 5 files changed, 1473 insertions(+), 2 deletions(-) create mode 100644 geoarrow/shapely/geoarrow/shapely/extension_array.py create mode 100644 geoarrow/shapely/geoarrow/shapely/extension_types.py diff --git a/geoarrow/shapely/geoarrow/shapely/__init__.py b/geoarrow/shapely/geoarrow/shapely/__init__.py index e69de29..178827d 100644 --- a/geoarrow/shapely/geoarrow/shapely/__init__.py +++ b/geoarrow/shapely/geoarrow/shapely/__init__.py @@ -0,0 +1,9 @@ +from .extension_array import PointArray +from .extension_types import ( + LineStringType, + MultiLineStringType, + MultiPointType, + MultiPolygonType, + PointType, + PolygonType, +) diff --git a/geoarrow/shapely/geoarrow/shapely/extension_array.py b/geoarrow/shapely/geoarrow/shapely/extension_array.py new file mode 100644 index 0000000..e300702 --- /dev/null +++ b/geoarrow/shapely/geoarrow/shapely/extension_array.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +import numpy as np +import pyarrow as pa +from numpy.typing import NDArray + +import shapely +from shapely import GeometryType + + +class PointArray(pa.ExtensionArray): + def to_shapely(self) -> NDArray[np.object_]: + """Convert to an array of shapely geometries""" + coord_dimension = self.type.coord_dimension + flat_coords = ( + self.storage.flatten() + .to_numpy(zero_copy_only=True, writable=False) + .reshape(-1, len(coord_dimension)) + ) + return shapely.from_ragged_array(GeometryType.POINT, flat_coords, None) + +class LineStringArray(pa.ExtensionArray): + def to_shapely(self) -> NDArray[np.object_]: + """Convert to an array of shapely geometries""" + coord_dimension = self.type.coord_dimension + flat_coords = ( + self.storage.flatten() + .to_numpy(zero_copy_only=True, writable=False) + .reshape(-1, len(coord_dimension)) + ) + return shapely.from_ragged_array(GeometryType.POINT, flat_coords, None) diff --git a/geoarrow/shapely/geoarrow/shapely/extension_types.py b/geoarrow/shapely/geoarrow/shapely/extension_types.py new file mode 100644 index 0000000..ef3c747 --- /dev/null +++ b/geoarrow/shapely/geoarrow/shapely/extension_types.py @@ -0,0 +1,382 @@ +from enum import Enum +from typing import Optional + +import numpy as np +import pyarrow as pa +from numpy.typing import NDArray + +import shapely +from shapely import GeometryType + +from .extension_array import PointArray + + +class CoordinateDimension(str, Enum): + XY = "xy" + XYZ = "xyz" + XYM = "xym" + XYZM = "xyzm" + + +class BaseGeometryType(pa.ExtensionType): + extension_name: str + coord_dimension: CoordinateDimension + + # def __init__(self): + # # attributes need to be set first before calling + # # super init (as that calls serialize) + # # self._crs = crs + # pa.ExtensionType.__init__(self, self._storage_type, self._extension_name) + + @property + def crs(self): + return self._crs + + def __arrow_ext_serialize__(self): + return b"CREATED" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type, serialized): + # return an instance of this subclass given the serialized + # metadata. + # TODO ignore serialized metadata for now + # serialized = serialized.decode() + # assert serialized.startswith("crs=") + # crs = serialized.split('=')[1] + # if crs == "": + # crs = None + return cls() + + +def coord_storage_type(*, interleaved: bool, dims: CoordinateDimension) -> pa.DataType: + """Generate the storage type of a geoarrow coordinate array + + Args: + interleaved: Whether coordinates should be interleaved or separated + dims: The number of dimensions + """ + if interleaved: + return pa.list_(pa.field(dims, pa.float64()), len(dims)) + + else: + if dims == CoordinateDimension.XY: + return pa.struct( + [ + ("x", pa.float64()), + ("y", pa.float64()), + ] + ) + if dims == CoordinateDimension.XYZ: + return pa.struct( + [ + ("x", pa.float64()), + ("y", pa.float64()), + ("z", pa.float64()), + ] + ) + if dims == CoordinateDimension.XYM: + return pa.struct( + [ + ("x", pa.float64()), + ("y", pa.float64()), + ("m", pa.float64()), + ] + ) + if dims == CoordinateDimension.XYZM: + return pa.struct( + [ + ("x", pa.float64()), + ("y", pa.float64()), + ("z", pa.float64()), + ("m", pa.float64()), + ] + ) + + +def linestring_storage_type( + *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False +) -> pa.DataType: + """Generate the storage type of a geoarrow.linestring array + + Args: + interleaved: Whether coordinates should be interleaved or separated + dims: The number of dimensions + large_list: Whether to use a large list with int64 offsets for the inner type + """ + vertices_type = coord_storage_type(interleaved=interleaved, dims=dims) + if large_list: + return pa.large_list(pa.field("vertices", vertices_type)) + else: + return pa.list_(pa.field("vertices", vertices_type)) + + +def polygon_storage_type( + *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False +) -> pa.DataType: + """Generate the storage type of a geoarrow.polygon array + + Args: + interleaved: Whether coordinates should be interleaved or separated + dims: The number of dimensions + large_list: Whether to use a large list with int64 offsets for the inner type + """ + rings_type = linestring_storage_type( + large_list=large_list, interleaved=interleaved, dims=dims + ) + if large_list: + return pa.large_list(pa.field("rings", rings_type)) + else: + return pa.list_(pa.field("rings", rings_type)) + + +def multipoint_storage_type( + *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False +) -> pa.DataType: + """Generate the storage type of a geoarrow.multipoint array + + Args: + interleaved: Whether coordinates should be interleaved or separated + dims: The number of dimensions + large_list: Whether to use a large list with int64 offsets for the inner type + """ + points_type = coord_storage_type(interleaved=interleaved, dims=dims) + if large_list: + return pa.large_list(pa.field("points", points_type)) + else: + return pa.list_(pa.field("points", points_type)) + + +def multilinestring_storage_type( + *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False +) -> pa.DataType: + """Generate the storage type of a geoarrow.multilinestring array + + Args: + interleaved: Whether coordinates should be interleaved or separated + dims: The number of dimensions + large_list: Whether to use a large list with int64 offsets for the inner type + """ + linestrings_type = linestring_storage_type( + large_list=large_list, interleaved=interleaved, dims=dims + ) + if large_list: + return pa.large_list(pa.field("linestrings", linestrings_type)) + else: + return pa.list_(pa.field("linestrings", linestrings_type)) + + +def multipolygon_storage_type( + *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False +) -> pa.DataType: + """Generate the storage type of a geoarrow.multipolygon array + + Args: + interleaved: Whether coordinates should be interleaved or separated + dims: The number of dimensions + large_list: Whether to use a large list with int64 offsets for the inner type + """ + polygons_type = polygon_storage_type( + large_list=large_list, interleaved=interleaved, dims=dims + ) + if large_list: + return pa.large_list(pa.field("polygons", polygons_type)) + else: + return pa.list_(pa.field("polygons", polygons_type)) + + +class PointType(BaseGeometryType): + extension_name = "geoarrow.point" + + def __init__(self, *, interleaved: bool, dims: CoordinateDimension): + self.coord_dimension = dims + + storage_type = coord_storage_type(interleaved=interleaved, dims=dims) + super().__init__(storage_type, self.extension_name) + + # def __init__(self): + # # attributes need to be set first before calling + # # super init (as that calls serialize) + # # self._crs = crs + # pa.ExtensionType.__init__(self, self._storage_type, self._extension_name) + + def __arrow_ext_class__(self): + return PointArray + + +class LineStringType(BaseGeometryType): + extension_name = "geoarrow.linestring" + + def __init__( + self, *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False + ): + self.coord_dimension = dims + + storage_type = linestring_storage_type( + interleaved=interleaved, dims=dims, large_list=large_list + ) + super().__init__(storage_type, self.extension_name) + + +class PolygonType(BaseGeometryType): + extension_name = "geoarrow.polygon" + + def __init__( + self, *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False + ): + self.coord_dimension = dims + + storage_type = polygon_storage_type( + interleaved=interleaved, dims=dims, large_list=large_list + ) + super().__init__(storage_type, self.extension_name) + + +class MultiPointType(BaseGeometryType): + extension_name = "geoarrow.multipoint" + + def __init__( + self, *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False + ): + self.coord_dimension = dims + + storage_type = multipoint_storage_type( + interleaved=interleaved, dims=dims, large_list=large_list + ) + super().__init__(storage_type, self.extension_name) + + +class MultiLineStringType(BaseGeometryType): + extension_name = "geoarrow.multilinestring" + + def __init__( + self, *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False + ): + self.coord_dimension = dims + + storage_type = multilinestring_storage_type( + interleaved=interleaved, dims=dims, large_list=large_list + ) + super().__init__(storage_type, self.extension_name) + + +class MultiPolygonType(BaseGeometryType): + extension_name = "geoarrow.multipolygon" + + def __init__( + self, *, interleaved: bool, dims: CoordinateDimension, large_list: bool = False + ): + self.coord_dimension = dims + + storage_type = multipolygon_storage_type( + interleaved=interleaved, dims=dims, large_list=large_list + ) + super().__init__(storage_type, self.extension_name) + + +def register_geometry_extension_types(): + for geom_type_class in [ + PointType, + LineStringType, + PolygonType, + MultiPointType, + MultiLineStringType, + MultiPolygonType, + ]: + # Provide a default to go into the registry, but at runtime, we can choose other + # type formulations + geom_type_instance = geom_type_class( + interleaved=True, dims=CoordinateDimension.XY + ) + try: + pa.register_extension_type(geom_type_instance) + + # If already registered with this id, unregister and re register + except pa.ArrowKeyError: + pa.unregister_extension_type(geom_type_instance.extension_name) + pa.register_extension_type(geom_type_instance) + + +# register_geometry_extension_types() +# shapely_arr = shapely.points([[1, 2], [3, 4], [5, 6], [5, 6]]) +# point_arr = construct_geometry_array(shapely_arr) +# point_arr.to_shapely() + +# x = point_arr.storage.flatten() +# x.to_numpy? +# dir(x) + +# point_arr.to_shapely() +# point_arr.type.coord_dimension + + +def construct_geometry_array( + shapely_arr: NDArray[np.object_], include_z: Optional[bool] = None +): + geom_type, coords, offsets = shapely.to_ragged_array( + shapely_arr, include_z=include_z + ) + + if coords.shape[-1] == 2: + dims = CoordinateDimension.XY + elif coords.shape[-1] == 3: + dims = CoordinateDimension.XYZ + else: + raise ValueError(f"Unexpected coords dimensions: {coords.shape}") + + if geom_type == GeometryType.POINT: + parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), len(dims)) + return pa.ExtensionArray.from_storage( + PointType(interleaved=True, dims=dims), parr + ) + + elif geom_type == GeometryType.LINESTRING: + assert len(offsets) == 1, "Expected one offsets array" + (offsets1,) = offsets + _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + parr = pa.ListArray.from_arrays(pa.array(offsets1), _parr) + return pa.ExtensionArray.from_storage( + LineStringType(interleaved=True, dims=dims), parr + ) + + elif geom_type == GeometryType.POLYGON: + assert len(offsets) == 2, "Expected two offsets arrays" + offsets1, offsets2 = offsets + _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + _parr1 = pa.ListArray.from_arrays(pa.array(offsets1), _parr) + parr = pa.ListArray.from_arrays(pa.array(offsets2), _parr1) + return pa.ExtensionArray.from_storage( + PolygonType(interleaved=True, dims=dims), parr + ) + + elif geom_type == GeometryType.MULTIPOINT: + assert len(offsets) == 1, "Expected one offsets array" + (offsets1,) = offsets + _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + parr = pa.ListArray.from_arrays(pa.array(offsets1), _parr) + return pa.ExtensionArray.from_storage( + MultiPointType(interleaved=True, dims=dims), parr + ) + + elif geom_type == GeometryType.MULTILINESTRING: + assert len(offsets) == 2, "Expected two offsets arrays" + offsets1, offsets2 = offsets + _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + _parr1 = pa.ListArray.from_arrays(pa.array(offsets1), _parr) + parr = pa.ListArray.from_arrays(pa.array(offsets2), _parr1) + return pa.ExtensionArray.from_storage( + MultiLineStringType(interleaved=True, dims=dims), parr + ) + + elif geom_type == GeometryType.MULTIPOLYGON: + assert len(offsets) == 3, "Expected three offsets arrays" + offsets1, offsets2, offsets3 = offsets + _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + _parr1 = pa.ListArray.from_arrays(pa.array(offsets1), _parr) + _parr2 = pa.ListArray.from_arrays(pa.array(offsets2), _parr1) + parr = pa.ListArray.from_arrays(pa.array(offsets3), _parr2) + return pa.ExtensionArray.from_storage( + MultiPolygonType(interleaved=True, dims=dims), parr + ) + + else: + raise ValueError(f"Unsupported type for geoarrow: {geom_type}") diff --git a/geoarrow/shapely/poetry.lock b/geoarrow/shapely/poetry.lock index c5e3912..d631857 100644 --- a/geoarrow/shapely/poetry.lock +++ b/geoarrow/shapely/poetry.lock @@ -1,5 +1,487 @@ # This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +[[package]] +name = "appnope" +version = "0.1.3" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = "*" +files = [ + {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, + {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, +] + +[[package]] +name = "asttokens" +version = "2.4.0" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.0-py2.py3-none-any.whl", hash = "sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69"}, + {file = "asttokens-2.4.0.tar.gz", hash = "sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +test = ["astroid", "pytest"] + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +optional = false +python-versions = "*" +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + +[[package]] +name = "black" +version = "23.9.1" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"}, + {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"}, + {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"}, + {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"}, + {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"}, + {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"}, + {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"}, + {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"}, + {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"}, + {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"}, + {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2023.7.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "comm" +version = "0.1.4" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.6" +files = [ + {file = "comm-0.1.4-py3-none-any.whl", hash = "sha256:6d52794cba11b36ed9860999cd10fd02d6b2eac177068fdd585e1e2f8a96e67a"}, + {file = "comm-0.1.4.tar.gz", hash = "sha256:354e40a59c9dd6db50c5cc6b4acc887d82e9603787f83b68c01a80a923984d15"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +lint = ["black (>=22.6.0)", "mdformat (>0.7)", "mdformat-gfm (>=0.3.5)", "ruff (>=0.0.156)"] +test = ["pytest"] +typing = ["mypy (>=0.990)"] + +[[package]] +name = "debugpy" +version = "1.8.0" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7fb95ca78f7ac43393cd0e0f2b6deda438ec7c5e47fa5d38553340897d2fbdfb"}, + {file = "debugpy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef9ab7df0b9a42ed9c878afd3eaaff471fce3fa73df96022e1f5c9f8f8c87ada"}, + {file = "debugpy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:a8b7a2fd27cd9f3553ac112f356ad4ca93338feadd8910277aff71ab24d8775f"}, + {file = "debugpy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d9de202f5d42e62f932507ee8b21e30d49aae7e46d5b1dd5c908db1d7068637"}, + {file = "debugpy-1.8.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:ef54404365fae8d45cf450d0544ee40cefbcb9cb85ea7afe89a963c27028261e"}, + {file = "debugpy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60009b132c91951354f54363f8ebdf7457aeb150e84abba5ae251b8e9f29a8a6"}, + {file = "debugpy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:8cd0197141eb9e8a4566794550cfdcdb8b3db0818bdf8c49a8e8f8053e56e38b"}, + {file = "debugpy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:a64093656c4c64dc6a438e11d59369875d200bd5abb8f9b26c1f5f723622e153"}, + {file = "debugpy-1.8.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:b05a6b503ed520ad58c8dc682749113d2fd9f41ffd45daec16e558ca884008cd"}, + {file = "debugpy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c6fb41c98ec51dd010d7ed650accfd07a87fe5e93eca9d5f584d0578f28f35f"}, + {file = "debugpy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:46ab6780159eeabb43c1495d9c84cf85d62975e48b6ec21ee10c95767c0590aa"}, + {file = "debugpy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:bdc5ef99d14b9c0fcb35351b4fbfc06ac0ee576aeab6b2511702e5a648a2e595"}, + {file = "debugpy-1.8.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:61eab4a4c8b6125d41a34bad4e5fe3d2cc145caecd63c3fe953be4cc53e65bf8"}, + {file = "debugpy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:125b9a637e013f9faac0a3d6a82bd17c8b5d2c875fb6b7e2772c5aba6d082332"}, + {file = "debugpy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:57161629133113c97b387382045649a2b985a348f0c9366e22217c87b68b73c6"}, + {file = "debugpy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:e3412f9faa9ade82aa64a50b602544efcba848c91384e9f93497a458767e6926"}, + {file = "debugpy-1.8.0-py2.py3-none-any.whl", hash = "sha256:9c9b0ac1ce2a42888199df1a1906e45e6f3c9555497643a85e0bf2406e3ffbc4"}, + {file = "debugpy-1.8.0.zip", hash = "sha256:12af2c55b419521e33d5fb21bd022df0b5eb267c3e178f1d374a63a2a6bdccd0"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.3" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "executing" +version = "1.2.0" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = "*" +files = [ + {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"}, + {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"}, +] + +[package.extras] +tests = ["asttokens", "littleutils", "pytest", "rich"] + +[[package]] +name = "importlib-metadata" +version = "6.8.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, + {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "ipykernel" +version = "6.25.2" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.25.2-py3-none-any.whl", hash = "sha256:2e2ee359baba19f10251b99415bb39de1e97d04e1fab385646f24f0596510b77"}, + {file = "ipykernel-6.25.2.tar.gz", hash = "sha256:f468ddd1f17acb48c8ce67fcfa49ba6d46d4f9ac0438c1f441be7c3d1372230b"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=20" +tornado = ">=6.1" +traitlets = ">=5.4.0" + +[package.extras] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.12.2" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipython-8.12.2-py3-none-any.whl", hash = "sha256:ea8801f15dfe4ffb76dea1b09b847430ffd70d827b41735c64a0638a04103bfc"}, + {file = "ipython-8.12.2.tar.gz", hash = "sha256:c7b80eb7f5a855a88efc971fda506ff7a91c280b42cdae26643e0f601ea281ea"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] + +[[package]] +name = "jedi" +version = "0.19.0" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.0-py2.py3-none-any.whl", hash = "sha256:cb8ce23fbccff0025e9386b5cf85e892f94c9b822378f8da49970471335ac64e"}, + {file = "jedi-0.19.0.tar.gz", hash = "sha256:bcf9894f1753969cbac8022a8c2eaee06bfa3724e4192470aaffe7eb6272b0c4"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jupyter-client" +version = "8.3.1" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.3.1-py3-none-any.whl", hash = "sha256:5eb9f55eb0650e81de6b7e34308d8b92d04fe4ec41cd8193a913979e33d8e1a5"}, + {file = "jupyter_client-8.3.1.tar.gz", hash = "sha256:60294b2d5b869356c893f57b1a877ea6510d60d45cf4b38057f1672d85699ac9"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.3.1" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.3.1-py3-none-any.whl", hash = "sha256:ae9036db959a71ec1cac33081eeb040a79e681f08ab68b0883e9a676c7a90dce"}, + {file = "jupyter_core-5.3.1.tar.gz", hash = "sha256:5ba5c7938a7f97a6b0481463f7ff0dbac7c15ba48cf46fa4035ca6e838aa1aba"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nest-asyncio" +version = "1.5.8" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.5.8-py3-none-any.whl", hash = "sha256:accda7a339a70599cb08f9dd09a67e0c2ef8d8d6f4c07f96ab203f2ae254e48d"}, + {file = "nest_asyncio-1.5.8.tar.gz", hash = "sha256:25aa2ca0d2a5b5531956b9e273b45cf664cae2b145101d73b86b199978d48fdb"}, +] + [[package]] name = "numpy" version = "1.24.4" @@ -37,6 +519,163 @@ files = [ {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, ] +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pathspec" +version = "0.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +optional = false +python-versions = "*" +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] + +[[package]] +name = "platformdirs" +version = "3.10.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, + {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + +[[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.39" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.39-py3-none-any.whl", hash = "sha256:9dffbe1d8acf91e3de75f3b544e4842382fc06c6babe903ac9acb74dc6e08d88"}, + {file = "prompt_toolkit-3.0.39.tar.gz", hash = "sha256:04505ade687dc26dc4284b1ad19a83be2f2afe83e7a828ace0c72f3a1df72aac"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psutil" +version = "5.9.5" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "psutil-5.9.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4"}, + {file = "psutil-5.9.5-cp27-none-win32.whl", hash = "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f"}, + {file = "psutil-5.9.5-cp27-none-win_amd64.whl", hash = "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42"}, + {file = "psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4"}, + {file = "psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d"}, + {file = "psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9"}, + {file = "psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30"}, + {file = "psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + [[package]] name = "pyarrow" version = "13.0.0" @@ -78,7 +717,410 @@ files = [ [package.dependencies] numpy = ">=1.16.6" +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pygments" +version = "2.16.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyproj" +version = "3.5.0" +description = "Python interface to PROJ (cartographic projections and coordinate transformations library)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyproj-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6475ce653880938468a1a1b7321267243909e34b972ba9e53d5982c41d555918"}, + {file = "pyproj-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61e4ad57d89b03a7b173793b31bca8ee110112cde1937ef0f42a70b9120c827d"}, + {file = "pyproj-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdd2021bb6f7f346bfe1d2a358aa109da017d22c4704af2d994e7c7ee0a7a53"}, + {file = "pyproj-3.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5674923351e76222e2c10c58b5e1ac119d7a46b270d822c463035971b06f724b"}, + {file = "pyproj-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd5e2b6aa255023c4acd0b977590f1f7cc801ba21b4d806fcf6dfac3474ebb83"}, + {file = "pyproj-3.5.0-cp310-cp310-win32.whl", hash = "sha256:6f316a66031a14e9c5a88c91f8b77aa97f5454895674541ed6ab630b682be35d"}, + {file = "pyproj-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:f7c2f4d9681e810cf40239caaca00079930a6d9ee6591139b88d592d36051d82"}, + {file = "pyproj-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7572983134e310e0ca809c63f1722557a040fe9443df5f247bf11ba887eb1229"}, + {file = "pyproj-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eccb417b91d0be27805dfc97550bfb8b7db94e9fe1db5ebedb98f5b88d601323"}, + {file = "pyproj-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:621d78a9d8bf4d06e08bef2471021fbcb1a65aa629ad4a20c22e521ce729cc20"}, + {file = "pyproj-3.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9a024370e917c899bff9171f03ea6079deecdc7482a146a2c565f3b9df134ea"}, + {file = "pyproj-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b7c2113c4d11184a238077ec85e31eda1dcc58ffeb9a4429830e0a7036e787d"}, + {file = "pyproj-3.5.0-cp311-cp311-win32.whl", hash = "sha256:a730f5b4c98c8a0f312437873e6e34dbd4cc6dc23d5afd91a6691c62724b1f68"}, + {file = "pyproj-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e97573de0ab3bbbcb4c7748bc41f4ceb6da10b45d35b1a294b5820701e7c25f0"}, + {file = "pyproj-3.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2b708fd43453b985642b737d4a6e7f1d6a0ab1677ffa4e14cc258537b49224b0"}, + {file = "pyproj-3.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b60d93a200639e8367c6542a964fd0aa2dbd152f256c1831dc18cd5aa470fb8a"}, + {file = "pyproj-3.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38862fe07316ae12b79d82d298e390973a4f00b684f3c2d037238e20e00610ba"}, + {file = "pyproj-3.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71b65f2a38cd9e16883dbb0f8ae82bdf8f6b79b1b02975c78483ab8428dbbf2f"}, + {file = "pyproj-3.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b752b7d9c4b08181c7e8c0d9c7f277cbefff42227f34d3310696a87c863d9dd3"}, + {file = "pyproj-3.5.0-cp38-cp38-win32.whl", hash = "sha256:b937215bfbaf404ec8f03ca741fc3f9f2c4c2c5590a02ccddddd820ae3c71331"}, + {file = "pyproj-3.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:97ed199033c2c770e7eea2ef80ff5e6413426ec2d7ec985b869792f04ab95d05"}, + {file = "pyproj-3.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:052c49fce8b5d55943a35c36ccecb87350c68b48ba95bc02a789770c374ef819"}, + {file = "pyproj-3.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1507138ea28bf2134d31797675380791cc1a7156a3aeda484e65a78a4aba9b62"}, + {file = "pyproj-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c02742ef3d846401861a878a61ef7ad911ea7539d6cc4619ddb52dbdf7b45aee"}, + {file = "pyproj-3.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:385b0341861d3ebc8cad98337a738821dcb548d465576527399f4955ca24b6ed"}, + {file = "pyproj-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fe6bb1b68a35d07378d38be77b5b2f8dd2bea5910c957bfcc7bee55988d3910"}, + {file = "pyproj-3.5.0-cp39-cp39-win32.whl", hash = "sha256:5c4b85ac10d733c42d73a2e6261c8d6745bf52433a31848dd1b6561c9a382da3"}, + {file = "pyproj-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:1798ff7d65d9057ebb2d017ffe8403268b8452f24d0428b2140018c25c7fa1bc"}, + {file = "pyproj-3.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d711517a8487ef3245b08dc82f781a906df9abb3b6cb0ce0486f0eeb823ca570"}, + {file = "pyproj-3.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:788a5dadb532644a64efe0f5f01bf508c821eb7e984f13a677d56002f1e8a67a"}, + {file = "pyproj-3.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73f7960a97225812f9b1d7aeda5fb83812f38de9441e3476fcc8abb3e2b2f4de"}, + {file = "pyproj-3.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fde5ece4d2436b5a57c8f5f97b49b5de06a856d03959f836c957d3e609f2de7e"}, + {file = "pyproj-3.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08db25b61cf024648d55973cc3d1c3f1d0818fabf594d5f5a8e2318103d2aa0"}, + {file = "pyproj-3.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a87b419a2a352413fbf759ecb66da9da50bd19861c8f26db6a25439125b27b9"}, + {file = "pyproj-3.5.0.tar.gz", hash = "sha256:9859d1591c1863414d875ae0759e72c2cffc01ab989dc64137fbac572cc81bf6"}, +] + +[package.dependencies] +certifi = "*" + +[[package]] +name = "pytest" +version = "7.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, + {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "pyzmq" +version = "25.1.1" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:381469297409c5adf9a0e884c5eb5186ed33137badcbbb0560b86e910a2f1e76"}, + {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:955215ed0604dac5b01907424dfa28b40f2b2292d6493445dd34d0dfa72586a8"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:985bbb1316192b98f32e25e7b9958088431d853ac63aca1d2c236f40afb17c83"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afea96f64efa98df4da6958bae37f1cbea7932c35878b185e5982821bc883369"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76705c9325d72a81155bb6ab48d4312e0032bf045fb0754889133200f7a0d849"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77a41c26205d2353a4c94d02be51d6cbdf63c06fbc1295ea57dad7e2d3381b71"}, + {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:12720a53e61c3b99d87262294e2b375c915fea93c31fc2336898c26d7aed34cd"}, + {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:57459b68e5cd85b0be8184382cefd91959cafe79ae019e6b1ae6e2ba8a12cda7"}, + {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:292fe3fc5ad4a75bc8df0dfaee7d0babe8b1f4ceb596437213821f761b4589f9"}, + {file = "pyzmq-25.1.1-cp310-cp310-win32.whl", hash = "sha256:35b5ab8c28978fbbb86ea54958cd89f5176ce747c1fb3d87356cf698048a7790"}, + {file = "pyzmq-25.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:11baebdd5fc5b475d484195e49bae2dc64b94a5208f7c89954e9e354fc609d8f"}, + {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:d20a0ddb3e989e8807d83225a27e5c2eb2260eaa851532086e9e0fa0d5287d83"}, + {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e1c1be77bc5fb77d923850f82e55a928f8638f64a61f00ff18a67c7404faf008"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d89528b4943d27029a2818f847c10c2cecc79fa9590f3cb1860459a5be7933eb"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90f26dc6d5f241ba358bef79be9ce06de58d477ca8485e3291675436d3827cf8"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2b92812bd214018e50b6380ea3ac0c8bb01ac07fcc14c5f86a5bb25e74026e9"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f957ce63d13c28730f7fd6b72333814221c84ca2421298f66e5143f81c9f91f"}, + {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:047a640f5c9c6ade7b1cc6680a0e28c9dd5a0825135acbd3569cc96ea00b2505"}, + {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7f7e58effd14b641c5e4dec8c7dab02fb67a13df90329e61c869b9cc607ef752"}, + {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c2910967e6ab16bf6fbeb1f771c89a7050947221ae12a5b0b60f3bca2ee19bca"}, + {file = "pyzmq-25.1.1-cp311-cp311-win32.whl", hash = "sha256:76c1c8efb3ca3a1818b837aea423ff8a07bbf7aafe9f2f6582b61a0458b1a329"}, + {file = "pyzmq-25.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:44e58a0554b21fc662f2712814a746635ed668d0fbc98b7cb9d74cb798d202e6"}, + {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e1ffa1c924e8c72778b9ccd386a7067cddf626884fd8277f503c48bb5f51c762"}, + {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1af379b33ef33757224da93e9da62e6471cf4a66d10078cf32bae8127d3d0d4a"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cff084c6933680d1f8b2f3b4ff5bbb88538a4aac00d199ac13f49d0698727ecb"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2400a94f7dd9cb20cd012951a0cbf8249e3d554c63a9c0cdfd5cbb6c01d2dec"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d81f1ddae3858b8299d1da72dd7d19dd36aab654c19671aa8a7e7fb02f6638a"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:255ca2b219f9e5a3a9ef3081512e1358bd4760ce77828e1028b818ff5610b87b"}, + {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a882ac0a351288dd18ecae3326b8a49d10c61a68b01419f3a0b9a306190baf69"}, + {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:724c292bb26365659fc434e9567b3f1adbdb5e8d640c936ed901f49e03e5d32e"}, + {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ca1ed0bb2d850aa8471387882247c68f1e62a4af0ce9c8a1dbe0d2bf69e41fb"}, + {file = "pyzmq-25.1.1-cp312-cp312-win32.whl", hash = "sha256:b3451108ab861040754fa5208bca4a5496c65875710f76789a9ad27c801a0075"}, + {file = "pyzmq-25.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:eadbefd5e92ef8a345f0525b5cfd01cf4e4cc651a2cffb8f23c0dd184975d787"}, + {file = "pyzmq-25.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:db0b2af416ba735c6304c47f75d348f498b92952f5e3e8bff449336d2728795d"}, + {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c133e93b405eb0d36fa430c94185bdd13c36204a8635470cccc200723c13bb"}, + {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:273bc3959bcbff3f48606b28229b4721716598d76b5aaea2b4a9d0ab454ec062"}, + {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cbc8df5c6a88ba5ae385d8930da02201165408dde8d8322072e3e5ddd4f68e22"}, + {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:18d43df3f2302d836f2a56f17e5663e398416e9dd74b205b179065e61f1a6edf"}, + {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:73461eed88a88c866656e08f89299720a38cb4e9d34ae6bf5df6f71102570f2e"}, + {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:34c850ce7976d19ebe7b9d4b9bb8c9dfc7aac336c0958e2651b88cbd46682123"}, + {file = "pyzmq-25.1.1-cp36-cp36m-win32.whl", hash = "sha256:d2045d6d9439a0078f2a34b57c7b18c4a6aef0bee37f22e4ec9f32456c852c71"}, + {file = "pyzmq-25.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:458dea649f2f02a0b244ae6aef8dc29325a2810aa26b07af8374dc2a9faf57e3"}, + {file = "pyzmq-25.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7cff25c5b315e63b07a36f0c2bab32c58eafbe57d0dce61b614ef4c76058c115"}, + {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1579413ae492b05de5a6174574f8c44c2b9b122a42015c5292afa4be2507f28"}, + {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3d0a409d3b28607cc427aa5c30a6f1e4452cc44e311f843e05edb28ab5e36da0"}, + {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21eb4e609a154a57c520e3d5bfa0d97e49b6872ea057b7c85257b11e78068222"}, + {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:034239843541ef7a1aee0c7b2cb7f6aafffb005ede965ae9cbd49d5ff4ff73cf"}, + {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f8115e303280ba09f3898194791a153862cbf9eef722ad8f7f741987ee2a97c7"}, + {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a5d26fe8f32f137e784f768143728438877d69a586ddeaad898558dc971a5ae"}, + {file = "pyzmq-25.1.1-cp37-cp37m-win32.whl", hash = "sha256:f32260e556a983bc5c7ed588d04c942c9a8f9c2e99213fec11a031e316874c7e"}, + {file = "pyzmq-25.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:abf34e43c531bbb510ae7e8f5b2b1f2a8ab93219510e2b287a944432fad135f3"}, + {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:87e34f31ca8f168c56d6fbf99692cc8d3b445abb5bfd08c229ae992d7547a92a"}, + {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c9c6c9b2c2f80747a98f34ef491c4d7b1a8d4853937bb1492774992a120f475d"}, + {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5619f3f5a4db5dbb572b095ea3cb5cc035335159d9da950830c9c4db2fbb6995"}, + {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a34d2395073ef862b4032343cf0c32a712f3ab49d7ec4f42c9661e0294d106f"}, + {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f0e6b78220aba09815cd1f3a32b9c7cb3e02cb846d1cfc526b6595f6046618"}, + {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3669cf8ee3520c2f13b2e0351c41fea919852b220988d2049249db10046a7afb"}, + {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2d163a18819277e49911f7461567bda923461c50b19d169a062536fffe7cd9d2"}, + {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:df27ffddff4190667d40de7beba4a950b5ce78fe28a7dcc41d6f8a700a80a3c0"}, + {file = "pyzmq-25.1.1-cp38-cp38-win32.whl", hash = "sha256:a382372898a07479bd34bda781008e4a954ed8750f17891e794521c3e21c2e1c"}, + {file = "pyzmq-25.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:52533489f28d62eb1258a965f2aba28a82aa747202c8fa5a1c7a43b5db0e85c1"}, + {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:03b3f49b57264909aacd0741892f2aecf2f51fb053e7d8ac6767f6c700832f45"}, + {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:330f9e188d0d89080cde66dc7470f57d1926ff2fb5576227f14d5be7ab30b9fa"}, + {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2ca57a5be0389f2a65e6d3bb2962a971688cbdd30b4c0bd188c99e39c234f414"}, + {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d457aed310f2670f59cc5b57dcfced452aeeed77f9da2b9763616bd57e4dbaae"}, + {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c56d748ea50215abef7030c72b60dd723ed5b5c7e65e7bc2504e77843631c1a6"}, + {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8f03d3f0d01cb5a018debeb412441996a517b11c5c17ab2001aa0597c6d6882c"}, + {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:820c4a08195a681252f46926de10e29b6bbf3e17b30037bd4250d72dd3ddaab8"}, + {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17ef5f01d25b67ca8f98120d5fa1d21efe9611604e8eb03a5147360f517dd1e2"}, + {file = "pyzmq-25.1.1-cp39-cp39-win32.whl", hash = "sha256:04ccbed567171579ec2cebb9c8a3e30801723c575601f9a990ab25bcac6b51e2"}, + {file = "pyzmq-25.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:e61f091c3ba0c3578411ef505992d356a812fb200643eab27f4f70eed34a29ef"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ade6d25bb29c4555d718ac6d1443a7386595528c33d6b133b258f65f963bb0f6"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0c95ddd4f6e9fca4e9e3afaa4f9df8552f0ba5d1004e89ef0a68e1f1f9807c7"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48e466162a24daf86f6b5ca72444d2bf39a5e58da5f96370078be67c67adc978"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abc719161780932c4e11aaebb203be3d6acc6b38d2f26c0f523b5b59d2fc1996"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ccf825981640b8c34ae54231b7ed00271822ea1c6d8ba1090ebd4943759abf5"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c2f20ce161ebdb0091a10c9ca0372e023ce24980d0e1f810f519da6f79c60800"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:deee9ca4727f53464daf089536e68b13e6104e84a37820a88b0a057b97bba2d2"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aa8d6cdc8b8aa19ceb319aaa2b660cdaccc533ec477eeb1309e2a291eaacc43a"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:019e59ef5c5256a2c7378f2fb8560fc2a9ff1d315755204295b2eab96b254d0a"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:b9af3757495c1ee3b5c4e945c1df7be95562277c6e5bccc20a39aec50f826cd0"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:548d6482dc8aadbe7e79d1b5806585c8120bafa1ef841167bc9090522b610fa6"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:057e824b2aae50accc0f9a0570998adc021b372478a921506fddd6c02e60308e"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2243700cc5548cff20963f0ca92d3e5e436394375ab8a354bbea2b12911b20b0"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79986f3b4af059777111409ee517da24a529bdbd46da578b33f25580adcff728"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:11d58723d44d6ed4dd677c5615b2ffb19d5c426636345567d6af82be4dff8a55"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:49d238cf4b69652257db66d0c623cd3e09b5d2e9576b56bc067a396133a00d4a"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fedbdc753827cf014c01dbbee9c3be17e5a208dcd1bf8641ce2cd29580d1f0d4"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc16ac425cc927d0a57d242589f87ee093884ea4804c05a13834d07c20db203c"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11c1d2aed9079c6b0c9550a7257a836b4a637feb334904610f06d70eb44c56d2"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e8a701123029cc240cea61dd2d16ad57cab4691804143ce80ecd9286b464d180"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61706a6b6c24bdece85ff177fec393545a3191eeda35b07aaa1458a027ad1304"}, + {file = "pyzmq-25.1.1.tar.gz", hash = "sha256:259c22485b71abacdfa8bf79720cd7bcf4b9d128b30ea554f01ae71fdbfdaa23"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "shapely" +version = "2.0.1" +description = "Manipulation and analysis of geometric objects" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shapely-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b06d031bc64149e340448fea25eee01360a58936c89985cf584134171e05863f"}, + {file = "shapely-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9a6ac34c16f4d5d3c174c76c9d7614ec8fe735f8f82b6cc97a46b54f386a86bf"}, + {file = "shapely-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:865bc3d7cc0ea63189d11a0b1120d1307ed7a64720a8bfa5be2fde5fc6d0d33f"}, + {file = "shapely-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45b4833235b90bc87ee26c6537438fa77559d994d2d3be5190dd2e54d31b2820"}, + {file = "shapely-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce88ec79df55430e37178a191ad8df45cae90b0f6972d46d867bf6ebbb58cc4d"}, + {file = "shapely-2.0.1-cp310-cp310-win32.whl", hash = "sha256:01224899ff692a62929ef1a3f5fe389043e262698a708ab7569f43a99a48ae82"}, + {file = "shapely-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:da71de5bf552d83dcc21b78cc0020e86f8d0feea43e202110973987ffa781c21"}, + {file = "shapely-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:502e0a607f1dcc6dee0125aeee886379be5242c854500ea5fd2e7ac076b9ce6d"}, + {file = "shapely-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7d3bbeefd8a6a1a1017265d2d36f8ff2d79d0162d8c141aa0d37a87063525656"}, + {file = "shapely-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f470a130d6ddb05b810fc1776d918659407f8d025b7f56d2742a596b6dffa6c7"}, + {file = "shapely-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4641325e065fd3e07d55677849c9ddfd0cf3ee98f96475126942e746d55b17c8"}, + {file = "shapely-2.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90cfa4144ff189a3c3de62e2f3669283c98fb760cfa2e82ff70df40f11cadb39"}, + {file = "shapely-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70a18fc7d6418e5aea76ac55dce33f98e75bd413c6eb39cfed6a1ba36469d7d4"}, + {file = "shapely-2.0.1-cp311-cp311-win32.whl", hash = "sha256:09d6c7763b1bee0d0a2b84bb32a4c25c6359ad1ac582a62d8b211e89de986154"}, + {file = "shapely-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:d8f55f355be7821dade839df785a49dc9f16d1af363134d07eb11e9207e0b189"}, + {file = "shapely-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:83a8ec0ee0192b6e3feee9f6a499d1377e9c295af74d7f81ecba5a42a6b195b7"}, + {file = "shapely-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a529218e72a3dbdc83676198e610485fdfa31178f4be5b519a8ae12ea688db14"}, + {file = "shapely-2.0.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91575d97fd67391b85686573d758896ed2fc7476321c9d2e2b0c398b628b961c"}, + {file = "shapely-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8b0d834b11be97d5ab2b4dceada20ae8e07bcccbc0f55d71df6729965f406ad"}, + {file = "shapely-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:b4f0711cc83734c6fad94fc8d4ec30f3d52c1787b17d9dca261dc841d4731c64"}, + {file = "shapely-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:05c51a29336e604c084fb43ae5dbbfa2c0ef9bd6fedeae0a0d02c7b57a56ba46"}, + {file = "shapely-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b519cf3726ddb6c67f6a951d1bb1d29691111eaa67ea19ddca4d454fbe35949c"}, + {file = "shapely-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:193a398d81c97a62fc3634a1a33798a58fd1dcf4aead254d080b273efbb7e3ff"}, + {file = "shapely-2.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e55698e0ed95a70fe9ff9a23c763acfe0bf335b02df12142f74e4543095e9a9b"}, + {file = "shapely-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f32a748703e7bf6e92dfa3d2936b2fbfe76f8ce5f756e24f49ef72d17d26ad02"}, + {file = "shapely-2.0.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a34a23d6266ca162499e4a22b79159dc0052f4973d16f16f990baa4d29e58b6"}, + {file = "shapely-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d173d24e85e51510e658fb108513d5bc11e3fd2820db6b1bd0522266ddd11f51"}, + {file = "shapely-2.0.1-cp38-cp38-win32.whl", hash = "sha256:3cb256ae0c01b17f7bc68ee2ffdd45aebf42af8992484ea55c29a6151abe4386"}, + {file = "shapely-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:c7eed1fb3008a8a4a56425334b7eb82651a51f9e9a9c2f72844a2fb394f38a6c"}, + {file = "shapely-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ac1dfc397475d1de485e76de0c3c91cc9d79bd39012a84bb0f5e8a199fc17bef"}, + {file = "shapely-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:33403b8896e1d98aaa3a52110d828b18985d740cc9f34f198922018b1e0f8afe"}, + {file = "shapely-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2569a4b91caeef54dd5ae9091ae6f63526d8ca0b376b5bb9fd1a3195d047d7d4"}, + {file = "shapely-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a70a614791ff65f5e283feed747e1cc3d9e6c6ba91556e640636bbb0a1e32a71"}, + {file = "shapely-2.0.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c43755d2c46b75a7b74ac6226d2cc9fa2a76c3263c5ae70c195c6fb4e7b08e79"}, + {file = "shapely-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad81f292fffbd568ae71828e6c387da7eb5384a79db9b4fde14dd9fdeffca9a"}, + {file = "shapely-2.0.1-cp39-cp39-win32.whl", hash = "sha256:b50c401b64883e61556a90b89948297f1714dbac29243d17ed9284a47e6dd731"}, + {file = "shapely-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:bca57b683e3d94d0919e2f31e4d70fdfbb7059650ef1b431d9f4e045690edcd5"}, + {file = "shapely-2.0.1.tar.gz", hash = "sha256:66a6b1a3e72ece97fc85536a281476f9b7794de2e646ca8a4517e2e3c1446893"}, +] + +[package.dependencies] +numpy = ">=1.14" + +[package.extras] +docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "stack-data" +version = "0.6.2" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.2-py3-none-any.whl", hash = "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8"}, + {file = "stack_data-0.6.2.tar.gz", hash = "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tornado" +version = "6.3.3" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.3.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d"}, + {file = "tornado-6.3.3-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a"}, + {file = "tornado-6.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f"}, + {file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a"}, + {file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2"}, + {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0"}, + {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16"}, + {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17"}, + {file = "tornado-6.3.3-cp38-abi3-win32.whl", hash = "sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3"}, + {file = "tornado-6.3.3-cp38-abi3-win_amd64.whl", hash = "sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5"}, + {file = "tornado-6.3.3.tar.gz", hash = "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe"}, +] + +[[package]] +name = "traitlets" +version = "5.10.0" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.10.0-py3-none-any.whl", hash = "sha256:417745a96681fbb358e723d5346a547521f36e9bd0d50ba7ab368fff5d67aa54"}, + {file = "traitlets-5.10.0.tar.gz", hash = "sha256:f584ea209240466e66e91f3c81aa7d004ba4cf794990b0c775938a1544217cd1"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.5.1)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "typing-extensions" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, +] + +[[package]] +name = "wcwidth" +version = "0.2.6" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, +] + +[[package]] +name = "zipp" +version = "3.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "23170c9f47585417860090b165381fbcbab62c728a3fda98ca87dc75631b5a2d" +content-hash = "2dc551ab92c73a1bb27dbd6182d6a84249fe74151a813edf2e66684db040f037" diff --git a/geoarrow/shapely/pyproject.toml b/geoarrow/shapely/pyproject.toml index b2b8ceb..df7f55b 100644 --- a/geoarrow/shapely/pyproject.toml +++ b/geoarrow/shapely/pyproject.toml @@ -5,15 +5,22 @@ description = "Interop between GeoArrow and Shapely geometries in Python" authors = [] readme = "README.md" packages = [ - { include = "shapely", from = "geoarrow" } + { include = "geoarrow" } ] [tool.poetry.dependencies] python = "^3.8" pyarrow = "^13" +shapely = "^2.0.1" +pyproj = "^3.4" [tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] +ipykernel = "^6.25.2" +black = "^23.9.1" +pytest = "^7.4.2" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" From b0f48ce7718a50b354763361d01bf629ff7dcefe Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 24 Sep 2023 16:48:38 -0400 Subject: [PATCH 03/10] Add initial point tests --- geoarrow/shapely/geoarrow/shapely/__init__.py | 30 ++++++++++++++++ .../geoarrow/shapely/extension_array.py | 15 ++++---- .../geoarrow/shapely/extension_types.py | 23 ------------ geoarrow/shapely/tests/__init__.py | 0 geoarrow/shapely/tests/test_point.py | 35 +++++++++++++++++++ 5 files changed, 74 insertions(+), 29 deletions(-) create mode 100644 geoarrow/shapely/tests/__init__.py create mode 100644 geoarrow/shapely/tests/test_point.py diff --git a/geoarrow/shapely/geoarrow/shapely/__init__.py b/geoarrow/shapely/geoarrow/shapely/__init__.py index 178827d..ef5b429 100644 --- a/geoarrow/shapely/geoarrow/shapely/__init__.py +++ b/geoarrow/shapely/geoarrow/shapely/__init__.py @@ -1,9 +1,39 @@ from .extension_array import PointArray from .extension_types import ( + CoordinateDimension, LineStringType, MultiLineStringType, MultiPointType, MultiPolygonType, PointType, PolygonType, + construct_geometry_array, ) + + +def register_geometry_extension_types(): + import pyarrow as pa + + for geom_type_class in [ + PointType, + LineStringType, + PolygonType, + MultiPointType, + MultiLineStringType, + MultiPolygonType, + ]: + # Provide a default to go into the registry, but at runtime, we can choose other + # type formulations + geom_type_instance = geom_type_class( + interleaved=True, dims=CoordinateDimension.XY + ) + try: + pa.register_extension_type(geom_type_instance) + + # If already registered with this id, unregister and re register + except pa.ArrowKeyError: + pa.unregister_extension_type(geom_type_instance.extension_name) + pa.register_extension_type(geom_type_instance) + + +register_geometry_extension_types() diff --git a/geoarrow/shapely/geoarrow/shapely/extension_array.py b/geoarrow/shapely/geoarrow/shapely/extension_array.py index e300702..eb63236 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_array.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_array.py @@ -19,13 +19,16 @@ def to_shapely(self) -> NDArray[np.object_]: ) return shapely.from_ragged_array(GeometryType.POINT, flat_coords, None) + class LineStringArray(pa.ExtensionArray): def to_shapely(self) -> NDArray[np.object_]: """Convert to an array of shapely geometries""" coord_dimension = self.type.coord_dimension - flat_coords = ( - self.storage.flatten() - .to_numpy(zero_copy_only=True, writable=False) - .reshape(-1, len(coord_dimension)) - ) - return shapely.from_ragged_array(GeometryType.POINT, flat_coords, None) + # TODO: + + # flat_coords = ( + # self.storage.flatten() + # .to_numpy(zero_copy_only=True, writable=False) + # .reshape(-1, len(coord_dimension)) + # ) + # return shapely.from_ragged_array(GeometryType.POINT, flat_coords, None) diff --git a/geoarrow/shapely/geoarrow/shapely/extension_types.py b/geoarrow/shapely/geoarrow/shapely/extension_types.py index ef3c747..a371370 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_types.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_types.py @@ -273,29 +273,6 @@ def __init__( super().__init__(storage_type, self.extension_name) -def register_geometry_extension_types(): - for geom_type_class in [ - PointType, - LineStringType, - PolygonType, - MultiPointType, - MultiLineStringType, - MultiPolygonType, - ]: - # Provide a default to go into the registry, but at runtime, we can choose other - # type formulations - geom_type_instance = geom_type_class( - interleaved=True, dims=CoordinateDimension.XY - ) - try: - pa.register_extension_type(geom_type_instance) - - # If already registered with this id, unregister and re register - except pa.ArrowKeyError: - pa.unregister_extension_type(geom_type_instance.extension_name) - pa.register_extension_type(geom_type_instance) - - # register_geometry_extension_types() # shapely_arr = shapely.points([[1, 2], [3, 4], [5, 6], [5, 6]]) # point_arr = construct_geometry_array(shapely_arr) diff --git a/geoarrow/shapely/tests/__init__.py b/geoarrow/shapely/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/geoarrow/shapely/tests/test_point.py b/geoarrow/shapely/tests/test_point.py new file mode 100644 index 0000000..4f39ff4 --- /dev/null +++ b/geoarrow/shapely/tests/test_point.py @@ -0,0 +1,35 @@ +import numpy as np +import shapely +from numpy.typing import NDArray +from shapely.testing import assert_geometries_equal + +from geoarrow.shapely import PointArray, construct_geometry_array +from geoarrow.shapely import CoordinateDimension + + +def points_2d() -> NDArray[np.object_]: + return shapely.points([0, 1, 2, 3], [4, 5, 6, 7]) + + +def points_3d() -> NDArray[np.object_]: + return shapely.points([0, 1, 2, 3], [4, 5, 6, 7], [9, 10, 11, 12]) + + +def test_round_trip_2d(): + shapely_geoms = points_2d() + point_array = construct_geometry_array(shapely_geoms) + assert isinstance(point_array, PointArray) + assert point_array.type.coord_dimension == CoordinateDimension.XY + + new_shapely_geoms = point_array.to_shapely() + assert_geometries_equal(shapely_geoms, new_shapely_geoms) + + +def test_round_trip_3d(): + shapely_geoms = points_3d() + point_array = construct_geometry_array(shapely_geoms) + assert isinstance(point_array, PointArray) + assert point_array.type.coord_dimension == CoordinateDimension.XYZ + + new_shapely_geoms = point_array.to_shapely() + assert_geometries_equal(shapely_geoms, new_shapely_geoms) From e4b426b884eb69982f01cba8f611fd816b831e59 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 24 Sep 2023 17:04:09 -0400 Subject: [PATCH 04/10] Implement point scalar --- geoarrow/shapely/geoarrow/shapely/__init__.py | 1 + .../geoarrow/shapely/extension_scalar.py | 23 +++++++++++++++++++ .../geoarrow/shapely/extension_types.py | 20 ++++------------ geoarrow/shapely/tests/test_point.py | 8 +++++++ 4 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 geoarrow/shapely/geoarrow/shapely/extension_scalar.py diff --git a/geoarrow/shapely/geoarrow/shapely/__init__.py b/geoarrow/shapely/geoarrow/shapely/__init__.py index ef5b429..e5eb754 100644 --- a/geoarrow/shapely/geoarrow/shapely/__init__.py +++ b/geoarrow/shapely/geoarrow/shapely/__init__.py @@ -1,4 +1,5 @@ from .extension_array import PointArray +from .extension_scalar import PointScalar from .extension_types import ( CoordinateDimension, LineStringType, diff --git a/geoarrow/shapely/geoarrow/shapely/extension_scalar.py b/geoarrow/shapely/geoarrow/shapely/extension_scalar.py new file mode 100644 index 0000000..73df76b --- /dev/null +++ b/geoarrow/shapely/geoarrow/shapely/extension_scalar.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +import pyarrow as pa + +import shapely +from shapely import GeometryType + + +# TODO: change the Python repr of the pyarrow scalar value so that it doesn't call as_py +# and create a new GEOS object every time it prints the scalar? + +class PointScalar(pa.ExtensionScalar): + def to_shapely(self) -> shapely.Point: + return self.as_py() + + def as_py(self) -> shapely.Point: + geoms = shapely.from_ragged_array( + GeometryType.POINT, + self.value.values.to_numpy().reshape(-1, len(self.type.coord_dimension)), + None, + ) + assert len(geoms) == 1 + return geoms[0] diff --git a/geoarrow/shapely/geoarrow/shapely/extension_types.py b/geoarrow/shapely/geoarrow/shapely/extension_types.py index a371370..b010c3d 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_types.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_types.py @@ -6,10 +6,10 @@ from numpy.typing import NDArray import shapely +from geoarrow.shapely.extension_array import PointArray +from geoarrow.shapely.extension_scalar import PointScalar from shapely import GeometryType -from .extension_array import PointArray - class CoordinateDimension(str, Enum): XY = "xy" @@ -202,6 +202,9 @@ def __init__(self, *, interleaved: bool, dims: CoordinateDimension): def __arrow_ext_class__(self): return PointArray + def __arrow_ext_scalar_class__(self): + return PointScalar + class LineStringType(BaseGeometryType): extension_name = "geoarrow.linestring" @@ -273,19 +276,6 @@ def __init__( super().__init__(storage_type, self.extension_name) -# register_geometry_extension_types() -# shapely_arr = shapely.points([[1, 2], [3, 4], [5, 6], [5, 6]]) -# point_arr = construct_geometry_array(shapely_arr) -# point_arr.to_shapely() - -# x = point_arr.storage.flatten() -# x.to_numpy? -# dir(x) - -# point_arr.to_shapely() -# point_arr.type.coord_dimension - - def construct_geometry_array( shapely_arr: NDArray[np.object_], include_z: Optional[bool] = None ): diff --git a/geoarrow/shapely/tests/test_point.py b/geoarrow/shapely/tests/test_point.py index 4f39ff4..b365491 100644 --- a/geoarrow/shapely/tests/test_point.py +++ b/geoarrow/shapely/tests/test_point.py @@ -18,18 +18,26 @@ def points_3d() -> NDArray[np.object_]: def test_round_trip_2d(): shapely_geoms = points_2d() point_array = construct_geometry_array(shapely_geoms) + assert isinstance(point_array, PointArray) assert point_array.type.coord_dimension == CoordinateDimension.XY new_shapely_geoms = point_array.to_shapely() assert_geometries_equal(shapely_geoms, new_shapely_geoms) + scalar = point_array[0] + assert scalar.as_py() == shapely_geoms[0] + def test_round_trip_3d(): shapely_geoms = points_3d() point_array = construct_geometry_array(shapely_geoms) + assert isinstance(point_array, PointArray) assert point_array.type.coord_dimension == CoordinateDimension.XYZ new_shapely_geoms = point_array.to_shapely() assert_geometries_equal(shapely_geoms, new_shapely_geoms) + + scalar = point_array[0] + assert scalar.as_py() == shapely_geoms[0] From bd373e4ef1defa0112aaf49a9b1d85aef228d9bb Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 24 Sep 2023 17:47:39 -0400 Subject: [PATCH 05/10] wip array and scalar classes --- geoarrow/shapely/geoarrow/shapely/__init__.py | 18 ++- .../geoarrow/shapely/extension_array.py | 114 +++++++++++++++-- .../geoarrow/shapely/extension_scalar.py | 116 +++++++++++++++++- .../geoarrow/shapely/extension_types.py | 60 +++++++-- geoarrow/shapely/tests/test_linestring.py | 30 +++++ geoarrow/shapely/tests/test_polygon.py | 35 ++++++ 6 files changed, 348 insertions(+), 25 deletions(-) create mode 100644 geoarrow/shapely/tests/test_linestring.py create mode 100644 geoarrow/shapely/tests/test_polygon.py diff --git a/geoarrow/shapely/geoarrow/shapely/__init__.py b/geoarrow/shapely/geoarrow/shapely/__init__.py index e5eb754..7db3b96 100644 --- a/geoarrow/shapely/geoarrow/shapely/__init__.py +++ b/geoarrow/shapely/geoarrow/shapely/__init__.py @@ -1,5 +1,19 @@ -from .extension_array import PointArray -from .extension_scalar import PointScalar +from .extension_array import ( + LineStringArray, + MultiLineStringArray, + MultiPointArray, + MultiPolygonArray, + PointArray, + PolygonArray, +) +from .extension_scalar import ( + LineString, + MultiLineString, + MultiPoint, + MultiPolygon, + Point, + Polygon, +) from .extension_types import ( CoordinateDimension, LineStringType, diff --git a/geoarrow/shapely/geoarrow/shapely/extension_array.py b/geoarrow/shapely/geoarrow/shapely/extension_array.py index eb63236..5a13370 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_array.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_array.py @@ -11,11 +11,10 @@ class PointArray(pa.ExtensionArray): def to_shapely(self) -> NDArray[np.object_]: """Convert to an array of shapely geometries""" - coord_dimension = self.type.coord_dimension flat_coords = ( self.storage.flatten() - .to_numpy(zero_copy_only=True, writable=False) - .reshape(-1, len(coord_dimension)) + .to_numpy() + .reshape(-1, len(self.type.coord_dimension)) ) return shapely.from_ragged_array(GeometryType.POINT, flat_coords, None) @@ -23,12 +22,103 @@ def to_shapely(self) -> NDArray[np.object_]: class LineStringArray(pa.ExtensionArray): def to_shapely(self) -> NDArray[np.object_]: """Convert to an array of shapely geometries""" - coord_dimension = self.type.coord_dimension - # TODO: - - # flat_coords = ( - # self.storage.flatten() - # .to_numpy(zero_copy_only=True, writable=False) - # .reshape(-1, len(coord_dimension)) - # ) - # return shapely.from_ragged_array(GeometryType.POINT, flat_coords, None) + + # TODO: shapely fails on version 2.0.1 with a read-only coords buffer, so we + # make a copy here by setting writable=True. + # ValueError: buffer source array is read-only + flat_coords = ( + self.storage.flatten() + .flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + geom_offsets = self.storage.offsets.to_numpy() + return shapely.from_ragged_array( + GeometryType.LINESTRING, flat_coords, (geom_offsets,) + ) + + +class PolygonArray(pa.ExtensionArray): + def to_shapely(self) -> NDArray[np.object_]: + """Convert to an array of shapely geometries""" + + # TODO: shapely fails on version 2.0.1 with a read-only coords buffer, so we + # make a copy here by setting writable=True. + # ValueError: buffer source array is read-only + flat_coords = ( + self.storage.flatten() + .flatten() + .flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + geom_offsets = self.storage.offsets.to_numpy() + ring_offsets = self.storage.flatten().offsets.to_numpy() + return shapely.from_ragged_array( + GeometryType.POLYGON, flat_coords, (ring_offsets, geom_offsets) + ) + + +class MultiPointArray(pa.ExtensionArray): + def to_shapely(self) -> NDArray[np.object_]: + """Convert to an array of shapely geometries""" + + # TODO: shapely fails on version 2.0.1 with a read-only coords buffer, so we + # make a copy here by setting writable=True. + # ValueError: buffer source array is read-only + flat_coords = ( + self.storage.flatten() + .flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + geom_offsets = self.storage.offsets.to_numpy() + return shapely.from_ragged_array( + GeometryType.MULTIPOINT, flat_coords, (geom_offsets,) + ) + + +class MultiLineStringArray(pa.ExtensionArray): + def to_shapely(self) -> NDArray[np.object_]: + """Convert to an array of shapely geometries""" + + # TODO: shapely fails on version 2.0.1 with a read-only coords buffer, so we + # make a copy here by setting writable=True. + # ValueError: buffer source array is read-only + flat_coords = ( + self.storage.flatten() + .flatten() + .flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + geom_offsets = self.storage.offsets.to_numpy() + ring_offsets = self.storage.flatten().offsets.to_numpy() + return shapely.from_ragged_array( + GeometryType.MULTILINESTRING, flat_coords, (ring_offsets, geom_offsets) + ) + + +class MultiPolygonArray(pa.ExtensionArray): + def to_shapely(self) -> NDArray[np.object_]: + """Convert to an array of shapely geometries""" + + # TODO: shapely fails on version 2.0.1 with a read-only coords buffer, so we + # make a copy here by setting writable=True. + # ValueError: buffer source array is read-only + flat_coords = ( + self.storage.flatten() + .flatten() + .flatten() + .flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + geom_offsets = self.storage.offsets.to_numpy() + polygon_offsets = self.storage.flatten().offsets.to_numpy() + ring_offsets = self.storage.flatten().flatten().offsets.to_numpy() + return shapely.from_ragged_array( + GeometryType.MULTIPOLYGON, + flat_coords, + (ring_offsets, polygon_offsets, geom_offsets), + ) diff --git a/geoarrow/shapely/geoarrow/shapely/extension_scalar.py b/geoarrow/shapely/geoarrow/shapely/extension_scalar.py index 73df76b..a875dbb 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_scalar.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_scalar.py @@ -1,23 +1,133 @@ from __future__ import annotations +import numpy as np import pyarrow as pa import shapely from shapely import GeometryType - # TODO: change the Python repr of the pyarrow scalar value so that it doesn't call as_py # and create a new GEOS object every time it prints the scalar? -class PointScalar(pa.ExtensionScalar): +# TODO: support separated coords; right now it assumes interleaved + + +class Point(pa.ExtensionScalar): def to_shapely(self) -> shapely.Point: return self.as_py() def as_py(self) -> shapely.Point: + coords = self.value.values.to_numpy().reshape( + -1, len(self.type.coord_dimension) + ) geoms = shapely.from_ragged_array( GeometryType.POINT, - self.value.values.to_numpy().reshape(-1, len(self.type.coord_dimension)), + coords, None, ) assert len(geoms) == 1 return geoms[0] + + +class LineString(pa.ExtensionScalar): + def to_shapely(self) -> shapely.LineString: + return self.as_py() + + def as_py(self) -> shapely.LineString: + coords = ( + self.value.values.flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + geom_offsets = np.array([0, coords.shape[0]], dtype=np.int32) + geoms = shapely.from_ragged_array( + GeometryType.LINESTRING, + coords, + (geom_offsets,), + ) + assert len(geoms) == 1 + return geoms[0] + + +class Polygon(pa.ExtensionScalar): + def to_shapely(self) -> shapely.Polygon: + return self.as_py() + + def as_py(self) -> shapely.Polygon: + coords = ( + self.value.values.flatten() + .flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + ring_offsets = self.value.values.offsets + geom_offsets = np.array([0, 1], dtype=np.int32) + geoms = shapely.from_ragged_array( + GeometryType.POLYGON, + coords, + (ring_offsets, geom_offsets), + ) + assert len(geoms) == 1 + return geoms[0] + + +class MultiPoint(pa.ExtensionScalar): + def to_shapely(self) -> shapely.MultiPoint: + return self.as_py() + + def as_py(self) -> shapely.MultiPoint: + coords = ( + self.value.values.flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + geom_offsets = np.array([0, coords.shape[0]], dtype=np.int32) + geoms = shapely.from_ragged_array( + GeometryType.MULTIPOINT, + coords, + (geom_offsets,), + ) + assert len(geoms) == 1 + return geoms[0] + + +class MultiLineString(pa.ExtensionScalar): + def to_shapely(self) -> shapely.MultiLineString: + return self.as_py() + + def as_py(self) -> shapely.MultiLineString: + coords = ( + self.value.values.flatten() + .flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + ring_offsets = self.value.values.offsets + geom_offsets = np.array([0, 1], dtype=np.int32) + geoms = shapely.from_ragged_array( + GeometryType.MULTILINESTRING, + coords, + (ring_offsets, geom_offsets), + ) + assert len(geoms) == 1 + return geoms[0] + + +class MultiPolygon(pa.ExtensionScalar): + def to_shapely(self) -> shapely.MultiPolygon: + return self.as_py() + + def as_py(self) -> shapely.MultiPolygon: + coords = ( + self.value.values.flatten() + .to_numpy(zero_copy_only=False, writable=True) + .reshape(-1, len(self.type.coord_dimension)) + ) + geom_offsets = np.array([0, coords.shape[0]], dtype=np.int32) + geoms = shapely.from_ragged_array( + GeometryType.MULTIPOLYGON, + coords, + (geom_offsets,), + ) + assert len(geoms) == 1 + return geoms[0] diff --git a/geoarrow/shapely/geoarrow/shapely/extension_types.py b/geoarrow/shapely/geoarrow/shapely/extension_types.py index b010c3d..55bd22d 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_types.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_types.py @@ -6,8 +6,22 @@ from numpy.typing import NDArray import shapely -from geoarrow.shapely.extension_array import PointArray -from geoarrow.shapely.extension_scalar import PointScalar +from geoarrow.shapely.extension_array import ( + LineStringArray, + MultiLineStringArray, + MultiPointArray, + MultiPolygonArray, + PointArray, + PolygonArray, +) +from geoarrow.shapely.extension_scalar import ( + LineString, + MultiLineString, + MultiPoint, + MultiPolygon, + Point, + Polygon, +) from shapely import GeometryType @@ -203,7 +217,7 @@ def __arrow_ext_class__(self): return PointArray def __arrow_ext_scalar_class__(self): - return PointScalar + return Point class LineStringType(BaseGeometryType): @@ -219,6 +233,12 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_class__(self): + return LineStringArray + + def __arrow_ext_scalar_class__(self): + return LineString + class PolygonType(BaseGeometryType): extension_name = "geoarrow.polygon" @@ -233,6 +253,12 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_class__(self): + return PolygonArray + + def __arrow_ext_scalar_class__(self): + return Polygon + class MultiPointType(BaseGeometryType): extension_name = "geoarrow.multipoint" @@ -247,6 +273,12 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_class__(self): + return MultiPointArray + + def __arrow_ext_scalar_class__(self): + return MultiPoint + class MultiLineStringType(BaseGeometryType): extension_name = "geoarrow.multilinestring" @@ -261,6 +293,12 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_class__(self): + return MultiLineStringArray + + def __arrow_ext_scalar_class__(self): + return MultiLineString + class MultiPolygonType(BaseGeometryType): extension_name = "geoarrow.multipolygon" @@ -275,6 +313,12 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_class__(self): + return MultiPolygonArray + + def __arrow_ext_scalar_class__(self): + return MultiPolygon + def construct_geometry_array( shapely_arr: NDArray[np.object_], include_z: Optional[bool] = None @@ -299,7 +343,7 @@ def construct_geometry_array( elif geom_type == GeometryType.LINESTRING: assert len(offsets) == 1, "Expected one offsets array" (offsets1,) = offsets - _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) parr = pa.ListArray.from_arrays(pa.array(offsets1), _parr) return pa.ExtensionArray.from_storage( LineStringType(interleaved=True, dims=dims), parr @@ -308,7 +352,7 @@ def construct_geometry_array( elif geom_type == GeometryType.POLYGON: assert len(offsets) == 2, "Expected two offsets arrays" offsets1, offsets2 = offsets - _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) _parr1 = pa.ListArray.from_arrays(pa.array(offsets1), _parr) parr = pa.ListArray.from_arrays(pa.array(offsets2), _parr1) return pa.ExtensionArray.from_storage( @@ -318,7 +362,7 @@ def construct_geometry_array( elif geom_type == GeometryType.MULTIPOINT: assert len(offsets) == 1, "Expected one offsets array" (offsets1,) = offsets - _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) parr = pa.ListArray.from_arrays(pa.array(offsets1), _parr) return pa.ExtensionArray.from_storage( MultiPointType(interleaved=True, dims=dims), parr @@ -327,7 +371,7 @@ def construct_geometry_array( elif geom_type == GeometryType.MULTILINESTRING: assert len(offsets) == 2, "Expected two offsets arrays" offsets1, offsets2 = offsets - _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) _parr1 = pa.ListArray.from_arrays(pa.array(offsets1), _parr) parr = pa.ListArray.from_arrays(pa.array(offsets2), _parr1) return pa.ExtensionArray.from_storage( @@ -337,7 +381,7 @@ def construct_geometry_array( elif geom_type == GeometryType.MULTIPOLYGON: assert len(offsets) == 3, "Expected three offsets arrays" offsets1, offsets2, offsets3 = offsets - _parr = pa.FixedSizeListArray.from_arrays(coords, 2) + _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) _parr1 = pa.ListArray.from_arrays(pa.array(offsets1), _parr) _parr2 = pa.ListArray.from_arrays(pa.array(offsets2), _parr1) parr = pa.ListArray.from_arrays(pa.array(offsets3), _parr2) diff --git a/geoarrow/shapely/tests/test_linestring.py b/geoarrow/shapely/tests/test_linestring.py new file mode 100644 index 0000000..8cc8002 --- /dev/null +++ b/geoarrow/shapely/tests/test_linestring.py @@ -0,0 +1,30 @@ +import numpy as np +import shapely +from numpy.typing import NDArray +from shapely.testing import assert_geometries_equal + +from geoarrow.shapely import ( + CoordinateDimension, + LineStringArray, + construct_geometry_array, +) + + +def linestrings_2d() -> NDArray[np.object_]: + ls0 = shapely.LineString([[0, 1], [1, 2], [3, 4]]) + ls1 = shapely.LineString([[3, 4], [5, 6]]) + return np.array([ls0, ls1]) + + +def test_round_trip_2d(): + shapely_geoms = linestrings_2d() + line_string_array = construct_geometry_array(shapely_geoms) + + assert isinstance(line_string_array, LineStringArray) + assert line_string_array.type.coord_dimension == CoordinateDimension.XY + + new_shapely_geoms = line_string_array.to_shapely() + assert_geometries_equal(shapely_geoms, new_shapely_geoms) + + scalar = line_string_array[0] + assert scalar.as_py() == shapely_geoms[0] diff --git a/geoarrow/shapely/tests/test_polygon.py b/geoarrow/shapely/tests/test_polygon.py new file mode 100644 index 0000000..1ac245b --- /dev/null +++ b/geoarrow/shapely/tests/test_polygon.py @@ -0,0 +1,35 @@ +import numpy as np +import shapely +from numpy.typing import NDArray +from shapely.testing import assert_geometries_equal + +from geoarrow.shapely import ( + CoordinateDimension, + PolygonArray, + construct_geometry_array, +) + + +def polygons_2d() -> NDArray[np.object_]: + p0 = shapely.box(0, 1, 5, 10) + + ext_ring = shapely.LinearRing(shapely.box(10, 20, 30, 40).exterior.coords) + int_ring = shapely.LinearRing(shapely.box(12, 22, 28, 38).exterior.coords[::-1]) + p1 = shapely.Polygon(ext_ring, [int_ring]) + assert p1.is_valid + + return np.array([p0, p1]) + + +def test_round_trip_2d(): + shapely_geoms = polygons_2d() + polygon_array = construct_geometry_array(shapely_geoms) + + assert isinstance(polygon_array, PolygonArray) + assert polygon_array.type.coord_dimension == CoordinateDimension.XY + + new_shapely_geoms = polygon_array.to_shapely() + assert_geometries_equal(shapely_geoms, new_shapely_geoms) + + scalar = polygon_array[0] + assert scalar.as_py() == shapely_geoms[0] From dabad33b881f3d144ee33f99724c05ea80b9e3b6 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 24 Sep 2023 17:50:14 -0400 Subject: [PATCH 06/10] Add multipoint test --- geoarrow/shapely/tests/test_multipoint.py | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 geoarrow/shapely/tests/test_multipoint.py diff --git a/geoarrow/shapely/tests/test_multipoint.py b/geoarrow/shapely/tests/test_multipoint.py new file mode 100644 index 0000000..c6c5723 --- /dev/null +++ b/geoarrow/shapely/tests/test_multipoint.py @@ -0,0 +1,31 @@ +import numpy as np +import shapely +from numpy.typing import NDArray +from shapely.testing import assert_geometries_equal + +from geoarrow.shapely import ( + CoordinateDimension, + MultiPointArray, + construct_geometry_array, +) + + +def multipoints_2d() -> NDArray[np.object_]: + mp0 = shapely.MultiPoint([[0, 1], [1, 2]]) + mp1 = shapely.MultiPoint([[3, 4], [5, 6], [7, 8]]) + + return np.array([mp0, mp1]) + + +def test_round_trip_2d(): + shapely_geoms = multipoints_2d() + multipoint_array = construct_geometry_array(shapely_geoms) + + assert isinstance(multipoint_array, MultiPointArray) + assert multipoint_array.type.coord_dimension == CoordinateDimension.XY + + new_shapely_geoms = multipoint_array.to_shapely() + assert_geometries_equal(shapely_geoms, new_shapely_geoms) + + scalar = multipoint_array[0] + assert scalar.as_py() == shapely_geoms[0] From fda15469b514f3580171dc56fb100352da1da2a0 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 24 Sep 2023 18:00:13 -0400 Subject: [PATCH 07/10] add multipolygon test --- .../geoarrow/shapely/extension_scalar.py | 15 ++++++- .../shapely/tests/test_multilinestring.py | 34 +++++++++++++++ geoarrow/shapely/tests/test_multipolygon.py | 42 +++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 geoarrow/shapely/tests/test_multilinestring.py create mode 100644 geoarrow/shapely/tests/test_multipolygon.py diff --git a/geoarrow/shapely/geoarrow/shapely/extension_scalar.py b/geoarrow/shapely/geoarrow/shapely/extension_scalar.py index a875dbb..9ca6caa 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_scalar.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_scalar.py @@ -11,6 +11,9 @@ # TODO: support separated coords; right now it assumes interleaved +# TODO: add tests where the selected scalar is _not_ the first polygon. The offsets are +# incorrect when not the first polygon. + class Point(pa.ExtensionScalar): def to_shapely(self) -> shapely.Point: @@ -120,14 +123,22 @@ def to_shapely(self) -> shapely.MultiPolygon: def as_py(self) -> shapely.MultiPolygon: coords = ( self.value.values.flatten() + .flatten() + .flatten() .to_numpy(zero_copy_only=False, writable=True) .reshape(-1, len(self.type.coord_dimension)) ) - geom_offsets = np.array([0, coords.shape[0]], dtype=np.int32) + polygon_offsets = self.value.values.offsets + ring_offsets = self.value.values.flatten().offsets + geom_offsets = np.array([0, 1], dtype=np.int32) geoms = shapely.from_ragged_array( GeometryType.MULTIPOLYGON, coords, - (geom_offsets,), + ( + ring_offsets, + polygon_offsets, + geom_offsets, + ), ) assert len(geoms) == 1 return geoms[0] diff --git a/geoarrow/shapely/tests/test_multilinestring.py b/geoarrow/shapely/tests/test_multilinestring.py new file mode 100644 index 0000000..c0e92e8 --- /dev/null +++ b/geoarrow/shapely/tests/test_multilinestring.py @@ -0,0 +1,34 @@ +import numpy as np +import shapely +from numpy.typing import NDArray +from shapely.testing import assert_geometries_equal + +from geoarrow.shapely import ( + CoordinateDimension, + MultiLineStringArray, + construct_geometry_array, +) + + +def multilinestrings_2d() -> NDArray[np.object_]: + ls0 = shapely.LineString([[0, 1], [1, 2], [3, 4]]) + ls1 = shapely.LineString([[3, 4], [5, 6]]) + + mls0 = shapely.MultiLineString([ls0]) + mls1 = shapely.MultiLineString([ls0, ls1]) + + return np.array([mls0, mls1]) + + +def test_round_trip_2d(): + shapely_geoms = multilinestrings_2d() + multilinestring_array = construct_geometry_array(shapely_geoms) + + assert isinstance(multilinestring_array, MultiLineStringArray) + assert multilinestring_array.type.coord_dimension == CoordinateDimension.XY + + new_shapely_geoms = multilinestring_array.to_shapely() + assert_geometries_equal(shapely_geoms, new_shapely_geoms) + + scalar = multilinestring_array[0] + assert scalar.as_py() == shapely_geoms[0] diff --git a/geoarrow/shapely/tests/test_multipolygon.py b/geoarrow/shapely/tests/test_multipolygon.py new file mode 100644 index 0000000..5ac7f97 --- /dev/null +++ b/geoarrow/shapely/tests/test_multipolygon.py @@ -0,0 +1,42 @@ +import numpy as np +import shapely +from numpy.typing import NDArray +from shapely.testing import assert_geometries_equal + +from geoarrow.shapely import ( + CoordinateDimension, + MultiPolygonArray, + construct_geometry_array, +) + + +def polygons_2d() -> NDArray[np.object_]: + p0 = shapely.box(0, 1, 5, 10) + + ext_ring = shapely.LinearRing(shapely.box(10, 20, 30, 40).exterior.coords) + int_ring = shapely.LinearRing(shapely.box(12, 22, 28, 38).exterior.coords[::-1]) + p1 = shapely.Polygon(ext_ring, [int_ring]) + assert p1.is_valid + + return np.array([p0, p1]) + + +def multipolygons_2d() -> NDArray[np.object_]: + p0, p1 = polygons_2d() + mp0 = shapely.MultiPolygon([p0]) + mp1 = shapely.MultiPolygon([p0, p1]) + return np.array([mp0, mp1]) + + +def test_round_trip_2d(): + shapely_geoms = multipolygons_2d() + multipolygon_array = construct_geometry_array(shapely_geoms) + + assert isinstance(multipolygon_array, MultiPolygonArray) + assert multipolygon_array.type.coord_dimension == CoordinateDimension.XY + + new_shapely_geoms = multipolygon_array.to_shapely() + assert_geometries_equal(shapely_geoms, new_shapely_geoms) + + scalar = multipolygon_array[0] + assert scalar.as_py() == shapely_geoms[0] From c6f487821c967c169b1137d97120d80f96625a50 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 24 Sep 2023 18:08:23 -0400 Subject: [PATCH 08/10] rename offsets --- .../geoarrow/shapely/extension_types.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/geoarrow/shapely/geoarrow/shapely/extension_types.py b/geoarrow/shapely/geoarrow/shapely/extension_types.py index 55bd22d..5c69579 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_types.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_types.py @@ -342,49 +342,49 @@ def construct_geometry_array( elif geom_type == GeometryType.LINESTRING: assert len(offsets) == 1, "Expected one offsets array" - (offsets1,) = offsets + (geom_offsets,) = offsets _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) - parr = pa.ListArray.from_arrays(pa.array(offsets1), _parr) + parr = pa.ListArray.from_arrays(pa.array(geom_offsets), _parr) return pa.ExtensionArray.from_storage( LineStringType(interleaved=True, dims=dims), parr ) elif geom_type == GeometryType.POLYGON: assert len(offsets) == 2, "Expected two offsets arrays" - offsets1, offsets2 = offsets + ring_offsets, geom_offsets = offsets _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) - _parr1 = pa.ListArray.from_arrays(pa.array(offsets1), _parr) - parr = pa.ListArray.from_arrays(pa.array(offsets2), _parr1) + _parr1 = pa.ListArray.from_arrays(pa.array(ring_offsets), _parr) + parr = pa.ListArray.from_arrays(pa.array(geom_offsets), _parr1) return pa.ExtensionArray.from_storage( PolygonType(interleaved=True, dims=dims), parr ) elif geom_type == GeometryType.MULTIPOINT: assert len(offsets) == 1, "Expected one offsets array" - (offsets1,) = offsets + (geom_offsets,) = offsets _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) - parr = pa.ListArray.from_arrays(pa.array(offsets1), _parr) + parr = pa.ListArray.from_arrays(pa.array(geom_offsets), _parr) return pa.ExtensionArray.from_storage( MultiPointType(interleaved=True, dims=dims), parr ) elif geom_type == GeometryType.MULTILINESTRING: assert len(offsets) == 2, "Expected two offsets arrays" - offsets1, offsets2 = offsets + ring_offsets, geom_offsets = offsets _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) - _parr1 = pa.ListArray.from_arrays(pa.array(offsets1), _parr) - parr = pa.ListArray.from_arrays(pa.array(offsets2), _parr1) + _parr1 = pa.ListArray.from_arrays(pa.array(ring_offsets), _parr) + parr = pa.ListArray.from_arrays(pa.array(geom_offsets), _parr1) return pa.ExtensionArray.from_storage( MultiLineStringType(interleaved=True, dims=dims), parr ) elif geom_type == GeometryType.MULTIPOLYGON: assert len(offsets) == 3, "Expected three offsets arrays" - offsets1, offsets2, offsets3 = offsets + ring_offsets, polygon_offsets, geom_offsets = offsets _parr = pa.FixedSizeListArray.from_arrays(coords.flatten(), 2) - _parr1 = pa.ListArray.from_arrays(pa.array(offsets1), _parr) - _parr2 = pa.ListArray.from_arrays(pa.array(offsets2), _parr1) - parr = pa.ListArray.from_arrays(pa.array(offsets3), _parr2) + _parr1 = pa.ListArray.from_arrays(pa.array(ring_offsets), _parr) + _parr2 = pa.ListArray.from_arrays(pa.array(polygon_offsets), _parr1) + parr = pa.ListArray.from_arrays(pa.array(geom_offsets), _parr2) return pa.ExtensionArray.from_storage( MultiPolygonType(interleaved=True, dims=dims), parr ) From 253c1e0e863bd3b8582a2a47a9a82df4b44fe959 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 24 Sep 2023 23:23:21 -0400 Subject: [PATCH 09/10] raise on deserialization --- .../geoarrow/shapely/extension_types.py | 71 +++++++++++-------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/geoarrow/shapely/geoarrow/shapely/extension_types.py b/geoarrow/shapely/geoarrow/shapely/extension_types.py index 5c69579..2a9373a 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_types.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_types.py @@ -36,31 +36,6 @@ class BaseGeometryType(pa.ExtensionType): extension_name: str coord_dimension: CoordinateDimension - # def __init__(self): - # # attributes need to be set first before calling - # # super init (as that calls serialize) - # # self._crs = crs - # pa.ExtensionType.__init__(self, self._storage_type, self._extension_name) - - @property - def crs(self): - return self._crs - - def __arrow_ext_serialize__(self): - return b"CREATED" - - @classmethod - def __arrow_ext_deserialize__(cls, storage_type, serialized): - # return an instance of this subclass given the serialized - # metadata. - # TODO ignore serialized metadata for now - # serialized = serialized.decode() - # assert serialized.startswith("crs=") - # crs = serialized.split('=')[1] - # if crs == "": - # crs = None - return cls() - def coord_storage_type(*, interleaved: bool, dims: CoordinateDimension) -> pa.DataType: """Generate the storage type of a geoarrow coordinate array @@ -207,11 +182,12 @@ def __init__(self, *, interleaved: bool, dims: CoordinateDimension): storage_type = coord_storage_type(interleaved=interleaved, dims=dims) super().__init__(storage_type, self.extension_name) - # def __init__(self): - # # attributes need to be set first before calling - # # super init (as that calls serialize) - # # self._crs = crs - # pa.ExtensionType.__init__(self, self._storage_type, self._extension_name) + def __arrow_ext_serialize__(self): + return b"" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): + raise NotImplementedError("Type deserialization not yet implemented") def __arrow_ext_class__(self): return PointArray @@ -233,6 +209,13 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_serialize__(self): + return b"" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): + raise NotImplementedError("Type deserialization not yet implemented") + def __arrow_ext_class__(self): return LineStringArray @@ -253,6 +236,13 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_serialize__(self): + return b"" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): + raise NotImplementedError("Type deserialization not yet implemented") + def __arrow_ext_class__(self): return PolygonArray @@ -273,6 +263,13 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_serialize__(self): + return b"" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): + raise NotImplementedError("Type deserialization not yet implemented") + def __arrow_ext_class__(self): return MultiPointArray @@ -293,6 +290,13 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_serialize__(self): + return b"" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): + raise NotImplementedError("Type deserialization not yet implemented") + def __arrow_ext_class__(self): return MultiLineStringArray @@ -313,6 +317,13 @@ def __init__( ) super().__init__(storage_type, self.extension_name) + def __arrow_ext_serialize__(self): + return b"" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): + raise NotImplementedError("Type deserialization not yet implemented") + def __arrow_ext_class__(self): return MultiPolygonArray From f1cc64bc9d6f7bd473d9033c69ebfe1f931c6fac Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Mon, 25 Sep 2023 00:12:29 -0400 Subject: [PATCH 10/10] Add minimal geopandas to geoarrow converter --- .../geoarrow/shapely/extension_types.py | 12 +++++----- .../geoarrow/shapely/geopandas_interop.py | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 geoarrow/shapely/geoarrow/shapely/geopandas_interop.py diff --git a/geoarrow/shapely/geoarrow/shapely/extension_types.py b/geoarrow/shapely/geoarrow/shapely/extension_types.py index 2a9373a..eecb97a 100644 --- a/geoarrow/shapely/geoarrow/shapely/extension_types.py +++ b/geoarrow/shapely/geoarrow/shapely/extension_types.py @@ -187,7 +187,7 @@ def __arrow_ext_serialize__(self): @classmethod def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): - raise NotImplementedError("Type deserialization not yet implemented") + return cls(interleaved=True, dims=CoordinateDimension.XY) def __arrow_ext_class__(self): return PointArray @@ -214,7 +214,7 @@ def __arrow_ext_serialize__(self): @classmethod def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): - raise NotImplementedError("Type deserialization not yet implemented") + return cls(interleaved=True, dims=CoordinateDimension.XY) def __arrow_ext_class__(self): return LineStringArray @@ -241,7 +241,7 @@ def __arrow_ext_serialize__(self): @classmethod def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): - raise NotImplementedError("Type deserialization not yet implemented") + return cls(interleaved=True, dims=CoordinateDimension.XY) def __arrow_ext_class__(self): return PolygonArray @@ -268,7 +268,7 @@ def __arrow_ext_serialize__(self): @classmethod def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): - raise NotImplementedError("Type deserialization not yet implemented") + return cls(interleaved=True, dims=CoordinateDimension.XY) def __arrow_ext_class__(self): return MultiPointArray @@ -295,7 +295,7 @@ def __arrow_ext_serialize__(self): @classmethod def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): - raise NotImplementedError("Type deserialization not yet implemented") + return cls(interleaved=True, dims=CoordinateDimension.XY) def __arrow_ext_class__(self): return MultiLineStringArray @@ -322,7 +322,7 @@ def __arrow_ext_serialize__(self): @classmethod def __arrow_ext_deserialize__(cls, storage_type: pa.DataType, serialized: bytes): - raise NotImplementedError("Type deserialization not yet implemented") + return cls(interleaved=True, dims=CoordinateDimension.XY) def __arrow_ext_class__(self): return MultiPolygonArray diff --git a/geoarrow/shapely/geoarrow/shapely/geopandas_interop.py b/geoarrow/shapely/geoarrow/shapely/geopandas_interop.py new file mode 100644 index 0000000..43428e7 --- /dev/null +++ b/geoarrow/shapely/geoarrow/shapely/geopandas_interop.py @@ -0,0 +1,22 @@ +from typing import List, Optional + +import geopandas as gpd +import numpy as np +import pyarrow as pa + +from geoarrow.shapely.extension_types import construct_geometry_array + + +def geopandas_to_geoarrow( + gdf: gpd.GeoDataFrame, + columns: Optional[List[str]] = None, + preserve_index: Optional[bool] = None, +): + df_attr = gdf.drop(columns=[gdf._geometry_column_name]) + + if columns is not None: + df_attr = df_attr[columns] + + table = pa.Table.from_pandas(df_attr, preserve_index=preserve_index) + geom_arr = construct_geometry_array(np.array(gdf.geometry)) + return table.append_column("geometry", geom_arr)