@@ -56,43 +56,113 @@ ggplot(fired) +
5656 ylab('Event duration (days)')
5757```
5858
59- In Python, we need 5 libraries to download and visualize the data.
60-
61- ``` {python, cache=TRUE, message=FALSE, warning=FALSE, results='hide'}
62- import requests
63- import zipfile
64- import geopandas as gpd
65- import matplotlib.pyplot as plt
66- import seaborn as sns
67- ```
68-
69- Download the data set:
70-
71- ``` {python, cache=TRUE, message=FALSE, warning=FALSE, results='hide'}
72- url = "https://scholar.colorado.edu/downloads/zw12z650d"
73- fired = requests.get(url)
74- data_file = "fired.zip"
75- with open(data_file, 'wb') as f:
76- f.write(fired.content)
77-
78- # Unzip the file
79- with zipfile.ZipFile(data_file, 'r') as zip_ref:
80- zip_ref.extractall()
81- ```
82-
83- Read it:
84-
85- ``` {python, cache=TRUE, message=FALSE, warning=FALSE, results='hide'}
86- fired = gpd.read_file("fired_conus_ak_to_January_2022_gpkg_shp/conus_ak_to2022001_events.shp")
87- ```
88-
89- Plot fire duration as a function of ignition day:
90-
91- ``` {python, cache=TRUE, message=FALSE, warning=FALSE, results='hide'}
92- plt.figure()
93- sns.scatterplot(data=fired, x='ig_day', y='event_dur')
94- sns.set_style('whitegrid')
95- plt.xlabel('Day')
96- plt.ylabel('Event duration (days)')
97- plt.show()
59+ In Python, the following code downloads the FIRED data set from CU Scholar using a
60+ session to avoid 403 errors and loads it into a GeoDataFrame. The snippet also
61+ shows how to access event- and daily-level layers.
62+
63+ ``` {python, cache=TRUE, message=FALSE, warning=FALSE}
64+ # Programmatically fetch FIRED from CU Scholar (handles 403) and load as GeoDataFrame.
65+ # No firedpy fallback.
66+
67+ import os, io, zipfile, tempfile, warnings
68+ from pathlib import Path
69+ import requests
70+ import geopandas as gpd
71+
72+ def load_fired_conus_ak(
73+ dataset_page: str = "https://scholar.colorado.edu/concern/datasets/d504rm74m",
74+ download_id: str = "h702q749s", # file id (works for Nov 2001–Mar 2021 CONUS+AK events/daily)
75+ which: str = "events", # "events" or "daily"
76+ prefer: str = "gpkg", # "gpkg" or "shp"
77+ timeout: int = 180,
78+ ) -> gpd.GeoDataFrame:
79+ """
80+ Returns a GeoDataFrame with FIRED polygons (CONUS+AK, Nov 2001–Mar 2021) in EPSG:4326.
81+ Downloads the ZIP from CU Scholar using a session + referer to avoid 403,
82+ extracts in a temporary directory, and reads the requested layer.
83+
84+ Parameters
85+ ----------
86+ dataset_page : str
87+ CU Scholar dataset landing page (used as Referer).
88+ download_id : str
89+ The /downloads/<id> token for the ZIP on that page.
90+ which : {"events","daily"}
91+ Choose event-level polygons or daily polygons.
92+ prefer : {"gpkg","shp"}
93+ Prefer GeoPackage or Shapefile when both exist.
94+ timeout : int
95+ Seconds for HTTP requests.
96+
97+ Raises
98+ ------
99+ RuntimeError if the download or layer selection fails.
100+ """
101+ assert which in ("events", "daily")
102+ assert prefer in ("gpkg", "shp")
103+
104+ inner = {
105+ ("events","gpkg"): "fired_conus-ak_events_nov2001-march2021.gpkg",
106+ ("events","shp") : "fired_conus-ak_events_nov2001-march2021.shp",
107+ ("daily","gpkg") : "fired_conus-ak_daily_nov2001-march2021.gpkg",
108+ ("daily","shp") : "fired_conus-ak_daily_nov2001-march2021.shp",
109+ }
110+ want_primary = inner[(which, prefer)]
111+ want_alternate = inner[(which, "shp" if prefer=="gpkg" else "gpkg")]
112+
113+ sess = requests.Session()
114+ headers = {
115+ "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
116+ "(KHTML, like Gecko) Chrome/125.0 Safari/537.36",
117+ "Accept": "*/*",
118+ "Referer": dataset_page,
119+ "Connection": "keep-alive",
120+ }
121+ r0 = sess.get(dataset_page, headers=headers, timeout=timeout)
122+ r0.raise_for_status()
123+
124+ zip_url = f"https://scholar.colorado.edu/downloads/{download_id}"
125+ resp = sess.get(zip_url, headers=headers, stream=True, timeout=timeout, allow_redirects=True)
126+ resp.raise_for_status()
127+
128+ ct = resp.headers.get("Content-Type", "")
129+ cd = resp.headers.get("Content-Disposition", "")
130+ if ("zip" not in ct.lower()) and (".zip" not in cd.lower()):
131+ warnings.warn("Response did not clearly indicate a ZIP; proceeding anyway.")
132+
133+ with tempfile.TemporaryDirectory() as td:
134+ zpath = Path(td) / "fired.zip"
135+ with open(zpath, "wb") as f:
136+ for chunk in resp.iter_content(chunk_size=1<<20):
137+ if chunk:
138+ f.write(chunk)
139+
140+ with zipfile.ZipFile(zpath, "r") as z:
141+ names = z.namelist()
142+ chosen = None
143+ for n in (want_primary, want_alternate):
144+ if n in names:
145+ chosen = n
146+ break
147+ if chosen is None:
148+ for n in names:
149+ if which in n and (n.endswith(".gpkg") or n.endswith(".shp")):
150+ chosen = n
151+ break
152+ if chosen is None:
153+ raise RuntimeError("Could not find a FIRED layer inside the ZIP "
154+ f"(looked for '{want_primary}' / '{want_alternate}').")
155+
156+ z.extract(chosen, path=td)
157+ gdf = gpd.read_file(Path(td) / chosen)
158+ if gdf.crs:
159+ gdf = gdf.to_crs("EPSG:4326")
160+ else:
161+ gdf.set_crs("EPSG:4326", inplace=True)
162+ return gdf
163+
164+ # -------------------- Example usage --------------------
165+ fired_events = load_fired_conus_ak(which="events", prefer="gpkg")
166+ fired_daily = load_fired_conus_ak(which="daily", prefer="gpkg")
167+ print(fired_events.head())
98168```
0 commit comments