Skip to content

Conversation

@tonio
Copy link
Member

@tonio tonio commented Oct 28, 2025

This PR adds client implementation for STAC (SpatioTemporal Asset Catalog) API endpoints, following the STAC API v1.0.0 specification.

Core functionality:

  • StacEndpoint class for querying STAC API instances
  • Full support for collections and items endpoints
  • Conformance class detection (STAC Core, OGC API Features)
  • Hypermedia-driven pagination via next links

Query capabilities:

  • Filter items by bounding box, datetime, limit
  • Single item and collection retrieval
  • URL building for custom queries

Type safety:

  • Complete TypeScript models for Catalog, Collection, Item
  • STAC-specific link utilities
  • Defensive validation with proper error handling

Specification Conformance

✅ STAC API - Core: Landing page, conformance declaration, link relations
✅ STAC API - Features: /collections, /collections/{id}, /collections/{id}/items
❌ STAC API - Item Search: Cross-collection search (optional, not implemented)

Testing & Documentation

  • Comprehensive test suite (endpoint, link utilities, info parsing)
  • Runnable example demonstrating all major features
  • Follows established patterns from OGC API Features module

API Surface

  import { StacEndpoint } from 'ogc-client';

  const stac = new StacEndpoint('https://api.stac.example.com');
  const collections = await stac.allCollections;
  const items = await stac.getCollectionItems('collection-id', {
    bbox: [5, 45, 6, 46],
    datetime: { start: new Date('2023-01-01'), end: new Date('2023-12-31') },
    limit: 10
  });

Copy link
Member

@jahow jahow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome contribution, thanks a lot!!

It might take a while to handle various implementations, I've tried running the example script against these endpoints but got errors (probably linked to the return format, see my other comment):

https://catalog.maap.eo.esa.int/catalogue

https://stac.dataspace.copernicus.eu/v1/

I haven't looked deeper. The PR looks really great though, the API is very close to OGC API which makes a lot of sense, and it's covered by tests. Thanks!

export function fetchStacDocument<T extends StacDocument>(
url: string
): Promise<T> {
return ogcFetchDocument(url) as Promise<T>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So ogcFetchDocument adds a f=json parameter to all its calls, it's not mandatory but it generally works across OGC API implementations.

For STAC implementations though, it looks like this parameter gives an error. For instance: https://catalog.maap.eo.esa.int/catalogue/collections/AeolusL0ProductsB16/items?f=json

Instead, we can probably drop this parameter completely?

Also, if we want to get a document in "full" STAC format we need to add httpAccept=application/geo+json;profile=https://stacspec.org, see:
https://catalog.maap.eo.esa.int/catalogue/collections/AeolusL0ProductsB16/items?httpAccept=application/geo%2Bjson;profile=https://stacspec.org

Otherwise we only get a GeoJSON and the parsing fails down the line.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this is probably specific to that implementation and the trick here would be to rely more on links given by the various documents; in OGC API we can build URLs ourselves in many cases which makes things simpler)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR updated to make it work with the catalog you pointed.
Also add some helpers function needed for gn-ui project (Load Collection/Item directly from urls).

Copy link
Member

@jahow jahow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great work, thanks! I made a couple of additional comments, especially regarding the fromItemUrl, fromCollectionUrl and fromUrl methods. I'll approve this PR and let you judge on whether this can be changed further or is already useful as is :)


// Add query parameters only if not already present in the link
// This preserves server-specific parameters (like httpAccept) from the link
if (options.limit !== undefined && !url.searchParams.has('limit')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we making sure the parameters are not present before changing them? I feel like the consumer of the API should be free to set whatever they want here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover of bugfix attempt (-: I'll clean up.

Comment on lines 151 to 154
static async fromItemUrl(url: string): Promise<StacItem> {
const doc = await fetchStacDocument<StacDocument>(url);
return parseStacItem(doc);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this as well as fromCollectionUrl aren't doing much more than a fetch, right? I mean it's doing validation, which is useful, but maybe we could create a whole StacEndpoint class out of an Item URL?

On other protocols (e.g. WFS and WMS), when a given url points to a specific layer or feature type, the endpoint class is created but only contains that single layer/feature type.

That might be too much change though, I feel like this PR is very close to being final. Just fishing for ideas :) thanks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, it was missing filtering options 😅. Should be better. About instanciating an endpoint, well it rises quite a lot of questions about implementation & return types, I'd rather keep it simple for now.
I'll let the PR open until being sure it fits our needs, if that's OK.

- List all available collections
- Get detailed collection metadata (extent, providers, license, etc.)
- Query items with various filters:
- Limit and offset (pagination)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "offset" is never supported by the STAC standard, so this might be misleading. It's only next and previous links.

console.log(` b) Two-step query with getItemsFromCollection:`);
console.log(` First fetch collection, then query items`);

const collectionResult = await StacEndpoint.fromUrl(collectionUrl);
Copy link
Member

@LHBruneton-C2C LHBruneton-C2C Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked out and wanted to run the example, but I got this error:

file:///home/lbruneton/dev/camptocamp/ogc-client/examples/stac-query.js:393
      const collectionResult = await StacEndpoint.fromUrl(collectionUrl);
            ^

SyntaxError: Identifier 'collectionResult' has already been declared

Seem to be already used from line 324.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

friday fixes :-/ looking at it right now.

@LHBruneton-C2C
Copy link
Member

Amazing work! The STAC example page helps a lot, it's easy to read and covers all useful cases.

tonio added 6 commits November 3, 2025 17:43
 - Remove f=json parameter incompatible with STAC items endpoints
 - Preserve query parameters from collection links (httpAccept, etc.)
 - Use link 'type' field as Accept header for full STAC format
 - Fix single item URL construction
 - Add bbox validation to prevent coordinate validation errors

Follows HATEOAS principles by respecting links in STAC documents.
Compatible with MAAP, Copernicus, and other STAC implementations.
Add StacEndpoint.fromCollectionUrl(), fromItemUrl(), and fromUrl()
static methods to load STAC resources directly from URLs.
Make stac_version and assets optional in StacItem to support
non-compliant but real-world STAC APIs like MAAP.

These fields are often omitted when inherited from parent context.
 - Extract duplicate filtering logic into applyFilterOptions helper
 - Remove unnecessary param checks for standard query filters
 - Rename getItemsFromCollectionUrl → getItemsFromUrl (works with items URLs)
 - Return StacItemsDocument instead of StacItem[] for pagination access
 - Update getItemsFromCollection to return full response consistently
 - Use library methods for pagination in examples
Copy link
Member

@tkohr tkohr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @tonio! I've just tested the temporal filtering and a bit of pagination and it works really fine:-)

@tonio tonio merged commit 1d137ae into camptocamp:main Nov 4, 2025
1 check passed
@tonio tonio deleted the stac branch November 4, 2025 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants