Search code examples
javascriptazuregeospatialopenlayers-3azure-maps

Azure conditional data driven styling based on function result


I'm looking at migrating from OpenLayers to Azure Maps, and need to go through a list of things that I can currently do and need to be able to do on Azure. One of those things is styling, where

I'm styling a layer based on a few different things, one of which is if the name of a feature exists in an array. There's a few other requirements, but they all stem from the same base need.

I've seen I can define a custom property for each polygon when styling to use, but I can't see how to set this based on a custom function (ie, whether a property is in an array).

Here's what I'm doing in OpenLayers (v3):

this._style = (feature, resolution) => {
    
    if (elsaMap.titlesWithContactInfo.includes(MapHelpers.Titles.getProperty(feature, MapHelpers.Titles.PROPERTY_NAMES.TITLE_NO))) {
        // Client has entered contact info, return named style
        return clientAddedContactStyle;
    }

    // No ownership information
    return noOwnershipStyle
}

Can this be done in Azure Maps? I've gone through the docs on styling based on conditional expressions, but it doesn't seem to help too much.

Alternatively, can I write the style for a layer as a pure function?

https://learn.microsoft.com/en-us/azure/azure-maps/data-driven-style-expressions-web-sdk#conditional-expressions


Solution

  • Callback functions for styling are not supported as the styling logic is handed off to the GPU for processing. However, you can convert your function logic to a data driven style expression. Style expressions can be used to recreate complex function logic as a set of instructions that GPU will process. This is significantly faster than using callbacks for styling and offloads this processing from the single CPU UI thread of the browser to the GPU. Here is an example expression that checks to see if a property (title), is within an array of values, and sets the color of a bubble accordingly (same principal works with polygons).

    <!DOCTYPE html>
    <html>
    <head>
        <title></title>
    
        <meta charset="utf-8" />
        <meta http-equiv="x-ua-compatible" content="IE=Edge" />
        <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/css/atlas.min.css?api-version=2" type="text/css" />
        <script src="https://atlas.microsoft.com/sdk/js/atlas.min.js?api-version=2"></script>
    
        <script type='text/javascript'>
            var map, datasource;
    
            function GetMap() {
                //Initialize a map instance.
                map = new atlas.Map('myMap', {
                    zoom: 5,
    
                    //Add your Azure Maps subscription key to the map SDK. Get an Azure Maps key at https://azure.com/maps
                    authOptions: {
                        authType: 'subscriptionKey',
                        subscriptionKey: 'tTk1JVEaeNvDkxxnxHm9cYaCvqlOq1u-fXTvyXn2XkA'
                    }
                });
    
                //Wait until the map resources are ready.
                map.events.add('ready', function () {
                    //Create a data source to add your data to.
                    datasource = new atlas.source.DataSource();
                    map.sources.add(datasource);
    
                    //Array of values to look into.
                    var titlesWithContactInfo = ['titleA', 'titleB', 'titleC'];
    
                    //Add some data that has a "title" property.
                    datasource.add([
                        new atlas.data.Feature(new atlas.data.Point([0, 0]), { title: 'titleA' }),
                        new atlas.data.Feature(new atlas.data.Point([0, 1]), { title: 'titleC' }),
                        new atlas.data.Feature(new atlas.data.Point([1, 1]), { title: 'titleX' })
                    ]);
    
                    //Create a layer that styles shapes based on the title property. 
                    var layer = new atlas.layer.BubbleLayer(datasource, null, {
                        color: [
                            'case',
    
                            //Get the title property from the feature and see if it is in the list of valid titles. 
                            ['in', ['get', 'title'], ['literal', titlesWithContactInfo]],
    
                            //Make the color green if the title is in the array.
                            'green',
    
                            //Make the color red if it isn't.
                            'red'
                        ]
                    });
    
                    map.layers.add(layer);
                });
            }
        </script>
        <style>
            html, body {
                height: 100%;
                width: 100%;
                margin: 0;
                padding: 0;
            }
        </style>
    </head>
    <body onload="GetMap()">
        <div id="myMap" style="position:relative;width:100%;height:100%;"></div>
    </body>
    </html>
    

    Note that if you change the titlesWithContactInfo array, you will need to update the option in the layer as it will only be aware of the values passed in when the option is set. Basically pass the same style information into the setOptions function of the layer.