第05章 - 图层系统详解
第05章:图层系统详解
5.1 图层概述
5.1.1 图层的作用
图层(Layer)是 Mapsui 中组织和管理地图内容的基本单位。每个图层代表一类地理信息,多个图层叠加形成完整的地图视图:
┌─────────────────────────────────────────────────────────────────┐
│ 地图视图 │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 标注图层 (最上层) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ POI 点图层 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 道路线图层 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 建筑面图层 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 底图瓦片图层 (最下层) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
5.1.2 图层类型体系
ILayer (接口)
│
BaseLayer (基类)
│
┌───────────────┼───────────────┐
│ │ │
MemoryLayer ImageLayer Layer
│ │
WritableLayer TileLayer
│
ObservableMemoryLayer
5.1.3 ILayer 接口
public interface ILayer : IDisposable
{
// 标识
string Name { get; set; }
long Id { get; }
// 状态
bool Enabled { get; set; } // 是否启用
double Opacity { get; set; } // 不透明度 (0-1)
bool Busy { get; } // 是否正在获取数据
// 样式
IStyle? Style { get; set; }
// 范围
MRect? Extent { get; }
// 缩放限制
double MinVisible { get; set; } // 最小可见分辨率
double MaxVisible { get; set; } // 最大可见分辨率
// 数据获取
IEnumerable<IFeature> GetFeatures(MRect extent, double resolution);
// 事件
event DataChangedEventHandler? DataChanged;
}
5.2 MemoryLayer - 内存图层
5.2.1 基本使用
MemoryLayer 是最常用的图层类型,用于存储和显示内存中的要素:
// 创建 MemoryLayer
var memoryLayer = new MemoryLayer
{
Name = "Points",
Features = CreateFeatures(),
Style = new SymbolStyle
{
SymbolScale = 0.5,
Fill = new Brush(Color.Red)
}
};
// 添加到地图
map.Layers.Add(memoryLayer);
// 创建要素的方法
private IEnumerable<IFeature> CreateFeatures()
{
var features = new List<PointFeature>();
var cities = new[]
{
(Name: "北京", Lon: 116.4, Lat: 39.9),
(Name: "上海", Lon: 121.5, Lat: 31.2),
(Name: "广州", Lon: 113.3, Lat: 23.1)
};
foreach (var city in cities)
{
var point = SphericalMercator.FromLonLat(city.Lon, city.Lat).ToMPoint();
var feature = new PointFeature(point);
feature["name"] = city.Name;
features.Add(feature);
}
return features;
}
5.2.2 动态更新数据
// 更新要素数据
var layer = map.Layers.FindLayer("Points") as MemoryLayer;
if (layer != null)
{
// 方式1:替换整个要素集合
layer.Features = newFeatures;
layer.DataHasChanged();
// 方式2:使用要素的可枚举属性(推荐用于大数据集)
layer.Features = GetFeaturesFromDatabase();
layer.DataHasChanged();
}
5.2.3 按需加载数据
// 创建延迟加载的 MemoryLayer
var lazyLayer = new MemoryLayer
{
Name = "LazyData",
Features = new LazyFeatureCollection(() => LoadFeaturesFromDatabase()),
Style = CreateStyle()
};
// 自定义延迟加载集合
public class LazyFeatureCollection : IEnumerable<IFeature>
{
private readonly Func<IEnumerable<IFeature>> _loader;
private IEnumerable<IFeature>? _cache;
public LazyFeatureCollection(Func<IEnumerable<IFeature>> loader)
{
_loader = loader;
}
public IEnumerator<IFeature> GetEnumerator()
{
_cache ??= _loader().ToList();
return _cache.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
5.3 WritableLayer - 可写图层
5.3.1 基本使用
WritableLayer 继承自 MemoryLayer,提供了添加、删除、清除要素的方法:
// 创建 WritableLayer
var writableLayer = new WritableLayer
{
Name = "EditablePoints",
Style = new SymbolStyle
{
SymbolScale = 0.5,
Fill = new Brush(Color.Blue)
}
};
// 添加要素
var point = SphericalMercator.FromLonLat(116.4, 39.9).ToMPoint();
var feature = new PointFeature(point);
feature["name"] = "新增点";
writableLayer.Add(feature);
writableLayer.DataHasChanged();
// 批量添加
writableLayer.AddRange(featureList);
writableLayer.DataHasChanged();
// 删除要素
writableLayer.TryRemove(feature);
writableLayer.DataHasChanged();
// 清除所有要素
writableLayer.Clear();
writableLayer.DataHasChanged();
5.3.2 与用户交互结合
public class DrawingManager
{
private readonly WritableLayer _drawLayer;
private readonly Map _map;
public DrawingManager(Map map)
{
_map = map;
_drawLayer = new WritableLayer
{
Name = "Drawing",
Style = CreateDrawingStyle()
};
_map.Layers.Add(_drawLayer);
}
public void AddPointAtClick(MPoint worldPosition)
{
var feature = new PointFeature(worldPosition);
feature["created"] = DateTime.Now;
_drawLayer.Add(feature);
_drawLayer.DataHasChanged();
}
public void DeleteFeature(IFeature feature)
{
_drawLayer.TryRemove(feature);
_drawLayer.DataHasChanged();
}
public void ClearAll()
{
_drawLayer.Clear();
_drawLayer.DataHasChanged();
}
public IEnumerable<IFeature> GetAllFeatures()
{
return _drawLayer.GetFeatures(null, 0);
}
private IStyle CreateDrawingStyle()
{
return new SymbolStyle
{
SymbolScale = 0.6,
Fill = new Brush(Color.Orange),
Outline = new Pen(Color.Black, 2)
};
}
}
5.4 Layer - 基于数据提供者的图层
5.4.1 Layer 类的特点
Layer 类使用数据提供者(Provider)来获取数据,支持异步数据获取:
// 创建基于 Provider 的 Layer
var layer = new Layer
{
Name = "WfsLayer",
DataSource = new WfsProvider(new Uri("https://example.com/wfs")),
Style = new VectorStyle
{
Fill = new Brush(Color.Blue),
Line = new Pen(Color.Red, 1)
}
};
// 配置数据获取
layer.DataSource.CRS = "EPSG:4326"; // 数据源坐标系
5.4.2 自定义 Provider
public class DatabaseProvider : IProvider
{
private readonly string _connectionString;
private readonly string _tableName;
public string? CRS { get; set; } = "EPSG:4326";
public MRect? Extent { get; private set; }
public DatabaseProvider(string connectionString, string tableName)
{
_connectionString = connectionString;
_tableName = tableName;
}
public IEnumerable<IFeature> GetFeatures(FetchInfo fetchInfo)
{
var features = new List<IFeature>();
using var connection = new NpgsqlConnection(_connectionString);
connection.Open();
var extent = fetchInfo.Extent;
var sql = $@"
SELECT id, name, ST_AsBinary(geom) as geom
FROM {_tableName}
WHERE geom && ST_MakeEnvelope({extent.MinX}, {extent.MinY},
{extent.MaxX}, {extent.MaxY}, 4326)";
using var cmd = new NpgsqlCommand(sql, connection);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
var wkb = (byte[])reader["geom"];
var geometry = new WKBReader().Read(wkb);
var feature = new GeometryFeature(geometry);
feature["id"] = reader["id"];
feature["name"] = reader["name"];
features.Add(feature);
}
return features;
}
}
5.5 TileLayer - 瓦片图层
5.5.1 使用在线瓦片服务
// OpenStreetMap
var osmLayer = OpenStreetMap.CreateTileLayer();
map.Layers.Add(osmLayer);
// 自定义瓦片源
var customTileLayer = new TileLayer(
new HttpTileSource(
new GlobalSphericalMercator(),
"https://tiles.example.com/{z}/{x}/{y}.png",
name: "CustomTiles"
)
);
map.Layers.Add(customTileLayer);
5.5.2 使用离线瓦片 (MBTiles)
// 使用 BruTile.MBTiles
var mbTilesPath = "path/to/tiles.mbtiles";
var mbTilesTileSource = new MbTilesTileSource(
new SQLiteConnectionString(mbTilesPath, false)
);
var offlineLayer = new TileLayer(mbTilesTileSource)
{
Name = "OfflineMap"
};
map.Layers.Add(offlineLayer);
5.5.3 配置瓦片缓存
// 配置文件缓存
var cacheDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MapsuiCache"
);
var tileSource = new HttpTileSource(
new GlobalSphericalMercator(),
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
name: "OSM",
persistentCache: new FileCache(cacheDir, "png")
);
var cachedLayer = new TileLayer(tileSource);
5.6 ImageLayer - 图片图层
5.6.1 显示单张图片
// 创建图片图层
var imageLayer = new ImageLayer("Overlay")
{
DataSource = new ImageProvider(
imageBytes,
new MRect(minX, minY, maxX, maxY) // 图片在地图上的范围
)
};
map.Layers.Add(imageLayer);
5.6.2 从文件加载图片
// 从文件加载
var imagePath = "path/to/overlay.png";
var imageBytes = File.ReadAllBytes(imagePath);
// 定义图片的地理范围(Web Mercator 坐标)
var extent = new MRect(
SphericalMercator.FromLonLat(116.0, 39.0).X,
SphericalMercator.FromLonLat(116.0, 39.0).Y,
SphericalMercator.FromLonLat(117.0, 40.0).X,
SphericalMercator.FromLonLat(117.0, 40.0).Y
);
var imageLayer = new ImageLayer("OverlayImage")
{
DataSource = new ImageProvider(imageBytes, extent),
Opacity = 0.7 // 设置透明度
};
5.7 RasterizingLayer - 栅格化图层
5.7.1 提高大数据量图层的性能
当矢量图层包含大量要素时,可以使用 RasterizingLayer 将其栅格化以提高渲染性能:
// 创建一个包含大量要素的原始图层
var originalLayer = new MemoryLayer
{
Name = "OriginalData",
Features = CreateLargeFeatureSet() // 假设有 10000+ 要素
};
// 包装为 RasterizingLayer
var rasterizedLayer = new RasterizingLayer(originalLayer)
{
Name = "RasterizedData"
};
map.Layers.Add(rasterizedLayer);
5.7.2 配置栅格化参数
var rasterizingLayer = new RasterizingLayer(
originalLayer,
renderTimeout: 1000, // 渲染超时(毫秒)
overscanRatio: 1.0 // 过扫描比例
);
5.8 MyLocationLayer - 位置图层
5.8.1 显示用户位置
// 创建位置图层
var myLocationLayer = new MyLocationLayer(map)
{
Name = "MyLocation"
};
map.Layers.Add(myLocationLayer);
// 更新位置
myLocationLayer.UpdateMyLocation(
new MPoint(
SphericalMercator.FromLonLat(116.4, 39.9).X,
SphericalMercator.FromLonLat(116.4, 39.9).Y
),
true // 是否动画移动
);
// 更新方向
myLocationLayer.UpdateMyDirection(45); // 角度
5.8.2 自定义位置样式
// MyLocationLayer 使用内置样式
// 如需完全自定义,可以创建自己的位置图层
public class CustomLocationLayer : MemoryLayer
{
private PointFeature _locationFeature;
public CustomLocationLayer()
{
Name = "CustomLocation";
Style = CreateLocationStyle();
}
public void UpdateLocation(double lon, double lat)
{
var point = SphericalMercator.FromLonLat(lon, lat).ToMPoint();
_locationFeature = new PointFeature(point);
Features = new[] { _locationFeature };
DataHasChanged();
}
private IStyle CreateLocationStyle()
{
return new SymbolStyle
{
SymbolScale = 0.8,
Fill = new Brush(Color.Blue),
Outline = new Pen(Color.White, 3)
};
}
}
5.9 图层集合管理
5.9.1 LayerCollection 操作
// 添加图层
map.Layers.Add(layer);
map.Layers.Insert(0, baseLayer); // 插入到指定位置
// 移除图层
map.Layers.Remove(layer);
map.Layers.RemoveAt(0);
// 查找图层
var layer = map.Layers.FindLayer("LayerName");
var allLayers = map.Layers.ToList();
// 调整顺序
map.Layers.Move(0, 2); // 将索引0的图层移动到索引2
// 清空
map.Layers.Clear();
5.9.2 图层可见性控制
// 控制图层可见性
layer.Enabled = true; // 显示
layer.Enabled = false; // 隐藏
// 基于缩放级别的可见性
layer.MinVisible = 500; // 分辨率小于500时可见(放大时)
layer.MaxVisible = 50000; // 分辨率大于50000时不可见(缩小时)
// 设置透明度
layer.Opacity = 0.5; // 50% 透明
5.9.3 图层事件
// 监听图层集合变化
map.Layers.CollectionChanged += (sender, args) =>
{
Console.WriteLine($"Layers changed: {args.Action}");
};
// 监听单个图层数据变化
layer.DataChanged += (sender, args) =>
{
if (args.Error != null)
{
Console.WriteLine($"Data error: {args.Error.Message}");
}
else
{
Console.WriteLine("Data loaded successfully");
}
};
5.10 图层组织最佳实践
5.10.1 图层组织策略
public class LayerOrganizer
{
public static void OrganizeLayers(Map map)
{
// 1. 底图图层(最底层)
var baseLayer = OpenStreetMap.CreateTileLayer();
baseLayer.Name = "BaseMap";
// 2. 面图层
var polygonLayer = new MemoryLayer
{
Name = "Polygons",
Features = LoadPolygons()
};
// 3. 线图层
var lineLayer = new MemoryLayer
{
Name = "Lines",
Features = LoadLines()
};
// 4. 点图层
var pointLayer = new MemoryLayer
{
Name = "Points",
Features = LoadPoints()
};
// 5. 标注图层(最顶层)
var labelLayer = new MemoryLayer
{
Name = "Labels",
Features = LoadLabels()
};
// 按顺序添加(先添加的在下层)
map.Layers.Add(baseLayer);
map.Layers.Add(polygonLayer);
map.Layers.Add(lineLayer);
map.Layers.Add(pointLayer);
map.Layers.Add(labelLayer);
}
}
5.10.2 图层管理器类
public class LayerManager
{
private readonly Map _map;
private readonly Dictionary<string, ILayer> _layerIndex = new();
public LayerManager(Map map)
{
_map = map;
}
public void AddLayer(ILayer layer, int? index = null)
{
if (_layerIndex.ContainsKey(layer.Name))
{
throw new ArgumentException($"Layer '{layer.Name}' already exists");
}
if (index.HasValue)
_map.Layers.Insert(index.Value, layer);
else
_map.Layers.Add(layer);
_layerIndex[layer.Name] = layer;
}
public ILayer? GetLayer(string name)
{
return _layerIndex.GetValueOrDefault(name);
}
public void RemoveLayer(string name)
{
if (_layerIndex.TryGetValue(name, out var layer))
{
_map.Layers.Remove(layer);
_layerIndex.Remove(name);
}
}
public void SetLayerVisibility(string name, bool visible)
{
if (_layerIndex.TryGetValue(name, out var layer))
{
layer.Enabled = visible;
}
}
public void SetLayerOpacity(string name, double opacity)
{
if (_layerIndex.TryGetValue(name, out var layer))
{
layer.Opacity = Math.Clamp(opacity, 0, 1);
}
}
public void MoveLayerToTop(string name)
{
if (_layerIndex.TryGetValue(name, out var layer))
{
var currentIndex = _map.Layers.IndexOf(layer);
var targetIndex = _map.Layers.Count - 1;
_map.Layers.Move(currentIndex, targetIndex);
}
}
}
5.11 本章小结
本章详细介绍了 Mapsui 的图层系统:
- 图层类型体系:ILayer 接口、BaseLayer 基类及各种具体实现
- MemoryLayer:内存中存储要素的基础图层
- WritableLayer:支持动态编辑的可写图层
- Layer:基于数据提供者的图层,支持异步数据获取
- TileLayer:瓦片图层,支持在线和离线瓦片
- ImageLayer:显示单张图片的图层
- RasterizingLayer:将矢量图层栅格化以提高性能
- MyLocationLayer:显示用户位置的特殊图层
- 图层集合管理:添加、删除、排序、可见性控制
- 最佳实践:图层组织策略和管理器类设计
在下一章中,我们将学习数据提供者与数据源的使用。
5.12 思考与练习
- 解释 MemoryLayer 和 WritableLayer 的区别,各适合什么场景?
- 实现一个支持分层可见性控制的图层管理器。
- 创建一个离线地图应用,使用 MBTiles 作为底图。
- 实现一个热力图图层,使用 RasterizingLayer 优化性能。
- 设计一个图层 TOC(Table of Contents)控件,支持显示/隐藏和调整顺序。

浙公网安备 33010602011771号