Intégrer les services Bing Maps dans son application Metro Windows 8 en XAML/C# (2/3)

Cet article fait suite à une 1ère partie dans laquelle nous avons mis en place une application Metro intégrant une carte Bing Maps.

Dans cette 2ème partie je vous propose de découvrir comment utiliser les services REST de Bing Maps afin d’intégrer des fonctionnalités de recherche de localités. Afin d’être “Metro Compliant” nous nous intègrerons au Search Charm de Windows 8.

Pour une documentation complète des services REST Bing Maps je vous invite à consulter la documentation en ligne sur MSDN.

Pour retrouver la position d’un lieu, Bing Maps met à disposition plusieurs services web : recherche par adresse, par requête, par point.

1 premier web service permet de retrouver une localisation à partir d’une adresse. Nous devons donc fournir à ce web service des paramètres tels que le pays, le code postal, la ville ou encore la rue. Voici un exemple d’URL permettant de retrouver la place du capitole à Toulouse : http://dev.virtualearth.net/REST/v1/Locations/FR/31000/toulouse/place%20du%20capitole?maxResults=5&key=XXXXXXXXX

Notez le paramètre key : vous devez indiquer ici la clé d’accès aux services Bing Maps, que vous vous êtes créés depuis le portail.

Voici la réponse à cette requête au format json :

[BuildActivity(HostEnvironmentOption.All)]
{
    "authenticationResultCode": "ValidCredentials",
    "brandLogoUri": "http://dev.virtualearth.net/Branding/logo_powered_by.png",
    "copyright": "Copyright © 2012 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.",
    "resourceSets": [
        {
            "estimatedTotal": 1,
            "resources": [
                {
                    "__type": "Location:http://schemas.microsoft.com/search/local/ws/rest/v1",
                    "bbox": [
                        43.60056228242932,
                        1.4366889613939073,
                        43.608287717570676,
                        1.4509130386060924
                    ],
                    "name": "Place du Capitole, 31000 Toulouse",
                    "point": {
                        "type": "Point",
                        "coordinates": [
                            43.604425,
                            1.443801
                        ]
                    },
                    "address": {
                        "addressLine": "Place du Capitole",
                        "adminDistrict": "Midi-Pyrénées",
                        "adminDistrict2": "Haute-Garonne",
                        "countryRegion": "France",
                        "formattedAddress": "Place du Capitole, 31000 Toulouse",
                        "locality": "Toulouse",
                        "postalCode": "31000"
                    },
                    "confidence": "High",
                    "entityType": "RoadBlock",
                    "geocodePoints": [
                        {
                            "type": "Point",
                            "coordinates": [
                                43.604425,
                                1.443801
                            ],
                            "calculationMethod": "Interpolation",
                            "usageTypes": [
                                "Display",
                                "Route"
                            ]
                        }
                    ],
                    "matchCodes": [
                        "Good"
                    ]
                }
            ]
        }
    ],
    "statusCode": 200,
    "statusDescription": "OK",
    "traceId": "985f20b23f014beb91b6fda5ee2c4c46|BL2M002306|02.00.138.500|BL2MSNVM001813, BL2MSNVM001263"
}

Ici la requête a renvoyé un seul résultat, dans lequel nous retrouvons un objet de type Point avec les coordonnées (43.604425, 1.443801).

Un petit coup de Bing Maps pour vérifier l’exactitude de ces coordonnées…


bingmaps1

Pour consulter la documentation détaillée de ce service de localisation par adresse, suivez ce lien.

Le second service de recherche de localisation se fait par requête. Une requête est tout simplement un champ texte que l’on soumet au service pour qu’il nous retourne les résultats correspondants. On peut par exemple effectuer la recherche d’un monument (ex: “Tour Eiffel”) ou encore d’une adresse (ex: “place du capitole, toulouse”).

En voici un exemple : http://dev.virtualearth.net/REST/v1/Locations?query=place%20du%20capitole%20toulouse&maxResults=5&key=XXXXXXXXX

Nous allons utiliser ce dernier service dans notre application et nous intégrer dans le charm de recherche de Windows 8 afin d’obtenir le résultat ci-dessous :


bingmaps2

Pour implémenter le charm de recherche, nous devons tout d’abord le déclarer au niveau du Manifest de l’application. Double-cliquez sur le fichier Package.appxmanifest, puis cliquez sur l’onglet Declarations. Dans la liste “Available Declarations” sélectionnez Search puis cliquez sur Add :


bingmaps3

Notre application est composée d’une unique page, MainPage, contenant la carte Bing Maps. C’est donc cette page qui traitera les recherches qu’effectue l’utilisateur.

Dans la classe MainPage, ajoutez une méthode SearchLocation qui prend en paramètre la requête de l’utilisateur :

[BuildActivity(HostEnvironmentOption.All)]
private async void SearchLocation(string query)
{
 
}

Pour interroger le service REST Bing Maps nous allons utiliser un client HTTP, via la classe HttpClient de l’espace de nom System.Net.Http. Cette classe possède une méthode GetStringAsync, prenant en paramètre une Uri et renvoyant une chaine de caractère représentant la réponse :

[BuildActivity(HostEnvironmentOption.All)]
string uri = "http://dev.virtualearth.net/REST/v1/Locations/" + query + "?maxResults=1&key=XXXXXXXXXXXXX";
 
var client = new HttpClient();
var result = await client.GetStringAsync(uri);

Dans notre variable result nous avons un flux json sous cette forme :

[BuildActivity(HostEnvironmentOption.All)]
{
    "authenticationResultCode": "ValidCredentials",
    "brandLogoUri": "http://dev.virtualearth.net/Branding/logo_powered_by.png",
    "copyright": "Copyright © 2012 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.",
    "resourceSets": [
        {
            "estimatedTotal": 1,
            "resources": [
                {
                    "__type": "Location:http://schemas.microsoft.com/search/local/ws/rest/v1",
                    "bbox": [
                        43.60056228242932,
                        1.4366889613939073,
                        43.608287717570676,
                        1.4509130386060924
                    ],
                    "name": "Place du Capitole, 31000 Toulouse",
                    "point": {
                        "type": "Point",
                        "coordinates": [
                            43.604425,
                            1.443801
                        ]
                    },
                    "address": {
                        "addressLine": "Place du Capitole",
                        "adminDistrict": "Midi-Pyrénées",
                        "adminDistrict2": "Haute-Garonne",
                        "countryRegion": "France",
                        "formattedAddress": "Place du Capitole, 31000 Toulouse",
                        "locality": "Toulouse",
                        "postalCode": "31000"
                    },
                    "confidence": "High",
                    "entityType": "RoadBlock",
                    "geocodePoints": [
                        {
                            "type": "Point",
                            "coordinates": [
                                43.604425,
                                1.443801
                            ],
                            "calculationMethod": "Interpolation",
                            "usageTypes": [
                                "Display",
                                "Route"
                            ]
                        }
                    ],
                    "matchCodes": [
                        "Good"
                    ]
                }
            ]
        }
    ],
    "statusCode": 200,
    "statusDescription": "OK",
    "traceId": "4558270dcc9949f0a5790553ede6281c|BL2M002301|02.00.138.500|BL2MSNVM002816, BL2MSNVM002809, BL2MSNVM003152, BL2MSNVM003206, BL2MSNVM001262"
}

Pour parcourir ce flux Json, nous pouvons utiliser les classes de l’espace de nom Windows.Data.Json, et notamment la classe JsonValue qui possède une méthode Parse :

[BuildActivity(HostEnvironmentOption.All)]
JsonValue value = Windows.Data.Json.JsonValue.Parse(result);

Une fois que nous avons notre objet JsonValue, il nous faut le parcourir pour récupérer le nom et les coordonnées des différents résultats de localisation. L’analyse du flux json présenté ci-dessus, nous permet d’en déduire le code suivant :

[BuildActivity(HostEnvironmentOption.All)]
var resources = value.GetObject()["resourceSets"].GetArray()[0].GetObject()["resources"].GetArray();
 
foreach (var item in resources)
{
    var name = item.GetObject()["name"].GetString();
 
    var coordinates = item.GetObject()["point"].GetObject()["coordinates"].GetArray();
 
    var latitude = coordinates[0].GetNumber();
    var longitude = coordinates[1].GetNumber();
    
}

Il ne reste plus qu’à créer un objet de type Location, l’objet Pushpin associé, de l’ajouter à la carte et de le positionner :

[BuildActivity(HostEnvironmentOption.All)]
var location = new Bing.Maps.Location(latitude, longitude);
 
Pushpin pushpin = new Pushpin() { Text = "!" };

MapLayer.SetPosition(pushpin, location);
ToolTipService.SetToolTip(pushpin, name);
myMaps.Children.Add(pushpin);

Le code complet de la méthode SearchLocation :

[BuildActivity(HostEnvironmentOption.All)]
private async void SearchLocation(string query)
{
    myMaps.Children.Clear();
 
    string uri = "http://dev.virtualearth.net/REST/v1/Locations/" + query + "?maxResults=1&key=XXXXXXXXX";
 
    try
    {
        var client = new HttpClient();
        var result = await client.GetStringAsync(uri);
 
        JsonValue value = Windows.Data.Json.JsonValue.Parse(result);
 
        var resources = value.GetObject()["resourceSets"].GetArray()[0].GetObject()["resources"].GetArray();
 
        Location firstLocation = null;
 
        foreach (var item in resources)
        {
            var name = item.GetObject()["name"].GetString();
 
            var coordinates = item.GetObject()["point"].GetObject()["coordinates"].GetArray();
 
            var latitude = coordinates[0].GetNumber();
            var longitude = coordinates[1].GetNumber();
 
            var location = new Bing.Maps.Location(latitude, longitude);
 
            Pushpin pushpin = new Pushpin() { Text = "!" };
 
            MapLayer.SetPosition(pushpin, location);
            ToolTipService.SetToolTip(pushpin, name);
            myMaps.Children.Add(pushpin);
 
            if (firstLocation == null && resources.Count == 1)
                firstLocation = location;
        }
 
        myMaps.SetView(firstLocation, 10, 10, TimeSpan.FromMilliseconds(1000));
    }
    catch 
    {
        MessageDialog dialog = new MessageDialog("Unable to find location");
        dialog.ShowAsync();
    }
}

L’évènement levé par une recherche utilisateur via le charm sera levé dans la classe App. Dans la classe MainPage, il nous faut ajouter un point d’entrée, qui permettra à l’application d’activer la recherche. Ajoutez une méthode statique Activate, qui récupère l’instance en cours de la page MainPage et qui appelle la méthode SearchLocation :

[BuildActivity(HostEnvironmentOption.All)]
public static void Activate(string queryText)
{
    var frame = Window.Current.Content as Frame;
 
    if (frame == null)
    {
        MainPage page = new MainPage();
        page.SearchLocation(queryText);
        Window.Current.Content = page;
    }
    else if (!(frame.Content is MainPage))
    {
        frame.Navigate(typeof(MainPage), queryText);
    }
    else
    {
        var mainPage = (MainPage)frame.Content;
        mainPage.SearchLocation(queryText);
    }
 
    Window.Current.Activate();
}

La dernière étape consiste à récupérer l’évènement levé lorsque l’utilisateur effectue une recherche depuis le Charm et à appeler la méthode Activate que nous venons d’écrire. Pour se brancher sur la recherche, il suffit d’implémenter la méthode OnSearchActivated dans la classe App :

[BuildActivity(HostEnvironmentOption.All)]
protected override void OnSearchActivated(SearchActivatedEventArgs args)
{
    MainPage.Activate(args.QueryText);
}

Vous pouvez maintenant tester la recherche. Déployer l’application, afficher le Charm et effectuer votre recherche.

Pour afficher le panel de recherche depuis notre application nous pouvons ajouter dans la page MainPage le bouton suivant dans le contrôle AppBar (à côté du bouton My Location) :

[BuildActivity(HostEnvironmentOption.All)]

Voici le résultat visuel :


Bingmaps5

Et pour activer le panel de recherche depuis le code, il faut utiliser la classe Windows.ApplicationModel.Search.SearchPane comme ceci :

[BuildActivity(HostEnvironmentOption.All)]
private void btnSearch_Click(object sender, RoutedEventArgs e)
{
    Windows.ApplicationModel.Search.SearchPane.GetForCurrentView().Show();
}

Nous en avons terminé pour la recherche d’un lieu. Dans la 3ème partie nous verrons comment récupérer et afficher la liste des incidents d’une zone géographique, ainsi que la recherche d’itinéraire.

En attendant, si vous recherchez plus d’informations sur le développement Windows 8, et plus particulièrement sur le Charm de recherche, je vous invite à consulter (en anglais) cet article de la MSDN, ainsi que les recommandations pour l’implémentation de ce Charm.

Et enfin, du côté des ressources françaises, retrouvez sur le site de la communauté Windows 8 une série d’articles sur l’implémentation des Charms.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s