← Research

Research note · August 28, 2026

Mapping 5,525 places museums never recorded as coordinates.

Museum records often know where they are without saying so: a caption in period French, a spelling nobody uses anymore, an excavation hierarchy. Turning 5,525 of those into map points, and the places the process went wrong.

linkedcultureatlasgeocodingdigitalhumanitiesculturalheritageengineering


If you have worked with a photographic collection you have seen records like these. A 19th-century Bonfils photograph captioned "Ruines de la salle Hypostyle." A Maxime Du Camp titled "Nubie. Ibsamboul. Partie septentrionale du Spéos d'Hathor."

Neither has a latitude or longitude anywhere in the catalog. Both know exactly where they are, in the way a caption knows things: implicitly, in the language of the period, and only if you happen to know which place used to be called "Ibsamboul."

LinkedCulture's Atlas, at linkedculture.org/atlas, now plots 5,525 of those implicit locations on a real map. Every point came from a record's own text rather than a curated location field or hand entry. This is what that took, including the parts I got wrong, in case you are considering something similar with your own collection.

Why a map rather than the visualization we already had

LinkedCulture already had something map-shaped: the Topic Galaxy, a force-directed layout of embedding space where distance means semantic similarity. It answers "what does this collection think is related to what." It cannot answer "where was this photograph taken." A cluster of Egyptian temple photographs sitting together in the Galaxy tells you they are about similar things. It does not tell you that one was taken at Karnak and another four hundred kilometers south at Abu Simbel.

So the Atlas is a real map: basemap tiles, pan and zoom, and clustering that collapses as you zoom out. Vanilla Leaflet with leaflet.markercluster, not a stylized layout borrowing the shape of a map.

Two cohorts, one pipeline

The first cohort is 3,156 records from fourteen 19th-century travel photographers already in the index: Bonfils, Frith, Beato, Du Camp, Teynard, Anderson, Sebah, Zangaki and others. Their portfolios do not stay inside Egypt. Du Camp's 1852 album is titled "Égypte, Nubie, Palestine et Syrie." Anderson worked mostly in Rome. Delamotte photographed the Crystal Palace in London. Filtering to "Egypt only" would have thrown away the actual shape of these expeditions, so the pilot kept every location a portfolio touched.

The second cohort came from noticing that the Museo Egizio's catalog already carries a structured provenance field: a comma-separated excavation hierarchy like "Egypt, Luxor / Thebes, Deir el-Medina, Tomb of Kha (TT8)." That is 2,579 records where the place names were curated by the museum's own catalogers rather than written as free text. Those did not need a language model at all. They got a deterministic parser that walks the hierarchy from most specific to least, matches against known Egyptological sites, and folds anything finer-grained into a note.

If your own collection has a field like that, start there. It is cheaper, more accurate, and it carries the institution's authority rather than mine.

Both cohorts write into the same table, record_augmentations, under method='location_mentions'. Nothing about museum_records, the authoritative source table, changes. Augmentations sit beside the record, never inside it.

Why a small local model could not do this

Before writing any extraction code I checked whether a small model already running on the server, qwen3:1.7b via Ollama, could handle it without an external API call. It could not, for a reason worth knowing if you are budgeting this work: the task is not named-entity recognition, it is historical geography.

"Louqsor" is French for Luxor. "Geezeh" is a period spelling of Giza. Those are pattern-matchable. "Ethiopia" is not. One Getty record is titled "The Temple of Amara, Ehtiopia". In 19th-century European usage, "Ethiopia", from the classical Greek Aethiopia, meant the lands south of Egypt generally, Nubia and Sudan, not the modern country. A model or a person without that piece of period usage will place the record two thousand kilometers from where it belongs, with total confidence, because "Ethiopia" is a real country and geocoding it succeeds.

The extraction pass caught that one and flagged it rather than resolving it silently. A pattern-matcher has no way to be suspicious of a word that is not wrong, only wrong for the century it is reading.

Across both cohorts, 643 records carried a flag of that kind: a period name, a garbled title, a museum's own uncertain attribution, or a photograph of a relocated antiquity. Several Nubian temples were moved stone by stone during the 1960s Aswan High Dam rescue campaign, so those get pinned where the photograph shows them, not where the stones stand today.

Resolving each place once

603 distinct places came out of the photographer cohort alone, and Luxor, Karnak and Philae recur constantly. A language model asked to resolve "Karnak" four hundred separate times has no memory between requests, so that is four hundred independent guesses at one coordinate, each slightly different and each a paid call.

So there is a location_gazetteer table: normalized place name, modern administrative region, coordinates, known raw-text aliases, and where the resolution came from. Every extraction batch checks it before re-deriving a place it has already seen. It is deliberately separate from record_augmentations, which is per-record. The gazetteer is per-place and persists across pilots, so the next cohort that mentions Karnak does not resolve Karnak again.

Letting a second source catch the first one's mistakes

Every coordinate an extraction pass proposes is a historically informed estimate rather than a verified fact, so each one gets checked against Nominatim, OpenStreetMap's geocoder, before it is trusted. Of 656 distinct places, 8 came back with the two sources more than fifty kilometers apart. Reading those eight was more useful than the 648 that agreed.

"Egypt" geocoded to Etowah County, Alabama, a town that shares the name. "Nile Valley" matched the Nile Valley Hotel, a specific building in Luxor. A record naming the biblical region of "Moab" and another naming "Edom" both resolved to "Jordan Estates", a residential subdivision in Noveleta, Cavite, in the Philippines, because the best text match for a place name plus the word "Jordan" was a subdivision literally called that, ten thousand kilometers from the region east of the Dead Sea. "Switzerland" landed on the Chinese Embassy's street address in Bern, which is at least inside the country.

None of those are bugs in Nominatim. They are the correct behavior of a text search returning its single best match, and a short common place name is where that kind of search is weakest. All eight were resolved by hand in favor of whichever source named the right entity, which was the historically informed estimate in six cases and Nominatim in the other two.

If you take one design idea from this, take that one. Disagreement between two reasonable methods is worth more attention than agreement, and eight arguments are a manageable amount of reading.

A city is not a temple

The map shipped, and the first real problem appeared within a day. Zoom into Asyut, a governorate capital of nearly half a million people, and 181 pins fanned out across specific streets, spiderfied apart so they would be clickable rather than stacked on one pixel. It looked precise. It was fabricated: nothing in any of those 181 records says more than "Asyut". Spreading them across real streets invented a level of detail the source text never had.

My first instinct was to scatter the pins slightly for readability, which was the right instinct aimed at the wrong problem. A temple complex like Deir el-Medina really is a bounded place a few hundred meters across, and nudging markers apart there is a fair approximation of "found somewhere in this place". A governorate is not a temple. The fix had to tell the two apart.

So all 656 distinct places got classified as a site (a temple, tomb or building with spatial extent measured in tens or hundreds of meters) or an area (a settlement, region or country, named with nothing more specific in the source text). This is the same historical-geography judgment the extraction needed, applied to places rather than records. 31 turned out to be areas, covering 1,565 records: Asyut, Cairo, Rome, Jerusalem, "Egypt" itself, the Dead Sea, the First and Second Cataracts.

Getting a second opinion caught something the first pass missed. Deir el-Medina, a genuinely bounded site, has 773 records at one point, more than any area including Asyut. The real problem was never areas versus sites, it was crowding, and crowding happens at busy sites too. So areas and any site with more than a handful of coincident records now collapse into a single marker, dashed and amber for an area, sized by how many records it holds, which opens a docked panel of the actual photographs instead of pretending to know exact positions.

The Egypt marker's panel: a photo grid of nineteenth-century views, docked beside a still-visible, still-pannable map
The Egypt marker's panel: a photo grid of nineteenth-century views, docked beside a still-visible, still-pannable map

That panel had its own bad first draft: a scrollable list of links with no pictures, which defeated the premise. A records dashboard with text where the images should be does not help anyone picture a place they have never been. The panel now shows a grid of photographs, docked to the side rather than covering the map, because the map is still the thing doing the explaining.

Saying why each pin is where it is

One gap remained after the precision was honest. A popup could tell you a record was placed at "El Guisr", but not why. The record's own words that produced the placement were sitting in the pipeline output and simply were not reaching the map.

My first fix hid them behind a small "?" that someone had to notice and click, which was the wrong instinct on a site whose premise is showing the work. Reasoning that only exists if someone clicks a question mark is reasoning most people never see.

So it became an always-visible line under every place name: Why mapped here:, no click required.

Writing that sentence honestly was harder than it looked. The obvious version, "the record names this place directly", is only true for 62% of the 5,525 mentions. For the other 38% real interpretive work happened: period spellings, French rendered into English, a site identified from what a photograph depicts rather than what its caption says. Asserting direct naming for those would have been the same fabricated confidence as the Asyut pins, just moved into a sentence instead of a coordinate.

A popup's always-visible "Why mapped here" line, for a case where the record does name the place directly: "Burgos Cathedral, from the River"
A popup's always-visible "Why mapped here" line, for a case where the record does name the place directly: "Burgos Cathedral, from the River"

The sentence is composed once, offline, from three honest states, rather than generated per record by a model narrating a decision after the fact. When the record's own words contain the resolved name, it says so and quotes them. When they do not, but the extraction left a real note explaining the resolution, that note becomes the reason. When neither applies, the sentence says exactly that: the record reads this, it was matched to that, and the record does not use that name. And when a museum's own catalog carries a "(?)" hedge on a findspot, that gets surfaced verbatim, as the museum's uncertainty rather than something the pipeline quietly resolved past.

Where it landed

5,525 location mentions, across 5,517 records, from 656 distinct places, live at linkedculture.org/atlas. Every coordinate traces to two sources, the record's own text and a public geocoder cross-check, and every pin says why in the open.

A few things I would carry into a similar project:

Historical terminology traps need a historically informed reader rather than a bigger pattern-matcher. "Ethiopia" is not a spelling problem, and neither is a temple relief captioned with a pharaoh's biblical name instead of his own.

Precision has a shape, and a marker should match it. Rendering a governorate and a temple the same way was its own quiet dishonesty, and it took a second opinion to see the whole of it.

A resolved place name is a reusable fact. The gazetteer means the next collection, in a different language, starts by checking what is already known.

This is a pilot across two cohorts, not the whole corpus of several hundred thousand records. What it suggests is that the location information was already present, in text museums have been writing for a hundred and fifty years, and mostly needed reading rather than creating.

If you have a collection with captions like these and want to compare notes, I would be glad to hear from you: contact@EdwardMonk.com.

In this note

Records

2 objects in the LinkedCulture index. Each page links on to the holding museum.