Chapter 12: Geospatial APIs, Standards, and Interoperability¶
Interoperability is one of the oldest and hardest geospatial software problems. Spatial data is produced by many agencies, vendors, sensors, communities, and systems. Standards help independent tools exchange data without losing meaning.
Learning Goals¶
- Explain why geospatial standards matter.
- Compare classic OGC services with modern OGC API standards.
- Design APIs for spatial filters, tiles, pagination, CRS policy, and metadata.
- Understand licensing, authentication, and compatibility testing.
Theory¶
Interoperability requires more than a shared file extension. Systems must agree about geometry, coordinates, attributes, units, time, metadata, semantics, and error behavior. A useful API tells clients not only what data exists, but how it can be queried, transformed, trusted, and cited.
Classic standards such as WMS, WFS, WCS, and WMTS helped establish service interoperability. Modern OGC API standards use web-native patterns such as HTTP, JSON, OpenAPI, links, and collections.
Research and Standards Foundations¶
The OGC describes its modern API standards as taking advantage of JSON, REST, and OpenAPI. OGC API - Features defines building blocks for discovering and querying feature collections on the web. Part 1 focuses on core feature access in WGS 84 longitude/latitude order; Part 2 addresses coordinate reference systems by reference; additional parts cover filtering and richer query behavior.
This matters because API design is a contract. A geospatial API should state CRS behavior, supported filters, paging strategy, geometry simplification behavior, datetime semantics, authentication requirements, response format, and links to metadata. Without those details, clients can appear to work while quietly making incorrect spatial assumptions.
Coding Examples¶
The old OGC Web Services pattern often used query-string operations such as GetMap and GetFeature. The modern OGC API pattern uses linked web resources such as /collections, /collections/{collectionId}/items, /tiles, and /processes. Both approaches are still common in production.
Classic WMS GetMap Request¶
WMS returns rendered map images. The client asks for a layer, style, bounding box, CRS, output size, and image format.
https://example.org/geoserver/wms?
SERVICE=WMS&
VERSION=1.3.0&
REQUEST=GetMap&
LAYERS=public:parcels&
STYLES=&
CRS=EPSG:4326&
BBOX=39.0,-105.5,40.0,-104.5&
WIDTH=800&
HEIGHT=600&
FORMAT=image/png&
TRANSPARENT=true
The response is a PNG, JPEG, or other map image. It is useful for display, but not for editing feature attributes. Watch axis order carefully: WMS 1.3.0 uses the axis order defined by the CRS, so EPSG:4326 can mean latitude/longitude in some WMS contexts.
Classic WFS GetFeature Request¶
WFS returns feature data rather than a rendered image. A simple request can ask for GeoJSON features inside a bounding box:
https://example.org/geoserver/wfs?
SERVICE=WFS&
VERSION=2.0.0&
REQUEST=GetFeature&
TYPENAMES=public:hydrants&
OUTPUTFORMAT=application/json&
SRSNAME=EPSG:4326&
BBOX=-105.5,39.0,-104.5,40.0,EPSG:4326
The response should be a feature collection. In modern systems, the equivalent design is usually easier to expose through OGC API - Features.
OGC API - Features: Discover Collections¶
OGC API - Features starts with discoverable resources. A client should not hard-code every URL if the API publishes links.
curl -s "https://demo.pygeoapi.io/master/collections?f=json"
A typical response advertises collections and links:
{
"links": [
{
"href": "https://example.org/api",
"rel": "self",
"type": "application/json",
"title": "This document"
}
],
"collections": [
{
"id": "parcels",
"title": "Tax parcels",
"extent": {
"spatial": {
"bbox": [[-105.5, 39.0, -104.5, 40.0]]
}
},
"itemType": "feature"
}
]
}
OGC API - Features: Query Items with bbox, datetime, and limit¶
curl -s "https://example.org/api/collections/parcels/items?bbox=-105.5,39.0,-104.5,40.0&datetime=2025-01-01T00:00:00Z/2025-12-31T23:59:59Z&limit=100&f=json"
Client-side JavaScript:
const params = new URLSearchParams({
bbox: "-105.5,39.0,-104.5,40.0",
datetime: "2025-01-01T00:00:00Z/2025-12-31T23:59:59Z",
limit: "100",
f: "json"
});
const url = `https://example.org/api/collections/parcels/items?${params}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`OGC API request failed: ${response.status}`);
}
const featureCollection = await response.json();
console.log(featureCollection.features.length);
Python client:
import requests
params = {
"bbox": "-105.5,39.0,-104.5,40.0",
"datetime": "2025-01-01T00:00:00Z/2025-12-31T23:59:59Z",
"limit": 100,
"f": "json",
}
response = requests.get(
"https://example.org/api/collections/parcels/items",
params=params,
timeout=30,
)
response.raise_for_status()
features = response.json()["features"]
print(f"received {len(features)} features")
Minimal OGC API - Features Endpoint Sketch¶
This is not a full compliant server, but it shows the shape of an endpoint that respects common OGC API - Features ideas: collection IDs, bounding boxes, limits, GeoJSON output, and pagination links.
from fastapi import FastAPI, HTTPException, Query, Request
from pydantic import BaseModel
app = FastAPI(title="Example OGC API - Features service")
class BBox(BaseModel):
west: float
south: float
east: float
north: float
def parse_bbox(value: str) -> BBox:
parts = [float(part) for part in value.split(",")]
if len(parts) != 4:
raise ValueError("bbox must contain west,south,east,north")
west, south, east, north = parts
if west >= east or south >= north:
raise ValueError("bbox min values must be less than max values")
return BBox(west=west, south=south, east=east, north=north)
@app.get("/collections/{collection_id}/items")
def get_collection_items(
request: Request,
collection_id: str,
bbox: str | None = None,
datetime: str | None = None,
limit: int = Query(default=100, ge=1, le=1000),
offset: int = Query(default=0, ge=0),
):
if collection_id != "parcels":
raise HTTPException(status_code=404, detail="Unknown collection")
parsed_bbox = parse_bbox(bbox) if bbox else None
# In production, push bbox, datetime, limit, and offset into SQL.
# Example PostGIS predicate:
# ST_Intersects(geom, ST_MakeEnvelope(west, south, east, north, 4326))
rows = query_parcels(parsed_bbox, datetime, limit, offset)
next_offset = offset + limit
next_url = str(
request.url.include_query_params(
bbox=bbox,
datetime=datetime,
limit=limit,
offset=next_offset,
)
)
return {
"type": "FeatureCollection",
"features": [row_to_geojson_feature(row) for row in rows],
"links": [
{
"href": str(request.url),
"rel": "self",
"type": "application/geo+json",
"title": "This page",
},
{
"href": next_url,
"rel": "next",
"type": "application/geo+json",
"title": "Next page",
},
],
"numberReturned": len(rows),
}
Important server-side checks:
- Validate
bboxorder and coordinate ranges. - State whether coordinates are WGS 84 longitude/latitude or a different CRS.
- Cap
limitto prevent accidental full-table downloads. - Return
self,next,collection,service-desc, and metadata links where appropriate. - Push spatial filtering into the database or spatial index; do not load all features and filter in application memory.
OpenAPI Sketch for a Feature Collection¶
paths:
/collections/{collectionId}/items:
get:
summary: Query features in a collection
parameters:
- name: collectionId
in: path
required: true
schema:
type: string
- name: bbox
in: query
required: false
schema:
type: string
example: "-105.5,39.0,-104.5,40.0"
- name: datetime
in: query
required: false
schema:
type: string
example: "2025-01-01T00:00:00Z/2025-12-31T23:59:59Z"
- name: limit
in: query
required: false
schema:
type: integer
minimum: 1
maximum: 1000
default: 100
responses:
"200":
description: GeoJSON feature collection
content:
application/geo+json:
schema:
type: object
OGC API - Tiles URL Pattern¶
OGC API - Tiles uses tile matrix sets and tile coordinates. A common URL template looks like this:
/collections/{collectionId}/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}
Example vector tile request:
curl -o roads.mvt \
"https://example.org/api/collections/roads/tiles/WebMercatorQuad/12/1542/965?f=mvt"
The server must document the tile matrix set, tile coordinate convention, tile format, bounds, and available zoom levels. A tile API without tile matrix metadata is only a URL pattern, not an interoperable contract.
OGC API - Processes Job Pattern¶
OGC API - Processes exposes geospatial operations as web resources. A client submits inputs, receives a job, polls status, and retrieves outputs.
curl -s -X POST "https://example.org/api/processes/buffer/execution" \
-H "Content-Type: application/json" \
-d '{
"inputs": {
"geometry": {
"type": "Point",
"coordinates": [-105.0, 39.7]
},
"distanceMeters": 500
}
}'
Example response:
{
"jobID": "buffer-20260512-001",
"status": "accepted",
"links": [
{
"href": "https://example.org/api/jobs/buffer-20260512-001",
"rel": "monitor",
"type": "application/json"
}
]
}
The engineering contract should say which CRS the input geometry uses, what units distanceMeters uses, whether the operation is planar or geodesic, and how long job results are retained.
Math¶
APIs expose spatial math through bounding boxes, geometry predicates, distance filters, tile coordinates, coordinate transformations, scale denominators, and query tolerances. API designers must decide which calculations happen server-side and which assumptions are visible to clients.
Equation companion: Math and Algorithms Reference
Tools of the Trade¶
- OGC API - Features, Tiles, Maps, Processes, Records.
- WMS, WMTS, WFS, WCS.
- GeoJSON, GeoPackage, COG, STAC, GeoParquet.
- OpenAPI, JSON Schema, JSON-LD.
- GeoServer, pygeoapi, pg_featureserv, stac-fastapi.
Examples of Real-World Solutions¶
- A national mapping agency publishes authoritative boundaries through OGC APIs.
- A climate data platform exposes STAC catalogs and COG assets.
- A city API supports bounding-box filters, pagination, and links to metadata.
- A research workflow combines data from several agencies because each publishes standard formats.
Working Practice Examples¶
- Design an API route for querying features by bounding box, datetime, and attribute filters.
- Write an OpenAPI sketch for a spatial collection endpoint.
- Compare GeoJSON and GeoParquet for API vs analytical workloads.
- Publish a tiny static STAC catalog for three raster assets.
- Write a client that follows
linksfrom/collectionsto a collection'sitemsendpoint. - Implement
bbox,limit, andoffsetparameters for a small GeoJSON feature API. - Compare a WMS
GetMapresponse with an OGC API - Featuresitemsresponse for the same layer. - Design an OGC API - Processes request for buffering a point, clipping a raster, or summarizing parcels inside a polygon.
Common Failure Modes¶
- Returning geometry without CRS assumptions.
- No pagination for large result sets.
- Ambiguous units in distance filters.
- Mixing licensing terms in derived outputs.
- Treating standards as documentation instead of testable contracts.
- Hard-coding URLs instead of following published links.
- Confusing rendered map images with feature data.
- Implementing
bboxbut forgetting CRS and axis-order rules. - Returning nonstandard JSON while claiming GeoJSON or OGC API compatibility.
Works Cited¶
"OGC API Standards." Open Geospatial Consortium, https://ogcapi.ogc.org/. Accessed 9 May 2026.
"OGC API - Features Standard." Open Geospatial Consortium, https://www.ogc.org/publications/standard/ogcapi-features/. Accessed 9 May 2026.
"OGC API - Processes." Open Geospatial Consortium, https://ogcapi.ogc.org/processes/. Accessed 12 May 2026.
"OGC API - Tiles Standard." Open Geospatial Consortium, https://www.ogc.org/publications/standard/ogcapi-tiles/. Accessed 12 May 2026.
"OGC Standards." Open Geospatial Consortium, https://www.ogc.org/standards/. Accessed 9 May 2026.
"SpatioTemporal Asset Catalog Specification." STAC, https://stacspec.org/. Accessed 9 May 2026.
Butler, Howard, et al. "The GeoJSON Format." RFC 7946, Internet Engineering Task Force, 2016, https://www.rfc-editor.org/rfc/rfc7946. Accessed 9 May 2026.
"GeoParquet Specification." GeoParquet, https://geoparquet.org/. Accessed 9 May 2026.
