Dieser Artikel wurde maschinell übersetzt.

Touch and Go

Zusammenstellen von Bing Maps-Kacheln in Windows Phone

Charles Petzold

Charles PetzoldDer Bewegungsmelder in Windows Phone konsolidiert Informationen aus der Telefons Kompass und Beschleunigungsmesser eine Drehmatrix zu erstellen, die die Ausrichtung des Telefons im dreidimensionalen Raum beschreibt.

Vor kurzem habe ich angefangen, nachzudenken, wie das Telefon Ausrichtung in Kombination mit Bing Maps verwendet werden könnte. Ich erwartete ein Quickie-Mashup, aber die Arbeit erwies sich als eher komplex sein.

Wenn Sie mit Karten-Standardprogramms auf Ihre Windows Phone experimentiert haben, wissen Sie, dass das Display in der Regel ausgerichtet wird, so dass Norden zur Spitze des Telefons ist. (Die einzige Ausnahme ist, wenn Sie die Karte nach dem Weg zu einem Ort, verwenden in dem Fall die Karte orientiert sich an die Richtung erkennen, in welcher, die Sie anreisen.) Zum Norden ist die Konvention für Karten, natürlich, aber in einigen Fällen, die Sie das Telefon-Karte, um bezogen auf das Telefon so drehen möchten vielleicht, dass Norden auf der Karte ist eigentlich Norden zeigen.

Scheint es so einfach, nicht wahr?

Karte-Control-Einschränkungen

In meinem Bestreben, einen rotierenden Karte auf das Telefon zu implementieren begann ich mit dem Silverlight-Steuerelement in Bing Maps für Windows Phone, die ein Steuerelement namens einfach Karte im Microsoft.Phone.Controls.Maps-Namespace ist.

Einstieg bei der Map-Steuerelement (oder etwas mit Zugriff auf Bing Maps programmgesteuert) müssen Sie sich registrieren die Bing Maps Account Center am bingmapsportal.com. Es ist ein Straight­Prozess erhalten Sie einen Anmeldeinformationen-Key, die Ihr Programmzugriff auf Bing Maps zu übermitteln.

Benötigen Sie einen Verweis auf die Assembly Microsoft.Phone.Controls.Maps in das Windows Phone-Projekt, das Map-Steuerelement verwendet und Sie wahrscheinlich wollen, eine XML-Namespace-Deklaration in der XAML-Datei (in einer Zeile):

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

Instanziieren einer Standardkarte ist trivial:

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

Führen Sie den eigentlichen Anmeldeinformationen-Schlüssel aus der Bing Maps Account Center erhaltenen.

Ja, eine Karte auf dem Bildschirm ist einfach, aber wenn ich Wege, es zu drehen, ich kam trocken. Sicher, die Map-Klasse eine Überschrift-Eigenschaft von der MapBase-Klasse erbt, aber anscheinend ist diese Eigenschaft nur für "bird's Eye" relevant Karten und die Karten-Manager unterstützt nur die Norm Straße und Luftbilder.

Natürlich, es ist ziemlich trivial, um das Steuerelement anzeigen zu drehen, indem ein RotateTransform-Objekt auf die RenderTransform-Eigenschaft, aber ich war nicht zufrieden mit dieser Lösung. Für eine Sache, tendenziell es verdecken den copyright-Hinweis am unteren Rand der Karte, und tun, die zu sein schien unter Verstoß gegen die Bedingungen, die ich beim Abrufen von Anmeldeinformationen Schlüssel zugestimmt.

Ich beschloss, das Karten-Steuerelement zu verzichten und stattdessen versuchen mein Glück auf einem viel niedrigeren Niveau durch Zugriff auf die Bing Maps SOAP-Dienste. Diese Reihe von Webdiensten ermöglicht einem Programm, die tatsächliche Bitmap-Fliesen zu erhalten, aus denen größere Karten aufgebaut sind.

Zugriff auf SOAP-Service

In einem Webdienst, gebaut um SOAP Simple Object Access Protocol () werden die Daten zwischen Ihrem Programm und den Server mithilfe von XML-Dokumenten übertragen. Diese Dokumente beinhalten sehr oft recht komplexe Datenstrukturen, so anstatt zu XML direkt behandeln, ist ein viel einfacher Ansatz haben Visual Studio erstellen einer Proxyklasse für Sie. Dies kann Ihr Programm Zugriff auf den Web-Dienst mit normalen (wenn auch asynchron) c#-Klassen und Methodenaufrufe.

Die Bing Maps SOAP-Services-Schnittstelle ist dokumentiert bei bit.ly/S3R4lG, und besteht aus vier separaten Dienste:

  • Geokodierung-Service: Passen Sie Adressen mit Längen- und Breitengrad.
  • Bilder-Service: Erhalten Sie Karten und Fliesen.
  • Routendienst: Anfahrtsbeschreibung.
  • Suche Service: Suchen Sie Menschen und Unternehmen.

Ich war nur die Bilder von Service interessiert.

Der herunterladbare Code für diesen Artikel ist ein einzelnes Projekt mit dem Namen RotatingMapTiles. Einen Proxy für den Bilder-Dienst hinzugefügt dieses Projekt durch die Wahl der Dienstverweis hinzufügen im Menü Projekt. Im Feld Adresse im Dialogfeld Dienstverweis hinzufügen kopiert habe ich die URL im Abschnitt Bing Maps SOAP-Dienste-Adressen der Dokumentation und gedrückten gehen aufgeführt. Wenn der Dienst gefunden wurde, gab ich es ein Name des ImageryService im Feld Namespace.

Der c#-Code generiert für diese Dienstleistung einen Namespace verfügt, die der normalen Projekt-Namespace ist gefolgt von den Namespace, die Sie angeben, wenn Sie den Dienstverweis zu erstellen, also die Datei MainPage.xaml.cs im RotatingMapTile Programm folgende enthält Direktive verwenden:

using RotatingMapTiles.ImageryService;

Der Bilder-Dienst unterstützt zwei Arten von Anfragen, bei denen die Funktionsaufrufe GetMapUriAsync und GetImageryMetadataAsync. Der erste Aufruf können Sie statische Karten einer bestimmten Lage, Größe und Zoom-Ebene, während die zweite Werke auf einem niedrigeren Niveau zu erhalten. Ordnet die Bing, unter die Metadaten, die dieser zweiten Aufruf eine URI-Vorlage ist, mit dem Sie die tatsächlichen Bitmap-Fliesen zuzugreifen, die verwendet werden liefert, zu montieren.

Abbildung 1 zeigt den Loaded-Ereignishandler für die Page-Klasse in der RotatingMapTiles machen zwei Aufrufe an die Bilder von Webdienst zum Abrufen von Metadaten für die MapStyle.Road und MapStyle.Aerial Stile Projekt. (MapStyle.Birdseye ist auch verfügbar, aber es ist eher komplexere verwenden.)

Abbildung 1 Code Zugriff auf Bing Maps Imagery-Webdienst

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");
}

Sie benötigen Ihren eigenen Bing Maps-Anmeldeinformationen-Schlüssel für den Platzhalter ersetzen.

Abbildung 2 wird der Ereignishandler für das Completed-Ereignis des asynchronen Aufrufs. Die Informationen umfassen einen URI für eine Bitmap mit dem Bing-Logo, so dass es leicht zu Krediten Bing Maps auf das Programm Bildschirm.

Abbildung 2 abgeschlossenen Handler für die Bing Maps Web-Service

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;
  }
}

Andere URI ist der, den Sie zum Zugriff auf der Kartenkacheln verwenden wollen. Es gibt separate URIs für die Straße und Luftbilder und URI enthält einen Platzhalter für eine Zahl, die genau die Fliese identifiziert.

Karten und Fliesen

Die Fliesen, die die Grundlage von Bing Maps sind Bitmaps, die immer 256 Pixel-Platz. Jede Kachel ist einen bestimmten Längengrad, Breitengrad und Zoom-Level zugeordnet und enthält ein Bild von einem quadratischen Bereich auf der Oberfläche der Erde abgeflacht, mit der gemeinsamen Mercator-Projektion.

Die extremste Zoom-Out-Ansicht ist bekannt als Ebene 1, und nur vier Fliesen sind erforderlich, um die ganze Welt zu decken – oder zumindest der Teil der Welt mit breiten zwischen positiven und negativen 85,05 ° — wie im Abbildung 3.

The Four Level 1 Tiles
Abbildung 3 die vier Stufe 1-Fliesen

Ich erkläre die Zahlen auf den Fliesen in einem Augenblick. Da die Fliesen 256 Pixel Quadrat sind, entspricht am Äquator jedes Pixel ca. 49 km.

Stufe 2 ist eine präzisere und jetzt 16 Steine die Erde bedecken, wie in Abbildung 4.

The 16 Level 2 Tiles
Abbildung 4 die 16 Level 2-Fliesen

Die Fliesen in Abbildung 4 sind auch 256 Pixel im Quadrat, so dass am Äquator jedes Pixel etwa 24 Meilen. Beachten Sie, dass jede Kachel in der Ebene 1 der gleichen Gegend wie 4 Fliesen in Ebene 2 umfasst.

Diese Regelung geht weiter: Stufe 3 hat 64 Fliesen, Level 4 hat 256 Fliesen und bis und bis und bis zu Level 21, die die Erde mit einer Gesamtfläche von mehr als 4 Billionen deckt Fliesen — 2 Millionen horizontal und 2 Millionen für eine Auflösung (am Äquator) von 3 Zoll pro Pixel vertikal.

Nummerierung der Fliesen

Da wenigstens ein paar von diesen Billionen von Fliesen einzeln von einem Programm müssen, die sie verwenden will verwiesen werden, müssen sie eine klare und konsistente Weise identifiziert werden. Es gibt drei Dimensionen beteiligt — Längengrad, Breitengrad und Zoom-Stufe — und auch eine praktische Prüfung: Um Festplatten-Zugriffe auf den Server zu minimieren, sollten Fliesen, die dem gleichen Bereich zugeordneten nahe beieinander, gespeichert werden, was bedeutet eine einzelne Nummerierung System, die alle drei Dimensionen sehr kluge Weise umfasst.

Die clevere Nummernsystem verwendet für diese Fliesen Karte heißt "Quadkey". Die MSDN Library-Artikel "Bing Maps Tile System" von Joe Schwartz (bit.ly/SxVojI) ist eine wirklich gute Erklärung des Systems (einschließlich hilfreiche Code), aber ich nehme hier einen etwas anderen Ansatz.

Jede Kachel hat einen einzigartigen Quadkey. Die Fliese aus dem Webdienst erhaltenen URIs enthalten eine Platzhalterzeichenfolge "{Quadkey}". Bevor Sie eines der URIs verwenden, auf eine Kachel zugreifen, müssen Sie diesen Platzhalter mit einem tatsächlichen Quadkey ersetzen.

Abbildung 3 und Abbildung 4 zeigen die Quadkeys für Zoom-Level 1 und 2.  Führende Nullen sind Quadkeys wichtig. (In der Tat, sollten Sie die Quadkey als eine Zeichenfolge anstelle einer Zahl denken.) Die Anzahl der Ziffern in einer Quadkey ist immer gleich die Zoom-Stufe der Fliese. Die Fliesen in Level 21 sind mit 21-stellige Quadkeys identifiziert.

Die einzelnen Ziffern einer Quadkey sind immer 0, 1, 2 oder 3. Die Quadkey ist also wirklich ein Base-4-Anzahl. Sehen Sie sich diese vier Ziffern in Binary (00, 01, 10, 11) und wie sie in einer Gruppe von vier Fliesen erscheinen. In jede Ziffer Base-4 das zweite Bit ist wirklich eine horizontale Koordinate, und das erste Bit ist eine vertikale Koordinate. Die Bits entsprechen einem Längen- und Breitengrad, die effektiv, in die Quadkey verzahnt sind.

Jede Kachel in Ebene 1 deckt der gleichen Fläche wie eine Gruppe von vier Fliesen in Ebene 2. Sie können ein Ziegel in Stufe 1 als "Eltern" vier "Kindern" in Ebene 2 vorstellen. Die Quadkey der Fliese Kind beginnt immer mit den gleichen Ziffern als übergeordnetes und fügt dann eine andere Ziffer (0, 1, 2 oder 3) je nach seiner Lage innerhalb des Bereichs des übergeordneten. Gehen von Elternteil zu Kind ist ein Zoom. Zoomen Sie ähnelt: Für jedes Kind Quadkey erhalten Sie einfach durch Beschneiden Sie die letzte Ziffer der übergeordneten Quadkey.

Hier ist wie ein Quadkey von einer tatsächlichen geographischen Längen- und Breitengrad abgeleitet.

Längengrad reicht von-180 ° bei der Inter­nationalen Datumsgrenze und dann steigt wieder Ost bis 180° in der internationalen Daten ein. Berechnen Sie für jede Länge zunächst eine relative Länge, der Bereich zwischen 0 und 1 mit 0,5, der Nullmeridian darstellt:

double relativeLongitude = (180 + longitude) / 360;

Jetzt konvertieren Sie, die in eine ganze Zahl, eine feste Anzahl von Bits:

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

In meinem Programm habe ich BITRES 29 21 Zoom-Level plus 8 Bits für die Pixelgröße der Kachel festgelegt. So identifiziert diese Ganzzahl eine Länge präzise den nächsten Pixel der Fliese in der höchsten Zoomstufe.

Die Berechnung der IntegerLatitude ist ein wenig komplexer, da die Mercator-Kartenprojektion Breitengraden komprimiert, wie Sie weiter von dem Äquator:

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));

Die IntegerLatitude reicht von 0 bei 85,05 ° nördlich des Äquators auf den Maximalwert bei 85,05 ° südlich des Äquators.

Der Center of Central Park in New York City hat eine Länge von-73.965368 ° und eine Breite 40.783271 °. Die relativen Werte sind (um nur ein paar Dezimalstellen), 0.29454 und 0.37572. Die 29-Bit-Ganzzahl-Längen- und Breitengrad Werte (Binär dargestellt und gruppiert für Lesbarkeit) sind:

0 1001 0110 1100 1110 0000 1000 0000
0 1100 0000 0101 1110 1011 0000 0000

Angenommen, Sie möchten eine Kachel zeigt, dass die Mitte von Central Park in einem Level 12-Zoom. Nehmen Sie die Top 12 Bit der ganzzahligen Längengraden und Breitengraden (aufpassen – die folgenden Ziffern gruppiert sind ein wenig anders als die 29-Bit-Versionen):

0100 1011 0110
0110 0000 0010

Dies sind zwei Binärzahlen, aber wir brauchen sie um eine Base-4-Reihe bilden kombiniert. Es gibt keine Möglichkeit, dies im Code über einfache arithmetische Operatoren zu tun. Sie benötigen eine kleine Routine, die tatsächlich durchläuft die einzelnen Bits und konstruiert eine längere ganze Zahl oder eine Zeichenfolge. Zur Veranschaulichung Sie einfach alle Bits in der Breite zu verdoppeln und die zwei Werte hinzufügen, als wären sie Base-4-Werte:

0100 1011 0110
0220 0000 0020
0320 1011 0130

Das Ergebnis ist die 12-stellige Quadkey müssen Sie den Platzhalter "{Quadkey}" in der URI ersetzen Sie erhalten aus dem Webdienst auf die Fliese Karte zugreifen.

Abbildung 5 zeigt eine Routine, ein Quadkey aus dem abgeschnittenen ganzzahligen Längengraden und Breitengraden zu konstruieren. Aus Gründen der Übersichtlichkeit habe ich die Logik in Abschnitte unterteilt, die eine Quadkey lange Ganzzahl und eine Quadkey-Zeichenfolge zu generieren.

Abbildung 5-Routine, um eine Quadkey zu berechnen

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();
}

Abbildung 6 zeigt die Straße und die Antenne Fliesen für diese Quadkey. Die Mitte von Central Park ist eigentlich Weg nach unten am unteren Rand dieser Bilder ein wenig nach links der Mitte. Dies ist aus dem ganzzahligen Längengraden und Breitengraden vorhersehbar. Schauen Sie sich die nächsten 8 Bit Ganzzahl-Längengrad, nach den ersten 12 Bit: Die Bits sind 0111 0000 oder 112. Die nächsten 8 Bits des Latitude sind 1111 0101 oder 245. Dies bedeutet, dass die Mitte von Central Park der 112. Pixel von Links und das 245th Pixel nach unten in die Fliesen.

The Tiles for Quadkey “032010110130”
Abbildung 6 die Fliesen für Quadkey "032010110130"

Fliesen Fliesen

Sobald Sie eine Ganzzahl Längen- und Breitengrad auf eine bestimmte Anzahl von Bits entspricht einer bestimmten Zoomstufe abgeschnitten haben, ist die Erlangung der angrenzender Fliesen ein Kinderspiel: Einfach Inkrementieren Sie und dekrementieren Sie der Längen- und Breitengrad Ganzzahlen und Form neue Quadkeys.

Sie haben bereits gesehen, drei Methoden der MainPage-Klasse des RotatingMapTiles Projekts. Das Programm verwendet ein GeoCoordinateWatcher um die Längen- und Breitengrad des Telefons zu erhalten und die Koordinaten in Ganzzahlwerte konvertiert werden, wie oben gezeigt. Der Anwendungsleiste hat drei Schaltflächen: Umschalten zwischen Straße und Luftbilder zu erhöhen und verringern die Zoomstufe.

Das Programm hat keine andere Touch-Oberfläche neben den Schaltflächen. Es immer zeigt den Speicherort aus der GeoCoordinateWatcher in der Mitte des Bildschirms gewonnen und konstruiert die gesamte-Karte mit 25 Bildelemente in einem 5 x 5-Array eine Konfiguration, die immer den 480 x 800 Pixel-Bildschirm auch bei Drehung füllt. Die MainPage-Klasse erstellt diese 25 Bildelemente und BitmapImage Objekte in seinem Konstruktor.

Immer wenn der GeoCoordinateWatcher kommt mit einem neuen Speicherort oder die Zoom-Stufe oder Karte Stiländerungen, die RefreshDisplay-Methode in Abbildung 7 nennt. Diese Methode zeigt, wie die neuen URIs abgerufen und setzen Sie einfach auf die vorhandenen BitmapImage-Objekte sind.

Abbildung 7 die RefreshDisplay-Methode in 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);
    }
}

Um dieses Programm relativ einfach zu halten, versucht es nicht, die Übergänge zwischen den Ansichten und Zoom-Stufen glätten. Der ganze Bildschirm geht oft leer, da neue Fliesen geladen werden.

Aber das Programm dreht sich die Karte. Die Drehung-Logik basiert auf der Weg-und/oder Geschwindigkeitsgeber und einer RotateTransform und ist ziemlich unabhängig vom Rest des Programms. Abbildung 8 zeigt mir meine Windows Phone (oder vielleicht die Windows Phone-Emulator) für einen Spaziergang über die Brooklyn Bridge. Die Oberseite des Telefons ist Spitze in Richtung I 'm walking, und der kleinen Pfeil in der linken oberen Ecke zeigt Nord an.

The RotatingMapTiles Display
Abbildung 8 die RotatingMapTiles-Anzeige

Charles Petzold ist ein langjähriger Beitrag zum MSDN Magazine und Autor von "Programming Windows, 6th Edition" (O' Reilly Media, 2012), ein Buch über das Schreiben von Anwendungen für Windows 8. Seiner Website lautet charlespetzold.com.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Thomas Petchel