• Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 29/04/2014

TP : Améliorer l'application météo avec géolocalisation et tuiles

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Que de nouvelles fonctionnalités découvertes. C’est le moment rêvé pour les mettre en pratique et améliorer notre superbe application météo de la partie précédente.

Nous allons jouer avec les tuiles et la géolocalisation. Vous êtes prêt ?
Alors c’est parti !

Instructions pour réaliser le TP

Franchement, une application météo où il faut absolument saisir le nom de la ville où nous nous trouvons, vous conviendrez avec moi que ce n'est pas terrible ! Alors que nos téléphones possèdent un GPS intégré… Nous allons donc proposer à l’utilisateur d’afficher la météo correspondant à sa position.
Donc première chose à faire, rajouter un bouton dans la barre d’application qui affichera la météo à votre position.
Ensuite, imaginons que je veuille rapidement connaître la météo de Paris et de Toulouse, histoire de savoir où il vaut mieux que je passe le week-end. Comme c’est quelque chose que je fais régulièrement, j’ai besoin de la possibilité de rajouter un raccourci vers ces villes. Mon application va donc permettre d’épingler des villes depuis la page de choix de villes. Si la ville a déjà été choisie, il faut que le bouton soit grisé. Bien sûr, le démarrage de l’application via des tuiles secondaires affichera directement la météo de la ville choisie. La tuile contiendra forcément le nom de la ville. Je vous laisse libre de choisir le modèle de tuile qui vous fait plaisir.

Voici un programme intéressant.

Attention avant de vous lancer, il va vous manquer quelque chose.
Le GPS fournit une position avec une longitude et une latitude alors que nous interrogions le service web de météo avec un nom de ville en entrée. Donc, soit il vous faut donc une solution pour transformer une position GPS en un nom de ville (on appelle cela du géocodage inversé), soit il faudrait que le service web accepte d’utiliser des coordonnées GPS.
Et vous savez quoi ? Il l’accepte. C'est la classe !
C’est le même principe, il suffit de remplacer le nom de la ville par les coordonnées sous la forme ci-dessous, avec la première coordonnée, la latitude, et la seconde, la longitude.

http://free.worldweatheronline.com/feed/weather.ashx?q=44.839073,-0.579113&format=json&num_of_days=5&key=MA_CLE_API

En résumé, je vous propose de réaliser les éléments suivants :
1. Utiliser le GPS pour permettre de vous géolocaliser,
2. Rajouter un bouton dans la barre d'application pour afficher la météo à votre position,
3. Rajouter deux tuiles de raccourci affichant la météo à Paris et à Toulouse.

Voilà, vous avez tout. À vous de jouer.

Correction

Je suis certain que vous vous en êtes très bien sorti avec ce petit exercice sympathique.
Voici ma correction, je ne vais pas tout re-détailler mais simplement les choses qui ont changé par rapport au TP précédent d’application météo.

1. Utiliser le GPS pour vous géolocaliser
Nous avons premièrement besoin d’utiliser le GPS afin de nous géolocaliser. Vous devez utiliser la classe Geolocator, via par exemple une variable privée :

private Geolocator geolocator;

Vous initialisez cette variable geolocator dans le constructeur :

public MainPage()
{
    InitializeComponent();
    geolocator = new Geolocator();
    DataContext = this;
}

2. Rajouter un bouton d'application pour afficher la météo à votre position
On rajoute également un bouton dans la barre d’application. Il possède une image (gps.png) et un texte (Ma position), et appelle une méthode (Position_Click) lorsqu’on clique dessus :

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True">
        <shell:ApplicationBarIconButton IconUri="/Assets/Icones/gps.png" Text="Ma position" Click="Position_Click"/>
        <shell:ApplicationBarIconButton IconUri="/Assets/Icones/add.png" Text="Ajouter" Click="Ajouter_Click"/>
        <shell:ApplicationBarIconButton IconUri="/Assets/Icones/feature.settings.png" Text="Choisir" Click="Choisir_Click"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Pour la peine, je me suis réalisé une petite image pour symboliser le GPS. Vous ne manquerez pas d’admirer ma maîtrise de Paint (image dans le chapitre suivant) ! N’oubliez pas de changer les propriétés de l'image pour mettre l'action de génération à « Contenu », et la propriété « Copier dans le répertoire » à « Copier si plus récent ».
Lorsque l'on clique sur le bouton, une méthode est appelée pour démarrer le service de localisation :

private async void Position_Click(object sender, EventArgs e)
{
    ChargementEnCours = true;
    try
    {
        Geoposition geoposition = await geolocator.GetGeopositionAsync(TimeSpan.FromMinutes(5), TimeSpan.FromSeconds(10));
        string position = geoposition.Coordinate.Latitude.ToString(NumberFormatInfo.InvariantInfo) + "," + geoposition.Coordinate.Longitude.ToString(NumberFormatInfo.InvariantInfo);
        WebClient client = new WebClient();
        string resultatMeteo = await client.DownloadStringTaskAsync(new Uri(string.Format("http://free.worldweatheronline.com/feed/weather.ashx?q={0}&format=json&num_of_days=5&key=MA_CLE_API", position, UriKind.Absolute)));
        TraiteResultats(resultatMeteo);
        NomVille = position;
    }
    catch (Exception)
    {
        MessageBox.Show("Vous devez activer le GPS pour pouvoir utiliser cette fonctionnalité");
        ChargementEnCours = false;
    }
}

Notez la présence du mot clé async dans la signature de la méthode. Il y a également un petit try/catch pour vérifier que le GPS est utilisable. Et puis j’appelle le service de localisation pour obtenir la position de l’utilisateur, que je fournis au service web. La méthode TraiteResultats contient la logique d’affichage qui a été factorisée, car utilisée à deux endroits :

private void TraiteResultats(string resultats)
{
    RootObject resultat = JsonConvert.DeserializeObject<RootObject>(resultats);
    List<Meteo> liste = new List<Meteo>();
    foreach (Weather temps in resultat.data.weather.OrderBy(w => w.date))
    {
        Meteo meteo = new Meteo { TemperatureMax = temps.tempMaxC + " °C", TemperatureMin = temps.tempMinC + " °C" };
        DateTime date;
        if (DateTime.TryParse(temps.date, out date))
        {
            meteo.Date = date.ToString("dddd dd MMMM");
            meteo.Temps = GetTemps(temps.weatherCode);
            WeatherIconUrl2 url = temps.weatherIconUrl.FirstOrDefault();
            if (url != null)
            {
                meteo.Url = new Uri(url.value, UriKind.Absolute);
            }
        }
        liste.Add(meteo);
    }
    ListeMeteo = liste;
    ChargementEnCours = false;
}

3. Rajouter deux tuiles secondaires affichant la météo à Paris et à Toulouse
Reste maintenant à gérer l’épinglage des villes. Pour ajouter un bouton, modifions la page XAML ChoisirVille que nous avons construite dans le TP précédent :

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <toolkit:ListPicker x:Name="Liste" ItemsSource="{Binding ListeVilles}" 
                        Header="Ville choisie :" 
                        CacheMode="BitmapCache">
    </toolkit:ListPicker>
    <Button Grid.Row="1" VerticalAlignment="Bottom" Content="Epingler" x:Name="BoutonEpingle" Tap="BoutonEpingle_Tap" />
</Grid>

Le clic sur le bouton ajoutera une tuile secondaire :

private void BoutonEpingle_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
    string ville = (string)IsolatedStorageSettings.ApplicationSettings["DerniereVille"];
    ShellTile tuileVille = ShellTile.ActiveTiles.FirstOrDefault(elt => elt.NavigationUri.ToString().Contains("ville=" + ville));
    if (tuileVille == null)
    {
        FlipTileData tuile = new FlipTileData
        {
            Title = "Météo " + ville,
        };

        ShellTile.Create(new Uri("/MainPage.xaml?ville=" + ville, UriKind.Relative), tuile, false);
        BoutonEpingle.IsEnabled = false;
    }  
}

Bien sûr, dans la query string, on indique le nom de la ville afin de pouvoir l’exploiter au démarrage de l’application. Dans l'évènement permettant de choisir sa ville, on en profite pour désactiver les boutons des villes qui ont déjà une tuile de raccourci :

private void Liste_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (Liste.SelectedItem != null)
    {
        IsolatedStorageSettings.ApplicationSettings["DerniereVille"] = (string)Liste.SelectedItem;
        ShellTile tuileVille = ShellTile.ActiveTiles.FirstOrDefault(elt => elt.NavigationUri.ToString().Contains("ville=" + (string)Liste.SelectedItem));
        BoutonEpingle.IsEnabled = tuileVille == null;
    }
}

Il ne reste plus qu’à exploiter l’information passée en query string au démarrage de l’application afin de charger la météo de la bonne ville. On peut le faire avec la méthode OnNavigatedTo par exemple :

protected async override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    string ville;
    if (NavigationContext.QueryString.TryGetValue("ville", out ville))
    {
        NavigationContext.QueryString.Remove("ville");
        IsolatedStorageSettings.ApplicationSettings["DerniereVille"] = ville;
    }

    if (IsolatedStorageSettings.ApplicationSettings.Contains("DerniereVille"))
    {
        Information.Visibility = Visibility.Collapsed;
        ChargementEnCours = true;
        NomVille = (string)IsolatedStorageSettings.ApplicationSettings["DerniereVille"];
        WebClient client = new WebClient();
        try
        {
            ChargementEnCours = false;
            string resultatMeteo = await client.DownloadStringTaskAsync(new Uri(string.Format("http://free.worldweatheronline.com/feed/weather.ashx?q={0}&format=json&num_of_days=5&key=MA_CLE_API", NomVille.Replace(' ', '+')), UriKind.Absolute));

            TraiteResultats(resultatMeteo);
        }
        catch (Exception)
        {
            MessageBox.Show("Impossible de récupérer les informations de météo, vérifiez votre connexion internet");
        }
    }
    else
        Information.Visibility = Visibility.Visible;

    base.OnNavigatedTo(e);
}

Et le tour est joué. Voici une belle application météo qui exploite les infos de géolocalisation et qui nous permet même d’avoir des raccourcis vers nos villes favorites. Pas mal comme TP, non ?

Aller plus loin

Vous aurez remarqué que lorsqu’on consulte la météo par rapport à ses coordonnées GPS, on ne dispose plus du nom de la ville. En général, ce n’est pas trop grave car nous savons où nous sommes … mais on pourrait améliorer notre application pour afficher le nom de la ville où nous nous trouvons, et pas les coordonnées GPS.
Il suffit d’utiliser le géocodage inversé — j’en ai parlé en introduction du TP — et il se trouve qu’il existe des services gratuits de géocodage inversé. Prenons par exemple celui de Google qui est assez facile à utiliser : si vous naviguez sur http://maps.googleapis.com/maps/api/ge [...] 3&sensor=true vous pourrez obtenir du code JSON qui nous indique notamment dans quelle ville nous nous trouvons. Il n’y a plus qu’à modifier notre code pour faire l’appel à ce service web et nous pourrons obtenir la ville où nous sommes, ce qui nous permettra d’ailleurs de la stocker dans le dossier local :

private async void Position_Click(object sender, EventArgs e)
{
    ChargementEnCours = true;
    WebClient client = new WebClient();
    string position;
    try
    {
        Geoposition geoposition = await geolocator.GetGeopositionAsync(TimeSpan.FromMinutes(5), TimeSpan.FromSeconds(10));
        position = geoposition.Coordinate.Latitude.ToString(NumberFormatInfo.InvariantInfo) + "," + geoposition.Coordinate.Longitude.ToString(NumberFormatInfo.InvariantInfo);
        string resultatMeteo = await client.DownloadStringTaskAsync(new Uri(string.Format("http://free.worldweatheronline.com/feed/weather.ashx?q={0}&format=json&num_of_days=5&key=MA_CLE_API", position, UriKind.Absolute)));
        TraiteResultats(resultatMeteo);
    }
    catch (Exception)
    {
        MessageBox.Show("Vous devez activer le GPS pour pouvoir utiliser cette fonctionnalité");
        ChargementEnCours = false;
        return;
    }
    try
    {
        string json = await client.DownloadStringTaskAsync(new Uri(string.Format("http://maps.googleapis.com/maps/api/geocode/json?latlng={0}&sensor=true", position), UriKind.Absolute));

        GeocodageInverse geocodageInverse = JsonConvert.DeserializeObject<GeocodageInverse>(json);
        if (geocodageInverse.status == "OK")
        {
            AddressComponent adresse = (from result in geocodageInverse.results
                                        from addressComponent in result.address_components
                                        from type in addressComponent.types
                                        where type == "locality"
                                        select addressComponent).FirstOrDefault();
            if (adresse == null)
            {
                MessageBox.Show("Impossible de déterminer le géocodage inversé");
                ChargementEnCours = false;
            }
            else
            {
                NomVille = adresse.long_name;
                IsolatedStorageSettings.ApplicationSettings["DerniereVille"] = adresse.long_name;
            }
        }
    }
    catch (Exception)
    {
        MessageBox.Show("Impossible de déterminer le géocodage inversé");
    }
}

Le résultat est montré dans la figure suivante.

Géocodage inversé sur l'application météo
Géocodage inversé sur l'application météo

Moi, je suis fan … et vous ?

Exemple de certificat de réussite
Exemple de certificat de réussite