Search code examples
c#asp.net-coreazure-maps

AzureMaps - Spatial - Post Closest Point C# SDK


Is there any a method that can get the closest point between a base point and a given set of target points. I can only found a restful-api but no documentation about using the c# sdk.

Here's the link for the Spatial - Post Closest Point Link: https://learn.microsoft.com/en-us/rest/api/maps/spatial/post-closest-point?view=rest-maps-2023-06-01&tabs=HTTP

I only found this documentation Link: https://learn.microsoft.com/en-us/azure/azure-maps/how-to-dev-guide-csharp-sdk sample snippet

// Use Azure Maps subscription key authentication 
var subscriptionKey = Environment.GetEnvironmentVariable("SUBSCRIPTION_KEY") ?? string.Empty;
var credential = new AzureKeyCredential(subscriptionKey);
var client = new MapsSearchClient(credential); 

SearchAddressResult searchResult = client.SearchAddress(
    "1301 Alaskan Way, Seattle, WA 98101, US");

if (searchResult.Results.Count > 0) 
{
    SearchAddressResultItem result = searchResult.Results.First(); 
    Console.WriteLine($"The Coordinate: ({result.Position.Latitude:F4}, {result.Position.Longitude:F4})"); 
}

Solution

  • Calculating the nearest point from a set of points is often called "Nearest Neighbor" calculation.

    Calculating the distance between two points is a fairly straight forward calculation that can be done in code, the REST API is mainly for those who are working in low code scenarios (e.g. power apps). It would be much cheaper to do this calculation directly in your code.

    First off if you have a bunch of addresses stored somewhere that you will be searching through, make your they have a latitude/longitude value stored with them. If they don't have this information, geocode those addresses ahead of time and store the latitude/longitude values. If your addresses are stored in Azure, the Azure Maps terms of use allow you to store the latitude/longitude values from the geocoding/search services. This will be significantly faster and cheaper than geocoding every address every time you do the distance calculations. Ideally, the only time you should need to do on demand geocoding is if the end user is passing in an address for a search.

    If your addresses are stored in a SQL, Postgress, or Cosmos, they all support some level of spatial calculations natively, and doing the calulcation in there would be better than to retrieve all the addresses from the database and doing the calculation in your code. Here are some related resources:

    If your address information is in memory/code, and you can use the Haversine formula to calculate the distance. Here is some sample code on how to do this:

    class Location {
        public double Latitude;
        public double Longitude;
        public string Address;
    }
    
    public Location? GetNearestLocation(Location searchOrigin, Location[] locations) {
        if(searchOrigin == null || locations == null || locations.Length == 0) {
            return null;
        }
    
        //Start with the first location as the closest.
        Location closest = locations[0];
        double closestDistance = HaversineDistance(searchOrigin, locations[0]);
        
        for(int i = 1; i < locations.Length; i++) {
            double distance = HaversineDistance(searchOrigin, locations[i]);
            
            if(distance < closestDistance) {
                closest = locations[i];
                closestDistance = distance;
            }
        }
        
        return closest;
    }
    
    public static double HaversineDistance(Location location1, Location location2) {
        var R = 6372.8; // In kilometers
        var dLat = ToRadians(location2.Latitude - location1.Latitude);
        var dLon = ToRadians(location2.Longitude - location1.Longitude);
        var lat1 = ToRadians(location1.Latitude);
        var lat2 = ToRadians(location2.Latitude);
       
        var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Sin(dLon / 2) * Math.Sin(dLon / 2) * Math.Cos(lat1) * Math.Cos(lat2);
        var c = 2 * Math.Asin(Math.Sqrt(a));
        return R * 2 * Math.Asin(Math.Sqrt(a));
    }
    
    public static double ToRadians(double angle) {
        return Math.PI * angle / 180.0;
    }