第17章-实战案例与综合应用

第17章:实战案例与综合应用

17.1 案例一:地图浏览器

17.1.1 功能需求

  • 加载多种数据格式(Shapefile、GeoJSON)
  • 支持图层管理(显示/隐藏、排序)
  • 基本地图操作(缩放、平移、查询)
  • 要素信息查询
  • 地图导出

17.1.2 核心代码

public class MapBrowser
{
    private readonly Map _map;
    private readonly MapBox _mapBox;
    private readonly HighlightLayer _highlightLayer;
    
    public MapBrowser(MapBox mapBox)
    {
        _mapBox = mapBox;
        _map = new Map(mapBox.Size);
        _map.BackColor = Color.White;
        
        _highlightLayer = new HighlightLayer(_map);
        
        _mapBox.Map = _map;
        _mapBox.MouseClick += OnMapClick;
    }
    
    public void LoadShapefile(string filePath)
    {
        var layerName = Path.GetFileNameWithoutExtension(filePath);
        var layer = new VectorLayer(layerName);
        layer.DataSource = new ShapeFile(filePath, true, true);
        layer.Style = CreateRandomStyle();
        
        _map.Layers.Add(layer);
        _map.ZoomToExtents();
        _mapBox.Refresh();
    }
    
    public void LoadGeoJson(string filePath)
    {
        var layerName = Path.GetFileNameWithoutExtension(filePath);
        var layer = new VectorLayer(layerName);
        layer.DataSource = new Ogr(filePath);
        layer.Style = CreateRandomStyle();
        
        _map.Layers.Add(layer);
        _map.ZoomToExtents();
        _mapBox.Refresh();
    }
    
    public void QueryFeature(PointF screenPoint)
    {
        var worldPoint = _map.ImageToWorld(screenPoint);
        var tolerance = _map.PixelWidth * 5;
        var envelope = new Envelope(
            worldPoint.X - tolerance, worldPoint.X + tolerance,
            worldPoint.Y - tolerance, worldPoint.Y + tolerance);
        
        foreach (var layer in _map.Layers.OfType<VectorLayer>())
        {
            var ds = new FeatureDataSet();
            layer.DataSource.ExecuteIntersectionQuery(envelope, ds);
            
            if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
            {
                var feature = ds.Tables[0].Rows[0] as FeatureDataRow;
                _highlightLayer.Highlight(feature.Geometry);
                ShowFeatureInfo(feature);
                _mapBox.Refresh();
                return;
            }
        }
    }
    
    public void ExportMap(string filePath, ImageFormat format)
    {
        using (var image = _map.GetMap())
        {
            image.Save(filePath, format);
        }
    }
    
    private VectorStyle CreateRandomStyle()
    {
        var random = new Random();
        return new VectorStyle
        {
            Fill = new SolidBrush(Color.FromArgb(
                150,
                random.Next(256),
                random.Next(256),
                random.Next(256))),
            Outline = new Pen(Color.Black, 1),
            EnableOutline = true
        };
    }
}

17.2 案例二:专题地图制作

17.2.1 功能需求

  • 加载行政区划数据
  • 根据人口数据创建分级色彩地图
  • 添加标注
  • 添加图例和比例尺

17.2.2 核心代码

public class ThematicMap
{
    private readonly Map _map;
    
    public ThematicMap(int width, int height)
    {
        _map = new Map(new Size(width, height));
        _map.BackColor = Color.White;
    }
    
    public void CreatePopulationMap(string dataPath, string populationField)
    {
        // 1. 加载数据
        var layer = new VectorLayer("Population");
        var provider = new ShapeFile(dataPath, true);
        layer.DataSource = provider;
        
        // 2. 创建分级主题
        var theme = CreateGraduatedTheme(provider, populationField);
        layer.Theme = theme;
        
        _map.Layers.Add(layer);
        
        // 3. 添加标注
        var labelLayer = new LabelLayer("Labels");
        labelLayer.DataSource = provider;
        labelLayer.LabelColumn = "NAME";
        labelLayer.Style = new LabelStyle
        {
            Font = new Font("Arial", 8),
            ForeColor = Color.Black,
            Halo = new Pen(Color.White, 2),
            CollisionDetection = true
        };
        _map.Layers.Add(labelLayer);
        
        _map.ZoomToExtents();
    }
    
    private ITheme CreateGraduatedTheme(IProvider provider, string field)
    {
        // 计算统计信息
        var stats = CalculateStatistics(provider, field);
        
        // 使用等间距分类
        var breaks = CalculateEqualInterval(stats.Min, stats.Max, 5);
        
        // 创建分级主题
        var theme = new ClassBreaksTheme(field);
        var colors = new[]
        {
            Color.FromArgb(255, 255, 178),
            Color.FromArgb(254, 204, 92),
            Color.FromArgb(253, 141, 60),
            Color.FromArgb(240, 59, 32),
            Color.FromArgb(189, 0, 38)
        };
        
        for (int i = 0; i < breaks.Length - 1; i++)
        {
            theme.AddBreak(breaks[i], breaks[i + 1], new VectorStyle
            {
                Fill = new SolidBrush(colors[i]),
                Outline = new Pen(Color.Gray, 0.5f),
                EnableOutline = true
            });
        }
        
        return theme;
    }
    
    public Image RenderWithLegend()
    {
        var mapImage = _map.GetMap();
        
        // 创建带图例的图片
        var finalImage = new Bitmap(_map.Size.Width + 200, _map.Size.Height);
        using (var g = Graphics.FromImage(finalImage))
        {
            g.Clear(Color.White);
            
            // 绘制地图
            g.DrawImage(mapImage, 0, 0);
            
            // 绘制图例
            DrawLegend(g, _map.Size.Width + 10, 10);
            
            // 绘制比例尺
            DrawScaleBar(g, 10, _map.Size.Height - 40);
        }
        
        return finalImage;
    }
    
    private void DrawLegend(Graphics g, int x, int y)
    {
        g.DrawString("人口密度 (人/km²)", new Font("Arial", 10, FontStyle.Bold), 
            Brushes.Black, x, y);
        
        var colors = new[]
        {
            (Color.FromArgb(255, 255, 178), "< 100"),
            (Color.FromArgb(254, 204, 92), "100 - 500"),
            (Color.FromArgb(253, 141, 60), "500 - 1000"),
            (Color.FromArgb(240, 59, 32), "1000 - 2000"),
            (Color.FromArgb(189, 0, 38), "> 2000")
        };
        
        int itemY = y + 25;
        foreach (var (color, label) in colors)
        {
            g.FillRectangle(new SolidBrush(color), x, itemY, 20, 15);
            g.DrawRectangle(Pens.Black, x, itemY, 20, 15);
            g.DrawString(label, new Font("Arial", 9), Brushes.Black, x + 25, itemY);
            itemY += 20;
        }
    }
}

17.3 案例三:实时定位系统

17.3.1 功能需求

  • 显示底图
  • 实时显示车辆/人员位置
  • 历史轨迹回放
  • 电子围栏告警

17.3.2 核心代码

public class RealtimeTrackingSystem
{
    private readonly Map _map;
    private readonly MapBox _mapBox;
    private readonly RealtimeGpsProvider _gpsProvider;
    private readonly TrackLayer _trackLayer;
    private readonly GeofenceManager _geofenceManager;
    private readonly Timer _updateTimer;
    
    public event Action<string, GpsPoint> GeofenceAlert;
    
    public RealtimeTrackingSystem(MapBox mapBox)
    {
        _mapBox = mapBox;
        _map = new Map(mapBox.Size);
        
        InitializeBaseLayers();
        InitializeTrackingLayers();
        
        _geofenceManager = new GeofenceManager();
        
        _updateTimer = new Timer(1000);  // 1秒更新
        _updateTimer.Elapsed += OnUpdateTimer;
        
        _mapBox.Map = _map;
    }
    
    private void InitializeBaseLayers()
    {
        // 添加瓦片底图
        var osmSource = KnownTileSources.Create(KnownTileSource.OpenStreetMap);
        var tileLayer = new TileLayer(osmSource, "OpenStreetMap");
        _map.BackgroundLayer.Add(tileLayer);
    }
    
    private void InitializeTrackingLayers()
    {
        // GPS 点图层(动态)
        _gpsProvider = new RealtimeGpsProvider(() => GetCurrentPositions());
        var gpsLayer = new VectorLayer("GPS Points", _gpsProvider);
        gpsLayer.Style = new VectorStyle
        {
            Symbol = Image.FromFile("car_icon.png"),
            SymbolScale = 0.5f
        };
        _map.VariableLayers.Add(gpsLayer);
        
        // 轨迹图层
        _trackLayer = new TrackLayer("Tracks");
        _map.Layers.Add(_trackLayer);
        
        // 电子围栏图层
        var geofenceLayer = new VectorLayer("Geofences");
        geofenceLayer.Style = new VectorStyle
        {
            Fill = new SolidBrush(Color.FromArgb(50, 255, 0, 0)),
            Outline = new Pen(Color.Red, 2),
            EnableOutline = true
        };
        _map.Layers.Add(geofenceLayer);
    }
    
    public void AddGeofence(string name, Polygon boundary)
    {
        _geofenceManager.AddGeofence(name, boundary);
    }
    
    private void OnUpdateTimer(object sender, ElapsedEventArgs e)
    {
        // 更新位置
        var positions = GetCurrentPositions();
        
        foreach (var pos in positions)
        {
            // 添加到轨迹
            _trackLayer.AddPoint(new TrackPoint
            {
                VehicleId = pos.VehicleId,
                Longitude = pos.Longitude,
                Latitude = pos.Latitude,
                Timestamp = pos.Timestamp
            });
            
            // 检查电子围栏
            var point = new GeometryFactory().CreatePoint(
                new Coordinate(pos.Longitude, pos.Latitude));
            
            var violations = _geofenceManager.CheckViolations(point);
            foreach (var violation in violations)
            {
                GeofenceAlert?.Invoke(violation, pos);
            }
        }
        
        // 刷新地图
        _mapBox.BeginInvoke((Action)(() => _mapBox.Refresh()));
    }
    
    public void StartTracking()
    {
        _updateTimer.Start();
    }
    
    public void StopTracking()
    {
        _updateTimer.Stop();
    }
    
    public void PlaybackTrack(string vehicleId, DateTime start, DateTime end)
    {
        var track = GetHistoricalTrack(vehicleId, start, end);
        _trackLayer.SetTrack(track);
        _mapBox.Refresh();
    }
}

17.4 案例四:Web 地图服务

17.4.1 功能需求

  • 提供 WMS/TMS 服务
  • 支持多图层
  • 要素查询 API
  • 缓存机制

17.4.2 完整实现

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSingleton<IMapService, MapService>();
    services.AddMemoryCache();
    services.AddResponseCaching();
}

// MapService.cs
public interface IMapService
{
    Map CreateMap(int width, int height, string[] layers = null);
    IEnumerable<LayerInfo> GetLayerInfos();
    FeatureCollection QueryFeatures(string layer, Envelope envelope);
}

public class MapService : IMapService
{
    private readonly string _dataPath;
    private readonly Dictionary<string, VectorStyle> _layerStyles;
    
    public MapService(IConfiguration config)
    {
        _dataPath = config["MapData:Path"];
        _layerStyles = new Dictionary<string, VectorStyle>();
        LoadLayerConfigurations();
    }
    
    public Map CreateMap(int width, int height, string[] layers = null)
    {
        var map = new Map(new Size(width, height));
        map.BackColor = Color.White;
        
        var shapeFiles = Directory.GetFiles(_dataPath, "*.shp");
        
        foreach (var shapeFile in shapeFiles)
        {
            var layerName = Path.GetFileNameWithoutExtension(shapeFile);
            
            if (layers != null && !layers.Contains(layerName))
                continue;
            
            var layer = new VectorLayer(layerName);
            layer.DataSource = new ShapeFile(shapeFile, true, true);
            layer.Style = _layerStyles.GetValueOrDefault(layerName) ?? GetDefaultStyle();
            
            map.Layers.Add(layer);
        }
        
        return map;
    }
    
    public FeatureCollection QueryFeatures(string layerName, Envelope envelope)
    {
        var shapeFile = Path.Combine(_dataPath, $"{layerName}.shp");
        
        if (!File.Exists(shapeFile))
            return new FeatureCollection();
        
        var provider = new ShapeFile(shapeFile, true);
        var ds = new FeatureDataSet();
        provider.ExecuteIntersectionQuery(envelope, ds);
        
        return ConvertToGeoJson(ds);
    }
}

// TmsController.cs
[ApiController]
[Route("tms/1.0.0/{layer}")]
public class TmsController : ControllerBase
{
    private readonly IMapService _mapService;
    private readonly IMemoryCache _cache;
    
    [HttpGet("{z:int}/{x:int}/{y:int}.png")]
    [ResponseCache(Duration = 3600)]
    public async Task<IActionResult> GetTile(string layer, int z, int x, int y)
    {
        var cacheKey = $"tile_{layer}_{z}_{x}_{y}";
        
        if (!_cache.TryGetValue(cacheKey, out byte[] tileBytes))
        {
            var envelope = TileToEnvelope(x, y, z);
            var map = _mapService.CreateMap(256, 256, new[] { layer });
            map.ZoomToBox(envelope);
            
            using (var image = map.GetMap())
            using (var stream = new MemoryStream())
            {
                image.Save(stream, ImageFormat.Png);
                tileBytes = stream.ToArray();
            }
            
            _cache.Set(cacheKey, tileBytes, TimeSpan.FromHours(1));
        }
        
        return File(tileBytes, "image/png");
    }
}

17.5 案例五:空间分析工具

17.5.1 功能需求

  • 缓冲区分析
  • 叠加分析
  • 最近邻分析
  • 结果可视化

17.5.2 核心代码

public class SpatialAnalysisTool
{
    private readonly Map _map;
    private readonly VectorLayer _resultLayer;
    
    public SpatialAnalysisTool(Map map)
    {
        _map = map;
        _resultLayer = new VectorLayer("Analysis Result");
        _resultLayer.Style = new VectorStyle
        {
            Fill = new SolidBrush(Color.FromArgb(100, 255, 0, 0)),
            Outline = new Pen(Color.Red, 2),
            EnableOutline = true
        };
        _map.Layers.Add(_resultLayer);
    }
    
    public Geometry BufferAnalysis(string layerName, double distance)
    {
        var layer = _map.Layers.FirstOrDefault(l => l.LayerName == layerName) as VectorLayer;
        if (layer == null)
            return null;
        
        var geometries = layer.DataSource.GetGeometriesInView(layer.Envelope);
        var buffers = geometries.Select(g => g.Buffer(distance)).ToList();
        var union = CascadedPolygonUnion.Union(buffers);
        
        _resultLayer.DataSource = new GeometryProvider(new[] { union });
        
        return union;
    }
    
    public Geometry IntersectionAnalysis(string layer1Name, string layer2Name)
    {
        var layer1 = GetLayer(layer1Name);
        var layer2 = GetLayer(layer2Name);
        
        var geom1 = UnionAllGeometries(layer1);
        var geom2 = UnionAllGeometries(layer2);
        
        var intersection = geom1.Intersection(geom2);
        
        _resultLayer.DataSource = new GeometryProvider(new[] { intersection });
        
        return intersection;
    }
    
    public IEnumerable<(FeatureDataRow source, FeatureDataRow nearest, double distance)> 
        NearestNeighborAnalysis(string sourceLayer, string targetLayer)
    {
        var sourceFeatures = GetFeatures(sourceLayer);
        var targetFeatures = GetFeatures(targetLayer);
        
        var index = new STRtree<FeatureDataRow>();
        foreach (var target in targetFeatures)
        {
            index.Insert(target.Geometry.EnvelopeInternal, target);
        }
        index.Build();
        
        var results = new List<(FeatureDataRow, FeatureDataRow, double)>();
        
        foreach (var source in sourceFeatures)
        {
            FeatureDataRow nearest = null;
            double minDistance = double.MaxValue;
            
            var envelope = source.Geometry.EnvelopeInternal.Copy();
            envelope.ExpandBy(1);  // 初始搜索范围
            
            var candidates = index.Query(envelope);
            foreach (var candidate in candidates)
            {
                var dist = source.Geometry.Distance(candidate.Geometry);
                if (dist < minDistance)
                {
                    minDistance = dist;
                    nearest = candidate;
                }
            }
            
            results.Add((source, nearest, minDistance));
        }
        
        return results;
    }
}

17.6 本章小结

本章通过五个实战案例展示了 SharpMap 的综合应用:

  1. 地图浏览器:基本的地图浏览和查询功能
  2. 专题地图:分级色彩地图制作
  3. 实时定位:车辆/人员实时追踪系统
  4. Web 服务:WMS/TMS 地图服务
  5. 空间分析:缓冲区、叠加、最近邻分析

这些案例涵盖了桌面应用和 Web 服务的主要场景,可以作为实际项目开发的参考。

17.7 参考资源


教程总结:本教程系统地介绍了 SharpMap 的各个方面,从基础概念到高级应用,希望能够帮助读者快速掌握 SharpMap 的使用方法,在实际项目中灵活运用。

posted @ 2026-01-08 14:09  我才是银古  阅读(2)  评论(0)    收藏  举报