Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Bring back GDAL and cli create-item #568

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ jobs:
working-directory: crates/cli
steps:
- uses: actions/checkout@v4
- name: Install GDAL
run: |
sudo apt-get update
sudo apt-get install -y gdal-bin libgdal-dev
- uses: dtolnay/rust-toolchain@1.81
- uses: Swatinem/rust-cache@v2
- name: Test
Expand All @@ -71,6 +75,10 @@ jobs:
working-directory: crates/cli
steps:
- uses: actions/checkout@v4
- name: Install GDAL
run: |
sudo apt-get update
sudo apt-get install -y gdal-bin libgdal-dev
- uses: dtolnay/rust-toolchain@1.81
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-python@v5
Expand Down Expand Up @@ -152,6 +160,19 @@ jobs:
run: uv sync --group stac-api-validator
- name: Validate
run: uv run scripts/validate-stac-server
test-gdal:
name: Test stac-gdal
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install GDAL
run: |
sudo apt-get update
sudo apt-get install -y gdal-bin libgdal-dev
- uses: dtolnay/rust-toolchain@1.81
- uses: Swatinem/rust-cache@v2
- name: Test
run: cargo test -p stac-gdal
lint:
name: Lint
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist/
pyrightconfig.json
site/
.cache
bacon.toml
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"crates/derive",
"crates/duckdb",
"crates/extensions",
"crates/gdal",
"crates/pgstac",
"crates/server",
]
Expand Down Expand Up @@ -46,6 +47,7 @@ cql2 = "0.3.0"
duckdb = "=1.0.0"
fluent-uri = "0.3.1"
futures = "0.3.31"
gdal = { version = "0.17.1", features = ["bindgen"] }
geo = "0.29.3"
geo-types = "0.7.13"
geoarrow = "0.3.0"
Expand Down Expand Up @@ -75,6 +77,8 @@ stac-api = { version = "0.6.2", path = "crates/api" }
stac-derive = { version = "0.1.0", path = "crates/derive" }
stac-duckdb = { version = "0.0.3", path = "crates/duckdb" }
stac-server = { version = "0.3.2", path = "crates/server" }
stac-extensions = { version = "0.1.0", path = "crates/extensions" }
stac-gdal = { version = "0.1.0", path = "crates/gdal" }
syn = "2.0"
tempfile = "3.13"
thiserror = "2.0"
Expand Down
9 changes: 8 additions & 1 deletion crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
[package]
categories = [
"science",
"data-structures",
"science:geo",
"command-line-utilities",
]
name = "stac-cli"
description = "Command line interface for stac-rs"
version = "0.4.1"
Expand All @@ -8,14 +14,14 @@ edition.workspace = true
homepage.workspace = true
repository.workspace = true
license.workspace = true
categories.workspace = true
rust-version.workspace = true

[features]
default = ["pgstac"]
duckdb = ["dep:stac-duckdb", "dep:duckdb"]
pgstac = ["stac-server/pgstac", "dep:tokio-postgres"]
python = ["dep:pyo3", "pgstac"]
gdal = ["dep:stac-gdal"]

[dependencies]
axum.workspace = true
Expand All @@ -38,6 +44,7 @@ stac = { workspace = true, features = [
] }
stac-api = { workspace = true, features = ["client"] }
stac-duckdb = { workspace = true, optional = true }
stac-gdal = { workspace = true, optional = true }
stac-server = { workspace = true, features = ["axum"] }
thiserror.workspace = true
tokio = { workspace = true, features = [
Expand Down
Binary file added crates/cli/assets/dataset.tif
Binary file not shown.
Binary file added crates/cli/assets/dataset_geo.tif
Binary file not shown.
6 changes: 5 additions & 1 deletion crates/cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// The verbosity stuff is cribbed from https://github.com/clap-rs/clap-verbosity-flag/blob/c621a6a8a7c0b6df8f1464a985a5d076b4915693/src/lib.rs and updated for tracing

use crate::{
subcommand::{search, serve, translate, validate},
subcommand::{item, search, serve, translate, validate},
Error, Result, Value,
};
use clap::Parser;
Expand Down Expand Up @@ -85,6 +85,9 @@ pub enum Subcommand {

/// Validate a STAC object using json-schema
Validate(validate::Args),

/// Create a STAC Item from a href.
Item(item::Args),
}

#[derive(Copy, Clone, Debug, Default)]
Expand All @@ -109,6 +112,7 @@ impl Args {
Subcommand::Serve(args) => self.serve(args).await,
Subcommand::Translate(args) => self.translate(args).await,
Subcommand::Validate(args) => self.validate(args).await,
Subcommand::Item(args) => self.create(args).await,
}
}

Expand Down
5 changes: 5 additions & 0 deletions crates/cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ pub enum Error {
#[error(transparent)]
Stac(#[from] stac::Error),

/// [stac_gdal::Error]
#[cfg(feature = "gdal")]
#[error(transparent)]
StacGdal(#[from] stac_gdal::Error),

/// [stac_duckdb::Error]
#[cfg(feature = "duckdb")]
#[error(transparent)]
Expand Down
48 changes: 48 additions & 0 deletions crates/cli/src/subcommand/item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use stac::{item::Builder as ItemBuilder, Asset, Value};
#[cfg(feature = "gdal")]
use stac_gdal::update_item;
use std::path::Path;

use crate::Result;

/// Arguments for the `create_item` subcommand.
#[derive(clap::Args, Debug, Clone)]
pub struct Args {
/// The input file.
// ///
// /// If not provided or `-`, the input will be read from standard input.
href: String,

/// Asset key
#[arg(default_value = "data")]
asset_key: String,

/// Semantic roles of the asset
#[arg(short, long)]
roles: Option<String>,
}

impl crate::Args {
pub async fn create(&self, args: &Args) -> Result<()> {
// TODO: Filename must be present or we need to react
let filename = Path::new(&args.href)
.file_name()
.and_then(|s| s.to_str())
.expect("Filename must be present");
let mut asset = Asset::new(&args.href);
if let Some(roles) = &args.roles {
asset = asset.role(roles);
}

#[allow(unused_mut)] // we need this for gdal's `update_item` below
let mut item = ItemBuilder::new(filename)
.asset(&args.asset_key, asset)
.build()?;

#[cfg(feature = "gdal")]
update_item(&mut item, false, true)?;

self.put(Value::from(item), None).await?;
Ok(())
}
}
1 change: 1 addition & 0 deletions crates/cli/src/subcommand/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod item;
#[cfg(feature = "pgstac")]
pub mod pgstac;
pub mod search;
Expand Down
10 changes: 10 additions & 0 deletions crates/cli/tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ fn validate_stdin() {
.unwrap();
command.arg("validate").write_stdin(item).assert().success();
}

#[test]
fn create_stac_item() {
let mut command = Command::cargo_bin("stacrs").unwrap();
command
.arg("item")
.arg("assets/dataset_geo.tif")
.assert()
.success();
}
5 changes: 5 additions & 0 deletions crates/core/src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ pub struct Asset {
/// Additional fields on the asset.
#[serde(flatten)]
pub additional_fields: Map<String, Value>,

/// Phantom field to make extensions work
#[serde(skip)]
pub extensions: Vec<String>,
}

/// Trait implemented by anything that has assets.
Expand Down Expand Up @@ -139,6 +143,7 @@ impl Asset {
statistics: None,
unit: None,
additional_fields: Map::new(),
extensions: Vec::new(),
}
}

Expand Down
12 changes: 12 additions & 0 deletions crates/core/src/bbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,18 @@ impl Default for Bbox {
}
}

impl From<[f64; 4]> for Bbox {
fn from(value: [f64; 4]) -> Self {
Bbox::TwoDimensional(value)
}
}

impl From<[f64; 6]> for Bbox {
fn from(value: [f64; 6]) -> Self {
Bbox::ThreeDimensional(value)
}
}

#[cfg(feature = "geo")]
impl From<geo::Rect> for Bbox {
fn from(rect: geo::Rect) -> Bbox {
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/statistics.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};

/// Statistics of all pixels in the band.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Statistics {
/// Mean value of all the pixels in the band
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down
3 changes: 2 additions & 1 deletion crates/extensions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub mod projection;
pub mod raster;

use serde::{de::DeserializeOwned, Serialize};
use stac::{Catalog, Collection, Error, Fields, Item, Result};
use stac::{Asset, Catalog, Collection, Error, Fields, Item, Result};
pub use {projection::Projection, raster::Raster};

/// A trait implemented by extensions.
Expand Down Expand Up @@ -215,6 +215,7 @@ macro_rules! impl_extensions {
};
}

impl_extensions!(Asset);
impl_extensions!(Item);
impl_extensions!(Catalog);
impl_extensions!(Collection);
Expand Down
21 changes: 21 additions & 0 deletions crates/gdal/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "stac-gdal"
description = "Instrument STAC objects with GDAL."
version = "0.1.0"
keywords = ["geospatial", "stac", "metadata", "geo", "raster"]
authors.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true
license.workspace = true
categories.workspace = true
rust-version.workspace = true

[dependencies]
gdal.workspace = true
geojson.workspace = true
log.workspace = true
serde_json.workspace = true
stac.workspace = true
stac-extensions.workspace = true
thiserror.workspace = true
Binary file added crates/gdal/assets/dataset.tif
Binary file not shown.
Binary file added crates/gdal/assets/dataset_geo.tif
Binary file not shown.
29 changes: 29 additions & 0 deletions crates/gdal/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use thiserror::Error;

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
/// [gdal::errors::GdalError]
#[error(transparent)]
GdalError(#[from] gdal::errors::GdalError),

/// [stac::Error]
#[error(transparent)]
STACError(#[from] stac::Error),

/// [std::num::ParseIntError]
#[error(transparent)]
ParseIntError(#[from] std::num::ParseIntError),

/// [serde_json::Error]
#[error(transparent)]
SerdeJson(#[from] serde_json::Error),

/// Failed to parse EPSG projection from proj extension.
#[error("Failed to parse EPSG projection from: `{0}`")]
ParseEPSGProjectionError(String),

/// Unsupported STAC extension
#[error("STAC extension `{0}` is not supported")]
UnsupportedExtension(String),
}
Loading
Loading