Thai HospSearch
A geospatial drill-down navigator that unifies Thailand's scattered hospital, school, and village codes with the province → district → subdistrict administrative hierarchy — in one map, instead of several incompatible government portals.
Thailand's healthcare administrative data is scattered across incompatible registries: the Ministry of Public Health's facility-code system, school and village code registries, and the standard province/district/subdistrict area-code hierarchy — each queryable only through separate, non-interoperable government portals with no shared geospatial view. Cross-referencing a facility against its administrative area, or finding everything within a district, meant working across several disconnected lookups by hand.
Facility, school, and village codes are pulled from their respective Ministry of Public Health sources and normalized against a standard province/amphoe/tambon reference dataset, so every record resolves cleanly through the full hierarchy: region → province → district → subdistrict → facility. Direct lookup by raw area code is also supported.
The geospatial layer is a Leaflet.js drill-down map across four polygon layers (health regions, provinces, districts, subdistricts) — clicking a region loads its child polygons, with code and name tooltips on hover. The whole thing runs serverless: all source data is preprocessed into a single ~50 MB SQLite database at build time and queried entirely client-side through a WebAssembly build of SQLite, with FTS5 trigram indexes carrying name search. There is no backend server and no query ever leaves the browser.
Hospital markers used to be fetched from the Ministry's GIS API directly by the visitor's browser, on every province or district click. That host turns out to be unreachable from outside Thailand, so the map failed for a whole class of visitors while looking perfectly healthy from inside the country.
The cause was a block on where the request came from, not the datacenter-IP filtering already known from the build pipeline. A commercial VPS in Bangkok reached the endpoint normally; a US residential connection and two commercial proxy providers with Thailand-targeted exit addresses were all refused — both providers classify the host as “Government” and decline it outright, including on a tier marketed as having no domain restrictions.
The fix removed the dependency rather than working around it. The points are now fetched once and baked into 77 static per-province GeoJSON files (~4.5 MB) served from the CDN, keeping the same lazy, click-triggered loading the map had before.
Rolling that out exposed a second defect that had been failing silently. Drilling into a district returned no markers at all while the province view looked correct, because the fetch script matched the Ministry's facility code against a nine-digit registry key when that code is only five digits — 0 of 11,347 points matched, and nothing raised an error. Reviewing the repair then showed the Ministry's own response already carries province and district codes on every point, so the lookup had been solving a problem that did not exist. Deriving the district code straight from the response resolves 11,347 of 11,347, including two facilities the registry could never have matched.
A live, publicly deployed utility rather than a one-off demo. Roughly 174,000 records resolve through the full hierarchy — 44,211 health facilities across 17 facility types (42,842 active, 1,369 cancelled, including 5,410 subdistrict health-promoting hospitals and 770 community hospitals), 42,255 schools and 77,316 villages, mapped to 13 health regions, 77 provinces, 929 districts and 7,436 subdistricts.
Refreshing that data is manual by necessity. The weekly build job was retired in September 2026 once it emerged that it had been rebuilding an unchanged database since June: the same origin block that broke the map also refuses the build runners, so every “successful” run had quietly fetched nothing. Updates now run from a Thailand-origin network when access allows, and the app's own footer was reworded to stop implying a cadence the pipeline cannot deliver.