diff --git a/.gitignore b/.gitignore
index d88455c..049d175 100644
--- a/.gitignore
+++ b/.gitignore
@@ -167,3 +167,4 @@ docs/generated
tests/duckdb-extensions
tests/out.json
+data/its-live*
diff --git a/docs/notebooks/its-live.ipynb b/docs/notebooks/its-live.ipynb
new file mode 100644
index 0000000..e13ae4f
--- /dev/null
+++ b/docs/notebooks/its-live.ipynb
@@ -0,0 +1,467 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "23e8ca35",
+ "metadata": {},
+ "source": [
+ "# ITS_LIVE case study\n",
+ "\n",
+ "An example of using **rustac** with [ITS_LIVE](https://its-live.jpl.nasa.gov/) STAC data.\n",
+ "We'll start just by looking at the Landsat ndjson.\n",
+ "\n",
+ "
\n",
+ "
AWS credentials
\n",
+ "
\n",
+ " You'll want to make sure you're running this notebook with your AWS credentials configured in your environment, and set your default region to us-west-2.\n",
+ "
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "a8a0bf03",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pathlib import Path\n",
+ "from obstore.store import S3Store\n",
+ "\n",
+ "destination = Path(\"../../data/its-live\")\n",
+ "source_store = S3Store(\n",
+ " bucket=\"its-live-data\", prefix=\"test-space/stac_catalogs/landsatOLI/v02\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "812b1fe1",
+ "metadata": {},
+ "source": [
+ "Let's list all of the ndjson files."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "d7a7afdd",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Found 5134 paths with sizes ranging from 2.9 kB to 233.0 MB\n",
+ "Total size of the files is 28.7 GB\n"
+ ]
+ }
+ ],
+ "source": [
+ "import humanize\n",
+ "\n",
+ "paths = []\n",
+ "sizes = []\n",
+ "for list_stream in source_store.list():\n",
+ " for object_meta in list_stream:\n",
+ " paths.append(object_meta[\"path\"])\n",
+ " sizes.append(object_meta[\"size\"])\n",
+ "\n",
+ "print(\n",
+ " f\"Found {len(paths)} paths with sizes ranging from {humanize.naturalsize(min(sizes))} to {humanize.naturalsize(max(sizes))}\"\n",
+ ")\n",
+ "print(f\"Total size of the files is {humanize.naturalsize(sum(sizes))}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b7161cba",
+ "metadata": {},
+ "source": [
+ "That's a lot of data!\n",
+ "We'd like to make one or more STAC Collections from it, but we don't really want to store that much ndjson locally.\n",
+ "**stac-geoparquet** is much more compact (especially when compressed) so let's copy-and-convert.\n",
+ "\n",
+ "This will take a while, on the order of an hour or two.\n",
+ "An implementation using in-region resources would be faster.\n",
+ "\n",
+ "\n",
+ "
Backfilling errors
\n",
+ "
\n",
+ " The block includes a check for already-existing files, so you can run it multiple times to pick up any files that errored.\n",
+ "
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1933f2c2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from asyncio import TaskGroup, Semaphore\n",
+ "import tqdm\n",
+ "import rustac\n",
+ "\n",
+ "# Limit the number of files we hold in memory at a time\n",
+ "semaphore = Semaphore(10)\n",
+ "# Store the ones that error, it's the internet after all, things will error\n",
+ "missed_paths = []\n",
+ "\n",
+ "\n",
+ "async def copy_and_convert(\n",
+ " source_path: str, source_store, destination_path: Path, progress_bar: tqdm.tqdm\n",
+ ") -> None:\n",
+ " async with semaphore:\n",
+ " try:\n",
+ " value = await rustac.read(source_path, store=source_store)\n",
+ " except Exception:\n",
+ " missed_paths.append(path)\n",
+ " progress_bar.update()\n",
+ " return\n",
+ "\n",
+ " # ndjson with only one item are read as an item, not a item collection\n",
+ " if value[\"type\"] == \"Feature\":\n",
+ " await rustac.write(str(destination_path), [value])\n",
+ " else:\n",
+ " assert value[\"type\"] == \"FeatureCollection\"\n",
+ " await rustac.write(str(destination_path), value)\n",
+ "\n",
+ " progress_bar.update()\n",
+ "\n",
+ "\n",
+ "progress_bar = tqdm.tqdm(total=len(paths), miniters=1)\n",
+ "async with TaskGroup() as task_group:\n",
+ " for path in paths:\n",
+ " destination_path = Path(destination / path).with_suffix(\".parquet\")\n",
+ " if destination_path.exists():\n",
+ " progress_bar.update()\n",
+ " else:\n",
+ " task_group.create_task(\n",
+ " copy_and_convert(path, source_store, destination_path, progress_bar)\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "287045de",
+ "metadata": {},
+ "source": [
+ "Alright!\n",
+ "Let's see what we got."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "85348f6c",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0 files errored\n",
+ "The 5134 stac-geoparquet files are 3.6 GB\n",
+ "That's 12.53% of the original size\n"
+ ]
+ }
+ ],
+ "source": [
+ "import os.path\n",
+ "\n",
+ "print(f\"{len(missed_paths)} files errored\")\n",
+ "\n",
+ "count = 0\n",
+ "size = 0\n",
+ "for path in destination.glob(\"**/*.parquet\"):\n",
+ " count += 1\n",
+ " size += os.path.getsize(path)\n",
+ "\n",
+ "print(f\"The {count} stac-geoparquet files are {humanize.naturalsize(size)}\")\n",
+ "print(f\"That's {100 * size / sum(sizes):.2f}% of the original size\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "af91a914",
+ "metadata": {},
+ "source": [
+ "Very cool.\n",
+ "We can use [DuckDB](https://duckdb.org/) to search into the files."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "6db49d82",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "┌──────────────┐\n",
+ "│ count_star() │\n",
+ "│ int64 │\n",
+ "├──────────────┤\n",
+ "│ 9907260 │\n",
+ "└──────────────┘"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import duckdb\n",
+ "\n",
+ "duckdb.sql(f\"select count(*) from read_parquet('{destination}/**/*.parquet')\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fa5e6374",
+ "metadata": {},
+ "source": [
+ "DuckDB recommends that each partition contains [100 MB](https://duckdb.org/docs/stable/data/partitioning/partitioned_writes.html#partitioned-writes) of data, but some of our files are much smaller.\n",
+ "Let's re-partition our data by year to get larger partitions."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "e555066c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "partitioned_destination = \"../../data/its-live-partitioned\"\n",
+ "# We limit the number of open files to not hose our processor, it defaults to 100\n",
+ "duckdb.sql(\"set partitioned_write_max_open_files = 4;\")\n",
+ "duckdb.sql(\n",
+ " f\"copy (select *, year(datetime) as year from read_parquet('{destination}/**/*.parquet', union_by_name=true)) to '{partitioned_destination}' (format parquet, partition_by (year), overwrite_or_ignore)\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f40a2807",
+ "metadata": {},
+ "source": [
+ "We can now query by year effectively."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "id": "80ead1a2",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "┌──────────────┐\n",
+ "│ count_star() │\n",
+ "│ int64 │\n",
+ "├──────────────┤\n",
+ "│ 1278258 │\n",
+ "└──────────────┘"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "duckdb.sql(\n",
+ " f\"select count(*) from read_parquet('{partitioned_destination}/**/*.parquet') where year = 2024\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "0dd70787",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "┌──────────────────────┐\n",
+ "│ column_name │\n",
+ "│ varchar │\n",
+ "├──────────────────────┤\n",
+ "│ type │\n",
+ "│ stac_version │\n",
+ "│ stac_extensions │\n",
+ "│ id │\n",
+ "│ version │\n",
+ "│ proj:code │\n",
+ "│ links │\n",
+ "│ assets │\n",
+ "│ collection │\n",
+ "│ datetime │\n",
+ "│ · │\n",
+ "│ · │\n",
+ "│ · │\n",
+ "│ platform │\n",
+ "│ scene_1_id │\n",
+ "│ scene_2_id │\n",
+ "│ scene_1_path_row │\n",
+ "│ scene_2_path_row │\n",
+ "│ sat:orbit_state │\n",
+ "│ percent_valid_pixels │\n",
+ "│ bbox │\n",
+ "│ geometry │\n",
+ "│ year │\n",
+ "├──────────────────────┤\n",
+ "│ 27 rows (20 shown) │\n",
+ "└──────────────────────┘"
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "duckdb.sql(\n",
+ " f\"select column_name from (describe select * from read_parquet('{partitioned_destination}/**/*.parquet'))\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9c9c8844",
+ "metadata": {},
+ "source": [
+ "Each partition's files is still **stac-geoparquet**, so we can read them back in if we want.\n",
+ "Most of them are pretty big, so we intentionally pick a smaller one for this example."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "cb353cb5",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "23\n"
+ ]
+ }
+ ],
+ "source": [
+ "item_collection = await rustac.read(\n",
+ " str(Path(partitioned_destination) / \"year=1982\" / \"data_0.parquet\")\n",
+ ")\n",
+ "print(len(item_collection[\"features\"]))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4886e961",
+ "metadata": {},
+ "source": [
+ "## Searching\n",
+ "\n",
+ "One of **rustac**'s features is the ability to use [STAC API search](https://api.stacspec.org/v1.0.0/item-search/) against **stac-geoparquet** files, no server required.\n",
+ "We can use a [cql2](https://github.com/developmentseed/cql2-rs/) filter to query by attributes."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "89b7e0b2",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "39696\n"
+ ]
+ }
+ ],
+ "source": [
+ "import cql2\n",
+ "\n",
+ "href = str(Path(partitioned_destination) / \"**\" / \"*.parquet\")\n",
+ "cql2_json = cql2.parse_text(\"percent_valid_pixels=100\").to_json()\n",
+ "items = await rustac.search(href, filter=cql2_json)\n",
+ "print(len(items))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "569bc117",
+ "metadata": {},
+ "source": [
+ "If we know we're going to go to a **geopandas** `GeoDataFrame`, we can search directly to an **arrow** table to make things a bit more efficient.\n",
+ "To do so, we'll need to use **rustac**'s `DuckdbClient`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "c7e2f43a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "from rustac import DuckdbClient\n",
+ "from geopandas import GeoDataFrame\n",
+ "\n",
+ "client = DuckdbClient()\n",
+ "table = client.search_to_arrow(href, filter=cql2_json)\n",
+ "data_frame = GeoDataFrame.from_arrow(table)\n",
+ "data_frame.plot()"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": ".venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.13.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/mkdocs.yml b/mkdocs.yml
index 007b52f..8de9484 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -23,6 +23,7 @@ nav:
- notebooks/read.ipynb
- notebooks/store.ipynb
- notebooks/stac-geoparquet.ipynb
+ - notebooks/its-live.ipynb
- notebooks/search.ipynb
- API:
- api/index.md
diff --git a/pyproject.toml b/pyproject.toml
index 0847ec4..edd89d9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -70,6 +70,7 @@ dev = [
docs = [
"arro3-core>=0.4.5",
"contextily>=1.6.2",
+ "cql2>=0.3.7",
"duckdb>=1.3.0",
"griffe>=1.6.0",
"humanize>=4.12.1",
diff --git a/uv.lock b/uv.lock
index 9af3deb..2373a59 100644
--- a/uv.lock
+++ b/uv.lock
@@ -408,6 +408,74 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c1/31/1ae946f11dfbd229222e6d6ad8e7bd1891d3d48bde5fbf7a0beb9491f8e3/contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", size = 236668, upload-time = "2024-11-12T10:57:39.061Z" },
]
+[[package]]
+name = "cql2"
+version = "0.3.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/0b/0011b4e45c888e1b8b9eaaec05716bb0b6f28c4110923b4654a7b8957ed3/cql2-0.3.7.tar.gz", hash = "sha256:8d70cc6005c379f22247e7f6d4afeb1455cd3d65f88c83326ac10e5aa0453a42", size = 157195, upload-time = "2025-05-19T17:37:21.076Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/72/8a90f9332eecc13aabfaf065d774049699ba4ed99780d037a29b402790a4/cql2-0.3.7-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:57e31ca5271e80a541e3fb7db4b02cce3aa390adbc2e215cff6ad2935f80ff70", size = 2564289, upload-time = "2025-05-19T17:36:20.065Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/26/2df495004633ac655bef6960d2fb20c5250bdbe3e37f45af5344d1322e5c/cql2-0.3.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0e5800ea76cb239e97a5127246345811426d7d36589ec5ec20c522ab3a3edcc0", size = 2446368, upload-time = "2025-05-19T17:36:15.804Z" },
+ { url = "https://files.pythonhosted.org/packages/26/07/d91d9dd25cc62af353d3c6e35bb14c92243788643f934263bdd1f3bf0813/cql2-0.3.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3de7e29fadda7d5bc8c24d10d807fbdfa7bc9919dadce5655e0345dc2bc25d14", size = 2694218, upload-time = "2025-05-19T17:34:57.289Z" },
+ { url = "https://files.pythonhosted.org/packages/55/35/81ebef8e306b890654512d37b26acd65cfc5eadcfe60d20d01a1ae0ad045/cql2-0.3.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f52197549b08db5bceb74d7220f067c75750542322c14d9496866698fb1b586", size = 2620888, upload-time = "2025-05-19T17:35:11.1Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/08/278ca5124c0595f0053578195e689dcda0e095fc55be9c546704c8bdb867/cql2-0.3.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc05d42bd600af41a26fd2c7b594e1b1e7ddc19e4e7159da2b377b968e892b5", size = 2898818, upload-time = "2025-05-19T17:35:54.683Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/0e/8f825cfbfc26d9a4866d1863c56c3a72b81af5167530ded38954d68de06c/cql2-0.3.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47d3730741e4663a772a2c12cdac58c573cefa4c1256dcca2a47e01179a2f2c7", size = 3148740, upload-time = "2025-05-19T17:35:27.797Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/c9/a6e6b5dc36e0ba0ceddcd268d799a346709d4de2e7e9569d4b4241ac4d30/cql2-0.3.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:095270d250c64f2f7ef4d41b6a9cc6db9d2cbac6fe0049b42e594cf586864785", size = 2863891, upload-time = "2025-05-19T17:35:40.736Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/08/81181581f8905c650a365014f3672d2bdc10cc8566940ef89c2bc9abf2cf/cql2-0.3.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d3b27dc924bea109f51533658e886a7ce2aae60d5048519ad6df864486f2787", size = 2793346, upload-time = "2025-05-19T17:36:05.068Z" },
+ { url = "https://files.pythonhosted.org/packages/43/15/59d745b752f54c11bc0a9095986315610106743973741adf4e7067f40b08/cql2-0.3.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b9c901ed0b2e53e8ea573d5ad181f63681ef05bc6c50d41ae495099a3063d8a1", size = 2858851, upload-time = "2025-05-19T17:36:25.902Z" },
+ { url = "https://files.pythonhosted.org/packages/28/88/3be90b2a8d373d0f5ae456de9ee3ce13cfc3764f28479263297c6f4b5823/cql2-0.3.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:7d316b2b946549de59d958a58196e7e464da465df279f5ce8ebff37c5c2359ac", size = 2882954, upload-time = "2025-05-19T17:36:39.622Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/91/8be8b0d6d4698df6841bd59f761d3a5782e7d8f94c567ed80ce1dadc80ff/cql2-0.3.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1acc3843140a4184fa2c23c252f48a7dd4019c3bf548ae188eee298af2a9e803", size = 2925149, upload-time = "2025-05-19T17:36:53.844Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/dc/f9859fa5fc3ea75d5b0ef0ffdfabf257ed3eb9c186edb8a18692d808f36d/cql2-0.3.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82f04ba267f478b888dc99ffb605c194398b9cd3cc390d9aae3012f89aa5279e", size = 2965319, upload-time = "2025-05-19T17:37:08.092Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/1a/4056f4b32c3e0c7507ad178622f44eba3fec85d0de7dd3b875b25677e956/cql2-0.3.7-cp311-cp311-win32.whl", hash = "sha256:676d5b7651e50963fcb72c0db745ffe16bb6500e81a374f93d091adca82153a4", size = 2101242, upload-time = "2025-05-19T17:37:31.668Z" },
+ { url = "https://files.pythonhosted.org/packages/13/eb/3e647ecb986126a1a38af4c8077a13bf3bc138bb8151cd6cdad0884a2c63/cql2-0.3.7-cp311-cp311-win_amd64.whl", hash = "sha256:15b2a47d0d2cb88cf1913b50cd85809cbf7716ca2f2561fe9341d34df572a87f", size = 2281785, upload-time = "2025-05-19T17:37:24.021Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/1e/2ab2c6c86600c169ecfc2470ee4426598d1fee24226ca2547fbb53bc41ec/cql2-0.3.7-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f753fc9ff1f02bcdc2409efceb8445cc2ac3a39d8b2a02cbc299df9361d16099", size = 2558369, upload-time = "2025-05-19T17:36:21.767Z" },
+ { url = "https://files.pythonhosted.org/packages/54/77/c93514fe1c1248c70bb7c58c45aabee305745dd1eabdd8fb052ce261bd28/cql2-0.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e076909b68aa8f94bea8ba4a1fd0256a3af9063dfc931220d7c3c1c98feeee26", size = 2441559, upload-time = "2025-05-19T17:36:17.294Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/2d/93d8fb620e1ad72dddecbc8db67703183d3ecbdaf82d31cc44b19a1c187e/cql2-0.3.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2bb92890661364b4fc7a35823bf95ade7a0e5fe00ae1455c2529e820bb72b21", size = 2694594, upload-time = "2025-05-19T17:34:58.553Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/35/7722a19e52235e4f53c0ad55b5e0cd504d71e7e72841cfe8df7b7882aaea/cql2-0.3.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d2b47b378e07696cfa021e99937c910ac8b7f404afc17c741fec9c861f49a77", size = 2620370, upload-time = "2025-05-19T17:35:12.716Z" },
+ { url = "https://files.pythonhosted.org/packages/07/6a/1a34d32f2398c07b3aab27438e3d45cbcef26080df83ff0c593b639eca41/cql2-0.3.7-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2fb1080477c445fb92f653d0f9552efe8684310b7a95573d98371400be7b04f", size = 2898508, upload-time = "2025-05-19T17:35:56.046Z" },
+ { url = "https://files.pythonhosted.org/packages/41/c7/d7cb8c4a8bc1db92f8d63e40029701d680b0227f3b3a4da52fa81a9ce32d/cql2-0.3.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:938316b6bcd4482c65fd888185b1e995c9a58528ad52bce02827bed7a70f5c27", size = 3148810, upload-time = "2025-05-19T17:35:29.175Z" },
+ { url = "https://files.pythonhosted.org/packages/db/14/6d5f1d5fb2ee17a3f358aee3812b21acaeff46ca00d8246f2926db9b139f/cql2-0.3.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2a0e95771195a4fcf809bad792ea1375cae0a8800f14d7b78e012932b779e70", size = 2865582, upload-time = "2025-05-19T17:35:42.219Z" },
+ { url = "https://files.pythonhosted.org/packages/94/63/ba0e443e430596d142e979e8ad93b162143a9efeedd1bcc0b7285506e4ba/cql2-0.3.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cab7481e43f57af7dd0cfd79129f00c28da1cd9953a1a6b08862d8dcf145040c", size = 2793023, upload-time = "2025-05-19T17:36:07.395Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/e2/0ef34e25cdbbc4001968720a0a3314a86e070524a81957a752126f1923f4/cql2-0.3.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:00469a86e6bfb27f61abe159b96e1bfda04a6d91cd6e8966f5f336bb7ee52522", size = 2859283, upload-time = "2025-05-19T17:36:27.299Z" },
+ { url = "https://files.pythonhosted.org/packages/81/85/961cc9aac5806cbf45ce6057b472a1f3a4a7902ac8e5a2b529ff1930a655/cql2-0.3.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7e228f3a948b08baf5140354167d06eca247b8b6cdcf2be629f7a76c29aa699b", size = 2881661, upload-time = "2025-05-19T17:36:41.377Z" },
+ { url = "https://files.pythonhosted.org/packages/30/3d/de1205213bbc458690ab293023b2f0cafd45c672c16eef3251aca963b14a/cql2-0.3.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ba60c016e473eb44b146128d89c5543eaeee842f7f0d502fad5c3c4514b227c7", size = 2925366, upload-time = "2025-05-19T17:36:55.242Z" },
+ { url = "https://files.pythonhosted.org/packages/97/58/ced20074f25c9afa1b88cd1dfdd0789c7f915abece27856140cf21155b32/cql2-0.3.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30d72f9ee22359c6482c1fa40390b92ae82d57f3338b670b6630660f5bab0648", size = 2965303, upload-time = "2025-05-19T17:37:10.893Z" },
+ { url = "https://files.pythonhosted.org/packages/50/26/3a1448563d74d90976f76c3a28c96fcb6c1197e9018d38019ba55101c863/cql2-0.3.7-cp312-cp312-win32.whl", hash = "sha256:0353819c9d16ca86ba84bba527a7f28eb0ce97a61b34dc9fd12f6285cd82685f", size = 2101212, upload-time = "2025-05-19T17:37:33.039Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/e3/9185e7d5989edb4181dd94ce2700dc92bb0c33a42d07f314fa9090420fe5/cql2-0.3.7-cp312-cp312-win_amd64.whl", hash = "sha256:c3ac07b473ce5dc12bf0056904f94596dda56ae2b1670a7f55a0fd4eed0c2773", size = 2283394, upload-time = "2025-05-19T17:37:25.333Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/35/0accfa660b905afc70f587919330c924a6f70a51d0897c1b685889f19715/cql2-0.3.7-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:93ce3c017ba814b4cbb944f6d989e3bd1e0e39dece3ff000fd6763e37c67d64c", size = 2558023, upload-time = "2025-05-19T17:36:23.028Z" },
+ { url = "https://files.pythonhosted.org/packages/01/53/bce290bdb87f9bfe07950cd72f104a9569b74be422cb32140629e0e2d4e3/cql2-0.3.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a7fe56629b5273f93cf6e914f4d113ac98a6fff27cee051fe12f1a2fccfa182", size = 2441820, upload-time = "2025-05-19T17:36:18.634Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/b6/3f59eb5ab8d4ece76d3235e5fb23eda08e9cebc3262a10547cba54510d7d/cql2-0.3.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a3010f8e546c69ff34757a2186eb45a62edec8481b5885d8a98ce4c0b3b7038", size = 2695578, upload-time = "2025-05-19T17:35:00.006Z" },
+ { url = "https://files.pythonhosted.org/packages/db/29/aff8edf1092343812cece602701b462abe4af15241a2ba3ecea1a35ac693/cql2-0.3.7-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:14512c76d3bac80fa9a91c796b957eb7367b07b39660f0fb1ed0753cafec0161", size = 2620539, upload-time = "2025-05-19T17:35:14.412Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/f2/8fd6330b0e855b452f28d80701a94b2b8528700d2449938153977be4355d/cql2-0.3.7-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:234200f577282f79ec2ea04103b5a59a2b3984d084ef922f98c70b7ad4abbe7a", size = 2898898, upload-time = "2025-05-19T17:35:57.399Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/6c/bc6a2497b48805aa1ebf3a32a3f25a6afe516761816d49988d070f324660/cql2-0.3.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f973538bcff35785ab5a29646435e11a1bc2dbdfc76b4b14d6f7db12079788df", size = 3149296, upload-time = "2025-05-19T17:35:30.761Z" },
+ { url = "https://files.pythonhosted.org/packages/de/0c/9d91ab7074204e36e91e4624bd8804cdc0e9a127441ee805ee16e4eb7a7d/cql2-0.3.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1990dac368c14aae4d2ed680451e2b0c4de7d1571a138a2824a7fa6f6a037c0f", size = 2865849, upload-time = "2025-05-19T17:35:44.088Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/24/b335f72326676fa15bb6e298b1bf86e9d466fa2cbc074665d5c163c142b9/cql2-0.3.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f49a85bd70c71eaf946c1af63050803aa6aae138caf78bc1dd0ef3589935d1a", size = 2793151, upload-time = "2025-05-19T17:36:09.287Z" },
+ { url = "https://files.pythonhosted.org/packages/82/73/0bcc678470146d8d42d21ce8b1d7ea76a00e6a0894f585a29e38ec2ecd9a/cql2-0.3.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cff45abd97bacada6725f494bfa95fda578a90414df09437eccda5202f844fc9", size = 2859722, upload-time = "2025-05-19T17:36:28.631Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/ca/63b84df3f576b830ce8a31c7b54600eebad1f0eb74dae52881530ceee009/cql2-0.3.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8d0aad5e9ce03c7d338c16ffff2fd050baa161f2bdb895ff8d36d77233823673", size = 2882253, upload-time = "2025-05-19T17:36:43.493Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/21/b2aeac23c88739844b72b95969755775edf7a85dcff0a73b847b1146f60f/cql2-0.3.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2b287f21c84886b1655733d73af7542581a23b420e98fbc957880c7b9d06db1c", size = 2925877, upload-time = "2025-05-19T17:36:56.693Z" },
+ { url = "https://files.pythonhosted.org/packages/64/42/523a0ac0e8b2a3e667f2f239d9083c3e7e128e56eb2b3840794912ec98ee/cql2-0.3.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d4913311fbc9d47c752531856d580f9c55d8aa23b0af0f5394d1f5a076af41d0", size = 2965714, upload-time = "2025-05-19T17:37:12.304Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/e9/4a4b305f6b279332fcd8275253446c1859070e560eeb9fd210fbab088571/cql2-0.3.7-cp313-cp313-win32.whl", hash = "sha256:cc866398e80639c6c4dd342ee0b3a0eb62d6b5ece6f45cf81e4e5ad99fdb6a10", size = 2101561, upload-time = "2025-05-19T17:37:34.412Z" },
+ { url = "https://files.pythonhosted.org/packages/43/64/4b85efadb3e8ecca4a85d842d349e15d2984230a4d99d6131a0a86d6f3c8/cql2-0.3.7-cp313-cp313-win_amd64.whl", hash = "sha256:4caad8ab44a33c3816fc218eeaa8576fb0e13e17ac342c0e9b3581bacaeaa961", size = 2283385, upload-time = "2025-05-19T17:37:26.796Z" },
+ { url = "https://files.pythonhosted.org/packages/00/47/f4c8481628665846b63db606cc38e934fc012f141cc1651786b67ca4039d/cql2-0.3.7-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:beda52a31edb2b763753ef15f854c6a8bbfefbb53dc293186b27d33b85454f78", size = 2692671, upload-time = "2025-05-19T17:35:01.401Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/9e/819d776c57602a7d1d77619ee8c0d376cded0f7c03a5ae7fd228c3142a12/cql2-0.3.7-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba05564bdc5933e3a95c4571670fd9d95f15200d5009d998bb938e607ded9731", size = 2617797, upload-time = "2025-05-19T17:35:15.718Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/26/7f0ac52da971edee397c82e0ae2e85d4124c6b7705f067fc69e052cf2239/cql2-0.3.7-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:289128d269959622475e9b37b6e83cb9a94274385cf3b7305fe907370d5ad306", size = 3147234, upload-time = "2025-05-19T17:35:32.121Z" },
+ { url = "https://files.pythonhosted.org/packages/82/c8/966364964a40469781e75a937a949f1f4d75ae18c061dcd42b9ff56aac01/cql2-0.3.7-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b7865fe10f9786ac81253d705ea20dfddcd214b99837b10d8eb2ec342622a08", size = 2864452, upload-time = "2025-05-19T17:35:45.403Z" },
+ { url = "https://files.pythonhosted.org/packages/91/9a/0fceddd02b79171cfe6867ea594ab0d849b1c5e857d5a04d783f40e4dde9/cql2-0.3.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:238b13f1c7d028395922b45f3fbae64342660d7afd45b7400fde239a9c37e771", size = 2855679, upload-time = "2025-05-19T17:36:30.07Z" },
+ { url = "https://files.pythonhosted.org/packages/95/cb/8466100671c828c5d652d59e8380fb00d01e918f60cf62d074d6a1b16ab2/cql2-0.3.7-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:73a248d9d2cb7668d396d188471b8c65b261df195cc768fb17e75181744f18fe", size = 2880686, upload-time = "2025-05-19T17:36:45.093Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/63/59ba2e3415c80561ca9f9f2637cb40f40279ef18efdf30d01511e480937f/cql2-0.3.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ba558fd2433d4dd752d8c4456ff2e1c05b31c05441ab88258c2eda8114ee59ed", size = 2923188, upload-time = "2025-05-19T17:36:58.049Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/0d/2bc0a15c8acce5cc07add8c527217b3005fab982fab24931c54a52a530ec/cql2-0.3.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3810ecdfdba44b5f605021ef8a70ee12a42faa4adbe813f2ff23a23f8a6bf0c6", size = 2964431, upload-time = "2025-05-19T17:37:13.711Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/65/5995533aa6581981780e45de61f403c99711226b9573c46c1e6971a227ce/cql2-0.3.7-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f519e569ec4b744b8dfbe9d8ada7106e7b18d94a0966457d729275f0e2a1f234", size = 2695118, upload-time = "2025-05-19T17:35:06.253Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/00/032e10ae05a96d84070714b2785c4eae3f6c25132785059a18ad55b1884b/cql2-0.3.7-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a35030b735fc943bbb6f5fdb556b691c6e796aa6785d2cb8cf0d1081fd8a33b", size = 2622879, upload-time = "2025-05-19T17:35:20.522Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/ab/05f77a3884bfa948b9015c49f7ce693dbe9d4e2b4685bfafde6a75a5b55d/cql2-0.3.7-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:073fbb201082aac7cc54f8acfd5495feab142c67895067ee60cb495a9d956d70", size = 2900048, upload-time = "2025-05-19T17:36:01.814Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/44/eb2d78228e029d27ba8afe4fc3b1721a6f54003f04eeb0399a06a059ff26/cql2-0.3.7-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0be7a2958a4949930805fd0cdbec2674cab1d24a195406f2c4fb6c44fa33f0e4", size = 3148251, upload-time = "2025-05-19T17:35:36.664Z" },
+ { url = "https://files.pythonhosted.org/packages/33/d6/0c9e5050ce0e002cc6b92ec230b3a875caa55a4d8473d70157f8a705f6e6/cql2-0.3.7-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3073c43c04d6cf5f108fd2837dcc995ceef7aea019235ed1945e6c6950957a61", size = 2863973, upload-time = "2025-05-19T17:35:49.455Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/a3/566f93f7225f22ca2b71193eba0c23c810b0cedc9376934d23d678e4ccfc/cql2-0.3.7-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c345908faaa75d4b9e783d8370a4c36cbd2c8132efb62e5d83e5b8aa12308a3", size = 2793272, upload-time = "2025-05-19T17:36:14.09Z" },
+ { url = "https://files.pythonhosted.org/packages/35/8c/0962335d8d1021b724196145e2a51cca490a768a5701147b06544cfaa03b/cql2-0.3.7-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4df0e1864d72bc5ecdd57db610c7ce5bded9109e8d14752268ec46f68a99ddc4", size = 2858862, upload-time = "2025-05-19T17:36:34.6Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/85/ca81cfc712a461d5ba8ee2939014fd9f5901fdb42ef55178924b845429a1/cql2-0.3.7-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:690f5832817633e564922ecc667f71bbf0b30060ea45e1a559b40c4a8d33c9be", size = 2883587, upload-time = "2025-05-19T17:36:49.289Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/f8/ca76817f8183b11a9e3bdc8c931b94c56a31eafca5ab2a25e30b3e79630c/cql2-0.3.7-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1bdc1f924a1dd3011f22d82b66b030664fd8a88c65fa3e2b1992245a0ebdf97b", size = 2925333, upload-time = "2025-05-19T17:37:03.149Z" },
+ { url = "https://files.pythonhosted.org/packages/93/76/7275e9fb58e58b76b13097582e4b488ba0639d59c6bad1f9b5657852cc42/cql2-0.3.7-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c344ce7644f3a7a328b57d4133986ccbce05336c11eb8de50d935b5d0d9527e", size = 2965142, upload-time = "2025-05-19T17:37:18.179Z" },
+]
+
[[package]]
name = "cssselect2"
version = "0.7.0"
@@ -2305,6 +2373,7 @@ dev = [
docs = [
{ name = "arro3-core" },
{ name = "contextily" },
+ { name = "cql2" },
{ name = "duckdb" },
{ name = "griffe" },
{ name = "humanize" },
@@ -2340,6 +2409,7 @@ dev = [
docs = [
{ name = "arro3-core", specifier = ">=0.4.5" },
{ name = "contextily", specifier = ">=1.6.2" },
+ { name = "cql2", specifier = ">=0.3.7" },
{ name = "duckdb", specifier = ">=1.3.0" },
{ name = "griffe", specifier = ">=1.6.0" },
{ name = "humanize", specifier = ">=4.12.1" },