Überlagern von nebeneinander angeordneten Bildern in einer Karte

Hinweis

MapControl und Kartendienste erfordern einen Karten-Authentifizierungsschlüssel namens MapServiceToken. Weitere Informationen zum Abrufen und Festlegen eines Kartenauthentifizierungsschlüssels finden Sie unter Anfordern eines Kartenauthentifizierungsschlüssels.

Überlagern Sie Bilder von Drittanbietern oder benutzerdefinierte nebeneinander angeordnete Bilder in einer Karte mithilfe von Kachelquellen. Verwenden Sie Kachelquellen, um spezielle Infos wie Wetterdaten, Einwohnerzahlen oder seismische Daten zu überlagern oder die Standardkarte vollständig zu ersetzen.

Tipp Um mehr über die Verwendung von Karten in Ihrer App zu erfahren, laden Sie das Universelle Windows-Plattform-Kartenbeispiel (UWP) auf Github herunter.

Übersicht über nebeneinander angeordnete Bilder

Kartendienste wie Nokia Karten und Bing Maps teilen Karten zum schnellen Abrufen und Anzeigen in quadratische Kacheln ein. Diese Kacheln messen 256 x 256 Pixel und werden vorab auf mehrere Detailebenen gerendert. Viele Dienste von Drittanbietern umfassen auch kartenbasierte Daten, die in Kacheln aufgeteilt sind. Verwenden Sie Kachelquellen, um Kacheln von Drittanbietern abzurufen oder benutzerdefinierte Kacheln zu erstellen. Überlagern Sie diese auf der angezeigten Karte im MapControl-Objekt.

Wichtig Wenn Sie Kachelquellen verwenden, müssen Sie keinen Code schreiben, um einzelne Kacheln anzufordern oder zu positionieren. Das MapControl-Objekt fordert bei Bedarf Kacheln an. Bei jeder Anforderung werden die X- und Y-Koordinaten und der Zoomfaktor für die einzelnen Kacheln angegeben. Geben Sie einfach das Format des URIs oder Dateinamens an, um die Kacheln in der UriFormatString-Eigenschaft abzurufen. Dazu fügen Sie ersetzbare Parameter in den Basis-URI oder Dateinamen ein, um anzugeben, wo die X- und Y-Koordinaten und der Zoomfaktor für jede Kachel übergeben werden.

In diesem Beispiel sehen Sie eine UriFormatString-Eigenschaft für eine HttpMapTileDataSource-Klasse. Diese zeigt die ersetzbaren Parameter für die X- und Y-Koordinaten und den Zoomfaktor an.

http://www.<web service name>.com/z={zoomlevel}&x={x}&y={y}

(Die X- und Y-Koordinaten stellen die Position der einzelnen Kachel innerhalb der Weltkarte auf der angegebenen Detailebene dar. Das Kachelnummerierungssystem beginnt mit {0, 0} in der oberen linken Ecke der Karte. Beispielsweise befindet sich die Kachel bei {1, 2} in der zweiten Spalte der dritten Zeile des Kachelrasters.)

Weitere Informationen über das Kachelsystem von Kartendiensten finden Sie unter Kachelsystem von Bing Maps.

Überlagern von Kacheln aus einer Kachelquelle

Überlagern Sie nebeneinander angeordnete Bilder aus einer Kachelquelle in einer Karte mithilfe der MapTileDataSource-Klasse.

  1. Instanziieren Sie eine der drei Klassen für Kacheldatenquellen, die von der MapTileDataSource-Klasse erben.

    Konfigurieren Sie die UriFormatString-Eigenschaft zum Anfordern der Kacheln, indem Sie ersetzbare Parameter im Basis-URI oder Dateinamen einfügen.

    Im folgenden Beispiel wird eine HttpMapTileDataSource-Klasse instanziiert. In diesem Beispiel wird der Wert der UriFormatString-Eigenschaft im Konstruktor der HttpMapTileDataSource-Klasse veranschaulicht.

        HttpMapTileDataSource dataSource = new HttpMapTileDataSource(
          "http://www.<web service name>.com/z={zoomlevel}&x={x}&y={y}");
    
  2. Instanziieren und konfigurieren Sie eine MapTileSource-Klasse. Geben Sie die MapTileDataSource-Klasse an, die Sie im vorherigen Schritt als DataSource-Eigenschaft der MapTileSource-Klasse festgelegt haben.

    Im folgenden Beispiel wird die DataSource-Eigenschaft im Konstruktor der MapTileSource-Klasse veranschaulicht.

        MapTileSource tileSource = new MapTileSource(dataSource);
    

    Sie können die Bedingungen, unter denen Kacheln angezeigt werden, mithilfe der Eigenschaften der MapTileSource-Klasse einschränken.

    • Zeigen Sie Kacheln nur in einem bestimmten geografischen Bereich an, indem Sie einen Wert für die Bounds-Eigenschaft angeben.
    • Zeigen Sie Kacheln nur auf bestimmten Detailebenen an, indem Sie einen Wert für die ZoomLevelRange-Eigenschaft angeben.

    Optional können Sie weitere Eigenschaften der MapTileSource-Klasse konfigurieren, die Auswirkungen auf das Laden oder die Anzeige von Kacheln haben, z. B. Layer, AllowOverstretch, IsRetryEnabled und IsTransparencyEnabled.

  3. Fügen Sie die MapTileSource-Klasse der TileSources-Collection dem MapControl-Objekt hinzu.

         MapControl1.TileSources.Add(tileSource);
    

Überlagern von Kacheln aus einem Webdienst

Überlagern Sie nebeneinander angeordnete Bilder aus einem Webdienst mithilfe der HttpMapTileDataSource-Klasse.

  1. Instanziieren Sie eine HttpMapTileDataSource-Klasse.

  2. Geben Sie das Format des URIs an, den der Webdienst als Wert für die UriFormatString-Eigenschaft erwartet. Um diesen Wert zu erstellen, fügen Sie dem Basis-URI die ersetzbaren Parameter hinzu. Im folgenden Codebeispiel hat die UriFormatString-Eigenschaft beispielsweise den Wert:

    http://www.<web service name>.com/z={zoomlevel}&x={x}&y={y}
    

    Der Webdienst muss einen URI unterstützen, der die ersetzbaren Parameter {x}, {y} und {zoomlevel} enthält. Die meisten Webdienste (z. B. Nokia, Bing und Google) unterstützen URIs in diesem Format. Benötigt der Webdienst zusätzliche Argumente, die mit der UriFormatString-Eigenschaft nicht zur Verfügung stehen, müssen Sie einen benutzerdefinierten URI erstellen. Mithilfe des UriRequested-Ereignisses können Sie einen benutzerdefinierten URI erstellen und zurückgeben. Weitere Infos finden Sie im Abschnitt Bereitstellen eines benutzerdefinierten URIs weiter unten in diesem Thema.

  3. Befolgen Sie die verbleibenden Schritte in der Übersicht über nebeneinander angeordnete Bilder.

Im folgenden Beispiel überlagern Kacheln aus einem fiktiven Webdienst eine Karte von Nordamerika. Der Wert der UriFormatString-Eigenschaft ist im Konstruktor der HttpMapTileDataSource-Klasse angegeben. In diesem Beispiel werden durch Festlegen der optionalen Bounds-Eigenschaft nur Kacheln innerhalb der geografischen Bereiche angegeben.

private void AddHttpMapTileSource()
{
    // Create the bounding box in which the tiles are displayed.
    // This example represents North America.
    BasicGeoposition northWestCorner =
        new BasicGeoposition() { Latitude = 48.38544, Longitude = -124.667360 };
    BasicGeoposition southEastCorner =
        new BasicGeoposition() { Latitude = 25.26954, Longitude = -80.30182 };
    GeoboundingBox boundingBox = new GeoboundingBox(northWestCorner, southEastCorner);

    // Create an HTTP data source.
    // This example retrieves tiles from a fictitious web service.
    HttpMapTileDataSource dataSource = new HttpMapTileDataSource(
        "http://www.<web service name>.com/z={zoomlevel}&x={x}&y={y}");

    // Optionally, add custom HTTP headers if the web service requires them.
    dataSource.AdditionalRequestHeaders.Add("header name", "header value");

    // Create a tile source and add it to the Map control.
    MapTileSource tileSource = new MapTileSource(dataSource);
    tileSource.Bounds = boundingBox;
    MapControl1.TileSources.Add(tileSource);
}
...
#include <winrt/Windows.Devices.Geolocation.h>
#include <winrt/Windows.UI.Xaml.Controls.Maps.h>
...
void MainPage::AddHttpMapTileSource()
{
    Windows::Devices::Geolocation::BasicGeoposition northWest{ 48.38544, -124.667360 };
    Windows::Devices::Geolocation::BasicGeoposition southEast{ 25.26954, -80.30182 };
    Windows::Devices::Geolocation::GeoboundingBox boundingBox{ northWest, southEast };

    Windows::UI::Xaml::Controls::Maps::HttpMapTileDataSource dataSource{
        L"http://www.<web service name>.com/z={zoomlevel}&x={x}&y={y}" };

    dataSource.AdditionalRequestHeaders().Insert(L"header name", L"header value");

    Windows::UI::Xaml::Controls::Maps::MapTileSource tileSource{ dataSource };
    tileSource.Bounds(boundingBox);

    MapControl1().TileSources().Append(tileSource);
}
...
void MainPage::AddHttpMapTileSource()
{
    BasicGeoposition northWest = { 48.38544, -124.667360 };
    BasicGeoposition southEast = { 25.26954, -80.30182 };
    GeoboundingBox^ boundingBox = ref new GeoboundingBox(northWest, southEast);

    auto dataSource = ref new Windows::UI::Xaml::Controls::Maps::HttpMapTileDataSource(
        "http://www.<web service name>.com/z={zoomlevel}&x={x}&y={y}");

    dataSource->AdditionalRequestHeaders->Insert("header name", "header value");

    auto tileSource = ref new Windows::UI::Xaml::Controls::Maps::MapTileSource(dataSource);
    tileSource->Bounds = boundingBox;

    this->MapControl1->TileSources->Append(tileSource);
}

Überlagern von Kacheln aus dem lokalen Speicher

Überlagern Sie nebeneinander angeordnete Bilder, die als Dateien im lokalen Speicher gespeichert sind, mithilfe der LocalMapTileDataSource-Klasse. In der Regel können Sie diese Dateien mit Ihrer App verpacken und verteilen.

  1. Instanziieren Sie eine LocalMapTileDataSource-Klasse.

  2. Geben Sie das Format der Dateinamen als Wert für die UriFormatString-Eigenschaft ein. Um diesen Wert zu erstellen, fügen Sie dem Basis-Dateinamen die ersetzbaren Parameter hinzu. Im folgenden Codebeispiel hat die UriFormatString-Eigenschaft beispielsweise den Wert:

        Tile_{zoomlevel}_{x}_{y}.png
    

    Wenn das Format der Dateinamen zusätzliche Argumente benötigt, die mit der UriFormatString-Eigenschaft nicht zur Verfügung stehen, müssen Sie einen benutzerdefinierten URI erstellen. Mithilfe des UriRequested-Ereignisses können Sie einen benutzerdefinierten URI erstellen und zurückgeben. Weitere Infos finden Sie im Abschnitt Bereitstellen eines benutzerdefinierten URIs weiter unten in diesem Thema.

  3. Befolgen Sie die verbleibenden Schritte in der Übersicht über nebeneinander angeordnete Bilder.

Zum Laden von Kacheln aus dem lokalen Speicher können Sie die folgenden Protokolle und Speicherorte verwenden:

Uri Weitere Informationen
ms-appx:/// Verweist auf das Stammelement des App-Installationsordners.
Dies ist der von der Package.InstalledLocation-Eigenschaft referenzierte Speicherort.
ms-appdata:///local Verweist auf das Stammelement des lokalen Speichers der App.
Dies ist der von der ApplicationData.LocalFolder-Eigenschaft referenzierte Speicherort.
ms-appdata:///temp Verweist auf die temporären Ordner der App.
Dies ist der Pfad, auf den die ApplicationData.TemporaryFolder-Eigenschaft verweist.

 

Im folgenden Beispiel werden Kacheln, die als Dateien im Installationsverzeichnis der App gespeichert werden, mithilfe des ms-appx:///-Protokolls geladen. Der Wert der UriFormatString-Eigenschaft ist im Konstruktor der LocalMapTileDataSource-Klasse angegeben. In diesem Beispiel werden Kacheln nur angezeigt, wenn der Zoomfaktor für die Karte innerhalb des Bereichs liegt, der durch die optionale ZoomLevelRange-Eigenschaft angegeben ist.

        void AddLocalMapTileSource()
        {
            // Specify the range of zoom levels
            // at which the overlaid tiles are displayed.
            MapZoomLevelRange range;
            range.Min = 11;
            range.Max = 20;

            // Create a local data source.
            LocalMapTileDataSource dataSource = new LocalMapTileDataSource(
                "ms-appx:///TileSourceAssets/Tile_{zoomlevel}_{x}_{y}.png");

            // Create a tile source and add it to the Map control.
            MapTileSource tileSource = new MapTileSource(dataSource);
            tileSource.ZoomLevelRange = range;
            MapControl1.TileSources.Add(tileSource);
        }

Bereitstellen eines benutzerdefinierten URIs

Wenn die ersetzbaren Parameter, die mit der UriFormatString-Eigenschaft der HttpMapTileDataSource-Klasse oder der UriFormatString-Eigenschaft der LocalMapTileDataSource-Klasse zur Verfügung stehen, nicht zum Abrufen Ihrer Kacheln ausreichen, müssen Sie einen benutzerdefinierten URI erstellen. Indem Sie einen benutzerdefinierten Handler für das UriRequested-Ereignis bereitstellen, können Sie einen benutzerdefinierten URI erstellen und zurückgeben. Das UriRequested-Ereignis wird für jede einzelne Kachel ausgelöst.

  1. Kombinieren Sie in Ihrem benutzerdefinierten Handler für das UriRequested-Ereignis die erforderlichen benutzerdefinierten Argumente mit den Eigenschaften X, Y und ZoomLevel der MapTileUriRequestedEventArgs-Klasse, um den benutzerdefinierten URI zu erstellen.
  2. Geben Sie die benutzerdefinierte URI in der Uri-Eigenschaft der MapTileUriRequest-Klasse zurück. Diese ist in der Request-Eigenschaft der MapTileUriRequestedEventArgs-Klasse enthalten.

Das folgende Beispiel zeigt, wie Sie einen benutzerdefinierten URI durch Erstellen eines benutzerdefinierten Handlers für das UriRequested-Ereignis bereitstellen. Sie erfahren zudem, wie Sie das Verzögerungsmuster implementieren, wenn Sie asynchron zum Erstellen des benutzerdefinierten URIs weitere Schritte ausführen müssen.

using Windows.UI.Xaml.Controls.Maps;
using System.Threading.Tasks;
...
            var httpTileDataSource = new HttpMapTileDataSource();
            // Attach a handler for the UriRequested event.
            httpTileDataSource.UriRequested += HandleUriRequestAsync;
            MapTileSource httpTileSource = new MapTileSource(httpTileDataSource);
            MapControl1.TileSources.Add(httpTileSource);
...
        // Handle the UriRequested event.
        private async void HandleUriRequestAsync(HttpMapTileDataSource sender,
            MapTileUriRequestedEventArgs args)
        {
            // Get a deferral to do something asynchronously.
            // Omit this line if you don't have to do something asynchronously.
            var deferral = args.Request.GetDeferral();

            // Get the custom Uri.
            var uri = await GetCustomUriAsync(args.X, args.Y, args.ZoomLevel);

            // Specify the Uri in the Uri property of the MapTileUriRequest.
            args.Request.Uri = uri;

            // Notify the app that the custom Uri is ready.
            // Omit this line also if you don't have to do something asynchronously.
            deferral.Complete();
        }

        // Create the custom Uri.
        private async Task<Uri> GetCustomUriAsync(int x, int y, int zoomLevel)
        {
            // Do something asynchronously to create and return the custom Uri.        }
        }

Überlagern von Kacheln aus einer benutzerdefinierten Quelle

Überlagern Sie benutzerdefinierte Kacheln mithilfe der CustomMapTileDataSource-Klasse. Erstellen Sie Kacheln programmgesteuert im Arbeitsspeicher, oder schreiben Sie eigenen Code, um vorhandene Kacheln aus einer anderen Quelle zu laden.

Stellen Sie zum Erstellen oder Laden von benutzerdefinierten Kacheln einen benutzerdefinierten Handler für das BitmapRequested-Ereignis bereit. Das BitmapRequested-Ereignis wird für jede einzelne Kachel ausgelöst.

  1. Kombinieren Sie in Ihrem benutzerdefinierten Handler für das BitmapRequested-Ereignis die erforderlichen benutzerdefinierten Argumente mit den Eigenschaften X, Y und ZoomLevel der MapTileBitmapRequestedEventArgs-Klasse, um eine benutzerdefinierte Kachel zu erstellen oder abzurufen.
  2. Geben Sie die benutzerdefinierte Kachel in der PixelData-Eigenschaft der MapTileBitmapRequest-Klasse zurück, die in der Request-Eigenschaft der MapTileBitmapRequestedEventArgs-Klasse enthalten ist. Bei der PixelData-Eigenschaft handelt es sich um den Typ IRandomAccessStreamReference.

Das folgende Beispiel zeigt, wie Sie benutzerdefinierte Kacheln durch Erstellen eines benutzerdefinierten Handlers für das BitmapRequested-Ereignis bereitstellen. In diesem Beispiel werden identische rote Kacheln erstellt, die teilweise deckend sind. Im Beispiel werden die Eigenschaften X, Y und ZoomLevel der MapTileBitmapRequestedEventArgs-Klasse ignoriert. Auch wenn dies kein praktisches Beispiel ist, wird im Beispiel veranschaulicht, wie Sie benutzerdefinierte Kacheln im Arbeitsspeicher erstellen können. Sie erfahren zudem, wie Sie das Verzögerungsmuster implementieren, wenn Sie asynchron zum Erstellen der benutzerdefinierten Kacheln weitere Schritte ausführen müssen.

using Windows.UI.Xaml.Controls.Maps;
using Windows.Storage.Streams;
using System.Threading.Tasks;
...
        CustomMapTileDataSource customDataSource = new CustomMapTileDataSource();
        // Attach a handler for the BitmapRequested event.
        customDataSource.BitmapRequested += customDataSource_BitmapRequestedAsync;
        customTileSource = new MapTileSource(customDataSource);
        MapControl1.TileSources.Add(customTileSource);
...
        // Handle the BitmapRequested event.
        private async void customDataSource_BitmapRequestedAsync(
            CustomMapTileDataSource sender,
            MapTileBitmapRequestedEventArgs args)
        {
            var deferral = args.Request.GetDeferral();
            args.Request.PixelData = await CreateBitmapAsStreamAsync();
            deferral.Complete();
        }

        // Create the custom tiles.
        // This example creates red tiles that are partially opaque.
        private async Task<RandomAccessStreamReference> CreateBitmapAsStreamAsync()
        {
            int pixelHeight = 256;
            int pixelWidth = 256;
            int bpp = 4;

            byte[] bytes = new byte[pixelHeight * pixelWidth * bpp];

            for (int y = 0; y < pixelHeight; y++)
            {
                for (int x = 0; x < pixelWidth; x++)
                {
                    int pixelIndex = y * pixelWidth + x;
                    int byteIndex = pixelIndex * bpp;

                    // Set the current pixel bytes.
                    bytes[byteIndex] = 0xff;        // Red
                    bytes[byteIndex + 1] = 0x00;    // Green
                    bytes[byteIndex + 2] = 0x00;    // Blue
                    bytes[byteIndex + 3] = 0x80;    // Alpha (0xff = fully opaque)
                }
            }

            // Create RandomAccessStream from byte array.
            InMemoryRandomAccessStream randomAccessStream =
                new InMemoryRandomAccessStream();
            IOutputStream outputStream = randomAccessStream.GetOutputStreamAt(0);
            DataWriter writer = new DataWriter(outputStream);
            writer.WriteBytes(bytes);
            await writer.StoreAsync();
            await writer.FlushAsync();
            return RandomAccessStreamReference.CreateFromStream(randomAccessStream);
        }
...
#include <winrt/Windows.Storage.Streams.h>
...
Windows::Foundation::IAsyncOperation<Windows::Storage::Streams::InMemoryRandomAccessStream> MainPage::CustomRandomAccessStream()
{
    constexpr int pixelHeight{ 256 };
    constexpr int pixelWidth{ 256 };
    constexpr int bpp{ 4 };

    std::array<uint8_t, pixelHeight * pixelWidth * bpp> bytes;

    for (int y = 0; y < pixelHeight; y++)
    {
        for (int x = 0; x < pixelWidth; x++)
        {
            int pixelIndex{ y * pixelWidth + x };
            int byteIndex{ pixelIndex * bpp };

            // Set the current pixel bytes.
            bytes[byteIndex] = (byte)(std::rand() % 256);        // Red
            bytes[byteIndex + 1] = (byte)(std::rand() % 256);    // Green
            bytes[byteIndex + 2] = (byte)(std::rand() % 256);    // Blue
            bytes[byteIndex + 3] = (byte)((std::rand() % 56) + 200);    // Alpha (0xff = fully opaque)
        }
    }

    // Create RandomAccessStream from byte array.
    Windows::Storage::Streams::InMemoryRandomAccessStream randomAccessStream;
    Windows::Storage::Streams::IOutputStream outputStream{ randomAccessStream.GetOutputStreamAt(0) };
    Windows::Storage::Streams::DataWriter writer{ outputStream };
    writer.WriteBytes(bytes);

    co_await writer.StoreAsync();
    co_await writer.FlushAsync();

    co_return randomAccessStream;
}
...
InMemoryRandomAccessStream^ TileSources::CustomRandomAccessStream::get()
{
    int pixelHeight = 256;
    int pixelWidth = 256;
    int bpp = 4;

    Array<byte>^ bytes = ref new Array<byte>(pixelHeight * pixelWidth * bpp);

    for (int y = 0; y < pixelHeight; y++)
    {
        for (int x = 0; x < pixelWidth; x++)
        {
            int pixelIndex = y * pixelWidth + x;
            int byteIndex = pixelIndex * bpp;

            // Set the current pixel bytes.
            bytes[byteIndex] = (byte)(std::rand() % 256);        // Red
            bytes[byteIndex + 1] = (byte)(std::rand() % 256);    // Green
            bytes[byteIndex + 2] = (byte)(std::rand() % 256);    // Blue
            bytes[byteIndex + 3] = (byte)((std::rand() % 56) + 200);    // Alpha (0xff = fully opaque)
        }
    }

    // Create RandomAccessStream from byte array.
    InMemoryRandomAccessStream^ randomAccessStream = ref new InMemoryRandomAccessStream();
    IOutputStream^ outputStream = randomAccessStream->GetOutputStreamAt(0);
    DataWriter^ writer = ref new DataWriter(outputStream);
    writer->WriteBytes(bytes);

    create_task(writer->StoreAsync()).then([writer](unsigned int)
    {
        create_task(writer->FlushAsync());
    });

    return randomAccessStream;
}

Ersetzen der Standardkarte

So ersetzen Sie die Standardkarte durch Drittanbieter- oder benutzerdefinierte Kacheln: