Maps & Geocoding

Render interactive maps, geocode addresses, reverse geocode coordinates, search for places, calculate routes, and let visitors draw directly on live maps.

GEOCODE — Address to Coordinates

Basic Geocode
MAP GEOCODE "10 Downing Street, London" SET ?loc
AFTER EMIT ?loc("lat") & ", " & ?loc("lon")
AFTER EMIT ?loc("house_number") & " " & ?loc("road") & ", " & ?loc("city")
Multiple Candidates, Country and Bounds Restriction
MAP GEOCODE "Springfield" LIMIT 5 SET ?candidates
AFTER MAP GEOCODE "Main Street" COUNTRY "za" SET ?loc
AFTER MAP GEOCODE "Main Street" BOUNDS ?box SET ?loc

REVERSE — Coordinates to Address

Reverse Geocode
MAP REVERSE LAT -33.9249 LON 18.4241 SET ?addr
AFTER EMIT ?addr("display_name")
AFTER MAP REVERSE LAT -33.9249 LON 18.4241 DETAIL "city" SET ?addr2

DETAIL controls how coarse the returned address is: "country", "region" (or "state"), "city", "road" (or "street"), and "house" (or "building"). Omitted, it resolves to the most specific address available. LNG is accepted everywhere as an alias for LON.

BOUNDARY — Administrative Region Shape

Get a Boundary Polygon
MAP BOUNDARY "South Africa" SET ?shape

SEARCH — Places of Interest

Search Places
MAP SEARCH "cafe" COUNTRY "South Africa" SET ?places
AFTER MAP SEARCH "cafe" COUNTRY "za" PROVINCE "Western Cape" SET ?places2
AFTER MAP SEARCH "cafe" BOUNDS ?box SET ?places3

Search by a plain place-type and sharpen it with the COUNTRY modifier — which accepts a full country name ("South Africa") or an ISO code ("za") — optionally narrowed further with PROVINCE / STATE or a BOUNDS viewbox. LIMIT caps the number of results (default 10).

Building Points, Lines, and Shapes

Points, lines, polygons, and circles are constructed as their own values before rendering — nothing is drawn directly onto a map. A rendered map is a URL, an immutable link, so every element it displays has to be supplied to MAP RENDER as input.

A Point
MAP GEOCODE "Cape Town, South Africa" SET ?loc
AFTER MAP POINT LAT ?loc("lat") LON ?loc("lon") LABEL "Cape Town" SET ?point1
A Styled Point — Icon, Popup, and Custom Pin
MAP POINT LAT -33.9249 LON 18.4241 LABEL "Cape Town" ICON "blue" POPUP "Open 9am–5pm" SET ?point1
AFTER MAP POINT LAT -33.9249 LON 18.4241 LABEL "HQ" PIN URL "https://mysite.com/pin.png" SET ?point2

ICON selects a named preset marker (e.g. "blue", "red"); PIN URL supplies a custom marker image from any URL. A point carries whichever it was given — both are optional, alongside LABEL and POPUP.

A Line
NEW ARRAY SET ?points
AFTER APPEND ?point1 TO ?points
AFTER APPEND ?point2 TO ?points
AFTER MAP LINE ?points SET ?routeline
A Polygon
MAP POLYGON ?points SET ?polygon1
A Circle
MAP CIRCLE LAT -33.9249 LON 18.4241 RADIUS 5 UNIT "km" SET ?circle1

RENDER — Composing Everything Into One Map

Every element — points, lines, polygons, circles, and heatmap data — is passed to RENDER together. The result is one URL, generated once, with everything already baked in.

Render a Single Point
MAP GEOCODE "Cape Town, South Africa" SET ?loc
AFTER MAP POINT LAT ?loc("lat") LON ?loc("lon") LABEL "Cape Town" SET ?point1
AFTER NEW ARRAY SET ?points
AFTER APPEND ?point1 TO ?points
AFTER MAP RENDER ?points ZOOM 13 SET ?url
AFTER EMIT ?url
Multi-Point Map from Database Rows
QUERY "stores" FROM "shop" SET ?rows
AFTER NEW ARRAY SET ?points
AFTER FOREACH ?rows SET ?row
OPEN
  MAP POINT LAT ?row("lat") LON ?row("lon") LABEL ?row("name") SET ?pt
  AFTER APPEND ?pt TO ?points
CLOSE
AFTER MAP RENDER ?points STYLE "satellite" AUTOFIT SET ?url
AFTER EMIT ?url
Full Composition — Points, Line, Polygon, Circle, Heatmap
NEW ARRAY SET ?points
AFTER APPEND ?point1 TO ?points
AFTER NEW ARRAY SET ?lines
AFTER APPEND ?routeline TO ?lines
AFTER NEW ARRAY SET ?polygons
AFTER APPEND ?polygon1 TO ?polygons
AFTER NEW ARRAY SET ?circles
AFTER APPEND ?circle1 TO ?circles
AFTER MAP RENDER ?points
LINES ?lines
POLYGONS ?polygons
CIRCLES ?circles
HEATMAP ?places
ZOOM 13
STYLE "satellite"
AUTOFIT
SET ?url

Tile Style Presets

Preset
standard — default street map tiles
satellite — Esri satellite imagery
topo — OpenTopoMap
toner — Stamen Toner, high-contrast black and white
watercolor — Stamen Watercolor
terrain — Stamen Terrain
light — CartoDB Positron
dark — CartoDB Dark Matter

Output Form — AS "html" or AS "image"

RENDER always returns a URL, never a file written into your namespace and never a share. Which URL depends on AS, optional and defaulting to "html":

Form Returns
AS "html" (default)A https://ocalt.com/maps/<token>.html URL serving a live, interactive map page — pan, zoom, and click markers. Embed it in an <iframe> or open it directly.
AS "image"A https://ocalt.com/maps/<token>.png URL — a rendered 800×600 PNG snapshot of the same map. A flat image, for emails, documents, or thumbnails.
Both Output Forms
MAP POINT LAT -33.9249 LON 18.4241 LABEL "Cape Town" SET ?p
AFTER NEW ARRAY SET ?points
AFTER APPEND ?p TO ?points
AFTER MAP RENDER ?points ZOOM 13 SET ?htmlUrl
AFTER MAP RENDER ?points ZOOM 13 STYLE "satellite" AS "image" SET ?pngUrl
AFTER EMIT ?htmlUrl
AFTER EMIT ?pngUrl
A rendered map does not touch your file-share quota. Maps are served from their own /maps/ token space, entirely separate from FILE SHARE and the per-tier share limit. Render as many as you like — none of them count against the shares documented on File Manager.

Tracking — A Map That Moves

A rendered map is a photograph: every coordinate is baked into the page when it is made, and the page never changes. A tracked map is the opposite. The page is a shell that asks Ocalt where things are, so a vehicle, a courier, a fleet or a delivery moves on a map somebody is already watching, without the page being regenerated.

Open a Tracked Map
NEW MAP TRACK NAMED "Delivery 4821"
AT -33.9249,18.4241
ZOOM 14
REFRESH 2
FOLLOW
TRAIL
SET ?track

AFTER EMIT ?track("url")
(* https://ocalt.com/maps/t-… — give this to whoever is watching *)

The URL is a normal link. Send it in an SMS, put it in an email, drop it in an <iframe>. Whoever opens it sees the map update itself for as long as they leave it open.

Modifier Description
NAMED "x"The page title
AT lat,lonWhere the map opens before anything has reported in
REFRESH nSeconds between updates. Default 3
FOLLOWKeep the viewport centred on whatever is moving
TRAILDraw the path travelled behind each marker
ZOOM, STYLEExactly as they work for MAP RENDER

Moving something

MAP MOVE writes a position. It can be called from anywhere, at any time, by any later script — a phone reporting its location, a vehicle’s tracker, a warehouse system, a simulation. AS names the marker, so one map can carry a whole fleet.

A Driver Reporting In
(* This runs every time the phone posts its position *)
PARSE !REQUEST("body") AS JSON SET ?in

AFTER MAP MOVE "t-4f2c…" TO ?in("lat"),?in("lon")
LABEL "Thabo — 12 min away"
HEADING ?in("heading")
AS "driver"

AFTER EMIT "ok"
A Whole Fleet on One Map
QUERY "vehicles" FROM "fleet" SET ?rows
AFTER FOREACH ?rows SET ?v
OPEN
  MAP MOVE ?track TO ?v("lat"),?v("lon") LABEL ?v("plate") AS ?v("id")
CLOSE

AFTER MAP POSITIONS ?track SET ?where
AFTER EMIT ?where
(* Every vehicle, where it was last seen, as ordinary OcaltQL data *)
A marker is named, so it moves rather than multiplies. Calling MAP MOVE again with the same AS name moves that marker; a new name adds one. MAP REMOVE ?track MARKER "driver" takes it off the map when the trip ends.
Who can watch, and who can move. The token is a public URL: anyone holding it can watch, which is the point — a customer should not need an Ocalt account to see their delivery. Moving a marker is different, and requires the account that created the map. A link that leaks lets someone watch a courier; it never lets them move one.

Reading positions back

MAP POSITIONS ?track returns what the map is currently showing, as ordinary data — so the same positions driving the map can drive a notification, an ETA calculation, or a row in a database.

Positions Into the Rest of the Language
MAP POSITIONS ?track SET ?markers
AFTER FOREACH ?markers SET ?m
OPEN
  MAP ROUTE FROM COORDINATES ?m("lat") & "," & ?m("lon")
  TO COORDINATES "-33.9321,18.8602" SET ?eta
  AFTER IF ?eta("duration_min") IS LESS THAN 5
  OPEN
    NOTIFICATION ?m("label") & " is nearly there"
  CLOSE
CLOSE

ROUTE — Driving, Cycling, and Walking Directions

Each endpoint — FROM, TO, and an optional VIA — takes either a place name (geocoded automatically) or exact coordinates via COORDINATES "lat,lon". Coordinates skip geocoding entirely, so they are exact and unambiguous; names are convenient but can land on the wrong same-named place, so sharpen them with COUNTRY / PROVINCE or a comma-structured string. Inside a COORDINATES value the comma and colon are interchangeable — "lat,lon" or "lat:lon".

Basic Route — by Name
MAP ROUTE FROM "Cape Town, South Africa" TO "Johannesburg, South Africa" SET ?r
AFTER EMIT ?r("distance_km") & " km — " & ?r("duration_min") & " min"
AFTER EMIT ?r("geometry")
AFTER EMIT ?r("steps")
By Exact Coordinates — Comma or Colon
MAP ROUTE FROM COORDINATES "-33.9249,18.4241" TO COORDINATES "-33.9321,18.8602" SET ?r
AFTER MAP ROUTE FROM COORDINATES "-33.9249:18.4241" TO COORDINATES "-33.9321:18.8602" SET ?r2
Constraining a Name with COUNTRY and PROVINCE
MAP ROUTE FROM "Cape Town" TO "Stellenbosch" COUNTRY "za" PROVINCE "Western Cape" SET ?r
Routing Profile
MAP ROUTE FROM "Cape Town" TO "Stellenbosch" PROFILE "cycling" SET ?r
AFTER MAP ROUTE FROM "Cape Town" TO "Stellenbosch" PROFILE "walking" SET ?r2
Multi-Stop Route via a Waypoint
MAP ROUTE FROM "Cape Town" VIA "Bloemfontein" TO "Johannesburg" SET ?r
AFTER MAP ROUTE FROM COORDINATES "-33.9249,18.4241" VIA COORDINATES "-29.1,26.2" TO COORDINATES "-26.2,28.05" SET ?r2
Alternative Routes
MAP ROUTE FROM "Cape Town" TO "Johannesburg" ALTERNATIVES SET ?routes
AFTER EMIT ?routes(0)("distance_km") & " km"

PROFILE accepts "driving" (default), "cycling", or "walking"MODE is an accepted alias. ALTERNATIVES makes the result an array of route objects instead of a single one. Without it, ?r is one object with distance_km, duration_min, geometry (a GeoJSON array of [lon,lat] points), and steps.

MATRIX — Many-to-Many Distances

Distance and Duration Matrix
MAP MATRIX ?points SET ?table

SNAP — Snap a Point to the Road Network

Snap to Nearest Road
MAP SNAP LAT -33.9251 LON 18.4239 SET ?snapped

DISTANCE — Straight-Line Distance

MAP DISTANCE returns the great-circle (haversine) distance in kilometres between two endpoints. Like ROUTE, each endpoint is either a place name (geocoded, sharpened by COUNTRY / PROVINCE) or exact COORDINATES "lat,lon" — comma or colon. This is straight-line distance, not travel distance; use ROUTE for road distance.

Haversine Distance — by Coordinates
MAP DISTANCE FROM COORDINATES "-33.9249,18.4241" TO COORDINATES "-26.2041,28.0473" SET ?km
AFTER EMIT ?km & " km"
By Name
MAP DISTANCE FROM "Cape Town, South Africa" TO "Johannesburg, South Africa" SET ?km
AFTER EMIT ?km & " km"

CLUSTER and BOUNDS

Cluster Nearby Points
MAP CLUSTER ?places SET ?clusters
Bounding Box Around a Set of Points
MAP BOUNDS ?places SET ?bounds

MAP LIVE — Interactive Maps the Visitor Can Draw On

Every MAP verb above produces a static result — a URL, an address, a distance. MAP LIVE is the opposite: it renders a real, interactive map into the page the visitor is looking at, lets them drop pins, draw lines, and draw polygons, and hands what they drew back to the script as ordinary OcaltQL values — the exact same shapes MAP POINT, MAP LINE, and MAP POLYGON produce, ready to feed straight into MAP RENDER or a database.

It has two forms. The halt-and-resume form (default, with SET) works exactly like GATHER and CONFIRM — it renders a centered popup, halts the script, and resumes once the visitor presses Done. The HTML node form (AS NODE) produces a native HTML node you embed anywhere in your own document at your own size, submitted through your own form.

Halt-and-Resume Form

Drop a Single Pin — Collect It Back
MAP LIVE
CENTER "-33.9249,18.4241" ZOOM 13
PINS 1
LABEL "Tap your delivery location, then press Done"
SET ?result
AFTER EMIT ?result("points")(0)("lat") & ", " & ?result("points")(0)("lon")
Fullscreen — Pins, Lines, and Polygons Together
MAP LIVE
FULLSCREEN STYLE "dark" AUTOFIT
PINS "unlimited"
DRAW LINES
DRAW POLYGONS
LABEL "Mark every stop, route, and delivery zone"
SET ?result
AFTER EMIT "Pins: " & COUNT ?result("points")
AFTER EMIT "Lines: " & COUNT ?result("lines")
AFTER EMIT "Zones: " & COUNT ?result("polygons")

?result is an object with three keys — points, lines, and polygons — each an array. Every entry is identical in shape to what the matching build verb produces, so the whole result can be handed straight to MAP RENDER.

Feeding the Result Straight into MAP RENDER
MAP LIVE CENTER "-33.9249,18.4241" ZOOM 12 PINS "unlimited"
LABEL "Drop the pins you want on the final map"
SET ?drawn
AFTER MAP RENDER ?drawn("points") ZOOM 12 STYLE "satellite" AUTOFIT SET ?url
AFTER EMIT ?url

Pre-Placing Shapes Before the Visitor Starts

The composition slots from MAP RENDER?points as the residue, plus LINES, POLYGONS, and CIRCLES — carry over unchanged. Anything passed in is drawn on the map before the visitor touches it, giving them existing context to add to.

Existing Stores Shown, Visitor Adds a New One
SELECT ROWS FROM DB "shop" TABLE "stores" SET ?rows
AFTER NEW ARRAY SET ?existing
AFTER FOREACH ?rows SET ?row
OPEN
  MAP POINT LAT ?row("lat") LON ?row("lon") LABEL ?row("name") SET ?pt
  AFTER APPEND ?pt TO ?existing
CLOSE
AFTER MAP LIVE ?existing
ZOOM 11 AUTOFIT
PINS 1
LABEL "Existing stores are shown — drop a pin where the new one goes"
SET ?result
AFTER EMIT ?result("points")(0)("lat") & ", " & ?result("points")(0)("lon")

Centering on the Visitor — with USER DEVICE

Location consent stays the job of USER DEVICEMAP LIVE never asks for a permission itself. Grant geolocation through USER DEVICE first, then CENTER VISITOR tells the map to center on the visitor's own position using that already-granted permission. Always handle the denied case — here, by falling back to a GEOIP-derived center.

USER DEVICE Grants Location, MAP LIVE Centers On It
USER DEVICE GEOLOCATION LABEL "Allow location so the map opens where you are" SET ?device
AFTER IF ?device("permissions")("geolocation") IS EQUAL TO "granted"
OPEN
  MAP LIVE CENTER VISITOR ZOOM 15 PINS 1
  LABEL "Confirm your exact location"
  SET ?result
CLOSE
OR
OPEN
  GEOIP LAT SET ?lat
  AFTER GEOIP LON SET ?lon
  AFTER MAP LIVE CENTER (?lat & "," & ?lon) ZOOM 11 PINS 1
  LABEL "We could not access your location — please drop a pin"
  SET ?result
CLOSE
AFTER EMIT ?result("points")(0)("lat") & ", " & ?result("points")(0)("lon")

HTML Node Form — Embedding at Your Own Size

Instead of halting, AS NODE NAME "field" returns a native HTML node — the same kind NEW HTML NODE produces — which you append into your own document with HTML APPEND. The map becomes part of a normal form: on submit, !POST('field') carries everything the visitor drew as a JSON string, read back with PARSE ... AS JSON.

A Sized Map Inside Your Own Page
NEW HTML SET ?page
OPEN
  HTML "body"
  OPEN
    HTML "h1" TEXT "Report a Pothole"
    AFTER HTML "form" ATTR "method" WITH "POST"
    OPEN
      HTML "div" ATTR "id" WITH "mapslot"
      AFTER HTML "button" ATTR "type" WITH "submit" TEXT "Submit Report"
    CLOSE
  CLOSE
CLOSE
AFTER MAP LIVE CENTER "-33.9249,18.4241" ZOOM 14
WIDTH "100%" HEIGHT 400
PINS 1
AS NODE NAME "location"
SET ?mapnode
AFTER HTML APPEND ?mapnode INTO ?page WHERE ID "mapslot"
AFTER EMIT ?page
Reading the Submission Back
PARSE !POST('location') AS JSON SET ?drawn
AFTER EMIT ?drawn("points")(0)("lat") & ", " & ?drawn("points")(0)("lon")
AFTER MAP RENDER ?drawn("points") ZOOM 14 SET ?url
AFTER EMIT ?url

MAP LIVE Modifier Reference

ModifierDescription
?points (residue)Pre-placed points drawn before the visitor starts — same as MAP RENDER
LINES ?lines / POLYGONS ?polygons / CIRCLES ?circlesPre-placed shapes, identical to MAP RENDER
CENTER "lat,lon"Explicit center coordinate
CENTER VISITORCenter on the visitor's browser location — requires a granted USER DEVICE GEOLOCATION
ZOOM nInitial zoom level
STYLE "preset"Tile style — the same presets as MAP RENDER
AUTOFITFit the view to all pre-placed shapes
WIDTH n|"n%" / HEIGHT n|"n%"Map dimensions. Default 100% × 480px
FULLSCREENFull-viewport map, overriding WIDTH/HEIGHT
PINS "unlimited" | PINS nAllow pin dropping, capped at n. Omit entirely to disable pins
DRAW LINESAllow the visitor to draw lines
DRAW POLYGONSAllow the visitor to draw polygons
LABEL "..."Instruction banner shown above the map, alongside the Done button
AS NODE NAME "field"Return an embeddable HTML node instead of halting; submits drawn data as JSON under field
SET ?resultHalt-and-resume: captures {points, lines, polygons} once the visitor presses Done
Site Mode only. The halt-and-resume form of MAP LIVE uses the same round-trip mechanism as GATHER, CONFIRM, and USER DEVICE — it renders, halts, and resubmits to the same script once the visitor presses Done. The plain credentialed API has no browser round-trip to resubmit against, the same boundary documented on Session & Cookies. The AS NODE form has no such restriction — it only builds an HTML node, exactly like any other HTML verb.