AWS Builder Center
Why Your Map Shows a Blue Route While the Underpass Is a Pond

Why Your Map Shows a Blue Route While the Underpass Is a Pond

I built DelhiJalNav — a PWA that uses on-device computer vision and AWS Lambda + DynamoDB to detect flooded roads and potholes in Delhi, rerouting riders before they reach the water. Here's what broke and what I learned.

A builder's account of routing Delhi's monsoon roads with edge ML and AWS.



The Ride That Should Have Ended Differently

It's mid-July, 7:45 AM. Rahul, a Swiggy delivery rider, is running three orders behind after a surprise downpour. His navigation
a clean blue route line, the kind that means "fastest, unobstructed" — shows Minto Bridge underpass as clear. Twelve minutes to the next drop, good timing. He accelerates down the incline.
By the time the front wheel hits the water, there's nothing to decide. The drainage has been overflowing for forty minutes. The map just hasn't noticed yet.
He wasn't the first that morning, and he won't be the last.



What This Post Is About

What I built: DelhiJalNav — a PWA that reroutes two-wheeler riders around Delhi's waterlogged roads and potholes using on-device computer vision and a live AWS hazard layer.The one insight: Traffic speed is a lagging indicator of road hazards. By the time your map slows Route A down, someone has already stalled out in 40 cm of water.AWS services used: API Gateway · Lambda · DynamoDB (with TTL) · Amazon Transcribe · Amazon Bedrock · (CloudWatch for observability)What works today: Routing engine (live), pothole detection (live), water segmentation (tested on device), AWS ingestion pipeline (live), voice report parsing (tested).


Here's what the app actually looks like this

The left panel plans a two-wheeler route from JLN Marg to Connaught Place. The center Leaflet map displays DelhiJalNav's green safe route. Top-right: the dashcam feed with live edge-ML overlays — green lines tracking lane geometry and water detection running at 8 fps. Bottom-right: the Google Maps navigation view showing the standard blue route. Our green safe route vs their blue — that contrast is the whole point.



Why Speed-Based Routing Can't See Water

Here's the timeline of a typical Delhi monsoon event:
DelhiJalNav tries to act at T+20, not T+70.
Standard routing optimizes for time — minimum sum of  length / speed  across all edges. When water starts pooling, cars keep entering the flooded sector because traffic speed hasn't changed yet. There's no sensor registering "water here." The map sees a fast, empty road and recommends it.
For a four-wheeled SUV, 5 inches of standing water is mostly an annoyance. For a delivery rider on a 100cc scooter, that same depth is an engine hydro-lock, a day's wages lost, and possibly a trip to the hospital. The routing problem isn't just about time. It's about what the road is actually made of right now.
The idea behind DelhiJalNav is simple: treat physical road conditions as a first-class routing constraint, not an afterthought inferred from traffic slowdowns.



The Cost Function (Plain English First, Math Second)

Every road segment has a base cost (its length). DelhiJalNav multiplies that cost by a penalty factor that grows with the severity of what's on the road.
In plain terms: a dry, smooth road has cost ≈ its length. A pothole-heavy road costs more. A waterlogged road costs much more. A road with serious flooding becomes impassable — infinite cost — and the routing algorithm drops it from the graph entirely.
The actual formula, in a code block to avoid rendering issues:
[AUTHOR: What are the actual values of W_pothole and W_water? Please supply them.]
The state machine matters here. A single report doesn't trigger a hard block. There's a two-stage process:
  • PENDING: One report arrives. The segment gets a soft penalty — still routable, just costlier. A single wrong report won't close Connaught Place Road.
  • CONFIRMED: A second independent rider corroborates it, or on-device confidence exceeds 0.85 with coverage above 40%. Now the hard block applies. Infinity cost. The edge is severed.
The distinction is important because "I'm not fully sure this road is flooded" and "two independent riders both saw standing water" are very different levels of evidence.
An illustrative example (not a confirmed real incident — [AUTHOR: verify or replace with a real commute you observed]):
  • Route A (via underpass): 4.8 km, estimated 12 min, water coverage 52% → CONFIRMED block, infinite cost
  • Route B (via Daryaganj / Ring Road): 7.1 km, estimated 19 min, dry tarmac, minor isolated potholes → Routable
DelhiJalNav picks Route B. You're 7 minutes slower. Your bike is undamaged.



Seeing the Road: Why Inference Runs on Your Phone

The obvious design would be: stream video to a cloud server, run ML there, send back results. I didn't do that, for three reasons.
First, connectivity. Delhi's cellular networks choke during heavy storms — exactly when you need hazard data most. A cloud-inference system fails precisely when it matters.
Second, bandwidth and cost. Streaming 1080p from thousands of vehicles would be expensive and slow.
Third, privacy. Street-level video from a moving vehicle picks up a lot more than potholes. License plates, faces, shop fronts. I'm not comfortable collecting that, and I don't think commuters should accept it as the price of a safer route.
So the phone does the ML. Only the result — a small JSON payload — gets sent to AWS.

The Two Models

Pothole detection (YOLOv8n, quantized): Trained primarily on the RDD2022 India subset (CC BY-SA 4.0 . Classifies detections into three severity tiers. Runs in ONNX Runtime Web, which means no app install needed — it works in a browser tab.
Water segmentation (SegFormer-b0): Classifies each pixel of road as dry tarmac, surface puddles, or deep water. Outputs a coverage percentage for the road region in frame.

A note on "depth" claims

The draft described pothole tiers using centimeter depth figures ("5–10 cm," ">10 cm"). A monocular phone camera cannot measure depth directly. What these tiers actually use is bounding box size and aspect ratio as a proxy  a larger bounding box covering more of the road plane suggests a more severe pothole. The centimeter labels are calibration heuristics, not physical measurements. I should have been clearer about that.
Similarly, the 40% water coverage threshold that triggers a hard block is based on what fraction of the visible road region is classified as water not an absolute depth. [AUTHOR: Explain how you calibrated the 15% and 40% thresholds. Was it manual annotation, a specific test drive, a grid search? This is worth explaining.]
The AWS Backbone
When a detection fires, here's what happens:
For voice and text reports, there's an additional step before Bedrock:
The Hinglish example from the draft is a good illustration of why this pipeline exists:
"Bhai, Minto Bridge ke neeche 3 foot paani bhara hai, gaadi mat nikaalna." →  { "location": "Minto Bridge Underpass", "type": "WATERLOGGING", "confidence": 0.95 } 

The TTL lesson I learned the hard way

The draft said "DynamoDB TTL automatically purges stale entries at expiry." That's mostly true, but with an important asterisk: DynamoDB TTL deletion is best-effort and can lag by up to ~48 hours after the TTL timestamp passes. AWS does not guarantee immediate deletion.
This means you can't rely on TTL to actually block stale hazards from appearing in queries. The right pattern — which DelhiJalNav now uses — is to filter by  valid_until  at read time in the Lambda query function. TTL is just garbage collection; your application logic can't depend on it for correctness.
Here's the relevant Lambda snippet:
I found this out when a test hazard I'd set to expire didn't disappear from queries for several hours after its TTL. Took me longer than I'd like to admit to realize the issue was in my read logic, not the TTL configuration.

A sample DynamoDB item

(Illustrative — field names and values are representative of the schema, not a confirmed live record)

Cost and scale

The following cost estimates use AWS public pricing and assumed workload sizes. Treat them as order-of-magnitude guides, not guarantees.
The hot geohash problem: During a storm, every rider converges on the same two or three underpasses. That means every write goes to the same DynamoDB partition key (the geohash of the affected segment). This is a classic hot partition problem. Mitigation options I'm considering: write sharding with a suffix (geohash + random 0–9), batching reports through an SQS queue with Lambda as consumer, or using DynamoDB adaptive capacity (which handles moderate bursts automatically in on-demand mode).

Security

  • IAM roles scoped to minimum required permissions per Lambda function
  • API Gateway rate throttling (default: 1,000 requests/sec, configurable)
  • AWS WAF on the API Gateway to filter malformed or abusive requests
  • Input validation in Lambda before any DynamoDB write (geofence check, confidence bounds, schema validation)
  • No raw video, no device IMEI, no license plate data stored — only the numeric hazard payload



What I've Actually Tested (This Is a Smoke Test, Not a Benchmark)

I want to be honest about the evidence base here.
13 scenes is a smoke test. It tells me the model isn't completely broken and can distinguish a flooded road from a dry one in decent light. It does not tell me the production false-positive rate, and I haven't claimed it does.
Per-class precision/recall/mAP figures aren't available yet for my fine-tuned weights. When they are, I'll add them. Until then, I'm not going to dress up 13 scenes as a confusion matrix.



Things That Broke

The TTL surprise. I described this above. I assumed TTL deletion was synchronous. It isn't. Cost: an afternoon of confused debugging and a rewrite of the query logic.
Wiper sweeps. At 30fps, a wiper blade crossing the frame looks, to the segmentation model, briefly like a water surface covering 30–40% of the road. We now apply a simple temporal filter — a hazard must persist across at least 3 consecutive frames before firing.
Night glare. Oncoming headlights reflecting off wet pavement spike the water-class confidence substantially. Raising the confidence threshold to 0.75 during detected low-light conditions helped, but nighttime performance is still the weakest point in the system. Do you have numbers for this? Even qualitative — "significantly more false positives" — is useful.
Mount angle drift. If the phone slides in its mount and starts capturing more sky than road, pothole detection degrades quietly — you don't get errors, you just get missed detections. The calibration reticle in the PWA addresses this, but it only helps if riders actually use it.
[AUTHOR: Add any other real failures you encountered. The more specific, the better. "I tried X, it failed because Y, I fixed it with Z" is the most credible content in any technical post.]



Trust, Corroboration, and Honest Limits of Privacy

The corroboration system works roughly as follows:
  1. First report →  PENDING  state, soft penalty on the edge
  1. Second independent report, or high-confidence single detection (>0.85, coverage >40%) →  CONFIRMED  state, hard block applied
  1. A rider passes through and detects dry conditions → eviction vote registered, hazard cleared
What the current anti-abuse setup does not do:
The draft mentioned "rotating daily hash" as the device identity mechanism. I want to be clear about its limits. A daily cryptographic hash prevents persistent tracking but doesn't provide device attestation — a motivated bad actor could submit multiple reports from the same device by rotating their hash manually. GPS coordinates can also be spoofed. There's no silver bullet here at the current stage of the project.
What I do have: geofence sanity checks (reports outside Delhi NCR are rejected), per-device rate limiting, and corroboration requirements before hard blocks apply. What I plan to add: reputation weighting (how consistent has this device token been historically?), and potentially integration with AWS WAF for pattern-based abuse detection.
This is a real limitation of a crowdsourced system built by one person. I think it's worth naming.
GPS trails: Even anonymized GPS traces can re-identify individuals over time (this is well-documented in the research literature). DelhiJalNav doesn't store GPS trails — it only stores the detected hazard point. But if the system scales, this is worth revisiting with a privacy engineer.



Who This Is For (and What It Could Mean Beyond Navigation)

The obvious user is a two-wheeler commuter trying to get home without drowning their engine. But the data has a second life.
Every confirmed hazard report is essentially a data point saying: this drain failed, at this location, at this time. Aggregated over a monsoon season, that's a map of where MCD drainage infrastructure is underperforming. A municipal dashboard consuming the same DynamoDB hazard index could surface which underpasses flood first, which drains clear fastest, and where preventive pumping would have the highest impact.
I haven't built that dashboard yet. But the data model supports it without modification.
A note on responsible use:
This system is a navigation aid, not a guarantee. A false negative — missing a flooded road — could send a rider into danger. A false positive — blocking a road that's actually clear — wastes time and erodes trust. I've tried to build corroboration in to reduce both failure modes, but no computer vision system running on a mobile browser is infallible.
If you use DelhiJalNav, use your judgment. If the app says take Route B but you can see Route A is clear, trust your eyes.
The system should also never be used to justify legal claims about road conditions. It is not a certified sensor.



What's Next

  1. IMD rainfall integration: Pulling India Meteorological Department rainfall data to pre-emptively flag historically flood-prone segments during active rain events — before any rider gets there.
  1. MCD pump telemetry: If drain pump status is available via a public API, integrating it would let the system predict flood clearance timing rather than just detecting current state.
  1. Mapillary-based baseline: Using Mapillary's open street-level imagery (instead of Google Earth, which likely conflicts with its Terms of Service for this use case [AUTHOR: verify and confirm your data sourcing]) to build a baseline pothole map for Delhi before any live detection.
  1. Offline mode: Store the last-known hazard graph locally on the device so riders in poor connectivity can still get rerouted based on the most recently fetched data.
  1. Pilot with a delivery partner: One delivery fleet doing a structured two-week trial would give me real-world recall data, real false-positive feedback, and — honestly — a much better sense of whether this is useful in practice.



Back at Minto Bridge

It's mid-July, 7:45 AM. Same rain, same delivery rider, same underpass.
This time, his phone shows the reroute. Not because traffic has slowed — it hasn't. Because three riders ahead of him, without knowing each other, all detected the same pooling water and sent a 240-byte payload to the same Lambda function. The hazard is  CONFIRMED . Route A is blocked. Route B adds seven minutes.
He takes Route B. He's still late. But he makes all three deliveries.
That's the whole idea.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article