Upload 2 files
Browse files- app.py +253 -42
- requirements.txt +3 -1
app.py
CHANGED
|
@@ -6,30 +6,84 @@ import geopandas as gpd
|
|
| 6 |
import urllib.request
|
| 7 |
import rasterio
|
| 8 |
import rasterio.merge
|
|
|
|
|
|
|
| 9 |
from bs4 import BeautifulSoup
|
| 10 |
from datetime import datetime
|
|
|
|
| 11 |
|
| 12 |
-
def generate_raster(iso: str, rp: int):
|
| 13 |
-
iso = iso.upper()
|
| 14 |
|
| 15 |
-
|
| 16 |
-
geometry_url = "https://gis.unhcr.org/arcgis/rest/services/core_v2/wrl_polbnd_adm1_a_unhcr/MapServer/0/query"
|
| 17 |
params = {
|
| 18 |
-
"where": f"iso3 = '{
|
| 19 |
"outFields": "*",
|
| 20 |
"outSR": "4326",
|
| 21 |
-
"f": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
}
|
| 23 |
-
response = requests.get(
|
| 24 |
-
if response.status_code
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
-
|
| 28 |
-
if
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
-
|
| 32 |
-
bounds = gdf.total_bounds # [left, bottom, right, top]
|
| 33 |
|
| 34 |
# --- Determine tiles to download ---
|
| 35 |
top = math.ceil(bounds[3] / 10) * 10
|
|
@@ -66,52 +120,151 @@ def generate_raster(iso: str, rp: int):
|
|
| 66 |
local_paths.append(local)
|
| 67 |
|
| 68 |
sources = [rasterio.open(p) for p in local_paths]
|
| 69 |
-
mosaic, transform = rasterio.merge.merge(sources)
|
| 70 |
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
| 73 |
"driver": "GTiff",
|
| 74 |
"height": mosaic.shape[1],
|
| 75 |
"width": mosaic.shape[2],
|
| 76 |
-
"transform":
|
| 77 |
})
|
| 78 |
|
| 79 |
-
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
|
| 83 |
-
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
for src in sources:
|
| 86 |
src.close()
|
| 87 |
for f in local_paths:
|
| 88 |
os.remove(f)
|
|
|
|
|
|
|
| 89 |
|
| 90 |
return output_path, f"✅ Raster generated for {iso} (RP {rp}). Click to download below."
|
| 91 |
|
| 92 |
|
| 93 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
iso3_upper = iso3.upper()
|
| 95 |
iso3_lower = iso3.lower()
|
| 96 |
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
print(f"Failed to download HOTOSM data for {iso3}. HTTP status: {response.status_code}")
|
| 103 |
return None
|
| 104 |
|
| 105 |
-
with open(local_filename, 'wb') as f:
|
| 106 |
-
for chunk in response.iter_content(chunk_size=8192):
|
| 107 |
-
f.write(chunk)
|
| 108 |
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
|
| 112 |
# Selection options
|
| 113 |
iso_options = [('Abyei', 'XAB'), ('Afghanistan', 'AFG'), ('Aksai Chin (Sovereignty unsettled)', 'XAC'), ('Aland Islands (FIN)', 'ALA'), ('Albania', 'ALB'), ('Algeria', 'DZA'), ('American Samoa', 'ASM'), ('Andorra', 'AND'), ('Angola', 'AGO'), ('Anguilla (GBR)', 'AIA'), ('Antarctica', 'ATA'), ('Antigua and Barbuda', 'ATG'), ('Argentina', 'ARG'), ('Armenia', 'ARM'), ('Aruba (K. of the Netherlands)', 'ABW'), ('Arunachal Pradesh (IND)', 'XAP'), ('Australia', 'AUS'), ('Austria', 'AUT'), ('Azerbaijan', 'AZE'), ('Bahamas', 'BHS'), ('Bahrain', 'BHR'), ('Bangladesh', 'BGD'), ('Barbados', 'BRB'), ('Belarus', 'BLR'), ('Belgium', 'BEL'), ('Belize', 'BLZ'), ('Benin', 'BEN'), ('Bermuda', 'BMU'), ('Bhutan', 'BTN'), ('Bolivarian Republic of Venezuela', 'VEN'), ('Bonaire Sint Eustatius and Saba', 'BES'), ('Bosnia and Herzegovina', 'BIH'), ('Botswana', 'BWA'), ('Bouvet Island', 'BVT'), ('Brazil', 'BRA'), ('British Indian Ocean Territory', 'IOT'), ('British Virgin Islands (GBR)', 'VGB'), ('Brunei Darussalam', 'BRN'), ('Bulgaria', 'BGR'), ('Burkina Faso', 'BFA'), ('Burundi', 'BDI'), ('Cambodia', 'KHM'), ('Cameroon', 'CMR'), ('Canada', 'CAN'), ('Cape Verde', 'CPV'), ('Cayman Islands (GBR)', 'CYM'), ('Central African Republic', 'CAF'), ('Chile', 'CHL'), ('China', 'CHN'), ('China/India (Sovereignty unsettled)', 'XCI'), ('Christmas Island (AUS)', 'CXR'), ('Cocos (Keeling) Islands', 'CCK'), ('Colombia', 'COL'), ('Comoros', 'COM'), ('Cook Islands', 'COK'), ('Costa Rica', 'CRI'), ('Croatia', 'HRV'), ('Cuba', 'CUB'), ('Curacao (K. of Netherlands)', 'CUW'), ('Cyprus', 'CYP'), ('Czech Republic', 'CZE'), ("Côte d'Ivoire", 'CIV'), ("Democratic Poeple's Rep. of Korea", 'PRK'), ('Democratic Republic of the Congo', 'COD'), ('Denmark', 'DNK'), ('Djibouti', 'DJI'), ('Dominica', 'DMA'), ('Dominican Republic', 'DOM'), ('Ecuador', 'ECU'), ('Egypt', 'EGY'), ('El Salvador', 'SLV'), ('Equatorial Guinea', 'GNQ'), ('Eritrea', 'ERI'), ('Estonia', 'EST'), ('Eswatini', 'SWZ'), ('Ethiopia', 'ETH'), ('Falkland Islands (Malvinas)', 'FLK'), ('Faroe Islands (DNK)', 'FRO'), ('Federated States of Micronesia', 'FSM'), ('Fiji', 'FJI'), ('Finland', 'FIN'), ('France', 'FRA'), ('French Guiana (FRA)', 'GUF'), ('French New Caledonia (FRA)', 'NCL'), ('French Polynesia', 'PYF'), ('French Southern and Antarctic Territories', 'ATF'), ('Gabon', 'GAB'), ('Gambia', 'GMB'), ('Georgia', 'GEO'), ('Germany', 'DEU'), ('Ghana', 'GHA'), ('Gibraltar', 'GIB'), ('Greece', 'GRC'), ('Greenland (DNK)', 'GRL'), ('Grenada', 'GRD'), ('Guadeloupe (FRA)', 'GLP'), ('Guam', 'GUM'), ('Guatemala', 'GTM'), ('Guernsey (GBR)', 'GGY'), ('Guinea', 'GIN'), ('Guinea-Bissau', 'GNB'), ('Guyana', 'GUY'), ('Haiti', 'HTI'), ("Hala'ib triangle (SDN)", 'XHT'), ('Heard Island and McDonald Islands (AUS)', 'HMD'), ('Holy See', 'VAT'), ('Honduras', 'HND'), ('Hong Kong', 'HKG'), ('Hungary', 'HUN'), ('Iceland', 'ISL'), ('Ilemi Triangle (SSD)', 'XIT'), ('India', 'IND'), ('Indonesia', 'IDN'), ('Iraq', 'IRQ'), ('Ireland', 'IRL'), ('Islamic Republic of Iran', 'IRN'), ('Isle of Man', 'IMN'), ('Israel', 'ISR'), ('Italy', 'ITA'), ('Jamaica', 'JAM'), ('Jammu and Kashmir', 'XJK'), ('Jan Mayen Island (NOR)', 'SJM'), ('Japan', 'JPN'), ('Jersey (GBR)', 'JEY'), ('Johnston Atoll', 'JTN'), ('Jordan', 'JOR'), ('Kazakhstan', 'KAZ'), ('Kenya', 'KEN'), ('Kiribati', 'KIR'), ('Kosovo (SRB)', 'KOS'), ('Kuril Islands (RUS)', 'XKI'), ('Kuwait', 'KWT'), ('Kyrgyzstan', 'KGZ'), ("Lao People's Democratic Republic", 'LAO'), ('Latvia', 'LVA'), ('Lebanon', 'LBN'), ('Lesotho', 'LSO'), ('Liberia', 'LBR'), ('Libya', 'LBY'), ('Liechtenstein', 'LIE'), ('Lithuania', 'LTU'), ('Luxembourg', 'LUX'), ("Ma'tan al-Sarra (EGY)", 'XMS'), ('Macao (CHN)', 'MAC'), ('Madagascar', 'MDG'), ('Malawi', 'MWI'), ('Malaysia', 'MYS'), ('Maldives', 'MDV'), ('Mali', 'MLI'), ('Malta', 'MLT'), ('Marshall Islands', 'MHL'), ('Martinique (FRA)', 'MTQ'), ('Mauritania', 'MRT'), ('Mauritius', 'MUS'), ('Mayotte (FRA)', 'MYT'), ('Mexico', 'MEX'), ('Midway Islands', 'MID'), ('Monaco', 'MCO'), ('Mongolia', 'MNG'), ('Montenegro', 'MNE'), ('Montserrat', 'MSR'), ('Morocco', 'MAR'), ('Mozambique', 'MOZ'), ('Myanmar', 'MMR'), ('Namibia', 'NAM'), ('Nauru', 'NRU'), ('Nepal', 'NPL'), ('Netherlands (Kingdom of the)', 'NLD'), ('New Zealand', 'NZL'), ('Nicaragua', 'NIC'), ('Niger', 'NER'), ('Nigeria', 'NGA'), ('Niue', 'NIU'), ('No code (ISO user specified)', 'AAA'), ('Norfolk Island (AUS)', 'NFK'), ('North Macedonia', 'MKD'), ('Northern Mariana Islands (USA)', 'MNP'), ('Norway', 'NOR'), ('Oman', 'OMN'), ('Pakistan', 'PAK'), ('Palau', 'PLW'), ('Panama', 'PAN'), ('Papua New Guinea', 'PNG'), ('Paracel Islands (Sovereignty unsettled)', 'XPI'), ('Paraguay', 'PRY'), ('Peru', 'PER'), ('Philippines', 'PHL'), ('Pitcairn Islands', 'PCN'), ('Plurinational State of Bolivia', 'BOL'), ('Poland', 'POL'), ('Portugal', 'PRT'), ('Puerto Rico', 'PRI'), ('Qatar', 'QAT'), ('Rep. of Chad', 'TCD'), ('Republic of Korea', 'KOR'), ('Republic of Moldova', 'MDA'), ('Republic of the Congo', 'COG'), ('Reunion (FRA)', 'REU'), ('Romania', 'ROU'), ('Russian Federation', 'RUS'), ('Rwanda', 'RWA'), ('Saint Barthelemy (FRA)', 'BLM'), ('Saint Helena', 'SHN'), ('Saint Kitts and Nevis', 'KNA'), ('Saint Lucia', 'LCA'), ('Saint Martin (FRA)', 'MAF'), ('Saint Pierre et Miquelon (FRA)', 'SPM'), ('Saint Vincent and the Grenadines', 'VCT'), ('Samoa', 'WSM'), ('San Marino', 'SMR'), ('Sao Tome and Principe', 'STP'), ('Sark (GBR)', 'XSA'), ('Saudi Arabia', 'SAU'), ('Scarborough Reef (Sovereignty unsettled)', 'XSR'), ('Senegal', 'SEN'), ('Senkaku Islands (Sovereignty unsettled)', 'XSI'), ('Serbia', 'SRB'), ('Seychelles', 'SYC'), ('Sierra Leone', 'SLE'), ('Singapore', 'SGP'), ('Sint Maarten (K. of Netherlands)', 'SXM'), ('Slovakia', 'SVK'), ('Slovenia', 'SVN'), ('Solomon Islands', 'SLB'), ('Somalia', 'SOM'), ('South Africa', 'ZAF'), ('South Georgia and the South Sandwich Islands (GBR)', 'SGS'), ('South Sudan', 'SSD'), ('Spain', 'ESP'), ('Spratly Islands (Sovereignty unsettled)', 'XSP'), ('Sri Lanka', 'LKA'), ('State of Palestine', 'PSE'), ('Sudan', 'SDN'), ('Suriname', 'SUR'), ('Sweden', 'SWE'), ('Switzerland', 'CHE'), ('Syrian Arab Republic', 'SYR'), ('Taiwan (CHN)', 'TWN'), ('Tajikistan', 'TJK'), ('Thailand', 'THA'), ('Timor-Leste', 'TLS'), ('Togo', 'TGO'), ('Tokelau', 'TKL'), ('Tonga', 'TON'), ('Trinidad and Tobago', 'TTO'), ('Tunisia', 'TUN'), ('Turkey', 'TUR'), ('Turkmenistan', 'TKM'), ('Turks and Caicos Islands (GBR)', 'TCA'), ('Tuvalu', 'TUV'), ('Uganda', 'UGA'), ('Ukraine', 'UKR'), ('United Arab Emirates', 'ARE'), ('United Kingdom of Great Britain and Northern Ireland', 'GBR'), ('United Republic of Tanzania', 'TZA'), ('United States Minor Outlying Islands', 'UMI'), ('United States Virgin Islands (USA)', 'VIR'), ('United States of America', 'USA'), ('Uruguay', 'URY'), ('Uzbekistan', 'UZB'), ('Vanuatu', 'VUT'), ('Viet Nam', 'VNM'), ('Wake Island', 'WAK'), ('Wallis and Futuna', 'WLF'), ('Western Sahara', 'ESH'), ('Yemen', 'YEM'), ('Zambia', 'ZMB'), ('Zimbabwe', 'ZWE')]
|
| 114 |
rp_options = [10, 20, 50, 75, 100, 200, 500]
|
|
|
|
| 115 |
|
| 116 |
with gr.Blocks(title="Flood Raster Downloader") as demo:
|
| 117 |
gr.Markdown("## E-VCA Flood Raster Downloader\nSelect a country and return period to generate the GloFAS flood hazard raster for that region.")
|
|
@@ -119,27 +272,85 @@ with gr.Blocks(title="Flood Raster Downloader") as demo:
|
|
| 119 |
with gr.Row():
|
| 120 |
iso_input = gr.Dropdown(choices=iso_options, label="Country", interactive=True, value=None)
|
| 121 |
rp_input = gr.Dropdown(choices=rp_options, label="Return Period (years)", value=10)
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
-
generate_btn = gr.Button("Generate Raster")
|
| 124 |
status_text = gr.Markdown("")
|
| 125 |
file_output = gr.Files(label="Downloads", interactive=False)
|
| 126 |
|
| 127 |
-
def wrapper(iso, rp):
|
| 128 |
-
raster_path, msg = generate_raster(iso, rp)
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
files = []
|
| 135 |
if raster_path:
|
| 136 |
files.append(raster_path)
|
| 137 |
-
if
|
| 138 |
-
files.append(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
return files, msg
|
| 141 |
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
if __name__ == "__main__":
|
| 145 |
demo.launch(share=True)
|
|
|
|
| 6 |
import urllib.request
|
| 7 |
import rasterio
|
| 8 |
import rasterio.merge
|
| 9 |
+
from rasterio.mask import mask
|
| 10 |
+
from rasterio.transform import array_bounds
|
| 11 |
from bs4 import BeautifulSoup
|
| 12 |
from datetime import datetime
|
| 13 |
+
import pandas as pd
|
| 14 |
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
def has_associated_features(iso_code, url="https://gis.unhcr.org/arcgis/rest/services/core_v2/wrl_polbnd_adm1_a_unhcr/MapServer/0/query"):
|
|
|
|
| 17 |
params = {
|
| 18 |
+
"where": f"iso3 = '{iso_code}'",
|
| 19 |
"outFields": "*",
|
| 20 |
"outSR": "4326",
|
| 21 |
+
"f": "json"
|
| 22 |
+
}
|
| 23 |
+
try:
|
| 24 |
+
response = requests.get(url, params=params, timeout=10)
|
| 25 |
+
if response.status_code == 200 and response.text.strip():
|
| 26 |
+
data = response.json()
|
| 27 |
+
if 'features' in data:
|
| 28 |
+
return len(data['features']) > 0, data
|
| 29 |
+
return False, None
|
| 30 |
+
except Exception as e:
|
| 31 |
+
return False, None
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def fetch_gis_subdivisions(iso_code, url="https://gis.unhcr.org/arcgis/rest/services/core_v2/wrl_polbnd_adm1_a_unhcr/MapServer/0/query"):
|
| 35 |
+
params = {
|
| 36 |
+
"where": f"iso3 = '{iso_code}'",
|
| 37 |
+
"outFields": "iso3,gis_name",
|
| 38 |
+
"returnGeometry": "false",
|
| 39 |
+
"f": "json"
|
| 40 |
}
|
| 41 |
+
response = requests.get(url, params=params)
|
| 42 |
+
if response.status_code == 200:
|
| 43 |
+
data = response.json()
|
| 44 |
+
if "features" in data:
|
| 45 |
+
return [f["attributes"]["gis_name"] for f in data["features"]]
|
| 46 |
+
return []
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def get_geometry(iso_code, gis_name=None, url="https://gis.unhcr.org/arcgis/rest/services/core_v2/wrl_polbnd_adm1_a_unhcr/MapServer/0/query"):
|
| 50 |
+
if gis_name:
|
| 51 |
+
params = {
|
| 52 |
+
"where": f"iso3 = '{iso_code}' AND gis_name = '{gis_name}'",
|
| 53 |
+
"outFields": "*",
|
| 54 |
+
"outSR": "4326",
|
| 55 |
+
"f": "geojson"
|
| 56 |
+
}
|
| 57 |
+
else:
|
| 58 |
+
params = {
|
| 59 |
+
"where": f"iso3 = '{iso_code}'",
|
| 60 |
+
"outFields": "*",
|
| 61 |
+
"outSR": "4326",
|
| 62 |
+
"f": "geojson"
|
| 63 |
+
}
|
| 64 |
+
response = requests.get(url, params=params)
|
| 65 |
+
if response.status_code == 200:
|
| 66 |
+
geojson_data = response.json()
|
| 67 |
+
if "features" in geojson_data and geojson_data["features"]:
|
| 68 |
+
return gpd.GeoDataFrame.from_features(geojson_data["features"])
|
| 69 |
+
return None
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def generate_raster(iso: str, rp: int, gis_name: str = None):
|
| 73 |
+
iso = iso.upper()
|
| 74 |
+
base_url = "https://gis.unhcr.org/arcgis/rest/services/core_v2/wrl_polbnd_adm1_a_unhcr/MapServer/0/query"
|
| 75 |
|
| 76 |
+
has_features, _ = has_associated_features(iso, base_url)
|
| 77 |
+
if has_features:
|
| 78 |
+
gdf = get_geometry(iso, url=base_url)
|
| 79 |
+
else:
|
| 80 |
+
if not gis_name:
|
| 81 |
+
return None, f"❌ Subdivision required for {iso}."
|
| 82 |
+
gdf = get_geometry(iso, gis_name)
|
| 83 |
+
if gdf is None or gdf.empty:
|
| 84 |
+
return None, f"❌ No geometry found for {gis_name} in {iso}."
|
| 85 |
|
| 86 |
+
bounds = gdf.total_bounds # left, bottom, right, top
|
|
|
|
| 87 |
|
| 88 |
# --- Determine tiles to download ---
|
| 89 |
top = math.ceil(bounds[3] / 10) * 10
|
|
|
|
| 120 |
local_paths.append(local)
|
| 121 |
|
| 122 |
sources = [rasterio.open(p) for p in local_paths]
|
|
|
|
| 123 |
|
| 124 |
+
mosaic, merge_transform = rasterio.merge.merge(sources)
|
| 125 |
+
|
| 126 |
+
temp_merged = "/tmp/temp_merged.tif"
|
| 127 |
+
temp_meta = sources[0].meta.copy()
|
| 128 |
+
temp_meta.update({
|
| 129 |
"driver": "GTiff",
|
| 130 |
"height": mosaic.shape[1],
|
| 131 |
"width": mosaic.shape[2],
|
| 132 |
+
"transform": merge_transform
|
| 133 |
})
|
| 134 |
|
| 135 |
+
with rasterio.open(temp_merged, "w", **temp_meta) as temp_dst:
|
| 136 |
+
temp_dst.write(mosaic)
|
| 137 |
+
|
| 138 |
+
# --- Crop to geometry ---
|
| 139 |
+
geoms = [feature["geometry"] for feature in gdf.__geo_interface__["features"]]
|
| 140 |
+
with rasterio.open(temp_merged) as src:
|
| 141 |
+
out_image, out_transform = mask(src, geoms, crop=True, nodata=-9999)
|
| 142 |
+
out_meta = src.meta.copy()
|
| 143 |
+
out_meta.update({
|
| 144 |
+
"driver": "GTiff",
|
| 145 |
+
"height": out_image.shape[1],
|
| 146 |
+
"width": out_image.shape[2],
|
| 147 |
+
"transform": out_transform,
|
| 148 |
+
"nodata": -9999
|
| 149 |
+
})
|
| 150 |
|
| 151 |
+
timestamp = datetime.utcnow().strftime("%Y%m%d%H%M%S")
|
| 152 |
+
output_path = f"./merged_raster_{iso}_{rp}_{timestamp}.tif"
|
| 153 |
|
| 154 |
+
with rasterio.open(output_path, "w", **out_meta) as dest:
|
| 155 |
+
dest.write(out_image)
|
| 156 |
+
|
| 157 |
+
# Cleanup
|
| 158 |
for src in sources:
|
| 159 |
src.close()
|
| 160 |
for f in local_paths:
|
| 161 |
os.remove(f)
|
| 162 |
+
if os.path.exists(temp_merged):
|
| 163 |
+
os.remove(temp_merged)
|
| 164 |
|
| 165 |
return output_path, f"✅ Raster generated for {iso} (RP {rp}). Click to download below."
|
| 166 |
|
| 167 |
|
| 168 |
+
def download_hotosm_data(iso3: str, feature_type: str = "populated_places", geometry: str = "points", save_dir: str = "./"):
|
| 169 |
+
"""
|
| 170 |
+
Download HOTOSM data (e.g., populated_places, roads, etc.) as SHP zip.
|
| 171 |
+
|
| 172 |
+
Args:
|
| 173 |
+
iso3 (str): ISO3 country code (e.g., 'SSD').
|
| 174 |
+
feature_type (str): HOTOSM feature type (e.g., 'populated_places', 'roads').
|
| 175 |
+
geometry (str): Geometry type ('points', 'lines', 'polygons').
|
| 176 |
+
save_dir (str): Directory to save the downloaded file.
|
| 177 |
+
|
| 178 |
+
Returns:
|
| 179 |
+
str or None: Path to saved file, or None if failed.
|
| 180 |
+
"""
|
| 181 |
iso3_upper = iso3.upper()
|
| 182 |
iso3_lower = iso3.lower()
|
| 183 |
|
| 184 |
+
# Build filename
|
| 185 |
+
filename = f"hotosm_{iso3_lower}_{feature_type}_{geometry}_shp.zip"
|
| 186 |
+
url = f"https://s3.dualstack.us-east-1.amazonaws.com/production-raw-data-api/ISO3/{iso3_upper}/{feature_type}/{geometry}/{filename}"
|
| 187 |
+
local_filename = os.path.join(save_dir, filename)
|
| 188 |
+
|
| 189 |
+
try:
|
| 190 |
+
response = requests.get(url, stream=True)
|
| 191 |
+
if response.status_code != 200:
|
| 192 |
+
print(f"[{iso3_upper}] Failed to download HOTOSM {feature_type}/{geometry}. HTTP {response.status_code}")
|
| 193 |
+
return None
|
| 194 |
+
|
| 195 |
+
os.makedirs(save_dir, exist_ok=True)
|
| 196 |
+
with open(local_filename, 'wb') as f:
|
| 197 |
+
for chunk in response.iter_content(chunk_size=8192):
|
| 198 |
+
f.write(chunk)
|
| 199 |
+
|
| 200 |
+
print(f"[{iso3_upper}] Downloaded: {local_filename}")
|
| 201 |
+
return local_filename
|
| 202 |
|
| 203 |
+
except Exception as e:
|
| 204 |
+
print(f"[{iso3_upper}] Error: {e}")
|
|
|
|
| 205 |
return None
|
| 206 |
|
|
|
|
|
|
|
|
|
|
| 207 |
|
| 208 |
+
def download_resource_ckan(iso3_code, dataset_prefix="cod-ab", download_dir="downloads"):
|
| 209 |
+
iso3 = iso3_code.lower()
|
| 210 |
+
dataset_id = f"{dataset_prefix}-{iso3}"
|
| 211 |
+
api_url = f"https://data.humdata.org/api/3/action/package_show?id={dataset_id}"
|
| 212 |
+
headers = {'User-Agent': 'Mozilla/5.0'}
|
| 213 |
+
|
| 214 |
+
# Match logic
|
| 215 |
+
if dataset_prefix == "cod-ps":
|
| 216 |
+
keyword_match = lambda name: name.startswith(f"{iso3}_admpop_adm") and name.endswith(".csv")
|
| 217 |
+
else:
|
| 218 |
+
keyword_match = lambda name: "_SHP.zip" in name
|
| 219 |
+
|
| 220 |
+
try:
|
| 221 |
+
response = requests.get(api_url, headers=headers)
|
| 222 |
+
if response.status_code != 200:
|
| 223 |
+
print(f"[{iso3.upper()}] Dataset not found (HTTP {response.status_code}): {dataset_id}")
|
| 224 |
+
return []
|
| 225 |
+
|
| 226 |
+
data = response.json()
|
| 227 |
+
if not data.get("success") or "result" not in data:
|
| 228 |
+
print(f"[{iso3.upper()}] API returned malformed response.")
|
| 229 |
+
return []
|
| 230 |
+
|
| 231 |
+
# Find and download all matching resources
|
| 232 |
+
os.makedirs(download_dir, exist_ok=True)
|
| 233 |
+
downloaded_files = []
|
| 234 |
+
|
| 235 |
+
for resource in data["result"].get("resources", []):
|
| 236 |
+
name = resource.get("name", "")
|
| 237 |
+
if keyword_match(name):
|
| 238 |
+
url = resource.get("url")
|
| 239 |
+
if not url:
|
| 240 |
+
continue
|
| 241 |
+
|
| 242 |
+
filepath = os.path.join(download_dir, name)
|
| 243 |
+
try:
|
| 244 |
+
with requests.get(url, stream=True) as r:
|
| 245 |
+
r.raise_for_status()
|
| 246 |
+
with open(filepath, 'wb') as f:
|
| 247 |
+
for chunk in r.iter_content(chunk_size=8192):
|
| 248 |
+
f.write(chunk)
|
| 249 |
+
print(f"[{iso3.upper()}] Downloaded: {filepath}")
|
| 250 |
+
downloaded_files.append(filepath)
|
| 251 |
+
except Exception as e:
|
| 252 |
+
print(f"[{iso3.upper()}] Failed to download {name}: {e}")
|
| 253 |
+
|
| 254 |
+
if not downloaded_files:
|
| 255 |
+
print(f"[{iso3.upper()}] No matching files found in dataset '{dataset_id}'.")
|
| 256 |
+
|
| 257 |
+
return downloaded_files
|
| 258 |
+
|
| 259 |
+
except Exception as e:
|
| 260 |
+
print(f"[{iso3.upper()}] Error: {e}")
|
| 261 |
+
return []
|
| 262 |
|
| 263 |
|
| 264 |
# Selection options
|
| 265 |
iso_options = [('Abyei', 'XAB'), ('Afghanistan', 'AFG'), ('Aksai Chin (Sovereignty unsettled)', 'XAC'), ('Aland Islands (FIN)', 'ALA'), ('Albania', 'ALB'), ('Algeria', 'DZA'), ('American Samoa', 'ASM'), ('Andorra', 'AND'), ('Angola', 'AGO'), ('Anguilla (GBR)', 'AIA'), ('Antarctica', 'ATA'), ('Antigua and Barbuda', 'ATG'), ('Argentina', 'ARG'), ('Armenia', 'ARM'), ('Aruba (K. of the Netherlands)', 'ABW'), ('Arunachal Pradesh (IND)', 'XAP'), ('Australia', 'AUS'), ('Austria', 'AUT'), ('Azerbaijan', 'AZE'), ('Bahamas', 'BHS'), ('Bahrain', 'BHR'), ('Bangladesh', 'BGD'), ('Barbados', 'BRB'), ('Belarus', 'BLR'), ('Belgium', 'BEL'), ('Belize', 'BLZ'), ('Benin', 'BEN'), ('Bermuda', 'BMU'), ('Bhutan', 'BTN'), ('Bolivarian Republic of Venezuela', 'VEN'), ('Bonaire Sint Eustatius and Saba', 'BES'), ('Bosnia and Herzegovina', 'BIH'), ('Botswana', 'BWA'), ('Bouvet Island', 'BVT'), ('Brazil', 'BRA'), ('British Indian Ocean Territory', 'IOT'), ('British Virgin Islands (GBR)', 'VGB'), ('Brunei Darussalam', 'BRN'), ('Bulgaria', 'BGR'), ('Burkina Faso', 'BFA'), ('Burundi', 'BDI'), ('Cambodia', 'KHM'), ('Cameroon', 'CMR'), ('Canada', 'CAN'), ('Cape Verde', 'CPV'), ('Cayman Islands (GBR)', 'CYM'), ('Central African Republic', 'CAF'), ('Chile', 'CHL'), ('China', 'CHN'), ('China/India (Sovereignty unsettled)', 'XCI'), ('Christmas Island (AUS)', 'CXR'), ('Cocos (Keeling) Islands', 'CCK'), ('Colombia', 'COL'), ('Comoros', 'COM'), ('Cook Islands', 'COK'), ('Costa Rica', 'CRI'), ('Croatia', 'HRV'), ('Cuba', 'CUB'), ('Curacao (K. of Netherlands)', 'CUW'), ('Cyprus', 'CYP'), ('Czech Republic', 'CZE'), ("Côte d'Ivoire", 'CIV'), ("Democratic Poeple's Rep. of Korea", 'PRK'), ('Democratic Republic of the Congo', 'COD'), ('Denmark', 'DNK'), ('Djibouti', 'DJI'), ('Dominica', 'DMA'), ('Dominican Republic', 'DOM'), ('Ecuador', 'ECU'), ('Egypt', 'EGY'), ('El Salvador', 'SLV'), ('Equatorial Guinea', 'GNQ'), ('Eritrea', 'ERI'), ('Estonia', 'EST'), ('Eswatini', 'SWZ'), ('Ethiopia', 'ETH'), ('Falkland Islands (Malvinas)', 'FLK'), ('Faroe Islands (DNK)', 'FRO'), ('Federated States of Micronesia', 'FSM'), ('Fiji', 'FJI'), ('Finland', 'FIN'), ('France', 'FRA'), ('French Guiana (FRA)', 'GUF'), ('French New Caledonia (FRA)', 'NCL'), ('French Polynesia', 'PYF'), ('French Southern and Antarctic Territories', 'ATF'), ('Gabon', 'GAB'), ('Gambia', 'GMB'), ('Georgia', 'GEO'), ('Germany', 'DEU'), ('Ghana', 'GHA'), ('Gibraltar', 'GIB'), ('Greece', 'GRC'), ('Greenland (DNK)', 'GRL'), ('Grenada', 'GRD'), ('Guadeloupe (FRA)', 'GLP'), ('Guam', 'GUM'), ('Guatemala', 'GTM'), ('Guernsey (GBR)', 'GGY'), ('Guinea', 'GIN'), ('Guinea-Bissau', 'GNB'), ('Guyana', 'GUY'), ('Haiti', 'HTI'), ("Hala'ib triangle (SDN)", 'XHT'), ('Heard Island and McDonald Islands (AUS)', 'HMD'), ('Holy See', 'VAT'), ('Honduras', 'HND'), ('Hong Kong', 'HKG'), ('Hungary', 'HUN'), ('Iceland', 'ISL'), ('Ilemi Triangle (SSD)', 'XIT'), ('India', 'IND'), ('Indonesia', 'IDN'), ('Iraq', 'IRQ'), ('Ireland', 'IRL'), ('Islamic Republic of Iran', 'IRN'), ('Isle of Man', 'IMN'), ('Israel', 'ISR'), ('Italy', 'ITA'), ('Jamaica', 'JAM'), ('Jammu and Kashmir', 'XJK'), ('Jan Mayen Island (NOR)', 'SJM'), ('Japan', 'JPN'), ('Jersey (GBR)', 'JEY'), ('Johnston Atoll', 'JTN'), ('Jordan', 'JOR'), ('Kazakhstan', 'KAZ'), ('Kenya', 'KEN'), ('Kiribati', 'KIR'), ('Kosovo (SRB)', 'KOS'), ('Kuril Islands (RUS)', 'XKI'), ('Kuwait', 'KWT'), ('Kyrgyzstan', 'KGZ'), ("Lao People's Democratic Republic", 'LAO'), ('Latvia', 'LVA'), ('Lebanon', 'LBN'), ('Lesotho', 'LSO'), ('Liberia', 'LBR'), ('Libya', 'LBY'), ('Liechtenstein', 'LIE'), ('Lithuania', 'LTU'), ('Luxembourg', 'LUX'), ("Ma'tan al-Sarra (EGY)", 'XMS'), ('Macao (CHN)', 'MAC'), ('Madagascar', 'MDG'), ('Malawi', 'MWI'), ('Malaysia', 'MYS'), ('Maldives', 'MDV'), ('Mali', 'MLI'), ('Malta', 'MLT'), ('Marshall Islands', 'MHL'), ('Martinique (FRA)', 'MTQ'), ('Mauritania', 'MRT'), ('Mauritius', 'MUS'), ('Mayotte (FRA)', 'MYT'), ('Mexico', 'MEX'), ('Midway Islands', 'MID'), ('Monaco', 'MCO'), ('Mongolia', 'MNG'), ('Montenegro', 'MNE'), ('Montserrat', 'MSR'), ('Morocco', 'MAR'), ('Mozambique', 'MOZ'), ('Myanmar', 'MMR'), ('Namibia', 'NAM'), ('Nauru', 'NRU'), ('Nepal', 'NPL'), ('Netherlands (Kingdom of the)', 'NLD'), ('New Zealand', 'NZL'), ('Nicaragua', 'NIC'), ('Niger', 'NER'), ('Nigeria', 'NGA'), ('Niue', 'NIU'), ('No code (ISO user specified)', 'AAA'), ('Norfolk Island (AUS)', 'NFK'), ('North Macedonia', 'MKD'), ('Northern Mariana Islands (USA)', 'MNP'), ('Norway', 'NOR'), ('Oman', 'OMN'), ('Pakistan', 'PAK'), ('Palau', 'PLW'), ('Panama', 'PAN'), ('Papua New Guinea', 'PNG'), ('Paracel Islands (Sovereignty unsettled)', 'XPI'), ('Paraguay', 'PRY'), ('Peru', 'PER'), ('Philippines', 'PHL'), ('Pitcairn Islands', 'PCN'), ('Plurinational State of Bolivia', 'BOL'), ('Poland', 'POL'), ('Portugal', 'PRT'), ('Puerto Rico', 'PRI'), ('Qatar', 'QAT'), ('Rep. of Chad', 'TCD'), ('Republic of Korea', 'KOR'), ('Republic of Moldova', 'MDA'), ('Republic of the Congo', 'COG'), ('Reunion (FRA)', 'REU'), ('Romania', 'ROU'), ('Russian Federation', 'RUS'), ('Rwanda', 'RWA'), ('Saint Barthelemy (FRA)', 'BLM'), ('Saint Helena', 'SHN'), ('Saint Kitts and Nevis', 'KNA'), ('Saint Lucia', 'LCA'), ('Saint Martin (FRA)', 'MAF'), ('Saint Pierre et Miquelon (FRA)', 'SPM'), ('Saint Vincent and the Grenadines', 'VCT'), ('Samoa', 'WSM'), ('San Marino', 'SMR'), ('Sao Tome and Principe', 'STP'), ('Sark (GBR)', 'XSA'), ('Saudi Arabia', 'SAU'), ('Scarborough Reef (Sovereignty unsettled)', 'XSR'), ('Senegal', 'SEN'), ('Senkaku Islands (Sovereignty unsettled)', 'XSI'), ('Serbia', 'SRB'), ('Seychelles', 'SYC'), ('Sierra Leone', 'SLE'), ('Singapore', 'SGP'), ('Sint Maarten (K. of Netherlands)', 'SXM'), ('Slovakia', 'SVK'), ('Slovenia', 'SVN'), ('Solomon Islands', 'SLB'), ('Somalia', 'SOM'), ('South Africa', 'ZAF'), ('South Georgia and the South Sandwich Islands (GBR)', 'SGS'), ('South Sudan', 'SSD'), ('Spain', 'ESP'), ('Spratly Islands (Sovereignty unsettled)', 'XSP'), ('Sri Lanka', 'LKA'), ('State of Palestine', 'PSE'), ('Sudan', 'SDN'), ('Suriname', 'SUR'), ('Sweden', 'SWE'), ('Switzerland', 'CHE'), ('Syrian Arab Republic', 'SYR'), ('Taiwan (CHN)', 'TWN'), ('Tajikistan', 'TJK'), ('Thailand', 'THA'), ('Timor-Leste', 'TLS'), ('Togo', 'TGO'), ('Tokelau', 'TKL'), ('Tonga', 'TON'), ('Trinidad and Tobago', 'TTO'), ('Tunisia', 'TUN'), ('Turkey', 'TUR'), ('Turkmenistan', 'TKM'), ('Turks and Caicos Islands (GBR)', 'TCA'), ('Tuvalu', 'TUV'), ('Uganda', 'UGA'), ('Ukraine', 'UKR'), ('United Arab Emirates', 'ARE'), ('United Kingdom of Great Britain and Northern Ireland', 'GBR'), ('United Republic of Tanzania', 'TZA'), ('United States Minor Outlying Islands', 'UMI'), ('United States Virgin Islands (USA)', 'VIR'), ('United States of America', 'USA'), ('Uruguay', 'URY'), ('Uzbekistan', 'UZB'), ('Vanuatu', 'VUT'), ('Viet Nam', 'VNM'), ('Wake Island', 'WAK'), ('Wallis and Futuna', 'WLF'), ('Western Sahara', 'ESH'), ('Yemen', 'YEM'), ('Zambia', 'ZMB'), ('Zimbabwe', 'ZWE')]
|
| 266 |
rp_options = [10, 20, 50, 75, 100, 200, 500]
|
| 267 |
+
subdivision_input = gr.Dropdown(choices=[], label="Subdivision", interactive=True, visible=False)
|
| 268 |
|
| 269 |
with gr.Blocks(title="Flood Raster Downloader") as demo:
|
| 270 |
gr.Markdown("## E-VCA Flood Raster Downloader\nSelect a country and return period to generate the GloFAS flood hazard raster for that region.")
|
|
|
|
| 272 |
with gr.Row():
|
| 273 |
iso_input = gr.Dropdown(choices=iso_options, label="Country", interactive=True, value=None)
|
| 274 |
rp_input = gr.Dropdown(choices=rp_options, label="Return Period (years)", value=10)
|
| 275 |
+
|
| 276 |
+
# Subdivision dropdown, initially hidden
|
| 277 |
+
subdivision_input = gr.Dropdown(choices=[], label="Subdivision", interactive=True, visible=False)
|
| 278 |
|
| 279 |
+
generate_btn = gr.Button("Generate Raster", visible=False)
|
| 280 |
status_text = gr.Markdown("")
|
| 281 |
file_output = gr.Files(label="Downloads", interactive=False)
|
| 282 |
|
| 283 |
+
def wrapper(iso, rp, subdivision):
|
| 284 |
+
raster_path, msg = generate_raster(iso, rp, subdivision)
|
| 285 |
+
|
| 286 |
+
iso3 = iso.split(",")[-1].strip() if "," in iso else iso # Handle format like "South Sudan, SSD"
|
| 287 |
+
|
| 288 |
+
# HOTOSM Downloads
|
| 289 |
+
pp_path = download_hotosm_data(iso3, feature_type="populated_places", geometry="points")
|
| 290 |
+
roads_path = download_hotosm_data(iso3, feature_type="roads", geometry="lines")
|
| 291 |
+
|
| 292 |
+
# CKAN Downloads
|
| 293 |
+
pop_paths = download_resource_ckan(iso3, dataset_prefix="cod-ps")
|
| 294 |
+
admin_path = download_resource_ckan(iso3, dataset_prefix="cod-ab")
|
| 295 |
|
| 296 |
files = []
|
| 297 |
if raster_path:
|
| 298 |
files.append(raster_path)
|
| 299 |
+
if pp_path:
|
| 300 |
+
files.append(pp_path)
|
| 301 |
+
if roads_path:
|
| 302 |
+
files.append(roads_path)
|
| 303 |
+
if admin_path:
|
| 304 |
+
files.extend(admin_path)
|
| 305 |
+
if pop_paths:
|
| 306 |
+
files.extend(pop_paths) # not append!
|
| 307 |
+
if not files:
|
| 308 |
+
msg += " ⚠️ No data could be downloaded."
|
| 309 |
|
| 310 |
return files, msg
|
| 311 |
|
| 312 |
+
def get_subdivision_options(iso_code):
|
| 313 |
+
has_features, _ = has_associated_features(iso_code)
|
| 314 |
+
if not has_features:
|
| 315 |
+
subdivisions = fetch_gis_subdivisions(iso_code)
|
| 316 |
+
return (
|
| 317 |
+
gr.update(choices=subdivisions, visible=True, value=None),
|
| 318 |
+
gr.update(visible=False),
|
| 319 |
+
gr.update(value="✅ Subdivisions loaded. Please select one.")
|
| 320 |
+
)
|
| 321 |
+
else:
|
| 322 |
+
return (
|
| 323 |
+
gr.update(visible=False),
|
| 324 |
+
gr.update(visible=True),
|
| 325 |
+
gr.update(value="") # Clear status if subdivisions aren't needed
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
def on_subdivision_selected(subdivision):
|
| 329 |
+
if subdivision:
|
| 330 |
+
return gr.update(visible=True), gr.update(visible=True)
|
| 331 |
+
return gr.update(visible=False), gr.update(visible=False)
|
| 332 |
+
|
| 333 |
+
iso_input.change(
|
| 334 |
+
fn=lambda iso: gr.update(value="🔄 Loading subdivisions..."),
|
| 335 |
+
inputs=iso_input,
|
| 336 |
+
outputs=status_text
|
| 337 |
+
).then(
|
| 338 |
+
fn=get_subdivision_options,
|
| 339 |
+
inputs=iso_input,
|
| 340 |
+
outputs=[subdivision_input, generate_btn, status_text]
|
| 341 |
+
)
|
| 342 |
+
|
| 343 |
+
subdivision_input.change(
|
| 344 |
+
fn=lambda s: gr.update(visible=True) if s else gr.update(visible=False),
|
| 345 |
+
inputs=subdivision_input,
|
| 346 |
+
outputs=generate_btn
|
| 347 |
+
)
|
| 348 |
+
|
| 349 |
+
generate_btn.click(
|
| 350 |
+
fn=wrapper,
|
| 351 |
+
inputs=[iso_input, rp_input, subdivision_input],
|
| 352 |
+
outputs=[file_output, status_text]
|
| 353 |
+
)
|
| 354 |
|
| 355 |
if __name__ == "__main__":
|
| 356 |
demo.launch(share=True)
|
requirements.txt
CHANGED
|
@@ -4,6 +4,8 @@ geopandas
|
|
| 4 |
gradio
|
| 5 |
matplotlib
|
| 6 |
numpy
|
|
|
|
| 7 |
rasterio
|
| 8 |
requests
|
| 9 |
-
shapely
|
|
|
|
|
|
| 4 |
gradio
|
| 5 |
matplotlib
|
| 6 |
numpy
|
| 7 |
+
pandas
|
| 8 |
rasterio
|
| 9 |
requests
|
| 10 |
+
shapely
|
| 11 |
+
urllib
|