Skip to content

Commit 8662c73

Browse files
mfisher87Mary-h86
andcommitted
Improve demo notebook narrative
"You should notice" callouts "In the JupyterGIS GUI..." callouts More prompts for UI actions Better basemap TODO: Visualizing correlation ... and more. Co-authored-by: Maryam Hosseini <[email protected]>
1 parent a3631a5 commit 8662c73

File tree

2 files changed

+152
-19
lines changed

2 files changed

+152
-19
lines changed

modules/06-geojupyter/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.jGIS

modules/06-geojupyter/demo.ipynb

Lines changed: 151 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525
"\n",
2626
"You can [🔗try it right now in JupyterLite](https://jupytergis.readthedocs.io/en/latest/lite/lab/)!\n",
2727
"\n",
28-
"Let's explore some functionality together (based on [🔗Carl Boettiger](https://ourenvironment.berkeley.edu/people/carl-boettiger)'s [🔗ESPM-288 course](https://espm-288.carlboettiger.info/)). We'll explore whether neighborhoods that were highly-rated (A) under the disciminatory 1930s practice of [🔗redlining](https://en.wikipedia.org/wiki/Redlining) are greener today than neighborhoods graded D?"
28+
"Let's explore some functionality together (based on [🔗Carl Boettiger](https://ourenvironment.berkeley.edu/people/carl-boettiger)'s [🔗ESPM-288 course](https://espm-288.carlboettiger.info/)). We'll explore the question: are neighborhoods that were highly-rated (A) under the disciminatory 1930s practice of [🔗redlining](https://en.wikipedia.org/wiki/Redlining) are greener today than neighborhoods graded D?\n",
29+
"\n",
30+
":::{warning}\n",
31+
"Please be aware that JupyterGIS is a young project and it's likely you'll run in to bugs.\n",
32+
"We encourage you to try to break it and [report issues you find on GitHub](https://github.com/geojupyter/jupytergis/issues)!\n",
33+
":::"
2934
]
3035
},
3136
{
@@ -72,15 +77,26 @@
7277
"source": [
7378
"from jupytergis import GISDocument\n",
7479
"\n",
75-
"jgis_project = GISDocument()\n",
80+
"jgis_project = GISDocument(\"new_haven_redlining_analysis.jGIS\")\n",
7681
"jgis_project.add_raster_layer(\n",
77-
" url=\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\",\n",
82+
" url=\"https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png\",\n",
7883
" name=\"Basemap\",\n",
7984
")\n",
8085
"\n",
86+
"# Open this in a new panel on the right\n",
8187
"jgis_project.sidecar()"
8288
]
8389
},
90+
{
91+
"cell_type": "markdown",
92+
"id": "40c7da62-6405-4bb7-8c81-ff950db59ff3",
93+
"metadata": {},
94+
"source": [
95+
"You should see a map on the right with a simple dark basemap.\n",
96+
"\n",
97+
"Try closing or shrinking the file browser on the left to make more room!"
98+
]
99+
},
84100
{
85101
"cell_type": "markdown",
86102
"id": "b2133145-ef92-4d5e-84b8-a53546ef1fe1",
@@ -108,14 +124,30 @@
108124
" con\n",
109125
" .read_geo(\"/vsicurl/http://dsl.richmond.edu/panorama/redlining/static/mappinginequality.gpkg\")\n",
110126
" .filter(_.city == \"New Haven\", _.residential)\n",
127+
" # Add a numeric grade to provide a scalar value for JupyterGIS symbology\n",
128+
" # \"A\" is ascii value 65, so we subtract 64 to start with 1.\n",
129+
" # TODO: Do this in Pandas not DuckDB! (.astype('category').cat.codes)\n",
130+
" # Or: https://pandas.pydata.org/docs/reference/api/pandas.Series.map.html#pandas.Series.map\n",
131+
" .mutate(numeric_grade=_.grade.ascii_str() - 64)\n",
111132
")\n",
112133
"\n",
113134
"new_haven_redlining = redlines.execute().set_crs(\"EPSG:4326\")\n",
114-
"# TODO: Set a numeric grade for graduated symbology\n",
135+
"new_haven_bbox = new_haven_redlining.total_bounds\n",
115136
"\n",
116137
"new_haven_redlining.to_file(INEQUALITY_GEOJSON_FILE, engine=\"fiona\")\n",
138+
"new_haven_redlining.head()"
139+
]
140+
},
141+
{
142+
"cell_type": "markdown",
143+
"id": "d5b8285d-b3c3-4dc8-b6dd-6c2c654ea806",
144+
"metadata": {},
145+
"source": [
146+
"You should notice that 5 rows of data are shown above. They're all residential neighborhoods with `grade` \"A\", meaning the neighborhoods were mostly occupied by white citizens. This grade was used to discriminate against non-white people seeking home loans.\n",
147+
"\n",
148+
"There is a `fill` column containing a hexadecimal color code, and a `geom` column containing polygon shapes.\n",
117149
"\n",
118-
"new_haven_bbox = new_haven_redlining.total_bounds"
150+
"There's an additional `numeric_grade` column which contains a number-encoded version of the `grade` column."
119151
]
120152
},
121153
{
@@ -125,7 +157,7 @@
125157
"source": [
126158
"#### Explore the data\n",
127159
"\n",
128-
"Let's explore the data a little bit. After running the cell below, **right-click the \"New Haven neighborhood redlining\" layer** in the JupyterGIS interface, and **select \"Zoom to layer\"**. "
160+
"Let's explore the data a little bit in JupyterGIS. First, add the data to the map:"
129161
]
130162
},
131163
{
@@ -141,16 +173,49 @@
141173
");"
142174
]
143175
},
176+
{
177+
"cell_type": "markdown",
178+
"id": "d10bc86d-5c57-4b81-b52b-731bcd153059",
179+
"metadata": {},
180+
"source": [
181+
"You should notice that the layers panel on the left of the JupyterGIS UI contains a new layer: \"New Haven neighborhood redlining\"."
182+
]
183+
},
144184
{
145185
"cell_type": "markdown",
146186
"id": "7663ab54-2d69-479b-b8ce-cbdc0bc13143",
147187
"metadata": {},
148188
"source": [
189+
":::{important} In the JupyterGIS UI...\n",
190+
"After running the cell above, **right-click the \"New Haven neighborhood redlining\" layer** in the JupyterGIS interface, and **select \"Zoom to layer\"**. \n",
191+
"\n",
149192
"Now, with the \"New Haven neighborhood redlining\" layer selected, **click the `i` (identify) icon in the toolbar** at the top of the JupyterGIS interface.\n",
150193
"\n",
151194
"Select some neighborhoods and view their \"Grade\" and \"Category\" attributes.\n",
195+
":::"
196+
]
197+
},
198+
{
199+
"cell_type": "markdown",
200+
"id": "37e363b6-8fbc-4cf0-aa77-1d099872fca7",
201+
"metadata": {},
202+
"source": [
203+
"#### Configure symbology\n",
204+
"\n",
205+
"This dataset has a \"fill\" attribute which determines which color each polygon should be rendered with based on its grade.\n",
206+
"\n",
207+
"::::::{important} In the JupyterGIS UI...\n",
152208
"\n",
153-
"TODO: Symbologize on numeric grade"
209+
":::{warning} Beware of bugs!\n",
210+
"The symbology menu is a bit fragile. Stick to the instructions and you'll be OK! If anything goes wrong, try removing the layer (right-click) and re-adding it.\n",
211+
":::\n",
212+
"\n",
213+
"Right-click the layer \"New Haven neighborhood redlining\" and select **Edit Symbology**. This symbology mode renders each feature based on a data attribute containing a color.\n",
214+
"\n",
215+
"**Select \"Canonical\"** from the \"Render Type\" menu. The \"Value\" field should contain the \"fill\" attribute already, so **click OK**.\n",
216+
"\n",
217+
"You should notice that the neighborhoods ranked \"A\" are green, \"B\" are blue, \"C\" are yellow, and \"D\" are red.\n",
218+
"::::::"
154219
]
155220
},
156221
{
@@ -198,11 +263,19 @@
198263
" bbox=new_haven_bbox,\n",
199264
" resolution=10,\n",
200265
" groupby=\"solar_day\",\n",
201-
" chunks = {}, # this tells odc to use dask\n",
266+
" chunks = {},\n",
202267
")\n",
203268
"data"
204269
]
205270
},
271+
{
272+
"cell_type": "markdown",
273+
"id": "bb7cfddd-8a32-447c-97f2-823a86b5531f",
274+
"metadata": {},
275+
"source": [
276+
"You should notice that the two requested data variables (near-infrared and red) are present in the xarray DataSet."
277+
]
278+
},
206279
{
207280
"cell_type": "markdown",
208281
"id": "81a2b33a-117d-411b-8f73-4dd873252738",
@@ -252,7 +325,7 @@
252325
" \"EPSG:4326\",\n",
253326
").rio.to_raster(\n",
254327
" raster_path=NDVI_GEOTIFF_FILE, \n",
255-
" driver=\"COG\",\n",
328+
" driver=\"COG\", # Cloud-Optimized GeoTIFF\n",
256329
")"
257330
]
258331
},
@@ -264,7 +337,13 @@
264337
"#### Explore the data\n",
265338
"\n",
266339
"Let's explore the data in JupyterGIS again.\n",
267-
"This time, we'll add the layer with the GUI.\n",
340+
"This time, we'll add the NDVI data as a layer with the GUI.\n",
341+
"\n",
342+
"::::::{important} In the JupyterGIS UI...\n",
343+
"\n",
344+
":::{warning} Beware of bugs!\n",
345+
"The symbology menu is a bit fragile. You may see some things that look a little weird, but stick to the instructions and you'll be OK! If anything goes wrong, try removing the layer (right-click) and re-adding it.\n",
346+
":::\n",
268347
"\n",
269348
"If the \"identify\" tool is still active, click the `i` icon in the toolbar again to disable it.\n",
270349
"\n",
@@ -281,21 +360,30 @@
281360
"\n",
282361
"Scroll down to **input the layer name as \"NDVI\"**.\n",
283362
"\n",
363+
"**Click \"OK\"** to add the layer to the map.\n",
364+
"\n",
365+
"You should notice that the data doesn't look right -- it's entirely black-and-white.\n",
366+
"\n",
367+
"We need to set up the symbology now.\n",
368+
"\n",
284369
"Finally, **right-click the \"NDVI\" layer** and **select \"Edit Symbology\"**. The symbology menu may take a moment to load. Be patient! **Select \"Classify\" then click \"OK\".**\n",
285370
"\n",
286371
"The brighter areas have a higher NDVI value, and the darker areas have a lower one.\n",
287372
"\n",
288-
"We can use the identify tool (`i` icon in the toolbar) to explore the raw values."
373+
"We can use the identify tool (`i` icon in the toolbar) to explore the raw values.\n",
374+
"\n",
375+
"You might notice that the colored polygons show through in areas where the NDVI is `NaN`. You can hide that layer with the 👁️ icon in the JupyterGIS layers panel.\n",
376+
"::::::"
289377
]
290378
},
291379
{
292380
"cell_type": "markdown",
293381
"id": "41a3f550-04da-4170-9e69-b4131d428fdd",
294382
"metadata": {},
295383
"source": [
296-
"### Calculating mean NDVI for each New Haven neighborhood\n",
384+
"### Zonal Statistics: Calculating mean NDVI for each New Haven neighborhood\n",
297385
"\n",
298-
"To find out whether neighborhoods graded \"A\" are greener than neighborhoods graded \"D\", we'll calculate the mean NDVI for each neighborhood using [🔗exactextract](https://isciences.github.io/exactextract/background.html), which is known for its capability to include fractional grid cells in its calculation (as opposed to other tools, where a cell is binary, either in or out)."
386+
"To find out whether neighborhoods graded \"A\" are greener than neighborhoods graded \"D\", we'll calculate the mean NDVI for each neighborhood using [🔗exactextract](https://isciences.github.io/exactextract/background.html), which is known for its capability to include fractional grid cells in its calculation (as opposed to other tools, where a cell is binary, either inside or outside the polygon)."
299387
]
300388
},
301389
{
@@ -310,15 +398,25 @@
310398
"new_haven_redlining_and_ndvi = exact_extract(\n",
311399
" NDVI_GEOTIFF_FILE,\n",
312400
" new_haven_redlining,\n",
313-
" \"mean_ndvi=mean\",\n",
401+
" \"mean_ndvi=mean\", # Give the column a human-readable name!\n",
314402
" include_geom = True,\n",
315403
" include_cols=[\"label\", \"grade\", \"city\", \"fill\"],\n",
316404
" output=\"pandas\",\n",
317405
")\n",
318406
"\n",
319407
"new_haven_redlining_and_ndvi.set_crs(\n",
320408
" \"EPSG:4326\"\n",
321-
").to_file(INEQUALITY_NDVI_GEOJSON_FILE, engine=\"fiona\")"
409+
").to_file(INEQUALITY_NDVI_GEOJSON_FILE, engine=\"fiona\")\n",
410+
"\n",
411+
"new_haven_redlining_and_ndvi.head()"
412+
]
413+
},
414+
{
415+
"cell_type": "markdown",
416+
"id": "2d9542f4-131a-4fb4-9328-7b7a2981510b",
417+
"metadata": {},
418+
"source": [
419+
"You should notice that the dataset now includes a `mean_ndvi` column with the results of our zonal statistics calculation! 🎉"
322420
]
323421
},
324422
{
@@ -328,10 +426,44 @@
328426
"metadata": {},
329427
"outputs": [],
330428
"source": [
331-
"# jgis_project.add_geojson_layer(\n",
332-
"# path=INEQUALITY_NDVI_GEOJSON_FILE,\n",
333-
"# name=\"New Haven neighborhood redlining w/ NDVI\",\n",
334-
"# ) # TODO"
429+
"jgis_project.add_geojson_layer(\n",
430+
" path=INEQUALITY_NDVI_GEOJSON_FILE,\n",
431+
" name=\"New Haven neighborhood redlining w/ NDVI\",\n",
432+
")"
433+
]
434+
},
435+
{
436+
"cell_type": "markdown",
437+
"id": "7a1d4e12-9363-4625-9001-bc6aef655a7f",
438+
"metadata": {},
439+
"source": [
440+
"Notice that there are now two copies of the New Haven neighborhoods polygon layer. The new one is titled the same as the old one with \"w/ NDVI\" appended.\n",
441+
"\n",
442+
"::::::{important} In the JupyterGIS UI...\n",
443+
"\n",
444+
":::{warning} Beware of bugs!\n",
445+
"If the new redlining w/ NDVI layer disappears, remove it from the map by right-clicking it in the JupyterGIS layers panel and selecting \"Remove Layer\". Then re-run the cell above.\n",
446+
":::\n",
447+
"\n",
448+
"Temporarily hide the \"NDVI\" raster layer with the 👁️ icon in the JupyterGIS layers panel so you can more clearly see the new layer.\n",
449+
"\n",
450+
"Hide the \"old\" copy of the redlining layer (\"New Haven neighborhood redlining\") with the 👁️ icon in the JupyterGIS layers panel.\n",
451+
":::::::\n",
452+
"\n"
453+
]
454+
},
455+
{
456+
"cell_type": "markdown",
457+
"id": "0e66de07-3861-414e-b2e0-d3d320d4673c",
458+
"metadata": {},
459+
"source": [
460+
"### Visualizing correlation\n",
461+
"\n",
462+
"Now that we have neighborhood grades and mean NDVI for each neighborhood, how do we visualize any potential correlation?\n",
463+
"\n",
464+
"TODO: Bivariate choropleth?\n",
465+
"\n",
466+
"TODO: ?"
335467
]
336468
},
337469
{

0 commit comments

Comments
 (0)