Skip to content

Commit

Permalink
feat: add GDAL-based item creator
Browse files Browse the repository at this point in the history
Also adds env_logger to cli
  • Loading branch information
gadomski committed Apr 9, 2024
1 parent 260d3f4 commit 0409b88
Show file tree
Hide file tree
Showing 21 changed files with 971 additions and 58 deletions.
67 changes: 52 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,71 @@ on:

env:
CARGO_TERM_COLOR: always
CARGO_TERM_VERBOSE: true

jobs:
test:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Set up Rust cache
uses: Swatinem/rust-cache@v2
- name: Test
run: cargo test -F geo -F schemars -F reqwest --no-default-features
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: conda-incubator/setup-miniconda@v3
- name: Set up Rust cache
uses: Swatinem/rust-cache@v2
- name: Test
run: cargo test -F geo -F schemars -F reqwest --no-default-features
without-gdal:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
args:
- "-p stac"
- "-p stac-api"
- "-p stac-async"
- "-p stac-validate"
- "-p stac-cli --no-default-features"
- "-p stac -F reqwest"
- "-p stac -p stac-api -F geo"
- "-p stac -p stac-api -F schemars"
steps:
- uses: actions/checkout@v4
- name: Set up Rust cache
uses: Swatinem/rust-cache@v2
- name: Format
run: cargo fmt --verbose
- name: Build
run: cargo build --verbose --all-features
- name: Test
run: cargo test --verbose --all-features
test-stac:
run: cargo test ${{ matrix.args }}
with-gdal:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
flags: ["", "--features geo", "--features reqwest", "--features schemars"]
args:
- "-p stac -F gdal"
- "-p stac-cli"
- "--all-features"
steps:
- uses: actions/checkout@v4
- name: Set up Rust cache
uses: Swatinem/rust-cache@v2
- name: Build
run: cargo build --verbose -p stac ${{ matrix.flags }}
- name: Install GDAL
run: |
sudo apt-get update
sudo apt-get install libgdal-dev
- name: Test
run: cargo test --verbose -p stac ${{ matrix.flags }}
run: cargo test ${{ matrix.args }}
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Rust cache
uses: Swatinem/rust-cache@v2
- name: Format
run: cargo fmt --check
doc:
runs-on: ubuntu-latest
env:
Expand All @@ -48,5 +81,9 @@ jobs:
- uses: actions/checkout@v4
- name: Set up Rust cache
uses: Swatinem/rust-cache@v2
- name: Install GDAL
run: |
sudo apt-get update
sudo apt-get install libgdal-dev
- name: Doc
run: cargo doc --all-features
8 changes: 1 addition & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
[workspace]
resolver = "2"
members = [
"stac",
"stac-api",
"stac-async",
"stac-cli",
"stac-validate",
]
members = ["stac", "stac-api", "stac-async", "stac-cli", "stac-validate"]
5 changes: 5 additions & 0 deletions stac-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ license = "MIT OR Apache-2.0"
keywords = ["geospatial", "stac", "metadata", "geo", "raster"]
categories = ["science", "data-structures"]

[features]
default = ["gdal"]
gdal = ["stac/gdal"]

[dependencies]
clap = { version = "4", features = ["derive"] }
console = "0.15"
env_logger = "0.11"
indicatif = "0.17"
reqwest = "0.12"
serde = "1"
Expand Down
24 changes: 24 additions & 0 deletions stac-cli/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ use stac_api::GetSearch;

#[derive(Debug, Subcommand)]
pub enum Command {
/// Creates a STAC item.
#[cfg(feature = "gdal")]
Create {
/// The href of a raster data file.
///
/// TODO support vectors.
href: String,

/// If true, don't pretty-print the output
#[arg(short, long)]
compact: bool,
},

/// Searches a STAC API.
Search {
/// The href of the STAC API.
Expand Down Expand Up @@ -100,6 +113,17 @@ impl Command {
pub async fn execute(self) -> Result<()> {
use Command::*;
match self {
#[cfg(feature = "gdal")]
Create { href, compact } => {
let builder = stac::gdal::ItemBuilder::new(&href)?;
let item = builder.into_item()?;
if compact {
println!("{}", serde_json::to_string(&item)?);
} else {
println!("{}", serde_json::to_string_pretty(&item)?);
}
Ok(())
}
Search {
href,
max_items,
Expand Down
3 changes: 3 additions & 0 deletions stac-cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub enum Error {
#[error(transparent)]
SerdeJson(#[from] serde_json::Error),

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

#[error(transparent)]
StacApi(#[from] stac_api::Error),

Expand Down
1 change: 1 addition & 0 deletions stac-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use stac_cli::Args;

#[tokio::main]
async fn main() {
env_logger::init();
let args = Args::parse();
match args.command.execute().await {
Ok(()) => return,
Expand Down
3 changes: 3 additions & 0 deletions stac/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ keywords = ["geospatial", "stac", "metadata", "geo", "raster"]
categories = ["science", "data-structures"]

[features]
gdal = ["dep:gdal"]
geo = ["dep:geo", "dep:geojson"]
reqwest = ["dep:reqwest"]
schemars = ["dep:schemars"]

[dependencies]
chrono = "0.4"
gdal = { version = "0.16", optional = true, features = ["bindgen"] }
geo = { version = "0.28", optional = true }
geojson = { version = "0.24", optional = true }
log = "0.4"
reqwest = { version = "0.12", optional = true, features = ["json", "blocking"] }
schemars = { version = "0.8", optional = true }
serde = { version = "1", features = ["derive"] }
Expand Down
Binary file added stac/assets/dataset.tif
Binary file not shown.
Binary file added stac/assets/has_srs.tif
Binary file not shown.
10 changes: 10 additions & 0 deletions stac/src/asset.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::Fields;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::collections::HashMap;
Expand Down Expand Up @@ -111,6 +112,15 @@ impl Asset {
}
}

impl Fields for Asset {
fn fields(&self) -> &Map<String, Value> {
&self.additional_fields
}
fn fields_mut(&mut self) -> &mut Map<String, Value> {
&mut self.additional_fields
}
}

#[cfg(test)]
mod tests {
use super::Asset;
Expand Down
27 changes: 20 additions & 7 deletions stac/src/catalog.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Error, Extensions, Href, Link, Links, Result, STAC_VERSION};
use crate::{Error, Extensions, Fields, Href, Link, Links, Result, STAC_VERSION};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

Expand Down Expand Up @@ -33,8 +33,9 @@ pub struct Catalog {

/// A list of extension identifiers the `Catalog` implements.
#[serde(rename = "stac_extensions")]
#[serde(skip_serializing_if = "Option::is_none")]
pub extensions: Option<Vec<String>>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub extensions: Vec<String>,

/// Identifier for the `Catalog`.
pub id: String,
Expand Down Expand Up @@ -74,7 +75,7 @@ impl Catalog {
Catalog {
r#type: CATALOG_TYPE.to_string(),
version: STAC_VERSION.to_string(),
extensions: None,
extensions: Vec::new(),
id: id.to_string(),
title: None,
description: description.to_string(),
Expand Down Expand Up @@ -104,6 +105,15 @@ impl Links for Catalog {
}
}

impl Fields for Catalog {
fn fields(&self) -> &Map<String, Value> {
&self.additional_fields
}
fn fields_mut(&mut self) -> &mut Map<String, Value> {
&mut self.additional_fields
}
}

impl TryFrom<Catalog> for Map<String, Value> {
type Error = Error;
fn try_from(catalog: Catalog) -> Result<Self> {
Expand All @@ -123,8 +133,11 @@ impl TryFrom<Map<String, Value>> for Catalog {
}

impl Extensions for Catalog {
fn extensions(&self) -> Option<&[String]> {
self.extensions.as_deref()
fn extensions(&self) -> &Vec<String> {
&self.extensions
}
fn extensions_mut(&mut self) -> &mut Vec<String> {
&mut self.extensions
}
}

Expand Down Expand Up @@ -154,7 +167,7 @@ mod tests {
assert_eq!(catalog.description, "a description");
assert_eq!(catalog.r#type, "Catalog");
assert_eq!(catalog.version, STAC_VERSION);
assert!(catalog.extensions.is_none());
assert!(catalog.extensions.is_empty());
assert_eq!(catalog.id, "an-id");
assert!(catalog.links.is_empty());
}
Expand Down
27 changes: 20 additions & 7 deletions stac/src/collection.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Asset, Assets, Error, Extensions, Href, Link, Links, Result, STAC_VERSION};
use crate::{Asset, Assets, Error, Extensions, Fields, Href, Link, Links, Result, STAC_VERSION};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::collections::HashMap;
Expand Down Expand Up @@ -36,8 +36,9 @@ pub struct Collection {

/// A list of extension identifiers the `Collection` implements.
#[serde(rename = "stac_extensions")]
#[serde(skip_serializing_if = "Option::is_none")]
pub extensions: Option<Vec<String>>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub extensions: Vec<String>,

/// Identifier for the `Collection` that is unique across the provider.
pub id: String,
Expand Down Expand Up @@ -171,7 +172,7 @@ impl Collection {
Collection {
r#type: COLLECTION_TYPE.to_string(),
version: STAC_VERSION.to_string(),
extensions: None,
extensions: Vec::new(),
id: id.to_string(),
title: None,
description: description.to_string(),
Expand Down Expand Up @@ -207,6 +208,15 @@ impl Links for Collection {
}
}

impl Fields for Collection {
fn fields(&self) -> &Map<String, Value> {
&self.additional_fields
}
fn fields_mut(&mut self) -> &mut Map<String, Value> {
&mut self.additional_fields
}
}

impl Provider {
/// Creates a new provider with the given name.
///
Expand Down Expand Up @@ -254,8 +264,11 @@ impl Assets for Collection {
}

impl Extensions for Collection {
fn extensions(&self) -> Option<&[String]> {
self.extensions.as_deref()
fn extensions(&self) -> &Vec<String> {
&self.extensions
}
fn extensions_mut(&mut self) -> &mut Vec<String> {
&mut self.extensions
}
}

Expand Down Expand Up @@ -311,7 +324,7 @@ mod tests {
assert!(collection.assets.is_empty());
assert_eq!(collection.r#type, "Collection");
assert_eq!(collection.version, STAC_VERSION);
assert!(collection.extensions.is_none());
assert!(collection.extensions.is_empty());
assert_eq!(collection.id, "an-id");
assert!(collection.links.is_empty());
}
Expand Down
17 changes: 17 additions & 0 deletions stac/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ pub enum Error {
#[error(transparent)]
ChronoParse(#[from] chrono::ParseError),

/// [gdal::errors::GdalError]
#[cfg(feature = "gdal")]
#[error(transparent)]
Gdal(#[from] gdal::errors::GdalError),

/// [geojson::Error]
#[cfg(feature = "geo")]
#[error(transparent)]
Expand Down Expand Up @@ -60,6 +65,10 @@ pub enum Error {
#[error("value is not a collection")]
NotACollection(Value),

/// The value did not serialize to an object.
#[error("value did not serialize to an object")]
NotAnObject,

/// Returned when trying to read from a url but the `reqwest` feature is not enabled.
#[error("reqwest is not enabled")]
ReqwestNotEnabled,
Expand All @@ -73,11 +82,19 @@ pub enum Error {
#[error(transparent)]
SerdeJson(#[from] serde_json::Error),

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

/// Returned when the `type` field of a STAC object does not equal `"Feature"`, `"Catalog"`, or `"Collection"`.
#[error("unknown \"type\": {0}")]
UnknownType(String),

/// [url::ParseError]
#[error(transparent)]
Url(#[from] url::ParseError),

/// Unsupported extension.
#[error("unsupported extension: {0}")]
UnsupportedExtension(String),
}
Loading

0 comments on commit 0409b88

Please sign in to comment.