Skip to content

Commit 65f8e24

Browse files
authored
feat: add geopandas notebook (#57)
cc @scottyhq
1 parent 30533c3 commit 65f8e24

File tree

12 files changed

+547187
-115
lines changed

12 files changed

+547187
-115
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Create a item collection from an arrow table ([#57](https://github.com/stac-utils/stacrs/pull/57))
12+
913
## [0.5.6-beta.0] - 2025-02-22
1014

1115
### Added

Cargo.lock

Lines changed: 34 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ name = "stacrs"
99
crate-type = ["cdylib"]
1010

1111
[dependencies]
12-
clap = "4.5.30"
12+
clap = "4.5.31"
1313
geojson = "0.24.1"
14-
pyo3 = { version = "0.23.4", features = ["extension-module"] }
14+
geoarrow = "0.4.0-beta.3"
15+
pyo3 = { version = "0.23.5", features = ["extension-module"] }
1516
pyo3-async-runtimes = { version = "0.23.0", features = [
1617
"tokio",
1718
"tokio-runtime",

docs/example.ipynb

Lines changed: 546619 additions & 79 deletions
Large diffs are not rendered by default.

pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ dependencies = []
2525

2626
[project.optional-dependencies]
2727
arrow = ["arro3-core>=0.4.5"]
28+
docs = [
29+
"jinja2>=3.1.4",
30+
]
2831

2932
[project.scripts]
3033
stacrs = "stacrs:main"
@@ -58,6 +61,9 @@ dev = [
5861
"stac-geoparquet>=0.6.0",
5962
]
6063
docs = [
64+
"contextily>=1.6.2",
65+
"humanize>=4.12.1",
66+
"jinja2>=3.1.4",
6167
"mike>=2.1.3",
6268
"mkdocs-jupyter>=0.25.1",
6369
"mkdocs-material[imaging]>=9.5.45",

src/arrow.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use crate::{Error, Json, Result};
2+
use geoarrow::table::Table;
3+
use pyo3::{prelude::*, IntoPyObjectExt};
4+
use pyo3_arrow::PyTable;
5+
use serde_json::Value;
6+
use stac::{Item, ItemCollection};
7+
8+
#[pyfunction]
9+
pub fn from_arrow(py: Python<'_>, table: PyTable) -> PyResult<Bound<PyAny>> {
10+
let (record_batches, mut schema) = table.into_inner();
11+
let record_batches = record_batches
12+
.into_iter()
13+
.map(|record_batch| {
14+
let record_batch = stac::geoarrow::with_native_geometry(record_batch, "geometry")?;
15+
Ok(record_batch)
16+
})
17+
.collect::<Result<Vec<_>>>()?;
18+
if !record_batches.is_empty() {
19+
schema = record_batches[0].schema();
20+
}
21+
let table = Table::try_new(record_batches, schema).map_err(Error::from)?;
22+
let item_collection = stac::geoarrow::from_table(table).map_err(Error::from)?;
23+
let item_collection = Json(item_collection).into_pyobject(py)?;
24+
Ok(item_collection)
25+
}
26+
27+
#[pyfunction]
28+
pub fn to_arrow(py: Python<'_>, items: Bound<PyAny>) -> PyResult<PyObject> {
29+
let value: Value = pythonize::depythonize(&items)?;
30+
let item_collection = if let Value::Array(array) = value {
31+
let items = array
32+
.into_iter()
33+
.map(|value| serde_json::from_value::<Item>(value).map_err(Error::from))
34+
.collect::<Result<Vec<_>>>()?;
35+
ItemCollection::from(items)
36+
} else {
37+
serde_json::from_value(value).map_err(Error::from)?
38+
};
39+
// TODO we might want to just allow use to go WKB right when we got to table?
40+
let (record_batches, mut schema) = stac::geoarrow::to_table(item_collection)
41+
.map_err(Error::from)?
42+
.into_inner();
43+
let record_batches = record_batches
44+
.into_iter()
45+
.map(|record_batch| {
46+
stac::geoarrow::with_wkb_geometry(record_batch, "geometry").map_err(Error::from)
47+
})
48+
.collect::<Result<Vec<_>>>()?;
49+
if !record_batches.is_empty() {
50+
schema = record_batches[0].schema();
51+
}
52+
let table = PyTable::try_new(record_batches, schema)?;
53+
let table = table.to_arro3(py)?;
54+
Ok(table.into_py_any(py)?)
55+
}

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ pub enum Error {
1212
#[error(transparent)]
1313
Geojson(#[from] geojson::Error),
1414

15+
#[error(transparent)]
16+
Geoarrow(#[from] geoarrow::error::GeoArrowError),
17+
1518
#[error(transparent)]
1619
Io(#[from] std::io::Error),
1720

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![deny(unused_crate_dependencies)]
22

3+
mod arrow;
34
mod cli;
45
mod duckdb;
56
mod error;
@@ -20,6 +21,8 @@ fn stacrs(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
2021

2122
m.add_class::<duckdb::DuckdbClient>()?;
2223

24+
m.add_function(wrap_pyfunction!(arrow::from_arrow, m)?)?;
25+
m.add_function(wrap_pyfunction!(arrow::to_arrow, m)?)?;
2326
m.add_function(wrap_pyfunction!(cli::main, m)?)?;
2427
m.add_function(wrap_pyfunction!(migrate::migrate, m)?)?;
2528
m.add_function(wrap_pyfunction!(migrate::migrate_href, m)?)?;

stacrs.pyi

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,36 @@ async def read(
212212
>>> item = await stacrs.read("item.json")
213213
"""
214214

215+
def from_arrow(
216+
table: arro3.core.Table,
217+
) -> dict[str, Any]:
218+
"""
219+
Converts an [arro3.core.table][] to a STAC item collection.
220+
221+
Requires **stacrs** to be installed with the `arrow` extra.
222+
223+
Args:
224+
table: The table
225+
226+
Returns:
227+
dict[str, Any]: The STAC item collection
228+
"""
229+
230+
def to_arrow(
231+
items: list[dict[str, Any]] | dict[str, Any],
232+
) -> arro3.core.Table:
233+
"""
234+
Converts items to an [arro3.core.table][].
235+
236+
Requires **stacrs** to be installed with the `arrow` extra.
237+
238+
Args:
239+
items: Either an iterable of items or a item collection
240+
241+
Returns:
242+
arro3.core.Table: The table
243+
"""
244+
215245
async def search(
216246
href: str,
217247
*,

0 commit comments

Comments
 (0)