Ride-share territories
Draw service zones from Geocode AI boundaries, tell which zone each driver is in, and move top-view car renders around a live map.
The fleet, live
Six cars drive across Washington, DC, split into four service zones made of its eight wards. The zone lines are Geocode AI boundaries, also made by Meter; the cars are Car Image renders in the top view, straight down from above, turned to each car's heading.
Ward boundaries by Geocode AI, also made by Meter. Cars are Car Image top views; each ring takes the color of the zone the car is in.
A zone is a list of boundaries
Store each service area as the ids of the city's own boundaries (wards, districts, postcodes) rather than a hand-drawn polygon. Searching and low-detail polygons are free and need no key; merging several into one shape for the map takes a Geocode AI key. Fetch the polygons once and cache them: ward lines change rarely.
# free, no key: find the boundaries a zone is made of
curl "https://geocodeai.dev/v1/boundaries/search?q=Ward%203%20Washington&kind=ward"
# → results[0].id: "r16408680"
# free at low detail: the polygon itself, as a GeoJSON Feature
curl "https://geocodeai.dev/v1/boundaries/r16408680?detail=low"
# one polygon for a zone of several wards (Wards 1, 2 and 3)
curl -X POST "https://geocodeai.dev/v1/boundaries/union" \
-H "Authorization: Bearer $GEOCODE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"include":["r16408707","r16408810","r16408680"],"detail":"low"}'Which zone is each driver in
On your server, test the latest fix of every driver against each zone in one batch. The answer lists one true or false per point, in order.
const GEOCODE = "https://geocodeai.dev";
/** Each zone as the Geocode AI boundaries it is made of. */
const zones = {
northwest: ["r16408707", "r16408810", "r16408680"],
east: ["r16394083", "r16389580"],
};
/** Drivers' last GPS fixes as [lon, lat]; up to 10,000 points per call. */
export async function zoneOf(drivers: [number, number][]) {
const zone: (string | null)[] = drivers.map(() => null);
for (const [name, ids] of Object.entries(zones)) {
for (const id of ids) {
const res = await fetch(`${GEOCODE}/v1/boundaries/${id}/contains`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GEOCODE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ points: drivers }),
});
// { id, total, inside_count, inside: [true, false, …] }
const { inside } = (await res.json()) as { inside: boolean[] };
inside.forEach((hit, i) => { if (hit) zone[i] ??= name; });
}
}
return zone; // null: outside every zone
}Top-view cars as markers
Mint one signed URL per vehicle in the fleet on your server, each with view: "top", and store it with the vehicle. The page gets a plain image URL; the key never reaches the browser. Each URL counts one vehicle image when it is created and loads free until it expires; renew: true keeps it serving. Look each vehicle up once for its id (by VIN, see Dealer & VIN tools).
/** One signed URL per fleet vehicle: straight down from above, trimmed, transparent. */
export async function fleetMarkers(vehicleIds: string[]) {
const res = await fetch("https://carimage.dev/api/v1/image-urls", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CAR_IMAGE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
images: vehicleIds.map((vehicle) => ({
vehicle, view: "top", width: 96, height: 192, fit: "inside", trim: true, format: "webp",
})),
ttl_seconds: 604800,
renew: true,
}),
});
const { data } = (await res.json()) as { data: { url: string }[] };
return data.map((image) => image.url);
}
// fleetMarkers(["veh_errc87t1jgata", "veh_94m80sn8fvcge"])Any map library takes an image as a marker. With MapLibre GL, rotate it with the map so the car points where it drives:
import maplibregl from "maplibre-gl";
// The render's nose points up, so a marker turns by the driver's heading.
export function carMarker(map: maplibregl.Map, url: string, fix: { lon: number; lat: number; heading: number }) {
const img = document.createElement("img");
img.src = url; // the signed URL from your server: no key in the browser
img.style.height = "40px";
return new maplibregl.Marker({ element: img, rotation: fix.heading, rotationAlignment: "map" })
.setLngLat([fix.lon, fix.lat])
.addTo(map);
}
// On each GPS fix: marker.setLngLat([lon, lat]).setRotation(heading)Lifetimes, renewal and billing are on Signed URLs; every Geocode AI operation and its cost is in the Geocode AI docs.