tlf-disaster-profiler — Known Limitations
Usage
Generate a full HTML report
from tlf_disaster_profiler import build_report, render_html
with open("alert.xml", encoding="utf-8") as f:
cap_xml_text = f.read()
report, wards_gdf = build_report(cap_xml_text)
html = render_html(report, wards_gdf)
with open("disaster_report.html", "w", encoding="utf-8") as f:
f.write(html)
Ward boundary data is found automatically from the installed tlf-geo-profiler
package — you don't need to locate or pass wards.parquet yourself. To override
it (e.g. testing against a different boundary file), pass it explicitly:
report, wards_gdf = build_report(cap_xml_text, wards_parquet_path="/custom/path/wards.parquet")
Include full building/road detail layers (opt-in, heavier)
report, wards_gdf = build_report(cap_xml_text, include_detail_layers=True)
Off by default — see the Overpass reliability notes below before turning this on for a dense urban alert.
Read the report dict directly, without generating HTML
build_report() returns plain dicts/lists, so you can use the data without ever
touching render_html():
for info in report["infos"]:
print(info["event"], "-", info["headline"])
for area in info["areas"]:
if area["status"] != "ok":
continue
print(f" {area['area_desc']}: "
f"{area['total_population_affected']:,} people, "
f"{area['total_households_affected']:,} households affected")
for ward in area["wards"]:
print(f" Ward {ward['ward_no']} ({ward['local_level']}, "
f"{ward['district']}): {ward['coverage_pct']}% covered")
Standalone functions — inspect one area without a full report
Useful in a notebook, or while debugging a new CAP source, without paying for a whole report:
from tlf_disaster_profiler.cap import parse_cap
from tlf_disaster_profiler.osm_query import (
named_amenities_query, get_building_geometries, get_road_geometries,
)
alert = parse_cap(cap_xml_text)
area = alert["infos"][0]["areas"][0] # a parsed circle/polygon area dict
amenities = named_amenities_query(area) # schools, hospitals, pharmacies, etc. — one request
for category, result in amenities.items():
if result["status"] == "ok" and result["count"]:
print(category, result["count"])
buildings = get_building_geometries(area) # full footprints — can time out, see below
roads = get_road_geometries(area) # full polylines — same caveat
Running the tests
uv sync
pytest tests/
These cover CAP parsing, osm_query's parsing/merging/bucketing logic (Overpass
itself is mocked, so no network calls), and overlap.py's geometry helpers
(against a small synthetic ward layout, not the real Nepal dataset) — see the
Overpass and boundary-data caveats below for what these tests intentionally don't
cover: live mirror behavior, real ward topology, and real population figures.
Amenity coverage
Earlier versions only queried schools and hospitals (plus a generic emergency=*
category). The report now covers, per ward, in one combined Overpass request:
schools, hospitals, health posts/clinics, pharmacies, police, fire stations,
shelters, religious sites (often used as informal gathering/relief points in
Nepal), marketplaces, drinking-water sources, and general emergency=*
infrastructure. Buildings and roads remain shape-wide counts only (see below for
why). This list reflects what's realistically useful for disaster response, not
every amenity tag OSM supports — more can be added the same way if needed.
This piece is being published as its own package; a separate layer that uses an LLM to add plain-language, Nepal-aware explanations on top of this structured output is a planned follow-up, not part of this package.
This package produces a best-effort disaster impact report from a CAP alert. It is built to degrade gracefully (nothing should crash the whole report), but "graceful" is not the same as "accurate everywhere." Read this before treating any number here as ground truth.
Boundary & admin data
- Ward boundaries come from OpenStreetMap, not an official Nepal government source.
A live Overpass check found 6,738 of 6,743 official wards (99.93%) mapped as
admin_level=9relations — the remaining ~5 wards will simply not appear in any report, with no fallback. nepalboundariespy(the existing PyPI package for this) was tested and found unusable — its README documents a ward-level API and column names that don't exist in the data it actually ships. This project bundles its own cleaned OSM export instead, built and verified once, not fetched live.- OSM boundary topology can have small gaps or slivers between adjacent wards. A point near a shared edge can occasionally resolve to the wrong ward, or to no ward at all — this is why amenities that fail to bin into any ward are shown as "unassigned" rather than silently dropped or silently misassigned.
Population figures
- Population per ward comes from Nepal's 2021 census, joined by ward through
tlf-geo-profiler's own admin-unit resolver — some wards may show population as
unknownif that name resolution fails. - Population-affected is a uniform-density estimate:
population × coverage_pct. It assumes people are spread evenly across a ward's area, which is essentially never true (a ward's population is rarely uniform — settlements cluster, and the actual overlap area matters far more than raw ward area). Treat this number as a rough order of magnitude, not a precise headcount.
OSM data coverage & the confidence label
- Every infrastructure/amenity count depends entirely on how well volunteers have
mapped that specific area in OpenStreetMap. Rural or remote areas can be sparsely
mapped — a "low" or zero count there may mean "poorly mapped," not "actually
empty." The
confidencelabel is a density-vs-threshold heuristic, not a ground-truth check, and it cannot tell the difference between those two cases. - A
"status": "failed"entry (query error, timeout) is deliberately kept distinct from a real zero count — but both can still under-report what's actually there.
Overpass reliability
- The package depends on public Overpass API mirrors (overpass-api.de and others),
which are shared community infrastructure with no SLA. They can rate-limit,
block requests (HTTP 406) that don't send a proper User-Agent, or simply be slow
or down. Multiple mirrors with retry/fallback are used, but if all of them are
degraded at once, entire categories will come back as
"failed". - Named amenities are fetched in one combined request per area (schools,
hospitals, health posts, pharmacies, police, fire stations, shelters, religious
sites, marketplaces, water sources, and
emergency=*together) rather than one request per category — fewer round trips, less load on shared mirrors. The trade-off: if that one combined request fails, every one of those categories fails together (all show"status": "failed"with the same reason), whereas previously an unrelated category could still succeed independently. - Counts-only by default for buildings/roads. Full-detail geometry queries for
dense urban areas (or large-radius alerts) were found to time out during testing.
The default report avoids this by requesting counts only. The optional detail
layers (
include_detail_layers=True, or the standaloneget_building_geometries/get_road_geometriesfunctions) carry the same timeout risk — they are opt-in for exactly this reason, and can still fail or return partial data on a big or dense alert area. - There is no caching layer. Re-running the same or an overlapping area re-queries Overpass from scratch every time, which is both slower and puts more load on shared public infrastructure than necessary.
- No authentication or query-rate governance is implemented — sending many reports back-to-back risks temporary blocking by an Overpass mirror.
River / flood-context data
- River names in OSM aren't standardized — the same river can appear under several spellings, or with/without "River"/"Nadi" suffixes. Deduping is a simple heuristic (lowercase, strip common suffixes) and can still either merge two different rivers with similar names, or fail to merge two spellings of the same one.
- Lakes and dams are not currently implemented in
flood_context— only named rivers are queried and drawn. - Many minor roads (tracks, footpaths, unclassified rural roads) have no
nametag in OSM at all — common in hilly/rural areas.get_road_geometries()andget_building_geometries()fall back to labeling these by type (e.g. "Unnamed track", "Unnamed residential") instead of a bare "unnamed", so at least the road/building type is visible for triage — but the actual name, where OSM simply never recorded one, can't be recovered.
Hazard-specific scope
- Flood gets a hazard-specific layer: nearby named rivers, drawn on the map, plus
every amenity (school, hospital, health post, pharmacy, police, fire station,
shelter, religious site, marketplace, water source, or emergency infrastructure)
within 100m of one of those rivers is flagged
flood_riskand shown in a different color, both on the map and in the ward table. This 100m check is a straight-line proximity heuristic — it does not account for terrain, elevation, or the real shape of a flood plain. A point 100m from a river on a hillside above it is not actually at flood risk; the flag doesn't know that. - Landslide gets its own hazard-specific layer too: roads within the alert area, extended 100m past its drawn edge, shown as "potentially affected" in a distinct color on the map. Same straight-line-buffer caveat as above — real landslide runout is governed by slope and material, not a flat 100m ring, so this is a rough first-pass flag for which roads to check, not a validated impact assessment. Earthquake alerts still get no hazard-specific layer — OSM has no usable fault line, soil type, or slope data, and building-material tags are too sparse to support a meaningful vulnerability layer for earthquakes specifically.
- The landslide road buffer reuses the same heavier
out geomroad query as the opt-in detail layers, so it carries the same timeout risk on a large or oddly shaped alert area — see the Overpass section above.
CAP source & parsing
- Nepal's own DHM CAP system is not live yet (still in draft/SOP stage). This package currently targets GDACS (gdacs.org) as its live feed for real earthquake and flood alerts, plus locally saved sample files for testing (GDACS alert files expire, so live testing against old alerts isn't possible).
- A CAP
<area>with no<circle>or<polygon>— i.e. geocode-only, referencing a district or region code — is marked"unsupported"and skipped. There is no lookup table yet to resolve those codes into coordinates.
Performance & environment
- Report generation makes live network calls (Overpass, OpenStreetMap tile servers) at build time — there is no offline mode.
- Turning on the detail layers for a dense urban alert can mean rendering tens of thousands of building polygons in the browser, which will noticeably slow down both page load and map interaction.
- A real
pytestsuite now covers CAP parsing,osm_query's parsing/merging/ bucketing logic, andoverlap.py's geometry helpers (see Usage above) — but it deliberately mocks Overpass and uses a synthetic ward layout rather than real Nepal data, so it verifies our own logic, not live Overpass behavior, real ward topology, or real population figures. There's no integration test against the actual bundledwards.parquetor a live Overpass mirror yet.
Release files for tlf-disaster-profiler 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tlf_disaster_profiler-0.1.0.tar.gz | 23.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tlf_disaster_profiler-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.5 kB
Release files / tlf_disaster_profiler-0.1.0.tar.gz
| Download URL | tlf_disaster_profiler-0.1.0.tar.gz |
|---|---|
| Size | 23.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
08cc8d51a29586d97ab97657eea423584c2de5a011a69596923f3cd6d2f1b328
|
|
BLAKE2b-256 checksum How to use checksums |
043b323245b89b43af80934b0b1e276c4de03141937376f04f660d36d41699ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / tlf_disaster_profiler-0.1.0-py3-none-any.whl
| Download URL | tlf_disaster_profiler-0.1.0-py3-none-any.whl |
|---|---|
| Size | 22.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
50b7c837ce0b41da1016186299ef31fe1a402c540a3d0962e95b85b05d3851af
|
|
BLAKE2b-256 checksum How to use checksums |
c8735632a89676b683e4e3609a91c5a09cb4bb2e2585bdd286d92a223c22eb2e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|