Geospatial Web Applications
Five geospatial applications for service requests, data review, simulated fleets, parcel scenarios, and inspections, each with a server-backed and standalone browser edition.
Why it matters
Maps sit inside larger workflows for editing, review, and export. Building each application with server-managed and browser-managed records makes storage, concurrency, and processing responsibilities explicit across the same synthetic geospatial use cases.
My contribution
I designed and implemented both editions as an AI-assisted personal learning project. My work includes spatial checks, edit-conflict handling, traceable exports, and backup restoration, using synthetic records throughout.
Result
The standalone editions are available as a static demo with local persistence and backup restoration. The server-backed editions remain unhosted prototypes.
Tools
- Ruby on Rails
- React
- PostgreSQL/PostGIS
- ArcGIS Maps SDK
- CesiumJS
- Redis and Sidekiq
- IndexedDB
- JSTS
- Docker
- GitHub Actions
These are learning prototypes using synthetic data. Only the standalone browser editions are publicly hosted; their roles are simulated and records stay in browser storage. Container checks and single-machine benchmarks do not establish production capacity, security certification, or operational adoption.
Project gallery
Screenshots use synthetic records, and each caption names its edition. Basemap attribution is visible in each image. 6 images. Select an image to view it full size.






The same workflow, two ways to run it
In the Data Quality Portal, users check a GeoJSON file, review invalid records, and approve an export that retains its exact inputs. The server-backed edition uses Rails, PostGIS, and background workers; the public demo performs its work in the browser.
This comparison makes responsibilities visible: server authentication and shared records in one edition, simulated roles and local browser storage in the other. Browser records do not synchronize with the server editions or across devices.
Technical details
Independent learning project · AI-assisted · Synthetic data · September to October 2026
A useful geospatial application needs more than a map. It needs clear record ownership, validated inputs, understandable processing states, reproducible outputs, and reliable behavior when users edit concurrently or connections fail.
- Build each workflow as a full-stack app: Rails APIs with ownership and staff checks, PostGIS spatial operations, and Redis and Sidekiq workers.
- Build a separate standalone browser edition of each app with IndexedDB storage, simulated roles, local spatial calculations, and same-app backup restoration.
- Reject stale edits with record versions and guard asynchronous jobs against obsolete results.
- Keep the inputs behind every export, parcel calculation, and profile, and store a SHA-256 digest with each approved dataset.
- Measure browser storage and server queries on synthetic data, then reduce unnecessary record writes and large-field reads.
- Verify both editions in CI, build and start all five container stacks, and check the published standalone apps through HTTP and browser workflows.
Five workflows, each built twice
Geospatial Web Applications connect spatial data with the web workflows around it: reporting an issue, reviewing an import, tracking a simulated vehicle, comparing a planning scenario, and documenting an infrastructure observation. Each application has a full-stack edition and a separate standalone browser edition, so the same workflow can be compared with server-managed records and background jobs or with browser-managed state and local calculations.
- Civic Works: geolocated service requests with linked maps and registers; text, category, status, and distance filters; staff assignment through permitted lifecycle states; GeoJSON imports that report rejected and duplicate records; and CSV exports.
- Data Quality Portal: GeoJSON checks of required attributes, coordinate structure, geometry types, and topology. Invalid records are excluded only after an explicit acknowledgment, and approval stores a versioned export snapshot with a SHA-256 digest of its exact bytes.
- Fleet Monitor: ten synthetic vehicles on replayable routes, with playback controls, trails, speed charts, staff-managed geofences, and sequence checks that reject older telemetry.
- Parcel Scenario Explorer: 24 synthetic parcels with adjustable floors, coverage, and unit-area assumptions, and up to four saved scenarios comparing floor area, unit capacity, open space, and floor-area ratio.
- Infrastructure Inspections: a CesiumJS corridor of eight synthetic assets, observations that staff resolve or reopen with history, and a 71-sample elevation profile exported as GeoJSON with source metadata.
Two architectures, distinct responsibilities
The two editions solve the same workflows with different engines. The public demo runs the standalone editions. Their records never synchronize with the full-stack databases, browser processing stops when the page closes, and ArcGIS basemaps and SDK resources still need internet access.
| Responsibility | Full-stack edition | Standalone browser edition |
|---|---|---|
| Application data | Separate PostgreSQL/PostGIS database per app | Separate IndexedDB database per app and browser origin, with individual record entries |
| Identity and permissions | Authenticated Rails APIs with ownership and staff checks | Locally selected demo identities with simulated workflow rules |
| Processing | Redis and Sidekiq workers | JavaScript actions and a coordinated fleet timer |
| Updates | Authenticated Action Cable notifications, refresh, and polling | Local subscriptions and compatible cross-tab notifications |
| Spatial operations | PostGIS validation, distance, area, and geodesic processing | JSTS validity checks and documented JavaScript spatial formulas |
| Delivery | Container stacks verified in CI; no public full-stack hosting | Published static demo with local browser persistence |
Engineering decisions
- Separate committed results from notification delivery: in the full-stack apps, a failed notification does not undo a successful write or calculation, and clients recover authoritative state through refresh and polling.
- Treat concurrent work as normal: record versions reject stale edits, job revisions guard against obsolete results, and standalone storage transactions stop one tab from overwriting a newer state.
- Limit failed sign-ins per account across multiple IP addresses, so changing addresses does not bypass the account-level limit in the full-stack apps.
- Apply Content Security Policy in both editions. The full-stack apps use Ruby 3.4, with security scanning that now includes the previously excluded end-of-life Ruby check.
- Wait for map and scene rendering in browser checks and record canvas presence, rather than stopping when the application interface loads.
- Preserve the inputs behind an output: approved exports, parcel calculations, and elevation profiles keep what is needed to explain their results.
- Restore deliberately: each standalone app validates a JSON backup and asks before replacing its local records. Rejected files leave existing data unchanged; restoration does not merge records or synchronize devices.
- Write only what changed: standalone collections store individual records, retain committed state in memory, and save changed entries with a revision check. Existing browser databases migrate to the new layout when opened.
- Keep both editions available: the original standalone conversion preserved all 371 previously tracked full-stack files; later improvements to either edition remain separately implemented.
Measured improvements on synthetic data
Benchmarks exposed two avoidable costs: rewriting the entire browser dataset for a small edit, and loading large stored GeoJSON fields for a server-side list. Record-level browser writes and summary-only database queries reduced those costs in the recorded checks.
- The browser improvement makes small changes cheaper; opening the app still reads its stored data, and the first large upload became somewhat slower.
- Server measurements cover API latency, 10 concurrent readers, and background-job completion on one runner. They are not production capacity guarantees or measurements of large WebSocket fan-out.
| Check | Before | After |
|---|---|---|
| Standalone small upload with 30 MB already stored, Chromium and real IndexedDB | 2,083.9 ms | 2.8 to 5.2 ms |
| Full-stack dataset list with 11 synthetic datasets, median on a GitHub-hosted runner | 140.6 ms | 4.6 ms |
Verification by edition
Full-stack edition: October 21, 2026 verification at source revision e09f695 passed 181 Rails tests with 1,528 assertions, 60 frontend tests, 18 browser scenarios, and five frontend builds. Browser scenarios exercised real APIs, databases, Redis, and workers with synthetic records. All five container checks passed, covering migrations, nginx responses, seeded data, and Sidekiq registration. These prototypes remain unhosted.
Standalone edition: separate checks passed 79 domain, storage, and packaging tests, 10 interface tests, and five static builds. Coverage includes backup validation and restoration, storage migration, stale writes, and spatial workflows using synthetic data.
The standalone demo was republished at the same source revision on October 21, 2026. Published-site checks passed backup export, restoration, and persistence after reload, and confirmed a rendered ArcGIS map or Cesium scene canvas in all five apps. These are checks of prototypes using synthetic data, not evidence of production readiness. They are source-project records, not application tests rerun for this portfolio update.