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

Add search #387

Merged
merged 1 commit into from
Sep 14, 2024
Merged
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
13 changes: 8 additions & 5 deletions .github/workflows/stacrs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
push:
tags:
- 'python-*'
pull_request:
paths:
- python/**
workflow_dispatch:

permissions:
Expand All @@ -25,11 +28,11 @@ jobs:
- name: Install dev requirements
run: pip install -r python/requirements-dev.txt
- name: Build
run: maturin build --manifest-path python/Cargo.toml --out dist
run: maturin build --manifest-path python/Cargo.toml --out dist -F geoparquet
- name: Install stacrs
run: pip install stacrs --find-links dist --no-index
- name: Check
run: ruff check python && ruff format --check python && mypy python
run: ruff check python && ruff format --check python && mypy python --config-file=python/pyproject.toml
- name: Test
run: pytest python/tests
linux:
Expand Down Expand Up @@ -60,7 +63,7 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml -F geoparquet
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -120,7 +123,7 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml -F geoparquet
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -148,7 +151,7 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml -F geoparquet
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v4
Expand Down
1 change: 1 addition & 0 deletions api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Added

- `Client` (from now-defunkt **stac-async**) ([#372](https://github.com/stac-utils/stac-rs/pull/372))
- `BlockingClient` ([#387](https://github.com/stac-utils/stac-rs/pull/387))

## [0.5.0] - 2024-09-05

Expand Down
68 changes: 67 additions & 1 deletion api/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! A STAC API client.

use crate::{Error, GetItems, Item, ItemCollection, Items, Result, Search, UrlBuilder};
use async_stream::try_stream;
use futures::{pin_mut, Stream, StreamExt};
Expand All @@ -6,7 +8,9 @@ use reqwest::{header::HeaderMap, IntoUrl, Method, StatusCode};
use serde::{de::DeserializeOwned, Serialize};
use serde_json::{Map, Value};
use stac::{Collection, Href, Link, Links};
use std::pin::Pin;
use tokio::{
runtime::{Builder, Runtime},
sync::mpsc::{self, error::SendError},
task::JoinHandle,
};
Expand All @@ -21,6 +25,17 @@ pub struct Client {
url_builder: UrlBuilder,
}

/// A client for interacting with STAC APIs without async.
#[derive(Debug)]
pub struct BlockingClient(Client);

/// A blocking iterator over items.
#[allow(missing_debug_implementations)]
pub struct BlockingIterator {
runtime: Runtime,
stream: Pin<Box<dyn Stream<Item = Result<Item>>>>,
}

impl Client {
/// Creates a new API client.
///
Expand Down Expand Up @@ -127,12 +142,12 @@ impl Client {
///
/// let client = Client::new("https://planetarycomputer.microsoft.com/api/stac/v1").unwrap();
/// let mut search = Search { collections: Some(vec!["sentinel-2-l2a".to_string()]), ..Default::default() };
/// search.items.limit = Some(1);
/// # tokio_test::block_on(async {
/// let items: Vec<_> = client
/// .search(search)
/// .await
/// .unwrap()
/// .take(1)
/// .map(|result| result.unwrap())
/// .collect()
/// .await;
Expand Down Expand Up @@ -226,6 +241,57 @@ impl Client {
}
}

impl BlockingClient {
/// Creates a new blocking client.
///
/// # Examples
///
/// ```
/// use stac_api::BlockingClient;
///
/// let client = BlockingClient::new("https://planetarycomputer.microsoft.com/api/stac/vi").unwrap();
/// ```
pub fn new(url: &str) -> Result<BlockingClient> {
Client::new(url).map(Self)
}

/// Searches an API, returning an iterable of items.
///
/// To prevent fetching _all_ the items (which might be a lot), it is recommended to pass a `max_items`.
///
/// # Examples
///
/// ```no_run
/// use stac_api::{Search, BlockingClient};
///
/// let client = BlockingClient::new("https://planetarycomputer.microsoft.com/api/stac/v1").unwrap();
/// let mut search = Search { collections: Some(vec!["sentinel-2-l2a".to_string()]), ..Default::default() };
/// let items: Vec<_> = client
/// .search(search)
/// .unwrap()
/// .map(|result| result.unwrap())
/// .take(1)
/// .collect();
/// assert_eq!(items.len(), 1);
/// ```
pub fn search(&self, search: Search) -> Result<BlockingIterator> {
let runtime = Builder::new_current_thread().enable_all().build()?;
let stream = runtime.block_on(async move { self.0.search(search).await })?;
Ok(BlockingIterator {
runtime,
stream: Box::pin(stream),
})
}
}

impl Iterator for BlockingIterator {
type Item = Result<Item>;

fn next(&mut self) -> Option<Self::Item> {
self.runtime.block_on(self.stream.next())
}
}

fn stream_items(
client: Client,
page: ItemCollection,
Expand Down
5 changes: 5 additions & 0 deletions api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ pub enum Error {
#[cfg(feature = "client")]
InvalidMethod(#[from] http::method::InvalidMethod),

/// [std::io::Error]
#[error(transparent)]
#[cfg(feature = "client")]
Io(#[from] std::io::Error),

/// [tokio::task::JoinError]
#[error(transparent)]
#[cfg(feature = "client")]
Expand Down
9 changes: 9 additions & 0 deletions api/src/filter.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::{convert::Infallible, str::FromStr};

use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

Expand All @@ -20,6 +22,13 @@ impl Default for Filter {
}
}

impl FromStr for Filter {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Filter::Cql2Text(s.to_string()))
}
}

#[cfg(test)]
mod tests {
use super::Filter;
Expand Down
4 changes: 2 additions & 2 deletions api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
)]

#[cfg(feature = "client")]
mod client;
pub mod client;
mod collections;
mod conformance;
mod error;
Expand All @@ -78,7 +78,7 @@ mod sort;
mod url_builder;

#[cfg(feature = "client")]
pub use client::Client;
pub use client::{BlockingClient, Client};
pub use {
collections::Collections,
conformance::{
Expand Down
1 change: 1 addition & 0 deletions python/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased]

- `migrate_href` ([#334](https://github.com/stac-utils/stac-rs/pull/334))
- `search` and `search_to` ([#387](https://github.com/stac-utils/stac-rs/pull/387))

## [0.0.3] - 2024-08-29

Expand Down
1 change: 1 addition & 0 deletions python/CODE_OF_CONDUCT
10 changes: 9 additions & 1 deletion python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ publish = false
name = "stacrs"
crate-type = ["cdylib"]

[features]
geoparquet = ["stac/geoparquet-compression"]

[dependencies]
geojson = "0.24"
pyo3 = "0.22"
pythonize = "0.22"
stac = { path = "../core", features = ["reqwest"] }
serde = "1"
serde_json = "1"
stac = { path = "../core", features = ["reqwest", "object-store-full"] }
stac-api = { path = "../api", features = ["client"] }
stac-validate = { path = "../validate" }
tokio = { version = "1", features = ["rt"] }
10 changes: 10 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ Then:
```python
import stacrs

# Searches a STAC API
items = stacrs.search(
"https://landsatlook.usgs.gov/stac-server",
collections="landsat-c2l2-sr",
intersects={"type": "Point", "coordinates": [-105.119, 40.173]},
sortby="-properties.datetime",
max_items=1,
)

# Validates a href using json-schema
stacrs.validate_href("https://raw.githubusercontent.com/radiantearth/stac-spec/v1.0.0/examples/simple-item.json")
```

Expand Down
1 change: 1 addition & 0 deletions python/docs/CODE_OF_CONDUCT
7 changes: 2 additions & 5 deletions python/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@

API documentation for **stacrs**.

## Migrate

::: stacrs.migrate
::: stacrs.migrate_href

## Validate

::: stacrs.search
::: stacrs.search_to
::: stacrs.validate
::: stacrs.validate_href
64 changes: 0 additions & 64 deletions python/docs/index.md

This file was deleted.

1 change: 1 addition & 0 deletions python/docs/index.md
4 changes: 4 additions & 0 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ Issues = "https://github.com/stac-utils/stac-rs/issues"

[tool.maturin]
features = ["pyo3/extension-module"]

[[tool.mypy.overrides]]
module = "pyarrow.*"
ignore_missing_imports = true
1 change: 1 addition & 0 deletions python/requirements-dev.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ maturin
mypy
ruff
pytest
stac-geoparquet
Loading