diff --git a/lonboard/_layer.py b/lonboard/_layer.py index 2773317c..30d83862 100644 --- a/lonboard/_layer.py +++ b/lonboard/_layer.py @@ -10,7 +10,13 @@ from __future__ import annotations import sys -from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple +from typing import ( + TYPE_CHECKING, + List, + Optional, + Sequence, + Tuple, +) import geopandas as gpd import ipywidgets @@ -34,6 +40,16 @@ NormalAccessor, PyarrowTableTrait, ) +from lonboard.types.layer import ( + BaseLayerKwargs, + BitmapLayerKwargs, + BitmapTileLayerKwargs, + HeatmapLayerKwargs, + PathLayerKwargs, + PointCloudLayerKwargs, + ScatterplotLayerKwargs, + SolidPolygonLayerKwargs, +) if TYPE_CHECKING: if sys.version_info >= (3, 11): @@ -41,6 +57,11 @@ else: from typing_extensions import Self + if sys.version_info >= (3, 12): + from typing import Unpack + else: + from typing_extensions import Unpack + class BaseLayer(BaseWidget): # Note: these class attributes are **not** serialized to JS @@ -185,7 +206,7 @@ def _add_extension_traits(self, extensions: Sequence[BaseExtension]): def default_geoarrow_viewport( - table: pa.Table + table: pa.Table, ) -> Optional[Tuple[Bbox, WeightedCentroid]]: # Note: in the ArcLayer we won't necessarily have a column with a geoarrow # extension type/metadata @@ -236,7 +257,11 @@ class BaseArrowLayer(BaseLayer): table: traitlets.TraitType def __init__( - self, *, table: pa.Table, _rows_per_chunk: Optional[int] = None, **kwargs + self, + *, + table: pa.Table, + _rows_per_chunk: Optional[int] = None, + **kwargs: Unpack[BaseLayerKwargs], ): # Check for Arrow PyCapsule Interface # https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html @@ -265,7 +290,11 @@ def __init__( @classmethod def from_geopandas( - cls, gdf: gpd.GeoDataFrame, *, auto_downcast: bool = True, **kwargs + cls, + gdf: gpd.GeoDataFrame, + *, + auto_downcast: bool = True, + **kwargs: Unpack[BaseLayerKwargs], ) -> Self: """Construct a Layer from a geopandas GeoDataFrame. @@ -311,6 +340,9 @@ class BitmapLayer(BaseLayer): ``` """ + def __init__(self, **kwargs: BitmapLayerKwargs): + super().__init__(**kwargs) # type: ignore + _layer_type = traitlets.Unicode("bitmap").tag(sync=True) image = traitlets.Unicode().tag(sync=True) @@ -411,6 +443,9 @@ class BitmapTileLayer(BaseLayer): ``` """ + def __init__(self, **kwargs: BitmapTileLayerKwargs): + super().__init__(**kwargs) # type: ignore + _layer_type = traitlets.Unicode("bitmap-tile").tag(sync=True) data = traitlets.Union( @@ -607,6 +642,25 @@ class ScatterplotLayer(BaseArrowLayer): ``` """ + def __init__( + self, + *, + table: pa.Table, + _rows_per_chunk: Optional[int] = None, + **kwargs: Unpack[ScatterplotLayerKwargs], + ): + super().__init__(table=table, _rows_per_chunk=_rows_per_chunk, **kwargs) + + @classmethod + def from_geopandas( + cls, + gdf: gpd.GeoDataFrame, + *, + auto_downcast: bool = True, + **kwargs: Unpack[ScatterplotLayerKwargs], + ) -> Self: + return super().from_geopandas(gdf=gdf, auto_downcast=auto_downcast, **kwargs) + _layer_type = traitlets.Unicode("scatterplot").tag(sync=True) table = PyarrowTableTrait( @@ -819,6 +873,25 @@ class PathLayer(BaseArrowLayer): ``` """ + def __init__( + self, + *, + table: pa.Table, + _rows_per_chunk: Optional[int] = None, + **kwargs: Unpack[PathLayerKwargs], + ): + super().__init__(table=table, _rows_per_chunk=_rows_per_chunk, **kwargs) + + @classmethod + def from_geopandas( + cls, + gdf: gpd.GeoDataFrame, + *, + auto_downcast: bool = True, + **kwargs: Unpack[PathLayerKwargs], + ) -> Self: + return super().from_geopandas(gdf=gdf, auto_downcast=auto_downcast, **kwargs) + _layer_type = traitlets.Unicode("path").tag(sync=True) table = PyarrowTableTrait( @@ -960,6 +1033,25 @@ class PointCloudLayer(BaseArrowLayer): ``` """ + def __init__( + self, + *, + table: pa.Table, + _rows_per_chunk: Optional[int] = None, + **kwargs: Unpack[PointCloudLayerKwargs], + ): + super().__init__(table=table, _rows_per_chunk=_rows_per_chunk, **kwargs) + + @classmethod + def from_geopandas( + cls, + gdf: gpd.GeoDataFrame, + *, + auto_downcast: bool = True, + **kwargs: Unpack[PointCloudLayerKwargs], + ) -> Self: + return super().from_geopandas(gdf=gdf, auto_downcast=auto_downcast, **kwargs) + _layer_type = traitlets.Unicode("point-cloud").tag(sync=True) table = PyarrowTableTrait( @@ -1056,6 +1148,25 @@ class SolidPolygonLayer(BaseArrowLayer): ``` """ + def __init__( + self, + *, + table: pa.Table, + _rows_per_chunk: Optional[int] = None, + **kwargs: Unpack[SolidPolygonLayerKwargs], + ): + super().__init__(table=table, _rows_per_chunk=_rows_per_chunk, **kwargs) + + @classmethod + def from_geopandas( + cls, + gdf: gpd.GeoDataFrame, + *, + auto_downcast: bool = True, + **kwargs: Unpack[SolidPolygonLayerKwargs], + ) -> Self: + return super().from_geopandas(gdf=gdf, auto_downcast=auto_downcast, **kwargs) + _layer_type = traitlets.Unicode("solid-polygon").tag(sync=True) table = PyarrowTableTrait( @@ -1193,10 +1304,20 @@ class HeatmapLayer(BaseArrowLayer): """ - def __init__(self, *args, table: pa.Table, **kwargs): + def __init__(self, *, table: pa.Table, **kwargs: Unpack[HeatmapLayerKwargs]): # NOTE: we override the default for _rows_per_chunk because otherwise we render # one heatmap per _chunk_ not for the entire dataset. - super().__init__(*args, table=table, _rows_per_chunk=len(self.table), **kwargs) + super().__init__(table=table, _rows_per_chunk=len(table), **kwargs) + + @classmethod + def from_geopandas( + cls, + gdf: gpd.GeoDataFrame, + *, + auto_downcast: bool = True, + **kwargs: Unpack[HeatmapLayerKwargs], + ) -> Self: + return super().from_geopandas(gdf=gdf, auto_downcast=auto_downcast, **kwargs) _layer_type = traitlets.Unicode("heatmap").tag(sync=True) diff --git a/lonboard/_map.py b/lonboard/_map.py index e877eed5..1654d38d 100644 --- a/lonboard/_map.py +++ b/lonboard/_map.py @@ -1,7 +1,8 @@ from __future__ import annotations +import sys from pathlib import Path -from typing import IO, Optional, Sequence, TextIO, Union +from typing import IO, TYPE_CHECKING, Optional, Sequence, TextIO, Union import ipywidgets import traitlets @@ -12,6 +13,14 @@ from lonboard._layer import BaseLayer from lonboard._viewport import compute_view from lonboard.basemap import CartoBasemap +from lonboard.types.map import MapKwargs + +if TYPE_CHECKING: + if sys.version_info >= (3, 12): + from typing import Unpack + else: + from typing_extensions import Unpack + # bundler yields lonboard/static/{index.js,styles.css} bundler_output_dir = Path(__file__).parent / "static" @@ -64,7 +73,9 @@ class Map(BaseAnyWidget): ``` """ - def __init__(self, layers: Union[BaseLayer, Sequence[BaseLayer]], **kwargs) -> None: + def __init__( + self, layers: Union[BaseLayer, Sequence[BaseLayer]], **kwargs: Unpack[MapKwargs] + ) -> None: """Create a new Map. Aside from the `layers` argument, pass keyword arguments for any other attribute diff --git a/lonboard/_viz.py b/lonboard/_viz.py index f4282995..a67999e8 100644 --- a/lonboard/_viz.py +++ b/lonboard/_viz.py @@ -1,5 +1,5 @@ -"""High-level, super-simple API for visualizing GeoDataFrames -""" +"""High-level, super-simple API for visualizing GeoDataFrames""" + from __future__ import annotations import json @@ -9,6 +9,7 @@ Any, Dict, List, + Optional, Protocol, Tuple, Union, @@ -31,6 +32,12 @@ from lonboard._map import Map from lonboard._utils import get_geometry_column_index from lonboard.basemap import CartoBasemap +from lonboard.types.layer import ( + PathLayerKwargs, + ScatterplotLayerKwargs, + SolidPolygonLayerKwargs, +) +from lonboard.types.map import MapKwargs if TYPE_CHECKING: import geopandas as gpd @@ -76,7 +83,11 @@ def __arrow_c_stream__(self, requested_schema: object | None = None) -> object: def viz( data: Union[VizDataInput, List[VizDataInput], Tuple[VizDataInput, ...]], - **kwargs, + *, + scatterplot_kwargs: Optional[ScatterplotLayerKwargs] = None, + path_kwargs: Optional[PathLayerKwargs] = None, + solid_polygon_kwargs: Optional[SolidPolygonLayerKwargs] = None, + map_kwargs: Optional[MapKwargs] = None, ) -> Map: """A high-level function to plot your data easily. @@ -99,13 +110,17 @@ def viz( Args: data: a data object of any supported type. - Named args: - Any other keyword arguments will be passed onto the relevant layer, either a - `ScatterplotLayer`, `PathLayer`, or `SolidPolygonLayer`. + Other args: + - scatterplot_kwargs: a `dict` of parameters to pass down to all generated + [`ScatterplotLayer`][lonboard.ScatterplotLayer]s. + - path_kwargs: a `dict` of parameters to pass down to all generated + [`PathLayer`][lonboard.PathLayer]s. + - solid_polygon_kwargs: a `dict` of parameters to pass down to all generated + [`SolidPolygonLayer`][lonboard.SolidPolygonLayer]s. + - map_kwargs: a `dict` of parameters to pass down to the generated + [`Map`][lonboard.Map]. - If you pass a `list` or `tuple` of data objects, `kwargs` will be passed to - _all_ layers. For more control over rendering, construct `Map` and `Layer` - objects directly. + For more control over rendering, construct `Map` and `Layer` objects directly. Returns: widget visualizing the provided data. @@ -116,16 +131,31 @@ def viz( if isinstance(data, (list, tuple)): layers = [ create_layer_from_data_input( - item, _viz_color=color_ordering[i % len(color_ordering)], **kwargs + item, + _viz_color=color_ordering[i % len(color_ordering)], + scatterplot_kwargs=scatterplot_kwargs, + path_kwargs=path_kwargs, + solid_polygon_kwargs=solid_polygon_kwargs, ) for i, item in enumerate(data) ] else: layers = [ - create_layer_from_data_input(data, _viz_color=color_ordering[0], **kwargs) + create_layer_from_data_input( + data, + _viz_color=color_ordering[0], + scatterplot_kwargs=scatterplot_kwargs, + path_kwargs=path_kwargs, + solid_polygon_kwargs=solid_polygon_kwargs, + ) ] - return Map(layers=layers, basemap_style=CartoBasemap.DarkMatter) + map_kwargs = {} if not map_kwargs else map_kwargs + + if "basemap_style" not in map_kwargs.keys(): + map_kwargs["basemap_style"] = CartoBasemap.DarkMatter + + return Map(layers=layers, **map_kwargs) def create_layer_from_data_input( @@ -267,7 +297,12 @@ def _viz_geo_interface( def _viz_geoarrow_table( - table: pa.Table, **kwargs + table: pa.Table, + *, + _viz_color: Optional[str] = None, + scatterplot_kwargs: Optional[ScatterplotLayerKwargs] = None, + path_kwargs: Optional[PathLayerKwargs] = None, + solid_polygon_kwargs: Optional[SolidPolygonLayerKwargs] = None, ) -> Union[ScatterplotLayer, PathLayer, SolidPolygonLayer]: table = remove_extension_classes(table) table = parse_wkb_table(table) @@ -277,44 +312,53 @@ def _viz_geoarrow_table( geometry_ext_type = geometry_field.metadata.get(b"ARROW:extension:name") if geometry_ext_type in [EXTENSION_NAME.POINT, EXTENSION_NAME.MULTIPOINT]: - if "get_fill_color" not in kwargs.keys(): - kwargs["get_fill_color"] = kwargs.pop("_viz_color") - if "radius_min_pixels" not in kwargs.keys(): + scatterplot_kwargs = {} if not scatterplot_kwargs else scatterplot_kwargs + + if "get_fill_color" not in scatterplot_kwargs.keys(): + scatterplot_kwargs["get_fill_color"] = _viz_color + + if "radius_min_pixels" not in scatterplot_kwargs.keys(): if len(table) <= 10_000: - kwargs["radius_min_pixels"] = 2 + scatterplot_kwargs["radius_min_pixels"] = 2 elif len(table) <= 100_000: - kwargs["radius_min_pixels"] = 1 + scatterplot_kwargs["radius_min_pixels"] = 1 elif len(table) <= 1_000_000: - kwargs["radius_min_pixels"] = 0.5 + scatterplot_kwargs["radius_min_pixels"] = 0.5 else: - kwargs["radius_min_pixels"] = 0.2 + scatterplot_kwargs["radius_min_pixels"] = 0.2 - return ScatterplotLayer(table=table, **kwargs) + return ScatterplotLayer(table=table, **scatterplot_kwargs) elif geometry_ext_type in [ EXTENSION_NAME.LINESTRING, EXTENSION_NAME.MULTILINESTRING, ]: - if "get_color" not in kwargs.keys(): - kwargs["get_color"] = kwargs.pop("_viz_color") - if "width_min_pixels" not in kwargs.keys(): + path_kwargs = {} if not path_kwargs else path_kwargs + + if "get_color" not in path_kwargs.keys(): + path_kwargs["get_color"] = _viz_color + + if "width_min_pixels" not in path_kwargs.keys(): if len(table) <= 1_000: - kwargs["width_min_pixels"] = 1.5 + path_kwargs["width_min_pixels"] = 1.5 elif len(table) <= 10_000: - kwargs["width_min_pixels"] = 1 + path_kwargs["width_min_pixels"] = 1 elif len(table) <= 100_000: - kwargs["width_min_pixels"] = 0.7 + path_kwargs["width_min_pixels"] = 0.7 else: - kwargs["width_min_pixels"] = 0.5 + path_kwargs["width_min_pixels"] = 0.5 - return PathLayer(table=table, **kwargs) + return PathLayer(table=table, **path_kwargs) elif geometry_ext_type in [EXTENSION_NAME.POLYGON, EXTENSION_NAME.MULTIPOLYGON]: - if "get_fill_color" not in kwargs.keys(): - kwargs["get_fill_color"] = kwargs.pop("_viz_color") - if "opacity" not in kwargs.keys(): - kwargs["opacity"] = 0.6 + solid_polygon_kwargs = {} if not solid_polygon_kwargs else solid_polygon_kwargs + + if "get_fill_color" not in solid_polygon_kwargs.keys(): + solid_polygon_kwargs["get_fill_color"] = _viz_color + + if "opacity" not in solid_polygon_kwargs.keys(): + solid_polygon_kwargs["opacity"] = 0.6 - return SolidPolygonLayer(table=table, **kwargs) + return SolidPolygonLayer(table=table, **solid_polygon_kwargs) raise ValueError(f"Unsupported extension type: '{geometry_ext_type}'.") diff --git a/lonboard/traits.py b/lonboard/traits.py index d56eb398..a3c3e5ae 100644 --- a/lonboard/traits.py +++ b/lonboard/traits.py @@ -396,7 +396,7 @@ class TextAccessor(FixedErrorTraitType): Various input is allowed: - A `str`. This will be used as the value for all objects. - - A numpy `ndarray` with a string data type Each value in the array will be used as + - A numpy `ndarray` with a string data type. Each value in the array will be used as the value for the object at the same row index. - A pandas `Series` with a string data type. Each value in the array will be used as the value for the object at the same row index. diff --git a/lonboard/types/__init__.py b/lonboard/types/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lonboard/types/layer.py b/lonboard/types/layer.py new file mode 100644 index 00000000..55db9997 --- /dev/null +++ b/lonboard/types/layer.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +import sys +from typing import ( + List, + Literal, + Protocol, + Sequence, + Tuple, + Union, +) + +import numpy as np +import pandas as pd +import pyarrow as pa +from numpy.typing import NDArray + +from lonboard._base import BaseExtension + +if sys.version_info >= (3, 12): + from typing import TypedDict +else: + from typing_extensions import TypedDict + + +class ArrowArrayExportable(Protocol): + def __arrow_c_array__( + self, requested_schema: object | None = None + ) -> Tuple[object, object]: + ... + + +IntFloat = Union[int, float] +Units = Literal["meters", "common", "pixels"] + +ColorAccessorInput = Union[ + List[int], + Tuple[int, int, int], + Tuple[int, ...], + str, + NDArray[np.uint8], + pa.FixedSizeListArray, + pa.ChunkedArray, + ArrowArrayExportable, +] +FloatAccessorInput = Union[ + int, + float, + NDArray[np.number], + pd.Series, + pa.FloatingPointArray, + pa.ChunkedArray, + ArrowArrayExportable, +] +NormalAccessorInput = Union[ + List[int], + Tuple[int, int, int], + Tuple[int, ...], + NDArray[np.floating], + pa.FixedSizeListArray, + pa.ChunkedArray, + ArrowArrayExportable, +] +TextAccessorInput = Union[ + str, + NDArray[np.str_], + pd.Series, + pa.StringArray, + pa.LargeStringArray, + pa.ChunkedArray, + ArrowArrayExportable, +] + + +class BaseLayerKwargs(TypedDict, total=False): + extensions: Sequence[BaseExtension] + pickable: bool + visible: bool + opacity: IntFloat + auto_highlight: bool + + +class BitmapLayerKwargs(BaseLayerKwargs, total=False): + image: str + bounds: Union[ + Tuple[IntFloat, IntFloat, IntFloat, IntFloat], + Tuple[ + Tuple[IntFloat, IntFloat], + Tuple[IntFloat, IntFloat], + Tuple[IntFloat, IntFloat], + Tuple[IntFloat, IntFloat], + ], + ] + desaturate: IntFloat + transparent_color: Sequence[IntFloat] + tint_color: Sequence[IntFloat] + + +class BitmapTileLayerKwargs(BaseLayerKwargs, total=False): + data: Union[str, Sequence[str]] + tile_size: int + zoom_offset: int + max_zoom: int + min_zoom: int + extent: Sequence[IntFloat] + max_cache_size: int + # max_cache_byte_size: int + refinement_strategy: Literal["best-available", "no-overlap", "never"] + max_requests: int + desaturate: IntFloat + transparent_color: Sequence[IntFloat] + tint_color: Sequence[IntFloat] + + +class PathLayerKwargs(BaseLayerKwargs, total=False): + width_units: Units + width_scale: IntFloat + width_min_pixels: IntFloat + width_max_pixels: IntFloat + joint_rounded: bool + cap_rounded: bool + miter_limit: int + billboard: bool + get_color: ColorAccessorInput + get_width: FloatAccessorInput + + +class PointCloudLayerKwargs(BaseLayerKwargs, total=False): + size_units: Units + point_size: IntFloat + get_color: ColorAccessorInput + get_normal: NormalAccessorInput + + +class ScatterplotLayerKwargs(BaseLayerKwargs, total=False): + radius_units: Units + radius_scale: IntFloat + radius_min_pixels: IntFloat + radius_max_pixels: IntFloat + line_width_units: Units + line_width_scale: IntFloat + line_width_min_pixels: IntFloat + line_width_max_pixels: IntFloat + stroked: bool + filled: bool + billboard: bool + antialiasing: bool + get_radius: FloatAccessorInput + get_fill_color: ColorAccessorInput + get_line_color: ColorAccessorInput + get_line_width: FloatAccessorInput + + +class SolidPolygonLayerKwargs(BaseLayerKwargs, total=False): + filled: bool + extruded: bool + wireframe: bool + elevation_scale: IntFloat + get_elevation: FloatAccessorInput + get_fill_color: ColorAccessorInput + get_line_color: ColorAccessorInput + + +class HeatmapLayerKwargs(BaseLayerKwargs, total=False): + radius_pixels: IntFloat + intensity: IntFloat + threshold: IntFloat + color_domain: Tuple[IntFloat, IntFloat] + aggregation: Literal["SUM", "MEAN"] + weights_texture_size: int + debounce_timeout: int + get_weight: FloatAccessorInput diff --git a/lonboard/types/map.py b/lonboard/types/map.py new file mode 100644 index 00000000..d14504ac --- /dev/null +++ b/lonboard/types/map.py @@ -0,0 +1,17 @@ +import sys +from typing import Any, Dict, Union + +from lonboard.basemap import CartoBasemap + +if sys.version_info >= (3, 12): + from typing import TypedDict +else: + from typing_extensions import TypedDict + + +class MapKwargs(TypedDict, total=False): + _initial_view_state: Dict[str, Any] + _height: int + show_tooltip: bool + picking_radius: int + basemap_style: Union[str, CartoBasemap] diff --git a/poetry.lock b/poetry.lock index 0a94c5f9..a88d49ca 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1176,13 +1176,13 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "griffe" -version = "0.41.0" +version = "0.41.1" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ - {file = "griffe-0.41.0-py3-none-any.whl", hash = "sha256:8aa7fc6eb00cb80af9c0198178c6b7110cb59fa2c5187bb13ea25eebbe4dd928"}, - {file = "griffe-0.41.0.tar.gz", hash = "sha256:850128c3198c18713eaf0a6cc8572e590a16b1965f72a4e871e66cf84740903f"}, + {file = "griffe-0.41.1-py3-none-any.whl", hash = "sha256:9b71b82d0177a278467ce362827296957f0ca59ff5437ee41a0d6247aee257fa"}, + {file = "griffe-0.41.1.tar.gz", hash = "sha256:09d12f955004102d7deeec2e1d87d07434fb2d42905d21b4e03c7cbf9cf78754"}, ] [package.dependencies] @@ -1320,13 +1320,13 @@ files = [ [[package]] name = "ipykernel" -version = "6.29.2" +version = "6.29.3" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" files = [ - {file = "ipykernel-6.29.2-py3-none-any.whl", hash = "sha256:50384f5c577a260a1d53f1f59a828c7266d321c9b7d00d345693783f66616055"}, - {file = "ipykernel-6.29.2.tar.gz", hash = "sha256:3bade28004e3ff624ed57974948116670604ac5f676d12339693f3142176d3f0"}, + {file = "ipykernel-6.29.3-py3-none-any.whl", hash = "sha256:5aa086a4175b0229d4eca211e181fb473ea78ffd9869af36ba7694c947302a21"}, + {file = "ipykernel-6.29.3.tar.gz", hash = "sha256:e14c250d1f9ea3989490225cc1a542781b095a18a19447fcf2b5eaf7d0ac5bd2"}, ] [package.dependencies] @@ -1349,7 +1349,7 @@ 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 (==0.23.4)", "pytest-cov", "pytest-timeout"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] [[package]] name = "ipython" @@ -1463,13 +1463,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "json5" -version = "0.9.17" +version = "0.9.18" description = "A Python implementation of the JSON5 data format." optional = false python-versions = ">=3.8" files = [ - {file = "json5-0.9.17-py2.py3-none-any.whl", hash = "sha256:f8ec1ecf985951d70f780f6f877c4baca6a47b6e61e02c4cd190138d10a7805a"}, - {file = "json5-0.9.17.tar.gz", hash = "sha256:717d99d657fa71b7094877b1d921b1cce40ab444389f6d770302563bb7dfd9ae"}, + {file = "json5-0.9.18-py2.py3-none-any.whl", hash = "sha256:3f20193ff8dfdec6ab114b344e7ac5d76fac453c8bab9bdfe1460d1d528ec393"}, + {file = "json5-0.9.18.tar.gz", hash = "sha256:ecb8ac357004e3522fb989da1bf08b146011edbd14fdffae6caad3bd68493467"}, ] [package.extras] @@ -2196,17 +2196,18 @@ min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-imp [[package]] name = "mkdocs-autorefs" -version = "0.5.0" +version = "1.0.1" description = "Automatically link across pages in MkDocs." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_autorefs-0.5.0-py3-none-any.whl", hash = "sha256:7930fcb8ac1249f10e683967aeaddc0af49d90702af111a5e390e8b20b3d97ff"}, - {file = "mkdocs_autorefs-0.5.0.tar.gz", hash = "sha256:9a5054a94c08d28855cfab967ada10ed5be76e2bfad642302a610b252c3274c0"}, + {file = "mkdocs_autorefs-1.0.1-py3-none-any.whl", hash = "sha256:aacdfae1ab197780fb7a2dac92ad8a3d8f7ca8049a9cbe56a4218cd52e8da570"}, + {file = "mkdocs_autorefs-1.0.1.tar.gz", hash = "sha256:f684edf847eced40b570b57846b15f0bf57fb93ac2c510450775dcf16accb971"}, ] [package.dependencies] Markdown = ">=3.3" +markupsafe = ">=2.0.1" mkdocs = ">=1.1" [[package]] @@ -2230,13 +2231,13 @@ pygments = ">2.12.0" [[package]] name = "mkdocs-material" -version = "9.5.11" +version = "9.5.12" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.11-py3-none-any.whl", hash = "sha256:788ee0f3e036dca2dc20298d65e480297d348a44c9d7b2ee05c5262983e66072"}, - {file = "mkdocs_material-9.5.11.tar.gz", hash = "sha256:7af7f8af0dea16175558f3fb9245d26c83a17199baa5f157755e63d7437bf971"}, + {file = "mkdocs_material-9.5.12-py3-none-any.whl", hash = "sha256:d6f0c269f015e48c76291cdc79efb70f7b33bbbf42d649cfe475522ebee61b1f"}, + {file = "mkdocs_material-9.5.12.tar.gz", hash = "sha256:5f69cef6a8aaa4050b812f72b1094fda3d079b9a51cf27a247244c03ec455e97"}, ] [package.dependencies] @@ -3181,13 +3182,13 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" 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"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] @@ -3240,17 +3241,17 @@ files = [ [[package]] name = "pywinpty" -version = "2.0.12" +version = "2.0.13" description = "Pseudo terminal support for Windows from Python." optional = false python-versions = ">=3.8" files = [ - {file = "pywinpty-2.0.12-cp310-none-win_amd64.whl", hash = "sha256:21319cd1d7c8844fb2c970fb3a55a3db5543f112ff9cfcd623746b9c47501575"}, - {file = "pywinpty-2.0.12-cp311-none-win_amd64.whl", hash = "sha256:853985a8f48f4731a716653170cd735da36ffbdc79dcb4c7b7140bce11d8c722"}, - {file = "pywinpty-2.0.12-cp312-none-win_amd64.whl", hash = "sha256:1617b729999eb6713590e17665052b1a6ae0ad76ee31e60b444147c5b6a35dca"}, - {file = "pywinpty-2.0.12-cp38-none-win_amd64.whl", hash = "sha256:189380469ca143d06e19e19ff3fba0fcefe8b4a8cc942140a6b863aed7eebb2d"}, - {file = "pywinpty-2.0.12-cp39-none-win_amd64.whl", hash = "sha256:7520575b6546db23e693cbd865db2764097bd6d4ef5dc18c92555904cd62c3d4"}, - {file = "pywinpty-2.0.12.tar.gz", hash = "sha256:8197de460ae8ebb7f5d1701dfa1b5df45b157bb832e92acba316305e18ca00dd"}, + {file = "pywinpty-2.0.13-cp310-none-win_amd64.whl", hash = "sha256:697bff211fb5a6508fee2dc6ff174ce03f34a9a233df9d8b5fe9c8ce4d5eaf56"}, + {file = "pywinpty-2.0.13-cp311-none-win_amd64.whl", hash = "sha256:b96fb14698db1284db84ca38c79f15b4cfdc3172065b5137383910567591fa99"}, + {file = "pywinpty-2.0.13-cp312-none-win_amd64.whl", hash = "sha256:2fd876b82ca750bb1333236ce98488c1be96b08f4f7647cfdf4129dfad83c2d4"}, + {file = "pywinpty-2.0.13-cp38-none-win_amd64.whl", hash = "sha256:61d420c2116c0212808d31625611b51caf621fe67f8a6377e2e8b617ea1c1f7d"}, + {file = "pywinpty-2.0.13-cp39-none-win_amd64.whl", hash = "sha256:71cb613a9ee24174730ac7ae439fd179ca34ccb8c5349e8d7b72ab5dea2c6f4b"}, + {file = "pywinpty-2.0.13.tar.gz", hash = "sha256:c34e32351a3313ddd0d7da23d27f835c860d32fe4ac814d372a3ea9594f41dde"}, ] [[package]] @@ -4264,4 +4265,4 @@ cli = ["click", "pyogrio"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "2c88607afb1f525b4d8cf2739e075798dad94450617c52c216001235f2957ac6" +content-hash = "9264152a098e4b8f12df98e096b9858d9d622eae04260d83277a3c76a755775a" diff --git a/pyproject.toml b/pyproject.toml index ba46bd72..6d936218 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ matplotlib = "^3.7" shapely = "^2" click = { version = "^8.1.7", optional = true } pyogrio = { version = "^0.7.2", optional = true } +typing-extensions = { version = "^4.6.0", python = "<3.12" } [tool.poetry.extras] cli = ["click", "pyogrio"]