Search code examples
javascriptbrowsermapsazure-mapsazure-digital-twins

How to render rail or train tracks in Azure Maps at all zoom levels?


I am trying to render train locations in Azure Maps. To give more context to the train locations I would like to show the train tracks. However, by default, train tracks only appear when very zoomed in. How can I control the zoom levels that rail or train tracks are displayed?

I have manually explored the JavaScript API and discovered that the default map when looking at Australia has two line-type layers related to rail: "Railway" and "Railway outline". These can be inspected as follows:

map.map.getLayer('Railway')
map.map.getLayer('Railway outline')

Layers are supposed to support minZoom and maxZoom properties as documented here, but setting these has no effect. Setting other properties, such as colour, works.

The map knows where the tracks are, how to render them, and lets you customise the rendering, how can I stop the disappearing when I zoom out?


Solution

  • All things that appear on the map, wether it is from Azure Maps or your own input consists of two things; data and a rendering layer(s). The undocumented code you accessed will retrieves two of the layers in the map. Now adjusting the min and max zoom of these layer would allow the railway data to be displayed if there is data available. This is where the issue lies. The data source being used in this scenario is the vector tiles used to create the base maps. For each zoom level there is a set of vector tiles that have specified data types in them. Not all data is available at all zoom levels as that would take forever to load tiles when zoomed out as they would be too big. So instead each data type has a zoom range in which they are available. The vector tile schema for Azure Maps tiles are not currently document (they also just had a major change in version 3 that would have broken any dependency on their structure). Digging into the Azure Maps vector tiles it looks like the zoom level range for where railway data is available is and the rendering layers associated with railway lines appear to use this full zoom level range. This means all the railway data is being displayed at the zoom levels it is available for.

    So, if you want to see railway data at additional zoom levels you will need to find another source for this data.

    If you only need this for a single country or US state, many countries/states make this data freely available in various geospatial file formats like GeoJSON or Shapefiles. Depending on those file sizes you could load the data in directly into the map or will need to turn it into a tile layer (raster or vector) to make it easier to load and render.

    If you want data for a smaller area, an easy way to get this is with Overpass Turbo which is a tool that downloads small data extracts from OpenStreetMaps. You can easily export this data as GeoJSON and display it in Azure Maps. Here is a simple query for retrieving the railway line data:

    /*
    This has been generated by the overpass-turbo wizard.
    The original search was:
    “railway=rail”
    */
    [out:json][timeout:800];
    // gather results
    (
      // query part for: “railway=rail”
      way["railway"="rail"]({{bbox}});
    );
    // print results
    out body;
    >;
    out skel qt;
    

    If you want global data the easiest place to find this would be OpenStreetMaps. You could extract the data from there, or you could take a look at OpenRailwayMaps. OpenRailwayMaps makes a raster tile layer of global railway lines available as documented here. Note that these are raster tile layers so you have no control over how they are rendered or what data appears at which zoom level. Here is a code sample of using this data in Azure Maps:

    <!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" />
    
        <!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
         <link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" type="text/css" />
        <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>
    
        <script>
            var map;
            
            function GetMap() {
                //Initialize a map instance.
                map = new atlas.Map('myMap', {
                    zoom: 2,
                    view: 'Auto',
    
                    authOptions: {
                        authType: 'subscriptionKey',
                        subscriptionKey: '<Your Azure Maps Key>'
                    }
                });
    
                map.events.add('ready', function () {
                    //Create a tile layer and add it to the map below the label layer.
                    //Tile layer from https://wiki.openstreetmap.org/wiki/OpenRailwayMap/API
                    map.layers.add(new atlas.layer.TileLayer({
                        tileUrl: 'https://tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png',
                        subdomains: ['a', 'b', 'c'],
                        tileSize: 256,
                        minSourceZoom: 2,
                        maxSourceZoom: 19
                    }), 'labels');
                });
            }
        </script>
    </head>
    <body onload="GetMap()">
        <div id="myMap" style="position:relative;width:100%;height:800px;"></div>
    </body>
    </html>
    

    Now if you want a lot more control of how the data is rendered, then vector tiles would be a better option. This tool appears to generate vector tiles of railway data used by the OpenRailwayMaps.

    Alternatively you could use OpenStreetMaps vector tiles and jus tleverage the railway data within it. Here is one source for OpenStreetMaps vector tiles.