forked from OSGeo/grass-tutorials
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththematic_maps.qmd
More file actions
1435 lines (985 loc) · 51.4 KB
/
thematic_maps.qmd
File metadata and controls
1435 lines (985 loc) · 51.4 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
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: "Making Thematic Maps"
author: "Michael Barton"
date: 2025-06-25
date-modified: today
lightbox: true
engine: knitter
image: img_thematic/thumbnail.webp
format:
html:
embed-resources: true
toc: true
code-tools: true
code-copy: true
code-fold: false
page-layout: article
categories: [beginner, intermediate, raster, vector, thematic maps, charts, legend]
description: This tutorial guides a user through multiple ways of creating thematic maps with raster and vector GIS data.
execute:
eval: false
copyright:
holder: Michael Barton
year: 2025
funding: "Creation of this tutorial was supported in part by US National Science Foundation grant FAIN 2303651."
---
Thematic maps are the most common form of analytical visualization done in GIS software. A thematic map uses color, or object shape or size, to represent geographic variation in some property, represented by categorical or numerical values in spatial data.
Most GIS software supports the creation of thematic maps from vector objects (points, lines, and areas). GRASS likewise enables the creation of thematic maps from vector data. It also supports the creation of thematic maps from rasters, which represent space a a grid of cells or pixels.
In this tutorial, we will explore the creation of thematic maps from both raster and vector geospatial data.
::: {.callout-note title="Dataset"}
This tutorial uses one of the standard GRASS sample data sets: nc_basic_spm_grass7. We will refer to place names in that data set, but it can be completed with any of the [standard sample data sets](https://grass.osgeo.org/download/data/) for any region. Keep in mind that the specific vector map attribute columns may not be available for a map from a different location, and you may need to use other attribute columns.
This tutorial is designed so that you can complete it using the **GRASS GUI**, GRASS commands from the **console or terminal**, or using GRASS commands in a **Jupyter Notebook** environment.
:::
::: {.callout-note title="Don't know how to get started?"}
If you are not sure how to get started with GRASS using its graphical user interface or using Python, checkout the tutorials [Get started with GRASS GIS GUI](../get_started/fast_track.qmd) and [Get started with GRASS & Python in Jupyter Notebooks](../get_started/fast_track_grass_and_python.qmd).
:::
# Raster Thematic Maps
Raster thematic maps use color to represent varying land cover, land use, or terrain characteristics across a map. This can be characteristics like slope, represented in degrees, that varies continuously or like land cover, represented by areas of a map encoded with distinct categories.
## Rasters with continuous variation: terrain slope
Terrain slope is an example of a characteristic represented by numeric values (in degrees) that vary continuously across a landscape. A map of slope can be created from an elevation map using the [r.slope.aspect](https://grass.osgeo.org/grass-stable/manuals/r.slope.aspect.html) tool.
This will produce a map of continuously varying slope across the entire map. Colors can be assigned to raster cells according to their slope values to visualize areas of high and low slope. The slope that each color represents can be shown in a legend to help users interpret the map.
::: {.callout-note title="Modeling terrain in GRASS"}
To learn more about modeling and visualizing slope and other terrain characteristics in GRASS, see the[Visualizing and Modeling Terrain from DEMs in GRASS](https://grass-tutorials.osgeo.org/content/tutorials/terrain_and_DEMs/GRASS_terrain.html) tutorial. For more information about rasters in GRASS, see [Raster data processing in GRASS GIS](https://grass.osgeo.org/grass-stable/manuals/rasterintro.html#raster-map-operations).
:::
Create a slope map from the *elevation* DEM map
::::::::: {.panel-tabset group="language"}
#### GUI
::::: grid
::: g-col-6
1. Open r.slope.aspect tool from the Raster/Terrain analysis/Slope and aspect menu.
2. Set elevation raster layer as the "Name of input elevation raster map".
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
3. Enter name *slope* for "Name for output slope raster map".
4. Click Run.
:::
::: g-col-6
{fig-align="right"}
:::
:::::
#### Command line
```{bash}
r.slope.aspect elevation=elevation slope=slope
```
#### Python
```{python}
gs.run_command("r.slope.aspect",
elevation="elevation",
slope="slope")
```
:::::::::
Here is the resulting slope map.

### Selecting a color table
The default colors of the slope map we created vary from pale yellow to violet as slope changes from flat to steep. We can assign different *color tables* to represent variation in slope with different colors.
- For example, we could change the **intensity** of a color to represent slope: pale color for low slopes and intense color for steep slopes.
- Color tables can be assigned, and custom color tables defined, using the [r.colors](https://grass.osgeo.org/grass-stable/manuals/r.colors.html) module.
- Let's apply the *reds* color table to the slope map so that the slope is represented by the intensity of the red color.
::::::::: {.panel-tabset group="language"}
#### GUI
::::: grid
::: g-col-6
1. Select [r.colors](https://grass.osgeo.org/grass-stable/manuals/r.colors.html) tool from the Raster/Manage colors menu.
2. In the "Map" tab, select the *slope* map in the "Name of raster map(s)" entry box.
:::
::: g-col-6
{fig-align="right"}
:::
:::::
3. In the Define tab, select *reds* as the color table.
::::: grid
::: g-col-6
{fig-align="right"}
:::
::: g-col-6
{fig-align="left" width="60%"}
:::
:::::
4. Click Run.
#### Command line
```{bash}
r.colors -e map=slope color=reds
```
#### Python
```{python}
gs.run_command("r.colors",
map="slope",
color="reds")
```
:::::::::

Try out some other color tables. The original default color is the *slope* color table. Try the *gyr* (green to yellow to red) color table with *histogram equalization* checked. You can also make a custom color table, as we describe later in this tutorial.
### Creating a legend
A **legend** can help you determine what the range of values present in your map and how the colors correspond to these values. The raster [d.legend](https://grass.osgeo.org/grass-stable/manuals/d.legend.html) tool lets you easily make a highly informative legend for this map.
:::::::::::::::::::::::: {.panel-tabset group="language"}
#### GUI
::::: grid
::: g-col-6
The raster *d.legend* tool can be accessed from the display window tool bar. This tool has many options. We'll explore several of them.
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
1. Under the Input tab, enter *slope* as the "Name of raster map".
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
2. Under the Title tab, enter "Slope" as the "Legend title", and enter 14 as the "Title font size".
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
3. Under the Advanced tab, enter the degree symbol (°) or the word "deg" as "Units to display".
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
4. Under the Gradient tab, check the "Add histogram to smoothed legend" box. Also enter *0,10,20,30* in the "Specific values to draw ticks" to display these evenly spaced values as legend ticks.
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
6. Under the Font settings tab, enter 12 as the "Font size" for the legend.
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
6. Finally, under the Background tab, check the box to show the background.
7. Click OK to see the legend. You can reposition and resize the legend with a mouse or numerically under the Options tab.
:::
::: g-col-6
{fig-align="right"}
:::
:::::
#### Command line
- A legend can be generated using the [d.legend](https://grass.osgeo.org/grass-stable/manuals/d.legend.html) command.
- A scale bar and north arrow can be generated using the [d.barscale](https://grass.osgeo.org/grass-stable/manuals/d.barscale.html) command.
```{bash}
d.legend -d -b raster=slope title=Slope title_fontsize=14 units=° label_values=0,10,20,30 fontsize=12 border_color=0:0:0:255
d.barscale -n length=2 units=kilometers segment=5 bgcolor=none width_scale=2
```
#### Python
- A legend can be generated using the [d.legend](https://grass.osgeo.org/grass-stable/manuals/d.legend.html) command.
- A scale bar and north arrow can be generated using the [d.barscale](https://grass.osgeo.org/grass-stable/manuals/d.barscale.html) command.
```{python}
gs.run_command("d.legend",
map="slope",
title="Slope",
title_fontsize=14,
units="°",
label_values="0,10,20,30",
fontsize=12,
border_color="0:0:0:255",
flags="db")
gs.run_command("d.barscale",
length=2,
units="kilometers",
segment=5,
bgcolor="none",
width_scale=2,
flags="n"
```
::::::::::::::::::::::::
Here is the result.
- It not only shows the range of slope values and colors of different slope values, but also the histogram shows the number of cells for each slope value.
- You can also add a scale bar and north arrow from the same menu bar item in the display window where you selected the legend tool.

## Categorizing continuous variation
Sometimes it is useful to divide continuous variation into a set of distinct categories for analysis or communication. For example, the slope map we created previously might be displayed as areas of low slope and high slope, rather than a continuum of slope values. For raster maps, this is done in GRASS through *reclassification* of the cell values. While there are several ways to do this, the easiest is with the [r.reclass](https://grass.osgeo.org/grass-stable/manuals/r.reclass.html) tool, found under the Raster/Change category menu. Below, we will use *r.reclass* to create a map of distinct slope categories from the slope map we produced previously.
### Statistics for categories
How should we group slope into categories? One way is to use statistical analysis to divide slope statistically into quartiles, each of which represents the same number of raster cells. GRASS has numerous tools for [statistical analyses](https://grass.osgeo.org/grass-stable/manuals/topic_statistics.html) of raster and vector maps.
- The [r.univar](https://grass.osgeo.org/grass-stable/manuals/r.univar.html) tool can be used to find the cutoff values for the 1st, 2nd, 3rd, and 4th quartiles.
- We could define the 1st quartile (25% of raster cells with the lowest slopes) as 'low slope', the 4th quartile (25% of cells with the highest slopes) as 'high slope', and the remaining cells as 'intermediate slope' (3rd and 4th quartiles, representing the 50% of cells with intermediate slopes).
::: {.panel-tabset group="language"}
#### GUI
1. Open *r.univar* from the Raster/Reports and Statistics menu.
2. Enter *slope* as the "Name of the raster map(s)" under the Required tab.
3. Click run.
#### Command line
```{bash}
r.univar map=slope percentile=25,50,75
```
#### Python
```{python}
gs.run_command("r.univar",
map="slope")
```
:::
::::: grid
::: g-col-5
Here is the result:
```
n: 2019304
minimum: 0
maximum: 38.6894
range: 38.6894
mean: 3.86452
mean of absolute values: 3.86452
standard deviation: 3.00791
variance: 9.04755
variation coefficient: 77.834
% sum: 7803645.55388512
1st quartile: 1.85464
median (even number of cells): 3.21512
3rd quartile: 5.02421
```
:::
::: g-col-7
To divide slope into quartiles, we need to create the following slope groups:
| slope | quartile | slope category |
|:---------:|:------------:|:------------:|
| 0-1.85 | 1st quartile | low slope |
| 1.86-5.02 | 2nd & 3rd quartiles | intermediate slope |
| 5.03-38.7 | 4th quartile | high slope |
:::
:::::
### Reclassification
Now we can use the results of the above statistical analysis to reclassify slope into four categories, each of which represents 1/4 of the total raster cells in the map.
:::::: {.panel-tabset group="language"}
## GUI
The [r.reclass](https://grass.osgeo.org/grass-stable/manuals/r.reclass.html) tool is found under the Raster/Change category menu.
::::: grid
::: g-col-6
1. Enter *slope* for the raster to be reclassified.
2. Enter *slope_quartiles* for the output reclassified map.
3. Enter the reclass rules directly in the text box or from a saved text file. Use and \* symbol to represent everything not covered by the specific reclass rules.
4. Click run.
```
0 thru 1.85 = 1 low slope
1.86 thru 5.02 = 2 intermediate slope
5.03 thru 38.7 = 3 high slope
```
:::
::: g-col-6
{fig-align="right"}
:::
:::::
## Command line
```{bash}
r.reclass input=slope output=slope_quartiles rules=- << EOF
0 thru 1.85 = 1 low slope
1.86 thru 5.02 = 2 intermediate slope
5.03 thru 38.7 = 3 high slope
EOF
```
## Python
```{python}
rules = """\
0 thru 1.85 = 1 low slope
1.86 thru 5.02 = 2 intermediate slope
5.03 thru 38.7 = 3 high slope
"""
gs.write_command("r.reclass",
input="slope",
output="slope_quartiles",
rules="-",
stdin=rules)
```
::::::
This creates a new map named *slope_quartiles* with slope divided into three categories.

### Assigning a custom color table
This map is helpful for showing areas with different slope categories. But it might be more interpretable with a different set of colors. We could pick a different pre-defined color table using the *r.colors* tool demonstrated previously. We can also define a custom color table using the same tool.
Creating a custom color table is as easy as specifying a category number followed by a color.
- The color can be one of the named colors that GRASS recognizes (*red,orange,yellow,green,blue,indigo,violet,white,black,gray,brown,magenta,aqua,grey,cyan,purple*) or
- the color can be specified as RGB values (0-255 for red:green:blue).
- For example, to create a color table using aqua, yellow, and red for categories 1-3 in the *slope_quartiles* map, you simply need to list them as:
```
1 aqua
2 yellow
3 red
```
- To specify the same colors as RGB, you would write the rules as:
```
1 100:128:255
2 255:255:0
3 255:0:0
```
You can save this to a plain text *rules file* and upload it in *r.colors* or you can specify it directly within the *r.colors* tool.
:::::::::::: {.panel-tabset group="language"}
#### GUI
::::: grid
::: g-col-6
1. Specify *slope_quartiles* as the map you want to assign the color table to
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
2. Enter the color rules in the "enter values directly" box
3. Press run
:::
::: g-col-6

:::
:::::
::::: grid
::: g-col-6
You can also specify color rules interactively by selecting Raster/Manage colors/Manage color rules interactively
:::
::: g-col-6
{fig-align="right"}
:::
:::::
#### Command line
```{bash}
r.colors -e map=slope_quartiles rules=- << EOF
1 aqua
2 yellow
3 red
EOF
```
#### Python
```{python}
rules = """\
1 aqua
2 yellow
3 red
"""
gs.write_command("r.colors",
map="slope_quartiles",
rules="-",
stdin=rules)
```
::::::::::::
Here is the resulting Slope quartile categories map with a custom color table.

## Rasters with distinct categories: land cover
In addition to contiuous values, raster cells can represent distinct categories, encoded with an integer value and a text label. When these categories are assigned different colors, this kind of thematic map is also called a *choropleth* map.
- The *landuse96_28m* map is an example of such a raster map. Load the *landuse96_28m* map into the layer manager.
- Because the colors vary according to category, it can already be seen as a raster thematic map. All that is lacking is an interpretive legend.
- You can make a legend for the *landuse96_28m* map in the same way we made a legend for the slope map, with a few minor differences.
::: {.panel-tabset group="language"}
#### GUI
1. In the *d.legend tool* select *landuse96_28m* for the map to create a legend from.
2. Put "Land Use" in the title and set the title font size to 14.
3. Under the Advanced tab, check the "Do not show categories number" box (you might want to show the numbers for a different map, however).
4. Set the font size to 12.
5. Check the "Show background" box.
6. Click "OK".
#### Command line
```{bash}
d.legend -c -b raster=landuse title="Land Use" title_fontsize=14 fontsize=12 border_color=0:0:0:255
```
#### Python
```{python}
gs.run_command("d.legend",
raster="landuse",
title="Land Use",
title_fontsize=14,
fontsize=12,
border_color="0:0:0:255",
flags="cb")
```
:::
Here is the land use thematic map.

::: {.callout-note title="Tip"}:
If you have a category map with very many categories, they may not be readable or even display as distinct categories. If you have a map with too many categories to create a readable legend, you may want to combine some categories or focus on a smaller geographic area to produce a useful thematic map.
:::
### Aggregating categories with reclassification
This thematic map might be more informative if the many land use and land cover categories could be condensed into fewer. As with the slope map, this can be done through reclassification to assign new values and labels to the existing landuse categories.
We an make a new reclass map with aggregated categories, named *landcover*. Using [*r.reclass*](https://grass.osgeo.org/grass-stable/manuals/r.reclass.html) as described previously, we can reduce the 21 original categories to 7 by combining all the developed land into a single category, all the herbaceous vegetation, all the shrub vegetation, and all the tree cover.
:::::: {.panel-tabset group="language"}
## GUI
1. Enter *landuse96_28m* for the raster to be reclassified.
2. Enter *landcover* for the output reclassified map.
3. Enter the reclass rules directly in the text box or from a saved text file.
```
1 2 = 1 developed
3 = 2 agriculture
4 6 = 3 herbaceous
7 thru 9 = 4 shrubland
10 thru 18 = 5 forest
20 = 6 water
21 = 7 sediment
```
## Command line
```{bash}
r.reclass input=landuse output=friction_landcover rules=- << EOF
1 2 = 1 developed
3 = 2 agriculture
4 6 = 3 herbaceous
7 thru 9 = 4 shrubland
10 thru 18 = 5 forest
20 = 6 water
21 = 7 sediment
EOF
```
## Python
```{python}
rules = """\
1 2 = 1 developed
3 = 2 agriculture
4 6 = 3 herbaceous
7 thru 9 = 4 shrubland
10 thru 18 = 5 forest
20 = 6 water
21 = 7 sediment
"""
gs.write_command("r.reclass",
input="landuse",
output="friction_landcover",
rules="-",
stdin=rules)
```
::::::
Here is the result with a legend.

### Custom colors for reclassified thematic map
Finally, we might want to change the colors from the default *viridis* color table to one that better shows land cover categories. This can be done using the [r.colors](https://grass.osgeo.org/grass-stable/manuals/r.colors.html) described previously for the reclassified slope map.
Here are some possible color assignments. Developed land (1) is orange, agricultural land (2) is yellow, other vegetation (3-5) is colored shades of green, water (6) is blue, and sediment (7) is brown. Assign these colors using *r.colors* as done previously for reclassified slope.
:::::::::::: {.panel-tabset group="language"}
#### GUI
1. Specify *landcover* as the map you want to assign the color table to
2. Enter the color rules in the "enter values directly" box
```
1 255:127:0
2 255:255:0
3 200:255:0
4 0:255:0
5 20:130:70
6 0:191:191
7 191:127:63
```
3. Press run
#### Command line
```{bash}
r.colors -e map=slope_quartiles rules=- << EOF
1 255:127:0
2 255:255:0
3 200:255:0
4 0:255:0
5 20:130:70
6 0:191:191
7 191:127:63
EOF
```
#### Python
```{python}
rules = """\
1 255:127:0
2 255:255:0
3 200:255:0
4 0:255:0
5 20:130:70
6 0:191:191
7 191:127:63
"""
gs.write_command("r.colors",
map="slope_quartiles",
rules="-",
stdin=rules)
```
::::::::::::
Here is the resulting reclassified and recolored land cover map.

# Vector Thematic Maps
## Thematic maps in the Layer Manager
### Different colors and sizes to represent the value of a column
One of the easiest ways to create a thematic map is with the layer manager. In this method, each layer is a different theme. You can control the information displayed and vector object properties (e.g., color or size) for each layer using the vector properties tool, [d.vect](https://grass.osgeo.org/grass-stable/manuals/d.vect.html).
For example, we can display the *schools* vector points map—overlaying a vector map of streets to show where the schools are located—using different colors and sizes for different grade levels. The grade levels are recorded in the *GLEVEL* column of the *schools* attribute table by categorical values of **E** for elementary schools, **M** for middle schools, and **H** for high schools.
- To do this, you simply need to display the *streets* map, and then the *schools* map in three additional layers.
- For each layer, select a grade level to display and color and size of the symbol.
- For each layer, you will also want to specify a label that will show up in a legend.
::: {.panel-tabset group="language"}
#### GUI
1. Display the *streets* map in the layer manager. In the vector properties, set the feature (line) color to grey to make it easier to see the school symbols. Click the layer properties button (or right click on the layer) for a contextual menu and select "Zoom to selected map(s)".
2. Display the *schools* map so it is above the *streets* map in the layer manager.
3. Open the vector properties tool for the *schools* map. Under the Selection tab, put the following query statement in the WHERE box. **GLEVEL='H'**. This will cause this layer to **only** display schools where the grade level is high school. You can also generate a query interactively by using the query builder button to the right of the entry box.
4. Click Apply (to display selected schools but not close the vector properties tool). Now you should only see the points representing high schools.
5. Under the vector properties Colors tab, set the "Area fill color" to **red**.
6. Under the vector properties Symbols tab, set the "Point and centroid symbol" to **basic/circle** and the "Symbol size" to **25**.
7. Under Legend tab, set the "Label to display after symbol..." to **high schools**.
7. Click OK. Now you will see high schools represented by large red circles.
8. Display the *schools* map again in a new layer above the high schools layer in the layer manager.
9. In the vector properties for this new *schools* layer, enter **GLEVEL='M'** (for middle schools) in the Selection WHERE box, set the "Area fill color" to **yellow**, and set the "Symbol size" to **20**.
10. Click OK. Now, along with high schools, you will see middle schools represented by slightly smaller, yellow circles.
11. Finally, display *schools* a third time in a new layer above the middle schools layer in the layer manager.
12. In the vector properties for this layer, enter **GLEVEL='E'** (for elementary schools) in the Selection WHERE box, set the "Area fill color" to **blue**, and set the "Symbol size" to **15**.
13. Click OK. Now, along with high schools and middle schools, you will also see elementary schools represented by even smaller, blue circles.
#### Command line
```{bash}
d.vect map=streets color=179:179:179:255 width=1 legend_label="Wake Forest streets"
d.vect map=schools where="GLEVEL='H'" color=0:29:57:255 fill_color=255:0:0:255 width=1 icon=basic/circle size=25 legend_label="high schools"
d.vect map=schools where="GLEVEL='M'" color=0:29:57:255 fill_color=255:255:0:255 width=1 icon=basic/circle size=20 legend_label="middle schools"
d.vect map=schools where="GLEVEL='E'" color=0:29:57:255 fill_color=0:0:255:255 width=1 icon=basic/circle size=15 legend_label="high schools"
```
#### Python
```{python}
gs.run_command("d.vect",
map="streets",
color="179:179:179",
width=1)
gs.run_command("d.vect",
map="schools",
where="GLEVEL = 'H'",
color="0:29:57",
fill_color="255:0:0",
width=1,
icon="basic/circle",
size=25,
legend_label="high schools")
gs.run_command("d.vect",
map="schools",
where="GLEVEL = 'M'",
color="0:29:57",
fill_color="255:255:0",
width=1,
icon="basic/circle",
size=20,
legend_label="middle schools")
gs.run_command("d.vect",
map="schools",
where="GLEVEL = 'E'",
color="0:29:57",
fill_color="0:0:255",
width=1,
icon="basic/circle",
size=15,
legend_label="high schools")
```
:::
Using different sizes (with larger symbols below smaller symbols), along with color, makes it easier to see where schools of different grade levels are co-located.

### Creating a legend
A legend can help you and other users better interpret the colored circles on the map. You can create a vector legend with the vector legend tool, [d.legend.vect](https://grass.osgeo.org/grass-stable/manuals/d.legend.vect.html), that you can access from the button bar at the top of the map display window.

The vector legend tool will create a legend with entries for all layers that are visible in a display. We can set a title and font, and control the background. But there are fewer options than the raster legend tool.
::: {.panel-tabset group="language"}
#### GUI
1. Open the vector legend tool.
2. Under the title tab, set the "Legend title" to 'Schools by Grade Level' and the "Title font size" to 14.
3. Under the Background tab, check the "Display legend background" box.
4. Under the Font settings tab, set the "Font size" to 12.
5. Click OK to generate the legend.
#### Command line
```{bash}
d.legend.vect -b at=74.0,30 title="Schools by Grade Level" fontsize=12 title_fontsize=14
d.barscale -n at=70,10 length=10 units=kilometers segment=5 width_scale=2
```
#### Python
```{python}
gs.run_command("d.legend.vect",
at="74.0,30",
title="Schools by Grade Level",
fontsize=12,
title_fontsize=14,
flags="b")
gs.run_command("d.barscale",
at="70,10",
length=10,
units="kilometers"
segment=5,
idth_scale=2,
flags="n")
```
:::
Here is the map with a legend. A scale bar has been added using the scale bar tool, [d.barscale](https://grass.osgeo.org/grass-stable/manuals/d.barscale.html), from the same button bar location as the vector legend tool.

::: {.callout-note title="Tip"}:
If you want to regenerate this thematic map in the future, you can do so quickly using GRASS commands.
- Open the vector properties tool for each layer, click the Copy button and then paste the command generated into a text file.
- Do the same for the vector legend tool and the scale bar tool.
- This will create a file with the following sequence of commands (these can also be seen by clicking the "Command line" tab in this tutorial:
```
d.vect map=streets color=179:179:179 width=1 legend_label="Wake Forest streets"
d.vect map=schools where="GLEVEL='H'" color=0:29:57 fill_color=255:0:0 width=1 icon=basic/circle size=25 legend_label="high schools"
d.vect map=schools where="GLEVEL='M'" color=0:29:57 fill_color=255:255:0 width=1 icon=basic/circle size=20 legend_label="middle schools"
d.vect map=schools where="GLEVEL='E'" color=0:29:57 fill_color=0:0:255 width=1 icon=basic/circle size=15 legend_label="high schools"
d.legend.vect -b at=74.0,30 title="Schools by Grade Level" fontsize=12 title_fontsize=14
d.barscale -n at=70,10 length=10 units=kilometers segment=5 width_scale=2
```
You can paste these commands into the GRASS GUI console one at a time, in this order to recreate this map.
:::
You can use this same approach to create thematic area and line vector maps. Here is the *census* map showing household size in each census tract.

::: {.callout-note title="Commands with settings to generate map"}:
d.vect map=census@PERMANENT where=HH_SIZE=0 color=0:29:57:255 fill_color=255:255:102:255 width=1 icon=basic/circle legend_label="no households"
d.vect map=census@PERMANENT where="HH_SIZE>0 AND HH_SIZE<2" color=0:29:57:255 fill_color=230:234:255:255 width=1 icon=basic/circle legend_label="< 2 people per household"
d.vect map=census@PERMANENT where="HH_SIZE>=2 AND HH_SIZE <= 4" color=0:29:57:255 fill_color=178:188:255:255 width=1 icon=basic/circle legend_label="2-4 people per household"
d.vect map=census@PERMANENT type=line,boundary,area,face where="HH_SIZE">4 color=0:0:0:255 fill_color=0:0:255:255 width=1 icon=basic/circle legend_label="> 4 people per household"
d.legend.vect -b at=75,25 title="Median Household Size" fontsize=12 title_fontsize=14
d.barscale -n at=70.5,10 length=4 units=kilometers segment=4 width_scale=2
:::
## Statistically Derived Thematic Maps
### Thematic mapping tool for choropleth maps
It can be useful to group continuous numerical values of a column into categories using various statistical measures of variability. The thematic mapping tool, [d.vect.thematic](https://grass.osgeo.org/grass-stable/manuals/d.vect.html), can do this using several statistical approaches, as well as supporting custom divisions. This tool only makes choropleth (i.e. using colors rather than symbol size for visualizing numerical values).
::::: grid
::: g-col-6
- The thematic mapping tool can be accessed from the special vector layer icon in the layer manager.
- It will create a new thematic vector layer.
:::
::: g-col-6
{fig-align="top"}
:::
:::::
#### Classification algorithms used in thematic mapping tool
| **Code** | \***Statistical grouping algorithm** |
|-:|:-------------------|
| int | Classes are grouped by equal divisions of the column values, the minimum to the maximum value. |
| std | Classes are divided by standard standard deviations below and above the mean. |
| qua | Classes are grouped into quantiles such that each group represents approximately an equal number of cases (rows of a column). |
| equ | Classes have equal probabilities of occurrence in a normal/Gaussian distribution. |
| dis | Classes are divided by natural breaks in the values of a column. The algorithm systematically searches for discontinuities in the slope of the cumulative frequencies curve of column values, by approximating this curve through straight line segments whose vertices define the class breaks. |
\**See the [v.class](https://grass.osgeo.org/grass-stable/manuals/v.class.html) manual for more detailed information.*
You can also select the number of classes to be calculated and specify custom breakpoints for classes instead of using the algorithms listed above for statistically calculated breaks.
We can use the thematic mapping tool to generate a new map of household size from the *census* map, where the categories are determined by standard deviation units rather than the arbitrary ones used above.
::: {.panel-tabset group="language"}
#### GUI
:::: grid
::: g-col-6
1. Open the thematic mapping tool to create a new thematic map layer.
2. Under the Required tab, enter *census* for the the source of the information to create the thematic map.
3. Select **HH_SIZE** for the column to be classified in the thematic map.
4. Select **4 colors** to use to visualize the standard deviation units. The example shown uses the interactive color picker (button to the right of the "Colors" entry box) to select:
- light blue (RGB 193:202:255:255) for 2-SD below the mean,
- dark blue (RGB 0:0:255:255) for 1-SD below the mean,
- dark red (RGB 251:2:7:255) for 1-SD above the mean, and
- light red (RGB 254:201:193:255) for 2-SD above the mean.
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
5. Under the Classes tab, select **std** for the standard deviation "Algorithm to use for classification", and **4** for the "Number of classes to define".
:::
::: g-col-6
{fig-align="right"}
:::
:::::
::::: grid
::: g-col-6
6. Under the Legend tab, check the "Create legend information and send to stdout" box. This will send legend rules to the terminal (not the GUI console) that we will use later.
7. Enter **Standard Deviation Units** in the "Thematic map title" box. This will become a subtitle in the legend.
8. Click OK.
:::
::: g-col-6
{fig-align="right"}
:::
:::::
9. Enter values for creating the legend as you did with the previous thematic map of households, and also enter **13** for the subtitle font size.