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

推荐订阅源

博客园_首页
The GitHub Blog
The GitHub Blog
美团技术团队
Know Your Adversary
Know Your Adversary
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The Register - Security
The Register - Security
Stack Overflow Blog
Stack Overflow Blog
Attack and Defense Labs
Attack and Defense Labs
G
Google Developers Blog
I
InfoQ
博客园 - 司徒正美
T
Troy Hunt's Blog
Google DeepMind News
Google DeepMind News
J
Java Code Geeks
MongoDB | Blog
MongoDB | Blog
博客园 - 聂微东
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
S
Security Affairs
M
MIT News - Artificial intelligence
Simon Willison's Weblog
Simon Willison's Weblog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Tailwind CSS Blog
量子位
Vercel News
Vercel News
月光博客
月光博客
V
Vulnerabilities – Threatpost
N
News and Events Feed by Topic
Hugging Face - Blog
Hugging Face - Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
L
LangChain Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
L
LINUX DO - 最新话题
F
Full Disclosure
The Hacker News
The Hacker News
Hacker News: Ask HN
Hacker News: Ask HN
T
Tor Project blog
A
Arctic Wolf
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Forbes - Security
Forbes - Security
IT之家
IT之家
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Y
Y Combinator Blog
GbyAI
GbyAI
B
Blog RSS Feed
V
Visual Studio Blog
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs

博客园 - 我才是银古

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

第八章:空间参考系统

8.1 概述

空间参考系统(Spatial Reference System,SRS)定义了几何对象的坐标如何对应到地球表面的位置。理解和正确使用空间参考是 GIS 开发的基础。

8.1.1 为什么需要空间参考

地球是一个不规则的椭球体,而地图是平面的。空间参考系统解决了以下问题:

  1. 定位:确定坐标的实际位置
  2. 测量:计算准确的距离和面积
  3. 转换:在不同坐标系之间转换数据

8.1.2 SpatialReference 类

public class SpatialReference
{
    // 构造函数
    public SpatialReference();
    public SpatialReference(int wkid);
    
    // 属性
    public int? Wkid { get; set; }           // Well-Known ID
    public int? LatestWkid { get; set; }     // 最新的 WKID
    public string? Wkt { get; set; }         // WKT 定义
    
    // 工厂方法
    public static SpatialReference Wgs84();       // EPSG:4326
    public static SpatialReference WebMercator(); // EPSG:3857
}

8.2 常用坐标系

8.2.1 地理坐标系

地理坐标系使用经度和纬度来定位地球表面的位置。

WGS 84 (EPSG:4326)

全球定位系统(GPS)使用的标准坐标系。

using Esri.Geometry.Core.SpatialReference;

// 创建 WGS 84 空间参考
var wgs84 = SpatialReference.Wgs84();
Console.WriteLine($"WKID: {wgs84.Wkid}");  // 4326

// 或者手动创建
var wgs84Manual = new SpatialReference(4326);

// WGS 84 坐标范围
// 经度:-180 到 180
// 纬度:-90 到 90

特点

  • 单位:度(degrees)
  • 使用:GPS 定位、全球数据
  • 适合:数据存储、交换

8.2.2 投影坐标系

投影坐标系将地球表面投影到平面上。

Web Mercator (EPSG:3857)

Web 地图(Google Maps、OpenStreetMap、ArcGIS Online)使用的标准投影。

// 创建 Web Mercator 空间参考
var webMercator = SpatialReference.WebMercator();
Console.WriteLine($"WKID: {webMercator.Wkid}");  // 3857

// Web Mercator 坐标范围
// X:约 -20037508 到 20037508 米
// Y:约 -20037508 到 20037508 米
// 注意:纬度 85.05° 以外区域被裁剪

特点

  • 单位:米(meters)
  • 使用:Web 地图显示
  • 优点:方便瓦片切分
  • 缺点:极地区域失真严重

8.2.3 其他常用坐标系

EPSG 名称 区域 用途
4326 WGS 84 全球 GPS、数据交换
3857 Web Mercator 全球 Web 地图
4490 CGCS2000 中国 中国官方坐标系
4549 CGCS2000 / 3-degree Gauss-Kruger zone 39 中国 投影坐标
32650 WGS 84 / UTM zone 50N 东经 114°-120° 高精度测量

8.3 使用空间参考

8.3.1 创建空间参考

using Esri.Geometry.Core.SpatialReference;

// 使用工厂方法
var wgs84 = SpatialReference.Wgs84();
var webMercator = SpatialReference.WebMercator();

// 使用 WKID
var utm50n = new SpatialReference(32650);

// 使用 WKT(对于自定义或不常见的坐标系)
var customSr = new SpatialReference
{
    Wkt = @"GEOGCS[""GCS_WGS_1984"",DATUM[""D_WGS_1984"",
           SPHEROID[""WGS_1984"",6378137.0,298.257223563]],
           PRIMEM[""Greenwich"",0.0],UNIT[""Degree"",0.0174532925199433]]"
};

// 同时设置 WKID 和 LatestWkid
var sr = new SpatialReference
{
    Wkid = 102100,      // 旧的 WKID
    LatestWkid = 3857   // 最新的 WKID
};

8.3.2 MapGeometry 与空间参考

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(121.4737, 31.2304), SpatialReference.Wgs84());
bool sameSR = mapGeom.SpatialReference?.Wkid == mapGeom2.SpatialReference?.Wkid;
Console.WriteLine($"相同空间参考:{sameSR}");  // true

8.3.3 导出时包含空间参考

using Esri.Geometry.Core.IO;

// Esri JSON 支持空间参考
var point = new Point(116.4074, 39.9042);
var sr = SpatialReference.Wgs84();
string json = EsriJsonExportOperator.Instance.Execute(point, sr);
Console.WriteLine(json);
// {"x":116.4074,"y":39.9042,"spatialReference":{"wkid":4326}}

8.4 大地测量计算

8.4.1 概念

当使用地理坐标系(如 WGS 84)时,需要使用大地测量方法来计算准确的距离和面积,因为地球是椭球体而非平面。

8.4.2 大地测量距离

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

// 使用 Vincenty 公式计算两点间的大地测量距离
var beijing = new Point(116.4074, 39.9042);   // 北京
var shanghai = new Point(121.4737, 31.2304);  // 上海

// 使用 GeometryEngine
double distance = GeometryEngine.GeodesicDistance(beijing, shanghai);
Console.WriteLine($"北京到上海:{distance:N0} 米");  // 约 1,067,890 米

// 使用操作符
double distance2 = GeodesicDistanceOperator.Instance.Execute(beijing, shanghai);

// 与简单欧几里得距离比较
double euclidean = GeometryEngine.Distance(beijing, shanghai);
Console.WriteLine($"欧几里得距离:{euclidean:F4} 度");
// 注意:度不能直接作为距离单位使用!

8.4.3 Vincenty 公式

GeodesicDistanceOperator 使用 Vincenty 公式计算 WGS 84 椭球上的精确距离:

// WGS 84 椭球参数
private const double WGS84_SEMI_MAJOR_AXIS = 6378137.0;        // 长半轴(米)
private const double WGS84_SEMI_MINOR_AXIS = 6356752.314245;   // 短半轴(米)
private const double WGS84_FLATTENING = 
    (WGS84_SEMI_MAJOR_AXIS - WGS84_SEMI_MINOR_AXIS) / WGS84_SEMI_MAJOR_AXIS;

// Vincenty 迭代计算
// 精度:约 0.5 毫米
// 适用范围:全球任意两点

8.4.4 大地测量面积

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

// 创建一个区域(经纬度坐标)
var region = new Polygon();
region.AddRing(new List<Point>
{
    new Point(116.0, 39.5),
    new Point(117.0, 39.5),
    new Point(117.0, 40.5),
    new Point(116.0, 40.5),
    new Point(116.0, 39.5)
});

// 计算大地测量面积
double geodesicArea = GeometryEngine.GeodesicArea(region);
Console.WriteLine($"大地测量面积:{geodesicArea:N0} 平方米");

// 与简单面积比较
double simpleArea = GeometryEngine.Area(region);
Console.WriteLine($"简单面积:{simpleArea} 平方度");
// 注意:平方度不是有效的面积单位!

8.4.5 距离单位转换

// 常用转换系数
const double METERS_PER_KILOMETER = 1000.0;
const double METERS_PER_MILE = 1609.344;
const double METERS_PER_NAUTICAL_MILE = 1852.0;

// 转换示例
double distanceMeters = GeometryEngine.GeodesicDistance(point1, point2);
double distanceKm = distanceMeters / METERS_PER_KILOMETER;
double distanceMiles = distanceMeters / METERS_PER_MILE;
double distanceNauticalMiles = distanceMeters / METERS_PER_NAUTICAL_MILE;

Console.WriteLine($"距离:{distanceKm:F2} 公里");
Console.WriteLine($"距离:{distanceMiles:F2} 英里");
Console.WriteLine($"距离:{distanceNauticalMiles:F2} 海里");

8.5 坐标转换基础

8.5.1 概念

几何数据经常需要在不同的空间参考之间转换。常见场景:

  1. GPS 数据(WGS 84)显示在 Web 地图(Web Mercator)上
  2. 合并来自不同源的数据
  3. 与特定应用程序集成

8.5.2 当前支持

geometry-api-net 当前提供空间参考的定义和存储,完整的投影转换需要外部库(如 ProjNet、DotSpatial)。

// 存储空间参考信息
var mapGeom = new MapGeometry(geometry, SpatialReference.Wgs84());

// 检查空间参考
if (mapGeom.SpatialReference?.Wkid == 4326)
{
    // 这是 WGS 84 数据
    Console.WriteLine("数据使用 WGS 84 坐标系");
}

8.5.3 手动转换示例

// WGS 84 到 Web Mercator 的简化转换
public static Point Wgs84ToWebMercator(Point wgs84Point)
{
    const double EARTH_RADIUS = 6378137.0;
    
    double x = wgs84Point.X * Math.PI / 180.0 * EARTH_RADIUS;
    double y = Math.Log(Math.Tan((90.0 + wgs84Point.Y) * Math.PI / 360.0)) * EARTH_RADIUS;
    
    return new Point(x, y);
}

// Web Mercator 到 WGS 84 的简化转换
public static Point WebMercatorToWgs84(Point webMercatorPoint)
{
    const double EARTH_RADIUS = 6378137.0;
    
    double lon = webMercatorPoint.X / EARTH_RADIUS * 180.0 / Math.PI;
    double lat = 180.0 / Math.PI * (2.0 * Math.Atan(Math.Exp(webMercatorPoint.Y / EARTH_RADIUS)) - Math.PI / 2.0);
    
    return new Point(lon, lat);
}

// 使用
var wgs84Point = new Point(116.4074, 39.9042);  // 北京
var webMercatorPoint = Wgs84ToWebMercator(wgs84Point);
Console.WriteLine($"Web Mercator: ({webMercatorPoint.X:F2}, {webMercatorPoint.Y:F2})");
// 输出约:(12958174.31, 4852167.63)

var backToWgs84 = WebMercatorToWgs84(webMercatorPoint);
Console.WriteLine($"WGS 84: ({backToWgs84.X:F4}, {backToWgs84.Y:F4})");

8.6 实际应用

8.6.1 GPS 轨迹处理

// GPS 数据通常是 WGS 84
var gpsPoints = new List<Point>
{
    new Point(116.4000, 39.9000),
    new Point(116.4010, 39.9005),
    new Point(116.4020, 39.9010),
    new Point(116.4030, 39.9008)
};

// 计算轨迹总长度(使用大地测量距离)
double totalDistance = 0;
for (int i = 1; i < gpsPoints.Count; i++)
{
    totalDistance += GeometryEngine.GeodesicDistance(gpsPoints[i - 1], gpsPoints[i]);
}
Console.WriteLine($"轨迹总长度:{totalDistance:F2} 米");

// 创建带空间参考的轨迹
var track = new Polyline();
track.AddPath(gpsPoints);
var mapTrack = new MapGeometry(track, SpatialReference.Wgs84());

8.6.2 服务区域分析

// 服务区域(WGS 84 坐标)
var serviceCenter = new Point(116.4074, 39.9042);

// 检查客户是否在服务范围内(5 公里)
var customers = new List<Point>
{
    new Point(116.42, 39.91),
    new Point(116.45, 39.95),
    new Point(116.50, 40.00)
};

const double serviceRadius = 5000; // 5 公里

foreach (var customer in customers)
{
    double distance = GeometryEngine.GeodesicDistance(serviceCenter, customer);
    bool inService = distance <= serviceRadius;
    Console.WriteLine($"客户 ({customer.X}, {customer.Y}): {distance:F0} 米, 服务范围内: {inService}");
}

8.6.3 地理围栏

// 定义地理围栏(WGS 84 坐标)
var geofence = new Polygon();
geofence.AddRing(new List<Point>
{
    new Point(116.40, 39.90),
    new Point(116.45, 39.90),
    new Point(116.45, 39.95),
    new Point(116.40, 39.95),
    new Point(116.40, 39.90)
});

// 附加空间参考信息
var geofenceWithSR = new MapGeometry(geofence, SpatialReference.Wgs84());

// 检查设备位置
var deviceLocation = new Point(116.42, 39.92);

bool insideGeofence = GeometryEngine.Contains(geofence, deviceLocation);
if (insideGeofence)
{
    Console.WriteLine("设备在围栏内");
}
else
{
    Console.WriteLine("设备已离开围栏");
    // 触发告警...
}

8.7 最佳实践

8.7.1 空间参考管理

// 1. 始终记录数据的空间参考
public class GeoData
{
    public Geometry Geometry { get; set; }
    public int Srid { get; set; }  // 空间参考 ID
}

// 2. 使用 MapGeometry 捆绑几何和空间参考
var data = new MapGeometry(geometry, SpatialReference.Wgs84());

// 3. 在处理前验证空间参考
if (data1.SpatialReference?.Wkid != data2.SpatialReference?.Wkid)
{
    throw new InvalidOperationException("空间参考不匹配");
}

8.7.2 距离和面积计算

// ✅ 对于地理坐标,使用大地测量方法
double correctDistance = GeometryEngine.GeodesicDistance(point1, point2);  // 米

// ❌ 不要对地理坐标使用欧几里得距离
double wrongDistance = GeometryEngine.Distance(point1, point2);  // 度!

// ✅ 对于投影坐标(米),可以使用欧几里得距离
// 假设 point1 和 point2 是 Web Mercator 坐标
double distanceInMeters = GeometryEngine.Distance(point1, point2);

8.7.3 坐标系统选择

场景 推荐坐标系 原因
数据存储 WGS 84 (4326) 通用标准
Web 显示 Web Mercator (3857) 瓦片兼容
面积计算 等面积投影 保持面积
距离计算 等距投影或大地测量 保持距离
本地测量 UTM 或本地投影 高精度

8.8 小结

本章介绍了 geometry-api-net 的空间参考系统支持:

  1. SpatialReference 类:使用 WKID 和 WKT 定义空间参考
  2. 常用坐标系:WGS 84 和 Web Mercator
  3. MapGeometry:将几何对象与空间参考捆绑
  4. 大地测量计算:准确的距离和面积计算
  5. 坐标转换基础:WGS 84 与 Web Mercator 之间的转换

关键要点:

  • 始终记录数据的空间参考
  • 对地理坐标使用大地测量方法计算距离和面积
  • 合并数据前确保空间参考一致

在下一章中,我们将学习邻近分析与位置服务,了解如何查找最近的几何要素。