{ "cells": [ { "cell_type": "markdown", "id": "32ae1cf3-5323-41ff-bcb4-6d7b5ceacd80", "metadata": {}, "source": [ "# Wildfire Live Map\n", "\n", "This code shows how to generate a map of wildfire hotspots in the United States. The map uses data from the NASA Fire Information for Resource Management System (FIRMS) API and updates every minute.\n", "\n", "To get a free API key (\"MAP_KEY\"), sign up here: https://firms.modaps.eosdis.nasa.gov/api/map_key\n", "\n", "MAP_KEY limit is 5000 transactions / 10-minute interval. Put the key in the `.env` file." ] }, { "cell_type": "code", "execution_count": 1, "id": "3b9904fc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Intel MKL WARNING: Support of Intel(R) Streaming SIMD Extensions 4.2 (Intel(R) SSE4.2) enabled only processors has been deprecated. Intel oneAPI Math Kernel Library 2025.0 will require Intel(R) Advanced Vector Extensions (Intel(R) AVX) instructions.\n", "Intel MKL WARNING: Support of Intel(R) Streaming SIMD Extensions 4.2 (Intel(R) SSE4.2) enabled only processors has been deprecated. Intel oneAPI Math Kernel Library 2025.0 will require Intel(R) Advanced Vector Extensions (Intel(R) AVX) instructions.\n" ] } ], "source": [ "import os\n", "import pandas as pd\n", "import geopandas as gpd\n", "import atexit\n", "\n", "from dotenv import load_dotenv\n", "from ipyleaflet import Map, GeoData, CircleMarker, MarkerCluster\n", "from apscheduler.schedulers.background import BackgroundScheduler\n", "from IPython.display import display, clear_output\n", "\n", "# Load environment variables from .env file\n", "load_dotenv()\n", "\n", "# Get the API key from the environment variable\n", "API_KEY = os.getenv('FIRMS_API_KEY')\n", "\n", "# Define the URL for the FIRMS API\n", "FIRMS_URL = f\"https://firms.modaps.eosdis.nasa.gov/api/country/csv/{API_KEY}/VIIRS_NOAA21_NRT/USA/2\"" ] }, { "cell_type": "code", "execution_count": 2, "id": "ead78cc6", "metadata": {}, "outputs": [], "source": [ "def get_color(frp):\n", " \"\"\"\n", " Returns a color based on the Fire Radiative Power (FRP) value\n", " Low FRP: Green/Yellow\n", " Medium FRP: Orange\n", " High FRP: Red\n", " Very High FRP: Dark Red\n", " \"\"\"\n", " if frp < 1.64:\n", " return '#ffff00' # Yellow\n", " elif frp < 3.77:\n", " return '#ffa500' # Orange\n", " elif frp < 11.77:\n", " return '#ff4500' # Red-Orange\n", " else:\n", " return '#ff0000' # Red" ] }, { "cell_type": "markdown", "id": "c32a10a4-dc68-44cf-9c2a-dac39a97871f", "metadata": {}, "source": [ "## Function to fetch and process data into a DataFrame" ] }, { "cell_type": "code", "execution_count": 3, "id": "56100fd7-b241-45ab-981f-c86cf3e24fdc", "metadata": {}, "outputs": [], "source": [ "def fetch_and_process_fire_data():\n", " \"\"\"Fetch and process fire data from the FIRMS API and return a GeoDataFrame with the data and color information.\"\"\"\n", " try:\n", " df = pd.read_csv(FIRMS_URL)\n", " df_subset = df[['latitude', 'longitude', 'bright_ti4', 'acq_date', 'acq_time', 'confidence', 'frp']]\n", " \n", " # Filter out low confidence data\n", " filtered_df = df_subset[df_subset['confidence'] != 'l'].copy()\n", " \n", " # Filter out rows with NaN values in critical columns\n", " filtered_df = filtered_df.dropna(subset=['latitude', 'longitude', 'frp'])\n", "\n", " # Add color column based on FRP\n", " filtered_df.loc[:, 'color'] = filtered_df['frp'].apply(get_color)\n", "\n", " # Create GeoDataFrame with style information\n", " geodf = gpd.GeoDataFrame(\n", " filtered_df, \n", " geometry=gpd.points_from_xy(filtered_df.longitude, filtered_df.latitude)\n", " )\n", " \n", " return geodf\n", " except Exception as e:\n", " print(f\"Error fetching data: {e}\")\n", " return None" ] }, { "cell_type": "markdown", "id": "b398d9d7", "metadata": {}, "source": [ "## Display Wildfire Map" ] }, { "cell_type": "code", "execution_count": 4, "id": "d1d1cf4a", "metadata": {}, "outputs": [], "source": [ "# Create a map centered around the USA\n", "wildfire_map = Map(center=(37.0902, -95.7129), zoom=4)\n", "\n", "def scheduled_task():\n", " \"\"\"Defined a scheduled task to update the layers of the map\"\"\"\n", "\n", " # Clear existing layers (retain only the base layer)\n", " wildfire_map.layers = wildfire_map.layers[:1] # Keep only the base layer\n", "\n", " # Fetch and process fire data\n", " gdf = fetch_and_process_fire_data()\n", " if gdf is not None:\n", " markers = []\n", " \n", " # Iterate over each point in the GeoDataFrame\n", " for idx, row in gdf.iterrows():\n", " # Create a CircleMarker with the specified color\n", " marker = CircleMarker(\n", " location=(row['latitude'], row['longitude']),\n", " radius=8,\n", " color=row['color'],\n", " fill_color=row['color'],\n", " fill_opacity=0.7,\n", " stroke=True,\n", " weight=1\n", " )\n", "\n", " # Add the marker to the cluster\n", " markers.append(marker)\n", "\n", " cluster = MarkerCluster(markers=markers)\n", " wildfire_map.add_layer(cluster)" ] }, { "cell_type": "code", "execution_count": 5, "id": "404b909f", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Run time of job \"scheduled_task (trigger: interval[0:01:00], next run at: 2025-01-20 09:26:18 PST)\" was missed by 0:00:21.741856\n", "Run time of job \"scheduled_task (trigger: interval[0:01:00], next run at: 2025-01-20 09:28:18 PST)\" was missed by 0:00:15.745688\n", "Run time of job \"scheduled_task (trigger: interval[0:01:00], next run at: 2025-01-20 09:29:18 PST)\" was missed by 0:00:51.825884\n", "Run time of job \"scheduled_task (trigger: interval[0:01:00], next run at: 2025-01-20 09:36:18 PST)\" was missed by 0:00:36.374801\n", "Run time of job \"scheduled_task (trigger: interval[0:01:00], next run at: 2025-01-20 09:45:18 PST)\" was missed by 0:00:08.867419\n" ] } ], "source": [ "# Set up a background scheduler to update the map every minute\n", "scheduler = BackgroundScheduler()\n", "scheduler.add_job(scheduled_task, 'interval', minutes=1) # Fetch every minute\n", "scheduler.start()\n", "\n", "# Display the initial map\n", "scheduled_task()" ] }, { "cell_type": "code", "execution_count": 6, "id": "8d4cde04", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "0c0bb33ad78646bdba584bb64946ef67", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Map(center=[37.0902, -95.7129], controls=(ZoomControl(options=['position', 'zoom_in_text', 'zoom_in_title', 'z…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# To prevent orphaned background processes when the notebook is closed, ensure that the scheduler shuts down properly.\n", "atexit.register(lambda: scheduler.shutdown())\n", "\n", "# Display the map (it will be updated every minute)\n", "display(wildfire_map)" ] }, { "cell_type": "code", "execution_count": null, "id": "b2975c58", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "firms-data", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.16" } }, "nbformat": 4, "nbformat_minor": 5 }