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

推荐订阅源

T
The Exploit Database - CXSecurity.com
F
Fortinet All Blogs
U
Unit 42
F
Full Disclosure
雷峰网
雷峰网
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
罗磊的独立博客
D
DataBreaches.Net
C
Check Point Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
O
OpenAI News
C
CXSECURITY Database RSS Feed - CXSecurity.com
aimingoo的专栏
aimingoo的专栏
S
Security @ Cisco Blogs
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Hacker News
The Hacker News
Webroot Blog
Webroot Blog
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google DeepMind News
Google DeepMind News
酷 壳 – CoolShell
酷 壳 – CoolShell
N
News | PayPal Newsroom
P
Proofpoint News Feed
B
Blog RSS Feed
MongoDB | Blog
MongoDB | Blog
C
Cybersecurity and Infrastructure Security Agency CISA
N
News and Events Feed by Topic
Google Online Security Blog
Google Online Security Blog
H
Help Net Security
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
M
MIT News - Artificial intelligence
Vercel News
Vercel News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
IT之家
IT之家
MyScale Blog
MyScale Blog
腾讯CDC

博客园 - 我才是银古

第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 博客园 - 我才是银古

第四章:空间关系操作符

4.1 空间关系概述

空间关系是 GIS 分析的基础,用于描述两个几何对象在空间上的相对位置关系。geometry-api-net 提供了 9 种符合 OGC 标准的空间关系测试操作符。

4.1.1 空间关系类型

关系 操作符 说明
包含 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 部分重叠

4.1.2 操作符设计模式

所有空间关系操作符都实现了 IBinaryGeometryOperator<bool> 接口(除了 DistanceOperator 返回 double):

public interface IBinaryGeometryOperator<TResult>
{
    TResult Execute(Geometry geometry1, Geometry geometry2, 
                    SpatialReference? spatialRef = null);
}

操作符采用单例模式,通过 Instance 属性获取实例:

// 使用操作符
bool result = ContainsOperator.Instance.Execute(geometry1, geometry2);

// 或使用 GeometryEngine
bool result = GeometryEngine.Contains(geometry1, geometry2);

4.2 Contains(包含)

4.2.1 定义

如果几何对象 A 包含几何对象 B,意味着 B 的所有点都在 A 的内部或边界上,且 B 的内部与 A 的内部相交。

数学表达

  • B 在 A 内部:B ⊆ A
  • Contains(A, B) = true 等价于 Within(B, A) = true

4.2.2 API

public class ContainsOperator : IBinaryGeometryOperator<bool>
{
    public static ContainsOperator Instance { get; }
    
    public bool Execute(Geometry geometry1, Geometry geometry2, 
                       SpatialReference? spatialRef = null);
}

4.2.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;
using Esri.Geometry.Core.Operators;

// 包络矩形包含点
var envelope = new Envelope(0, 0, 100, 100);
var pointInside = new Point(50, 50);
var pointOutside = new Point(150, 50);

bool contains1 = ContainsOperator.Instance.Execute(envelope, pointInside);  // true
bool contains2 = ContainsOperator.Instance.Execute(envelope, pointOutside); // false

// 使用 GeometryEngine
bool contains3 = GeometryEngine.Contains(envelope, pointInside);  // true

// 多边形包含点(使用光线投射算法)
var polygon = new Polygon();
polygon.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(100, 0),
    new Point(100, 100),
    new Point(0, 100),
    new Point(0, 0)
});

bool inPolygon = GeometryEngine.Contains(polygon, new Point(50, 50));  // true
bool inPolygon2 = GeometryEngine.Contains(polygon, new Point(150, 50)); // false

4.2.4 实现原理:光线投射算法

对于点在多边形内的测试,ContainsOperator 使用光线投射算法(Ray Casting Algorithm)

private static bool IsPointInPolygon(Polygon polygon, Point point)
{
    if (polygon.IsEmpty || polygon.RingCount == 0)
        return false;

    var inside = false;
    var x = point.X;
    var y = point.Y;

    // 测试外环
    var ring = polygon.GetRing(0);
    if (ring.Count < 3) return false;

    // 光线投射:从点向右发射一条射线,计算与多边形边界的交点数
    // 奇数次交点 = 在内部,偶数次交点 = 在外部
    for (int i = 0, j = ring.Count - 1; i < ring.Count; j = i++)
    {
        double xi = ring[i].X, yi = ring[i].Y;
        double xj = ring[j].X, yj = ring[j].Y;

        if (((yi > y) != (yj > y)) &&
            (x < (xj - xi) * (y - yi) / (yj - yi) + xi))
        {
            inside = !inside;
        }
    }

    // 检查孔洞
    for (var ringIndex = 1; ringIndex < polygon.RingCount; ringIndex++)
    {
        var holeRing = polygon.GetRing(ringIndex);
        // ... 类似的光线投射检测
        // 如果点在孔洞内,则不在多边形内
    }

    return inside;
}

算法复杂度:O(n),其中 n 是多边形顶点数。

4.2.5 应用场景

// 场景:判断用户是否在服务区域内
var serviceArea = new Polygon();
serviceArea.AddRing(new List<Point>
{
    new Point(116.2, 39.7),
    new Point(116.6, 39.7),
    new Point(116.6, 40.1),
    new Point(116.2, 40.1),
    new Point(116.2, 39.7)
});

var userLocation = new Point(116.4, 39.9);

if (GeometryEngine.Contains(serviceArea, userLocation))
{
    Console.WriteLine("用户在服务区域内");
}
else
{
    Console.WriteLine("用户不在服务区域内");
}

4.3 Intersects(相交)

4.3.1 定义

如果两个几何对象有任何共同的点,则它们相交。这是最常用的空间关系测试。

数学表达:A ∩ B ≠ ∅

4.3.2 API

public class IntersectsOperator : IBinaryGeometryOperator<bool>
{
    public static IntersectsOperator Instance { get; }
    
    public bool Execute(Geometry geometry1, Geometry geometry2, 
                       SpatialReference? spatialRef = null);
}

4.3.3 使用示例

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

// 两个重叠的包络矩形
var env1 = new Envelope(0, 0, 100, 100);
var env2 = new Envelope(50, 50, 150, 150);
var env3 = new Envelope(200, 200, 300, 300);

bool intersects1 = GeometryEngine.Intersects(env1, env2);  // true
bool intersects2 = GeometryEngine.Intersects(env1, env3);  // false

// 点与包络矩形
var point = new Point(50, 50);
bool intersects3 = GeometryEngine.Intersects(env1, point);  // true

// 多边形相交
var poly1 = new Polygon();
poly1.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(50, 0),
    new Point(50, 50),
    new Point(0, 50),
    new Point(0, 0)
});

var poly2 = new Polygon();
poly2.AddRing(new List<Point>
{
    new Point(25, 25),
    new Point(75, 25),
    new Point(75, 75),
    new Point(25, 75),
    new Point(25, 25)
});

bool polyIntersects = GeometryEngine.Intersects(poly1, poly2);  // true

4.3.4 实现原理

IntersectsOperator 根据不同的几何类型组合采用不同的算法:

public bool Execute(Geometry geometry1, Geometry geometry2, 
                   SpatialReference? spatialRef = null)
{
    // 空几何不相交
    if (geometry1.IsEmpty || geometry2.IsEmpty)
        return false;

    // 包络矩形相交测试(快速过滤)
    if (geometry1 is Envelope env1 && geometry2 is Envelope env2)
        return env1.Intersects(env2);

    // 点与包络矩形
    if (geometry1 is Envelope env && geometry2 is Point pt)
        return env.Contains(pt);

    // 点与多边形
    if (geometry1 is Polygon poly && geometry2 is Point p)
        return ContainsOperator.Instance.Execute(poly, p);

    // 默认使用包络矩形相交测试
    var envelope1 = geometry1.GetEnvelope();
    var envelope2 = geometry2.GetEnvelope();
    return envelope1.Intersects(envelope2);
}

4.3.5 应用场景

// 场景:查找与查询范围相交的所有对象
var queryExtent = new Envelope(116.3, 39.8, 116.5, 40.0);
var buildings = new List<Polygon> { /* 建筑物多边形列表 */ };

var intersectingBuildings = buildings
    .Where(b => GeometryEngine.Intersects(queryExtent, b))
    .ToList();

Console.WriteLine($"找到 {intersectingBuildings.Count} 个相交的建筑物");

4.4 Distance(距离)

4.4.1 定义

计算两个几何对象之间的最短欧几里得距离。如果两个几何对象相交,距离为 0。

4.4.2 API

public class DistanceOperator : IBinaryGeometryOperator<double>
{
    public static DistanceOperator Instance { get; }
    
    public double Execute(Geometry geometry1, Geometry geometry2, 
                         SpatialReference? spatialRef = null);
}

4.4.3 使用示例

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

// 两点之间的距离
var point1 = new Point(0, 0);
var point2 = new Point(3, 4);

double distance = GeometryEngine.Distance(point1, point2);
Console.WriteLine($"距离:{distance}");  // 5(勾股定理:3² + 4² = 5²)

// 点到包络矩形的距离
var envelope = new Envelope(10, 10, 20, 20);
var testPoint = new Point(0, 0);

double distToEnv = GeometryEngine.Distance(testPoint, envelope);
Console.WriteLine($"点到矩形的距离:{distToEnv:F4}");  // ~14.14

// 点在矩形内部,距离为 0
var pointInside = new Point(15, 15);
double distInside = GeometryEngine.Distance(pointInside, envelope);
Console.WriteLine($"内部点距离:{distInside}");  // 0

4.4.4 实现原理

public double Execute(Geometry geometry1, Geometry geometry2, 
                     SpatialReference? spatialRef = null)
{
    // 点与点的距离
    if (geometry1 is Point p1 && geometry2 is Point p2)
        return p1.Distance(p2);

    // 点与包络矩形的距离
    if (geometry1 is Point point && geometry2 is Envelope envelope)
    {
        // 如果点在包络内,距离为 0
        if (envelope.Contains(point))
            return 0;

        // 计算点到最近边的距离
        double dx = Math.Max(0, Math.Max(envelope.XMin - point.X, point.X - envelope.XMax));
        double dy = Math.Max(0, Math.Max(envelope.YMin - point.Y, point.Y - envelope.YMax));
        return Math.Sqrt(dx * dx + dy * dy);
    }

    // 包络矩形之间的距离
    if (geometry1 is Envelope env1 && geometry2 is Envelope env2)
    {
        if (env1.Intersects(env2))
            return 0;

        double dx = Math.Max(0, Math.Max(env1.XMin - env2.XMax, env2.XMin - env1.XMax));
        double dy = Math.Max(0, Math.Max(env1.YMin - env2.YMax, env2.YMin - env1.YMax));
        return Math.Sqrt(dx * dx + dy * dy);
    }

    // 对于其他几何类型,使用包络近似
    return Execute(geometry1.GetEnvelope(), geometry2.GetEnvelope());
}

4.4.5 应用场景

// 场景:查找最近的商店
var userLocation = new Point(116.4, 39.9);
var stores = new List<(string name, Point location)>
{
    ("商店A", new Point(116.41, 39.91)),
    ("商店B", new Point(116.38, 39.88)),
    ("商店C", new Point(116.45, 39.95))
};

var nearestStore = stores
    .OrderBy(s => GeometryEngine.Distance(userLocation, s.location))
    .First();

double nearestDistance = GeometryEngine.Distance(userLocation, nearestStore.location);
Console.WriteLine($"最近的商店:{nearestStore.name},距离:{nearestDistance:F4} 度");

4.5 Equals(相等)

4.5.1 定义

判断两个几何对象在空间上是否完全相同(考虑容差)。

4.5.2 API

public class EqualsOperator : IBinaryGeometryOperator<bool>
{
    public static EqualsOperator Instance { get; }
    
    public bool Execute(Geometry geometry1, Geometry geometry2, 
                       SpatialReference? spatialRef = null);
}

4.5.3 使用示例

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

// 相同坐标的点
var point1 = new Point(10.0, 20.0);
var point2 = new Point(10.0, 20.0);

bool equals1 = GeometryEngine.Equals(point1, point2);  // true

// 容差范围内的点
var point3 = new Point(10.0000001, 20.0000001);
bool equals2 = GeometryEngine.Equals(point1, point3);  // true(在默认容差内)

// 不同的点
var point4 = new Point(11.0, 21.0);
bool equals3 = GeometryEngine.Equals(point1, point4);  // false

// 相同的包络矩形
var env1 = new Envelope(0, 0, 100, 100);
var env2 = new Envelope(0, 0, 100, 100);
bool equals4 = GeometryEngine.Equals(env1, env2);  // true

4.5.4 实现原理

public bool Execute(Geometry geometry1, Geometry geometry2, 
                   SpatialReference? spatialRef = null)
{
    if (geometry1 == null || geometry2 == null)
        return false;

    if (geometry1.Type != geometry2.Type)
        return false;

    if (geometry1.IsEmpty && geometry2.IsEmpty)
        return true;

    if (geometry1.IsEmpty != geometry2.IsEmpty)
        return false;

    // 点的相等性
    if (geometry1 is Point p1 && geometry2 is Point p2)
        return p1.Equals(p2, GeometryConstants.DefaultTolerance);

    // 包络矩形的相等性
    if (geometry1 is Envelope env1 && geometry2 is Envelope env2)
    {
        return Math.Abs(env1.XMin - env2.XMin) <= GeometryConstants.DefaultTolerance &&
               Math.Abs(env1.YMin - env2.YMin) <= GeometryConstants.DefaultTolerance &&
               Math.Abs(env1.XMax - env2.XMax) <= GeometryConstants.DefaultTolerance &&
               Math.Abs(env1.YMax - env2.YMax) <= GeometryConstants.DefaultTolerance;
    }

    // 其他类型使用包络比较
    var envelope1 = geometry1.GetEnvelope();
    var envelope2 = geometry2.GetEnvelope();
    return Execute(envelope1, envelope2);
}

4.6 Disjoint(分离)

4.6.1 定义

如果两个几何对象没有任何共同的点,则它们分离。这是 Intersects 的逆操作。

数学表达:A ∩ B = ∅

4.6.2 API

public class DisjointOperator : IBinaryGeometryOperator<bool>
{
    public static DisjointOperator Instance { get; }
    
    public bool Execute(Geometry geometry1, Geometry geometry2, 
                       SpatialReference? spatialRef = null);
}

4.6.3 使用示例

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

var env1 = new Envelope(0, 0, 10, 10);
var env2 = new Envelope(20, 20, 30, 30);  // 完全分离
var env3 = new Envelope(5, 5, 15, 15);    // 重叠

bool disjoint1 = GeometryEngine.Disjoint(env1, env2);  // true
bool disjoint2 = GeometryEngine.Disjoint(env1, env3);  // false

// 等价于 !Intersects
bool notIntersects = !GeometryEngine.Intersects(env1, env2);  // true

4.6.4 实现原理

public bool Execute(Geometry geometry1, Geometry geometry2, 
                   SpatialReference? spatialRef = null)
{
    // Disjoint 是 Intersects 的逆
    return !IntersectsOperator.Instance.Execute(geometry1, geometry2, spatialRef);
}

4.7 Within(在内部)

4.7.1 定义

如果几何对象 A 完全在几何对象 B 的内部,则 A 在 B 内。这是 Contains 的逆操作。

数学表达:Within(A, B) = Contains(B, A)

4.7.2 API

public class WithinOperator : IBinaryGeometryOperator<bool>
{
    public static WithinOperator Instance { get; }
    
    public bool Execute(Geometry geometry1, Geometry geometry2, 
                       SpatialReference? spatialRef = null);
}

4.7.3 使用示例

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

var smallEnv = new Envelope(20, 20, 30, 30);
var largeEnv = new Envelope(0, 0, 100, 100);

bool within1 = GeometryEngine.Within(smallEnv, largeEnv);  // true
bool within2 = GeometryEngine.Within(largeEnv, smallEnv);  // false

// 点在多边形内
var point = new Point(50, 50);
var polygon = new Polygon();
polygon.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(100, 0),
    new Point(100, 100),
    new Point(0, 100),
    new Point(0, 0)
});

bool pointWithin = GeometryEngine.Within(point, polygon);  // true

4.7.4 实现原理

public bool Execute(Geometry geometry1, Geometry geometry2, 
                   SpatialReference? spatialRef = null)
{
    // Within 是 Contains 的逆:A within B = B contains A
    return ContainsOperator.Instance.Execute(geometry2, geometry1, spatialRef);
}

4.8 Crosses(穿过)

4.8.1 定义

如果两个几何对象的内部相交,但既不完全相同,也没有一个包含另一个,则它们穿过。通常用于线与线或线与面的关系。

4.8.2 API

public class CrossesOperator : IBinaryGeometryOperator<bool>
{
    public static CrossesOperator Instance { get; }
    
    public bool Execute(Geometry geometry1, Geometry geometry2, 
                       SpatialReference? spatialRef = null);
}

4.8.3 使用示例

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

// 两条交叉的线
var line1 = new Polyline();
line1.AddPath(new List<Point>
{
    new Point(0, 50),
    new Point(100, 50)  // 水平线
});

var line2 = new Polyline();
line2.AddPath(new List<Point>
{
    new Point(50, 0),
    new Point(50, 100)  // 垂直线
});

bool crosses = GeometryEngine.Crosses(line1, line2);  // true

// 线穿过多边形
var polyline = new Polyline();
polyline.AddPath(new List<Point>
{
    new Point(-10, 50),
    new Point(110, 50)  // 穿过多边形
});

var polygon = new Polygon();
polygon.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(100, 0),
    new Point(100, 100),
    new Point(0, 100),
    new Point(0, 0)
});

bool lineCrossesPoly = GeometryEngine.Crosses(polyline, polygon);  // true

4.9 Touches(接触)

4.9.1 定义

如果两个几何对象仅在边界上有共同点,而内部不相交,则它们接触。

4.9.2 API

public class TouchesOperator : IBinaryGeometryOperator<bool>
{
    public static TouchesOperator Instance { get; }
    
    public bool Execute(Geometry geometry1, Geometry geometry2, 
                       SpatialReference? spatialRef = null);
}

4.9.3 使用示例

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

// 两个相邻的多边形
var poly1 = new Polygon();
poly1.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(50, 0),
    new Point(50, 100),
    new Point(0, 100),
    new Point(0, 0)
});

var poly2 = new Polygon();
poly2.AddRing(new List<Point>
{
    new Point(50, 0),   // 共享边界
    new Point(100, 0),
    new Point(100, 100),
    new Point(50, 100), // 共享边界
    new Point(50, 0)
});

bool touches = GeometryEngine.Touches(poly1, poly2);  // true(共享边界)

// 点在多边形边界上
var boundaryPoint = new Point(0, 50);  // 在 poly1 的左边界上
bool pointTouches = GeometryEngine.Touches(boundaryPoint, poly1);  // true

4.10 Overlaps(重叠)

4.10.1 定义

如果两个相同维度的几何对象相交,但既不完全相同,也没有一个完全包含另一个,则它们重叠。

4.10.2 API

public class OverlapsOperator : IBinaryGeometryOperator<bool>
{
    public static OverlapsOperator Instance { get; }
    
    public bool Execute(Geometry geometry1, Geometry geometry2, 
                       SpatialReference? spatialRef = null);
}

4.10.3 使用示例

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

// 两个部分重叠的多边形
var poly1 = new Polygon();
poly1.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(60, 0),
    new Point(60, 60),
    new Point(0, 60),
    new Point(0, 0)
});

var poly2 = new Polygon();
poly2.AddRing(new List<Point>
{
    new Point(40, 40),
    new Point(100, 40),
    new Point(100, 100),
    new Point(40, 100),
    new Point(40, 40)
});

bool overlaps = GeometryEngine.Overlaps(poly1, poly2);  // true

// 两个包络矩形重叠
var env1 = new Envelope(0, 0, 50, 50);
var env2 = new Envelope(25, 25, 75, 75);
bool envOverlaps = GeometryEngine.Overlaps(env1, env2);  // true

4.11 空间关系矩阵

4.11.1 关系总结

下表显示了各种空间关系之间的逻辑关系:

条件 Disjoint Intersects Contains Within Touches Overlaps Crosses Equals
A ∩ B = ∅
A ∩ B ≠ ∅
B ⊆ A
A ⊆ B
仅边界相交
部分内部重叠
线性穿过
A = B

4.11.2 互斥关系

// Disjoint 和 Intersects 互斥
Disjoint(A, B) = !Intersects(A, B)

// Contains 和 Within 是逆关系
Contains(A, B) = Within(B, A)

4.12 最佳实践

4.12.1 性能优化

// 1. 先用包络矩形快速过滤
var queryEnvelope = new Envelope(100, 100, 200, 200);
var candidates = allGeometries
    .Where(g => g.GetEnvelope().Intersects(queryEnvelope));

// 2. 再进行精确测试
var results = candidates
    .Where(g => GeometryEngine.Intersects(g, queryPolygon))
    .ToList();

4.12.2 常见错误

// ❌ 错误:直接精确测试大量几何对象
var results = allGeometries
    .Where(g => GeometryEngine.Contains(polygon, g))  // 性能差
    .ToList();

// ✅ 正确:先包络过滤
var polygonEnvelope = polygon.GetEnvelope();
var results = allGeometries
    .Where(g => polygonEnvelope.Intersects(g.GetEnvelope()))
    .Where(g => GeometryEngine.Contains(polygon, g))
    .ToList();

4.12.3 选择正确的关系测试

需求 推荐操作符
检查点是否在区域内 Contains
检查两个区域是否有交集 Intersects
查找最近的对象 Distance
检查两个对象是否相同 Equals
检查线是否穿过区域 Crosses
检查区域是否相邻 Touches

4.13 小结

本章详细介绍了 geometry-api-net 提供的 9 种空间关系操作符:

  1. Contains:测试包含关系,使用光线投射算法
  2. Intersects:测试是否有共同部分
  3. Distance:计算最短距离
  4. Equals:测试空间相等性
  5. Disjoint:测试分离关系(Intersects 的逆)
  6. Within:测试在内部关系(Contains 的逆)
  7. Crosses:测试穿过关系
  8. Touches:测试接触关系
  9. Overlaps:测试重叠关系

这些操作符是进行空间分析的基础,在下一章中我们将学习几何运算操作符,它们可以生成新的几何对象。