Custom Functions: Colors!

792
1
06-01-2023 10:30 AM
jcarlson
MVP Esteemed Contributor
8 1 792

Using Arcade in Dashboards is great! There are some really neat ways to make your dashboard elements more effective. (Just check out @JenniferAcunto 's series Dashboards That Pop for lots of great content!)

Occasionally, though, I run into spots where I'd like to be able to do just a little bit more. Here are a few things I've been using to help make it easier.

Colors as Numbers

In most examples and tutorials, we work with colors in their hex codes. I prefer a good hex code myself, but unless I'm missing something, there's no inverse of the function ToHex, so getting numeric values out of a hexadecimal string, while not impossible, is kind of annoying in Arcade.

For some of the functions below, it's a lot easier to work with our red, green, and blue values as actual numbers. Or rather, as an array of numbers. But to use these in our output, we need to get them back to a string. Ironically, the example expression given for the function ToHex is exactly this. We can call that function inside of Map to apply it to each item in our array.

 

function hexed(col_arr) {
    return `#${Concatenate(Map(col_arr, ToHex))}`
}

 

jcarlson_0-1685636106076.png

Darken and Lighten

In my daily work, I use both ArcGIS Pro and QGIS regularly. One of my favorite functions on the Q side is darken (and its counterpart lighten). The general idea: take any color, and shift its value by a specified amount. Hard (but not impossible) to do with a hex string, but an array of numbers? Easy!

Again, we can just define the function and use Map to accomplish this.

 

function darken(value) { return value - 15 }

 

jcarlson_1-1685636610491.png

 

In Practice: Table Row Striping and Dynamic Background Colors

Having row striping in a table is great. But when you turn on Arcade formatting, that goes away. Yes, we have the global $rowindex, but what if individual cells have dynamic background colors? Let's say I have a cell that shows up as red, yellow, or green.

 

field_name: {
    backgroundColor: Decode(
        $datapoint['field_name'],
        'High', 'red',
        'Med', 'yellow',
        'Low', 'green',
        ''
    )
}

 

Implementing row striping with the rowindex global isn't hard, but it is tedious!

 

field_name: {
    backgroundColor: Decode(
        $datapoint['field_name'],
        'High', Iif($rowindex % 2 == 1, 'red', 'slightly darker red'),
        'Med', Iif($rowindex % 2 == 1, 'yellow', 'slightly darker yellow'),
        'Low', Iif($rowindex % 2 == 1, 'green', 'slightly darker green'),
        ''
    )
}

 

Repeat for every cell with a dynamic background color? No thanks! Let's wrap up those other functions into one:

 

function striper(col_arr) {
  return iif(
    $rowindex % 2 == 1,
    hexed(col_arr),
    hexed(Map(col_arr, darken))
  )
}

 

Here it is, applying across columns with per-column colors specified. Note that I only need to define the "primary" color, and the striper function keeps the consistent 15-point shift across all of them.

jcarlson_2-1685637439272.png

But I did mention dynamic cell colors. So let's color our population column differently whether it is even or odd.

jcarlson_4-1685637760198.png

If your colors are too divergent, it's possible that this can make your table harder to see. But the point is really that by using a function like this, you can have consistent row striping without having to worry about re-defining a lighter and darker color together. Just play around with your primary colors! Is the striping too obvious? Turn it down in the darken function, and it adjusts everywhere!

A Two-Point Color Scale

Now for something less qualitative, more quantitative. What if I wanted a cell's background to continuously scale? Again, having our color arrays of numeric values makes this easy.

 

function color_scale(value, min_val, max_val, min_col, max_col) {

  // get position along scale, contrain to defined endpoints
  var position = Constrain((value - min_val) / (max_val - min_val), 0, 1)

  // calculate new color
  return hexed([
    (round(max_col[0] - min_col[0] * position)) + (min_col[0]),
    (round(max_col[1] - min_col[1] * position)) + (min_col[1]),
    (round(max_col[2] - min_col[2] * position)) + (min_col[2])
  ])
}

 

And here it is in practice!

jcarlson_5-1685640043175.png

You wouldn't really want to apply row striping to that. But you could use the darken function to get the text to follow the color scale. I'm not saying you should, but again, the point is that by working with colors as numeric arrays, you can do some interesting things with colors in your Dashboards. Try it out!

jcarlson_6-1685640449119.png

 

1 Comment
About the Author
I'm a GIS Analyst for Kendall County, IL. When I'm not on the clock, you can usually find me contributing to OpenStreetMap, knitting, or nattering on to my family about any and all of the above.