cboettig commited on
Commit
29b5892
·
1 Parent(s): d44b7a8
Files changed (4) hide show
  1. Dockerfile +1 -1
  2. fire.py +52 -37
  3. pages/01_leafmap.py +3 -32
  4. solara-test.ipynb +91 -53
Dockerfile CHANGED
@@ -13,7 +13,7 @@ COPY /pages ./pages
13
  ENV PROJ_LIB='/opt/conda/share/proj'
14
 
15
  USER root
16
- RUN apt-get update && apt-get -y install git
17
  RUN chown -R ${NB_UID} ${HOME}
18
  USER ${NB_USER}
19
 
 
13
  ENV PROJ_LIB='/opt/conda/share/proj'
14
 
15
  USER root
16
+ RUN apt-get update && apt-get -y install git git-lfs
17
  RUN chown -R ${NB_UID} ${HOME}
18
  USER ${NB_USER}
19
 
fire.py CHANGED
@@ -7,6 +7,43 @@ import geopandas as gpd
7
  import dask.distributed
8
  import matplotlib.pyplot as plt
9
  import rioxarray
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
 
12
  nps = gpd.read_file("/vsicurl/https://huggingface.co/datasets/cboettig/biodiversity/resolve/main/data/NPS.gdb")
@@ -15,47 +52,25 @@ calfire = gpd.read_file("/vsicurl/https://huggingface.co/datasets/cboettig/biodi
15
 
16
  # extract and reproject the Joshua Tree NP Polygon
17
  jtree = nps[nps.PARKNAME == "Joshua Tree"].to_crs(calfire.crs)
 
18
  # All Fires in the DB that intersect the Park
19
  jtree_fires = jtree.overlay(calfire, how="intersection")
20
 
21
  # Extract a polygon if interest. > 2015 for Sentinel, otherwise we can use LandSat
22
  recent = jtree_fires[jtree_fires.YEAR_ > "2015"]
23
  big = recent[recent.Shape_Area == recent.Shape_Area.max()].to_crs("EPSG:4326")
24
- datetime = big.ALARM_DATE.item() + "/" + big.CONT_DATE.item()
 
25
  box = big.buffer(0.005).bounds.to_numpy()[0] # Fire bbox + buffer
26
- #box = jtree.to_crs("EPSG:4326").bounds.to_numpy()[0] # Park bbox
27
-
28
- # STAC Search for this imagery in space/time window
29
- items = (
30
- pystac_client.Client.
31
- open("https://planetarycomputer.microsoft.com/api/stac/v1",
32
- modifier=planetary_computer.sign_inplace).
33
- search(collections=["sentinel-2-l2a"],
34
- bbox=box,
35
- datetime=datetime,
36
- query={"eo:cloud_cover": {"lt": 10}}).
37
- item_collection())
38
-
39
-
40
- # Time to compute:
41
- client = dask.distributed.Client()
42
- # landsat_bands = ["nir08", "swir16"]
43
- sentinel_bands = ["B08", "B12", "SCL"]
44
-
45
- # The magic of gdalwarper. Can also resample, reproject, and aggregate on the fly
46
- data = odc.stac.load(items,
47
- bands=sentinel_bands,
48
- bbox=box
49
- )
50
-
51
- swir = data["B12"].astype("float")
52
- nir = data["B08"].astype("float")
53
-
54
- # can resample and aggregate in xarray. compute with dask
55
- nbs = (((nir - swir) / (nir + swir)).
56
- # resample(time="MS").
57
- # median("time", keep_attrs=True).
58
- compute()
59
- )
60
-
61
- nbs.rio.to_raster(raster_path="nbs.tif", driver="COG")
 
7
  import dask.distributed
8
  import matplotlib.pyplot as plt
9
  import rioxarray
10
+ from datetime import datetime, timedelta
11
+
12
+
13
+ def stac_search(box, datetime):
14
+ # STAC Search for this imagery in space/time window
15
+ items = (
16
+ pystac_client.Client.
17
+ open("https://planetarycomputer.microsoft.com/api/stac/v1",
18
+ modifier=planetary_computer.sign_inplace).
19
+ search(collections=["sentinel-2-l2a"],
20
+ bbox=box,
21
+ datetime=datetime,
22
+ query={"eo:cloud_cover": {"lt": 10}}).
23
+ item_collection())
24
+ return items
25
+
26
+ def compute_nbs(items, box):
27
+ # Time to compute:
28
+ client = dask.distributed.Client()
29
+ # landsat_bands = ["nir08", "swir16"]
30
+ sentinel_bands = ["B08", "B12", "SCL"] # NIR, SWIR, and Cloud Mask
31
+
32
+ # The magic of gdalwarper. Can also resample, reproject, and aggregate on the fly
33
+ data = odc.stac.load(items,
34
+ bands=sentinel_bands,
35
+ bbox=box
36
+ )
37
+ # Compute the Normalized Burn Ratio, must be float
38
+ swir = data["B12"].astype("float")
39
+ nir = data["B08"].astype("float")
40
+ # can resample and aggregate in xarray. compute with dask
41
+ nbs = (((nir - swir) / (nir + swir)).
42
+ # resample(time="MS").
43
+ # median("time", keep_attrs=True).
44
+ compute()
45
+ )
46
+ return nbs
47
 
48
 
49
  nps = gpd.read_file("/vsicurl/https://huggingface.co/datasets/cboettig/biodiversity/resolve/main/data/NPS.gdb")
 
52
 
53
  # extract and reproject the Joshua Tree NP Polygon
54
  jtree = nps[nps.PARKNAME == "Joshua Tree"].to_crs(calfire.crs)
55
+
56
  # All Fires in the DB that intersect the Park
57
  jtree_fires = jtree.overlay(calfire, how="intersection")
58
 
59
  # Extract a polygon if interest. > 2015 for Sentinel, otherwise we can use LandSat
60
  recent = jtree_fires[jtree_fires.YEAR_ > "2015"]
61
  big = recent[recent.Shape_Area == recent.Shape_Area.max()].to_crs("EPSG:4326")
62
+
63
+ # Get bounding box + dates before & after fire for STAC search
64
  box = big.buffer(0.005).bounds.to_numpy()[0] # Fire bbox + buffer
65
+ alarm_date = datetime.strptime(big.ALARM_DATE.item(), "%Y-%m-%dT%H:%M:%S+00:00")
66
+ before_date = alarm_date - timedelta(days=14)
67
+ after_date = alarm_date + timedelta(days=14)
68
+ search_dates = before_date.strftime("%Y-%m-%d") + "/" + after_date.strftime("%Y-%m-%d")
69
+
70
+ # here we go!
71
+ items = stac_search(box, search_dates)
72
+ nbs = compute_nbs(items, box)
73
+
74
+ # write first and last date to tif
75
+ nbs.isel(time=0).rio.to_raster(raster_path="before.tif", driver="COG")
76
+ nbs.isel(time=(nbs.time.size-1)).rio.to_raster(raster_path="after.tif", driver="COG")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pages/01_leafmap.py CHANGED
@@ -23,37 +23,8 @@ big = recent[recent.Shape_Area == recent.Shape_Area.max()].to_crs("EPSG:4326")
23
  datetime = big.ALARM_DATE.item() + "/" + big.CONT_DATE.item()
24
  box = big.buffer(0.01).bounds.to_numpy()[0] # Fire bbox + buffer #box = jtree.to_crs("EPSG:4326").bounds.to_numpy()[0] # Park bbox
25
 
26
- items = ( # STAC Search for this imagery in space/time window
27
- pystac_client.Client.
28
- open("https://planetarycomputer.microsoft.com/api/stac/v1",
29
- modifier=planetary_computer.sign_inplace).
30
- search(collections=["sentinel-2-l2a"],
31
- bbox=box,
32
- datetime=datetime,
33
- query={"eo:cloud_cover": {"lt": 10}}).
34
- item_collection())
35
-
36
- # Time to compute:
37
- client = dask.distributed.Client()
38
- sentinel_bands = ["B08", "B12", "SCL"]
39
- # The magic of gdalwarper. Can also resample, reproject, and aggregate on the fly
40
- data = odc.stac.load(items, bands=sentinel_bands, bbox=box)
41
- swir = data["B12"].astype("float")
42
- nir = data["B08"].astype("float")
43
- # can resample and aggregate in xarray. compute with dask
44
- nbs = (((nir - swir) / (nir + swir)).
45
- # resample(time="MS").
46
- # median("time", keep_attrs=True).
47
- compute()
48
- )
49
-
50
- import tempfile
51
- import os
52
- temp_dir = tempfile.gettempdir()
53
- nbs_file = os.path.join(temp_dir, "random_filename.tif")
54
- nbs.rio.to_raster(raster_path=nbs_file, driver="COG")
55
-
56
- nbs_url = "/vsicurl/https://huggingface.co/datasets/cboettig/solara-data/resolve/main/nbs.tif"
57
 
58
 
59
  class Map(leafmap.Map):
@@ -61,7 +32,7 @@ class Map(leafmap.Map):
61
  super().__init__(**kwargs)
62
  # Add what you want below
63
  self.add_gdf(jtree_fires)
64
- self.add_cog_layer(nbs_url)
65
 
66
 
67
  @solara.component
 
23
  datetime = big.ALARM_DATE.item() + "/" + big.CONT_DATE.item()
24
  box = big.buffer(0.01).bounds.to_numpy()[0] # Fire bbox + buffer #box = jtree.to_crs("EPSG:4326").bounds.to_numpy()[0] # Park bbox
25
 
26
+ before_url = "/vsicurl/https://huggingface.co/datasets/cboettig/solara-data/resolve/main/before.tif"
27
+ after_url = "/vsicurl/https://huggingface.co/datasets/cboettig/solara-data/resolve/main/after.tif"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
 
30
  class Map(leafmap.Map):
 
32
  super().__init__(**kwargs)
33
  # Add what you want below
34
  self.add_gdf(jtree_fires)
35
+ self.add_split_map(before_url, after_url)
36
 
37
 
38
  @solara.component
solara-test.ipynb CHANGED
@@ -2,7 +2,7 @@
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
- "execution_count": 3,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
@@ -20,7 +20,7 @@
20
  },
21
  {
22
  "cell_type": "code",
23
- "execution_count": 4,
24
  "metadata": {},
25
  "outputs": [],
26
  "source": [
@@ -36,7 +36,7 @@
36
  },
37
  {
38
  "cell_type": "code",
39
- "execution_count": 6,
40
  "metadata": {},
41
  "outputs": [],
42
  "source": [
@@ -53,60 +53,96 @@
53
  "# Extract a polygon if interest. > 2015 for Sentinel, otherwise we can use LandSat\n",
54
  "recent = jtree_fires[jtree_fires.YEAR_ > \"2015\"]\n",
55
  "big = recent[recent.Shape_Area == recent.Shape_Area.max()].to_crs(\"EPSG:4326\")\n",
56
- "datetime = big.ALARM_DATE.item() + \"/\" + big.CONT_DATE.item()\n",
57
- "box = big.buffer(0.005).bounds.to_numpy()[0] # Fire bbox + buffer\n",
58
- "#box = jtree.to_crs(\"EPSG:4326\").bounds.to_numpy()[0] # Park bbox\n",
59
- "\n",
60
- "# STAC Search for this imagery in space/time window\n",
61
- "items = (\n",
62
- " pystac_client.Client.\n",
63
- " open(\"https://planetarycomputer.microsoft.com/api/stac/v1\",\n",
64
- " modifier=planetary_computer.sign_inplace).\n",
65
- " search(collections=[\"sentinel-2-l2a\"],\n",
66
- " bbox=box,\n",
67
- " datetime=datetime,\n",
68
- " query={\"eo:cloud_cover\": {\"lt\": 10}}).\n",
69
- " item_collection())\n",
70
- "\n",
71
- "\n",
72
- "# Time to compute:\n",
73
- "\n",
74
- "client = dask.distributed.Client()\n",
75
- "# landsat_bands = [\"nir08\", \"swir16\"]\n",
76
- "sentinel_bands = [\"B08\", \"B12\", \"SCL\"]\n",
77
- "\n",
78
- "# The magic of gdalwarper. Can also resample, reproject, and aggregate on the fly\n",
79
- "data = odc.stac.load(items,\n",
80
- " bands=sentinel_bands,\n",
81
- " bbox=box\n",
82
- ")\n",
83
- "\n",
84
- "swir = data[\"B12\"].astype(\"float\")\n",
85
- "nir = data[\"B08\"].astype(\"float\")\n",
86
- "\n",
87
- "# can resample and aggregate in xarray. compute with dask\n",
88
- "nbs = (((nir - swir) / (nir + swir)).\n",
89
- " # resample(time=\"MS\").\n",
90
- " # median(\"time\", keep_attrs=True).\n",
91
- " compute()\n",
92
- ")\n",
93
- "\n",
94
- "\n",
95
- "import tempfile\n",
96
- "import os\n",
97
- "temp_dir = tempfile.gettempdir()\n",
98
- "nbs_file = os.path.join(temp_dir, \"random_filename.tif\")\n",
99
- "nbs.rio.to_raster(raster_path=nbs_file, driver=\"COG\")\n"
100
  ]
101
  },
102
  {
103
  "cell_type": "code",
104
- "execution_count": 7,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  "metadata": {},
106
  "outputs": [],
107
  "source": [
108
  "\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  "\n",
 
 
 
 
 
 
 
 
 
 
110
  "class Map(leafmap.Map):\n",
111
  " def __init__(self, **kwargs):\n",
112
  " super().__init__(**kwargs)\n",
@@ -114,8 +150,10 @@
114
  " # self.add_gdf(jtree, layer_name = \"Joshua Tree NP\")\n",
115
  " # self.add_gdf(jtree_fires)\n",
116
  " self.add_gdf(big, later_name = big.FIRE_NAME.item())\n",
117
- " self.add_raster(nbs_file)\n",
118
- " self.add_stac_gui()\n",
 
 
119
  "\n",
120
  "\n",
121
  "@solara.component\n",
@@ -141,13 +179,13 @@
141
  },
142
  {
143
  "cell_type": "code",
144
- "execution_count": 8,
145
  "metadata": {},
146
  "outputs": [
147
  {
148
  "data": {
149
  "application/vnd.jupyter.widget-view+json": {
150
- "model_id": "887d65f2a633403a8132bdd1c7ce49b7",
151
  "version_major": 2,
152
  "version_minor": 0
153
  },
@@ -183,7 +221,7 @@
183
  "name": "python",
184
  "nbconvert_exporter": "python",
185
  "pygments_lexer": "ipython3",
186
- "version": "3.10.12"
187
  }
188
  },
189
  "nbformat": 4,
 
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
+ "execution_count": 2,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
 
20
  },
21
  {
22
  "cell_type": "code",
23
+ "execution_count": 3,
24
  "metadata": {},
25
  "outputs": [],
26
  "source": [
 
36
  },
37
  {
38
  "cell_type": "code",
39
+ "execution_count": 4,
40
  "metadata": {},
41
  "outputs": [],
42
  "source": [
 
53
  "# Extract a polygon if interest. > 2015 for Sentinel, otherwise we can use LandSat\n",
54
  "recent = jtree_fires[jtree_fires.YEAR_ > \"2015\"]\n",
55
  "big = recent[recent.Shape_Area == recent.Shape_Area.max()].to_crs(\"EPSG:4326\")\n",
56
+ "box = big.buffer(0.01).bounds.to_numpy()[0] # Fire bbox + buffer\n",
57
+ "#box = jtree.to_crs(\"EPSG:4326\").bounds.to_numpy()[0] # Park bbox\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  ]
59
  },
60
  {
61
  "cell_type": "code",
62
+ "execution_count": 5,
63
+ "metadata": {},
64
+ "outputs": [],
65
+ "source": [
66
+ "from datetime import datetime, timedelta\n",
67
+ "alarm_date = datetime.strptime(big.ALARM_DATE.item(), \"%Y-%m-%dT%H:%M:%S+00:00\") \n",
68
+ "before_date = alarm_date - timedelta(days=14)\n",
69
+ "after_date = alarm_date + timedelta(days=14)"
70
+ ]
71
+ },
72
+ {
73
+ "cell_type": "code",
74
+ "execution_count": 6,
75
+ "metadata": {},
76
+ "outputs": [],
77
+ "source": [
78
+ "search_dates = big.ALARM_DATE.item() + \"/\" + big.CONT_DATE.item()\n",
79
+ "search_dates = before_date.strftime(\"%Y-%m-%d\") + \"/\" + after_date.strftime(\"%Y-%m-%d\")"
80
+ ]
81
+ },
82
+ {
83
+ "cell_type": "code",
84
+ "execution_count": null,
85
  "metadata": {},
86
  "outputs": [],
87
  "source": [
88
  "\n",
89
+ "def stac_search(box, datetime): \n",
90
+ " # STAC Search for this imagery in space/time window\n",
91
+ " items = (\n",
92
+ " pystac_client.Client.\n",
93
+ " open(\"https://planetarycomputer.microsoft.com/api/stac/v1\",\n",
94
+ " modifier=planetary_computer.sign_inplace).\n",
95
+ " search(collections=[\"sentinel-2-l2a\"],\n",
96
+ " bbox=box,\n",
97
+ " datetime=datetime,\n",
98
+ " query={\"eo:cloud_cover\": {\"lt\": 10}}).\n",
99
+ " item_collection())\n",
100
+ " return items\n",
101
+ "\n",
102
+ "def compute_nbs(items, box):\n",
103
+ " # Time to compute:\n",
104
+ " client = dask.distributed.Client()\n",
105
+ " # landsat_bands = [\"nir08\", \"swir16\"]\n",
106
+ " sentinel_bands = [\"B08\", \"B12\", \"SCL\"] # NIR, SWIR, and Cloud Mask\n",
107
+ "\n",
108
+ " # The magic of gdalwarper. Can also resample, reproject, and aggregate on the fly\n",
109
+ " data = odc.stac.load(items,\n",
110
+ " bands=sentinel_bands,\n",
111
+ " bbox=box\n",
112
+ " )\n",
113
+ " # Compute the Normalized Burn Ratio, must be float\n",
114
+ " swir = data[\"B12\"].astype(\"float\")\n",
115
+ " nir = data[\"B08\"].astype(\"float\")\n",
116
+ " # can resample and aggregate in xarray. compute with dask\n",
117
+ " nbs = (((nir - swir) / (nir + swir)).\n",
118
+ " # resample(time=\"MS\").\n",
119
+ " # median(\"time\", keep_attrs=True).\n",
120
+ " compute()\n",
121
+ " )\n",
122
+ " return nbs\n",
123
+ "\n",
124
+ "items = stac_search(box, search_dates)\n",
125
+ "nbs = compute_nbs(items, box)\n",
126
+ "\n"
127
+ ]
128
+ },
129
+ {
130
+ "cell_type": "code",
131
+ "execution_count": 11,
132
+ "metadata": {},
133
+ "outputs": [],
134
+ "source": [
135
  "\n",
136
+ "nbs.isel(time=0).rio.to_raster(raster_path=\"before.tif\", driver=\"COG\")\n",
137
+ "nbs.isel(time=(nbs.time.size-1)).rio.to_raster(raster_path=\"after.tif\", driver=\"COG\")\n"
138
+ ]
139
+ },
140
+ {
141
+ "cell_type": "code",
142
+ "execution_count": 14,
143
+ "metadata": {},
144
+ "outputs": [],
145
+ "source": [
146
  "class Map(leafmap.Map):\n",
147
  " def __init__(self, **kwargs):\n",
148
  " super().__init__(**kwargs)\n",
 
150
  " # self.add_gdf(jtree, layer_name = \"Joshua Tree NP\")\n",
151
  " # self.add_gdf(jtree_fires)\n",
152
  " self.add_gdf(big, later_name = big.FIRE_NAME.item())\n",
153
+ " #self.add_raster(\"before.tif\", layer_name = \"before\", colormap=\"viridis\")\n",
154
+ " #self.add_raster(\"after.tif\", layer_name = \"after\", colormap=\"viridis\")\n",
155
+ " self.split_map(\"before.tif\", \"after.tif\")\n",
156
+ " #self.add_stac_gui()\n",
157
  "\n",
158
  "\n",
159
  "@solara.component\n",
 
179
  },
180
  {
181
  "cell_type": "code",
182
+ "execution_count": 15,
183
  "metadata": {},
184
  "outputs": [
185
  {
186
  "data": {
187
  "application/vnd.jupyter.widget-view+json": {
188
+ "model_id": "b0f148fc3c664528a23eeec53cec6a03",
189
  "version_major": 2,
190
  "version_minor": 0
191
  },
 
221
  "name": "python",
222
  "nbconvert_exporter": "python",
223
  "pygments_lexer": "ipython3",
224
+ "version": "3.11.6"
225
  }
226
  },
227
  "nbformat": 4,