Custom Legends
Legends provide insight behind the data of a visualization, defining what various colors and symbols mean for a particular data set. They are used in conjunction with renderer styles to tell a particular story about that data.
Adding Legends
In order to render your own legends, you'll first need to set up a legend control which is responsible for managing and rendering a series of legends. This legend control can be added to your map or other part of your application interface.
The following example creates a LegendControl instance and adds it to the map:
// Initialize a legend control
var legendControl = LegendControl()
// Configure legend control...
// Adds a legend control to the map.
controller.add(legendControl: legendControl)Once you've set up a legend control instance, you can add legends to it using the addLegend method.
There are two types of legends: bar and point. Refer to the legend reference documentation for the full list of supported configuration options for both legend types.
Bar Legends
Bar legends consist of a horizontal color scale bar and are used to show data across a minimum and maximum value range.
For example, the following adds a legend representing heat index values on a map in the value range of 80 F (26.67 C) and 130 F (54.44 C):
func buildHeatIndexBarLegend() -> any Legend {
let colorStops = [
ColorStop(26.666666666667, .fromString("#f33300")),
ColorStop(32.222222222222, .fromString("#d00000")),
ColorStop(37.777777777778, .fromString("#720000")),
ColorStop(43.333333333333, .fromString("#ce0046")),
ColorStop(48.888888888889, .fromString("#ff4b98")),
ColorStop(54.44, .fromString("#ffadad")),
]
let options = ColorScaleOptions(stops: colorStops, range: 26.67...54.44)
let barItem = BarLegendItem<UnitTemperature>(colorScaleOptions: options)
.height(14)
.rounded(false)
.labels(BarLegendLabels()
// use a different label interval for metric
.values(.every { units in
units == UnitTemperature.celsius ? 5 : 10
})
)
return BarLegend(id: "heat-index")
.title("Heat Index")
.labelColor(.white)
.titleColor(.black)
.measurement(.temperature)
.currentUnits(.celcius)
.items([ barItem ])
}
Updating Bar Legends
You can update an existing legend using the legend control's update(legend:) method and passing in the configuration overrides you want to update the legend with.
For instance, to customize the the title label color and backgroundColor of the Legend, you would need to update the legend text color and commit the changes on the LegendControl:
let heatIndexBar = buildHeatIndexBarLegend()
legendControl.backgroundColor = .black
legendControl.update(legend: heatIndexBar.titleColor(.white))
Or, you want to update the legend labels to categorize values in the range rather than show actual numerical values:
var tempBar = heatIndexBar.items[0]
tempBar.labels = tempBar.labels
.normalized(true)
.values(.labels({ _ in
return [
(0.0, "Uncomfortable"), // left label
(0.5, "Hot"), // middle label
(1.0, "Dangerous"), // right label
]
}))
legendControl.update(legend: heatIndexBar.items([ tempBar ]))
Note that in this last example we are setting item.labels.normalzed to true since we are providing the label values in the range of 0 to 1 (0 being the left side of the bar and 1 being the right). If we had left this value as the default of false, then the labels would be placed in the value range defined in colorscale.range and no labels would render since their values between 0 and 1 are outside that range.
Point Legends
Point legends consist of symbols, colors and labels and are used to show categories, groups or types of data.
For example, the following configuration defines a legend for air quality, where values are categorized and plotted on the map as colored circles based on their level type:
func buildAirQualityLegend() -> any Legend {
PointLegend(id: "air-quality")
.title("Air Quality")
.radius(6)
.margins(CGSize(width: 3, height: 5))
.items([
PointLegendItem(color: .fromString("#29e11f"), label: "Good"),
PointLegendItem(color: .fromString("#f8f92a"), label: "Moderate"),
PointLegendItem(color: .fromString("#f9681b"), label: "Sensitive Groups"),
PointLegendItem(color: .fromString("#f60115"), label: "Unhealthy"),
PointLegendItem(color: .fromString("#7a2c83"), label: "Very Unhealthy"),
PointLegendItem(color: .fromString("#65001b"), label: "Hazardous"),
])
}
You can see in the above example how we are defining a color and label value for each item we want to display in the legend.
Updating Point Legends
As described above with bar legends, you can also update an existing point legend the legend control's update(legend:) method and passing in the configuration overrides you want to update the legend with. Here's a similar example switching the title color:
let airQualityLegend = buildAirQualityLegend()
legendControl.add(legend: airQualityLegend)
legendControl.backgroundColor = .black
legendControl.update(legend: airQualityLegend
.titleColor(.white)
.labelColor(.white))
Removing Legends
To remove a legend from a LegendControl instance, use the remove(legend:) method and provide the unique identifier you used when adding the legend originally:
legendControl.removeLegend(id: "air-quality")Note, however, that removeLegend may not always remove the legend instance from the control. That's because a legend is only removed when the total number of references associated with it is zero. A legend's reference count increments each time you call addLegend with the same unique identifier in order to prevent the same legend from being rendered multiple times in the case of multiple layers using the same layer.
If you must completely remove a legend instance regardless of the total number of remaining references, you can force its removal by passing true as the second argument:
legendControl.removeLegend(id: "air-quality", force: true)