第11章 - 瓦片图层与地图服务

第11章:瓦片图层与地图服务

11.1 瓦片地图概述

11.1.1 瓦片金字塔结构

                    ┌───┐
         Level 0    │ 0 │           1 个瓦片 (全球)
                    └───┘
                    
                ┌───┬───┐
      Level 1   │ 0 │ 1 │           4 个瓦片
                ├───┼───┤
                │ 2 │ 3 │
                └───┴───┘
                
            ┌───┬───┬───┬───┐
            │ 0 │ 1 │ 2 │ 3 │
   Level 2  ├───┼───┼───┼───┤      16 个瓦片
            │ 4 │ 5 │ 6 │ 7 │
            ├───┼───┼───┼───┤
            │...│...│...│...│
            └───┴───┴───┴───┘

瓦片总数 = 4^n (n 为缩放级别)

11.1.2 瓦片编址方式

编址方式 描述 URL 示例
ZXY (OSM/Google) z/x/y 格式 /{z}/{x}/{y}.png
TMS 翻转 Y 轴 /{z}/{x}/{-y}.png
QuadKey (Bing) 四叉树编码 /{quadkey}.png

11.2 OpenStreetMap 瓦片

11.2.1 基本使用

using Mapsui.Tiling;

// 创建 OSM 瓦片图层
var osmLayer = OpenStreetMap.CreateTileLayer();
map.Layers.Add(osmLayer);

11.2.2 自定义 OSM 服务器

// 使用自定义 OSM 服务器
var customOsmSource = new HttpTileSource(
    new GlobalSphericalMercator(),
    "https://your-osm-server/{z}/{x}/{y}.png",
    name: "CustomOSM",
    attribution: new Attribution("© OpenStreetMap contributors")
);

var customOsmLayer = new TileLayer(customOsmSource)
{
    Name = "Custom OSM"
};

11.2.3 OSM 变体服务

public static class OsmVariants
{
    // Humanitarian OSM
    public static TileLayer CreateHotLayer()
    {
        return new TileLayer(new HttpTileSource(
            new GlobalSphericalMercator(),
            "https://tile-{s}.openstreetmap.fr/hot/{z}/{x}/{y}.png",
            new[] { "a", "b", "c" },
            name: "HOT"
        ));
    }
    
    // OpenTopoMap
    public static TileLayer CreateOpenTopoLayer()
    {
        return new TileLayer(new HttpTileSource(
            new GlobalSphericalMercator(),
            "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
            new[] { "a", "b", "c" },
            name: "OpenTopoMap"
        ));
    }
    
    // Stamen Terrain
    public static TileLayer CreateStamenTerrainLayer()
    {
        return new TileLayer(new HttpTileSource(
            new GlobalSphericalMercator(),
            "http://tile.stamen.com/terrain/{z}/{x}/{y}.png",
            name: "Stamen Terrain"
        ));
    }
}

11.3 自定义瓦片源

11.3.1 HttpTileSource

using BruTile;
using BruTile.Web;

// 创建自定义 HTTP 瓦片源
var tileSource = new HttpTileSource(
    new GlobalSphericalMercator(minZoomLevel: 0, maxZoomLevel: 18),
    "https://tiles.example.com/{z}/{x}/{y}.png",
    name: "CustomTiles",
    attribution: new Attribution("© Custom Provider")
);

var tileLayer = new TileLayer(tileSource)
{
    Name = "Custom Tiles"
};

11.3.2 带请求头的瓦片源

// 自定义请求配置
var httpTileSource = new HttpTileSource(
    new GlobalSphericalMercator(),
    "https://api.example.com/tiles/{z}/{x}/{y}.png",
    configureHttpRequestMessage: message =>
    {
        message.Headers.Add("Authorization", "Bearer your-token");
        message.Headers.Add("Referer", "https://your-app.com");
    }
);

11.3.3 天地图服务

public static class TiandituLayers
{
    private const string Token = "YOUR_TIANDITU_TOKEN";
    
    // 矢量底图
    public static TileLayer CreateVectorLayer()
    {
        var url = $"http://t{{s}}.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={{x}}&TILEROW={{y}}&TILEMATRIX={{z}}&tk={Token}";
        
        return new TileLayer(new HttpTileSource(
            new GlobalSphericalMercator(),
            url,
            new[] { "0", "1", "2", "3", "4", "5", "6", "7" },
            name: "天地图矢量"
        ));
    }
    
    // 矢量注记
    public static TileLayer CreateVectorAnnotationLayer()
    {
        var url = $"http://t{{s}}.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={{x}}&TILEROW={{y}}&TILEMATRIX={{z}}&tk={Token}";
        
        return new TileLayer(new HttpTileSource(
            new GlobalSphericalMercator(),
            url,
            new[] { "0", "1", "2", "3", "4", "5", "6", "7" },
            name: "天地图注记"
        ));
    }
    
    // 影像底图
    public static TileLayer CreateImageryLayer()
    {
        var url = $"http://t{{s}}.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={{x}}&TILEROW={{y}}&TILEMATRIX={{z}}&tk={Token}";
        
        return new TileLayer(new HttpTileSource(
            new GlobalSphericalMercator(),
            url,
            new[] { "0", "1", "2", "3", "4", "5", "6", "7" },
            name: "天地图影像"
        ));
    }
}

11.4 离线瓦片 (MBTiles)

11.4.1 使用 MBTiles 文件

using BruTile.MbTiles;
using SQLite;

// 从 MBTiles 文件创建瓦片源
var mbtilesPath = "maps/offline.mbtiles";
var mbTilesTileSource = new MbTilesTileSource(
    new SQLiteConnectionString(mbtilesPath, false)
);

var offlineLayer = new TileLayer(mbTilesTileSource)
{
    Name = "Offline Map"
};

map.Layers.Add(offlineLayer);

11.4.2 MBTiles 信息查询

public class MbTilesInfo
{
    public static void PrintInfo(string mbtilesPath)
    {
        using var connection = new SQLiteConnection(mbtilesPath);
        
        // 查询元数据
        var metadata = connection.Query<MbTilesMetadata>(
            "SELECT name, value FROM metadata"
        );
        
        Console.WriteLine("MBTiles Metadata:");
        foreach (var item in metadata)
        {
            Console.WriteLine($"  {item.Name}: {item.Value}");
        }
        
        // 查询瓦片数量
        var count = connection.ExecuteScalar<int>(
            "SELECT COUNT(*) FROM tiles"
        );
        Console.WriteLine($"Total tiles: {count}");
        
        // 查询缩放级别范围
        var minZoom = connection.ExecuteScalar<int>(
            "SELECT MIN(zoom_level) FROM tiles"
        );
        var maxZoom = connection.ExecuteScalar<int>(
            "SELECT MAX(zoom_level) FROM tiles"
        );
        Console.WriteLine($"Zoom levels: {minZoom} - {maxZoom}");
    }
    
    private class MbTilesMetadata
    {
        public string Name { get; set; }
        public string Value { get; set; }
    }
}

11.4.3 瓦片缓存

// 创建带文件缓存的瓦片源
var cacheDir = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
    "MapsuiCache"
);

var cachedTileSource = new HttpTileSource(
    new GlobalSphericalMercator(),
    "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
    name: "CachedOSM",
    persistentCache: new FileCache(cacheDir, "png")
);

var cachedLayer = new TileLayer(cachedTileSource)
{
    Name = "Cached OSM"
};

11.5 WMS 服务

11.5.1 基本 WMS 图层

using Mapsui.Providers.Wms;

// 创建 WMS 提供者
var wmsUrl = "https://example.com/wms";
var wmsProvider = new WmsProvider(new Uri(wmsUrl));
wmsProvider.LayerNames = new[] { "roads", "buildings" };
wmsProvider.CRS = "EPSG:3857";
wmsProvider.Version = "1.3.0";
wmsProvider.ImageFormat = "image/png";

// 创建 WMS 图层
var wmsLayer = new ImageLayer
{
    Name = "WMS Layer",
    DataSource = wmsProvider
};

map.Layers.Add(wmsLayer);

11.5.2 WMS GetCapabilities

public static async Task<WmsCapabilities> GetWmsCapabilitiesAsync(string wmsUrl)
{
    using var client = new HttpClient();
    var capabilitiesUrl = $"{wmsUrl}?SERVICE=WMS&REQUEST=GetCapabilities";
    var xml = await client.GetStringAsync(capabilitiesUrl);
    
    return ParseWmsCapabilities(xml);
}

public class WmsCapabilities
{
    public string Title { get; set; }
    public List<WmsLayer> Layers { get; set; }
    public List<string> SupportedCrs { get; set; }
    public List<string> SupportedFormats { get; set; }
}

11.6 WMTS 服务

11.6.1 使用 WMTS

using BruTile.Wmts;

// 从 WMTS Capabilities 创建瓦片源
var wmtsUrl = "https://example.com/wmts/1.0.0/WMTSCapabilities.xml";

var wmtsTileSource = await CreateWmtsTileSourceAsync(
    wmtsUrl,
    "layerIdentifier"
);

var wmtsLayer = new TileLayer(wmtsTileSource)
{
    Name = "WMTS Layer"
};

private static async Task<ITileSource> CreateWmtsTileSourceAsync(
    string capabilitiesUrl, 
    string layerIdentifier)
{
    using var client = new HttpClient();
    using var stream = await client.GetStreamAsync(capabilitiesUrl);
    
    var tileSources = WmtsParser.Parse(stream);
    
    return tileSources.First(ts => 
        ts.Name.Equals(layerIdentifier, StringComparison.OrdinalIgnoreCase)
    );
}

11.7 ArcGIS 服务

11.7.1 ArcGIS REST 服务

using Mapsui.ArcGIS;

// ArcGIS 动态地图服务
var dynamicServiceUrl = "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer";
var dynamicProvider = new ArcGISDynamicProvider(new Uri(dynamicServiceUrl));

var dynamicLayer = new ImageLayer
{
    Name = "ArcGIS Dynamic",
    DataSource = dynamicProvider
};

// ArcGIS 瓦片服务
var tileServiceUrl = "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer";
var tileLayer = CreateArcGisTileLayer(tileServiceUrl, "World Imagery");

private static TileLayer CreateArcGisTileLayer(string url, string name)
{
    var tileSource = new ArcGISTileSource(
        url,
        new GlobalSphericalMercator()
    );
    
    return new TileLayer(tileSource) { Name = name };
}

11.7.2 Esri 底图服务

public static class EsriBasemaps
{
    private const string BaseUrl = "https://services.arcgisonline.com/ArcGIS/rest/services";
    
    public static TileLayer CreateStreetMap() => 
        CreateEsriLayer($"{BaseUrl}/World_Street_Map/MapServer", "Esri Street");
    
    public static TileLayer CreateTopographic() => 
        CreateEsriLayer($"{BaseUrl}/World_Topo_Map/MapServer", "Esri Topo");
    
    public static TileLayer CreateImagery() => 
        CreateEsriLayer($"{BaseUrl}/World_Imagery/MapServer", "Esri Imagery");
    
    public static TileLayer CreateOceans() => 
        CreateEsriLayer($"{BaseUrl}/Ocean_Basemap/MapServer", "Esri Oceans");
    
    public static TileLayer CreateGray() => 
        CreateEsriLayer($"{BaseUrl}/Canvas/World_Light_Gray_Base/MapServer", "Esri Gray");
    
    private static TileLayer CreateEsriLayer(string url, string name)
    {
        // 使用标准 XYZ 格式的 Esri 服务
        var tileUrl = $"{url}/tile/{{z}}/{{y}}/{{x}}";
        
        var tileSource = new HttpTileSource(
            new GlobalSphericalMercator(),
            tileUrl,
            name: name
        );
        
        return new TileLayer(tileSource) { Name = name };
    }
}

11.8 底图切换

11.8.1 底图管理器

public class BasemapManager
{
    private readonly Map _map;
    private readonly Dictionary<string, Func<ILayer>> _basemapFactories;
    private string _currentBasemap;
    
    public BasemapManager(Map map)
    {
        _map = map;
        _basemapFactories = new Dictionary<string, Func<ILayer>>
        {
            ["OSM"] = () => OpenStreetMap.CreateTileLayer(),
            ["Esri Street"] = () => EsriBasemaps.CreateStreetMap(),
            ["Esri Imagery"] = () => EsriBasemaps.CreateImagery(),
            ["Esri Topo"] = () => EsriBasemaps.CreateTopographic(),
            ["天地图矢量"] = () => TiandituLayers.CreateVectorLayer(),
            ["天地图影像"] = () => TiandituLayers.CreateImageryLayer()
        };
    }
    
    public IEnumerable<string> AvailableBasemaps => _basemapFactories.Keys;
    
    public string CurrentBasemap => _currentBasemap;
    
    public void SetBasemap(string basemapName)
    {
        if (!_basemapFactories.ContainsKey(basemapName))
            throw new ArgumentException($"Unknown basemap: {basemapName}");
        
        // 移除现有底图
        var existingBasemap = _map.Layers.FirstOrDefault(l => l.Name == "Basemap");
        if (existingBasemap != null)
            _map.Layers.Remove(existingBasemap);
        
        // 添加新底图
        var newBasemap = _basemapFactories[basemapName]();
        newBasemap.Name = "Basemap";
        _map.Layers.Insert(0, newBasemap);
        
        _currentBasemap = basemapName;
    }
}

11.9 本章小结

本章详细介绍了 Mapsui 的瓦片图层与地图服务:

  1. 瓦片地图概述:金字塔结构和编址方式
  2. OpenStreetMap 瓦片:基本使用和变体服务
  3. 自定义瓦片源:HttpTileSource 和天地图
  4. 离线瓦片:MBTiles 文件和瓦片缓存
  5. WMS 服务:Web Map Service 的使用
  6. WMTS 服务:Web Map Tile Service 的使用
  7. ArcGIS 服务:动态服务和瓦片服务
  8. 底图切换:底图管理器实现

在下一章中,我们将学习 NTS 几何处理集成。

11.10 思考与练习

  1. 实现一个支持多种底图切换的控件。
  2. 创建一个瓦片下载工具,将在线瓦片保存为 MBTiles。
  3. 实现 WMS GetFeatureInfo 功能,获取要素属性。
  4. 创建一个混合图层,叠加影像底图和矢量注记。
  5. 实现瓦片预加载功能,提前加载相邻区域的瓦片。
posted @ 2026-01-08 14:40  我才是银古  阅读(16)  评论(0)    收藏  举报