Experiencing a long response time while plotting boundaries.

Nilesh Khonde 60 Reputation points
2024-09-03T13:14:25.43+00:00

Hi, I am trying to plot boundaries on a map using the Azure Maps API (GET https://atlas.microsoft.com/search/address/{format}?api-version=1.0&query={query}).

Please refer to this documentation:

https://video2.skills-academy.com/en-us/rest/api/maps/search/get-search-address?view=rest-maps-1.0&tabs=HTTP

I have taken sample code from the Azure Maps sample code repository:

https://samples.azuremaps.com/?search=boun&sample=search-for-boundaries

I made some changes to that code and am trying to plot different regions on the map, for example, countries. It is taking an unexpectedly long time to plot some regions, like the USA (15 seconds to plot boundaries), Canada(25 seconds), and Australia.

You can try selecting the entityType as "country" and searching for countries. I just want to know why it is unexpectedly taking so long to plot regions. Am I missing some parameters or configurations? Any suggestions would be greatly appreciated.

I am also trying to plot multiple countries or states, but it does not return results. For example, if I search for multiple locations like the USA, UK, India, and Japan, it is unable to plot the boundaries. In short, if I want to plot multiple boundaries on the map—regardless of whether they are countries, states, or postal codes—how can I perform this action?


<!DOCTYPE html>
<html lang="en">

<head>
    <title>Search for boundaries - Azure Maps Web SDK Samples</title>

    <meta charset="utf-8" />
    <link rel="shortcut icon" href="/favicon.ico" />

    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
    <meta name="description"
        content="This sample shows how to use the Services module for Azure Maps to search for locations that have boundaries and display them on the map. Azure Maps provides boundary data for administrative areas such as states, countries, cities, postal codes, and other boundaries such as industrial areas." />
    <meta name="keywords"
        content="Microsoft maps, map, gis, API, SDK, services, module, geolocation, search, geocode, geocoding, adminstrative boundaries, boundary, boundaries, polygon" />
    <meta name="author" content="Microsoft Azure Maps" />
    <meta name="version" content="1.0" />
    <meta name="screenshot" content="screenshot.jpg" />

    <!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
    <link href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.css" rel="stylesheet" />
    <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.js"></script>

    <!-- Add a reference to the Azure Maps Rest Helper JavaScript file. -->
    <script src="https://samples.azuremaps.com/lib/azure-maps/azure-maps-helper.min.js"></script>
    <script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>

    <script>
        var map, datasource, layer, popup, temp = [], poly, entityType;
        var coordinates = [
            [40.7128, -74.0060],
            [40.7128, -74.0060],
            [47.616929080838865, -122.3137219288834],
            [19.074262052473877, 73.00994617239397]
        ]
        // Your Azure Maps client id for accessing your Azure Maps account.
        var azureMapsClientId = 'e6b6ab59-eb5d-4d25-aa57-581135b927f0';

        // URL to your authentication service that retrieves an Microsoft Entra ID Token.
        var tokenServiceUrl = 'https://samples.azuremaps.com/api/GetAzureMapsToken';

        // URL for the Azure Maps Search Geocoder API.
        var geocodeUrl = 'https://{azMapsDomain}/search/address/json?api-version=1.0&query={query}&entityType={entityType}';
        // GET https://atlas.microsoft.com/search/address/{format}?api-version=1.0&query={query}

        // URL for the Azure Maps Search Polygon API.
        var polygonUrl = 'https://{azMapsDomain}/search/polygon/json?api-version=1.0&geometries={geometries}';

        // Function to retrieve an Azure Maps access token.


        function getMap() {
            // Initialize a map instance.
            map = new atlas.Map('myMap', {
                // style: 'grayscale_light',
                view: 'Auto',

                // Add authentication details for connecting to Azure Maps.
                authOptions: {
                    authType: 'subscriptionKey',
                    subscriptionKey: ''
                }
            });

            // Wait until the map resources are ready.
            map.events.add('ready', function () {
                removeUserContent(map)
                // Create a data source and add it to the map.
                datasource = new atlas.source.DataSource("MainDatasource");
                map.sources.add(datasource);

                // Add a layers for rendering the boundaries as polygons.
                layer = new atlas.layer.PolygonLayer(datasource, null, {
                    fillColor: '#5aff96'
                });
                //Create a line layer for greater control of rendering the outline of the polygon.
                var lineLayer = new atlas.layer.LineLayer(datasource, 'myLineLayer', {
                    strokeColor: '#0064c8',
                    strokeWidth: 1
                });
                map.layers.add([layer, lineLayer]);

                // Create a popup but leave it closed so we can update it and display it later.
                popup = new atlas.Popup();

                // Add a click event to the layer.
                map.events.add('click', layer, layerClicked);
            });
        }

        function search() {
            try {
                var query = document.getElementById('input').value;
                entityType = document.getElementById('regionalType').value;
                // Remove any previous results from the map.
                datasource.clear();
                document.getElementById('resultList').innerHTML = '';
                popup.close();
                document.getElementById('loadingIcon').style.display = '';
                test(query)
            } catch (error) {
                console.log(error.message);
            }
        }
        function test(query) {
            // Search for locations.
            var geocodeRequestUrl = geocodeUrl
                .replace('{query}', encodeURIComponent(query))
                .replace('{entityType}', entityType);

            processRequest(geocodeRequestUrl).then(geocode => {
                var r = geocode.results;
                var geomIds = [];
                var html = ['<h3>Results</h3><ol>'];

                // Loop through the results and extract the ones that have boundaries.
                for (var i = 0; i < r.length; i++) {
                    if (r[i].dataSources && r[i].dataSources.geometry && r[i].dataSources.geometry.id) {
                        // Store the boundary id.
                        geomIds.push(r[i].dataSources.geometry.id);

                        // Create a link to select and zoom into the boundary.
                        html.push(`<li><a href="javascript:void(0)" onclick="viewPolygon('${r[i].dataSources.geometry.id}');">${r[i].address.freeformAddress}</a><br/>${r[i].entityType}</li>`);
                    }
                }

                html.push('</ol>');

                if (geomIds.length > 0) {
                    // Filter the polygon layer to the first polygon.
                    // This Line Was Causing The to Show Only One Region at a time.
                    // layer.setOptions({ filter: ['==', ['get', 'providerID'], geomIds[0]] });
                    // layer.setOptions({ filter: ['==', ['get', 'providerID'], geomIds[1]] });

                    // Retrieve the boundary polygons. The response may be large, so allow a longer abort time.
                    var polygonRequestUrl = polygonUrl
                        .replace('{geometries}', geomIds.join());

                    processRequest(polygonRequestUrl).then(boundaries => {
                        html.push(`Response size: ${Math.round(JSON.stringify(boundaries).length / 1024 / 1024 * 100) / 100}MB`);

                        // Get the results in GeoJSON format and add it to the data source.
                        for (var j = 0; j < boundaries.additionalData.length; j++) {
                            var geojson = boundaries.additionalData[j].geometryData;
                            for (var f = 0; f < geojson.features.length; f++) {
                                geojson.features[f].properties.providerID = geojson.features[f].id;
                            }
                            datasource.add(geojson);
                        }

                        // Focus on the first polygon in the list.
                        // viewPolygon(boundaries.additionalData[0].geometryData.features[0].id);

                        // Display the list of boundaries.
                        document.getElementById('resultList').innerHTML = html.join('');

                        // Hide the loading icon.
                        document.getElementById('loadingIcon').style.display = 'none';
                    });
                } else {
                    alert('No boundary found.');

                    // Hide the loading icon.
                    document.getElementById('loadingIcon').style.display = 'none';
                }
            });
        }
        function viewPolygon(geomId) {
            popup.close();

            // Filter the polygon layer to only display the boundary with the specified geometry id.
            layer.setOptions({ filter: ['==', ['get', 'providerID'], geomId] });

            // Retrieve the boundary shape from the data source that has the required geometry id.
            var s = datasource.getShapes().find(x => x.getProperties().providerID === geomId);

            // Update the map camera so that it focuses on the geometry.
            map.setCamera({
                bounds: atlas.data.BoundingBox.fromData(s),
                padding: 40
            });
        }

        function layerClicked(e) {
            // Get the GeoJSON version of the feature.
            var f = e.shapes[0].toJson();

            // Calculate stats for the boundary.
            var numPos = 0, numPolygons = 0, i, j;

            if (f.geometry.type === 'Polygon') {
                numPolygons = 1;

                // Count the number of positions in the polygons.
                for (i = 0; i < f.geometry.coordinates.length; i++) {
                    numPos += f.geometry.coordinates[i].length;
                }
            } else {
                // Must by MultiPolygon

                // Count the number of polygons in the MultiPolygon
                numPolygons = f.geometry.coordinates.length;

                // Count the number of positions in all the polygons.
                for (i = 0; i < f.geometry.coordinates.length; i++) {
                    for (j = 0; j < f.geometry.coordinates[i].length; j++) {
                        numPos += f.geometry.coordinates[i][j].length;
                    }
                }
            }

            // Set the popup options.
            popup.setOptions({
                // Update the content of the popup.
                content: `<div style='padding:15px;'>Type: ${f.geometry.type}<br/># polygons: ${numPolygons.toLocaleString()}<br/># positions: ${numPos.toLocaleString()}</div>`,

                // Place the popup where the user clicked.
                position: e.position
            });

            // Open the popup.
            popup.open(map);
        }
    </script>
</head>

<body onload="getMap()">
    <div id="myMap" style="position:relative;width:100%;min-width:290px;height:600px;"></div>

    <div style="position:absolute;top:15px;left:15px;background-color:white;padding:10px;border-radius:10px;">
        <input type="text" id="input" value="Sydney, NSW" />
        <select id="regionalType">
            <option value="">Select Region Type</option>
            <option value="Country">Country</option>
            <option value="CountrySecondarySubdivision">Country Secondary Subdivision</option>
            <option value="CountrySubdivision">Country Subdivision</option>
            <option value="CountryTertiarySubdivision">Country Tertiary Subdivision</option>
            <option value="Municipality">Municipality</option>
            <option value="MunicipalitySubdivision">Municipality Subdivision</option>
            <option value="Neighbourhood">Neighbourhood</option>
            <option value="PostalCodeArea">Postal Code Area</option>
        </select>
        <input type="button" onClick="search()" value="Search" />
        <div id="resultList"></div>
    </div>

    <img id="loadingIcon" src="/images/loadingIcon.gif" title="Loading"
        style="position:absolute;left:calc(50% - 25px);top:250px;display:none;" />

    <fieldset style="width:calc(100% - 30px);min-width:290px;margin-top:10px;">
        <legend>Search for boundaries</legend>
        This sample shows how to use Azure Maps to search for locations that have boundaries and display them on the
        map.
        Azure Maps provides boundary data for administrative areas such as states, countries, cities, postal codes, and
        other boundaries such as industrial areas.
    </fieldset>
</body>

</html>


Azure Maps
Azure Maps
An Azure service that provides geospatial APIs to add maps, spatial analytics, and mobility solutions to apps.
700 questions
0 comments No comments
{count} votes

3 answers

Sort by: Most helpful
  1. rbrundritt 17,491 Reputation points Microsoft Employee
    2024-09-03T17:31:26.84+00:00

    The issue you are experience is due to that service only returning the high-resolution version of the boundary. Large countries, end up having a ton of points and have a very large file size (Canada is over 32MB in size). So it takes a while for it to download, then it takes a decent amount of processing by the map to get it into a format that can be displayed on the map with decent performance (once it is displayed on the map the first time you likely find you can pan and zoom fairly easily, many older map controls simply wouldn't be able to load such large complex polygons).

    There are two solutions to this issue:

    1. If you want to regularly display the same boundaries, check to see if you can find a set of open data for them. Hosting boundary data with your app will be a lot cheaper longer term than calling the Azure Maps REST service to retrieve it. The Azure Maps REST boundary service(s) are a good option when you don't know what boundaries the user will want, when the boundaries may need to align with geopolitical sensitive situations (disputed borders and you may have users in one of these countries and need the "right" polygon to be displayed), or you need to access a boundary type that may not be available as open data (postal codes for example). I highlight this option first as I've often found developer gravitate towards these REST services first, but this option makes a lot more sense financially for a lot of apps.
    2. Upgrade to the latest boundary service in Azure Maps: https://video2.skills-academy.com/en-us/rest/api/maps/search/get-polygon?view=rest-maps-2024-04-01&tabs=HTTP This service recently moved out of preview and into general availability state. It functions differently than the old boundary service, but has several benefits, such as the option to retrieve different resolutions of the boundaries, the response being a GeoJSON object you can pass right into a data source in the Web SDK without any additional processing needed, and it can also be used without the geocoding service as you only need a coordinate that intersects the boundary you want (may not apply to your scenario). I submitted an update to that sample here: https://github.com/Azure-Samples/AzureMapsCodeSamples/blob/main/Samples/Services%20Module/Search%20for%20boundaries/Search%20for%20boundaries.html

    At first I did have a third option which consisted of reducing the resolution of the returned polygon using a simple algorithm, but you would still have a long download time, and there wouldn't be much time saved on the rendering side since the map would reduce the resolution of the polygon anyways to make the zoom level of the map.

    1 person found this answer helpful.
    0 comments No comments

  2. Nilesh Khonde 60 Reputation points
    2024-09-04T14:16:37.23+00:00

    Hi @rbrundritt ,

    Thank you for your response. As per your reply, we cannot store the boundary data in the application because we need to search multiple times and across different locations. The new API you mentioned in your response and sample code resolves my response time issue. However, in the old API, I used to use entityType to restrict my search to specific levels, such as country, postal code, and county.

    In the new API, how can I restrict the search to a particular postal code and ensure it is precise? I've added multiple parameters to the query, like country, state, and the desired postal code region address, but it still returns multiple results instead of just one postal code. For example:

    
            var geocodeUrl = 'https://{azMapsDomain}/geocode?api-version=2023-06-01&view=Auto&countryRegion={countryRegion}&postalCode={postalCode}&adminDistrict={adminDistrict}';
    
    var geocodeRequestUrl = geocodeUrl
                    .replace('{view}',"Auto").replace('{countryRegion}',"USA").replace('{adminDistrict}',"California").replace('{postalCode}',"90251");
    
    
    

    It returns multiple results instead of just one.

    1. My second question is: Can I use this URL (geocode URL) directly, replace the values I want, and then send it to the API? I was trying to use the entire API query this way.
            // var geocodeUrl='GET https://atlas.microsoft.com/geocode?api-version=2023-06-01&view={view}&addressLine={addressLine}&countryRegion={countryRegion}&adminDistrict={adminDistrict}&adminDistrict2={adminDistrict2}&adminDistrict3={adminDistrict3}&locality={locality}&postalCode={postalCode}'
    
    
    

    But it was giving me an error in the network response.

    1. Before your response, I was using the old sample code API, and the getPolygon method used to return multiple polygon collections, which I used to filter the markers in my coordinates collection. However, the new API's getPolygon method returns a direct JSON object, which is a geometry collection. I'm finding it difficult to filter this with Turf. I am sharing both different responses. The geometry collection is the new API result I am getting. Screenshot (68) Screenshot (69) I am Using
          turf.booleanPointInPolygon
      

    And here is my sample code. I have created a function to generate a MultiPolygon from the data, but I want to use Turf instead of my current code.

    Note I Have Used Hardcorded data into my code

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <title>Search for boundaries - Azure Maps Web SDK Samples</title>
    
        <meta charset="utf-8" />
        <link rel="shortcut icon" href="/favicon.ico" />
    
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <meta name="description"
            content="This sample shows how to use the Services module for Azure Maps to search for locations that have boundaries and display them on the map. Azure Maps provides boundary data for administrative areas such as states, countries, cities, postal codes, and other boundaries such as industrial areas." />
        <meta name="keywords"
            content="Microsoft maps, map, gis, API, SDK, services, module, geolocation, search, geocode, geocoding, adminstrative boundaries, boundary, boundaries, polygon" />
        <meta name="author" content="Microsoft Azure Maps" />
        <meta name="version" content="1.0" />
        <meta name="screenshot" content="screenshot.jpg" />
    
        <!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
        <link href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.css" rel="stylesheet" />
        <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/3/atlas.min.js"></script>
        <script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
    
        <!-- Add a reference to the Azure Maps Rest Helper JavaScript file. -->
        <script src="https://samples.azuremaps.com/lib/azure-maps/azure-maps-helper.min.js"></script>
    
        <script>
            var displayedLocation;
            var map, datasource, layer, popup, temp = [], poly, primaryMarkerLayer, hoverLayer, entityType;
            var coordinates = [
                [40.7128, -74.0060],
                [34.0522, -118.2437],
                [41.8781, -87.6298],
                [29.7604, -95.3698],
                [33.4484, -112.0740],
                [39.9526, -75.1652],
                [37.7749, -122.4194],
                [32.7767, -96.7970],
                [30.2672, -97.7431],
                [35.2271, -80.8431],
                [49.6062, -122.3321],
                [25.7617, -80.1918],
                [42.3601, -71.0589],
                [38.9072, -77.0369],
                [36.7783, -119.4179],
                [33.7490, -84.3880],
                [34.0522, -118.2437],
                [40.7128, -74.0060],
                [37.7749, -122.4194],
                [41.8781, -87.6298],
                [32.7157, -117.1611],
                [39.2904, -76.6122],
                [29.7604, -95.3698],
                [33.4484, -112.0740],
                [35.2271, -80.8431],
                [30.2672, -97.7431],
                [34.0522, -118.2437],
                [42.3601, -71.0589],
                [38.9072, -77.0369],
                [36.7783, -119.4179],
                [33.7490, -84.3880],
                [40.7128, -74.0060],
                [37.7749, -122.4194],
                [41.8781, -87.6298],
                [32.7157, -117.1611],
                [39.2904, -76.6122],
                [29.7604, -95.3698],
                [33.4484, -112.0740],
                [35.2271, -80.8431],
                [30.2672, -97.7431],
                [34.0522, -118.2437],
                [42.3601, -71.0589],
                [38.9072, -77.0369],
                [36.7783, -119.4179],
                [33.7490, -84.3880],
                [40.7128, -74.0060],
                [37.7749, -122.4194],
                [41.8781, -87.6298],
                [32.7157, -117.1611],
                [39.2904, -76.6122],
                [29.7604, -95.3698],
                [33.4484, -112.0740],
                [35.2271, -80.8431],
                [30.2672, -97.7431],
                [34.0522, -118.2437],
                [42.3601, -71.0589],
                [38.9072, -77.0369],
                [36.7783, -119.4179],
                [33.7490, -84.3880],
                [40.7128, -74.0060],
                [19.079047958462464, 73.02419406695735],
                [19.200018343066304, 72.81517632316684],
                [19.02502055135496, 73.10387092438363]
            ]
            // Your Azure Maps client id for accessing your Azure Maps account.
            var azureMapsClientId = 'e6b6ab59-eb5d-4d25-aa57-581135b927f0';
    
            // URL to your authentication service that retrieves an Microsoft Entra ID Token.
            var tokenServiceUrl = 'https://samples.azuremaps.com/api/GetAzureMapsToken';
    
            // URL for the Azure Maps Search Geocoder API.
            var geocodeUrl = 'https://{azMapsDomain}/geocode?api-version=2023-06-01&view=Auto&countryRegion={countryRegion}&postalCode={postalCode}&adminDistrict={adminDistrict}';
            // var geocodeUrl='GET https://atlas.microsoft.com/geocode?api-version=2023-06-01&view={view}&addressLine={addressLine}&countryRegion={countryRegion}&adminDistrict={adminDistrict}&adminDistrict2={adminDistrict2}&adminDistrict3={adminDistrict3}&locality={locality}&postalCode={postalCode}'
    
            // URL for the Azure Maps Search Polygon API.
            var polygonUrl = 'https://{azMapsDomain}/search/polygon?api-version=2023-06-01&coordinates={coordinates}&view=Auto&resultType={resultType}&resolution={resolution}';
    
            //A list of entity types that the boundary service supports.
            var boundaryEntityTypes = ['adminDistrict', 'adminDistrict2', 'countryRegion', 'locality', 'neighborhood', 'postalCode', 'postalCode2', 'postalCode3', 'postalCode4'];
    
            //A simple local-cache to store information about boundaries for this sample.
            // Format: { id: { position: [], entityType: '', bbox: [] } } where the id is a pipe delimited formatted address and entity type since many places could have the same formatted address.
            //Not caching boundary polygon for this sample as we want to be able to switch between resolutions.
            var boundaryCache = {}, entityType;
    
            // Function to retrieve an Azure Maps access token.
            
    
    
            function getMap() {
                // Initialize a map instance.
                // Initialize a map instance.
                map = new atlas.Map('myMap', {
                    // style: 'grayscale_light',
                    view: 'Auto',
    
                    // Add authentication details for connecting to Azure Maps.
                    authOptions: {
                        // Use Microsoft Entra ID authentication.
                        // authType: 'anonymous',
                        // clientId: azureMapsClientId,
                        // getToken: getToken
    
                        // Alternatively, use an Azure Maps key.
                        // Get an Azure Maps key at https://azure.com/maps.
                        // NOTE: The primary key should be used as the key.
                        authType: 'subscriptionKey',
                        subscriptionKey: ''
    
                    }
                });
    
                // Wait until the map resources are ready.
                map.events.add('ready', function () {
                    // Create a data source and add it to the map.
                    datasource = new atlas.source.DataSource();
                    map.sources.add(datasource);
    
                    // Add a layers for rendering the boundaries as polygons.
                    layer = new atlas.layer.PolygonLayer(datasource, null, {
                        fillColor: 'hotpink'
                    });
                    map.layers.add(layer, 'labels');
                    plotMarkersonMap(coordinates)
                    // Create a popup but leave it closed so we can update it and display it later.
                    popup = new atlas.Popup();
    
                    // Add a click event to the layer.
                    map.events.add('click', layer, layerClicked);
                });
            }
    
    
            function search() {
                var query = document.getElementById('input').value;
                entityType = document.getElementById('regionalType').value;
                // Remove any previous results from the map.
                datasource.clear();
                document.getElementById('resultList').innerHTML = '';
                popup.close();
    
                // Show the loading icon.
                document.getElementById('loadingIcon').style.display = '';
    
                var query = "Texas,Florida,California,Alaska"
                query = query.split(",")
                // query.map(function (item) {
                test()
                // })
    
    
            };
            function test() {
                // Search for locatoins.
                var geocodeRequestUrl = geocodeUrl
                    .replace('{view}',"Auto").replace('{countryRegion}',"USA").replace('{adminDistrict}',"California").replace('{postalCode}',"90251");
                    // .replace('{view}', "Auto").replace('{countryRegion}', "USA");
    
    
                processRequest(geocodeRequestUrl).then(fc => {
                    var r = fc.features;
                    var html = ['<h3>Results</h3><ol>'];
                    var hasBoundaries = false;
                    var firstResult;
    
                    // Loop through the results and extract the ones that might have boundaries.
                    for (var i = 0; i < r.length; i++) {
                        var t = r[i].properties.type;
    
                        //Convert the type property to a boundary Entity type value as it seems these values differ between services.
                        switch (t) {
                            case 'CountryRegion':
                                t = 'countryRegion';
                                break;
                            case 'AdminDivision1':
                                t = 'adminDistrict';
                                break;
                            case 'AdminDivision2':
                                t = 'adminDistrict2';
                                break;
                            case 'Postcode1':
                                t = 'postalCode';
                                break;
                            case 'Postcode2':
                                t = 'postalCode2';
                                break;
                            case 'Postcode3':
                                t = 'postalCode3';
                                break;
                            case 'Postcode4':
                                t = 'postalCode4';
                                break;
                            case 'PopulatedPlace':
                                t = 'locality';
                                break;
                            case 'Neighborhood':
                                t = 'neighborhood';
                                break;
                        }
    
    
                        //Check to see if the search result could have a boundary.
                        if (boundaryEntityTypes.indexOf(t) != -1) {
                            var id = `${r[i].properties.address.formattedAddress}|${t}`;
    
                            //Cache some basic information about the location.
                            boundaryCache[id] = {
                                position: r[i].geometry.coordinates,
                                entityType: t
                            };
    
                            if (!firstResult) {
                                firstResult = id;
                            }
    
                            // Create a link to select and zoom into the boundary.
                            html.push(`<li><a href="javascript:void(0)" onclick="getPolygon('${id}');">${r[i].properties.address.formattedAddress}</a> (${t}, Confidence: ${r[i].properties.confidence})</li>`);
    
                            hasBoundaries = true;
                        }
                    }
    
                    html.push('</ol>');
    
                    if (hasBoundaries) {
                        // Focus on the first polygon in the list.
                        getPolygon(firstResult);
    
                        // Display the list of results.
                        document.getElementById('resultList').innerHTML = html.join('');
    
                        // Hide the loading icon.
                        document.getElementById('loadingIcon').style.display = 'none';
                    } else {
                        document.getElementById('resultList').innerHTML = 'No boundary found.';
    
                        // Hide the loading icon.
                        document.getElementById('loadingIcon').style.display = 'none';
                    }
                });
            }
            function plotLayersonMap() {
                iconPromises = [
                    map.imageSprite.add('marker_icon', `<svg width="26" height="28" viewBox="-4 0 36 36" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd">
                        <path d="M14 0c7.732 0 14 5.641 14 12.6C28 23.963 14 36 14 36S0 24.064 0 12.6C0 5.641 6.268 0 14 0Z" fill="#fff" stroke="#000" stroke-width="1"/><circle fill="#FF474C" fill-rule="nonzero" cx="14" cy="14" r="10" /></g>
                    </svg>`)];
                Promise.all(iconPromises).then(function () {
                    //Check If Main DataSource is already Added or not loaded then add it
                    if (!map.sources.isSourceLoaded("MainDatasource")) { //??
    
                        // Create a SymbolLayer with filtering based on zoom level and entity type.
                        primaryMarkerLayer = new atlas.layer.SymbolLayer(datasource, "PrimaryMarkerLayer", {
                            iconOptions: {
                                image: 'marker_icon',
                                options: true,
                                allowOverlap: true,
                                ignorePlacement: true,
                            },
                            textOptions: {
                                textField: ['get', ''],
                                anchor: "top", // Show the label on top of the pin.
                                color: ['get', 'black'], // Set the color of the text.
                                options: true,
                                allowOverlap: true,
                                ignorePlacement: true,
                                // offset: [0, -5], // Adjust offset to move text above the pin.
                                haloColor: 'white', // Add halo effect for better visibility.
                                haloWidth: 9, // Set halo width.
                            },
    
                        });
    
                        hoverLayer = new atlas.layer.SymbolLayer(datasource, "HoverLayer", {
                            iconOptions: {
                                image: 'none',//Hide the icon image.
                                allowOverlap: true,
                                ignorePlacement: true,
                            },
                            textOptions: {
                                textField: ['get', ''], //Alternatively replace with 'label' property name.
                                anchor: "top", //Show the label below the pin.
                                color: ['get', 'black'], //Set the color of the text. Alternatively pass in a color value here if the color will always be one color.
                                allowOverlap: true,
                                ignorePlacement: true,
                            },
                            filter: ['==', ['id'], ''] //Default filter to an ID of empty string. This should never happen, so this layer won't render anything at this time.
                        });
                        map.events.add('mousemove', primaryMarkerLayer, function (e) {
                            hoverLayer.setOptions({ filter: ['==', ['get', '_azureMapsShapeId'], e.shapes[0].getId()] });
                        });
                        // add touch events 
                        map.events.add('mouseleave', primaryMarkerLayer, function (e) {
                            hoverLayer.setOptions({ filter: ['==', ['get', '_azureMapsShapeId'], ''] });
                        });
    
                        map.layers.add([primaryMarkerLayer, hoverLayer]);
                        datasource.clear();
                        datasource.add(temp)
    
                    }
                });
    
            }
    
    
            function plotMarkersonMap(longlatColl) {
    
                longlatColl.forEach(function (data, index) {
                    let markers = new atlas.Shape(new atlas.data.Point([parseFloat(data[1]), parseFloat(data[0])]), undefined, {
                        color: 'DodgerBlue',
                        label: "test",
                        title: "test",
                    });
                    temp.push(markers);
                })
                plotLayersonMap()
            }
          
            function getPolygon(id) {
                popup.close();
    
                //Make sure we have information in the cache for the provided ID.
                if (boundaryCache[id]) {
                    displayedLocation = id;
    
                    var b = boundaryCache[id];
    
                    //Get the selected resolution type.
                    var resolution = document.getElementById('resolutionSelector').value;
    
                    //Retrieve the boundary from Azure Maps.
    
                    // Show the loading icon.
                    document.getElementById('loadingIcon').style.display = '';
    
                    // Retrieve the boudary polygons. The response may be large, so allow a longer abort time.
                    var polygonRequestUrl = polygonUrl
                        .replace('{coordinates}', b.position.join(','))
                        .replace('{resultType}', b.entityType)
                        .replace('{resolution}', resolution);
    
                    processRequest(polygonRequestUrl).then(f => {
                        console.log(`Response size for '${id}' boundary: ${Math.round(JSON.stringify(f).length / 1024 / 1024 * 100) / 100}MB`);
                        let test = [];
                        //Cache the boundary.
                        b.boundary = f;
                         poly = geometryCollectionToMultiPolygon(f.geometry.geometries);
                        datasource.add(f);
    
    
                        //Caclaulte and cache the bounding box of the boundary.
                        b.bbox = atlas.data.BoundingBox.fromData(f);
    
                        //Update the map camera so that it focuses on the geometry.
                        map.setCamera({ bounds: b.bbox, padding: 40 });
    
                        // Hide the loading icon.
                        document.getElementById('loadingIcon').style.display = 'none';
                    }, e => {
                        //An error occurred, clear the data source.
                        alert('Unable to retrieve boundary for this location.');
    
                        // datasource.clear();
    
                        // Hide the loading icon.
                        document.getElementById('loadingIcon').style.display = 'none';
                    });
                }
            }
            function geometryCollectionToMultiPolygon(geometryCollection) {
                const polygons = geometryCollection.filter(geometry => geometry.type === 'Polygon');
    
                const multiPolygon = {
                    "type": "MultiPolygon",
                    "coordinates": polygons.map(polygon => polygon.coordinates)
                };
    
                return multiPolygon;
            }
            function reloadBoundary() {
                if (displayedLocation) {
                    getPolygon(displayedLocation);
                }
            }
    
            function layerClicked(e) {
                // Get the GeoJSON version of the feature.
                var f = e.shapes[0].toJson();
    
                // Calculate stats for the boundary.
                var numPos = 0, numPolygons = 0, i, j;
    
                if (f.geometry.type === 'Polygon') {
                    numPolygons = 1;
    
                    // Count the number of positions in the polygons.
                    for (i = 0; i < f.geometry.coordinates.length; i++) {
                        numPos += f.geometry.coordinates[i].length;
                    }
                } else {
                    // Must by MultiPolygon
    
                    // Count the number of polygons in the MultiPolygon
                    numPolygons = f.geometry.coordinates.length;
    
                    // Count the number of positions in all the polygons.
                    for (i = 0; i < f.geometry.coordinates.length; i++) {
                        for (j = 0; j < f.geometry.coordinates[i].length; j++) {
                            numPos += f.geometry.coordinates[i][j].length;
                        }
                    }
                }
    
                // Set the popup options.
                popup.setOptions({
                    // Update the content of the popup.
                    content: `<div style='padding:15px;'>Type: ${f.geometry.type}<br/># polygons: ${numPolygons.toLocaleString()}<br/># positions: ${numPos.toLocaleString()}</div>`,
    
                    // Place the popup where the user clicked.
                    position: e.position
                });
    
                // Open the popup.
                popup.open(map);
            }
            function filter() {
                let functionName = "filter"
                let filterrecords = [];
                let isWithinShape = false;
                try {
                    temp.map(function (data, index) {
                        isWithinShape = turf.booleanPointInPolygon(data.data.geometry.coordinates, poly)
                        if (isWithinShape) {
                            filterrecords.push(data);
                        }
                    })
    
                } catch (error) {
                    console.log(error.message + "" + functionName)
    
                }
            }
        </script>
    </head>
    
    <body onload="getMap()">
        <div id="myMap" style="position:relative;width:100%;min-width:290px;height:600px;"></div>
    
        <div style="position:absolute;top:15px;left:15px;background-color:white;padding:10px;border-radius:10px;">
            <input type="text" id="input" value="London" />
            <input type="button" onClick="search()" value="Search" />
            <select id="regionalType">
                <option value="">Select Region Type</option>
                <option value="Country">Country</option>
                <option value="CountrySecondarySubdivision">Country Secondary Subdivision</option>
                <option value="CountrySubdivision">Country Subdivision</option>
                <option value="CountryTertiarySubdivision">Country Tertiary Subdivision</option>
                <option value="Municipality">Municipality</option>
                <option value="MunicipalitySubdivision">Municipality Subdivision</option>
                <option value="Neighbourhood">Neighbourhood</option>
                <option value="PostalCodeArea">Postal Code Area</option>
            </select>
            Resolution:
            <select id="resolutionSelector" onchange="reloadBoundary()">
                <option value="small">Small</option>
                <option value="medium" selected>Medium</option>
                <option value="large">Large</option>
                <option value="huge">Huge</option>
            </select>
            <input type="button" onClick="filter()" value="Filter" />
    
            <div id="resultList"></div>
        </div>
    
        <img id="loadingIcon" src="/images/loadingIcon.gif" title="Loading"
            style="position:absolute;left:calc(50% - 25px);top:250px;display:none;" />
    
        <fieldset style="width:calc(100% - 30px);min-width:290px;margin-top:10px;">
            <legend>Search for boundaries</legend>
            This sample shows how to use Azure Maps to search for locations that have boundaries and display them on the
            map.
            Azure Maps provides boundary data for administrative areas such as states, countries, cities, postal codes, and
            other boundaries such as industrial areas.
        </fieldset>
    </body>
    
    </html>
    
    

  3. Nilesh Khonde 60 Reputation points
    2024-09-05T13:33:20.84+00:00

    I have some more questions. Suppose I have to search for the USA, Japan, India, and China. I want to plot all these regions on the map at the same time, but when I pass all the countries in the query, only one or two countries are being plotted, and the rest are not. So, what I did was create a collection of countries and send them one by one to the API. However, I am concerned that this will definitely increase my transactions. But in Bing Maps, we used to use

      Microsoft.Maps.SpatialDataService.GeoDataAPIManager.getBoundary
    
    
    

    In Bing Maps, we used to send a collection of countries, and it would iterate through them internally. Am I missing something while using the API, or does it only plot one result at a time?

    I'm also a bit confused about whether I should directly send all my searches as a single query or follow the approach of sending them one by one, as in the code examples above.

    does new api supports multiple query approach or single query approach.

       var geocodeRequestUrl = geocodeUrl
                    .replace('{view}',"Auto").replace('{countryRegion}',"USA").replace('{adminDistrict}',"California").replace('{postalCode}',"90251");
    
    
    

    or just send the same address as a query.

    1. Single Query Approach: If the API supports bulk location plotting (like Bing Maps used to), you should be able to pass all countries in a single request, and the API will handle the iteration and plotting internally.
    2. Multiple Requests Approach: If the API doesn’t support bulk plotting, sending locations one by one will increase the number of transactions. This method could be inefficient if you have a large number of countries or locations to plot.

    In my use case, I need to plot the exact result when the user provides any address.

    For example, when a user gives a city name and selects the USA as the country, it should plot only New York City, not the entire state of New York. I hope you understand.In old api

    GET https://atlas.microsoft.com/search/address/{format}?api-version=1.0&query={query}&typeahead={typeahead}&limit={limit}&ofs={ofs}&countrySet={countrySet}&lat={lat}&lon={lon}&radius={radius}&topLeft={topLeft}&btmRight={btmRight}&language={language}&extendedPostalCodesFor={extendedPostalCodesFor}&entityType={entityType}&view={view}
    
    
    

    We used to provide the entityType to restrict the search to the city or state level.

    Screenshot (70)


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.