From 855796f013558377dce51603585046e6d34cc6c4 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Mon, 28 Sep 2026 08:11:26 +0200 Subject: [PATCH 1/2] feat(proposal): introduce separation of map concerns from render --- PROPOSAL-maps-and-styling-separation.md | 109 ++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 PROPOSAL-maps-and-styling-separation.md diff --git a/PROPOSAL-maps-and-styling-separation.md b/PROPOSAL-maps-and-styling-separation.md new file mode 100644 index 0000000..813eee6 --- /dev/null +++ b/PROPOSAL-maps-and-styling-separation.md @@ -0,0 +1,109 @@ +# Proposal: separate "map" concerns from `render` + +Status: **discussion draft**. + +## Context + +`render` didn't start from "how do I make a map." It started from wanting to describe a *composite* +derived from existing assets ([composite](https://github.com/stac-extensions/composite)), which +overlapped confusingly with [virtual-assets](https://github.com/stac-extensions/virtual-assets)' +cross-reference/repositioning model. What we settled on was narrowing `render`'s scope specifically +to *rendering*: how to combine assets (or bands within assets) with a small set of usual display +parameters (rescale, colormap, resampling, expression, band selection). + +[PR #27](https://github.com/stac-extensions/render/pull/27) (adding `minmax_resolution`/ +`tilematrixsets`, fixing [#16](https://github.com/stac-extensions/render/issues/16)) surfaced that +`minmax_zoom` and its proposed replacements don't fit cleanly into that scope: + +- A bare zoom integer is ambiguous across mapping libraries (different default tile sizes). +- A bare resolution number is meaningless without a CRS/bbox to interpret it against. +- A bare TileMatrixSet id is resolvable without GDAL/PROJ for well-known ids (OGC publishes static + JSON for these), and a custom CRS can be self-hosted and referenced by URI instead of needing a + central registry. + +That last point reframed the debate: `minmax_zoom`/`tilematrixsets` are web-mapping/tiling concepts, +not properties of *how pixel values are transformed*, which is what `render` was actually scoped to. +The same `render` object can inform a GIS desktop template (QGIS layer styling, a print layout) where +"zoom level" isn't a meaningful concept at all — so tiling concerns don't belong inside `render` even +though they're clearly still needed *somewhere*. + +That question moved to a maintainer discussion, which converged on a three-way structural split: + +- **Data Sources** — `assets`, [web-map-links](https://github.com/stac-extensions/web-map-links) +- **Styling** — `render` (raster), a `rel=stylesheet` link (vector + external raster styles, proposed + in [#21](https://github.com/stac-extensions/render/issues/21) / + [PR #28](https://github.com/stac-extensions/render/pull/28), based on OGC API - Styles) +- **Maps** — composes a data source with an optional style into a viewable map, carrying map-specific + constraints such as zoom/tile-matrix-set limits + +This isn't a novel split: **OGC API - Maps** (draft: +[20-058](https://docs.ogc.org/DRAFTS/20-058.html), overview: +[ogcapi-workshop.ogc.org](https://ogcapi-workshop.ogc.org/api-deep-dive/maps/)) already models exactly +this — `/collections/{id}/map` (data + default style) and +`/collections/{id}/styles/{styleId}/map` (data + a specific style) are both "map" resources, distinct +from **OGC API - Styles**' ([20-009](https://docs.ogc.org/DRAFTS/20-009.html)) independent `/styles` +catalog. [tiled-assets](https://github.com/stac-extensions/tiled-assets) (Proposal maturity) is the +closest existing STAC prior art for TMS-scoped zoom limits and should be referenced/aligned with here, +not duplicated. No `stac-extensions/maps` (or similarly scoped) extension currently exists. + +## Proposed mechanism + +A new `rel: "map"` link — shaped like `web-map-links`' existing `rel=wmts`/`rel=xyz` pattern rather +than a new inline object, since a "map" is itself a fetchable/renderable resource (an actual endpoint +or preview), matching OGC API - Maps' resource-oriented model. It reuses the `render` cross-reference +attribute already established by `web-map-links` integration, rather than duplicating data-source or +styling info onto the link itself. + +Raster case — data (assets, implicit via the `render` cross-reference) + style (a `renders` entry) + +map-specific constraints: + +```jsonc +{ + "rel": "map", + "href": "https://tiles.example.com/{z}/{x}/{y}.png", + "type": "image/png", + "title": "True color map", + "render": "true-color", + "tilematrixsets": { "WebMercatorQuad": [8, 22] } +} +``` + +Vector case — data + style (a `rel=stylesheet` link) + constraints: + +```jsonc +{ + "rel": "map", + "href": "https://tiles.example.com/vector/{z}/{x}/{y}.pbf", + "type": "application/vnd.mapbox-vector-tile", + "title": "Roads map", + "stylesheet": "https://example.com/styles/roads.json", + "minzoom": 0, + "maxzoom": 14 +} +``` + +What this would resolve: + +- `render` stays scoped to pixel transformation (rescale, colormap, resampling, expression, `bidx`); + `minmax_zoom`/`tilematrixsets` are deprecated there in favor of living on the `rel=map` link. +- @vincentsarago's precision need (TMS-scoped zoom) and @m-mohr's simplicity need (bare `minzoom`/`maxzoom`) + can both be offered on the same link. +- Reuses an existing cross-reference pattern instead of inventing a new one. + +**Open question**: whether a map link should offer `tilematrixsets` and bare `minzoom`/`maxzoom` +side by side, or just one of them. + +## What changes in `render` + +- extension description updated to reflect limited scope to pixel transformation (rescale, colormap, resampling, expression, `bidx`) +- `minmax_zoom` and the `tilematrixsets` field proposed in PR #27 would be deprecated/removed from + `render`'s own scope once this lands. +- PR #27 would be refocused (or closed) once the group agrees on where zoom/tiling constraints belong. + +## Open questions / next steps + +- New `stac-extensions/maps` repository +- How does the `rel=map` link's `tilematrixsets` relate to + [tiled-assets](https://github.com/stac-extensions/tiled-assets)' existing `tiles:tile_matrix_set_links` + — reuse, align, or intentionally keep separate? +- TMS-scoped zoom vs. bare `minzoom`/`maxzoom` vs. both, per the open question above. From e3d26fd7851dbf625947bba4375615318d79f076 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Tue, 29 Sep 2026 16:11:15 +0200 Subject: [PATCH 2/2] feat(proposal): refine map separation by introducing new link structure and constraints --- PROPOSAL-maps-and-styling-separation.md | 110 +++++++++++++++--------- 1 file changed, 68 insertions(+), 42 deletions(-) diff --git a/PROPOSAL-maps-and-styling-separation.md b/PROPOSAL-maps-and-styling-separation.md index 813eee6..47a701f 100644 --- a/PROPOSAL-maps-and-styling-separation.md +++ b/PROPOSAL-maps-and-styling-separation.md @@ -48,62 +48,88 @@ not duplicated. No `stac-extensions/maps` (or similarly scoped) extension curren ## Proposed mechanism -A new `rel: "map"` link — shaped like `web-map-links`' existing `rel=wmts`/`rel=xyz` pattern rather -than a new inline object, since a "map" is itself a fetchable/renderable resource (an actual endpoint -or preview), matching OGC API - Maps' resource-oriented model. It reuses the `render` cross-reference -attribute already established by `web-map-links` integration, rather than duplicating data-source or -styling info onto the link itself. - -Raster case — data (assets, implicit via the `render` cross-reference) + style (a `renders` entry) + -map-specific constraints: +**web-map-links** — where TMS/zoom information belongs, since it's a property of a specific *service*: + +- Existing rel types (`xyz`, `wmts`, `wms`, `pmtiles`, `3d-tiles`, `tilejson`) are unchanged. `render` + as a link attribute isn't one of them either — it's defined by `render`'s own integration with + web-map-links, not by web-map-links itself. +- There's already real prior art for per-TMS zoom constraints inside web-map-links today, closer than + `tiled-assets`: WMTS's REST encoding lets a `uriTemplate` variable like `{TileMatrix}` carry a JSON + Schema constraint (minimum/maximum) via `variables`, and the built-in `{TileMatrixSet}`/`{TileMatrix}` + placeholders are already tile-matrix-set aware without any custom field. Extending that same + `variables` mechanism to OGC API – Tiles (and to `xyz`, which today has no way to constrain `{z}` at + all) may be a smaller change than a new field, and worth checking before adding one. +- `maps` and `tiles` are added for OGC API – Maps and OGC API – Tiles, since both are formally distinct + protocols from what `xyz`/`wmts` already cover. + +Illustrative shape, field names TBD — using the existing `variables`/JSON-Schema-constraint mechanism +rather than a new field: ```jsonc -{ - "rel": "map", - "href": "https://tiles.example.com/{z}/{x}/{y}.png", - "type": "image/png", - "title": "True color map", - "render": "true-color", - "tilematrixsets": { "WebMercatorQuad": [8, 22] } -} +"links": [ + { + "rel": "tiles", + "type": "image/png", + "uriTemplate": "https://tiles.example.com/tiles/{tileMatrixSetId}/{z}/{x}/{y}.png", + "render": "true-color", + "variables": { + "tileMatrixSetId": { "const": "WebMercatorQuad" }, + "z": { "type": "integer", "minimum": 8, "maximum": 22 } + } + } +] ``` -Vector case — data + style (a `rel=stylesheet` link) + constraints: +**maps** — a composition document: + +- A projection +- A default extent +- An ordered list of layers, bottom to top +- Each layer has exactly one data source (an asset, a web-map-link, or a `render` reference) and + optionally one style (a `rel=stylesheet` link or a `render` reference) + +A single data source with a single style and its own zoom limits is just a map with one layer — the +composition also covers what a single link structurally can't: a COG rendered client-side with no tile +server at all, GeoParquet paired with a stylesheet, several layers shown together (see +[#22](https://github.com/stac-extensions/render/issues/22)), or a basemap layer underneath the data. +`source` is only needed when there's no `style.render` to imply it — a `render` entry already has its +own `assets`, so restating them on the layer would be redundant (and could disagree with it). Illustrative +shape, field names TBD: ```jsonc -{ - "rel": "map", - "href": "https://tiles.example.com/vector/{z}/{x}/{y}.pbf", - "type": "application/vnd.mapbox-vector-tile", - "title": "Roads map", - "stylesheet": "https://example.com/styles/roads.json", - "minzoom": 0, - "maxzoom": 14 +"maps": { + "default": { + "projection": "EPSG:3857", + "extent": [-122.52, 37.70, -122.35, 37.83], + "layers": [ + { "source": { "web_map_link": "basemap" } }, + { "style": { "render": "true-color" } }, + { "source": { "web_map_link": "roads-tiles" }, "style": { "stylesheet": "https://example.com/styles/roads.json" } } + ] + } } ``` -What this would resolve: - -- `render` stays scoped to pixel transformation (rescale, colormap, resampling, expression, `bidx`); - `minmax_zoom`/`tilematrixsets` are deprecated there in favor of living on the `rel=map` link. -- @vincentsarago's precision need (TMS-scoped zoom) and @m-mohr's simplicity need (bare `minzoom`/`maxzoom`) - can both be offered on the same link. -- Reuses an existing cross-reference pattern instead of inventing a new one. - -**Open question**: whether a map link should offer `tilematrixsets` and bare `minzoom`/`maxzoom` -side by side, or just one of them. +**render** — carries no visibility/zoom/resolution field at all, including for the client-side-rendering +case. That case is already covered above: a COG rendered client-side with no tile server is just a +`maps` document with one layer and no explicit TMS, so a dedicated escape hatch on `render` would +duplicate what `maps` already does, and reopen the exact scope creep this split exists to close. +Keeping `render` at zero map-adjacent fields is a stronger, simpler boundary than a narrow exception +for "just this one case." ## What changes in `render` -- extension description updated to reflect limited scope to pixel transformation (rescale, colormap, resampling, expression, `bidx`) -- `minmax_zoom` and the `tilematrixsets` field proposed in PR #27 would be deprecated/removed from - `render`'s own scope once this lands. -- PR #27 would be refocused (or closed) once the group agrees on where zoom/tiling constraints belong. +- Extension description updated to reflect the narrower scope: pixel transformation only (rescale, + colormap, resampling, expression, `bidx`). +- `minmax_zoom` and the `tilematrixsets` field proposed in PR #27 are removed from `render`'s scope + entirely — TMS/zoom for tiled services moves to `web-map-links`, and the client-side/no-tile-server + case moves to `maps` (see above). `render` keeps no map-adjacent field of any kind. +- PR #27 would be closed or refocused once the group agrees on this. ## Open questions / next steps -- New `stac-extensions/maps` repository -- How does the `rel=map` link's `tilematrixsets` relate to +- Detailed **maps v1** and **web-map-links v2** proposals, written up before any schema here. +- New `stac-extensions/maps` repository: scope, ownership. +- How do web-map-links' per-TMS zoom ranges relate to [tiled-assets](https://github.com/stac-extensions/tiled-assets)' existing `tiles:tile_matrix_set_links` — reuse, align, or intentionally keep separate? -- TMS-scoped zoom vs. bare `minzoom`/`maxzoom` vs. both, per the open question above.