CHAPTER 2
We will now call the Azure Maps API and return a map image. We will perform the task using two different security authentication methods; Shared Key and AAD. To do this, we will create an application using Microsoft Blazor and display map tiles and interactive maps.
Initially, we will only retrieve and display a single map tile. We will conclude this chapter by demonstrating how you can retrieve a full interactive map and even display live traffic.
Note: See this website for more information about Microsoft Blazor.
Note: Azure Map controls can be placed in any kind of web application, including simple HTML pages, ASP.NET applications, and Blazor WebAssembly applications.
If you do not already have the following software installed, these steps are required to create the application:
Note: The requirements to create applications using Blazor are constantly evolving. For the latest requirements, see this article.

Figure 5: Create Blazor Server Application
To create the application, open Visual Studio and select Create a new project > Blazor Server App > Next.

Figure 6: Create BlazorStoreFinder Project
Set the Project name to BlazorStoreFinder and click Next.
On the next screen, select .NET 6.0 as the Framework and click Create.

Figure 7: BlazorStoreFinder Project
The project will be created.
In the following steps, we will retrieve a map image using a shared key.

Figure 8: Search and Open Azure Maps Account
Navigate to the Azure Portal (https://portal.azure.com/), log in, click All resources, and search for the Azure Maps account you created earlier.

Figure 9: Copy Shared Key
Click the Authentication section and copy the text from the Primary Key field.

Figure 10: Add New Item
In Visual Studio, right-click the Pages folder and select New Item.

Figure 11: Create Razor Component
Create a Razor component named MapImagekey.razor.
Replace all the code with the following.
Code Listing 1: MapImageKey Header
@page "/mapimagekey" @using System.Text; |
This instructs the control to be loaded when a user navigates to the mapimagekey URL and adds the required using statements for the StringBuilder code that will be added later.
Next, add the following user interface code.
Code Listing 2: MapImageKey UI
<input type="text" placeholder="Latitude" @bind="Latitude" /> <input type="text" placeholder="Longitude" @bind="Longitude" /> <input type="text" placeholder="SubscriptionKey" @bind="SubscriptionKey" /> <br /> <br /> <button class="btn btn-success" @onclick="GetTile">Get Tile</button> <br /> <br /> @if (isLoading) { <div class="spinner-border text-primary" role="status"></div> } <br /> @if (PngImage != "") { <div class="row"> <div style="width: 300px; height: 300px;"> <img src="@PngImage" height="300" width="300"> </div> </div> } |
This creates text boxes for the user to enter a latitude, longitude, an Azure Maps shared key, and a Get Tile button to click to retrieve a map image.
If an image is retrieved (PngImage), it will be displayed in an img control.
Add the following additional code.
Code Listing 3: MapImageKey Variable Declarations
@code { string PngImage = string.Empty; bool isLoading = false; string SubscriptionKey = ""; // Location of Los Angeles string Latitude = "34.0522"; string Longitude = "-118.2437"; |
This sets the default latitude and longitude to Los Angeles.
Finally, add the following code to call the Azure Maps API and retrieve the tile image.
Code Listing 4: MapImageKey GetTile Method
public async Task GetTile() { PngImage = ""; isLoading = true; // Create an HTTP Client to make the REST call. using (var client = new System.Net.Http.HttpClient()) { // Build the URL. StringBuilder sb = new StringBuilder(); // Request a PNG image. sb.Append("https://atlas.microsoft.com/map/static/png?"); // Pass the Subscription Key. sb.Append($"subscription-key={SubscriptionKey}&"); // Specify the API version, layer type, and zoom level. sb.Append("api-version=1.0&layer=basic&style=main&zoom=10&"); // Pass the Latitude and Longitude sb.Append($"¢er={Longitude},%20{Latitude}"); // Request that a pin be placed at the Latitude and Longitude. sb.Append($"&pins=default%7C%7C{Longitude}+{Latitude}"); // Set the URL var url = new Uri(sb.ToString()); // Call Azure Maps and get the response. var Response = await client.GetAsync(url); // Read the response. var responseContent = await Response.Content.ReadAsByteArrayAsync(); // Convert the response to an image. PngImage = $"data:image/png;base64,{Convert.ToBase64String(responseContent)}"; isLoading = false; StateHasChanged(); } } } |
This passes the Azure Maps shared key (in the SubscriptionKey variable) to the Azure Maps API, along with the latitude and longitude, and requests a map tile for the location. It also requests that a map pin marker be placed at the latitude and longitude location.

Figure 12: NavMenu.razor
We will now add a link to the MapImageKey control on the navigation menu. Open the NavMenu.razor control and add the following code under the existing code for the Home link.
Code Listing 5: MapImageKey NavLink
<div class="nav-item px-3"> <NavLink class="nav-link" href="mapimagekey"> <span class="oi oi-map" aria-hidden="true"></span> Map (Key) </NavLink> </div> |
On the Visual Studio toolbar, select Debug > Start Debugging (F5) to run the project.

Figure 13: Display Image Using Shared Key
When the project opens up in your web browser, click the Map (Key) link to navigate to the MapImageKey.razor control.
Enter the shared key (that you copied from the portal.azure.com page earlier) in the box labeled SubscriptionKey and click Get Tile.
The map tile will be displayed. You can enter different latitude and longitude values in the first two text boxes and click Get Tile to retrieve different map tile images.
We will now retrieve the same image using AAD authentication. The setup is more involved; however, we will be able to leverage the authentication code for the remaining examples in this book.
We will create an Azure service principal that will be assigned to the Azure Maps Data Reader role in the Azure Maps account we created earlier. The Azure documentation states that "the security principal defines the access policy and permissions for the user/application in the Azure AD tenant.” Later, in the Blazor application, we will request an authentication token for the Azure service principal. We will then use this authentication token to make calls to the Azure Maps API.

Figure 14: New Registration
In the Azure portal, navigate to the Azure Active Directory section and select App registrations > New registration.

Figure 15: Register AzureMapsApp
Name it AzureMapsApp, select Accounts in this organizational directory only, and click Register.

Figure 16: Copy Client ID and Tenant ID
Copy and save the Client ID and Tenant ID, because you will need them in a later step.

Figure 17: Certificates & Secrets
Click Certificates & secrets to navigate to that section. Then, click New client secret.

Figure 18: Create Client Secret
Give it a name and an expiration date. Click Add to create the secret.

Figure 19: Copy Secret Value
After the Client Secret is created, click Copy to clipboard next to the value and save it (you will need it in a later step).

Figure 20: Add a Permission
Click API permissions to navigate to that section and click Add a permission.

Figure 21: Select Azure Maps API
Select APIs my organization uses, search for Azure Maps, and click it to select it.

Figure 22: Add Permissions
Check the box next to Access Azure Maps and click Add permissions.

Figure 23: Grant Admin Consent Button
Finally, click the Grant admin consent button.

Figure 24: Copy Azure Maps Client ID
Return to the Azure Maps account created earlier, select the Authentication section, and copy and save the Client ID (you will need it in a later step).

Figure 25: Add Role Assignment
Select Access control (IAM) > Role assignments. Click Add and select Add role assignment.

Figure 26: Assign Azure Maps Data Reader
Search for Azure Maps, then select the Azure Maps Data Reader role, and click Next.

Figure 27: Select Members
Under Assign access to, select User, group, or service principal. Then click Select members.

Figure 28: Select AzureMapsApp
Search for the name of the Azure app registration, created earlier, and select it. Then, click Select.

Figure 29: Review and Assign
Click Review + assign, then on the next screen, click Review + assign again.

Figure 30: Manage NuGet Packages
In Visual Studio, right-click the project node and select Manage NuGet Packages.

Figure 31: Install Microsoft.Identity.Client
Install the Microsoft.Identity.Client NuGet package.

Figure 32: Open appsettings.json
Next, we will store the required values, gathered earlier, in the application settings file. Open the appsettings.json file and replace all the contents with the following (replacing the highlighted yellow sections with your own values).
Code Listing 6: appsettings.json
{ "ConnectionStrings": { "DefaultConnection": "** To be filled in later **" }, "AzureMaps": { "ClientId": "{{ Client ID from Azure Maps Account }}", "AadTenant": "{{ Azure Tenant ID }}", "AadAppId": "{{ Client ID from Azure App Registration }}", "AppKey": "{{ AppKey (secret) from Azure App Registration }}" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" } |
Note: The ClientId from the Azure Maps account will be consumed in the “Displaying Azure Maps” section covered later.

Figure 33: AuthService.cs
Create a Services directory and a AuthService.cs static class using the following code.
Code Listing 7: AuthService
using Microsoft.Identity.Client; using Microsoft.JSInterop; namespace BlazorStoreFinder { public static class AuthService { private const string AuthorityFormat = "https://login.microsoftonline.com/{0}/oauth2/v2.0";
private const string MSGraphScope = "https://atlas.microsoft.com/.default";
public static string? ClientId; public static string? AadTenant; public static string? AadAppId; public static string? AppKey; internal static void SetAuthSettings(IConfigurationSection AzureMaps) { // A call in Program.cs to this method is made to set the values. ClientId = AzureMaps.GetValue<string>("ClientId"); AadTenant = AzureMaps.GetValue<string>("AadTenant"); AadAppId = AzureMaps.GetValue<string>("AadAppId"); AppKey = AzureMaps.GetValue<string>("AppKey"); } [JSInvokable] public static async Task<string> GetAccessToken() { // Create a confidential client. IConfidentialClientApplication daemonClient; // Create a builder for the confidential client. daemonClient = ConfidentialClientApplicationBuilder .Create(AadAppId) .WithAuthority(string.Format(AuthorityFormat, AadTenant)) .WithClientSecret(AppKey) .Build(); // Get the access token for the confidential client. AuthenticationResult authResult = await daemonClient.AcquireTokenForClient(new[] { MSGraphScope }) .ExecuteAsync(); // Return the access token. return authResult.AccessToken; } } } |

Figure 34: Update Program.cs
Next, open the Program.cs file and replace the line of code:
var app = builder.Build();
With the following code that will get the settings from the appsettings.json, call the original Build() code and initialize the static AuthService class with the settings from the appsettings.json file.
Code Listing 8: AzureMaps Settings
// Get the Azure Maps settings from appsettings.json. var AzureMaps = builder.Configuration.GetSection("AzureMaps"); // Build the application and return the startup type. var app = builder.Build(); // Initialize the AuthService so the later calls to // GetAccessToken will work. BlazorStoreFinder.AuthService.SetAuthSettings(AzureMaps); |

Figure 35: Create MapImageAuthToken.razor
Create a new Razor control called MapImageAuthToken.razor using the following code.
Code Listing 9: MapImageAuthToken.razor
@page "/mapimageauthtoken" @using System.Text; <div class="row"> <div style="width: 300px; height: 300px;"> <img src="@PngImage" height="300" width="300"> </div> </div> @code { string PngImage = string.Empty; string Latitude = ""; string Longitude = ""; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { Latitude = Convert.ToDouble("34.0522").ToString(); Longitude = Convert.ToDouble("-118.2437").ToString(); // Call GetTile() method to get the PNG image. PngImage = await GetTile(); StateHasChanged(); } } public async Task<string> GetTile() { String PngImage = ""; // Create a HTTP client to make the REST call. using (var client = new System.Net.Http.HttpClient()) { // Get an access token from AuthService. var AccessToken = await AuthService.GetAccessToken(); client.DefaultRequestHeaders.Accept.Clear(); // Pass the Azure Maps Client Id. client.DefaultRequestHeaders.Add("x-ms-client-id", AuthService.ClientId);
// Pass the access token in the auth header. client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers .AuthenticationHeaderValue("Bearer", AccessToken); // Build the URL. StringBuilder sb = new StringBuilder(); // Request a PNG image. sb.Append("https://atlas.microsoft.com/map/static/png?"); // Specify the API version, layer type, and zoom level. sb.Append("api-version=1.0&layer=basic&style=main&zoom=12&"); // Pass the latitude and longitude. sb.Append($"¢er={Longitude},%20{Latitude}"); // Request that a pin be placed at the latitude and longitude. sb.Append($"&pins=default%7C%7C{Longitude}+{Latitude}"); // Set the URL. var url = new Uri(sb.ToString()); // Call Azure Maps and get the response. var Response = await client.GetAsync(url); // Read the response. var responseContent = await Response.Content.ReadAsByteArrayAsync(); // Convert the response to an image. PngImage = } return PngImage; } } |
Add the following code to NavMenu.razor to add a link to the control.
Code Listing 10: Map (Token) Link
<div class="nav-item px-3"> <NavLink class="nav-link" href="mapimageauthtoken"> <span class="oi oi-map-marker" aria-hidden="true"></span> Map (Token) </NavLink> </div> |

Figure 36: Tile Using Auth Token
Run the application, click the Map (Token) link, and view the result.
We will now update the code to determine automatically the user’s current latitude and longitude, and display a map tile of that location.
Install the following NuGet package: Darnton.Blazor.DeviceInterop.
Open the Program.cs file and add the following code (before the var app = builder.Build() line).
Code Listing 11: Add Darnton.Blazor.DeviceInterop
// Configure the Darnton Geolocation component builder.Services .AddScoped< Darnton.Blazor.DeviceInterop.Geolocation.IGeolocationService, Darnton.Blazor.DeviceInterop.Geolocation.GeolocationService >(); |
Return to the MapImageAuthToken.razor control and add the following lines of code under the @using System.Text line.
Code Listing 12: Darnton Using and Inject Statements
@using Darnton.Blazor.DeviceInterop.Geolocation; @inject IGeolocationService GeolocationService |
Next, alter all the UI markup code to the following.
Code Listing 13: Darnton Markup
@if (CurrentPositionResult != null) { <div class="row"> <div style="width: 300px; height: 300px;"> <img src="@PngImage" height="300" width="300"> </div> </div> } else { @if (isLoading) { <div class="spinner-border text-primary" role="status"></div> } } |
Add the following fields to the @code section.
Code Listing 14: Darnton Fields
bool isLoading = true; protected GeolocationResult? CurrentPositionResult { get; set; } |
Finally, change the entire OnAfterRenderAsync method to the following.
Code Listing 15: Darnton OnAfterRenderAsync Method
protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { // Get current location // will cause a popup to show to ask permission. CurrentPositionResult = await GeolocationService.GetCurrentPosition(); if (CurrentPositionResult.IsSuccess) { // Get latitude and longitude. string? CurrentLatitude = CurrentPositionResult.Position?.Coords?.Latitude.ToString("F2"); string? CurrentLongitude = CurrentPositionResult.Position?.Coords?.Longitude.ToString("F2"); // Set latitude and longitude // (to be consumed by GetTile() method). if (CurrentLatitude != null && CurrentLongitude != null) { Latitude = Convert.ToDouble(CurrentLatitude).ToString(); Longitude = Convert.ToDouble(CurrentLongitude).ToString(); } // Call GetTile() method to get the PNG image. PngImage = await GetTile(); isLoading = false; StateHasChanged(); } } } |

Figure 37: Allow Location Pop-up
Run the application and navigate to the Map (Token) page. A pop-up will appear asking for permission to obtain your current location. Click Allow.

Figure 38: Tile of Your Current Location
A map tile of your current location will be displayed.
In the final example for this chapter, we will display an interactive Azure map. To do this, we will use the open source AzureMapsControl.Components control. This is a Microsoft Blazor component that wraps the Azure Maps JavaScript control in an API that allows programmatic interaction using C# rather than JavaScript.
Install the following NuGet package: AzureMapsControl.Components.
Open the Program.cs file and add the following using statement.
Code Listing 16: AzureMapsControl Using Statement
using AzureMapsControl.Components; |
Next, add the following code (before the var app = builder.Build() line).
Code Listing 17: AddAzureMapsControl Configuration
// This code configures anonymous authentication // for the Azure Maps API. // An auth token will be required to access the maps. builder.Services.AddAzureMapsControl( configuration => configuration.ClientId = AzureMaps.GetValue<string>("ClientId")); |
The preceding code passes the ClientId from the Azure Maps account, stored in the appsettings.json file, to the Blazor AzureMapsControl control that will be used to display the map.

Figure 39: AzureMapsControl Layout Page Configuration
Open the Pages/_Layout.cshtml file and before this line:
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
add the following code to the head section.
Code Listing 18: AzureMapsControl Stylesheet Code
<link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" type="text/css" /> <link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/drawing/0.1/atlas-drawing.min.css" type="text/css" /> <link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/indoor/0.1/atlas-indoor.min.css" type="text/css" /> <style> #map { position: absolute; width: 60%; min-width: 300px; height: 60%; min-height: 250px; } </style> |
Finally, add the following code before the closing body tag on the page.
Code Listing 19: AzureMapsControl JavaScript Code
<script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"> </script> <script src="https://atlas.microsoft.com/sdk/javascript/drawing/0.1/atlas-drawing.min.js"> </script> <script src="https://atlas.microsoft.com/sdk/javascript/indoor/0.1/atlas-indoor.js"> </script> <script src="_content/AzureMapsControl.Components/azure-maps-control.js"></script> <script type="text/javascript"> azureMapsControl.Extensions.getTokenCallback = (resolve, reject, map) => { DotNet.invokeMethodAsync('BlazorStoreFinder', 'GetAccessToken') .then(function (response) { return response; }).then(function (token) { resolve(token); }); }; </script> |
This code adds the required JavaScript libraries, as well a custom JavaScript method, that will retrieve an access token by calling the GetAccessToken method in the AuthService class created earlier.
This is required to allow the Blazor AzureMapsControl to pass the auth token to the Azure Maps JavaScript API.
Note: For more information on the DotNet.invokeMethodAsync method used in the previous code, see: “Call .NET methods from JavaScript functions in ASP.NET Core Blazor” in this article.

Figure 40: AzureMapDisplay.razor Control
Create a new Razor control called AzureMapDisplay.razor using the following code.
Code Listing 20: AzureMapDisplay Using Statements
@page "/azuremapdisplay" @using AzureMapsControl.Components.Map @using Darnton.Blazor.DeviceInterop.Geolocation; @inject IGeolocationService GeolocationService |
Add the following UI markup code to display a Show Traffic button. When clicked, it will call a method (to be created later) that will display the current traffic conditions on the map.
Code Listing 21: Show Traffic Button
<button @onclick="ShowTraffic">Show Traffic</button> |
Next, add the following UI markup code to instantiate the AzureMap control.
Code Listing 22: AzureMap Control
<AzureMap Id="map" CameraOptions="new CameraOptions { Zoom = 10 }" StyleOptions="new StyleOptions { ShowLogo = false }" EventActivationFlags="MapEventActivationFlags.None() .Enable(MapEventType.Ready)" TrafficOptions="new AzureMapsControl.Components.Traffic.TrafficOptions { Incidents = false, Flow = AzureMapsControl.Components.Traffic .TrafficFlow.Relative }" OnReady="OnMapReadyAsync" /> |
Add the following code to create needed fields.
Code Listing 23: AzureMapDisplay Fields
@code { MapEventArgs? myMap; protected GeolocationResult? CurrentPositionResult { get; set; }
} |
Add the following method that will run when the AzureMap control invokes the onMapReadyAsync event.
This method will determine your current location and set your current latitude and longitude on the map.
Code Listing 24: AzureMapDisplay OnMapReadyAsync Method
public async Task OnMapReadyAsync(MapEventArgs eventArgs) { myMap = eventArgs;
// Get current location // will cause a popup to show to ask permission. CurrentPositionResult = await GeolocationService.GetCurrentPosition(); if (CurrentPositionResult.IsSuccess) { // Get latitude and longitude. string? CurrentLatitude = CurrentPositionResult.Position?.Coords?.Latitude.ToString("F2"); string? CurrentLongitude = CurrentPositionResult.Position?.Coords?.Longitude.ToString("F2"); if (CurrentLatitude != null && CurrentLongitude != null) { // Set the latitude and longitude as the map camera center. await eventArgs.Map.SetCameraOptionsAsync( options => options.Center = new AzureMapsControl.Components.Atlas.Position ( Convert.ToDouble(CurrentLongitude), Convert.ToDouble(CurrentLatitude) )); } } } |
Lastly, add the following method that will enable the map to display the current traffic conditions when triggered by the Show Traffic button.
Code Listing 25: Show Traffic Method
public async Task ShowTraffic() { if (myMap != null) { // Enable traffic and set options. await myMap.Map .SetTrafficOptionsAsync(options => options.Incidents = true); } } |
Add the following code to NavMenu.razor to add a link to the control.
Code Listing 26: AzureMap Link
<div class="nav-item px-3"> <NavLink class="nav-link" href="azuremapdisplay"> <span class="oi oi-aperture" aria-hidden="true"></span> Azure Map </NavLink> </div> |

Figure 41: Display Map with Traffic
Run the application and navigate to the Azure Map page.
The map will appear displaying your current location. Clicking the Show Traffic button will display current traffic conditions.