← Fornland

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::searchPAScurrently 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::fetchGeographPhotoskey-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


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:

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 searchAllBedomningarkulturarvsdata.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

C. Restrictive (non-open) licences

The application text claims everything is open public data. Two exceptions:

  1. 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.
  2. 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:

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 â — , 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:

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.

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.

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 sourceUrlcommunities-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.

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åtatomtKå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

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.jsonsnapshot 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 fetchMuseumsapp 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 fetchWikidataHistoricalEventsapp LIMIT 5000 5,000 SPARQL COUNT = 41,983 upstream 7 Aug 2026
Wikidata archaeological sites fetchArchaeologicalSitesapp 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.jsonroutes 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:

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:

  1. 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 by kulturminneId; townscapes by 3-decimal coordinate) but performs no global cross-source deduplication, because the registers share no common identifier.
  2. Parent/child structures. Norwegian Lokalitet records contain Enkeltminne children; 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.
  3. 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.

"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:

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:

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.