javascriptbing-mapsmaplibre-gl

Bing Maps as layer on MapLibre GL


I want to render a BingMaps as a raster layer on a MapLibre GL terrain. I know that MapLibre GL is a fork of MapBox GL and that MapBox can do this (or at least I think it can):

https://github.com/mapbox/mapbox-gl-native/issues/5485

Is there any way to accomplish this inside MapLibre GL currently? Or is this only possible in MapBox GL?

Cheers


Solution

  • This can be done fairly easily. The one extra bit is that you need to request the latest tile URL from Bing Maps every time your app loads. This creates the map session and ensures you have the latest URL (the URL's are modified with each data update to bust the cache). Doing this also ensures alignment with the Bing Maps terms of use. This is documented here: https://learn.microsoft.com/en-us/bingmaps/rest-services/directly-accessing-the-bing-maps-tiles

    Note that MapLibre GL JS supports {quadkey} tile URL parameter.

    Here is a quick sample using Bing Maps imagery in MapLibre GL:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <title>Add a raster tile source</title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <script src="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.js"></script>
    <link href="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.css" rel="stylesheet" />
    <style>
        body { margin: 0; padding: 0; }
        #map { position: absolute; top: 0; bottom: 0; width: 100%; }
    </style>
    </head>
    <body onload="initBingMaps()">
    <div id="map"></div>
    <script>
        var map;
        
        var BingMapsKey = '<Your Bing Maps Key>';
        
        var BingMapsImagerySet = 'Aerial'; //Alternatively, use 'AerialWithLabelsOnDemand' if you also want labels on the map.
        
        var BingMapsImageryMetadataUrl = `https://dev.virtualearth.net/REST/V1/Imagery/Metadata/${BingMapsImagerySet}?output=json&include=ImageryProviders&key=${BingMapsKey}`;
    
        
        function initBingMaps() {
            
            fetch(BingMapsImageryMetadataUrl).then(r => r.json()).then(r => {
                
                var tileInfo = r.resourceSets[0].resources[0];
                
                //Bing Maps supports subdoamins which can make tile loading faster. Create a tile URL for each subdomain. 
                var tileUrls = [];
                
                tileInfo.imageUrlSubdomains.forEach(sub => {
                    tileUrls.push(tileInfo.imageUrl.replace('{subdomain}', sub));
                });
                
                //Use the image provider info to create attributions.
                var attributions = tileInfo.imageryProviders.map(p => {
                    return p.attribution;
                }).join(', ');
            
                //Create a style using a raster layer for the Bing Maps tiles.
                var style = {
                    'version': 8,
                    'sources': {
                        'bing-maps-raster-tiles': {
                            'type': 'raster',
                            'tiles': tileUrls,
                            'tileSize': tileInfo.imageWidth,
                            'attribution': attributions,
                            
                            //Offset set min/max zooms by one as Bign Maps is designed are 256 size tiles, while MapLibre is designed for 512 tiles.
                            'minzoom': 1,
                            'maxzoom': 20
                        }
                    },
                    'layers': [
                        {
                            'id': 'bing-maps-tiles',
                            'type': 'raster',
                            'source': 'bing-maps-raster-tiles',
                            'minzoom': 0,
                            'maxzoom': 23   //Let the imagery be overscaled to support deeper zoom levels.
                        }
                    ]
                };
                
                //If you want to add terrian, you can either append it onto the stlye like this, or add it inline above.
                
                //Add the source
                style.sources.terrainSource = {
                    type: 'raster-dem',
                    url: 'https://demotiles.maplibre.org/terrain-tiles/tiles.json',
                    tileSize: 256
                };
                
                style.terrain = {
                    source: 'terrainSource',
                    exaggeration: 1
                };
                
                //Load MapLibre with this style.
                loadMap(style);
            });
            
        }
        
        function loadMap(baseMapsStyle) {
            map = new maplibregl.Map({
                container: 'map', // container id
                style: baseMapsStyle,
                zoom: 12,
                center: [11.39085, 47.27574],
                pitch: 52,
            });
        
        }
    </script>
    
    </body>
    </html>
    

    Here is what it looks like once you added your Bing Maps key to the above code:

    enter image description here