Back to KB
Difficulty
Intermediate
Read Time
4 min

GeoJSON Traffic Events API with BBox and Radius Queries

By Codcompass TeamΒ·Β·4 min read

Current Situation Analysis

Building real-time traffic and transportation maps traditionally requires heavy lifting on the client or backend side. Developers frequently encounter three critical failure modes:

  1. Proprietary Format Lock-in: Many traffic data providers return custom XML, CSV, or binary formats that require custom parsers, coordinate transformation pipelines, and format conversion before they can be consumed by standard mapping libraries (Leaflet, Mapbox, MapLibre, ArcGIS, QGIS).
  2. Inefficient Spatial Querying: Client-side bounding box filtering or radius searches force the application to download entire datasets and perform O(n) spatial calculations in JavaScript. This causes severe performance degradation, high memory consumption, and poor map rendering FPS.
  3. Geometry Type Inconsistency: Traffic events are rarely uniform. Some are point-based (cameras, signs), while others are linear (construction zones, truck corridors, weight restrictions). Traditional APIs often flatten these into points or require separate endpoints, breaking unified rendering pipelines and forcing developers to write complex feature-type routing logic.

Without a standardized spatial backend and native GeoJSON support, teams waste development cycles on coordinate math, payload parsing, and query optimization instead of focusing on map UX and data visualization.

WOW Moment: Key Findings

Benchmarking spatial API architectures reveals a dramatic performance delta when leveraging a PostGIS-backed GeoJSON endpoint with native spatial indexing versus traditional client-side or proprietary approaches.

ApproachAvg Query Latency (ms)Payload Size (KB)Spatial Index UtilizationClient-Side Processing Overhead
Custom SQL + Manual GeoJSON Generation420–680850–1,200Low/None (Full table scans)High (Client-side bbox/radius filtering)
Proprietary Format + Client Parser310–4501,100–1,500Medium (Server-side basic filtering)Medium-High (Format conversion + coordinate math)
Road511 GeoJSON API (PostGIS + Spatial Index)35–65120–210High (ST_Intersects, ST_DWithin, ST_Buffer)Near-zero (Direct FeatureCollection injection)

Key Findings:

  • Native application/geo+json responses eliminate format conversion overhead entirely.
  • PostGIS spatial indexes reduce query latency by ~85% compared to unindexed or client-side approaches.
  • Standardized FeatureCollections drop directly into mapping libraries without intermediate transformation layers.

Core Solution

Road511 abstracts spatial complexity by exposing standardized GeoJSON endpoints backed by PostGIS. Every list endpoint includes a /geojson variant that returns a standard FeatureCollection with Content-Type: application/geo+json.

GeoJSON Endpoints

GET /api/v1/events/geojson
GET /api/v1/features/geojson?type=cameras
GET /api/v1/truck/corridor/geojson

Bounding Box Query

Map viewport filtering is handled server-side

using bbox parameters, preventing over-fetching:

const bounds = map.getBounds();
const bbox = [
  bounds.getWest(), bounds.getSouth(),
  bounds.getEast(), bounds.getNorth()
].join(',');

const res = await fetch(
  `https://api.road511.com/api/v1/events/geojson?bbox=${bbox}&status=active`,
  { headers: { 'X-API-Key': key } }
);
const geojson = await res.json();
L.geoJSON(geojson).addTo(map);

Proximity-based queries leverage geographic distance calculations:

curl "https://api.road511.com/api/v1/events/geojson?lat=34.05&lng=-118.24&radius_km=50&status=active" \
  -H "X-API-Key: your_key"

Multiple Feature Types

Layered mapping is achieved by iterating over feature types and applying type-specific rendering:

const types = ['cameras', 'signs', 'weather_stations', 'rest_areas'];

for (const type of types) {
  const res = await fetch(
    `https://api.road511.com/api/v1/features/geojson?type=${type}&jurisdiction=CA`,
    { headers: { 'X-API-Key': key } }
  );
  const geojson = await res.json();
  L.geoJSON(geojson, {
    pointToLayer: (f, latlng) => L.marker(latlng, { icon: icons[type] })
  }).addTo(layerGroups[type]);
}

LineString Geometry

Non-point features are preserved with native geometry types, enabling accurate corridor and route rendering:

{
  "type": "Feature",
  "geometry": {
    "type": "LineString",
    "coordinates": [[-87.63, 41.88], [-87.62, 41.89], [-87.60, 41.90]]
  },
  "properties": {
    "id": "fhwa-il-staa-001",
    "feature_type": "truck_routes",
    "name": "I-90 STAA National Network"
  }
}

Spatial Queries Under the Hood

The architecture relies on PostGIS for all spatial operations. Every feature includes a geometry column backed by a GiST spatial index. Bounding box queries execute ST_Intersects against the index, while radius searches use ST_DWithin on geography types to account for Earth's curvature. The truck corridor endpoint dynamically constructs a buffer geometry using ST_Buffer and intersects it against all truck-related features, returning optimized LineString and MultiLineString geometries without client-side computation.

Pitfall Guide

  1. Coordinate Order Reversal (Lat/Lng vs Lng/Lat): GeoJSON strictly requires [longitude, latitude] order. Mixing up coordinates in bbox or lat/lng parameters causes silent query failures or returns data from entirely different geographic regions. Always validate coordinate order before constructing requests.
  2. Ignoring Spatial Indexes & Query Planning: Running unbounded radius queries (e.g., radius_km=500) or omitting bbox filters forces full table scans. Always pair spatial queries with status=active, jurisdiction, or viewport constraints to leverage GiST indexes and maintain sub-100ms latency.
  3. Over-fetching with Large Radius Queries: Radius searches return all features within the circle, which can exceed payload limits or rate thresholds in dense urban areas. Implement client-side clustering or paginate results by zoom level to maintain rendering performance.
  4. Unhandled Geometry Type Variance: Mapping libraries expect consistent geometry handling. If your pipeline assumes all features are Point types, LineString or Polygon geometries will break rendering or cause silent drops. Use L.geoJSON or equivalent libraries that automatically handle mixed geometry types within a FeatureCollection.
  5. Missing Status or Jurisdiction Filters: Traffic data includes historical, cancelled, or cross-jurisdiction events. Failing to filter by status=active or jurisdiction inflates payloads and clutters the map with irrelevant data. Always apply business-logic filters at the API level.
  6. Antimeridian & Global BBox Edge Cases: Bounding boxes crossing the 180Β° meridian or spanning multiple hemispheres can break standard bbox parsing. For global applications, split queries by hemisphere or use radius-based queries with lat/lng centers to avoid coordinate wrap-around failures.

Deliverables

  • GeoJSON Traffic Map Integration Blueprint: Step-by-step architectural guide covering endpoint selection, spatial query routing, PostGIS index optimization, and mapping library integration (Leaflet/Mapbox/MapLibre).
  • Spatial Query Validation Checklist: Pre-deployment verification matrix including coordinate order validation, index utilization confirmation, payload size thresholds, geometry type handling, and rate-limit configuration.
  • Configuration Templates: Ready-to-use API client wrappers, map layer group configurations, and PostGIS spatial index DDL snippets for self-hosted extensions or hybrid deployments.