-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathqgis-gee-workshop.Rmd
More file actions
514 lines (331 loc) · 29.7 KB
/
qgis-gee-workshop.Rmd
File metadata and controls
514 lines (331 loc) · 29.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
---
title: "From Cloud to Desktop: Working with Earth Engine Data in QGIS (Full workshop)"
subtitle: "Unlocking cloud geospatial data with with the Google Earth Engine Plugin for QGIS."
author: "Ujaval Gandhi"
fontsize: 12pt
output:
html_document:
df_print: paged
toc: yes
toc_depth: 2
highlight: pygments
includes:
in_header: copy-code.html
after_body: comment.html
# word_document:
# toc: false
# fig_caption: false
# pdf_document:
# latex_engine: xelatex
# toc: yes
# toc_depth: 3
header-includes:
- \usepackage{fancyhdr}
- \pagestyle{fancy}
- \renewcommand{\footrulewidth}{0.4pt}
- \fancyhead[LE,RO]{\thepage}
- \geometry{left=1in,top=0.75in,bottom=0.75in}
- \fancyfoot[CE,CO]{{\includegraphics[height=0.5cm]{images/cc-by-nc.png}} Ujaval Gandhi http://www.spatialthoughts.com}
classoption: a4paper
---
\newpage
***
```{r echo=FALSE, fig.align='center', out.width='75%', out.width='250pt'}
knitr::include_graphics('images/spatial_thoughts_logo.png')
```
***
\newpage
# Introduction
Google Earth Engine is a cloud-based platform that enables working with large-scale earth observation datasets effectively. The [Google Earth Engine Plugin for QGIS](https://github.com/gee-community/qgis-earthengine-plugin) brings this power to the desktop and enables QGIS users to combine their geospatial workflows with cloud-based datasets. This workshop will give you hands-on experience using plugin to leverage cloud-based datasets in desktop-based geospatial workflows.
[](https://www.youtube.com/watch?v=FjyXQqoSUUo){target="_blank"}
[Watch the Video ↗](https://www.youtube.com/watch?v=FjyXQqoSUUo){target="_blank"}
[Access the Presentation ↗](https://docs.google.com/presentation/d/1yogtMCTOkELcXWLKp285vHQqhHezZpBLObVSeAzDs7w/edit?usp=sharing){target="_blank"}
----
# Installation and Setting up the Environment
## Install QGIS
This workshop requires QGIS LTR version 3.44 or above. Please review [QGIS-LTR Installation Guide](install-qgis-ltr.html) for step-by-step instructions.
## Sign-up for Google Earth Engine
If you already have a Google Earth Engine account, you can skip this step.
Visit our [GEE Sign-Up Guide](gee-sign-up.html) for step-by-step instructions.
## Install the Google Earth Engine Plugin for QGIS
This workshops requires the **Google Earth Engine Plugin for QGIS**. The plugin can be installed by the Plugin Manager from the official QGIS plugin repository and involves a few extra steps to authenticate with your Google Earth Engine account and set the Google Cloud project.
Visit the [QGIS Earth Engine Plugin Installation Guide](https://gee-community.github.io/qgis-earthengine-plugin/installation/) for step-by-step instructions.
## Get the Data Package
We have created a data package containing checkpoint projects that will allow you to load the results of each section. You can download the data package from [qgis-gee-workshop.zip](https://github.com/spatialthoughts/courses/releases/download/data/qgis_gee_workshop.zip). Once downloaded, unzip the contents to a folder on your computer.
# Get the Workshop Video
The workshop is accompanied by a video covering the all the sections. This video is recorded from our live online class and is edited to make them easier to consume for self-study. We have 2 versions of the videos:
## YouTube
The video on YouTube is ideal for online learning and sharing. You may also turn on *Subtitles/closed-captions* and adjust the playback speed to suit your preference. [Access the YouTube Video ↗](https://youtu.be/FjyXQqoSUUo){target="_blank"}
## Vimeo
We have also made full-length video available on Vimeo. This video can be downloaded for offline learning. [Access the Vimeo Video ↗](https://vimeo.com/1148230550?share=copy){target="_blank"}
----
# Overview of the Earth Engine Data Model
Earth Engine supported georeferenced raster and vector datasets. The terminology used by Earth Engine is different than used by QGIS. Below is the explanation of common data types in Earth Engine.
* **Image**: A single georeferenced raster dataset. It can have one or more bands. The image metadata is stored as *properties*. Each band in the image can have different data type, spatial resolution (i.e. scale) and projection.
* **ImageCollection**: The most common type of dataset used in Earth Engine that consists of multiple *Image* objects. Images over both space and time can be put in a single ImageCollection.
* **FeatureCollection**: Vector datasets are represented as FeatureCollections in Earth Engine. Each *Feature* in the collection consists of a *geometry* and some *properties*.
# Accessing Earth Engine Datasets
## Official Data Catalog
The Google Earth Engine team maintains a large catalog of ready-to-use datasets that can be readily accessed via the plugin. You can search and browse the datasets at the [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets).
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/main_catalog.png')
```
## Awesome GEE Community Catalog
The Earth Engine Community, led by Samapriya Roy maintains another large catalog of datasets that have been processed and ingested in Earth Engine. This catalog can be accessed from [awesome-gee-community-catalog](https://gee-community-catalog.org/). The dataset available in this catalog can also be used directly in the QGIS Earth Engine plugin.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/community_catalog.png')
```
## User-Uploaded Assets
Earth Engine users can also upload their own datasets and use them in the plugin. This is useful if you wanted to access your private assets through the QGIS Earth Engine plugin. You must be either the owner of the asset or need read access to the asset.
# Hands-on With QGIS Earth Engine Plugin
## 1. Downloading Images from Earth Engine
The Google Earth Engine plugin comes with a handy **Export Image to GeoTIFF** algorithm that allows you to download images from GEE directly to your computer as GeoTIFF files. In this tutorial, we will use the plugin to create a Sentinel-2 median composite for a region and download it as a GeoTIFF file.
1. Open QGIS. To help us select a region of interest, it will be helpful to have a basemap. From the QGIS Browser Panel, locate the *XYZ Tiles → OpenStreetMap* layer and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image1.png')
```
2. We can use the QGIS’s built-in geocoder to search for a place. For this tutorial, we want to download imagery over Paris. Select the locator bar in the bottom left corner and enter the search term `> paris`. Make sure there is a space between the `>` character and the search term. From the results, click on the first one to zoom to the location.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image2.png')
```
3. Zoom and pan the map to the desired extent. Open the Processing Toolbox from *Processing → Toolbox*. Locate the *Add Image Collection* algorithm from the Google Earth Engine provider. Double-click to open it.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image3.png')
```
4. To add an Image Collection, we first need to find the Image Collection ID for the Sentinel-2 Level-2A collection.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image4.png')
```
5. Open the [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets/) and navigate to the dataset page for [Harmonized Sentinel-2 MSI: MultiSpectral Instrument, Level-2A (SR)](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR_HARMONIZED). Copy the Image Collection ID `COPERNICUS/S2_SR_HARMONIZED` shown on the page.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image5.png')
```
6. Back in the *Add Image Collection* dialog, paste the Image Collection ID `COPERNICUS/S2_SR_HARMONIZED`. Next, we can apply a metadata filter to select images with less cloud. In the *Filter by Image Properties* section, select `CLOUDY_PIXEL_PERCENTAGE` as the property, `<` as the operator and enter `30` as the value. This will select all images with < 30% cloud cover.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image6.png')
```
7. In the *Compositing* section, select `Median` as the *Compositing Method*. This method will take all available images and calculate the median value for each pixel. For optical imagery, such as Sentinel-2, this method is useful in selecting the best representative pixel for the chosen duration without being affected by outliers such as clouds and cloud-shadow. We want to create an annual composite, so in the *Filter by Dates* section, enter the *Start* and *End* dates as `01-01-2024` and `01-01-2025`.
> Remember that the *End* date in the Earth Engine date filter is *exclusive* - so we need to add an extra day to ensure do not exclude the last day of the year.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image7.png')
```
8. Expand the *Filter by Extent (Bounds)* section. Click the `Map Canvas Extent` button to load the selected region in the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image8.png')
```
9. Before loading the image, we need to specify the visualization parameters. For multi-band images, we can select 3 bands to be used for visualization. We will visualize the resulting image in natural color, so select `B4` (Red) , `B3` (Green) and `B2` (Blue) as the bands. The typical range of pixel values for Sentinel-2 images are between 0-3000, so enter `0` as *Min* and `3000` as *Max*. Check the *Clip to Extent* box to load only the pixels within the selected extent and click *Run*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image9.png')
```
10. Once the algorithm finishes, a new layer `IC: COPERNICUS/S2_SR_HARMONIZED (Median)` will be added to the *Layers* panel. This layer is being streamed from the Earth Engine servers. As you zoom or pan the image - new pixels will be computed on-the-fly and displayed. Lets download this image so we can use it within QGIS for analysis. Locate the `Export → Export Image to GeoTIFF` algorithm from the Google Earth Engine provider in the Processing Toolbox. Double-click to open it.
> If the image appears blank, move the map slightly using the Pan tool. This will refresh the canvas and request loading of the tiles from Earth Engine.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image10.png')
```
11. In the Export Image to GeoTIFF dialog, select `IC: COPERNICUS/S2_SR_HARMONIZED (Median)` as the EE Image. We want to export this at its native resolution, so enter `10` as the *Scale (meters)*. We can also specify the projection in which we want the output image. Click the *Select CRS* button.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image11.png')
```
12. It is recommended to use a Projected CRS suitable for the region of interest. UTM is a good choice for such a CRS. We can find the CRS for the UTM Zone where our region is located. If you do not know the UTM Zone, you can use this handy [What UTM Zone am I in?](https://mangomap.com/robertyoung/maps/69585/what-utm-zone-am-i-in-#) map. Paris is located in the *UTM Zone 31N*. Search and select the `WGS84 / UTM zone 31N (EPSG:32631)` CRS. Once selected, click the arrow at the top of the selector to go back to the previous dialog.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image12.png')
```
13. Next, select the Map Canvas Extent as the Extent of the image for Export. We can choose the subset and order of bands to export. For this tutorial, lets export the B4, B3 and B2 bands. You can select each band from the left-hand section and use the >> button to add them to the list of output bands.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image13.png')
```
14. For the *Output File*, browse to a directory on your computer and enter the file name as `s2_median_composite.tif`. Once configured, click *Run*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image14.png')
```
15. The requested region will be divided into smaller tiles and each tile will be downloaded separately. The algorithm will then merge the downloaded tiles into a single mosaic and save it at the requested location. Depending on the size of the region, resolution, number of bands, and your internet bandwidth, this process can take some time. Once the algorithm finishes, click *Close*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image15.png')
```
16. In the layer panel, turn off the `IC: COPERNICUS/S2_SR_HARMONIZED (Median)` layer as it is no longer required. In the *Browser* panel, locate the directory where you saved the output file. Drag and drop the `s2_median_composite.tif` file to the canvas to see the downloaded image.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/download_image16.png')
```
## 2. Building Workflows with Model Designer
The processing algorithms provided by the Google Earth Engine plugin can be used in the QGIS Model Designer to automate workflows that combine data from the Earth Engine Data Catalog with processing algorithms from QGIS.
In this section, we will build a model that will run the following steps in a single workflow:
- Load landcover data from the Earth Engine Data Catalog
- Download the data as a GeoTIFF file for the chosen region
- Calculate area of each landcover class
- Convert the computed area to square kilometers
- Add input validation to the model
```{r echo=FALSE, fig.align='center', out.width='75%', fig.cap='QGIS Model for Calculating Landcover Statistics'}
knitr::include_graphics('images/qgis_gee_workshop/model_output.png')
```
There are many landcover datasets available in the [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets) as well as [GEE Community Catalog](https://gee-community-catalog.org/). A similar process can be used to load any of the available datasets.
We will use the [ESA WorldCover 10m v200](https://developers.google.com/earth-engine/datasets/catalog/ESA_WorldCover_v200?hl=en) global land cover map for 2021 at 10 m resolution. Visit the [dataset description page](https://developers.google.com/earth-engine/datasets/catalog/ESA_WorldCover_v200?hl=en#description) and note the ImageCollection ID.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/catalog1.png')
```
This dataset contains a data band named **Map** and 11-class classification with each class assigned a value between 10-100.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/catalog2.png')
```
### 2.1 Building the model
1. Open QGIS. From the QGIS Browser Panel, locate the supplied `bangalore.geojson` file and double-click to open it. This will load a vector layer containing a single feature representing the municipal boundary for the city of Bengaluru, India. Next, open the Processing Toolbox from *Processing → Toolbox*. From the toolbar, select *Models → Create New Model..*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model1.png')
```
2. In the Model Designer dialog, enter the model Name as `Calculate Landcover Statistics` and click the *Save model* button. When prompted to enter the file name, enter `calculate_landcover_statistics.model3`.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model2.png')
```
3. We will now start building the model. The first input to the model will be the layer with region boundary. From the *Inputs* tab, scroll down to find the *+ Vector Layer* input. Drag it to the canvas. In the *Vector Layer Parameter Definition* dialog, enter `Input Vector Layer` as the *Description* and select `Polygon` as the *Geometry type*. Click *OK*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model3.png')
```
4. Next we will add an input for the user to specify the CRS to be used for the analysis. Locate the *+ CRS* input and drag it to the canvas. In the *CRS Parameter Definition* dialog, enter `Projection for Area Calculation` as the *Description*. As the layer we are using falls in the UTM Zone 43N, we can enter the *Default value* of the CRS as `EPSG:32643 - WGS84 / UTM Zone 43N`. Click *OK*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model4.png')
```
5. Once the inputs are configured, lets start building the workflow. First step is to reproject the input layer to the specified CRS. Switch to the *Algorithms* tab, search and locate the *Vector general → Reproject layer* algorithm and drag it to the canvas. In the *Vector general - Reproject layer* dialog, switch to *Using model input* for *Input layer* and select the `Input Vector Layer` parameter. Similarly for *Target CRS* select the `Projection for Area Calculation` parameter. Click *OK*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model5.png')
```
6. Next step is to load the dataset from Earth Engine. Scroll down to the *Google Earth Engine provider* and locate the *Add Image Collection* algorithm. Drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model6.png')
```
7. Enter the Earth Engine Image Collection ID `ESA/WorldCover/v200` for the ESA WorldCover dataset. For the *Extent*, switch to *Using algorithm output* and select `"Reprojected" from algorithm "Reproject layer"`. For the *Extent CRS*, select *Using model input* and select `Projection for Area Calculation`.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model7.png')
```
8. Scroll down in the dialog and delete the default help text for both *Filter Image Properties* and *Visualization Parameters (JSON)*. Select `Mosaic` for the *Compositing Method* and click *OK*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model8.png')
```
9. The next step is to connect the *Export Image to GeoTIFF* algorithm. Locate it and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model9.png')
```
10. In the *Export Image to GeoTIFF* dialog box, for the *EE Image Name*, switch to *Using algorithm output* and select `"Layer Name"from algorithm "Add Image Collection"`. For *Extent*, switch to *Using algorithm output* and select `"Reprojected" from algorithm "Reproject layer"`. The spatial resolution of the dataset is 10m, so enter `10` as the *Scale (meters)*. Enter `Map` as the *Bands to export*. Click OK.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model10.png')
```
11. The previous step will download the data for the given extent as a GeoTIFF file. Let's clip this to the area of interest. Search and locate the *GDAL → Raster extraction → Clip raster by mask layer* algorithm and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model11.png')
```
12. In the *Raster extraction - Clip raster by mask layer* dialog, configure the *Input layer* by switching to *Using algorithm output* and selecting `"Output File" from algorithm "Export Image to GeoTIFF"`. Specify the *Mask layer* by switching to *Using algorithm output* and selecting `"Reprojected" from algorithm "Reproject layer"`. Click *OK*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model12.png')
```
13. Now we will add a step to calculate the area for each class. This is done using the *Raster analysis → Raster layer unique values report* algorithm. Locate it and drag it to the canvas.
> Note: The *Raster layer unique values report* algorithm gives you the areas for the entire raster layer. If you want to calculate areas for different regions over the raster, you can use the **Zonal histogram** tool to get pixel counts for each class and then multiply it with the area of each pixel.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model13.png')
```
14. In the *Raster analysis - Raster layer unique values report*, configure the *Input layer* by switching to *Using algorithm output* and selecting `"Clipped (mask)" from algorithm "Clip raster by mask layer"`. Keep other parameter values to their default and click *OK*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model14.png')
```
15. The results from the previous step will be a table with columns **value** and **m2** containing the class value and total area in square meters. Let's convert the area to square kilometers. Locate the *Vector table → Field calculator* algorithm and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model15.png')
```
16. In the *Vector table - Field Calculator* dialog, configure the *Input layer* by switching to *Using algorithm output* and selecting `"Unique values table" from algorithm "Raster layer unique values report"`. Enter the *Field name* as `area_sqkm` and *Result field type* as `Decimal (double)`. For the *Formula*, we will divide the area in square meters by 1000 x 1000 (i.e. 1e6) to convert it to square kilometers. Enter the expression as `"m2"/1e6`.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model16.png')
```
17. As this table will be our final output, enter the *Calculate* layer name as `class_areas` and click *OK*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model17.png')
```
18. The model is now complete. Click the *Run model* button.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model18.png')
```
19. In the *Calculate Landcover Statistics*, select `bangalore` as the *Input Vector Layer*. Keep the other values to their default and click *Run*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model19.png')
```
20. Once the model run completes, a new layer `class_areas` will be added to the *Layers* panel. Open the attribute table and you will see new column with class area statistics computed from the ESA WorldCover dataset..
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model20.png')
```
21. It is a good practice to save the model along with the QGIS project so it is always available to whoever is using the project file. Click the *Save model in project* button to embed the model in your QGIS project file.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model21.png')
```
22. From the main QGIS interface, you will now see a new folder *Project models* in the Processing Toolbox containing the model we created.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/model22.png')
```
If you'd like to catch up to this point, you can load the `Model_Designer_Checkpoint1.qgz` file in the `solutions` folder of the data package.
### 2.2 Validation and Organization
At this point, the model works and generates the landcover statistics for any input region in the world. Let's learn how to get the model production ready by adding some validation checks and adding visual cues to make model maintenance easy.
1. The model is saved in the Processing Toolbox in the *Project models* folder. Select the *Calculate Landcover Statistics* model, right-click and select *Edit Model...*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation1.png')
```
2. Our input vector layer is expected to have only a single polygon in it. The model does not work with vector layers containing more than 1 polygon. To ensure the supplied layer meets this criteria, we will add a check and raise an error if the feature count of the layer is more than 1. Locate the *Modeler tools → Calculate expression* algorithm and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation2.png')
```
3. In the *Modeler tools - Calculate expression* dialog, click the *Expression* button.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation3.png')
```
4. We will use the expression function `layer_property()` to get a count of features for the input layer. Enter the expression as shown below and click *OK*..
```
layer_property(@input_vector_layer, 'feature_count')
```
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation4.png')
```
5. Next locate the *Modeler tools → Raise exception* algorithm and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation5.png')
```
6. In the **Modeler tools - Raise exception* dialog, enter `The Input Layer has more than 1 feature` as the *Error message*. For the *Condition*, switch to *Pre-calculated Value*, enter the following expression. This configuration will throw an error when the number of features is greater than 1.
```
@Calculate_expression_OUTPUT > 1
```
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation6.png')
```
7. We want the input validation to happen before any of the processing steps. So we add the validation as a dependency to the first step of the workflow. Click the box for *Reproject layer*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation7.png')
```
8. In the *Vector general - Reproject layer* dialog, scroll down to the bottom and click the *...* button for *Dependencies*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation8.png')
```
9. Select both `Calculate expression` and `Raise exception` algorithms and click *OK*. This will ensure these algorithms are completed before this step.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation9.png')
```
10. Test the model by selecting any data layer having more than 1 polygon and you will see the exception raise by the model.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation10.png')
```
11. It is a good practice to add some documentation to the model and add visual cues to help maintainers understand the workflow better. Let's add a group box to group and label certain steps of the workflow. Go to *Edit → Add Group Box*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation11.png')
```
12. Click on the canvas to add a group box. Align it over the steps related to Google Earth Engine. Double-click the box and enter the *Title* as `Earth Engine` and select a color of your choice.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation12.png')
```
13. Similarly add group boxes for the steps related to QGIS processing and input validation.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation13.png')
```
14. Now the model is complete. Save it in the QGIS project.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/qgis_gee_workshop/validation14.png')
```
If you'd like to catch up to this point, you can load the `Model_Designer_Checkpoint2.qgz` file in the `solutions` folder of the data package.
# Data Credits
* Sentinel-2 Level-2A: Contains Copernicus Sentinel data.
* Zanaga, D., Van De Kerchove, R., Daems, D., De Keersmaecker, W., Brockmann, C., Kirches, G., Wevers, J., Cartus, O., Santoro, M., Fritz, S., Lesiv, M., Herold, M., Tsendbazar, N.E., Xu, P., Ramoino, F., Arino, O., 2022. ESA WorldCover 10 m 2021 v200. (doi:10.5281/zenodo.7254221)
# License
This workshop material is licensed under a [Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/). You are free to re-use and adapt the material but are required to give appropriate credit to the original author as below:
*Cloud-based Remote Sensing with QGIS and Google Earth Engine* by Ujaval Gandhi [www.spatialthoughts.com](https://spatialthoughts.com)
© 2025 Spatial Thoughts [www.spatialthoughts.com](https://spatialthoughts.com)