-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy path04-Symbolizing-features.Rmd
More file actions
executable file
·384 lines (257 loc) · 24.1 KB
/
04-Symbolizing-features.Rmd
File metadata and controls
executable file
·384 lines (257 loc) · 24.1 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
# Symbolizing features
## Introduction
Effective map design is not just about placing data on a map--it's about communicating information clearly and accurately. This chapter explores the principles and practices behind symbolizing spatial features, focusing on how color and classification schemes can be used to enhance the readability and interpretability of maps.
The chapter begins by breaking down the three perceptual dimensions of color--**hue**, **lightness**, and **saturation**--and explains how each can be used to represent different types of data. It then introduces the concept of color spaces, including the commonly used HSV (Hue-Saturation-Value) model and perceptually accurate models like CIELAB and Munsell, highlighting how human perception of color can differ from software-generated color models.
The chapter also covers classification schemes used in choropleth mapping:
+ **Qualitative** schemes for categorical data,
+ **Sequential** schemes for ordered data,
+ **Divergent** schemes for data with a meaningful midpoint.
It emphasizes the importance of choosing appropriate classification intervals, such as **equal interval**, **quantile**, and **Jenks natural breaks**, and demonstrates how different choices can significantly affect the appearance and interpretation of a map.
Finally, the chapter introduces tools like ColorBrewer, which help cartographers select color palettes that are visually effective, colorblind-safe, and suitable for grayscale printing.
## Color
Color plays a central role in map design, influencing how effectively spatial patterns and relationships are communicated. In cartography, color is not just aesthetic--it encodes meaning and guides interpretation. To use color effectively, it’s important to understand its three perceptual dimensions: **hue**, **lightness** and **saturation**. These dimensions interact to shape how we perceive and differentiate features in a map features. In the following sections, we’ll explore each dimension and its implications for symbolizing different types of data.
### Hue
**Hue** is the perceptual dimension most commonly associated with color names--such as red, green, or blue. In cartography, hue is typically used to represent **categorical** data, where each category is assigned a distinct color. This allows map readers to easily differentiate between classes without implying any order or magnitude. However, the choice of hues should be made carefully, as some colors may carry cultural connotations or be difficult to distinguish for individuals with color vision deficiencies.
```{r f04-hue, fig.cap = "An example of eight different hues. Hues are associated with color names such as green, red or blue.", echo=FALSE, fig.height=0.2, fig.width = 3, fig.align='center'}
library(munsell)
library(colorspace)
# Function
pal <- function(col, border = "light gray", ...)
{ n <- length(col)
plot(0, 0, type="n", xlim = c(0, 1), ylim = c(0, 1),
axes = FALSE, xlab = "", ylab = "", ...)
rect(0:(n-1)/n, 0, 1:n/n, 1, col = col, border = border) }
# Create different hues
OP <- par( mar=c(0,0,0,0))
h <- rainbow(8, s = 1, v = 0.8)
pal(h)
par(OP)
```
Note that magentas and purples are not part of the natural visible light spectrum; instead they are a mix of reds and blues (or violets) from the spectrum's tail ends.
### Lightness
**Lightness**--sometimes referred to as **value**--describes how much light is reflected or emitted from a surface, influencing how bright or dark a color appears. In cartographic design, lightness is particularly useful for symbolizing **ordinal**, **interval**, or **ratio** data, where a progression from light to dark can intuitively represent increasing values. Unlike hue, which separates categories, lightness enables us to show variation within a single category or color. However, care should be taken to maintain sufficient contrast between classes, especially in grayscale or colorblind-safe designs
```{r f04-lightness, fig.cap = "Eight different hues (across columns) with decreasing lightness values (across rows).", echo=FALSE, fig.height=0.8, fig.width = 3,warning=FALSE, message=FALSE, fig.align='center'}
# Function
pal <- function(col, border = "light gray", ...)
{ n <- length(col)
plot(0, 0, type="n", xlim = c(0, 1), ylim = c(0, 1),
axes = FALSE, xlab = "", ylab = "", ...)
rect(0:(n-1)/n, 0, 1:n/n, 1, col = col, border = border) }
# Different lightness values
h1 <- rainbow(8, s = 1, v = 0.8)
h2 <- rainbow(8, s = 1, v = 0.6)
h3 <- rainbow(8, s = 1, v = 0.4)
h4 <- rainbow(8, s = 1, v = 0.2)
OP <- par( mfrow=c(4,1), mar=c(0,0,0,0))
pal(h1); pal(h2); pal(h3); pal(h4)
par(OP)
```
### Saturation
**Saturation**--also referred to as chroma--describes the intensity or vividness of a color. Highly saturated colors appear bold and vibrant, while low-saturation colors appear muted or grayish. In cartographic design, saturation can be used to **emphasize specific features** or categories, but it should be applied with care. Overuse can lead to visual clutter or misinterpretation, especially when combined with other color dimensions. Saturation is best used to subtly enhance contrast or draw attention to key map elements.
```{r f04-saturation, fig.cap = "Eight different hues (across columns) with decreasing saturation values (across rows).", echo=FALSE, fig.height=0.8, fig.width = 3,warning=FALSE, message=FALSE, fig.align='center'}
library(munsell)
library(colorspace)
# Function
pal <- function(col, border = "light gray", ...)
{ n <- length(col)
plot(0, 0, type="n", xlim = c(0, 1), ylim = c(0, 1),
axes = FALSE, xlab = "", ylab = "", ...)
rect(0:(n-1)/n, 0, 1:n/n, 1, col = col, border = border) }
# Different saturation values
h1 <- rainbow(8, v = 1, s = 0.8)
h2 <- rainbow(8, v = 1, s = 0.6)
h3 <- rainbow(8, v = 1, s = 0.4)
h4 <- rainbow(8, v = 1, s = 0.2)
OP <- par( mfrow=c(4,1), mar=c(0,0,0,0))
pal(h1); pal(h2); pal(h3); pal(h4)
par(OP)
```
## Color Space
The three perceptual dimensions of color--hue, lightness, and saturation--can be visualized as forming a three-dimensional color space. While one might expect this space to be a **cube**, it is more accurately represented as a **cone**: hue wraps around the circumference, saturation extends outward from the center, and lightness runs vertically.
```{r f04-color-space, echo=FALSE, fig.cap = "This is how the software defines the color space. But does this match our perception of color space?", fig.align='center'}
knitr::include_graphics("img/Color_space_std_small.jpg")
```
This model helps explain how colors blend, fade, or become indistinguishable as saturation or lightness changes.
However, most software-defined color spaces (like HSV) assume symmetry, which does not reflect how humans actually perceive color. For example, we can distinguish more shades of blue than yellow at the same saturation and lightness.
```{r f04-munsell, fig.cap = "A cross section of the color space with constant hues and lightness values and decreasing saturation values where the two hues merge.", echo=FALSE, fig.height=.5, fig.width = 8, warning=FALSE, message=FALSE, fig.align='center'}
library(munsell)
library(colorspace)
# Function
pal <- function(col, border = "light gray", ...)
{ n <- length(col)
plot(0, 0, type="n", xlim = c(0, 1), ylim = c(0, 1),
axes = FALSE, xlab = "", ylab = "", ...)
rect(0:(n-1)/n, 0, 1:n/n, 1, col = col, border = border) }
# Create slice from symmetrical HSV colorspace
h = rep(0.127,12); v = rep(.7,12); s = seq(.0,.8,length.out=30)
cl1.hex = hsv(h,rev(s),v)
cl2.hex = hsv(h+0.5,s,v)
OP <- par( mar=c(0,0,0,0))
pal(c(cl1.hex,cl2.hex), border=NA)
par(OP)
```
How many distinct yellows can you perceive? How many distinct blues? Do the counts seem to match? Unless you have exceptionally acute color vision, you’ll likely notice a discrepancy--even though the software has generated exactly 30 distinct shades of each. To confirm this, we can add borders around each color swatch to visually verify that the number of distinct colors is indeed the same.
```{r f04-munsell-bis, fig.cap = "A cross section of the color space with each color distinctly outlined.", echo=FALSE, fig.height=.5, fig.width = 8, warning=FALSE, message=FALSE, fig.align='center'}
## Function
pal <- function(col, border = "light gray", ...)
{ n <- length(col)
plot(0, 0, type="n", xlim = c(0, 1), ylim = c(0, 1),
axes = FALSE, xlab = "", ylab = "", ...)
rect(0:(n-1)/n, 0, 1:n/n, 1, col = col, border = border) }
# Create slice from symmetrical HSV colorspace
h = rep(0.127,12); v = rep(.7,12); s = seq(.0,.8,length.out=30)
cl1.hex = hsv(h,rev(s),v)
cl2.hex = hsv(h+0.5,s,v)
OP <- par( mar=c(0,0,0,0))
pal(c(cl1.hex,cl2.hex))
par(OP)
```
y now, it should be evident that symmetrical color spaces—like those commonly used in software--do not accurately reflect how humans perceive color. Our visual system is more sensitive to certain hues and less so to others, resulting in perceptual asymmetries that these models fail to capture. More rigorously designed color spaces, such as **CIELAB** and **Munsell**, address this limitation by modeling color as a non-symmetrical space that better aligns with human perception. For instance, in the Munsell system, a vertical slice of the color cone along the blue/yellow axis reveals noticeable differences in how many distinct shades of each hue we can perceive.
```{r f04-munsell-slice, fig.cap = "A slice of the Munsell color space.", echo=FALSE, fig.height=2, fig.width = 4, warning=FALSE, message=FALSE, fig.align='center'}
library(ggplot2)
OP <- par( mar=c(0,0,0,0))
p <- complement_slice("5Y")
p$layers[[2]] <- NULL # remove text layer
p
par(OP)
```
As illustrated by the Munsell color space, our ability to distinguish colors is not uniform across hues. For example, we can perceive fewer distinct shades of yellow than blue at comparable lightness levels. In one comparison, 29 unique shades of yellow were discernible (excluding grayscale values where saturation = 0), while 36 shades of blue were distinguishable.
This perceptual asymmetry has important implications for map design. So how can we apply our understanding of color spaces to make better choices when symbolizing features? The next section introduces three foundational color schemes--**qualitative**, **sequential** and **divergent**--each tailored to different types of data and mapping goals.
## Classification
Once you've selected an appropriate color space, the next step is to determine how to apply color to your data. This involves choosing a classification scheme--a method for grouping data values into discrete **classes**, each of which is represented by a distinct **color swatch**. The choice of classification scheme depends on the nature of your data and the message you want your map to convey. In the following sections, we’ll explore three common approaches to classification: **qualitative**, **sequential**, and **divergent**.
### Qualitative color scheme
Qualitative color schemes are used to symbolize data that have no inherent order--such as categories or types. These schemes assign distinct hues to each class. To maintain perceptual balance, the hues are typically chosen to have equal lightness and saturation, ensuring that no category appears more prominent than another.
```{r f04-qualitative, fig.cap = "Example of four different qualitative color schemes. Color hex numbers are superimposed on each palette.", echo=FALSE, fig.height=1, fig.width = 4}
library(RColorBrewer)
n <- 6
OP <- par( mfrow=c(4,1), mar=c(0.5,0,0,0))
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"Set1"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab=" ")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"Set1"),col="grey20",cex=0.8)
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"Set2"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab="")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"Set2"),col="grey50",cex=0.8)
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"Paired"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab="")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"Paired"),col="grey30",cex=0.8)
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"Pastel1"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab="")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"Pastel1"),col="grey40",cex=0.8)
par(OP)
```
These schemes are ideal for mapping variables like land cover types, political affiliations, or survey responses. However, care should be taken when selecting hues, especially in contexts where cultural associations may influence interpretation. For example, it may not make sense to assign "blue" to Republican regions or "red"" to Democratic ones.
```{r f04-qualitative-map, fig.cap = "Map of 2012 election results shown in a qualitative color scheme. Note the use of three hues (red, blue and gray) of equal lightness and saturation.", echo=FALSE, fig.width = 4.5,fig.height=4, message=FALSE, fig.align='center'}
library(sf)
# Read in map data and compute a rate for mapping
s <- st_read("img/Data/ME_elections_2012.shp", quiet = TRUE)
# Using plot.sf
OP <- par(mar=c(0,0,0,0))
plot(s["W"], pal = c("blue","#dddddd","red"), main = NULL, border = NA )
par(OP)
```
### Sequential color scheme
Sequential color schemes are used to represent data that follow a meaningful order, such as income, temperature, elevation, or infection rates. These schemes typically progress from light to dark, with lighter shades representing lower values and darker shades representing higher ones. This visual gradient helps convey magnitude and direction in the data. Most sequential schemes use a single hue, but some may incorporate two hues to emphasize a broader range of values.
```{r f04-sequential, fig.cap = "Example of four different sequential color schemes. Color hex numbers are superimposed on each palette.", echo=FALSE, fig.height=1, fig.width = 4, warning=FALSE, message=FALSE, fig.align='center'}
n <- 6
OP <- par( mfrow=c(4,1), mar=c(0.5,0,0,0))
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"Blues"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab=" ")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"Blues"),col="grey20",cex=0.7)
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"Oranges"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab="")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"Oranges"),col="grey50",cex=0.7)
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"YlGn"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab="")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"YlGn"),col="grey50",cex=0.7)
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"YlOrBr"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab="")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"YlOrBr"),col="grey40",cex=0.7)
par(OP)
```
The following example uses a sequential color scheme to map household income in Maine using a green-based scheme.
```{r f04-sequential-map, fig.cap = "Map of household income shown in a sequential color scheme. Note the use of a single hue (green) and 7 different lightness levels.", echo=FALSE, fig.width = 4.5, fig.height=4, fig.align='center'}
library(classInt)
library(sf)
s <- st_read("img/Data/Maine_income_ed.shp", quiet = TRUE)
n <- 7
brk <- classIntervals(s$Household_,n,style="equal")
OP <- par(mar=c(0,0,0,2), las = 1, omi=c(0,0,0,0.6))
#spplot(s, "Household_", at=brk$brks,col.regions = brewer.pal(n, "Greens"), col="transparent",
# colorkey = list(
# labels=list(at=brk$brks,labels = sprintf("$%0.0f",brk$brks), cex=0.5))
#)
plot(s["Household_"], main = NULL, breaks = brk$brk, border = NA,
pal = RColorBrewer::brewer.pal(n , "Greens"),
at = brk$brks)
par(OP)
```
### Divergent color scheme
Divergent color schemes are used to represent ordered data that revolve around a meaningful central value--such as a median, mean, or zero. These schemes are particularly effective when the goal is to highlight differences on either side of a reference point, making them well-suited for visualizing change, deviation, or polarity.
A typical divergent scheme uses two contrasting hues—one for values below the center and one for values above. Lightness and saturation are adjusted symmetrically to reflect the magnitude of deviation from the central value. This approach helps viewers quickly identify extremes and interpret the direction and intensity of variation.
The following examples showcase several divergent palettes:
```{r f04-divergent, fig.cap = "Example of four different divergent color schemes. Color hex numbers are superimposed onto each palette.", echo=FALSE, fig.height=1, fig.width = 4, fig.align='center'}
n <- 6
OP <- par( mfrow=c(4,1), mar=c(0.5,0,0,0))
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"BrBG"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab=" ")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"BrBG"),col="grey20",cex=0.7)
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"RdBu"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab="")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"RdBu"),col="grey50",cex=0.7)
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"PuOr"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab="")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"PuOr"),col="grey50",cex=0.7)
image(x=1:n, y=1,as.matrix(1:n), col = brewer.pal(n,"RdYlGn"), ylab = "", xaxt = "n", yaxt = "n",
bty = "n",xlab="")
text(x=seq(1,n, length.out=n), y=0.75, brewer.pal(n,"RdYlGn"),col="grey40",cex=0.7)
par(OP)
```
Building on the previous example in section 4.4.2, we can also represent income data using a divergent color scheme--particularly useful when emphasizing variation around a central value. In this case, we center the scheme on the median household income of $36,641. Values below the median are shown using a brown hue, while values above the median are represented with a green-blue hue. Each hue is further divided into progressively lighter or darker shades to reflect the degree of deviation from the median, allowing for a more nuanced interpretation of income disparities across regions.
```{r f04-divergent-map, fig.cap = "This map of household income uses a divergent color scheme where two different hues (brown and blue-green) are used for two sets of values separated by the median income of $36,641. Each hue is further divided into progressively lighter or darker shades.", echo=FALSE, fig.height=4, fig.width = 4.5, warning=FALSE, message=FALSE, fig.align='center'}
n = 6
brk <- classIntervals(s$Household_,n,style="quantile")
# spplot(s, "Household_", at=brk$brks,col.regions = brewer.pal(n, "BrBG"), col="transparent",
# colorkey = list(
# labels=list(at=brk$brks,labels = sprintf("$%0.0f",brk$brks), cex=0.5))
# )
OP <- par(mar=c(0,0,0,2), las = 1, omi=c(0,0,0,0.6))
plot(s["Household_"], main = NULL, breaks = brk$brks, border = NA,
pal = RColorBrewer::brewer.pal(n, "BrBG"),
at = brk$brks)
par(OP)
```
## Choosing an Appropriate Color Scheme
Selecting the right color scheme for your map depends on both the nature of your data and the number of classes you wish to represent. Fortunately, tools like [ColorBrewer](http://colorbrewer2.org/)--developed by Cynthia Brewer and colleagues at Pennsylvania State University--offer curated palettes tailored to different data types: qualitative, sequential, and divergent. The site also provides guidance on choosing colorblind-safe schemes and palettes that translate well to grayscale, which is especially useful for print publications.
```{r echo=FALSE, fig.width=4, dpi=60, fig.align='center'}
knitr::include_graphics("img/ColorBrewer.png")
```
One important design consideration is the number of color swatches used. ColorBrewer limits most schemes to 12 or fewer swatches, reflecting the reality that human perception struggles to reliably distinguish more than a handful of colors in a legend. For example, try matching nine shades of green in a map to their corresponding legend entries--it quickly becomes a challenge.
## Classification Intervals
The way data values are grouped into intervals can significantly affect how a map looks and how its patterns are interpreted. In the previous examples, two different classification schemes were used: an **equal interval** scheme for the sequential map and a **quantile interval** scheme for the divergent map.
Classification intervals define how the full range of data is divided into discrete classes, each represented by a color swatch. Different schemes produce different visual outcomes, even when applied to the same dataset. For instance, **equal interval** schemes divide the data range into evenly spaced segments, which is useful for uniformly distributed data. In contrast, **quantile schemes** ensure that each class contains an equal number of features, which can help balance visual representation across a map.
Other options include the **Jenks natural breaks** method which uses an algorithm to identify clusters in the data, optimizing class boundaries to reflect inherent groupings. The following figure compares these three schemes--quantile, equal interval, and Jenks--applied to the same income dataset. Notice how each scheme emphasizes different aspects of the distribution.
```{r diff-class, echo=FALSE, dpi=60, fig.align='center', fig.cap="Three different representations of the same spatial data using different classification intervals."}
knitr::include_graphics("img/Three_breaks_compared_2.png")
```
The **quantile interval** scheme ensures that each color swatch is represented an equal number of times. If we have 20 polygons and 5 classes, the interval breaks will be such that each color is assigned to 4 different polygons.
The **equal interval** scheme divides the range of values into equal interval widths. If the polygon values range from 10,000 to 25,000 and we have 5 classes, the intervals will be [10,000 ; 13,000], [13,000 ; 16,000], ..., [22,000 ; 25,000].
The **Jenks interval** scheme (aka natural breaks) uses an algorithm that identifies clusters in the dataset. The number of clusters is defined by the desired number of intervals.
It may help to view the breaks when superimposed on top of a distribution of the attribute data. In the following figure. the three classification intervals are superimposed on a histogram of the per-household income data. The histogram shows the distribution of values as “bins” where each bin represents a range of income values. The y-axis shows the frequency (or number of occurrences) for values in each bin.
```{r f04-three-breaks, echo=FALSE, fig.cap = "Three different classification intervals used in the three maps. Note how each interval scheme encompasses different ranges of values.", dpi=60, fig.align='center'}
knitr::include_graphics("img/Three_hist_intervals.png")
```
### An Interactive Example
The following interactive frame demonstrates the different "looks" a map can take given different combinations of classification schemes and class numbers.
```{r echo = FALSE, out.width="780px"}
knitr::include_app("https://hobbes.colby.edu/Manny/Classification/", height = "530px")
```
## Summary
This chapter introduces key principles for symbolizing spatial data using color and classification schemes to improve map readability and interpretation.
+ Color Dimensions: Explains how hue, lightness, and saturation represent different data types—categorical, ordered, and intensity-based—and how they interact perceptually.
+ Color Spaces: Highlights the limitations of symmetrical models like HSV and introduces perceptually accurate alternatives such as CIELAB and Munsell.
+ Classification Schemes: Describes three main approaches:
+ Qualitative for unordered categories,
+ Sequential for ordered data,
+ Divergent for data centered around a meaningful midpoint.
+ Color Selection Tools: Introduces ColorBrewer for choosing effective, accessible palettes.
+ Classification Intervals: Compares equal interval, quantile, and Jenks natural breaks, showing how each affects data representation.