Partager via


Cet article a fait l'objet d'une traduction automatique.

Saisie tactile et accès

Assemblage des vignettes de cartes Bing dans Windows Phone

Charles Petzold

Télécharger l'exemple de code

Charles PetzoldLe détecteur de mouvement en Windows Phone consolide les informations de la boussole et l'accéléromètre pour créer une matrice de rotation qui décrit l'orientation du téléphone dans l'espace 3D du téléphone.

Récemment, j'ai commencé à réfléchir à comment l'orientation du téléphone pourrait être utilisée en combinaison avec Bing Maps.J'ai prévu un mashup quickie, mais l'emploi s'est avéré pour être un peu plus complexe.

Si vous avez expérimenté le programme standard de cartes sur votre Windows Phone, vous savez que l'affichage est généralement alignée afin que le nord est vers le haut du téléphone.(La seule exception est lorsque vous utilisez la carte pour les directions à un endroit, auquel cas la carte est orientée pour indiquer la direction que vous voyagiez.) Jusqu'à north est la convention pour les cartes, bien sûr, mais dans certains cas, que vous pouvez la carte du téléphone pour faire pivoter par rapport au téléphone alors que vers le nord sur la carte est effectivement pointant vers le Nord.

Il semble si simple, non ?

Carte des limites de contrôle

Dans ma quête pour mettre en œuvre un plan de rotation sur le téléphone, j'ai commencé avec le contrôle Silverlight de Bing Maps pour Windows Phone, dont le centre est un contrôle nommé tout simplement la carte dans l'espace de noms Microsoft.Phone.Controls.Maps.

Pour commencer avec le contrôle de la carte (ou quoi que ce soit portant sur l'accès par programme à Bing Maps) vous devez vous inscrire sur le Bing Maps Account Center à bingmapsportal.com.C'est une ligne droite­vers l'avant pour obtenir une clé d'informations d'identification qui donne votre accès aux programmes à Bing Maps.

Dans le projet Windows Phone, qui utilise le contrôle de la carte, vous aurez besoin d'une référence à l'assembly Microsoft.Phone.Controls.Maps et vous voudrez probablement une déclaration d'espace de noms XML dans le fichier XAML (sur une seule ligne) :

xmlns:maps="clr-namespace:Microsoft.Phone.Controls.Maps;
  assembly=Microsoft.Phone.Controls.Maps"

L'instanciation d'une carte de base est alors trivial :

<maps:Map CredentialsProvider="credentials-key" />

Insérez la clé informations d'identification réelles que vous avez obtenus depuis le Bing Maps Account Center.

Oui, obtenir une carte sur l'écran est facile, mais quand j'ai cherché des moyens pour la faire tourner, je suis venu sec. Bien sûr, la classe Map hérite une propriété d'en-tête de la classe MapBase, mais apparemment cette propriété s'applique uniquement aux « œil de l'oiseau » cartes et le contrôle de la carte prend en charge uniquement le standard pour la route et des vues aériennes.

Bien sûr, il est assez trivial pour faire pivoter le contrôle de la carte en définissant un objet RotateTransform à la propriété RenderTransform, mais je n'étais pas du tout satisfait de cette solution. Tout d'abord, il avait tendance à obscurcir la notice de copyright en bas de la carte et la pratique qui semblait être en violation des conditions que j'ai accepté lors de l'obtention d'une clé informations d'identification.

J'ai décidé d'abandonner le contrôle des cartes et plutôt tenter ma chance sur un niveau beaucoup plus faible en accédant à des Services SOAP de Bing Maps. Cet ensemble de services Web permet à un programme obtenir les tuiles de bitmap réelle d'où les grandes cartes sont construits.

Accéder au Service SOAP

Dans un service Web est construit autour de SOAP Simple Object Access Protocol (), les informations sont transférées entre votre programme et le serveur à l'aide de documents XML. Ces documents comportent très souvent des structures de données complexes, donc au lieu de traiter directement les données XML, une approche beaucoup plus facile est d'avoir Visual Studio crée une classe proxy pour vous. Cela permet à votre programme accéder au service Web avec les classes c# normales (quoique asynchrone) et les appels de méthode.

L'interface Services Bing Maps SOAP est documentée à bit.ly/S3R4lGet se compose de quatre services distincts :

  • Service géocode : Correspondre les adresses avec la longitude et la latitude.
  • Service d'imagerie : Obtenir des cartes et des tuiles.
  • Service de l'itinéraire : Obtenir les directions.
  • Service de recherche : Localiser les personnes et les entreprises.

J'étais seulement intéressé par le Service d'imagerie.

Le code téléchargeable de cet article est un projet unique nommé RotatingMapTiles. J'ai ajouté un proxy pour le Service d'imagerie à ce projet en sélectionnant Ajouter une référence de Service dans le menu projet. Dans le champ adresse de la boîte de dialogue Ajouter une référence de Service, j'ai copié l'URL répertoriée dans la section adresses de Services Bing Maps SOAP de la documentation et pressé d'aller. Lorsque le service était situé, je l'ai donné un nom de ImageryService dans le champ Namespace.

Le code c# généré pour ce service a un espace de noms est l'espace de noms du projet normale, suivie de l'espace de noms que vous spécifiez lorsque vous créez la référence de service, alors le fichier MainPage.xaml.cs dans le programme de RotatingMapTile contient les éléments suivants à l'aide de la directive :

using RotatingMapTiles.ImageryService;

Le Service d'imagerie prend en charge deux types de demandes portant sur les appels de fonctions GetMapUriAsync et GetImageryMetadataAsync. Le premier appel permet d'obtenir des mappages statiques d'un niveau de zoom, la taille et l'emplacement particulier, tandis que la seconde fonctionne à un niveau inférieur. Parmi les métadonnées, que ce deuxième appel retourne est un modèle URI qui vous permet d'accéder les tuiles bitmap actuelles utilisées pour assembler tous le Bing maps.

Figure 1 montre le gestionnaire chargé de la classe MainPage dans le RotatingMapTiles faisant deux appels au service Web d'imagerie pour obtenir les métadonnées pour les styles MapStyle.Road et MapStyle.Aerial du projet. (MapStyle.Birdseye est également disponible mais il est beaucoup plus complexe à utiliser).

Figure 1 Code d'accès le Service Bing Maps Imagery Web

void OnMainPageLoaded(object sender, RoutedEventArgs e)
{
  // Initialize the Bing Maps imagery service
  ImageryServiceClient imageryServiceClient =
    new ImageryServiceClient("BasicHttpBinding_IImageryService");
    imageryServiceClient.GetImageryMetadataCompleted +=
      GetImageryMetadataCompleted;
  // Make a request for the road metadata
  ImageryMetadataRequest request = new ImageryMetadataRequest
  {
    Credentials = new Credentials
    {
      ApplicationId = "credentials-key"
    },
    Style = MapStyle.Road
  };
  imageryServiceClient.GetImageryMetadataAsync(request, "road");
  // Make a request for the aerial metadata
  request.Style = MapStyle.Aerial;
  imageryServiceClient.GetImageryMetadataAsync(request, "aerial");
}

Vous aurez besoin de votre propre clé informations d'identification de Bing Maps pour remplacer l'espace réservé.

La figure 2 montre le gestionnaire pour l'événement Completed de l'appel asynchrone. L'information inclut un URI pour un bitmap avec le logo Bing, il est donc facile à crédit Bing Maps à l'écran du programme.

Figure 2 le gestionnaire terminé pour le Service Web de Bing Maps

void GetImageryMetadataCompleted(object sender,
   GetImageryMetadataCompletedEventArgs args)
{
  if (!args.Cancelled && args.Error == null)
  {
    // Get the "powered by" bitmap
    poweredByBitmap.UriSource = args.Result.BrandLogoUri;
    poweredByDisplay.Visibility = Visibility.Visible;
    // Get the range of map levels available
    ImageryMetadataResult result = args.Result.Results[0];
    minimumLevel = result.ZoomRange.From;
    maximumLevel = result.ZoomRange.To;
    // Get the URI and make some substitutions
    string uri = result.ImageUri;
    uri = uri.Replace("{subdomain}", result.ImageUriSubdomains[0]);
    uri = uri.Replace("&token={token}", "");
    uri = uri.Replace("{culture}", "en-US");
    if (args.UserState as string == "road")
      roadUriTemplate = uri;
    else
      aerialUriTemplate = uri;
    if (roadUriTemplate != null && aerialUriTemplate != null)
      RefreshDisplay();
  }
  else
  {
    errorTextBlock.Text =
      "Cannot access Bing Maps: " + args.Error.Message;
  }
}

L'autre URI est celui que vous utiliserez pour accéder aux carreaux des carte. Il ya des URIs distinct pour la route et les vues aériennes et l'URI contient un espace réservé pour un numéro qui identifie précisément le carreau que vous voulez.

Cartes et carreaux

Les tuiles qui forment la base de Bing Maps sont des bitmaps qui sont toujours 256 pixels carrés. Chaque tuile est associé à un certain niveau de zoom, la latitude et la longitude et contient une image d'une zone carrée sur la surface de la terre aplatie à l'aide de la projection de Mercator courante.

La vue zoom-out plus extrême est appelée niveau 1, et seulement quatre tuiles sont nécessaires pour couvrir le monde entier — ou du moins la partie du monde avec latitudes entre positif et négatif 85.05 ° — comme indiqué dans Figure 3.

The Four Level 1 Tiles
Figure 3 les quatre tuiles de niveau 1

Je vais vous expliquer les chiffres sur les carreaux dans un instant. Parce que les tuiles sont 256 pixels carrés, à l'Équateur, chaque pixel est équivalent à environ 49 km.

Niveau 2 est plus granulaire, et 16 tuiles couvrent désormais la terre, comme le montre Figure 4.

The 16 Level 2 Tiles
La figure 4 le 16 niveau 2 tuiles

Les carreaux en Figure 4 sont également 256 pixels carrés, donc à l'Équateur, chaque pixel est environ 24 km. Notez que chaque tuile en niveau 1 couvre la même superficie que 4 tuiles au niveau 2.

Ce régime se poursuit : Niveau 3 a 64 tuiles, niveau 4 a 256 tuiles et vers le haut et vers le haut et jusqu'à niveau 21, qui couvre la terre avec un total de plus de 4 billions de carreaux — 2 millions horizontalement et 2 millions verticalement pour résolution de 3 pouces par pixel (à l'Équateur).

Les tuiles de numérotation

Parce qu'au moins quelques-uns de ces centaines de milliards de tuiles doivent être référencées individuellement par un programme qui souhaite les utiliser, ils doivent être identifiés de manière claire et cohérente. Il y a trois dimensions impliquées — niveau de zoom, la latitude et la longitude et une considération pratique aussi bien : Pour limiter les accès disque sur le serveur, carreaux associée à la même zone doit être conservé près les uns des autres, qui implique un système de numérotation unique qui englobe les trois dimensions d'une façon très intelligente.

L'ingénieux système de numérotation car ils correspondent à carreaux est appelé un « quadkey ». L'article de MSDN Library, « Bing Maps tuile System » par Joe Schwartz (bit.ly/SxVojI) est une très bonne explication du système (y compris le code utile), mais je vais prendre une approche quelque peu différente ici.

Chaque tuile a une quadkey unique. La tuile URI obtenu à partir du service Web contient une chaîne d'espace réservé « {quadkey} ». Avant d'utiliser l'un des URI pour accéder une tuile, vous devez remplacer cet espace réservé avec un quadkey réel.

Figure 3 et Figure 4 montrer les quadkeys de zoom niveaux 1 et 2.  Des zéros non significatifs sont importants dans quadkeys. (En effet, vous pouvez penser la quadkey comme une chaîne au lieu d'un nombre.) Le nombre de chiffres dans un quadkey est toujours égal au niveau de zoom de la tuile. 21 Chiffres quadkeys sont identifiés les tuiles au niveau 21.

Les chiffres individuels d'un quadkey sont toujours 0, 1, 2 ou 3. Ainsi, le quadkey est en réalité un nombre de base-4. Regardez ces quatre chiffres binaires (00, 01, 10, 11) et comment ils s'affichent dans un groupe de quatre tuiles. Dans chaque chiffre de base-4, le deuxième bit est vraiment une coordonnée horizontale et le premier bit est une coordonnée verticale. Les bits correspondent à une longitude et la latitude, qui sont effectivement entrelacés dans le quadkey.

Chaque tuile de niveau 1 couvre la zone même comme un groupe de quatre tuiles au niveau 2. Vous pouvez penser de la dalle au niveau 1 comme un "parent" quatre "enfants" du niveau 2. Le quadkey de la dalle enfant commence toujours par les mêmes chiffres que son parent et puis ajoute un autre chiffre (0, 1, 2 ou 3) en fonction de son emplacement dans la zone de son parent. Cours de parent à enfant est un zoom vers le haut. Zoom vers le bas est similaire : Pour n'importe quel enfant quadkey, vous obtenez le quadkey parent simplement par ébranchage off le dernier chiffre.

Voici comment dériver un quadkey d'une latitude longitude géographique réelle.

Gammes de longitude de-180 ° à l'Inter­national ligne Date, puis recommencer est à 180 ° sur la ligne de données internationale des augmentations. Pour toute longitude, tout d'abord calculer une longitude relative comprise entre 0 et 1 avec 0,5 représentant le méridien d'origine :

double relativeLongitude = (180 + longitude) / 360;

Maintenant que convertir un entier d'un nombre fixe de bits :

int integerLongitude =
  (int)(relativeLongitude * (1 << BITRES));

Dans mon programme, j'ai défini BITRES à 29 pour les niveaux de zoom 21 plus de 8 bits pour la taille en pixels de la dalle. Ainsi, cet entier identifie une longitude précise au pixel plus proche de la dalle du plus haut niveau de zoom.

Le calcul d'integerLatitude est un peu plus complexe car la projection cartographique Mercator compresse latitudes comme vous obtenez plus loin de l'Équateur :

double sinTerm = Math.Sin(Math.PI * latitude / 180);
double relativeLatitude =
  0.5 - Math.Log((1 + sinTerm) / (1 - sinTerm)) 
    / (4 * Math.PI);
int integerLatitude = (int)(relativeLatitude * (1 << BITRES));

L'integerLatitude va de 0 à 85.05 ° au nord de l'Équateur à la valeur maximale à 85.05 ° au sud de l'Équateur.

Le centre de Central Park à New York City a une longitude de-73.965368 ° et 40.783271 ° de latitude. Les valeurs relatives sont (à quelques décimales) 0.29454 et 0.37572. Sont les valeurs de longitude et de latitude 29 bits entier en (montré en binaire et regroupés pour la lisibilité) :

0 1100 0000 0101 1110 1011 0000 0000

0 1100 0000 0101 1110 1011 0000 0000

Supposons que l'on veuille une tuile qui indique le centre de Central Park dans un zoom au niveau 12. Prenez le top 12 bits des latitudes et des longitudes entier (attention — les chiffres suivants sont regroupés un peu différemment que les versions 29 bits) :

0100 1011 0110

0110 0000 0010

Voici les deux nombres binaires, mais il faut les combiner pour former un nombre de base-4. Il n'y a aucun moyen de le faire dans le code à l'aide d'opérateurs arithmétiques simples. Vous avez besoin d'un peu de routine qui traverse les bits individuels et construit un entier long ou une chaîne. À titre d'illustration, vous pouvez simplement doubler tous les bits à la latitude et ajoutez les deux valeurs comme si elles étaient des valeurs de base-4 :

0100 1011 0110

0220 0000 0020

0320 1011 0130

Le résultat est le quadkey à 12 chiffres, vous devrez remplacer l'espace réservé « {quadkey} » dans l'URI que vous téléchargez depuis le service Web pour accéder à la mosaïque de la carte.

Figure 5 présente une routine pour construire une quadkey des latitudes et longitudes entier tronqué. Pour plus de clarté, j'ai séparé la logique en sections qui génèrent un entier long quadkey et une chaîne de quadkey.

Routine de la figure 5 pour calculer une Quadkey

string ToQuadKey(int longitude, int latitude, int level)
{
  long quadkey = 0;
  int mask = 1 << (level - 1);
  for (int i = 0; i < level; i++)
  {
    quadkey <<= 2;
    if ((longitude & mask) != 0)
      quadkey |= 1;
    if ((latitude & mask) != 0)
      quadkey |= 2;
    mask >>= 1;
  }
  strBuilder.Clear();
  for (int i = 0; i < level; i++)
  {
    strBuilder.Insert(0, (quadkey & 3).ToString());
    quadkey >>= 2;
  }
  return strBuilder.ToString();
}

Figure 6 montre la route et aérienne carreaux pour cette quadkey. Le centre du parc Central est en fait le chemin vers le bas au bas de ces images, un peu à gauche du centre. Ceci est prévisible des latitudes et longitudes entier. Regardez les 8 bits de la longitude entier après les 12 premiers bits : Les bits sont 0111 0000 ou 112. Les 8 bits de la latitude sont 1111 0101 ou 245. Cela signifie que le centre du parc Central est le 112e pixel de la gauche et la 245e pixel vers le bas dans les tuiles.

The Tiles for Quadkey “032010110130”
Figure 6 les tuiles pour Quadkey « 032010110130 »

Carrelage carreaux

Une fois que vous avez tronqué un entier longitude et latitude à un certain nombre de bits correspondant à un pourcentage de zoom particulier, obtenir des tuiles adjacentes est un clin d'oeil : Simplement incrémenter et décrémenter la latitude et la longitude entiers et quadkeys nouvelle forme.

Vous avez déjà vu les trois méthodes de la classe MainPage du projet RotatingMapTiles. Le programme utilise un GeoCoordinateWatcher pour obtenir la longitude et la latitude du téléphone et convertit les coordonnées en valeurs entières, comme illustré précédemment. La barre d'application comporte trois boutons : pour basculer entre les vues aériennes et de la route et pour augmenter ou diminuer le niveau de zoom.

Le programme n'a aucune autre interface tactile en plus des boutons. Toujours, il affiche l'emplacement de GeoCoordinateWatcher dans le centre de l'écran et construit la carte totale avec 25 éléments dans un tableau de 5 x 5, une configuration qui remplit toujours l'écran 480 x 800 pixel, même avec la rotation de l'Image. La classe MainPage crée ces 25 éléments d'images et les objets BitmapImage dans son constructeur.

Chaque fois que le GeoCoordinateWatcher arrive avec un nouvel emplacement, ou les changements de style de zoom au niveau ou à la carte, la méthode RefreshDisplay dans Figure 7 est appelée. Cette méthode montre comment les nouveaux URI sont obtenus et simplement définie pour les objets existants du BitmapImage.

Figure 7 la méthode de RefreshDisplay en RotatingMapTiles

void RefreshDisplay()
{
  if (roadUriTemplate == null || aerialUriTemplate == null)
    return;
  if (integerLongitude == -1 || integerLatitude == -1)
    return;
  // Get coordinates and pixel offsets based on current zoom level
  int croppedLongitude = integerLongitude >> BITRES - zoomLevel;
  int croppedLatitude = integerLatitude >> BITRES - zoomLevel;
  int xPixelOffset = (integerLongitude >> BITRES - zoomLevel - 8) % 256;
  int yPixelOffset = (integerLatitude >> BITRES - zoomLevel - 8) % 256;
  // Prepare for the loop
  string uriTemplate = mapStyle ==
    MapStyle.Road ? roadUriTemplate : aerialUriTemplate;
  int index = 0;
  int maxValue = (1 << zoomLevel) - 1;
  // Loop through the 5x5 array of Image elements
  for (int row = -2; row <= 2; row++)
    for (int col = -2; col <= 2; col++)
    {
      // Get the Image and BitmapImage
      Image image = imageCanvas.Children[index] as Image;
      BitmapImage bitmap = image.Source as BitmapImage;
      index++;
      // Check if you've gone beyond the bounds
      if (croppedLongitude + col < 0 ||
        croppedLongitude + col > maxValue ||
        croppedLatitude + row < 0 ||
        croppedLatitude + row > maxValue)
      {
        bitmap.UriSource = null;
      }
      else
      {
        // Calculate a quadkey and set URI to bitmap
        int longitude = croppedLongitude + col;
        int latitude = croppedLatitude + row;
        string strQuadkey =
          ToQuadKey(longitude, latitude, zoomLevel);
        string uri = uriTemplate.Replace("{quadkey}", strQuadkey);
        bitmap.UriSource = new Uri(uri);
      }
      // Position the Image element
      Canvas.SetLeft(image, col * 256 - xPixelOffset);
      Canvas.SetTop(image, row * 256 - yPixelOffset);
    }
}

Pour maintenir ce programme assez simple, il ne tente pas de lisser les transitions entre les vues et les niveaux de zoom. Souvent la totalité de l'écran se vide comme nouvelles tuiles sont chargés.

Mais le programme tourne à la carte. La logique de rotation est basée sur le capteur de mouvement et un RotateTransform et est quasiment indépendante du reste du programme. Figure 8 me prendre mon Windows Phone (ou peut-être l'émulateur Windows Phone) montre une promenade sur le pont de Brooklyn. Le haut du téléphone est pointu je marche dans la direction, et la petite flèche dans le coin supérieur gauche indique au nord.

The RotatingMapTiles Display
Figure 8 Affichage RotatingMapTiles

Charles Petzold est un contributeur de longue date à MSDN Magazine et l'auteur de « La programmation Windows, 6e édition » (o ' Reilly Media, 2012), un livre sur l'écriture d'applications pour Windows 8. Son site Web est charlespetzold.com.

Merci à l'expert technique suivant d'avoir relu cet article : Thomas Petchel