<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Roman Kotenko</title>
    <description>The latest articles on DEV Community by Roman Kotenko (@road511).</description>
    <link>https://dev.to/road511</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3869750%2F0aff7c69-b543-4a64-a0a2-32b0207973a3.png</url>
      <title>DEV Community: Roman Kotenko</title>
      <link>https://dev.to/road511</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/road511"/>
    <language>en</language>
    <item>
      <title>Truck Parking Availability on the Route, Before the Clock Runs Out</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Fri, 24 Jul 2026 07:31:59 +0000</pubDate>
      <link>https://dev.to/road511/truck-parking-availability-on-the-route-before-the-clock-runs-out-17n4</link>
      <guid>https://dev.to/road511/truck-parking-availability-on-the-route-before-the-clock-runs-out-17n4</guid>
      <description>&lt;p&gt;It's 2am and a driver's 11-hour clock has maybe 30 minutes left on it. The rest area at the next exit is full — trucks are already lined up on the on-ramp shoulder. The next public lot is 40 miles ahead, which the clock won't cover, and nobody knows whether &lt;em&gt;it&lt;/em&gt; has room either. So the driver does what tens of thousands do every night: parks somewhere they shouldn't, or keeps rolling past the legal limit. Truck parking consistently ranks among the trucking industry's top concerns year after year, and the reason it stays unsolved at the cab level is simple — &lt;strong&gt;a legal break is worthless if there's nowhere to stop, and the driver finds out too late to do anything about it.&lt;/strong&gt; That last part is the part software can fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding a Space Is a Planning Problem, Not a Luck Problem
&lt;/h2&gt;

&lt;p&gt;The shortage is real and structural — there are far more trucks needing overnight rest than there are legal spaces for them. But a huge share of the daily pain isn't the raw shortage; it's the &lt;em&gt;information gap&lt;/em&gt;. The driver can't see which lots are full until pulling in, and the dispatcher planning the load has no parking picture at all. A stop that looks fine on a map at 9am dispatch is a gravel shoulder by the time the truck arrives at midnight.&lt;/p&gt;

&lt;p&gt;Turning that into a planning decision needs two things the route already implies: &lt;strong&gt;where the parking is&lt;/strong&gt; along the corridor, and &lt;strong&gt;how much of it is actually open&lt;/strong&gt; when the truck will be there. The first has existed for years. The second is finally becoming available — in pieces — and that's the part that changes the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Exists — in Two Forms
&lt;/h2&gt;

&lt;p&gt;Truck-parking data comes from two complementary public sources, and they answer different questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The national inventory — where parking exists.&lt;/strong&gt; A federal facilities dataset catalogs public rest areas and known truck-parking sites across the country, with locations, capacity, and basic attributes. This is your map of &lt;em&gt;where&lt;/em&gt; a truck can legally stop — comprehensive in coverage, but static: it tells you a site has 60 spaces, not how many are free right now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live availability — how many spaces are open.&lt;/strong&gt; A growing number of state DOTs run &lt;strong&gt;Truck Parking Information Management Systems (TPIMS)&lt;/strong&gt; — the in-pavement sensors and roadside signs you've seen reading "REST AREA — 12 SPACES." Those same counts are published as data. Coverage is still a handful of states rather than the whole map, but where a site is instrumented, you get a live, refreshing count of open spaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The catch is the one that shows up everywhere in this domain: these are separate feeds in separate formats from separate agencies, each on its own schema and refresh cadence — the same per-source integration tax we wrote about in &lt;a href="https://road511.com/blog/north-american-road-data-challenges.html" rel="noopener noreferrer"&gt;the five road-data problems every NA fleet team knows&lt;/a&gt;. Stitched together and normalized, though, they become one answer: the full map of where to stop, with live counts layered on wherever a state publishes them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Parking Stop Needs to Tell You
&lt;/h2&gt;

&lt;p&gt;A parking feature is only useful to a planner when it answers the operational questions, not just "there's a rest area here":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Where, and which side&lt;/strong&gt; — the exact location and the direction of travel it serves, so a northbound truck isn't routed to a southbound-only lot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What kind, and how big&lt;/strong&gt; — public rest area versus known truck stop, and the total truck-space capacity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How many are open right now&lt;/strong&gt; — the live available-space count where the site is instrumented, with the timestamp of the last update so stale data is obvious rather than misleading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's there&lt;/strong&gt; — amenities that decide whether a stop is viable for a 10-hour reset versus a quick 30-minute break.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Putting It on the Corridor
&lt;/h2&gt;

&lt;p&gt;The operational need isn't "a database of rest areas." It's "show me the truck parking &lt;em&gt;on this route&lt;/em&gt;, with whatever live counts exist, before the driver commits to a stop." Once the inventory and the TPIMS feeds are normalized into one schema, &lt;code&gt;/truck/corridor&lt;/code&gt; buffers the straight line between two points and returns every truck-related feature that intersects it — parking among them — instead of a fan-out across federal and state sources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/truck/corridor?
  from_lat=41.88&amp;amp;from_lng=-87.63&amp;amp;
  to_lat=39.10&amp;amp;to_lng=-94.58&amp;amp;
  buffer_km=5

{
  "features": [
    {
      "id": "tpims-IL00055IS002210",
      "feature_type": "truck_parking",
      "name": "I-55 NB Rest Area (Lincoln)",
      "jurisdiction": "IL",
      "road_name": "I-55 NB",
      "latitude": 40.71, "longitude": -89.62,
      "properties": {
        "capacity": 62,
        "available_spaces": 9,
        "occupancy_pct": 85,
        "data_updated": "2026-06-26T06:18:00Z",
        "amenities": ["Vending Machines"],
        "open": true,
        "_distance_km": "1.20"
      }
    },
    {
      "id": "BTS-tp-4471",
      "feature_type": "truck_parking",
      "name": "US-67 Truck Parking",
      "jurisdiction": "IL",
      "road_name": "US-67",
      "latitude": 39.80, "longitude": -90.20,
      "properties": {
        "capacity": 40,
        "county": "Morgan",
        "milepost": "12.4",
        "truck_parking": true,
        "_distance_km": "3.80"
      }
    }
  ],
  "total": 2
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One call, the Chicago–Kansas City corridor, every known parking site on it, each stamped with &lt;code&gt;_distance_km&lt;/code&gt; off the line. Where a state runs TPIMS, the site carries a live &lt;code&gt;available_spaces&lt;/code&gt; / &lt;code&gt;occupancy_pct&lt;/code&gt; and a &lt;code&gt;data_updated&lt;/code&gt; timestamp; where it doesn't, the site still shows up from the national BTS inventory with &lt;code&gt;capacity&lt;/code&gt; only and no &lt;code&gt;available_spaces&lt;/code&gt; key at all — an honest "we know it's here, we don't have a live count" instead of a false promise. That distinction is the whole point: a planner can prefer instrumented sites near the deadline and fall back to known lots everywhere else. (&lt;code&gt;/truck/corridor&lt;/code&gt; is a paid-plan endpoint; the same features are on &lt;code&gt;/features?type=truck_parking&amp;amp;bbox=…&lt;/code&gt; for an area query.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Parking Meets the HOS Clock
&lt;/h2&gt;

&lt;p&gt;Parking and hours-of-service are the same problem looked at from two ends. The clock decides &lt;em&gt;when&lt;/em&gt; the truck has to stop; the parking data decides &lt;em&gt;whether it can&lt;/em&gt;. On its own, a list of nearby lots is useful; tied to the driver's remaining hours, it becomes a go/no-go answer.&lt;/p&gt;

&lt;p&gt;That's exactly how it connects to &lt;a href="https://road511.com/blog/hos-break-planning-api.html" rel="noopener noreferrer"&gt;HOS break planning on the route&lt;/a&gt;: send the driver's clock with the route, and each required break and drive-limit stop comes back with the truck parking reachable &lt;em&gt;before&lt;/em&gt; the deadline — and a &lt;code&gt;feasible&lt;/code&gt; flag when nothing legal is in range. Layer live availability on top and "reachable" sharpens into "reachable and likely to have room," so dispatch can move the stop earlier while there's still slack in the clock, instead of discovering the lot is full when there's no clock left to spend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Build With It
&lt;/h2&gt;

&lt;p&gt;With truck parking in the same schema as everything else on the route, the integrations get short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dispatch that plans the overnight, not just the delivery&lt;/strong&gt; — place the rest stop at load planning, with capacity and live counts in view, not at 2am.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Driver alerts as the clock winds down&lt;/strong&gt; — surface the next few viable lots ahead, instrumented ones flagged, while there are still hours to reach them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest ETAs&lt;/strong&gt; — a reset has to happen somewhere; planning the actual stop makes the arrival estimate real instead of optimistic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Corridor risk and compliance views&lt;/strong&gt; — parking alongside bridge clearances, work zones, and incidents in one layer (see &lt;a href="https://road511.com/blog/truck-routing-hazard-warnings.html" rel="noopener noreferrer"&gt;truck routing with corridor hazard warnings&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Provenance, and the Honest Limits
&lt;/h2&gt;

&lt;p&gt;Parking data comes from official sources — the federal facilities inventory and state DOT TPIMS feeds — which is what makes it authoritative rather than crowd-guessed. We don't overstate it. Live counts exist only where a state has instrumented a site, so most of the map is still inventory-only; capacity and amenity detail vary by source; and a live count is only as fresh as the feed behind it, which is why every count carries its update timestamp. This is &lt;em&gt;information&lt;/em&gt;, not a reservation — the public feeds tell you what's open, not hold a space for you; booking lives in separate private systems. The job we take on is keeping the federal inventory and each state's TPIMS feed parsed, current, and in one shape as they grow and drift — so it's not yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://road511.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — the &lt;code&gt;features&lt;/code&gt; and corridor surface, including &lt;code&gt;truck_parking&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — see truck-parking sites and live counts where states publish them&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no credit card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The next legal stop, with room — before the clock runs out
&lt;/h3&gt;

&lt;p&gt;The national truck-parking inventory plus live space counts where states publish them, normalized into one schema and queryable along any corridor — and tied to the HOS clock so the break is planned, not gambled. Free 14-day trial. No credit card. &lt;strong&gt;&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Get a free API key&lt;/a&gt;&lt;/strong&gt; or &lt;a href="https://road511.com/docs.html" rel="noopener noreferrer"&gt;read the docs&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://road511.com/blog/truck-parking-availability-api.html" rel="noopener noreferrer"&gt;road511.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>trucking</category>
      <category>api</category>
      <category>logistics</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Five Road-Data Problems Every Cross-Border European Fleet Hits</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Sun, 12 Jul 2026 07:18:21 +0000</pubDate>
      <link>https://dev.to/road511/five-road-data-problems-every-cross-border-european-fleet-hits-2i3</link>
      <guid>https://dev.to/road511/five-road-data-problems-every-cross-border-european-fleet-hits-2i3</guid>
      <description>&lt;p&gt;If you build routing, dispatch, or telematics software for European trucking, the routing algorithm is rarely what slows the roadmap down. The road data is. A truck plan from Rotterdam to Bucharest crosses a dozen jurisdictions, and each one publishes what's happening on its roads — incidents, tolls, restrictions, charging — its own way, on its own schedule, under its own rules. Below are the five problems that come up in nearly every conversation we have with European fleet and logistics-software teams. None are exotic. All of them quietly burn engineering time.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Twenty-Seven Countries, Twenty-Seven Access Points
&lt;/h2&gt;

&lt;p&gt;The EU ITS Directive did get every Member State to stand up a National Access Point (NAP) publishing road data, mostly as DATEX II. That sounds like a solved problem until you actually connect to a second country. The standard leaves room, and the NAPs use all of it — different DATEX profiles and versions, different authentication, different update cadences, different ideas of what a given field means. And plenty of operationally useful data never travels as DATEX II at all: it sits on municipal open-data portals or in national feeds that predate the plumbing.&lt;/p&gt;

&lt;p&gt;So a team that wants genuine continental coverage ends up writing and &lt;em&gt;maintaining&lt;/em&gt; a separate integration per country, then babysitting all of them as feeds quietly change shape. This is the problem that eats the most time, because it never finishes. We wrote up &lt;a href="https://napspan.com/blog/datex-ii-normalization-european-naps.html" rel="noopener noreferrer"&gt;how we normalize DATEX II across the European NAPs&lt;/a&gt; and &lt;a href="https://napspan.com/blog/open-data-beyond-datex-ii.html" rel="noopener noreferrer"&gt;how the non-DATEX sources fold into the same schema&lt;/a&gt; — the short version is that the per-country mess becomes one consistent &lt;code&gt;events&lt;/code&gt; and &lt;code&gt;features&lt;/code&gt; contract, and keeping the adapters alive becomes our problem instead of yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Toll Bill Changes at Every Border
&lt;/h2&gt;

&lt;p&gt;There is no single European toll. There are vignettes, distance-based schemes, per-axle HGV charges, urban congestion fees, and tunnel and bridge tolls — each country with its own model, its own vehicle classes, and its own emission-class surcharges. A route that looks cheapest on distance can be the most expensive once the tolls land, and the difference between a 5-axle Euro VI rig and a smaller van is real money on every leg.&lt;/p&gt;

&lt;p&gt;For a planner, an estimate that ignores vehicle class is an estimate that's wrong at invoice time. For a software team, pricing a cross-border route by hand means modelling a dozen national toll systems and keeping them current as rates change. We pulled this together so a route comes back with a vehicle-class-aware toll breakdown per country — see &lt;a href="https://napspan.com/blog/toll-cost-routing-api.html" rel="noopener noreferrer"&gt;vehicle-class-aware toll costs across Europe&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Where the Clock Runs Out — and Whether There's Parking
&lt;/h2&gt;

&lt;p&gt;EU Regulation 561/2006 driving-time rules and the chronic shortage of secure truck parking are really one problem wearing two hats. Knowing a driver will hit the daily driving limit at 18:40 is only half of it. The half that strands a truck — or forces an unsafe roadside stop — is whether there's a legal, reachable place to take the break when the clock runs out, and that answer needs both the driving-time projection &lt;em&gt;and&lt;/em&gt; live parking data on the same corridor.&lt;/p&gt;

&lt;p&gt;Most teams solve the first half and discover the second half at the lay-by. We tied them together: send the driver's clock with the route and get back every required break, each paired with the parking reachable before the deadline and a &lt;code&gt;feasible&lt;/code&gt; flag when none is. That's the subject of &lt;a href="https://napspan.com/blog/hos-break-planning-api.html" rel="noopener noreferrer"&gt;break planning under EU 561/2006&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Ban You Didn't Know About Until the Border
&lt;/h2&gt;

&lt;p&gt;Cross-border trucking runs on a patchwork of national restrictions that no consumer map knows about: weekend and night driving bans, sectoral and sector-specific bans on Alpine corridors, low-emission zones in city centres, country-by-country weight and dimension limits, and seasonal restrictions. A plan that's legal in one country can be illegal across the line at the same hour — and the penalty for finding out the hard way is a fine, a forced wait, or a turned-back load.&lt;/p&gt;

&lt;p&gt;The operational need isn't "a list of every rule in Europe." It's "tell me about the restrictions and variable-message signs &lt;em&gt;on this corridor&lt;/em&gt;, before the driver gets there" — restrictions and signage from the official national feeds, normalized into the same queryable &lt;code&gt;features&lt;/code&gt; the rest of the API speaks, filterable by country and bounding box. One schema for a German night ban and an Austrian sectoral ban means you check one place instead of twenty-seven.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Charging an Electrifying Fleet, Across the Gaps
&lt;/h2&gt;

&lt;p&gt;As fleets electrify, "where can this vehicle actually charge on the corridor" becomes a routing input, not a nice-to-have. The EU's AFIR regulation obliges charge-point operators to publish charging data through the NAPs, and where a Member State carries a native EV feed we serve it. But AFIR rollout is uneven, and several countries don't expose a mature EV feed yet — so most of our EV coverage comes from an openly-licensed registry rather than from live per-connector telemetry.&lt;/p&gt;

&lt;p&gt;That registry gives you coordinates, connector standards, and power ratings, and where a source publishes its own last-checked time we pass it through as &lt;code&gt;properties.source_updated_at&lt;/code&gt; so you can judge how fresh an entry is — typically days to months, because a community registry is not live telemetry. See &lt;a href="https://napspan.com/blog/open-data-beyond-datex-ii.html" rel="noopener noreferrer"&gt;folding open data into one European API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Common Thread
&lt;/h2&gt;

&lt;p&gt;Every one of these is the same shape of problem: the data exists, but it's fragmented across national authorities, published in incompatible formats, governed by different licenses, and only useful once it's joined to a route and kept fresh. Each one is solvable in-house — and each one, solved in-house, becomes a permanent maintenance commitment that competes with the product you actually sell.&lt;/p&gt;

&lt;p&gt;That's the entire premise of NAPSPAN: do that aggregation once, for everyone, and hand you one normalized API — events, features, tolls, restrictions, charging, routing with corridor hazards — instead of twenty-seven integrations to keep alive. The data is served from Europe, every source is license-classified before it ships, and you spend your engineering time on the part that makes your product yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://napspan.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — &lt;code&gt;events&lt;/code&gt;, &lt;code&gt;features&lt;/code&gt;, routing, and the &lt;code&gt;?jurisdiction=&lt;/code&gt; / &lt;code&gt;?type=&lt;/code&gt; filters in one reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.napspan.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — see the normalized data across the European NAPs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.napspan.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;One API instead of twenty-seven integrations.&lt;/strong&gt; Continental coverage — incidents, tolls, truck restrictions, EV charging, and routing — normalized into one GeoJSON schema and served from Europe. Free 14-day trial. No card. &lt;a href="https://portal.napspan.com/signup" rel="noopener noreferrer"&gt;Get a free API key&lt;/a&gt; or &lt;a href="https://napspan.com/docs.html" rel="noopener noreferrer"&gt;read the docs&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://napspan.com/blog/european-road-data-challenges.html" rel="noopener noreferrer"&gt;napspan.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>logistics</category>
      <category>eu</category>
      <category>api</category>
      <category>trucking</category>
    </item>
    <item>
      <title>Five Road-Data Problems Every North American Fleet Team Knows Too Well</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Wed, 08 Jul 2026 13:30:00 +0000</pubDate>
      <link>https://dev.to/road511/five-road-data-problems-every-north-american-fleet-team-knows-too-well-nle</link>
      <guid>https://dev.to/road511/five-road-data-problems-every-north-american-fleet-team-knows-too-well-nle</guid>
      <description>&lt;p&gt;If you build routing, dispatch, or telematics software for North American trucking, your roadmap has a recurring tax line on it — road data. Not the routing algorithm, not the UI: the unglamorous job of knowing what's actually happening on the pavement, in fifty states and a dozen provinces that each run their own system their own way. Below are the five problems that come up in nearly every conversation we have with fleet and logistics-software teams. None of them are exotic. All of them quietly burn engineering time.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Fifty States, Fifty Dialects
&lt;/h2&gt;

&lt;p&gt;There is no single "US traffic API." There are dozens of state and provincial 511 systems, and they agree on almost nothing — different formats (some JSON, some XML, some an ArcGIS layer, some a scraped map endpoint), different field names for the same concept, different authentication, different refresh rates, different ideas of what "severity" even means. A team that wants national coverage ends up writing and &lt;em&gt;maintaining&lt;/em&gt; a separate integration per jurisdiction, then babysitting all of them as feeds silently change shape.&lt;/p&gt;

&lt;p&gt;This is the problem that eats the most time, because it never finishes. A feed that worked in March drops a field in September; a state migrates its portal and the old endpoint 404s. We wrote up &lt;a href="https://road511.com/blog/normalizing-511-traffic-apis.html" rel="noopener noreferrer"&gt;how we normalize 30+ of these systems into one schema&lt;/a&gt; — the short version is that the per-jurisdiction mess becomes one consistent &lt;code&gt;events&lt;/code&gt; and &lt;code&gt;features&lt;/code&gt; contract, and keeping the adapters alive becomes our problem instead of yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Bridge Your Truck Doesn't Fit Under
&lt;/h2&gt;

&lt;p&gt;A consumer nav app routes a 13′6″ trailer under a 12′8″ bridge without a second thought, because it has no idea a truck is driving. Low-clearance strikes are expensive, dangerous, and almost entirely preventable with the right data on the route — but that data is scattered across state bridge inventories and the federal National Bridge Inventory, in formats never meant for real-time routing.&lt;/p&gt;

&lt;p&gt;The operational need isn't "a database of bridges." It's "tell me about the low ones &lt;em&gt;on this corridor&lt;/em&gt;, before the driver gets there." That's a join between clearance data and the route geometry, and it's exactly the kind of thing teams underestimate until the first claim comes in. More on that in &lt;a href="https://road511.com/blog/bridge-clearance-api-fleet-routing.html" rel="noopener noreferrer"&gt;bridge clearances for fleet routing&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Limits That Appear Every Spring
&lt;/h2&gt;

&lt;p&gt;Seasonal weight restrictions — spring thaw load limits, frost laws — are one of the most under-served data problems in North American trucking. They're posted by states and counties on their own schedules, in their own formats, sometimes as a PDF, and they change as the ground does. A load that's legal one week is over the limit the next, on the same road.&lt;/p&gt;

&lt;p&gt;For a planner, the cost of missing one is a fine, a forced reroute, or a refused load. For a software team, the cost is trying to track a hundred separate posting authorities by hand. We pulled this together so the restriction shows up as structured, queryable data alongside everything else — see &lt;a href="https://road511.com/blog/spring-load-limits-api-trucking.html" rel="noopener noreferrer"&gt;spring load limits as an API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Where the Clock Runs Out — and Whether There's Parking
&lt;/h2&gt;

&lt;p&gt;Hours-of-Service compliance and the chronic truck-parking shortage are really one problem wearing two hats. Knowing a driver will hit the 11-hour driving limit at 6:40 PM is only half of it. The half that strands a truck is whether there's a legal place to stop when the clock runs out — and that answer needs both the HOS projection &lt;em&gt;and&lt;/em&gt; live parking data on the same corridor.&lt;/p&gt;

&lt;p&gt;Most teams solve the first half and discover the second half at the roadside. We tied them together: send the driver's clock with the route and get back every required break and stop, each paired with the parking reachable before the deadline and a &lt;code&gt;feasible&lt;/code&gt; flag when none is. That's the subject of &lt;a href="https://road511.com/blog/hos-break-planning-api.html" rel="noopener noreferrer"&gt;HOS break planning on the route&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The Data Is Stale the Moment You Cache It
&lt;/h2&gt;

&lt;p&gt;Incidents, closures, and work zones are only useful while they're current. A closure feed you poll once an hour is a closure feed that's wrong for up to an hour — and a routing decision made on a stale closure is a driver sitting in a backup your system told them to enter. But polling every state's feed aggressively, parsing it, deduping it, and aging out the resolved events is its own piece of infrastructure that has nothing to do with your product.&lt;/p&gt;

&lt;p&gt;The honest answer here isn't "real-time magic" — it's that the freshness and lifecycle work should already be done by the time you query. Events carry their own start and end times and move from active to archived on their own; you ask for what's current on a corridor and get the current answer, with the polling cadence handled upstream. Pair it with the camera feeds for visual confirmation (&lt;a href="https://road511.com/blog/traffic-camera-feeds-api.html" rel="noopener noreferrer"&gt;traffic camera feeds&lt;/a&gt;) and the dispatcher has eyes on the corridor without standing up a single scraper.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Common Thread
&lt;/h2&gt;

&lt;p&gt;Every one of these is the same shape of problem: the data exists, but it's fragmented across authorities, published in incompatible formats, and only useful once it's joined to a route and kept fresh. Each one is solvable in-house — and each one, solved in-house, becomes a permanent maintenance commitment that competes with the product you actually sell.&lt;/p&gt;

&lt;p&gt;That's the entire premise of Road511: do that aggregation once, for everyone, and hand you one normalized API — events, features, bridge clearances, restrictions, routing with corridor hazards — instead of fifty integrations to keep alive. You spend your engineering time on the part that makes your product yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://road511.com/docs.html" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; — the &lt;code&gt;events&lt;/code&gt;, &lt;code&gt;features&lt;/code&gt;, and truck-routing surface in one reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — see the normalized data across states and provinces&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no credit card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://road511.com/blog/north-american-road-data-challenges.html" rel="noopener noreferrer"&gt;road511.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>trucking</category>
      <category>api</category>
      <category>logistics</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Hours-of-Service Break Planning, Right on the Route</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Mon, 22 Jun 2026 10:00:44 +0000</pubDate>
      <link>https://dev.to/road511/hours-of-service-break-planning-right-on-the-route-2end</link>
      <guid>https://dev.to/road511/hours-of-service-break-planning-right-on-the-route-2end</guid>
      <description>&lt;p&gt;A consumer nav app tells the driver where to turn. It will not tell the dispatcher where the 11-hour driving clock runs out — and, more importantly, whether there is legal parking when it does. That second question is the one that strands a truck at 11&amp;nbsp;PM on the shoulder of an off-ramp with every nearby lot already full.&lt;/p&gt;

&lt;p&gt;Road511’s routing endpoint now answers it. Send the driver’s Hours-of-Service clock along with the route, and the response carries an &lt;code&gt;hos[]&lt;/code&gt; array: every point on the corridor where the driver must take a break or stop driving under the selected regime, the projected time they reach it, the legal deadline, and — the part that matters operationally — the truck parking and rest areas actually reachable before that deadline.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;It rides the same call as everything else routing does: &lt;code&gt;POST /api/v1/routing/route&lt;/code&gt;. You already send an origin, a destination, and a &lt;code&gt;truck&lt;/code&gt; profile. To get the HOS projection, add an &lt;code&gt;hos&lt;/code&gt; block &lt;em&gt;inside&lt;/em&gt; the truck object describing the driver’s clock at departure.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST "https://api.road511.com/api/v1/routing/route" \
  -H "X-API-Key: your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "origin":      { "lat": 41.8781, "lng": -87.6298 },
    "destination": { "lat": 39.7392, "lng": -104.9903 },
    "departure_time": "2026-06-08T06:00:00Z",
    "truck": {
      "profile":  "tractor",
      "weight_t": 36.0,
      "height_m": 4.2,
      "axles":    5,
      "hos": {
        "ruleset":           "us",
        "drive_remaining_s": 39600,
        "duty_remaining_s":  50400,
        "since_break_s":     0
      }
    },
    "enrichment": {
      "include_features": ["truck_parking", "rest_areas"]
    }
  }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That’s a Chicago→Denver run for a fresh driver on US rules: 11 hours of driving left (&lt;code&gt;39600&lt;/code&gt; s), a 14-hour duty window (&lt;code&gt;50400&lt;/code&gt; s), and zero driving time since the last break. Every counter is “seconds remaining” against the named ruleset’s limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The HOS Clock
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;hos&lt;/code&gt; object is the driver’s state, not a fixed policy. Store only the &lt;code&gt;ruleset&lt;/code&gt; on a reusable truck profile — the per-trip counters are supplied inline on each request and merge on top.&lt;/p&gt;

&lt;p&gt;Field&lt;/p&gt;

&lt;p&gt;Meaning&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ruleset&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The regime: &lt;code&gt;us&lt;/code&gt;, &lt;code&gt;canada_south&lt;/code&gt;, or &lt;code&gt;eu&lt;/code&gt;. Empty defaults from the deployment region.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drive_remaining_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Driving-limit budget left.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;duty_remaining_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On-duty window budget left.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;since_break_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Driving time accrued since the last qualifying break.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;elapsed_remaining_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Canada’s elapsed (wall-clock) window budget.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cycle_remaining_s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Weekly / cycle budget (60/70-hour US, 70/120-hour Canada, 56h/90h EU).&lt;/p&gt;

&lt;p&gt;Any counter you leave out is read as “a fresh driver” — the ruleset’s full limit. The counters are pointers under the hood, so an explicit &lt;code&gt;0&lt;/code&gt; (“out of hours right now”) is distinct from “not supplied.”&lt;/p&gt;

&lt;h2&gt;
  
  
  The Response
&lt;/h2&gt;

&lt;p&gt;The HOS projection is attached to the primary route as an &lt;code&gt;hos[]&lt;/code&gt; array, alongside the &lt;code&gt;warnings[]&lt;/code&gt; and &lt;code&gt;features[]&lt;/code&gt; you may already be using.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "routes": [
    {
      "summary": { "distance_m": 1480200, "duration_s": 54600 },
      "geometry": { "type": "LineString", "coordinates": [ /* ... */ ] },
      "hos": [
        {
          "reason": "break_required",
          "distance_along_route_m": 642000,
          "projected_time":  "2026-06-08T14:00:00Z",
          "legal_deadline":  "2026-06-08T14:00:00Z",
          "feasible": true,
          "suggested_stops": [
            { "type": "rest_area", "name": "I-80 Walcott Rest Area",
              "distance_along_route_m": 631500, "capacity": 22,
              "geometry": { "type": "Point", "coordinates": [-90.7830, 41.5900] } },
            { "type": "truck_parking", "name": "I-80 Tipton Truck Parking",
              "distance_along_route_m": 612000,
              "capacity": 140, "available_spots": 31,
              "as_of": "2026-06-08T13:20:00Z",
              "geometry": { "type": "Point", "coordinates": [-91.1300, 41.7700] } }
          ]
        },
        {
          "reason": "drive_limit",
          "distance_along_route_m": 905800,
          "projected_time":  "2026-06-08T17:30:00Z",
          "legal_deadline":  "2026-06-08T17:30:00Z",
          "feasible": false,
          "suggested_stops": []
        }
      ]
    }
  ],
  "route_id": "rt_5d91c7e2"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Each stop carries a &lt;code&gt;reason&lt;/code&gt; — the limit that trips there — the &lt;code&gt;distance_along_route_m&lt;/code&gt; where it lands, the &lt;code&gt;projected_time&lt;/code&gt; the driver reaches it, and the &lt;code&gt;legal_deadline&lt;/code&gt; the rule allows. The four reasons:&lt;/p&gt;

&lt;p&gt;Reason&lt;/p&gt;

&lt;p&gt;What tripped&lt;/p&gt;

&lt;p&gt;&lt;code&gt;break_required&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The driver hit the drive-time-since-break limit and owes a rest break.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drive_limit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The daily driving limit is exhausted — the driver must stop for the day.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;duty_window&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The on-duty window closed (or, in Canada, the elapsed window) before the driving limit did.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cycle_limit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The weekly/cycle budget ran out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part That Matters: Reachable Parking
&lt;/h2&gt;

&lt;p&gt;Projecting &lt;em&gt;where&lt;/em&gt; the clock runs out is the easy half. The operationally useful half is whether the driver can actually stop there. Each &lt;code&gt;hos&lt;/code&gt; stop attaches a &lt;code&gt;suggested_stops[]&lt;/code&gt; list — the truck parking, truck rest areas, and rest areas reachable &lt;em&gt;before&lt;/em&gt; the legal deadline, ordered closest-to-the-deadline first so the driver squeezes the most legal driving out of the day. That list is drawn from Road511’s own live 511 parking data, the same normalized feeds behind the rest of the API.&lt;/p&gt;

&lt;p&gt;And the flag that no consumer navigation app surfaces: &lt;code&gt;feasible&lt;/code&gt;. When it’s &lt;code&gt;true&lt;/code&gt;, at least one legal stop is reachable in time. When it’s &lt;code&gt;false&lt;/code&gt; — as on the second stop above — &lt;strong&gt;no legal parking is reachable before the deadline&lt;/strong&gt;. That is a real compliance risk you want to see at dispatch time, not at the roadside. (&lt;code&gt;feasible&lt;/code&gt; is omitted entirely if the parking lookup didn’t run, so a missing flag never reads as a misleading “false.”)&lt;/p&gt;

&lt;h2&gt;
  
  
  The Regimes
&lt;/h2&gt;

&lt;p&gt;Four Hours-of-Service regimes ship today, each implemented against its actual regulation:&lt;/p&gt;

&lt;p&gt;Ruleset&lt;/p&gt;

&lt;p&gt;Regulation&lt;/p&gt;

&lt;p&gt;Key limits&lt;/p&gt;

&lt;p&gt;&lt;code&gt;us&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;49 CFR 395 (FMCSA, property-carrying)&lt;/p&gt;

&lt;p&gt;11h driving, 14h duty window, 30-min break after 8h driving, 10h daily rest&lt;/p&gt;

&lt;p&gt;&lt;code&gt;canada_south&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;SOR/2005-313 (south of 60°N)&lt;/p&gt;

&lt;p&gt;13h driving, bounded by both a 14h duty window and a 16h elapsed window, 10h daily rest&lt;/p&gt;

&lt;p&gt;&lt;code&gt;canada_north&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;SOR/2005-313 (north of 60°N)&lt;/p&gt;

&lt;p&gt;15h driving, 18h duty window, 8h daily rest&lt;/p&gt;

&lt;p&gt;&lt;code&gt;eu&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Regulation (EC) No 561/2006&lt;/p&gt;

&lt;p&gt;9h driving, 45-min break after 4.5h driving, 11h daily rest&lt;/p&gt;

&lt;p&gt;The weekly/cycle caps (US 60/70-hour, Canada 70/120-hour, EU 56h/90h) are modeled when you supply &lt;code&gt;cycle_remaining_s&lt;/code&gt;; left unset, the projection focuses on the daily limits. The European &lt;code&gt;aetr&lt;/code&gt; regime is planned. If you don’t set a ruleset, the routing handler defaults it from the deployment region — but Canadian operators should set &lt;code&gt;canada_south&lt;/code&gt; or &lt;code&gt;canada_north&lt;/code&gt; explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Fits
&lt;/h2&gt;

&lt;p&gt;The HOS projection is computed on the route geometry that’s already in hand — it’s pure projection over the polyline, not a second round of routing. It runs for the primary route only, and it rides the routing call you’re already making: &lt;strong&gt;no separate endpoint, no separate charge&lt;/strong&gt; beyond the routing request itself. Routing is a paid-plan feature (featured on Pro+) with a free 14-day trial, so you can wire HOS into a dispatch flow and try it end-to-end before committing.&lt;/p&gt;

&lt;p&gt;Pair it with the corridor &lt;code&gt;warnings[]&lt;/code&gt; (bridge clearances, weight restrictions, rail crossings, work zones) and the same request answers three questions at once: can this truck legally run this corridor, where will the driver have to stop, and is there parking when they do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="///docs.html#truck-routing"&gt;API docs&lt;/a&gt; — full request and response reference for &lt;code&gt;POST /api/v1/routing/route&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="///blog/truck-routing-hazard-warnings.html"&gt;Truck routing with live hazard warnings&lt;/a&gt; — the corridor enrichment this builds on&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt; — no credit card, 14-day trial&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Plan the route, the legal stop, and the parking — in one call
&lt;/h3&gt;

&lt;p&gt;Send the driver’s HOS clock with the route and get back every required break, every drive-limit stop, and the parking reachable before each deadline. Free 14-day trial. No credit card.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Get Free API Key&lt;/a&gt; &lt;a href="///docs.html#truck-routing"&gt;Read the Docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>trucking</category>
      <category>api</category>
      <category>go</category>
      <category>logistics</category>
    </item>
    <item>
      <title>Normalizing DATEX II Across 30 European National Access Points</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Fri, 05 Jun 2026 11:37:15 +0000</pubDate>
      <link>https://dev.to/road511/normalizing-datex-ii-across-30-european-national-access-points-2g58</link>
      <guid>https://dev.to/road511/normalizing-datex-ii-across-30-european-national-access-points-2g58</guid>
      <description>&lt;p&gt;EU Directive 2010/40/EU mandates that every Member State operates a &lt;strong&gt;National Access Point (NAP)&lt;/strong&gt; that publishes real-time traffic, safety, and multimodal travel data in &lt;strong&gt;DATEX II&lt;/strong&gt;. On paper, that means one standard, 27 publishers, one integration.&lt;/p&gt;

&lt;p&gt;In practice, every NAP serves a different DATEX II profile, over a different transport, behind a different authentication flow, on a different cadence. "DATEX II compliant" is a checkbox each Member State ticks differently.&lt;/p&gt;

&lt;p&gt;This is what we ran into building &lt;a href="https://napspan.com" rel="noopener noreferrer"&gt;NAPSPAN&lt;/a&gt;, a unified API across EU 27 + UK + EFTA. Here's the architecture that lets us treat 30 incompatible DATEX II feeds as if they were one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spec, Then Reality
&lt;/h2&gt;

&lt;p&gt;DATEX II is a mature European standard maintained by &lt;a href="https://datex2.eu/" rel="noopener noreferrer"&gt;datex2.eu&lt;/a&gt; and coordinated by &lt;a href="https://napcore.eu/" rel="noopener noreferrer"&gt;NAPCORE&lt;/a&gt;. Each delegated regulation references it as the mandatory exchange format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;(EU) 2022/670&lt;/strong&gt; — RTTI: real-time events, speeds, road conditions&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(EU) No 886/2013&lt;/strong&gt; — SRTI: free, open access to safety alerts&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(EU) No 885/2013&lt;/strong&gt; — SSTP: safe and secure truck parking&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(EU) 2017/1926&lt;/strong&gt; — MMTIS: multimodal travel information&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;AFIR Article 20&lt;/strong&gt; — live EV charging-station data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the spec is clear. The implementation is anything but.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the NAPs Actually Diverge
&lt;/h2&gt;

&lt;p&gt;"DATEX II compliant" hides at least five axes of divergence:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Profile
&lt;/h3&gt;

&lt;p&gt;Each Member State publishes a national &lt;em&gt;profile&lt;/em&gt; — a constrained subset of the DATEX II model. Germany's profile (&lt;em&gt;German Traffic Data Profile&lt;/em&gt;) defines incidents, road works, and traffic flow on Autobahn + Bundesstraßen. France's &lt;a href="https://transport.data.gouv.fr/" rel="noopener noreferrer"&gt;transport.data.gouv.fr&lt;/a&gt; uses different sub-elements. The Netherlands' NDW publishes a tighter event taxonomy. The UK's &lt;a href="https://nap.tdt.gov.uk/" rel="noopener noreferrer"&gt;NAP TDT&lt;/a&gt; wraps DATEX into TIH-specific containers. The &lt;a href="https://repo.datex2.eu/implementations/profile_directory/" rel="noopener noreferrer"&gt;DATEX II profiles directory&lt;/a&gt; lists them all — same root schema, every country a different shape.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Version
&lt;/h3&gt;

&lt;p&gt;DATEX II v2.3 and v3.x are both in the wild. Some NAPs publish both. Some are mid-migration. The XML namespaces are different. Some elements were renamed between versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Transport
&lt;/h3&gt;

&lt;p&gt;DATEX II is a payload format, not a transport. NAPs ship it over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;HTTPS pull&lt;/strong&gt; — you GET an XML document on a schedule&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;HTTPS push (subscription)&lt;/strong&gt; — you register an endpoint, the NAP POSTs to you&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SOAP-over-HTTPS&lt;/strong&gt; — envelope-wrapped pull&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;OCIT-C&lt;/strong&gt; — the German telematics interface used by Mobilithek&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;WFS / GeoJSON&lt;/strong&gt; — some city-level supplements (Hamburg, Düsseldorf)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Authentication
&lt;/h3&gt;

&lt;p&gt;The matrix here is wide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;None&lt;/strong&gt; — SRTI safety feeds are mandated free and open&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Registered consumer (no key)&lt;/strong&gt; — NAP whitelists your origin or service endpoint&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Bearer / API key&lt;/strong&gt; — standard token in a header&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;TLS client certificate (mTLS)&lt;/strong&gt; — Germany's Mobilithek&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;OAuth2&lt;/strong&gt; — some operator portals&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Cadence and freshness
&lt;/h3&gt;

&lt;p&gt;Some publications update every 30 seconds (motorway flow). Others refresh nightly (planned road-work calendars). Some sit behind a CDN that lies about freshness via stale &lt;code&gt;Last-Modified&lt;/code&gt; headers. Adaptive backoff matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Format Zoo, in DATEX II Form
&lt;/h2&gt;

&lt;p&gt;Here's roughly the same incident from three different NAPs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobilithek (DE)&lt;/strong&gt; — OCIT-C envelope wrapping a v2.3 &lt;code&gt;SituationPublication&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;d2:payloadPublication
    xsi:type="d2:SituationPublication"
    xmlns:d2="http://datex2.eu/schema/2/2_0"&amp;gt;
  &amp;lt;d2:situation id="DE_BAB7_4711"&amp;gt;
    &amp;lt;d2:situationRecord
        xsi:type="d2:Accident"
        id="DE_BAB7_4711_R1"&amp;gt;
      &amp;lt;d2:probabilityOfOccurrence&amp;gt;certain&amp;lt;/d2:probabilityOfOccurrence&amp;gt;
      &amp;lt;d2:groupOfLocations xsi:type="d2:Point"&amp;gt;
        &amp;lt;d2:pointByCoordinates&amp;gt;
          &amp;lt;d2:pointCoordinates&amp;gt;
            &amp;lt;d2:latitude&amp;gt;53.5511&amp;lt;/d2:latitude&amp;gt;
            &amp;lt;d2:longitude&amp;gt;9.9937&amp;lt;/d2:longitude&amp;gt;
          &amp;lt;/d2:pointCoordinates&amp;gt;
        &amp;lt;/d2:pointByCoordinates&amp;gt;
      &amp;lt;/d2:groupOfLocations&amp;gt;
    &amp;lt;/d2:situationRecord&amp;gt;
  &amp;lt;/d2:situation&amp;gt;
&amp;lt;/d2:payloadPublication&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;NDW (NL)&lt;/strong&gt; — v3.x with a tighter &lt;code&gt;VmsTablePublication&lt;/code&gt;-style envelope, JSON-LD permitted:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "@context": "https://datex2.eu/schema/3/jsonld",
  "publicationCreator": { "country": "nl", "nationalIdentifier": "NDW" },
  "situation": [{
    "id": "NDW_2026_05_01_42",
    "situationRecord": [{
      "@type": "Accident",
      "severity": "high",
      "groupOfLocations": {
        "locationContainedInGroup": [{
          "pointByCoordinates": { "latitude": 52.0907, "longitude": 5.1214 }
        }]
      }
    }]
  }]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;UK NAP TDT&lt;/strong&gt; — DATEX wrapped in a TIH container with a per-publisher feed envelope:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;tih:feed xmlns:tih="https://nap.tdt.gov.uk/tih"&amp;gt;
  &amp;lt;tih:publisher&amp;gt;National Highways&amp;lt;/tih:publisher&amp;gt;
  &amp;lt;d2:payloadPublication xsi:type="d2:SituationPublication"&amp;gt;
    &amp;lt;d2:situation id="NH-INC-9001"&amp;gt;...&amp;lt;/d2:situation&amp;gt;
  &amp;lt;/d2:payloadPublication&amp;gt;
&amp;lt;/tih:feed&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Same regulation. Same standard. Three different parsers required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;The solution is the same one we use on the North American side: an adapter registry with one function signature.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type FetchFunc func(ctx context.Context, sr SourceResource) (*FetchResult, error)

type FetchResult struct {
    Events        []TrafficEvent
    Features      []Feature
    ResponseBytes int
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Every NAP adapter — whether it's pulling Mobilithek's OCIT-C broker, polling NDW's v3 JSON-LD, or unwrapping a TIH envelope — implements this one interface. It takes a source-resource config (URL, credentials, country code, profile version) and returns normalized events and features.&lt;/p&gt;

&lt;p&gt;Registration happens at init time:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func init() {
    Register("mobilithek", "events",         fetchMobilithekSituations)
    Register("mobilithek", "afir_charging",  fetchMobilithekAFIR)
    Register("mobilithek", "parking",        fetchMobilithekParking)
    Register("ndw",        "events",         fetchNDWSituations)
    Register("ndw",        "vms",            fetchNDWVMS)
    Register("uk_tdt",     "events",         fetchUKTDTSituations)
    Register("transport_data_gouv_fr", "events", fetchFRSituations)
    // ... 30 NAPs, ~120 (adapter, resource) registrations
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The scheduler doesn't know or care which DATEX II profile, version, or transport each adapter handles. It just calls &lt;code&gt;Fetch()&lt;/code&gt; and gets back normalized events and features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Normalized Model
&lt;/h2&gt;

&lt;p&gt;Everything converges into two tables:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;traffic_events&lt;/code&gt;&lt;/strong&gt; — time-bounded incidents with lifecycle tracking:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type TrafficEvent struct {
    ID                 string
    Source             string      // "mobilithek", "ndw", "uk_tdt"
    Jurisdiction       string      // "DE", "NL", "GB"
    Type               EventType   // incident, construction, closure, weather
    Severity           Severity    // minor, moderate, major, critical
    Status             EventStatus // active, archived
    Title              string
    Description        string
    AffectedRoads      []string
    Direction          string
    LanesAffected      string
    Latitude, Longitude float64
    StartTime          time.Time
    EndTime            *time.Time
    EstimatedEndTime   *time.Time
    RoadClass          string      // motorway, trunk, primary, secondary
    Metadata           json.RawMessage // DATEX-II-specific fields preserved
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;features&lt;/code&gt;&lt;/strong&gt; — a generic table for everything else. Cameras, VMS signs, weather stations, parking sites, EV charging stations, truck-route segments, bridge clearances — all use the same table with a &lt;code&gt;feature_type&lt;/code&gt; discriminator and type-specific fields in a JSONB &lt;code&gt;properties&lt;/code&gt; column. Adding a new resource type requires zero schema migrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hard Parts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Profile Field Mapping
&lt;/h3&gt;

&lt;p&gt;The German profile uses &lt;code&gt;locationDescriptor&lt;/code&gt; with &lt;code&gt;roadName&lt;/code&gt;. The Dutch v3 profile uses a top-level &lt;code&gt;roadInformation&lt;/code&gt; structure. The UK feeds bury the road name three levels deep inside &lt;code&gt;tih:routeContext&lt;/code&gt;. Each adapter has a small mapping function that knows where its profile keeps each canonical field. Where a profile has nothing to map to, the field is left empty — we don't synthesize data we don't have.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Lifecycle Tracking
&lt;/h3&gt;

&lt;p&gt;A DATEX II &lt;code&gt;SituationRecord&lt;/code&gt; has a &lt;code&gt;situationRecordVersion&lt;/code&gt; and a &lt;code&gt;situationRecordVersionTime&lt;/code&gt;. Each new pull may bring a new version of the same record. Some NAPs increment the version on every minor metadata change; others only on substantive updates.&lt;/p&gt;

&lt;p&gt;The solution: diff the current state against the previous fetch. Track every change in an &lt;code&gt;event_history&lt;/code&gt; table — severity changes, description updates, lane changes, archival. This enables analytics like clearance time percentiles and corridor reliability across borders.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Coordinate Reference Systems
&lt;/h3&gt;

&lt;p&gt;Most NAPs publish WGS84 (EPSG:4326). Some German state feeds publish ETRS89 / UTM zones. A few city feeds use the local cadastral system. PostGIS handles transformation, but each adapter has to know what it's receiving.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Rate Limiting at Scale
&lt;/h3&gt;

&lt;p&gt;30 NAPs, each with multiple resource types (events, VMS, parking, EV charging, weather), each polling on a 30 sec to 5 min cadence. The scheduler uses per-server semaphores with configurable concurrency limits and request gaps. Circuit breakers back off on repeated failures. Adaptive backoff increases poll intervals when a NAP is slow or returning errors — important when a single NAP can otherwise drag down a whole region's freshness.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Schema-Free Feature Types
&lt;/h3&gt;

&lt;p&gt;When we started on the North American side, we had separate tables for cameras, VMS, parking. Every new data type meant a migration. The pivot to a generic &lt;code&gt;features&lt;/code&gt; table with JSONB properties was the best architectural decision in the project. On the EU side, that decision pays off again the moment AFIR EV charging data comes online — zero schema changes, just a new &lt;code&gt;feature_type&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API
&lt;/h2&gt;

&lt;p&gt;After all that normalization work, the API is straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get active incidents in Germany:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl "https://api.napspan.com/api/v1/events?jurisdiction=DEU&amp;amp;type=incident&amp;amp;status=active" \
  -H "X-API-Key: your_key"

{
  "data": [
    {
      "id": "de_mobilithek_4711",
      "jurisdiction": "DEU",
      "type": "incident",
      "severity": "major",
      "title": "Verkehrsunfall A7 Richtung Hamburg",
      "affected_roads": ["A7"],
      "direction": "Hamburg",
      "lanes_affected": "2 of 3 lanes closed",
      "latitude": 53.5511,
      "longitude": 9.9937,
      "start_time": "2026-05-01T08:15:00Z",
      "estimated_end_time": "2026-05-01T12:00:00Z"
    }
  ],
  "total": 142,
  "limit": 100,
  "offset": 0,
  "has_more": true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Get traffic cameras in the Netherlands as GeoJSON:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl "https://api.napspan.com/api/v1/features/geojson?type=cameras&amp;amp;jurisdiction=NLD" \
  -H "X-API-Key: your_key"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Drop the response directly into Leaflet, Mapbox, or any GeoJSON-compatible tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query a cross-border box (Berlin to Amsterdam):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl "https://api.napspan.com/api/v1/events?status=active&amp;amp;\
bbox=4.90,52.37,13.40,52.52" \
  -H "X-API-Key: your_key"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;One call, two NAPs (Mobilithek + NDW), one consistent response shape — a bounding box spans the border with no per-country fan-out. (For events tied to an actual driven path rather than a box, &lt;code&gt;POST /routing/route&lt;/code&gt; returns them as ranked &lt;code&gt;warnings[]&lt;/code&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the Data
&lt;/h2&gt;

&lt;p&gt;Once normalized, the dataset includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Real-time &lt;strong&gt;RTTI&lt;/strong&gt; events — incidents, road works, traffic flow&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SRTI&lt;/strong&gt; safety alerts (free, open under 886/2013)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;VMS&lt;/strong&gt; dynamic message-sign content with current displayed text&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SSTP&lt;/strong&gt; safe and secure truck parking with availability&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;AFIR&lt;/strong&gt; Article 20 EV charging-point status&lt;/li&gt;
&lt;li&gt;  Traffic cameras with image URLs (where the NAP exposes them)&lt;/li&gt;
&lt;li&gt;  RWIS-style weather-station readings&lt;/li&gt;
&lt;li&gt;  TEN-T core network corridor metadata&lt;/li&gt;
&lt;li&gt;  Bridge clearances and weight restrictions per Member State&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Go&lt;/strong&gt; — chi router, pgx v5, slog, encoding/xml + custom DATEX schema parsers&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;PostgreSQL + PostGIS&lt;/strong&gt; — spatial queries, GeoJSON generation, corridor intersection&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Redis&lt;/strong&gt; — response caching, feature detail caching, rate-limit counters, webhook idempotency (&lt;strong&gt;mandatory&lt;/strong&gt; — the API and worker refuse to start if Redis is unreachable)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Vue 3 + Leaflet&lt;/strong&gt; — &lt;a href="https://map.napspan.com" rel="noopener noreferrer"&gt;live traffic map&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;The API is live with a free tier (no credit card):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://map.napspan.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — explore the data visually&lt;/li&gt;
&lt;li&gt;  &lt;a href="///docs.html"&gt;API docs&lt;/a&gt; — full endpoint reference&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://portal.napspan.com" rel="noopener noreferrer"&gt;Developer portal&lt;/a&gt; — sign up and get an API key&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you're building anything on European mobility data — logistics, fleet routing, navigation, urban-mobility dashboards — we'd love to hear which NAPs and which DATEX II profiles you most need normalized first. The hardest part isn't the parsing; it's knowing which Member State publishes which data on which transport behind which auth flow. After 30 NAPs, we've built a pretty good map of that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ready to try NAPSPAN?
&lt;/h3&gt;

&lt;p&gt;Free 14-day trial. No credit card. EU 27 + UK + EFTA, normalized.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://portal.napspan.com/signup" rel="noopener noreferrer"&gt;Get Free API Key&lt;/a&gt; &lt;a href="https://map.napspan.com/" rel="noopener noreferrer"&gt;Explore the Map&lt;/a&gt;&lt;/p&gt;

</description>
      <category>datex2</category>
      <category>eu</category>
      <category>go</category>
      <category>api</category>
    </item>
    <item>
      <title>Truck Routing That Warns You Before the Bridge Does</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Sat, 30 May 2026 10:55:44 +0000</pubDate>
      <link>https://dev.to/road511/truck-routing-that-warns-you-before-the-bridge-does-2l5k</link>
      <guid>https://dev.to/road511/truck-routing-that-warns-you-before-the-bridge-does-2l5k</guid>
      <description>&lt;p&gt;A routing API will happily hand a truck the fastest line between two points. What it usually won't tell you is that the line runs under a 4.0 m overpass with a 4.2 m load on the trailer, crosses an at-grade rail line you're legally required to stop at with placarded hazmat, or threads a county road that's posted closed for construction this week.&lt;/p&gt;

&lt;p&gt;Flow APIs — HERE, TomTom, INRIX — are excellent at &lt;em&gt;speed&lt;/em&gt;: where traffic is moving, how long the trip takes. That's congestion data. It is not infrastructure data. The overpass height, the bridge weight rating, the truck-restricted segment, the rail crossing, the active work zone — those live in 57 different state and provincial 511 systems, and a flow API doesn't read them.&lt;/p&gt;

&lt;p&gt;That gap is what Road511's routing endpoint closes. We take the route geometry from HERE, then enrich the corridor from our own live traffic database — the same normalized 511 data behind the rest of the API — and return the hazards as a structured &lt;code&gt;warnings[]&lt;/code&gt; array scored against &lt;em&gt;your&lt;/em&gt; truck's dimensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;One call to &lt;code&gt;POST /api/v1/routing/route&lt;/code&gt;. You send an origin, a destination, and — required — a &lt;code&gt;truck&lt;/code&gt; profile. The profile is what makes a clearance "critical" instead of trivia: a 4.2 m height flags the 4.0 m overpass; a 36-tonne gross weight flags the posted 30-tonne bridge; a &lt;code&gt;hazardous&lt;/code&gt; flag turns public at-grade rail crossings into mandatory-stop warnings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.road511.com/api/v1/routing/route"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "origin":      { "lat": 47.6062, "lng": -122.3321 },
    "destination": { "lat": 47.6588, "lng": -117.4260 },
    "truck": {
      "profile":  "tractor",
      "weight_t": 36.0,
      "height_m": 4.2,
      "width_m":  2.6,
      "length_m": 21.0,
      "axle_count": 5,
      "hazardous": ["flammable"]
    },
    "alternatives": 1,
    "avoid": ["toll_road"],
    "enrichment": {
      "buffer_m": 100,
      "clearance_pad_m": 0.15,
      "min_severity": "warning",
      "include_features": ["truck_parking", "rest_areas"]
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's Seattle to Spokane for a 4.2 m, 36-tonne, placarded tractor-trailer. The &lt;code&gt;truck&lt;/code&gt; object accepts &lt;code&gt;profile&lt;/code&gt; (&lt;code&gt;tractor&lt;/code&gt;, &lt;code&gt;straight&lt;/code&gt;, or &lt;code&gt;box&lt;/code&gt;), &lt;code&gt;weight_t&lt;/code&gt;, &lt;code&gt;height_m&lt;/code&gt;, and optional &lt;code&gt;width_m&lt;/code&gt;, &lt;code&gt;length_m&lt;/code&gt;, &lt;code&gt;axle_count&lt;/code&gt;, and &lt;code&gt;hazardous&lt;/code&gt;. You can add &lt;code&gt;waypoints&lt;/code&gt;, request up to 3 &lt;code&gt;alternatives&lt;/code&gt;, and &lt;code&gt;avoid&lt;/code&gt; any of &lt;code&gt;tolls&lt;/code&gt;, &lt;code&gt;ferries&lt;/code&gt;, or &lt;code&gt;tunnels&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Response
&lt;/h2&gt;

&lt;p&gt;You get back a &lt;code&gt;routes[]&lt;/code&gt; array. Each route carries a &lt;code&gt;summary&lt;/code&gt;, a GeoJSON &lt;code&gt;geometry&lt;/code&gt; (a &lt;code&gt;LineString&lt;/code&gt; you can drop straight onto a map), and the part that matters — a &lt;code&gt;warnings[]&lt;/code&gt; array. Every warning is positioned: a &lt;code&gt;type&lt;/code&gt;, a &lt;code&gt;severity&lt;/code&gt; (&lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, or &lt;code&gt;critical&lt;/code&gt;), a &lt;code&gt;distance_along_route_m&lt;/code&gt;, a &lt;code&gt;projected_arrival_time&lt;/code&gt;, a &lt;code&gt;source&lt;/code&gt;, its own &lt;code&gt;geometry&lt;/code&gt;, and type-specific &lt;code&gt;properties&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"distance_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;449300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duration_s"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16980&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"has_tolls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LineString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-122.3321&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.6062&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-120.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-117.4260&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.6588&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"warnings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bridge_clearance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bridge clearance below truck height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I-90 over Snoqualmie Pass on-ramp clearance 4.11m; your truck is 4.20m."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"distance_along_route_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;38200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"projected_arrival_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-29T15:12:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nbi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WA-000000000123456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-121.97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.51&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"clearance_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"truck_height_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"facility_carried"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I-90"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"feature_crossed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Snoqualmie Pass on-ramp"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rail_crossing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mandatory hazmat stop — 49 CFR 392.10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Public at-grade crossing; hazmat carrier must stop."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"distance_along_route_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;201400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"projected_arrival_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-29T17:41:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fra"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"094821X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-119.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"at_grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"railroad"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BNSF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"crossing_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"At Grade"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"traffic_event"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Construction on US-2 EB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US-2 EB right lane closed for paving"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"distance_along_route_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;372600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"projected_arrival_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-29T19:08:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wsdot-4471822"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-118.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.45&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"sub_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"construction"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"truck_parking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ritzville Rest Area"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"distance_along_route_m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;250100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"capacity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Point"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-118.38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;47.13&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"route_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rt_8f3c2a1b"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The route geometry is HERE's. Everything in &lt;code&gt;warnings[]&lt;/code&gt; and &lt;code&gt;features[]&lt;/code&gt; is Road511's enrichment of the corridor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Warning Types
&lt;/h2&gt;

&lt;p&gt;A warning is only emitted when the corridor actually contains the hazard — and for the dimension-checked types, only when your profile would violate it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;What it flags&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bridge_clearance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overpass clearance vs your &lt;code&gt;height_m&lt;/code&gt; + &lt;code&gt;clearance_pad_m&lt;/code&gt;. &lt;strong&gt;Critical&lt;/strong&gt; — the one that ends loads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;truck_restriction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A segment your profile is barred from. &lt;strong&gt;Critical&lt;/strong&gt;, per violated dimension.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;weight_restriction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Posted or seasonal weight limit your gross weight exceeds. &lt;strong&gt;Critical&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rail_crossing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Public at-grade crossing. &lt;strong&gt;Critical&lt;/strong&gt; for placarded hazmat (49 CFR 392.10 mandatory stop); &lt;code&gt;info&lt;/code&gt; for high-volume otherwise.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;traffic_event&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Live incident, closure, or construction on the corridor.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;future_construction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Planned work zones the route passes through.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;special_event&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scheduled events affecting nearby roads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;weigh_station&lt;/code&gt; / &lt;code&gt;inspection_station&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Scale houses and inspection sites along the route.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alert&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Jurisdiction-issued advisories on the corridor.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;weather&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Conditions reported near the route.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each warning's &lt;code&gt;properties&lt;/code&gt; are type-specific: a &lt;code&gt;bridge_clearance&lt;/code&gt; reports the measured &lt;code&gt;clearance_m&lt;/code&gt; and your &lt;code&gt;truck_height_m&lt;/code&gt;. The data is only as fresh as the underlying feed — clearances come from inventories that refresh on the order of days, while &lt;code&gt;traffic_event&lt;/code&gt; warnings ride the live incident feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tuning the Enrichment
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;enrichment&lt;/code&gt; object controls how aggressively the corridor is scanned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;buffer_m&lt;/code&gt; — how far off the line to look, 10–1000 m (default 100).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clearance_pad_m&lt;/code&gt; — safety margin added to your height before a clearance flags, 0–1 m (default 0.15).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;min_severity&lt;/code&gt; — drop everything below &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, or &lt;code&gt;critical&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exclude_types&lt;/code&gt; — suppress warning types you don't care about.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;skip_temporal_filter&lt;/code&gt; — include time-bounded items outside their active window.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_distance_m&lt;/code&gt; — cap how far along the route enrichment runs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include_features&lt;/code&gt; — opt into a separate &lt;code&gt;features[]&lt;/code&gt; channel of &lt;code&gt;truck_parking&lt;/code&gt;, &lt;code&gt;truck_rest_areas&lt;/code&gt;, and &lt;code&gt;rest_areas&lt;/code&gt; near the route.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Saved Routes, Re-Evaluation, and Monitoring
&lt;/h2&gt;

&lt;p&gt;Every routing call is auto-saved for &lt;strong&gt;30 minutes&lt;/strong&gt; under the &lt;code&gt;route_id&lt;/code&gt;. Refetch with &lt;code&gt;GET /api/v1/routing/route/saved/{id}&lt;/code&gt; inside that window and the warnings are &lt;em&gt;re-evaluated against current conditions&lt;/em&gt; — a closure that popped up since you planned shows up on the refetch. Refetches do &lt;strong&gt;not&lt;/strong&gt; consume routing quota.&lt;/p&gt;

&lt;p&gt;To keep a route past the 30-minute TTL, call &lt;code&gt;POST /api/v1/routing/route/saved/{id}/persist&lt;/code&gt;. Persisted routes count against your plan's saved-route slots, and they can be put under &lt;strong&gt;webhook monitoring&lt;/strong&gt; — so when a new critical warning lands on a route you've already dispatched, you get notified instead of polling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quota and Access
&lt;/h2&gt;

&lt;p&gt;Routing is a paid-plan feature, featured most heavily on &lt;strong&gt;Pro+&lt;/strong&gt;. The Free 14-day trial includes a small daily cap so you can try it. Quota is a monthly routing-call bucket; FIFO top-up packs add calls on top. Refetching a saved route is free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Different
&lt;/h2&gt;

&lt;p&gt;HERE and TomTom sell you &lt;em&gt;flow&lt;/em&gt; — how fast the road is moving. Road511 adds the &lt;em&gt;infrastructure&lt;/em&gt; — what your specific truck physically and legally can't do on that road. The routing endpoint puts both in one response: a drivable HERE geometry, plus a corridor scan against the live 511 data that flow APIs were never built to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://road511.com/docs.html#truck-routing" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.road511.com/signup" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>trucking</category>
      <category>go</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fuel Price API for Fleet Cost Planning</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Thu, 21 May 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/road511/fuel-price-api-for-fleet-cost-planning-1ag9</link>
      <guid>https://dev.to/road511/fuel-price-api-for-fleet-cost-planning-1ag9</guid>
      <description>&lt;p&gt;Fleet fuel costs are the second-largest operating expense after labor. Having state-level fuel price data in the same API as your truck routes, bridge clearances, and corridor restrictions means you can estimate total route costs in one integration.&lt;/p&gt;

&lt;p&gt;Road511 now serves weekly US state-level and monthly Canadian province-level fuel prices from government sources.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;US — EIA (Energy Information Administration)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Weekly retail prices for all 50 states&lt;/li&gt;
&lt;li&gt;Regular, midgrade, premium gasoline + diesel&lt;/li&gt;
&lt;li&gt;USD per gallon&lt;/li&gt;
&lt;li&gt;Updated every Monday&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Canada — Statistics Canada&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monthly retail prices for 12 provinces/territories&lt;/li&gt;
&lt;li&gt;Regular, premium gasoline + diesel&lt;/li&gt;
&lt;li&gt;CAD cents per litre&lt;/li&gt;
&lt;li&gt;Updated monthly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are official government data, free, and unrestricted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Latest Prices
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/fuel-prices?country=US&amp;amp;fuel_type=diesel&amp;amp;latest=true"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jurisdiction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fuel_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"diesel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.892&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gallon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"period_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-07"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"frequency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"weekly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eia"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jurisdiction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fuel_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"diesel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.241&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gallon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"period_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-07"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"frequency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"weekly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eia"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"has_more"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Historical Trends
&lt;/h2&gt;

&lt;p&gt;Track how prices change over time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/fuel-prices?jurisdiction=CA&amp;amp;fuel_type=diesel&amp;amp;from=2025-01-01&amp;amp;to=2026-04-10"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns every weekly data point for California diesel over the past 15 months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-Border Queries
&lt;/h2&gt;

&lt;p&gt;Get both US and Canadian prices in one response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/fuel-prices?fuel_type=diesel&amp;amp;latest=true&amp;amp;limit=100"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: US prices are in USD/gallon, Canadian in CAD cents/litre. Convert as needed for your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combine with Corridor Data
&lt;/h2&gt;

&lt;p&gt;The real value is combining fuel prices with Road511's other data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Plan the route&lt;/strong&gt; — check bridge clearances and weight restrictions via &lt;code&gt;/truck/corridor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estimate fuel cost&lt;/strong&gt; — get diesel prices for each state the route crosses via &lt;code&gt;/fuel-prices&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check conditions&lt;/strong&gt; — verify road conditions and active incidents via &lt;code&gt;/events&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All from one API, one key, one integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fleet fuel budgeting&lt;/strong&gt; — forecast costs based on state-level price trends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route cost optimization&lt;/strong&gt; — factor fuel price differences into route selection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate calculations&lt;/strong&gt; — logistics platforms adjusting freight rates by fuel cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics&lt;/strong&gt; — correlate fuel prices with traffic patterns, delivery frequency&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.road511.com" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.road511.com" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>trucking</category>
      <category>go</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Comparing Traffic Data APIs: Road511 vs HERE vs TomTom vs INRIX</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Tue, 19 May 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/road511/comparing-traffic-data-apis-road511-vs-here-vs-tomtom-vs-inrix-55cg</link>
      <guid>https://dev.to/road511/comparing-traffic-data-apis-road511-vs-here-vs-tomtom-vs-inrix-55cg</guid>
      <description>&lt;p&gt;If you're building an app that needs traffic data, you have a few options. The big names — HERE, TomTom, INRIX — dominate the market. But they all sell the same thing: traffic flow from probe data (GPS traces from phones and connected vehicles).&lt;/p&gt;

&lt;p&gt;Road511 is different. It aggregates infrastructure data from government 511 systems that the big providers don't touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Each Provider Actually Offers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data Type&lt;/th&gt;
&lt;th&gt;Road511&lt;/th&gt;
&lt;th&gt;HERE&lt;/th&gt;
&lt;th&gt;TomTom&lt;/th&gt;
&lt;th&gt;INRIX&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Traffic flow (speed/congestion)&lt;/td&gt;
&lt;td&gt;Limited (select states)&lt;/td&gt;
&lt;td&gt;Yes (core product)&lt;/td&gt;
&lt;td&gt;Yes (core product)&lt;/td&gt;
&lt;td&gt;Yes (core product)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic events/incidents&lt;/td&gt;
&lt;td&gt;57 jurisdictions&lt;/td&gt;
&lt;td&gt;Yes (own sources)&lt;/td&gt;
&lt;td&gt;Yes (own sources)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live cameras (10K+)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DMS sign messages&lt;/td&gt;
&lt;td&gt;Yes (30+ states)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bridge clearances (621K)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Truck routes (479K STAA segments)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weight restrictions&lt;/td&gt;
&lt;td&gt;Yes (multi-state)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spring load limits&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Road conditions (surface state)&lt;/td&gt;
&lt;td&gt;Yes (15+ states)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RWIS weather stations&lt;/td&gt;
&lt;td&gt;Yes (20+ states)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rest areas &amp;amp; truck parking&lt;/td&gt;
&lt;td&gt;Yes (30+ states)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EV charging (100K+ stations)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fuel prices (state-level)&lt;/td&gt;
&lt;td&gt;Yes (EIA + StatCan)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event lifecycle history&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GeoJSON native&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhook notifications&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Via HERE Platform&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Entry Price&lt;/th&gt;
&lt;th&gt;What You Get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Road511&lt;/td&gt;
&lt;td&gt;$0 (14-day trial) / $29/mo&lt;/td&gt;
&lt;td&gt;Full API access, 50K req/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HERE&lt;/td&gt;
&lt;td&gt;~$200/mo&lt;/td&gt;
&lt;td&gt;Traffic flow + incidents, limited calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TomTom&lt;/td&gt;
&lt;td&gt;~$150/mo&lt;/td&gt;
&lt;td&gt;Traffic flow + incidents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INRIX&lt;/td&gt;
&lt;td&gt;~$50K/year&lt;/td&gt;
&lt;td&gt;Enterprise only, negotiated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Road511 at $29-99/mo is 10-100x cheaper than HERE/TomTom for similar request volumes, and has data categories they simply don't offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Which
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use HERE/TomTom if you need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time traffic flow (speed, congestion, travel times)&lt;/li&gt;
&lt;li&gt;Turn-by-turn routing with live traffic&lt;/li&gt;
&lt;li&gt;Global coverage (Europe, Asia, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Road511 if you need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Camera feeds, DMS messages, road conditions&lt;/li&gt;
&lt;li&gt;Bridge clearances, truck routes, weight restrictions&lt;/li&gt;
&lt;li&gt;Cross-state normalized 511 data&lt;/li&gt;
&lt;li&gt;Event lifecycle analytics (clearance times, trends)&lt;/li&gt;
&lt;li&gt;EV charging station data&lt;/li&gt;
&lt;li&gt;Fuel prices by state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use both if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building a fleet management platform — HERE/TomTom for routing + Road511 for truck restrictions, bridge clearances, fuel prices, and camera verification&lt;/li&gt;
&lt;li&gt;You're building a comprehensive navigation app — traffic flow from one, infrastructure data from the other&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Real Differentiator
&lt;/h2&gt;

&lt;p&gt;HERE, TomTom, and INRIX compete with each other on the same data: traffic flow. They spend hundreds of millions on probe data collection.&lt;/p&gt;

&lt;p&gt;Road511 provides the infrastructure data that nobody else aggregates. 10,000 cameras, 621,000 bridges, 479,000 truck route segments, real-time sign messages, road conditions — all normalized from 57 government sources into one API.&lt;/p&gt;

&lt;p&gt;The data is complementary, not competitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.road511.com" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.road511.com" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>go</category>
    </item>
    <item>
      <title>How to Build a Traffic Dashboard with Road511 + Leaflet</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Thu, 14 May 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/road511/how-to-build-a-traffic-dashboard-with-road511-leaflet-220l</link>
      <guid>https://dev.to/road511/how-to-build-a-traffic-dashboard-with-road511-leaflet-220l</guid>
      <description>&lt;p&gt;Let's build a real-time traffic dashboard from scratch. By the end, you'll have a map showing live incidents, camera popups with images, and road condition overlays — all powered by Road511's GeoJSON API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Create an &lt;code&gt;index.html&lt;/code&gt; with Leaflet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/leaflet@1.9/dist/leaflet.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;#map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/leaflet@1.9/dist/leaflet.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"app.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initialize the Map
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your_api_key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.road511.com/api/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setView&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;39.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;98.5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// center of US&lt;/span&gt;
&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://tile.openstreetmap.org/{z}/{x}/{y}.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;attribution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;copy; OpenStreetMap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Layer 1: Traffic Events
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadEvents&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBounds&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getWest&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSouth&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEast&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNorth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/events/geojson?bbox=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;status=active&amp;amp;limit=500`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;severityColors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;critical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#991b1b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;major&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ef4444&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;moderate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f59e0b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;minor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#22c55e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;pointToLayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latlng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;circleMarker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latlng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fillColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;severityColors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#6b7280&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fillOpacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;onEachFeature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindPopup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
        &amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;
        &amp;lt;span style="color:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;severityColors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/span&amp;gt;
        &amp;amp;middot; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;br&amp;gt;
        &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;affected_roads&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
      `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Layer 2: Cameras with Image Popups
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadCameras&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jurisdiction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/features/geojson?type=cameras&amp;amp;jurisdiction=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;jurisdiction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;camIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;divIcon&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;📷&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;camera-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;iconSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;pointToLayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latlng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latlng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;camIcon&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;onEachFeature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindPopup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
        &amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;
        &amp;lt;img src="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" width="320" loading="lazy"
             onerror="this.src='data:image/svg+xml,&amp;lt;svg/&amp;gt;'"&amp;gt;
      `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Layer 3: Road Conditions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadRoadConditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jurisdiction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/features/geojson?type=road_conditions&amp;amp;jurisdiction=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;jurisdiction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// properties.condition is raw per-DOT text ("Bare Dry", "Cvd Snw", "3 - dry",&lt;/span&gt;
  &lt;span class="c1"&gt;// "Clear", ...), so key colors off keywords instead of exact enum values.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conditionColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ef4444&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/snow|ice|icy/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#8b5cf6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/wet|rain|flood/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#3b82f6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/dry|bare|clear/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#22c55e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#6b7280&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;conditionColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;onEachFeature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindPopup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
        &amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;
        Condition: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
      `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Put It Together
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;layers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;loadEvents&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;loadCameras&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cameras (CA)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;loadRoadConditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Road Conditions (CA)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Refresh events when the map moves&lt;/span&gt;
&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moveend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Remove old events layer, load new one for current viewport&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Auto-Refresh
&lt;/h2&gt;

&lt;p&gt;Events change frequently. Add a 60-second refresh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;eventsLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearLayers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadEvents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;newLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eachLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;eventsLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add sign messages as a layer&lt;/li&gt;
&lt;li&gt;Add weather stations with condition badges&lt;/li&gt;
&lt;li&gt;Show truck restrictions along a corridor&lt;/li&gt;
&lt;li&gt;Use the analytics endpoints to show a sidebar with incident trends&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;See a working example&lt;/a&gt; — built with this exact approach&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.road511.com" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.road511.com" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Road511/road511-examples" rel="noopener noreferrer"&gt;Full code examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
    <item>
      <title>Spring Load Limits API for Trucking Compliance</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Tue, 12 May 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/road511/spring-load-limits-api-for-trucking-compliance-j40</link>
      <guid>https://dev.to/road511/spring-load-limits-api-for-trucking-compliance-j40</guid>
      <description>&lt;p&gt;Every spring, northern states and provinces impose weight restrictions on roads as frost leaves the ground. The pavement is weakest during thaw, and unrestricted heavy loads cause millions in damage.&lt;/p&gt;

&lt;p&gt;The problem: every state publishes restrictions differently, with different dates, formats, and geographic references. Fleet operators check each state manually.&lt;/p&gt;

&lt;p&gt;Road511 normalizes spring load limits from MN, WI, MI, ON, QC, and NS into one API with polyline geometry and active date ranges.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/features?type=weight_restrictions&amp;amp;jurisdiction=MN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mn-wr-zone3-seg12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jurisdiction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"feature_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"weight_restrictions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CSAH 5 (Southeast frost zone)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-10T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"end_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-15T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"restriction_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"seasonal_weight"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"weight_limit_tons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"frost_zone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Southeast"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"from_ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0+00.000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"to_ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9+00.995"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each restriction has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polyline geometry&lt;/strong&gt; — the exact road segment, not just a point&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start/end dates&lt;/strong&gt; — when the restriction is active&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weight limit&lt;/strong&gt; — MN posts a &lt;em&gt;per-axle&lt;/em&gt; limit in tons (&lt;code&gt;weight_limit_tons&lt;/code&gt;: a 5 means 5 t/axle, not gross), MI a percentage reduction, WI by road class&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Segment endpoints&lt;/strong&gt; — from/to mile-point references (&lt;code&gt;from_ref&lt;/code&gt;/&lt;code&gt;to_ref&lt;/code&gt;, e.g. &lt;code&gt;0+00.000&lt;/code&gt;), source-specific per state&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Coverage
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State/Province&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Minnesota&lt;/td&gt;
&lt;td&gt;Spring load limits&lt;/td&gt;
&lt;td&gt;~26 segments with ton limits by frost zone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wisconsin&lt;/td&gt;
&lt;td&gt;Class II + Posted&lt;/td&gt;
&lt;td&gt;148 restricted highways + 19 posted roads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Michigan&lt;/td&gt;
&lt;td&gt;Truck Operator Route Map&lt;/td&gt;
&lt;td&gt;~1,757 segments with 25-35% weight reductions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ontario&lt;/td&gt;
&lt;td&gt;Spring load limits&lt;/td&gt;
&lt;td&gt;143 polyline segments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quebec&lt;/td&gt;
&lt;td&gt;Trucking network restrictions&lt;/td&gt;
&lt;td&gt;Province-wide route restrictions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nova Scotia&lt;/td&gt;
&lt;td&gt;Spring weight restrictions&lt;/td&gt;
&lt;td&gt;8,599 restricted segments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Corridor Check
&lt;/h2&gt;

&lt;p&gt;Use the truck corridor endpoint to check if a planned route crosses any active restrictions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/truck/corridor?from_lat=44.98&amp;amp;from_lng=-93.27&amp;amp;to_lat=46.87&amp;amp;to_lng=-96.77&amp;amp;buffer_km=10&amp;amp;weight=30"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Minneapolis to Fargo — returns every weight restriction along the corridor that your 30-ton load would violate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Historical Tracking
&lt;/h2&gt;

&lt;p&gt;Road511 tracks when restrictions are posted and lifted via &lt;code&gt;feature_history&lt;/code&gt;. Over multiple seasons, this data shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When each state typically starts/ends restrictions&lt;/li&gt;
&lt;li&gt;Which zones get restricted first (geographic progression of thaw)&lt;/li&gt;
&lt;li&gt;Year-over-year pattern changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.road511.com" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.road511.com" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>trucking</category>
      <category>go</category>
      <category>webdev</category>
    </item>
    <item>
      <title>GeoJSON Traffic Events API with BBox and Radius Queries</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Thu, 07 May 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/road511/geojson-traffic-events-api-with-bbox-and-radius-queries-3cbl</link>
      <guid>https://dev.to/road511/geojson-traffic-events-api-with-bbox-and-radius-queries-3cbl</guid>
      <description>&lt;p&gt;If you're building a traffic map, the last thing you want is to parse a proprietary format and convert coordinates. Road511's GeoJSON endpoints return standard FeatureCollections that drop directly into Leaflet, Mapbox, MapLibre, ArcGIS, QGIS, or any GeoJSON-compatible tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  GeoJSON Endpoints
&lt;/h2&gt;

&lt;p&gt;Every list endpoint in Road511 has a &lt;code&gt;/geojson&lt;/code&gt; variant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/events/geojson
GET /api/v1/features/geojson?type=cameras
GET /api/v1/truck/corridor/geojson
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All return &lt;code&gt;Content-Type: application/geo+json&lt;/code&gt; with a standard FeatureCollection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bounding Box Query
&lt;/h2&gt;

&lt;p&gt;Show events in the current map viewport:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBounds&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getWest&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSouth&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEast&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNorth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://api.road511.com/api/v1/events/geojson?bbox=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;status=active`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geojson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geojson&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Radius Search
&lt;/h2&gt;

&lt;p&gt;Find everything within 50km of a point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/events/geojson?lat=34.05&amp;amp;lng=-118.24&amp;amp;radius_km=50&amp;amp;status=active"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multiple Feature Types
&lt;/h2&gt;

&lt;p&gt;Build a layered map with different feature types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cameras&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;weather_stations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rest_areas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`https://api.road511.com/api/v1/features/geojson?type=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;jurisdiction=CA`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geojson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geojson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;pointToLayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latlng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latlng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layerGroups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  LineString Geometry
&lt;/h2&gt;

&lt;p&gt;Not everything is a point. Construction events, truck routes, and weight restrictions include polyline geometry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feature"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LineString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mf"&gt;-87.63&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;41.88&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-87.62&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;41.89&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;-87.60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;41.90&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fhwa-il-staa-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"feature_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"truck_routes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I-90 STAA National Network"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Spatial Queries Under the Hood
&lt;/h2&gt;

&lt;p&gt;Road511 uses PostGIS for all spatial operations. Every feature has a geometry column with a spatial index. Bounding box queries use &lt;code&gt;ST_Intersects&lt;/code&gt;, radius queries use &lt;code&gt;ST_DWithin&lt;/code&gt; on geography. The truck corridor endpoint builds a buffer geometry with &lt;code&gt;ST_Buffer&lt;/code&gt; and intersects it against all truck-related features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — built entirely on Road511's GeoJSON endpoints&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.road511.com" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.road511.com" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Road511/road511-examples" rel="noopener noreferrer"&gt;Code examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>geojson</category>
    </item>
    <item>
      <title>Real-Time DMS Sign Messages API: What's on the Highway Signs Right Now</title>
      <dc:creator>Roman Kotenko</dc:creator>
      <pubDate>Thu, 30 Apr 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/road511/real-time-dms-sign-messages-api-whats-on-the-highway-signs-right-now-462g</link>
      <guid>https://dev.to/road511/real-time-dms-sign-messages-api-whats-on-the-highway-signs-right-now-462g</guid>
      <description>&lt;p&gt;Dynamic Message Signs (DMS) — those big electronic boards over highways — display real-time traveler information: incident warnings, travel times, amber alerts, construction notices.&lt;/p&gt;

&lt;p&gt;Road511 captures the current message from every DMS sign across 30+ US states and Canadian provinces. Nobody else aggregates this data.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Get
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.road511.com/api/v1/features?type=signs&amp;amp;jurisdiction=GA&amp;amp;limit=10"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ga-sign-i85-042"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jurisdiction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I-85 NB at Clairmont Rd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"latitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;33.8103&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"longitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-84.3179&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CRASH AHEAD / I-85 NB AT SR 42 / RIGHT LANE BLOCKED"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;message&lt;/code&gt; field contains the exact text currently displayed on the sign, including line breaks encoded as &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;DMS messages are a leading indicator. The sign updates within minutes of an incident, often before the event appears in some 511 feeds. Cross-referencing sign messages with traffic events gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster incident detection&lt;/strong&gt; — sign changes before event feeds update&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOT response time analysis&lt;/strong&gt; — how long after an incident before a sign is posted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traveler information coverage&lt;/strong&gt; — which corridors have DMS signs, which are blind spots&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Historical message patterns&lt;/strong&gt; — Road511 now tracks message changes over time&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Navigation apps&lt;/strong&gt; — show upcoming sign messages on the route&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic management&lt;/strong&gt; — monitor DMS coverage and response times&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research&lt;/strong&gt; — analyze traveler information effectiveness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emergency alerting&lt;/strong&gt; — detect amber/silver alerts from sign text&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://map.road511.com" rel="noopener noreferrer"&gt;Live map&lt;/a&gt; — click any sign marker to see current message&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.road511.com" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://portal.road511.com" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
