Наложение плиточных изображений на карту

Примечание

MapControl и службы карт требуют ключ проверки подлинности Maps, который называется MapServiceToken. Дополнительные сведения о получении и установке ключа проверки подлинности карт см. в статье Запрос ключа проверки подлинности карт.

Выполняйте наложение сторонних или настраиваемых мозаичных изображений на карту с помощью источников таких изображений. Используйте источники мозаичных изображений для наложения специальных сведений, таких как данные о погоде, населении или сейсмической активности, а также для полной замены карты по умолчанию.

Совет. Чтобы получить дополнительные сведения об использовании карт в приложении, скачайте пример карты универсальной платформы Windows (UWP) с сайта GitHub.

Общие сведения о мозаичном изображении

Службы карт, например Карты Nokia и Карты Bing, нарезают карты на квадратные плитки для быстрого восстановления и отображения. Эти плитки имеют размер 256 пикселей на 256 пикселей и предварительно обрабатываются на нескольких уровнях детализации. Многие сторонние службы также предоставляют данные на основе карт, порезанные на плитки. Используйте источники плиточных данных для получения сторонних плиток или создания собственных пользовательских плиток и наложите их на карту, которая отображается в MapControl.

Важно При использовании источников плиток вам не нужно писать код для запроса или размещения отдельных плиток. MapControl запрашивает плитки по мере необходимости. Каждый запрос задает координаты X и Y и масштаб для отдельной плитки. Можно просто задать формат универсального кода ресурса (URI) или имя файла, которые будут использоваться для получения плиток, в свойстве UriFormatString. То есть, вы вставляете заменяемые параметры в базовый универсальный код ресурса (URI) или имя файла, чтобы указать, где нужно передать координаты X и Y, а также масштаб для каждой плитки.

Вот пример свойства UriFormatString для HttpMapTileDataSource, в котором показаны заменяемые параметры для координат X и Y и масштаба.

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

(Координаты X и Y представляют расположение отдельной плитки на карте мира на указанном уровне детализации. Система нумеровки плиток начинается с {0, 0} в левом верхнем углу карты. Например, плитка в {1, 2} находится во втором столбце третьей строки сетки плиток.)

Дополнительные сведения о системе плиток, использующейся службами карт, см. в разделе Система плиток Карт Bing.

Наложение плиток из источника плиточных данных

Наложите плиточные изображения из источника плиточных данных на карту с помощью MapTileDataSource.

  1. Создайте экземпляр одного из трех классов источника плиточных данных, производный от MapTileDataSource.

    Настройте UriFormatString для запросов плиток путем вставки заменяемых параметров в базовый универсальный код ресурса (URI) или имя файла.

    Следующий пример создает экземпляр HttpMapTileDataSource. В этом примере задано значение UriFormatString в конструкторе HttpMapTileDataSource.

        HttpMapTileDataSource dataSource = new HttpMapTileDataSource(
          "http://www.<web service name>.com/z={zoomlevel}&x={x}&y={y}");
    
  2. Создайте экземпляр и настройте MapTileSource. Задайте MapTileDataSource, настроенный на предыдущем шаге как DataSource для MapTileSource.

    В следующем примере DataSource указано в конструкторе MapTileSource.

        MapTileSource tileSource = new MapTileSource(dataSource);
    

    Можно ограничить условия, при которых отображаются плитки, с помощью свойств MapTileSource.

    • Отображайте плитки только в определенной географической зоне, указав значение для свойства Bounds.
    • Отображайте плитки только при определенном уровне детализации, указав значения для свойства ZoomLevelRange.

    При необходимости настройте другие свойства для MapTileSource, которые влияют на загрузку или отображение плиток, например Layer, AllowOverstretch, IsRetryEnabled и IsTransparencyEnabled.

  3. Добавьте MapTileSource в коллекцию TileSources элемента MapControl.

         MapControl1.TileSources.Add(tileSource);
    

Наложение плиток из веб-службы

Наложите плиточные изображения из веб-службы с помощью HttpMapTileDataSource.

  1. Создайте экземпляр HttpMapTileDataSource.

  2. Укажите формат универсального кода ресурса (URI), который веб-служба ожидает в качестве значения свойства UriFormatString. Чтобы создать это значение, вставьте заменяемые параметры в базовый универсальный код ресурса (URI). Например, в следующем примере кода значение свойства UriFormatString является таким:

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

    Веб-служба должна поддерживать универсальный код ресурса (URI), который содержит заменяемые параметры {x}, {y} и {zoomlevel}. Большинство веб-служб (например, Nokia, Bing и Google) поддерживают URI такого формата. Если веб-служба требует дополнительных аргументов, которые недоступны со свойством UriFormatString, следует создать пользовательский универсальный код ресурса (URI). Создайте и верните универсальный код ресурса (URI) путем обработки события UriRequested. Подробнее см. в разделе Указание пользовательского универсального кода ресурса (URI) далее в данной статье.

  3. Затем выполните оставшиеся шаги, описанные ранее в разделе Общие сведения о мозаичных изображениях.

В следующем примере показано наложение плиток из вымышленной веб-службы на карту Северной Америки. Значение для UriFormatString указано в конструкторе HttpMapTileDataSource. В этом примере плитки отображаются только внутри географических границ, заданных необязательным свойством Bounds.

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

Наложение плиток из локального хранилища

Наложите плиточные изображения, хранящиеся как файлы в локальном хранилище, с помощью LocalMapTileDataSource. Обычно такие файлы упаковываются и распространяются вместе с приложением.

  1. Создайте экземпляр LocalMapTileDataSource.

  2. Задайте формат имен файлов в качестве значения свойства UriFormatString. Чтобы создать это значение, вставьте заменяемые параметры в базовое имя файла. Например, в следующем примере кода значение UriFormatString равно:

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

    Если формат имен файлов требует дополнительных аргументов, которые недоступны со свойством UriFormatString, следует создать пользовательский универсальный код ресурса (URI). Создайте и верните универсальный код ресурса (URI) путем обработки события UriRequested. Подробнее см. в разделе Указание пользовательского универсального кода ресурса (URI) далее в данной статье.

  3. Затем выполните оставшиеся шаги, описанные ранее в разделе Общие сведения о мозаичных изображениях.

Для загрузки мозаичных данных из локального хранилища можно использовать следующие протоколы и адреса:

URI Дополнительные сведения
ms-appx:/// Указывает на корневой каталог папки установки приложения.
Это расположение, на которое ссылается свойство Package.InstalledLocation.
ms-appdata:///local Указывает на корневой каталог локального хранилища приложения.
Это расположение, на которое ссылается свойство ApplicationData.LocalFolder.
ms-appdata:///temp Указывает на временную папку приложения.
Это расположение, на которое ссылается свойство ApplicationData.TemporaryFolder.

 

В следующем примере плитки, хранящиеся в виде файлов в установочной папке приложения, загружаются с помощью протокола ms-appx:///. Значение для UriFormatString указано в конструкторе LocalMapTileDataSource. В этом примере плитки отображаются, только если масштаб карты находится в диапазоне, заданном с помощью необязательного свойства ZoomLevelRange.

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

Указание пользовательского универсального кода ресурса (URI)

Если заменяемых параметров, доступных со свойством UriFormatString класса HttpMapTileDataSource или со свойством UriFormatString класса LocalMapTileDataSource, недостаточно для получения плиток, следует создать пользовательский универсальный код ресурса (URI). Создайте и верните пользовательский универсальный код ресурса (URI), предоставив пользовательский обработчик для события UriRequested. Событие UriRequested вызывается для каждой отдельной плитки.

  1. Для создания пользовательского универсального кода ресурса (URI) объедините в пользовательском обработчике для события UriRequested необходимые пользовательские аргументы со свойствами X, Y и ZoomLevel класса MapTileUriRequestedEventArgs.
  2. Верните пользовательский универсальный код ресурса (URI) в свойстве Uri класса MapTileUriRequest, который содержится в свойстве Request класса MapTileUriRequestedEventArgs.

В следующем примере показано, как предоставить пользовательский универсальный код ресурса (URI) путем создания пользовательского обработчика для события UriRequested. В этом примере также показано, как реализовать шаблон откладывания, если необходимо выполнить действие асинхронно для создания пользовательского универсального кода ресурса (URI).

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

Наложение плиток из пользовательского источника

Наложение пользовательских плиток с помощью CustomMapTileDataSource. Создайте плитки программным образом в памяти на лету или напишите собственный код для загрузки имеющихся плиток из другого источника.

Для создания или загрузки пользовательских плиток укажите пользовательский обработчик для события BitmapRequested. Событие BitmapRequested вызывается для каждой отдельной плитки.

  1. Для создания или получения пользовательской плитки объедините в пользовательском обработчике для события BitmapRequested необходимые пользовательские аргументы со свойствами X, Y и ZoomLevel класса MapTileBitmapRequestedEventArgs.
  2. Верните пользовательскую плитку в свойстве PixelData класса MapTileBitmapRequest, который содержится в свойстве Request класса MapTileBitmapRequestedEventArgs. Свойство PixelData имеет тип IRandomAccessStreamReference.

В следующем примере показано, как предоставить пользовательские плитки путем создания пользовательского обработчика для события BitmapRequested. В этом примере создаются идентичные красные частично непрозрачные плитки. В этом примере игнорируются свойства X, Y и ZoomLevel класса MapTileBitmapRequestedEventArgs. Несмотря на то, что это не реальный пример, в нем показано, как можно создавать пользовательские плитки в памяти на лету. В этом примере также показано, как реализовать шаблон откладывания, если необходимо выполнить действие асинхронно для создания настраиваемой плитки.

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

Замена карты по умолчанию

Чтобы полностью заменить карту по умолчанию сторонними или пользовательскими плитками, выполните следующие действия.