C# REST SDK Developers Guide

The Azure Maps C# SDK supports functionality available in the Azure Maps Rest API, like searching for an address, routing between different coordinates, and getting the geo-location of a specific IP address. This article introduces the C# REST SDK with examples to help you get started building location-aware applications in C# that incorporate the power of Azure Maps.

Note

Azure Maps C# SDK supports any .NET version that is compatible with .NET standard 2.0. For an interactive table, see .NET Standard versions.

Prerequisites

Tip

You can create an Azure Maps account programmatically, Here's an example using the Azure CLI:

az maps account create --kind "Gen2" --account-name "myMapAccountName" --resource-group "<resource group>" --sku "G2"

Create a .NET project

The following PowerShell code snippet demonstrates how to use PowerShell to create a console program MapsDemo with .NET 7.0. You can use any .NET standard 2.0-compatible version as the framework.

dotnet new console -lang C# -n MapsDemo -f net7.0 
cd MapsDemo 

Install required packages

To use Azure Maps C# SDK, we need to install the required packages. Each of the Azure Maps services including search, routing, rendering and geolocation are each in their own package. Since the Azure Maps C# SDK is in public preview, you need to add the --prerelease flag:

dotnet add package Azure.Maps.Rendering --prerelease
dotnet add package Azure.Maps.Routing --prerelease
dotnet add package Azure.Maps.Search --prerelease
dotnet add package Azure.Maps.Geolocation --prerelease

Azure Maps services

Service Name  NuGet package  Samples 
Search Azure.Maps.Search search samples
Routing Azure.Maps.Routing  routing samples
Rendering Azure.Maps.Rendering rendering sample
Geolocation Azure.Maps.Geolocation geolocation sample

Create and authenticate a MapsSearchClient

The client object used to access the Azure Maps Search APIs require either an AzureKeyCredential object to authenticate when using an Azure Maps subscription key or a TokenCredential object with the Azure Maps client ID when authenticating using Azure Active Directory (Azure AD). For more information on authentication, see Authentication with Azure Maps.

Using an Azure AD credential

You can authenticate with Azure AD using the Azure Identity library. To use the DefaultAzureCredential provider, you'll need to install the Azure Identity client library for .NET:

dotnet add package Azure.Identity 

You'll need to register the new Azure AD application and grant access to Azure Maps by assigning the required role to your service principal. For more information, see Host a daemon on non-Azure resources. During this process you'll get an Application (client) ID, a Directory (tenant) ID, and a client secret. Copy these values and store them in a secure place. You'll need them in the following steps.

Set the values of the Application (client) ID, Directory (tenant) ID, and client secret of your Azure AD application, and the map resource’s client ID as environment variables:

Environment Variable Description
AZURE_CLIENT_ID Application (client) ID in your registered application
AZURE_CLIENT_SECRET The value of the client secret in your registered application
AZURE_TENANT_ID Directory (tenant) ID in your registered application
MAPS_CLIENT_ID The client ID in your Azure Map resource

Now you can create environment variables in PowerShell to store these values:

$Env:AZURE_CLIENT_ID="Application (client) ID"
$Env:AZURE_CLIENT_SECRET="your client secret"
$Env:AZURE_TENANT_ID="your Directory (tenant) ID"
$Env:MAPS_CLIENT_ID="your Azure Maps client ID"

After setting up the environment variables, you can use them in your program to instantiate the AzureMapsSearch client:

using System;
using Azure.Identity; 
using Azure.Maps.Search; 

var credential = new DefaultAzureCredential(); 
var clientId = Environment.GetEnvironmentVariable("MAPS_CLIENT_ID"); 
var client = new MapsSearchClient(credential, clientId); 

Important

The other environment variables created above, while not used in the code sample here, are required by DefaultAzureCredential(). If you do not set these environment variables correctly, using the same naming conventions, you will get run-time errors. For example, if your AZURE_CLIENT_ID is missing or invalid you will get an InvalidAuthenticationTokenTenant error.

Using a subscription key credential

You can authenticate with your Azure Maps subscription key. Your subscription key can be found in the Authentication section in the Azure Maps account as shown in the following screenshot:

A screenshot showing the subscription key in the Authentication section of an Azure Maps account.

Now you can create environment variables in PowerShell to store the subscription key:

$Env:SUBSCRIPTION_KEY="your subscription key"

Once your environment variable is created, you can access it in your code:

using System;
using Azure; 
using Azure.Maps.Search; 

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

Fuzzy search an entity

The following code snippet demonstrates how, in a simple console application, to import the Azure.Maps.Search package and perform a fuzzy search on“Starbucks” near Seattle. In Program.cs:

using System;
using Azure; 
using Azure.Core.GeoJson; 
using Azure.Maps.Search; 
using Azure.Maps.Search.Models; 

// 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.FuzzySearch( 
    "Starbucks", new FuzzySearchOptions 
    { 
        Coordinates = new GeoPosition(-122.34255, 47.61010), 
        Language = SearchLanguage.EnglishUsa 
    }); 


// Print the search results 
foreach (var result in searchResult.Results) 
{ 
    Console.WriteLine($""" 
        * {result.Address.StreetNumber} {result.Address.StreetName} 
          {result.Address.Municipality} {result.Address.CountryCode} {result.Address.PostalCode} 
          Coordinate: ({result.Position.Latitude:F4}, {result.Position.Longitude:F4}) 
        """); 
} 

The above code snippet demonstrates how to create a MapsSearchClient object using your Azure credentials, then uses its FuzzySearch method, passing in the point of interest (POI) name "Starbucks" and coordinates GeoPosition(-122.31, 47.61). This all gets wrapped up by the SDK and sent to the Azure Maps REST endpoints. When the search results are returned, they're written out to the screen using Console.WriteLine.

The following libraries are used:

  1. Azure.Maps.Search is required for the MapsSearchClient class.
  2. Azure.Maps.Search.Models is required for the SearchAddressResult class.
  3. Azure.Core.GeoJson is required for the GeoPosition struct used by the FuzzySearchOptions class.

To run your application, go to the project folder and execute dotnet run in PowerShell:

dotnet run 

You should see a list of Starbucks address and coordinate results:

* 1912 Pike Place 
  Seattle US 98101 
  Coordinate: 47.61016, -122.34248 
* 2118 Westlake Avenue 
  Seattle US 98121 
  Coordinate: 47.61731, -122.33782 
* 2601 Elliott Avenue 
  Seattle US 98121 
  Coordinate: 47.61426, -122.35261 
* 1730 Howell Street 
  Seattle US 98101 
  Coordinate: 47.61716, -122.3298 
* 220 1st Avenue South 
  Seattle US 98104 
  Coordinate: 47.60027, -122.3338 
* 400 Occidental Avenue South 
  Seattle US 98104 
  Coordinate: 47.5991, -122.33278 
* 1600 East Olive Way 
  Seattle US 98102 
  Coordinate: 47.61948, -122.32505 
* 500 Mercer Street 
  Seattle US 98109 
  Coordinate: 47.62501, -122.34687 
* 505 5Th Ave S 
  Seattle US 98104 
  Coordinate: 47.59768, -122.32849 
* 425 Queen Anne Avenue North 
  Seattle US 98109 
  Coordinate: 47.62301, -122.3571 

Search an address

Call the SearchAddress method to get the coordinate of an address. Modify the Main program from the sample as follows:

// 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})"); 
}

Results returned by the SearchAddress method are ordered by confidence score and because searchResult.Results.First() is used, only the coordinates of the first result will be returned.

Azure Maps Search also provides some batch query methods. These methods will return Long Running Operations (LRO) objects. The requests might not return all the results immediately, so users can choose to wait until completion or query the result periodically. The example below demonstrates how to call the batched reverse search methods:

var queries = new List<ReverseSearchAddressQuery>() 
{ 
    new ReverseSearchAddressQuery(new ReverseSearchOptions() 
    { 
        Coordinates = new GeoPosition(2.294911, 48.858561) 
    }), 
    new ReverseSearchAddressQuery(new ReverseSearchOptions() 
    { 
        Coordinates = new GeoPosition(-122.127896, 47.639765), 
        RadiusInMeters = 5000 
    }) 
};

In the above example, two queries are passed to the batched reverse search request. To get the LRO results, you have few options. The first option is to pass WaitUntil.Completed to the method. The request will wait until all requests are finished and return the results:

// Wait until the LRO return batch results 
Response<ReverseSearchAddressBatchOperation> waitUntilCompletedResults = client.ReverseSearchAddressBatch(WaitUntil.Completed, queries); 

// Print the result addresses 
printReverseBatchAddresses(waitUntilCompletedResults.Value); 

Another option is to pass WaitUntil.Started. The request will return immediately, and you'll need to manually poll the results:

// Manual polling the batch results 
Response<ReverseSearchAddressBatchOperation> manualPollingOperation = client.ReverseSearchAddressBatch(WaitUntil.Started, queries);

// Keep polling until we get the results
while (true)
{
    manualPollingOperation.Value.UpdateStatus();
    if (manualPollingOperation.Value.HasCompleted) break;
    Task.Delay(1000);
}
printReverseBatchAddresses(manualPollingOperation);

We can also call WaitUntilCompletion() to explicitly wait for the result:

Response<ReverseSearchAddressBatchOperation> manualPollingResult = manualPollingResults.WaitUntilCompleted();

printReverseBatchAddresses(manualPollingResult.Value);

The third method requires the operation ID to get the results, which will be cached on the server side for 14 days:

  ReverseSearchAddressBatchOperation longRunningOperation = client.ReverseSearchAddressBatch(WaitUntil.Started, queries);

  // Get batch results by ID 
  string operationId = longRunningOperation.Value.Id;

  // After the LRO completes, create a new operation
  // to get the results from the server
  ReverseSearchAddressBatchOperation newOperation = new ReverseSearchAddressBatchOperation(client, operationId);
  Response<ReverseSearchAddressBatchOperation> newOperationResult = newOperation.WaitForCompletion();

printReverseBatchAddresses(newOperationResult);

The complete code for reverse address batch search with operation ID:

using system;
using Azure;
using Azure.Core.GeoJson;
using Azure.Maps.Search;
using Azure.Maps.Search.Models;

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

var queries = new List<ReverseSearchAddressQuery>()
{
    new ReverseSearchAddressQuery(new ReverseSearchOptions()
    {
        Coordinates = new GeoPosition(2.294911, 48.858561)
    }),
    new ReverseSearchAddressQuery(new ReverseSearchOptions()
    {
        Coordinates = new GeoPosition(-122.127896, 47.639765),
        RadiusInMeters = 5000
    })
};

// Manual polling the batch results
ReverseSearchAddressBatchOperation longRunningOperation = client.ReverseSearchAddressBatch(WaitUntil.Started, queries);

// Get batch results by ID
string operationId = longRunningOperation.Id;

// A few days later, create a new operation and get the result from server
ReverseSearchAddressBatchOperation newOperation = new ReverseSearchAddressBatchOperation(client, operationId);
Response<ReverseSearchAddressBatchResult> newOperationResult = newOperation.WaitForCompletion();
printReverseBatchAddresses(newOperationResult.Value);
void printReverseBatchAddresses(ReverseSearchAddressBatchResult batchResult)
{
    // Print the search results
    for (int i = 0; i < batchResult.Results.Count; i++)
    {
        Console.WriteLine($"Possible addresses for query {i}:");
        var result = batchResult.Results[i];
        foreach (var address in result.Addresses)
        {
            Console.WriteLine($"{address.Address.FreeformAddress}");
        }
    }
}

Additional information

The Azure.Maps Namespace in the .NET documentation.