Fornland — upstream data sources
Compiled 7 August 2026 by auditing every module in src/api/ (58 files),
the serverless proxies in api/, all 84 files in public/data/, and the
scripts/ generators that produce those snapshots. Fetch targets were read
from the code itself and, for the primary national registers, confirmed with
live HTTP probes on the compile date.
Revised 16 August 2026 for the two Baltic registers added since (Estonia,
Latvia) and for the Estonian conservation-area building grades, and 17 August
2026 for the Swedish antikvariska bedömningar, the Greenlandic withdrawals and
the Estonian historic green areas, and 19 August 2026 for the Netherlands, and 5 September 2026 for
Germany (Hessen, Berlin, Niedersachsen, Hamburg, Sachsen, Saarland).
Everything not marked with a revision date still carries its 7 August
measurement; the new rows were measured on their own revision date by the same
method, resultType=hits against the register's own WFS, or by fetching the
whole layer where it is small enough to count exactly. The Dutch figures come
from scripts/measure-rce.mjs, which counts against the whole register rather
than against a LIMIT, because a bare LIMIT on a SPARQL endpoint returns the
head of the graph and not a sample.
Counting rule. One source = one distinct data-providing organisation +
dataset. A dataset queried through several endpoints, layers or FeatureServer
indices counts once (Historic England's NHLE = 1, though Fornland queries
layers 0, 6, 7 and 9). Wikipedia across 8 language editions = 1. Excluded as
not sources: XML namespaces (schema.org, opengis.net, w3.org,
georss.org), Fornland's own infrastructure (fornland.com, its Supabase
project, api.anthropic.com), and generic CORS relays (allorigins.win).
Basemap/tile providers are listed separately as presentation infrastructure,
not heritage data. URLs that are only ever shown to the user and never
fetched are listed in §5 as link-out targets and are not counted.
Headline
More than 1.8 million heritage records from 86 data sources, across eighteen heritage jurisdictions in fifteen countries and territories, drawn from twenty-seven national and regional registers.
Of the 86 sources, 77 are country- or territory-specific and 9 are cross-border or pan-European. The record figure is measured, not estimated — see "Record counts" below for the per-source table and for why the noun is records and not sites. The jurisdiction count treats England, Scotland, Wales and Northern Ireland separately, because each runs its own register with its own schema and its own Fornland module; see §6 for the three defensible ways to count and why this one was chosen. See the Appendix for the honest reading of the "how many registers?" question.
An eighty-seventh source is built but not counted. Brukerminner arrived with the
curated walks, and it is the only source reachable through nothing else, so it
counts on the day the walk layer is registered in src/constants/index.ts and
not before. That layer is held back until the Oslo grant period opens on
1 October 2026, so this document stays at 86. The register figure would stay at
twenty-seven either way. Brukerminner is place knowledge written by members of the
public and hosted by Riksantikvaren, not a register with a schema and a
protection status, so it belongs in the source count and not in the register
count. Wikimedia Commons does not move the figure at all. It was already counted
for the enrichment pipeline, and the walk galleries read the same API through a
second module.
The figure lives in five places and they have gone out of step twice, in both
directions. 6bab5a7 landed the walk engine on main and raised
docs/metadata-normalization.md to 80 while this file, docs/FORNLAND-BRIEF.md
and both languages of src/components/MethodologyModal.tsx stayed at 79. Then
on 22 August all five went to 80 on a merge that registered the layer, and back
to 79 the same day when it was unregistered again. Neither state was public for
long, but the second one was pushed, which is the closer call of the two. The
lesson is the one already written down, with a clause added: a source count
moves in every place on the same day, or it does not move, and what it moves
with is the registration, not the data file.
1. Country and territory sources
Denmark (DK)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Fund og Fortidsminder | Slots- og Kulturstyrelsen | Archaeological sites, barrows, rock art, industrial heritage, marine wrecks | WFS (www.kulturarv.dk/ffgeoserver/public/wfs) |
Danish public-sector open data | src/api/heritage.ts, barrow.ts, rockart.ts, industrial.ts, shipwrecks.ts |
| Fredede og Bevaringsværdige Bygninger (FBB) | Slots- og Kulturstyrelsen | Listed buildings (fbb:view_bygning_fredede) |
WFS (www.kulturarv.dk/geoserver/wfs) |
Danish public-sector open data | src/api/buildings.ts |
| Fund og Fortidsminder record pages | Slots- og Kulturstyrelsen | Per-site fredningstekst, anlæg/datering, responsible museum | HTML scrape of the record page | Public data | src/api/enrichment.ts::fetchDanishDetail |
| Industrihistoriens Danmarkskort | Slots- og Kulturstyrelsen | 161 regional industrial monuments designated 2003–2007 | Static snapshot (slks-industrial.json), scraped from slks.dk + geocoded via Nominatim |
Danish public-sector open data | src/api/industrial.ts; generator scripts/generate-slks-industrial.mjs |
| Nationale Kulturmiljøer (2025) | Slots- og Kulturstyrelsen | 16 towns designated national cultural environments | Static snapshot (kulturmiljo.json) |
Danish public-sector open data | src/api/townscape.ts |
| Plandata.dk — Værdifulde kulturmiljøer | Erhvervsstyrelsen / Danish municipalities | ~2,557 municipally designated cultural environments | Static snapshot from WFS (geoserver.plandata.dk) |
Danish public-sector open data | src/api/townscape.ts; generator scripts/fetch-townscapes.mjs |
| Nationalmuseet collections | Nationalmuseet | Archaeological objects + CC-licensed object photography | REST API, Elasticsearch POST (api.natmus.dk) |
Open API; only CC/PD-licensed images are used | src/api/museumCollections.ts |
| SMK — Statens Museum for Kunst | SMK | Artworks depicting heritage sites | REST API (api.smk.dk) |
Open API | src/api/enrichment.ts::fetchSMKCollection |
| Lex.dk | Lex.dk / Danish national encyclopedia | Encyclopedia entries for named sites | REST/JSON (lex.dk/<name>.json) |
Free metadata licence | src/api/enrichment.ts::fetchLexDk |
| Trap Danmark | Trap Danmark / Lex.dk | Topographic encyclopedia entries | REST/JSON (trap.lex.dk/<name>.json) |
Metadata free; short excerpts under citation right (§22); only CC/PD images used | src/api/enrichment.ts::fetchTrapDk |
| Evald Tang Kristensen corpus (UCLA Nexus2) | UCLA Scandinavian Section | 900 Danish folk legends with place attributions; 431 dated 1887–1898 from the collector's own fieldtrip register; 813 filed under his own subject heading from cetk_indices.json |
Static snapshot from GitHub | Public domain (19th-c. collection) | src/api/folklore.ts; generators scripts/process-nexus2.mjs, build-danish-folklore-data.mjs |
| Snublesten Danmark | snublesten.dk | 152 Danish Stolpersteine with biographies, counted in the shipped file on 21 Aug 2026. This row said 159, which the generator may once have produced but the app has never served | Static snapshot (scrape + geocode) | None published. Same position as the Norwegian row below, and the same reuse of biography text and hotlinked photographs. "Public data" stood here until 21 Aug 2026, which reads as a licence and is not one | src/api/stolpersteine.ts; generator scripts/scrape-stolpersteine.mjs |
Norway (NO)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Kulturminner (Askeladden) | Riksantikvaren, distributed via Geonorge | Heritage localities + enkeltminner: archaeology, buildings, rock art, industrial heritage, wrecks | WFS 2.0 GML (wfs.geonorge.no/skwms1/wfs.kulturminner) via own edge proxy /api/no-heritage; the building layers additionally baked whole into public/data/no-bygg/ |
NLOD 1.0. Geonorge's metadata for dataset c72906a0-2bc2-41d7-bea2-c92d368e3c49 gives OtherConstraintsLink = data.norge.no/nlod/no/1.0 and UseLimitations = "Ingen begrensninger på bruk er oppgitt", checked 21 Aug 2026. Read rather than inferred from the "Åpne data" badge, because SEFRAK wears the same badge and still demands written permission in OtherConstraints. So informasjon may be quoted with credit, which is what lets a curated walk stop carry a quote from this register |
api/no-heritage.ts; consumed by src/api/heritage.ts, buildings.ts, rockart.ts, industrial.ts, shipwrecks.ts, noBygg.ts; generator scripts/fetch-norwegian-buildings.mjs |
| Kulturmiljøer | Riksantikvaren, via Geonorge | 845 cultural environments of national interest | Static snapshot from WFS (wfs.geonorge.no/skwms1/wfs.kulturmiljoer) |
NLOD | src/api/townscape.ts; generator scripts/fetch-townscapes.mjs |
| Kulturminnesøk KMS API | Riksantikvaren | Per-locality detail + photographs with credit | REST API (kms-api.kulturminnesok.no) |
Images CC BY with photographer credit | src/api/enrichment.ts::fetchNorwegianDetail |
| Lokalhistoriewiki | Norsk lokalhistorisk institutt / Nasjonalbiblioteket | Local-history articles geosearched by coordinate | MediaWiki API (lokalhistoriewiki.no/api.php) |
CC BY-SA | src/api/enrichment.ts, src/api/wikiArticle.ts |
| Oslo Byleksikon | Selskabet for Oslo Byes Vel (MediaWiki) | ~7,580 encyclopedia articles on Oslo places, streets, institutions | Static snapshot from MediaWiki API | None published. action=query&meta=siteinfo answers with the literal placeholders [license_name] and [license_url], i.e. the MediaWiki default was never filled in. This row said "Wiki licence terms" until 21 Aug 2026, which named a licence that does not exist. Every view credits Selskabet for Oslo Byes Vel and links back. This row also claimed Fornland "quotes no prose", which was measured false on 21 Aug 2026. public/data/oslo-byleksikon-texts.json ships an extract for all 7,580 articles, median 537 characters, 1,876 of them over 1,000, and ByleksikonDetail.tsx:134 renders it as the panel description. Hero and gallery images are hotlinked from oslobyleksikon.no. The reuse is substantive and the licence question is therefore about text and images, not about linking. |
src/api/osloByleksikon.ts; generator scripts/generate-oslo-byleksikon.mjs |
| Brukerminner | Riksantikvaren | Place knowledge submitted by the public: memories and local knowledge tied to a specific place. 316 records inside the Oslo bbox, of which 2 carry a walk stop. Baked into the walk file rather than read live | OGC API Features (api.ra.no/brukerminner), bbox measured working and CORS measured open. The whole of api.ra.no answered 502 on 20 and 21 Aug 2026, at the root as well as at this path, while kulturminnesok.no stayed up. The generator falls back to the two stops as already published and prints that it did, so an outage cannot quietly rewrite them |
CC BY 4.0, declared by the API itself | scripts/build-oslo-walks.mjs; shipped in public/data/oslo-walks.json, read by src/api/curatedWalks.ts |
| DigitaltMuseum | KulturIT (Norwegian/Swedish museums) | Museum objects near a site, with images | REST/Solr (api.dimu.org) |
Public API | src/api/museumCollections.ts, src/api/enrichment.ts::fetchDigitaltMuseum |
| Snublestein.no | Jødisk Museum i Oslo | 757 Norwegian Stolpersteine with biographies | Static snapshot (POST scrape) | None published. The site states no terms at all. This row said "Public data" until 21 Aug 2026, which reads as a licence and is not one. Measured the same day, Fornland ships a biography for 909 of the file's 919 records, median 1,015 characters and up to 9,461, and hotlinks 889 photographs from the two sites' own image servers. That is substantive reuse, not a link. | src/api/stolpersteine.ts; generator scripts/scrape-stolpersteine.mjs |
Sweden (SE)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Kulturmiljöregistret / Fornsök lämningar (via K-samsök) | Riksantikvarieämbetet | Swedish ancient monuments: heritage, barrows, rock art, industrial, wrecks | K-samsök search API, recordSchema=presentation (kulturarvsdata.se/ksamsok/api) |
CC0 | src/api/ksamsokLamning.ts::searchAllBedomningar; consumed by heritage.ts, barrow.ts, rockart.ts, industrial.ts, shipwrecks.ts |
| Bebyggelseregistret (BeBR) | Riksantikvarieämbetet | Listed/protected buildings (serviceName=bbra) |
K-samsök search API, recordSchema=rdf |
CC0 | src/api/buildings.ts::fetchSwedishBuildings |
| Kulturmiljöregistret, per-lämning record | Riksantikvarieämbetet | Terrain, orientation, vegetation, component remains, oral tradition, excavation reports, the pre-2018 RAÄ number | JSON-LD record (kulturarvsdata.se/raa/lamning/<uuid>, Accept: application/json) |
CC0 | src/api/ksamsokDetail.ts::fetchLamningDetail, via enrichment.ts::fetchSwedishDetail |
| K-samsök aggregated collections | Riksantikvarieämbetet | Museum objects near a coordinate (bounding-box search) | K-samsök search API, XML | CC0 | src/api/museumCollections.ts |
| SHFA — Svenskt HällristningsForskningsArkiv | University of Gothenburg | Rock-art documentation images | REST API (shfa.dh.gu.se/api/image/) |
CC BY | src/api/rockart.ts::fetchSHFAImages |
| Riksintressen för kulturmiljövården | Länsstyrelserna / Riksantikvarieämbetet | 1,663 national heritage interests, enriched from värdebeskrivning PDFs | Static snapshot from ArcGIS REST (ext-geodata-nationella-visning.lansstyrelsen.se) |
CC BY 4.0 | src/api/townscape.ts; generators scripts/fetch-townscapes.mjs, enrich-se-townscapes.mjs |
| Sägenkartan | ISOF — Institutet för språk och folkminnen | Folk legends with coordinates from Sweden and Norway. 2.831 records in total, measured 22 Aug 2026: 1.399 from ISOF Göteborg (DAG), 167 from ISOF Uppsala (DFU), and 1.265 (44,7 %) from Norsk folkeminnesamling at the University of Oslo, which the API returns under archive.country = norway with nfs id prefix. Calling the layer "Swedish" was wrong here and in LegalModal.tsx until 22 Aug 2026. |
REST API, Elasticsearch (ull.isof.se/folkeservice/api/es/documents/) |
Public data (not verified against a stated licence) | src/api/folklore.ts::fetchISoFLegends |
| Norse World | Uppsala University | 532 medieval place references attested in Old Swedish/Old Danish texts | Static snapshot from GeoJSON API (norseworld.nordiska.uu.se) |
CC BY | src/App.tsx (norse-world layer); generator scripts/fetch-norse-world.mjs |
| Rundata | Rundata / Uppsala University | 11,473 runic inscriptions: transliteration, translation, dating | REST API (rundata.info/api/inscription/<slug>) |
ODbL | src/hooks/useEnrichment.ts |
Finland (FI)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Rajapinta suojellut (muinaisjäännökset, rakennukset) | Museovirasto / Finnish Heritage Agency | Archaeological sites, barrows, rock art, industrial heritage, wrecks, protected buildings | WFS (geoserver.museovirasto.fi/geoserver/rajapinta_suojellut/wfs) |
CC BY 4.0 | src/api/finland.ts |
| Kyppi.fi record pages | Museovirasto | Per-site kuvaus description text |
HTML scrape via CORS relay | CC BY 4.0 | src/api/enrichment.ts::fetchFinnishDetail |
| RKY — Valtakunnallisesti merkittävät rakennetut kulttuuriympäristöt | Museovirasto | 1,240 nationally significant built cultural environments | Static snapshot from WFS + rky.fi detail scrape | CC BY 4.0 | src/api/townscape.ts; generators scripts/fetch-townscapes.mjs, enrich-rky.mjs |
| Finna | Kansalliskirjasto (National Library of Finland) | Finnish museum/archive objects near a site | REST/Solr (api.finna.fi) |
Public API | src/api/museumCollections.ts |
| SKVR — Suomen Kansan Vanhat Runot | Finnish Literature Society (SKS), corpus via GitHub | 648 geolocated Finnish-Karelian folk poems | Static snapshot from XML corpus | Public domain | src/api/folklore.ts; generator scripts/build-finnish-folklore.mjs |
Estonia (EE)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Kultuurimälestiste register | Muinsuskaitseamet / Estonian Heritage Board | 12,536 listed monuments: architecture, archaeology, historical monuments, Gulf of Finland war wrecks | WFS (gsavalik.envir.ee/geoserver/muinsuskaitse/ows) |
CC BY 4.0 | src/api/estonia.ts |
| Muinsuskaitsealade hoonete väärtusklassid | Muinsuskaitseamet / Estonian Heritage Board | 3,127 graded buildings inside the eleven conservation areas, with the Board's own appraisal text | WFS, layers kaitsekat_a + kaitsekat_b on the same GeoServer |
CC BY 4.0 | src/api/estonia.ts |
| Ajaloolised haljasalad | Muinsuskaitseamet / Estonian Heritage Board | 134 historic parks, gardens, avenues and squares inside the same eleven areas | WFS, layer ajal_haljasala on the same GeoServer |
CC BY 4.0 | src/api/estonia.ts |
The second and third rows are different populations from the first, not
enrichments of it. kaitsekat_* grades every building standing inside one of the eleven
muinsuskaitsealad — the protected town centres of Tallinn, Tartu, Pärnu,
Viljandi, Võru, Paide, Kuressaare, Rakvere, Haapsalu, Lihula and Rebala — on
an A/B/C scale set by that area's protection order. A point-in-polygon join
against the listed monuments matched 1 of 26 in Kuressaare and 4 of 268 in
Tartu, and the few matches were neighbouring addresses rather than the same
building, so the two are kept apart.
Grade C is deliberately excluded. Its 3,522 records are neutral fabric:
only 974 carry any text at all, and the most frequent string is "Abihoone"
(outbuilding, 198 times), followed by "Kuur" and "Katusealune". A and B are
3,318 features, 3,295 of them with prose, which is what makes the layer worth
fetching. Those 3,318 features collapse to 3,127 pins because multi-part
buildings repeat their etak_id; the GeoServer fid looks like a key
(kaitsekat_a.0) but is a row index and shifts between requests, so
etak_id — the national topographic building identifier — is what Fornland
deduplicates on.
The appraisal text is the source's own pohjendus field and carries the
dating in prose rather than in a column. estonianBuildingDate() lifts it out
where the surrounding words mark it as a construction date (971 of 3,295,
29%); the rest keep the prose without a dating.
The third row is the greenery those same protection orders cover: 385 rows of
which 134 are named places, the rest being habitat classes recorded in the
name column for the one rural area. It is the only Estonian layer with no date
of any kind, in a column or in prose, so every one of the 134 is Udateret.
Appendix J has the full reading, including the three columns that each hold
something other than what they are called.
Two extra sources, no extra register. By the counting rule at the top of
this file — one organisation plus one dataset — kaitsekat_* and
ajal_haljasala are the second and third Estonian sources, which is why the
total moves from 70 to 72. Neither is a seventeenth-plus register: both are set
by the protection orders for the muinsuskaitsealad, administered by the same
body under the same act, so the register count stays at seventeen.
Latvia (LV)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Valsts aizsargājamo kultūras pieminekļu saraksts | Nacionālā kultūras mantojuma pārvalde (NKMP) | 7,409 protected monuments plotted, from a register of 10,322 entries: archaeology, architecture, art, historical monuments | WFS (geoserver.mantojums.lv/geoserver/monument/ows) |
No licence statement on the service itself; the same register is CC0 1.0 on data.gov.lv — see the note below | src/api/latvia.ts |
The dating, the condition, the public accessibility and the protection date
are none of them on the WFS. They come from the same register's CSV
distribution on data.gov.lv, published by Kultūras informācijas sistēmu
centrs under CC0 1.0, joined on the protection number and committed as
public/data/latvia-register.json by scripts/build-latvia-register.mjs.
Condition and accessibility are the register's own assessment: 132 monuments
are recorded as ruinous or lost, and 3,637 cannot be visited at all.
Counted as one source, not two. The counting rule at the top of this file says one organisation + dataset, and KISC + the CSV is formally a different pair from NKMP + the WFS. But it is the same register, so counting it twice would inflate the source total on a technicality. Recorded here rather than resolved silently: a stricter reading of the rule gives 74 sources, not 73.
Lithuania (LT)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Kultūros vertybių registras (KVR) | Kultūros paveldo departamentas (KPD) | 21,202 records: architecture, fine art, archaeology, mythological sites, historic town districts | ArcGIS REST, two FeatureServers (osp-sdg.stat.gov.lt/arcgis/rest/services/KPD/kvr_taskai_enriched and …/kvr_poligonai_enriched) |
CC BY 4.0 on the state data portal — see the note below | src/api/lithuania.ts |
Both layers are read, and that is the load-bearing decision. They look like
two renderings of one register and are nothing of the kind: 8,843 point codes
against 12,360 polygon codes share exactly one code between them. The tipas
column says why — the point layer is 8,581 components of a complex against 251
standalone objects and 11 complexes, while the polygon layer is 10,177
standalone objects, 2,020 complexes, 152 areas and 10 components. The points are
the parts and the polygons are the places. Shipping only the points would have
been the Norwegian Enkeltminne trap in its purest form: a map of fragments of
monuments it never showed. Deduplicated across both layers, 21,202 records reach
the map, after dropping the eight whose protection has been revoked outright.
Licence. The department publishes its own ArcGIS Online copies of the same register with no licence statement at all. The mirror on the state data agency's portal carries CC BY 4.0, which is why that is the one Fornland reads. Both are CORS-open.
Polygons arrive as server-side centroids (returnCentroid=true), so no
geometry crosses the wire. That makes them areas drawn as points, with the
caveat that always carries: right on the map, wrong for anything that measures a
distance. The median footprint is 1,783 m², which is a building — but the top
tenth is above 41,000 m² and the largest is 7.5 km², a historic town district
reduced to one dot in its middle. Note that the polygon layer honours
returnCentroid despite omitting supportsReturningQueryGeometryCentroid from
its metadata, while the point layer answers HTTP 400 to the same request; the
two request shapes differ for that reason.
Dating. amzius is free text in Lithuanian, filled on 16,023 of the 21,202
records, and src/api/lithuania.ts::parseAmzius reads 16,010 of them — 99.92%.
The thirteen it declines are named individually in the module's JSDoc and are
genuinely undatable. The resulting period split is 12,181 Historisk Tid, 5,192
Udateret, 2,777 Jernalder, 759 Middelalder, 232 Stenalder and 61 Bronzealder.
Not read, on purpose. The register's thirteen archeologinis /
architekturinis / … flags are its own classification of why an object matters
(65.5% architectural, 16.9% fine art, 9.7% archaeological, 2.5% mythological) and
would make a good heritage-panel facet the day one exists. viesas reads like a
public-access flag and is emphatically not one: it is 1 on every record in both
layers, so mapping it to access would have invented a fact. povandeninis is 0
on all of them. 10,360 records are registrinis — registered without being
state-protected — and they stay, because they are still cultural property.
One unverified claim, stated plainly. nuoroda, the register's own link
column, is filled on every record and is used as sourceUrl. It could not be
verified end to end: kvr.kpd.lt sits behind a Cloudflare bot check that answers
403 to curl, to headless Chromium and to a headed Playwright browser alike. The
URL shape is the department's own, so the claim is theirs, and an ordinary
browser should pass the check.
Netherlands (NL)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Rijksmonumentenregister + rijksbeschermde stads- en dorpsgezichten | Rijksdienst voor het Cultureel Erfgoed (RCE) | 63,103 national monuments (61,642 built, 1,461 archaeological) with the register's own listing text, plus 472 protected townscapes | SPARQL over linked open data (api.linkeddata.cultureelerfgoed.nl/datasets/rce/cho/services/cho/sparql) for the monuments, RCE's own WFS (services.rce.geovoorziening.nl/rce/wfs) for the townscapes |
CC BY 4.0 | src/api/netherlands.ts, public/data/townscapes-nl.json |
Withdrawn listings sit in the same graph as live ones. ceo:Rijksmonument
has 67,496 entities; only 63,103 hold the legal status rijksmonument. 4,348
are geen rijksmonument and 43 are voorbeschermd. Without the status filter
Fornland would publish 4,348 revoked protections as protected, which is the
NunaGIS trap in a different language.
The label on a Dutch pin is the function, not the name. Measured on the
whole register rather than on a sample: functie 100.0%, omschrijving 99.9%,
stijl 31.5% (18.5% once the 8,215 records reading n.v.t. are dropped),
bouwjaar 26.2%, type 15.2%, naam 13.1%. In Amsterdam naam is 3.7%, because
grachtenpanden are numbered and not named. Two consequences worth stating
before anyone rediscovers them: the name field cannot carry the pin, and 74% of
Dutch pins come out Udateret.
Rows are not records here either. Each monument carries several geometries,
measured at 4.0 geometry rows per monument, so the query groups by monument and
samples. A SELECT without GROUP BY returned five rows for one Amsterdam
hotel.
There is no centroid function on this endpoint. The engine is Virtuoso
rather than Triply's own, and geof:centroid, bif:st_centroid, bif:st_x
and bif:st_geomfromtext all answer "Undefined procedure". Only
geof:sfIntersects works. What saves the layer is that the monuments carry
their own points: 63,057 of the 63,086 live monuments with geometry have a
POINT, so filtering to those costs 29 records and removes the whole polygon
problem.
CSV rather than JSON, same query and same endpoint. Amsterdam at z11 is
603 kB as SPARQL-results-JSON and 155 kB as CSV, because the JSON form repeats
the variable name and a {"type","value"} wrapper in every cell.
The truncation ceiling has to be measured, not read. SPARQL has no
exceededTransferLimit, so the only signal is the row count equalling the
LIMIT. Amsterdam z13, Amsterdam z11 and the whole Randstad at z9 all truncate
at 2,000. Randstad z9 takes 10.3 s, which is the number to watch. Adding all
five optional fields costs 8 bytes per record over a bare query, so there is
nothing to save by trimming the projection.
ceo:ArcheologischTerrein is not read, and the reason is not squeamishness.
All 13,025 archaeological terrains are in the graph with waardering and
omschrijving, but ceo:heeftGeometrie appears on none of them and there is no
two-hop route to a geometry. They are not withheld: all 11,211 that carry a
confidentiality flag read openbaar. The geometry is not on PDOK either (the
ps-ch polygons are rijksmonumenten, namespace nlps-rijksmonumenten) and not
on RCE's WFS; archis, archis2, amk and archeologie all answer 404. This
was the most interesting layer in the integration and it is unavailable.
PDOK is not the source, and the measurement is here so nobody tries again.
service.pdok.nl/rce/ps-ch/wfs/v1_0 is CORS-open, holds 63,570 points and
4,687 polygons and speaks EPSG:4326. But the INSPIRE-harmonised schema carries
neither a name nor a type. It has localid, legalfoundationdate, ciCitation
and some code-list URIs, and nothing else. Harmonisation cost the attribute
schema, and the result would be pins with no label.
Dutch world heritage comes through the existing UNESCO layer, not through
RCE. The eleven Dutch sites are already in the Wikidata-backed unesco module,
so reading ceo:Werelderfgoed as well would have put two pins on each of them.
Counted as one source, not two. RCE serves the monuments over SPARQL and the townscapes over its own WFS, which is one organisation and arguably two datasets. Same call as Latvia above, and recorded the same way: a stricter reading of the counting rule gives 79 sources, not 78.
Germany (DE)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Hessische Denkmalliste (DenkXweb) | Landesamt für Denkmalpflege Hessen | 50,483 Kulturdenkmäler (buildings) and 2,486 Gesamtanlagen (historic town cores and ensembles), each with the register's own listing text | Live WFS per viewport, geodienste.denkx.de/geoserver/denkx/wfs, types baudenkmal and flaechendenkmal, EPSG:4326, JSON |
dl-de/by-2-0 | src/api/germany.ts (fetchHessenBuildings) |
| Berliner Denkmalliste | Landesdenkmalamt Berlin | 8,314 objects: 6,493 Baudenkmale, 681 Gesamtanlagen, 564 Gartendenkmale, 509 Ensembles, 67 Bodendenkmale, with address, designation, dating and architect | Static public/data/berlin-denkmale.json, built by scripts/build-berlin-register.mjs from the WFS gdi.berlin.de/services/wfs/denkmale (geometry, type) joined on the object number to dm_liste.txt on berlin.de (the text) |
WFS dl-de/zero-2-0; list CC BY 4.0 | src/api/germany.ts (fetchBerlinBuildings, fetchBerlinHeritage) |
| Denkmalatlas Niedersachsen | Niedersächsisches Landesamt für Denkmalpflege (NLD) | 119,937 records: 86,876 Bau und Kunst, 9,022 Baudenkmal Gruppe, 24,032 Archäologie, typed in the register's thesaurus, with listing text on about half | Live OGC API Features per viewport, geodatendienste.denkmalatlas.niedersachsen.de/xtraserver-webapi/denkmale/v1, collection protectedsite, property selection, fachbereich filter; listing text per record on click |
CC BY-SA 4.0 | src/api/germany.ts (fetchNiedersachsenBuildings, fetchNiedersachsenHeritage), src/api/enrichment.ts (fetchNiedersachsenDetail) |
| Hamburger Denkmalliste | Denkmalschutzamt Hamburg (Behörde für Kultur und Medien) and Archäologisches Museum Hamburg | 13,836 listed objects (11,970 Baudenkmale, 1,929 Ensembles, 560 Denkmalobjekte, 296 Grenzsteine, 137 Gartendenkmale) with designation, type, year, address and architect; 2,681 Bodendenkmale with find spot, element type and epoch from/to | Static public/data/hamburg-denkmale.json, built by scripts/build-hamburg-register.mjs from HH_WFS_Denkmalschutz (geo+json, pages of 10,000) and HH_WFS_Denkmal_public (GML) |
dl-de/by-2-0 | src/api/germany.ts (fetchHamburgBuildings, fetchHamburgHeritage) |
| Sächsische Denkmalliste | Landesamt für Denkmalpflege Sachsen (LfD) | Kulturdenkmäler as areas (141,294 rows), lines (21,596) and points (16,336), each with Eigenname (28%), Kurzcharakteristik (99.9%), Datierung (100%), address, Gemeinde and Ortsteil; one object lies in several rows | Live WFS per viewport, cardomap3.idu.de/lfds/public/ogc.ashx?PkgId=5, types L1/L2/L3, GML only, parsed with DOMParser; parts pooled by obj_nr |
Capabilities none/none; GDK record CC BY 3.0 DE | src/api/germany.ts (fetchSachsenBuildings) |
| Saarländische Denkmalliste | Landesdenkmalamt Saarland (LDA) | 3,337 Baudenkmale as points with address, Kreis, Gemeinde, Ortsteil and the register's line ("Langgarten 3, Gasthaus, 1912-13 (Einzeldenkmal)", filled on 3,298) | Live ArcGIS WFS per viewport, geoportal.saarland.de/arcgis/services/Internet/Denkmalschutz/MapServer/WFSServer, outputFormat=ESRIGEOJSON |
None stated: capabilities say no fees and no access constraints; asked | src/api/germany.ts (fetchSaarlandBuildings) |
Germany has no national register. Each of the sixteen Länder keeps its own
Denkmalliste under its own law, on its own service, with its own schema and
licence, and src/api/germany.ts is therefore a table of Länder behind one
door: fetchGermanBuildings asks only the Länder whose own box the viewport
touches. Hessen is the first. The survey of all sixteen, with endpoints,
counts and licences measured on 5 September 2026, is docs/TYSKLAND.md §1.
One register, one source, one Land. Hessen counts as a regional register in its own right, which is what moves the register figure from twenty to twenty-one and the jurisdiction figure from seventeen to eighteen; the country figure moves from fourteen to fifteen. The other Länder will each add a register and a source as they land, and the country figure will not move again.
The listing text cannot be left out, so it is kept. propertyName on this
GeoServer drops the geometry when it is not named and never drops
legalFoundationDocument, measured on the same 2,000-record page three ways
(with everything 3.55 MB, "light" 5.12 MB because the geometry came back more
verbose, no geometry 2.33 MB). So a page over central Frankfurt is about 1 MB
gzipped for the 2,000 records that survive the shared cap, against 3,200
matched at z11. What that pays for is the prose itself: median 493
characters, and 1,703 of 2,000 records state a year or a century in it, which
is where Hessian dating will come from.
The archaeological type is not read. archaeologischesDenkmal holds
41,387 records, and over a 0.8° box around Frankfurt all 2,000 of a page
carried one and the same text, the statutory notice "Bodendenkmal nach § 2
Abs. 2 HDSchG. Im Umkreis von 300 m ist mit Bodendenkmälern zu rechnen", a
code such as "FFM-Zentrum 37" for a name, no type, no period and no link
(publicationSource null on all 2,000). A pin would say only that something
protected lies here. Measured and rejected 5 September 2026; the Hessian
figure understates the register by that much.
siteName is four columns in one. Kreis, Gemeinde, Ortsteil and name,
joined by underscores, on all 2,000 sampled; the name is a street address on
1,806 of them. The fetcher splits it, and keeps the Ortsteil only when it is
not the Gemeinde.
Berlin's WFS has no names, and the list has no coordinates. The WFS
carries four fields, gisid, id, link and typ, and nothing a pin
could be labelled with. The Landesdenkmalamt's Denkmalliste, a 1.6 MB text
file on berlin.de, carries "Ackerstraße 1B, Mietshaus, 1878 (D)" for each
object number, and the two join on that number at 8,307 of 8,314. So Berlin
is one static file rather than a live call: the WFS answers polygons only,
about 5 MB a page, and 8,314 objects is the whole city. The generator pages
the WFS with startIndex and sortBy=id, which works although the
capabilities say ImplementsResultPaging has no value, and pools the parts
of one object before taking a centroid, because the 9,578 features the WFS
reports are rows and not records. It refuses to write fewer than 8,000
rows, a join under 90%, a file smaller than the last, or a link off the
daobj.php?obj_dok_nr= pattern, which is why the link is not stored.
The list line is parsed, and the parser was measured. Address, name,
dating and architect come out of "Address, Name, 1878 von Architect;
Umbau 1949": 7,724 of 8,314 carry a dating in 1,470 distinct forms, 5,834
an architect, 136 no name because the address is the name ("U-Bahnhof
Kochstraße"), and a first version lost 753 names by taking a house-number
continuation such as "4-5a" for the dating. The dating is kept as written;
reading "um 1300" and "Mitte 18. Jh." into years is the timeline's job
(docs/TYSKLAND.md §6).
Niedersachsen is read live and nothing is written down, because of the
licence. CC BY-SA 4.0 binds derived datasets, so there is no static file
and no snapshot; every record the app shows is fetched from the API in the
session that shows it. The API does not gzip and answers polygons only: a
2,000-record page over Hannover is 4.6 MB with every property and 2.5 MB
with the nine the pins need, of which the polygons are 815 bytes a record
and cannot be dropped. The listing text, 1.1 kB on the half that have one,
is what the property selection leaves out, and it is fetched per record on
click. properties= has to name geometry or the features come back
without one, and id or they come back without their feature id (836
features, none with an id, until it was named); the CQL filter parameter answers 400 and the plain
fachbereich= parameter is what filters. limit accepts 5,000. bild.href
is a viewer page and not an image, so it is not used.
The archaeology vocabulary is the register's own. Over a 1.5° box:
Grabhügel 1,558 of 2,000, Grabhügelfeld 137, Burg 60, Landwehr 53,
Wallanlage, Wölbacker, Kreuzstein, Wüstung, Ringwall. objekttyp for
buildings is a thesaurus path ("Siedlungswesen ➔ Häusergruppe") and the
leaf is the type. siteName ends in the address for 1,659 of 1,919
buildings and in a name for 260; the fetcher takes the name when the last
segment is not the address, carries no digit and is not part of the Gemeinde
string.
Hamburg is a file, for three measured reasons. The host does not gzip,
prints its JSON with indentation, and answers footprints only, so a
2,000-record page over the centre is 6.5 MB and propertyName is ignored
whenever geometry is asked for. The whole city is 12,289 gebaeude and pages
at 10,000, so the generator reads it in two calls. Rows are not records here
either: 14,892 features carried 13,836 fisids, a building in several
footprints being several rows, and the parts are pooled. The info line
("Schöne Aussicht 14, Villa Keitel (Villa / 1898 / Entwurf: Haller,
Martin)") is what carries the address and the architect; name, type and
year are fields of their own. 317 records marked NK, listed only as part
of an ensemble, are left out. The archaeology type's geo+json answer is cut
off after the first feature's id, with or without a bbox, so it is read as
GML; its epoche_beginn (31 distinct values, Mittelalter Hamburg 1,067,
Neolithikum 375) is what dates the site. No record has a page of its own;
the link is the dataset on the Transparenzportal.
Sachsen answers GML and nothing else, and is read anyway. The server
gzips, so a 2,000-polygon page over central Dresden is 946 kB on the wire
for 4.6 MB of XML, parsed in the browser with DOMParser. It reports
numberMatched="unknown" and numberReturned="2147483647", so the members
are what is counted. The bbox must be the urn form with latitude first; the
short form answered nothing. Rows are not records: 2,000 members carried
1,444 distinct obj_nr, a Sachgesamtheit lying in several polygons, and
the fetcher pools them. The three row counts above are therefore rows, and
the statewide object count is not yet measured; Sachsen is kept out of the
grand total until it is. The report link
(Denkmalliste_Report.aspx?HIDA_Nr=) answers a PDF per object and was
found through the LfD's own Denkmalliste, not through the WFS.
The German dating forms are on the timeline, and the prose is not.
utils/yearRange.ts::yearsFromDating learned the shapes the Länder write on
5 September 2026, measured over the 21,303 filled dating strings in the
Berlin and Hamburg files: a bare year, "1912-13" with the century left off
the end, "1919 / 1920", "1885, um", "um 1895", "18. Jh.", "19. Jh., Ende",
"1. Hälfte 19. Jh.", "1920er Jahre", "nach 1918", and Sachsen's
"Kern 1749 (Kirche); nach 1955", where the earliest clause is the building
and everything after a semicolon is later work. Coverage went from 96.4 to
100 per cent in Berlin and 94.1 to 99.8 in Hamburg, and nothing changed in
the Irish and Northern Irish files, the two non-German corpora carrying the
same field. 92.7 per cent of Berlin's buildings and 99.6 per cent of
Hamburg's now carry a timeline span, median one year wide.
Hessen's dating stays in the prose, measured and rejected. The listing text yields a year for 92.2 per cent of the 1,957 records on the Frankfurt page, and a rule that only accepts a year standing next to a building verb (erbaut, errichtet, Bauzeit) yields one for 26.6 per cent. Both are wrong often enough to matter on the pin somebody opens: the Hölderlin monument gets the poet's year, the Taunus crossing gets a year about the road, and Wilhelmsbad, whose text says "in den Jahren 1777 bis 1785", gets 1743 from an earlier sentence. A displayed "Erbaut" row has to be the register's statement, not the app's reading of a paragraph, so Hessen and Niedersachsen stay undated until their registers carry a date field.
Germany reaches five cross-border layers, three of them through tiles. Measured against WDQS on 5 September 2026, and the split is decided by what each layer's own query can carry:
| Layer | German records | How |
|---|---|---|
| Stolpersteine | 47,238 | public/data/de-stolpersteine/, tiles |
| Castles, palaces, monasteries, forts | 19,081 (8,967 Burgen, 4,923 Schlösser, 4,777 Klöster, 414 Festungen und Kastelle) | public/data/de-monuments/, tiles |
| Churches, chapels, cathedrals | 57,133 | public/data/de-churches/, tiles |
| Museums | 7,215 | public/data/de-museums/, tiles |
| Megaliths | 3,196 | added to the existing query, with wd:Q1129743 |
| UNESCO | 31 | added to UNESCO_STATES |
| Historical events | ~350 | added to the country filter |
The churches are the largest of them, and the reason is the pattern they break. Every other country's churches are one Overpass call for the whole country, held in memory for the session; the Netherlands is the biggest of those at 5,033 elements and 739 kB. Germany holds 57,133 churches, chapels and cathedrals with a coordinate in Wikidata, measured 8 September 2026, which is ten times that and would be handed to every visitor. They are quarter-degree tiles instead: 826 files, 4.38 MB in all, a median of 46 records a tile, and a city viewport touches four of them. Overpass would answer with more, since OSM maps buildings the encyclopaedia does not, but Overpass also answers with nothing at all while it is refusing a machine, which it was on 8 September.
The Roman forts came in through the fort category. wd:Q88205, the
castrum, holds 360 items in Germany, every one of them named "Kastell …", and
they are the Limes. The app's monument categories have no word for a Kastell,
so they are written as fort beside the 56 Festungen, which is why that
number moves from 56 to 414. Wikidata dates almost none of them: four of the
360 carry an inception and one an end date, so they arrive undated. A timeline
that named the building of Saalburg and the fall of the Limes would need a
hand-dated table like the Orkney one, and that is what is missing, not the
pins.
The first three are tiled because their queries cannot take Germany. The
museum one says so in its own header: twelve countries already match 9,387
museums against a row limit of 8,000, and the six Wikipedia OPTIONALs mean
8,000 rows carry 5,349 museums. The monument layer asks Overpass for four
kinds across nine countries in one call. The megalith query had room, 605
rows before and 3,903 after, under a limit raised from 5,000 to 8,000 — and
it answered zero for Germany until wd:Q1129743, the Großsteingrab, was
added: the class German megalithic tombs are actually filed under.
Niedersachsen answers wrong about one request in three. Measured 12 Sep
2026: the same URL answered 200, 404, 200 within a second, on every
discipline and with or without the properties list. It is not a schema
change and not an outage, so niPage asks a second time after a non-400,
and only a repeat failure reaches the layer. The weekly health run caught
it as an empty heritage DE-NI; before the retry, a pan that landed on the
bad third showed no Niedersachsen at all until the user moved again.
Schleswig-Holstein is a register with no geometry, joined to one. The
Landesamt für Denkmalpflege publishes the whole Denkmalliste as open data
under CC BY 4.0, fourteen files, one per Kreis and kreisfreie Stadt,
rewritten weekly; measured 8 September 2026 they hold 11,603 objects. What
they do not hold is a coordinate, and the DISH map service answers 404. The
office's own object number is in Wikidata as P6102, "Denkmalliste
Schleswig-Holstein Objektnummer", 7,068 of them with a coordinate, and
7,019 join, which is 60 per cent of the list. The other 4,584 are in the
register and not on the map, and that is the honest state until the office
publishes geometry.
What the register gives that the Wikidata reserve could not: the office's own
name for the object, its Kulturdenkmaltyp, the street address on 99 per
cent, the Begründung for listing it on 78 per cent, and a photograph on 77
per cent, served from the same portal and CORS-open. Where both answer for
the same building the register wins, matched on the Wikidata item behind the
object number, so the pin is not doubled.
The dating is the register's own statement, not a reading of its prose.
Beschreibung is written "Name; Dating; description", and field two starts
with a date on 3,872 of the 11,603 — "1890-1897", "um 1907", "1. Hälfte 19.
Jh." — which utils/yearRange.ts already reads. On the 7,019 that join it is
1,311. Anything else is left undated rather than guessed at, which is the
distinction the Hessen prose failed (see the Germany row in §1).
Lübeck is not in it. The city is kreisfrei and publishes its own list, as XLSX only, so its buildings still come from the Wikidata reserve.
The nine Länder without a service are read from Wikidata. Measured on
5 September 2026 and reproduced: 234,054 objects carry a coordinate and one
of the fourteen designations those Länder use, Schleswig-Holstein's among them
until its own register landed on 8 September, and fetchGermanWikidataBuildings
in src/api/germany.ts asks WDQS for them one viewport at a time.
| Land | Objects with a coordinate | Designation |
|---|---|---|
| Bayern | 195,600 | Baudenkmal 144,765 + Bodendenkmal 49,962 + Ensemble 873 |
| Nordrhein-Westfalen | 13,879 | Baudenkmal 11,834 + Kulturdenkmal 1,960 + Bodendenkmal 85 |
| Schleswig-Holstein | 6,927 | Kulturdenkmal |
| Baden-Württemberg | 3,927 | Kulturdenkmal |
| Brandenburg | 3,418 | Baudenkmal |
| Thüringen | 2,871 | Kulturdenkmal |
| Sachsen-Anhalt | 2,750 | Kulturdenkmal |
| Bremen | 2,281 | Kulturdenkmal |
| Mecklenburg-Vorpommern | 1,849 | Baudenkmal |
| Rheinland-Pfalz | 1,514 | Kulturdenkmal |
The designation is the gate, not a box. The obvious way to keep the
reserve off the six Länder that answer for themselves is to drop every row
inside a register's bounding box, and it is wrong: Niedersachsen's box holds
Bremen, Schwerin and half of Nordrhein-Westfalen, and Bremen went from 154
buildings in a city viewport to 5. Baudenkmal in Bayern says which list an
object stands on and no box is needed. Two designations say nothing —
Baudenkmal (Q811165) and Kulturdenkmal in Deutschland (Q11691318), used in
every Land — and those two, and only those two, are measured against the
register boxes; they carry 78 of the 126 objects in a Stuttgart viewport, so
dropping them outright would cost more than it saves.
Ehemaliges Baudenkmal in Bayern is deliberately not asked for: it names
objects struck off the list.
Measured in the browser on 5 September 2026, pins rendered in a city viewport at zoom 12, before and after: München 152 → 544, Köln 101 → 267, Bremen 134 → 277, Magdeburg 66 → 170, Potsdam 96 → 185, Kiel 44 → 107, Erfurt 23 → 91, Stuttgart 194 → 266, Mainz 118 → 148, Schwerin 28 → 56. The six register Länder do not move, which is the check that the designation gate holds.
The query costs 235 kB gzip for München's 7,581 objects and two to six seconds
cold, a quarter of a second once WDQS has that URL cached; elsewhere it is
under a second. The street address (P6375, on 96.3 per cent of them) costs
nothing measurable and is what keeps the pins apart: a Wikidata label here is
the Land's descriptor, and 2,627 of those 7,581 objects are called
Mietshaus. The name is written the way Berlin's register writes its own
line, Mietshaus, Türkenstraße 5. It declines to run at all above 1.2° of longitude or 0.9° of
latitude, because Bayern alone would answer with more than the map can draw.
This is a stand-in and not a register: a Wikidata item rarely carries the
listing text, and the day a Land opens a service its own register replaces it.
It rarely carries a year either. P571 is present on 4,728 of the 144,765
Bavarian Baudenkmäler, 3.3 per cent, so most reserve pins reach the timeline
undated. That is the same shortfall measured for building years everywhere
else in Wikidata, and it is a reason to keep asking the offices.
The map could not reach half of Germany. maxBounds stopped at 49°N,
which was the south of England when the app ended at the British Isles. A
deep link to Munich (48.14) landed the camera at 49.02, and every register
and layer south of that line was unreachable. Moved to 47°N on 5 September
2026, which clears Oberstdorf at 47.27.
Saarland's register line is parsed like Berlin's. text_1 reads
"Address, Name, Dating von Architect (Status)", with a cadastral "Flur 9,
Flurstück 2532/24" pair on 374 of 3,337 that is dropped; 2,589 name a year
or a century and 1,008 an architect. The three Bodendenkmale are not read.
Iceland (IS)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Minjastofnun heritage layers | Minjastofnun Íslands (Cultural Heritage Agency of Iceland), hosted on gis.is | Fornleifaskrá (archaeological register), protected buildings, garðar, industrial heritage | WFS (gis.is/geoserver/Minjastofnun/ows) |
Icelandic public open data | src/api/iceland.ts |
| Verndarsvæði / friðlýst svæði | Minjastofnun Íslands | 18 protected cultural environments | Static snapshot | Public data | src/api/townscape.ts; generator scripts/enrich-is-townscapes.mjs |
| Sagnagrunnur | Stofnun Árna Magnússonar (Árni Magnússon Institute), via ismus.is | 9,167 Icelandic folk legends with coordinates | Static snapshot from REST API (ismus.is/django/api/folklore) |
Public data | src/api/folklore.ts; generator scripts/fetch-sagnagrunnur.mjs |
| Icelandic Saga Map (DataARC) | DataARC / CASTU consortium | 8,748 saga place references from 37 sagas | GeoJSON over HTTPS (raw.githubusercontent.com/castuofa/dataarc-source) |
CC BY-NC — see Appendix | src/api/folklore.ts |
| SagaMap | Háskóli Íslands (University of Iceland) | Saga manuscript links used as record provenance | Referenced per record from the DataARC payload | Academic release | src/api/folklore.ts |
Greenland (GL)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| NunaGIS Nunniffiit + Cultures | Asiaq / Greenland National Museum and Archives (Nunatta Katersugaasivia Allagaateqarfialu) | ~5,499 archaeological sites, Norse settlements (layer 10), Thule-culture sites (layer 14), whaling stations (layer 7) | ArcGIS REST, live per-viewport (kort.nunagis.gl/server/rest/services/Nunniffiit) |
Greenlandic public open data | src/api/greenland.ts |
| NunaGIS protected cultural environments | Asiaq / Greenland National Museum | 4 protected cultural environments | Static snapshot (townscapes-gl.json) |
Public data | src/api/townscape.ts |
| Knud Rasmussen / Hinrich Rink collections | Public-domain published ethnography | 28 Greenlandic myths and legends | Curated static snapshot | Public domain | src/api/folklore.ts |
England (GB-ENG)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| National Heritage List for England (NHLE) | Historic England | Scheduled monuments (layer 6), listed buildings (layer 0), registered parks & gardens (layer 7), protected wrecks (layer 9); plus baked snapshots for barrows, rock art, industrial | ArcGIS REST (services-eu1.arcgis.com/ZOdPfBS3aqqDYPUQ/.../NHLE_v02_VIEW) |
Open Government Licence v3 | src/api/england.ts, shipwrecks.ts, townscape.ts, barrow.ts; snapshots barrows-en.json, buildings-en.json, rockart-en.json, industrial-en.json, townscapes-en.json |
| Heritage at Risk Register 2025 | Historic England | 4,886 at-risk sites, used as a detail-panel badge | Static snapshot | Open Government Licence v3 | src/api/heritageAtRisk.ts |
| UK Museum Accreditation Scheme | Arts Council England | 1,306 accredited museums with addresses and governing bodies | Static snapshot | Open Government Licence | src/api/wikidata.ts::fetchHeAccreditedMuseums |
| Portable Antiquities Scheme | British Museum / Amgueddfa Cymru | Public-recorded archaeological finds near a site | REST API (finds.org.uk/database/search/results/bbox/...) |
CC BY (as claimed upstream) | src/api/museumCollections.ts::searchPAS — currently returns HTTP 403; see Appendix |
| Victoria and Albert Museum collections | V&A | Museum objects matching a site name | REST API (api.vam.ac.uk/v2) |
Open API | src/api/museumCollections.ts::searchVandA |
| Geograph Britain and Ireland | Geograph Project Ltd | Geotagged landscape photography | REST API (api.geograph.org.uk/syndicator.php) |
CC BY-SA 2.0 | src/api/enrichment.ts::fetchGeographPhotos — key-gated, inactive; see Appendix |
| Itiner-e Roman roads | Itiner-e project (peer-reviewed, Zenodo) | 1,434 Roman road segments in Britannia | Static snapshot | CC BY 4.0 | src/App.tsx (roman-roads layers) |
Scotland (GB-SCT)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Canmore | Historic Environment Scotland | National record of the historic environment, point index | ArcGIS REST (inspire.hes.scot/arcgis/rest/services/CANMORE/Canmore_Points) |
HES Open Data Licence | src/api/scotland.ts |
| HES Designations — listed buildings | Historic Environment Scotland | 5,000 Category A/B/C listed buildings | Static snapshot | HES Open Data Licence | src/api/scotland.ts::fetchScottishBuildings |
| HES Gardens & Designed Landscapes | Historic Environment Scotland | 371 designed landscapes | Static snapshot | HES Open Data Licence | src/api/townscape.ts |
Wales (GB-WLS)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Cadw designations | Cadw (Welsh Government), via DataMapWales | Scheduled ancient monuments (inspire-wg:Cadw_SAM), listed buildings (inspire-wg:Cadw_ListedBuildings) |
WFS 2.0 (datamap.gov.wales/geoserver/inspire-wg/wfs) |
Open Government Licence | src/api/wales.ts |
| NMRW terrestrial sites (Coflein) | RCAHMW, via DataMapWales | National Monuments Record of Wales terrestrial sites (geonode:rcahmw_nmrw_terrestrialsites) |
WFS 2.0 (datamap.gov.wales/geoserver/geonode/wfs) |
Open Government Licence v3; Crown Database Right RCAHMW | src/api/wales.ts |
| NMRW maritime sites (Coflein) | RCAHMW, via DataMapWales | National Monuments Record of the Sea; 5,947 wrecks out of 8,612 maritime records (geonode:nmrw_maritimesites_rcahmw_wgs84), filtered server-side on nmrw_herit='WRECK' |
WFS 2.0 (datamap.gov.wales/geoserver/geonode/wfs) |
Open Government Licence v3; Crown Database Right RCAHMW | src/api/wales.ts::fetchWelshShipwrecks |
| Conservation areas | The 25 Welsh local planning authorities, via DataMapWales | 529 conservation areas (geonode:conservation_areas_wales) |
WFS 2.0, baked at build time | Open Government Licence | scripts/fetch-townscapes.mjs, public/data/townscapes-wa.json |
| Register of Historic Parks and Gardens | Cadw, via DataMapWales | 385 registered parks and gardens with grade, site type and main phase (geonode:cadw_rhpg_registeredareas) |
WFS 2.0, baked at build time | Open Government Licence | scripts/fetch-townscapes.mjs, public/data/townscapes-wa.json |
Three layers, one new source. Cadw's parks-and-gardens register and RCAHMW's maritime record are further datasets from bodies already counted, the same call the Netherlands and Latvia rows record, so they add layers and not sources. The conservation areas do add one: they are designated by the 25 Welsh local planning authorities, who are a different data-providing body that DataMapWales only publishes for. That is what moved the headline from 78 to 79 on 20 August 2026.
Not read, and why, so nobody adds them back. inspire-wg:Cadw_HistoricLandscapes
(58) has a median span of 15.2 km and a largest of 61.7 km; those are regions,
and a centroid pin would be wrong in everything that measures a distance.
geonode:cadw_guardianshipmonuments (133) shares sm_number with
Cadw_SAM.SAMNumber on 131 of them, so the pins are already drawn.
geonode:rcahmw_historicplacenames_wales_recordednames (666,308) is six times
the whole terrestrial layer and belongs in the search index, not on the map.
The 2,665 non-wreck maritime records are seascapes, aircraft, findspots, fish
traps, landing points, navigation aids and anchorages.
Northern Ireland (GB-NIR)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Historic Environment Division GIS Data | Department for Communities (DfC), Historic Environment Division | Monuments, listed buildings, industrial heritage, wrecks, battlefields | ArcGIS REST (services2.arcgis.com/BdBkthNLO9mzGAMO/.../Historic_Environment_Division_GIS_Data) |
Open Government Licence | src/api/northernireland.ts; snapshot industrial-ni.json, shipwrecks-ni.json |
Republic of Ireland (IE)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Sites and Monuments Record (SMR) + thematic layers | National Monuments Service (DHLGH) | Monuments, sweathouses, holy wells, stone circles, prehistoric rock art, monuments-to-visit | ArcGIS REST (services-eu1.arcgis.com/HyjXgkV6KGMSF3jt/...) |
CC BY 4.0 | src/api/ireland.ts, rockart.ts, barrow.ts, detailScrapers.ts |
| NIAH — National Inventory of Architectural Heritage | NIAH (DHLGH) | Protected/recorded buildings with appraisals | ArcGIS REST (.../NIAHBuildingsOpenData) |
CC BY 4.0 | src/api/ireland.ts, detailScrapers.ts; snapshot buildings-ie.json |
| Wreck Inventory of Ireland Database | National Monuments Service, Underwater Archaeology Unit | 3,564 Irish shipwrecks | ArcGIS REST (services3.arcgis.com/ArIWe98F0BECP9pn/.../Wrecks) |
CC BY 4.0 | src/api/shipwrecks.ts::fetchIrishShipwrecks |
Faroe Islands (FO)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Fornminni | Tjóðsavnið (Faroese National Museum) | 456 jarðføst fornminni | ArcGIS REST (services2.arcgis.com/tejdLVdIEWVbaZae/.../Fornminni) |
Open, token-free | src/api/faroe.ts::fetchFaroeseHeritage |
| Friðaðir bygningar | Tjóðsavnið | 113 listed buildings | ArcGIS REST, same organisation | Open, token-free | src/api/faroe.ts::fetchFaroeseBuildings |
| Krígsminni (Suðuroy + Vágar) | Tjóðsavnið | 125 traces of the 1940–45 British garrison | ArcGIS REST, two services | Open, token-free | src/api/faroe.ts::fetchFaroeseWarHeritage |
| Coast Watch | Tjóðsavnið / Jon S. Hansen | 38 civilian wartime lookout posts | ArcGIS REST, same organisation | Open, token-free | src/api/faroe.ts::fetchFaroeseCoastWatch, appended to the war heritage layer |
| OpenStreetMap (Faroese extract) | OSM contributors | 66 churches and chapels | Static snapshot from Overpass | ODbL | src/api/overpass.ts::fetchChurchesByCountry; generator scripts/prefetch-churches.mjs |
Tjóðsavnið's register was connected on 15 August 2026 (59e8367). Before that
Faroese coverage was 66 OSM churches, and three paragraphs of this file still
said so until 18 August — including the sentence "No Faroese national heritage
register is integrated", which was published on /data-sources for three days
after it stopped being true. public/data/townscapes-fo.json is still an empty
array despite the comment in src/api/townscape.ts claiming 80 records.
Isle of Man (IM)
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| OpenStreetMap (Manx extract) | OSM contributors | 120 churches, 4 runestones/crosses | Static snapshot from Overpass | ODbL | src/api/overpass.ts; generator scripts/prefetch-overpass-gb.mjs |
No Manx national heritage register is integrated. Manx National Heritage
is named in src/components/AboutModal.tsx as a data source but appears
nowhere in fetch code — that claim should be corrected. Isle of Man coverage
is OSM plus Open Plaques (which includes IM) plus Wikidata/Wikipedia.
2. Cross-border and pan-European sources
| Source | Operator | What it provides | Access method | Licence | Fornland module |
|---|---|---|---|---|---|
| Wikidata | Wikimedia Foundation | Megaliths, museums, UNESCO sites, historical events, archaeological sites, shipwrecks, hoards, fortifications, inscribed stones, lighthouses, ship details, entity graph | SPARQL (query.wikidata.org/sparql) + baked snapshots |
CC0 | src/api/wikidata.ts, archaeological.ts, megalith.ts, graph.ts, enrichment.ts, museumCollections.ts |
| Wikipedia (da, no, sv, fi, is, en, de, fr) | Wikimedia Foundation | Article summaries, geosearch, section text, full-article reader | REST v1 + MediaWiki Action API | CC BY-SA | src/api/enrichment.ts, wikiArticle.ts |
| Wikimedia Commons | Wikimedia Foundation | Geosearched and category-based site photography with credit/licence metadata. Also the 339 pictures in the curated walks' stop galleries, read at build time from 45 named categories and their subcategories, of which 183 predate 1970 and the oldest is a 1550 engraving | MediaWiki Action API (commons.wikimedia.org/w/api.php) |
CC0 / CC BY / CC BY-SA per file, filtered per file and recorded per picture | src/api/enrichment.ts::fetchCommonsNearby, fetchCommonsCategoryImages; scripts/walkMedia.mjs for the walk galleries |
| OpenStreetMap via Overpass API | OSM contributors; Overpass hosted by overpass-api.de | Churches, monuments, museums, battlefields, runestones, memorial plaques, wrecks, Greenlandic buildings, routes | Overpass QL (live for Nordics, baked snapshots for GB/IE/IM) | ODbL | src/api/overpass.ts, plaques.ts, wikidata.ts, greenland.ts, shipwrecks.ts |
| Nominatim | OSM Foundation | Forward and reverse geocoding for address search | REST API (nominatim.openstreetmap.org) |
ODbL | src/hooks/useSearch.ts, src/components/ExploreAddressInput.tsx, src/App.tsx |
| Europeana | Europeana Foundation | Digitised heritage objects, provider facets, agent/person entities | REST API (api.europeana.eu) |
Metadata CC0; content licences vary by contributing institution | src/api/enrichment.ts::fetchEuropeana, fetchEuropeanaEntity |
| UNESCO World Heritage + Global Geoparks | UNESCO | World Heritage Sites and Geoparks in scope, with inscription years | SPARQL via Wikidata + data.unesco.org references |
Public data | src/api/wikidata.ts |
| Atlas of Hillforts of Britain and Ireland | Ralston & Lock (Univ. of Oxford / Univ. of Edinburgh), via ADS | 4,147 hillforts with archaeological descriptions | Static snapshot (DOI 10.5284/1118744) | CC BY-SA 4.0 | src/App.tsx (hillfort layer), src/api/enrichment.ts |
| Open Plaques | Open Plaques project | 17,371 commemorative plaques across GB, IE and IM with inscriptions | Static snapshot from monthly S3 GeoJSON dump | PDDL 1.0 (public domain) | src/api/plaques.ts; generator scripts/fetch-open-plaques.mjs |
3. Presentation infrastructure (not heritage data, not counted)
| Provider | What it provides | Licence |
|---|---|---|
CARTO basemaps (basemaps.cartocdn.com) |
Vector basemap styles and glyphs (Voyager, Dark Matter) | CC BY 3.0 |
Esri World Imagery (server.arcgisonline.com) |
Satellite imagery layer | Esri terms; Maxar, Earthstar Geographics and contributors |
| Mapzen / AWS Terrain Tiles | DEM tiles for the 3D terrain toggle | Public domain |
| ipapi.co | Coarse IP-based country detection for initial UI language | Free tier |
4. Excluded by the counting rule
- XML namespaces appearing in WFS/GML payloads and filter documents:
schema.org,www.opengis.net,www.w3.org,www.georss.org. These are schema identifiers, never fetched. - Fornland's own infrastructure:
www.fornland.com, the project's Supabase instance (bzaapmotlajmefgdkfnh.supabase.co), andapi.anthropic.com(used byapi/history.tsto generate narrative text — a generative service, not a heritage register). - Generic CORS relays:
api.allorigins.win, used only to reach kyppi.fi, trove.scot and other origins that omit CORS headers. The data source is the origin behind it, and is counted there. - Code-hosting mirrors:
raw.githubusercontent.comis the transport for the DataARC saga corpus and the SKVR XML volumes; both are counted under their actual academic operators.
5. Link-out targets — displayed, never fetched
These URLs are written into sourceUrl fields so users can open the
authoritative record. Fornland does not request data from them. They must
not be counted as data sources.
| Target | Role | Where it appears |
|---|---|---|
kulturminnesok.no (public site) |
Human-readable Norwegian record page. Link-out only — Fornland fetches Norwegian data from Geonorge WFS and detail from the separate kms-api.kulturminnesok.no API. |
src/api/heritage.ts, buildings.ts, townscape.ts |
historicengland.org.uk/listing/the-list/list-entry/<id> |
Official NHLE list entry page | src/api/england.ts, snapshots |
trove.scot/site/<canmoreId>/ |
Canmore public record page (successor to canmore.org.uk). Scraped opportunistically for description text via the CORS relay, but never the primary data path. | src/api/scotland.ts, detailScrapers.ts |
portal.historicenvironment.scot/designation/<id> |
HES designation record page | buildings-sc.json, townscapes-sc.json |
heritagedata.maps.arcgis.com/apps/webappviewer/...&query=<layer>,SMRS,<smrs> |
Irish SMR record, opened in the NMS Historic Environment Viewer. The obvious-looking archaeology.ie/smr-record/<smrs> is a 404 for every record and maps.archaeology.ie/HistoricEnvironment/?SMRS=<smrs> loses its query on redirect; see src/api/irishSmr.ts |
src/api/ireland.ts, rockart.ts, barrow.ts |
heritageireland.ie, discoverireland.ie and ~13 more |
Visitor pages for the 100 curated monuments-to-visit, taken from the register's own external_link column |
src/api/ireland.ts |
buildingsofireland.ie/building/<regNo>/ |
NIAH public record page | src/api/ireland.ts |
apps.communities-ni.gov.uk/NISMR-PUBLIC/Details.aspx?MonID=<id> |
NI Sites & Monuments Record page | src/api/northernireland.ts |
cadw.gov.wales, coflein.gov.uk/en/site/<nprn> |
Cadw and RCAHMW record pages | src/api/wales.ts |
kyppi.fi/to.aspx?id=112.<id> |
Finnish register record page (scraped for description only, via relay) | src/api/finland.ts |
monumentenregister.cultureelerfgoed.nl/monumenten/<rijksmonumentnummer> |
Dutch national monument record page. The register is a single-page app, so an HTTP 200 says nothing about whether the number resolves; the key is the rijksmonumentnummer and not the entity URI | src/api/netherlands.ts |
www.kulturarv.dk/fundogfortidsminder/Lokalitet/<systemnr>, .../fbb/bygningvis.pub |
Danish record pages (the FoF one is also scraped for detail text) | src/api/heritage.ts, buildings.ts |
whc.unesco.org/en/list/<id>, unesco.org/en/iggp/geoparks |
UNESCO inscription pages | src/api/wikidata.ts |
openplaques.org/plaques/<id> |
Plaque record page | src/api/plaques.ts |
openstreetmap.org/<type>/<id> |
OSM element page | src/api/overpass.ts, greenland.ts |
artscouncil.org.uk/.../uk-museum-accreditation-scheme |
Scheme description page | src/api/wikidata.ts |
skvr.fi, sagamap.hi.is/is/, etkspace.scandinavian.ucla.edu/danishfolklore/, sagnagrunnur.arnastofnun.is/en/legend/<id> |
Folklore corpus project homepages / record pages | src/api/folklore.ts |
gis.is/, kort.nunagis.gl/, minjastofnun.is, plandata.dk, raa.se |
Portal homepages used as generic provenance links on snapshot records | src/api/iceland.ts, townscape.ts |
Danish regional museum sites (moesgaardmuseum.dk, romu.dk, vikingeskibsmuseet.dk, museum-sonderjylland.dk and ~18 more) |
Static lookup table mapping a museum name to its homepage, for the "responsible museum" line in Danish detail panels | src/api/enrichment.ts::lookupMuseumUrl |
ironbridge.org.uk, newlanark.org, saltairevillage.info |
Curated industrial-heritage site homepages | public/data/uk-industrial.json |
6. Countries and territories covered
Seventeen, per src/types/index.ts::Country plus the England/Scotland/Wales/
Northern Ireland split that Fornland treats as separate register domains.
| # | Territory | ISO | National register integrated? | Coverage mode |
|---|---|---|---|---|
| 1 | Denmark | DK | Yes — Fund og Fortidsminder, FBB | Live WFS per viewport; townscapes + industrial from snapshot |
| 2 | Norway | NO | Yes — Askeladden via Geonorge | Live WFS per viewport through own edge proxy; protected buildings complete from baked tiles (public/data/no-bygg/, all 80,657, live proxy only above 36 tiles); townscapes from snapshot |
| 3 | Sweden | SE | Yes — Kulturmiljöregistret, BeBR (via K-samsök) | Live API per viewport; riksintressen from snapshot |
| 4 | Finland | FI | Yes — Museovirasto | Live WFS per viewport; RKY from snapshot |
| 5 | Iceland | IS | Yes — Minjastofnun Íslands | Live WFS per viewport; folklore + townscapes from snapshot |
| 6 | Greenland | GL | Yes — NunaGIS (Greenland National Museum) | Live ArcGIS REST per viewport, verified working 7 Aug 2026; buildings via Overpass; townscapes from snapshot (4 records) |
| 7 | England | GB-ENG | Yes — Historic England NHLE | Live ArcGIS REST per viewport; barrows/rock art/industrial/parks from snapshot |
| 8 | Scotland | GB-SCT | Yes — Canmore / HES | Canmore live ArcGIS REST; listed buildings and designed landscapes from snapshot |
| 9 | Wales | GB-WLS | Yes — Cadw + RCAHMW via DataMapWales | Live WFS per viewport |
| 10 | Northern Ireland | GB-NIR | Yes — DfC Historic Environment Division | Live ArcGIS REST per viewport; industrial + wrecks from snapshot |
| 11 | Republic of Ireland | IE | Yes — National Monuments Service + NIAH | Live ArcGIS REST per viewport; buildings + wrecks from snapshot |
| 12 | Estonia | EE | Yes — Muinsuskaitseamet | Live WFS per viewport, monuments, conservation-area buildings and historic green areas on separate layers |
| 13 | Latvia | LV | Yes — NKMP | Live WFS per viewport; dating from a committed CC0 lookup |
| 14 | Lithuania | LT | Yes — KVR / Kultūros paveldo departamentas | Live ArcGIS REST per viewport, points and polygon centroids on separate layers |
| 15 | Netherlands | NL | Yes — Rijksdienst voor het Cultureel Erfgoed (since 19 Aug 2026) | Live SPARQL per viewport over RCE's linked open data; townscapes from snapshot (472 records) |
| 16 | Faroe Islands | FO | Yes — Tjóðsavnið (since 15 Aug 2026) | Live ArcGIS REST per viewport across four services; plus a static OSM snapshot (66 churches) |
| 17 | Isle of Man | IM | No | Static OSM snapshot (120 churches, 4 crosses) + Open Plaques + Wikidata |
| 18 | Germany | DE | Yes — Hessen, Berlin, Niedersachsen, Hamburg, Sachsen, Saarland (since 5 Sep 2026) and Schleswig-Holstein (8 Sep, its open-data list joined to Wikidata for coordinates); the other nine Länder from their Wikidata listings, 234,054 objects, until each opens a service, see docs/TYSKLAND.md |
Per Land: Hessen, Sachsen and Saarland live WFS and Niedersachsen live OGC API per viewport, Berlin and Hamburg one static file each; gated per Land on the register's own box |
Three defensible ways to count, depending on what "country or territory" means:
- 15 — sovereign states and dependent territories: DK, NO, SE, FI, IS, GL, GB, IE, EE, LV, LT, NL, FO, IM, DE. The strictest reading.
- 18 — the rows in the table above, treating each UK heritage jurisdiction (England, Scotland, Wales, Northern Ireland) separately. This is the honest description of the engineering: each has its own register, schema, operator and Fornland module. As of 5 September 2026 the app and this document say 18 heritage jurisdictions across 15 countries and territories (17 and 14 from 19 August, before Germany) — both halves are checkable against the table above, and neither requires an assessor to accept a judgement call. The 7 August wording was the same construction with the pre-Baltic figures, 13 and 10, the 16 August wording had 15 and 12, and 17 August had 16 and 13.
Note that the two numbers were 13 and 10 before the Baltic states, 15 and 12 after Estonia and Latvia, 16 and 13 with Lithuania, 17 and 14 with the Netherlands, and 18 and 15 with Germany; the gap of three is the UK split and does not move.
A third reading was in use before 7 August 2026 and should not come back: it counted England, Scotland and Wales separately while folding Northern Ireland into Ireland or omitting it, which gave 12 on the pre-Baltic table. It was the weakest of the three because it is internally arbitrary — the same rule applied consistently gives either 10 or 13, never 12.
Beware the coincidences, of which there are now two. The retired reading gave 12 on the pre-Baltic table; the strict reading gave 12 on the post-Baltic table and gives 13 with Lithuania — which is also what the retired reading would now produce on its own terms. A figure of 12 or 13 in old material says nothing until you know which table it came from.
Note also that Isle of Man is in all three counts and has no national heritage register behind it (see that row above). The Faroes were in the same position until Tjóðsavnið was integrated on 15 August 2026, and the sentence here still said so until 19 August. Any claim that Fornland "covers" 14 or 17 territories should be read as covering the map, not as integrating 14 or 17 national registers — that number is 26 (see Appendix D), and the one territory without one is named in the table above.
7. Snapshot freshness
Everything in §1 and §2 is fetched live, so what a visitor sees is whatever the register served at that moment. A minority of layers are pre-baked into static files in this repository instead, either because the register publishes no queryable endpoint or because the layer is small enough that a snapshot is cheaper than a request. Those files have an age, and this is it.
123 files, not 123 sources. This number counts
something different from the 86 in the headline and does not correct
it. Several files belong to one source, the townscape layer being one file per
country and Oslo Byleksikon being split into pins and texts. A few are not data
from anyone but caches Fornland derived itself, such as
enrichment-cache.json and rundata-mapping.json. A tiled layer is listed
once, by its index, with a trailing slash.
Where the date comes from. 18 of the 123 files carry a stamp their own generator wrote, saying when the data was pulled. The other 105 are dated by the commit that last touched them, which is the later of the two and therefore flatters: a file pulled in February and pushed in March is dated March here. The 123 dates fall on 34 distinct days, so they are refreshes and not one bulk commit.
Oldest first, 26 February 2026 to 20 September 2026, measured when this page was built on 21 September 2026.
| File | Refreshed |
|---|---|
battlefields.json |
2026-02-26 |
folklore-interpretations.json |
2026-02-26 |
runestones.json |
2026-02-26 |
churches-dk.json |
2026-02-27 |
churches-no.json |
2026-02-27 |
churches-se.json |
2026-02-27 |
monuments.json |
2026-02-27 |
museums.json |
2026-02-28 |
history-dk.json |
2026-03-01 |
history-no.json |
2026-03-01 |
history-se.json |
2026-03-01 |
finnish-folklore.json |
2026-03-07 |
skvr-places.json |
2026-03-07 |
greenland-folklore.json |
2026-03-08 |
slks-industrial.json |
2026-03-08 |
townscapes-no.json |
2026-03-08 |
townscapes/ |
2026-03-08 |
kulturmiljo.json |
2026-03-09 |
townscapes-fi.json |
2026-03-09 |
townscapes-se.json |
2026-03-09 |
townscapes-dk.json |
2026-03-10 |
townscapes-fo.json |
2026-03-10 |
townscapes-gl.json |
2026-03-10 |
townscapes-is.json |
2026-03-10 |
british-folklore.json |
2026-03-19 |
monuments-gb.json |
2026-03-19 |
museums-osm-gb.json |
2026-03-19 |
plaques-gb.json |
2026-03-19 |
runestones-gb.json |
2026-03-19 |
barrows-en.json |
2026-03-20 |
churches-gb.json |
2026-03-20 |
churches-ie.json |
2026-03-20 |
heritage-at-risk.json |
2026-03-20 |
industrial-en.json |
2026-03-20 |
rockart-en.json |
2026-03-20 |
routes.json |
2026-03-20 |
battlefields-gb.json |
2026-03-21 |
enrichment-cache.json |
2026-03-21 |
irish-mythology.json |
2026-03-21 |
museums-he.json |
2026-03-21 |
rockart-ie.json |
2026-03-21 |
rockart-ie/ |
2026-03-21 |
site-narratives.json |
2026-03-21 |
stolpersteine-nordic/ |
2026-03-21 |
stolpersteine.json |
2026-03-21 |
uk-industrial.json |
2026-03-21 |
doggerland.json |
2026-03-22 |
icelandic-folklore.json |
2026-03-22 |
shipwrecks-gb.json |
2026-03-22 |
shipwrecks-ie.json |
2026-03-22 |
shipwrecks-ie/ |
2026-03-22 |
roman-en.json |
2026-03-26 |
shipwrecks-wikidata.json |
2026-03-26 |
wrecks-wd/ |
2026-03-26 |
curated-conflicts.json |
2026-03-27 |
fortifications.json |
2026-03-28 |
hillfort-descriptions.json |
2026-03-28 |
hillforts.json |
2026-03-28 |
hoards.json |
2026-03-28 |
inscribed-stones.json |
2026-03-28 |
roman-roads-curated.json |
2026-03-28 |
roman-roads-gb.json |
2026-03-28 |
rundata-mapping.json |
2026-03-28 |
icelandic-folklore-pins.json |
2026-03-30 |
icelandic-folklore-texts.json |
2026-03-30 |
oslo-byleksikon-texts.json |
2026-04-13 |
churches-im.json |
2026-05-01 |
norse-world.json |
2026-05-20 |
churches-fi.json |
2026-08-06 |
churches-fo.json |
2026-08-06 |
churches-gl.json |
2026-08-06 |
churches-is.json |
2026-08-06 |
estonian-sacred-sites.json |
2026-08-16 |
latvia-register.json |
2026-08-16 |
buildings-en.json |
2026-08-18 |
buildings-en/ |
2026-08-18 |
buildings-ie.json |
2026-08-18 |
buildings-ie/ |
2026-08-18 |
buildings-ni.json |
2026-08-18 |
buildings-ni/ |
2026-08-18 |
buildings-sc.json |
2026-08-18 |
heritage-ni.json |
2026-08-18 |
heritage-ni/ |
2026-08-18 |
industrial-ni.json |
2026-08-18 |
industrial-ni/ |
2026-08-18 |
shipwrecks-ni.json |
2026-08-18 |
townscapes-en.json |
2026-08-18 |
townscapes-sc.json |
2026-08-18 |
townscapes-nl.json |
2026-08-19 |
townscapes-wa.json |
2026-08-20 |
no-bygg/ |
2026-08-21 |
no-stedsnavn/ |
2026-08-21 |
oslo-byleksikon-pins.json |
2026-08-21 |
oslo-byleksikon.json |
2026-08-21 |
heritage-art.json |
2026-08-22 |
oslo-walks.json |
2026-08-22 |
thingsites.json |
2026-08-22 |
danish-folklore-pins.json |
2026-08-23 |
danish-folklore.json |
2026-08-23 |
etk-railways.json |
2026-08-23 |
danish-folklore-en.json |
2026-08-24 |
danish-folklore-texts.json |
2026-08-24 |
etk-walks.json |
2026-08-28 |
museums-wd/ |
2026-09-02 |
wikidata-archaeological.json |
2026-09-02 |
wikidata-battles.json |
2026-09-02 |
wikidata-events.json |
2026-09-02 |
wikidata-museums.json |
2026-09-02 |
lighthouses.json |
2026-09-03 |
monument-years.json |
2026-09-03 |
no-datering/ |
2026-09-03 |
site-dates.json |
2026-09-03 |
building-years.json |
2026-09-05 |
de-be/ |
2026-09-05 |
de-hh-arch/ |
2026-09-05 |
de-hh-bld/ |
2026-09-05 |
de-museums/ |
2026-09-05 |
de-stolpersteine/ |
2026-09-05 |
openplaques/ |
2026-09-05 |
de-churches/ |
2026-09-08 |
de-monuments/ |
2026-09-08 |
de-sh/ |
2026-09-08 |
wiki-prose.json |
2026-09-20 |
Appendix — audit findings
A. Comment vs. code disagreements
| File | Comment says | Code actually fetches |
|---|---|---|
src/api/rockart.ts:5 |
"pub.raa.se (SE via buildRaaFilter)" | Corrected 17 Aug 2026 to searchAllBedomningar → kulturarvsdata.se/ksamsok/api. pub.raa.se and buildRaaFilter no longer exist. |
src/api/industrial.ts:6-7 |
"pub.raa.se SE via buildRaaFilter" | Same — corrected 17 Aug 2026. |
src/api/shipwrecks.ts:4-5 |
"pub.raa.se (SE via shared buildRaaFilter)" | Same — corrected 17 Aug 2026. |
src/api/townscape.ts:22 |
"80 FO Kulturminder (Wikidata)" | townscapes-fo.json is [] — zero records. |
src/api/townscape.ts:21 |
"10 GL Fredet kulturmiljø + UNESCO" | townscapes-gl.json holds 4 records. |
src/api/greenland.ts:5 |
"Nunalerinermut paasissutissat" | Service path is Nunniffiit (Nunniffiit_Public + Cultures); the quoted Greenlandic name does not correspond to the service. |
src/components/LegalModal.tsx (all 4 languages) |
"ISOF … (garm.isof.se)" | ull.isof.se/folkeservice/api/es/documents/. |
src/components/LegalModal.tsx (all 4 languages) |
"Riksantikvaren / Askeladden (kart.ra.no)" | wfs.geonorge.no/skwms1/wfs.kulturminner. kart.ra.no was retired in early 2026 and is no longer contacted. |
src/components/LegalModal.tsx (all 4 languages) |
"Riksantikvarieämbetet / Fornsök (pub.raa.se)" | Corrected 17 Aug 2026 to kulturarvsdata.se, which is now the only Swedish host Fornland fetches from. app.raa.se survives as the link-out sourceUrl on Swedish pins; its REST API was never reachable from a browser (no CORS header) and the code that called it is gone. |
src/components/LegalModal.tsx (all 4 languages) |
"Historic Environment Scotland / Canmore (canmore.org.uk)" | inspire.hes.scot/arcgis/…; canmore.org.uk now redirects to trove.scot and is link-out only. |
src/components/AboutModal.tsx:231-252 |
Lists "Manx National Heritage (Isle of Man)" as a data source | No Manx National Heritage endpoint exists anywhere in the codebase. |
src/components/MethodologyModal.tsx:15 |
"fifteen national heritage databases", "twelve countries" | Corrected 16 Aug 2026 to seventeen registers across twelve countries, and again 17 Aug 2026 to eighteen across thirteen when Lithuania was added, 18 Aug to nineteen when the Faroese register was entered, and 19 Aug to twenty across fourteen when the Netherlands was added. "Fifteen" was right at one register per operating agency until Estonia and Latvia were added (see §D). The old "twelve countries" was reachable only by counting England, Scotland and Wales separately while omitting Northern Ireland — the same digit the strict reading now gives for an unrelated reason. Isle of Man is in the modal's country list and has no national register behind it; the Faroes were in the same position until 15 Aug 2026. |
None of these are functional bugs — the code works. They are documentation drift, and every one of them is the kind of thing an assessor could check in thirty seconds.
B. Sources that are integrated but not currently returning data
- Portable Antiquities Scheme (
finds.org.uk): returns HTTP 403 to server-side and browser-UA requests alike as of 7 Aug 2026. The code swallows the error and returns[], so the app degrades silently. Do not claim PAS as active coverage without re-verifying from a browser. - Geograph Britain and Ireland:
fetchGeographPhotosrequiresVITE_GEOGRAPH_KEY, which is not present in.env.local. The function returns[]immediately when the key is absent. Integrated, dormant. heritage-ni.jsonandbuildings-ni.jsonwere both empty arrays until 18 Aug 2026, so Northern Irish heritage and buildings came from the live ArcGIS service, capped at 500 records per viewport. Both are now generated in full; every Northern Irish layer has a generator.
C. Restrictive (non-open) licences
The application text claims everything is open public data. Two exceptions:
- DataARC Icelandic Saga Map — CC BY-NC. The non-commercial clause is a genuine restriction, not an open licence under the Open Definition. This supplies the saga layer (8,748 place references, one of Fornland's most distinctive datasets). Fornland is free and non-commercial, so use is compliant, but a blanket "all data is open" claim is inaccurate while this source is included. Either qualify the claim or state the exception.
- Trap Danmark — metadata is freely available but the article text is used under Danish citation right (§22), not an open licence. The code correctly limits itself to short excerpts and CC/PD images. Again compliant, but not "open data".
Additionally, Esri World Imagery (satellite basemap) is proprietary imagery under Esri's terms, not open. It is presentation infrastructure rather than heritage data, but it should not be swept into an "everything is open" sentence.
Everything else is CC0, CC BY, CC BY-SA, ODbL, PDDL, OGL v3, NLOD, or national public-sector open data — genuinely open.
D. Honest assessment: is "around 90 open public registers" defensible?
No. The highest number this audit supports is 80 distinct data sources
— this paragraph said 70 until the Baltic rows were added, 73 until the four
Tjóðsavnið services were entered on 18 August, 77 until RCE was entered on
19 August, 79 until Hessen, 80 until Berlin, 81 until Niedersachsen, 82 until Hamburg and 84 until Sachsen and Saarland were entered on 5 September, and it goes to 86 on the day the curated walks publish
Brukerminner; the Headline at the
top of the file is the maintained figure — and even that is generous in places: it counts encyclopedias
(Lex.dk, Trap, Lokalhistoriewiki, Oslo Byleksikon), museum collection APIs
(SMK, Nationalmuseet, DigitaltMuseum, Finna, V&A), photo archives (Commons,
Geograph, SHFA) and folklore corpora alongside statutory heritage registers.
If the claim is specifically "open public registers" — meaning official
statutory heritage inventories maintained by a national or devolved
authority — the defensible count is 38: Fund og Fortidsminder, FBB,
Industrihistoriens Danmarkskort, Nationale Kulturmiljøer, Plandata
kulturmiljøer, Askeladden/Kulturminner, NO Kulturmiljøer, Kulturmiljöregistret,
Bebyggelseregistret, SE Riksintressen, Museovirasto rajapinta_suojellut, RKY,
Minjastofnun fornleifaskrá, IS verndarsvæði, NunaGIS Nunniffiit, NHLE,
HE Heritage at Risk, Canmore, HES designations, Cadw, RCAHMW/Coflein, DfC HED,
NMS SMR, NIAH, Wreck Inventory of Ireland, Muinsuskaitseamet kultuurimälestiste
register, Valsts aizsargājamo kultūras pieminekļu saraksts, Kultūros vertybių
registras, Tjóðsavnið fornminnaskrá, FO friðaðir bygningar,
rijksmonumentenregister, rijksbeschermde stads- en dorpsgezichten, Hessische
Denkmalliste, Berliner Denkmalliste, Denkmalatlas Niedersachsen, Hamburger Denkmalliste, Sächsische
Denkmalliste, Saarländische Denkmalliste. Collapse
those to
one entry per operating agency (SLKS, Riksantikvaren, RAÄ, Museovirasto,
Minjastofnun, NunaGIS/Greenland National Museum, Historic England, HES, Cadw,
RCAHMW, DfC HED, NMS, NIAH, Erhvervsstyrelsen, Länsstyrelserna,
Muinsuskaitseamet, Nacionālā kultūras mantojuma pārvalde, Kultūros paveldo
departamentas, Tjóðsavnið, Rijksdienst voor het Cultureel Erfgoed, Landesamt
für Denkmalpflege Hessen, Landesdenkmalamt Berlin, Niedersächsisches Landesamt
für Denkmalpflege, Denkmalschutzamt Hamburg, Landesamt für Denkmalpflege
Sachsen, Landesdenkmalamt Saarland) and you get
26 — the figure
MethodologyModal.tsx should state, and the one public number in the app that
this audit fully supports. It said "fifteen" until the Baltic registers were
added, "seventeen" until Lithuania, "eighteen" until the Faroese register
was entered here on 18 August, "nineteen" until RCE was entered on 19
August and "twenty" until the six German Länder were entered on 5 September, each correct on its own table. The two krígsminni
surveys and the coast watch are Tjóðsavnið's too but are field surveys rather
than statutory inventories, so they are counted as sources and not as
registers.
"Around 90" appears to have no basis in the codebase and would not survive an
assessor opening src/api/. The safe, checkable formulations are:
- "85 upstream data sources across 15 countries and territories" — fully defensible against this document. Note that the 15 here is the strict sovereign-state reading of the current table; a 12 or 13 in older material could have come from either of two readings, so see the coincidence warning in §6 before comparing it to anything.
- "Around 25 official national and regional heritage registers, plus cross-border open datasets including Wikidata, Wikimedia Commons, OpenStreetMap, Europeana and Wikipedia" — the most conservative and the most robust under questioning.
- If a larger number is wanted, count layers rather than sources: Fornland runs roughly 60 distinct layer fetchers. But call them layers, not registers.
One further correction needed before any external claim is filed: do not claim Manx National Heritage — it is not integrated, and the Isle of Man is served only by an OpenStreetMap-derived snapshot, Open Plaques and incidental Wikidata coverage. Describing IM as "covered" is defensible only if "covered" means "the map shows something there." This paragraph said the same of the Faroe Islands until 18 August, three days after Tjóðsavnið was connected; the Faroese claim is now safe to make, and 732 Faroese records back it.
E. Fields the registers publish that Fornland used to discard (16–17 August 2026)
A field-by-field sweep of the live responses, sampling 400–600 records per source, against what the fetchers actually mapped. Fill rates below are from those samples, not from the whole register.
Wales — the fetcher was reading a schema that does not exist. wales.ts
declared property names nobody had verified against DescribeFeatureType, so
every scheduled monument came out typed "Scheduled Monument", dated "Udateret",
identified by its own coordinates and linked to the Cadw front page. The unit
tests encoded the same invented fixtures, so they passed while production was
wrong. The real columns are Name, SiteType (100% filled, 121 distinct
values), BroadClass, Period (100%, 10 values, all of which map through
mapRcahmwPeriod), DesignationDate, SAMNumber, RecordNumber,
Community, UnitaryAuthority and Report; listed buildings add Location
(591/600, averaging 111 characters of descriptive prose) and Grade.
Two traps worth knowing: text columns are space-padded to the database field
width, so a SAM Name arrives with 120-odd trailing spaces and must be
trimmed before comparison; and Report URLs are http and answer 301, so they
are upgraded to https before display rather than costing every reader a
redirect.
England — Esri dates are epoch milliseconds, and negative before 1970.
SchedDate (179/500) and ListDate (500/500) were being dropped because they
did not look like dates. esriYear() in england.ts converts them and
sanity-bounds the result to 1800–2100. The listed-building year is joined into
protectionStatus ("Grade II, listed 1949") rather than a field of its own,
because BuildingDetail's timeline reads the first four-digit number out of
that string — so the year earns a timeline point for free.
Iceland — the register writes "unknown" as data. óþekkt, óskilgreind,
ómetið and ekki vitað are stored as ordinary values, so they were rendering
as a pin's type and as its description. isReal() filters them. That opened up
astand (381/400), which is a visibility scale rather than a condition one —
heillegar through sést ekki — and vidhengi (353/400), which holds survey
report PDFs and gives Icelandic records a real source link instead of
gis.is. Buildings gain honnudur (architect) and umtak_fridlyst
(the protection's scope, as prose).
Northern Ireland — the address column contains literal newlines and is
stored in capitals, so it ran together into one shouted string in the pin
title. niAddress() splits, tidies and title-cases it. FormerUse was not
even requested in outFields; it now is, and CurrentUse is shown only when
it differs, which it does for a minority — the two agreed on 333 of 400
sampled records.
A correctness fix that surfacing Welsh prose forced. GB is one Country
code covering four heritage jurisdictions, and six detail panels credited their
source with t(\detail.source${country}`)— so Welsh, Scottish and Northern Irish records were all attributed to Historic England.src/utils/sourceLabel.tsmatches the record's ownsourceUrl` host against the four bodies and falls back
to the country key. Adding Welsh descriptions without this would have put Cadw's
words under Historic England's name.
Ireland — every monument pin linked to a 404, and the register's own link is
no better. archaeology.ie/smr-record/<SMRS> returns "Page not found" for
every record; it was the link on all six Irish monument fetchers. The SMR's own
WEBSITE_LI column offers maps.archaeology.ie/HistoricEnvironment/?SMRS=<key>,
which 301s to the Historic Environment Viewer and drops the query string in the
process, so the reader lands on the front door with nothing selected. Only the
Web AppBuilder query=<layer>,SMRS,<key> form opens the record and its popup.
All three answer 200, because the viewer is a single-page app — the working one
was established by loading each in a real browser and reading the rendered text.
src/api/irishSmr.ts builds that form and reads the key back out of it, which
matters because the on-click description fetch routes on the key rather than the
host. Separately, TOWNLAND is filled on 100% of records and was being fetched
and thrown away, so a county's forty "Standing stone" pins were forty identical
titles; it is now part of the name, title-cased out of the register's capitals.
WEB_NOTES was already fetched on click and remains so — at ~850 characters on
every record it would multiply the viewport payload about ninefold.
Ireland — a column filled on 151,306 of 151,308 records, and on 17,531 of
them what it says is that there is nothing to say. WEB_NOTES is the
Archaeological Survey's field description, and 11.6% of the register carries
instead a 482-character notice that the record has not been uploaded yet and an
invitation to visit the Archive Unit in Dublin on a Friday. It arrived as a
filled description, which is the field that suppresses the Wikipedia extract
in every Irish detail panel, so those monuments were showing an apology in place
of an article that was there all along. The rest of the column ends in
bookkeeping: a compiler credit (76,980), an upload date (94,782, in seven
spellings) and an image manifest with its list of filenames (471). All of it is
now stripped in src/api/irishSmr.ts::irishNotes, which the five readers of the
column share. Two things about the shape of those notes are only visible in the
data: 965 records state a preservation order under the National Monuments Acts
and on some of them it sits after the compiler credit, so cutting the note at
the credit would silently drop a legal protection notice; and on about 4% of
records the credit is a line inside the closing prose paragraph rather than a
paragraph of its own. Hence line-wise filtering rather than truncation. The
colon is what tells a credit from a citation — "Compiled by Olive Alcock, Kathy
de hÓra and Paul Gosling (Dublin: Stationery Office, 1999)" is a book, and eight
records open a sentence with "Recorded by Westropp (1905, 350-51) who
commented…". api/rockart.ts and api/barrow.ts read the column too; barrow
did not read it at all until now, and rock art wrote it through raw.
Norway — the proxy was already sending the type, and the fetcher was
overwriting it with a constant. api/no-heritage.ts parses lokalitetsart
out of the GML and passes it on as art; heritage.ts ignored it and wrote
type = "Arkeologisk minne" for every Norwegian site, so funnsted, gravminne,
bergkunst and kokegrop were one undifferentiated category in the panel, in
search and in the glossary. opprinneligFunksjon fared worse: it was mapped
straight through, which put a bare number under the heading "Funksjon". Both
arrive as SOSI codes, and the code lists live at
register.geonorge.no/api/sosi-kodelister/kulturminner/<subregister> — note the
/api/ segment, since the path without it answers 404 rather than redirecting,
and the subregister names are prefixed (kulturminnelokalitetart, not
lokalitetsart). The 159 site-type codes and 29 function codes are baked into
src/data/sosiKulturminner.ts by scripts/fetch-norwegian-lookups.mjs rather than
fetched per map pan, because a failed runtime fetch would quietly bring the
numbers back. Every art value in the sampled L-ARK bbox resolved. 9900
("Funksjon uspesifisert") is dropped rather than decoded, as it is the
register's way of writing "we do not know" and accounts for about a fifth of
the filled values.
The third bug was a consequence of the first two rather than a separate
mistake. isSamiSite(type, "NO", originalFunction) matches on words —
teltboplass, flytteveg for rein, reindrift — and it was being handed a constant
and a number, so it was structurally incapable of returning true. A Finnmark
sample in which roughly one record in nine carries a Sami site type or a
reindeer-herding function produced not one flag. Pin colour keys off period
rather than type (src/utils/geojson.ts), so giving 159 real type names to a
country that had one did not disturb the map render path. The 674 codes of
enkeltminneart were left out on purpose: enkeltminne are read for their
dating and never drawn as pins.
Norway, second pass — the municipality was a number and the detail fetch
404'd. kommune arrives as "0301", not "Oslo", and went straight into the
panel's municipality row, where it read as a typo; the 357-entry number-to-name
table is now generated alongside the SOSI lists. The on-click enrichment was
worse. fetchNorwegianDetail passed the pin's whole id to
kms-api.kulturminnesok.no/api/v2/search/, but Geonorge ids carry an
enkeltminne suffix on four records in five, and /search/134198-1 is a 404
where /search/134198 is a 200 — buildings.ts had been stripping the suffix
correctly all along. Behind that 404 sat a second dead end: the description was
read from data.features[0].properties.beskrivelse, and this endpoint has no
features key at all, on 0 of 150 sampled records. The text is at
details.text, filled on all 150. The alternative,
details.info → Askeladdenbeskrivelse, is longer because it concatenates the
locality's description with each enkeltminne's, but it arrives with <br />
markup and headings like "Beskrivelse fra lokalitet:", so the cleaner field
wins.
The same pass added two fields that were sitting unread in the GML.
høyesteVern → vernetype is filled on 1269 of 1269 sampled localities and now
fills protectionStatus; it matters more than it sounds, because FJE,
"Fjernet (automatisk fredet)", is 16% of an Oslo bbox — those sites no longer
physically exist. The pin stays, since a destroyed grave mound is still a
documented one, but the panel says so rather than implying there is something
to go and see. UAV ("Uavklart vernestatus"), the single largest value at 36%,
is dropped as a non-answer. antallEnkeltminner fills siteCount, a row that
had been translated into nine languages as "Enkeltminner" and never once
populated; it is shown only above one, since a locality of one says nothing.
Going the other way, linkAskeladden is filled on every record and is no
longer passed on at all: askeladden.ra.no is Riksantikvaren's caseworker system
and answers with a login wall.
Measured and rejected, Norway. vernedato is filled on 100% of records and
still unusable, for two reasons. It means different things depending on
vernetype — a decision date for vedtaksfredet, a registration date for
automatisk fredet, whose protection follows from the object's age rather than
from any decision — and 16.2% of it is the sentinel 1900-01-01. Rendering
either as "fredet siden" states something false. synlig is false on 38.9%,
which is a genuinely useful thing to tell a visitor, and is deferred only
because it needs a new label in nine languages.
Greenland — we were publishing records the National Museum had taken down.
NunaGIS retires a site by flagging it, not by deleting it, and its own viewer
filters the flags out client-side; ours asked for where=1=1. That put 39
withdrawn records on the map (34 ISDELETED, 5 ISREMOVED of 5,500) plus 58
Cultures records not cleared for publication (2 Norse, 56 Thule). The two layer
groups need different clauses: ISPUBLIC exists on the Public layer but is
'Yes' on all 5,500 rows, and ISDELETED/ISREMOVED do not exist on the
Cultures layers at all — naming a column that is not there is an HTTP 400 for
the whole layer, not a narrower result set.
The link was the second problem. Every Greenlandic pin pointed at
.../FeatureServer/<n>/<objectid>, which is the ArcGIS REST Services Directory:
a bare attribute dump titled "Feature (ID: 1234)" with a Login link and no map.
NunaGIS runs a public Web AppBuilder viewer, and only extent=xmin,ymin,xmax,ymax,4326
moves it — center+level, marker and query were each loaded in a headless
browser and each left it on the whole-Greenland view.
LOCDESCRIPTION is free text with labelled fields folded into it, and the
parser knew only the English labels. Run over 1,000 live records through the
real code path, 172 descriptions printed the spelling twice ("New Greenlandic
spelling: Akunnaaq. Old Greenlandic spelling: Akúnâq. · Ny: Akunnaaq · Gl:
Akúnâq") because a record carrying nothing but spellings returned the raw string
as its description and the caller then appended its own summary of it; 111
opened on a stray full stop; 69 left a Danish label standing as prose.
Ny grønlandsk stavemåde alone opens 101 of 1,724 descriptions, and the labels
are sometimes split by a hard newline mid-phrase. A further 1,263 of 1,721 spent
their first line repeating the pin's own title, which is now dropped.
The mojibake that must not be fixed with a substitution. 23.7% of Greenlandic
descriptions have Danish å mangled into â — pâ, ogsâ, stavemâde,
omrâdet. But â is a real letter in pre-1973 Greenlandic orthography, marking a
long vowel, and the same corpus holds Akúnâq, Naujât, Ingmíkârtikajât and
Sârdloq. A blanket â→å would rewrite every one of those into nonsense. The
repair is therefore a whole-word whitelist, built by enumerating all 237
â-bearing tokens in the register (673 occurrences) and keeping only the Danish
ones. Four stems (omrâd, gârd, fâreholder, ârhundred) cover the Danish
compounds and appear in no Greenlandic token.
Two smaller things came out of the same pass. CULTURE is the layer's own name
repeated on every row — "Norse", "Thule", "Industrial Whaling" — so
type: p.CULTURE || 'Norrøn bosættelse' put an English type in a Danish panel
and could never reach the Danish fallback beside it. And BRUUNNO, Daniel
Bruun's ruin-group numbering (Ø076, V014, M003), was neither requested nor
used: 173 of 628 Norse records have no LOCNAME, so a whole fjord of pins read
"Norrøn bosættelse" with nothing to tell them apart. It is the same problem
TOWNLAND solved for Ireland.
Sweden — a filter with no reason behind it, repeated five times. Every Swedish layer pinned itself to a single antikvarisk bedömning, a habit carried over from the retired WFS. The barrow layer asked for övrig kulturhistorisk lämning, which is the one assessment a burial mound is least likely to carry, and so drew 16 of the 250 barrows in a Stockholm bbox. Rock art was pinned to fornlämning and missed a quarter of the panels. Industrial drew 3 of 10. The heritage layer asked for two of the four and left out 27% of the register. There are seven bedömningar; four belong on the map.
Fanning the query out over all four, rather than dropping the filter, is the
part worth remembering. K-samsök caps every request at 500 records, and an
unfiltered Hällristning* search over Bohuslän returns exactly 500 — the cap,
in the one region where Swedish rock art is densest. Four requests raise the
ceiling to 2,000 and exclude the three unwanted bedömningar for free. Measured
over that same Bohuslän bbox afterwards: barrows 710, rock art 610, industrial
200. The shared helper is searchAllBedomningar, and it takes no bedömning
argument at all, so the old filter cannot come back by accident.
The bedömning is also the closest thing Sweden has to a protection status — fornlämning is protected by kulturmiljölagen and the other three are not — and because the query is split by category, the panel gets that field without a second request. It also fixes the period: a fornlämning whose lämningstyp maps to nothing now defaults to Oldtid rather than grey Udateret, and a möjlig fornlämning inherits the same default, while an unassessed lämning does not.
What RAÄ writes in the description field, and what to strip. 286 of 500 sampled descriptions end in "Beskrivningen är inte kvalitetssäkrad. Information kan saknas, vara felaktig eller inaktuell.", 285 of them to the character. It says nothing about the site, and what it does say is true of every register Fornland reads. It is dropped. The variant beginning "Beskrivningen enligt tidigare inventering" is kept, because that one is about the site. The prose itself comes out of fixed-width inventory books whose line breaks were deleted rather than turned into spaces, so sentences run together — "ca 12 fornlämningar.Dessa utgjordes av…", 135 of 500. Only the full-stop-then-capital case is repaired; "3rektangulära" and "diamoch" need a word boundary that is not there to be found.
Names carry two kinds of register cruft. A tenth of records have a pres:tag at
all, and a tenth of those arrive wrapped in literal double quotes — "Ekeby",
"PRÅM 4" — which went into the pin title. itemLabel repeats the tag verbatim,
quotes included, so stripping them from the name alone left the type reading
"Ekeby", Bytomt/gårdstomt; both forms have to be matched. Six of 437 named
records across three bboxes also keep the enumerator from the numbered list they
were typed from (1) Riksdagshuset), and it is sometimes outside the quotes
(1) "Rösaringen"), so it comes off first. Quotes inside a name are left
alone: Kilstugan" el "Kilen is two alternative names, not a quoted one.
Sweden's buildings — the register said everything three times.
Bebyggelseregistret serves RDF in which each block is repeated once per
representation the record carries, and fetchSwedishBuildings was reading the
flat pres:description, which is those repeats concatenated. A Skåne mill whose
only description runs to 1,096 characters was arriving as 3,290 — the same
paragraph three times over, which the panel then truncated mid-repeat. Across
213 Skåne records the fetcher was carrying 602,342 characters of text that hold
345,699 characters of information. The typed blocks underneath say it once, and
say which kind of text it is: Beskrivning Inventeringsår is the building as
surveyed, Historik is its history. Both are now kept, survey first, separated
by a blank line rather than run together. Five of 213 also had
pres:description reading Texten saknas while several thousand characters of
real description sat in the typed blocks beside it.
Two fields were being discarded entirely. ns5:spec carries the protection
heading — Byggnadsminne under chapter 3 of kulturmiljölagen, Statligt
byggnadsminne, Kyrkligt kulturminne under chapter 4 — with the decision date
appended, on 187 of 191 Stockholm records and 213 of 213 in Skåne. Swedish
buildings had no protectionStatus at all before this. Getting it onto the pin
needed three corrections. BuildingDetail puts the first four-digit number in
that string on the timeline, so the Förordning (2013:558) clause every state
listing carries would have dated all 551 of them to 2013, and a bare SFS
reference such as Kulturhistoriskt märklig byggnad (1942:354) to 1942; both
are stripped. Where a building carries several decisions — 31 of 213 in Skåne,
usually a state listing later confirmed as an ordinary one — the earliest is
what made it protected. And the 50 records nationally whose heading begins
Hävt keep the heading and lose the year: the protection has been lifted, so
neither the label nor the timeline may imply otherwise. The second field is the
pair of category lists, Historiska/ursprungliga kategorier and Nuvarande kategorier, which map onto originalFunction/currentFunction — fields the
type and the panel already had. Both lists arrive duplicated and in arbitrary
order, so they are sorted before comparison; unsorted, Kvarn, Husbehovskvarn
and Husbehovskvarn, Kvarn read as a change of use, which is how a third of
Skåne's mills acquired a spurious one. Neither is shown where it only repeats
the class name already on screen as the type, which removes about half of them
again: Stockholm ends at 92 original and 31 current uses out of 191.
Dropped rather than added: the fallback that put the class name in the description field, which told a reader that a Fyrplats is a fyrplats.
Measured and deliberately not acted on, BeBR. cadastralUnit (205 of 213)
is the property designation, countyName (100%) the län; neither has a slot in
ProtectedBuilding and adding one means a label in eight complete languages for
one country's benefit. The architect is in there — ns5:title "Arkitekt" beside
an ns5:fullName — but on 3 of 191 records, and the two elements did not
resolve consistently under the ksamsök namespace when probed, so it is not worth
the code it would take. A whole-Sweden bbox returns 2,690 records against the
500-record cap, which matters only at a zoom nobody browses buildings at; unlike
the lämning layers this one is not fanned out.
Sweden's per-site record — a primary branch that had never once run.
fetchSwedishDetail asked app.raa.se/open/fornsok/api/lamning/lamning/<uuid>
first and treated K-samsök as a fallback. That endpoint answers 200 to curl with
a rich JSON body and sends no Access-Control-Allow-Origin. Driven from a real
Chromium page on www.fornland.com, the fetch throws "Failed to fetch" — so every
Swedish panel Fornland has ever drawn was served by the fallback, which read
three fields. The branch is gone. This is the third register to prove the same
thing in a different disguise: a 200 is not evidence, after RAÄ's WFS answering
200 with "Service WFS is disabled" and the SPA deep links that render an error
page under a 200.
K-samsök's JSON-LD record for the same lämning is CORS-open and is RAÄ's
documented API rather than their app's private one, so it is now the only path.
Read the typed blocks in @graph, not the Entity: ksam:ItemDescription,
ItemSpecification, ItemNumber and ItemName carry the values, while the
Entity's ksam:itemDescription and friends are {'@id': …} back-references —
and one of those is an object where several are an array, so walking the Entity
means handling both shapes to arrive at the same nodes. The labels in
ksam:type are unique across the four block kinds, so one flat map keyed by the
Swedish label is enough.
Fill rates across 120 records from eight regions, which is what decides whether a field earns a row:
| Field | Filled | Shown as |
|---|---|---|
Aktualitetsstatus |
100% (9% ej bekräftad i fält) | badge, only when false |
RAÄ-nummer |
85.8% | metadata row — the key to the pre-2018 literature |
Terräng |
65% | metadata row |
Referens: skriftlig källa / kartmaterial |
46% | source list |
Undersökningsstatus |
100%, but Okänd on 54% | metadata row when it says something |
Skadestatus |
100%, but Okänd on 65% | condition row when Denmark's enum is absent |
Orientering |
29% | metadata row |
Vegetation |
13% | metadata row |
Placering |
100% (8% ej synlig ovan mark) | badge, only when nothing survives |
Ingående lämningar |
11% present, 7% non-empty | metadata row |
Tradition |
2% | its own aside — the closest a state register comes to folklore |
Three of those numbers are the point. Okänd is the register writing "I do not
know" into the same column as an answer, and passing it through would have put
"Condition: Unknown" on the majority of Swedish panels; it is filtered in api/,
as project_placeholders_as_data says to. Ingående lämningar is present but an
empty string on a third of the records that carry it, so presence had to be
re-measured as content. And Placering has a third value, i byggnad eller under
vatten, which is also nothing to look at but says where the remains are rather
than that they are gone — the badge would misreport it, so only ej synlig ovan
mark sets it.
Two fields needed repair. Ingående lämningar concatenates its entries with a
bare full stop: "…antal 14.Hög - Form: Rund, antal 1" is two kinds of grave
reading as one sentence with a typo. Every entry ends in a count, so the join is
always digit-then-capital and nothing inside an entry looks like that. And the
description in the full record carries the same disclaimer as the search
record — which matters more here, because on 18% of records the disclaimer is
the whole field, so an unfiltered read would have replaced a description the
pan path already found with a paragraph about data quality. The cleaner is
exported from ksamsokLamning.ts rather than written twice; the pan path keeps
its whitespace-flattening variant, the panel does not, because a record ending
"…\n\n- - Äldre beskrivning:\n…" has a heading in it.
The full record is roughly five times a presentation item — 8.4 kB against
1.7 kB — so it is fetched once, on panel open, and never as part of a pan.
Nothing is composed into prose inside api/: the enrichment LRU is keyed
${selectedType}:${id} with no language in it, so a sentence written in a
fetcher would survive a language switch. Structured fields go out, t() labels
them in the panel.
All five Swedish lämning layers read it: heritage, barrow, rock art, industrial
and shipwrecks. Only heritage sets raaUuid; the other four keep the same uuid
inside the Fornsök sourceUrl they show the reader, so useEnrichment parses it
back out rather than threading an id through four site types. The rendering is
shared — components/details/RegisterFields.tsx exports the seven metadata rows,
the two grey badges and the two asides, because the alternative was writing them
out five times. It is worth more on the four than on heritage: none of those site
types has a field for the antikvariska bedömning, so a Swedish barrow panel
showed no protection at all even though searchAllBedomningar had known the
category all along.
Extending it exposed an unrelated defect that had been swallowing far more than
these columns. useEnrichment races each layer group's chain against an 8 s
deadline, and the handler turned the rejection into setEnrichment(null) — so a
chain that ran long threw away everything it had already found. This group runs
the longest chain in the app, and a Swedish barrow in Uppsala lost the race every
time: no municipality, no Europeana, no historical image, nothing at all. The
deadline now resolves with the accumulator instead of rejecting, and the chain
publishes again if it finishes afterwards. Two related caps came out of the same
measurement: the register record is merged the moment it lands rather than at the
end of the chain, so a slow Wikidata query costs the reader a "did you know"
rather than the record; and fetchSwedishRelatedImages is capped at 2.5 s inside
fetchSwedishDetail, because it is three more round trips after the record — one
of them through an http→https redirect — and fifteen fields were arriving too
late to render while waiting on a photograph.
The defect was not confined to that group. Seven other branches raced their own
copy of the same rejecting timeout, so a museum, a lighthouse, a plaque or a
folklore pin could equally well render blank on a slow connection. They now share
one runRaced(acc, enrich, pinId) helper, which is what makes the fix hold: the
old shape put the same twenty lines in eight places, and the failure it caused is
invisible in a diff — adding a fetch anywhere in a chain could delete unrelated
fields further down it. Two smaller things fell out of consolidating them. The
Heritage at Risk lookup on British listed buildings used to run after the publish
and mutate the rendered object without telling React, so the badge only appeared
if something else happened to re-render the panel; it is now part of the chain and
merged when it lands. And the community-content merge was being asked for
pin_id=undefined on every folklore pin, because a folklore selection is an array
of records with no single id — that request is simply not made any more, and the
panel already fetches community content per record itself.
One label was wrong, in English only. Orientering is not a compass facing but a
position relative to a landmark — "17 m S om Aspebergsristningen, RAÄ-nr 12" —
which is what the Danish, Norwegian, Finnish and Icelandic labels already said.
The English read "Orientation" and now reads "Position in the terrain".
Measured and deliberately not acted on. Icelandic haettumat (risk
assessment, 363/400), Northern Irish Thatched/Vernacular and Irish
REFERENCES_ (the excavation and publication bibliography, filled on 137 of
400 sampled records) are all real and all fetchable; they were left for a later
pass rather than forgotten. The Irish one is waiting on somewhere in the detail
panel to put a citation list. Measured and
rejected as worthless: Faroese Lýsing (0% filled), Finnish selite (3%),
Danish bevaringsvaerdi (a -1 sentinel throughout) and Scottish
ARCHITECTU/ARCHAEOLOG (single-character "Y" flags, not text). Every
register named in this appendix has now been probed.
F. Second pass, 17 August 2026 — and two recommendations the measurement killed
Four fields were queued from appendix E. Measuring them first changed two of the four, and turned up a bug that would have swallowed all of them.
The bug came first. Heritage records have no in-memory index: App.tsx
builds the selected record out of the MapLibre feature's properties, on both
the click path and the deep-link path. toHeritageGeoJSON copies an explicit
property list, and protectionStatus, condition, access and nkah were
not on it — so the Norwegian vernetype, the Swedish antikvarisk bedömning and
the Latvian condition and access fields, all added during the first pass, were
fetched, cached and never rendered. Nothing failed anywhere. geojson.test.ts
now drives the assertion off Object.keys of a fully-populated HeritageSite,
so the next field left out of the literal fails a test instead of disappearing.
Norway, synlig — added. Present on all 6,297 localities across five
sampled viewports and false on 4.8% (Oslo), 8.6% (Trøndelag), 11.1% (Lofoten),
13.4% (Finnmark) and 21.3% (Rogaland). Nearly independent of vernetype: 321
of Rogaland's 393 invisible sites are not marked fjernet, and 156 of the
sites that are marked fjernet are still visible. So the protection row does
not already say it. Shown as a badge rather than a metadata row — it is the one
thing worth knowing before setting out, and only the negative is carried.
Scotland, PARISH — dropped. It was queued as "the disambiguator TOWNLAND
gave Ireland". It is not: duplicate-titled records fall only 344→325 in Orkney,
596→592 on Skye and 179→174 in Edinburgh. Name plus type falls to 34/182/8, and
the type is already on screen.
Scotland, ACCURACY and ALTNAME — added instead. ACCURACY is filled on
every record and spelled in plain English; 6.6% of an Orkney bbox, 5.2% of Skye
and 7.9% of Edinburgh are only good to a kilometre or are a "General location",
which is a badge. ALTNAME is filled on 24–58% of a viewport and is a genuinely
different name — Verron is also Knowe of Verron, Granton Harbour is also
HMS Lochinvar. It does not disambiguate (333 Orkney duplicates → 250); it
earns its place because the other name is often the one a visitor has heard, and
searching for it used to find nothing. Now in the panel and in the search index.
Northern Ireland, Derelict — added. Whole-register tally: Yes 542,
Partially 396, Demolished 389, Patially 1 (the register's own typo), No 8,823,
blank 5,711. Mapped onto the existing HeritageCondition scale, which cost one
new member: derelict is abandoned and decaying, Latvia's avārijas is a
declared structural emergency, and folding one into the other would put words in
a register's mouth. The 389 demolished buildings still have pins; the panel now
says so.
Northern Ireland, CurrentGra — a defect found while reading it. The column
is a listing grade for 6,554 buildings (A, B+, B1, B2, legacy B) and something
else for 6,664 more: Record Only (6,171), D1 Record Only (485) and
Delisted (8). The Grade ${x} template was putting "Grade Record Only" — a
grade that does not exist — on 42% of Northern Ireland's pins, and using it as
the building's type. Now "Recorded, not listed" and "Delisted".
Latvia, region — measured worthless, closed. Filled on 400 of 400, and
carrying one single distinct value across a whole Riga viewport; five values
nationally. municipality already carries the city, parsed out of address.
Nothing replaces it.
Latvia, is_actual — measured, not understood, not acted on. It looks like
a Greenland-style withdrawal flag and is not. False on 30.5% of Riga, 95.8% of
Kurzeme, 88.2% of Latgale and 64.8% of the whole country — and false on Aglonas
bazilika and Liepājas Sv. Jāzepa katoļu baznīca, both standing and both
protected. Whatever it records, it is not "this monument is gone". Left alone.
Norway, nøyaktighet — deferred on an unsettled unit. Values 500, 1000,
200, 100, 10, 50, 2000 and 0. SOSI documents NØYAKTIGHET in centimetres in some
realisations, so 500 is either 5 m or 500 m. Shipping it either way would state
something we have not established. synlig covers the practical case for now.
G. Iceland, 17 August 2026 — a risk axis, and a name bug found beside it
Sample throughout: five viewports over Reykjavík, Suðurland, Akureyri,
Austurland and Vestfirðir, 8,495 features off
Minjastofnun:fornleifaskraning_punktar. DescribeFeatureType on that layer
lists 24 columns; nine were read, four were already in use.
haettumat — added. Filled on 7,332 of 8,495 (86.3%), by viewport 74.7% to
98.5%. Eight distinct values that fold to four after case: engin hætta 3,949,
hætta 2,095, mikil hætta 1,280, lítil hætta 7, stórhætta 1.
It is not a restatement of astand, which the codebase already maps to
condition. The crosstab settles it: 234 sites are vel greinanlegar and
mikil hætta at once — well preserved and about to be destroyed — and 326 are
sést ekki under engin hætta, gone already and threatened by nothing. Same
finding as Norwegian synlig against vernetype: two columns that sound alike
and disagree in both directions.
engin hætta maps to undefined rather than to a fourth grade, because a row reading "no risk" on 46% of Icelandic pins is noise, not information. Of the three that remain, only mikil hætta gets the red badge — 1,281 records, 15.1% of the sample. Badging the middle grade too would have put red on 39.8%, which is where a warning stops being read.
haettuorsok + haetta_tilefni — added as one field. Neither column is
complete on its own: haettuorsok covers 3,090 records, haetta_tilefni 2,577,
and 456 of the second are rows where the first is blank. Read in that order they
name a threat on 3,499 records. Together they carry 24 Icelandic phrases, nearly
all genitive "vegna X", which collapse to ten stable ids; the mapping was
verified against live data and all ten populate (development 1,320, disturbance
527, roadworks 437, forestry 362, erosion 245, wind erosion 228, habitation 175,
cultivation 77, natural hazard 70, building 58).
116 rows name a threat while leaving haettumat blank. The cause is dropped on
those rather than inferred into a grade — the panel keys its row off the grade,
so carrying the cause alone would repeat the silent-drop failure this appendix
opens with.
The name bug, found while reading the same rows. serheiti (the site's own
name) is filled on 38.7%, so 5,208 of 8,495 pins fell back to
heiti_jardar_1847_heiti_jardar_nuverandi. That column is the 1847 farm name
and the present one written as old/new, and it was being used raw. The result
shipped as the pin label, the panel title and the search string:
- 3,239 records read
Þingvellir/,Vestdalur/,Fjörður/— a bare trailing slash - 958 read
Blikastaðir/Blikastaðir, the same word twice - only 285 carried two genuinely different names
- 49 read
óljóst/— the register writing unclear into the name column
serheiti uses the same separator on 93 records, but there for real aliases
(Hulduhóll / Hóllinn). One rule now handles both: split on /, drop
placeholder parts, take the first as the name and a differing later part as
altName. After the change no Icelandic name contains a slash, altName is
populated on 34.8%, and the generic Fornleifar fallback rises from 8.5% to
8.9% because óljóst was added to the placeholder pattern. The barrow and
industrial fetchers read the same layer and got the split too, along with an
isReal() on hlutverk that was missing — they were shipping óþekkt as a
monument type.
Not read. heimsott (survey visit date) is filled on 94.3% but arrives in
706 distinct shapes across the sample — 2010-08-27, 2019/08/23, 08.10.14,
18.05.15, 10-02-2026 — with day-first and month-first indistinguishable.
horfin_vegna (why the site is gone) is 12.2% filled and the two commonest
values are ekki vitað and annað, both placeholders. hus_og_mannvirki, the
buildings layer, has no risk column at all, so this pass is archaeology only.
BarrowSite and IndustrialSite come off the same rows and could carry the
grade, but neither type has a risk field yet and both panels would need it.
One perf finding fell out of the same sweep and is recorded here so it is not
rediscovered from scratch: the Welsh SAM layer ships roughly 690 kB gzip per
viewport, almost all of it MultiSurface geometry that exists only to compute
a centroid. The response already carries easting/northing — the same
centroid in British National Grid — so an OSGB36→WGS84 transform would cut the
payload sharply. Trimming with propertyName while keeping geom saves 3% and
is not the answer; the geometry has to go.
Corrected after doing it — see appendix H. The guess above was "about fortyfold"; measured on the wire it is twentyfold. The estimate was made by eye from one response and the correction is left visible rather than quietly edited, because a doubled saving is exactly the kind of number that gets repeated into a funding application.
H. Wales, 17 August 2026 — dropping the geometry, and what the columns hid
Three viewports throughout — mid Wales (-4.3,51.5,-3.0,52.3), Gwynedd and
Anglesey (-4.9,52.6,-3.6,53.4), the valleys north of Cardiff
(-3.4,51.4,-2.9,51.8) — plus two more over Pembrokeshire and Flintshire for
the vocabulary counts. 1,304 Cadw SAM records and 2,500 RCAHMW records.
The payload, measured on the wire. Requesting Cadw_SAM with every column
except geom returns the same 500 features with geometry: null:
| viewport | with geometry | without | ratio | records |
|---|---|---|---|---|
| mid Wales | 729,650 B | 37,034 B | 19.7× | 500 |
| Gwynedd | 619,384 B | 34,167 B | 18.1× | 500 |
| valleys | 428,519 B | 23,550 B | 18.2× | 304 |
easting/northing are filled on 1,303 of the 1,304. src/utils/osgb.ts does
the inverse Transverse Mercator on Airy 1830 plus the Helmert seven-parameter
shift; against Ordnance Survey's own worked example it lands within a metre.
Is the grid reference the same point as the centroid? No, and that is the interesting part. Distance between the two, per viewport: median 6–8 m, p90 46–61 m, p99 ~215 m, max 328 m. The grid reference falls inside the polygon's bounding box on 493 of 500. Sorting by distance sorts by monument shape: every record over 130 m is a tramroad, a Roman road, a field system or a dockyard spanning 600 m to 2.7 km, where a vertex-average centroid is not a better answer than the surveyor's own point — it is a different arbitrary one, and it drifts towards wherever the digitiser clicked most. Of the seven whose grid reference lands outside the polygon, five are within 32 m of a monument under 30 m across, which is rounding. Two are genuinely wrong in Cadw's table (Pant Sychbant Round Cairn, 328 m; Troedrhiwhir Mountain ring cairn, 86 m). Two bad pins in 1,304 against a twentyfold payload cut.
Name_cy — added as altName. Filled on 1,210 of 1,304, but identical to
Name on 95% of them; only 82 of 2,500 across five viewports differ. Those 82
are real: Abaty Ystrad Fflur for Strata Florida Abbey, Castell Weble for
Weobley Castle, Beddrod Siambr Bryn Celli Ddu. One is not — Name_cy reads
"Caerleon Test" — which is why the alias is filtered.
SiteType_cy, Period_cy, BroadClass_cy — not read. All three are
filled at 95–100% and genuinely translated (Cynhanesyddol, Carnedd Gron,
Crefyddol, Defodol ac Angladdol). Serving them would mean the fetcher knowing
the UI language, which no fetcher does; period is mapped to internal IDs and
already translates, and type is raw text everywhere in the app. Left for
whenever a language argument reaches api/. Note the RCAHMW copies of the same
idea (cyfnod, math_o_saf, hen_sir) arrive mojibaked — Ol-Ganoloesol and
Sir F�n — so that layer's Welsh columns are unusable as they stand.
Cadw Listed Buildings — measured and left alone. It has the same
Easting/Northing and the same _cy family, but its geometry is Point, so
dropping it saves 1.1× rather than 20×. Not worth the change. Name_cy differs
from Name on 12 of 1,500 and ProtectedBuilding has no alias field.
DesignationStatus_en is "Designated" on all 1,500.
RCAHMW — two flags off columns nobody had read. DescribeFeatureType on
geonode:rcahmw_nmrw_terrestrialsites_rcahmw_bng omits both name and
location, yet the service serves them; the schema cannot be trusted as the
column list, only a real GetFeature can.
evidence, 97–99% filled, 17 values.Documentsalone is 38.1% — over a third of the largest Welsh layer is known from a deed, a map or an antiquarian's notebook, with nothing standing. WithCropmark(0.8%),Buried Features(0.2%) andFinds(0.1%) that is 39.2% now carryingnotVisible, the same field Riksantikvaren'ssynligfeeds. The remaining vocabulary — Earthwork 18.3%, Building 14.2%, Other Structure 9.2%, Complex 3.2%, Ruin 2.4%, then Excavated Feature, Landform, Buried Features, Topography, Mechanical Structure, Wreck — describes something that is there.Not recordedis 11.0% and says nothing either way.locationis not about the place. It is one of eight fixed sentences about where the grid reference was taken: at centre of site 72.1%, Not recorded 17.1%, at a central point within extensive site or complex 5.3%, at point along linear site 2.5%. The two that admit to a rounded square — corner of kilometre square (0.6%) and corner of 5km sheet (0.2%) — now setapproximate. Twenty records in 2,500, each of which can be a kilometre or two and a half out while looking as precise as its neighbours.
A shouting bug fixed in passing. site_type is ALL CAPS and the old
title-caser was charAt(0) + slice(1).toLowerCase(), which rendered "SHEEP
FOLD" as "Sheep fold" and "BISHOP'S PALACE" as "Bishop's palace" across the
whole 112,894-record layer.
Not read. osgb_ngr (the grid reference as a string, 100%), mapsheet,
kmsq, old_county (Merioneth, Cardiganshire — historic counties, 100%
filled, but municipality already carries the unitary authority),
lastupdate (66–99%, dd/mm/yyyy hh:mm), and lat/long, which duplicate
the geometry as strings.
I. Northern Ireland, 17 August 2026 — one link for 15,862 buildings, and a column that will not be read
Layer 1 of the Historic Environment Division's FeatureServer is the Historic Buildings Record: 15,862 records, counted live. Eighteen columns, of which the fetcher was reading nine.
The link was the real finding. Every building carried the same
sourceUrl — communities-ni.gov.uk/services/buildings-database, the
department's page about the database. A visitor who clicked "view source" on
a specific house in Garvagh landed on a page describing the register in
general. MainID, which was in the schema and unread, is the key the public
app answers to:
apps.communities-ni.gov.uk/Buildings/buildview.aspx?id=<MainID>.
Verified rather than assumed, because a 200 from an ASP.NET page proves
nothing on its own. Four ids were fetched and the HB Ref No on the rendered
page compared to the API's HB_ref:
| MainID | API HB_ref |
page HB Ref No |
|---|---|---|
| 9544 | HB03/02/034 | HB03/02/034 |
| 8974 | HB12/12/014 | HB12/12/014 |
| 8977 | HB12/12/018 | HB12/12/018 |
| 8979 | HB12/12/026 | HB12/12/026 |
MainID is non-null on all 15,862 records, so the landing page survives only
as a fallback that should never fire.
Two yes/no columns worth a row each.
Vernacular— built in the local tradition rather than to an architect's design. Yes on 942, No on 9,409, blank or null on 5,511. Rendered in the existing Style row, where Wikidata'sarchitecturalStyleused to be the only possible source.Thatched— Yes on 267, or 1.7% of the register, No on 13,589, blank or null on 2,006. Rendered in the existing Material row. 237 buildings are both, and 30 are thatched without being flagged vernacular. This is the one fact in the layer that decides whether a house is worth a detour, and it was being fetched past.
Both columns spell yes two ways: Yes on the great majority and Y on
exactly one record each. niYes matches both. A strict === 'Yes' would have
dropped two buildings without a word, which is the same failure mode as the
register's own Patially typo in the Derelict column.
Neither field needed new plumbing. Buildings reach BuildingDetail through an
in-memory buildingIndex map rather than through a narrow GeoJSON property
literal, so unlike heritage sites there is no gate to widen — only the two
optional fields on ProtectedBuilding and the register-wins precedence that
architect already established.
A column that is queryable but not readable. Extent holds the extent of
listing as free text — "House, outbuildings and walling" — and the service
will filter on it: Extent IS NOT NULL counts 15,818, Extent <> ' '
counts 12,650, Extent LIKE '%house%' counts 3,220, Extent LIKE 'B%'
counts 535. It will not return it. Requested by name in outFields, the key
is simply absent from every feature; the same under f=geojson, and under a
groupByFieldsForStatistics aggregation the group labels come back null while
the counts are correct. The field is declared in the layer schema with
esriFieldTypeString, length 254. So the value exists, the SQL engine can see
it, and the serialiser withholds it — presumably a field excluded from the
hosted view.
The text is visible on buildview.aspx, which means reading it for the whole
register is 15,862 HTML requests against a public-sector ASP.NET app. Not
done, and noted here so nobody spends another hour on the API believing they
have the query wrong. This one belongs beside the WFS traps in
project_wfs_bbox_crs: a field can pass the schema, pass a WHERE clause,
and still never serialise. DescribeFeatureType was already known to
under-report; this is the mirror image.
Not read. TxtIGRef (Irish Grid as a string, 14,856 records — the
geometry already gives the position), ChkSS (an internal check column whose
values are True, False, No and, on at least one record, the grid
reference J3340 7284), BU_ID (null on every record sampled), OBJECTID
and OBJECTID_1. The page also shows a Date of Listing and a long Exterior
Description and Setting that the API does not publish at all.
Appendix J. Estonia, 17 August 2026 — a layer whose column names describe nothing they contain
muinsuskaitse:ajal_haljasala is the historic parks, gardens, avenues and
squares inside the eleven muinsuskaitsealad: 385 rows, three columns, and not
one of the three holds what its name says.
| Column | Name means | Actually holds |
|---|---|---|
nimi |
name | the conservation area — Tallinn, Pärnu, Rebala |
pohjendus |
justification | the green area's name in the eleven towns, a habitat class in Rebala |
tyyp_ala |
type of area | Ajalooline_haljasala on all 385 rows |
Reading nimi as the name would have produced 385 pins called Tallinn and
Tartu, in exactly the way the Icelandic farm-name column produced 53 per cent
of pins called Þingvellir/. The name is in pohjendus, the free-text column
the register uses for the protection order's reasoning everywhere else.
Rebala is a different register in the same table. It is the one rural
conservation area and it accounts for 253 of the 385 rows, where pohjendus
holds the biotope rather than a name: loopealne (alvar) 77 times, the Natura
habitat name Aas-rebasesaba ja ürt-punanupuga niidud 58, lubjarikas
aruniit 54, and five other classes. Those 251 rows are dropped. Not because
the grasslands do not matter — they are the historic land use Rebala exists to
protect — but because a pin has to be somewhere a visitor can go and read
about, and 77 pins reading "alvar" across one parish is neither. It is the same
judgement grade C got above. Two Rebala rows survive, Vandjala soo and
Männikuraba, because those are named places. The eight classes are matched
against an explicit list rather than a lowercase-initial heuristic: the
register capitalises exactly one of the eight.
385 rows in, 134 pins out, verified against the whole live layer rather than a sample:
| rows | |
|---|---|
| fetched | 385 |
| dropped as a habitat class | 251 |
| plotted | 134 |
of those, marked (ettepanek) — proposed, not yet designated |
23 |
| duplicate ids among the 134 | 0 |
The 23 proposals carry protectionStatus: "Proposed, not yet designated",
because the other 111 are designated and the difference is the sort of thing a
map should not flatten.
Type comes from the name, since tyyp_ala cannot distinguish anything.
Estonian puts the head noun last in these phrases — Ringi tn allee is the
avenue of Ringi street — so the final word decides, which also catches the
welded compounds (Rannapark, Krahviaed). That yields 47 avenues, 34 parks,
26 unclassified green areas, 13 gardens, 8 squares and 6 churchyards.
Churchyards are tested first, or kirikupark and kirikuaed would be read as
a park and a garden. One row of the 134 is knowingly misread: Mõisapark koos
Tasuja pst alleega is a manor park with an avenue attached and ends on the
avenue. Left alone rather than special-cased, because a rule weighing the whole
string would have to pick a winner between two nouns and would get more than
one row wrong.
No dates anywhere. The layer has no date column and the names carry none,
so all 134 come out Udateret. Most Estonian town parks are nineteenth-century,
laid out where the ramparts had been, but the register does not say so and
neither does Fornland.
Two smaller traps. There is no id column, and the GeoServer fid
(ajal_haljasala.44) is a row index that moves when the table is reloaded — the
same trap kaitsekat_* set — so the id is built from the centroid, which is
also what separates the eight polygons of Ringi tn allee. And one record ends in
a non-breaking space, which needs no handling at all: JavaScript's \s and
trim() both count U+00A0 as whitespace. Recorded because it looks as though
it should.
Not read. Nothing. All three columns are read, one of them twice.
Appendix K. Northern Ireland battlesites, 18 August 2026 — a description that was one letter of the alphabet
Layer 8 of the Historic Environment Division's FeatureServer is the battlesites
inventory: 324 records, small enough that the whole vocabulary can be read
rather than sampled. The fetcher took the name, the date and OUTCOME, and
wrote OUTCOME straight into description. On 140 of the 324 that made the
entire description of the pin read "B: victory".
A and B are never shown to the reader. They are SIDEA and SIDEB, two
columns the fetcher did not read at all, so the letter that decided the battle
pointed at nothing on the page. niBattleOutcome now resolves the letter
against the sides and writes "Victory to Lloyd (Williamite)."; six spellings of
the same outcome occur (B victory, A: victory, A. victory, B:victory,
Victory: B, and B: victoy, which is the register's own typo).
"Inconclusive" and "unknown" are different claims. The first is a fact about
the battle; the second is a fact about the register. Collapsing them, which is
what a naive "no winner" branch does, would state something nobody said about 22
sites. unknown, Unknown, unkmown (a second register typo), uncertain and
n/a now yield no outcome sentence at all, while anything appended to them is
kept — "unknown, both men reportedly killed" becomes "Both men reportedly
killed."
The sides needed their own reader. They are written Commander: Faction,
and on 75 of the 648 values the commander already carries a bracket, so the
obvious X (Y) join produced "Nugent (defending) (British)". niBattleSide
splices the faction into the existing bracket instead. Half the column is a bare
party name with no colon, and those pass through untouched, quotes and slashes
included.
38 place names hold more than one battle — Carrickfergus alone has ten —
which put ten identical entries in the search index. The name now carries the
year, taken from the first three-or-four-digit run in the date column, not the
last: days and months are written two-digit throughout, so the first long run is
the year in every format the column uses (07/05/1689, 623 AD,
02 - 10/10/1600), and a range (1641-42) gives the year it started. 251
distinct names became 313.
56 records are a siege, and EventCategory has had a "siege" label in ten
languages and its own icon since the Overpass layer was written. They rendered as
battlefields. TYPE is also now the description's opening sentence, since the
register's own classification ("Ambush", "Skirmish", "Repulsed on first attack,
taken next day") says more than the category does.
Seven records have no side, no type and no outcome. Their description stays
empty on purpose: HistoricalEventDetail.tsx:102 reads
curatedDesc || wikiExtract || description, so unlike the Irish panels an empty
description is what lets Wikipedia through.
Not read. GRIDREF (100% filled, but it is the geometry the pin already
has, written as a string) and MAIN_REF (89%, a bibliography — the same verdict
as Irish REFERENCES_, and waiting on the same thing: somewhere in the panel to
put a citation list). CENTURY duplicates the year now in the name.
Appendix L. Norway, 18 August 2026 — the Sámi filter was reading type names when the law was in the record
Riksantikvaren's WFS carries app:verneparagraf inside the same
<app:høyesteVern><app:Vern> block as the vernetype the app already reads.
It is the legal paragraph the protection rests on, written out in full, and one
of its values is a statement rather than a category:
4.2 (Samisk kulturminne fra år 1917 eller eldre) — kulturminneloven § 4 second
paragraph, which automatically protects Sámi cultural heritage older than 1917.
The field is worthless in most of the country and decisive in one part of it. Fill rate by viewport: Oslo 5.7%, Trondheim 7.0%, Jæren 45.8%, Finnmark 88.8%. The § 4.2 value specifically is 183 of 606 localities in a Karasjok bbox, and 0 of 1 919 at Jæren and 0 of 1 863 in Trondheim — as it should be, since the paragraph only applies in Sápmi. A national average would have read as a half-filled field of unclear use and hidden both facts.
What it replaced was a guess. sami drives a period filter of its own
(useGeoJsonLayers.ts:99), and until now it was set by isSamiSite, which
matches words: teltboplass, flytteveg for rein, reindrift. Run over the 582
archaeological localities of that Karasjok bbox against the register's own legal
designation:
| Sámi by law (§ 4.2) | 174 |
| found by the word test | 42 |
| of those, correct | 37 |
| missed | 137 |
The misses are not marginal types: 96 are Fangstlokalitet and 33 Bosetning/aktivitetsområde, both perfectly ordinary Norwegian words. The Sámi filter was showing under a quarter of what the law protects as Sámi.
The paragraph is now read as an addition to the word test, not a replacement,
because its absence proves nothing — 352 of the same 606 localities carry the
generic 4 (Automatisk fredning), many of them Sámi in fact and none of them so
stated. Reading 4 as a flag would have turned the filter into "everything in
Finnmark".
Not read. The rest of the paragraph's values are a long tail that repeats
what the panel shows already: 14 (Fredning av skipsfunn) appears only on
lokalitetsart 20168, which decodes to Skipsfunn; 4j (Gravminner ...) only on
20164, Gravminne; every 8.1/8.4 (Dispensasjon) sits on a vernetype the
panel already prints as Fjernet; and 1 (Utgått paragraf) is a lapsed
paragraph, which is a non-answer written as data. Single-digit counts each.
Also rejected: app:opphav. The Assebakti record that prompted this is
sourced Sametinget, which looks like the same signal for free. It is not.
Sametinget registered 190 localities in the Karasjok bbox, of which 41 are not
§ 4.2, while 34 of the § 4.2 records were registered by someone else. opphav
names the caseworking body, not the heritage.
Still open. L-BVF — the protected-buildings layer — carries § 4.2 as well,
on 9 of the 17 buildings in the same bbox. ProtectedBuilding has no sami
field and the filter in useGeoJsonLayers only looks at heritage features, so
carrying it there is a wider change than this one and has not been made.
Appendix M. Iceland and Estonia, 18 August 2026 — an address the name did not give, and two columns that gave nothing
Minjastofnun hus_og_mannvirki, gotuheiti_nr, 99.8% filled. The register
keeps the address in a column of its own, and it is not the name: on 977 of the
1 523 buildings in a Reykjavík bbox the two strings are identical, and on the
other 546 the name is the house and this is where it stands — Dómkirkjan í
Reykjavík at Kirkjustræti 16, Stjórnarráðshúsið at Lækjargata 1,
Viðeyjarstofa on Viðey. Only the difference is carried, into a new
ProtectedBuilding.address and the detail.address row that already existed
in twelve languages for the Oslo Byleksikon panel.
heiti is now trimmed. 17 of the 1 526 names arrive with a trailing space
("Bankastræti 10 "), which the panel cannot show and a sort or a search cannot
miss.
Not read: postnr (100%) — the same answer as stadur one notch finer,
under a panel that already has a map. easting/northing are the geometry
written twice. tegund_fridlyst (99.9%) names the legal instrument
("Aldursfriðun skv. lögum nr. 80/2012" on 1 215, "Friðlýst með sérstakri
ákvörðun" on 302) and splits the layer exactly where fridad_fridlyst already
splits it, which the panel already prints; three records cite law 80/2013,
which does not exist. The four booleans aldursfridud_kirkja,
fridlyst_kirkja, aldursfridad_hus_mannv and fridlyst_hus_mannv are
fridad_fridlyst taken apart.
Rejected after measuring: deep links to minjastofnun.is. Every Icelandic
building currently links to https://gis.is/, the geodata portal, because the
WFS carries no page reference. Minjastofnun's own site does have per-building
pages — 512 of them, at
/is/byggingararfur/fridlyst-hus-og-mannvirki/<slug>, paginated 12 to a page
across 43 pages (?page=N works only on the canonical /is/... path; the
short path 301s and drops the query, so all ten pages I first fetched were
byte-identical). The slug is built from the page title, not from any identifier
in the WFS, so linking means matching two registers by name. Measured against
the 306 listed buildings in the Reykjavík bbox: 127 unambiguous matches, 16
ambiguous, 163 with no match at all. Cross-checking construction year does
not rescue it — the two sources disagree on 20 of the 127, almost all of them
correct matches recorded from different years (Sundhöll Reykjavíkur 1932 vs
1929), while the one match that is genuinely wrong (Landakot — gamla
prestshúsið → landakotsskoli, a different building) has years that disagree
for the same innocent-looking reason. A verifier that drops good links and
keeps the bad one is not a verifier. Shipping this would put a confident wrong
link on some pins, which is worse than the honest generic one.
Muinsuskaitseamet kpo_malestised, two columns closed off. tyyp is
tyyp_nimi as a screaming code (KPO_LIIK_AJALOO_KUNSTIMALESTIS) and is not
read as a fallback: in a 1 838-record Tallinn/Harjumaa bbox exactly one record
has the code without the name, so decoding it means a code table for a single
pin, and the existing fallback Kinnismälestis is the register's own word for
it. nahtuse_liik_nimi is filled on 100% and is worth nothing for the opposite
reason — it is "Kinnismälestis" on 1 836 of the 1 838 and "Muinsuskaitseala" on
the other two.
Appendix N. The Sámi filter, 21 August 2026 — every term counted one at a time
Appendix L put the Norwegian half of the Sámi filter on the register's own legal paragraph. This is the vocabulary half, in all three countries, where outside Norway there is no such paragraph and the type words are all there is.
The criterion, after this pass, is one sentence and the same in all three registers. The flag means the register names the site as Sámi, either in law or with a term that exists only in Sámi material culture. Nothing is inferred from latitude. Four of the terms in the list matched no record at all, and three matched southern Sweden more than Sápmi; both kinds were invisible in the app, and both were found by counting each term separately against the register's own count facility, nationally and in a control region where the answer must be zero.
RAÄ, kåtatomt → Kåta. The Swedish list carried kåtatomt, anchored
^…$. There is no lämningstyp by that name: 0 records nationally, against
1 778 for the register's actual word Kåta, 1 201 of them north of 64.5° and 10
south of 62°. The hut site is the most recognisable Sámi remain in the register
and the filter had never shown one. Nothing failed, because a term that matches
nothing looks exactly like a term whose sites are not in view.
Three terms removed for the opposite reason. All three are ordinary Swedish site types that Sápmi also has, and all three were in the list on the strength of the word alone:
| Term | National | North of 64.5° | South of 62° | Kept |
|---|---|---|---|---|
Förvaringsanläggning |
3,644 | 1,109 | 2,166 | no |
Fångstgropssystem |
2,600 | 647 | 351 | no |
Offerplats |
377 | 258 | 91 | no |
Kåta (added) |
1,778 | 1,201 | 10 | yes |
Viste |
1,071 | 968 | 0 | yes |
Renvall |
663 | 547 | 1 | yes |
Rengärda |
356 | 289 | 0 | yes |
Bengömma |
269 | 228 | 1 | yes |
Stalotomt |
167 | 167 | 0 | yes |
Hornsamling |
45 | 40 | 0 | yes |
Förvaringsanläggning is a majority-southern type, so the filter was wrong about it more often than right. The list drops from 9 192 records to 4 349, of which 12 lie south of 62°, and stops claiming Härjedalen storage pits and Västergötland trapping systems.
Offerplats survived the first pass of this appendix and should not have. Its
91 southern records are 31 in Skåne, 14 in Västergötland, 9 in Småland, 8 in
Uppland, 5 on Öland and 1 on Gotland, which is 88% of all the error left in the
Swedish list. RAÄ does not distinguish a sieidi from a Bronze Age sacrificial
site: the lämningstyp is the same word, and itemSpecification carries only the
antiquarian assessment (Fornlämning, Möjlig fornlämning, Övrig
kulturhistorisk lämning, Ingen antikvarisk bedömning), which is the same
distribution north and south. Its own description mentions something Sámi in 28
of 258 northern records and 3 of 91 southern ones, so prose does not separate
them either. The 286 northern sacrificial sites stay on the map and stop being
counted as Sámi, which is the honest reading of a register that does not say.
Not read: Härd. The temptation, and the reason it is not here. Hearths are
281 of 500 pins in a Jokkmokk viewport and 182 of 500 in Kiruna, against 1 in
Uppland, 3 on Gotland and 4 in Västergötland — 17 852 nationally, 14 313 of them
north of 64.5°. In Norrbotten the árran hearth genuinely is the commonest Sámi
remain. But the register does not say so: of 281 Jokkmokk hearths exactly 1
mentions anything Sámi in its description, and the rest are geometry. Flagging
them would be inferring ethnicity from latitude, which is the app's own guess
dressed as the register's answer.
Museovirasto, a branch with no caller. isSamiSite has had a Finnish arm
since it was written — kodanpohj, seidat, seidakivi, árran, gieddi —
and fetchFinnishHeritage never called it. One of those five, seidakivi, is
another kåtatomt: 0 records nationally, where the register writes seidat, 62
of them. Wiring it to tyyppi, the obvious
field, would have kept it silent: tyyppi is the broad category and reads
asuinpaikat, , , on an Inari árran hearth exactly as it does on a settlement
in Häme. The vocabulary is one level down, in alatyyppi, and it is unambiguous
there. (Museovirasto renamed that column tyypin_tarkenne on or before
1 Sep 2026 and dropped the padding at the same time; the fetcher reads the
new name, and the rock art and industrial layers answered 400 in between.)
| Viewport | Records | Sámi by alatyyppi |
|---|---|---|
| Utsjoki | 408 | 87 |
| Inari | 701 | 92 |
| Enontekiö | 256 | 49 |
| Häme (control) | 2,254 | 0 |
Per term, counted nationally and south of 63°: kodanpohj 136/2, seidat 62/0, árran 90/1, gieddi 121/0. Two candidates were tried and match nothing at all in the register, saamelais and poroerotus, and are not in the list.
alatyyppi is a four-slot comma list with the unused slots left as spaces, so
the Finnish match is a substring where the Swedish one is anchored — a record
reads ei määritelty, kentät (gieddi), , and the term can sit in any slot.
Not read: purnut. The Sámi storage pit, 44 in Utsjoki and 14 in Inari, and
also a perfectly ordinary Finnish settler's cache. No control count separates
them.
Also rejected: propagating the Norwegian flag down to the enkeltminne. The
plan for this work assumed the E-ARK children carried Sámi evidence the
locality did not, and they do not. 79.9% of enkeltminner with a Sámi art code
already carry § 4.2 themselves, and their localities carry it too: propagating
the paragraph up adds 5 localities across Karasjok, Tysfjord, Røros and Tromsø,
and propagating the 20 Sámi art codes adds 15, both out of 2 095. Doing it would
mean regenerating all 177 public/data/no-datering/ tiles and bumping
TILE_VERSION, because the live enkeltminne query delivers 60% of a locality's
declared children and a flag set from it would flicker as the user pans.
Rejected on the arithmetic.
Riksantikvaren, two words matched against the wrong table. Found on a
re-audit of the change above, an hour after it was committed. The Norwegian arm
read gammetuft, gamme, teltboplass and flytteveg for rein. Its only
caller hands it decodeLokalitetsart(p.art), which resolves the 159
lokalitetsart codes and deliberately does not fall back to the 674
enkeltminneart ones, for the reason given under the SOSI lookups above.
Gamme and Gammetuft exist only in the
second table. Two of the four terms could not fire, for the same reason
kåtatomt could not, and a unit test asserted isSamiSite('gammetuft', 'NO')
was true — which it is, of a string no register ever produces.
Over 2 095 localities in the Karasjok, Røros, Tromsø and Tysfjord viewports:
| Term | Field | Localities matched |
|---|---|---|
teltboplass |
lokalitetsart | 12 |
flytteveg for rein |
lokalitetsart | 2 |
gammetuft |
lokalitetsart | 0 |
gamme |
lokalitetsart | 0 |
reindrift |
funksjon (SOSI 2510) | 11 |
| § 4.2 (Appendix L) | verneparagraf | 183 |
The two live terms and the funksjon add 9 localities the paragraph does not
already carry. Only two of the 159 lokalitetsart values are Sámi words at all,
so the word test was never going to carry Norway; the law does, and this is the
supplement.
src/utils/format.test.ts now walks SOSI_LOKALITETSART and asserts the set of
values isSamiSite accepts is exactly {Teltboplass, Flytteveg for rein}. A
term that matches nothing the table can produce fails there, and so does a new
Sámi lokalitetsart that ought to be picked up. A handwritten string cannot
pass that test, which was the whole defect.
Record counts (measured 7 August 2026; Baltic rows 16 August 2026)
Every figure below was obtained from the upstream service's own counting
facility on 7 August 2026 — or, for the Estonian and Latvian rows, on
16 August 2026 — using count-only queries (no pagination, no bulk
download). Static snapshots in public/data/ were counted as exact array
lengths. Where Fornland ships a capped snapshot rather than the live
register, the count is the snapshot size, because that is what a user can
actually browse — the register's larger holding is noted in the Method column.
Method reference
- ArcGIS REST —
?where=1%3D1&returnCountOnly=true&f=jsonon the layer's/queryendpoint. - WFS 2.0 —
service=WFS&version=2.0.0&request=GetFeature&typeNames=<layer>&resultType=hits, reading thenumberMatchedattribute. - K-samsök / SOCH —
method=search&version=1.1&query=<K-samsök query>&hitsPerPage=1&recordSchema=presentation, reading<totalHits>. - Wikidata SPARQL —
SELECT (COUNT(DISTINCT ?item) AS ?n)variant of the app's own query, with the sameVALUES ?type/VALUES ?countrysets. - Overpass —
[out:json][timeout:N]; area[...]; (...); out count;(note:[out:count];is not valid Overpass QL and returns "Unknown output format: count"). - Static —
len(json.load(open(f)))via python3.
Denmark
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Fund og Fortidsminder | public:fundogfortidsminder_punkt_fredet |
35,161 | WFS hits | 7 Aug 2026 |
| Fund og Fortidsminder | public:fundogfortidsminder_punkt_marin (wrecks) |
13,716 | WFS hits | 7 Aug 2026 |
| Fredede bygninger (SLKS) | fbb:view_bygning_fredede |
7,134 | WFS hits | 7 Aug 2026 |
| Townscapes | townscapes-dk.json |
2,557 | Static | 7 Aug 2026 |
| Churches (OSM) | amenity=place_of_worship DK |
2,747 | Static (live count 2,755) | 7 Aug 2026 |
| Danish folklore | danish-folklore-pins.json |
900 | Static | 7 Aug 2026 |
| SLKS industrial | slks-industrial.json |
161 | Static | 7 Aug 2026 |
| Denmark subtotal | 62,376 |
Note: Fornland derives four archaeological layers from
fundogfortidsminder_punkt_fredet via client-side filtering. It is counted
once here.
Norway
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Askeladden (Geonorge) | app:Lokalitet, lokalitetskategori=L-ARK |
233,658 | WFS hits + FES filter | 7 Aug 2026 |
| Askeladden (Geonorge) | app:Lokalitet, L-BVF |
54,749 | WFS hits + FES filter | 7 Aug 2026 |
| Askeladden (Geonorge) | app:Enkeltminne, E-BYG (58,852) + E-UTE (7,725) |
66,577 | WFS hits + FES filter | 7 Aug 2026 |
| Askeladden (Geonorge) | app:Enkeltminne, E-TEK (industrial) |
12,035 | WFS hits + FES filter | 7 Aug 2026 |
| Askeladden (Geonorge) | app:Enkeltminne, E-MAR (maritime) |
3,268 | WFS hits + FES filter | 7 Aug 2026 |
| Oslo Byleksikon | oslo-byleksikon-pins.json, georeferenced only |
5,514 | Static (7,580 articles, 5,514 with coords) | 21 Aug 2026 |
| Churches (OSM) | NO | 3,089 | Static (live count 3,087) | 7 Aug 2026 |
| Townscapes | townscapes-no.json |
845 | Static | 7 Aug 2026 |
| Norway subtotal | 379,735 |
Also excluded: the curated walks. public/data/oslo-walks.json carries 6
walks and 57 stops, counted in the shipped file. They were published on 22 Aug
2026 by merging the branch oslo-vandringer, and were on that branch alone
before then. Either way they are deliberately not in the subtotal and not in the 1.8 million,
because that figure counts records held in registers and a walk is editorial.
The prose is written for Fornland. The places the stops sit on are already
counted, once, under Oslo Byleksikon (22), Kulturminnesøk (33) and Brukerminner
(2). Counting the stops again would count the same place twice.
The 33 Kulturminnesøk stops sit on Askeladden localities, which are inside the 1.8 million already; naming Kulturminnesøk on the stop is a credit and not a second source. The 339 pictures in the stop galleries are Wikimedia Commons files and are not records in any register, so they raise no count either.
Brukerminner was documented in the Norway table above and kept out of the source figure while the walk file shipped empty, on the grounds that counting it would claim a source the site does not serve. Publishing the walks serves two of its records, so the figure is 80 from that commit onward. Re-measured in the shipped file on 21 Aug 2026 rather than carried forward from August.
Excluded from the Norwegian subtotal: Enkeltminne E-ARK = 382,194.
This is the single largest exclusion in this audit and it is deliberate.
src/api/heritage.ts:82 fetches E-ARK only to build a
periodByLokId date map; the features are never pushed to sites and never
become pins. E-ARK records are also children of Lokalitet records
(they carry a lokalitetId), so counting both would double count the same
physical site 5-10 times over. The rock-art and barrow layers are additionally
app-side filtered subsets of E-ARK, so they are not added separately either.
Whole-register figures, for reference: app:Lokalitet = 291,238,
app:Enkeltminne = 470,342.
A WFS hit is a row, and a row is not a building. Measured 21 Aug 2026 while
baking the whole building register into public/data/no-bygg/: the two building
layers came to 122,679 hits, and those hits carried 80,657 distinct
kulturminneId, a ratio of 1.52. Geonorge emits one row per polygon part, so
a site with a courtyard or a split footprint is counted as many times as it has
parts. The 54,749 and 66,577 above are therefore rows; the buildings behind them
are about two thirds of that, and the pins on the map are the two thirds. Every
"WFS hits" figure in this file has the same property to some unmeasured degree —
Norway is simply the one that has now been counted both ways.
Sweden
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| K-samsök / RAÄ | kmr_lamningar, itemSpecification="Fornlämning" (318,779) + "Övrig kulturhistorisk lämning" (277,411) + "Möjlig fornlämning" (98,712) + "Ingen antikvarisk bedömning" (62,176) |
757,078 | SOCH <totalHits> |
17 Aug 2026 |
| Bebyggelseregistret | bbra |
2,690 | SOCH <totalHits> |
7 Aug 2026 |
| Churches (OSM) | SE | 3,870 | Static (live count 3,934) | 7 Aug 2026 |
| Townscapes | townscapes-se.json |
1,663 | Static | 7 Aug 2026 |
| Sweden subtotal | 765,301 |
Whole-register reference: kmr_lamningar holds 763,787 records. Fornland
requests four of the seven antikvariska bedömningar (757,078) and leaves out
ej kulturhistorisk lämning (6,709), överförd till annan lämning (2,043) and
utgår på grund av felregistrering (1,527) — assessed as not heritage, merged
into a record already on the map, and entered in error respectively.
The four are disjoint: all six pairs return <totalHits>0 when AND-ed, so
adding them is not double counting. The seven bedömningar together come to
767,357, which is 3,570 more than the register holds, so a few records do carry
two values — but not two of the four we draw.
The wrecks row that used to sit here (8,846) has been removed. It counted
itemName="Fartygs-/båtlämning", which is a slice of kmr_lamningar rather
than a separate dataset. While only two bedömningar were fetched some of those
wrecks were genuinely new; now that all four are fetched, every one of them is
already inside the row above.
Two caveats about what these numbers mean in practice. Each K-samsök request is
capped at 500 records, so one viewport over a dense region returns 2,000
lämningar rather than everything the bbox holds — the register total is what
exists, not what a single pan draws. And itemSpecification conflates two
different things: the antikvariska bedömningen above, and whether the record was
confirmed in the field (bekräftad i fält, 718,358; uppgift om lämning, ej
bekräftad i fält, 41,859). The second pair is a genuine confidence signal and
is not currently read.
Finland
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Museovirasto | rajapinta_suojellut:muinaisjaannos_piste |
41,460 | WFS hits | 7 Aug 2026 |
| Museovirasto | rajapinta_suojellut:suojellut_rakennukset_piste |
2,290 | WFS hits | 7 Aug 2026 |
| Townscapes | townscapes-fi.json |
1,240 | Static | 7 Aug 2026 |
| Churches (OSM) | FI | 1,104 | Static (live count 1,104 — exact match) | 7 Aug 2026 |
| Finnish folklore | finnish-folklore.json |
648 | Static | 7 Aug 2026 |
| Finland subtotal | 46,742 |
Note: Fornland's barrow, rock-art, industrial and wreck layers for Finland are
all CQL_FILTER views of the same muinaisjaannos_piste typeName. Counted
once.
Estonia
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Muinsuskaitseamet | muinsuskaitse:kpo_malestised |
12,536 | WFS hits | 16 Aug 2026 |
| Muinsuskaitseamet | muinsuskaitse:kaitsekat_a + kaitsekat_b, deduplicated on etak_id |
3,127 | WFS hits (520 + 2,798 features) then distinct etak_id |
16 Aug 2026 |
| Muinsuskaitseamet | muinsuskaitse:ajal_haljasala, named places only |
134 | Whole layer fetched (385 rows), 251 habitat classes dropped — appendix J | 17 Aug 2026 |
| Estonia subtotal | 15,797 |
Latvia
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| NKMP | monument:monuments_view_culturalobject_public, is_monument=true |
7,409 | WFS hits + CQL filter | 16 Aug 2026 |
| Latvia subtotal | 7,409 |
Note: the layer returns 10,322 without the filter. The 2,913 difference is
objects listed as culturally valuable without being protected monuments;
src/api/latvia.ts drops them because the register keys their public pages on
an internal id rather than a protection number, so Fornland has no stable link
to offer. Quote 7,409 as Latvia's coverage, not 10,322.
Lithuania
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| KPD | kvr_taskai_enriched/FeatureServer/0 + kvr_poligonai_enriched/FeatureServer/0, deduplicated on unikalus_kodas |
21,202 | returnCountOnly per layer (8,856 + 12,378 features), then distinct unikalus_kodas across both (8,843 + 12,360) minus the one code they share and the eight revoked |
17 Aug 2026 |
| Lithuania subtotal | 21,202 |
Note: the two layers overlap by exactly one code, which is what established that they are the parts and the places rather than two views of one list — see the Lithuania row in §1. Counting only the point layer would have given 8,843, and almost all of it fragments.
Not counted here, so the Lithuanian figure is understated: adding Lithuania also
extended three cross-border queries — wd:Q37 in the Wikidata museum and
historical-event queries, and LT in the Overpass museum and church areas. Those
rows live under Cross-border sources and were not re-measured.
Netherlands
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| RCE | ceo:Rijksmonument, heeftJuridischeStatus = rijksmonument, heeftMonumentAard = onroerend gebouwd |
61,642 | COUNT(DISTINCT ?m) over the whole graph |
19 Aug 2026 |
| RCE | same, heeftMonumentAard = archeologisch |
1,461 | COUNT(DISTINCT ?m) over the whole graph |
19 Aug 2026 |
| RCE | Townscapes (rijksbeschermde stads- en dorpsgezichten) |
472 | full WFS fetch, counted exactly | 19 Aug 2026 |
| Netherlands subtotal | 63,575 |
Note the three things this count is not. It is not 67,496, which is every
ceo:Rijksmonument in the graph including 4,348 revoked listings and 43
provisional ones. It is not a row count: monuments carry 4.0 geometry rows each,
so COUNT(*) would have returned about a quarter of a million. And it does not
include ceo:ArcheologischTerrein, whose 13,025 records exist but carry no
geometry anywhere Fornland could reach, so the Dutch figure understates the
register by that much. See the Netherlands row in §1 for where that was looked
for.
The eleven Dutch world heritage sites are counted under Cross-border, in the Wikidata UNESCO row, and were already there before the Netherlands was added.
Germany
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Landesamt für Denkmalpflege Hessen | denkx:baudenkmal |
50,483 | WFS resultType=hits, whole state |
5 Sep 2026 |
| Landesamt für Denkmalpflege Hessen | denkx:flaechendenkmal |
2,486 | WFS resultType=hits, whole state |
5 Sep 2026 |
| Landesdenkmalamt Berlin | denkmale:denkmale, distinct id |
8,314 | full WFS fetch, three pages, counted exactly (9,578 rows) | 5 Sep 2026 |
| NLD Niedersachsen | protectedsite, fachbereich=Bau und Kunst |
86,876 | OGC API numberMatched |
5 Sep 2026 |
| NLD Niedersachsen | protectedsite, fachbereich=Baudenkmal Gruppe |
9,022 | OGC API numberMatched |
5 Sep 2026 |
| NLD Niedersachsen | protectedsite, fachbereich=Archäologie |
24,032 | OGC API numberMatched |
5 Sep 2026 |
| Denkmalschutzamt Hamburg | five built types, distinct fisid, KD only |
13,836 | full WFS fetch, counted exactly (14,892 rows, 317 NK dropped) | 5 Sep 2026 |
| Archäologisches Museum Hamburg | fundplaetze_elemente_bodendenkmale |
2,681 | full WFS fetch as GML, counted exactly | 5 Sep 2026 |
| LDA Saarland | Denkmalschutz:Baudenkmale |
3,337 | WFS hits, and a full Esri GeoJSON fetch counted the same | 5 Sep 2026 |
| LD Schleswig-Holstein | Denkmalliste, fourteen open-data files, distinct Objektnummer |
11,603 (7,019 with a coordinate) | full download, counted exactly | 8 Sep 2026 |
| LfD Sachsen | L1 + L2 + L3, rows | (179,226 rows) | WFS hits; objects not yet counted, 1,444 per 2,000 rows on the Dresden page; not in the subtotal | 5 Sep 2026 |
| Wikidata, the ten Länder without a service | fourteen designations, P625 present |
234,054 | WDQS COUNT(DISTINCT ?i), reproduced |
5 Sep 2026 |
| Germany subtotal | 201,067 from registers, 234,054 from Wikidata |
Not counted: denkx:archaeologischesDenkmal, 41,387 records, rejected on
content (see the Germany row in §1); denkx:welterbe, 188 Limes polygons,
which are for the timeline's named stops and not yet read. The other fifteen
Länder are surveyed in docs/TYSKLAND.md and enter this table as each one
lands.
Iceland
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Minjastofnun | Minjastofnun:fornleifaskraning_punktar |
33,848 | WFS hits | 7 Aug 2026 |
| Minjastofnun | Minjastofnun:hus_og_mannvirki |
3,763 | WFS hits | 7 Aug 2026 |
| Icelandic folklore | icelandic-folklore-pins.json |
9,167 | Static | 7 Aug 2026 |
| Churches (OSM) | IS | 386 | Static (live count 386 — exact match) | 7 Aug 2026 |
| Townscapes | townscapes-is.json |
18 | Static | 7 Aug 2026 |
| Iceland subtotal | 47,182 |
Note: heritage, barrow and industrial layers are all CQL views of
fornleifaskraning_punktar. Counted once.
Greenland
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| NunaGIS | Nunniffiit_Public/FeatureServer/0 |
5,461 | ArcGIS returnCountOnly, ISDELETED='No' AND ISREMOVED='No' |
17 Aug 2026 |
| NunaGIS | Cultures/FeatureServer/14 (Thule) |
2,901 | ArcGIS returnCountOnly, ISPUBLIC='Yes' |
17 Aug 2026 |
| NunaGIS | Cultures/FeatureServer/10 (Norse) |
626 | ArcGIS returnCountOnly, ISPUBLIC='Yes' |
17 Aug 2026 |
| Churches (OSM) | GL | 62 | Static (live count 62 — exact match) | 7 Aug 2026 |
| Greenland folklore | greenland-folklore.json |
28 | Static | 7 Aug 2026 |
| NunaGIS | Cultures/FeatureServer/7 (whaling) |
13 | ArcGIS returnCountOnly, ISPUBLIC='Yes' |
17 Aug 2026 |
| Townscapes | townscapes-gl.json |
4 | Static | 7 Aug 2026 |
| Greenland subtotal | 9,095 |
The three NunaGIS rows fell by 97 on 17 August 2026. Nothing was removed from the register; the fetchers stopped asking for records the National Museum has withdrawn or not cleared for publication. Unfiltered the layers still answer 5,500 / 2,957 / 628. See appendix E.
Faroe Islands
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Tjóðsavnið | Fornminni/FeatureServer/0 |
456 | ArcGIS returnCountOnly |
18 Aug 2026 |
| Tjóðsavnið | Friðaðir_bygningar/FeatureServer/0 |
113 | ArcGIS returnCountOnly |
18 Aug 2026 |
| Tjóðsavnið | Krígsminni_á_Tvøroyri/FeatureServer/0 |
24 | ArcGIS returnCountOnly |
18 Aug 2026 |
| Tjóðsavnið | Krigsminni_vaga/FeatureServer/0 |
101 | ArcGIS returnCountOnly |
18 Aug 2026 |
| Tjóðsavnið / Jon S. Hansen | Coast_Watch/FeatureServer/0 |
38 | ArcGIS returnCountOnly |
18 Aug 2026 |
| Churches (OSM) | FO | 66 | Static (live count 66 — exact match) | 7 Aug 2026 |
| Faroe Islands subtotal | 798 |
The five Tjóðsavnið rows were counted on 18 August 2026, three days after the
register was connected; until then this table said 66 and the file said no
Faroese national register was integrated. townscapes-fo.json is empty (0)
despite the comment in src/api/townscape.ts claiming 80 records.
England
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Historic England NHLE | Layer 6, Scheduled Monuments | 20,001 | ArcGIS returnCountOnly | 7 Aug 2026 |
| Historic England NHLE | Layer 0, Listed Buildings — snapshot cap | 20,000 | Static buildings-en.json; register holds 379,680 |
7 Aug 2026 |
| Barrows | barrows-en.json |
7,039 | Static | 7 Aug 2026 |
| Roman sites | roman-en.json |
2,742 | Static | 7 Aug 2026 |
| Historic England NHLE | Layer 7, Parks & Gardens | 1,721 | ArcGIS returnCountOnly | 7 Aug 2026 |
| Townscapes | townscapes-en.json |
1,720 | Static | 7 Aug 2026 |
| Industrial | industrial-en.json |
846 | Static | 7 Aug 2026 |
| Rock art | rockart-en.json |
402 | Static | 7 Aug 2026 |
| Historic England NHLE | Layer 9, Protected Wrecks | 57 | ArcGIS returnCountOnly | 7 Aug 2026 |
| England subtotal | 54,528 |
Important for any public claim: Fornland ships a 20,000-record snapshot of English listed buildings, not the full 379,680. The live NHLE layer 0 is only a fallback. Claiming the full NHLE would overstate what is browsable by ~360,000.
Scotland
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| HES Canmore | CANMORE/Canmore_Points/MapServer/0 |
313,579 | ArcGIS returnCountOnly | 7 Aug 2026 |
| Listed buildings | buildings-sc.json — snapshot cap |
5,000 | Static | 7 Aug 2026 |
| Townscapes | townscapes-sc.json |
371 | Static | 7 Aug 2026 |
| Scotland subtotal | 318,950 |
Wales
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| RCAHMW / Coflein | geonode:rcahmw_nmrw_terrestrialsites_rcahmw_bng |
112,894 | WFS hits | 7 Aug 2026 |
| Cadw | inspire-wg:Cadw_ListedBuildings |
30,117 | WFS hits | 7 Aug 2026 |
| Cadw | inspire-wg:Cadw_SAM |
4,237 | WFS hits | 7 Aug 2026 |
| RCAHMW / Coflein | geonode:nmrw_maritimesites_rcahmw_wgs84, nmrw_herit='WRECK' |
5,947 | full WFS fetch, counted exactly (8,612 maritime records in all) | 20 Aug 2026 |
| Local planning authorities | geonode:conservation_areas_wales |
529 | full WFS fetch, counted exactly | 20 Aug 2026 |
| Cadw | geonode:cadw_rhpg_registeredareas |
385 | full WFS fetch, counted exactly | 20 Aug 2026 |
| Wales subtotal | 154,109 |
Confirms the code comment in src/api/wales.ts: Coflein is ~27x Cadw SAM.
Northern Ireland
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| DfC Historic Environment Division | FeatureServer layer 0 (heritage) | 17,919 | Static heritage-ni.json, built by scripts/build-ni-heritage.mjs (register 18,509; 2 carry no geometry, 588 the register itself types NON-ANTIQUITY or DUPLICATE) |
18 Aug 2026 |
| DfC HED | Layer 1 (historic buildings) | 15,857 | Static buildings-ni.json, built by scripts/build-ni-buildings.mjs (register 15,862; 5 carry no geometry) |
18 Aug 2026 |
| DfC HED | Layer 2 (industrial) | 15,780 | Static industrial-ni.json, built by scripts/build-ni-industrial.mjs (register 15,784; 4 carry no geometry) |
18 Aug 2026 |
| DfC HED | Layer 5 (wrecks) | 383 | Static shipwrecks-ni.json, built by scripts/build-ni-shipwrecks.mjs |
18 Aug 2026 |
| DfC HED | Layer 8 (battlesites) | 324 | ArcGIS returnCountOnly | 7 Aug 2026 |
| Northern Ireland subtotal | 50,263 |
Only 9,198 of the 15,857 buildings are listed (grades A, B+, B1, B2 and the legacy B). The rest were surveyed and never listed, or have been delisted; they are carried because the survey record is the interesting part, but the layer should not be described as a list of protected buildings.
The 588 dropped from layer 0 are not a sampling decision. NON-ANTIQUITY is the register's own verdict on a feature it examined and found to be a quarry, a drain or a natural rise; DUPLICATE is a record superseded by another one. Three non-antiquities are kept because the state still schedules them or holds them in State Care, which means the type column was never updated. Plotting the other 588 as heritage pins would state the opposite of what the register says.
Ireland
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| National Monuments Service | SMROpenData/0 |
151,308 | ArcGIS returnCountOnly | 7 Aug 2026 |
| Wreck Inventory of Ireland | Wrecks/FeatureServer/3 |
3,564 | ArcGIS returnCountOnly | 7 Aug 2026 |
| NMS | Holy Wells | 3,025 | ArcGIS returnCountOnly | 7 Aug 2026 |
| Churches (OSM) | IE | 1,881 | Static | 7 Aug 2026 |
| NIAH | buildings-ie.json — National rating only |
937 | Static; register holds 48,327 | 7 Aug 2026 |
| Rock art | rockart-ie.json |
878 | Static | 7 Aug 2026 |
| NMS | Sweathouses | 258 | ArcGIS returnCountOnly | 7 Aug 2026 |
| NMS | Stone Circles | 255 | ArcGIS returnCountOnly | 7 Aug 2026 |
| NMS | Monuments to Visit | 100 | ArcGIS returnCountOnly | 7 Aug 2026 |
| Irish mythology | irish-mythology.json |
50 | Static | 7 Aug 2026 |
| Ireland subtotal | 162,256 |
The thematic NMS FeatureServers (holy wells, stone circles, sweathouses, monuments to visit) are conceptual subsets of the SMR, published as separate services. They are listed for transparency but their 3,638 records are treated as overlap below.
Isle of Man
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Churches (OSM) | IM | 120 | Static | 7 Aug 2026 |
| Isle of Man subtotal | 120 |
Cross-border sources
| Source | Layer/dataset counted | Count | Method | Date |
|---|---|---|---|---|
| Wikidata shipwrecks | shipwrecks-wikidata.json, inside SCAND_BOUNDS |
28,135 | Static (28,143 total; 8 outside bbox) | 7 Aug 2026 |
| Open Plaques | plaques-openplaques.json, inside bbox |
17,319 | Static (17,371 total) | 7 Aug 2026 |
| Churches (OSM) GB | churches-gb.json |
15,320 | Static | 7 Aug 2026 |
| Wikidata museums | fetchMuseums — app LIMIT 8000 |
8,000 | SPARQL COUNT = 9,162 upstream | 7 Aug 2026 |
| OSM museums (Nordic) | tourism=museum, nodes+ways+relations |
5,015 | Overpass out count; (kumi mirror) |
7 Aug 2026 |
| Plaques GB | plaques-gb.json |
5,118 | Static | 7 Aug 2026 |
| Wikidata historical events | fetchWikidataHistoricalEvents — app LIMIT 5000 |
5,000 | SPARQL COUNT = 41,983 upstream | 7 Aug 2026 |
| Wikidata archaeological sites | fetchArchaeologicalSites — app LIMIT 5000 |
5,000 | SPARQL COUNT = 9,327 upstream | 7 Aug 2026 |
| Hillforts | hillforts.json |
4,147 | Static | 7 Aug 2026 |
| Monuments GB | monuments-gb.json |
4,021 | Static | 7 Aug 2026 |
| Museums OSM GB | museums-osm-gb.json |
3,249 | Static | 7 Aug 2026 |
| Historic monuments (OSM) | castle/manor/monastery/fort, Nordic | 2,352 | Overpass out count; |
7 Aug 2026 |
| Roman roads GB | roman-roads-gb.json → routes |
1,434 | Static | 7 Aug 2026 |
| Inscribed stones | inscribed-stones.json |
1,375 | Static | 7 Aug 2026 |
| Museums (HE accredited) | museums-he.json |
1,306 | Static | 7 Aug 2026 |
| Stolpersteine | stolpersteine.json |
919 | Static | 7 Aug 2026 |
| Lighthouses | lighthouses.json |
885 | Static | 7 Aug 2026 |
| Wikidata battles/sieges | fetchWikidataBattles (LIMIT 2000, not reached) |
768 | SPARQL COUNT | 7 Aug 2026 |
| Wikidata megaliths | fetchMegaliths (LIMIT 5000, not reached) |
586 | SPARQL COUNT | 7 Aug 2026 |
| Shipwrecks GB | shipwrecks-gb.json |
511 | Static | 7 Aug 2026 |
| Runestones (OSM) | historic=rune_stone, Nordic |
434 | Overpass out count; |
7 Aug 2026 |
| Fortifications | fortifications.json |
433 | Static | 7 Aug 2026 |
| Norse World | norse-world.json, inside bbox |
172 | Static (532 total; mostly outside bbox) | 7 Aug 2026 |
| Curated conflicts | curated-conflicts.json |
150 | Static | 7 Aug 2026 |
| Thing sites | thingsites.json |
147 | Static | 7 Aug 2026 |
| British folklore | british-folklore.json |
72 | Static | 7 Aug 2026 |
| Hoards | hoards.json |
66 | Static | 7 Aug 2026 |
| Battlefields (curated) | battlefields.json |
39 | Static | 7 Aug 2026 |
| Battlefields GB | battlefields-gb.json |
32 | Static | 7 Aug 2026 |
| Runestones GB | runestones-gb.json |
4 | Static | 7 Aug 2026 |
| Cross-border subtotal | 112,009 |
Four more static files sit beside these and are not additional sources:
wikidata-museums.json (5,483), wikidata-battles.json (868),
wikidata-events.json (4,740) and wikidata-archaeological.json (4,535) are
snapshots of the four live Wikidata queries above, written by
npm run prefetch:wikidata and served only when WDQS throws, answers non-2xx
or answers nothing (src/api/wikidataSnapshot.ts). Measured 2 Sep 2026 at
13:00 UTC from two IPs, WDQS took 16,5 s for the museums against 0,5 s the
evening before and ran the events past their timeout, which left a first-time
visitor with three empty layers; Overpass layers have had the same kind of
fallback since spring. A snapshot is never written into the live cache, so
the next visit asks WDQS again. Refresh with npm run prefetch:wikidata
followed by npm run stamp:data; the generator refuses to write an empty or
shrunken answer.
Grand total
| Territory | Records |
|---|---|
| Sweden | 765,301 |
| Norway | 379,735 |
| Scotland | 318,950 |
| Ireland | 162,256 |
| Wales | 154,109 |
| Cross-border | 112,009 |
| Netherlands | 63,575 |
| Denmark | 62,376 |
| Germany | 201,067 |
| England | 54,528 |
| Northern Ireland | 50,263 |
| Iceland | 47,182 |
| Finland | 46,742 |
| Lithuania | 21,202 |
| Estonia | 15,797 |
| Greenland | 9,095 |
| Latvia | 7,409 |
| Faroe Islands | 798 |
| Isle of Man | 120 |
| GRAND TOTAL (records fetched) | 2,472,514 |
This column sums to the total exactly; check it if you edit a row. Two numbers
were in circulation before 18 August 2026: this table read 2,200,012 while
MethodologyModal.tsx published 2,200,585, a difference of 573 that nothing in
the file accounts for. The table is the measured figure and the modal now
states it.
The 7 August total was 2,004,131, and the 16 August total 2,179,383. Lithuania adds 21,202 on 17 August. Estonia and Latvia add 23,072 between them; Greenland gives back 96 on 17 August by no longer publishing records the National Museum has withdrawn; Sweden adds 152,142 the same day, by asking for all four drawable antikvariska bedömningar instead of two and by dropping a wrecks row that had become a subset of the row above it; and Estonia adds 134 historic green areas, also on 17 August. The Netherlands adds 63,575 on 19 August. Oslo Byleksikon adds a net 267 on 21 August, when the geocoding moved from Nominatim to Kartverket's adresse-API: 499 articles gained a pin because the address parser learned multiword streets ("Karl Johans gate 31") and street-titled articles, 232 lost theirs because the register no longer knows the house number the article's era used, and no pin is better than a pin at the wrong end of Trondheimsveien.
The published claim stays "more than 1.8 million". The dedup floor is still 2,013,726, and the Dutch records are deliberately not added to it. Unlike Lithuania, which no source in the app covered before it, the Netherlands was already reached by the Wikidata and Overpass layers, and the overlap between those and the rijksmonumentenregister has not been measured. Until it is, none of the 63,575 is claimed as distinct. The headline therefore has considerably more room under it than it did, which is the right direction for a figure that is quoted in submitted applications, where a number moving between a submission and its assessment is worse than a conservative one.
Sources that could NOT be counted
Per the "record failures honestly" rule, these are marked uncounted, not estimated:
| Source | Why it could not be counted |
|---|---|
| OSM battlefields (Nordic) | Both overpass-api.de and overpass.kumi.systems returned empty responses / dispatcher errors across repeated attempts on 7 Aug 2026, including per-country and bbox-scoped fallbacks. Counting was abandoned rather than continue hammering a public service. Static floor: 32 (battlefields-gb.json) + 39 (curated) = 71, already included above. The live Nordic OSM battlefield layer is not included in the grand total, so the total is understated by that layer. |
| PAS (Portable Antiquities Scheme) | Endpoint returns HTTP 403 (see Appendix B). Dead source — correctly contributes 0. |
| Geograph | Key-gated, dormant. Contributes 0. |
Two further caveats that make the grand total conservative, not inflated:
- Wikidata museums, historical events and archaeological sites are truncated by
client-side
LIMITclauses (8,000 of 9,162; 5,000 of 41,983; 5,000 of 9,327). Counted at the app's limit, not the register's size — a shortfall of ~45,000 records that exist upstream but are not currently browsable. - English listed buildings are capped at a 20,000-record snapshot against a register of 379,680.
Why this is NOT a count of distinct physical sites
The grand total of 2,179,383 is a count of records fetched across all sources, not a count of distinct places. It cannot honestly be presented as the latter, for these reasons:
- Cross-source duplication is real and unquantified. A Danish medieval
church can appear simultaneously as an SLKS listed building, an OSM
place_of_worship, a townscape component and a Wikidata item. Fornland deduplicates within some layers (museums by QID then by coordinate+name; Norwegian features bykulturminneId; townscapes by 3-decimal coordinate) but performs no global cross-source deduplication, because the registers share no common identifier. - Parent/child structures. Norwegian
Lokalitetrecords containEnkeltminnechildren; Irish thematic layers are subsets of the SMR. The worst of these (E-ARK, 382,194) is already excluded above, but L-BVF vs E-BYG/E-UTE overlap remains. - Different things counted as one. Folklore pins are legend locations, not monuments; plaques commemorate people, not sites; Oslo Byleksikon entries are encyclopaedia articles about streets and buildings.
Quantifying the maximum plausible overlap — every OSM church against its national register, every museum across four museum sources, all shipwreck layers against the Wikidata wreck set, all townscapes against listed-building registers, all folklore pins, all Irish thematic subsets, Norwegian L-BVF vs E-BYG/E-UTE, and Oslo Byleksikon against Norwegian registers — gives a ceiling of 186,859 potentially duplicated or "soft" records.
Even in the extreme worst case where every single one of those is a duplicate, the floor is 1,992,524 distinct records — the 7 August figure of 1,817,272, less the 96 Greenland has since withdrawn, plus the 23,072 Baltic records, the 152,142 Swedish ones and the 134 Estonian green areas, none of which overlap anything in the ceiling above. That is also 2,179,383 minus the whole ceiling, which is the arithmetic the two figures have to agree on. The realistic figure sits between that floor and 2,179,383, because most of those 186,859 are genuinely distinct (an OSM church is often not separately listed; most shipwrecks appear in only one register).
The 63,575 Dutch records are outside both numbers. Their overlap with the Wikidata and Overpass layers, which already reached the Netherlands, has not been measured, so they raise neither the ceiling nor the floor until it is.
Recommended public claim
"More than 1.8 million heritage records from 79 sources across seventeen heritage jurisdictions in fourteen countries and territories."
This is the defensible formulation, and it survives the hardest questions an assessor can ask:
- It is rounded down from 2,271,447, and down past the worst-case deduplication floor of 2,013,726. It holds even if a sceptic rejects every overlapping record listed above.
- It says "records", not "sites" or "places" — which is literally what is counted, and avoids claiming a deduplication that has not been performed.
- The per-source table above lets any individual figure be re-derived with a single curl command.
If a phrase describing places rather than records is needed, the safe wording is "more than 1.8 million heritage records covering well over a million distinct places" — the second clause is comfortable, since even the extreme worst case leaves 1.85 million records and the duplication ceiling is under 10% of the total.
Verdict on the claims that were live before this audit:
- "1.5 million" (
MethodologyModal.tsx,docs/metadata-normalization.md) and "1.6 million" (AboutModal.tsx, ten language variants) were both supportable — each sits below even the worst-case floor of 1,817,272. The problem was never that either number was too high. It was that the app stated three different figures simultaneously (1.5m in the page source, 1.6m in the About modal, 1,000,000+ on the welcome sheet), so an assessor who opened both would see the project contradict itself. - Both also used the noun "sites". Fornland performs no global cross-source deduplication — the registers share no common identifier — so "sites" was the part that would not have survived an interview.
Resolved 7 August 2026. Every public occurrence now reads
1.8 million records (Danish/Norwegian/Swedish: registrerede kulturminder
— register entries, not distinct places), across index.html, the web
manifest, api/og.ts, api/category.ts, src/App.tsx, AboutModal,
MethodologyModal, src/i18n, both PDF generators and this document.
MethodologyModal additionally carries the records-vs-places explanation in
full, in English and Danish, so the caveat is reachable from inside the app
rather than only from this file.