-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathadvanced-qgis.Rmd
executable file
·1548 lines (997 loc) · 86.8 KB
/
advanced-qgis.Rmd
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
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
title: "Advanced QGIS (Full Course)"
subtitle: "Learn Techniques for Automating GIS Workflows and Advanced Visualizations."
author: "Ujaval Gandhi"
fontsize: 12pt
output:
# pdf_document:
# toc: yes
# toc_depth: 3
# fig_caption: false
html_document:
df_print: paged
toc: yes
toc_depth: 3
highlight: pygments
includes:
after_body: comment.html
# word_document:
# toc: no
# fig_caption: false
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='250pt'}
knitr::include_graphics('images/spatial_thoughts_logo.png')
```
***
\newpage
# Introduction
This class focuses on techniques for the automation of GIS workflows. You will learn techniques that will help you be more productive, create beautiful visualizations and solve complex spatial analysis problems. This class is ideal for participants who already use QGIS and want to take their skills to the next level.
Below are the topics covered in this class
- Processing Framework - Algorithms, Batch processing, and Modeler
- Animating time-series data
- Creating 3D fly-through from aerial imagery
- Advanced Expressions for enabling faster data editing, fuzzy matching, and more.
Before we do the exercises, it will help you to learn a bit more about open-source and how the QGIS project operates.
[![Watch the video](https://img.youtube.com/vi/ny-rH9FBpn8/mqdefault.jpg)](https://www.youtube.com/watch?v=ny-rH9FBpn8&list=PLppGmFLhQ1HIqNiNWxVqs5wBLiA_UrKTQ&index=1){target="_blank"}
[Watch the Video ↗](https://www.youtube.com/watch?v=ny-rH9FBpn8&list=PLppGmFLhQ1HIqNiNWxVqs5wBLiA_UrKTQ&index=1){target="_blank"}
[Access the Presentation ↗](https://docs.google.com/presentation/d/13R-RbR5LJmBr5F-VAN0OXh7fRxRC8olFIl2yrOFgG-w/edit?usp=sharing){target="_blank"}
# Software
This course requires QGIS LTR version 3.34.
Please review [QGIS-LTR Installation Guide](install-qgis-ltr.html) for step-by-step instructions.
# Get the Data Package
The examples in this class use a variety of datasets. All the required layers, project files etc. are supplied to you in the ``advanced_qgis.zip`` file along with your class materials. Unzip this file to the `Downloads` directory.
Download [advanced_qgis.zip](https://github.com/spatialthoughts/courses/releases/download/data/advanced_qgis.zip).
> Note: Certification and Support are only available for participants in our paid instructor-led classes.
# Get the Course Videos
The course is accompanied by a set of videos covering the all the modules. These videos are recorded from our live instructor-led classes and are edited to make them easier to consume for self-study. We have 2 versions of the videos:
## YouTube
We have created a YouTube Playlist with separate videos for each section and exercise to enable effective online-learning. [Access the YouTube Playlist ↗](https://www.youtube.com/playlist?list=PLppGmFLhQ1HIqNiNWxVqs5wBLiA_UrKTQ){target="_blank"}
## Vimeo
We are also making combined full-length video for each module available on Vimeo. These videos can be downloaded for offline learning. [Access the Vimeo Playlist ↗](https://vimeo.com/showcase/11232120){target="_blank"}
# Concept: Processing Framework
QGIS 2.0 introduced a new concept called Processing Framework. Previously known as Sextante, the Processing Framework provides an environment within QGIS to run native and third-party algorithms for processing data. It is now the recommended way to run any type of data processing and analysis within QGIS - including tasks such as selecting features, altering attributes, saving layers etc. - that can be accomplished by other means. But leveraging the processing framework allows you to be more productive, fast, and less error-prone.
The Processing Framework consists of the following distinct elements that work together.
- **Processing Toolbox**: Contains individual tools (i.e. algorithms) grouped by providers and functionality. There are hundreds of algorithms available out of the box. They are organized by *Providers*. The tools created by QGIS developers are available as a **Native** QGIS provider. Processing Framework providers an easy way to integrate tools written by other software and libraries such as GDAL, GRASS, and SAGA. QGIS Plugins can also add new functionality via processing algorithms in the toolbox.
- **Batch Processing Interface**: Allows any processing tool to be executed on multiple layers together.
- **Model Designer**: This allows the user to define the workflow and chain multiple processing steps together using a drag-and-drop mechanism.
- **History Manager**: Records and stores all executions of algorithms to allow users to reproduce the past analysis.
- **Results Viewer**: An interface to view non-spatial outputs from algorithms - such as tables and charts.
[![View Presentation](images/advanced_qgis/processing.png){width="400px"}](https://docs.google.com/presentation/d/1Vej8xgY710C-dsxneVXO_6UkXNkcIrYukzgJqxI2j3k/edit?usp=sharing){target="_blank"}
[View the Presentation](https://docs.google.com/presentation/d/1Vej8xgY710C-dsxneVXO_6UkXNkcIrYukzgJqxI2j3k/edit?usp=sharing){target="_blank"}
We will learn about each of these through hands-on exercises in the following sections.
# 1. Using Processing Toolbox
This exercise aims is to show how a multi-step spatial analysis problem can be solved using a purely processing-based workflow. This exercise also shows the richness of available algorithms in QGIS that can do sophisticated operations that previously needed plugins or were more complex.
We will work with roads extracted from OpenStreetMap for the state of Karnataka in India. The admin boundary for the state and the districts come from DataMeet.
## 1.1 Extract, Transform and Load
We will learn about some useful processing algorithms for basic data processing operations such as Extract, Transform and Load (ETL). Our goal for this section is to read a road layer and a boundary layer from a GeoPackage and calculate what is the total length of the national highways in a state. We will also see how to append the resulting layer to same GeoPackage.
1. Browse to the data directory and expand ``karnataka.gpkg``. Drag and drop the ``karnataka``, ``karnataka_districts`` and ``karnataka_major_roads`` layers to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka1.png')
```
2. The layer ``karnataka_major_roads`` contain all major roads, including national highways, state highways, major arterial roads etc. Select the layer and use the keyboard shortcut **F6** to open the attribute table.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka2.png')
```
3. You will notice that the ``ref`` column has information about road designation. As we are interested in only national highways, we can use the information in this column to extract relevant road segments.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka3.png')
```
4. You can use tools such as *Select by expression*, export the selected features as a new layer and continue to work. But the processing toolbox providers a much seamless workflow. Search for the algorithm **Extract by expression**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka4.png')
```
5. Enter the following expression to extract the features where the value of the ``ref`` field starts with the letters ``NH``.
> We are using *Regular Expression (or RegEx)* to match the field value to a specific pattern. Regular expressions are quite powerful and can be used for many complex data filtering operations. [Here's a good tutorial](https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285) that explains the basics of regular expressions.
```
regexp_match("ref", '^NH')
```
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka5.png')
```
6. You will get a new layer ``Matching Features`` in the Layers Panel. Next, we want to calculate the length of each segment. You can use the built-in algorithm **Add geometry attributes**
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka6.png')
```
7. The source layer is in the Geographic CRS EPSG:4326. But for the analysis, we want the lengths to be measured in meters/kilometers. The algorithm provides us with a handy option to calculate the distances in **Elliposidal** math - which is ideal for layers in the geographic CRS.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka7.png')
```
8. A new layer with an additional field called ``length`` will be added to the Layers panel. The distances in this field are in meters. Let's convert them to kilometers. You may reach out to the trusty QGIS field calculator to add a new field. That's a perfectly valid way - but as mentioned earlier, there is a *processing* way to do things which is the preferred way. Search and open the **Field Calculator** processing algorithm instead and enter the following expression.
```
"length"/1000
```
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka8.png')
```
9. A new layer with the field ``length_km`` will be added to the Layers panel. Now we are ready to find out the answer. We just need to sum up the values in the ``length_km`` field. Use the **Basic Statistics for Fields** algorithm.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka9.png')
```
10. In the *Basic Statistics for Fields* dialog, select ``Calculated`` as the *Input layer* and ``length_km`` as the *Field to calculate statistics on*. Click *Run*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka10.png')
```
11. The result is a table of different statistics on the column. As the result is not a layer, it will be displayed in the *Results Viewer*. The panel will contain the link to an HTML file containing the statistics. The **Sum** contains the total length of national highways in the state.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka11.png')
```
> *What do you think of the results?* The resulting number may not be perfect because the OpenStreetMap database may have missing roads or are classified differently. But it is close to the number provided in the [official statistics](https://morth.nic.in/sites/default/files/PragatiKiNayiGati/pdf/karnataka.pdf).
12. The final layer is called ``Calculated`` and is a temporary memory layer. Let's save it to the disk so we can use it later. The layer contains many fields which are not relevant to us, so let's delete some columns before saving them. The *classic* way to do this is to toggle editing and use the *Delete Column* button from the Attribute Table. If you wanted to rename/reorder certain fields, that needed a plugin. But now, we have a really easy processing algorithm called **Refactor Fields** that can add, delete, rename, re-order and change the field types all at once. Delete fields that are not required and save the result as the layer ``national_highways`` in the source ``karnataka.gpkg``.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka12.png')
```
13. The layer ``national_highways`` will be added to the Layers panel.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/karnataka13.png')
```
We have now finished the first part of this exercise. Your output should match the contents of the `Processing_Tools_Checkpoint1.qgz` file in the `solutions` folder.
### 1.1.1 Challenge
There are many features in the ``national_highways`` layer where the `"name"` attribute is **NULL**. Find an appopriate algorithm from the Processing Toolbox to create a new layer with the features having NULL values removed.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/1_1_1_challenge.png')
```
## 1.2 Spatial Join and Summary Statistics
We achieved the goal of the exercise, but we can explore the results a bit better if we can break down the results into a smaller administrative unit. Let's try to calculate the length of national highways for each district in the state. This will require us to perform a spatial join and analyze the results using group statistics.
1. Add the ``karnataka_districts`` layer from the ``karnataka.gpkg`` file. This layer contains the geometry of each district along with its name in the **DISTRICT** field.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/join01.png')
```
2. To add the name of the district to the roads layer, we need to perform a spatial-join. This is done using the **Join attributes by Location** algorithm. Search and locate the tool in the toolbox. Double-click to open it. For *Join to features in*, select the ``national_highways`` layer and *By comparing to* as ``karnataka_districts``. This will join each feature in the roads layer to the intersecting feature from the districts layer. Select only the **DISTRICT** field to be added to the output. For the *Join type*, you have 3 options. Here we want to do a one-to-one join where the for each road feature, we want to select one district feature. Select the **Take attributes of the feature with largest overlap only** option.
> In our dataset, the are some road geometries that cross district boundaries - causing part of the length being counted as part of another district. You can instead use the *Sum line lengths* algorithm from the Processing Toolbox which gives you accurate line lenghts contained in each polygon.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/join02.png')
```
3. The new layer ``Joined layer`` now has the intersecting district name in the **DISTRICT** field. We can now sum the road lengths and group them for each district. You may recall that in earlier versions of QGIS, you needed a plugin called Group Stats to do this. But now we can do this via the built-in **Statistic by Categories** algorithm. Select the ``Joined layer`` as the *Input vector layer*, **length_km** as the *Field to calculate statistics on*. Select the **DISTRICT** field as the *Field(s) with Categories*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/join03.png')
```
4. Save the layer to the ``karnataka.gpkg`` as a layer named ``national_highways_by_districts``. Click *Run*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/join04.png')
```
5. The output of the algorithm is a table containing various statistics on the ``length_km`` column for each district. The values in the **Sum** column is the total length of national highways in the district.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/join05.png')
```
Note that we did data processing, spatial analysis and statistical analysis - all using just processing algorithms in a fast, reproducible and intuitive workflow. We have now finish the first part of this exercise. Your output should match the contents of the `Processing_Tools_Checkpoint2.qgz` file in the `solutions` folder.
### 1.2.1 Challenge
Apply the following formatting changes on the ``national_highways_by_districts`` layer and save it to ``karnataka.gpkg`` as a new layer ``national_highways_by_districts_formatted``.
* Rename the **DISTRICT** field to **district**.
* Rename the **sum** field to **total_length**.
* Round the lengths in the **total_length** field to nearest integer using the `round()` function.
* Delete all remaining fields.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/1_2_1_challenge.png')
```
Hint: You can apply an expression on a field in the *Refactor Fields* algorithm by entering it in the *Source Expression* column.
# 2. Batch Processing
So far we have run the algorithm on 1 layer at a time. But each processing algorithm can also be run in a **Batch** mode on multiple inputs. This provides an easy way to process large amounts of data and automate repetitive tasks.
The batch processing interface can be invoked by right-clicking any processing algorithm and choosing **Execute as Batch Process**.
## 2.1 Clip Multiple Layers
We will take multiple country-level data layers and use the batch processing operation to clip them to a state polygon in a single operation.
1. Open the **batch_processing** project.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch01.png')
```
2. Next, right-click on the ``state boundary`` layer, and click **Zoom to Layer**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch02.png')
```
3. Now that we have centered on our area of interest,select the ``places`` layer. Go to the the **Processing** menu and select **toolbox**. Now in *Processing Toolbox*, search for **clip**, under **Vector overlay → Clip** right-click and select **Execute as Batch Process..**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch03.png')
```
4. In the *Batch processing - Clip* dialog box, click the *Autofill...* under the *Input layer* column and choose *Select from Open Layers..*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch04.png')
```
5. Select all data layers except ``state_boundary`` and ``places`` then click **OK**. The ``places`` layer is on the top and it gets selected by default.We won't select it manually to avoid repetition.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch05.png')
```
6. Under *Overlay Layer* select the **state_boundary** layer. Then click the drop-down button near *Autofill...* and choose to **Fill Down**. This will auto-populate all the *Overlay Layer* with the ``state_boundary`` layer.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch06.png')
```
7. Under *Clipped*, select *...* button to add the output filenames.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch07.png')
```
8. In the *Save File* dialog box, enter the *File name* as ``clipped_``. This is just the prefix of the name that you will auto-complete in the next step. Select **GPKG files (.gpkg)** in *Save as type*. Click **Save**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch08.png')
```
9. Next, you will be prompted to choose the *Autofill settings*. Select to **Fill with parameter values** as the *Autofill mode*, and **Input layer** as the *Parameter to use*. Click **OK**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch09.png')
```
10. Note that each row has a different file name now. The output file name is the result of the chosen prefix and the autofill settings. Make sure the *Load layers on completion* box is checked and click *Run*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch10.png')
```
11. The resulting clipped layers will be added to the *Layers panel*. Turn off all previous layers in the Layers panel to see the results clearly. We can now combine all the clipped layers into a single *geopackage* file for ease of sharing. Now in the *Processing Toolbox*, search and select the **Package Layers**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch11.png')
```
12. In the *Package Layers* dialog box, in *Input layers* choose all the clipped layers and in *Destination GeoPackage* click *...* and select **Save to File...**. Then save the file as ``clipped.gpkg``. Click **Run**
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch12.png')
```
13. Now a new ``clipped.gpkg`` geopackage layer will be created with all the clipped layers in it.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/batch13.png')
```
Your output should match the contents of the `Batch_Processing_Checkpoint1.qgz` file in the `solutions` folder.
### 2.1.1 Challenge
Reprojected all the clipped layers to a UTM projection. You can reproject all the layers to the CRS **WGS84 / UTM zone 43N EPSG:32643**. Save the reprojected layers to the same GeoPackage ``clipped.gpkg``
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2_1_1_challenge.png')
```
# 3. Model Designer
GIS Workflows typically involve many steps - with each step generating intermediate output that is used by the next step. If you change the input data or want to tweak a parameter, you will need to run through the entire process again manually. Fortunately, Processing Framework provides a model designer that can help you define your workflow and run it with a single invocation. You can also run these workflows as a batch over a large number of inputs.
## 3.1 Workflow Automation
National Geospatial-Intelligence Agency’s Maritime Safety Information portal provides a shapefile of all incidents of maritime piracy in the form of Anti-shipping Activity Messages. We can create a density map by aggregating the incident points over a global hexagonal grid.
The steps needed to create a hex-bin layer suitable for visualization is as follows:
- Reproject the input to an equal-area projection
- Create a global hexagonal grid layer
- Select all grids that intersect with at least 1 point
- Count points within each grid
We will now learn how to build a model that runs the above processing steps in a single workflow.
1. Open the **maritime_piracy** project.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler1.png')
```
2. Launch the modeler from **Processing → Model Designer**
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler2.png')
```
3. In the Processing Modeler dialog, locate the *Model Properties* panel. Enter ``piracy_hexbin`` as the Name of the model. Note that there is a History panel in the bottom left, which keeps track of all the work done in Model Builder. Click the Save button.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler3.png')
```
4. Save the model as ```piracy_hexbin.model3```. Also, check the folder in which it is being saved. *Processing → models*, from this location QGIS will read the model by default.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler4.png')
```
5. Now, we can start building a graphical model of our processing pipeline. The Processing modeler dialog contains a left-hand panel and the main canvas. On the left-hand panel, locate the Inputs panel listing various types of input data types. Scroll down and select the **+ Vector Layer** input. Drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler5.png')
```
6. Enter **Input Points** as the *Description* and **Point** as the *Geometry type*. This input represents the piracy incidents point layer.
```{r echo=FALSE, fig.align='center', out.width='50%'}
knitr::include_graphics('images/advanced_qgis/modeler6.png')
```
7. Next, drag another **+ Vector Layer** input to the canvas. Enter **Base Layer** as the *Description* and **Polygon** as the *Geometry type*. This input represents the natural earth global land layer in which we will use as the extent of the grid layer.
```{r echo=FALSE, fig.align='center', out.width='50%'}
knitr::include_graphics('images/advanced_qgis/modeler7.png')
```
8. As we are generating a global hexagonal grid, we can ask the user to supply us with the grid size as an input instead of hard-coding it as part of our model. This way, the user can quickly experiment with different grid sizes without changing the model at all. Select a **+ Number** input and drag it to the canvas. Enter **Grid Size** as the Parameter name, **Integer** as the Number Type, **200000** as Default Value, and click OK.
```{r echo=FALSE, fig.align='center', out.width='50%'}
knitr::include_graphics('images/advanced_qgis/modeler8.png')
```
9. Now that we have our user inputs defined, we are ready to add processing steps. All of the processing algorithms are available to you under the *Algorithms* tab. The first step in our pipeline will be to reproject the base layer to the Project CRS. Search for the **Reproject layer** algorithm and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler9.png')
```
10. In the *Reproject layer* dialog, select **Base Layer**, by clicking the button under the *Input Layer* and select **Model Input** as the *Input layer*. Check the Use **project CRS** as the *Target CRS*. Click OK.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler10.png')
```
11. In the Processing Modeler canvas, you will notice a connection appear between the ```+ Base Layer``` input and the ```Reproject layer``` algorithm. This connection indicates the flow of our processing pipeline. The next step is to create a hexagonal grid. Now search for the **Create grid** algorithm and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler11.png')
```
12. In the Create grid dialog, choose **Hexagon (polygon)** as the *Grid type*. Select **Algorithm Output** by clicking the button under *Grid extent* and choose **'Reprojected' from the algorithm 'Reproject Layer'** as the Grid extent.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler12.png')
```
13. Select **Model input** by clicking the button under *Horizontal Spacing* and choose **Grid Size** as the *Horizontal Spacing*. Click OK.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler13.png')
```
14. Select **Model input** by clicking the button under *Vertical Spacing* and choose **Grid Size** as the *Vertical Spacing*. Click OK.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler14.png')
```
15. At this point, we have a global hexagonal grid. The grid spans the full extent of the base layer, including land areas and places where there are no points. Let us filter out those grid polygons where there are no input points. Search for **Extract by location** algorithm and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler15.png')
```
16. Select **Algorithm Output** from the icon below *Extract features from*. Now choose **'Grid' from the algorithm 'Generate Grid'**. Click **...** at the right end.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler16.png')
```
17. Select **Intersect** by clicking the button, as Where the features(geometric predicate). And click the triangle button to go back to the previous window. Now click OK.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler17.png')
```
18. Select **Model input** by clicking the button under *By comparing to the features from* and choose **Input Points** as *By comparing to the features from*. Click OK.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler18.png')
```
19. Now, we have only those grid polygons that contain some input points. To aggregate these points, we will use **Count points in polygon** algorithm. Search and drag it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler19.png')
```
20. Select **Algorithm Output** by clicking the button under *Polygons* and choose **'Extracted (location)' from algorithm 'Extract by location'** for *Polygons*. Now, Select **Model Input** by clicking the button under *Points* and choose **Input Points** for *Points*. Type **NUMPOINTS** in **Count field name**. At the bottom, name the Count output layer as **Aggregated**. Click OK.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler20.png')
```
21. The model is now complete. Click the *Save* button.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler21.png')
```
22. Switch to the main QGIS window. You can find your newly created model in the *Processing Toolbox* under **Models →piracy_hexbin**. Double-click to run the model.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler22.png')
```
23. Our *Base Layer* is the ``ne_10m_land`` and the *Input Points* layer is ``ASAM_events``. The Grid Size needs to be specified in the units of the selected CRS. Enter ``100000`` as the Grid Size. Grid size is in the unit of the current CRS, which is meters. Click Run to start the processing pipeline. Once the process finishes, click Close.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler23.png')
```
24. You will see a new layer ``Aggregated`` loaded as the result of the model. As you explore, you will notice the layer contains an attribute called *NUMPOINTS* containing the number of piracy incidents points contained within that grid feature.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler24.png')
```
25. To ensure that you are always able to reproduce your results, it is recommended to bundle the model in your project. The modeler interface has a button **Save model in project**. Click on it to embed the model in your QGIS project file.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler25.png')
```
26. Once the model is embedded, Whenever you open the project, the model will be available to the user under the **Project models** in the Processing Toolbox.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/modeler26.png')
```
Your output should match the contents of the `Modeler_Checkpoint1.qgz` file in the `solutions` folder.
### 3.1.1 Challenge
Add a step to extract the grid with the highest piracy incidents.
Hint: The expression ``maximum("NUMPOINTS")`` will give you the highest value contained in the ``"NUMPOINTS"`` field. Use it to build your complete expression.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/3_1_1_challenge.png')
```
## Concept: Spatial Indexing
When you ran your model, you may have noticed a warning message **No spatial index exists for the input layer, performance will be severely degraded**. This is because certain spatial queries make use of a spatial index and QGIS warns you when having a spatial index can speed up your operations. PostGIS documentation has a good [overview of spatial indexes](https://postgis.net/workshops/postgis-intro/indexing.html) and why they are important.
You can compare a *spatial index* to a book *index*. When you want to search for a particular term, rather than scanning each page sequentially, you can speed up your search by looking up the index and directly going to the pages where the word appears. Spatial indexes work in similarly. You spent the effort once to create the index and all subsequent operations can make use of it. When you create a spatial index, each feature's bounding box is used to establish its relationship with other features. This is stored alongside the dataset and can be used by algorithms. When trying to determine spatial relationships, the algorithms speed-up the look-up using the following *two-pass* method:
* Step 1: Use the spatial index to determine which target features' bounding boxes intersect with the source feature's bounding box. Since the spatial index already has computed this - this is very fast. The result is a list of candidate features.
* Step 2: Now that there is a small subset of candidate features, iterate over them and use their full geometry to evaluate exact intersections.
For large datasets, this approach helps reduce the processing time significantly.
[![View Presentation](images/advanced_qgis/spatial_indexing.png){width="400px"}](https://docs.google.com/presentation/d/1K1lR1JfonxGbmj8rCIVln_WkbWv6-tOYTYHAEJKAiw4/edit?usp=sharing){target="_blank"}
[View the Presentation](https://docs.google.com/presentation/d/1K1lR1JfonxGbmj8rCIVln_WkbWv6-tOYTYHAEJKAiw4/edit?usp=sharing){target="_blank"}
## 3.2 Using Spatial Index
QGIS has built-in tools to create and use spatial indexes. Let's see how we can create a spatial index for a layer and use it in our model.
1. Double click on the ```piracy_hexbin``` model from *Processing Toolbox*, select **ne_10m_land** as *Base Layer*, **200000** as *Grid size* and **ASAM_events** as *Inputs Points*. Click Run.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index01.png')
```
2. Switch to *Log*, and note down the execution time of the model. In this instance we got *34.80 seconds* (The exact time will vary depending on your computer configuration). Also note that it prompts a warning *No spatial index exists for input layer, performance will be severely degraded*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index02.png')
```
3. To create spatial index right-click the **Aggregated** layer and select *Properties*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index03.png')
```
4. In the *Layer Properties* dialog, switch to the *Source* tab. You will see the *Create Spatial Index* button. This button indicates that this layer does not currently have a spatial index. Click that button to create a spatial index. But we will use a better and more scalable way. Click **OK** and close the *Layer Properties* dialog.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index04.png')
```
5. Go to **Processing → Toolbox**. Search for the algorithm **Vector general → Create spatial index** and double-click to launch it.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index05.png')
```
6. Select **Aggregated** layer as the *Input layer* and click *Run*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index06.png')
```
7. Creating a spatial index is usually a fast operation. Once the algorithm finishes, right-click on the **Aggregated** layer and select *Properties*. Switch to the *Source* tab and you will see that the button has now changed to *Spatial Index Exists* - indicating that the layer now has a spatial index.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index07.png')
```
8. While creating a spatial index on a layer like this is useful, it is even more powerful to have the spatial index created during the execution of our model. We will see how to add the spatial index creation step in our model. Right-click the ```piracy_hexbin``` model and select *Edit Model…*. Search **Create spatial index** in *Algorithms* tab and drag it to the model canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index08.png')
```
9. Choose **Algorithm output** from the button below *Input Layer*, and select **“Grid” from algorithm “Create Grid”.** Click OK.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index09.png')
```
10. You will see that the newly added ```Create spatial index``` algorithm is connected to the ```Create grid``` algorithm. But the workflow should be, **Create grid → Create spatial index → Extract from location**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index10.png')
```
11. Right-click the ```Extract by location```
layer and select *Edit*. In *Extract features from* select **“Indexed Layer” from algorithm “Create spatial index”**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index11.png')
```
12. Now you will see the model diagram updated and the connection changed between the ```Create Grid``` and ```Extract by location``` layer. Click *Save model*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index12.png')
```
13. Now return to the main canvas and select ```piracy_hexbin``` model.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index13.png')
```
14. Select **ne_10m_land** as *Base Layer*, **200000** as *Grid size* and **ASAM_events** as *Inputs Points*. Click Run.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index14.png')
```
15. In this instance we got *3.39 seconds* (The exact time will vary depending on your computer configuration). Also, note that it has executed 5 algorithms which is including creating the spatial index.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/Index15.png')
```
Your output should match the contents of the `Modeler_Checkpoint2.qgz` file in the `solutions` folder.
### 3.2.1 Challenge
Can you change the model so that instead of entering the grid size in meters, the user can enter the size in kilometers?
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/3_2_1_challenge.png')
```
Hint: The **Create Grid** algorithm expects the size in meters, so you will have to convert the input to meters. Instead of using *Model Input*, use *Pre-calculated Value* to enter an expression.
## 3.3 Enabling Reproducible Workflows
GeoPackage is an open and flexible data format that makes data management simple. We saw how you can package multiple layers in a GeoPackage. We will now learn how to embed QGIS projects inside of a goepackage.
QGIS projects contain information of layer configuration, symbology, label positions and even models. We can make the reproducing your workflow even easier by embedding the QGIS Project in the same geopackage containing the source layers.
1. Now, lets save this project in ``martime_piracy.gpkg`` geopackage. Click **Project → Save To → GeoPackage...**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/reproducibleWorkflows01.png')
```
2. In the *Save project to GeoPackage* dialog box, click ... to browse to martime_piracy geopackage.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/reproducibleWorkflows02.png')
```
3. Enter project name as ``martime_piracy`` and click **OK**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/reproducibleWorkflows03.png')
```
4. Now in *Browser*, under ``martime_piracy.gpkg`` the project is saved. You now have just 1 GeoPackage file that contains all your input layers, styles, project and models. Sharing this 1 file will enable anyone to reproduce your output using the embedded model and layers.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/reproducibleWorkflows04.png')
```
# Assignment
The following assignment is designed to help you practice the skills learnt so far in the course and explore automation using the model designer.
Your task is to create a model called **Calculate Highway Statistics** that will automate the workflow outlined in the previous section [1. Using Processing Toolbox](#using-processing-toolbox).
Your model is expected to take following 3 inputs
* *Roads Layer*: A vector layer of the road network. i.e. `karnataka_major_roads`.
* *Admin Boundaries Layer*: A vector layer containing admin boundaries. i.e. `karnataka_districts`
* *Highway Type*: An enum input with 2 options: *National Highway* and *State Highway*.
```{r echo=FALSE, fig.align='center', out.width='75%', fig.cap='Model Inputs'}
knitr::include_graphics('images/advanced_qgis/assignment1.png')
```
The model should take the input and calculate the total length of highways in each admin polygon. The result should be a formatted table containing 2 columns: `district` and `total_length`. Test the model on the layers provided in the `karnataka.gpkg` file in the data package.
```{r echo=FALSE, fig.align='center', out.width='40%', fig.cap='Expected Model Output'}
knitr::include_graphics('images/advanced_qgis/assignment2.png')
```
**Tips and Best Practices**:
* Test your model at each step. After adding an algorithm, enter a name for the output, save the model and run it. The output will be loaded in QGIS. Ensure the generated output is as expected. If you get an error, fix it before adding another step.
* You need to extract the correct type of highways from the roads layer based on the model input for the parameter *Highway Type*. If the selected type is **National Highway**, you must extract the features with `ref` column values starting with **NH**. If the selected type is **State Highway**, you must extract the features with `ref` column values starting with **SH**. You may use either `if` or the `CASE` conditionals to build your expression.
* The *Enum* input type is used to define a set of pre-selected values that the user can pick from. The values are displayed as strings, but in the algorithm they are stored as integers. If the user selected the first option - its value will be 0. The second option will have the value 1 and so on.
* The *Refactor Fields* algorithm will need to be configured manually in the modeler. The input to that algorithm is an intermediate layer which has not been generated yet, so the model cannot pre-fill the values. Configure that algorithm from the main QGIS window and copy the parameter values from there.
* Use *Modeler Tools → Raise message* algorithm to display the value of any variable during the model execution.
**Starter Model**: The hardest part of the assignment is to take the Enum input and write an expression to extract the appropriate highway type. We encourage you to try to figure this out yourself. If you struggle with this, you can download the [`assignment_hint.model3`](https://drive.google.com/uc?export=download&id=1GMF_YfsvpHmex22WdNC6b0Fx5PsFPttQ) file which has the first step configured. You will need to add the remaining steps to complete the model. You can open this downloaded file from the *Processing Toolbox Menu → Models → Open Existing Model*.
# 4. 2D Animations
Time is an important component of many spatial datasets. Along with location information, time providers another dimension for analysis and visualization of data. If you are working with a dataset that contains timestamps or have observations recorded at multiple time-steps, you can easily visualize it using the **Temporal Controller** in QGIS 3.14 or above.
## 4.1 Animated Temporal Navigation
We will continue to work with the maritime piracy dataset. First, we will create a heatmap visualization and then animate the heatmap to show how the piracy hot-spots have changed over the past 2 decades.
1. Open the **maritime_piracy** project from the data package. There are thousands of incidents and it is difficult to determine with more piracy. Rather than individual points, a better way to visualize this data is through a heatmap. Select the ``ASAM_events`` layers and click the **Open the layer Styling Panel** button in the Layers panel. Click the **Single symbol** drop-down.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d01.png')
```
2. In the renderer selection drop-down, select **Heatmap** renderer. Next, select the **Viridis** color ramp from the Color ramp selector. Adjust the Radius value to **5.0.** At the bottom, expand the *Layer Rendering* section and adjust the opacity to **75.0%**. This gives a nice visual effect of the hotspots with the land layer below.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d02.png')
```
3. Now let’s animate this data to show the yearly map of piracy incidents. Right click on ``ASAM_event`` layer, and choose *Properties.*
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d03.png')
```
4. In the layer properties dialog box, select the *Temporal Settings* tab and enable it by clicking the checkbox.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d04.png')
```
5. The source data contains an attribute ``dateofocc`` - representing the date on which the incident took place. This is the field that will be used to determine the points that are rendered for each time period. Select **Single Field with Data/Time** in *Configuration* Drop down menu, **dateofocc** as Field. Click *OK*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d05.png')
```
6. Now a *Clock symbol* will appear next to the layer name. Click on the **Temporal Control Panel** (Clock icon) from Map Navigation Toolbar.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d06.png')
```
7. Click on the *Animated Temporal Navigation* (play icon).
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d07.png')
```
8. By default, the date range is set to the current date. Click *Set to Full Range* button to set the date range from the dataset.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d08.png')
```
9. Now the data will be set to ``2000-01-02`` to ``2018-01-01``. Update the start date to ``2000-01-01`` and set the *Step* to **years**. Click *play* button to start rendering.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d09.png')
```
10. It will be useful to add a label to the animation showing the date of the animation frame being displayed. We can use the *Title Decoration* to place that label. Go to **View → Decorations → Title Label Decorations.** Click the checkbox to enable it and then the **Insert an Expression** button.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d10.png')
```
11. Enter the following expression to display the year and click *OK*.
```format_date(@map_start_time, 'yyyy-MM-dd')```
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d11.png')
```
12. Select font size as **25**, set background color as **white.** In placement choose **Top Center.** Now click OK.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/2d12.png')
```
Your output should match the contents of the `2DAnimation_Checkpoint1.qgz` file in the `solutions` folder.
### Challenge 4.1.1
Update the heatmap to use a weight field. We want to give more importance to more serious attacks. We can use the Hostility Description contained in the **hostility_D** field of the `ASAM_events` layer and assign weights to different types of events. Once created, style the new layer using the *Heatmap* renderer with the *weight* value.
Hint: Use the *Field Calculator* algorithm to create a new layer with a field called `weight`. You can use the expression below to assign different weights to each category.
```
CASE
WHEN "hostilit_D" = 'Suspicious Approach' THEN 1
WHEN "hostilit_D" = 'Kidnapping' THEN 2
WHEN "hostilit_D" = 'Pirate Assault' THEN 5
WHEN "hostilit_D" = 'Naval Engagement' THEN 10
ELSE 1
END
```
## 4.2 Exporting Animation
### 4.2.1 Animation using EzGIF.com
1. To export these as images and convert them as GIF select the **Export Animation** (save icon) in the Temporal controller window.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/export01.png')
```
2. Choose the directory to save the images and select the ``ne_10_land`` for map extent.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/export02.png')
```
3. Once the export finishes, you will see PNG images for each year in the output directory. Now let’s create an animated GIF from these images. There are many options for creating animations from individual image frames. We like ![[ezgif.com](https://ezgif.com/maker)] for an easy and online tool. Visit the site and click Choose Files and select all the .png files. You may want to sort the images by Type to allow easy bulk selection of only .png files. Once selected, click the Upload and make a GIF! button.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/export03.png')
```
### 4.2.2 Animation using ImageMagick
For exporting large animations, it is preferable to compose the individual frames to a GIF on your own machine. [ImageMagick](https://imagemagick.org/) is a free and open-source command-line tool that is widely used for bulk image processing. You can [Download and Install](https://imagemagick.org/script/download.php) it on your system. Once installed, it can be run from Windows Command Prompt or a Mac/Linux Terminal. Navigate to the directory containing exported image frames using the `cd` command and run the following command to create the GIF.
```
magick -delay 50 -loop 0 *.png animation.gif
```
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/imagemagick.png')
```
### Challenge 4.2.1
Export your animation and create an animated GIF showing the changes in piracy hotspots over time.
## 4.3 Animating GPS Tracks
GPS tracks have become ubiquitous in modern life. With GPS built-into most phones, many of us capture the tracks while running or biking outdoors. Cab companies use GPS tracks collected during the trip to determine fares. Delivery and logistics companies store and analyze millions of GPS tracks from their assets to derive location intelligence.
We will now learn how to collect a GPS track and create an animation from it. We will produce an animation that looks like shown below.
![[View Animated GIF ↗](images/advanced_qgis/gps_animation.gif){target="_blank"}](images/advanced_qgis/gps_animation.png)
### 4.3.1 Collecting a GPS Track
We encourage you to collect your own GPS track for this exercise. You can collect a GPS track from a smartphone and download it to your computer. The default format for storing GPS tracks is [GPS Exchange Format (GPX)](https://en.wikipedia.org/wiki/GPS_Exchange_Format). It is a XML-based text format that allows storing points, tracks and routes in a single file. You can use any of the options below to capture your track.
- Android: There are many GPS logger apps. We recommend the open-source [GPS Logger app](https://www.basicairdata.eu/projects/android/android-gps-logger/).
- iOS: A good free and open-source app is the [Open GPX Tracker](https://apps.apple.com/app/open-gpx-tracker/id984503772) for your iPhone.
- Strava: You can also record your activity using the popular fitness tracking app [Strava](https://www.strava.com/). Once the activity is uploaded, you can use the [GPX Export](https://support.strava.com/hc/en-us/articles/216918437-Exporting-your-Data-and-Bulk-Export#h_01GDP2JB35R4ECM0E6YAH316B9) option to download a GPX file.
If you do not have your own data, you can use the ``sample_gps_track.gpx`` file from your data package.
### 4.3.2 Loading a Basemap
We will use a plugin called **QuickMapServices** to select and load a basemap.
1. Open QGIS. From the *Plugins* menu choose *Manage and Install Plugins...*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/basemap1.png')
```
2. The *Plugins* dialog contains all the available plugins in QGIS. Under the *All* tab, search for **quickmapservices**. It has different basemaps that can be used based on your purpose. Click on the *Install Plugin*, to add this plugin to QGIS.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/basemap2.png')
```
3. Once installed, check the box next to the *QuickMapServices* label to enable it. Click *Close*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/basemap3.png')
```
4. Now you will see a new *Web* menu added to the menu-bar. Go to *Web → QuickMapServices* menu. You will see some map providers and available basemaps. We can enable a few more providers to have many more options. Click on the *Web → QuickMapServices → Settings*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/basemap4.png')
```
5. In the *Settings* dialog, switch to the *More services* tab. Click on the ``Get contributed pack`` to download 3rd-party basemaps.
> You will see a warning against using contributed services. Some of these services may have restrictions on their usage and/or attribution requirement that you need to follow. Please review them before using them in your project.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/basemap5.png')
```
6. Once the new services are added, you will see many more options in the *Web → QuickMapServices* menu.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/basemap6.png')
```
### 4.3.3 Visualizing and Creating Animation
1. Start a new QGIS Project. Locate your GPS file in the *Browser* panel. If you are using the track from the data package, locate the ``sample_gps_track.gpx`` file. Drag and drop it to the canvas.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/gps_visualize1.png')
```
2. As the file contains multiple data types, a pop-up will ask us to select the layers to add. Hold the *Shift* key and select both ``track_points`` and ``tracks`` layers. Click *Add Layers*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/gps_visualize2.png')
```
3. Two new layers will be added to the *Layers* panel. To add some context to the map, we should add a basemap. A dark background map works best for the visualization we want to create. Go to *Web → QuickMapServices → CartoDB → Dark Matter [no labels]* layer.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/gps_visualize3.png')
```
4. Turn off the visibility of the ``track_points`` layer by un-checking the box next to it. Select the ``tracks`` layer and click *Open the Layer Styling Panel*. You can change the line *Color* to **Blue** and *Width* to **0.2**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/gps_visualize4.png')
```
5. Turn on the visibility of the ``track_points`` layer and drag it above the ``tracks`` layer. This changes the rendering order and the points will be drawn on top of the line layer. In the *Layer Styling Panel*, select *Simple marker* symbol. Change the point *Size* to ``1`` . Choose a lighter shade of Blue as the *Fill color* and a ``Transparent Stroke`` as *Stroke color*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/gps_visualize5.png')
```
6. Depending on how fast you were moving and how often the data was collected, you may have a lot of points. You may want to filter out some points so we can create a meaningful animation. We can apply an expression to display only a subset of points from the layer using *Geometry Generators*. Change the *Symbol layer type* from **Simple Marker** to **Geometry generator**.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/gps_visualize6.png')
```
7. Enter the following expression. The following expression displays every 10th point from the original track. Adjust that number for your dataset.
```
if(@id % 10 = 0, $geometry, NULL)
```
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/gps_visualize7.png')
```
8. We want to give a glow effect to the points as it is animating. To achieve this effect, we will apply a different symbology. Right-click the ``track_points`` layer and choose *Duplicate Layer*.
```{r echo=FALSE, fig.align='center', out.width='75%'}
knitr::include_graphics('images/advanced_qgis/gps_visualize8.png')