第14章 - 性能优化与最佳实践
第14章:性能优化与最佳实践
14.1 性能优化原则
14.1.1 性能瓶颈分析
┌─────────────────────────────────────────────────────────────────┐
│ 性能瓶颈来源 │
├─────────────────────────────────────────────────────────────────┤
│ 数据获取 │ 渲染性能 │ 内存管理 │
│ ───────── │ ───────── │ ───────── │
│ • 网络请求 │ • 要素数量过多 │ • 瓦片缓存不当 │
│ • 数据解析 │ • 复杂几何 │ • 要素重复创建 │
│ • 数据库查询 │ • 样式计算 │ • 事件订阅泄漏 │
└─────────────────────────────────────────────────────────────────┘
14.1.2 性能监控
// 启用性能小部件
var performanceWidget = new PerformanceWidget
{
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
MarginX = 10,
MarginY = 10
};
map.Widgets.Add(performanceWidget);
// 自定义性能监控
public class PerformanceMonitor
{
private readonly Stopwatch _stopwatch = new();
private readonly List<double> _frameTimes = new();
public void StartFrame()
{
_stopwatch.Restart();
}
public void EndFrame()
{
_stopwatch.Stop();
_frameTimes.Add(_stopwatch.Elapsed.TotalMilliseconds);
if (_frameTimes.Count > 100)
_frameTimes.RemoveAt(0);
}
public double AverageFrameTime => _frameTimes.Average();
public double FPS => 1000.0 / AverageFrameTime;
}
14.2 数据层优化
14.2.1 减少要素数量
// 视口裁剪:只加载视口范围内的要素
public IEnumerable<IFeature> GetVisibleFeatures(
IEnumerable<IFeature> allFeatures,
MRect viewport)
{
return allFeatures.Where(f =>
f.Extent?.Intersects(viewport) == true
);
}
// 按缩放级别过滤
public IEnumerable<IFeature> FilterByZoomLevel(
IEnumerable<IFeature> features,
double resolution)
{
var zoomLevel = ZoomLevelResolutions.GetZoomLevel(resolution);
return features.Where(f =>
{
var minZoom = f["minZoom"] as int? ?? 0;
var maxZoom = f["maxZoom"] as int? ?? 20;
return zoomLevel >= minZoom && zoomLevel <= maxZoom;
});
}
14.2.2 几何简化
using NetTopologySuite.Simplify;
// 根据分辨率简化几何
public Geometry SimplifyForDisplay(Geometry geometry, double resolution)
{
// 计算合适的简化容差
var tolerance = resolution * 2; // 2 像素容差
return TopologyPreservingSimplifier.Simplify(geometry, tolerance);
}
// 缓存简化后的几何
public class SimplifiedGeometryCache
{
private readonly Dictionary<(Geometry, int), Geometry> _cache = new();
public Geometry GetSimplified(Geometry geometry, double resolution)
{
var zoomLevel = ZoomLevelResolutions.GetZoomLevel(resolution);
var key = (geometry, zoomLevel);
if (!_cache.TryGetValue(key, out var simplified))
{
var tolerance = GetToleranceForZoom(zoomLevel);
simplified = TopologyPreservingSimplifier.Simplify(geometry, tolerance);
_cache[key] = simplified;
}
return simplified;
}
private double GetToleranceForZoom(int zoom) => zoom switch
{
< 5 => 10000,
< 10 => 1000,
< 15 => 100,
_ => 10
};
}
14.2.3 数据分页和懒加载
// 分页加载大数据集
public class PaginatedDataLoader
{
private readonly int _pageSize;
private readonly Func<int, int, IEnumerable<IFeature>> _loader;
public PaginatedDataLoader(
Func<int, int, IEnumerable<IFeature>> loader,
int pageSize = 1000)
{
_loader = loader;
_pageSize = pageSize;
}
public async IAsyncEnumerable<IFeature> LoadAllAsync()
{
int page = 0;
while (true)
{
var features = await Task.Run(() =>
_loader(page * _pageSize, _pageSize).ToList()
);
if (features.Count == 0) break;
foreach (var feature in features)
yield return feature;
page++;
}
}
}
14.3 渲染优化
14.3.1 使用 RasterizingLayer
// 将复杂矢量图层栅格化
var complexLayer = new MemoryLayer
{
Features = GetComplexFeatures() // 大量复杂几何
};
var rasterizedLayer = new RasterizingLayer(complexLayer)
{
Name = "RasterizedComplexLayer"
};
map.Layers.Add(rasterizedLayer);
14.3.2 样式优化
// 避免在每次渲染时创建新样式
// 不好的做法:
feature.Styles = new[] { new SymbolStyle { Fill = new Brush(Color.Red) } };
// 好的做法:预先创建样式并重用
private static readonly IStyle _pointStyle = new SymbolStyle
{
SymbolScale = 0.5,
Fill = new Brush(Color.Red)
};
feature.Styles = new[] { _pointStyle };
14.3.3 减少重绘
// 批量更新要素
public void UpdateFeatures(WritableLayer layer, IEnumerable<IFeature> features)
{
// 不好的做法:每次添加都触发重绘
foreach (var feature in features)
{
layer.Add(feature);
layer.DataHasChanged(); // 每次都重绘
}
// 好的做法:批量操作后统一重绘
foreach (var feature in features)
{
layer.Add(feature);
}
layer.DataHasChanged(); // 只重绘一次
}
14.4 瓦片缓存优化
14.4.1 配置合理的缓存
// 内存缓存
var memoryCache = new MemoryCache<byte[]>(
minTiles: 100,
maxTiles: 500
);
// 文件缓存
var cacheDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MapsuiCache"
);
var fileCache = new FileCache(cacheDir, "png")
{
MaxTileCount = 10000 // 最大缓存瓦片数
};
// 组合缓存
var tileSource = new HttpTileSource(
new GlobalSphericalMercator(),
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
persistentCache: fileCache
);
14.4.2 预加载瓦片
public class TilePreloader
{
private readonly ITileSource _tileSource;
public async Task PreloadAreaAsync(MRect extent, int minZoom, int maxZoom)
{
for (int zoom = minZoom; zoom <= maxZoom; zoom++)
{
var tiles = GetTilesInExtent(extent, zoom);
await Parallel.ForEachAsync(tiles, async (tile, ct) =>
{
try
{
await _tileSource.GetTileAsync(tile);
}
catch { /* 忽略错误 */ }
});
}
}
private IEnumerable<TileInfo> GetTilesInExtent(MRect extent, int zoom)
{
// 计算范围内的所有瓦片
var schema = new GlobalSphericalMercator();
return schema.GetTileInfos(extent, zoom);
}
}
14.5 内存管理
14.5.1 及时释放资源
public class MapManager : IDisposable
{
private Map _map;
private bool _disposed;
public void Dispose()
{
if (_disposed) return;
// 取消事件订阅
_map.Info -= OnMapInfo;
_map.DataChanged -= OnDataChanged;
// 清除图层
foreach (var layer in _map.Layers.ToList())
{
_map.Layers.Remove(layer);
(layer as IDisposable)?.Dispose();
}
// 清除小部件
_map.Widgets.Clear();
_map.Dispose();
_disposed = true;
}
}
14.5.2 避免内存泄漏
// 使用弱引用避免循环引用
public class WeakEventHandler
{
private readonly WeakReference<Map> _mapRef;
public WeakEventHandler(Map map)
{
_mapRef = new WeakReference<Map>(map);
}
public void OnMapInfo(object sender, MapInfoEventArgs e)
{
if (_mapRef.TryGetTarget(out var map))
{
// 处理事件
}
}
}
14.6 最佳实践
14.6.1 图层组织
// 合理组织图层顺序
public void SetupLayers(Map map)
{
// 1. 底图(最下层)
map.Layers.Add(CreateBaseLayer());
// 2. 面图层
map.Layers.Add(CreatePolygonLayer());
// 3. 线图层
map.Layers.Add(CreateLineLayer());
// 4. 点图层
map.Layers.Add(CreatePointLayer());
// 5. 标注图层(最上层)
map.Layers.Add(CreateLabelLayer());
}
// 使用适当的缩放限制
layer.MinVisible = ZoomLevelResolutions.GetResolution(18);
layer.MaxVisible = ZoomLevelResolutions.GetResolution(5);
14.6.2 异步操作
// 数据加载应该是异步的
public async Task LoadDataAsync()
{
// 显示加载指示
ShowLoading();
try
{
// 在后台线程加载数据
var features = await Task.Run(() => LoadFeaturesFromDatabase());
// 在 UI 线程更新图层
await Dispatcher.InvokeAsync(() =>
{
_dataLayer.Features = features;
_dataLayer.DataHasChanged();
});
}
finally
{
HideLoading();
}
}
14.6.3 错误处理
// 全局错误处理
Mapsui.Logging.Logger.LogDelegate += (level, message, ex) =>
{
if (level == Mapsui.Logging.LogLevel.Error)
{
// 记录错误日志
_logger.LogError(ex, $"Mapsui error: {message}");
}
};
// 数据加载错误处理
map.DataChanged += (sender, args) =>
{
if (args.Error != null)
{
HandleDataError(args.Error);
}
};
14.7 常见问题诊断
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 地图卡顿 | 要素过多 | 简化几何、分级显示、栅格化 |
| 内存占用高 | 缓存过大 | 调整缓存大小、及时释放 |
| 加载慢 | 网络请求多 | 使用缓存、预加载 |
| 渲染模糊 | 分辨率不匹配 | 检查 DPI 设置 |
14.8 本章小结
本章详细介绍了 Mapsui 的性能优化:
- 性能瓶颈分析:数据获取、渲染、内存管理
- 数据层优化:视口裁剪、几何简化、分页加载
- 渲染优化:栅格化、样式重用、批量更新
- 瓦片缓存:内存缓存、文件缓存、预加载
- 内存管理:资源释放、避免泄漏
- 最佳实践:图层组织、异步操作、错误处理
在下一章中,我们将学习各平台集成实战。
14.9 思考与练习
- 实现一个自适应细节层次(LOD)系统。
- 创建一个性能分析工具,统计各图层的渲染时间。
- 实现一个智能缓存管理器,自动清理过期缓存。
- 优化一个包含 100000+ 要素的地图应用。
- 实现后台瓦片预加载功能。

浙公网安备 33010602011771号