Version

Displaying Geographic Imagery from Bing Maps using Custom Classes

Purpose

This topic provides information on how to display geographic imagery from Bing Maps in the background content of the UltraGeographicMap™ control.

Required background

The following table lists the topics required as a prerequisite to understanding this topic.

Topic Purpose

This topic provides information on how to add the UltraGeographicMap control to an application page.

This topic provides information about layout of map elements in the UltraGeographicMap control.

This topic provides an overview of rendering geographic imagery in the background content of the UltraGeographicMap control.

In this topic

This topic contains the following sections:

Geographic Imagery from Bing Maps

Overview

The Bing Maps is a licensed geographic imagery mapping service created by the Microsoft® company. This geographic imagery service can be access directly on http://www.bing.com/maps web site. The control displays geographic imagery from the Bing Maps in the map background content using the BingMapsMapImagery class. However, the control by the default displays geographic imagery from the Open Street Maps in the map background content. Therefore, you must configure the control to display geographic imagery from the Bing Maps.

Previews

The following images are previews of the control in supported map styles of geographic imagery from the Bing Maps service.

GeographicMap Displaying Geographic Imagery from Bing Maps 2.png

Bing Map imagery with Road style

GeographicMap Displaying Geographic Imagery from Bing Maps 1.png

Bing Map imagery with Aerial and Labels style

GeographicMap Displaying Geographic Imagery from Bing Maps 3.png

Bing Map imagery with Aerial style

Requirements

To complete the following procedure, you need to fulfill the following requirements:

Requirement Description

Obtained Bing Maps API Key

In order to use geographic imagery from Bing Maps, you must register and obtain Map API key from the www.bingmapsportal.com website. You must provide this Map API key to the Bing Maps Connector object.

Added Assembly References

The Bing Maps Connector requires the following assembly references:

  • System.Runtime.Serialization.dll

Implemented Bing Maps Connector

The Bing Maps Connector is a custom class that sets up Bing Maps REST imagery service and provides imagery tiles via Http web requests without complicated configuration of an application for the Bing Maps service.

The following code provides implementation of the Bing Maps Connector class.

In Visual Basic:

Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Net ' HttpWebRequest
Imports System.Runtime.Serialization ' DataContract, DataMember
Imports System.Runtime.Serialization.Json ' DataContractJsonSerializer

Namespace Infragistics.Samples.Services
    ''' <summary>
    ''' <para> Represents a connector class that sets up BingMaps REST imagery service </para>
    ''' <para> and provides imagery tiles via Http web requests.</para>
    ''' <remarks>Bing Maps REST Services: http://msdn.microsoft.com/en-us/library/ff701713.aspx </remarks>
    ''' </summary>
    Public Class BingMapsConnector
        Implements INotifyPropertyChanged
        Public Sub New()
            Me.IsInitialized = False
        End Sub

#Region "Events"
        Public Event ImageryInitialized As EventHandler
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
#End Region

#Region "Properties"
        Private _apiKeyProperty As String
        ''' <summary>
        ''' Gets or sets an API key required by the Bing Maps imagery service.
        ''' <remarks>This key must be obtained from the http://www.bingmapsportal.com website. </remarks>
        ''' </summary>
        Public Property ApiKey() As String
            Get
                Return Me._apiKeyProperty
            End Get
            Set
                Me._apiKeyProperty = Value
                Me.Validate()
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ApiKeyProperty"))
            End Set
        End Property

        Private _imageryStyle As BingMapsImageryStyle
        ''' <summary>
        ''' <para> Gets or sets a map style of the Bing Maps imagery tiles. </para>
        ''' <para> For example: Aerial, AerialWithLabels, or Road map style. </para>
        ''' </summary>
        Public Property ImageryStyle() As BingMapsImageryStyle
            Get
                Return Me._imageryStyle
            End Get
            Set
                Me._imageryStyle = Value
                Me.Validate()
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ApiKeyProperty"))
            End Set
        End Property

        Private _tilePath As String
        ''' <summary>
        ''' Gets an imagery tile path for the Bing Maps service.
        ''' </summary>
        Public Property TilePath() As String
            Get
                Return _tilePath
            End Get
            Private Set
                _tilePath = Value
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("TilePath"))
            End Set
        End Property

        Private _subDomains As ObservableCollection(Of String)
        ''' <summary>
        ''' Gets a collection of image URI sub-domains for the Bing Maps service.
        ''' </summary>
        Public Property SubDomains() As ObservableCollection(Of String)
            Get
                Return _subDomains
            End Get
            Private Set
                _subDomains = Value
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("SubDomains"))
            End Set
        End Property

        Private _isInitialized As Boolean
        ''' <summary>
        ''' Gets a status whether the Bing Maps service is initialized.
        ''' </summary>
        Public Property IsInitialized() As Boolean
            Get
                Return _isInitialized
            End Get
            Private Set
                _isInitialized = Value
            End Set
        End Property

        Private _isAutoInitialized As Boolean
        ''' <summary>
        ''' Gets or sets whether the Bing Maps service should be auto-initialized upon valid property values.
        ''' </summary>
        Public Property IsAutoInitialized() As Boolean
            Get
                Return Me._isAutoInitialized
            End Get
            Set
                Me._isAutoInitialized = Value
                Me.Validate()
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("IsAutoInitialized"))
            End Set
        End Property
#End Region

#Region "Methods"
        Private Sub Validate()
            Me.IsInitialized = False
            If Not IsValidApiKey() Then
                Return
            End If
            If Me.IsAutoInitialized Then
                Initialize()
            End If
        End Sub

        Private Function IsValidApiKey() As Boolean
            If [String].IsNullOrEmpty(Me.ApiKey) OrElse Me.ApiKey.Length < 20 Then
                Return False
            End If

            Return True
        End Function

        Public Sub Initialize()
            If Not IsValidApiKey() Then
                Me.IsInitialized = False
                System.Diagnostics.Debug.WriteLine(Convert.ToString("Detected Invalid BingMaps API key: ") & Me.ApiKey)
                Return
            End If

            Me.IsInitialized = True
            ' for more info on setting up web requests to BingMaps REST imagery service
            ' refer to: http://msdn.microsoft.com/en-us/library/ff701716.aspx
            Dim bingUrl = "http://dev.virtualearth.net/REST/v1/Imagery/Metadata/"
            Dim imagerySet = Me.ImageryStyle
            bingUrl = bingUrl & imagerySet
            Dim parms = (Convert.ToString("key=") & Me.ApiKey) + "&include=ImageryProviders"
            Dim url = bingUrl & "?" & parms
            Dim req = HttpWebRequest.Create(url)
            req.BeginGetResponse(AddressOf GetResponseCompleted, req)
        End Sub
#End Region

#Region "Event Handlers"
        Private Sub GetResponseCompleted(res As IAsyncResult)
            Dim req = DirectCast(res.AsyncState, HttpWebRequest)
            Dim response = req.EndGetResponse(res)
            ' alternatively, parsing of BingResponse can be performed using LINQ to XML
            ' instead of using JSON deserializer
            Dim json = New DataContractJsonSerializer(GetType(BingResponse))
            Dim resp = DirectCast(json.ReadObject(response.GetResponseStream()), BingResponse)
            If resp.ResourceSets Is Nothing OrElse resp.ResourceSets.Count < 1 OrElse resp.ResourceSets(0).Resources Is Nothing OrElse resp.ResourceSets(0).Resources.Count < 1 Then
                Return
            End If

            Dim imageUrl = resp.ResourceSets(0).Resources(0).ImageUrl
            Dim subDomains__1 = resp.ResourceSets(0).Resources(0).ImageUrlSubdomains
            If imageUrl Is Nothing OrElse subDomains__1 Is Nothing Then
                Return
            End If

            TilePath = imageUrl
            SubDomains = New ObservableCollection(Of String)(subDomains__1)

            RaiseEvent ImageryInitialized(Me, New EventArgs())
        End Sub
#End Region
    End Class

    ''' <summary>
    ''' Determines map style for the Bing Maps imagery.
    ''' </summary>
    Public Enum BingMapsImageryStyle
        ''' <summary>
        ''' Specifies the Aerial map style without road or labels overlay.
        ''' </summary>
        Aerial
        ''' <summary>
        ''' Specifies the Aerial map style with road and labels overlay.
        ''' </summary>
        AerialWithLabels
        ''' <summary>
        ''' Specifies the Roads map style without aerial overlay.
        ''' </summary>
        Road
#Region "Not supported Bing Maps styles by the UltraGeographicMap control"
        ''''// <summary>
        ''''// Specifies the Bird’s eye (oblique-angle) map style
        ''''// </summary>
        'Birdseye,
        ''''// <summary>
        ''''// Specifies the Bird’s eye map style with road and labels overlay.
        ''''// </summary>
        'BirdseyeWithLabels,
#End Region
    End Enum

    <DataContract>
    Public Class BingResponse
        Public Sub New()
            ResourceSets = New List(Of BingResourceSet)()
        End Sub
        <DataMember(Name:="resourceSets")>
        Public Property ResourceSets() As List(Of BingResourceSet)
            Get
                Return m_ResourceSets
            End Get
            Set
                m_ResourceSets = Value
            End Set
        End Property
        Private m_ResourceSets As List(Of BingResourceSet)
    End Class

    <DataContract>
    Public Class BingResourceSet
        Public Sub New()
            Resources = New List(Of ImageryMetadata)()
        End Sub
        <DataMember(Name:="resources")>
        Public Property Resources() As List(Of ImageryMetadata)
            Get
                Return m_Resources
            End Get
            Set
                m_Resources = Value
            End Set
        End Property
        Private m_Resources As List(Of ImageryMetadata)
    End Class

    <DataContract([Namespace]:="http://schemas.microsoft.com/search/local/ws/rest/v1")>
    Public Class ImageryMetadata
        Public Sub New()
            ImageUrlSubdomains = New List(Of String)()
        End Sub
        <DataMember(Name:="imageUrl")>
        Public Property ImageUrl() As String
            Get
                Return m_ImageUrl
            End Get
            Set
                m_ImageUrl = Value
            End Set
        End Property
        Private m_ImageUrl As String
        <DataMember(Name:="imageUrlSubdomains")>
        Public Property ImageUrlSubdomains() As List(Of String)
            Get
                Return m_ImageUrlSubdomains
            End Get
            Set
                m_ImageUrlSubdomains = Value
            End Set
        End Property
        Private m_ImageUrlSubdomains As List(Of String)
    End Class
End Namespace

In C#:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Net;                           // HttpWebRequest
using System.Runtime.Serialization;         // DataContract, DataMember
using System.Runtime.Serialization.Json;    // DataContractJsonSerializer

namespace Infragistics.Samples.Services
{
    /// <summary>
    /// <para> Represents a connector class that sets up BingMaps REST imagery service </para>
    /// <para> and provides imagery tiles via Http web requests.</para>
    /// <remarks>Bing Maps REST Services: http://msdn.microsoft.com/en-us/library/ff701713.aspx </remarks>
    /// </summary>
    public class BingMapsConnector : INotifyPropertyChanged
    {
        public BingMapsConnector()
        {
            this.IsInitialized = false;
        }

        #region Events
        public event EventHandler ImageryInitialized;
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Properties
        private string _apiKeyProperty;
        /// <summary>
        /// Gets or sets an API key required by the Bing Maps imagery service.
        /// <remarks>This key must be obtained from the http://www.bingmapsportal.com website. </remarks>
        /// </summary>
        public string ApiKey
        {
            get { return this._apiKeyProperty; }
            set
            {
                this._apiKeyProperty = value;
                this.Validate();
                if(this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs("ApiKeyProperty"));
                }
            }
        }

        private BingMapsImageryStyle _imageryStyle;
        /// <summary>
        /// <para> Gets or sets a map style of the Bing Maps imagery tiles. </para>
        /// <para> For example: Aerial, AerialWithLabels, or Road map style. </para>
        /// </summary>
        public BingMapsImageryStyle ImageryStyle
        {
            get { return this._imageryStyle; }
            set
            {
                this._imageryStyle = value;
                this.Validate();
                if(this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs("ApiKeyProperty"));
                }
            }
        }

        private string _tilePath;
        /// <summary>
        /// Gets an imagery tile path for the Bing Maps service.
        /// </summary>
        public string TilePath
        {
            get { return _tilePath; }
            private set
            {
                _tilePath = value;
                if(PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("TilePath"));
                }
            }
        }

        private ObservableCollection<string> _subDomains;
        /// <summary>
        /// Gets a collection of image URI sub-domains for the Bing Maps service.
        /// </summary>
        public ObservableCollection<string> SubDomains
        {
            get
            {
                return _subDomains;
            }
            private set
            {
                _subDomains = value;
                if(PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("SubDomains"));
                }
            }
        }

        /// <summary>
        /// Gets a status whether the Bing Maps service is initialized.
        /// </summary>
        public bool IsInitialized { get; private set; }

        private bool _isAutoInitialized;
        /// <summary>
        /// Gets or sets whether the Bing Maps service should be auto-initialized upon valid property values.
        /// </summary>
        public bool IsAutoInitialized
        {
            get { return this._isAutoInitialized; }
            set
            {
                this._isAutoInitialized = value;
                this.Validate();
                if(this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs("IsAutoInitialized"));
                }
            }
        }
        #endregion

        #region Methods
        private void Validate()
        {
            this.IsInitialized = false;
            if(!IsValidApiKey())
            {
                return;
            }
            if(this.IsAutoInitialized)
            {
                Initialize();
            }
        }

        private bool IsValidApiKey()
        {
            if(String.IsNullOrEmpty(this.ApiKey) || this.ApiKey.Length < 20)
            {
                return false;
            }

            return true;
        }
        public void Initialize()
        {
            if(!IsValidApiKey())
            {
                this.IsInitialized = false;
                System.Diagnostics.Debug.WriteLine("Detected Invalid BingMaps API key: " + this.ApiKey);
                return;
            }

            this.IsInitialized = true;
            // for more info on setting up web requests to BingMaps REST imagery service
            // refer to: http://msdn.microsoft.com/en-us/library/ff701716.aspx
            var bingUrl = "http://dev.virtualearth.net/REST/v1/Imagery/Metadata/";
            var imagerySet = this.ImageryStyle;
            bingUrl += imagerySet;
            var parms = "key=" + this.ApiKey + "&include=ImageryProviders";
            var url = bingUrl + "?" + parms;
            var req = HttpWebRequest.Create(url);
            req.BeginGetResponse(GetResponseCompleted, req);
        }
        #endregion

        #region Event Handlers
        private void GetResponseCompleted(IAsyncResult res)
        {
            var req = (HttpWebRequest)res.AsyncState;
            var response = req.EndGetResponse(res);
            // alternatively, parsing of BingResponse can be performed using LINQ to XML
            // instead of using JSON deserializer
            var json = new DataContractJsonSerializer(typeof(BingResponse));
            var resp = (BingResponse)json.ReadObject(response.GetResponseStream());
            if(resp.ResourceSets == null ||
                resp.ResourceSets.Count < 1 ||
                resp.ResourceSets[0].Resources == null ||
                resp.ResourceSets[0].Resources.Count < 1)
            {
                return;
            }

            var imageUrl = resp.ResourceSets[0].Resources[0].ImageUrl;
            var subDomains = resp.ResourceSets[0].Resources[0].ImageUrlSubdomains;
            if(imageUrl == null || subDomains == null)
            {
                return;
            }

            TilePath = imageUrl;
            SubDomains = new ObservableCollection<string>(subDomains);

            if(ImageryInitialized != null)
            {
                ImageryInitialized(this, new EventArgs());
            }
        }
        #endregion
    }

    /// <summary>
    /// Determines map style for the Bing Maps imagery.
    /// </summary>
    public enum BingMapsImageryStyle
    {
        /// <summary>
        /// Specifies the Aerial map style without road or labels overlay.
        /// </summary>
        Aerial,
        /// <summary>
        /// Specifies the Aerial map style with road and labels overlay.
        /// </summary>
        AerialWithLabels,
        /// <summary>
        /// Specifies the Roads map style without aerial overlay.
        /// </summary>
        Road,
        #region Not supported Bing Maps styles by the UltraGeographicMap control
        ///// <summary>
        ///// Specifies the Bird’s eye (oblique-angle) map style
        ///// </summary>
        //Birdseye,
        ///// <summary>
        ///// Specifies the Bird’s eye map style with road and labels overlay.
        ///// </summary>
        //BirdseyeWithLabels,
        #endregion
    }

    [DataContract]
    public class BingResponse
    {
        public BingResponse()
        {
            ResourceSets = new List<BingResourceSet>();
        }

        [DataMember(Name = "resourceSets")]
        public List<BingResourceSet> ResourceSets { get; set; }
    }

    [DataContract]
    public class BingResourceSet
    {
        public BingResourceSet()
        {
            Resources = new List<ImageryMetadata>();
        }

        [DataMember(Name = "resources")]
        public List<ImageryMetadata> Resources { get; set; }
    }

    [DataContract(Namespace = "http://schemas.microsoft.com/search/local/ws/rest/v1")]
    public class ImageryMetadata
    {
        public ImageryMetadata()
        {
            ImageUrlSubdomains = new List<string>();
        }

        [DataMember(Name = "imageUrl")]
        public string ImageUrl { get; set; }

        [DataMember(Name = "imageUrlSubdomains")]
        public List<string> ImageUrlSubdomains { get; set; }
    }
}

The following table summarizes important members of the Bing Maps Connector class:

Member Description

ApiKey

Represents a property for setting an API key required for the Bing Maps imagery service. This key must be obtained from the http://www.bingmapsportal.com website.

IsAutoInitialized

Represents a property that ensures that the Bing Maps imagery service is initialized and geographic imagery tiles are requested.

ImageryStyle

Represents a property for setting a map style of the Bing Maps imagery tiles. This property can be set to the following BingMapsImageryStyle enumerable values:

  • Satellite - Specifies the Satellite map style without road or labels overlay.

  • Satellite WithLabels - Specifies the Satellite map style with road and labels overlay.

  • Road - Specifies the Roads map style without Satellite overlay.

ImageryInitialized

Represents an event that occurs when geographic imagery tiles from Bing Maps service have been initialized and they are ready for rendering in the control.

Procedure

The following procedure demonstrates how to display geographic imagery from Bing Maps in the background content of the UltraGeographicMap control.

Add and set up the BingMapsConnector object

Add the BingMapsConnector object with your own Bing Maps key and create an event handler for the ImageryInitialized event.

In Visual Basic:

Private WithEvents connector As New BingMapsConnector()

connector.ApiKey = "BING_MAPS_API_KEY"
connector.ImageryStyle = BingMapsImageryStyle.Road
connector.IsAutoInitialized = True

In C#:

BingMapsConnector connector = new BingMapsConnector();
connector.ApiKey = "BING_MAPS_API_KEY";
connector.ImageryStyle = BingMapsImageryStyle.Road;
connector.ImageryInitialized += OnImageryInitialized;
connector.IsAutoInitialized = true;

Implement an event handler for the ImageryInitialized event of the Bing Maps Connector object

Implement an event handler for the ImageryInitialized event in order to invoke a method for updating the UltraGeographicMap control with geographic imagery from Bing Maps service.

In Visual Basic:

Private Sub OnImageryInitialized(sender As Object, e As EventArgs) Handles connector.ImageryInitialized
    Me.GeoMap.Invoke(DirectCast(Sub() UpdateBingMaps(sender), Action))
End Sub

In C#:

private void OnImageryInitialized(object sender, EventArgs e)
{
    this.GeoMap.Invoke((Action)(() => UpdateBingMaps(sender)));
}

Implement a method for updating the UltraGeographicMap control with geographic imagery

Implement a method for updating the UltraGeographicMap control with geographic imagery from Bing Maps service using the BingMapsMapImagery class.

In Visual Basic:

Private Sub UpdateBingMaps(sender As Object)
      Dim connector = DirectCast(sender, Infragistics.Samples.Services.BingMapsConnector)
      Me.GeoMap.SetImagery(New BingMapsMapImagery() With {
            .TilePath = connector.TilePath, .SubDomains = connector.SubDomains})
End Sub

In C#:

private void UpdateBingMaps(object sender)
{
   var connector = (Infragistics.Samples.Services.BingMapsConnector)sender;
    this.GeoMap.SetImagery(
       new BingMapsMapImagery()
       {
           TilePath = connector.TilePath,
           SubDomains = connector.SubDomains
       });
}

Verify the results

Build and run your project to verify the result. If you have implemented the steps correctly, the displayed should look like the one in the Previews section above.

Related Content

The following topics provide additional information related to this topic.

Topic Purpose

This topic provides information on how to bind geographic imagery in the UltraGeographicMap control.

This topic provides information about layout of map elements in the UltraGeographicMap control.

This topic provides information on how to display geographic imagery from Open Street Maps service in the UltraGeographicMap control.