第一章:geometry-api-net 项目概述与框架理念
1.1 项目简介
geometry-api-net 是一个面向 .NET 平台的高性能几何计算库,是 Esri Geometry API for Java 的 C# 移植版本。该库提供了完整的几何对象模型、空间关系测试、几何运算、数据格式转换等核心功能,是开发 GIS 应用程序的基础工具库。
1.1.1 项目背景
在地理信息系统(GIS)开发领域,几何计算是最基础也是最核心的功能之一。无论是简单的点线面绘制,还是复杂的空间分析,都离不开强大的几何计算引擎。Esri 作为全球领先的 GIS 软件厂商,开源了其 Java 版本的几何 API,geometry-api-net 正是基于该项目的设计理念和算法实现,为 .NET 生态系统提供了同等级别的几何计算能力。
1.1.2 技术规格
| 技术项 | 说明 |
|---|---|
| 目标框架 | .NET Standard 2.0 |
| 开发语言 | C# 7.0+ |
| JSON 支持 | System.Text.Json 8.0.6 |
| 测试框架 | xUnit |
| 开源协议 | LGPL 2.1 |
选择 .NET Standard 2.0 作为目标框架,可以确保库在各种 .NET 运行时环境中的兼容性:
- .NET Framework 4.6.1+
- .NET Core 2.0+
- .NET 5/6/7/8+
- Xamarin
- Unity
1.2 核心设计理念
1.2.1 单例模式的操作符设计
geometry-api-net 采用了操作符单例模式(Operator Singleton Pattern)作为核心设计范式。这种设计模式的核心思想是:将几何运算封装为独立的操作符类,每个操作符类都是单例的,通过 Instance 属性获取唯一实例。
// 操作符接口定义
public interface IGeometryOperator<TResult>
{TResult Execute(Geometry geometry, SpatialReference? spatialRef = null);
}public interface IBinaryGeometryOperator<TResult>
{TResult Execute(Geometry geometry1, Geometry geometry2, SpatialReference? spatialRef = null);
}
这种设计带来了几个重要优势:
- 内存效率:操作符实例在整个应用生命周期内只创建一次,避免了频繁创建对象的开销
- 线程安全:使用
Lazy<T>实现延迟初始化,确保线程安全 - 一致的 API:所有操作符都遵循统一的接口规范,学习一个就能掌握全部
- 可扩展性:新增操作符只需实现相应接口,无需修改现有代码
// 典型的操作符实现
public class BufferOperator : IGeometryOperator<Polygon>
{private static readonly Lazy<BufferOperator> _instance = new(() => new BufferOperator());private BufferOperator() { }public static BufferOperator Instance => _instance.Value;public Polygon Execute(Geometry geometry, SpatialReference? spatialRef = null){// 实现逻辑}
}
1.2.2 几何类型层次结构
框架采用了清晰的类型层次结构设计,所有几何类型都继承自抽象基类 Geometry:
Geometry (抽象基类)
├── Point (点)
├── MultiPoint (多点)
├── Line (线段)
├── Polyline (折线)
├── Polygon (多边形)
└── Envelope (包络矩形)
基类定义了所有几何对象的公共属性和行为:
public abstract class Geometry
{// 几何类型标识public abstract GeometryType Type { get; }// 是否为空public abstract bool IsEmpty { get; }// 几何维度:点=0, 线=1, 面=2public abstract int Dimension { get; }// 获取边界矩形public abstract Envelope GetEnvelope();// 类型判断便捷属性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 virtual double CalculateArea2D() { ... }public virtual double CalculateLength2D() { ... }public virtual Geometry Copy() { ... }public virtual bool IsValid() { ... }
}
这种设计体现了开放封闭原则(OCP):对扩展开放,对修改封闭。添加新的几何类型只需继承 Geometry 基类,无需修改现有代码。
1.2.3 关注点分离
框架将功能清晰地划分为不同的命名空间,实现了良好的关注点分离:
Esri.Geometry.Core
├── Geometries/ # 几何类型定义
│ ├── Geometry.cs # 抽象基类
│ ├── Point.cs # 点
│ ├── MultiPoint.cs # 多点
│ ├── Line.cs # 线段
│ ├── Polyline.cs # 折线
│ ├── Polygon.cs # 多边形
│ └── Envelope.cs # 包络矩形
├── Operators/ # 几何操作符
│ ├── 空间关系操作符
│ ├── 几何运算操作符
│ └── 集合操作符
├── SpatialReference/ # 空间参考系统
└── IO/ # 数据格式导入导出├── WKT 处理├── WKB 处理├── GeoJSON 处理└── Esri JSON 处理
1.2.4 不可变性与数据封装
框架在数据封装方面采用了保守策略,内部集合对象通过 AsReadOnly() 方法返回只读视图:
public class Polygon : Geometry
{private readonly List<List<Point>> _rings;// 返回只读列表,防止外部修改public IReadOnlyList<Point> GetRing(int index){return _rings[index].AsReadOnly();}public IEnumerable<IReadOnlyList<Point>> GetRings(){return _rings.Select(r => r.AsReadOnly());}
}
1.3 项目架构
1.3.1 整体架构图
┌─────────────────────────────────────────────────────────────────┐
│ Application Layer │
│ (用户应用程序 / GIS 系统) │
├─────────────────────────────────────────────────────────────────┤
│ GeometryEngine │
│ (简化的静态 API,统一入口) │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Operators │ │ I/O │ │ Spatial │ │
│ │ 操作符层 │ │ 数据格式层 │ │ Reference │ │
│ ├──────────────┤ ├──────────────┤ │ 空间参考层 │ │
│ │ Contains │ │ WKT Export │ ├──────────────┤ │
│ │ Intersects │ │ WKT Import │ │ WGS84 │ │
│ │ Buffer │ │ WKB Export │ │ WebMercator │ │
│ │ Union │ │ WKB Import │ │ Custom WKID │ │
│ │ Simplify │ │ GeoJSON │ │ │ │
│ │ ... │ │ EsriJSON │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Geometry Layer │
│ 几何对象层 │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ Point │ │Polyline│ │Polygon │ │Envelope│ │ Line │ │
│ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ │
│ ┌────────────┐ ┌────────────┐ │
│ │ MultiPoint │ │MapGeometry │ │
│ └────────────┘ └────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Foundation Layer │
│ 基础层 │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │GeometryConstants │ │ GeometryType │ │
│ │ 常量定义 │ │ 类型枚举 │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
1.3.2 GeometryEngine - 统一入口
GeometryEngine 是框架提供的简化 API,它将所有操作符封装为静态方法,使得日常使用更加便捷:
public static class GeometryEngine
{// 空间关系测试public static bool Contains(Geometry g1, Geometry g2);public static bool Intersects(Geometry g1, Geometry g2);public static double Distance(Geometry g1, Geometry g2);// 几何运算public static Geometry Buffer(Geometry g, double distance);public static Geometry ConvexHull(Geometry g);public static Point Centroid(Geometry g);// 集合运算public static Geometry Union(Geometry g1, Geometry g2);public static Geometry Intersection(Geometry g1, Geometry g2);public static Geometry Difference(Geometry g1, Geometry g2);// 数据导入导出public static string GeometryToWkt(Geometry g);public static Geometry GeometryFromWkt(string wkt);public static string GeometryToGeoJson(Geometry g);public static Geometry GeometryFromGeoJson(string json);
}
选择使用 GeometryEngine 还是直接使用操作符类,取决于具体场景:
- 日常使用:推荐
GeometryEngine,代码更简洁 - 批量处理:推荐直接使用操作符,避免方法调用开销
- 需要自定义行为:必须使用操作符类
1.4 功能清单
1.4.1 几何类型
| 类型 | 类名 | 维度 | 说明 |
|---|---|---|---|
| 点 | Point | 0 | 二维或三维点,支持 M 值 |
| 多点 | MultiPoint | 0 | 点的集合 |
| 线段 | Line | 1 | 由两个点定义的线段 |
| 折线 | Polyline | 1 | 由一条或多条路径组成 |
| 多边形 | Polygon | 2 | 由一个或多个环组成 |
| 包络矩形 | Envelope | 2 | 轴对齐的边界矩形 |
1.4.2 空间关系操作符
| 操作符 | 类名 | 说明 |
|---|---|---|
| 包含 | ContainsOperator | 测试 A 是否包含 B |
| 相交 | IntersectsOperator | 测试 A 和 B 是否相交 |
| 距离 | DistanceOperator | 计算 A 和 B 之间的距离 |
| 相等 | EqualsOperator | 测试 A 和 B 是否空间相等 |
| 分离 | DisjointOperator | 测试 A 和 B 是否分离 |
| 在内部 | WithinOperator | 测试 A 是否在 B 内部 |
| 穿过 | CrossesOperator | 测试 A 是否穿过 B |
| 接触 | TouchesOperator | 测试 A 和 B 是否接触 |
| 重叠 | OverlapsOperator | 测试 A 和 B 是否重叠 |
1.4.3 几何运算操作符
| 操作符 | 类名 | 说明 |
|---|---|---|
| 缓冲区 | BufferOperator | 创建几何对象的缓冲区 |
| 凸包 | ConvexHullOperator | 计算凸包 |
| 面积 | AreaOperator | 计算面积 |
| 长度 | LengthOperator | 计算长度或周长 |
| 简化 | SimplifyOperator | 使用 Douglas-Peucker 算法简化 |
| 质心 | CentroidOperator | 计算质心 |
| 边界 | BoundaryOperator | 计算边界 |
| 裁剪 | ClipOperator | 裁剪到包络范围 |
| 概化 | GeneralizeOperator | 概化几何对象 |
| 密化 | DensifyOperator | 密化几何对象 |
| 偏移 | OffsetOperator | 创建偏移曲线 |
1.4.4 集合操作符
| 操作符 | 类名 | 说明 |
|---|---|---|
| 并集 | UnionOperator | 计算两个几何对象的并集 |
| 交集 | IntersectionOperator | 计算两个几何对象的交集 |
| 差集 | DifferenceOperator | 计算两个几何对象的差集 |
| 对称差 | SymmetricDifferenceOperator | 计算对称差 |
1.4.5 大地测量操作符
| 操作符 | 类名 | 说明 |
|---|---|---|
| 大地测量距离 | GeodesicDistanceOperator | 计算椭球面上的距离 |
| 大地测量面积 | GeodesicAreaOperator | 计算椭球面上的面积 |
1.4.6 数据格式支持
| 格式 | 导入 | 导出 | 说明 |
|---|---|---|---|
| WKT | ✅ | ✅ | Well-Known Text |
| WKB | ✅ | ✅ | Well-Known Binary |
| GeoJSON | ✅ | ✅ | GeoJSON 格式 |
| Esri JSON | ✅ | ✅ | Esri 专有 JSON 格式 |
1.5 与其他库的比较
1.5.1 与 NetTopologySuite (NTS) 比较
| 特性 | geometry-api-net | NetTopologySuite |
|---|---|---|
| 设计理念 | 操作符单例模式 | JTS 移植 |
| API 风格 | 静态方法 + 操作符 | 实例方法 |
| 性能重点 | 轻量级、快速 | 功能完整 |
| 依赖 | 最小化 | 较多 |
| 适用场景 | 嵌入式、轻量级应用 | 企业级 GIS |
1.5.2 选择建议
选择 geometry-api-net 的场景:
- 需要轻量级几何计算库
- 对启动时间和内存占用敏感
- 与 Esri 产品集成
- 学习 GIS 几何算法
选择 NetTopologySuite 的场景:
- 需要完整的 JTS 功能
- 与 Entity Framework 集成
- 需要高级拓扑操作
- 处理复杂的空间查询
1.6 测试覆盖
项目具有全面的测试覆盖,共计 255+ 个测试用例:
- 28 个几何类型测试
- 14 个空间关系操作符测试
- 23 个附加操作符测试
- 12 个几何操作测试
- 20 个高级操作符测试
- 24 个邻近和 GeometryEngine 测试
- 23 个集合操作测试
- 17 个 WKT 导入/导出测试
- 10 个 WKB 导入/导出测试
- 8 个 GeoJSON 导入/导出测试
- 18 个 SimplifyOGC 操作符测试
- 17 个几何辅助方法测试
1.7 小结
geometry-api-net 是一个设计精良、功能完备的 .NET 几何计算库。其核心设计理念包括:
- 操作符单例模式:提供一致的 API 和良好的性能
- 清晰的类型层次:符合 OGC 标准的几何模型
- 关注点分离:功能模块化,易于理解和维护
- 多格式支持:WKT、WKB、GeoJSON、Esri JSON
- 全面的测试:255+ 个测试用例确保代码质量
在接下来的章节中,我们将深入学习每个功能模块的使用方法和实现原理。