Skip to content

Commit b3cc440

Browse files
committed
Doc: Add land cover categorical data example
1 parent a820b06 commit b3cc440

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed

ci/download_sample_data.sh

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ DEST_DIR=${DATA_ROOT}/sao_miguel
99
mkdir -p ${DEST_DIR}
1010
wget https://github.com/isciences/exactextractr/raw/master/inst/sao_miguel/concelhos.gpkg -P ${DEST_DIR}
1111
wget https://github.com/isciences/exactextractr/raw/master/inst/sao_miguel/clc2018_v2020_20u1.tif -P ${DEST_DIR}
12+
wget https://github.com/isciences/exactextractr/raw/master/inst/sao_miguel/clc2018_v2020_20u1.tif.vat.dbf -P ${DEST_DIR}
1213
wget https://github.com/isciences/exactextractr/raw/master/inst/sao_miguel/eu_dem_v11.tif -P ${DEST_DIR}
1314
wget https://github.com/isciences/exactextractr/raw/master/inst/sao_miguel/gpw_v411_2020_count_2020.tif -P ${DEST_DIR}
1415
wget https://github.com/isciences/exactextractr/raw/master/inst/sao_miguel/gpw_v411_2020_density_2020.tif -P ${DEST_DIR}

python/doc/examples_python.rst

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ Usage examples
99

1010
basic_usage
1111
noise_average_udf
12+
landcov

python/doc/landcov.ipynb

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "0",
6+
"metadata": {},
7+
"source": [
8+
"# Working with categorical data: land cover\n",
9+
"\n",
10+
"This example builds upon the sample data for São Miguel introduced [here](basic_usage.ipynb) to demonstrate the use of exactextract with categorical land cover data.\n",
11+
"\n",
12+
"The following plot shows a sample of the [CORINE 2018 landcover dataset](https://land.copernicus.eu/en/products/corine-land-cover) that is distributed with exactextractr."
13+
]
14+
},
15+
{
16+
"cell_type": "code",
17+
"execution_count": null,
18+
"id": "1",
19+
"metadata": {},
20+
"outputs": [],
21+
"source": [
22+
"import geopandas as gpd\n",
23+
"import rasterio\n",
24+
"import rasterio.plot\n",
25+
"from matplotlib import pyplot\n",
26+
"\n",
27+
"landcov = 'sao_miguel/clc2018_v2020_20u1.tif'\n",
28+
"concelhos = 'sao_miguel/concelhos.gpkg'\n",
29+
"\n",
30+
"fig, ax = pyplot.subplots()\n",
31+
"\n",
32+
"with rasterio.open(landcov) as r:\n",
33+
" rasterio.plot.show(r, ax=ax, cmap='tab20c')\n",
34+
" \n",
35+
"admin = gpd.read_file(concelhos)\n",
36+
"admin.plot(ax=ax, facecolor='none', edgecolor='black')"
37+
]
38+
},
39+
{
40+
"cell_type": "markdown",
41+
"id": "2",
42+
"metadata": {},
43+
"source": [
44+
"## Summarizing land cover classifications\n",
45+
"\n",
46+
"One of the most basic questions we might ask is which land cover type is predominant in each _concelho_. We can do this with the built-in `mode` summary operation. The `minority` and `variety` operations are also applicable to categorical data and provide the least-common classification and number of distinct classifications, respectively."
47+
]
48+
},
49+
{
50+
"cell_type": "code",
51+
"execution_count": null,
52+
"id": "3",
53+
"metadata": {},
54+
"outputs": [],
55+
"source": [
56+
"from exactextract import exact_extract\n",
57+
"\n",
58+
"df = exact_extract(landcov, concelhos, [\"variety\", \"mode\", \"minority\"], include_cols='name', output='pandas')\n",
59+
"display(df)"
60+
]
61+
},
62+
{
63+
"cell_type": "markdown",
64+
"id": "4",
65+
"metadata": {},
66+
"source": [
67+
"If needed, we can load the attribute table distributed with the landcover dataset and replace the numeric codes above with their descriptions."
68+
]
69+
},
70+
{
71+
"cell_type": "code",
72+
"execution_count": null,
73+
"id": "5",
74+
"metadata": {},
75+
"outputs": [],
76+
"source": [
77+
"import geopandas as pd\n",
78+
"\n",
79+
"classes = gpd.read_file('sao_miguel/clc2018_v2020_20u1.tif.vat.dbf').set_index('Value')\n",
80+
"for col in ('mode', 'minority'):\n",
81+
" df[col] = df[col].map(classes['LABEL3'])\n",
82+
"display(df)"
83+
]
84+
},
85+
{
86+
"cell_type": "markdown",
87+
"id": "6",
88+
"metadata": {},
89+
"source": [
90+
"## Calculating the fraction of each land cover type\n",
91+
"\n",
92+
"If we want more detailed information, the `unique` operation provides an array of distinct landcover types within each polygon.\n",
93+
"The `frac` operation provides a matching array with fraction of the polygon's Cartesian area that is covered by each type."
94+
]
95+
},
96+
{
97+
"cell_type": "code",
98+
"execution_count": null,
99+
"id": "7",
100+
"metadata": {},
101+
"outputs": [],
102+
"source": [
103+
"df = exact_extract(landcov, concelhos, [\"unique\", \"frac\"], include_cols='name', output='pandas')\n",
104+
"display(df)"
105+
]
106+
},
107+
{
108+
"cell_type": "markdown",
109+
"id": "8",
110+
"metadata": {},
111+
"source": [
112+
"To join these codes with the description, we can unnest the data using the pandas ``explode`` function and then remap the fields as done previously."
113+
]
114+
},
115+
{
116+
"cell_type": "code",
117+
"execution_count": null,
118+
"id": "9",
119+
"metadata": {},
120+
"outputs": [],
121+
"source": [
122+
"df = df.explode(['unique', 'frac'])\n",
123+
"df['unique'] = df['unique'].map(classes['LABEL3'])\n",
124+
"display(df)"
125+
]
126+
},
127+
{
128+
"cell_type": "markdown",
129+
"id": "10",
130+
"metadata": {},
131+
"source": [
132+
"## Condensed output with ``frac_as_map``\n",
133+
"\n",
134+
"If we are working with JSON output, it is also possible to view the same information in a map format:"
135+
]
136+
},
137+
{
138+
"cell_type": "code",
139+
"execution_count": null,
140+
"id": "11",
141+
"metadata": {},
142+
"outputs": [],
143+
"source": [
144+
"exact_extract(landcov, concelhos, 'frac', include_cols='name', output='geojson', output_options={'frac_as_map': True})[:2]"
145+
]
146+
},
147+
{
148+
"cell_type": "markdown",
149+
"id": "12",
150+
"metadata": {},
151+
"source": [
152+
"## Summarizing population land cover\n",
153+
"\n",
154+
"One extension of the analysis above is to see which land covers are associated with human population in a given concelho. Is the population primary urban or rural?\n",
155+
"\n",
156+
"As described in the [basic usage example](basic_usage.ipynb), the population density raster provides the most robust results in the presence of partially-covered pixels.\n",
157+
"\n",
158+
"We are able to perform this analysis because the CORINE sample distributed with exactextractr has been reprojected from its native Lambert Equal Area projection into geographic coordinates consistent with GPW. Otherwise, working with multiple rasters in different projections requires transformation to a common grid using tools such as GDAL.\n",
159+
"\n",
160+
"Very little about the call to `exact_extract` requires changing to incorporate population. We swap `weighted_frac` for `frac` and set `weights = pop_density`:"
161+
]
162+
},
163+
{
164+
"cell_type": "code",
165+
"execution_count": null,
166+
"id": "13",
167+
"metadata": {},
168+
"outputs": [],
169+
"source": [
170+
"pop_density = 'sao_miguel/gpw_v411_2020_density_2020.tif'\n",
171+
"\n",
172+
"df = exact_extract(landcov, concelhos, [\"unique\", \"weighted_frac\"], weights=pop_density, include_cols='name', output='pandas')\n",
173+
"df = df.explode(['unique', 'weighted_frac']).astype({'weighted_frac':'float64'})\n",
174+
"df['unique'] = df['unique'].map(classes['LABEL3'])\n",
175+
"df.sort_values('weighted_frac', ascending=False).drop_duplicates('name')"
176+
]
177+
},
178+
{
179+
"cell_type": "markdown",
180+
"id": "14",
181+
"metadata": {},
182+
"source": [
183+
"Looking at the highest-population land cover type in each concelho, we can can see that the western/central concelhos of Lagoa, Ponta Delgada, Ribeira Grande, and Vila Franca do Campo have a more urban population than Nordeste or Povoação to the east."
184+
]
185+
}
186+
],
187+
"metadata": {
188+
"kernelspec": {
189+
"display_name": "Python 3 (ipykernel)",
190+
"language": "python",
191+
"name": "python3"
192+
},
193+
"language_info": {
194+
"codemirror_mode": {
195+
"name": "ipython",
196+
"version": 3
197+
},
198+
"file_extension": ".py",
199+
"mimetype": "text/x-python",
200+
"name": "python",
201+
"nbconvert_exporter": "python",
202+
"pygments_lexer": "ipython3",
203+
"version": "3.11.2"
204+
}
205+
},
206+
"nbformat": 4,
207+
"nbformat_minor": 5
208+
}

0 commit comments

Comments
 (0)