03-核心几何类型详解

第三章:核心几何类型详解

3.1 几何类型概述

geometry-api-net 提供了一套完整的几何类型系统,遵循 OGC(开放地理空间联盟)的简单要素规范。所有几何类型都继承自抽象基类 Geometry,形成了清晰的类型层次结构。

3.1.1 几何类型枚举

public enum GeometryType
{
    Unknown = 0,
    Point = 1,
    Line = 2,
    Envelope = 3,
    MultiPoint = 4,
    Polyline = 5,
    Polygon = 6
}

3.1.2 类型层次结构

Geometry (抽象基类)
│
├── 零维几何(Dimension = 0)
│   ├── Point      - 单个点
│   └── MultiPoint - 点的集合
│
├── 一维几何(Dimension = 1)
│   ├── Line       - 由两点定义的线段
│   └── Polyline   - 由一条或多条路径组成的折线
│
└── 二维几何(Dimension = 2)
    ├── Polygon    - 由一个或多个环组成的多边形
    └── Envelope   - 轴对齐的边界矩形

3.1.3 Geometry 抽象基类

所有几何类型共享的基础功能:

public abstract class Geometry
{
    // 核心属性
    public abstract GeometryType Type { get; }
    public abstract bool IsEmpty { get; }
    public abstract int Dimension { get; }
    
    // 类型判断
    public bool IsPoint => Type == GeometryType.Point || Type == GeometryType.MultiPoint;
    public bool IsLinear => Type == GeometryType.Line || Type == GeometryType.Polyline;
    public bool IsArea => Type == GeometryType.Polygon || Type == GeometryType.Envelope;
    
    // 核心方法
    public abstract Envelope GetEnvelope();
    public virtual double CalculateArea2D();
    public virtual double CalculateLength2D();
    public virtual Geometry Copy();
    public virtual bool IsValid();
}

3.2 Point(点)

3.2.1 概念与特性

Point 是最基本的几何类型,表示空间中的一个位置。它可以是二维的(X, Y),也可以携带高程(Z)和测量值(M)。

特性

  • 维度:0
  • 无面积、无长度
  • 可包含可选的 Z 坐标和 M 值
  • 空点的 X 和 Y 为 NaN

3.2.2 类定义

public class Point : Geometry
{
    // 构造函数
    public Point();                           // 创建空点
    public Point(double x, double y);         // 创建二维点
    public Point(double x, double y, double z); // 创建三维点
    
    // 坐标属性
    public double X { get; set; }
    public double Y { get; set; }
    public double? Z { get; set; }  // 可选高程
    public double? M { get; set; }  // 可选测量值
    
    // Geometry 实现
    public override GeometryType Type => GeometryType.Point;
    public override bool IsEmpty => double.IsNaN(X) || double.IsNaN(Y);
    public override int Dimension => 0;
    
    // 方法
    public double Distance(Point other);
    public bool Equals(Point other, double tolerance = GeometryConstants.DefaultTolerance);
    public override Envelope GetEnvelope();
}

3.2.3 使用示例

using Esri.Geometry.Core.Geometries;

// 创建二维点
var point2D = new Point(116.4074, 39.9042);
Console.WriteLine($"二维点:({point2D.X}, {point2D.Y})");

// 创建三维点
var point3D = new Point(116.4074, 39.9042, 50.5);
Console.WriteLine($"三维点:({point3D.X}, {point3D.Y}, {point3D.Z})");

// 设置测量值
point3D.M = 100.0;
Console.WriteLine($"测量值 M:{point3D.M}");

// 计算两点间距离
var pointA = new Point(0, 0);
var pointB = new Point(3, 4);
double distance = pointA.Distance(pointB);
Console.WriteLine($"距离:{distance}");  // 输出:5

// 判断点是否为空
var emptyPoint = new Point();
Console.WriteLine($"是否为空:{emptyPoint.IsEmpty}");  // true

// 获取点的包络
var envelope = point2D.GetEnvelope();
// 单点的包络是一个点大小的矩形
Console.WriteLine($"包络:({envelope.XMin}, {envelope.YMin}) - ({envelope.XMax}, {envelope.YMax})");

// 点的相等性比较(带容差)
var p1 = new Point(1.0, 2.0);
var p2 = new Point(1.0000001, 2.0000001);
bool areEqual = p1.Equals(p2, tolerance: 0.0001);
Console.WriteLine($"是否相等:{areEqual}");  // true

3.2.4 实现细节

Point 类的核心实现非常轻量:

// 空点判断
public override bool IsEmpty => double.IsNaN(X) || double.IsNaN(Y);

// 距离计算使用欧几里得公式
public double Distance(Point other)
{
    if (other == null) throw new ArgumentNullException(nameof(other));
    
    var dx = X - other.X;
    var dy = Y - other.Y;
    return Math.Sqrt(dx * dx + dy * dy);
}

// 相等性比较(考虑容差)
public bool Equals(Point other, double tolerance = GeometryConstants.DefaultTolerance)
{
    if (other == null) return false;
    
    return Math.Abs(X - other.X) <= tolerance && 
           Math.Abs(Y - other.Y) <= tolerance;
}

3.3 MultiPoint(多点)

3.3.1 概念与特性

MultiPoint 是点的集合,用于表示多个离散的位置。

特性

  • 维度:0
  • 可包含任意数量的点
  • 空 MultiPoint 的 Count 为 0

3.3.2 类定义

public class MultiPoint : Geometry
{
    // 构造函数
    public MultiPoint();
    public MultiPoint(IEnumerable<Point> points);
    
    // 属性
    public int Count { get; }
    public override GeometryType Type => GeometryType.MultiPoint;
    public override bool IsEmpty => _points.Count == 0;
    public override int Dimension => 0;
    
    // 方法
    public Point GetPoint(int index);
    public void Add(Point point);
    public IEnumerable<Point> GetPoints();
    public override Envelope GetEnvelope();
}

3.3.3 使用示例

using Esri.Geometry.Core.Geometries;

// 创建空的 MultiPoint
var multiPoint = new MultiPoint();

// 添加点
multiPoint.Add(new Point(10, 20));
multiPoint.Add(new Point(30, 40));
multiPoint.Add(new Point(50, 60));

Console.WriteLine($"点数量:{multiPoint.Count}");  // 3

// 从集合创建
var points = new List<Point>
{
    new Point(0, 0),
    new Point(10, 10),
    new Point(20, 20)
};
var multiPoint2 = new MultiPoint(points);

// 获取单个点
var firstPoint = multiPoint.GetPoint(0);
Console.WriteLine($"第一个点:({firstPoint.X}, {firstPoint.Y})");

// 遍历所有点
foreach (var point in multiPoint.GetPoints())
{
    Console.WriteLine($"点:({point.X}, {point.Y})");
}

// 获取包络矩形
var envelope = multiPoint.GetEnvelope();
Console.WriteLine($"包络:({envelope.XMin}, {envelope.YMin}) - ({envelope.XMax}, {envelope.YMax})");
// 输出:包络:(10, 20) - (50, 60)

3.3.4 应用场景

// 场景:表示多个 POI 点
var restaurants = new MultiPoint();
restaurants.Add(new Point(116.40, 39.90));  // 餐厅1
restaurants.Add(new Point(116.41, 39.91));  // 餐厅2
restaurants.Add(new Point(116.42, 39.89));  // 餐厅3

// 获取所有餐厅的边界范围
var bounds = restaurants.GetEnvelope();
Console.WriteLine($"餐厅分布范围:{bounds.Width}° x {bounds.Height}°");

3.4 Line(线段)

3.4.1 概念与特性

Line 表示由两个点定义的线段,是最简单的线性几何类型。

特性

  • 维度:1
  • 由起点和终点组成
  • 可计算长度

3.4.2 类定义

public class Line : Geometry
{
    // 构造函数
    public Line();
    public Line(Point start, Point end);
    
    // 属性
    public Point Start { get; set; }
    public Point End { get; set; }
    public double Length { get; }
    
    public override GeometryType Type => GeometryType.Line;
    public override bool IsEmpty { get; }
    public override int Dimension => 1;
    
    // 方法
    public override Envelope GetEnvelope();
}

3.4.3 使用示例

using Esri.Geometry.Core.Geometries;

// 创建线段
var start = new Point(0, 0);
var end = new Point(10, 10);
var line = new Line(start, end);

// 获取属性
Console.WriteLine($"起点:({line.Start.X}, {line.Start.Y})");
Console.WriteLine($"终点:({line.End.X}, {line.End.Y})");
Console.WriteLine($"长度:{line.Length:F4}");  // 约 14.1421

// 获取包络
var envelope = line.GetEnvelope();
Console.WriteLine($"包络:({envelope.XMin}, {envelope.YMin}) - ({envelope.XMax}, {envelope.YMax})");
// 输出:包络:(0, 0) - (10, 10)

3.5 Polyline(折线)

3.5.1 概念与特性

Polyline 是由一条或多条路径(Path)组成的线性几何,每条路径由一系列有序的点组成。

特性

  • 维度:1
  • 可包含多条不连续的路径
  • 每条路径至少需要 2 个点
  • 可计算总长度

3.5.2 类定义

public class Polyline : Geometry
{
    // 构造函数
    public Polyline();
    
    // 属性
    public int PathCount { get; }
    public double Length { get; }
    
    public override GeometryType Type => GeometryType.Polyline;
    public override bool IsEmpty { get; }
    public override int Dimension => 1;
    
    // 方法
    public void AddPath(IEnumerable<Point> points);
    public IReadOnlyList<Point> GetPath(int index);
    public IEnumerable<IReadOnlyList<Point>> GetPaths();
    public override Envelope GetEnvelope();
}

3.5.3 使用示例

using Esri.Geometry.Core.Geometries;

// 创建折线
var polyline = new Polyline();

// 添加第一条路径
polyline.AddPath(new List<Point>
{
    new Point(0, 0),
    new Point(10, 0),
    new Point(10, 10)
});

// 添加第二条路径(不连续)
polyline.AddPath(new List<Point>
{
    new Point(20, 0),
    new Point(30, 0),
    new Point(30, 10)
});

Console.WriteLine($"路径数量:{polyline.PathCount}");  // 2
Console.WriteLine($"总长度:{polyline.Length}");  // 40

// 获取特定路径
var path1 = polyline.GetPath(0);
Console.WriteLine($"第一条路径点数:{path1.Count}");  // 3

// 遍历所有路径
int pathIndex = 0;
foreach (var path in polyline.GetPaths())
{
    Console.WriteLine($"路径 {pathIndex++}:{path.Count} 个点");
}

// 获取包络
var envelope = polyline.GetEnvelope();
Console.WriteLine($"包络:({envelope.XMin}, {envelope.YMin}) - ({envelope.XMax}, {envelope.YMax})");

3.5.4 应用场景

// 场景1:表示道路
var road = new Polyline();
road.AddPath(new List<Point>
{
    new Point(116.30, 39.90),
    new Point(116.35, 39.92),
    new Point(116.40, 39.91),
    new Point(116.45, 39.93)
});

Console.WriteLine($"道路长度:{road.Length}°");

// 场景2:表示河流(多条支流)
var river = new Polyline();

// 主河道
river.AddPath(new List<Point>
{
    new Point(0, 0),
    new Point(50, 50),
    new Point(100, 50)
});

// 支流1
river.AddPath(new List<Point>
{
    new Point(50, 50),
    new Point(60, 80),
    new Point(80, 100)
});

// 支流2
river.AddPath(new List<Point>
{
    new Point(50, 50),
    new Point(40, 80),
    new Point(30, 100)
});

Console.WriteLine($"河流系统:{river.PathCount} 条水道");

3.6 Polygon(多边形)

3.6.1 概念与特性

Polygon 是由一个或多个环(Ring)组成的面状几何。第一个环是外环(外边界),后续的环是内环(孔洞)。

特性

  • 维度:2
  • 环必须闭合(首尾点相同)
  • 第一个环为外环,后续环为孔洞
  • 可计算面积

3.6.2 类定义

public class Polygon : Geometry
{
    // 构造函数
    public Polygon();
    
    // 属性
    public int RingCount { get; }
    public double Area { get; }
    
    public override GeometryType Type => GeometryType.Polygon;
    public override bool IsEmpty { get; }
    public override int Dimension => 2;
    
    // 方法
    public void AddRing(IEnumerable<Point> points);
    public IReadOnlyList<Point> GetRing(int index);
    public IEnumerable<IReadOnlyList<Point>> GetRings();
    public override Envelope GetEnvelope();
}

3.6.3 使用示例

using Esri.Geometry.Core.Geometries;

// 创建简单多边形(矩形)
var rectangle = new Polygon();
rectangle.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(100, 0),
    new Point(100, 50),
    new Point(0, 50),
    new Point(0, 0)  // 闭合
});

Console.WriteLine($"面积:{rectangle.Area}");  // 5000

// 创建带孔洞的多边形
var polygonWithHole = new Polygon();

// 外环
polygonWithHole.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(100, 0),
    new Point(100, 100),
    new Point(0, 100),
    new Point(0, 0)
});

// 内环(孔洞)
polygonWithHole.AddRing(new List<Point>
{
    new Point(25, 25),
    new Point(75, 25),
    new Point(75, 75),
    new Point(25, 75),
    new Point(25, 25)
});

Console.WriteLine($"环数量:{polygonWithHole.RingCount}");  // 2
// 面积计算包含孔洞:10000 - 2500 = 7500(实际实现可能只计算外环)

// 遍历环
int ringIndex = 0;
foreach (var ring in polygonWithHole.GetRings())
{
    string ringType = ringIndex == 0 ? "外环" : $"孔洞 {ringIndex}";
    Console.WriteLine($"{ringType}:{ring.Count} 个点");
    ringIndex++;
}

3.6.4 面积计算原理

Polygon 使用鞋带公式(Shoelace Formula)计算面积:

// Area 属性的实现
public double Area
{
    get
    {
        double area = 0;
        foreach (var ring in _rings)
        {
            var count = ring.Count;
            if (count < 3) continue;
            
            double ringArea = 0;
            for (var i = 0; i < count - 1; i++)
            {
                ringArea += ring[i].X * ring[i + 1].Y - ring[i + 1].X * ring[i].Y;
            }
            // 闭合环
            ringArea += ring[count - 1].X * ring[0].Y - ring[0].X * ring[count - 1].Y;
            area += Math.Abs(ringArea) * 0.5;
        }
        return area;
    }
}

鞋带公式的数学表达:
$$
A = \frac{1}{2} \left| \sum_{i=0}^{n-1} (x_i y_{i+1} - x_{i+1} y_i) \right|
$$

3.6.5 应用场景

// 场景:城市行政区域
var district = new Polygon();
district.AddRing(new List<Point>
{
    new Point(116.30, 39.85),
    new Point(116.50, 39.85),
    new Point(116.50, 40.05),
    new Point(116.30, 40.05),
    new Point(116.30, 39.85)
});

Console.WriteLine($"区域面积:{district.Area} 平方度");

// 检查点是否在区域内
var testPoint = new Point(116.40, 39.95);
bool isInDistrict = GeometryEngine.Contains(district, testPoint);
Console.WriteLine($"测试点在区域内:{isInDistrict}");

3.7 Envelope(包络矩形)

3.7.1 概念与特性

Envelope 是一个轴对齐的边界矩形(AABB),用于表示几何对象的最小边界框。

特性

  • 维度:2
  • 轴对齐(边平行于坐标轴)
  • 由最小和最大坐标定义
  • 常用于空间索引和快速过滤

3.7.2 类定义

public class Envelope : Geometry
{
    // 构造函数
    public Envelope();
    public Envelope(double xMin, double yMin, double xMax, double yMax);
    
    // 坐标属性
    public double XMin { get; set; }
    public double YMin { get; set; }
    public double XMax { get; set; }
    public double YMax { get; set; }
    
    // 计算属性
    public double Width { get; }
    public double Height { get; }
    public Point Center { get; }
    public double Area { get; }
    
    public override GeometryType Type => GeometryType.Envelope;
    public override bool IsEmpty { get; }
    public override int Dimension => 2;
    
    // 方法
    public bool Contains(Point point);
    public bool Intersects(Envelope other);
    public void Merge(Point point);
    public void Merge(Envelope other);
    public override Envelope GetEnvelope();
}

3.7.3 使用示例

using Esri.Geometry.Core.Geometries;

// 创建包络矩形
var envelope = new Envelope(0, 0, 100, 50);

// 获取基本属性
Console.WriteLine($"宽度:{envelope.Width}");   // 100
Console.WriteLine($"高度:{envelope.Height}");  // 50
Console.WriteLine($"面积:{envelope.Area}");    // 5000
Console.WriteLine($"中心点:({envelope.Center.X}, {envelope.Center.Y})");  // (50, 25)

// 测试包含关系
var point1 = new Point(50, 25);  // 中心点
var point2 = new Point(150, 25); // 外部点
Console.WriteLine($"包含 point1:{envelope.Contains(point1)}");  // true
Console.WriteLine($"包含 point2:{envelope.Contains(point2)}");  // false

// 测试相交
var envelope2 = new Envelope(50, 0, 150, 50);
Console.WriteLine($"相交:{envelope.Intersects(envelope2)}");  // true

// 合并点
var envelope3 = new Envelope(0, 0, 10, 10);
envelope3.Merge(new Point(20, 20));
Console.WriteLine($"合并后:({envelope3.XMin}, {envelope3.YMin}) - ({envelope3.XMax}, {envelope3.YMax})");
// 输出:(0, 0) - (20, 20)

// 合并包络
var envA = new Envelope(0, 0, 10, 10);
var envB = new Envelope(5, 5, 15, 15);
envA.Merge(envB);
Console.WriteLine($"合并后:({envA.XMin}, {envA.YMin}) - ({envA.XMax}, {envA.YMax})");
// 输出:(0, 0) - (15, 15)

3.7.4 应用场景

// 场景1:快速空间过滤
var searchArea = new Envelope(116.3, 39.8, 116.5, 40.0);
var points = new List<Point>
{
    new Point(116.4, 39.9),   // 在范围内
    new Point(116.2, 39.9),   // 不在范围内
    new Point(116.45, 39.95)  // 在范围内
};

var filteredPoints = points.Where(p => searchArea.Contains(p)).ToList();
Console.WriteLine($"范围内的点数量:{filteredPoints.Count}");  // 2

// 场景2:计算多个几何对象的总边界
var geometries = new List<Geometry>
{
    new Point(10, 10),
    new Point(50, 50),
    new Envelope(0, 0, 20, 20)
};

var totalBounds = new Envelope();
foreach (var geom in geometries)
{
    totalBounds.Merge(geom.GetEnvelope());
}
Console.WriteLine($"总边界:({totalBounds.XMin}, {totalBounds.YMin}) - ({totalBounds.XMax}, {totalBounds.YMax})");

3.8 MapGeometry(地图几何)

3.8.1 概念与特性

MapGeometry 将几何对象与其空间参考系统捆绑在一起,是在地图中使用几何对象的标准方式。

3.8.2 类定义

public class MapGeometry : IEquatable<MapGeometry>
{
    // 构造函数
    public MapGeometry();
    public MapGeometry(Geometry? geometry, SpatialReference? spatialReference);
    
    // 属性
    public Geometry? Geometry { get; set; }
    public SpatialReference? SpatialReference { get; set; }
    
    // 方法
    public bool Equals(MapGeometry? other);
    public override bool Equals(object? obj);
    public override int GetHashCode();
    public override string ToString();
    
    // 运算符
    public static bool operator ==(MapGeometry? left, MapGeometry? right);
    public static bool operator !=(MapGeometry? left, MapGeometry? right);
}

3.8.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;
using Esri.Geometry.Core.SpatialReference;

// 创建带空间参考的几何对象
var point = new Point(116.4074, 39.9042);
var wgs84 = SpatialReference.Wgs84();
var mapGeom = new MapGeometry(point, wgs84);

Console.WriteLine($"几何类型:{mapGeom.Geometry?.Type}");
Console.WriteLine($"空间参考 WKID:{mapGeom.SpatialReference?.Wkid}");

// 比较相等性
var mapGeom2 = new MapGeometry(new Point(116.4074, 39.9042), SpatialReference.Wgs84());
Console.WriteLine($"相等:{mapGeom.Equals(mapGeom2)}");  // true

// 输出为字符串(Esri JSON 格式)
Console.WriteLine(mapGeom.ToString());

3.9 几何对象的深拷贝

所有几何对象都支持通过 Copy() 方法进行深拷贝:

using Esri.Geometry.Core.Geometries;

// 复制点
var point = new Point(10, 20);
point.Z = 30;
point.M = 100;

var pointCopy = (Point)point.Copy();
Console.WriteLine($"复制后:({pointCopy.X}, {pointCopy.Y}, {pointCopy.Z}, M={pointCopy.M})");

// 复制多边形
var polygon = new Polygon();
polygon.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(10, 0),
    new Point(10, 10),
    new Point(0, 10),
    new Point(0, 0)
});

var polygonCopy = (Polygon)polygon.Copy();

// 修改原始对象不影响副本
polygon.AddRing(new List<Point> { /* 新环 */ });
Console.WriteLine($"原始多边形环数:{polygon.RingCount}");     // 2
Console.WriteLine($"副本多边形环数:{polygonCopy.RingCount}"); // 1

3.10 几何常量

GeometryConstants 类定义了框架使用的常量值:

public static class GeometryConstants
{
    // 默认容差(用于浮点数比较)
    public const double DefaultTolerance = 1e-10;
    
    // 极小值(用于避免除以零)
    public const double Epsilon = 1e-12;
}

3.11 小结

本章详细介绍了 geometry-api-net 的核心几何类型:

  1. Point:最基本的零维几何,支持 Z 和 M 值
  2. MultiPoint:点的集合
  3. Line:由两点定义的线段
  4. Polyline:由多条路径组成的折线
  5. Polygon:由多个环组成的多边形,支持孔洞
  6. Envelope:轴对齐的边界矩形
  7. MapGeometry:几何与空间参考的捆绑

这些几何类型构成了整个框架的基础,后续的所有操作符都是对这些类型的操作。在下一章中,我们将学习如何使用空间关系操作符来测试几何对象之间的空间关系。

posted @ 2025-12-03 16:29  我才是银古  阅读(8)  评论(0)    收藏  举报