Failed deliveries, abandoned checkouts, and a data team reconciling four spellings of the same district. One clean, multilingual view of Thai geography instead.
An address that doesn't resolve doesn't fail quietly. It becomes a call to the customer, a second delivery attempt, a driver idling in the wrong soi, or an order that never gets placed.
Most companies carry this as a cost line they've never named.
Defaults are placeholders, not benchmarks. This ignores the customers who never came back, which is usually the larger number.
Pull Bangkok from three sources and you get three cities: Bangkok, Krung Thep, Krung Thep Maha Nakhon, sometimes Bangkok Metropolis — with the Thai in a different field and the Chinese missing.
Every join written on those strings is a bug waiting for a release. Here, every alias points at one permanent id.
Every chip above is the same record. Nothing here is fuzzy — these are stored aliases.
Thai has no single correct romanisation. Jatujak, Chatuchak and Chatujak are one market; Thonglor and Thong Lo are one street. When search returns nothing, the customer retypes once — then leaves.
We match on how a name sounds, so spellings nobody has written down still land on the right place.
No alias list was consulted — “Jatujak” isn't stored anywhere. It matched on sound.
The same index powers the dropdown your customers actually see.
Chinese-speaking buyers and travellers are a material share of Thai property, retail and tourism revenue — usually served by a second, worse dataset, or by machine translation that renames a district every time it runs.
All three scripts live on the place itself. Set the language once and names, addresses, route steps and region labels follow.
Same record, same id, three scripts — stored, not translated at request time.
The labels on the map switch with it.
Your customer shares a location. Your driver needs a road, a khet, a postcode — in Thai, in the postal form, not a transliteration of an English guess. That gap is where redeliveries come from.
Click the map to move the pin. The Thai line is the postal form, not a transliteration of the English one.
Distance, duration and steps over geometry classified for Thai traffic — so the ETA you show a customer and the SLA you sign with a client come from the same numbers.
A radius on a map is a guess dressed up as data — five kilometres means something different beside an expressway than it does down a soi. These are real Valhalla drive-time contours from our own routing engine: 8, 15, 23 and 30 minutes, computed on the same road network the directions above just routed you across.
Area and place counts per band come straight off the geometry Valhalla returns — a geodesic calculation over the actual polygon, not a cell count from a hand-rolled grid. Move the slider and every number updates from the same contour that's drawn.
One real Valhalla drive-time contour at a time, at the engine's own resolution. Each budget is its own file, fetched when the slider reaches it and kept in memory after that.
Nobody publishes observed traffic volume for Thailand, and this archive publishes less than that: the road layer carries no lane count and no speed limit. What it carries is a classification — motorway, trunk, primary, secondary, tertiary, minor, service — and a oneway flag where somebody tagged one.
So the vehicles on the map are derived from that classification and nothing else. A motorway carries more of them and moves them faster than a soi does because that is what the class implies, not because anything here counted a car. Direction follows the tiles where oneway is set, and splits both ways where it isn't.
It reads well as a network at a glance — which corridors carry a city, where a fleet would concentrate. It reads badly as anything you'd route against, and we won't sell it as one.
| class | per km | km/h | on screen |
|---|---|---|---|
| motorway | 26 | 88 | — |
| trunk | 21 | 72 | — |
| primary | 17 | 48 | — |
| secondary | 12 | 37 | — |
| tertiary | 8 | 30 | — |
| minor | 5 | 22 | — |
| service | 2 | 12 | — |
Derived from the road classification and nothing else. The tiles carry no lane count, no speed limit and no vehicle count, so the numbers above are what a class implies, not what a junction does.
Unlike the section above, nothing here is modelled. Building footprints in the archive carry render_height, and many of them also carry render_min_height — the floor a volume starts at. The panel counts both, live, off whatever tiles are loaded under the camera.
That second field is why this is worth tilting for. King Power Mahanakhon, at the centre of the frame, arrives as roughly thirty stacked volumes stepping from 270 m to 315 m, which is what produces its pixelated silhouette — a single box extruded to one height would lose it. The pin reads the tallest footprint in frame straight off the record; no number in this section was typed in by hand.
Two attributes on the building layer, extruded as they are. The stacked-volume count is what makes the tilt worth doing: a tower with setbacks arrives as a pile of separate solids, each with its own floor height, and only an extrusion with a real base shows that.
Everything above this line is data we hold. This is not: it's a three.js scene handed to the map through a custom WebGL layer, given a model matrix built from one lng/lat, and drawn into the same frame as the buildings.
It's the MapScale mark, 205 m over Lumphini Park, on a mast that touches the grass at 13.7312, 100.5425. Deliberately nothing you could mistake for a building — a floating diamond in two rings is obviously ours, so there's no question about which shape came from the archive and which one we put there.
Drag it, or hold ⌘ and scroll to zoom. It stays on that patch of grass through both, because the anchor is a mercator coordinate rather than a screen position — which is the difference between a 3D object on a map and a picture pinned to a page. It writes into the same depth buffer as the building extrusions, so a tower standing between you and it cuts it off rather than the other way round.
That scale factor is the reason a constant will not do: mercator units are fractions of the projected world, so a metre is worth a different number of them at every latitude. Computed at 13.7312 N, the object stands 205 m tall here — and would stand 205 m tall in Chiang Mai, from the same code with the coordinate changed.
A vehicle at its last ping, a floor plan on its plot, a crane on a site, a coverage volume over a cell tower — same call, more triangles. We'll take the geometry and the coordinate; the camera, the occlusion and the projection are already here.
"Show me everything in this province" is the most common question a management team asks a map, and the most expensive to build from scratch. Thai geography nests — 77 provinces, 928 districts beneath them, subdistricts beneath those — and almost every question on that tree lands somewhere a name-keyed dataset breaks the first time a transliteration changes.
Click down through it. Each level is joined to the one above on the administrative code, never on the label — the khet everyone spells Watthana is Vadhana in the source data, and a join on that string would drop it without a single error.
These are the official boundaries. A shape you draw yourself — a delivery zone, a sales territory — is a different query against the same places; tell us the polygon and we'll scope it rather than fake one here.
Boundaries: RTSD / UNOCHA COD-AB, published on HDX (CC BY-IGO), vendored as static files and simplified for display — they are not in our own tiles. Levels are joined on the PCODE — the official romanisation of วัฒนา is “Vadhana”, so a join on the English name would drop a district and look like it worked.
Global providers are excellent almost everywhere. Thailand is one of the places where "almost" is expensive — and where a local vendor can be held accountable in the same time zone.
| Typical global provider | MapScale | |
|---|---|---|
| Thai place names | Varies by dataset and by field | Official Thai on every record |
| Romanisation variants | Exact match, misses on spelling | Phonetic, spelling-independent |
| Chinese names | Sparse or machine-translated | Stored, reviewed, stable |
| Admin geometry | Province level, often coarse | Province, amphoe and tambon |
| Billing | Per map load and session, hard to forecast | Per call, flat rate |
| Support | Ticket queue, English, offset time zone | Thai and English, Bangkok hours |
| Data residency | Vendor's choice | ap-southeast-1, contractually |
| Fixing a wrong name | Community edit, weeks to propagate | You report it, we fix the record |
Describes common behaviour across global providers rather than a named vendor. Bring us a specific one and we'll benchmark against it with your data.
One rate per call across every endpoint. No per-map-load billing, no session accounting, no invoice that triples the month a campaign works.
Prices shown as placeholders pending the published rate card.
Send a sample export. We come back with the share that resolves cleanly, the failure patterns in your data, and what they're plausibly costing — before anyone signs anything.