Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Smart Checkpoints — Mapbox map driver

A map driver: a separate process that renders a Smart Checkpoints project on a real basemap and serves that page to the operator console.

It is deliberately small. It answers no questions, resolves no distances, and holds no state. Its whole job is to hold a socket open — which is what makes the map view exist — and to serve one page that speaks the console's postMessage bridge.

   camera ─┐
           ├─► server ──► console ──postMessage──► this page (iframe)
 distance ─┘      ▲            │
   driver         └────────────┘
                   Socket.IO

The console holds the single connection to the server and forwards project state into the frame. This driver never opens a socket to the server for data, never talks to the distance driver, and never sees an API key in the browser.

Why it is a separate process

The server must never import a mapping library, and must never know whether the map is Mapbox, Leaflet or something else. Adding an eleventh distance driver has to cost a map driver nothing, and the other way round.

That also means this page cannot be served out of the Smart Checkpoints server's public/ directory: that directory is wiped and regenerated by every console build. Hosting its own page from its own process is the point.

Running it

npm install
cp .env.example .env      # then fill in SC_API_KEY and MAPBOX_TOKEN
npm start
Variable Meaning
SC_WS_URL The driver channel. Default ws://localhost:3000/distance-driver
SC_API_KEY The project's operator key. Required
SC_MAP_HOST / SC_MAP_PORT Where the page is served. Default 127.0.0.1:4100
SC_MAP_PUBLIC_URL The address announced to the server, when it differs from the bind address
MAPBOX_TOKEN Mapbox access token. Required, and never committed
MAPBOX_STYLE Any Mapbox style URL. Default light-v11, which is what the console is

The token is a browser credential — it has to reach the browser to be of any use — so it is read from the environment and served to the page at /env.js. It is handed only to whoever can already reach this port, which is why the server binds to loopback unless told otherwise. Scope it and rotate it at Mapbox.

Mapbox GL JS is served by this driver, from /vendor/, not from a CDN. A page that is going to be embedded in a console on another origin cannot depend on a third-party origin being fetchable from inside an iframe: privacy extensions and frame-level third-party blocking stop exactly that, and it fails silently - a blank map view with nothing on screen to say why. npm install puts the library in place; the page says so plainly if it is missing.

Internet access is still required for tiles: they come from Mapbox. A project on a private camera network with no route out needs a different map driver, and that is exactly why the map is a driver rather than part of the console.

Announce, then approve

On connecting, this driver tells the server where its page is (uiUrl in the auth message). That is a proposal, not a setting. Whoever holds a project API key would otherwise be choosing what renders inside the console's own chrome: a cross-origin frame cannot read the console, but it is handed every checkpoint, distance and violation in the project and could paint a convincing fake console around them.

So the announced address lands in pending_map_driver_url with status pending, and the console shows it under Project with the origin spelled out. An operator approves it once. Only then does the console embed it.

  • Reconnecting on the same address stays approved — restarting this driver does not send anybody back to the approval screen.
  • Announcing a different address goes back to pending, and the previous approval is dropped: it described a page that is no longer being served.

Changing SC_MAP_PORT or SC_MAP_PUBLIC_URL therefore needs one approval in the console afterwards. That is the intended cost.

The bridge

Every message is { v: 2, type, payload }. Both sides pass the other's origin as targetOrigin and check event.origin on receipt — never "*", in either direction. The console tells this page its origin by setting referrerpolicy="origin" on the frame, so document.referrer is the console's origin and nothing more. Opened outside a console, this page renders nothing: there is nobody to talk to, and guessing would mean posting a project's checkpoints at an origin nobody approved.

Console → page

Type Payload
sc:init { projectId, protocolVersion, origin: {lat,lng} }
sc:graph the whole snapshot: nodes with flags, edges with distance, status, limit and path
sc:node-updated one node
sc:edge-updated one edge, including new path
sc:congestion { [connectionId]: c }
sc:diagnostics node and edge flags
sc:selection { kind: "node" | "edge" | null, id }

Page → console

Type Payload
sc:ready { protocolVersion, capabilities }
sc:select { kind, id }
sc:node-moved { nodeId, latitude, longitude } — only because this driver declared capabilities.nodeDrag

Everything arriving from the console is validated here before it is believed, and everything arriving from here is validated by the console. Neither side trusts the other; both trust an origin.

What it draws

  • Edges follow the path a distance driver measured — the real road shape, a GeoJSON LineString in WGS84, longitude first. An edge with no geometry is drawn as the straight line between its endpoints, which is what the graph view draws for everything and is the honest thing to show: nobody has said where that road goes.
  • Solid means enforcing. An edge whose distance_status is not ok is drawn broken, because it decides no violations.
  • A yellow ring is a checkpoint the data-quality checks have flagged.

That is what makes the graph/map toggle worth having: both views draw the same geometry, so a checkpoint that looks fine on the graph and sits inside a building on the map has told you something.

What it says when there is nothing on the map

An empty map has several causes and they all look identical, so the page keeps a line under the legend saying which one it is:

On screen What it means
connected - waiting for the console to send this project The basemap is up and the handshake is done; no snapshot has arrived
the console sent no checkpoints - this project has none yet A snapshot arrived and it was empty
3 checkpoints · 3 edges · 2 drawn on real roads, 1 straight... Drawn - and how much of it is measured road rather than a straight line
basemap error: ... A tile or a glyph failed after the map was up. The map stays usable

Failures before the basemap is up cover the map instead, because there is nothing behind them worth looking at: no Mapbox token, the library missing, or a style Mapbox refused.

The console has one of its own. If this page never completes the handshake it says so and names the address, because a page that did not load cannot say anything at all - and "the driver's HTTP port is not reachable from this browser" is otherwise indistinguishable from "the map is broken".

Dragging a checkpoint

Dragging a checkpoint here is a proposal. A basemap is where a bad GPS fix becomes obvious, so it is worth supporting — but moving a camera throws away every distance measured to it, and that decision belongs to the console, which asks first and says how far in metres. On drop the checkpoint goes back where it was and stays there until the console says otherwise, because until then it has not moved.

Licence

MIT.

About

Map driver that renders a Smart Checkpoints project on a real basemap and serves it to the operator console.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages