Project
Changelog¶
All notable changes to the Portolan specification are recorded here. The Portolan
STAC profile keeps its own changelog in stac/CHANGELOG.md.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning under the pre-1.0 bump policy described in the README.
0.2.0 - 2026-08-28¶
A catalog declares this version by carrying
https://schemas.portolan-sdi.org/portolan/v0.2.0/schema.json in stac_extensions.
The v0.1.0, v0.1.1, and v0.1.2 schemas stay published unchanged, so a
catalog authored against any of them keeps validating against it.
This release is breaking, and it bumps MINOR under the pre-1.0 policy. Every
catalog that conformed to v0.1.2 still conforms. A validator built against
v0.1.2 does not. It reports an error for a self link and for an absolute
structural href. v0.2.0 allows both. rashid 0.1.8 is the first
release that carries the new rules.
Changed¶
- Core takes no position on relative versus absolute links
(
PORTO-CORE-034retired,specs/portolan/core.md): the rule that all structural links be relative, and that objects carry noselflink, is gone. Both link styles have sound uses. Relative links keep a catalog portable, and absolute links name a location some hosts and clients handle more reliably, so the choice belongs to the publisher rather than the standard (#159). The Links section now points to the STAC best practices on the use of links for the trade-offs. - Requirements manifest: 128 requirements, now 88 MUST, 23 SHOULD, and 17 MAY.
- The versioning policy defines
misvalidate(README.md): the breaking rule tested both catalogs and tools, and the non-breaking rule tested only catalogs, so the two rules were not opposites and a relaxation matched both (#182). A tool now misvalidates when it reports an error against a conforming catalog, or reports no error against a non-conforming one. Under that definition a relaxation is breaking, because a released tool rejects what the new version allows. A table lists the verdict for each kind of change. After1.0.0, a relaxation bumps MINOR rather than MAJOR.
Added¶
- A published root catalog SHOULD carry a
selflink (PORTO-CORE-081,specs/portolan/core.md): a catalog served over the internet from a single fixed URL SHOULD carry an absoluteselflink on its root catalog, matching the STAC best practice. The link records the canonical location, so a downloaded copy still says where it came from and a tool can turn relative references into URLs from the metadata alone. Enforcement isprocess: whether a catalog is served from a fixed URL is a publisher fact a validator cannot see in the files. - Guidance on links for git-backed catalogs
(
specs/best-practices/git-backed-catalogs.md): a new "Links and the publish step" section. It recommends relative links and noselflink in the tracked tree, so the same files validate in the repository, in a preview, and in production, and it has the publish step write the absoluteselflink onto the root catalog from thepublic_baseit already holds. It also notes where absolute asset hrefs serve clients better, such as the Source Cooperative file listing.
0.1.2 - 2026-08-20¶
A catalog declares this version by carrying
https://schemas.portolan-sdi.org/portolan/v0.1.2/schema.json in stac_extensions.
The v0.1.0 and v0.1.1 schemas stay published unchanged, so a catalog authored
against either version keeps validating against it.
Nothing in this release asks more of a catalog that already conforms. The
fan-out guidance is a SHOULD that rashid reports as a warning, and the
multilingual rules permit a structure the profile previously left unspecified.
The schema carries no change beyond its own $id.
Added¶
PORTO-CORE-078: a catalog or collection with twenty or more children SHOULD organize them into subcatalogs, thematic or otherwise. A flat list of dozens of children is hard to browse, and the profile already allows a catalog at every level above a leaf, including below a collection. rashid reports the fan-out as a warning (PTL-CAT-001).- Multilingual catalogs (
PORTO-CORE-079,PORTO-CORE-080): catalogs can publish a separate STAC tree for each language.alternatelinks connect the trees without adding them to the catalog hierarchy. Each tree has its own root, while equivalent objects use the same IDs. - Multilingual catalog guidance: a new best-practices page explains how to structure, link, translate, and maintain the language trees.
- Requirements manifest: 128 requirements, now 89 MUST, 22 SHOULD, and 17 MAY.
0.1.1 - 2026-08-14¶
A catalog declares this version by carrying
https://schemas.portolan-sdi.org/portolan/v0.1.1/schema.json in stac_extensions.
The v0.1.0 schema stays published unchanged, so a catalog authored against that
version keeps validating against it.
Two changes ask something new of a catalog that already conforms. A collection
with more than one style now MUST mark exactly one style asset with the default
role (PORTO-CORE-070, raised from SHOULD), and a root catalog that publishes an
icon link MUST give it a type (PORTO-CORE-075).
Added¶
PORTO-FMT-046andPORTO-FMT-047: a vector collection SHOULD document its columns with the STAC table extension, carryingtable:columnson the collection, and an item holding a GeoParquet data asset SHOULD carry the field in itsproperties. Those are the two placements the extension defines, and the collection is the only one a partitioned collection has, since its data sits behindpartition:globrather than in an asset.PORTO-FMT-048permits per-asset declaration where a collection's data assets describe differing schemas. The same SHOULD already covered tabular collections (PORTO-FMT-037); nothing asked a vector collection for its schema, so a catalog could publish a hundred attribute columns that a client could only discover by reading the Parquet footer. The reference catalog movestable:columns,table:primary_geometry, andtable:row_countfrom thedataasset to the collection to match, andexamples/tools/stacio.pywrites them there from now on.- GeoParquet (PORTO-FMT-044): a validator MUST NOT fail a file for missing a spatial-ordering threshold that its row-group count puts out of reach. Row ordering is a separate rule and is not waived along with those thresholds, so a file with fewer than five row groups is still checked on its rows (#127).
- Data Storage (PORTO-CORE-073): a validator MUST probe the servers hosting the catalog's own cloud-native assets and MUST NOT require upstream servers to satisfy the section's requirements. This writes down the carve-out reis already applies to upstream assets (#89).
- Assets: the
sourceandcollection-mirrorroles are defined where the other roles are listed.sourcenames the upstream original a cloud-native asset was derived from, which PORTO-FMT-002 has asked a mirror to carry since 0.1.0; the role gives that asset a name and settles what carrying one costs. It is a reference, satisfied by an href pointing at the upstream server, so no publisher retains or rehosts an original to comply.collection-mirroris already required on a published item mirror by PORTO-FMT-041; this documents it rather than adding a requirement. Neither role is enumerated in the JSON Schema, which still accepts any non-empty role string (#90). - Formats (PORTO-FMT-045): a validator MUST apply the format requirements to
the catalog's own cloud-native assets and MUST NOT fail an asset for its
format because that asset carries the
sourcerole. An upstream original is a Shapefile or a GeoPackage as often as not, and judging it as a COG or a GeoParquet was never intended. The role records provenance, not hosting; which servers the Data Storage requirements reach is settled by PORTO-CORE-073 (#90). - Catalog Logo (PORTO-CORE-074 through PORTO-CORE-077): the root catalog MAY
publish a logo as a link with
rel: "icon". A registry lists many catalogs side by side, and a logo makes each one recognizable before the reader has read a word. STAC defines nologorelation,iconis registered with IANA, and stac-js and STAC Browser already read it. Aniconlink MUST declare atypedrawn from the seven image media types a browser renders in an<img>element, since a client drops an icon whose media type it does not recognize. The link SHOULD carry atitle, which a page renders as the image's accessible label, and itshrefSHOULD be relative, which keeps the catalog portable. The reference catalog publishes the Portolan logo from_assets/(#136). - Git-backed catalogs: a new best-practices page,
specs/best-practices/git-backed-catalogs.md, covers publishing a catalog whose source of truth is a Git repository. It recommendsvcsandissueslinks on the root catalog so software can find the repository and its issue tracker. Both relations are registered with IANA. This is a recommended convention, not a Portolan requirement, and the page says so (#145).
Changed¶
- Reworked the golden-example styles into named multi-variant sets, three to
five per vector Collection (#81 follow-up, demonstrating the multiple
data-driven styles
specs/best-practices/styling.mdrecommends). Each manifest variant declares its own type, categorical, binned graduated, heatmap, outline, labels, or a raw-expression escape hatch, with a title and a description carried onto the asset. Graduated styles emit binnedstepexpressions rather than continuous interpolates so clients can derive legends (#118). Collections that render from source, the two Natural Earth layers, now carry styles too, sourcing the GeoParquet itself, which portolan-browser binds onto the data it loads.build.py --styles-onlyre-authors styles against the committed tree without refetching data. - GeoParquet (PORTO-FMT-006): the low-overlap and high-locality checks now apply only to files with five or more row groups, and the high-locality limit moves from about 25% of the file extent to about 30%. Both checks measure a percentage across the row groups, so with fewer than five groups the percentage cannot land on a useful value, and a well-ordered four-row-group file was failing for that reason alone. The 30% limit comes from measuring Hilbert-sorted data, which is how producers usually sort: boxes cover 0.263 to 0.274 of the extent at five row groups and 0.250 to 0.270 at six, so the old 25% failed those files for having few row groups rather than for their ordering. Files with seven or more groups already passed at 25%. The low-overlap limit is unchanged, as is the rule that rows MUST be spatially ordered, which still applies to every file. The 50% skip rate for a 10% query window now reads as the benefit the limit delivers, not as a second check (#127).
- "Item mirror" is now used consistently (PORTO-FMT-040, PORTO-FMT-041,
PORTO-FMT-043). The STAC-GeoParquet copy of a collection's items is now always
referred to as an item mirror or STAC-GeoParquet mirror, leaving the
unqualified term mirror to its longstanding provenance meaning: a catalog
republishing data it did not produce. This is a wording-only change. The
requirements are unchanged, and the upstream role value
collection-mirroris unaffected (#99). - Data Storage (PORTO-CORE-043, PORTO-CORE-044, PORTO-CORE-045): clarifies
that the HTTP requirements apply to servers hosting the catalog's own
cloud-native assets. This includes range support,
206 Partial Content,Accept-Ranges,Content-Length, and CORS. Upstream originals hosted by third parties are out of scope, and validators do not probe those hosts. The requirements themselves are unchanged; this change only makes their scope explicit, matching the behavior already implemented in reis (#89). - Data Storage (PORTO-CORE-045): the CORS requirement now names the header
set a browser actually needs. Allowed request headers add
If-Match,If-Modified-Since,If-None-Match, andIf-Unmodified-Sincenext toRange, and exposed response headers addContent-Type. Conditional requests let a client revalidate a cached range instead of refetching it. Provider-specific headers such asx-amz-*andx-goog-*stay out of scope, as does versioning, which the specification does not yet define (#56). - Assets (PORTO-CORE-028):
file:sizeandfile:checksumdrop from MUST to SHOULD. A catalog that describes data it does not host often cannot produce a checksum, so the old MUST left it a choice between failing conformance and publishing a fabricated value (#112). - Assets (PORTO-CORE-030): the publish-time regeneration rule is restated as
an outcome. Any
file:sizeandfile:checksuman asset carries MUST match the bytes itshrefresolves to, whenever and however they were written. Its enforcement moves fromprocesstovalidator, since a data pass can check it. - Assets (PORTO-CORE-029): reworded to govern a
file:checksumwhere one is present. Multihash encoding is still required, unchanged in force. - Requirements manifest: 125 requirements, now 88 MUST, 21 SHOULD, and 16 MAY.
- Default style is identified by a
defaultrole (specs/portolan/core.md): a collection with more than one style now MUST mark exactly one style asset withroles: ["style", "default"], replacing the previous guidance that the default SHOULD be "listed first". STACassetsis an unordered JSON object and STAC states that asset keys carry no meaning a client is expected to understand, so neither order nor key is a reliable signal; STAC does encourage multiple roles per asset, so a second role lets a client find the default deterministically with no extension.PORTO-CORE-070moves fromSHOULDtoMUST, and the reference catalog and generator are updated to match. - Rewrote the golden-example documentation to the standard in
specs/best-practices/documentation.md(#81). Every Collection README now opens with a researched narrative and numbers, carries a tested Quick Start, a described schema table, suggested uses, and candid limitations, and every AGENTS.md is dataset-specific with join keys, quirks, CRS consequences, and tested query recipes. The prose lives in the manifest as per-collection markdown templates with generated{{placeholder}}blocks, so structure varies by Collection while counts, schemas, and code cannot drift from the built assets. Catalog-level READMEs became collections tables. A newcheck_docs.pyexecutes every code block in the committed docs, andbuild.py --docs-onlyregenerates documentation without refetching data. - The
boundaries/netherlands-provincesexample now mirrors its upstream ISO 19115 record from the Nationaal Georegister as ametadata-role asset. - Corrected example metadata found during research for #81. The
raster/sample-coglicense is CC0-1.0, matching rasterio's dedication of its test images, its temporal extent starts at the Landsat 7 launch instead of a placeholder, the Natural Earth temporal extents match the May 2022 5.1.x releases, and the Eurostat join documentation targetsISO_A2_EHwith the EL and UK remaps, the rawISO_A2join silently dropped France, Norway, and Kosovo.
0.1.0 - 2026-07-27¶
First tagged release, consolidating the specification from a working draft into a
versioned standard. A catalog declares this version by carrying
https://schemas.portolan-sdi.org/portolan/v0.1.0/schema.json in stac_extensions.
Added¶
- Normative core (
specs/portolan/core.md): catalog structure, conformance and versioning, catalogs, collections, items, assets, links, human-readable titles, bounding boxes and spatial extent, temporal metadata, data storage, providers, source provenance, license,AGENTS.md,README.md, metadata, and visualization. - Format requirements (
specs/portolan/formats.md): vector as GeoParquet with PMTiles, raster as COG, non-geospatial tabular data as Parquet, and point clouds as COPC. - Requirements manifest
(
specs/portolan/requirements.yaml): 115 requirements (84 MUST, 17 SHOULD, 14 MAY), each with a stable ID, a severity, an enforcement mode, and a verbatim quote from the prose.specs/tools/check_requirements.pyanchors every normative keyword in the prose to a manifest entry, and CI fails on drift. - Conformance model: the versioned schema URI is the single signal of
specification version, declared on catalogs and collections only, with items
inheriting from their collection. No separate version property exists, no
portolan:-prefixed fields are defined, andversions.jsonis a tooling artifact rather than a catalog requirement. Dataset versioning uses the STAC version extension. Conformance means passing the validator, in three separable passes: STAC structural, Portolan metadata, and Portolan data. - Collection mirrors (PORTO-FMT-040 through 043): a raster collection that models
scenes as items SHOULD publish a stac-geoparquet mirror of those items at
items.parquetin the collection root, registered as a collection-level asset with media typeapplication/vnd.apache.parquetand the rolecollection-mirror. The mirror reproduces the collection's items exactly at publish time, one row per item, and the GeoParquet storage rules bind it as they bind any other vector asset. - Item-level assets for multi-scene raster collections, so each scene carries its own data rather than collapsing into collection-level assets.
- Optimized GeoTIFF conformance: a COG must meet OGC 21-026's
/req/optimized_geotiffrequirements class, including square viewport-sized internal tiles, internal reduced-resolution overviews, and GeoTIFF keys on the full-resolution IFD. - Partition binding: a collection carrying any
partition:*field declares the incubating partition extension and carriespartition:scheme,partition:keys, andpartition:glob, wherepartition:globis the normative bulk-access path. - Portolan STAC profile (
stac/): a JSON Schema for the structural requirements checkable from STAC JSON alone, plus the normative registry of reused STAC extensions. Seestac/CHANGELOG.md. - Best practices (
specs/best-practices/), non-normative: catalog philosophy, documentation guidance forREADME.mdandAGENTS.md, the catalog grader, styling, and conversion defaults. - Incubating specs (
specs/incubating/): raster styling, point clouds, GeoTIFF statistics headers, and STAC-GeoParquet. - Reference catalogs (
examples/) with the generator that builds them, converting vector data through DuckDB and rasters through rasterio and rio-cogeo, rendering thumbnails and PMTiles without a GDAL CLI, and validating every build with rashid. - Schema publishing at
schemas.portolan-sdi.org, deployed from the tracked schema versions on each release, with portolan-sdi extension schemas pinned instac/portolan-extensions.jsonand fetched from their source repositories at build time. - CI gates: every PR touching normative content names its companion rashid PR or
carries the
no-validator-changelabel; the requirements manifest check anchors prose to IDs; the profile test suite lints markdown, pins the canonical schema URI, and validates the examples.