Laurent Kempé
Bing Maps Silverlight control
Utilisation du Bing Maps Silverlight control au sein d’une application Silverlight MVVM.
Par Laurent Kempé publié le 10/12/2009 à 11:54, lu 4570 fois,
Microsoft vient de publier à travers son moteur de recherche Bing, le Bing Maps Silverlight Control SDK que vous pouvez télécharger sur cette page.
Vous pouvez aussi en expérimenter une version interactive sur la page Bing Maps Silverlight Control Interactive SDK. Je vous recommande fortement d’y faire un tour car vous pouvez y voir le code et ce code fait en direct.
Pour commencer il faut vous enregistrer sur le site suivant, Maps Account Center, afin de créer un compte et de récupérer une clé Bing Map que vous devrez utiliser pour pouvoir utiliser ce service.
Nous allons voir comment utiliser ce contrôle afin de:
  • Pouvoir naviguer sur une carte Bing Maps
  • Permettre à l’utilisateur de double cliquer sur la carte afin de spécifier un emplacement
  • Permettre à l’utilisateur de varier le rayon de recherche autour de l’emplacement choisi
  • Permettre à l’utilisateur de spécifier un texte à rechercher et de démarrer une recherche
Voici le résultat attendu :
 
/content/e663c9c3-e631-4aa5-98e1-c7c91a18c095/image1.jpeg
 
Pour cela nous allons une fois de plus utiliser le ‘pattern’ Model View ViewModel (MVVM) afin d’éviter d’avoir du code non lié à la vue dans le code behind de notre vue. Nous utilisons une fois de plus l’excellent framework MVVM Light Toolkit de Laurent Bugnion.
Pour commencer, créer un projet Silverlight 3, puis ajouter les références aux assemblies de MVVM Light Toolkit :
  • GalaSoft.MvvmLight
  • GalaSoft.MvvmLight.Extras
Et aux assemblies du Bing Maps Silverlight Control :
  • Microsoft.Maps.MapControl.Common
  • Microsoft.Maps.MapControl.Common
Ajouter une classe SearchViewModel qui hérite de ViewModelBase.
Cette classe va exposer :
  • Une commande qui sera appelée lorsque que l’utilisateur cliquera sur le bouton de recherche
  • Une propriété CurrentLocation de type Location potentiellement utilisée lors d’une recherche
  • Une propriété Radius de type double elle aussi potentiellement utilisée lors d’une recherche

using GalaSoft.MvvmLight;

using GalaSoft.MvvmLight.Command;

using Microsoft.Maps.MapControl;

 

namespace SilverlightMap.ViewModel

{

    public class SearchViewModel : ViewModelBase

    {

        private const double DefaultRadius = 20.0;

 

        private double _radius;

 

        public SearchViewModel()

        {

            SearchCommand = new RelayCommand<string>(Search);

            Radius = DefaultRadius;

        }

 

        public double Radius

        {

            get { return _radius; }

            set

            {

                _radius = value;

                RaisePropertyChanged("Radius");

            }

        }

 

        public Location CurrentLocation { get; set; }

 

        public RelayCommand<string> SearchCommand { get; private set; }

 

        private void Search(string searchText)

        {

            //todo appeler notre service de recherche utilisant le searchText et la CurrentLocation

        }

    }

}

Passons maintenant à la partie XAML de notre application.
Nous devons référencer dans le fichier MainPage.xaml, les namespaces du MVVM Light Toolkit et du Bing Maps Silverlight Control :

< UserControl

   x:Class="SilverlightMap.MainPage"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   xmlns:BingMap="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"

   xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight"

   mc:Ignorable="d"

   d:DesignWidth="640"

   d:DesignHeight="480">

Puis nous ajoutons un contrôle Map à notre XAML, dans lequel vous devez utiliser la clé créée sur Maps Account Center . Nous déclarons un event handler sur l’événement MouseDoubleClick ; MyMap_OnMouseDoubleClick :

< BingMap : Map

   MouseDoubleClick="MyMap_OnMouseDoubleClick"

   Grid.Row="1"

   Grid.Column="1"

   Grid.RowSpan="2"

   Grid.ColumnSpan="2"

   HorizontalContentAlignment="Stretch"

   VerticalContentAlignment="Center"

   IsTabStop="false"

   x:Name="myMap"

   CredentialsProvider="Votre Clef"

   Mode="AerialWithLabels"

   Center="-26.04740,132.45890"

   ZoomLevel="5">

</ BingMap : Map >

Nous ajoutons aussi un Slider dont la propriété Value est attachée à la propriété Radius de notre ViewModel SearchViewModel.

< Slider

   Grid.Column="1"

   Margin="30"

   Minimum="1"

   Maximum="50"

   Value="{Binding Radius, Mode=TwoWay}" />

Enfin il nous reste à déclarer le bouton qui lancera la recherche, qui lui est attaché à la commande SearchCommand de notre ViewModel  SearchViewModel. Cette commande reçoit aussi le contenu de la propriété Text d’une TextBox en paramètre de la commande :

< Button

   x:Name="SearchButton"

   cmd:ButtonBaseExtensions.Command="{Binding SearchCommand}"

   cmd:ButtonBaseExtensions.CommandParameter="{Binding ElementName=SearchTextBox, Path=Text}"

   Width="22"

   Height="22"

Passons au code de notre vue MainPage.
Tout d’abord la méthode MyMap_OnMouseDoubleClick :

/// <summary>

/// Handles the OnMouseDoubleClick event of the MyMap control.

/// </summary>

/// <param name="sender"> The source of the event. </param>

/// <param name="e"> The <see cref="Microsoft.Maps.MapControl.MapMouseEventArgs"/> instance

/// containing the event data. </param>

private void MyMap_OnMouseDoubleClick(object sender, MapMouseEventArgs e)

{

    var location = myMap.ViewportPointToLocation(e.ViewportPoint);

    _searchViewModel.CurrentLocation = location;

    AddCircle(_searchViewModel.CurrentLocation, _searchViewModel.Radius, .5);

}

Cette méthode reçoit en paramètre un objet de type MapMouseEventArgs dont la propriété ViewportPoint est utilisée afin de pouvoir les coordonnées du double click en Location contenant la longitude et l’altitude. Ces informations sont ensuite utilisées afin de mettre à jour la propriété CurrentLocation de notre ViewModel SearchViewModel. Enfin nous appelons la méthode AddCircle afin de dessiner un cercle sur la carte.
Ces méthodes proviennent du blog « Bing Maps Silverlight Control Part 4: Drawing Circles To Scale »

private void AddCircle(Location location, double radius, double opacity)

{       

    var polygon = new MapPolygon

                      {

                          Fill = new SolidColorBrush(Colors.Green),

                          Stroke = new SolidColorBrush(Colors.Blue),

                          StrokeThickness = 5,

                          Opacity = opacity,

                          Locations = DrawACircle(location, radius)

                      };

 

    myMap.Children.Clear();

    myMap.Children.Add(polygon);

}

 

public LocationCollection DrawACircle(Location location, double radius)

{

    var locations = new LocationCollection();

    const double earthRadius = GeoCodeCalc.EarthRadiusInMiles;

    var latitude = GeoCodeCalc.ToRadian(location.Latitude);

    //radians           

    var longitude = GeoCodeCalc.ToRadian(location.Longitude);

    //radians           

    var d = radius / earthRadius;

    // d = angular distance covered on earths surface      

    for (var x = 0; x <= 360; x++)

    {

        var brng = GeoCodeCalc.ToRadian(x); //radians       

        var latRadians = Math.Asin(Math.Sin(latitude) *

                        Math.Cos(d) + Math.Cos(latitude) *

                        Math.Sin(d) *

                        Math.Cos(brng));

        var lngRadians = longitude +

                            Math.Atan2(Math.Sin(brng) * Math.Sin(d) * Math.Cos(latitude),

                                       Math.Cos(d) - Math.Sin(latitude) * Math.Sin(latRadians));

        var pt = new Location(180.0 * latRadians / Math.PI, 180.0 * lngRadians / Math.PI);

        locations.Add(pt);

    }

    return locations;

}

Qui utilisent la classe GeoCodeCalc trouvée sur le blog de Chris Pietschmann, « Calculate Distance Between Geocodes in C# and JavaScript » :

using System;

 

namespace SilverlightMap

{

    public static class GeoCodeCalc

    {

        public const double EarthRadiusInMiles = 3956.0;

        public const double EarthRadiusInKilometers = 6367.0;

 

        public static double ToRadian(double val)

        {

            return val*(Math.PI/180);

        }

 

        public static double DiffRadian(double val1, double val2)

        {

            return ToRadian(val2) - ToRadian(val1);

        }

 

        ///<summary>

        /// Calculate the distance between two geocodes. Defaults to using Miles.

        ///</summary>

        public static double CalcDistance(double lat1, double lng1, double lat2, double lng2)

        {

            return CalcDistance(lat1, lng1, lat2, lng2, GeoCodeCalcMeasurement.Miles);

        }

 

        ///<summary>

        /// Calculate the distance between two geocodes.

        ///</summary>

        public static double CalcDistance(double lat1, double lng1, double lat2, double lng2, GeoCodeCalcMeasurement m)

        {

            var radius = EarthRadiusInMiles;

            if (m == GeoCodeCalcMeasurement.Kilometers)

            {

                radius = EarthRadiusInKilometers;

            }

            return radius*2*

                   Math.Asin(Math.Min(1,

                                      Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(lat1, lat2))/2.0), 2.0) +

                                                Math.Cos(ToRadian(lat1))*Math.Cos(ToRadian(lat2))*

                                                Math.Pow(Math.Sin((DiffRadian(lng1, lng2))/2.0), 2.0)))));

        }

    }

 

    public enum GeoCodeCalcMeasurement

    {

        Miles = 0,

        Kilometers = 1

    }

}

Enfin le constructeur de MainPage, crée le ViewModel SearchViewModel et l’associe au DataContext de MainPage. Il s’enregistre aussi sur l’événement PropertyChanged de SearchViewModel et appelle la méthode AddCircle lorsque la propriété Radius de SearchViewModel est modifiée. Comme cette propriété est attachée au slider, quand l’utilisateur bouge le slider le cercle est redessiné avec le nouveau rayon spécifié par Radius :

public partial class MainPage : UserControl

{

    private readonly SearchViewModel _searchViewModel;

 

    public MainPage()

    {

        InitializeComponent();

 

        _searchViewModel = new SearchViewModel();

        _searchViewModel.PropertyChanged += (sender, args) =>

                                                {

                                                    if (args.PropertyName == "Radius")

                                                    {

                                                        AddCircle(_searchViewModel.CurrentLocation,

                                                                  _searchViewModel.Radius,

                                                                  .5);

                                                    }

                                                };

        DataContext = _searchViewModel;

    }

 

Nous avons donc une application qui sépare la partie purement vue ; tracer un cercle, convertir des coordonnées écran en latitude et longitude de la partie ViewModel qui mémorise un rayon, la latitude et longitude et permet d’exécuter une recherche à l’aide du texte entré par l’utilisateur dans l’interface.
 
» Démarrer une discussion