Add CoverageJSON (CovJSON) as a new output format to TiTiler via the titiler-covjson extension package. This enables any TiTiler deployment to serve geospatial raster and time series data in the OGC CoverageJSON format, supporting interoperability with CovJSON-aware visualization tools (e.g., Leaflet-CovJSON) and OGC API - Coverages consumers.
- All endpoints return valid CoverageJSON conforming to the OGC specification
- Content type
application/prs.coverage+jsonis properly served - Point, bbox, transect, tile, time series, and metadata endpoints are operational
- Integration with existing TiTiler readers (COG, STAC, MosaicJSON) works
- Unit tests cover all domain types (Grid, Point, PointSeries, Polygon, Trajectory)
- Performance is acceptable for typical raster sizes (< 2s for a 256x256 tile)
Priority: P0 (Foundation)
Description: Adopt covjson-pydantic (v0.7.0, KNMI) as the CovJSON model layer. Create helper utilities on top.
NOTE: See 06-existing-libraries-analysis.md for full analysis of this library.
Tasks:
- Add
covjson-pydantic>=0.7.0as a project dependency - Create
titiler_covjson/helpers.pywith convenience factories:create_spatial_2d_ref(crs_uri)->ReferenceSystemConnectionObjectcreate_temporal_ref()->ReferenceSystemConnectionObjectcrs_to_ogc_uri(rasterio.CRS)-> OGC CRS URI stringcreate_unit(unit_str)->Unit(UCUM mapping)numpy_dtype_to_ndarray(dtype, ...)->NdArrayFloat/NdArrayInt/NdArrayStr
- Evaluate gap: Polygon domain type not supported in covjson-pydantic (see gap analysis in doc 06)
- Write unit tests validating helper output and serialization against CovJSON spec examples
- Vendor
covjson-validatorJSON schemas for use in integration tests (Story 13)
Deliverables: titiler_covjson/helpers.py, tests/test_helpers.py, updated pyproject.toml
Estimated effort: XS (0.5-1 day)
Priority: P0 (Foundation)
Description: Create the CoverageInput dataclass that decouples TiTiler/rio-tiler data from the CovJSON conversion.
Tasks:
- Create
titiler_covjson/input.pywith CoverageInput and BandInfo dataclasses - Implement
imagedata_to_coverage_input()converter from rio-tiler ImageData - Handle nodata/mask propagation from ImageData to masked arrays
- Handle CRS extraction and band metadata extraction from reader info
- Write unit tests
Deliverables: titiler_covjson/input.py, tests/test_input.py
Estimated effort: S (1-2 days)
Priority: P0 (Foundation)
Description: Implement the modeler that converts CoverageInput to CovJSON Coverage objects.
Tasks:
- Create
titiler_covjson/modeler.pywith RasterCovJSONModeler class - Implement domain type detection (
_get_domain_type) - Implement axis creation for all domain types (
_create_axes)- Grid (start/stop/num)
- Point/PointSeries (x, y, optional z, optional t)
- Polygon/PolygonSeries (composite polygon + optional t)
- MultiPoint (composite tuple)
- Trajectory (composite tuple)
- Implement reference system creation with CRS URI conversion
- Implement parameter creation from band metadata
- Implement range creation (NdArray with null for nodata)
- Implement CoverageCollection creation for multi-result responses
- Write unit tests for each domain type conversion path
Deliverables: titiler_covjson/modeler.py, tests/test_modeler.py
Estimated effort: M (3-5 days)
Priority: P1
Description: Implement the /covjson/point endpoint for pixel-level value extraction.
Tasks:
- Create FastAPI route
GET /covjson/point - Accept parameters: url, lon, lat, bands, band_names, nodata
- Use rio-tiler Reader.point() for value extraction
- Convert to CoverageInput and then to Coverage (DomainType: Point)
- Return with
application/prs.coverage+jsoncontent type - Write integration tests
Deliverables: Route in titiler_covjson/routes.py
Estimated effort: S (1-2 days)
Priority: P1
Description: Implement the /covjson/bbox endpoint for area-based coverage retrieval.
Tasks:
- Create FastAPI route
GET /covjson/bbox - Accept parameters: url, bbox, bands, width, height, max_size, format, aggregation, nodata
- For
format=full: use Reader.part() for raster extract -> Grid domain - For
format=aggregated: compute stats over bbox -> Polygon domain - Enforce max_size limits
- Write integration tests
Deliverables: Route in titiler_covjson/routes.py
Estimated effort: M (2-3 days)
Priority: P1
Description: Implement the /covjson/transect endpoint for line-based sampling.
Tasks:
- Create FastAPI route
GET /covjson/transect - Accept parameters: url, coordinates (pipe-separated), bands, resolution, buffer, nodata
- Parse coordinate string into Shapely LineString
- Sample values along line at resolution intervals using Reader.point()
- If buffer > 0, buffer line and aggregate values within buffer zone
- Convert to CoverageInput (Trajectory domain) -> Coverage
- Write integration tests
Deliverables: Route in titiler_covjson/routes.py
Estimated effort: M (2-3 days)
Priority: P1
Description: Implement the /covjson/tiles/{z}/{x}/{y} endpoint serving tiles as CovJSON.
Tasks:
- Create FastAPI route
GET /covjson/tiles/{z}/{x}/{y} - Accept parameters: url, bands, tile_size, nodata
- Use Reader.tile(x, y, z) for tile data access
- Convert ImageData to Coverage (DomainType: Grid, 256x256)
- Set appropriate cache headers
- Write integration tests
Deliverables: Route in titiler_covjson/routes.py
Estimated effort: S (1-2 days)
Priority: P2
Description: Implement the /covjson/info endpoint returning metadata without data values.
Tasks:
- Create FastAPI route
GET /covjson/info - Accept parameters: url, collection
- Use Reader.info() for metadata extraction
- Build Coverage with domain and parameters but empty ranges
- Include full extent bounds and band metadata
- Write integration tests
Deliverables: Route in titiler_covjson/routes.py
Estimated effort: S (1 day)
Priority: P2
Description: Implement the /covjson/timeseries endpoint for temporal queries across STAC collection items.
Tasks:
- Create FastAPI route
GET /covjson/timeseries - Accept parameters: collection, lon, lat (or bbox), bands, datetime, aggregation, limit
- Query STAC catalog to resolve items within datetime range
- For each item, extract point or aggregated values using Reader
- Build temporal axis from item datetimes
- Return Coverage with PointSeries or PolygonSeries domain
- Handle pagination/limit for large collections
- Write integration tests
Deliverables: Route in titiler_covjson/routes.py
Estimated effort: L (5-8 days)
Priority: P2
Description: Implement the /covjson/overview endpoint for low-resolution overview data.
Tasks:
- Create FastAPI route
GET /covjson/overview - Accept parameters: url, bbox, bands, width, height
- Use COG overview levels for fast access at reduced resolution
- Return Coverage with Grid domain at overview resolution
- Write integration tests
Deliverables: Route in titiler_covjson/routes.py
Estimated effort: S (1-2 days)
Priority: P1
Description: Package all CovJSON endpoints as a TiTiler extension router.
Tasks:
- Create
titiler_covjson/router.pyas a TiTilerFactoryExtensionor standaloneAPIRouter - Register custom response class for
application/prs.coverage+json - Add OpenAPI documentation (tags, descriptions, examples)
- Wire into TiTiler application startup
- Add configuration options (max_size, default_tile_size, etc.)
- Write integration test with TiTiler test app
Deliverables: titiler_covjson/router.py, updated TiTiler app configuration
Estimated effort: M (2-3 days)
Priority: P3
Description: Support TiledNdArray range type for large coverages where sending the full values array is impractical.
Tasks:
- Implement TileSet URL template generation
- When bbox coverage exceeds a threshold (e.g., > 1024x1024), use TiledNdArray instead of NdArray
- URL template points to CovJSON tile endpoint:
/covjson/tiles/{z}/{x}/{y}?url=... - Client can progressively fetch tiles
- Write tests
Deliverables: Updated modeler, tiled range support
Estimated effort: M (3-4 days)
Priority: P2
Description: Create user-facing documentation and add CovJSON schema validation using covjson-validator schemas.
Tasks:
- Write user documentation (endpoint usage, examples, supported formats)
- Vendor
covjson-validatorJSON Schemas into test fixtures for offline validation - Create integration tests that validate API responses against the official CovJSON JSON Schema (
covjson.org/schema/dev/coveragejson.json) - Port useful runtime checks from
covjson-validator(axis/shape consistency, monotonicity) into test helpers - Create example Jupyter notebook demonstrating CovJSON API usage
- Validate output against CovJSON playground
- Document content negotiation and MIME types
Deliverables: docs, notebook, validation test suite
Estimated effort: M (2-3 days)
Story 1 (covjson-pydantic + helpers) ──┐
├──> Story 3 (Modeler) ──┐
Story 2 (CoverageInput) ──┘ │
├──> Story 4 (Point)
├──> Story 5 (Bbox)
├──> Story 6 (Transect)
├──> Story 7 (Tile)
├──> Story 8 (Info)
├──> Story 9 (TimeSeries)
├──> Story 10 (Overview)
│
v
Story 11 (Router Integration)
│
v
Story 12 (TiledNdArray)
Story 13 (Docs + covjson-validator)
| Sprint | Stories | Focus |
|---|---|---|
| Sprint 1 | 1, 2, 3 | Foundation: models, input, modeler |
| Sprint 2 | 4, 5, 7, 11 | Core endpoints: point, bbox, tile + router |
| Sprint 3 | 6, 8, 10 | Extended endpoints: transect, info, overview |
| Sprint 4 | 9, 12, 13 | Advanced: time series, tiled ranges, docs |
| Risk | Impact | Mitigation |
|---|---|---|
| Large CovJSON payloads for high-res rasters | Performance, memory | Use max_size limits; implement TiledNdArray (Story 12); gzip compression |
| CovJSON spec compliance edge cases | Interop with CovJSON clients | Use covjson-pydantic built-in validators + covjson-validator schemas in tests |
covjson-pydantic missing Polygon domain type |
Cannot represent aggregated bbox results as Polygon | Use PolygonSeries without t axis, or contribute upstream (issue #25) |
covjson-pydantic pre-1.0 API stability |
Breaking changes on minor version bump | Pin to >=0.7.0,<1.0; monitor releases |
| Time series across many STAC items | Slow response times | Limit parameter; async fetching; COG overview usage |
| CRS handling complexity | Incorrect coordinates | Default to WGS84; validate with rasterio; test with projected CRS |
| numpy dtype serialization (NaN, inf) | Invalid JSON | Replace NaN/inf with null; use masked arrays |
- CoverageJSON Specification (OGC)
- covjson-pydantic (KNMI) - Pydantic v2 models for CovJSON (adopted as dependency)
- covjson-validator - JSON Schema + runtime validator (used for test validation)
- CovJSON Playground
- TiTiler Documentation
- rio-tiler Documentation
- OGC API - Coverages
- Existing libraries analysis - Detailed assessment of covjson-pydantic and covjson-validator