Skip to Content
ExamplesCustom Radar Colorscale

MapsGL Android - Customizing radar color scale

This example swaps the radar layer’s color scale while the map is running — between the built-in per-precipitation-type scales, a custom set, and three generic palettes — with the legend on screen so you can watch it follow.

It is the Android port of the MapsGL JS Customizing radar color scale example. The complete activity ships in the MapsGL Android SDK demo  as CustomRadarColorscaleActivity.

Radar over the central United States in a custom green-to-red color scale, with a three-bar legend showing the rain, mix and snow ramps
The same radar drawn in purple, magenta and orange by a plasma palette, with a single combined legend bar

Changing the scale

Set the scale on the built-in configuration before the layer is added, or call MapController.setPaintProperty once it is on the map. setPaintProperty rebuilds the color lookup tables and updates the legend. The property path matches the JS example’s sample.colorscale:

val config = WeatherService.Radar(controller.service) val paint = (config.layer.paint as SampleLayerPaint).sample paint.colorScale = perTypeScale() val layer = controller.addWeatherLayer(config) ?: return controller.setPaintProperty(layer.id, "sample.colorscale", singleScale())

sample.colorScale is also accepted. The value is a ColorScaleOptions.

Per-precipitation-type scales: nested stop lists

The JS example passes three masks and relies on array order:

masks: [ { colorscale: rain }, { colorscale: mix }, { colorscale: snow } ]

Nest one stop list per type inside stops:

ColorScaleOptions( stops = listOf( ColorScales.radarRain.stops, ColorScales.radarMix.stops, ColorScales.radarSnow.stops, ), )

ColorScaleOptions.stops accepts a flat list of ColorStop or a nested list with one band per precipitation type. Nested stops are read in rain, mix, snow order.

normalized stays false here, because the stop values are real dBZ numbers.

Color strings

ColorStop accepts a Color or a color string. Strings use Android’s #RRGGBB and #AARRGGBB form, so the JS 'rgba(0,0,0,0)' is "#00000000". Alpha leads.

private val CLEAR = listOf( ColorStop(0.0, "#00000000"), ColorStop(5.9, "#00000000"), ) val CUSTOM_RAIN = CLEAR + listOf( ColorStop(6.0, "#588d45"), ColorStop(40.0, "#a1943a"), ColorStop(70.0, "#fa0000"), )

That transparent lead-in is not decoration: without it the lowest color in the scale floods the map everywhere there is no precipitation. The JS example does the same thing by passing ['rgba(0,0,0,0)'] as the prefix argument to getColorScale.

Palettes

ColorScales provides the built-in weather ramps, including radarRain, radarMix, and radarSnow. A generic palette such as viridis or plasma is a list of 0..1 control points in your own code. Map those positions onto the radar dBZ range so the fill and the legend share the same stops:

// The numbers LegendCode.RADAR's own bars use, so the palette covers exactly what the legend draws. private const val DBZ_MIN = 6.0 private const val DBZ_MAX = 87.0 private fun paletteOverDbz(palette: List<Pair<Double, String>>): List<ColorStop> = CLEAR + palette.map { (position, hex) -> ColorStop(DBZ_MIN + position * (DBZ_MAX - DBZ_MIN), hex) }

Use dBZ stops for radar. normalized = true is for data that is already in the 0..1 range, such as the wind-particles scale.

One legend bar for a single scale

LegendCode.RADAR is three bars — rain, mix, and snow. A single scale for every precipitation type belongs on one bar. After setPaintProperty, fold the template down to that bar and drop the type word from its labels. The legend title already says Radar.

Register the legend control before adding the layer, so it is there when the layer creates its legend. See Using legends.

@Suppress("UNCHECKED_CAST") private fun combineLegendBarsIfOneScale() { if (SCALE_NAMES[scaleIndex] in PER_TYPE_SCALES) return val legend = legendControl.getLegend(LEGEND_ID) as? BarLegend<Dimension> ?: return val bar = legend.items.firstOrNull() ?: return val combined = bar.copy( labels = bar.labels.copy( values = BarLegendLabels.Values.FromLabels<Dimension> { listOf(0.0 to "Light", 1.0 to "Heavy") }, ), ) legendControl.update(legend.copy(items = listOf(combined))) }

Call this after the layer is added, which is when the legend exists. A later setPaintProperty with per-type stops writes those stops back into the configuration’s three bars.

LEGEND_ID is LegendCode.RADAR.id. Dimension is com.xweather.mapsgl.extensions.Dimension.

© 2026 Xweather (opens in a new tab)Terms of Service (opens in a new tab)Privacy Policy (opens in a new tab)