diff --git a/.readthedocs.yml b/.readthedocs.yml index 554fd63..1152361 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -9,6 +9,8 @@ build: os: ubuntu-22.04 tools: python: "3.10" + apt_packages: + - graphviz jobs: pre_build: # Generate the Sphinx configuration for this Jupyter Book so it builds. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index ac81e5a..2f2109e 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -121,6 +121,7 @@ poetry install --extras "raster spatial vector" ``` poetry install --extras=docs # or `pip install .[docs]` +sudo apt install graphviz # if rendering graphviz plots jupyter-book build docs/ ``` diff --git a/docs/_config.yml b/docs/_config.yml index 14f394b..595f42e 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -4,10 +4,12 @@ title: zen3geo author: The zen3geo Team -# Force re-execution of notebooks on each build. +# Cache execution outputs of notebooks on each build. # See https://jupyterbook.org/content/execute.html execute: execute_notebooks: cache + # https://jupyterbook.org/en/latest/content/execute.html#setting-execution-timeout + timeout: 300 # Define the name of the latex output file for PDF builds latex: @@ -30,6 +32,7 @@ html: sphinx: config: autodoc_typehints: 'description' + execution_show_tb: True html_show_copyright: false html_theme_options: # https://sphinx-book-theme.readthedocs.io/en/stable/customize/sidebar-secondary.html @@ -63,7 +66,7 @@ sphinx: - 'https://pytorch.org/data/main' - null xarray: - - 'https://xarray.pydata.org/en/latest/' + - 'https://docs.xarray.dev/en/latest/' - null xbatcher: - 'https://xbatcher.readthedocs.io/en/latest/' diff --git a/docs/_toc.yml b/docs/_toc.yml index c16edc4..be5c14a 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -9,6 +9,8 @@ chapters: sections: - title: πŸ€„ Chipping and Batching file: chipping + - title: 🫧 Vector Segmentation Masks + file: vector-segmentation-masks - title: πŸ“– API Reference file: api - title: πŸ“† Changelog diff --git a/docs/vector-segmentation-masks.md b/docs/vector-segmentation-masks.md new file mode 100644 index 0000000..3f04de1 --- /dev/null +++ b/docs/vector-segmentation-masks.md @@ -0,0 +1,465 @@ +--- +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Vector segmentation masks + +> *Clouds float by, water flows on; +> in movement there is no grasping, in Chan there is no settling* + +For πŸ§‘β€πŸ« supervised machine learning, labels 🏷️ are needed in addition to the +input image πŸ–ΌοΈ. Here, we'll step through an example workflow on matching vector +🚏 label data (points, lines, polygons) to πŸ›°οΈ Earth Observation data inputs. +Specifically, this tutorial will cover: + +- Reading shapefiles πŸ“ directly from the web via {doc}`pyogrio ` +- Rasterizing vector polygons from a {py:class}`geopandas.GeoDataFrame` to an {py:class}`xarray.DataArray` +- Pairing πŸ›°οΈ satellite images with the rasterized label masks and feeding them into a DataLoader + + +## πŸŽ‰ **Getting started** + +These are the tools πŸ› οΈ you'll need. + +```{code-cell} +import matplotlib.pyplot as plt +import numpy as np +import planetary_computer +import pyogrio +import pystac +import torch +import torchdata +import xarray as xr +import zen3geo +``` + +## 0️⃣ Find cloud-hosted raster and vector data β›³ + +In this case study, we'll look at the flood water extent over Johor, +Malaysia πŸ‡²πŸ‡Ύ on 15 Dec 2019 that were digitized by πŸ‡ΊπŸ‡³ UNITAR-UNOSAT's rapid +mapping service over Synthetic Aperture Radar (SAR) πŸ›°οΈ images. Specifically, +we'll be using the πŸ‡ͺπŸ‡Ί Sentinel-1 Ground Range Detected (GRD) product's VV +polarization channel. + +πŸ”— Links: +- https://www.unitar.org/maps/unosat-rapid-mapping-service +- https://unitar.org/maps/countries +- [Microsoft Planetary Computer STAC Explorer](https://planetarycomputer.microsoft.com/explore?c=103.6637%2C2.1494&z=8.49&v=2&d=sentinel-1-grd&s=false%3A%3A100%3A%3Atrue&ae=0&m=cql%3Afc3d85b6ab43d3e8ebe168da0206f2cf&r=VV%2C+VH+False-color+composite) + +To start, let's get the πŸ›°οΈ satellite scene we'll be using for this tutorial. + +```{code-cell} +item_url = "https://planetarycomputer.microsoft.com/api/stac/v1/collections/sentinel-1-grd/items/S1A_IW_GRDH_1SDV_20191215T224757_20191215T224822_030365_037955" + +# Load the individual item metadata and sign the assets +item = pystac.Item.from_file(item_url) +signed_item = planetary_computer.sign(item) +signed_item +``` + +This is how the Sentinel-1 🩻 image looks like over Johor in Peninsular +Malaysia on 15 Dec 2019. + +![Sentinel-1 image over Johor, Malaysia on 20191215](https://planetarycomputer.microsoft.com/api/data/v1/item/preview.png?collection=sentinel-1-grd&item=S1A_IW_GRDH_1SDV_20191215T224757_20191215T224822_030365_037955&assets=vv&assets=vh&expression=vv%2Cvh%2Cvv%2Fvh&rescale=0%2C500&rescale=0%2C300&rescale=0%2C7&tile_format=png) + +### Load and reproject image data πŸ”„ + +To keep things simple, we'll load just the VV channel into a DataPipe via +{py:class}`zen3geo.datapipes.rioxarray.RioXarrayReaderIterDataPipe` πŸ˜€. + +```{code-cell} +url = signed_item.assets["vv"].href +dp = torchdata.datapipes.iter.IterableWrapper(iterable=[url]) +# Reading lower resolution grid using overview_level=3 +dp_rioxarray = dp.read_from_rioxarray(overview_level=3) +dp_rioxarray +``` + +The Sentinel-1 image from Planetary Computer comes in longitude/latitude 🌐 +geographic coordinates by default (EPSG:4326). To make the pixels more equal πŸ”² +area, we can project it to a 🌏 local projected coordinate system instead. + +```{code-cell} +def reproject_to_local_utm(dataarray: xr.DataArray, resolution: float=100.0) -> xr.DataArray: + """ + Reproject an xarray.DataArray grid from EPSG;4326 to a local UTM coordinate + reference system. + """ + # Estimate UTM coordinate reference from a single pixel + pixel = dataarray.isel(y=slice(0, 1), x=slice(0,1)) + new_crs = dataarray.rio.reproject(dst_crs="EPSG:4326").rio.estimate_utm_crs() + + return dataarray.rio.reproject(dst_crs=new_crs, resolution=resolution) +``` + +```{code-cell} +dp_reprojected = dp_rioxarray.map(fn=reproject_to_local_utm) +``` + +```{note} +Universal Transverse Mercator (UTM) isn't actually an equal-area projection +system. However, Sentinel-1 πŸ›°οΈ satellite scenes from Copernicus are usually +distributed in a UTM coordinate reference system, and UTM is typically a close +enough 🀏 approximation to the local geographic area, or at least it won't +matter much when we're looking at spatial resolutions over several 10s of +metres πŸ™‚. +``` + +### Transform and visualize raster data πŸ”Ž + +Let's visualize πŸ‘€ the Sentinel-1 image, but before that, we'll transform πŸ”„ +the VV data from linear to [decibel](https://en.wikipedia.org/wiki/Decibel) +scale. + +```{code-cell} +def linear_to_decibel(dataarray: xr.DataArray) -> xr.DataArray: + """ + Transforming the input xarray.DataArray's VV or VH values from linear to + decibel scale using the formula ``10 * log_10(x)``. + """ + # Mask out areas with 0 so that np.log10 is not undefined + da_linear = dataarray.where(cond=dataarray != 0) + da_decibel = 10 * np.log10(da_linear) + return da_decibel +``` + +```{code-cell} +dp_decibel = dp_reprojected.map(fn=linear_to_decibel) +dp_decibel +``` + +As an aside, we'll be using the Sentinel-1 image datapipe twice later, once as +a template to create a blank canvas 🎞️, and another time by itself πŸͺž. This +requires forking 🍴 the DataPipe into two branches, which can be achieved using +{py:class}`torchdata.datapipes.iter.Forker` (functional name: `fork`). + +```{code-cell} +dp_decibel_canvas, dp_decibel_image = dp_decibel.fork(num_instances=2) +dp_decibel_canvas, dp_decibel_image +``` + +Now to visualize the transformed Sentinel-1 image πŸ–ΌοΈ. Let's zoom in πŸ”­ to one +of the analysis extent areas we'll be working on later. + +```{code-cell} +it = iter(dp_decibel_image) +dataarray = next(it) + +da_clip = dataarray.rio.clip_box(minx=371483, miny=190459, maxx=409684, maxy=229474) +da_clip.isel(band=0).plot.imshow(figsize=(11.5, 9), cmap="Blues_r", vmin=18, vmax=26) +``` + +Notice how the darker blue areas πŸ”΅ tend to correlate more with water features +like the meandering rivers and the 🐚 sea on the NorthEast. This is because the +SAR πŸ›°οΈ signal which is side looking reflects off flat water bodies like a +mirror πŸͺž, with little energy getting reflected πŸ™… back directly to the sensor +(hence why it looks darker ⚫). + +### Load and visualize cloud-hosted vector files πŸ’  + +Let's now load some vector data from the web πŸ•ΈοΈ. These are polygons of the +segmented 🌊 water extent digitized by UNOSAT's AI Based Rapid Mapping Service. +We'll be converting these vector polygons to 🌈 raster masks later. + +πŸ”— Links: +- https://github.com/UNITAR-UNOSAT/UNOSAT-AI-Based-Rapid-Mapping-Service +- [Humanitarian Data Exchange link to polygon dataset](https://data.humdata.org/dataset/waters-extents-as-of-15-december-2019-over-kota-tinggi-and-mersing-district-johor-state-of) +- [Disaster Risk Monitoring Using Satellite Imagery online course](https://courses.nvidia.com/courses/course-v1:DLI+S-ES-01+V1) + +```{code-cell} +# https://gdal.org/user/virtual_file_systems.html#vsizip-zip-archives +shape_url = "/vsizip/vsicurl/https://unosat-maps.web.cern.ch/MY/FL20191217MYS/FL20191217MYS_SHP.zip/ST1_20191215_WaterExtent_Johor_AOI2.shp" +``` + +This is a shapefile containing πŸ”· polygons of the mapped water extent. Let's +put it into a DataPipe called {py:class}`zen3geo.datapipes.PyogrioReader` +(functional name: ``read_from_pyogrio``). + +```{code-cell} +dp_shapes = torchdata.datapipes.iter.IterableWrapper(iterable=[shape_url]) +dp_pyogrio = dp_shapes.read_from_pyogrio() +``` + +This will take care of loading the shapefile into a +{py:class}`geopandas.GeoDataFrame` object. Let's take a look at the data table +πŸ“Š to see what attributes are inside. + +```{code-cell} +it = iter(dp_pyogrio) +geodataframe = next(it) +geodataframe.dropna(axis="columns") +``` + +Cool, and we can also visualize the polygons πŸ”· on a 2D map. To align the +coordinates with the πŸ›°οΈ Sentinel-1 image above, we'll first use +{py:meth}`geopandas.GeoDataFrame.to_crs` to reproject the vector from 🌐 +EPSG:4326 (longitude/latitude) to 🌏 EPSG:32648 (UTM Zone 48N). + +```{code-cell} +print(f"Original bounds in EPSG:4326:\n{geodataframe.bounds}") +gdf = geodataframe.to_crs(crs="EPSG:32648") +print(f"New bounds in EPSG:32648:\n{gdf.bounds}") +``` + +Plot it with {py:meth}`geopandas.GeoDataFrame.plot`. This vector map πŸ—ΊοΈ should +correspond to the zoomed in Sentinel-1 image plotted earlier above. + +```{code-cell} +gdf.plot(figsize=(11.5, 9)) +``` + +```{tip} +Make sure to understand your raster and vector datasets well first! Open the +files up in your favourite 🌐 Geographic Information System (GIS) tool, see how +they actually look like spatially. Then you'll have a better idea to decide on +how to create your data pipeline. The zen3geo way puts you as the Master πŸ§™ in +control. +``` + + +## 1️⃣ Create a canvas to paint on 🎨 + +In this section, we'll work on converting the flood water 🌊 polygons above +from a 🚩 vector to a 🌈 raster format, i.e. rasterization. This will be done +in two steps πŸ“Ά: + +1. Defining a blank canvas 🎞️ +2. Paint the polygons onto this blank canvas πŸ§‘β€πŸŽ¨ + +For this, we'll be using tools from {py:meth}`zen3geo.datapipes.datashader`. +Let's see how this can be done. + +### Blank canvas from template raster πŸ–ΌοΈ + +A canvas represents a 2D area with a height and a width πŸ“. For us, we'll be +using a {py:class}`datashader.Canvas`, which also defines the range of y-values +(ymin to ymax) and x-values (xmin to xmax), essentially coordinates for +every unit πŸ‡Ύ height and πŸ‡½ width. + +Since we already have a Sentinel-1 πŸ›°οΈ raster grid with defined height/width +and y/x coordinates, let's use it as a πŸ“„ template to define our canvas. This +is done via {py:class}`zen3geo.datapipes.XarrayCanvas` (functional name: +``canvas_from_xarray``). + +```{code-cell} +dp_canvas = dp_decibel_canvas.canvas_from_xarray() +dp_canvas +``` + +Cool, and here's a quick inspection πŸ‘€ of the canvas dimensions and metadata. + +```{code-cell} +it = iter(dp_canvas) +canvas = next(it) +print(f"Canvas height: {canvas.plot_height}, width: {canvas.plot_width}") +print(f"Y-range: {canvas.y_range}") +print(f"X-range: {canvas.x_range}") +print(f"Coordinate reference system: {canvas.crs}") +``` + +This information should match the template the Sentinel-1 dataarray 🏁. + +```{code-cell} +print(f"Dimensions: {dict(dataarray.sizes)}") +print(f"Affine transform: {dataarray.rio.transform()}") +print(f"Bounding box: {dataarray.rio.bounds()}") +print(f"Coordinate reference system: {dataarray.rio.crs}") +``` + +### Rasterize vector polygons onto canvas πŸ–ŒοΈ + +Now's the time to paint or rasterize the +vector {py:class}`geopandas.GeoDataFrame` polygons πŸ”· onto the blank +{py:class}`datashader.Canvas`! This would enable us to have a direct pixel-wise +X -> Y mapping ↔️ between the Sentinel-1 image (X) and target flood label (Y). + +The vector polygons can be rasterized or painted πŸ–ŒοΈ onto the template canvas +using {py:class}`zen3geo.datapipes.DatashaderRasterizer` (functional name: +``rasterize_with_datashader``). + +```{code-cell} +dp_datashader = dp_canvas.rasterize_with_datashader(vector_datapipe=dp_pyogrio) +dp_datashader +``` + +This will turn the vector {py:class}`geopandas.GeoDataFrame` into a +raster {py:class}`xarray.DataArray` grid, with the spatial coordinates and +bounds matching exactly with the template Sentinel-1 image 😎. + +```{note} +Since we have just one Sentinel-1 πŸ›°οΈ image and one raster πŸ’§ flood +mask, we have an easy 1:1 mapping. There are two other scenarios supported by +{py:class}`zen3geo.datapipes.DatashaderRasterizer`: + +1. N:1 - Many {py:class}`datashader.Canvas` objects to one vector + {py:class}`geopandas.GeoDataFrame`. The single vector geodataframe will be + broadcasted to match the length of the canvas list. This is useful for + situations when you have a 🌐 'global' vector database that you want to pair + with multiple πŸ›°οΈ satellite images. +2. N:N - Many {py:class}`datashader.Canvas` objects to many vector + {py:class}`geopandas.GeoDataFrame` objects. In this case, the list of grids + **must** ❗ have the same length as the list of vector geodataframes. E.g. + if you have 5 grids, there must also be 5 vector files. This is so that a + 1:1 pairing can be done, useful when each raster tile πŸ–½ has its own + associated vector annotation. +``` + +```{seealso} +For more details on how rasterization of polygons work behind the scenes 🎦, +check out {doc}`Datashader `'s documentation on: + +- {doc}`The datashader pipeline ` (especially the + section on Aggregation). +- {doc}`Rendering large collections of polygons ` +``` + + +## 2️⃣ Combine and conquer βš”οΈ + +So far, we've got two datapipes that should be πŸ§‘β€πŸ€β€πŸ§‘ paired up in an X -> Y +manner: + +1. The pre-processed Sentinel-1 🌈 raster image in ``dp_decibel_image`` +2. The rasterized πŸ’§ flood segmentation masks in ``dp_datashader`` + +One way to get these two pieces in a Machine Learning ready chip format is via +a stack, slice and split ℒ️ approach. Think of it like a sandwich πŸ₯ͺ, we first +stack the bread 🍞 and lettuce πŸ₯¬, and then slice the pieces πŸ• through the +layers once. Ok, that was a bad analogy, let's just stick with tensors πŸ€ͺ. + +### Stacking the raster layers πŸ₯ž + +Each of our 🌈 raster inputs are {py:class}`xarray.DataArray` objects with the +same spatial resolution and extent πŸͺŸ, so these can be stacked into an +{py:class}`xarray.Dataset` with multiple data variables. First, we'll zip 🀐 +the two datapipes together using {py:class}`torchdata.datapipes.iter.Zipper` +(functional name: ``zip``) + +```{code-cell} +dp_zip = dp_decibel_image.zip(dp_datashader) +dp_zip +``` + +This will result in a DataPipe where each item is a tuple of (X, Y) pairs πŸ§‘β€πŸ€β€πŸ§‘. +Just to illustrate what we've done so far, we can use +{py:class}`torchdata.datapipes.utils.to_graph` to visualize the data pipeline +⛓️. + +```{code-cell} +torchdata.datapipes.utils.to_graph(dp=dp_zip) +``` + +Next, let's combine πŸ–‡οΈ the two (X, Y) {py:class}`xarray.DataArray` objects in +the tuple into an {py:class}`xarray.Dataset` using +{py:class}`torchdata.datapipes.iter.Collator` (functional name: `collate`). +We'll also βœ‚οΈ clip the dataset to a bounding box area where the target water +mask has no 0 or NaN values. + +```{code-cell} +def xr_collate_fn(image_and_mask: tuple) -> xr.Dataset: + """ + Combine a pair of xarray.DataArray (image, mask) inputs into an + xarray.Dataset with two data variables named 'image' and 'mask'. + """ + # Turn 2 xr.DataArray objects into 1 xr.Dataset with multiple data vars + image, mask = image_and_mask + dataset: xr.Dataset = image.isel(band=0).to_dataset(name="image") + dataset["mask"] = mask + + # Clip dataset to bounding box extent of where labels are + mask_extent: tuple = mask.where(cond=mask == 1, drop=True).rio.bounds() + clipped_dataset: xr.Dataset = dataset.rio.clip_box(*mask_extent) + + return clipped_dataset +``` + +```{code-cell} +dp_dataset = dp_zip.collate(collate_fn=xr_collate_fn) +dp_dataset +``` + +Double check to see that resulting {py:class}`xarray.Dataset`'s image and mask +looks ok πŸ™†β€β™‚οΈ. + +```{code-cell} +it = iter(dp_dataset) +dataset = next(it) + +# Create subplot with VV image on the left and Water mask on the right +fig, axs = plt.subplots(ncols=2, figsize=(11.5, 4.5), sharey=True) +dataset.image.plot.imshow(ax=axs[0], cmap="Blues_r") +axs[0].set_title("Sentinel-1 VV channel") +dataset.mask.plot.imshow(ax=axs[1], cmap="Blues") +axs[1].set_title("Water mask") +plt.show() +``` + +### Slice into chips and turn into tensors πŸ—‘οΈ + +To cut πŸ”ͺ the {py:class}`xarray.Dataset` into 128x128 sized chips, we'll use +{py:class}`zen3geo.datapipes.XbatcherSlicer` (functional name: +`slice_with_xbatcher`). Refer to {doc}`./chipping` if you need a πŸ§‘β€πŸŽ“ refresher. + +```{code-cell} +dp_xbatcher = dp_dataset.slice_with_xbatcher(input_dims={"y": 128, "x": 128}) +dp_xbatcher +``` + +Next step is to convert the 128x128 chips into a {py:class}`torch.Tensor` via +{py:class}`torchdata.datapipes.iter.Mapper` (functional name: `map`). The πŸ›°οΈ +Sentinel-1 image and πŸ’§ water mask will be split out at this point too. + +```{code-cell} +def dataset_to_tensors(chip: xr.Dataset) -> (torch.Tensor, torch.Tensor): + """ + Converts an xarray.Dataset into to two torch.Tensor objects, the first one + being the satellite image, and the second one being the target mask. + """ + image: torch.Tensor = torch.as_tensor(chip.image.data) + mask: torch.Tensor = torch.as_tensor(chip.mask.data.astype("uint8")) + + return image, mask +``` + +```{code-cell} +dp_map = dp_xbatcher.map(fn=dataset_to_tensors) +dp_map +``` + +At this point, we could do some batching and collating, but we'll point you +again to {doc}`./chipping` to figure it out 😝. Let's take a look at a graph +of the complete data pipeline. + +```{code-cell} +torchdata.datapipes.utils.to_graph(dp=dp_map) +``` + +Sweet, time for the final step ⏩. + +### Into a DataLoader πŸ‹οΈ + +Pass the DataPipe into {py:class}`torch.utils.data.DataLoader` 🀾! + +```{code-cell} +dataloader = torch.utils.data.DataLoader(dataset=dp_map) +for i, batch in enumerate(dataloader): + image, mask = batch + print(f"Batch {i} - image: {image.shape}, mask:{mask.shape}") +``` + +Now go train some flood water detection models 🌊🌊🌊 + +```{seealso} +To learn more about AI-based flood mapping with SAR, check out these resources: + +- [UNOSAT/NVIDIA Disaster Risk Monitoring Using Satellite Imagery online course](https://event.unitar.org/full-catalog/disaster-risk-monitoring-using-satellite-imagery) +- [Code to train a Convolutional Neural Network for flood segmentation](https://github.com/UNITAR-UNOSAT/UNOSAT-AI-Based-Rapid-Mapping-Service/blob/master/Fastai%20training.ipynb) +``` diff --git a/poetry.lock b/poetry.lock index cef601d..93f3b0c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -302,6 +302,14 @@ doc = ["sphinx-holoviz-theme", "nbsite (>=0.6.1)", "bokeh", "matplotlib", "holov build = ["wheel", "setuptools (>=30.3.0)", "pyct (>=0.4.4)", "param (>=1.7.0)"] all = ["wheel", "twine", "sphinx-holoviz-theme", "setuptools (>=30.3.0)", "rfc3986", "pytest-mpl", "pytest-cov", "pytest (>=2.8.5)", "pyct (>=0.4.4)", "param (>=1.7.0)", "numpy", "nbsmoke (>=0.2.6)", "nbsite (>=0.6.1)", "matplotlib", "keyring", "holoviews", "flake8", "bokeh"] +[[package]] +name = "cycler" +version = "0.11.0" +description = "Composable style cycles" +category = "main" +optional = true +python-versions = ">=3.6" + [[package]] name = "dask" version = "2022.6.1" @@ -481,6 +489,28 @@ calc = ["shapely"] s3 = ["boto3 (>=1.2.4)"] test = ["pytest (>=3)", "pytest-cov", "boto3 (>=1.2.4)", "mock"] +[[package]] +name = "fonttools" +version = "4.35.0" +description = "Tools to manipulate font files" +category = "main" +optional = true +python-versions = ">=3.7" + +[package.extras] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] +unicode = ["unicodedata2 (>=14.0.0)"] +ufo = ["fs (>=2.2.0,<3)"] +type1 = ["xattr"] +symfont = ["sympy"] +repacker = ["uharfbuzz (>=0.23.0)"] +plot = ["matplotlib"] +pathops = ["skia-pathops (>=0.5.0)"] +lxml = ["lxml (>=4.0,<5)"] +interpolatable = ["munkres", "scipy"] +graphite = ["lz4 (>=1.7.4.2)"] +all = ["xattr", "unicodedata2 (>=14.0.0)", "munkres", "brotli (>=1.0.1)", "scipy", "brotlicffi (>=0.8.0)", "uharfbuzz (>=0.23.0)", "skia-pathops (>=0.5.0)", "sympy", "matplotlib", "lz4 (>=1.7.4.2)", "zopfli (>=0.1.4)", "lxml (>=4.0,<5)", "fs (>=2.2.0,<3)"] + [[package]] name = "fsspec" version = "2022.5.0" @@ -549,6 +579,19 @@ python-versions = ">=3.7" [package.dependencies] gitdb = ">=4.0.1,<5" +[[package]] +name = "graphviz" +version = "0.20.1" +description = "Simple Python interface for Graphviz" +category = "main" +optional = true +python-versions = ">=3.7" + +[package.extras] +dev = ["tox (>=3)", "flake8", "pep8-naming", "wheel", "twine"] +docs = ["sphinx (>=5)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] +test = ["pytest (>=7)", "pytest-mock (>=3)", "mock (>=4)", "pytest-cov", "coverage"] + [[package]] name = "greenlet" version = "1.1.2" @@ -922,6 +965,14 @@ category = "main" optional = true python-versions = ">=3.6" +[[package]] +name = "kiwisolver" +version = "1.4.4" +description = "A fast implementation of the Cassowary constraint solver" +category = "main" +optional = true +python-versions = ">=3.7" + [[package]] name = "latexcodec" version = "2.0.1" @@ -993,6 +1044,25 @@ category = "main" optional = true python-versions = ">=3.7" +[[package]] +name = "matplotlib" +version = "3.5.3" +description = "Python plotting package" +category = "main" +optional = true +python-versions = ">=3.7" + +[package.dependencies] +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.0.1" +numpy = ">=1.17" +packaging = ">=20.0" +pillow = ">=6.2.0" +pyparsing = ">=2.2.1" +python-dateutil = ">=2.7" +setuptools_scm = ">=4,<7" + [[package]] name = "matplotlib-inline" version = "0.1.3" @@ -1918,6 +1988,23 @@ docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tideli testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-enabler (>=1.0.1)", "pytest-perf", "mock", "flake8-2020", "virtualenv (>=13.0.0)", "wheel", "pip (>=19.1)", "jaraco.envs (>=2.2)", "pytest-xdist", "jaraco.path (>=3.2.0)", "build", "filelock (>=3.4.0)", "pip-run (>=8.8)", "ini2toml[lite] (>=0.9)", "tomli-w (>=1.0.0)", "pytest-black (>=0.3.7)", "pytest-cov", "pytest-mypy (>=0.9.1)"] testing-integration = ["pytest", "pytest-xdist", "pytest-enabler", "virtualenv (>=13.0.0)", "tomli", "wheel", "jaraco.path (>=3.2.0)", "jaraco.envs (>=2.2)", "build", "filelock (>=3.4.0)"] +[[package]] +name = "setuptools-scm" +version = "6.4.2" +description = "the blessed package to manage your versions by scm tags" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +packaging = ">=20.0" +setuptools = "*" +tomli = ">=1.0.0" + +[package.extras] +test = ["pytest (>=6.2)", "virtualenv (>20)"] +toml = ["setuptools (>=42)"] + [[package]] name = "shapely" version = "1.8.2" @@ -2378,7 +2465,7 @@ test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -2575,7 +2662,7 @@ docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco-itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [extras] -docs = ["datashader", "jupyter-book", "planetary-computer", "pystac", "xbatcher"] +docs = ["datashader", "graphviz", "jupyter-book", "matplotlib", "planetary-computer", "pyogrio", "pystac", "spatialpandas", "xbatcher"] raster = ["xbatcher"] spatial = ["datashader", "spatialpandas"] vector = ["pyogrio"] @@ -2583,7 +2670,7 @@ vector = ["pyogrio"] [metadata] lock-version = "1.1" python-versions = ">=3.8, <3.12" -content-hash = "08e393600042c86adfda294c481b14b83ff927b758af164ad9bbe43a22137561" +content-hash = "ce71899fd3579a202d8da58540cfac40ea1f35f124f072e79641a37a078ff2e8" [metadata.files] affine = [ @@ -2770,6 +2857,10 @@ colorcet = [ {file = "colorcet-3.0.0-py2.py3-none-any.whl", hash = "sha256:074027a442921813d4328f03c200a55c8ac73d19901919abcd0c6fb67fa79664"}, {file = "colorcet-3.0.0.tar.gz", hash = "sha256:21c522346a7aa81a603729f2996c22ac3f7822f4c8c303c59761e27d2dfcf3db"}, ] +cycler = [ + {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, + {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, +] dask = [ {file = "dask-2022.6.1-py3-none-any.whl", hash = "sha256:fbd2707070ee8cba080a297301c3984de3d0dee211f30c81387d5fa8adc7de74"}, {file = "dask-2022.6.1.tar.gz", hash = "sha256:6ecc4571da3e5575dd1fa1537237434908f81b929f7f2a3493a7f6eb8507903e"}, @@ -2842,6 +2933,10 @@ fiona = [ {file = "Fiona-1.8.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b4eaf5b88407421d6c9e707520abd2ff16d7cd43efb59cd398aa41d2de332c"}, {file = "Fiona-1.8.21.tar.gz", hash = "sha256:3a0edca2a7a070db405d71187214a43d2333a57b4097544a3fcc282066a58bfc"}, ] +fonttools = [ + {file = "fonttools-4.35.0-py3-none-any.whl", hash = "sha256:0292e391c1b46f2308bda20ea2a2dd5253725e7e2d3a1928b631338eb318eb22"}, + {file = "fonttools-4.35.0.zip", hash = "sha256:1cfb335c0abdeb6231191dc4f9d7ce1173e2ac94b335c617e045b96f9c974aea"}, +] fsspec = [ {file = "fsspec-2022.5.0-py3-none-any.whl", hash = "sha256:2c198c50eb541a80bbd03540b07602c4a957366f3fb416a1f270d34bd4ff0926"}, {file = "fsspec-2022.5.0.tar.gz", hash = "sha256:7a5459c75c44e760fbe6a3ccb1f37e81e023cde7da8ba20401258d877ec483b4"}, @@ -2858,6 +2953,10 @@ gitpython = [ {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, ] +graphviz = [ + {file = "graphviz-0.20.1-py3-none-any.whl", hash = "sha256:587c58a223b51611c0cf461132da386edd896a029524ca61a1462b880bf97977"}, + {file = "graphviz-0.20.1.zip", hash = "sha256:8c58f14adaa3b947daf26c19bc1e98c4e0702cdc31cf99153e6f06904d492bf8"}, +] greenlet = [ {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, {file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"}, @@ -3003,6 +3102,76 @@ jupyterlab-widgets = [ {file = "jupyterlab_widgets-1.1.0-py3-none-any.whl", hash = "sha256:c2a9bd3789f120f64d73268c066ed3b000c56bc1dda217be5cdc43e7b4ebad3f"}, {file = "jupyterlab_widgets-1.1.0.tar.gz", hash = "sha256:d5f41bc1713795385f718d44dcba47e1e1473c6289f28a95aa6b2c0782ee372a"}, ] +kiwisolver = [ + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"}, + {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"}, +] latexcodec = [ {file = "latexcodec-2.0.1-py2.py3-none-any.whl", hash = "sha256:c277a193638dc7683c4c30f6684e3db728a06efb0dc9cf346db8bd0aa6c5d271"}, {file = "latexcodec-2.0.1.tar.gz", hash = "sha256:2aa2551c373261cefe2ad3a8953a6d6533e68238d180eb4bb91d7964adb3fe9a"}, @@ -3091,6 +3260,43 @@ markupsafe = [ {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, ] +matplotlib = [ + {file = "matplotlib-3.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a206a1b762b39398efea838f528b3a6d60cdb26fe9d58b48265787e29cd1d693"}, + {file = "matplotlib-3.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd45a6f3e93a780185f70f05cf2a383daed13c3489233faad83e81720f7ede24"}, + {file = "matplotlib-3.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d62880e1f60e5a30a2a8484432bcb3a5056969dc97258d7326ad465feb7ae069"}, + {file = "matplotlib-3.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ab29589cef03bc88acfa3a1490359000c18186fc30374d8aa77d33cc4a51a4a"}, + {file = "matplotlib-3.5.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2886cc009f40e2984c083687251821f305d811d38e3df8ded414265e4583f0c5"}, + {file = "matplotlib-3.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c995f7d9568f18b5db131ab124c64e51b6820a92d10246d4f2b3f3a66698a15b"}, + {file = "matplotlib-3.5.3-cp310-cp310-win32.whl", hash = "sha256:6bb93a0492d68461bd458eba878f52fdc8ac7bdb6c4acdfe43dba684787838c2"}, + {file = "matplotlib-3.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:2e6d184ebe291b9e8f7e78bbab7987d269c38ea3e062eace1fe7d898042ef804"}, + {file = "matplotlib-3.5.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6ea6aef5c4338e58d8d376068e28f80a24f54e69f09479d1c90b7172bad9f25b"}, + {file = "matplotlib-3.5.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:839d47b8ead7ad9669aaacdbc03f29656dc21f0d41a6fea2d473d856c39c8b1c"}, + {file = "matplotlib-3.5.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3b4fa56159dc3c7f9250df88f653f085068bcd32dcd38e479bba58909254af7f"}, + {file = "matplotlib-3.5.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:94ff86af56a3869a4ae26a9637a849effd7643858a1a04dd5ee50e9ab75069a7"}, + {file = "matplotlib-3.5.3-cp37-cp37m-win32.whl", hash = "sha256:35a8ad4dddebd51f94c5d24bec689ec0ec66173bf614374a1244c6241c1595e0"}, + {file = "matplotlib-3.5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:43e9d3fa077bf0cc95ded13d331d2156f9973dce17c6f0c8b49ccd57af94dbd9"}, + {file = "matplotlib-3.5.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:22227c976ad4dc8c5a5057540421f0d8708c6560744ad2ad638d48e2984e1dbc"}, + {file = "matplotlib-3.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf618a825deb6205f015df6dfe6167a5d9b351203b03fab82043ae1d30f16511"}, + {file = "matplotlib-3.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9befa5954cdbc085e37d974ff6053da269474177921dd61facdad8023c4aeb51"}, + {file = "matplotlib-3.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3840c280ebc87a48488a46f760ea1c0c0c83fcf7abbe2e6baf99d033fd35fd8"}, + {file = "matplotlib-3.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dacddf5bfcec60e3f26ec5c0ae3d0274853a258b6c3fc5ef2f06a8eb23e042be"}, + {file = "matplotlib-3.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b428076a55fb1c084c76cb93e68006f27d247169f056412607c5c88828d08f88"}, + {file = "matplotlib-3.5.3-cp38-cp38-win32.whl", hash = "sha256:874df7505ba820e0400e7091199decf3ff1fde0583652120c50cd60d5820ca9a"}, + {file = "matplotlib-3.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:b28de401d928890187c589036857a270a032961411934bdac4cf12dde3d43094"}, + {file = "matplotlib-3.5.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3211ba82b9f1518d346f6309df137b50c3dc4421b4ed4815d1d7eadc617f45a1"}, + {file = "matplotlib-3.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6fe807e8a22620b4cd95cfbc795ba310dc80151d43b037257250faf0bfcd82bc"}, + {file = "matplotlib-3.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c096363b206a3caf43773abebdbb5a23ea13faef71d701b21a9c27fdcef72f4"}, + {file = "matplotlib-3.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcdfcb0f976e1bac6721d7d457c17be23cf7501f977b6a38f9d38a3762841f7"}, + {file = "matplotlib-3.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e64ac9be9da6bfff0a732e62116484b93b02a0b4d4b19934fb4f8e7ad26ad6a"}, + {file = "matplotlib-3.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:73dd93dc35c85dece610cca8358003bf0760d7986f70b223e2306b4ea6d1406b"}, + {file = "matplotlib-3.5.3-cp39-cp39-win32.whl", hash = "sha256:879c7e5fce4939c6aa04581dfe08d57eb6102a71f2e202e3314d5fbc072fd5a0"}, + {file = "matplotlib-3.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:ab8d26f07fe64f6f6736d635cce7bfd7f625320490ed5bfc347f2cdb4fae0e56"}, + {file = "matplotlib-3.5.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:99482b83ebf4eb6d5fc6813d7aacdefdd480f0d9c0b52dcf9f1cc3b2c4b3361a"}, + {file = "matplotlib-3.5.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f814504e459c68118bf2246a530ed953ebd18213dc20e3da524174d84ed010b2"}, + {file = "matplotlib-3.5.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57f1b4e69f438a99bb64d7f2c340db1b096b41ebaa515cf61ea72624279220ce"}, + {file = "matplotlib-3.5.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d2484b350bf3d32cae43f85dcfc89b3ed7bd2bcd781ef351f93eb6fb2cc483f9"}, + {file = "matplotlib-3.5.3.tar.gz", hash = "sha256:339cac48b80ddbc8bfd05daae0a3a73414651a8596904c2a881cfd1edb65f26c"}, +] matplotlib-inline = [ {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"}, @@ -3862,6 +4068,10 @@ setuptools = [ {file = "setuptools-62.3.3-py3-none-any.whl", hash = "sha256:d1746e7fd520e83bbe210d02fff1aa1a425ad671c7a9da7d246ec2401a087198"}, {file = "setuptools-62.3.3.tar.gz", hash = "sha256:e7d11f3db616cda0751372244c2ba798e8e56a28e096ec4529010b803485f3fe"}, ] +setuptools-scm = [ + {file = "setuptools_scm-6.4.2-py3-none-any.whl", hash = "sha256:acea13255093849de7ccb11af9e1fb8bde7067783450cee9ef7a93139bddf6d4"}, + {file = "setuptools_scm-6.4.2.tar.gz", hash = "sha256:6833ac65c6ed9711a4d5d2266f8024cfa07c533a0e55f4c12f6eff280a5a9e30"}, +] shapely = [ {file = "Shapely-1.8.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c9e3400b716c51ba43eea1678c28272580114e009b6c78cdd00c44df3e325fa"}, {file = "Shapely-1.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce0b5c5f7acbccf98b3460eecaa40e9b18272b2a734f74fcddf1d7696e047e95"}, diff --git a/pyproject.toml b/pyproject.toml index 8c8971b..854de1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,9 @@ pyogrio = {version = ">=0.4.0", extras = ["geopandas"], optional = true} spatialpandas = {version = ">=0.4.0", optional = true} xbatcher = {version = ">=0.1.0", optional = true} # Docs +graphviz = {version = "*", optional = true} jupyter-book = {version="*", optional=true} +matplotlib = {version = "*", optional = true} planetary-computer = {version="*", optional=true} pystac = {version="*", optional=true} @@ -38,9 +40,13 @@ pytest = "*" [tool.poetry.extras] docs = [ "datashader", + "graphviz", "jupyter-book", + "matplotlib", "planetary-computer", + "pyogrio", "pystac", + "spatialpandas", "xbatcher" ] raster = ["xbatcher"]