08-空间参考系统

第八章:空间参考系统

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 之间的转换

关键要点:

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

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

posted @ 2025-12-03 16:29  我才是银古  阅读(10)  评论(0)    收藏  举报