Skip to content
C.W.K.
Stream
Lesson 01 of 05 · published

The Atocha Story

~12 min · swift, navigation, walking, arrival, design

Level 0Lost
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

The walk that arrived on the wrong street

Dad walked to Madrid Puerta de Atocha with Pathfinder guiding him. The destination was a pin, and the pin stood where pins usually stand: on the station's centre point. Pathfinder did exactly what a guided walk does. It asked for a walking route to that point, followed it fix by fix, and spoke each turn in Pippa's voice. The route ended on the street two levels below the departures hall, and there it said "turn left". The hall was up long stairs. By the route's own standard nothing failed: it reached its destination. The destination was the failure.

The commit that answered it puts the diagnosis in one sentence: "Every map app does this: a destination is a 2D point with no level." To a map, a place is a latitude and a longitude. A station is a building with many doors on several levels, and the point in its middle is none of them. The walking network a provider routes on mostly lacks the stairs, escalators and levels inside such a building, and GPS cannot tell one level from another. So turn-by-turn guidance ends where the map's knowledge ends, and inside a station that is well before the door.

What a better ending is made of

Coop todo #369 asked for "level-aware arrival at large buildings (entrances, stairs, floors) instead of the map's center point". Architecture invariant 21 is the rule it became: a walk to a large building ends at its door, on its level. The pieces are this track's lessons:

  1. A door instead of a centre. An Entrance is a point, how many floors it sits above the street the walk arrives on, a short Korean note, and who said so. The provider is asked to route to the door, never to the centre.
  2. A ranking of witnesses. Dad's own confirmed door outranks the door Pippa's pin names, which outranks OpenStreetMap's tags.
  3. A careful question to OpenStreetMap. One Overpass query that tells a door on the station's own outline from the museum's door across the road.
  4. A memory. The phone keeps the doors it learned, the one Dad confirmed and the ones he turned down, so a station's door is known again with no network.
  5. A different last 100 metres. No more turns. Plain words about where the door is from where Dad faces, Pippa's note, and the barometer's verdict on the level. The street under the door does not count as arrival.

Which walks get the treatment

A café's door is a few metres from its pin, so most walks end exactly as before. LargeVenue.matches decides which places are big enough to need a door. A place qualifies if it already carries an entrance; if its Apple or Google category is a train station, an airport, a transit or subway station, a mall, a stadium, a museum or a university; or if its name holds a word for one in the languages of the places Dad travels (station, estación, gare, Bahnhof, aeropuerto, terminal, mall, museo, catedral, 터미널, 공항, 백화점, 駅, 空港 and more), matched with case and accents folded away. Korean gets one more rule: a name that ends in 역 or 몰, like 서울역 or 코엑스몰. Ending, never beginning, so 역삼동 stays a neighbourhood.

Now the awkward part. "Madrid Puerta de Atocha" says nothing about a station, and the kit's test asserts exactly that: Atocha's name does not match. A pin from Pippa carries no category either. The rescue comes from the map's data instead of the words. When OpenStreetMap reports that the destination stands inside a station, or inside a building of three or more levels, OSMEntrances.Reading.isLargeVenue is true, and the app's EntranceFinder notes that centre for as long as it runs. The station that started all this qualifies by its building, not by its name.

Two boundaries hold the design in place. Only a walk is planned to a door. A car going to Atocha is routed to the building, and if the destination was already routed to a door, the driving plan strips it back to the centre, because a pedestrian entrance on a footpath is no place to send a car. And none of it runs in a simulation, which asks no network and has no barometer.

Reaching the point is not reaching the place. When the model of a thing is smaller than the thing (two numbers for a building of many doors and levels), the system needs the missing dimensions written down somewhere, and it needs to know which destinations are missing them.

Code

PathfinderKit, Arrival/Entrance.swift: LargeVenue.matches (verbatim excerpt)·swift
  public static func matches(_ place: Place) -> Bool {
    if place.entrance != nil { return true }
    let category = (place.category ?? "").lowercased().replacingOccurrences(of: " ", with: "")
    if categories.contains(where: { category.contains($0) }) { return true }
    let name = fold(place.name)
    if words.contains(where: { name.contains(fold($0)) }) { return true }
    let bare = name.trimmingCharacters(in: .whitespaces)
    return suffixes.contains { bare.hasSuffix($0) && bare.count > 1 }
  }
A minimal sketch: the large-venue test with trimmed word lists, run on six places (compiled with swiftc and run)·swift
import Foundation

// A minimal sketch of Pathfinder's LargeVenue name test: a category, a word
// in several languages (case and accents folded), or a Korean suffix.
// Lists trimmed from the kit; the logic is the same.
let words = ["station", "estacion", "gare", "bahnhof", "airport", "aeropuerto",
             "terminal", "mall", "shopping", "museum", "cathedral",
             "터미널", "공항", "백화점", "쇼핑", "駅", "空港"]
let suffixes = ["역", "몰"]
let categories = ["trainstation", "train_station", "airport", "subway_station", "shoppingmall", "museum"]

func fold(_ text: String) -> String {
  text.folding(options: [.caseInsensitive, .diacriticInsensitive], locale: nil)
}

func isLargeVenue(name: String, category: String? = nil) -> Bool {
  let category = (category ?? "").lowercased().replacingOccurrences(of: " ", with: "")
  if categories.contains(where: { category.contains($0) }) { return true }
  let name = fold(name)
  if words.contains(where: { name.contains(fold($0)) }) { return true }
  let bare = name.trimmingCharacters(in: .whitespaces)
  return suffixes.contains { bare.hasSuffix($0) && bare.count > 1 }
}

for (name, category) in [
  ("Estación de Sevilla-Santa Justa", nil),
  ("서울역", nil),
  ("역삼동 카페", nil),
  ("코엑스몰", nil),
  ("Madrid Puerta de Atocha", nil),
  ("Madrid Puerta de Atocha", "Train Station"),
] as [(String, String?)] {
  print(isLargeVenue(name: name, category: category) ? "large " : "plain ", name, category ?? "")
}

External links

Exercise

Pick three large buildings you actually use: a station, an airport or a mall, and an office or university building. For each, find where your map app drops its pin, then find the door you really walk through. Write down the entrance you would hand a navigator: the door's coordinate, how many floors it sits above the street you arrive on, and one short line a stranger could follow. Then run the sketch's name test on the three names. Which one would it miss, and what other evidence would catch it?
Hint
Satellite view and OpenStreetMap's own map both show entrances as small marks on building outlines. Count floors from the street you actually arrive on, not from the building's ground floor: a door can be "two up" from one side of a station and level with the street on the other.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.