| name | geometry-api-net |
| description | geometry-api-net 是一个 .NET Standard 2.0 的空间几何计算库,提供对二维和三维几何对象的创建、解析、序列化以及空间关系运算能力。 |
| tags | ["dotnet","geometry","esri","spatial","wkt","wkb","geojson","geodesic","csharp"] |
项目地址: https://github.com/znlgis/geometry-api-net
本文件旨在帮助 AI 快速理解并使用 geometry-api-net 库进行空间几何开发。
项目概述
这是一个 .NET Standard 2.0 的空间几何计算库,提供 6 种几何类型、25+ 空间运算算子和 4 种序列化格式。
- 核心包:
Esri.Geometry.Core
- JSON 扩展包:
Esri.Geometry.Json
- 许可证: LGPL-2.1-only
快速开始
dotnet add package Esri.Geometry.Core
dotnet add package Esri.Geometry.Json
命名空间
| 命名空间 | 用途 |
|---|
OpenGIS.Esri.Geometry.Core | 核心入口,GeometryEngine 便捷 API |
OpenGIS.Esri.Geometry.Core.Geometries | 几何类型(Point, Polygon 等) |
OpenGIS.Esri.Geometry.Core.Operators | 空间运算算子 |
OpenGIS.Esri.Geometry.Core.SpatialReference | 空间参考 / 坐标系统 |
OpenGIS.Esri.Geometry.Core.IO | 序列化(WKT, WKB, GeoJSON, EsriJson) |
OpenGIS.Esri.Geometry.Json.Converters | System.Text.Json 转换器 |
几何类型
GeometryType 枚举
public enum GeometryType
{
Unknown = 0,
Point = 1,
Line = 2,
Envelope = 3,
MultiPoint = 4,
Polyline = 5,
Polygon = 6
}
Geometry 基类(抽象)
所有几何类型继承自 Geometry,共有属性和方法:
geometry.Type
geometry.IsEmpty
geometry.Dimension
geometry.IsPoint
geometry.IsLinear
geometry.IsArea
geometry.GetEnvelope()
geometry.CalculateArea2D()
geometry.CalculateLength2D()
geometry.Copy()
geometry.IsValid()
Point — 点
var p = new Point();
var p = new Point(10, 20);
var p = new Point(10, 20, 30);
p.X
p.Y
p.Z
p.M
p.Distance(otherPoint)
p.Equals(otherPoint, tolerance)
Line — 线段
var line = new Line();
var line = new Line(new Point(0, 0), new Point(10, 10));
line.Start
line.End
line.Length
Envelope — 外包矩形
var env = new Envelope();
var env = new Envelope(0, 0, 100, 100);
env.XMin, env.YMin, env.XMax, env.YMax
env.Width
env.Height
env.Center
env.Area
env.Contains(point)
env.Intersects(otherEnv)
env.Merge(point)
env.Merge(otherEnv)
MultiPoint — 多点
var mp = new MultiPoint();
var mp = new MultiPoint(new[] { new Point(1, 2), new Point(3, 4) });
mp.Add(new Point(5, 6));
mp.Count
mp.GetPoint(0)
mp.GetPoints()
Polyline — 折线
var polyline = new Polyline();
polyline.AddPath(new[] {
new Point(0, 0), new Point(10, 0), new Point(10, 10)
});
polyline.PathCount
polyline.Length
polyline.GetPath(0)
polyline.GetPaths()
Polygon — 多边形
var polygon = new Polygon();
polygon.AddRing(new[] {
new Point(0, 0), new Point(10, 0), new Point(10, 10),
new Point(0, 10), new Point(0, 0)
});
polygon.RingCount
polygon.Area
polygon.GetRing(0)
polygon.GetRings()
两种 API 风格
风格 1:GeometryEngine 静态方法(推荐)
GeometryEngine 提供所有操作的便捷静态方法入口,是最简洁的使用方式:
using OpenGIS.Esri.Geometry.Core;
bool result = GeometryEngine.Contains(geometry1, geometry2);
bool result = GeometryEngine.Intersects(geometry1, geometry2);
bool result = GeometryEngine.Within(geometry1, geometry2);
bool result = GeometryEngine.Disjoint(geometry1, geometry2);
bool result = GeometryEngine.Crosses(geometry1, geometry2);
bool result = GeometryEngine.Touches(geometry1, geometry2);
bool result = GeometryEngine.Overlaps(geometry1, geometry2);
bool result = GeometryEngine.Equals(geometry1, geometry2);
double dist = GeometryEngine.Distance(geometry1, geometry2);
Geometry result = GeometryEngine.Union(geometry1, geometry2);
Geometry result = GeometryEngine.Intersection(geometry1, geometry2);
Geometry result = GeometryEngine.Difference(geometry1, geometry2);
Geometry result = GeometryEngine.SymmetricDifference(geometry1, geometry2);
Geometry buffer = GeometryEngine.Buffer(geometry, distance);
Geometry hull = GeometryEngine.ConvexHull(geometry);
Geometry simple = GeometryEngine.Simplify(geometry, tolerance);
Geometry simpleOGC = GeometryEngine.SimplifyOGC(geometry, spatialRef);
bool isSimple = GeometryEngine.IsSimpleOGC(geometry, spatialRef);
Geometry general = GeometryEngine.Generalize(geometry, maxDeviation);
Geometry dense = GeometryEngine.Densify(geometry, maxSegmentLength);
Geometry clipped = GeometryEngine.Clip(geometry, clipEnvelope);
Geometry offset = GeometryEngine.Offset(geometry, distance);
Point center = GeometryEngine.Centroid(geometry);
Geometry boundary = GeometryEngine.Boundary(geometry);
double area = GeometryEngine.Area(geometry);
double length = GeometryEngine.Length(geometry);
double geodesicDist = GeometryEngine.GeodesicDistance(point1, point2);
double geodesicArea = GeometryEngine.GeodesicArea(polygon);
Proximity2DResult nearest = GeometryEngine.GetNearestCoordinate(geometry, point, testPolygonInterior);
Proximity2DResult nearest = GeometryEngine.GetNearestVertex(geometry, point);
Proximity2DResult[] results = GeometryEngine.GetNearestVertices(geometry, point, searchRadius, maxVertexCount);
string wkt = GeometryEngine.GeometryToWkt(geometry);
Geometry fromWkt = GeometryEngine.GeometryFromWkt(wktString);
byte[] wkb = GeometryEngine.GeometryToWkb(geometry, bigEndian);
Geometry fromWkb = GeometryEngine.GeometryFromWkb(wkbBytes);
string json = GeometryEngine.GeometryToGeoJson(geometry);
Geometry fromJson = GeometryEngine.GeometryFromGeoJson(geoJsonString);
string esri = GeometryEngine.GeometryToEsriJson(geometry);
Geometry fromEsri = GeometryEngine.GeometryFromEsriJson(esriJsonString);
风格 2:Operator 单例模式
每个算子都通过 Instance 单例属性获取(Lazy 初始化),适合需要更精细控制的场景:
using OpenGIS.Esri.Geometry.Core.Operators;
bool contains = ContainsOperator.Instance.Execute(geom1, geom2);
bool intersects = IntersectsOperator.Instance.Execute(geom1, geom2);
double distance = DistanceOperator.Instance.Execute(geom1, geom2);
bool equals = EqualsOperator.Instance.Execute(geom1, geom2);
bool disjoint = DisjointOperator.Instance.Execute(geom1, geom2);
bool within = WithinOperator.Instance.Execute(geom1, geom2);
bool crosses = CrossesOperator.Instance.Execute(geom1, geom2);
bool touches = TouchesOperator.Instance.Execute(geom1, geom2);
bool overlaps = OverlapsOperator.Instance.Execute(geom1, geom2);
Geometry union = UnionOperator.Instance.Execute(geom1, geom2);
Geometry intersection = IntersectionOperator.Instance.Execute(geom1, geom2);
Geometry difference = DifferenceOperator.Instance.Execute(geom1, geom2);
Geometry symDiff = SymmetricDifferenceOperator.Instance.Execute(geom1, geom2);
Geometry buffer = BufferOperator.Instance.Execute(geometry, distance);
Geometry convexHull = ConvexHullOperator.Instance.Execute(geometry);
Geometry simplified = SimplifyOperator.Instance.Execute(geometry, tolerance);
Point centroid = CentroidOperator.Instance.Execute(geometry);
Geometry boundary = BoundaryOperator.Instance.Execute(geometry);
double area = AreaOperator.Instance.Execute(geometry);
double length = LengthOperator.Instance.Execute(geometry);
Geometry offset = OffsetOperator.Instance.Execute(geometry, distance);
double geodesicDist = GeodesicDistanceOperator.Instance.Execute(point1, point2);
double geodesicArea = GeodesicAreaOperator.Instance.Execute(polygon);
Proximity2DResult result = Proximity2DOperator.Instance.GetNearestCoordinate(geometry, point, testPolygonInterior);
Proximity2DResult result = Proximity2DOperator.Instance.GetNearestVertex(geometry, point);
Proximity2DResult[] results = Proximity2DOperator.Instance.GetNearestVertices(geometry, point, searchRadius, maxVertexCount);
序列化格式
WKT(Well-Known Text)
using OpenGIS.Esri.Geometry.Core.IO;
string wkt = WktExportOperator.ExportToWkt(geometry);
Geometry geometry = WktImportOperator.ImportFromWkt("POINT (10.5 20.7)");
WKB(Well-Known Binary)
byte[] wkb = WkbExportOperator.ExportToWkb(geometry);
byte[] wkb = WkbExportOperator.ExportToWkb(geometry, bigEndian: true);
Geometry geometry = WkbImportOperator.ImportFromWkb(wkbBytes);
GeoJSON
string geoJson = GeoJsonExportOperator.ExportToGeoJson(geometry);
Geometry geometry = GeoJsonImportOperator.ImportFromGeoJson(geoJsonString);
Esri JSON
string esriJson = EsriJsonExportOperator.Instance.Execute(geometry);
Geometry geometry = EsriJsonImportOperator.ImportFromEsriJson(esriJsonString);
System.Text.Json 集成
using OpenGIS.Esri.Geometry.Json.Converters;
var options = new JsonSerializerOptions();
options.Converters.Add(new PointJsonConverter());
string json = JsonSerializer.Serialize(point, options);
Point point = JsonSerializer.Deserialize<Point>(json, options);
空间参考
using OpenGIS.Esri.Geometry.Core.SpatialReference;
var wgs84 = SpatialReference.Wgs84();
var webMercator = SpatialReference.WebMercator();
var sr = new SpatialReference(4490);
sr.Wkid
sr.LatestWkid
sr.Wkt
var mapGeom = new MapGeometry(geometry, SpatialReference.Wgs84());
mapGeom.Geometry
mapGeom.SpatialReference
Proximity2DResult(邻近搜索结果)
Proximity2DResult result = GeometryEngine.GetNearestCoordinate(geometry, point);
result.IsEmpty
result.Coordinate
result.VertexIndex
result.Distance
result.IsRightSide
常见开发场景
场景 1:判断点是否在多边形内
var polygon = new Polygon();
polygon.AddRing(new[] {
new Point(0, 0), new Point(10, 0), new Point(10, 10),
new Point(0, 10), new Point(0, 0)
});
var point = new Point(5, 5);
bool inside = GeometryEngine.Contains(polygon, point);
场景 2:计算两个经纬度点之间的大地距离(米)
var beijing = new Point(116.4074, 39.9042);
var shanghai = new Point(121.4737, 31.2304);
double meters = GeometryEngine.GeodesicDistance(beijing, shanghai);
场景 3:创建缓冲区
var point = new Point(10, 20);
Geometry buffer = GeometryEngine.Buffer(point, 5.0);
var envelope = buffer.GetEnvelope();
场景 4:计算两个区域的交集
var env1 = new Envelope(0, 0, 10, 10);
var env2 = new Envelope(5, 5, 15, 15);
Geometry result = GeometryEngine.Intersection(env1, env2);
场景 5:合并多个几何体
var p1 = new Point(0, 0);
var p2 = new Point(10, 10);
Geometry merged = GeometryEngine.Union(p1, p2);
场景 6:GeoJSON 往返序列化
var polygon = new Polygon();
polygon.AddRing(new[] {
new Point(0, 0), new Point(1, 0), new Point(1, 1),
new Point(0, 1), new Point(0, 0)
});
string geoJson = GeometryEngine.GeometryToGeoJson(polygon);
Geometry restored = GeometryEngine.GeometryFromGeoJson(geoJson);
场景 7:从 WKT 导入几何体并做空间分析
var geom1 = GeometryEngine.GeometryFromWkt("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))");
var geom2 = GeometryEngine.GeometryFromWkt("POINT (5 5)");
bool contains = GeometryEngine.Contains(geom1, geom2);
double distance = GeometryEngine.Distance(geom1, geom2);
场景 8:获取几何体的凸包
var multiPoint = new MultiPoint(new[] {
new Point(0, 0), new Point(10, 0), new Point(5, 10),
new Point(3, 3), new Point(7, 2)
});
Geometry hull = GeometryEngine.ConvexHull(multiPoint);
场景 9:计算多边形的质心
var polygon = new Polygon();
polygon.AddRing(new[] {
new Point(0, 0), new Point(10, 0), new Point(10, 10),
new Point(0, 10), new Point(0, 0)
});
Point centroid = GeometryEngine.Centroid(polygon);
场景 10:线的简化与加密
var polyline = new Polyline();
polyline.AddPath(new[] {
new Point(0, 0), new Point(5, 0.1), new Point(10, 0),
new Point(10, 5), new Point(10, 10)
});
Geometry simplified = GeometryEngine.Simplify(polyline, 0.5);
Geometry densified = GeometryEngine.Densify(polyline, 2.0);
Geometry generalized = GeometryEngine.Generalize(polyline, 1.0);
构建与测试
dotnet build
dotnet test
dotnet run --project samples/OpenGIS.Esri.Geometry.Samples
AI 使用建议
推荐工作流
- 创建几何对象:使用
Point、Polyline、Polygon 等构造函数创建几何
- 空间运算:优先使用
GeometryEngine 静态方法(API 最简洁);需要精细控制时使用 Operator.Instance 单例模式
- 格式转换:使用
GeometryEngine.GeometryToGeoJson() / GeometryFromWkt() 等方法进行 WKT、WKB、GeoJSON、Esri JSON 互转
- 大地测量:对地理坐标的距离/面积计算,使用
GeometryEngine.GeodesicDistance() / GeodesicArea()
- 序列化集成:使用
OpenGIS.Esri.Geometry.Json.Converters 命名空间的 System.Text.Json 转换器
关键注意事项
- Polygon 环必须闭合:首尾点必须相同
- 返回类型需转换:集合运算返回
Geometry 基类,需根据实际类型做类型转换
- 大地测量基于 WGS84:
GeodesicDistance 使用 Vincenty 公式
- GeoJSON 导出规则:单路径 Polyline 导出为
LineString,多路径导出为 MultiLineString
- Operator 使用 Lazy 单例:所有 Operator 类通过
Instance 属性获取单例实例
相关技能
注意事项
- Polygon 环必须闭合:首尾点必须相同
- 浮点容差:内部使用
GeometryConstants.DefaultTolerance(1e-10)进行浮点比较,该值为内部常量,不可外部配置
- 返回类型需转换:集合运算返回
Geometry 基类,需根据实际类型做类型转换
- Operator 使用 Lazy 单例:所有 Operator 类通过
Instance 属性获取单例实例
- 集合 getter 返回只读视图:
GetRing(), GetPath() 等返回 IReadOnlyList<Point>
- 大地测量基于 WGS84:
GeodesicDistance 使用 Vincenty 公式,GeodesicArea 使用球面超量公式
- GeoJSON 导出规则:单路径 Polyline 导出为
LineString,多路径导出为 MultiLineString;Envelope 导出为 Polygon