Experimenting with planning color standards

Andy Cochran
NYC Planning Tech
Published in
5 min readAug 14, 2018

In my last post, I described the challenge of using color to visualize 50+ data points in our mapping applications, showed how computers aren’t great at color theory, and outlined some considerations for how Planning Labs might standardize its use of color.

In this post, let’s take a more manual/human approach.

With a few lines of code (Sass color functions), incorporating existing color standards, and applying a bit of perceptual judgement, can we generate a large palette containing colors that are both distinguishable and attractive?

✅ Starting with standards

American Planning Association LBCS’s slate gray and cyan are the same hue, as are its red and brown, yellow and beige, both greens, blues, purples…

If we start with the colors prescribed in APA’s Land-Based Classification Standards (LBCS), we can see that many of these colors are the exact same hue, with differing levels of saturation and lightness.

We can prove this if we translate them from rgb() to hsl(). For example…

LBCS defines “dark slate gray” as rgb(47,79,79), which translates to hsl(180deg,25%,25%). And “dark cyan” is defined as rgb(0,139,139), which is hsl(180deg,100%,27%). Notice that both colors have a hue at 180 degrees on the color wheel. Cyan simply has more saturation than slate gray (and a little more lightness).

Now that we know which LBCS colors have the same hue, let’s create a Sass map with a key for each color that has a unique hue:

$palette: (
slategray: rgb(47,79,79), // same hue as dark cyan
forestgreen: rgb(34,139,34), // same hue as light green
olivedrab: rgb(107,142,35),
yellow: rgb(255,255,0), // same hue as beige
wheat: rgb(139,126,102),
tan: rgb(139,90,43), // same hue as bisque3
red: rgb(255,0,0), // same hue as brown4
pink: rgb(255,192,203),
purple: rgb(160,32,240), // same hue as purple4
blue: rgb(0,0,255), // same hue as blue4
);

Sass has a hue() function that gets the hue component of a color. We can use this function to display a color for each key in $palette.

@each $name, $color in $palette {
$hue: hue($color);
.#{$name} {
background-color: hsl($hue,100%,50%);
}
}
Every unique hue of all colors defined in LBCS

These colors are the brightest, most saturated version of the hues — with 100% saturation and 50% lightness (half way between black and white).

Now let’s generate four colors from each hue:

1) The base color: hsl($hue,100%,50%)

2) A lighter version of the base color: hsl($hue,100%,85%)

3) A darker version: hsl($hue,100%,24%)

4) And a grayer, desaturated version: hsl($hue,35%,24%)

This 40-color palette contains colors very close to the 17 LBCS colors. The darker version of our slategray is almost exactly the LBCS-defined dark slate gray. The lighter version of forestgreen is the LBCS-defined light green. And we have pink, wheat, and olive drab, multiple blues and purples…

🤢 Ew. This palette could be improved.

This palette isn’t great. Some hues are more visually distinct than others. The reds are almost identical. The oranges and greens are too similar. And, overall, the saturation is quite garish.

Let’s create a new palette that tweaks the existing colors and fills in discernible gaps in the color wheel, creating 12 visually distinct hues, each with saturation/lightness variants.

1) Let’s call our first hue aqua, which contains LBCS’s “dark slate gray” and “dark cyan”

2) Then we’ll have a hue called green, which contains LBCS’s “light green” and “forest green”

3) Since aqua and green are rather visually distinct, there’s room to add a teal hue between them using Sass’s adjust-hue() function

4) The hue containing LBCS’s “olive drab” is chartreuse, so let’s call it that

5) The yellow hue will contain “yellow” and “beige”

6) The hue containing “tan” is orange, so let’s call it orange

7) Let’s call the “wheat” hue gold, and since it’s closer to orange than yellow we’ll rotate it 8 degrees on the color wheel so it’s more visually distinct

8) The red hue will contain “red,” “brown4,” and “pink”

9) Since LBCS’s “red” and “pink” hues are so similar, let’s rotate the pink hue -25 degrees to create a fuchsia that’s distinct from both red and purple

10) The purple hue will contain “purple” and “purple4”

11) When lightened or darkened, LBCS’s “blue” starts to look purplish, so let’s call this hue indigo

12) And for the last hue, there’s enough space in the color wheel to add a true blue between indigo and aqua

$palette: (
aqua: $lbcs-dark-slate-gray,
teal: adjust-hue($lbcs-forest-green, 40deg),
green: $lbcs-forest-green,
chartreuse: $lbcs-olive-drab,
yellow: $lbcs-yellow,
gold: adjust-hue($lbcs-wheat, 8deg),
orange: $lbcs-tan,
red: $lbcs-red,
fuchsia: adjust-hue($lbcs-pink, -25deg),
purple: $lbcs-purple,
indigo: $lbcs-blue,
blue: adjust-hue($lbcs-dark-slate-gray, 22deg),
);

One final adjustment: To make the base colors less garish, let’s reduce their saturation from 100% to 85%.

🎉 The final product: a palette of 48 distinct colors based on American Planning Association standards

This color palette takes the hues of colors defined in Land-Based Classification Standards, fills in remaining gaps in the HSL color space, adjusts the hues to increase visual distinction, and provides four versions of each of its 12 hues in varying saturation and lightness. Below each swatch, the color is shown at 80%, 60%, 45%, and 30% opacity.

🤔 What’s next?

48 colors give us quite a large palette to choose from. But that’s still fewer than the 50+ we’d need to have a distinct color for every in data point our Zoning & Land Use map. Could we experiment with the hue variations? Perhaps there’s room for more than four colors within each hue.

Assigning color to data is further complicated by the fact that some colors have specific meaning and are are reserved for certain data (e.g. parks = green, residential = yellow). Sometimes two data points call for the same color. Can we experiment with fill patterns? Each pattern would double the number of symbologies available to us.

Planning Labs is currently working on a “Layer Service” — a single place to store style configurations for layers in all of our web maps. This will help us to standardize symbologies across our apps. We’ll be able to improve our styles in one place and all our apps can benefit. As we build this service, we can experiment with color and fill pattern, and better understand how standards can work in practice.

--

--