--- title: Build a Truck-Safe Route Planner | Plaza Docs description: A Python CLI that plans routes between cities, scans for low bridges and weight restrictions, and finds truck stops along the way. --- Regular routing apps send trucks under low bridges. This CLI plans a route between two cities, then scans the corridor for low clearance bridges, weight-restricted roads, and truck stops — a safety audit layer on top of routing. ## What you’ll use - **Geocoding** to resolve city names to coordinates - **Routing** to plan the driving route - **PlazaQL** to scan for bridge clearances, weight limits, and truck stops along the route corridor ## Key code After getting a route, we build a bounding box around it with a 5km buffer on each side and run three PlazaQL scans against that corridor. The low bridge scan queries everything with a `maxheight` tag: ``` query = f'$$ = search(maxheight: *).bbox({bbox[0]:.6f}, {bbox[1]:.6f}, {bbox[2]:.6f}, {bbox[3]:.6f});' fc = plaza.query(query) ``` Then we parse the heights and keep only bridges lower than the truck. OSM’s `maxheight` tag is a mess — people tag it every way imaginable, so the parser handles metric, imperial, and edge cases: ``` # Handles: "4.5", "4.5 m", "14'6\"", "15'", "none", "default" _IMPERIAL_RE = re.compile(r"""(\d+)'(?:\s*(\d+)(?:"|''|in)?)?""") def parse_height(value): m = _IMPERIAL_RE.match(value) if m: feet = int(m.group(1)) inches = int(m.group(2)) if m.group(2) else 0 return feet * 0.3048 + inches * 0.0254 # ... fallback to metric ``` If your parser chokes on `14'6"` and skips it, you miss a low bridge. The imperial format is common in the US and UK. Truck stops use a clean two-tag filter — fuel stations tagged as HGV-friendly: ``` query = f'$$ = search(amenity: "fuel", hgv: "yes").bbox({bbox[0]:.6f}, {bbox[1]:.6f}, {bbox[2]:.6f}, {bbox[3]:.6f});' ``` We also pull amenity tags like `shower=yes`, `restaurant=yes`, and `hgv:parking=yes` to show what services each stop has. ## How it works 1. **Geocode** both cities, then **route** between them. 2. **Build a bounding box** around the route coordinates with a 5km buffer, giving a corridor roughly 10km wide. The buffer matters for curved routes — a tight box around a straight line misses hazards where the road bends. 3. **Three PlazaQL scans** against the corridor: `maxheight` for low bridges, `maxweight` for weight restrictions, and `amenity=fuel + hgv=yes` for truck stops. 4. **Parse and filter** the results. Low bridges below the truck’s height get flagged with how much clearance is missing. 5. **Print a summary** with hazard counts and worst-case clearance. ## Full source The complete working app is at [github.com/plazafyi/example-apps/truck-router](https://github.com/plazafyi/example-apps/tree/main/truck-router). It’s a Python CLI with separate modules for the Plaza API client, corridor scanning, and height/weight parsing. Terminal window ``` git clone https://github.com/plazafyi/example-apps cd example-apps/truck-router uv sync export PLAZA_API_KEY=your-key-here python main.py "Chicago, IL" "Detroit, MI" 4.1 ``` ## Variations to try **Add height to the route request.** If Plaza’s routing engine supports vehicle profiles in the future, you could pass `vehicle_height` directly and get a route that avoids low bridges automatically. For now, the scan-and-warn approach works. **Score the route.** Assign a danger score based on how many hazards were found. 0 bridges = green, 1-2 = yellow, 3+ = red. **Export as GPX.** Truckers use GPS units that import GPX files. Convert the route geometry to GPX format and the hazard points as waypoints. The driver loads it into their GPS and sees warnings as they approach each bridge. **Multi-stop trips.** If the truck has multiple delivery stops, geocode each one, use the optimize endpoint to find the best visit order, then scan each leg independently.