OGC API Client
Status: implemented in src/core/ogc-tiles.ts, src/core/ogc-maps.ts,
src/core/ogc-processes.ts, src/core/ogc-records.ts,
src/core/stac.ts, and (for OGC API Features) src/core/surfaces.ts.
This document is the developer reference for the first-party OGC API
adapter. It complements the design notes in
shared-client-contract.md and the
capability matrix in protocol-capability-matrix.md.
Canonical OGC API Features queries can be explained before execution through
@honua/sdk-js/query-planner. The deterministic
ogc-api-features-query-v1 output exposes the collection, CQL2 text filter,
properties, sort, bbox, CRS, and paging request without fetching metadata or
rows. See Deterministic query planner.
Conformance areas covered
| Area | Surface | Source protocol | Notes |
|---|---|---|---|
| OGC API Features | client.ogcFeatures() |
ogc-features |
landing → collections → items, CQL2 filtering, transactions where the server advertises them |
| OGC API Tiles | client.ogcTiles() |
ogc-tiles |
tileset discovery, vector + raster tiles on the canonical /collections/{id}/tiles/{tms}/… route (styled tiles deferred — see below) |
| OGC API Maps | client.ogcMaps() |
ogc-maps |
dataset / collection map renders, styled access |
| OGC API Processes | client.ogcProcesses() |
(none — job runner) | discovery, async execution, IJobRun<T> surface |
| OGC API Records | client.ogcRecords() |
ogc-records |
metadata/catalog discovery, record search, record detail, raw response access |
| STAC API | client.stac() |
stac |
landing, collections, items, cross-collection search |
OGC conformance class identifiers (e.g.
http://www.opengis.net/spec/ogcapi-features-3/1.0/conf/filter) are
not primary SDK types. negotiateOgcCapabilities(protocol, conformance)
translates a /conformance response into a canonical Capabilities set;
hasOgcConformanceClass(conformance, substring) is the explicit
substring gate for callers that want to branch on a specific extension.
Getting started
import { HonuaClient } from "@honua/sdk-js/honua";
const baseUrl = "https://your-honua-server.example";
const client = new HonuaClient({ baseUrl });
OGC API Features (already shipped)
const ogc = client.ogcFeatures();
const items = await ogc.collection("parcels").items({ limit: 50, filter: "STATUS = 'ACTIVE'" });
OGC API Tiles
const tiles = client.ogcTiles();
// 1) Discover tilesets the server advertises for a collection
const tilesets = await tiles.tilesets({ collectionId: "parcels" });
// 2) Bind to one tileset and fetch a tile
const tileset = tiles.tileset("parcels", "WebMercatorQuad");
const tile = await tileset.tile({ tileMatrix: 5, tileRow: 9, tileCol: 12 });
console.log(tile.contentType); // "application/vnd.mapbox-vector-tile"
console.log(tile.bytes.byteLength);
// Raster tiles use the same route and rely on the `Accept` header to
// negotiate the output media type.
const png = await tileset.tile({ tileMatrix: 4, tileRow: 2, tileCol: 3, accept: "image/png" });
Styled-tile access (the OGC /styles/{styleId}/tiles/… route) is part of
the OGC API Tiles standard but is not currently exposed by the Honua
server. The SDK intentionally does not synthesize that path today; it
will be added when the server endpoint ships.
OGC API Maps
const maps = client.ogcMaps();
// Dataset-level render across multiple collections
const map = await maps.map({
width: 1024,
height: 1024,
bbox: [-122, 37, -120, 38],
collections: ["parcels", "roads"],
format: "png", // short-name token; `image/png` also accepted and normalized
});
// Collection-level styled render
const styled = await maps.collection("parcels", "topographic").map({
width: 512,
height: 512,
});
The format option is serialized to the server's f query parameter
as a short-name token (png, jpeg, jpg, tiff, tif); the SDK
normalizes image/… media-type aliases for ergonomics and sends a
media-type Accept header regardless. The request envelope intentionally
has no filter field — honua-server's Maps request model has no matching
property, so a CQL2 filter would be silently ignored. Callers who need a
custom query parameter can still pass one through extraParams.
OGC API Processes
import type { IJobRun } from "@honua/sdk-js/honua";
const processes = client.ogcProcesses();
const job: IJobRun<{ result: GeoJSON.Feature }> = await processes.execute({
processId: "buffer",
inputs: { feature: somePoint, distance: 500 },
mode: "async",
});
const unwatch = job.watch((snap) => {
console.log("status=", snap.status, "progress=", snap.progress?.percent);
});
try {
const { outputs } = await job.results();
console.log("result feature:", outputs.result);
} catch (error) {
// HonuaJobFailedError carries status, errorCode, details
console.error(error);
} finally {
unwatch();
}
IJobRun is the canonical async-operation surface. It is reused for
every long-running operation in the SDK; the OGC Processes runner is
just one implementation. cancel() is idempotent against the documented
404 / terminal-race paths but does not silently swallow other failures:
404("job already gone"): return the cached status.409 "Cannot dismiss completed job"(honua-server'sJobEndpointsterminal-state pre-check / post-apply / durable conflict — the job raced the caller into a terminal state): re-poll the job and return the authoritative terminal status if the poll confirms it. If the poll surfaces a non-terminal status, the original 409 is rethrown so the caller is not lied to.409 "Dismiss could not be confirmed"(backend dismissal request unconfirmed) and409 "Cancellation not supported"(backend lacks dismissal capability): rethrow theHonuaHttpErrorso the caller can branch or retry. Discrimination is by RFC 7807 problem-detailstitle(anddetailsubstring fallback).
Terminal-failure snapshots populate JobError from statusInfo.exception
when the server provides it, and fall back to statusInfo.message
otherwise (honua-server's StatusInfo DTO only emits message). The
resulting HonuaJobFailedError.message carries the server's reason text.
STAC API
const stac = client.stac();
// GET /search
const page = await stac.search({
bbox: [-122, 37, -120, 38],
collections: ["sentinel-2"],
filter: "cloud_cover < 10",
filterLang: "cql2-text",
limit: 50,
offset: 0,
});
// GET /search with intersects + fields selection
const filtered = await stac.search({
intersects: somePolygon,
fields: { include: ["id", "properties.datetime"], exclude: ["assets.thumbnail"] },
});
// POST /search (large filter bodies, big intersects geometries, etc.)
const post = await stac.search({
usePost: true,
intersects: somePolygon,
collections: ["sentinel-2"],
});
// Drain across pages — advances via the rel=next link's `?offset=N`
const everything = await stac.searchAll({ collections: ["sentinel-2"] });
StacSearchRequest.offset is honua-server's canonical paging token: its
/stac/search GET handler binds [FromQuery] int? offset and emits a
rel=next link whose href carries ?offset=N. searchAll /
searchStream follow that link verbatim. next is kept as optional
support for non-Honua STAC servers that advertise an opaque ?next=…
token instead. intersects and fields are serialized onto the GET
query (intersects as JSON; fields as id,properties.datetime,-assets.thumbnail)
in addition to the POST body.
OGC API Records
const records = client.ogcRecords();
// Discover available metadata catalogs.
const catalogs = await records.collections();
// Search one catalog. Records query parameters are catalog metadata
// filters, not STAC item-search filters.
const page = await records.collection("catalog").search({
q: ["roads", "parcels"],
type: ["service", "collection"],
externalIds: ["FeatureServer:transportation"],
bbox: [-158, 21, -157, 22],
datetime: "2025-01-01/2025-12-31",
limit: 25,
offset: 0,
});
const detail = await records.collection("catalog").record({ recordId: "service-transportation" });
// Raw response access keeps the shared auth/interceptor/retry pipeline but
// leaves body decoding to the caller.
const html = await records.collection("catalog").rawRecord({
recordId: "service-transportation",
responseFormat: "html",
accept: "text/html",
});
Records and STAC are intentionally separate surfaces. STAC describes spatiotemporal assets and collection/item search for imagery/data assets. Records describes metadata about resources: services, layers, collections, maps, scenes, styles, STAC collections, source descriptors, and other catalog entries. Honua admin metadata and migration inventory remain Honua-specific control-plane/read-model APIs; Records is the standards-facing public catalog view over stable metadata.
OgcRecordsSearchRequest supports the Records core search parameters
bbox, datetime, limit, q, ids, type, and externalIds, plus
optional filter / filter-lang / filter-crs when the server
advertises the Records filtering conformance class. searchAll and
searchStream follow rel=next links that carry ?offset=N.
Canonical Source registration
Source-shaped OGC adapters register through the shared client contract
from @honua/sdk-js/contract. Cross-protocol code consumes them through
the same Dataset / Source vocabulary as GeoServices / OData:
import { createDataset, PROTOCOL_DEFAULT_CAPABILITIES } from "@honua/sdk-js/contract";
const dataset = createDataset({
id: "parcels",
client,
sources: [
{
id: "parcels-ogc",
protocol: "ogc-features",
locator: { url: baseUrl, collectionId: "parcels" },
capabilities: PROTOCOL_DEFAULT_CAPABILITIES["ogc-features"],
},
{
id: "parcels-tiles",
protocol: "ogc-tiles",
locator: { url: baseUrl, collectionId: "parcels", tileMatrixSetId: "WebMercatorQuad" },
capabilities: PROTOCOL_DEFAULT_CAPABILITIES["ogc-tiles"],
},
{
id: "parcels-map",
protocol: "ogc-maps",
locator: { url: baseUrl, collectionId: "parcels", styleId: "topographic" },
capabilities: PROTOCOL_DEFAULT_CAPABILITIES["ogc-maps"],
},
{
id: "parcels-stac",
protocol: "stac",
locator: { url: baseUrl, collectionId: "parcels" },
capabilities: PROTOCOL_DEFAULT_CAPABILITIES.stac,
},
{
id: "catalog-records",
protocol: "ogc-records",
locator: { url: baseUrl, collectionId: "catalog" },
capabilities: PROTOCOL_DEFAULT_CAPABILITIES["ogc-records"],
},
],
});
const features = await dataset.source("parcels-ogc")!.query({ where: "STATUS = 'ACTIVE'" });
const stacItems = await dataset.source("parcels-stac")!.query({ where: "cloud_cover < 10" });
const catalogRecords = await dataset.source("catalog-records")!.query({ where: "type = 'service'" });
const tileset = dataset.source("parcels-tiles")!.adapter("ogc-tiles"); // HonuaOgcTileset
const map = dataset.source("parcels-map")!.adapter("ogc-maps"); // HonuaOgcCollectionMap
const recordsCatalog = dataset.source("catalog-records")!.adapter("ogc-records"); // HonuaOgcRecordCollection
OGC API Tiles and OGC API Maps are render-only; their Source.query*
methods throw HonuaCapabilityNotSupportedError. Renderers always
reach the underlying class through Source.adapter("ogc-tiles") /
Source.adapter("ogc-maps").
Capability negotiation and graceful degradation
import { HonuaClient } from "@honua/sdk-js/honua";
import { negotiateOgcCapabilities, hasOgcConformanceClass } from "@honua/sdk-js/honua";
const client = new HonuaClient({ baseUrl: "https://example.test" });
const conformance = await client.getOgcFeaturesConformance();
const caps = negotiateOgcCapabilities("ogc-features", conformance);
if (caps.has("queryAggregate")) {
// server advertises ogcapi-features-3/conf/filter; a strict Source can allow
// the adapter's documented client-side aggregation fallback.
}
if (hasOgcConformanceClass(conformance, "features-4/1.0/conf/create-replace-delete")) {
// server advertises Part 4 transactions
}
Under capabilityPolicy: "degraded", OGC API Features falls back to
client-side aggregation and metadata-bbox extent (see
protocol-capability-matrix.md for
the per-protocol coverage). All other adapters honour the strict
behaviour: a missing capability throws HonuaCapabilityNotSupportedError.
Conformance fixtures
The repo ships parametrized conformance suites under test/contract/:
ogc-tiles.test.ts— tileset discovery + tile fetch + Source adapterogc-maps.test.ts— dataset / collection / styled map rendersogc-processes.test.ts—IJobRunlifecycle, cancel, error pathsogc-records.test.ts— Records query params, paging, raw access, Source adapterstac.test.ts— GET / POST search + Source adapterogc-conformance.test.ts— conformance-class negotiation
These run against mock fetch responders and are the regression baseline for "passes against Honua Server's current OGC parity matrix" (Features CITE-certified, Tiles CITE-certified, Maps, Processes async).