Hi there!
I've been scouring the community, dev resources, and Pro's Help section but can't find a solution to my problem. I have a custom expression in Arcade to assign labels. I would like to be able to simply write in this same expression assigned colors for each label. This would be simple enough normally, however, I'm combining 3 or more fields based on their results so attribute symbology (I don't think) is an option. Is there anyway possible I can just write in an RGB/hex value into the expression instead of creating new fields or doing a ton of individual work arounds? I have 44 symbol classes and I'd like to avoid doing a ton of manual entries for this.
Here's the expression I'm using currently:
var pt1 = $feature.Points_1
var pt2 = $feature.Points_2
var pt3 = $feature.Points_3
if (pt1 == 0 && pt2 == 0 && pt3 == 0){
return "None"
}
else if (pt1 >= 1 && pt2 >= 1 && pt3 >= 1){
return Concatenate($feature.Trtmt_1 + ", " + $feature.Trtmt_2 + ", " + $feature.Trtmt_3 + " - " + (pt1 + pt2 + pt3))
}
else if (pt1 >= 1 && pt2 >= 1 && pt3 == 0){
return Concatenate($feature.Trtmt_1 + ", " + $feature.Trtmt_2 + " - " + (pt1 + pt2))
}
else if (pt1 >= 1 && pt2 == 0 && pt3 >= 1){
return Concatenate($feature.Trtmt_1 + ", " + $feature.Trtmt_3 + " - " + (pt1 + pt3))
}
else if (pt1 == 0 && pt2 >= 1 && pt3 >= 1){
return Concatenate($feature.Trtmt_2 + ", " + $feature.Trtmt_3 + " - " + (pt2 + pt3))
}
if (pt1 == 0 && pt2 == 0 && pt3 == 0){
return "None";
}
else if (pt1 == 1 && pt2 == 0 && pt3 == 0){
return "Overlay - 1";
}
else if (pt1 == 1.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 1.5";
}
else if (pt1 == 2 && pt2 == 0 && pt3 == 0){
return "Overlay - 2";
}
else if (pt1 == 2.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 2.5";
}
else if (pt1 == 3 && pt2 == 0 && pt3 == 0){
return "Overlay - 3";
}
else if (pt1 == 3.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 3.5";
}
else if (pt1 == 4 && pt2 == 0 && pt3 == 0){
return "Overlay - 4";
}
else if (pt1 == 4.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 4.5";
}
else if (pt1 == 5 && pt2 == 0 && pt3 == 0){
return "Overlay - 5";
}
else if (pt1 == 5.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 5.5";
}
else if (pt1 == 6 && pt2 == 0 && pt3 == 0){
return "Overlay - 6";
}
else if (pt1 == 6.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 6.5";
}
else if (pt1 == 7 && pt2 == 0 && pt3 == 0){
return "Overlay - 7";
}
else if (pt1 == 7.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 7.5";
}
else if (pt2 == 8 && pt2 == 0 && pt3 == 0){
return "Overlay - 8";
}
else if (pt1 == 8.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 8.5";
}
else if (pt1 == 9 && pt2 == 0 && pt3 == 0){
return "Overlay - 9";
}
else if (pt1 == 9.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 9.5";
}
else if (pt1 == 10 && pt2 == 0 && pt3 == 0){
return "Overlay - 10";
}
else if (pt1 == 10.5 && pt2 == 0 && pt3 == 0){
return "Overlay - 10.5";
}
else if (pt2 == 1 && pt1 == 0 && pt3 == 0){
return "Seal - 1";
}
else if (pt2 == 1.5 && pt1 == 0 && pt3 == 0){
return "Seal - 1.5";
}
else if (pt2 == 2 && pt1 == 0 && pt3 == 0){
return "Seal - 2";
}
else if (pt2 == 2.5 && pt1 == 0 && pt3 == 0){
return "Seal - 2.5";
}
else if (pt2 == 3 && pt1 == 0 && pt3 == 0){
return "Seal - 3";
}
else if (pt2 == 3.5 && pt1 == 0 && pt3 == 0){
return "Seal - 3.5";
}
else if (pt2 == 4 && pt1 == 0 && pt3 == 0){
return "Seal - 4";
}
else if (pt2 == 4.5 && pt1 == 0 && pt3 == 0){
return "Seal - 4.5";
}
else if (pt2 == 5 && pt1 == 0 && pt3 == 0){
return "Seal - 5";
}
else if (pt2 == 5.5 && pt1 == 0 && pt3 == 0){
return "Seal - 5.5";
}
else if (pt2 == 6 && pt1 == 0 && pt3 == 0){
return "Seal - 6";
}
else if (pt2 == 6.5 && pt1 == 0 && pt3 == 0){
return "Seal - 6.5";
}
else if (pt2 == 7 && pt1 == 0 && pt3 == 0){
return "Seal - 7";
}
else if (pt2 == 7.5 && pt1 == 0 && pt3 == 0){
return "Seal - 7.5";
}
else if (pt2 == 8 && pt1 == 0 && pt3 == 0){
return "Seal - 8";
}
else if (pt2 == 8.5 && pt1 == 0 && pt3 == 0){
return "Seal - 8.5";
}
else if (pt2 == 9 && pt1 == 0 && pt3 == 0){
return "Seal - 9";
}
else if (pt2 == 9.5 && pt1 == 0 && pt3 == 0){
return "Seal - 9.5";
}
else if (pt2 == 10 && pt1 == 0 && pt3 == 0){
return "Seal - 10";
}
else if (pt2 == 10.5 && pt1 == 0 && pt3 == 0){
return "Seal - 10.5";
}
else if (pt3 == 1 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 1";
}
else if (pt3 == 1.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 1.5";
}
else if (pt3 == 2 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 2";
}
else if (pt3 == 2.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 2.5";
}
else if (pt3 == 3 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 3";
}
else if (pt3 == 3.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 3.5";
}
else if (pt3 == 4 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 4";
}
else if (pt3 == 4.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 4.5";
}
else if (pt3 == 5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 5";
}
else if (pt3 == 5.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 5.5";
}
else if (pt3 == 6 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 6";
}
else if (pt3 == 6.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 6.5";
}
else if (pt3 == 7 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 7";
}
else if (pt3 == 7.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 7.5";
}
else if (pt3 == 8 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 8";
}
else if (pt3 == 8.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 8.5";
}
else if (pt3 == 9 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 9";
}
else if (pt3 == 9.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 9.5";
}
else if (pt3 == 10 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 10";
}
else if (pt3 == 10.5 && pt1 == 0 && pt2 == 0){
return "Crack Fill - 10.5";
}
Are you looking at adding a formatting tag to each of these? If so, that would look like this
//...
else if (pt1 == 0 && pt2 >= 1 && pt3 >= 1){
return `<CLR red = '255'>${$feature.Trtmt_2}, ${$feature.Trtmt_3} - ${(pt2 + pt3)}</CLR>`;
}
if (pt1 == 0 && pt2 == 0 && pt3 == 0){
return `<CLR red = '255' green = '200' blue = '100'>None</CLR>`;
}
//...
This also uses template literals to embed expressions.
I'm trying to change the line segment colors on the map without having to do it manually.
Your expression for "Overlay - 8" is wrong!
You labeling expression can be massively simplified:
var pt = [
$feature.Points_1,
$feature.Points_2,
$feature.Points_3,
]
// count the zeroes
var zeroes = 0
for(var p in pt) { zeroes += pt[p] == 0 }
// all values are zero -> None
if(zeroes == 3) { return "None" }
// two values are zero -> return the non-zero value
if(zeroes == 2) {
if(pt[0] > 0) { return "Overlay - " + pt[0] }
if(pt[1] > 0) { return "Seal - " + pt[1] }
if(pt[2] > 0) { return "Crack Fill - " + pt[2] }
}
// one or no value(s) are zero -> return all non-zero Trtmt values and the sum of the Points values
var trtmt = [
$feature.Trtmt_1,
$feature.Trtmt_2,
$feature.Trtmt_3,
]
var trt = []
for(var t in trtmt) {
if(pt[t] > 0) { Push(trt, trtmt[t]) }
}
return Concatenate(trt, ", ") + " - " + Sum(pt)
You can input that expression into the symbology as Unique Value "field":
This will give you a legend like this:
But that's not really practical. There are 65 conditions in your original expression, and there are actually a lot more possible values, because you take the sum of all non-zero Point values. You would have to format all the colors, and it wouldn't provide any useful information to your viewers, because the color would be too similar.
In my opinion, a better way would be to only symbolize the combination of treatments and only display the actual values in the label.
This is the last part of the label expression, slightly adapted:
var pt = [
$feature.Points_1,
$feature.Points_2,
$feature.Points_3,
]
var trtmt = [
$feature.Trtmt_1,
$feature.Trtmt_2,
$feature.Trtmt_3,
]
var trt = []
for(var t in trtmt) {
if(pt[t] > 0) { Push(trt, trtmt[t]) }
}
if(Count(trt) == 0) { return "None" }
return Concatenate(trt, ", ")
And this gives you a much more manageable legend (only 8 items) that actually provides information to your viewers:
Together with the labels:
Okay first of all, wow! Thank you for giving me a way to simplify the code. I've been working in excel for the past 2 weeks straight so my first instinct is to write things out how it's required there. Lol it's very counter-productive.
Second of all, I agree that scaling it back it the best solution. The only problem is, is finding a visual solution for 9,500 line segments that can be shown on an 11x17 print out. 😩 I think I'll combine some of the points system (i.e. lump segments with points 1-3 as one) so there's far less colors/variables. I unfortunately can't totally remove the points category just yet. And I was truly looking for a way to just add colors automatically instead of manually doing it. So thanks for helping with the scaled back code, at least!
OK, I may have found a solution that works.
You can enable symbol property connections in the symbology, which lets you choose your color programmatically (or with a field). I didn't suggest that originally, because this isn't reflected in the legend.
But luckily, you have three treatment types which you can let correspond to the three color channels of an RGB value (red, green, blue). You can then vary the lightness of the color by using the rgb channel of the missing treatment(s). The varying lightness won't show up in the legend, so you have a manageable number of items there, and your viewers can get a sense of your point system. But they won't be able to directly get the points by looking at the color, because the color doesn't show up in the legend, so they still have to rely on labels or popups for that.
var rgb_channel_min = 0
var rgb_channel_max = 200
var low_values_are_lighter = false
var pt = [
$feature.Points_1,
$feature.Points_2,
$feature.Points_3,
]
var zeroes = 0
for(var p in pt) { zeroes += Includes([null, 0], pt[p]) }
var value = Sum(pt)
var max_value = (3 - zeroes) * 10.5
var normalized_value = value / max_value
var rgb_channel = IIf(low_values_are_lighter,
rgb_channel_max - normalized_value * (rgb_channel_max - rgb_channel_min),
rgb_channel_min + normalized_value * (rgb_channel_max - rgb_channel_min)
)
var rgb = [0, 0, 0]
for(var p in pt) {
rgb[p] = IIf(pt[p] > 0 && zeroes > 0, 255, Round(rgb_channel))
}
return `rgb (${Concatenate(rgb, ", ")})`
So it's still a little manual work, but only for 8 items, not for 65+. Here is the result: