Bing API Custom Overlay Flicker Bug Report

PeterTownsend-7781 21 Reputation points
2022-09-13T20:28:07.017+00:00

You are making it too hard to report bugs with the V8 Web API Control. The Report a Bug link doesn't even send you to this place.

If you add a CustomOverlay to the map, it causes TileSource based TileLayers to flicker when you pan the map.

In this first example, first we add the OSM street map layer and then add the PanningOverlay custom overlay from the samples. Put this in an HTML file and set your own API key.

https://gist.github.com/mlptownsend/e705745310a34a2426b24ce655dd8658

And in this example, I do NOT add the custom overlay to the map, and it pans just fine.

https://gist.github.com/mlptownsend/ce4fad5a4fda0f7d9993f7c70738ec48

Azure Maps
Azure Maps
An Azure service that provides geospatial APIs to add maps, spatial analytics, and mobility solutions to apps.
587 questions
Windows Maps
Windows Maps
A Microsoft app that provides voice navigation and turn-by-turn driving, transit, and walking directions.
245 questions
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. rbrundritt 15,231 Reputation points Microsoft Employee
    2022-09-14T08:26:08.39+00:00

    Looking into this I managed to reproduce your issue... it both examples you provided, and in a third one where I just had a tile layer and no code for a custom overlay. For others who look at this thread, the issue is that when the map is panned either with the mouse, or by calling the setView method, the vector road lines of Bing Maps appear to flash above the added tile layer momentarily. Here is a video of the issue:

    240951-tilelayerroadflash.gif

    Looking through your code I thought at first that the issue might be related to your use of a callback function for the uriConstructor of the tile source as that can impact performance. You can simply replace "{z}" with "{zoom}" once in the OSM tile URL before passing the URL string as the uri constructor rather than doing this for every tile via the callback. Here is an example:

       var osmUrl = 'https://a.tile.openstreetmap.org/{zoom}/{x}/{y}.png';  
         
       var baseLayer = new Microsoft.Maps.TileLayer({  
       	mercator: new Microsoft.Maps.TileSource({  
       		uriConstructor: osmUrl.replace('{z}', '{zoom}'),  
       		minZoom: 0,  
       		maxZoom: 20  
       	}),  
       	zIndex: 10000,  
       	visible: true  
       });  
       map.layers.insert(baseLayer);  
    

    That said, I still was able to reproduce the issue.

    I then tried one of the existing Bing Maps samples: https://samples.bingmapsportal.com/tile-layer/quadkey and noticed a very small flash with the tiles refreshing, but nothing as significant as what your example is doing.

    Taking a closer look at your example I noticed that there were some general markup issues, and after fixing those the issue still persisted. Here is a minimalized cleaned up version of the code that reproduces this issue.

       <!DOCTYPE html>  
       <html lang="en">  
       <head>  
           <title></title>  
         
           <meta charset="utf-8" />  
           <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />  
             
           <script>  
           var map;  
         
           function GetMap() {  
               map = new Microsoft.Maps.Map('#myMap', {  
                   center: new Microsoft.Maps.Location(25, -90),  
                   zoom: 5  
               });  
         
               map.layers.insert(new Microsoft.Maps.TileLayer({  
       				mercator: new Microsoft.Maps.TileSource({  
       				uriConstructor: 'https://a.tile.openstreetmap.org/{zoom}/{x}/{y}.png'  
       			})  
               }));  
       	}  
           </script>      
       </head>  
       <body>  
           <div id="myMap" style="position:relative;width:100%;height:100%;"></div>  
         
           <script type="text/javascript" src="https://www.bing.com/api/maps/mapcontrol?&callback=GetMap&key=YOURKEY"></script>  
       </body>  
       </html>  
    

    I next tried using a different tile service (ESRI world imagery) and found that the issue again didn't appear nearly as much as it was in your example.

       map.layers.insert(new Microsoft.Maps.TileLayer({  
       		mercator: new Microsoft.Maps.TileSource({  
       		uriConstructor: 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{zoom}/{y}/{x}'  
       	})  
       }));  
    

    Finally, I narrowed it down to the time it is taking the tiles to load. You are loading OSM tiles with the subdomain set to "a", but the tile service actually uses multiple subdomains. This is causing two bottlenecks in the loading of the tiles that is resulting in the Bing Maps roads having time to render once before the tiles are ready to render.

    1. The tile servers' cache for each subdomain likely has a limited set of tiles cached (i.e. odd rows, even column tile IDs), and since you are using a single subdomain, three out of four tiles aren't in the that tile servers' cache for that subdomain endpoint.
    2. Browsers limit the number of concurrent requests to the same URL domain. Usually 8 requests. To fill a full screen 1080p monitor (1920×1080) with tiles that are 256x256 tiles it takes over 32 tile requests. While the first 8 tile requests are made quickly, the rest of the tile requests wait for one or more of the other requests to complete before the next ones can be requested. Using subdomains in the tile URL tricks the browser and gets around this limitation (there is another higher limit for total concurrent http requests, but that's for another day when if you have a bunch of tile layers on a map).

    Now the Bing Maps tile layer class uses the numbers 0 through 3 as subdomains, while OSM uses letters a, b, c, and one URL without a subdmain. So, to leverage subdomains we have to use a callback for the uriConstructor and convert the subdomains as needed. Here is a code sample.

       <!DOCTYPE html>  
       <html lang="en">  
       <head>  
           <title></title>  
         
           <meta charset="utf-8" />  
           <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />  
             
           <script>  
           var map;  
         
           function GetMap() {  
               map = new Microsoft.Maps.Map('#myMap', {  
                   center: new Microsoft.Maps.Location(25, -90),  
                   zoom: 5  
               });  
         
       		var baseUrl = 'https://{subdomain}tile.openstreetmap.org/{z}/{x}/{y}.png';  
       		var osmSubdomains = ['','a.','b.','c.'];  
          
               map.layers.insert(new Microsoft.Maps.TileLayer({  
       				mercator: new Microsoft.Maps.TileSource({  
       				uriConstructor:  function(tile) {  
       					//Calculate a numbered subdomain based on the tiles x/y (row/col) position.  
       					const subdomain = tile.x%2 + tile.y%2;  
       				  
       					baseUrl = baseUrl.replace("{z}", "{zoom}").replace('{subdomain}', osmSubdomains[subdomain]);  
       					  
       					return baseUrl.replace('{x}', String(tile.x)).replace('{y}', String(tile.y)).replace('{zoom}', String(tile.zoom));  
       				},  
       			})  
               }));  
       	}  
           </script>      
       </head>  
       <body>  
           <div id="myMap" style="position:relative;width:100%;height:100%;"></div>  
         
           <script type="text/javascript" src="https://www.bing.com/api/maps/mapcontrol?&callback=GetMap&key=YOURKEY"></script>  
       </body>  
       </html>  
    

    Now, I was fairly confident this would resolve the issue, but it didn't. I was in disbelief at this point. I ended up trying a bunch of other tile layers from other services and I couldn't reproduce the issue. I could only reproduce this with the OSM tile service. I was about to give up when I decided to take a closer look at the response headers from these services, and as it turns out, I found something. The response headers for OSM tiles have a cache "expires" tag for "Wed, 31 Aug 2022 07:26:24 GMT" (likely will change relative to when you read this, but the key is that this is a date in the past) while all other tile services I tested with have future dates for the expiry which is the expectation. Basically, it looks like the issue is that because of this tag in the OSM tiles, the browser cache is being ignored and every tile has to be re-requested and reloaded from the server when the map moves (even a little bit). This is what is causing your issue. The Bing Maps Web SDK is an older SDK that uses standard HTML 5 canvas for rendering, not WebGL as browser support for WebGL when this SDK came out was not great. The HTML 5 canvas provided the ability to render lots more data than previous technologies (SVG DOM elements), but is also a lot tricker for heavily dynamic controls like this. The Bing Maps control is good at rendering large data sets but struggles if you try and make a lot of data changes quickly, like in an animation. In this case, because the cache expiry is in the past, these tiles are constantly reloading like an animation and as a result causing this visual defect.

    Now that we know what the issue is there are a couple of options to get around it:

    1. Setup a proxy service to pass all the tiles through and modify the expires header (possibly improve some of the others).
    2. Download the OSM tiles and setup your own tile server (you have full control over the headers). Generally, OSM doesn't want people directly accessing their tile servers as per this policy: https://operations.osmfoundation.org/policies/tiles/
    3. If you are just getting started with your development and don't have a hard requirement to use Bing Maps, consider using Microsoft's newer map platform Azure Maps (i.e. the name of this forum category). Azure Maps came out about 5 years ago and uses WebGL which has much better browser support now than when Bing Maps V8 SDK was released and has a lot better performance. I have used the OSM tiles with the Azure Maps SDK many times and know that they work fine. One key thing there is that there is a setting in the Azure Maps SDK for automatically reloading tiles when the cache expires (https://learn.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.serviceoptions?view=azure-maps-typescript-latest#azure-maps-control-atlas-serviceoptions-refreshexpiredtiles). If this is an option for you, here are some useful resources:

    https://azure.com/maps

    https://samples.azuremaps.com/

    https://learn.microsoft.com/en-us/azure/azure-maps/

    As for the "Report a bug" link in the Bing Maps site, that's for data issues. For technical support you can contact the support team or use these forums: https://www.microsoft.com/en-us/maps/support There is also a link to get support on the Bing Maps account portal.

    2 people found this answer helpful.
    0 comments No comments

  2. PeterTownsend-7781 21 Reputation points
    2022-09-20T20:34:35.307+00:00

    Unfortunately I'm kinda stuck with the Bing Maps API requirement and can't switch to Azure.

    The tile source we are ultimately consuming is of our own creation, and we are serving a Cache-Control w/ a week long expiration. We weren't sending Expires, so I added it and it changed nothing to the behavior. Particularly the behavior when a CustomOverlay is added. The examples you provided aren't adding a custom overlay.

    Here are a couple more examples. I turned off the built-in Bing basemaps on these. You'll definitely see the flickering.

    https://gist.github.com/mlptownsend/51684e0e988250dd3630b4d5fa6d67b7
    This one uses the WMS example from the tutorials. Same problem.

    https://gist.github.com/mlptownsend/45885a1f2b40544373e583340dfd8920
    This uses basemap.nationalmap.gov's ARCGIS service.