AWS Builder Center
PARVAT: From a Flood Photo to a Safer Route, Built on AWS

PARVAT: From a Flood Photo to a Safer Route, Built on AWS

PARVAT turns citizen photos of flooded streets into live risk maps. It combines AI depth assessment, rainfall and terrain data, and serverless AWS services to help people choose safer routes and help authorities decide where to send help first.

WeMakeDevs x AWS Environmental Hacks | By Hardik Khanduja, with Kamal, Reetu and Pranad

TLDR

PARVAT is a flood-monitoring platform. A citizen photographs a waterlogged street, AI estimates the water depth and whether the road is passable, and a live map shows the report. A risk engine then combines citizen reports with rainfall and terrain signals to rank which areas need help first. We built it with Amazon S3, API Gateway, Lambda, ECR, App Runner, CloudFront, IAM and Terraform, plus an AI assessment workflow.

The problem: nobody knows which road is passable

During heavy rain, the question is rarely whether it is raining. It is whether the road ahead is safe to cross. A forecast tells you how much rain is coming. It does not tell a commuter that the underpass two kilometres ahead is waist-deep, and it does not tell a local authority which of ten reported streets should get a pump first. In hill regions like Himachal Pradesh, a few blocked roads can cut off an entire route.
The information already exists. It sits in people's phones, as photos taken from the edge of a flooded street. PARVAT's idea is simple: turn those photos into structured, location-tagged data, and turn that data into decisions.

What PARVAT does

  • Citizens report a flooded road with a photo and their location, and get a plain-language reading of the water level back.
  • Commuters and residents see a live map of reports and risk by district, so they can choose a safer route.
  • Local authorities get a ranked priority list that answers one question: where do we send help first? Each hotspot carries a suggested action, clearly labelled as advisory.
[IMAGE: the report flow and the AI result with the depth gauge]

Architecture

Plain text
Citizen phone (React + Vite)
|
v
Express application backend
(Docker -> Amazon ECR -> AWS App Runner)
|
v
Amazon API Gateway -> AWS Lambda (reports, assessment, risk)
| |
| +--> Amazon S3 (private bucket, presigned uploads)
| +--> AI image assessment (depth, passability, confidence)
| +--> Risk engine (rainfall, elevation, slope, reports)
v
Live map, hotspots and priority list
Static frontend: private S3 bucket + CloudFront (Origin Access Control)
Networking and infrastructure: Terraform (VPC, subnets, IGW, route tables), IAM
We chose serverless for the core because flood reporting is bursty. Traffic spikes exactly when it rains hardest, and nobody wants to run idle servers the rest of the year. Lambda and API Gateway scale with the storm, and S3 takes the heavy photo uploads directly so they never touch our backend.

Web app structure

PARVAT is a three-layer web app: a React and Vite frontend, a Node.js and Express application backend, and an AWS-backed Cloud API behind it. The frontend never touches AWS data services directly. Every request goes through the Express backend.

Frontend screens

  • Landing page: explains the product in seconds, with a phone-framed live map, a three-step interactive demo, who it helps, and an "Under the hood" architecture view.
  • Live map: hazards and risk across Himachal Pradesh, with district boundaries, hotspot markers coloured by risk band, filters by district and band, and tabs for Live map, Priority and Alerts.
  • Hotspot details: a risk score ring, risk band, risk factors, recent citizen reports with AI readings, and a suggested action.
  • Priority list: hotspots ranked by risk score with a district breakdown, so authorities see where to send help first.
  • Alerts: warnings with severity, affected area and time.
  • Report a hazard: a three-step flow: photo capture or upload, location, then a review step with the hazard type (flooding, landslide, waterlogging, road damage, other) and an optional description.

Frontend layers

  • Design system: shared colours, type scale and spacing, plus reusable parts such as risk badges that pair colour with an icon and a label, a score ring and a water-depth gauge.
  • API layer: one place wraps every backend call, so screens never handle raw responses, timeouts or error codes.
  • Map layer: MapLibre with OpenStreetMap tiles and a district boundary layer, with hotspot markers on top.
  • Resilience: the last good data stays on screen when a request fails, and errors appear as friendly messages with a retry.

Application backend (Node.js and Express)

The backend is stateless and runs in a Docker container. It validates input such as coordinates and description length, shapes responses for the UI, turns Cloud API failures and timeouts into clear errors, and enforces CORS. A mock mode lets the frontend run without AWS during development.
Plain text
backend/
src/
routes/ reports, hotspots, risk, alerts, health
controllers/
services/ Cloud API client and mock data
middleware/ validation, errors, rate limits
utils/
app.js
Dockerfile
package.json
  • /health: liveness check used by the deployment.
  • /api/v1/reports: create a report, list reports, read one report and poll its AI assessment, confirm or correct the AI reading.
  • /api/v1/hotspots: ranked hotspots with risk score and band.
  • /api/v1/risk: risk data for the map.
  • /api/v1/risk/assessment: location-level risk assessment from the risk engine.
  • /api/v1/alerts: alerts near a location.

Cloud API (API Gateway and Lambda)

  • POST /cloud/uploads/presign: returns a presigned S3 URL for a direct photo upload.
  • POST /cloud/reports: creates a report and returns its ID and a report token.
  • GET /cloud/reports/{id}: returns one report with its processing status and AI assessment.
  • GET /cloud/reports: lists reports by geohash, with a limit and a paging cursor.
  • PATCH /cloud/reports/{id}: lets a citizen confirm or correct the AI reading; an invalid token returns 403.

A report's journey through the app

  1. The user picks a photo and a location, and the app checks the coordinates.
  1. The app asks the backend for an upload URL, and the browser uploads the photo straight to the private S3 bucket.
  1. The backend creates the report through the Cloud API and receives a report ID and a token.
  1. The app polls the report while the AI assessment runs, from analyzing to assessed.
  1. The user sees depth, passability and confidence, and can confirm or correct the reading using the token.
  1. The map refreshes hotspots and risk, so the new report shows up for everyone.

Configuration

Configuration lives in environment variables, not in the repository: the backend port, the allowed origins for CORS, the Cloud API base URL and timeout, and the switch for mock mode. The frontend reads only the backend's base URL.

How it works

1. Reporting without exposing storage

Photos never go through our server. The app asks the backend for a presigned URL, the browser uploads the photo straight to S3, and then the app creates a report that references the uploaded file along with latitude and longitude. The bucket stays private the whole time. The tricky part was bucket permissions and CORS, which we cover below.

2. Reading the water: AI assessment

For each photo, the assessment returns a depth class (ankle, knee, waist, or unknown), a passability rating, a confidence score and a short rationale. One design rule shaped everything: honest uncertainty. If a photo has no usable reference object, the model should say unknown rather than guess. The report screen tells people to include a tyre, a kerb or a person for scale, and users can confirm or correct the result, which feeds back into the workflow.
[IMAGE: a report with the AI reading and the confirm or correct control]

3. From reports to risk

Reetu designed the mathematics, and Pranad turned it into working Python. The risk engine combines rainfall, elevation, slope, water accumulation and citizen report density into a score and a band: LOW, MODERATE, HIGH or SEVERE, along with the factors behind the number.
We validated it with golden test cases. Kamal deployed the risk assessment behind API Gateway and Lambda, and all 12 deployed golden cases matched the expectations from our JavaScript fixtures. We also tested edge cases on purpose: missing environmental data, an invalid latitude and negative rainfall. Running the same cases against two implementations caught drift early.

4. The map and the dashboard

The frontend is React and Vite with a MapLibre map and district boundaries. Hotspots appear as markers coloured by risk band. Selecting one opens a panel with the risk score, the factors behind it, recent citizen reports and a suggested action. The priority list ranks hotspots so a ward team can see where to go first. Every view carries one honest line: community-submitted and AI-assessed, advisory only, and in an emergency call 112.
[IMAGE: the priority list and hotspot details]

Deployment

  • Infrastructure as code: Kamal used Terraform to define the VPC, public and private subnets, an Internet Gateway and route tables.
  • Backend: the Express app is containerised with Docker, pushed to Amazon ECR and deployed on AWS App Runner.
  • Frontend: the production build is served from a private S3 bucket with public access blocked, behind CloudFront using Origin Access Control.
  • APIs: API Gateway and Lambda handle report submission and cloud operations.

Challenges and what we learned

CORS between the deployed frontend and backend. Requests that worked locally failed in production. Kamal diagnosed the cross-origin failures, corrected the backend CORS configuration, rebuilt and pushed the Docker image, and verified the response headers on the deployed API. Lesson: check the real response headers, not what you think the config says.
Report failures caused by missing geohash data. Some report and hotspot responses broke because location data was incomplete. We fixed the configuration and made the backend tolerant of partial data.
A running service serving an old version. With App Runner and ECR, understanding why the service was still running an older image took real investigation. We learned to verify what is actually deployed after every push.
The UI must never break. Early on, a failed request showed a raw 502 on the map. We changed it to keep the last good data on screen with a quiet retry, and to show friendly messages instead of status codes.
Watching our own demo. When we screen-recorded our app and watched it as a judge would, we found test data from the wrong city on a Himachal map, a clipped banner and jargon that citizens would never use. Record your demo early and watch it with fresh eyes.

Building with AI assistants

We used AI tools throughout: Kiro for the backend scaffold, Google Gemini for the frontend, and Claude for planning, documentation and prompt design. They sped us up, but the decisions that mattered were human: which uncertainty to show, what to call advisory, and what to leave out.

Limits and what is next

PARVAT does not replace official warnings. A single photo gives an approximate depth, not a measurement, and we have not validated the system against ground-truth flooding at scale. Next steps are calibrating the risk model against real flood spots, delivering proactive alerts to people near active hotspots, adding more regions, and building a fuller dispatch workflow for authorities.

The team

  • Hardik built the React and Vite interface, the flood-risk map, the citizen reporting flow, and the Node.js and Express application backend that connects the app to the AWS-backed APIs.
  • Kamal led cloud infrastructure, DevOps and AWS integration: Terraform, API Gateway, Lambda, S3 uploads, container deployment, CloudFront, and production debugging.
  • Reetu led AI and ML research and the mathematical modelling of flood risk, including weighting, normalisation, thresholds and calibration.
  • Pranad led the AI and ML engineering, implementing the risk model and its data preprocessing and feature engineering.

Our feedback on the AWS services

What we liked. AWS let us build PARVAT as modular pieces. S3 with presigned URLs stored photos without a public bucket. Lambda and API Gateway gave us serverless APIs with no servers to manage. ECR and App Runner made container deployment simple. IAM controlled access between services, VPC gave us a networking foundation, and Terraform let us provision it all consistently as code.
What could be better.
  • S3: secure direct browser uploads need careful bucket permissions, presigned URLs and CORS. Clearer end-to-end guidance would help new builders.
  • Lambda and API Gateway: debugging an error across the two meant checking several configurations and logs. Integrated tracing and clearer error messages would help.
  • App Runner and ECR: it was hard to see why a running service still served an older image. More explicit deployment diagnostics would help.
  • IAM: finding the minimum permissions for service-to-service calls was challenging.
  • Terraform: more examples for structuring environments and managing state would help beginners.

Try it

The code is open at  https://github.com/kamalkumar07/project\_nala , the demo video is at  https://youtu.be/x3iW5icvYv0 , and you can try the live app at  https://parvat.d3vdee76u71men.amplifyapp.com/ . If you are building something for your own community, start with one real problem and one photo. We would love to hear what you would add.
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