I am using Mapbox to add a red rectangle to a map. I would like to modify the colours of my rectangle. I use the raster-color
Paint property to achieve this.
Mapbox provides the example Add a raster image to a map layer. Based on this, I create the folowing:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<link
href="https://api.mapbox.com/mapbox-gl-js/v3.4.0/mapbox-gl.css"
rel="stylesheet"
/>
<script src="https://api.mapbox.com/mapbox-gl-js/v3.4.0/mapbox-gl.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken =
"pk.eyJ1IjoicGFsc3phYm8iLCJhIjoiY2xrNWY3cDhuMGpiajNwbzdlNzlscHc1eSJ9.Sppso1puTUCwR03aaWUHsQ";
const map = new mapboxgl.Map({
container: "map",
maxZoom: 10,
minZoom: 0,
zoom: 6,
center: [-82, 0],
style: "mapbox://styles/mapbox/dark-v11",
});
map.on("load", () => {
map.addSource("image", {
type: "image",
url: "https://upload.wikimedia.org/wikipedia/commons/8/89/Alizarin_crimson_(color).jpg",
coordinates: [
[-83, 1],
[-81, 1],
[-81, -1],
[-83, -1],
],
});
map.addLayer({
id: "radar-layer",
type: "raster",
source: "image",
paint: {
"raster-fade-duration": 0,
},
});
});
</script>
</body>
</html>
A red square does indeed show up on the Ecuadorian shores (location specified by coordinates
above):
To obtain the exact RGB values of the red rectangle, I download it (curl "https://upload.wikimedia.org/wikipedia/commons/8/89/Alizarin_crimson_(color).jpg" --output shape.jpg
), then process it with Python:
import imageio.v3 as iio
import numpy as np
array = iio.imread('shape.jpg')
print(np.unique(array[:,:,0],return_counts=True))
print(np.unique(array[:,:,1],return_counts=True))
print(np.unique(array[:,:,2],return_counts=True))
Output:
(array([226], dtype=uint8), array([686460]))
(array([38], dtype=uint8), array([686460]))
(array([53], dtype=uint8), array([686460]))
This tells us that every pixel in the rectangle has colour RGB(226,38,53)
. Knowing this, I can modify paint
so that all source pixels which have R values between 221 and 229 are shown as rgb(123,222,111,255)
(an example colour).
paint: {
"raster-color": [
"interpolate",
["linear"],
["raster-value"],
220 / 255,
"rgba(0,0,0,0)",
221 / 255,
"rgba(123,222,111,255)",
229 / 255,
"rgba(123,222,111,255)",
230 / 255,
"rgba(0,0,0,0)",
],
"raster-color-mix": [1, 0, 0, 0],
"raster-color-range": [220 / 255, 230 / 255],
"raster-resampling": "nearest",
},
Specifying raster-color-mix
to be [1, 0, 0, 0]
ensures that the so-called raster-value
is equal to the R channel. This then serves as a parameter of raster-color
, which is:
Defines a color map by which to colorize a raster layer, parameterized
by the["raster-value"]
expression and evaluated at 256 uniformly
spaced steps over the range specified byraster-color-range
.
raster-color-range
specifies the range I want raster-color
to be parametrized over. Keeping it as narrow as possible helps, as the parametrisation happens in 256 steps (source: raster-color
line 2).
Editing paint
does indeed result in a differently coloured square:
However, if I want to use a narrower range of R values, the square does not show up. According to Python (see above), R channel is always 226, so remapping all values between 225 and 227 should indeed work:
paint: {
"raster-color": [
"interpolate",
["linear"],
["raster-value"],
224 / 255,
"rgba(0,0,0,0)",
225 / 255,
"rgba(123,222,111,255)",
227 / 255,
"rgba(123,222,111,255)",
228 / 255,
"rgba(0,0,0,0)",
],
"raster-color-mix": [1, 0, 0, 0],
"raster-color-range": [224 / 255, 228 / 255],
"raster-resampling": "nearest",
},
But no square shows up. After a few trial and error, I find that the correct range to remap is somewhere between 223.3 and 223.4. If I use these values, the square does indeed show up again:
paint: {
"raster-color": [
"interpolate",
["linear"],
["raster-value"],
223.3 / 255,
"rgba(0,0,0,0)",
223.31 / 255,
"rgba(123,222,111,255)",
223.39 / 255,
"rgba(123,222,111,255)",
223.4 / 255,
"rgba(0,0,0,0)",
],
"raster-color-mix": [1, 0, 0, 0],
"raster-color-range": [223.3 / 255, 223.4 / 255],
"raster-resampling": "nearest",
},
I can conclude that if I treat the pixels (all of them have R=226) as if their R value is somewhere between 223.3 and 223.4, I can recolour them.
Based on experiments on a different image: when R is 118, the range I need to specify for recolouring is centered roughly on 116.631.
Finding the correct range by trial and error is quite tedious, and prone to error. Using a wide range is problematic as well, as I often want to recolour pixels with similar R values differently. Wide, overlapping ranges prevent this, so I want to use as narrow ranges as possible.
How can I find out the values I need to use in raster-color
to recolour those (and only those) pixels with a given R
value?