第11章-空间查询与分析

第11章:空间查询与分析

11.1 空间查询基础

11.1.1 范围查询

// 创建查询范围
var queryEnvelope = new Envelope(116.0, 117.0, 39.0, 40.0);

// 执行范围查询
var featureDataSet = new FeatureDataSet();
vectorLayer.DataSource.ExecuteIntersectionQuery(queryEnvelope, featureDataSet);

// 获取结果
if (featureDataSet.Tables.Count > 0)
{
    var table = featureDataSet.Tables[0];
    Console.WriteLine($"查询到 {table.Rows.Count} 个要素");
    
    foreach (FeatureDataRow row in table.Rows)
    {
        Console.WriteLine($"ID: {row["ID"]}, Name: {row["NAME"]}");
        Console.WriteLine($"几何类型: {row.Geometry.GeometryType}");
    }
}

11.1.2 几何查询

// 创建查询几何
var factory = new GeometryFactory();
var queryPoint = factory.CreatePoint(new Coordinate(116.4, 39.9));
var queryBuffer = queryPoint.Buffer(0.1);  // 0.1度缓冲区

// 执行几何查询
var featureDataSet = new FeatureDataSet();
vectorLayer.DataSource.ExecuteIntersectionQuery(queryBuffer, featureDataSet);

11.1.3 点击查询

public class PointQueryHelper
{
    private readonly Map _map;
    
    public PointQueryHelper(Map map)
    {
        _map = map;
    }
    
    public IEnumerable<FeatureDataRow> QueryAtPoint(PointF screenPoint, double tolerance = 5)
    {
        // 转换为地理坐标
        var worldPoint = _map.ImageToWorld(screenPoint);
        
        // 创建查询范围(考虑容差)
        double worldTolerance = tolerance * _map.PixelWidth;
        var queryEnvelope = new Envelope(
            worldPoint.X - worldTolerance,
            worldPoint.X + worldTolerance,
            worldPoint.Y - worldTolerance,
            worldPoint.Y + worldTolerance);
        
        var results = new List<FeatureDataRow>();
        
        // 查询所有矢量图层
        foreach (var layer in _map.Layers.OfType<VectorLayer>().Where(l => l.Enabled))
        {
            var ds = new FeatureDataSet();
            layer.DataSource.ExecuteIntersectionQuery(queryEnvelope, ds);
            
            if (ds.Tables.Count > 0)
            {
                results.AddRange(ds.Tables[0].Rows.Cast<FeatureDataRow>());
            }
        }
        
        return results;
    }
}

11.2 属性查询

11.2.1 使用 LINQ 查询

// 获取所有要素
var allFeatures = GetAllFeatures(vectorLayer);

// LINQ 过滤
var filtered = allFeatures
    .Where(f => Convert.ToDouble(f["POPULATION"]) > 1000000)
    .OrderByDescending(f => Convert.ToDouble(f["POPULATION"]))
    .Take(10);

foreach (var feature in filtered)
{
    Console.WriteLine($"{feature["NAME"]}: {feature["POPULATION"]}");
}

// 辅助方法
private IEnumerable<FeatureDataRow> GetAllFeatures(VectorLayer layer)
{
    var ds = new FeatureDataSet();
    layer.DataSource.ExecuteIntersectionQuery(layer.Envelope, ds);
    
    if (ds.Tables.Count > 0)
    {
        return ds.Tables[0].Rows.Cast<FeatureDataRow>();
    }
    return Enumerable.Empty<FeatureDataRow>();
}

11.2.2 SQL 风格查询

public class FeatureQuery
{
    public static IEnumerable<FeatureDataRow> Where(IEnumerable<FeatureDataRow> features, 
        Func<FeatureDataRow, bool> predicate)
    {
        return features.Where(predicate);
    }
    
    public static IEnumerable<FeatureDataRow> Select(IEnumerable<FeatureDataRow> features,
        string[] columns)
    {
        // 投影查询(只返回指定字段)
        foreach (var feature in features)
        {
            var newRow = feature.Table.NewRow() as FeatureDataRow;
            newRow.Geometry = feature.Geometry;
            
            foreach (var col in columns)
            {
                if (feature.Table.Columns.Contains(col))
                {
                    newRow[col] = feature[col];
                }
            }
            
            yield return newRow;
        }
    }
}

// 使用
var results = FeatureQuery.Where(allFeatures, f => 
    f["TYPE"]?.ToString() == "City" && 
    Convert.ToDouble(f["POPULATION"] ?? 0) > 500000);

11.3 空间关系判断

11.3.1 基本空间关系

using NetTopologySuite.Geometries;

var factory = new GeometryFactory();

var polygon = factory.CreatePolygon(new Coordinate[]
{
    new Coordinate(0, 0),
    new Coordinate(10, 0),
    new Coordinate(10, 10),
    new Coordinate(0, 10),
    new Coordinate(0, 0)
});

var point = factory.CreatePoint(new Coordinate(5, 5));
var outsidePoint = factory.CreatePoint(new Coordinate(15, 15));

// 空间关系判断
Console.WriteLine($"包含: {polygon.Contains(point)}");         // true
Console.WriteLine($"在内部: {point.Within(polygon)}");         // true
Console.WriteLine($"相交: {polygon.Intersects(point)}");       // true
Console.WriteLine($"分离: {polygon.Disjoint(outsidePoint)}");  // true
Console.WriteLine($"相邻: {polygon.Touches(point)}");          // false

11.3.2 空间关系查询

public class SpatialRelationQuery
{
    public static IEnumerable<FeatureDataRow> Contains(
        IEnumerable<FeatureDataRow> features, Geometry queryGeometry)
    {
        return features.Where(f => f.Geometry.Contains(queryGeometry));
    }
    
    public static IEnumerable<FeatureDataRow> Within(
        IEnumerable<FeatureDataRow> features, Geometry queryGeometry)
    {
        return features.Where(f => f.Geometry.Within(queryGeometry));
    }
    
    public static IEnumerable<FeatureDataRow> Intersects(
        IEnumerable<FeatureDataRow> features, Geometry queryGeometry)
    {
        return features.Where(f => f.Geometry.Intersects(queryGeometry));
    }
    
    public static IEnumerable<FeatureDataRow> WithinDistance(
        IEnumerable<FeatureDataRow> features, Geometry queryGeometry, double distance)
    {
        return features.Where(f => f.Geometry.Distance(queryGeometry) <= distance);
    }
}

11.4 空间分析

11.4.1 缓冲区分析

// 单一缓冲区
var point = factory.CreatePoint(new Coordinate(116.4, 39.9));
var buffer = point.Buffer(0.1);  // 0.1 度缓冲区

// 不同端点样式
var lineBuffer1 = line.Buffer(10, EndCapStyle.Round);    // 圆形端点
var lineBuffer2 = line.Buffer(10, EndCapStyle.Flat);     // 平头端点
var lineBuffer3 = line.Buffer(10, EndCapStyle.Square);   // 方形端点

// 多环缓冲区
public static List<Geometry> CreateMultiRingBuffer(Geometry geometry, double[] distances)
{
    var buffers = new List<Geometry>();
    Geometry previousBuffer = geometry;
    
    foreach (var distance in distances.OrderBy(d => d))
    {
        var buffer = geometry.Buffer(distance);
        var ring = buffer.Difference(previousBuffer);
        buffers.Add(ring);
        previousBuffer = buffer;
    }
    
    return buffers;
}

// 使用
var rings = CreateMultiRingBuffer(centerPoint, new[] { 1.0, 2.0, 3.0, 5.0 });

11.4.2 叠加分析

// 交集
var intersection = polygon1.Intersection(polygon2);

// 并集
var union = polygon1.Union(polygon2);

// 差集
var difference = polygon1.Difference(polygon2);

// 对称差
var symDifference = polygon1.SymmetricDifference(polygon2);

// 批量合并
public static Geometry UnionAll(IEnumerable<Geometry> geometries)
{
    return geometries.Aggregate((current, next) => current.Union(next));
}

// 使用 CascadedPolygonUnion 提高性能
using NetTopologySuite.Operation.Union;
var geometryList = new List<Geometry> { polygon1, polygon2, polygon3 };
var unionResult = CascadedPolygonUnion.Union(geometryList);

11.4.3 距离计算

// 两点距离
var distance = point1.Distance(point2);

// 最近点
var nearestPoints = NetTopologySuite.Operation.Distance.DistanceOp
    .NearestPoints(geometry1, geometry2);

// 查找最近要素
public static FeatureDataRow FindNearest(IEnumerable<FeatureDataRow> features, 
    Geometry queryGeometry)
{
    return features
        .OrderBy(f => f.Geometry.Distance(queryGeometry))
        .FirstOrDefault();
}

// 查找距离范围内的要素
public static IEnumerable<FeatureDataRow> FindWithinDistance(
    IEnumerable<FeatureDataRow> features, Geometry queryGeometry, double maxDistance)
{
    return features
        .Where(f => f.Geometry.Distance(queryGeometry) <= maxDistance)
        .OrderBy(f => f.Geometry.Distance(queryGeometry));
}

11.4.4 面积和长度计算

// 面积(坐标系单位)
double area = polygon.Area;

// 长度(坐标系单位)
double length = lineString.Length;

// 周长
double perimeter = polygon.Length;

// 计算地理坐标系下的实际距离(使用 Haversine 公式)
public static double CalculateDistance(Coordinate point1, Coordinate point2)
{
    const double R = 6371000;  // 地球半径(米)
    
    double lat1 = point1.Y * Math.PI / 180;
    double lat2 = point2.Y * Math.PI / 180;
    double deltaLat = (point2.Y - point1.Y) * Math.PI / 180;
    double deltaLon = (point2.X - point1.X) * Math.PI / 180;
    
    double a = Math.Sin(deltaLat / 2) * Math.Sin(deltaLat / 2) +
               Math.Cos(lat1) * Math.Cos(lat2) *
               Math.Sin(deltaLon / 2) * Math.Sin(deltaLon / 2);
    
    double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
    
    return R * c;  // 米
}

// 计算地理坐标系下的面积
public static double CalculateGeographicArea(Polygon polygon)
{
    // 使用投影转换到平面坐标系计算
    // 或使用球面几何公式
    throw new NotImplementedException("需要更复杂的球面计算");
}

11.5 空间索引

11.5.1 使用 STRtree

using NetTopologySuite.Index.Strtree;

// 创建空间索引
var index = new STRtree<FeatureDataRow>();

// 添加要素到索引
foreach (FeatureDataRow feature in featureTable.Rows)
{
    index.Insert(feature.Geometry.EnvelopeInternal, feature);
}

// 构建索引
index.Build();

// 查询
var queryEnvelope = new Envelope(116.0, 117.0, 39.0, 40.0);
var results = index.Query(queryEnvelope);

// 精确过滤
var preciseResults = results
    .Where(f => f.Geometry.Intersects(queryGeometry))
    .ToList();

11.5.2 使用 Quadtree

using NetTopologySuite.Index.Quadtree;

var quadtree = new Quadtree<FeatureDataRow>();

// 添加要素
foreach (FeatureDataRow feature in featureTable.Rows)
{
    quadtree.Insert(feature.Geometry.EnvelopeInternal, feature);
}

// 查询
var results = quadtree.Query(queryEnvelope);

11.6 空间分析示例

11.6.1 点在面内查询

public class PointInPolygonQuery
{
    private readonly STRtree<FeatureDataRow> _polygonIndex;
    
    public PointInPolygonQuery(IEnumerable<FeatureDataRow> polygons)
    {
        _polygonIndex = new STRtree<FeatureDataRow>();
        
        foreach (var polygon in polygons)
        {
            _polygonIndex.Insert(polygon.Geometry.EnvelopeInternal, polygon);
        }
        
        _polygonIndex.Build();
    }
    
    public FeatureDataRow FindContainingPolygon(Point point)
    {
        var candidates = _polygonIndex.Query(point.EnvelopeInternal);
        
        return candidates.FirstOrDefault(p => p.Geometry.Contains(point));
    }
    
    public Dictionary<string, int> CountPointsInPolygons(IEnumerable<Point> points, 
        string polygonIdField)
    {
        var counts = new Dictionary<string, int>();
        
        foreach (var point in points)
        {
            var polygon = FindContainingPolygon(point);
            if (polygon != null)
            {
                string id = polygon[polygonIdField]?.ToString() ?? "";
                if (!counts.ContainsKey(id))
                    counts[id] = 0;
                counts[id]++;
            }
        }
        
        return counts;
    }
}

11.6.2 最近邻分析

public class NearestNeighborAnalysis
{
    public static Dictionary<FeatureDataRow, FeatureDataRow> FindNearestNeighbors(
        IEnumerable<FeatureDataRow> sourceFeatures,
        IEnumerable<FeatureDataRow> targetFeatures)
    {
        var results = new Dictionary<FeatureDataRow, FeatureDataRow>();
        var targetIndex = new STRtree<FeatureDataRow>();
        
        foreach (var target in targetFeatures)
        {
            targetIndex.Insert(target.Geometry.EnvelopeInternal, target);
        }
        targetIndex.Build();
        
        foreach (var source in sourceFeatures)
        {
            var envelope = source.Geometry.EnvelopeInternal;
            envelope.ExpandBy(1);  // 初始搜索范围
            
            FeatureDataRow nearest = null;
            double minDistance = double.MaxValue;
            
            while (nearest == null)
            {
                var candidates = targetIndex.Query(envelope);
                
                foreach (var candidate in candidates)
                {
                    var dist = source.Geometry.Distance(candidate.Geometry);
                    if (dist < minDistance)
                    {
                        minDistance = dist;
                        nearest = candidate;
                    }
                }
                
                if (nearest == null)
                {
                    envelope.ExpandBy(envelope.Width);  // 扩大搜索范围
                }
            }
            
            results[source] = nearest;
        }
        
        return results;
    }
}

11.7 本章小结

本章介绍了 SharpMap 的空间查询与分析功能:

  1. 空间查询:范围查询、几何查询、点击查询
  2. 属性查询:LINQ 查询、SQL 风格查询
  3. 空间关系:Contains、Within、Intersects 等判断
  4. 空间分析:缓冲区、叠加分析、距离计算
  5. 空间索引:STRtree、Quadtree 的使用

下一章预告:第12章将介绍 WinForms 桌面应用开发。

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