1
0
This repository has been archived on 2026-08-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
tera/src/cities/sf.ts
T
karti f81d5218d4 feat: SFO, LAX, both bridges, a road that reads as a road, and aeroplanes that move
**The aeroplanes were stuck because the wire could not describe motion.**
`WireAircraft` carried position, altitude and heading and nothing else, so the
client could only interpolate between the last two observations: every aircraft
replayed a segment it had already flown, arrived at the newest known point, and
sat still until the next poll landed five to fifteen seconds later. The feed had
the missing numbers the whole time and the server threw them away. Sampled live
from `api.adsb.lol/v2/point` while writing this — `gs` ground speed, `track`,
`baro_rate`, plus `r` registration and `t` type designator. They are on the wire
now in SI, aircraft dead-reckon along their own track and correct toward the
truth when a fix lands, and the click card an anonymous visitor gets says
"B739 · N68834". That last part is the enrichment FR24 was wanted for, obtained
from an ODbL feed we may actually republish.

**SFO and LAX exist.** A new `engine/airports.ts` composes an airport from
runways, taxiways, aprons and terminal masses, with markings drawn on a canvas
rather than modelled; the pattern of the runways is what the eye recognises from
altitude, long before any building does. SFO is the two crossing pairs on the bay
fill; LAX is the four parallels either side of the terminal horseshoe, plus the
Southland fields under the traffic that actually flies there.

**The Golden Gate and the Bay Bridge are those bridges.** One kit in
`engine/bridges.ts`, because a suspension bridge is a repeated tower, a catenary
main cable, a series of hangers and a deck — so both are configurations rather
than two private implementations. The Bay Bridge carries the real 2013 topology:
two suspension towers west of Yerba Buena, one east, then the piered causeway.
The freeway stopped being a wireframe overlay and became a road, with shoulders,
a median, and lane markings as texture.

**And the board got faster while all of that landed.** California went from
728,744 triangles and 562 draw calls to 391,169 and 371 — headroom from 2.8% to
47.8%. The Bay Area board is 506,550 triangles lighter than before this work.
Two things paid for it:

- `transmission: 0.08` on the aircraft cockpit glass. three.js runs a full
  transmission backdrop pass whenever any rendered material has transmission
  above zero, re-drawing the entire opaque scene into a second target every
  frame — so the city was rendering terrain, every block and every freeway piece
  TWICE. Measured by patching only that number in a copy of the built bundle:
  703,267 tris / 562 draws with it, 398,608 / 371 without. The material was
  already `transparent: true, opacity: 0.86`, so it was buying nothing.
- Flatness-adaptive terrain LOD, which collapses runs of lattice cells wherever
  the height and colour agree with the quad replacing them. The coastline is
  provably untouched — a patch collapses only when every point is on land and
  agrees about `park` — and a test asserts the drawn footprint matches the
  cell-by-cell area to 1e-6. `createTerrain` got *faster*: the vertices it stops
  emitting cost more than the flatness scan costs to run.

**The budget now watches the boards this was built on.** There was no `bay-area`
or `socal` cell — so SFO, LAX and both bridges all landed in frames nothing
measured, which is how a cap you do not have looks from the inside. Both are in
the matrix now with caps set from measurement, and the rationale lives in the
harness because JSON cannot hold a comment.

Two known defects ship with this, both recorded in TODO.md rather than hidden:

- `bay-area.desktop` drops about one frame in twenty (p50 16.7, p95 33.3). It is
  desktop-only and not fill rate — mobile runs the same 2.26 M triangles at a
  comparable pixel count and holds 16.7 flat — which points at the 2048 shadow
  map desktop uses against handheld's 1024. Measured at the commit before this
  work with the same harness: identical p95 33.3. Pre-existing, and invisible
  until the cell existed.
- The aeroplane glyph is still about 1.5x the Golden Gate's main span at chapter
  zoom, down from 2.5x. `GLYPH_MAX_SCALE` is 52 because the raw scale at the far
  end of the California orbit is 51.0 at a 60-degree field of view, and 26 —
  tried first — put the glyph at 0.0123 of the frame against the 0.012 where the
  wings stop resolving. The real fix is to clamp against the camera's focus
  distance rather than the aircraft's, which is a signature change.

Tests 1020 -> 1137. Typecheck, build, eight budget cells, no-binaries,
provenance, zero-config boot, dependency licences, arena source hashes and the
UI smoke across two viewports and two access tiers all pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 05:06:12 -07:00

2949 lines
93 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* San Francisco — a Tera city pack.
*
* Pure data. Coastlines, hills, districts, landmarks and camera chapters,
* traced by hand to be recognizable rather than survey-accurate: the
* peninsula's tip, the Golden Gate, the Bayshore's industrial hook down to
* Candlestick, Ocean Beach's long straight edge.
*
* **Traced from scratch, on purpose.** Importing this geometry from
* OpenStreetMap would have been faster and would have made the whole pack
* ODbL — a share-alike obligation that cannot live inside an Apache-2.0 repo
* without infecting it. Original expression is the only kind of map data this
* project can actually give away. See ARCHITECTURE.md §3.2.
*
* The relief is the reason this city was first: San Francisco is a place whose
* plan you cannot read without its section. Twin Peaks and Mount Davidson are
* the wall the Sunset hides behind, Potrero and Bernal are why the Mission
* feels like a floor, and the AI cluster in Hayes Valley sits in the bowl below
* all of it. A flat San Francisco is a street grid on a napkin.
*
* ## The pack is now the whole Bay Area
*
* It began as the seven-by-seven city with the Peninsula as a distant chapter
* the camera pulled back over, and the pull-back was over empty water, because
* there was nothing south of the county line to look at. There is now: the
* Peninsula down to San Jose, the South Bay, the East Bay from Richmond to
* Fremont, and Marin north to San Rafael with Mount Tamalpais on it.
*
* Everything added here is **additive**. Not one San Francisco coordinate moved
* — the city renders exactly as it did — and where a new landmass meets an old
* one it retraces the old one's vertices rather than being drawn alongside it.
* See `PENINSULA` and `MARIN_NORTH` for why that matters more than it sounds.
*
* ## This pack requires the focus-region lattice
*
* The map is now 0.85° × 0.89°, about twenty times the area it was. At the
* ~45 m cells San Francisco is built at, a uniform lattice over that is 3.7M
* points and roughly eleven seconds of build — which is the failure
* ARCHITECTURE.md §5 predicted for Los Angeles, arriving early. So this pack
* declares `coarseFactor` and three `focusRegions`, and **it is not loadable
* until `World.buildField` honours them**: San Francisco and the two places a
* chapter puts the camera on the ground stay at 45 m, and the ~90 km of bay
* between them is built at 270 m, where nothing is lost because there is
* nothing there but water and the flanks of two mountain ranges.
*/
import type { Airport } from "../engine/airports.ts";
import type { Bridge, City, District, Hill, Landmark, LatLng } from "../engine/types.ts";
/**
* San Francisco geography for the atlas, all coordinates `[lat, lng]`.
*
* Traced to be recognizable rather than survey-accurate: the peninsula's tip,
* the Golden Gate, the Bayshore's industrial hook down to Candlestick, Ocean
* Beach's long straight edge. Building scatter is clipped to these polygons so
* nothing ever lands in the water.
*
* Written from scratch for SF. The NYC atlas this project takes its interaction
* model from has an equivalent file, and not one coordinate of it transfers —
* different city, different coastline, and above all different *relief*. NYC
* renders on flat ground; the whole point of an SF map is that SF is not flat,
* so this file also carries HILLS, which has no NYC counterpart.
*/
// ---- Coastlines -----------------------------------------------------------
/**
* The city proper, traced clockwise from Fort Point (the Golden Gate Bridge's
* south anchorage) — north shore east to the Ferry Building, down the Bayshore
* to Candlestick, west along the San Mateo county line, then north up Ocean
* Beach and around Lands End.
*/
export const SAN_FRANCISCO: LatLng[] = [
// North shore, west -> east
[37.8106, -122.4775], // Fort Point, under the bridge
[37.8085, -122.473],
[37.8062, -122.468], // Crissy Field west
[37.8055, -122.462], // Crissy Field
[37.807, -122.455], // Marina west
[37.8075, -122.448], // Marina Green
[37.8068, -122.44],
[37.8065, -122.43], // Fort Mason
[37.808, -122.425], // Aquatic Park
[37.8085, -122.42], // Hyde Street Pier
[37.8095, -122.415], // Fisherman's Wharf
[37.81, -122.41], // Pier 39
[37.8085, -122.405], // Pier 27
[37.8035, -122.399], // Piers 15-19
[37.7975, -122.393], // Ferry Building
[37.793, -122.388], // Rincon / Bay Bridge landing
[37.786, -122.3865], // South Beach
[37.78, -122.387], // Oracle Park / China Basin
[37.776, -122.3855], // Mission Bay
[37.77, -122.383],
[37.763, -122.382], // Pier 70 / Dogpatch
[37.756, -122.38], // Potrero Point
[37.748, -122.38], // Islais Creek
[37.74, -122.376], // Bayview
[37.733, -122.37], // Hunters Point, the shipyard hook
[37.728, -122.366],
[37.723, -122.377], // India Basin south
[37.718, -122.383], // Candlestick Point
[37.711, -122.39],
[37.708, -122.398], // county line, southeast corner
// San Mateo county line, east -> west
[37.708, -122.41],
[37.7085, -122.43],
[37.709, -122.455],
[37.71, -122.48],
[37.711, -122.498], // Lake Merced
[37.713, -122.506], // Fort Funston
// Pacific shore (Ocean Beach), south -> north
[37.72, -122.507],
[37.73, -122.509],
[37.74, -122.51],
[37.75, -122.5105],
[37.76, -122.511],
[37.77, -122.512],
[37.778, -122.513], // Cliff House / Sutro Baths
[37.782, -122.512],
[37.786, -122.506], // Lands End
[37.788, -122.499],
[37.7885, -122.491], // China Beach
[37.791, -122.487],
[37.7935, -122.4835], // Baker Beach
[37.799, -122.4805], // Presidio bluff
[37.805, -122.479],
];
/**
* Everything on the west and south sides of the bay: the Peninsula from the
* county line down through San Mateo and Palo Alto, and the Santa Clara Valley
* round the bottom of the bay to the hills east of San Jose.
*
* One polygon rather than a chain of towns, because it is one piece of ground —
* the bay is the hole in it, not the thing that divides it. The name is a
* stretch by the time it reaches Alum Rock and it is kept anyway; splitting the
* land into a "peninsula" and a "south bay" would only invent a border that
* nobody standing on it could find.
*
* The first six points and the last six are **not free coordinates**. They are
* `SAN_FRANCISCO`'s southern boundary, retraced vertex for vertex in reverse,
* so the two shore plates tile exactly along the county line. Anything else
* leaves either a sliver of open water down the middle of Daly City or two
* coplanar plates fighting for the same pixels at y=0.
*/
export const PENINSULA: LatLng[] = [
// Bay shore, north -> south, from the county line at Candlestick.
[37.708, -122.398],
[37.7, -122.39], // Sierra Point, Brisbane
[37.69, -122.386], // Oyster Point
[37.678, -122.383], // Point San Bruno
[37.665, -122.393],
[37.652, -122.39], // South San Francisco, north of the field
[37.643, -122.377], // where the airport's fill begins
[37.64, -122.358], // SFO, northeast corner
[37.623, -122.354], // SFO's bay edge — the runways are built out onto the mud
[37.607, -122.357], // SFO, southeast corner
[37.598, -122.345], // Burlingame
[37.59, -122.33], // Coyote Point
[37.582, -122.318], // Seal Point, San Mateo
[37.578, -122.27], // Foster City, the north edge of the fill
[37.573, -122.25], // San Mateo Bridge, west toe
[37.558, -122.245],
[37.545, -122.24], // Redwood Shores
[37.535, -122.215], // Bair Island and the port
[37.522, -122.19], // Redwood City
[37.51, -122.17], // Menlo Park bayfront
[37.498, -122.152],
[37.487, -122.14], // Dumbarton, west toe
[37.472, -122.115], // the Palo Alto baylands
[37.462, -122.095],
[37.45, -122.07], // Shoreline, Mountain View
[37.44, -122.04], // Moffett Field
[37.432, -122.01], // the Sunnyvale marsh
[37.428, -121.975], // the mouth of the Guadalupe, at Alviso
[37.435, -121.945], // the bottom of the bay
// The next three are shared with EAST_BAY, reversed, for the same reason the
// county line is shared with the city.
[37.443, -121.925], // Milpitas, where the East Bay shore takes over
[37.42, -121.9],
[37.38, -121.845], // the Alum Rock hills
// South and west along the frame, under Mount Hamilton and over the range.
[37.395, -121.75],
[37.185, -121.75],
[37.185, -122.0],
[37.19, -122.33], // the coast again, north of Año Nuevo
// Pacific shore, south -> north. Empty for fifty miles, which is the whole
// point of it: the range comes down to the water and nobody built here.
[37.23, -122.36],
[37.27, -122.4], // Pescadero
[37.32, -122.41], // San Gregorio
[37.375, -122.42], // Tunitas
[37.43, -122.435],
[37.47, -122.445], // Half Moon Bay
[37.5, -122.47], // Pillar Point
[37.535, -122.512], // Montara
[37.565, -122.518], // Devil's Slide
[37.598, -122.508], // Pacifica
[37.63, -122.5], // Sharp Park
[37.66, -122.494], // Mussel Rock
[37.69, -122.5], // Daly City
[37.706, -122.504],
// SAN_FRANCISCO's county line, retraced in reverse.
[37.713, -122.506],
[37.711, -122.498],
[37.71, -122.48],
[37.709, -122.455],
[37.7085, -122.43],
[37.708, -122.41],
];
/**
* The Marin Headlands across the Golden Gate. Present for context and for the
* bridge to land on — the north side of the Gate is most of what you see from
* any camera pointed at the bridge, and without it the span ends in fog.
*
* Its inland edge, the last five points, was a frame cut rather than a
* coastline: it is where the map used to stop. `MARIN_NORTH` picks those five
* up vertex for vertex and carries on, so the cut is now an interior seam
* between two plates that meet exactly.
*/
export const MARIN: LatLng[] = [
[37.829, -122.532], // Point Bonita
[37.8255, -122.527],
[37.824, -122.517], // Rodeo Cove
[37.8265, -122.505],
[37.829, -122.493], // Kirby Cove
[37.832, -122.483], // Golden Gate Bridge, north anchorage
[37.8335, -122.476], // Fort Baker
[37.839, -122.479], // Horseshoe Bay
[37.846, -122.483],
[37.857, -122.4855], // Sausalito
[37.868, -122.49],
[37.874, -122.4945],
// inland edge, kept close: Marin is here to carry the bridge and close the
// Gate, not to compete with the city for the eye.
[37.8765, -122.5125],
[37.8685, -122.5255],
[37.855, -122.5335],
[37.8425, -122.5395],
[37.8335, -122.5405],
];
/**
* Marin proper: Tiburon and Mill Valley, Corte Madera and San Rafael on the bay
* side, and the Tamalpais massif dropping straight into the Pacific on the
* other.
*
* Richardson Bay is deliberately left open — the water between Sausalito and
* Strawberry is a real inlet and closing it would weld Tiburon to the
* headlands. The two plates only touch at its head, which is also the only
* place the ground is continuous.
*/
export const MARIN_NORTH: LatLng[] = [
[37.874, -122.4945], // the head of Richardson Bay, shared with MARIN
[37.882, -122.492],
[37.886, -122.477], // Strawberry
[37.878, -122.463], // Strawberry Point
[37.87, -122.452], // Belvedere and Tiburon
[37.888, -122.448],
[37.905, -122.462], // Paradise Cay
[37.92, -122.492], // Corte Madera
[37.935, -122.487], // Larkspur Landing
[37.945, -122.481], // Point San Quentin — the Richmond bridge lands here
[37.958, -122.487], // San Rafael, up its creek
[37.972, -122.47],
[37.99, -122.445], // Point San Pedro and China Camp
[38.005, -122.47],
[38.025, -122.52], // Terra Linda, at the north frame
[38.028, -122.6],
[38.01, -122.635],
// The Pacific side. Bolinas Ridge and Tamalpais come down to the sea, which
// is why there is no road along this coast and no town on it.
[37.965, -122.64],
[37.93, -122.638],
[37.905, -122.63], // Stinson Beach
[37.885, -122.605], // Muir Woods, the coast turning southeast
[37.865, -122.588],
[37.845, -122.57], // Muir Beach
// MARIN's inland edge, retraced in reverse.
[37.8335, -122.5405],
[37.8425, -122.5395],
[37.855, -122.5335],
[37.8685, -122.5255],
[37.8765, -122.5125],
];
/**
* The East Bay: Fremont round to Richmond, and the Diablo country behind the
* ridge.
*
* This replaces a twelve-point stub whose "inland" edge was two points at the
* frame — enough to land the Bay Bridge on and nothing else. The shore is now
* traced properly, including the two things about it that read from the air:
* the Oakland field out on its spit, and the way the whole east side runs
* dead straight from Hayward to Richmond because it is diked salt marsh rather
* than a coastline.
*
* Everything east of the ridge is genuinely land — the Diablo valley — so the
* polygon runs to the frame rather than stopping at the crest and leaving a
* cliff into nothing.
*/
export const EAST_BAY: LatLng[] = [
[37.443, -121.925], // Milpitas, shared with PENINSULA
[37.462, -121.965], // Coyote Creek
[37.485, -122.01], // Warm Springs
[37.5, -122.05],
[37.512, -122.082], // Dumbarton, east toe
[37.535, -122.108], // Newark
[37.562, -122.128], // Coyote Hills
[37.598, -122.14], // the Hayward marsh
[37.628, -122.128], // San Mateo Bridge, east toe
[37.655, -122.152], // San Lorenzo
[37.69, -122.185], // San Leandro
[37.706, -122.205],
[37.718, -122.228], // the spit the Oakland field's long runway sits on
[37.732, -122.238],
[37.738, -122.226],
[37.745, -122.222], // Bay Farm
[37.757, -122.252], // Alameda, south shore
[37.775, -122.305], // Alameda Point
[37.795, -122.33], // the outer harbour
[37.818, -122.333], // the mole the Bay Bridge comes down on
[37.84, -122.32], // Emeryville
[37.858, -122.318], // the Berkeley Marina
[37.87, -122.322],
[37.885, -122.335], // the Albany Bulb
[37.905, -122.345], // Point Isabel
[37.918, -122.368], // Point Richmond
[37.93, -122.39], // RichmondSan Rafael Bridge, east toe
[37.945, -122.378], // the inner harbour
[37.962, -122.395],
[37.978, -122.425], // Point San Pablo
// San Pablo Bay's south shore, west -> east.
[37.995, -122.37],
[38.01, -122.31], // Pinole
[38.025, -122.26], // Rodeo
// The frame, and back down to Milpitas along the boundary PENINSULA shares.
[38.03, -122.2],
[38.03, -121.75],
[37.395, -121.75],
[37.38, -121.845],
[37.42, -121.9],
];
export const ALCATRAZ: LatLng[] = [
[37.8285, -122.4245],
[37.8285, -122.4215],
[37.8262, -122.4205],
[37.8248, -122.4222],
[37.8255, -122.4248],
];
export const YERBA_BUENA: LatLng[] = [
[37.8125, -122.3675],
[37.8125, -122.3615],
[37.8085, -122.3595],
[37.8055, -122.3635],
[37.8075, -122.3685],
];
/** Treasure Island — the flat man-made rectangle north of Yerba Buena. */
export const TREASURE_ISLAND: LatLng[] = [
[37.829, -122.3735],
[37.829, -122.3655],
[37.8175, -122.3635],
[37.8145, -122.3695],
[37.818, -122.3745],
];
export const ANGEL_ISLAND: LatLng[] = [
[37.8695, -122.4345],
[37.868, -122.4245],
[37.8625, -122.4205],
[37.8575, -122.4265],
[37.859, -122.4365],
[37.8645, -122.4405],
];
export const ISLANDS = [ALCATRAZ, YERBA_BUENA, TREASURE_ISLAND, ANGEL_ISLAND];
/** Everything the buildings may stand on. */
export const LANDMASSES = [SAN_FRANCISCO, PENINSULA, MARIN, MARIN_NORTH, EAST_BAY, ...ISLANDS];
// ---- Parks and open space -------------------------------------------------
/**
* Golden Gate Park: three miles long, half a mile deep, and famously a
* rectangle — the one piece of SF geography you can trace with four points.
*/
export const GOLDEN_GATE_PARK: LatLng[] = [
[37.7745, -122.5105],
[37.7735, -122.454],
[37.7655, -122.4535],
[37.7665, -122.51],
];
export const PRESIDIO: LatLng[] = [
[37.8106, -122.4775],
[37.801, -122.479],
[37.791, -122.483], // Baker Beach
[37.7885, -122.474],
[37.7875, -122.46], // Lombard gate
[37.799, -122.453],
[37.8055, -122.462],
[37.8085, -122.473],
];
export const MOUNT_SUTRO: LatLng[] = [
[37.7625, -122.4625],
[37.7615, -122.4535],
[37.7535, -122.4545],
[37.7525, -122.462],
];
export const DOLORES_PARK: LatLng[] = [
[37.7615, -122.4285],
[37.761, -122.4245],
[37.7585, -122.4248],
[37.759, -122.4288],
];
export const BUENA_VISTA: LatLng[] = [
[37.7705, -122.4455],
[37.7695, -122.4395],
[37.7655, -122.4405],
[37.7665, -122.446],
];
export const MCLAREN_PARK: LatLng[] = [
[37.7245, -122.4235],
[37.7225, -122.4095],
[37.7145, -122.412],
[37.7165, -122.4245],
];
export const LINCOLN_PARK: LatLng[] = [
[37.7855, -122.5055],
[37.7845, -122.4955],
[37.7795, -122.4965],
[37.7805, -122.506],
];
/**
* Twin Peaks and Mount Davidson are parks in fact as well as on this map —
* the two highest points in the city are public open space, which is why the
* ridge reads green from every angle while Nob Hill and Bernal, just as steep,
* are solid houses to the summit.
*/
export const TWIN_PEAKS: LatLng[] = [
[37.7605, -122.4535],
[37.7595, -122.4425],
[37.7495, -122.4415],
[37.7485, -122.4525],
];
export const MOUNT_DAVIDSON: LatLng[] = [
[37.7435, -122.4605],
[37.7425, -122.4495],
[37.7335, -122.4505],
[37.7345, -122.4605],
];
export const GLEN_CANYON: LatLng[] = [
[37.7455, -122.4475],
[37.7448, -122.4395],
[37.7375, -122.4405],
[37.7385, -122.4485],
];
// ---- Open space beyond the city -------------------------------------------
/**
* The greenbelt is the reason the Bay Area looks the way it does from above,
* and it is not decoration: the watershed lands, the regional park districts
* and the ridge preserves are why fifteen contiguous cities on the Peninsula
* stop dead at the same contour line instead of running over the top.
*
* Traced coarsely on purpose. These are large, soft-edged holdings, and at the
* 270 m cells the bay is built at, four points is as much shape as survives.
*/
/** San Bruno Mountain, the wall between the city and the Peninsula. */
export const SAN_BRUNO_MOUNTAIN: LatLng[] = [
[37.702, -122.452],
[37.699, -122.412],
[37.676, -122.418],
[37.679, -122.458],
];
/** Sweeney Ridge and the Montara hills, above Pacifica. */
export const SWEENEY_RIDGE: LatLng[] = [
[37.625, -122.482],
[37.62, -122.432],
[37.545, -122.475],
[37.552, -122.512],
];
/**
* The San Francisco watershed: Crystal Springs, San Andreas Lake, and the
* Cahill Ridge above them. Closed to the public since 1930 and visibly
* untouched, which is why the west side of the Peninsula reads as one
* unbroken green band from the air.
*/
export const CRYSTAL_SPRINGS_WATERSHED: LatLng[] = [
[37.6, -122.44],
[37.594, -122.375],
[37.49, -122.325],
[37.5, -122.42],
];
/** The Skyline ridge, north half — Kings Mountain down to Windy Hill. */
export const SKYLINE_NORTH: LatLng[] = [
[37.49, -122.39],
[37.478, -122.31],
[37.36, -122.21],
[37.375, -122.3],
];
/** The Skyline ridge, south half — Monte Bello, Black Mountain, Castle Rock. */
export const SKYLINE_SOUTH: LatLng[] = [
[37.365, -122.215],
[37.35, -122.135],
[37.21, -122.03],
[37.228, -122.13],
];
/** Almaden Quicksilver and the Santa Teresa hills, closing San Jose to the south. */
export const SANTA_TERESA: LatLng[] = [
[37.245, -121.845],
[37.238, -121.79],
[37.198, -121.8],
[37.205, -121.855],
];
/** Tamalpais and the Marin watershed. */
export const TAMALPAIS: LatLng[] = [
[37.96, -122.63],
[37.952, -122.545],
[37.888, -122.555],
[37.896, -122.635],
];
/** Tilden and Wildcat Canyon, along the Berkeley ridge. */
export const BERKELEY_RIDGE: LatLng[] = [
[37.925, -122.255],
[37.918, -122.212],
[37.858, -122.19],
[37.866, -122.235],
];
/** Redwood and Chabot, along the Oakland ridge. */
export const OAKLAND_RIDGE: LatLng[] = [
[37.855, -122.195],
[37.848, -122.145],
[37.72, -122.085],
[37.73, -122.135],
];
export const MOUNT_DIABLO_PARK: LatLng[] = [
[37.925, -121.97],
[37.918, -121.86],
[37.838, -121.87],
[37.845, -121.975],
];
/** Mission Peak and the Fremont hills. */
export const MISSION_PEAK_PARK: LatLng[] = [
[37.555, -121.91],
[37.548, -121.855],
[37.45, -121.83],
[37.46, -121.885],
];
/**
* The south bay marshes — the Palo Alto baylands, Shoreline, the Alviso ponds
* and Coyote Hills. Rendered as open space because that is what they are, even
* though half of them are salt evaporator, not grass.
*/
export const BAYLANDS: LatLng[] = [
[37.475, -122.12],
[37.468, -122.095],
[37.44, -122.045],
[37.452, -122.085],
];
export const ALVISO_MARSH: LatLng[] = [
[37.462, -121.978],
[37.455, -121.94],
[37.425, -121.955],
[37.432, -122.005],
];
export const COYOTE_HILLS: LatLng[] = [
[37.575, -122.128],
[37.568, -122.098],
[37.538, -122.105],
[37.545, -122.135],
];
export const PARKS = [
GOLDEN_GATE_PARK,
PRESIDIO,
MOUNT_SUTRO,
TWIN_PEAKS,
MOUNT_DAVIDSON,
GLEN_CANYON,
DOLORES_PARK,
BUENA_VISTA,
MCLAREN_PARK,
LINCOLN_PARK,
SAN_BRUNO_MOUNTAIN,
SWEENEY_RIDGE,
CRYSTAL_SPRINGS_WATERSHED,
SKYLINE_NORTH,
SKYLINE_SOUTH,
SANTA_TERESA,
TAMALPAIS,
BERKELEY_RIDGE,
OAKLAND_RIDGE,
MOUNT_DIABLO_PARK,
MISSION_PEAK_PARK,
BAYLANDS,
ALVISO_MARSH,
COYOTE_HILLS,
];
/** Lake Merced, the only real inland water. */
export const LAKE_MERCED: LatLng[] = [
[37.7285, -122.4965],
[37.7275, -122.4885],
[37.7215, -122.4875],
[37.7185, -122.4935],
[37.7235, -122.4985],
];
/** Stow Lake, inside Golden Gate Park — small, but it reads at chapter zoom. */
export const STOW_LAKE: LatLng[] = [
[37.7712, -122.4785],
[37.7708, -122.4745],
[37.7688, -122.4742],
[37.7685, -122.4785],
];
/**
* San Andreas Lake and Crystal Springs, lying in the fault trace itself — the
* two of them draw a straight line thirteen miles long, which is the most
* legible thing the San Andreas does anywhere on this map.
*/
export const SAN_ANDREAS_LAKE: LatLng[] = [
[37.6, -122.408],
[37.598, -122.4],
[37.575, -122.386],
[37.573, -122.394],
];
export const CRYSTAL_SPRINGS: LatLng[] = [
[37.552, -122.372],
[37.549, -122.362],
[37.497, -122.318],
[37.494, -122.33],
];
/** Lake Merritt, a tidal lagoon with downtown Oakland wrapped around it. */
export const LAKE_MERRITT: LatLng[] = [
[37.808, -122.264],
[37.806, -122.256],
[37.798, -122.253],
[37.797, -122.263],
];
/**
* Only the three above, and no reservoir in the hills.
*
* `terrain.ts` draws inland water as a flat plate 0.05 units above sea level,
* which is right for a lagoon and wrong for Chabot or Calaveras: those sit a
* few hundred metres up, and at 3.6× exaggeration the terrain around them
* stands four units clear of a lake that stays at the waterline. The plate
* would be buried under the hill it is supposed to be in. Ponding water at
* altitude needs the engine to carry a surface height, and until it does,
* leaving them out is more honest than drawing them wrong.
*/
export const INLAND_WATER = [
LAKE_MERCED,
STOW_LAKE,
SAN_ANDREAS_LAKE,
CRYSTAL_SPRINGS,
LAKE_MERRITT,
];
// ---- Relief ---------------------------------------------------------------
/**
* SF's hills, as radial peaks summed into a heightfield by terrain.ts.
*
* This is the file's reason for existing. San Francisco is a city whose plan
* you cannot read without its section: Twin Peaks and Mount Davidson are the
* wall the Sunset hides behind, Potrero and Bernal are why the Mission feels
* like a floor, and the AI cluster in Hayes Valley sits in the bowl below all
* of it. A flat SF is just a street grid on a napkin.
*
* `elevation` is metres above sea level at the summit; `radius` is roughly
* where the hill meets the flats, in degrees of latitude. Both are eyeballed
* to look right in section, not surveyed.
*/
export const HILLS: Hill[] = [
// The central ridge — the city's spine
{ name: "Twin Peaks", lat: 37.7544, lng: -122.4477, elevation: 281, radius: 0.016 },
{ name: "Mount Davidson", lat: 37.7382, lng: -122.4544, elevation: 283, radius: 0.015 },
{ name: "Mount Sutro", lat: 37.7576, lng: -122.4585, elevation: 275, radius: 0.012 },
{ name: "Golden Gate Heights", lat: 37.7527, lng: -122.4737, elevation: 200, radius: 0.014 },
{ name: "Forest Hill", lat: 37.7466, lng: -122.4676, elevation: 195, radius: 0.012 },
{ name: "Diamond Heights", lat: 37.7414, lng: -122.4404, elevation: 190, radius: 0.011 },
// Downtown's hills — small, steep, and the ones people mean by "SF hills"
{ name: "Nob Hill", lat: 37.793, lng: -122.4161, elevation: 103, radius: 0.008 },
{ name: "Russian Hill", lat: 37.801, lng: -122.418, elevation: 90, radius: 0.007 },
{ name: "Telegraph Hill", lat: 37.8024, lng: -122.4058, elevation: 84, radius: 0.005 },
{ name: "Rincon Hill", lat: 37.786, lng: -122.39, elevation: 30, radius: 0.005 },
{ name: "Pacific Heights", lat: 37.7925, lng: -122.438, elevation: 113, radius: 0.011 },
{ name: "Lone Mountain", lat: 37.779, lng: -122.452, elevation: 116, radius: 0.008 },
// The southern and eastern hills
{ name: "Bernal Heights", lat: 37.743, lng: -122.416, elevation: 130, radius: 0.009 },
{ name: "Potrero Hill", lat: 37.758, lng: -122.4, elevation: 91, radius: 0.009 },
{ name: "Corona Heights", lat: 37.7655, lng: -122.438, elevation: 156, radius: 0.007 },
{ name: "Buena Vista", lat: 37.7685, lng: -122.442, elevation: 175, radius: 0.008 },
{ name: "Bayview Hill", lat: 37.7205, lng: -122.394, elevation: 130, radius: 0.008 },
{ name: "McLaren Ridge", lat: 37.7195, lng: -122.418, elevation: 170, radius: 0.011 },
{ name: "Hunters Point Hill", lat: 37.729, lng: -122.38, elevation: 71, radius: 0.007 },
// The Presidio and the western headlands
{ name: "Presidio Ridge", lat: 37.7985, lng: -122.4665, elevation: 110, radius: 0.01 },
{ name: "Lands End", lat: 37.784, lng: -122.5045, elevation: 90, radius: 0.008 },
// Marin — the wall on the far side of the Gate, taller than anything in SF
{ name: "Marin Headlands", lat: 37.8275, lng: -122.5065, elevation: 280, radius: 0.016 },
{ name: "Wolfback Ridge", lat: 37.8425, lng: -122.5, elevation: 240, radius: 0.014 },
{ name: "Slacker Ridge", lat: 37.8345, lng: -122.4915, elevation: 210, radius: 0.009 },
{ name: "Fort Baker Rise", lat: 37.8375, lng: -122.4785, elevation: 120, radius: 0.007 },
{ name: "Sausalito Ridge", lat: 37.8595, lng: -122.5015, elevation: 230, radius: 0.013 },
{ name: "Angel Island", lat: 37.8625, lng: -122.4305, elevation: 240, radius: 0.007 },
// East Bay — the front slope above Montclair. The crest proper is another
// two hundred metres up and a little further east; it is in EAST_BAY_HILLS
// below, and this entry is now the shoulder it stands on rather than the
// whole of the far side of the bay.
{ name: "Oakland Hills", lat: 37.83, lng: -122.235, elevation: 330, radius: 0.03 },
];
/**
* The Santa Cruz Mountains — the western wall, and the reason the Peninsula is
* a strip.
*
* This is the single fact that explains the shape of everything south of the
* county line. The range runs unbroken from Sweeney Ridge to Loma Prieta, it is
* two to three times the height of anything in San Francisco, and it comes down
* within four miles of the water for most of its length. Fifteen cities, two
* freeways and a railroad are all squeezed into what is left, in that order,
* which is why they are in that order.
*
* Written as a close chain rather than as a few big summits, and the radii are
* the load-bearing numbers rather than the elevations. `elevationAt` fades a
* hill off as `(1 - d²)²`, which reaches **exactly zero** at `radius` — so two
* summits spaced further apart than their radii have sea level between them.
* Sixteen peaks at their true heights and honest 3 km radii produced a row of
* golf balls with the Peninsula visible through the gaps. Spacing is now under
* a radius everywhere, which is also closer to the truth: this is a broad
* range with one crest, not a line of cones.
*/
export const SANTA_CRUZ_MOUNTAINS: Hill[] = [
{ name: "Sweeney Ridge", lat: 37.607, lng: -122.452, elevation: 380, radius: 0.03 },
{ name: "Montara Mountain", lat: 37.57, lng: -122.462, elevation: 540, radius: 0.032 },
{ name: "Cahill Ridge", lat: 37.535, lng: -122.43, elevation: 520, radius: 0.032 },
{ name: "Crystal Springs Ridge", lat: 37.5, lng: -122.395, elevation: 560, radius: 0.032 },
{ name: "Kings Mountain", lat: 37.465, lng: -122.355, elevation: 640, radius: 0.034 },
{ name: "Skyline at Woodside", lat: 37.43, lng: -122.315, elevation: 700, radius: 0.034 },
{ name: "Windy Hill", lat: 37.395, lng: -122.27, elevation: 640, radius: 0.034 },
{ name: "Skyline at Page Mill", lat: 37.36, lng: -122.225, elevation: 700, radius: 0.036 },
{ name: "Monte Bello", lat: 37.342, lng: -122.19, elevation: 780, radius: 0.036 },
{ name: "Black Mountain", lat: 37.322, lng: -122.15, elevation: 850, radius: 0.038 },
{ name: "Monte Bello Ridge", lat: 37.29, lng: -122.115, elevation: 800, radius: 0.036 },
{ name: "Castle Rock", lat: 37.25, lng: -122.095, elevation: 900, radius: 0.038 },
{ name: "El Sombroso", lat: 37.232, lng: -122.055, elevation: 850, radius: 0.04 },
{ name: "Loma Chiquita", lat: 37.215, lng: -122.02, elevation: 850, radius: 0.042 },
{ name: "Mount Thayer", lat: 37.202, lng: -121.975, elevation: 900, radius: 0.042 },
{ name: "Sierra Azul", lat: 37.19, lng: -121.93, elevation: 950, radius: 0.044 },
];
/**
* The low hills the Peninsula's towns are actually built on. None of them are
* mountains; all of them are the difference between a flat strip and a strip
* with a west side worth living on.
*/
export const PENINSULA_HILLS: Hill[] = [
{ name: "San Bruno Mountain", lat: 37.6895, lng: -122.435, elevation: 400, radius: 0.018 },
{ name: "Buri Buri Ridge", lat: 37.635, lng: -122.44, elevation: 220, radius: 0.012 },
{ name: "Hillsborough", lat: 37.575, lng: -122.365, elevation: 200, radius: 0.014 },
{ name: "Belmont Hills", lat: 37.505, lng: -122.31, elevation: 200, radius: 0.014 },
{ name: "Emerald Hills", lat: 37.465, lng: -122.283, elevation: 180, radius: 0.012 },
{ name: "Stanford Foothills", lat: 37.41, lng: -122.185, elevation: 190, radius: 0.014 },
{ name: "Los Altos Hills", lat: 37.36, lng: -122.14, elevation: 210, radius: 0.014 },
{ name: "Santa Teresa Hills", lat: 37.222, lng: -121.812, elevation: 340, radius: 0.016 },
];
/**
* The east wall: the Berkeley and Oakland ridge, the Fremont hills, and Mount
* Diablo behind them.
*
* The ridge is the more important of the two, because it is what the city looks
* at. Vollmer and Grizzly are over five hundred metres — twice Twin Peaks — and
* they run for thirty miles without a gap, so from anywhere in San Francisco the
* east side of the bay is a single unbroken skyline with a city at its foot.
* Diablo is a third again as tall and forty miles off, and does the same job for
* the whole basin.
*/
export const EAST_BAY_HILLS: Hill[] = [
// The ridge, unbroken from Point Pinole to the Fremont hills. Same spacing
// rule as the Santa Cruz chain: nothing is further from its neighbour than
// its own radius, because a gap here would put a hole through the wall the
// whole east side of the map is supposed to be.
{ name: "San Pablo Ridge", lat: 37.968, lng: -122.3, elevation: 360, radius: 0.028 },
{ name: "Sobrante Ridge", lat: 37.945, lng: -122.268, elevation: 400, radius: 0.028 },
{ name: "Wildcat Peak", lat: 37.912, lng: -122.245, elevation: 420, radius: 0.028 },
{ name: "Vollmer Peak", lat: 37.885, lng: -122.215, elevation: 570, radius: 0.026 },
{ name: "Grizzly Peak", lat: 37.872, lng: -122.205, elevation: 520, radius: 0.022 },
{ name: "Round Top", lat: 37.848, lng: -122.192, elevation: 500, radius: 0.026 },
{ name: "Redwood Peak", lat: 37.822, lng: -122.168, elevation: 500, radius: 0.028 },
{ name: "Chabot Ridge", lat: 37.792, lng: -122.14, elevation: 420, radius: 0.03 },
{ name: "Fairmont Ridge", lat: 37.76, lng: -122.115, elevation: 400, radius: 0.03 },
{ name: "Lake Chabot Hills", lat: 37.728, lng: -122.09, elevation: 380, radius: 0.03 },
{ name: "Hayward Hills", lat: 37.694, lng: -122.062, elevation: 380, radius: 0.032 },
{ name: "Palomares Ridge", lat: 37.655, lng: -122.03, elevation: 450, radius: 0.034 },
{ name: "Walpert Ridge", lat: 37.615, lng: -121.995, elevation: 500, radius: 0.04 },
{ name: "Sunol Ridge", lat: 37.572, lng: -121.945, elevation: 600, radius: 0.042 },
{ name: "Mission Ridge", lat: 37.542, lng: -121.912, elevation: 700, radius: 0.038 },
{ name: "Mission Peak", lat: 37.512, lng: -121.88, elevation: 780, radius: 0.04 },
{ name: "Monument Peak", lat: 37.47, lng: -121.86, elevation: 780, radius: 0.036 },
{ name: "Berryessa Hills", lat: 37.43, lng: -121.83, elevation: 500, radius: 0.04 },
{ name: "Alum Rock Hills", lat: 37.38, lng: -121.795, elevation: 550, radius: 0.04 },
{ name: "Hamilton Foothills", lat: 37.34, lng: -121.775, elevation: 550, radius: 0.038 },
{ name: "Silver Creek Hills", lat: 37.3, lng: -121.77, elevation: 500, radius: 0.038 },
// Behind the ridge, and deliberately *not* joined to it. Briones and Las
// Trampas are separated from the crest by the San Pablo and San Ramon
// valleys, and Diablo stands alone out of flat country — so these three are
// isolated on purpose, and the saddles between them are the point.
{ name: "Briones", lat: 37.935, lng: -122.13, elevation: 450, radius: 0.03 },
{ name: "Las Trampas Ridge", lat: 37.815, lng: -122.045, elevation: 570, radius: 0.03 },
{ name: "Mount Diablo", lat: 37.881, lng: -121.914, elevation: 1173, radius: 0.045 },
{ name: "Diablo North Peak", lat: 37.895, lng: -121.885, elevation: 950, radius: 0.03 },
];
/**
* Marin, north of the headlands — and Tamalpais, which was missing.
*
* Seven hundred and eighty-four metres, eight miles from the Ferry Building,
* with nothing in front of it. Every northward view from the city ends on this
* mountain, and the map was ending on open water instead.
*/
export const MARIN_HILLS: Hill[] = [
{ name: "Mount Tamalpais", lat: 37.9235, lng: -122.5965, elevation: 784, radius: 0.034 },
{ name: "Blithedale Ridge", lat: 37.905, lng: -122.56, elevation: 400, radius: 0.018 },
{ name: "Bolinas Ridge", lat: 37.955, lng: -122.615, elevation: 500, radius: 0.028 },
{ name: "Loma Alta", lat: 37.965, lng: -122.575, elevation: 450, radius: 0.026 },
{ name: "Ring Mountain", lat: 37.905, lng: -122.485, elevation: 180, radius: 0.012 },
{ name: "San Rafael Hill", lat: 37.985, lng: -122.528, elevation: 350, radius: 0.016 },
];
/**
* Every hill in the pack. `HILLS` is left meaning what its comment says it
* means — San Francisco's own — because that array is the argument for this
* whole file and shrinking it into a general list would lose the point.
*/
export const ALL_HILLS: Hill[] = [
...HILLS,
...SANTA_CRUZ_MOUNTAINS,
...PENINSULA_HILLS,
...EAST_BAY_HILLS,
...MARIN_HILLS,
];
// ---- Streets --------------------------------------------------------------
/**
* Market Street, the diagonal that breaks SF's two grids against each other.
* Everything downtown is oriented to it, so it is the first line to draw and
* the one that makes the rest of the street plan legible.
*/
export const MARKET_STREET: LatLng[] = [
[37.7946, -122.3949], // Ferry Building
[37.7897, -122.3999],
[37.7855, -122.4055],
[37.781, -122.411],
[37.7765, -122.417],
[37.772, -122.423],
[37.768, -122.429],
[37.762, -122.435], // Castro
];
export const THE_EMBARCADERO: LatLng[] = [
[37.8085, -122.4165],
[37.8065, -122.4095],
[37.803, -122.4025],
[37.7975, -122.3945],
[37.7925, -122.389],
[37.7855, -122.3875],
];
export const VAN_NESS: LatLng[] = [
[37.8055, -122.4225],
[37.7965, -122.4215],
[37.7855, -122.4205],
[37.7745, -122.4195],
];
export const GEARY: LatLng[] = [
[37.7865, -122.4035],
[37.7855, -122.42],
[37.7845, -122.4455],
[37.7805, -122.4735],
[37.7795, -122.4955],
];
export const MISSION_STREET: LatLng[] = [
[37.7905, -122.3965],
[37.7845, -122.4045],
[37.7785, -122.4125],
[37.7715, -122.4195],
[37.7635, -122.4195],
[37.7525, -122.4185],
[37.7405, -122.4235],
];
export const NINETEENTH_AVE: LatLng[] = [
[37.8045, -122.4715],
[37.7845, -122.4745],
[37.7645, -122.4755],
[37.7395, -122.4755],
[37.7175, -122.4735],
];
export const THIRD_STREET: LatLng[] = [
[37.7885, -122.3985],
[37.7795, -122.3915],
[37.7695, -122.3895],
[37.7565, -122.3885],
[37.7395, -122.3895],
[37.7255, -122.3925],
];
export const HIGHWAY_101: LatLng[] = [
[37.8065, -122.4275], // Lombard, feeding the bridge
[37.7985, -122.4215],
[37.7885, -122.4065],
[37.7755, -122.4055],
[37.7565, -122.4055],
[37.7365, -122.4055],
[37.7135, -122.3985],
];
export const INTERSTATE_280: LatLng[] = [
[37.7775, -122.3925],
[37.7665, -122.3975],
[37.7555, -122.4055],
[37.7425, -122.4145],
[37.7275, -122.4415],
[37.7105, -122.4595],
];
// ---- The roads out of town ------------------------------------------------
/**
* Beyond the county line the freeways stop being streets and start being the
* only reason the shape of the region makes sense: everything on the Peninsula
* is arranged in three parallel bands — 101 on the mud, El Camino and the
* railroad in the middle, 280 along the foot of the hills — and every town on
* this map is a rung between them.
*
* `BAYSHORE_101` and `JUNIPERO_SERRA_280` begin on the last coordinate of
* `HIGHWAY_101` and `INTERSTATE_280` respectively, so the ribbons meet at the
* county line instead of ending in mid-air over Visitacion Valley.
*/
export const BAYSHORE_101: LatLng[] = [
[37.7135, -122.3985], // where HIGHWAY_101 leaves the city
[37.695, -122.4],
[37.673, -122.406],
[37.65, -122.41],
[37.628, -122.4],
[37.605, -122.385],
[37.583, -122.36],
[37.56, -122.33],
[37.535, -122.295],
[37.51, -122.255],
[37.487, -122.218],
[37.465, -122.18],
[37.443, -122.145],
[37.42, -122.105],
[37.405, -122.065],
[37.393, -122.02],
[37.383, -121.975],
[37.37, -121.935],
[37.352, -121.905],
[37.336, -121.888],
];
export const JUNIPERO_SERRA_280: LatLng[] = [
[37.7105, -122.4595], // where INTERSTATE_280 leaves the city
[37.695, -122.455],
[37.673, -122.443],
[37.65, -122.428],
[37.628, -122.412],
[37.605, -122.398],
[37.58, -122.383],
[37.553, -122.36],
[37.528, -122.336],
[37.505, -122.313],
[37.48, -122.288],
[37.455, -122.255],
[37.432, -122.222],
[37.41, -122.19],
[37.388, -122.155],
[37.365, -122.115],
[37.343, -122.07],
[37.325, -122.02],
[37.315, -121.965],
[37.318, -121.92],
[37.328, -121.892],
];
/**
* El Camino Real, the middle band. Older than every grid it passes through,
* which is why the grids are all rotated to meet it and why the rotation
* changes as it goes: nearly northsouth at the county line, nearly eastwest
* by Santa Clara. Read the `gridAngle` of the Peninsula districts down the file
* and this road is what they are all turning against.
*/
export const EL_CAMINO_REAL: LatLng[] = [
[37.708, -122.468],
[37.688, -122.455],
[37.665, -122.432],
[37.643, -122.412],
[37.62, -122.393],
[37.595, -122.375],
[37.57, -122.35],
[37.545, -122.32],
[37.52, -122.288],
[37.495, -122.252],
[37.472, -122.212],
[37.45, -122.175],
[37.428, -122.135],
[37.408, -122.095],
[37.392, -122.05],
[37.378, -122.005],
[37.365, -121.96],
[37.352, -121.925],
[37.34, -121.898],
];
/** The Nimitz, running the east shore from the bridge touchdown to San Jose. */
export const NIMITZ_880: LatLng[] = [
[37.822, -122.3],
[37.803, -122.283],
[37.782, -122.258],
[37.757, -122.228],
[37.73, -122.198],
[37.7, -122.163],
[37.672, -122.125],
[37.645, -122.095],
[37.618, -122.068],
[37.588, -122.04],
[37.558, -122.012],
[37.528, -121.988],
[37.498, -121.962],
[37.468, -121.938],
[37.44, -121.915],
[37.41, -121.9],
[37.38, -121.893],
[37.352, -121.888],
];
export const EASTSHORE_80: LatLng[] = [
[37.822, -122.302],
[37.835, -122.302],
[37.85, -122.298],
[37.865, -122.3],
[37.88, -122.31],
[37.895, -122.32],
[37.91, -122.332],
[37.925, -122.34],
[37.945, -122.34],
[37.965, -122.33],
[37.988, -122.315],
[38.01, -122.295],
];
/** The 580, from the Richmond bridge round the hills and out to the Diablo valley. */
export const MACARTHUR_580: LatLng[] = [
[37.93, -122.39],
[37.92, -122.36],
[37.905, -122.34],
[37.885, -122.318],
[37.862, -122.298],
[37.838, -122.272],
[37.812, -122.245],
[37.788, -122.222],
[37.762, -122.19],
[37.74, -122.152],
[37.718, -122.11],
[37.702, -122.062],
[37.7, -122.01],
[37.702, -121.95],
[37.705, -121.9],
];
/** The 680, behind the ridge — San Jose to Fremont and on up the inland valley. */
export const INTERSTATE_680: LatLng[] = [
[37.345, -121.885],
[37.372, -121.878],
[37.4, -121.87],
[37.428, -121.862],
[37.458, -121.865],
[37.49, -121.882],
[37.52, -121.905],
[37.55, -121.92],
[37.585, -121.93],
[37.625, -121.938],
[37.67, -121.945],
[37.72, -121.945],
[37.78, -121.945],
[37.85, -121.96],
[37.9, -122.01],
];
export const HIGHWAY_237: LatLng[] = [
[37.408, -122.055],
[37.412, -122.02],
[37.412, -121.985],
[37.418, -121.95],
[37.428, -121.92],
];
export const HIGHWAY_85: LatLng[] = [
[37.412, -122.075],
[37.395, -122.075],
[37.375, -122.065],
[37.352, -122.045],
[37.33, -122.02],
[37.3, -121.995],
[37.275, -121.96],
[37.258, -121.915],
[37.25, -121.87],
];
export const HIGHWAY_17: LatLng[] = [
[37.33, -121.93],
[37.31, -121.945],
[37.288, -121.955],
[37.262, -121.965],
[37.238, -121.98],
];
/** Highway 1, the coast road, which has nothing on either side of it for fifty miles. */
export const COAST_HIGHWAY: LatLng[] = [
[37.706, -122.492],
[37.688, -122.487],
[37.665, -122.486],
[37.64, -122.488],
[37.615, -122.491],
[37.592, -122.494],
[37.568, -122.504],
[37.545, -122.5],
[37.52, -122.486],
[37.498, -122.462],
[37.478, -122.444],
[37.455, -122.434],
[37.425, -122.424],
[37.39, -122.414],
[37.35, -122.408],
[37.31, -122.4],
[37.27, -122.392],
[37.235, -122.352],
[37.2, -122.325],
];
/** Highway 92, San Mateo over the ridge to Half Moon Bay — one of four ways across. */
export const HIGHWAY_92: LatLng[] = [
[37.573, -122.25],
[37.56, -122.288],
[37.552, -122.325],
[37.545, -122.362],
[37.53, -122.4],
[37.51, -122.43],
[37.492, -122.455],
];
/** Highway 84, the Dumbarton landing through Redwood City to Woodside. */
export const HIGHWAY_84: LatLng[] = [
[37.487, -122.145],
[37.483, -122.18],
[37.482, -122.215],
[37.478, -122.245],
[37.465, -122.272],
[37.45, -122.29],
[37.432, -122.305],
];
/** 101 in Marin — the Waldo grade, Mill Valley, Corte Madera, San Rafael. */
export const MARIN_101: LatLng[] = [
[37.833, -122.48],
[37.845, -122.487],
[37.86, -122.495],
[37.878, -122.51],
[37.895, -122.518],
[37.912, -122.52],
[37.93, -122.522],
[37.948, -122.528],
[37.968, -122.532],
[37.99, -122.535],
[38.012, -122.538],
];
// ---- Airports -------------------------------------------------------------
/**
* San Francisco International, on the fill at the bay edge below the city.
*
* The board has had real aircraft over it since the ADS-B layer landed, and
* every one of them was descending onto nothing. This is the ground they were
* descending onto.
*
* ## The headings are the whole of it
*
* SFO is two crossing pairs, and that pattern is its signature from any
* altitude a board camera sits at: long before a terminal is a terminal, the
* eye reads four bars making an X on a square of fill. So the numbers below
* that matter most are the two bearings, and they are **true**, not the
* magnetic ones the runways are named after.
*
* A runway designator is magnetic and rounded to the nearest ten degrees. San
* Francisco's declination is about 13.5° east, so "28R" is a runway pointing
* **298.6° true** and "1L" one pointing **26.5°**. Building from the painted
* numbers instead would lay the whole airport thirteen degrees out and put the
* 28s on a heading that misses the city they point at. The two check against
* each other and against history: at the ~17° east declination of the years the
* designators were assigned, 118.6° true is 101.6° magnetic — "10" — and 26.5°
* true is 9.5° magnetic — "01".
*
* The two pairs are **92.1° apart**, which is the other thing worth being exact
* about. SFO crosses at very nearly a right angle; an airfield whose runways
* cross at seventy degrees is a different airport, and reads as a generic one.
*
* ## The relative geometry
*
* Everything is placed from the airport reference point at 37.6189, 122.3750,
* which is what makes the four runways consistent with each other rather than
* four independent guesses:
*
* - **10L/28R** is 11,870 ft (3,618 m). **10R/28L** is 11,381 ft (3,469 m),
* 229 m — 750 ft — to its south-south-west. That separation is the famous
* one: the closest parallel pair in the United States used for simultaneous
* approaches, and the reason every SFO arrival in low cloud is a single-file
* arrival. The 28 thresholds sit abeam each other at the shoreline, so the
* 149 m 10R/28L gives away comes off its western end.
* - **1L/19R** is 7,650 ft (2,332 m). **1R/19L** is 8,650 ft (2,637 m), 213 m
* — 700 ft — to its east-south-east. The 01 thresholds sit abeam at the
* south end and the extra length runs north.
*
* The 28 thresholds land ~70 m inside the bay edge traced in `PENINSULA`, which
* is not luck: that outline was drawn with "SFO's bay edge — the runways are
* built out onto the mud" written on the vertex, and now they are.
*
* Terminals are **massed rather than surveyed**. The horseshoe is right — the
* International Terminal across the west end, Terminals 1 and 3 as the arms
* reaching east, Terminal 2 and the garages in the court, the tower in the
* middle of it — and it sits in the wedge between the west ends of the 28s and
* the south ends of the 01s, which is where SFO's terminals are. Individual
* boarding-area fingers are not modelled: at 94 m per scene unit a boarding
* area is two pixels, and the aircraft parked against it are not.
*/
export const SFO: Airport = {
id: "KSFO",
name: "San Francisco International",
lat: 37.6189,
lng: -122.375,
// 13 ft. Carried because it is a fact about the place; the kit grades the
// field onto the terrain rather than lifting it to this, which on a board
// exaggerating relief 3.6× would stand SFO fourteen metres above its own
// shoreline. See `engine/airports.ts`.
elevation: 4,
/**
* The fill, traced to meet `PENINSULA`'s bay edge without crossing it.
*
* The eastern four lie a few tens of metres inside coastline vertices this
* pack already had — the airport is on made ground and its eastern boundary
* *is* the shore. The western edge runs a few hundred metres east of US-101,
* which is where the fence actually is.
*/
field: [
[37.6425, -122.3782],
[37.6398, -122.3592],
[37.6232, -122.3548],
[37.6072, -122.3576],
[37.6048, -122.381],
[37.6105, -122.3935],
[37.6262, -122.3968],
[37.6355, -122.3905],
],
runways: [
// 10L [37.62861, -122.39335] -> 28R [37.61305, -122.35733]
{ id: "10L/28R", lat: 37.62083, lng: -122.37534, heading: 118.6, length: 3618, width: 61, designators: ["10L", "28R"] },
// 10R [37.62616, -122.39311] -> 28L [37.61124, -122.35857]
{ id: "10R/28L", lat: 37.6187, lng: -122.37584, heading: 118.6, length: 3469, width: 61, designators: ["10R", "28L"] },
// 1L [37.60783, -122.38525] -> 19R [37.62657, -122.37345]
{ id: "1L/19R", lat: 37.6172, lng: -122.37935, heading: 26.5, length: 2332, width: 61, designators: ["1L", "19R"] },
// 1R [37.60697, -122.38309] -> 19L [37.62817, -122.36975]
{ id: "1R/19L", lat: 37.61757, lng: -122.37642, heading: 26.5, length: 2637, width: 61, designators: ["1R", "19L"] },
],
/**
* The four outer parallels — A and B outside the 10s, F and Z outside the 01s.
*
* Typed as coordinates rather than computed here, because a city pack is data
* and `airports.ts` imports three.js. `parallelTaxiway()` produced them and
* `sfoAirport.test.ts` asserts they still match what it produces, so the
* derivation is checked without the pack having to run it.
*/
taxiways: [
{ id: "A", path: [[37.62939, -122.39126], [37.61487, -122.35763]] },
{ id: "B", path: [[37.62434, -122.39281], [37.61046, -122.36066]] },
{ id: "F", path: [[37.60907, -122.38586], [37.62621, -122.37507]] },
{ id: "Z", path: [[37.60711, -122.38091], [37.6267, -122.36858]] },
],
aprons: [
// The terminal court and the stands around it, square to the 01s the way
// the whole complex is.
{
id: "terminal",
polygon: [
[37.61834, -122.37961],
[37.62211, -122.38915],
[37.61407, -122.39421],
[37.6103, -122.38467],
],
},
// The north field: cargo and the maintenance base, and the reason there is
// a kilometre and a half of airport north of the 19 thresholds.
{
id: "north-field",
polygon: [
[37.6314, -122.3738],
[37.63381, -122.37989],
[37.62706, -122.38414],
[37.62465, -122.37805],
],
},
],
/**
* The horseshoe, and where the aeroplanes go.
*
* Terminals 1 and 3 are the two arms and they run out to meet the
* International Terminal at the closed end, so the complex reads as a U at
* the scale it is seen from rather than as five separate slabs. `gates` puts
* the stands on the **outside** of the U on all three, which is where SFO's
* are: the court inside holds Terminal 2, the garages and the roadway, and
* nothing that needs a wingspan.
*/
terminals: [
// Heights are the real ones — a terminal is two or three storeys over a very
// large footprint — and they are the numbers to keep even though the board
// exaggerates its vertical 3.6× and makes a 26 m building stand ninety
// metres tall. Trimming them to compensate would put SFO on a different
// vertical scale from every house in San Bruno behind it, and the eye
// notices that far sooner than it notices a tall terminal.
{ id: "international", lat: 37.61749, lng: -122.39016, length: 600, width: 120, height: 28, heading: 26.5, gates: { count: 6, side: -1 } },
{ id: "t3", lat: 37.6187, lng: -122.38559, length: 500, width: 100, height: 17, heading: 116.5, gates: { count: 6, side: -1 } },
{ id: "t1", lat: 37.61387, lng: -122.38863, length: 500, width: 100, height: 17, heading: 116.5, gates: { count: 6, side: 1 } },
{ id: "t2", lat: 37.61596, lng: -122.3863, length: 300, width: 80, height: 15, heading: 26.5 },
{ id: "garages", lat: 37.61516, lng: -122.38427, length: 340, width: 90, height: 21, heading: 26.5 },
// The north field, square to the 10s rather than to the terminals, which is
// what stops the maintenance base reading as more of the same building. A
// widebody hangar is genuinely as tall as a six-storey block.
{ id: "maintenance", lat: 37.62806, lng: -122.38135, length: 260, width: 120, height: 25, heading: 118.6 },
{ id: "cargo", lat: 37.63013, lng: -122.37715, length: 220, width: 100, height: 14, heading: 118.6, gates: { count: 3, side: 1 } },
],
// No `tower`: this pack draws SFO's as a labelled `Landmark`, because that is
// what the minimap reads. See `LANDMARKS` above.
};
/**
* Mineta San José, two parallels at the head of the valley.
*
* Here because it was already on this board as a pair of pale strips and the
* kit is what those strips wanted to be — not because the South Bay needs an
* airport modelled. Two 11,000 ft runways 700 ft apart on 131.5° true (which
* is "12" once the same 13.4° of easterly declination comes off it), the
* terminal frontage on the north-east side where Highway 87 runs, and nothing
* else: from a camera that can see San Jose at all, SJC is its runways.
*
* The plate is kept east of 121.946 and between 37.348 and 37.377 on purpose.
* That is the gap this pack's districts already leave — `santa-clara` stops at
* the airport's west fence, `san-jose-north` above it and `san-jose-downtown`
* below — and a field plate that crossed into one of them would have houses
* scattered over it.
*/
export const SJC: Airport = {
id: "KSJC",
name: "Norman Y. Mineta San José International",
lat: 37.3626,
lng: -121.9291,
elevation: 19,
/**
* The plate is **asymmetric about the runways** on purpose: it runs 430 m
* south-west of the centreline and only 230 m north-east, because this pack's
* `BAYSHORE_101` passes about 240 m north-east of runway 12L and a field that
* reached past it would have a freeway drawn across the middle of the airport.
*
* It is also kept east of 121.946 and between 37.348 and 37.377, which is the
* gap this pack's districts already leave — `santa-clara` stops at the west
* fence, `san-jose-north` above and `san-jose-downtown` below — and a plate
* that crossed into one would have houses scattered over it.
*/
field: [
[37.35343, -121.91214],
[37.34899, -121.91708],
[37.37042, -121.94756],
[37.37486, -121.94261],
],
runways: [
// 12L [37.3733, -121.94249] -> 30R [37.35334, -121.91411]
{ id: "12L/30R", lat: 37.36332, lng: -121.9283, heading: 131.5, length: 3353, width: 46, designators: ["12L", "30R"] },
// 12R [37.37186, -121.94409] -> 30L [37.3519, -121.91571]
{ id: "12R/30L", lat: 37.36188, lng: -121.9299, heading: 131.5, length: 3353, width: 46, designators: ["12R", "30L"] },
],
taxiways: [{ id: "A", path: [[37.37377, -121.94061], [37.35489, -121.91375]] }],
aprons: [
{
id: "terminal",
polygon: [
[37.36036, -121.92315],
[37.3615, -121.92187],
[37.36817, -121.93136],
[37.36702, -121.93263],
],
},
],
// Terminals A and B as one frontage on the north-east side, where Highway 87
// and Airport Boulevard run, with the stands facing back at the runways.
terminals: [
{ id: "ab", lat: 37.3644, lng: -121.9271, length: 620, width: 90, height: 16, heading: 131.5, gates: { count: 7, side: 1 } },
],
};
/** Every airport on this board, in the order a pack would list them. */
export const AIRPORTS: Airport[] = [SFO, SJC];
/**
* ## Runways are no longer roads
*
* They were, until this pack got an airport: four hand-typed two-point paths on
* *magnetic* headings — thirteen degrees out of true, with the 01/19 pair nearly
* nine hundred metres too long — handed to `createRoads` as `kind: "street"`,
* because a pale strip on flat ground was as close as the road builder could
* get. The kit draws the same centrelines properly, with paint on them, the
* fill under them and the terminals beside them, so those entries are gone from
* `ROADS` above.
*
* **The two cannot both be present.** A road ribbon drapes 0.14 units — thirteen
* metres at this board's scale — above the terrain and a runway quad sits flush
* on it, so a board carrying both floats a dark forty-seven-metre stripe over
* every runway on the Peninsula. It was photographed before it was deleted.
*
* `runwayCentreline()` in `engine/airports.ts` reproduces those paths from the
* `SFO` and `SJC` declarations above, if anything ever wants a runway as a line
* again.
*/
export const ROADS: City["roads"] = [
{ path: MARKET_STREET, width: 0.34, kind: "street" },
{ path: THE_EMBARCADERO, width: 0.28, kind: "street" },
{ path: VAN_NESS, width: 0.26, kind: "street" },
{ path: GEARY, width: 0.24, kind: "street" },
{ path: MISSION_STREET, width: 0.22, kind: "street" },
{ path: NINETEENTH_AVE, width: 0.24, kind: "street" },
{ path: THIRD_STREET, width: 0.22, kind: "street" },
{ path: HIGHWAY_101, width: 0.3, kind: "freeway" },
{ path: INTERSTATE_280, width: 0.3, kind: "freeway" },
{ path: BAYSHORE_101, width: 0.32, kind: "freeway" },
{ path: JUNIPERO_SERRA_280, width: 0.3, kind: "freeway" },
{ path: NIMITZ_880, width: 0.32, kind: "freeway" },
{ path: EASTSHORE_80, width: 0.32, kind: "freeway" },
{ path: MACARTHUR_580, width: 0.3, kind: "freeway" },
{ path: INTERSTATE_680, width: 0.28, kind: "freeway" },
{ path: HIGHWAY_237, width: 0.24, kind: "freeway" },
{ path: HIGHWAY_85, width: 0.26, kind: "freeway" },
{ path: HIGHWAY_17, width: 0.24, kind: "freeway" },
{ path: MARIN_101, width: 0.28, kind: "freeway" },
{ path: EL_CAMINO_REAL, width: 0.24, kind: "street" },
{ path: COAST_HIGHWAY, width: 0.2, kind: "street" },
{ path: HIGHWAY_92, width: 0.2, kind: "street" },
{ path: HIGHWAY_84, width: 0.2, kind: "street" },
];
// ---- Bridges --------------------------------------------------------------
export const GOLDEN_GATE_BRIDGE: Bridge = {
name: "Golden Gate Bridge",
// Both ends run well onto land. Started at the waterline, the deck appeared
// to float with its approaches missing — the span has to touch the Presidio
// bluff on one side and Fort Baker on the other to read as a bridge.
path: [
[37.8025, -122.4752],
[37.8106, -122.4775],
[37.8155, -122.4783],
[37.825, -122.479],
[37.8325, -122.4798],
[37.8375, -122.4806],
],
towers: [
[37.8155, -122.4783],
[37.825, -122.479],
],
towerHeight: 227,
deckHeight: 67,
sag: 0.55,
color: 0xc0442c, // International Orange
};
export const BAY_BRIDGE: Bridge = {
name: "Bay Bridge",
path: [
[37.7905, -122.3885],
[37.7965, -122.3805],
[37.8035, -122.3725],
[37.8095, -122.3648], // Yerba Buena
[37.8155, -122.3535],
[37.8205, -122.3405],
[37.8225, -122.3275],
],
towers: [
[37.7955, -122.3815],
[37.8035, -122.3725],
[37.8165, -122.3515],
],
towerHeight: 160,
deckHeight: 58,
sag: 0.4,
color: 0x9aa6b2,
};
/**
* The other three crossings.
*
* None of them is a suspension bridge and all three are drawn with the
* suspension builder, which is a compromise worth stating. What they actually
* are is miles of low trestle with one high span in the middle for the ship
* channel — so each gets a **single** tower rather than a pair. That matters:
* `createBridge` only treats a segment as a main span when it runs between two
* towers, so one tower produces two shallow curves instead of one deep
* catenary, and the result reads as a causeway with a hump in it. Which is what
* you see from the air.
*
* The RichmondSan Rafael gets two, because it genuinely has two humps — it was
* built with a ship channel at each end and a dip between them, and looks it.
*/
export const SAN_MATEO_BRIDGE: Bridge = {
name: "San MateoHayward Bridge",
path: [
[37.5745, -122.2585],
[37.578, -122.255],
[37.5865, -122.2405],
[37.6, -122.212],
[37.615, -122.175],
[37.628, -122.128],
[37.6305, -122.1235],
],
towers: [[37.5865, -122.2405]],
towerHeight: 58,
deckHeight: 14,
sag: 0.3,
color: 0x9aa6b2,
};
export const DUMBARTON_BRIDGE: Bridge = {
name: "Dumbarton Bridge",
path: [
[37.4835, -122.1505],
[37.487, -122.146],
[37.4975, -122.1175],
[37.512, -122.086],
[37.515, -122.0815],
],
towers: [[37.4975, -122.1175]],
towerHeight: 48,
deckHeight: 12,
sag: 0.28,
color: 0x8fa0ac,
};
export const RICHMOND_SAN_RAFAEL_BRIDGE: Bridge = {
name: "RichmondSan Rafael Bridge",
path: [
[37.9475, -122.4855],
[37.945, -122.481],
[37.9405, -122.452],
[37.9355, -122.418],
[37.93, -122.39],
[37.9275, -122.3845],
],
towers: [
[37.9405, -122.452],
[37.9355, -122.418],
],
towerHeight: 56,
deckHeight: 18,
sag: 0.26,
color: 0xa0a8ae,
};
export const BRIDGES = [
GOLDEN_GATE_BRIDGE,
BAY_BRIDGE,
SAN_MATEO_BRIDGE,
DUMBARTON_BRIDGE,
RICHMOND_SAN_RAFAEL_BRIDGE,
];
// ---- Landmarks ------------------------------------------------------------
/**
* Buildings placed by hand because the eye looks for them. Everything else
* downtown is procedural scatter; these are the five or six shapes that tell
* you at a glance that you are looking at San Francisco and not Seattle.
*/
export const LANDMARKS: Landmark[] = [
{
name: "Salesforce Tower",
lat: 37.7897,
lng: -122.3972,
height: 326,
footprint: 0.00055,
shape: "tower",
label: true,
},
{
name: "Transamerica Pyramid",
lat: 37.7952,
lng: -122.4028,
height: 260,
footprint: 0.00048,
shape: "pyramid",
color: 0xdfe4e8,
label: true,
},
{
name: "Coit Tower",
lat: 37.8024,
lng: -122.4058,
height: 64,
footprint: 0.00016,
shape: "cylinder",
color: 0xe6e2d8,
label: true,
},
{
name: "Ferry Building",
lat: 37.7955,
lng: -122.3937,
height: 74,
footprint: 0.0003,
shape: "tower",
color: 0xe8dfcd,
label: true,
},
{
name: "555 California",
lat: 37.7924,
lng: -122.4038,
height: 237,
footprint: 0.0005,
shape: "box",
color: 0x8c6f5e,
},
{
name: "181 Fremont",
lat: 37.7889,
lng: -122.3963,
height: 244,
footprint: 0.00035,
shape: "tower",
},
{
name: "Sutro Tower",
lat: 37.7552,
lng: -122.4528,
height: 298,
footprint: 0.00022,
shape: "tower",
color: 0xd06a4a,
label: true,
},
{
name: "Oracle Park",
lat: 37.7786,
lng: -122.3893,
height: 38,
footprint: 0.0009,
shape: "cylinder",
color: 0x4a6b52,
label: true,
},
{
name: "Palace of Fine Arts",
lat: 37.8029,
lng: -122.4484,
height: 49,
footprint: 0.00035,
shape: "cylinder",
color: 0xdcc9ad,
},
// Beyond the county line. Chosen the same way as the city's: the shape that
// tells you which town you are over. Deliberately none of them is a corporate
// campus — a building named for a company would put a trademark on the map,
// and ARCHITECTURE.md §3.1 is the reason there is not one in this repo.
{
/**
* The airport's tower, and the one piece of SFO that is **not** in `SFO`
* below.
*
* `Airport` has a `tower` field and the kit draws a much better one — a
* tapered shaft with a cab on it, in the airport's own palette. It is
* deliberately not used here, because this landmark was already on the board
* and a landmark is more than a mesh: `label: true` is what puts SFO on the
* **minimap**, and `minimap.ts` reads `city.landmarks`, not the airport. Two
* towers four hundred metres apart is what you get if you forget that, and
* it was photographed before it was noticed.
*
* Moved to sit in the terminal court with the rest of the complex, which is
* where SFO's tower actually stands.
*/
name: "SFO Control Tower",
lat: 37.6174,
lng: -122.3883,
height: 67,
footprint: 0.00014,
shape: "tower",
color: 0xd8d2c4,
label: true,
},
{
name: "Hoover Tower",
lat: 37.4275,
lng: -122.1697,
height: 87,
footprint: 0.0002,
shape: "cylinder",
color: 0xe4d7bd,
label: true,
},
{
name: "Hangar One",
lat: 37.4103,
lng: -122.0489,
height: 60,
footprint: 0.0011,
shape: "cylinder",
color: 0xb6bcc0,
label: true,
},
{
name: "Shoreline Amphitheatre",
lat: 37.4266,
lng: -122.0805,
height: 28,
footprint: 0.0004,
shape: "pyramid",
color: 0xdfe3e6,
},
{
name: "San Jose City Hall",
lat: 37.3382,
lng: -121.8863,
height: 86,
footprint: 0.00028,
shape: "tower",
label: true,
},
{
name: "Bank of Italy Building",
lat: 37.335,
lng: -121.8905,
height: 71,
footprint: 0.00022,
shape: "box",
color: 0xd9cdb8,
},
{
name: "Ordway Building",
lat: 37.8095,
lng: -122.262,
height: 123,
footprint: 0.00042,
shape: "box",
color: 0xb4bcc4,
label: true,
},
{
name: "Tribune Tower",
lat: 37.8016,
lng: -122.2717,
height: 93,
footprint: 0.00018,
shape: "tower",
color: 0xc7b49c,
label: true,
},
{
name: "Sather Tower",
lat: 37.8721,
lng: -122.2578,
height: 94,
footprint: 0.00016,
shape: "tower",
color: 0xe6ddc8,
label: true,
},
{
name: "Marin County Civic Center",
lat: 37.9985,
lng: -122.5312,
height: 26,
footprint: 0.0006,
shape: "cylinder",
color: 0xd6c9a8,
},
];
// ---- Districts ------------------------------------------------------------
/**
* Where buildings go, how tall, and how densely. The scatter in buildings.ts
* reads these; the chapters in `chapters.ts` fly to them.
*
* `density` is buildings per square degree-ish — tuned by eye. `maxHeight` and
* `minHeight` are metres, and the distribution between them is cubed so that
* tall buildings stay rare and the skyline keeps a silhouette.
*/
export const DISTRICTS: District[] = [
{
id: "fidi",
name: "Financial District",
polygon: [
[37.8005, -122.4065],
[37.7975, -122.3925],
[37.7885, -122.3925],
[37.7855, -122.4045],
[37.7925, -122.4105],
],
minHeight: 40,
maxHeight: 185,
gridAngle: 0.8,
palette: "downtown",
towerChance: 0.15,
},
{
id: "soma",
name: "SoMa",
polygon: [
[37.7885, -122.4085],
[37.7855, -122.3895],
[37.7715, -122.3945],
[37.7735, -122.4165],
],
minHeight: 18,
maxHeight: 130,
gridAngle: 0.8,
palette: "downtown",
towerChance: 0.035,
},
{
id: "mission-bay",
name: "Mission Bay",
polygon: [
[37.7765, -122.3925],
[37.7745, -122.3835],
[37.7625, -122.3845],
[37.7645, -122.3985],
],
minHeight: 20,
maxHeight: 95,
gridAngle: 0.8,
palette: "downtown",
towerChance: 0.1,
},
{
id: "hayes-valley",
name: "Hayes Valley",
polygon: [
[37.7815, -122.4315],
[37.7785, -122.4185],
[37.7695, -122.4215],
[37.7715, -122.4345],
],
minHeight: 12,
maxHeight: 48,
gridAngle: 0.03,
palette: "residential",
towerChance: 0.03,
},
{
id: "nob-hill",
name: "Nob Hill & Russian Hill",
polygon: [
[37.8055, -122.425],
[37.8025, -122.4055],
[37.7885, -122.4085],
[37.7905, -122.4265],
],
minHeight: 14,
maxHeight: 85,
gridAngle: 0.02,
palette: "residential",
towerChance: 0.06,
},
{
id: "north-beach",
name: "North Beach & Wharf",
polygon: [
[37.8095, -122.4185],
[37.8065, -122.4045],
[37.7995, -122.4045],
[37.8015, -122.4205],
],
minHeight: 10,
maxHeight: 40,
gridAngle: 0.05,
palette: "residential",
towerChance: 0.02,
},
{
id: "mission",
name: "The Mission",
polygon: [
[37.7695, -122.4295],
[37.7675, -122.405],
[37.7455, -122.4075],
[37.7485, -122.4305],
],
minHeight: 9,
maxHeight: 34,
gridAngle: 0.03,
palette: "residential",
towerChance: 0.012,
},
{
id: "potrero",
name: "Potrero & Dogpatch",
polygon: [
[37.7665, -122.4055],
[37.7635, -122.3835],
[37.7495, -122.3845],
[37.7525, -122.4065],
],
minHeight: 8,
maxHeight: 42,
gridAngle: 0.8,
palette: "industrial",
towerChance: 0.015,
},
{
id: "marina",
name: "Marina & Pacific Heights",
polygon: [
[37.8055, -122.4485],
[37.8035, -122.4265],
[37.7855, -122.4295],
[37.7885, -122.4525],
],
minHeight: 9,
maxHeight: 40,
gridAngle: 0.0,
palette: "residential",
towerChance: 0.01,
},
{
id: "richmond",
name: "The Richmond",
polygon: [
[37.7885, -122.5065],
[37.7855, -122.4525],
[37.7715, -122.4545],
[37.7745, -122.5095],
],
minHeight: 8,
maxHeight: 26,
gridAngle: 0.0,
palette: "residential",
towerChance: 0.004,
},
{
id: "sunset",
name: "The Sunset",
polygon: [
[37.7645, -122.5105],
[37.7615, -122.4595],
[37.7275, -122.4665],
[37.7315, -122.5085],
],
minHeight: 8,
maxHeight: 22,
gridAngle: 0.0,
palette: "residential",
towerChance: 0.002,
},
{
id: "western-addition",
name: "Western Addition & Haight",
polygon: [
[37.7855, -122.4535],
[37.7825, -122.4295],
[37.7665, -122.4355],
[37.7695, -122.4585],
],
minHeight: 9,
maxHeight: 38,
gridAngle: 0.02,
palette: "residential",
towerChance: 0.008,
},
{
id: "bernal",
name: "Bernal & Glen Park",
polygon: [
[37.7485, -122.4405],
[37.7455, -122.4055],
[37.7275, -122.4105],
[37.7305, -122.4455],
],
minHeight: 8,
maxHeight: 24,
gridAngle: 0.06,
palette: "residential",
towerChance: 0.003,
},
{
id: "bayview",
name: "Bayview & Hunters Point",
polygon: [
[37.7475, -122.4005],
[37.7445, -122.3765],
[37.7185, -122.3855],
[37.7245, -122.4085],
],
minHeight: 7,
maxHeight: 28,
gridAngle: 0.72,
palette: "industrial",
towerChance: 0.004,
},
{
id: "excelsior",
name: "Excelsior & Ingleside",
polygon: [
[37.7305, -122.4655],
[37.7285, -122.4145],
[37.7095, -122.4205],
[37.7115, -122.4695],
],
minHeight: 7,
maxHeight: 20,
gridAngle: 0.12,
palette: "residential",
towerChance: 0.002,
},
{
id: "castro",
name: "Castro & Noe Valley",
polygon: [
[37.7695, -122.4485],
[37.7675, -122.4265],
[37.7405, -122.4305],
[37.7425, -122.4495],
],
minHeight: 8,
maxHeight: 26,
gridAngle: 0.03,
palette: "residential",
towerChance: 0.002,
},
{
id: "west-portal",
name: "West Portal & Forest Hill",
polygon: [
[37.7605, -122.4795],
[37.7585, -122.4465],
[37.7345, -122.4525],
[37.7365, -122.4785],
],
minHeight: 7,
maxHeight: 22,
gridAngle: 0.18,
palette: "residential",
towerChance: 0.002,
},
{
id: "glen-park",
name: "Glen Park & Diamond Heights",
polygon: [
[37.7505, -122.4585],
[37.7495, -122.4375],
[37.7295, -122.4415],
[37.7315, -122.4595],
],
minHeight: 7,
maxHeight: 24,
gridAngle: 0.2,
palette: "residential",
towerChance: 0.003,
},
{
id: "lakeshore",
name: "Lakeshore & Parkmerced",
polygon: [
[37.7365, -122.5035],
[37.7345, -122.4655],
[37.7105, -122.4695],
[37.7125, -122.5045],
],
minHeight: 8,
maxHeight: 46,
gridAngle: 0.08,
palette: "residential",
towerChance: 0.012,
},
{
id: "presidio-heights",
name: "Presidio & Laurel Heights",
polygon: [
[37.7935, -122.4655],
[37.7915, -122.4445],
[37.7815, -122.4465],
[37.7835, -122.4665],
],
minHeight: 9,
maxHeight: 32,
gridAngle: 0.0,
palette: "residential",
towerChance: 0.005,
},
{
id: "sausalito",
name: "Sausalito",
polygon: [
[37.868, -122.492],
[37.866, -122.484],
[37.845, -122.482],
[37.847, -122.492],
],
minHeight: 7,
maxHeight: 18,
gridAngle: 0.35,
palette: "industrial",
towerChance: 0.002,
},
{
id: "oakland",
name: "Oakland",
polygon: [
[37.815, -122.295],
[37.812, -122.26],
[37.788, -122.262],
[37.792, -122.297],
],
minHeight: 10,
maxHeight: 120,
gridAngle: 0.45,
palette: "downtown",
towerChance: 0.05,
},
// ---- The Peninsula ------------------------------------------------------
//
// Fourteen towns in a row between the bay and the ridge, and the interesting
// thing about them is the `gridAngle` column. Every one of these places was
// laid out against El Camino and the railroad, and that line swings roughly
// forty-five degrees on its way south — nearly northsouth where it leaves
// the county line, nearly eastwest by Santa Clara. So the grid rotates
// steadily as the camera travels down the strip, which is a thing you can
// actually see from the air and which no single city-wide bearing would ever
// produce. San Francisco's own downtown sits at 0.8; Daly City, four miles
// away, is at 1.22.
//
// `coverage` is lower out here than in the city, and it is doing two jobs.
// The honest one is that the Peninsula is detached houses on lots and the
// Sunset is not. The practical one is budget: everything in `blocks.ts` goes
// into one `InstancedMesh` that is also drawn again into the shadow map, and
// San Francisco alone puts about 77,000 boxes in it. These thirty districts
// at the city's density would have added 190,000 more; at the coverage below
// they add 111,000, which lands the whole region at 2.4× the city — for
// twenty times the area. They are the built cores, not municipal boundaries.
{
id: "daly-city",
name: "Daly City",
polygon: [
[37.708, -122.496],
[37.706, -122.446],
[37.682, -122.452],
[37.684, -122.498],
],
minHeight: 8,
maxHeight: 26,
gridAngle: 1.22,
palette: "residential",
towerChance: 0.004,
coverage: 0.55,
},
{
id: "south-san-francisco",
name: "South San Francisco",
polygon: [
[37.678, -122.452],
[37.676, -122.398],
[37.652, -122.404],
[37.654, -122.456],
],
minHeight: 8,
maxHeight: 34,
gridAngle: 1.1,
palette: "industrial",
towerChance: 0.008,
coverage: 0.4,
},
{
id: "san-bruno",
name: "San Bruno",
polygon: [
[37.648, -122.456],
[37.646, -122.404],
[37.626, -122.41],
[37.628, -122.458],
],
minHeight: 7,
maxHeight: 22,
gridAngle: 1.05,
palette: "residential",
towerChance: 0.002,
coverage: 0.4,
},
{
id: "millbrae-burlingame",
name: "Millbrae & Burlingame",
// The east edge stops short of -122.41 up at the airport's latitude and
// only reaches the bay south of it. There is nothing to build on between
// those two lines: it is SFO, and boxes scattered across the 19s would be
// the most conspicuous mistake on the Peninsula.
polygon: [
[37.622, -122.456],
[37.62, -122.41],
[37.592, -122.384],
[37.594, -122.444],
],
minHeight: 7,
maxHeight: 28,
gridAngle: 0.96,
palette: "residential",
towerChance: 0.004,
coverage: 0.36,
},
{
id: "san-mateo",
name: "San Mateo",
// Kept east of -122.382 and north of 37.560, which is the gap between San
// Andreas Lake and Crystal Springs. The watershed reservoirs lie in the
// fault trace right behind the town, and a district drawn over them puts
// houses in the water supply.
polygon: [
[37.588, -122.376],
[37.586, -122.318],
[37.56, -122.326],
[37.562, -122.382],
],
minHeight: 8,
maxHeight: 42,
gridAngle: 0.87,
palette: "residential",
towerChance: 0.01,
coverage: 0.42,
},
{
id: "foster-city",
name: "Foster City",
polygon: [
[37.575, -122.285],
[37.572, -122.256],
[37.55, -122.258],
[37.553, -122.288],
],
minHeight: 8,
maxHeight: 28,
// Built on dredged fill in the sixties and laid out against its own
// lagoons, so it ignores El Camino entirely — the one break in the
// Peninsula's rotation, and it shows.
gridAngle: 1.2,
palette: "residential",
towerChance: 0.006,
coverage: 0.4,
},
{
id: "belmont-san-carlos",
name: "Belmont & San Carlos",
polygon: [
[37.552, -122.33],
[37.549, -122.268],
[37.52, -122.272],
[37.523, -122.334],
],
minHeight: 7,
maxHeight: 24,
gridAngle: 0.8,
palette: "residential",
towerChance: 0.003,
coverage: 0.32,
},
{
id: "redwood-city",
name: "Redwood City",
polygon: [
[37.518, -122.276],
[37.515, -122.212],
[37.484, -122.22],
[37.487, -122.284],
],
minHeight: 9,
maxHeight: 44,
gridAngle: 0.7,
palette: "downtown",
towerChance: 0.012,
coverage: 0.4,
},
{
id: "menlo-park",
name: "Menlo Park & Atherton",
polygon: [
[37.484, -122.22],
[37.481, -122.156],
[37.45, -122.164],
[37.453, -122.228],
],
minHeight: 7,
maxHeight: 22,
gridAngle: 0.64,
palette: "residential",
towerChance: 0.002,
coverage: 0.28,
},
{
id: "palo-alto",
name: "Palo Alto",
polygon: [
[37.455, -122.162],
[37.452, -122.1],
[37.42, -122.108],
[37.423, -122.17],
],
// Fifty feet, by ordinance, since 1971. The most valuable square mile in
// the country is two storeys tall on purpose, and a `maxHeight` that let it
// grow a skyline would be the single most obviously wrong thing on the map.
minHeight: 8,
maxHeight: 24,
gridAngle: 0.61,
palette: "residential",
towerChance: 0.002,
coverage: 0.36,
},
{
id: "stanford",
name: "Stanford",
polygon: [
[37.442, -122.2],
[37.44, -122.174],
[37.414, -122.178],
[37.416, -122.204],
],
minHeight: 8,
maxHeight: 30,
gridAngle: 0.55,
palette: "residential",
towerChance: 0.004,
// A campus is mostly not buildings, and this one owns eight thousand acres
// it has deliberately never built on.
coverage: 0.22,
},
{
id: "mountain-view",
name: "Mountain View",
polygon: [
[37.42, -122.112],
[37.417, -122.048],
[37.386, -122.056],
[37.389, -122.12],
],
minHeight: 7,
maxHeight: 28,
gridAngle: 0.44,
palette: "residential",
towerChance: 0.005,
coverage: 0.34,
},
{
id: "sunnyvale",
name: "Sunnyvale",
polygon: [
[37.4, -122.05],
[37.397, -121.996],
[37.366, -122.004],
[37.369, -122.058],
],
minHeight: 7,
maxHeight: 26,
gridAngle: 0.26,
// The campuses along here are two- and three-storey tilt-up boxes on
// parking, which is the industrial palette whatever the tenant is worth.
palette: "industrial",
towerChance: 0.005,
coverage: 0.34,
},
{
id: "santa-clara",
name: "Santa Clara",
// The east edge stops at Mineta's west fence rather than at the freeway.
// Both eastern vertices were 0.004° further east and the corner reached over
// the airport's graded plate, which put a wedge of scattered houses across
// the north-west end of 12L/30R. Same argument, and same fix, as
// `millbrae-burlingame` up at SFO.
polygon: [
[37.372, -122.0],
[37.369, -121.946],
[37.34, -121.954],
[37.343, -122.008],
],
minHeight: 7,
maxHeight: 30,
gridAngle: 0.17,
palette: "industrial",
towerChance: 0.006,
coverage: 0.34,
},
// ---- The South Bay ------------------------------------------------------
{
id: "san-jose-downtown",
name: "Downtown San Jose",
polygon: [
[37.35, -121.906],
[37.347, -121.874],
[37.322, -121.878],
[37.325, -121.91],
],
minHeight: 10,
// Eighty-eight metres and not a foot more: downtown sits directly under the
// approach to Mineta, two miles away, and the FAA surface caps the whole
// core. The biggest city on this map has the shortest downtown on it, and
// that is why.
maxHeight: 88,
// The pueblo grid, forty-odd degrees off the suburban grid that surrounds
// it — the same argument Market Street settles in San Francisco, decided
// the same way and about eighty years earlier.
gridAngle: 0.6,
palette: "downtown",
towerChance: 0.05,
coverage: 0.5,
},
{
id: "san-jose-north",
name: "North San Jose",
polygon: [
[37.412, -121.958],
[37.408, -121.9],
[37.378, -121.908],
[37.382, -121.966],
],
minHeight: 7,
maxHeight: 30,
gridAngle: 0.3,
palette: "industrial",
towerChance: 0.006,
coverage: 0.34,
},
{
id: "san-jose-east",
name: "East San Jose",
polygon: [
[37.372, -121.86],
[37.368, -121.804],
[37.336, -121.812],
[37.34, -121.868],
],
minHeight: 6,
maxHeight: 18,
gridAngle: 0.08,
palette: "residential",
towerChance: 0.001,
coverage: 0.32,
},
{
id: "willow-glen",
name: "Willow Glen & Cambrian",
polygon: [
[37.322, -121.93],
[37.318, -121.87],
[37.286, -121.878],
[37.29, -121.938],
],
minHeight: 6,
maxHeight: 18,
gridAngle: 0.06,
palette: "residential",
towerChance: 0.001,
coverage: 0.32,
},
// ---- The East Bay -------------------------------------------------------
//
// The bearings here have nothing to do with the Peninsula's. Oakland and
// Alameda are laid against the estuary, Berkeley is very nearly true north,
// and everything from San Leandro south turns further with the shore.
{
id: "richmond",
name: "Richmond",
polygon: [
[37.948, -122.36],
[37.945, -122.318],
[37.918, -122.324],
[37.921, -122.366],
],
minHeight: 7,
maxHeight: 26,
gridAngle: 0.45,
palette: "industrial",
towerChance: 0.003,
coverage: 0.36,
},
{
id: "el-cerrito-albany",
name: "El Cerrito & Albany",
polygon: [
[37.918, -122.318],
[37.915, -122.282],
[37.888, -122.288],
[37.891, -122.324],
],
minHeight: 8,
maxHeight: 26,
gridAngle: 0.1,
palette: "residential",
towerChance: 0.003,
coverage: 0.45,
},
{
id: "berkeley",
name: "Berkeley",
polygon: [
[37.888, -122.29],
[37.885, -122.248],
[37.855, -122.254],
[37.858, -122.296],
],
minHeight: 9,
maxHeight: 44,
gridAngle: 0.06,
palette: "residential",
towerChance: 0.008,
coverage: 0.55,
},
{
id: "emeryville",
name: "Emeryville",
polygon: [
[37.845, -122.312],
[37.842, -122.286],
[37.826, -122.29],
[37.829, -122.316],
],
minHeight: 10,
maxHeight: 60,
gridAngle: 0.45,
palette: "downtown",
towerChance: 0.02,
coverage: 0.45,
},
{
id: "alameda",
name: "Alameda",
polygon: [
[37.784, -122.294],
[37.781, -122.246],
[37.762, -122.248],
[37.765, -122.274],
],
minHeight: 7,
maxHeight: 24,
gridAngle: 0.52,
palette: "residential",
towerChance: 0.002,
coverage: 0.42,
},
{
id: "san-leandro",
name: "San Leandro",
polygon: [
[37.735, -122.185],
[37.732, -122.14],
[37.706, -122.146],
[37.709, -122.19],
],
minHeight: 7,
maxHeight: 24,
gridAngle: 0.52,
palette: "residential",
towerChance: 0.002,
coverage: 0.36,
},
{
id: "hayward",
name: "Hayward",
polygon: [
[37.69, -122.115],
[37.687, -122.062],
[37.658, -122.068],
[37.661, -122.12],
],
minHeight: 7,
maxHeight: 28,
gridAngle: 0.6,
palette: "residential",
towerChance: 0.004,
coverage: 0.36,
},
{
id: "union-city-newark",
name: "Union City & Newark",
polygon: [
[37.612, -122.075],
[37.609, -122.02],
[37.58, -122.026],
[37.583, -122.08],
],
minHeight: 7,
maxHeight: 22,
gridAngle: 0.72,
palette: "industrial",
towerChance: 0.002,
coverage: 0.32,
},
{
id: "fremont",
name: "Fremont",
polygon: [
[37.56, -122.01],
[37.556, -121.95],
[37.522, -121.958],
[37.526, -122.018],
],
minHeight: 7,
maxHeight: 24,
gridAngle: 0.85,
palette: "residential",
towerChance: 0.002,
coverage: 0.32,
},
{
id: "milpitas",
name: "Milpitas",
polygon: [
[37.462, -121.92],
[37.458, -121.876],
[37.434, -121.88],
[37.438, -121.924],
],
minHeight: 7,
maxHeight: 24,
gridAngle: 0.2,
palette: "industrial",
towerChance: 0.003,
coverage: 0.34,
},
// ---- Marin --------------------------------------------------------------
{
id: "mill-valley",
name: "Mill Valley & Corte Madera",
polygon: [
[37.93, -122.545],
[37.927, -122.5],
[37.895, -122.508],
[37.898, -122.552],
],
minHeight: 7,
maxHeight: 20,
gridAngle: 0.4,
palette: "residential",
towerChance: 0.001,
// Steep, wooded and zoned to stay that way. Half the lots on this hillside
// have more tree over them than roof.
coverage: 0.28,
},
{
id: "san-rafael",
name: "San Rafael",
polygon: [
[37.985, -122.545],
[37.982, -122.5],
[37.958, -122.506],
[37.961, -122.55],
],
minHeight: 7,
maxHeight: 30,
gridAngle: 0.62,
palette: "residential",
towerChance: 0.004,
coverage: 0.36,
},
];
const WHOLE_BOARD: City["chapters"][number] = {
id: "all",
number: "01",
label: "The Whole Board",
shortLabel: "Whole Board",
focus: { lat: 37.775, lng: -122.432, distance: 620, height: 430, rotation: 0.52 },
description:
"The Pacific on one side, the bay in the middle, and the Diablo range closing the east. San Francisco sits on the tip of the peninsula; the valley runs sixty miles south of it.",
};
export const CHAPTERS: City["chapters"] = [
WHOLE_BOARD,
{
id: "hayes-valley",
number: "02",
label: "Hayes Valley",
shortLabel: "Hayes Valley",
focus: { lat: 37.7765, lng: -122.4241, distance: 34, height: 25, rotation: 0.4 },
description:
"Cerebral Valley. A dozen square blocks of Victorians and hacker houses that ended up the densest concentration of AI companies anywhere, sitting in the bowl below Buena Vista and Corona Heights.",
},
{
id: "soma",
number: "03",
label: "SoMa",
shortLabel: "SoMa",
focus: { lat: 37.7799, lng: -122.4038, distance: 42, height: 30, rotation: 0.78 },
description:
"South of Market, where the grid turns forty-six degrees and the blocks get long. The infrastructure and platform companies, warehouses turned offices, and the tallest thing west of the Mississippi.",
},
{
id: "fidi",
number: "04",
label: "FiDi & Jackson Square",
shortLabel: "FiDi",
focus: { lat: 37.7945, lng: -122.4015, distance: 36, height: 28, rotation: 0.95 },
description:
"The old money and the venture money, stacked between Montgomery and the Embarcadero under the pyramid. Enterprise sales offices and the funds that write the cheques.",
},
{
id: "mission-bay",
number: "05",
label: "Mission Bay",
shortLabel: "Mission Bay",
focus: { lat: 37.7702, lng: -122.3905, distance: 34, height: 24, rotation: 0.72 },
description:
"Landfill turned biotech campus. Everything here was built after 1998, which is why it is the only part of the city on a grid nobody argues about.",
},
{
id: "mission",
number: "06",
label: "Mission, Potrero & Dogpatch",
shortLabel: "Mission",
focus: { lat: 37.7605, lng: -122.4055, distance: 46, height: 30, rotation: 0.5 },
description:
"The flat sunny part and the hill above it. Applied-AI teams and hardware startups in the old industrial edge along the water, with Bernal closing the valley to the south.",
},
// Chapters 07 onward are the rest of the region. 01 through 06 are untouched
// and still open on the city; the pull-back that used to be chapter 07 now
// lands on something.
//
// Every pose here is deliberately inside `hypot(distance, height) < 340`,
// because that is `maxDistance` on the city scene's `SceneKit` and
// `controls.update()` will quietly reel the camera in past it. At this
// `latScale` that is about 32 km of standoff, so the region gets five
// chapters rather than one impossible wide shot. If those limits are ever
// raised, these are the numbers to raise with them.
{
id: "peninsula",
number: "07",
label: "The Peninsula",
shortLabel: "Peninsula",
focus: { lat: 37.545, lng: -122.3, distance: 235, height: 150, rotation: 1.45 },
description:
"Twenty miles of city between the water and the ridge, and never more than four miles wide, because the Santa Cruz Mountains come down almost to the bay. Daly City to San Mateo, an airport built out onto the mud, and the two freeways — 101 on the flat, 280 along the foot of the hills — that carry everybody between them.",
},
{
id: "silicon-valley",
number: "08",
label: "Silicon Valley",
shortLabel: "The Valley",
focus: { lat: 37.425, lng: -122.115, distance: 185, height: 118, rotation: 2.95 },
description:
"Menlo Park, Palo Alto, Mountain View, Sunnyvale, looking south down the valley. The most expensive ground in the country is two storeys high: Palo Alto caps its buildings at fifty feet, and the campuses along the bay edge are low pale boxes on old salt marsh.",
},
{
id: "south-bay",
number: "09",
label: "San Jose & the South Bay",
shortLabel: "San Jose",
focus: { lat: 37.345, lng: -121.905, distance: 175, height: 112, rotation: 3.6 },
description:
"The largest city on this map and the flattest-looking one, because the airport is in the middle of it and nothing downtown may stand up into the approach. The pueblo grid sits forty degrees off the suburban grid around it, and at the bottom of the frame the valley closes with the marshes at Alviso.",
},
{
id: "east-bay",
number: "10",
label: "The East Bay",
shortLabel: "East Bay",
focus: { lat: 37.815, lng: -122.268, distance: 170, height: 112, rotation: 1.35 },
description:
"Oakland and Berkeley on the flats with the ridge standing behind them. Grizzly Peak and Vollmer are twice the height of Twin Peaks and run thirty miles without a gap, which is why the far side of the bay reads as one wall with a city at the bottom of it. Mount Diablo is the one behind that.",
},
{
id: "north-bay",
number: "11",
label: "Marin & Tamalpais",
shortLabel: "Marin",
focus: { lat: 37.925, lng: -122.545, distance: 190, height: 125, rotation: 0.35 },
description:
"Seven hundred and eighty-four metres of mountain eight miles from the Ferry Building, with nothing in front of it. Every northward view from San Francisco ends on this silhouette. Sausalito and Mill Valley are in its lap; San Rafael is round the corner on the bay side.",
},
{
id: "bay-area",
number: "12",
label: "The Bay Area",
shortLabel: "The Bay",
focus: { lat: 37.63, lng: -122.24, distance: 280, height: 150, rotation: 0.15 },
description:
"Looking north up the bay from off San Mateo, with three crossings ahead and two behind. The two ranges are what make this one room rather than a coastline: the Santa Cruz Mountains on the west, the Diablo range on the east, and everything anyone built squeezed onto the strips between them and the water.",
},
];
// ---- The pack ------------------------------------------------------------
export const SAN_FRANCISCO_CITY: City = {
id: "sf",
name: "San Francisco Bay Area",
// The centre stays on the city, even though the map now runs forty miles
// south of it. `center` is the origin of scene space and nothing else —
// moving it would renumber every coordinate in the scene to no purpose, and
// San Francisco is still what this pack is about.
center: { lat: 37.7749, lng: -122.4194 },
bounds: { minLat: 37.18, maxLat: 38.03, minLng: -122.64, maxLng: -121.75 },
// Unchanged, and it has to be. One scene unit is ~94 m here and `blocks.ts`
// holds a fixed 0.42-unit lot, so `latScale` is really a statement about the
// size of a city block. Shrinking it to make the whole bay fill the frame the
// way the city used to would need about a quarter of this, and every house in
// San Jose would then be a hundred and seventy metres across. The region is
// wide in this pack because the bounds are wide, not because the scale moved.
latScale: 1180,
/**
* Tuned down from 4.5 once the hills were built on. Bare domes needed the
* height to register at all; a hillside covered in houses shows its slope by
* itself, and 4.5 turned Twin Peaks into a volcano.
*/
verticalExaggeration: 3.6,
// ~45 m cells inside a focus region, ~270 m outside one.
//
// This is the LOD ARCHITECTURE.md §5 said Los Angeles would need, arriving a
// city early: a uniform 45 m lattice over the whole bay is 3.7M points, and
// that is a map nobody can load. The first region is exactly the bounds this
// pack used to have, so San Francisco is built at the resolution it has
// always been built at and renders identically. The other two are the two
// places outside the city where a chapter puts the camera near the ground,
// which is the rule: fine where a chapter lands, coarse everywhere else.
//
// Nothing else needs it. The Peninsula's bay edge and the Santa Clara Valley
// are flat, so a coarser lattice loses nothing there; the coastlines stay
// crisp at any resolution regardless, because `terrain.ts` draws them from
// the shore plates rather than from the grid.
cellLat: 0.000404,
cellLng: 0.000511,
coarseFactor: 10,
/**
* ONE focus region, and a high coarse factor, because of how `buildAxis` in
* world.ts refines: per axis, not per rectangle. A focus region sharpens its
* whole row *and* its whole column, so two regions at opposite corners of a
* board refine almost all of it. Measured on this pack: one region is 0.53M
* lattice points and a 2.3 s field build; adding the Peninsula and South Bay
* regions took it to 1.64M and 9 s for detail nobody is looking at from a
* board this wide.
*
* The consequence is deliberate: San Francisco itself stays at ~45 m cells,
* and the rest of the bay renders at 450 m. At this zoom that is the right
* trade. A true quadtree would let every region be fine, and is the thing to
* build if the wider bay ever needs to be walked rather than looked at.
*/
focusRegions: [
{ minLat: 37.6975, maxLat: 37.8925, minLng: -122.5725, maxLng: -122.2175 },
],
coastFalloff: 0.0018, // ~200 m
landmasses: LANDMASSES,
parks: PARKS,
inlandWater: INLAND_WATER,
hills: ALL_HILLS,
districts: DISTRICTS,
landmarks: LANDMARKS,
bridges: BRIDGES,
roads: ROADS,
chapters: CHAPTERS,
/**
* SFO and SJC, drawn by `engine/airports.ts` as graded plates with the real
* runway headings on them. The four runways that used to be pale `Road`
* strips are gone from `ROADS` above and must stay gone: a road ribbon drapes
* thirteen metres over the terrain at this scale, so a board carrying both
* would float a dark stripe across every runway the kit lays flush.
*/
airports: AIRPORTS,
};
export default SAN_FRANCISCO_CITY;