import gradio as gr import importlib import os from data.options import iso_options, rp_options from data.geometry import fetch_gis_subdivisions, has_associated_features from data.hotosm import download_hotosm_data from data.ckan import download_resource_ckan from analysis import run_analyses # Discover available hazard modules dynamically def get_available_hazards(): hazard_dir = "hazards" files = os.listdir(hazard_dir) hazards = [ f.replace(".py", "") for f in files if f.endswith(".py") and f != "__init__.py" ] return sorted([(h.capitalize(), h) for h in hazards]) hazard_options = get_available_hazards() # Gradio app with gr.Blocks(title="Hazard Raster Downloader") as demo: gr.Markdown("## eVCA Hazard and Exposure Data Downloader\nSelect a country, hazard, and return period to generate hazard rasters and supporting exposure data.") with gr.Row(): hazard_input = gr.Dropdown(choices=hazard_options, label="Hazard", value="flood", interactive=True) iso_input = gr.Dropdown(choices=iso_options, label="Country", interactive=True, value=None) rp_input = gr.Dropdown(choices=rp_options, label="Return Period (years)", value=10) subdivision_input = gr.Dropdown(choices=[], label="Subdivision", interactive=True, visible=False) generate_btn = gr.Button("Generate Raster", visible=False) status_text = gr.Markdown("") file_output = gr.Files(label="Downloads", interactive=False) analysis_output = gr.Markdown("", label="Analysis Results") def wrapper(hazard, iso, rp, subdivision): # Dynamic import of selected hazard module try: hazard_module = importlib.import_module(f"hazards.{hazard}") generate_func = getattr(hazard_module, f"generate_{hazard}_raster") except (ModuleNotFoundError, AttributeError): return [], f"❌ Error: No raster generation function found for hazard '{hazard}'." # Extract just the subdivision name if it's not the country-level option subdivision_name = None if subdivision and not subdivision.endswith("(Country level)"): subdivision_name = subdivision # Generate raster raster_path, msg = generate_func(iso, rp, subdivision_name) iso3 = iso.split(",")[-1].strip() if "," in iso else iso # HOTOSM Downloads pp_path = download_hotosm_data(iso3, feature_type="populated_places", geometry="points") roads_path = download_hotosm_data(iso3, feature_type="roads", geometry="lines") buildings_path = download_hotosm_data(iso3, feature_type="buildings", geometry="polygons") # CKAN Downloads pop_paths = download_resource_ckan(iso3, dataset_prefix="cod-ps") admin_path = download_resource_ckan(iso3, dataset_prefix="cod-ab") files = [] if raster_path: files.append(raster_path) if pp_path: files.append(pp_path) if roads_path: files.append(roads_path) if buildings_path: files.append(buildings_path) if admin_path: files.extend(admin_path) if pop_paths: files.extend(pop_paths) # Run all available analyses analysis_results = run_analyses( raster_path=raster_path, pp_path=pp_path, roads_path=roads_path, buildings_path=buildings_path, admin_path=admin_path, pop_paths=pop_paths ) if analysis_results: analysis_msg = analysis_results else: analysis_msg = "✅ No analyses could be run." if not files: msg += " ⚠️ No data could be downloaded." return files, msg, analysis_msg def get_subdivision_options(iso_code): has_features, data = has_associated_features(iso_code) subdivisions = fetch_gis_subdivisions(iso_code) # Lookup the country name from iso_options country_name = next((name for name, code in iso_options if code == iso_code), iso_code) if not has_features: # Only subdivisions available return ( gr.update(choices=subdivisions, visible=True, value=None), gr.update(visible=False), gr.update(value=f"✅ Subdivisions loaded for {country_name}. Please select one.") ) else: # Add the whole country as the first option country_option = f"{country_name} (Country level)" return ( gr.update(choices=[country_option] + subdivisions, visible=True, value=country_option), gr.update(visible=False), gr.update(value=f"✅ Country and subdivisions loaded for {country_name}. You may choose either.") ) iso_input.change( fn=lambda iso: gr.update(value="🔄 Loading subdivisions..."), inputs=iso_input, outputs=status_text ).then( fn=get_subdivision_options, inputs=iso_input, outputs=[subdivision_input, generate_btn, status_text] ) subdivision_input.change( fn=lambda s: gr.update(visible=True) if s else gr.update(visible=False), inputs=subdivision_input, outputs=generate_btn ) generate_btn.click( fn=wrapper, inputs=[hazard_input, iso_input, rp_input, subdivision_input], outputs=[file_output, status_text, analysis_output] ) if __name__ == "__main__": demo.launch(share=True)