HarisH Sharma
HarisH Sharma

Reputation: 1247

Azure map generates huge billing, details says due to "Azure Maps - Location Insights"

Below is my Azure map sample. In which I am using:

  1. Azure maps html marker layer - for showing markers on map
  2. Azure maps spider clusters - for showing clusters on map
  3. Atlas drawing css and js.
  4. Atlas css and js.
<html lang="en">
<head>
    <!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
    <link href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" rel="stylesheet" />
    <link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/drawing/1/atlas-drawing.min.css" type="text/css" />

    <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>
    <script src="https://atlas.microsoft.com/sdk/javascript/drawing/1/atlas-drawing.min.js"></script>
    <script src="https://raw.githubusercontent.com/Azure-Samples/azure-maps-html-marker-layer/main/dist/azure-maps-html-marker-layer.min.js"></script>
    <script src="https://raw.githubusercontent.com/Azure-Samples/azure-maps-spider-clusters/main/dist/azure-maps-spider-clusters.min.js"></script>
    <style>
        .customInfobox { max-width: 240px; padding: 10px; font-size: 12px; margin-right: 20px; white-space: normal }
        .customInfobox .name { font-size: 14px; font-weight: bold; margin-bottom: 5px }
        .popup-content-container .popup-close { top: 12px !important; right: 6px !important; color: #ffffff !important; font-size: 16px !important; line-height: 18px !important; height: 15px !important; background: #000000 !important; width: 15px !important; border-radius: 50px !important; display: flex !important; justify-content: center !important; align-items: center !important; }
        .atlas-map-canvas { width: 100% !important }
    </style>
</head>
<body onload="GetMap()">
    <div id="myMap" style="position:relative;width:100%;min-width:290px;height:600px;"></div>
    <script>
        var map, datasource, popup, spiderManager;
        function GetMap() {
            //Initialize a map instance.
            map = new atlas.Map('myMap', {
                center: [-110, 50],
                zoom: 2,
                view: 'Auto',
                //Add authentication details for connecting to Azure Maps.
                authOptions: {
                    //Use Azure Active Directory authentication.
                    authType: 'subscriptionKey',
                    subscriptionKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
                }
            });

            var cordinates = [{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [-84.28295, 30.46454] }, "properties": { "Name": "aa", "Status": "online" } }, { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-84.28295, 30.46454] }, "properties": { "Name": "bb", "Status": "offline" } },
            { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-54.28295, 60.46454] }, "properties": { "Name": "cc", "Status": "offline" } },
            { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-58.28295, 66.46454] }, "properties": { "Name": "dd", "Status": "online" } }];

            var positions = { "type": "FeatureCollection", "features": cordinates };
            map.setCamera({
                //center map
                bounds: atlas.data.BoundingBox.fromData(positions),
                padding: 50
            });

            map.events.add('ready', function () {
                //Load customized icons for use with the symbol layer.
                var iconPromises = [
                    map.imageSprite.createFromTemplate('onlineIcon', 'marker', '#4cae4c', '#fff'),
                    map.imageSprite.createFromTemplate('offlineIcon', 'marker', '#808080', '#fff')
                ];

                //Wait for icons to load into the map sprite.
                Promise.all(iconPromises).then(() => {
                    //Create a data source to add your data to.
                    datasource = new atlas.source.DataSource(null, {
                        //Tell the data source to cluster point data.
                        cluster: true,

                        //The radius in pixels to cluster points together.
                        // clusterRadius: 45,
                        clusterProperties: { //Calculate counts for each entity type in a cluster
                            'online': ['+', ['case', ['==', ['get', 'Status'], 'online'], 1, 0]],
                            'offline': ['+', ['case', ['==', ['get', 'Status'], 'offline'], 1, 0]]
                        },

                        //The maximium zoom level in which clustering occurs.
                        //If you zoom in more than this, all points are rendered as symbols.
                        clusterMaxZoom: 24,
                        maxZoom: 24
                    });

                    //set data to datasource
                    datasource.setShapes(positions)
                    map.sources.add(datasource);

                    //Create a layer for rendering clustered data in the data source.
                    var clusterBubbleLayer = new atlas.layer.BubbleLayer(datasource, null, {
                        //Scale the size of the clustered bubble based on the number of points inthe cluster.
                        radius: [
                            'step',
                            ['get', 'point_count'],
                            20,         //Default of 20 pixel radius.
                            100, 30,    //If point_count >= 100, radius is 30 pixels.
                            750, 40     //If point_count >= 750, radius is 40 pixels.
                        ],

                        //Change the color of the cluster based on the value on the point_cluster property of the cluster.
                        color: [
                            'case', //Use a conditional case expression.
                            // all offline
                            ['>', ['get', 'offline'], 0] && ['==', ['get', 'online'], 0],
                            '#a5a5a5',

                            // all online
                            ['>', ['get', 'online'], 0] && ['==', ['get', 'offline'], 0],
                            '#6aa84f',

                            // online and offline both
                            '#ff9900'
                        ],
                        strokeWidth: 0,
                        filter: ['has', 'point_count'] //Only rendered data points which have a point_count property, which clusters do.
                    });

                    //Create a layer to render the individual locations.
                    var shapeLayer = new atlas.layer.SymbolLayer(datasource, null, {
                        //Define style for individual points.
                        iconOptions: {
                            //Use a case expression to select the image icon based on the Status property of the data point.
                            image: [
                                'case',

                                //Check if status is online
                                ['==', ['get', 'Status'], 'online'],
                                'onlineIcon',

                                //Offline/default icon.
                                'offlineIcon'
                            ]
                        },

                        filter: ['!', ['has', 'point_count']] //Filter out clustered points from this layer.
                    });

                    //Add the clusterBubbleLayer and two additional layers to the map.
                    map.layers.add([
                        clusterBubbleLayer,

                        //Create a symbol layer to render the count of locations in a cluster.
                        new atlas.layer.SymbolLayer(datasource, null, {
                            iconOptions: {
                                image: 'none', //Hide the icon image.
                            },
                            textOptions: {
                                textField: '{point_count_abbreviated}',
                                offset: [0, 0.4]
                            },
                            filter: ['!', ['has', 'point_count']]
                        }),

                        shapeLayer
                    ]);

                    markerLayer = new atlas.layer.HtmlMarkerLayer(datasource, null, {
                        markerCallback: function (id, position, properties) {
                            //Check to see if marker represents a cluster.
                            if (properties.cluster) {
                                return new atlas.PieChartMarker({
                                    position: position,
                                    colors: '#ffffff',
                                    fillColor: 'white',
                                    strokeColor: 'white',
                                    text: properties.point_count_abbreviated
                                });
                            } else {
                                //for single marker
                                return new atlas.HtmlMarker({
                                    visible: false
                                });
                            }
                        }
                    });

                    // map.events.add('click', markerLayer, markerClicked);

                    map.layers.add(markerLayer);

                    //Create an instance of the spider manager.
                    spiderManager = new atlas.SpiderClusterManager(map, clusterBubbleLayer, shapeLayer);

                    //Add event handler for when a feature is selected.
                    map.events.add('featureSelected', spiderManager, function (e) {
                        if (e.cluster) {
                            showPopup(e.cluster.geometry.coordinates, e.shape.getProperties(), [0, 0]);
                        } else {
                            showPopup(e.shape.getCoordinates(), e.shape.getProperties(), [0, -20]);
                        }
                    });

                    //Add event handler for when a feature is unselected.
                    map.events.add('featureUnselected', spiderManager, function () {
                        hidePopup();
                    });
                });
            });
        }

        var popupTemplate = '<div class="customInfobox"><div class="name">{name} ({status})</div></div>';
        showPopup = function (position, properties, pixelOffset) {
            var content = popupTemplate.replace(/{name}/g, properties.Name).replace(/{status}/g, properties.Status);
            popup.setOptions({
                //Update the content of the popup.
                content: content,
                //Update the position of the popup with the symbols coordinate.
                position: position,
                //Offset the popups position for better alignment with the layer.
                pixelOffset: pixelOffset
            });

            //Open the popup.
            popup.open(map);
        }

        hidePopup = function () {
            popup.close();
        }
    </script>
</body>
</html>

Everything is working fine but the problem is that I am not using any IoT related services in my map but Azure Billing generates the too much bill for the "Azure Maps - Location Insights".

What is this mean and how can I disable it because its generating too much billing.

Thanks in advance.

Upvotes: 0

Views: 1073

Answers (1)

rbrundritt
rbrundritt

Reputation: 17954

Location insights is a group of Azure Maps services, mostly related to searching and geocoding. The map does use the reverse geocoding service to power the screen reader for accessibility when the map view is in an area that has little details in the vector map tiles. when your map is over non-populated areas such as forests and oceans, this service is more likely to be called. When the map is over populated areas, it is very unlikely to be called as the vector tiles are more likely to have labels that can be used to power the screen reader.

Note that the above behavior is for version 2 of the Azure Maps Web SDK. If you are using version 1, it calls the reverse geocoder every time the map moves, and it is highly recommended that you upgrade to v2 (it is backwards compatible, just point to the latest version). V1 was replaced by v2 within 6 months of the initial release of Azure Maps about 4 years ago.

Also, worth noting that unlike map tiles, calls to the reverse geocoder do not get cached since the input coordinate will almost always be unique.

All of that said, if you are using v2 and would prefer the map to not update the screen reader if it can't find a label in the vector tiles (reduce accessibility), you can add the following option to the map options when loading the map:

enableAccessibilityLocationFallback: false

Now all of the above assumes that the reverse geocoding service is the source of your usage of the location insights category. You can verify this by doing the following:

  1. Go to your Azure Maps account in the Azure portal.
  2. Go to metrics, and set the Metric dropdown to usage.
  3. Then click on "Add filter", and set "Property" to "API Name" and "Values" to "SearchReverseAddress".
  4. Check to see this volume of usage matches the spike you are seeing. If it doesn't look at the other options in the "Values" dropdown and check to see if there are any other "Search" related values in there. If so, check the usage until you find which API the usage is coming from.

If you make the above change and continue to see usage for location insights, then it is likely that your subscription key is being used somewhere else with one of the location insights services. This could be another either another one of your apps, or someone else on your team if you have a shared subscription, or if your app is publicly available (code or hosted web app) someone could have grabbed your key and used it to call one of the other services. Using Azure Active Directory for the auth in production is generally recommended and can prevent someone from stealing your key.

Upvotes: 2

Related Questions