my objective is to have a mapbox map with a custom layer overlaid on it. that custom layer should be masked by an other mapbox map.
usecase:
i have some default map. on top of that i want to render some special heatmap. that heatmap has to only be shown above roads.
current approach:
- add layers to mapbox that produce a map where all roads are white and background is black
- add a custom layer that copies the content of the framebuffer to a texture
- add layers to render some default map
- add custom layer that draws the special heatmap using the above texture as a mask.
problem:
the order in which the layers are rendered seems to not match the order in which they are added to mapbox. this example has a custom layer that outputs the color of the center pixel in the framebuffer at the moment it is called:
var map = window.map = new mapboxgl.Map({
container: 'map',
zoom: 16,
center: [8.30468, 47.05232],
style: {
"version":8,
"name":"test",
"sources": {
"openmaptiles": {
"type": "vector",
"url": "https://api.maptiler.com/tiles/v3/tiles.json?key=X0HhMcrAjHfR6MvFLSSn"
}
},
"layers":[]}
});
var getPixelLayer = {
id: 'highlight',
type: 'custom',
onAdd: function (map, gl) { },
render: function (gl, matrix) {
var pixels = new Uint8Array(4);
gl.readPixels(Math.round(gl.drawingBufferWidth/2), Math.round(gl.drawingBufferHeight/2), 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
document.getElementById("color").innerHTML = pixels[0]+","+pixels[1]+","+pixels[2];
}
};
map.on('load', function() {
//expected output: "255,255,255" everywhere
//actual output: "0,0,0" everywhere
/*
map.addLayer({"id":"bkg1","paint":{"background-color":"rgb(255, 255, 255)"},"type":"background"});
map.addLayer(getPixelLayer);
map.addLayer({"id":"bkg2","paint":{"background-color":"rgb(0, 0, 0)"},"type":"background"});
*/
//expected output: "0,0,0" everywhere
//actual output: "177,177,177" above buildings
// "0,0,0" above streets
// "0,0,0" above background
map.addLayer({"id":"bkg1","paint":{"background-color":"rgb(0, 0, 0)"},"type":"background"});
map.addLayer(getPixelLayer);
map.addLayer({"id":"roads","layout":{"line-cap":"round","line-join":"round","visibility":"visible"},"paint":{"line-color":"#fff","line-offset":0,"line-width":{"base":1.4,"stops":[[8,1.2],[16,12]]}},"source":"openmaptiles","source-layer":"transportation","type":"line"});
map.addLayer({"id":"building","paint":{"fill-color":"rgb(177, 177, 177)"},"source":"openmaptiles","source-layer":"building","type":"fill"});
});
https://jsfiddle.net/t31cj5v0/
as can be seen the rendering order seems to be background->buildings->custom_layer->streets instead of background->custom_layer->streets->buildings. The layer orderings appears correct on screen at the end, but doing what i want is not possible like this.
does anyone know what causes this behaviour and how it could be avoided?
other implementations i tried:
i also tried to render the black-white street map to a different mapbox object with synced movement. this works, but since each mapbox has its own webgl context i cant use the resulting black-white texture in the other context.
featurerequest (?)
it would be really nice if some "mask" layer could be implemented to accomplish this objective natively. but right now i'm just searching for some hack to do this. thanks in advance.