left-icon

Azure Maps Using Blazor Succinctly®
by Michael Washington

Previous
Chapter

of
A
A
A

CHAPTER 2

Displaying Azure Maps in Blazor

Displaying Azure Maps in Blazor


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.

Install .NET Core and Visual Studio

If you do not already have the following software installed, these steps are required to create the application:

  1. Install the .NET 6 SDK from this site.
  2. Install Visual Studio 2022 version 17.1 (or later), with the ASP.NET, web development, and NET Core workload here.

Note: The requirements to create applications using Blazor are constantly evolving. For the latest requirements, see this article.

Create the application

Create Blazor Server Application

Figure 5: Create Blazor Server Application

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

Create BlazorStoreFinder Project

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.

BlazorStoreFinder Project

Figure 7: BlazorStoreFinder Project

The project will be created.

Render image using a shared key

In the following steps, we will retrieve a map image using a shared key.

Search and Open Azure Maps Account

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.

Copy Shared Key

Figure 9: Copy Shared Key

Click the Authentication section and copy the text from the Primary Key field.

Add New Item

Figure 10: Add New Item

In Visual Studio, right-click the Pages folder and select New Item.

Create Razor Component

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($"&center={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.

NavMenu.razor

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.

Display Image Using Shared Key

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.

Render image using AAD token

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.

Create the service principal

New Registration

Figure 14: New Registration

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

Register AzureMapsApp

Figure 15: Register AzureMapsApp

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

Copy Client ID and Tenant ID

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.

Certificates & Secrets

Figure 17: Certificates & Secrets

Click Certificates & secrets to navigate to that section. Then, click New client secret.

Create Client Secret

Figure 18: Create Client Secret

Give it a name and an expiration date. Click Add to create the secret.

Copy Secret Value

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).

Add a Permission

Figure 20: Add a Permission

Click API permissions to navigate to that section and click Add a permission.

Select Azure Maps API

Figure 21: Select Azure Maps API

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

Add Permissions

Figure 22: Add Permissions

Check the box next to Access Azure Maps and click Add permissions.

Grant Admin Consent Button

Figure 23: Grant Admin Consent Button

Finally, click the Grant admin consent button.

Assign the service principal to the Azure Maps account

Copy Azure Maps Client ID

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).

Add Role Assignment

Figure 25: Add Role Assignment

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

Assign Azure Maps Data Reader

Figure 26: Assign Azure Maps Data Reader

Search for Azure Maps, then select the Azure Maps Data Reader role, and click Next.

Select Members

Figure 27: Select Members

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

Select AzureMapsApp

Figure 28: Select AzureMapsApp

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

Review and Assign

Figure 29: Review and Assign

Click Review + assign, then on the next screen, click Review + assign again.

Store and retrieve the service principal settings

Manage NuGet Packages

Figure 30: Manage NuGet Packages

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

Install Microsoft.Identity.Client

Figure 31: Install Microsoft.Identity.Client

Install the Microsoft.Identity.Client NuGet package.

Open appsettings.json

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.

AuthService.cs

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;

        }

    }

}

Update Program.cs

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);

Display the tile

 Create MapImageAuthToken.razor

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($"&center={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)}";

        }

        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>

Tile Using Auth Token

Figure 36: Tile Using Auth Token

Run the application, click the Map (Token) link, and view the result.

Retrieve your current location

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();

        }

    }

}

Allow Location Pop-up

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.

Tile of Your Current Location

Figure 38: Tile of Your Current Location

A map tile of your current location will be displayed.

Render map using AzureMapsControl.Components

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.

AzureMapsControl Layout Page Configuration

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.

Display an Azure map

AzureMapDisplay.razor Control

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>

Display Map with Traffic

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.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.