覆盖地图上的平铺图像

重要

必应地图企业版服务停用

Windows.Services.Maps 命名空间中的 UWP MapControl 和地图服务依赖于必应地图。 必应地图企业版已弃用,并且将停用,此时 MapControl 和服务将不再接收数据。

有关详细信息,请参阅必应地图开发人员中心和必应地图文档

注意

MapControl 和地图服务需要称为 MapServiceToken 的地图身份验证密钥。 有关获取和设置地图身份验证密钥的详细信息,请参阅请求地图身份验证密钥

使用磁贴源覆盖地图上的第三方或自定义平铺图像。 使用磁贴源覆盖专业信息(例如,天气数据、人口数据或地震数据),或者使用磁贴源替换整个默认地图。

平铺图像概述

必应地图等地图服务将地图切成方形图块,以便快速检索和显示。 这些磁贴的大小为 256 像素,以 256 像素为单位,并预先呈现在多个细节级别。 许多第三方服务还提供基于地图的数据,这些数据被切入磁贴。 使用磁贴源检索第三方磁贴,或创建自己的自定义磁贴,并将其覆盖在 MapControl 中显示的地图上。

使用磁贴源时,无需编写代码来请求或定位各个磁贴。 MapControl 请求磁贴,因为它需要磁贴。 每个请求指定单个磁贴的 X 和 Y 坐标和缩放级别。 只需指定要用于检索 UriFormatString 属性中的磁贴的 Uri 或文件名的格式。 也就是说,在基 URI 或文件名中插入可替换参数,以指示在何处传递 X 和 Y 坐标以及每个磁贴的缩放级别。

下面是 HttpMapTileDataSourceUriFormatString 属性示例,该属性显示 X 坐标和 Y 坐标和缩放级别的可替换参数。

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

(X 和 Y 坐标表示世界地图中各个磁贴在指定细节级别的位置。图块编号系统从地图左上角的 {0, 0} 开始。例如,位于 {1, 2} 的磁贴位于磁贴网格第三行的第二列中。

有关映射服务使用的图块系统的详细信息,请参阅 必应地图图块系统

覆盖磁贴源中的磁贴

使用 MapTileDataSource 覆盖地图上图块源中的平铺图像。

  1. 实例化从 MapTileDataSource 继承的三个磁贴数据源类之一。

    UriFormatString 配置为通过插入基 Uri 或文件名中的可替换参数来请求磁贴。

    以下示例实例化 HttpMapTileDataSource 此示例在 HttpMapTileDataSource 的构造函数中指定 UriFormatString 的值

        HttpMapTileDataSource dataSource = new HttpMapTileDataSource(
          "http://www.<web service name>.com/z={zoomlevel}&x={x}&y={y}");
    
  2. 实例化和配置 MapTileSource 指定在上一步中配置的 MapTileDataSource 作为 MapTileSource 的 DataSource

    以下示例在 MapTileSource 的构造函数中指定 DataSource

        MapTileSource tileSource = new MapTileSource(dataSource);
    

    可以使用 MapTileSource 的属性来限制磁贴显示的条件。

    (可选)配置影响加载或显示磁贴的 MapTileSource 的其他属性,例如 Layer、AllowOverstretchIsRetryEnabled 和 IsTransparencyEnabled。

  3. MapTileSource 添加到 MapControl TileSources 集合。

         MapControl1.TileSources.Add(tileSource);
    

覆盖 Web 服务中的磁贴

使用 HttpMapTileDataSource 覆盖从 Web 服务检索的平铺图像。

  1. 实例化 HttpMapTileDataSource

  2. 指定 Web 服务期望的 URI 格式作为 UriFormatString 属性的值 若要创建此值,请在基 URI 中插入可替换参数。 例如,在以下代码示例中,UriFormatString 的值为:

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

    Web 服务必须支持包含可替换参数 {x}、{y} 和 {zoomlevel} 的 URI。 大多数 Web 服务(例如 Nokia、必应和 Google)都支持采用此格式的 URI。 如果 Web 服务需要 UriFormatString 属性不可用的其他参数,则必须创建自定义 URI。 通过处理 UriRequested 事件创建并返回自定义 URI。 有关详细信息,请参阅本主题后面的“ 提供自定义 URI ”部分。

  3. 然后,按照之前在 平铺图像概述中介绍的剩余步骤进行操作。

以下示例覆盖北美地图上虚构的 Web 服务中的磁贴。 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。 通过处理 UriRequested 事件创建并返回自定义 URI。 有关详细信息,请参阅本主题后面的“ 提供自定义 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

如果可用于 HttpMapTileDataSource 的 UriFormatString 属性或 LocalMapTileDataSource UriFormatString 属性的可替换参数不足以检索磁贴,则必须创建自定义 URI。 通过为 UriRequested 事件提供自定义处理程序来创建和返回自定义 URI。 为每个单独的磁贴引发 UriRequested 事件。

  1. 在 UriRequested 事件的自定义处理程序中,将所需的自定义参数与 MapTileUriRequestedEventArgs XYZoomLevel 属性相结合,以创建自定义 URI。
  2. 返回 MapTileUriRequestUri 属性中的自定义 URI,该属性包含在 MapTileUriRequestedEventArgsRequest 属性中。

以下示例演示如何通过为 UriRequested 事件创建自定义处理程序来提供自定义 URI。 它还演示如何实现延迟模式(如果必须异步执行某些操作才能创建自定义 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 事件的自定义处理程序中,将所需的自定义参数与 MapTileBitmapRequestedEventArgs XYZoomLevel 属性相结合,以创建或检索自定义磁贴。
  2. 返回 MapTileBitmapRequest PixelData 属性中的自定义磁贴,该属性包含在 MapTileBitmapRequestedEventArgs 的请求属性中。 PixelData 属性的类型为 IRandomAccessStreamReference

以下示例演示如何通过为 BitmapRequested 事件创建自定义处理程序来提供自定义磁贴。 本示例创建部分不透明的红色磁贴。 该示例忽略 MapTileBitmapRequestedEventArgsXY ZoomLevel 属性。 虽然这不是实际示例,但该示例演示了如何动态创建内存中自定义磁贴。 该示例还演示如何在异步执行某些操作以创建自定义磁贴时实现延迟模式。

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

替换默认映射

若要将默认地图完全替换为第三方或自定义磁贴,请执行以下操作: