This repository contains the Dash web application for visualizing hurricane impact forecasts. The application displays interactive maps, probabilistic analysis, and impact reports based on pre-processed hurricane data.
- Ahead-of-the-Storm-DATAPIPELINE: Data processing pipeline for initializing base data and processing storm impact files that are read by the application
- TC-ECMWF-Forecast-Pipeline: Pipeline for processing ECMWF BUFR tropical cyclone and wind forecast data
- Python 3.11+ installed
- Virtual environment activated (
.venv) - Environment variables configured in
.envfile- Start from the provided example:
cp sample_env .env - Edit values to match your environment (Snowflake, optional Azure)
- Start from the provided example:
- Pre-processed data available (see Data Requirements below)
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install --upgrade pip
pip install -r requirements.txtSNOWFLAKE_ACCOUNT,SNOWFLAKE_USER,SNOWFLAKE_PASSWORDSNOWFLAKE_WAREHOUSE,SNOWFLAKE_DATABASE,SNOWFLAKE_SCHEMASNOWFLAKE_USE_RO(optional, defaultfalse): whentrue, connects usingSNOWFLAKE_RO_USER/SNOWFLAKE_RO_PASSWORDinstead ofSNOWFLAKE_USER/SNOWFLAKE_PASSWORD(a separate, more restricted read-only role)
RESULTS_DIR(default:results): stores report templates and generated JSON reportsROOT_DATA_DIR(default:geodb)VIEWS_DIR(default:aos_views)REPORT_TEMPLATE_FILE(default:impact-report-template.html)
Two independent variables control how the app loads impact data:
IMPACT_DATA_SOURCE: controls which data source is used for impact views:
-
STAGE(default): downloads CSV/Parquet files from the file store (seeIMPACT_DATA_STOREbelow) -
SQL: queries Snowflake materialized tables (*_MAT) directly via SQL, faster, no file downloads, works regardless ofIMPACT_DATA_STOREas long as Snowflake credentials are presentWhen
IMPACT_DATA_SOURCE=SQLthe app queries:MERCATOR_TILE_IMPACT_MAT: tile-level probabilistic impactADMIN_ALL_IMPACT_MAT: admin-region impact with probabilityMERCATOR_TILE_CCI_MAT/ADMIN_ALL_CCI_MAT: Child Climate Index overlaysSCHOOL_IMPACT_MAT/HC_IMPACT_MAT: point data for schools and health centresTRACK_MAT: per-ensemble-member severity and envelope geometry
These tables must be set up first, see
snowflake/mat_tables/README.md.
IMPACT_DATA_STORE: controls where stage files are stored (only relevant when IMPACT_DATA_SOURCE=STAGE):
LOCAL(default): local filesystem- For local development: uses local filesystem
- The real production SPCS deployment uses
SNOWFLAKEinstead (seeIMPACT_DATA_SOURCEabove), not a mounted volume -- theDockerfile's ownVOLUME ["/DataStore"]declaration (capitalized, different from this doc's older lowercase/datastorereferences) is unused by the live service
BLOB: Azure Blob Storage (read-only)SNOWFLAKE: Snowflake internal stage (read-only)
Note: Snowflake is used for BOTH raw hurricane forecast data (TC_TRACKS / TC_ENVELOPES_COMBINED tables) AND impact data. IMPACT_DATA_SOURCE=SQL uses the Snowflake connection that is already required for forecast data.
- If using Azure Blob Storage:
ADLS_ACCOUNT_URL,ADLS_SAS_TOKEN,ADLS_CONTAINER_NAME - If using Snowflake stage:
SNOWFLAKE_STAGE_NAME(name of the Snowflake internal stage)
SPCS_RUN: set totrueto enable OAuth token auth via/snowflake/session/token(default:false)SPCS_TOKEN_PATH(default:/snowflake/session/token)SNOWFLAKE_HOST,SNOWFLAKE_PORT: required whenSPCS_RUN=true
TILE_SERVER_URL(optional): the tile server base URL used by browser clientside callbacks. Two different defaults apply depending on deployment mode: in local development (bothSPCS_RUNandBEHIND_REVERSE_PROXYunset/false) the app falls back tohttp://localhost:8001; when running behind nginx (SPCS or Azure,BEHIND_REVERSE_PROXY=true) it's sent to the browser as an empty string so the browser resolves tile/geojson requests againstwindow.location.origininstead. Set explicitly only if the tile server is on a different host than the one serving the Dash app.BEHIND_REVERSE_PROXY(optional, defaultfalse): settruewhenever nginx fronts both Dash and the tile server on the same host (SPCS and Azure both use this), which changes theTILE_SERVER_URLbehavior above.
MAPBOX_ACCESS_TOKEN(optional, falls back to OpenStreetMap tiles)
When IMPACT_DATA_SOURCE=SQL (recommended): no local data files are needed. The app queries Snowflake MAT tables directly.
When IMPACT_DATA_SOURCE=STAGE: pre-processed impact views must be available in the configured IMPACT_DATA_STORE. For IMPACT_DATA_STORE=LOCAL, the following directories are expected:
{ROOT_DATA_DIR}/{VIEWS_DIR}/mercator_views/: base Mercator tiles (demographic/infrastructure){ROOT_DATA_DIR}/{VIEWS_DIR}/school_views/: school impact data{ROOT_DATA_DIR}/{VIEWS_DIR}/hc_views/: health centre impact data{ROOT_DATA_DIR}/{VIEWS_DIR}/shelter_views/: shelter impact data{ROOT_DATA_DIR}/{VIEWS_DIR}/wash_views/: WASH facility impact data{ROOT_DATA_DIR}/{VIEWS_DIR}/track_views/: hurricane track data
To generate the required data, follow the setup guide in the Ahead-of-the-Storm-DATAPIPELINE repository:
- Initialize base data (demographic and infrastructure data, one-time setup)
- Process storm data (run regularly to update with new storm data from Snowflake)
The hurricane forecast data is processed by the TC-ECMWF-Forecast-Pipeline and loaded into Snowflake.
The Dash app and tile server must both be running. Start them in two separate terminals:
# Terminal 1: Dash app (http://127.0.0.1:8050)
python app.py
# Terminal 2: FastAPI tile server (http://127.0.0.1:8001)
# IMPACT_DATA_STORE=SNOWFLAKE is required here even if your .env sets LOCAL for the
# Dash app -- the tile server's own impact-tile endpoints need it explicitly.
IMPACT_DATA_STORE=SNOWFLAKE uvicorn services.tile_server:app --host 0.0.0.0 --port 8001 --reloadProduction runs as a Docker container on Snowflake Container Services (SPCS). entrypoint.sh orchestrates three processes inside the container:
nginx 0.0.0.0:8000 (public, reverse proxy + tile cache)
├─► gunicorn Dash 127.0.0.1:8050 (1 worker × 8 threads)
└─► uvicorn tiles 127.0.0.1:8001 (FastAPI tile server)
# Build
docker build -t unicef-dash-app:latest . --platform=linux/amd64A single gunicorn worker (1 process × 8 threads) is required to avoid fork-safety issues with snowflake-connector-python and Dash callback-map race conditions. Do not increase --workers beyond 1.
- Map shell (
/): Full-bleed Global/Country-Analysis map, the app's main view. Independently toggleable hazard layers (Sustained Wind, Gust, River Flooding, Rainfall, Storm Surge preview) with per-hazard threshold sliders; a tile-click Hazard Contribution popup; a Full Impact Breakdown modal with a printable report page; schools/health centers/shelters/WASH facilities at risk; population impact tiles; i18n (es/fr/bn); a real Alert Email viewer (per-country, opened from the Active Storms list) and a real Warning Email viewer (per-storm). An older page with a different feature set (deterministic/probabilistic/worst-case impact metrics) is kept at/legacyas a reference/fallback, unlinked from any navigation.
-
Forecast Analysis (
/analysis): Statistical analysis including:- Box plots showing impact distribution across ensemble members
- Exceedance probability curves
- Impact summaries for population, children, infants, schools, health centers, and built surface
- Percentile analysis
-
Impact Report (
/report): HTML-based impact report with detailed administrative-level breakdowns -
AI Agent: the
HURRICANE_INTELLIGENCESnowflake Cortex agent generates situation reports from the same MAT tables the map shell reads (seesnowflake/intelligence/), queried throughsnowflake_utils.py.
- If using
IMPACT_DATA_SOURCE=SQL: verify Snowflake MAT tables are populated (seesnowflake/mat_tables/README.md) - If using
IMPACT_DATA_SOURCE=STAGE: verify that impact views exist in{ROOT_DATA_DIR}/{VIEWS_DIR}/ - Run the storm processing pipeline from the DATAPIPELINE repository
- Check that Snowflake contains the expected storm data
- Verify all
SNOWFLAKE_*environment variables are set correctly - Check network connectivity to Snowflake
- Ensure Snowflake credentials have proper permissions
- Verify the tile server is running:
curl http://localhost:8001/healthshould return{"status":"ok"} - Verify
TILE_SERVER_URLenv var points to the tile server (default:http://localhost:8001) - Verify
MAPBOX_ACCESS_TOKENis set (optional but recommended) - Check browser console for JavaScript errors
- Verify all dependencies are installed:
pip install -r requirements.txt - Check that Python version is 3.11 or higher
- Review error logs for specific package or import errors
- Report template:
{RESULTS_DIR}/impact-report-template.html(default:results/impact-report-template.html) - Generated reports:
{RESULTS_DIR}/jsons/ - Base views:
{ROOT_DATA_DIR}/{VIEWS_DIR}/mercator_views/(e.g.,geodb/aos_views/mercator_views/) - Impact views:
{ROOT_DATA_DIR}/{VIEWS_DIR}/school_views/(schools){ROOT_DATA_DIR}/{VIEWS_DIR}/hc_views/(health centers){ROOT_DATA_DIR}/{VIEWS_DIR}/shelter_views/(shelters){ROOT_DATA_DIR}/{VIEWS_DIR}/wash_views/(WASH facilities){ROOT_DATA_DIR}/{VIEWS_DIR}/track_views/(hurricane tracks)
- Frontend: Dash with Mantine Components, MapLibre GL JS for tile rendering (Dash Leaflet as map container), Plotly for charts
- Backend: Python with GeoPandas for geospatial processing; FastAPI tile server sidecar (
services/tile_server.py, port 8001) serves WebP raster tiles and MVT vector tiles - Data Sources:
- Snowflake: hurricane track/envelope data (
TC_TRACKS,TC_ENVELOPES_COMBINED) and, whenIMPACT_DATA_SOURCE=SQL, impact data via materialized tables (*_MAT) - Pre-processed impact views via giga-spatial, used when
IMPACT_DATA_SOURCE=STAGE(local filesystem, Azure Blob, or Snowflake stage)
- Snowflake: hurricane track/envelope data (
- AI Agent:
HURRICANE_INTELLIGENCESnowflake Cortex agent: generates situation reports from the same MAT tables (seesnowflake/intelligence/) - Deployment: Docker container on Snowflake Container Services (SPCS): nginx reverse proxy + gunicorn Dash app + uvicorn tile server (see
entrypoint.shandDockerfile)
