Work · Government - economic development / free zones (GCC)

Geospatial Economic Zones Intelligence Demo.

RoleSole builder - data pipeline, mapping engine, deployment
Period2026 - Q3
StatusDeployed to private cloud environment (organisation network only)
Scale~4,400 LOC app · ~2,100 LOC data pipelines · ~780 LOC tests · 24.9 MB packaged basemap · sole author
Next.js 16React 19TypeScriptMapLibre GL 5.24PMTiles/ProtomapsMapbox GL (opt-in)GeoJSONshadcn/Base UITailwindPythonPlaywrightAzure App Service

Geospatial Economic Zones Intelligence Demo

An executive GIS demonstration of economic zones - sector concentrations, companies, anchor assets, and curated strategic implications - engineered to run with zero external network calls.


1. The problem

Senior government stakeholders needed to explore a city’s economic free zones: inspect sector concentrations, companies, and anchor assets; move from a metro-wide overview into individual zones and entities; and understand curated strengths, gaps, and strategic implications.

The defining constraint was not functional. It was operational: this had to be presentable in a room where the network cannot be trusted, and where a live map-tile request to a third-party service is both a reliability risk and a data-governance question. A demo that fails because a corporate firewall blocks a tile CDN is a demo that fails in front of the client.

So the requirement became: a fully local, read-only, desktop-first GIS application with no runtime external dependency whatsoever.


2. Architecture and the decisions behind it

Mapping engine. MapLibre GL JS with a locally packaged vector basemap (Protomaps/OSM PMTiles archive) covering the metro region through zoom 14 - enough for both a city-wide overview and detailed zone navigation. Style JSON, glyphs, sprites, icons, fonts, and attribution are all packaged locally.

I explicitly removed a commercial cloud mapping service from the baseline after evaluating it - the dependency bought nothing the packaged basemap didn’t provide, while introducing exactly the runtime network risk the brief prohibited.

Business data. Partitioned GeoJSON for zones, assets, and per-zone entities, with generated local JSON for summaries, taxonomy, evidence, and curated insights. Partitioning is deliberate: keeping overlays as separate per-zone payloads means filtering, evidence lookup, and clustering stay truthful to the underlying data rather than approximations over a pre-aggregated blob.

Data preparation. A reproducible offline Python pipeline generates every artifact, so the dataset can be rebuilt from source rather than hand-maintained.

Scope discipline, then a controlled exception. Deployment, live business integrations, runtime geocoding, mobile-first design, and generative AI during the presentation were all explicitly out of scope. When a stakeholder later requested 3D, I added it as a local extrusion mode (zones extruded by the active metric) plus an opt-in, token-gated satellite/3D-buildings mode that stays hidden unless a token is configured - so the local-first, zero-external-call default is structurally preserved rather than merely intended.

Evidence provenance - and it is real data, not illustrative. Unusually for a demo, the zone records carry cited official facts with live source URLs and a checked-on date, footprints derived from open mapping data are explicitly disclaimed as not legal or cadastral boundaries, and the dataset manifest carries a version and as-of stamp. Entity coverage is a curated sample per zone rather than an exhaustive scrape - so “real” applies to the sourced facts and geometry, not to comprehensive company-level coverage, and the distinction is stated rather than blurred. In a demo shown to government stakeholders, an unsourced assertion is a liability and an overstated one is worse.

The analysis layer - the most substantive logic in the build

Beyond mapping, the app computes genuine spatial and structural analysis (~570 LOC): haversine great-circle distances, medians, a value-chain completeness classifier per industry cluster, fragmentation metrics (top-three concentration share, density within 5 km and 10 km radii), and a maturity/readiness scoring system.

The part I’d single out: the scorer refuses to produce a score for dimensions the data cannot ground. Of seven dimensions, four are grounded and three return null rather than a number - and there is a test asserting that the ungrounded three never fabricate a score. That is the same principle as the strategy knowledge agent’s evidence gate, arrived at independently in a completely different domain: a visible gap beats a plausible number, and the way you keep that true over time is to make it a test rather than an intention.


3. Verification

The testing approach is the part I’d point to:

  • Data-contract tests that parse the PMTiles binary header directly - asserting spec version, tile type, that the archive’s bounds cover the full presentation camera, and that max zoom is sufficient - plus boundary geometry structure, provenance fields, and land-clip correctness. A genuine binary-format contract test, so a malformed data rebuild fails loudly rather than rendering a subtly wrong map.
  • Analysis invariant tests over the live dataset, including the “never fabricate an ungrounded score” assertion above.
  • Reducer tests covering stale-response race handling - the bug class where a slow request for a previously-selected zone resolves after a newer one and silently overwrites the view.
  • End-to-end Playwright coverage across zone views, metric lenses, evidence interactions, and reset.
  • A visual QA capture command that drives a real browser through the overview, all five zones, both metric lenses and the analysis panel, recording every network request whose host is not localhost into a set - and failing the run with a non-zero exit code if that set is non-empty, or on any console error, HTTP error, or unexpected request failure.

That last one is the load-bearing test: it converts “should have no external dependencies” from an intention into an automatically enforced invariant. The offline guarantee is verified by assertion, not by hoping nobody added a font link.

It is also correctly scoped, which I’d flag as the more mature detail: the script explicitly documents that it does not exercise 3D mode, because 3D intentionally streams from a remote provider and would need its own allowlisted capture. So the guarantee is stated precisely as “zero remote requests on the default 2D path” rather than an unqualified claim the test couldn’t actually support.

Full typecheck, lint, unit, build, and e2e gates run as standard.


4. Deployment

Later deployed to managed cloud app hosting restricted to the organisation’s network via private endpoint, in-region. Two infrastructure details worth noting: DNS was provisioned through the platform-engineering self-service tooling rather than a central request queue, and deployment over a private endpoint required temporarily pinning host resolution to the endpoint IP to push the build - a small but non-obvious piece of enterprise-network problem-solving.


5. What I’d highlight

  • Engineering to an operational constraint. “Must not touch the network” drove every architectural decision, including removing a mapping service I had already evaluated.
  • Making the constraint testable. The visual QA gate that rejects any remote request is what turns an offline promise into a guarantee.
  • Truthful data partitioning so filtering and evidence remain accurate rather than approximated.
  • Handling scope change without abandoning the principle - 3D added, local-first default structurally preserved via token gating.
  • Provenance-backed insight for a government audience.

6. Skills demonstrated

geospatial engineering · MapLibre GL JS · vector tiles / PMTiles packaging · offline-first architecture · GeoJSON data partitioning · Next.js 16 / React / TypeScript · Python data pipelines · Playwright end-to-end testing · data-contract testing · automated environmental invariants · Azure App Service with private endpoints · enterprise DNS and networking

The first step

Twenty minutes. You describe where AI is stuck.

You leave the call knowing whether I can help, roughly what it would take, and what it would cost to find out for sure. If I am not the right person, I will say so on the call.

The 20 minutes, in order

  1. You talk first. Where AI is stuck, what has been tried, what it costs today.
  2. I answer plainly. Whether I can help, and what I would look at first.
  3. You leave with a next step. An audit scope, a pointer elsewhere, or a clean no.

Before you book

  1. Your stack is not too messy to start. Messy is the normal starting condition.
  2. Training that does not survive the week is the normal outcome. These sessions build on your backlog and ship something real.
  3. You do not need budget approved to take the call. You need it approved to start step two.