惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志
C
Check Point Blog
Stack Overflow Blog
Stack Overflow Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
M
MIT News - Artificial intelligence
L
LangChain Blog
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - Franky
WordPress大学
WordPress大学
博客园_首页
Y
Y Combinator Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Visual Studio Blog
L
LINUX DO - 最新话题
S
Security @ Cisco Blogs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Help Net Security
Help Net Security
大猫的无限游戏
大猫的无限游戏
Hugging Face - Blog
Hugging Face - Blog
The GitHub Blog
The GitHub Blog
Schneier on Security
Schneier on Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
Jina AI
Jina AI
雷峰网
雷峰网
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 【当耐特】
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
人人都是产品经理
人人都是产品经理
Microsoft Security Blog
Microsoft Security Blog
V
V2EX
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
宝玉的分享
宝玉的分享
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
P
Proofpoint News Feed
N
Netflix TechBlog - Medium
Martin Fowler
Martin Fowler
O
OpenAI News
P
Proofpoint News Feed
H
Help Net Security
S
Securelist
Vercel News
Vercel News
Hacker News: Ask HN
Hacker News: Ask HN
博客园 - 三生石上(FineUI控件)

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第三章:核心几何类型详解
我才是银古 · 2026-06-22 · via 博客园 - 我才是银古

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

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:几何与空间参考的捆绑

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