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

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 最新话题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Forbes - Security
Forbes - Security
博客园 - 司徒正美
Hugging Face - Blog
Hugging Face - Blog
W
WeLiveSecurity
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
N
News and Events Feed by Topic
V
V2EX
Stack Overflow Blog
Stack Overflow Blog
Engineering at Meta
Engineering at Meta
PCI Perspectives
PCI Perspectives
Martin Fowler
Martin Fowler
T
The Exploit Database - CXSecurity.com
F
Full Disclosure
WordPress大学
WordPress大学
S
Security Affairs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
SegmentFault 最新的问题
P
Privacy International News Feed
IT之家
IT之家
M
MIT News - Artificial intelligence
G
GRAHAM CLULEY
Hacker News: Ask HN
Hacker News: Ask HN
D
DataBreaches.Net
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
Check Point Blog
美团技术团队
Security Latest
Security Latest
Cyberwarzone
Cyberwarzone
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
H
Help Net Security
宝玉的分享
宝玉的分享
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
The Cloudflare Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AI
AI
I
InfoQ
N
News | PayPal Newsroom
TaoSecurity Blog
TaoSecurity Blog

博客园 - 我才是银古

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

第七章:数据格式导入导出

7.1 概述

geometry-api-net 支持多种标准的几何数据格式,使您能够轻松地与其他 GIS 系统、数据库和 Web 服务进行数据交换。

7.1.1 支持的格式

格式 导入 导出 用途
WKT 人类可读的文本格式
WKB 紧凑的二进制格式
GeoJSON Web 应用和 API
Esri JSON ArcGIS 产品集成

7.1.2 格式选择指南

场景 推荐格式 原因
调试和日志 WKT 人类可读
数据库存储 WKB 紧凑高效
Web API GeoJSON 标准通用
ArcGIS 集成 Esri JSON 原生支持
跨平台交换 WKB 或 GeoJSON 广泛支持

7.2 WKT(Well-Known Text)

7.2.1 概念

WKT 是 OGC 定义的文本标记语言,用于表示几何对象。它是人类可读的,便于调试和日志记录。

语法示例

POINT (30 10)
LINESTRING (30 10, 10 30, 40 40)
POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))
MULTIPOINT ((10 40), (40 30), (20 20), (30 10))
MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))
MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))

7.2.2 导出 API

public static class WktExportOperator
{
    public static string ExportToWkt(Geometry geometry);
}

7.2.3 导入 API

public static class WktImportOperator
{
    public static Geometry ImportFromWkt(string wkt);
}

7.2.4 使用示例

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

// === 导出为 WKT ===

// 点
var point = new Point(10.5, 20.7);
string pointWkt = WktExportOperator.ExportToWkt(point);
Console.WriteLine(pointWkt);  // POINT (10.5 20.7)

// 三维点
var point3D = new Point(10, 20, 30);
string point3DWkt = WktExportOperator.ExportToWkt(point3D);
Console.WriteLine(point3DWkt);  // POINT Z (10 20 30)

// 折线
var polyline = new Polyline();
polyline.AddPath(new List<Point>
{
    new Point(0, 0),
    new Point(10, 10),
    new Point(20, 0)
});
string lineWkt = WktExportOperator.ExportToWkt(polyline);
Console.WriteLine(lineWkt);  // LINESTRING (0 0, 10 10, 20 0)

// 多路径折线
var multiLine = new Polyline();
multiLine.AddPath(new[] { new Point(0, 0), new Point(10, 10) });
multiLine.AddPath(new[] { new Point(20, 20), new Point(30, 30) });
string multiLineWkt = WktExportOperator.ExportToWkt(multiLine);
Console.WriteLine(multiLineWkt);  // MULTILINESTRING ((0 0, 10 10), (20 20, 30 30))

// 多边形
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)
});
string polyWkt = WktExportOperator.ExportToWkt(polygon);
Console.WriteLine(polyWkt);  // POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))

// 多点
var multiPoint = new MultiPoint();
multiPoint.Add(new Point(10, 20));
multiPoint.Add(new Point(30, 40));
string mpWkt = WktExportOperator.ExportToWkt(multiPoint);
Console.WriteLine(mpWkt);  // MULTIPOINT (10 20, 30 40)

// 包络矩形(导出为多边形)
var envelope = new Envelope(0, 0, 100, 100);
string envWkt = WktExportOperator.ExportToWkt(envelope);
Console.WriteLine(envWkt);  // POLYGON ((0 0, 100 0, 100 100, 0 100, 0 0))

// 空几何
var emptyPoint = new Point();
string emptyWkt = WktExportOperator.ExportToWkt(emptyPoint);
Console.WriteLine(emptyWkt);  // POINT EMPTY

// === 使用 GeometryEngine ===
string wkt = GeometryEngine.GeometryToWkt(polygon);

7.2.5 从 WKT 导入

// 导入点
var point = WktImportOperator.ImportFromWkt("POINT (10.5 20.7)");
Console.WriteLine($"类型:{point.Type}");  // Point

// 导入三维点
var point3D = WktImportOperator.ImportFromWkt("POINT Z (10 20 30)");
if (point3D is Point p3d)
{
    Console.WriteLine($"Z 值:{p3d.Z}");  // 30
}

// 导入折线
var line = WktImportOperator.ImportFromWkt("LINESTRING (0 0, 10 10, 20 0)");
if (line is Polyline polyline)
{
    Console.WriteLine($"点数:{polyline.GetPath(0).Count}");  // 3
}

// 导入多边形
var polygon = WktImportOperator.ImportFromWkt(
    "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))");
if (polygon is Polygon poly)
{
    Console.WriteLine($"面积:{poly.Area}");  // 100
}

// 导入带孔洞的多边形
var polyWithHole = WktImportOperator.ImportFromWkt(
    "POLYGON ((0 0, 100 0, 100 100, 0 100, 0 0), (25 25, 75 25, 75 75, 25 75, 25 25))");
if (polyWithHole is Polygon pwh)
{
    Console.WriteLine($"环数:{pwh.RingCount}");  // 2
}

// 导入多点
var multiPoint = WktImportOperator.ImportFromWkt("MULTIPOINT (10 20, 30 40)");

// 导入多折线
var multiLine = WktImportOperator.ImportFromWkt(
    "MULTILINESTRING ((0 0, 10 10), (20 20, 30 30))");

// 使用 GeometryEngine
var geom = GeometryEngine.GeometryFromWkt("POINT (5 5)");

7.2.6 WKT 格式规范

// 点
POINT (x y)
POINT Z (x y z)
POINT M (x y m)
POINT ZM (x y z m)
POINT EMPTY

// 多点
MULTIPOINT ((x1 y1), (x2 y2), ...)
MULTIPOINT (x1 y1, x2 y2, ...)  // 简化语法

// 线串
LINESTRING (x1 y1, x2 y2, ...)
LINESTRING EMPTY

// 多线串
MULTILINESTRING ((x1 y1, x2 y2), (x3 y3, x4 y4))

// 多边形
POLYGON ((外环), (孔洞1), (孔洞2), ...)

// 多多边形
MULTIPOLYGON (((外环1)), ((外环2), (孔洞)))

7.3 WKB(Well-Known Binary)

7.3.1 概念

WKB 是 WKT 的二进制表示形式,更紧凑,适合数据库存储和网络传输。

二进制结构

字节序 (1 字节): 0 = 大端, 1 = 小端
几何类型 (4 字节): 1=Point, 2=LineString, 3=Polygon, 4=MultiPoint, 5=MultiLineString, 6=MultiPolygon
坐标数据 (8 字节每个双精度值)

7.3.2 导出 API

public static class WkbExportOperator
{
    public static byte[] ExportToWkb(Geometry geometry, bool bigEndian = false);
}

7.3.3 导入 API

public static class WkbImportOperator
{
    public static Geometry ImportFromWkb(byte[] wkb);
}

7.3.4 使用示例

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

// === 导出为 WKB ===

var point = new Point(10.5, 20.7);

// 默认小端字节序
byte[] wkb = WkbExportOperator.ExportToWkb(point);
Console.WriteLine($"WKB 长度:{wkb.Length} 字节");  // 21 字节

// 大端字节序(用于某些系统)
byte[] wkbBigEndian = WkbExportOperator.ExportToWkb(point, bigEndian: true);

// 使用 GeometryEngine
byte[] wkb2 = GeometryEngine.GeometryToWkb(point);

// 导出多边形
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)
});
byte[] polyWkb = WkbExportOperator.ExportToWkb(polygon);
Console.WriteLine($"多边形 WKB 长度:{polyWkb.Length} 字节");

// === 从 WKB 导入 ===

var imported = WkbImportOperator.ImportFromWkb(wkb);
if (imported is Point p)
{
    Console.WriteLine($"导入的点:({p.X}, {p.Y})");  // (10.5, 20.7)
}

// 使用 GeometryEngine
var geom = GeometryEngine.GeometryFromWkb(polyWkb);
Console.WriteLine($"导入类型:{geom.Type}");  // Polygon

// 往返测试
var original = new Point(123.456, 789.012);
byte[] exported = WkbExportOperator.ExportToWkb(original);
var roundTripped = WkbImportOperator.ImportFromWkb(exported);
Console.WriteLine($"往返成功:{GeometryEngine.Equals(original, roundTripped)}");  // true

7.3.5 WKB 字节结构

// Point WKB 结构 (21 字节)
// [0]     字节序: 0x01 (小端) 或 0x00 (大端)
// [1-4]   类型: 0x01000000 (Point)
// [5-12]  X 坐标 (double)
// [13-20] Y 坐标 (double)

// Polygon WKB 结构
// [0]     字节序
// [1-4]   类型: 0x03000000 (Polygon)
// [5-8]   环数量
// 对于每个环:
//   [n-n+3]   点数量
//   [...]     点坐标 (每个点 16 字节: X + Y)

7.3.6 WKB 与数据库

// 存储到数据库
byte[] wkb = GeometryEngine.GeometryToWkb(geometry);
// INSERT INTO spatial_table (geom) VALUES (@wkb)

// 从数据库读取
// byte[] wkb = reader["geom"] as byte[];
var geometry = GeometryEngine.GeometryFromWkb(wkb);

// PostGIS 示例 SQL
// INSERT INTO my_table (geom) VALUES (ST_GeomFromWKB(@wkb, 4326))
// SELECT ST_AsBinary(geom) FROM my_table

7.4 GeoJSON

7.4.1 概念

GeoJSON 是基于 JSON 的地理数据格式,广泛用于 Web GIS 应用。它是 IETF RFC 7946 标准。

结构示例

{
  "type": "Point",
  "coordinates": [102.0, 0.5]
}

{
  "type": "Polygon",
  "coordinates": [
    [[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]
  ]
}

7.4.2 导出 API

public class GeoJsonExportOperator : IGeometryOperator<string>
{
    public static GeoJsonExportOperator Instance { get; }
    
    public static string ExportToGeoJson(Geometry geometry);
}

7.4.3 导入 API

public static class GeoJsonImportOperator
{
    public static Geometry ImportFromGeoJson(string geoJson);
}

7.4.4 使用示例

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

// === 导出为 GeoJSON ===

// 点
var point = new Point(10.5, 20.3, 30.7);
string pointJson = GeoJsonExportOperator.ExportToGeoJson(point);
Console.WriteLine(pointJson);
// {"type":"Point","coordinates":[10.5,20.3,30.7]}

// 多点
var multiPoint = new MultiPoint();
multiPoint.Add(new Point(10, 20));
multiPoint.Add(new Point(30, 40));
string mpJson = GeoJsonExportOperator.ExportToGeoJson(multiPoint);
Console.WriteLine(mpJson);
// {"type":"MultiPoint","coordinates":[[10,20],[30,40]]}

// 折线(单路径 = LineString)
var line = new Polyline();
line.AddPath(new[] { new Point(0, 0), new Point(10, 10) });
string lineJson = GeoJsonExportOperator.ExportToGeoJson(line);
Console.WriteLine(lineJson);
// {"type":"LineString","coordinates":[[0,0],[10,10]]}

// 折线(多路径 = MultiLineString)
var multiLine = new Polyline();
multiLine.AddPath(new[] { new Point(0, 0), new Point(10, 10) });
multiLine.AddPath(new[] { new Point(20, 20), new Point(30, 30) });
string multiLineJson = GeoJsonExportOperator.ExportToGeoJson(multiLine);
Console.WriteLine(multiLineJson);
// {"type":"MultiLineString","coordinates":[[[0,0],[10,10]],[[20,20],[30,30]]]}

// 多边形
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)
});
string polyJson = GeoJsonExportOperator.ExportToGeoJson(polygon);
Console.WriteLine(polyJson);
// {"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}

// 包络矩形(作为多边形导出)
var envelope = new Envelope(0, 0, 100, 100);
string envJson = GeoJsonExportOperator.ExportToGeoJson(envelope);

// 使用 GeometryEngine
string json = GeometryEngine.GeometryToGeoJson(polygon);

// === 从 GeoJSON 导入 ===

// 导入点
var point2 = GeoJsonImportOperator.ImportFromGeoJson(
    "{\"type\":\"Point\",\"coordinates\":[10.5,20.3]}");
if (point2 is Point p)
{
    Console.WriteLine($"导入的点:({p.X}, {p.Y})");
}

// 导入多边形
var poly2 = GeoJsonImportOperator.ImportFromGeoJson(
    "{\"type\":\"Polygon\",\"coordinates\":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}");
if (poly2 is Polygon polygon2)
{
    Console.WriteLine($"导入的多边形面积:{polygon2.Area}");
}

// 使用 GeometryEngine
var geom = GeometryEngine.GeometryFromGeoJson("{\"type\":\"Point\",\"coordinates\":[5,5]}");

7.4.5 GeoJSON 格式规范

// Point
{"type": "Point", "coordinates": [x, y]}
{"type": "Point", "coordinates": [x, y, z]}

// MultiPoint
{"type": "MultiPoint", "coordinates": [[x1, y1], [x2, y2]]}

// LineString
{"type": "LineString", "coordinates": [[x1, y1], [x2, y2], [x3, y3]]}

// MultiLineString
{"type": "MultiLineString", "coordinates": [[[x1, y1], [x2, y2]], [[x3, y3], [x4, y4]]]}

// Polygon (外环逆时针,孔洞顺时针)
{"type": "Polygon", "coordinates": [[[x1, y1], [x2, y2], [x3, y3], [x1, y1]]]}

// Polygon with hole
{"type": "Polygon", "coordinates": [
  [[外环坐标]],
  [[孔洞坐标]]
]}

// MultiPolygon
{"type": "MultiPolygon", "coordinates": [[[[多边形1坐标]]], [[[多边形2坐标]]]]}

// GeometryCollection
{"type": "GeometryCollection", "geometries": [
  {"type": "Point", "coordinates": [x, y]},
  {"type": "LineString", "coordinates": [[x1, y1], [x2, y2]]}
]}

// Feature (带属性的几何)
{
  "type": "Feature",
  "geometry": {"type": "Point", "coordinates": [x, y]},
  "properties": {"name": "示例点", "value": 123}
}

// FeatureCollection
{
  "type": "FeatureCollection",
  "features": [
    {"type": "Feature", "geometry": {...}, "properties": {...}},
    {"type": "Feature", "geometry": {...}, "properties": {...}}
  ]
}

7.4.6 Web 应用集成

// 在 ASP.NET Core 控制器中使用
[HttpGet("geometry/{id}")]
public IActionResult GetGeometry(int id)
{
    var geometry = _repository.GetById(id);
    string geoJson = GeometryEngine.GeometryToGeoJson(geometry);
    
    return Content(geoJson, "application/geo+json");
}

[HttpPost("geometry")]
public IActionResult CreateGeometry([FromBody] string geoJson)
{
    var geometry = GeometryEngine.GeometryFromGeoJson(geoJson);
    _repository.Add(geometry);
    
    return Ok();
}

7.5 Esri JSON

7.5.1 概念

Esri JSON 是 Esri 定义的专有 JSON 格式,用于 ArcGIS 产品系列。与 GeoJSON 相比,它包含更多 Esri 特有的属性。

结构示例

// Point
{"x": 10.5, "y": 20.3}
{"x": 10.5, "y": 20.3, "z": 30.7}

// Polyline
{
  "paths": [[[0, 0], [10, 10], [20, 0]]]
}

// Polygon
{
  "rings": [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]]
}

// 带空间参考
{
  "x": 10.5,
  "y": 20.3,
  "spatialReference": {"wkid": 4326}
}

7.5.2 导出 API

public class EsriJsonExportOperator : IGeometryOperator<string>
{
    public static EsriJsonExportOperator Instance { get; }
    
    public string Execute(Geometry geometry, 
                         SpatialReference? spatialReference = null);
}

7.5.3 导入 API

public static class EsriJsonImportOperator
{
    public static Geometry ImportFromEsriJson(string esriJson);
}

7.5.4 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;
using Esri.Geometry.Core.IO;
using Esri.Geometry.Core.SpatialReference;

// === 导出为 Esri JSON ===

// 点
var point = new Point(10.5, 20.3);
string pointJson = EsriJsonExportOperator.Instance.Execute(point);
Console.WriteLine(pointJson);
// {"x":10.5,"y":20.3}

// 三维点
var point3D = new Point(10.5, 20.3, 30.7);
string point3DJson = EsriJsonExportOperator.Instance.Execute(point3D);
Console.WriteLine(point3DJson);
// {"x":10.5,"y":20.3,"z":30.7}

// 带空间参考
var sr = SpatialReference.Wgs84();
string pointWithSR = EsriJsonExportOperator.Instance.Execute(point, sr);
Console.WriteLine(pointWithSR);
// {"x":10.5,"y":20.3,"spatialReference":{"wkid":4326}}

// 折线
var polyline = new Polyline();
polyline.AddPath(new[] { new Point(0, 0), new Point(10, 10), new Point(20, 0) });
string lineJson = EsriJsonExportOperator.Instance.Execute(polyline);
Console.WriteLine(lineJson);
// {"paths":[[[0,0],[10,10],[20,0]]]}

// 多边形
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)
});
string polyJson = EsriJsonExportOperator.Instance.Execute(polygon);
Console.WriteLine(polyJson);
// {"rings":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}

// 包络矩形
var envelope = new Envelope(0, 0, 100, 100);
string envJson = EsriJsonExportOperator.Instance.Execute(envelope);
Console.WriteLine(envJson);
// {"xmin":0,"ymin":0,"xmax":100,"ymax":100}

// 使用 GeometryEngine
string json = GeometryEngine.GeometryToEsriJson(polygon);

// === 从 Esri JSON 导入 ===

// 导入点
var point2 = EsriJsonImportOperator.ImportFromEsriJson("{\"x\":10.5,\"y\":20.3}");

// 导入折线
var line = EsriJsonImportOperator.ImportFromEsriJson(
    "{\"paths\":[[[0,0],[10,10],[20,0]]]}");

// 导入多边形
var poly = EsriJsonImportOperator.ImportFromEsriJson(
    "{\"rings\":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}");

// 导入包络矩形
var env = EsriJsonImportOperator.ImportFromEsriJson(
    "{\"xmin\":0,\"ymin\":0,\"xmax\":100,\"ymax\":100}");

// 使用 GeometryEngine
var geom = GeometryEngine.GeometryFromEsriJson("{\"x\":5,\"y\":5}");

7.5.5 Esri JSON 格式规范

// Point
{"x": number, "y": number}
{"x": number, "y": number, "z": number}
{"x": number, "y": number, "m": number}
{"x": number, "y": number, "z": number, "m": number}

// Multipoint
{
  "points": [[x1, y1], [x2, y2], ...]
}

// Polyline
{
  "paths": [
    [[x1, y1], [x2, y2], ...],  // 路径 1
    [[x3, y3], [x4, y4], ...]   // 路径 2
  ]
}

// Polygon
{
  "rings": [
    [[x1, y1], [x2, y2], ...],  // 外环
    [[x3, y3], [x4, y4], ...]   // 孔洞
  ]
}

// Envelope
{
  "xmin": number,
  "ymin": number,
  "xmax": number,
  "ymax": number
}

// 带空间参考
{
  "x": number,
  "y": number,
  "spatialReference": {
    "wkid": 4326
  }
}

7.5.6 与 ArcGIS 服务集成

// 发送到 ArcGIS REST 服务
async Task<string> QueryFeatureService(Polygon queryGeometry)
{
    string esriJson = GeometryEngine.GeometryToEsriJson(queryGeometry);
    
    using var client = new HttpClient();
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("geometry", esriJson),
        new KeyValuePair<string, string>("geometryType", "esriGeometryPolygon"),
        new KeyValuePair<string, string>("inSR", "4326"),
        new KeyValuePair<string, string>("f", "json")
    });
    
    var response = await client.PostAsync(
        "https://services.arcgis.com/.../query",
        content);
    
    return await response.Content.ReadAsStringAsync();
}

7.6 格式转换

7.6.1 格式间转换

using Esri.Geometry.Core;
using Esri.Geometry.Core.IO;

// WKT → GeoJSON
string wkt = "POINT (10 20)";
var geometry = WktImportOperator.ImportFromWkt(wkt);
string geoJson = GeoJsonExportOperator.ExportToGeoJson(geometry);

// GeoJSON → WKB
string json = "{\"type\":\"Point\",\"coordinates\":[10,20]}";
var geom = GeoJsonImportOperator.ImportFromGeoJson(json);
byte[] wkb = WkbExportOperator.ExportToWkb(geom);

// WKB → Esri JSON
byte[] data = { /* WKB 数据 */ };
var geometry2 = WkbImportOperator.ImportFromWkb(data);
string esriJson = EsriJsonExportOperator.Instance.Execute(geometry2);

// 批量转换
List<string> wktList = new() { "POINT (1 1)", "POINT (2 2)", "POINT (3 3)" };
List<string> jsonList = wktList
    .Select(wkt => WktImportOperator.ImportFromWkt(wkt))
    .Select(geom => GeoJsonExportOperator.ExportToGeoJson(geom))
    .ToList();

7.6.2 通用转换器

public static class GeometryFormatConverter
{
    public enum Format { WKT, WKB, GeoJSON, EsriJSON }
    
    public static string Convert(string input, Format from, Format to)
    {
        // 解析输入
        Geometry geometry = from switch
        {
            Format.WKT => WktImportOperator.ImportFromWkt(input),
            Format.GeoJSON => GeoJsonImportOperator.ImportFromGeoJson(input),
            Format.EsriJSON => EsriJsonImportOperator.ImportFromEsriJson(input),
            _ => throw new ArgumentException("Unsupported input format")
        };
        
        // 生成输出
        return to switch
        {
            Format.WKT => WktExportOperator.ExportToWkt(geometry),
            Format.GeoJSON => GeoJsonExportOperator.ExportToGeoJson(geometry),
            Format.EsriJSON => EsriJsonExportOperator.Instance.Execute(geometry),
            _ => throw new ArgumentException("Unsupported output format")
        };
    }
    
    public static byte[] ConvertToWkb(string input, Format from)
    {
        Geometry geometry = from switch
        {
            Format.WKT => WktImportOperator.ImportFromWkt(input),
            Format.GeoJSON => GeoJsonImportOperator.ImportFromGeoJson(input),
            Format.EsriJSON => EsriJsonImportOperator.ImportFromEsriJson(input),
            _ => throw new ArgumentException("Unsupported format")
        };
        
        return WkbExportOperator.ExportToWkb(geometry);
    }
}

// 使用
string result = GeometryFormatConverter.Convert(
    "POINT (10 20)", 
    GeometryFormatConverter.Format.WKT, 
    GeometryFormatConverter.Format.GeoJSON
);

7.7 最佳实践

7.7.1 格式选择

// 调试时使用 WKT
Console.WriteLine($"几何对象:{GeometryEngine.GeometryToWkt(geometry)}");

// 存储到数据库使用 WKB
byte[] data = GeometryEngine.GeometryToWkb(geometry);

// Web API 返回 GeoJSON
return Ok(GeometryEngine.GeometryToGeoJson(geometry));

// 与 ArcGIS 交互使用 Esri JSON
string esriJson = GeometryEngine.GeometryToEsriJson(geometry);

7.7.2 错误处理

try
{
    var geometry = WktImportOperator.ImportFromWkt(wkt);
}
catch (FormatException ex)
{
    Console.WriteLine($"WKT 格式错误:{ex.Message}");
}
catch (ArgumentException ex)
{
    Console.WriteLine($"参数错误:{ex.Message}");
}

// 安全解析
public static Geometry? TryParseWkt(string wkt)
{
    try
    {
        return WktImportOperator.ImportFromWkt(wkt);
    }
    catch
    {
        return null;
    }
}

7.7.3 性能优化

// 批量处理时使用 WKB(更紧凑)
var geometries = new List<Geometry>();
var wkbData = geometries
    .Select(g => WkbExportOperator.ExportToWkb(g))
    .ToList();

// 避免频繁序列化/反序列化
// ❌ 不好
foreach (var item in items)
{
    string json = GeometryEngine.GeometryToGeoJson(geometry);
    ProcessJson(json);
}

// ✅ 好
string json = GeometryEngine.GeometryToGeoJson(geometry);
foreach (var item in items)
{
    ProcessWithGeometry(geometry);
}

7.8 小结

本章详细介绍了 geometry-api-net 支持的四种数据格式:

  1. WKT:人类可读的文本格式,适合调试和日志
  2. WKB:紧凑的二进制格式,适合数据库存储
  3. GeoJSON:JSON 格式,适合 Web 应用
  4. Esri JSON:Esri 专有格式,适合 ArcGIS 集成

每种格式都有其最佳使用场景,选择合适的格式可以提高开发效率和系统性能。

在下一章中,我们将学习空间参考系统,了解如何处理不同坐标系之间的转换。