Lesson 3 — What you’ll learn
This tutorial covers two intertwined topics. One is the architectural pattern for building maps in Rails declaratively, with Phlex on the component side and the Vera gem wrapping MapLibre. The other is GIS proper — the database concepts, query patterns, and performance considerations of working with spatial data. The two arrive together across ten modules.
The arc roughly mirrors how a real spatial application develops. You start with one point on a map and end with a half-million-row dataset, vector tiles, drawn polygons, and live position streams. Every module introduces concepts that the next module relies on.
Module 1 — Why this tutorial exists
You’re reading it. Framing.
Module 2 — Setup and your first map
Install Rails 8, PostgreSQL, and PostGIS. Configure the dispatch app’s chassis (which ships pre-built — the chassis isn’t what we’re learning to build). Add the Vera gem. Render an empty map of Australia. By the end of the module you have a working development environment with the basic Phlex map component on the page.
The conceptual content here is light: what MapLibre is, how it differs from Leaflet and Mapbox, what a tile server is, why coordinates use [lat, lng] in some libraries and [lng, lat] in others. Mostly setup.
Module 3 — Geometry in PostgreSQL
The first deep module. We add PostGIS to the database, learn what
geometry and geography columns are, set up a Depot model with
a point column, insert one row via SQL, and import the ABS SA3
boundaries via a rake task. By the end you understand SRIDs (and
why 4326 is what we use), the difference between geometry and
geography types, and what’s actually inside a geometry column at
the bytes level.
This module is also where Audience A might find the slowest pace. If you already know SRIDs and projections you can move quickly. The boundary-import lesson is worth doing regardless because the boundaries become the foundation for everything later.
Module 4 — Data into maps
The bridge. We write a controller action that returns depot locations as GeoJSON. We connect that GeoJSON to the map. We use the Phlex DSL to declare a circle layer with paint properties — colour, opacity, radius. We render multiple layers from the same source.
By the end of this module the application shows real depot data on a real map. This is also the module where Audience B starts to see the architecture click — there’s a clear pipeline from PostGIS to Rails to JSON to the browser, and each stage is something you can inspect and reason about.
Module 5 — Interaction
Click handling. Popups, both simple-text and Turbo-Frame-loaded. Hover state via feature-state and the on_hover paint patch. URL-driven filtering. And one critical chapter on the three Turbo primitives — morph, frames, and streams — and when each is the right choice. That conceptual chapter pays back across every later module.
By the end of Module 5 the map is interactive. You can click depots to see details. You can filter by URL parameters. The application starts to feel like a tool, not a demo.
Module 6 — Polygons and spatial relationships
The chapter where service areas come alive. We render the ABS
polygons as fill layers. Click a polygon, drill into its data —
which depots are inside, how many jobs were performed there. We do
real spatial joins with ST_Within. We build a choropleth — the
classic GIS visualisation where polygon colour encodes a value.
The conceptual content includes the data-vs-display principle:
hover state shouldn’t corrupt the choropleth’s encoding because
choropleth colour means something specific to the reader. Small
detail, huge impact on map design.
Module 7 — Working at scale
The performance module. We step up the seed data from 50,000 jobs
to 500,000 and watch the queries slow down. We add a GiST index
and watch them speed back up. We discover that not every spatial
function uses the index — ST_DWithin does, ST_Distance < N
doesn’t — and learn why. We cluster the points on the map. We
implement viewport-based loading. We serve vector tiles from
PostGIS using ST_AsMVT.
This is the module where you stop building a toy and start building production-shaped software. The chapter on how spatial indexes actually work is worth the price of admission for many readers — most GIS-with-Rails resources don’t go this deep.
Module 8 — Drawing and saving
Geoman integration for drawing tools. The dynamic-import pattern that keeps drawing JavaScript out of pages that don’t need it. Saving drawn geometry to PostGIS. And the payoff — querying within drawn shapes. “Show me jobs inside this polygon I just drew.” The loop closes between user input and spatial query.
Module 9 — Real-time
The headline module. Action Cable broadcasts. A position simulator that nudges technician GPS coordinates toward their next job. The live dispatch view where vehicles glide across the map in real time. By the end of Module 9 the application looks and feels like a real operations tool. Multiple browsers watch the same fleet.
This is also where the role-aware UI gets its biggest workout. Each role’s real-time view is different — a manager watches the fleet nationally, a dispatcher watches their region, a field officer watches their own ETA.
Module 10 — Production
Deployment, performance considerations beyond the database, system testing for spatial features, and a forward-looking chapter on what’s next. Less about new techniques, more about taking what you’ve built and putting it in front of real users.
What you’ll know by the end
Specifically, by the time you finish Module 10:
You’ll know how to set up a PostGIS-backed Rails 8 application and configure it for spatial work — adapter selection, geometry columns, indexes, fixture data.
You’ll know how to declare maps as Phlex components, with sources, layers, paint properties, popups, hover state, and click handling — without writing custom JavaScript for the common cases.
You’ll know how to think about spatial data — when to use geometry vs geography, what an SRID is and why 4326 is usually the right choice, what bounding boxes are and why they matter for performance, when a query benefits from an index and when it doesn’t.
You’ll know how to structure the queries that real spatial applications run — points-in-polygons, distance queries, polygon overlap, viewport-bounded queries — and how to make them fast at scale.
You’ll know how to integrate maps with Hotwire — when to use Turbo morph for state changes, when to use Turbo Frames for independent regions, when to use Turbo Streams for server-pushed updates.
You’ll know how to handle large datasets — vector tiles served from PostGIS, clustering, viewport-bounded loading, when GeoJSON stops scaling and what to do about it.
You’ll know how to build role-aware UIs in a real Rails application, with policy-driven access control and scoped queries that compose with spatial constraints.
You’ll know how to stream real-time spatial data to maps using Action Cable, including the practical considerations of what should be persistent state versus transient state.
That’s the technical surface. There’s a less concrete benefit too: you’ll have a mental model for spatial software that makes the next project — whatever it is — much easier than the first one was. The specifics of MapLibre, PostGIS, and the Vera gem are tools you’ve chosen. The mental model — what spatial data is, how databases find things in space, what makes a query fast or slow — is general.
The next lesson covers the three user roles that shape how the application is structured. After that, Module 2 starts.