AWS Builder Center

Building FloodSense AI: One Map for Floods, Heatwaves, Droughts and Leaking Pipes, Powered by AWS

TL;DR: FloodSense AI shows flood, heatwave, drought and water-leak risk for any city on a single map. It explains each risk in 17 languages with Amazon Bedrock, warns people by email or SMS through Amazon SNS before things get worse, and turns citizen reports of leaking pipes into a repair list ranked by litres wasted per day. The whole stack is serverless, defined in one SAM template, and deploys with one command.

1. The problem we wanted to solve

Cities now face floods, heatwaves and water shortage in the same year, sometimes in the same month. Three things kept bothering us:
  1. Warnings are too broad. A regional alert says it will rain heavily. It does not say this underpass will flood.
  1. Warnings are in one language. Many of the people most exposed to heat and floods do not read the language the alert is written in.
  1. Water is wasted while others run dry. Leaking taps, tanks and burst pipes lose thousands of litres a day, often unreported, even during droughts.
We asked: can one lightweight tool give a resident a street-level view of all of these risks, tell them what to do in their own language, warn them proactively, and also help water crews decide which leak to fix first?
That became FloodSense AI.

2. What FloodSense AI does

Layer
What it does
Flood
Risk map and hotspots from rain forecast plus terrain, and a safer route that avoids estimated high-risk roads
Heatwave
Risk map using feels-like temperature, humidity, days above 40 C, and nearby green and water cover
Drought
A city-level index from the last 91 days of rain compared with the 10-year normal, dry-spell length and evaporation
Water leaks
Citizens report leaks (with an optional photo); each is ranked by estimated litres lost per day, boosted when the area is also dry
Safe places
Nearest hospitals, shelters and higher ground, with distance and direction
Ask FloodSense
A guarded Q&A box that answers only from the live data for your place
AI briefing
One short briefing across all hazards, highest risk first, in your language
Early-warning alerts
Email or SMS, per area, only when risk rises, in each subscriber's language
Resilience score
One 0-100 number combining flood, heat and drought
Every hazard also has a scenario slider (storm millimetres, heatwave temperature, percent of normal rain) so we can demo a monsoon downpour or a 48 C heatwave on a perfectly sunny day. Scenario alerts are clearly labelled  [SIMULATED] .
It works for any city, because it only uses open global data: Open-Meteo for weather, Copernicus DEM for elevation, and OpenStreetMap for parks, roads, hospitals and shelters. No API keys needed.

3. Architecture at a glance

Plain text
Browser (React + Leaflet)
| \
| HTTPS \ presigned upload (photos)
v v
CloudFront --> S3 (website) S3 (private photo bucket, 30-day expiry)
| |
v v
Lambda Function URL --> Lambda (FastAPI + Mangum, container image)
|-- Amazon Bedrock (Nova Lite) + Guardrails
|-- Amazon DynamoDB (leaks, watch list, cache, rate limits)
|-- Amazon SNS (email + SMS, filter policies)
|-- Amazon Rekognition (photo checks)
|-- Open-Meteo / Copernicus DEM / OpenStreetMap (free data)

Amazon EventBridge (every 3 hours) --> same Lambda --> early-warning monitor --> SNS
The whole thing is one  template.yaml  deployed with AWS SAM. There are no servers, no clusters and nothing running when nobody is using it.

4. Three design principles that shaped everything

Principle 1: Rules decide, AI explains

We did not want a black box deciding who gets warned. Every risk score comes from a transparent formula you can read in the code. Amazon Bedrock only turns the numbers into plain-language advice. If Bedrock is off, slow or unreachable, the app falls back to fixed rule-based tips, so the endpoint never fails because of AI.

Principle 2: Graceful degradation everywhere

Every AWS feature has a local or fallback mode. Without  LEAKS_TABLE , leak reports live in memory. Without  ALERTS_TOPIC_ARN , alerts are simulated and kept in memory. Without  PHOTO_BUCKET , photo upload is switched off. Without Bedrock, rule-based advice is used. This made local development painless and made the app far more robust in a live demo.

Principle 3: Be honest about uncertainty

Every answer says it is an estimate, not an official forecast. Flood risk has no drainage data. Heat uses a green-cover proxy, not measured surface temperature. Litres lost per leak type are planning assumptions. We wrote these limitations into the product and the README instead of hiding them.

5. Deep dive: the risk engines

Flood

Flood risk combines how hard it is raining with how likely water is to collect at that spot. Rain comes from Open-Meteo (24-hour total and peak hourly rate). Terrain comes from the 90 m Copernicus DEM: how many metres lower the spot is than its surroundings, and how flat the ground is. Low, flat ground under heavy rain scores high.
For routing we use OSMnx and NetworkX. We compute two paths on the real road graph: the plain shortest path, and one where each road segment's length is multiplied by a risk penalty (a fully high-risk road feels 9x longer). The app shows both and tells you how much extra distance the safer route costs.

Heatwave

Heat risk uses feels-like temperature (a heat index from air temperature and humidity), the number of days at 40 C or above in the next 7, and a green-and-water-cover proxy from OpenStreetMap parks, forests and water. Spots far from green cover score higher, which is a simple stand-in for the urban heat island effect.

Drought

Drought is regional, so we compute one index per city:
Plain text
score = 0.5 * deficit + 0.25 * dry_spell + 0.25 * evaporative_demand
  • deficit: the last 91 days of rain versus the average for the same 91 days over the previous 10 years (40 percent of normal or less counts as the maximum).
  • dry_spell: consecutive days with under 1 mm of rain (30 or more days is the maximum).
  • evaporative demand: forecast evapotranspiration over the next 7 days.
We scale down deficit and dry spell when the normal rain for that window is tiny, because no rain in a dry season is expected and is not a drought. This avoided obviously wrong results for desert cities.

Leaks: turning reports into priorities

Each leak type has a rough planning assumption for water lost per day: a drip about 20 L, a steady stream 2,000 L, an overflowing tank 1,000 L and a burst pipe 50,000 L. Priority uses a log scale so a burst pipe outranks a drip without drowning it out, and rises when the area is also under drought stress:
Plain text
loss = log10(litres_per_day) / 5
score = loss * (0.6 + 0.4 * drought_score)
When a crew marks a leak fixed, the app adds up the litres per day it no longer wastes and shows them as tanker trips (10,000 L each) and people-days of water (135 L per person per day, a common Indian urban planning norm). That impact counter turns a pile of reports into a story you can show a city official.

6. How we used each AWS service

AWS Lambda, Function URLs and Mangum

The backend is a FastAPI app wrapped with Mangum and packaged as a Lambda container image. A container was the only practical choice because OSMnx, NetworkX and NumPy are too large for a zip package. The same image runs the web API and the scheduled monitor: the handler checks whether the event came from EventBridge and routes it:
Python
def handler(event, context):
if isinstance(event, dict) and event.get('source') == 'aws.events':
return asyncio.run(monitor.run())
return _api(event, context)
A Lambda Function URL gives a public HTTPS endpoint with no API Gateway to configure. One gotcha: on accounts created after October 2025, a public Function URL needs both  lambda:InvokeFunctionUrl  and  lambda:InvokeFunction , otherwise you get a 403. Our template adds an explicit  AWS::Lambda::Permission  with  InvokedViaFunctionUrl: true  to handle it.
Because the heavy routing library is imported lazily, only when  /safe-route  is called, cold starts for every other endpoint stay light.

Amazon Bedrock (Nova Lite) with the Converse API

We use Amazon Nova Lite through the Bedrock Converse API for four jobs: hazard advisories, a combined briefing, the grounded Q&A, and translating alerts. It is fast and inexpensive enough to call on demand.
The prompts are strict. For advice, the model is given only the computed facts (rain in mm, elevation, depression below surroundings, factor scores) and told to use only those facts, say it is an estimate, and write plain text in the chosen language. For Q&A, the question is wrapped so it is treated as a question and never as instructions, and the model must say so when the facts do not answer it instead of inventing numbers.
Every Bedrock call has a hard timeout and a fallback, so a slow model never blocks a user.

Amazon Bedrock Guardrails

The "Ask FloodSense" box accepts free text, which is where abuse happens. We attached a Guardrail with content filters for hate, violence, insults, misconduct and prompt attacks, plus PII anonymisation for emails and phone numbers. The guardrail ID is passed into the same Converse call, so no extra code path was needed. Blocked questions get a friendly message that the assistant can only help with flood, heat, drought and water topics. The template has an  EnableGuardrail  parameter so the stack still deploys in accounts where the Guardrail resource is not available.

Amazon DynamoDB: four jobs in one service

We used DynamoDB (pay-per-request, encrypted, with TTL) for four different things:
  1. Leak reports. A  CellIndex  global secondary index keys each report by a roughly 5 km grid cell. Loading a map view queries only the cells that view touches, instead of scanning the table.
  1. Alert watch list. One item per ~11 km area, storing the subscriber languages and the last level and time we alerted. It auto-expires after 90 days.
  1. Shared cache. Weather, drought and OpenStreetMap answers are cached in DynamoDB, so a cold start or a second Lambda instance reuses them instead of hitting the free upstream APIs again.
  1. Rate limiting. Per-IP and per-contact counters use an atomic  ADD  update in fixed time windows, so limits hold across all Lambda instances. The limiter fails open, so a DynamoDB hiccup never takes the app down.
One table design per purpose kept each access pattern simple, and TTL meant we never wrote cleanup code.

Amazon SNS: language-aware alerts with filter policies

This was our favourite part. A person subscribes an email or phone number to an area, a minimum risk level and a language. We create an SNS subscription with a filter policy on message attributes:
Plain text
FilterPolicy: { area: [key], level: [levels at or above their minimum], lang: [their language] }
FilterPolicyScope: MessageAttributes
When the monitor finds elevated risk, it publishes one message per language, tagged with  area ,  level  and  lang  attributes. SNS routes each message to exactly the right subscribers. We wrote no subscriber-matching code at all.
SNS also lets one publish carry different bodies per protocol, so email gets the full alert (risk by hazard, nearby hospitals, advice) and SMS gets a short text of about 250 characters, from the same call using  MessageStructure: json .

Amazon EventBridge: the proactive part

A single  rate(3 hours)  schedule invokes the same Lambda. The monitor loops over the watch list, assesses flood, heat and drought for each area, and decides whether to send:
Python
def should_send(level, last_level, last_sent):
if LEVELS.index(level) < 1: # never alert on low risk
return False
return LEVELS.index(level) > LEVELS.index(last_level) or time.time() - last_sent > RESEND_AFTER_S
So people are alerted when risk rises, or once a day if it stays elevated, but never spammed every three hours. One failing area never stops the others, and a failing data source only drops that hazard from the alert.

Amazon Rekognition and Amazon S3: photo evidence for leak reports

Citizen reports are more useful with a photo, but photos are a moderation and cost risk. Our flow:
  1. The browser asks the API for a presigned POST. The API returns a one-time upload form limited to one JPEG, 4 MB, valid for 5 minutes.
  1. The browser resizes the photo on the device (max 1280 px) and uploads it directly to a private S3 bucket, so the image never passes through Lambda.
  1. When the report is saved, Lambda calls Rekognition.  DetectModerationLabels  blocks and deletes inappropriate images.  DetectLabels  looks for water and a pipe, tap or hose.
  1. The report gets a status: verified (water and pipe), partial, or unclear. It is a helper for reviewers, not proof, and we say so.
A lifecycle rule deletes every photo after 30 days. Viewing a photo uses a 10-minute presigned GET link, generated locally with no network call.

Amazon CloudFront and S3: hosting the frontend

The React app is built with Vite, uploaded to a private S3 bucket and served through CloudFront using Origin Access Control, so the bucket itself is never public. A custom error response maps 403 to  index.html  for single-page-app routing, and a managed response-headers policy adds security headers.

AWS SAM and CloudFormation: one command to deploy everything

 template.yaml  defines the three DynamoDB tables, SNS topic, both S3 buckets, the Guardrail, the Lambda function with least-privilege policies, the EventBridge schedule, CloudFront and its permissions.  deploy.sh  runs  sam build ,  sam deploy , builds the frontend pointing at the new API, syncs it to S3, invalidates CloudFront and prints the live URL. A judge can recreate the whole project from a clean account with  ./deploy.sh .

7. Security and responsible AI

  • Least privilege: the Lambda role only gets the specific tables, topic, bucket and Rekognition actions it needs.
  • Abuse limits: per-IP limits on costly endpoints (Ask, routing, photo upload, leak posting), at most 3 sign-ups per contact per day (contacts are hashed, never stored), and one alert per area every 2 minutes.
  • Input limits: bounding boxes are validated and size-capped so one request cannot trigger huge map queries.
  • Privacy: subscriptions expire after 90 days, leak data and photos after 30 days.
  • Honest labelling: simulated alerts are tagged, and every answer carries a disclaimer that it is not an official warning.
  • Grounded AI: the assistant may only use supplied facts, treats the question as data and sits behind Guardrails.
We also noted what a production version would add: Cognito for staff, AWS WAF in front of the API, and reserved concurrency as a cost ceiling.

8. Challenges and what we learned

The 403 on Function URLs. Everything deployed but the API returned 403 Forbidden. The cause was the newer requirement for the extra  lambda:InvokeFunction  permission. We fixed it in the template and now document it.
S3 presigned uploads and 307 redirects. Browser uploads to a new bucket hit redirects on the global endpoint. Forcing the regional endpoint with signature v4 fixed it.
SMS is not just a switch. New accounts start in the SNS SMS sandbox, so only verified numbers receive messages, and in India real delivery needs DLT registration. We designed email as the dependable demo channel and SMS as the extension.
Free APIs have limits too. When we also deployed the app to a shared host, Open-Meteo returned 429 Too Many Requests because many apps share the same outbound IP. We added a shared client that retries with backoff, caches answers, limits parallel calls and falls back to the last good answer. That also cut calls on AWS.
Rate limiting behind a proxy. Behind a platform proxy every visitor looks like the same IP, so we read the real address from  X-Forwarded-For .
Design for fallback first. Building the local and fallback mode first meant the app never fully broke, and development was fast without any AWS credentials.

9. Try it yourself

Bash
# Run locally
cd backend && pip install -r requirements.txt
USE_BEDROCK=0 uvicorn main:app --reload
cd frontend && cp .env.example .env && npm install && npm run dev

# Deploy the full AWS stack
./deploy.sh
In Bedrock (us-east-1), make sure Amazon Nova Lite is available to your account first.
A 3-minute demo path:
  1. Pick Mumbai, Flood tab, slide to Extreme, and click a hotspot for an AI advisory.
  1. Find safe places nearby, then plan a safer route.
  1. Switch to Jaipur, Heatwave tab, Severe 48 C.
  1. Drought tab, Severe 30 percent.
  1. Leaks tab: load demo reports, report one with a photo, mark one fixed and watch Impact so far change.
  1. Subscribe your email in Hindi, move the slider to Extreme and press Send an alert now.
  1. Choose Hindi and press Brief me.

10. What is next

  • Validate risk scores against known waterlogging spots and official heat records.
  • Use satellite land-surface temperature for heat instead of a green-cover proxy.
  • Add reservoir and groundwater levels to the drought index.
  • WhatsApp alerts, since many people prefer them to SMS.
  • Cache OSM road graphs in S3 for faster routing.
  • Add staff sign-in with Amazon Cognito and a dashboard for repair crews.

11. Final thoughts

The best surprise was how little glue code AWS needed. SNS filter policies replaced our subscriber-matching logic, DynamoDB TTL replaced cleanup jobs, Bedrock Guardrails replaced a custom safety filter, and EventBridge plus one Lambda replaced a scheduler. That left our time for the part that matters: making climate risk understandable to the people who face it.
Tags: AWS, Amazon Bedrock, Lambda, DynamoDB, SNS, EventBridge, Rekognition, Climate Tech, Serverless, Hackathon
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