第08章:几何输出函数
8.1 几何输出函数概述
PostGIS 提供了多种几何输出函数,用于将几何对象转换为各种格式的文本或二进制表示。这些函数对于数据交换、可视化和与其他系统集成至关重要。
8.1.1 输出格式分类
┌─────────────────────────────────────────────────────────────┐
│ 几何输出格式分类 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 文本格式 │
│ ├── WKT (Well-Known Text) - OGC 标准文本格式 │
│ ├── EWKT (Extended WKT) - PostGIS 扩展文本格式 │
│ ├── GeoJSON - JSON 格式 │
│ ├── GML (Geography Markup) - XML 格式 │
│ ├── KML (Keyhole Markup) - Google Earth 格式 │
│ └── SVG (Scalable Vector) - 可缩放矢量图形 │
│ │
│ 二进制格式 │
│ ├── WKB (Well-Known Binary) - OGC 标准二进制格式 │
│ ├── EWKB (Extended WKB) - PostGIS 扩展二进制格式 │
│ ├── TWKB (Tiny WKB) - 紧凑二进制格式 │
│ └── MVT (Mapbox Vector Tile) - 矢量瓦片格式 │
│ │
│ 其他格式 │
│ ├── Encoded Polyline - Google 编码折线 │
│ ├── GeoHash - 空间哈希 │
│ └── LatLonText - 经纬度文本 │
│ │
└─────────────────────────────────────────────────────────────┘
8.2 WKT 输出
8.2.1 ST_AsText (WKT)
-- 基本用法
SELECT ST_AsText(ST_MakePoint(116.4, 39.9));
-- POINT(116.4 39.9)SELECT ST_AsText(ST_MakeLine(ST_MakePoint(0, 0), ST_MakePoint(1, 1)));
-- LINESTRING(0 0,1 1)SELECT ST_AsText(ST_MakeEnvelope(0, 0, 1, 1));
-- POLYGON((0 0,0 1,1 1,1 0,0 0))-- 3D 几何
SELECT ST_AsText(ST_MakePoint(116.4, 39.9, 45.5));
-- POINT Z (116.4 39.9 45.5)-- 带 M 值
SELECT ST_AsText(ST_MakePointM(116.4, 39.9, 100));
-- POINT M (116.4 39.9 100)-- 4D 几何
SELECT ST_AsText(ST_GeomFromText('POINT ZM(116.4 39.9 45.5 100)'));
-- POINT ZM (116.4 39.9 45.5 100)-- 控制精度(小数位数)
SELECT ST_AsText(ST_MakePoint(116.40740001, 39.90420001), 4);
-- POINT(116.4074 39.9042)SELECT ST_AsText(ST_MakePoint(116.40740001, 39.90420001), 2);
-- POINT(116.41 39.9)-- 多几何类型
SELECT ST_AsText(ST_Collect(ST_MakePoint(0, 0),ST_MakePoint(1, 1)
));
-- MULTIPOINT((0 0),(1 1))
8.2.2 ST_AsEWKT (Extended WKT)
-- EWKT 包含 SRID 信息
SELECT ST_AsEWKT(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326));
-- SRID=4326;POINT(116.4 39.9)SELECT ST_AsEWKT(ST_SetSRID(ST_MakePoint(116.4, 39.9, 45.5), 4326));
-- SRID=4326;POINT Z (116.4 39.9 45.5)-- 控制精度
SELECT ST_AsEWKT(ST_SetSRID(ST_MakePoint(116.40740001, 39.90420001), 4326), 4);
-- SRID=4326;POINT(116.4074 39.9042)-- 与 ST_AsText 的区别
SELECT ST_AsText(geom) AS wkt,ST_AsEWKT(geom) AS ewkt
FROM poi LIMIT 1;
-- wkt: POINT(116.4 39.9)
-- ewkt: SRID=4326;POINT(116.4 39.9)
8.3 WKB 输出
8.3.1 ST_AsBinary (WKB)
-- 获取 WKB(十六进制显示)
SELECT ST_AsBinary(ST_MakePoint(116.4, 39.9));
-- 返回 bytea 类型-- 转换为十六进制字符串
SELECT encode(ST_AsBinary(ST_MakePoint(116.4, 39.9)), 'hex');-- 指定字节序
-- 'NDR': 小端序(默认)
-- 'XDR': 大端序
SELECT encode(ST_AsBinary(ST_MakePoint(0, 0), 'NDR'), 'hex');
SELECT encode(ST_AsBinary(ST_MakePoint(0, 0), 'XDR'), 'hex');-- WKB 不包含 SRID
SELECT ST_SRID(ST_GeomFromWKB(ST_AsBinary(ST_SetSRID(ST_MakePoint(0, 0), 4326))));
-- 0(SRID 丢失)
8.3.2 ST_AsEWKB (Extended WKB)
-- EWKB 包含 SRID
SELECT encode(ST_AsEWKB(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)), 'hex');-- 保留 SRID
SELECT ST_SRID(ST_GeomFromEWKB(ST_AsEWKB(ST_SetSRID(ST_MakePoint(0, 0), 4326))));
-- 4326-- 指定字节序
SELECT encode(ST_AsEWKB(ST_SetSRID(ST_MakePoint(0, 0), 4326), 'XDR'), 'hex');
8.3.3 ST_AsHEXEWKB
-- 直接返回十六进制字符串
SELECT ST_AsHEXEWKB(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326));
-- 0101000020E610000066666666662D5D4066666666E6F34340-- 等价于
SELECT encode(ST_AsEWKB(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)), 'hex');
8.3.4 ST_AsTWKB (Tiny WKB)
-- TWKB 是一种紧凑的二进制格式
-- 适合网络传输和存储-- 基本用法
SELECT encode(ST_AsTWKB(ST_MakePoint(116.4, 39.9)), 'hex');-- 控制精度(XY 精度)
SELECT encode(ST_AsTWKB(ST_MakePoint(116.4074, 39.9042), 4), 'hex'); -- 4位小数
SELECT encode(ST_AsTWKB(ST_MakePoint(116.4074, 39.9042), 2), 'hex'); -- 2位小数-- 完整参数
-- ST_AsTWKB(geom, precision_xy, precision_z, precision_m, include_sizes, include_bboxes)
SELECT encode(ST_AsTWKB(ST_SetSRID(ST_MakePoint(116.4074, 39.9042, 45.5), 4326),6, -- XY 精度(6位小数)2, -- Z 精度0, -- M 精度true, -- 包含大小信息true -- 包含边界框
), 'hex');-- 比较不同格式的大小
SELECT pg_column_size(ST_AsBinary(geom)) AS wkb_size,pg_column_size(ST_AsEWKB(geom)) AS ewkb_size,pg_column_size(ST_AsTWKB(geom, 6)) AS twkb_size
FROM complex_polygons
LIMIT 5;-- TWKB 聚合
SELECT encode(ST_AsTWKB(array_agg(geom),ARRAY[id]::bigint[], -- ID 数组6
), 'hex')
FROM poi
WHERE category = '餐饮';
8.4 GeoJSON 输出
8.4.1 ST_AsGeoJSON
-- 基本用法
SELECT ST_AsGeoJSON(ST_MakePoint(116.4, 39.9));
-- {"type":"Point","coordinates":[116.4,39.9]}SELECT ST_AsGeoJSON(ST_MakeLine(ST_MakePoint(0, 0), ST_MakePoint(1, 1)));
-- {"type":"LineString","coordinates":[[0,0],[1,1]]}SELECT ST_AsGeoJSON(ST_MakeEnvelope(0, 0, 1, 1));
-- {"type":"Polygon","coordinates":[[[0,0],[0,1],[1,1],[1,0],[0,0]]]}-- 控制精度
SELECT ST_AsGeoJSON(ST_MakePoint(116.40740001, 39.90420001), 4);
-- {"type":"Point","coordinates":[116.4074,39.9042]}-- 包含 CRS 信息
SELECT ST_AsGeoJSON(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326), 15, 2);
-- {"type":"Point","crs":{"type":"name","properties":{"name":"EPSG:4326"}},"coordinates":[116.4,39.9]}-- 参数说明
-- ST_AsGeoJSON(geom, maxdecimaldigits, options)
-- options 位掩码:
-- 0 = 无选项
-- 1 = 包含边界框
-- 2 = 使用短 CRS(EPSG:xxxx)
-- 4 = 使用长 CRS(urn:ogc:def:crs:EPSG::xxxx)
-- 8 = GeoJSON 规范严格模式-- 包含边界框
SELECT ST_AsGeoJSON(ST_MakeEnvelope(0, 0, 1, 1), 15, 1);
-- {"type":"Polygon","bbox":[0,0,1,1],"coordinates":[[[0,0],[0,1],[1,1],[1,0],[0,0]]]}-- 3D 几何
SELECT ST_AsGeoJSON(ST_MakePoint(116.4, 39.9, 45.5));
-- {"type":"Point","coordinates":[116.4,39.9,45.5]}
8.4.2 构建完整的 GeoJSON Feature
-- 创建 GeoJSON Feature
SELECT json_build_object('type', 'Feature','geometry', ST_AsGeoJSON(geom)::json,'properties', json_build_object('id', id,'name', name,'category', category)
)
FROM poi
WHERE id = 1;-- 创建 GeoJSON FeatureCollection
SELECT json_build_object('type', 'FeatureCollection','features', json_agg(json_build_object('type', 'Feature','geometry', ST_AsGeoJSON(geom)::json,'properties', json_build_object('id', id,'name', name,'category', category)))
)
FROM poi
WHERE category = '餐饮';-- 使用 row_to_json 简化
SELECT json_build_object('type', 'FeatureCollection','features', json_agg(json_build_object('type', 'Feature','geometry', ST_AsGeoJSON(geom, 6)::json,'properties', to_jsonb(t) - 'geom'))
)
FROM (SELECT id, name, category, geom FROM poi) t;
8.4.3 使用 jsonb 函数
-- 使用 jsonb 构建(更高效)
SELECT jsonb_build_object('type', 'FeatureCollection','features', jsonb_agg(jsonb_build_object('type', 'Feature','id', id,'geometry', ST_AsGeoJSON(geom)::jsonb,'properties', jsonb_build_object('name', name,'category', category)))
)
FROM poi
WHERE ST_Intersects(geom, ST_MakeEnvelope(116, 39, 117, 40, 4326));
8.5 GML 和 KML 输出
8.5.1 ST_AsGML
-- 基本 GML 输出
SELECT ST_AsGML(ST_MakePoint(116.4, 39.9));
-- <gml:Point><gml:coordinates>116.4,39.9</gml:coordinates></gml:Point>-- GML 3 格式
SELECT ST_AsGML(3, ST_MakePoint(116.4, 39.9));
-- <gml:Point><gml:pos>116.4 39.9</gml:pos></gml:Point>-- 控制精度
SELECT ST_AsGML(3, ST_MakePoint(116.40740001, 39.90420001), 4);-- 包含 SRID
SELECT ST_AsGML(3, ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326), 15, 1);
-- 添加 srsName 属性-- 完整参数
-- ST_AsGML(version, geom, maxdecimaldigits, options, nprefix, id)
SELECT ST_AsGML(3, -- GML 版本ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326),6, -- 精度17, -- options: 1(srsName) + 16(namespace)'custom', -- 命名空间前缀'point1' -- ID
);-- options 位掩码:
-- 1 = 输出 srsName
-- 2 = 使用长 SRID URN
-- 4 = GML 3.2 格式
-- 16 = 声明命名空间
-- 32 = LineString 使用 srsDimension
8.5.2 ST_AsKML
-- 基本 KML 输出
SELECT ST_AsKML(ST_MakePoint(116.4, 39.9));
-- <Point><coordinates>116.4,39.9</coordinates></Point>-- 控制精度
SELECT ST_AsKML(ST_MakePoint(116.40740001, 39.90420001), 4);
-- <Point><coordinates>116.4074,39.9042</coordinates></Point>-- 3D 几何(KML 使用高程)
SELECT ST_AsKML(ST_MakePoint(116.4, 39.9, 45.5));
-- <Point><coordinates>116.4,39.9,45.5</coordinates></Point>-- 带名称前缀
SELECT ST_AsKML(ST_MakePoint(116.4, 39.9), 15, 'kml');
-- <kml:Point><kml:coordinates>116.4,39.9</kml:coordinates></kml:Point>-- 创建完整的 KML 文档
SELECT '<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"><Document><name>POI 数据</name>' ||
string_agg('<Placemark><name>' || name || '</name><description>' || COALESCE(category, '') || '</description>' ||ST_AsKML(geom) ||'</Placemark>',''
) ||'</Document>
</kml>'
FROM poi
WHERE category = '景点';
8.6 SVG 输出
8.6.1 ST_AsSVG
-- 基本 SVG 路径
SELECT ST_AsSVG(ST_MakePoint(116.4, 39.9));
-- cx="116.4" cy="-39.9" (注意 Y 轴翻转)SELECT ST_AsSVG(ST_MakeLine(ST_MakePoint(0, 0), ST_MakePoint(1, 1)));
-- M 0 0 L 1 -1SELECT ST_AsSVG(ST_MakeEnvelope(0, 0, 1, 1));
-- M 0 0 L 0 -1 1 -1 1 0 Z-- 控制精度和相对/绝对路径
-- ST_AsSVG(geom, rel, maxdecimaldigits)
-- rel = 0: 绝对路径 (M, L)
-- rel = 1: 相对路径 (m, l)SELECT ST_AsSVG(ST_MakeEnvelope(0, 0, 1, 1), 0, 2); -- 绝对路径
SELECT ST_AsSVG(ST_MakeEnvelope(0, 0, 1, 1), 1, 2); -- 相对路径-- 创建 SVG 文档
WITH bounds AS (SELECT ST_XMin(ST_Collect(geom)) AS min_x,ST_YMin(ST_Collect(geom)) AS min_y,ST_XMax(ST_Collect(geom)) AS max_x,ST_YMax(ST_Collect(geom)) AS max_yFROM districts
),
svg_paths AS (SELECT id, name, ST_AsSVG(geom, 0, 2) AS pathFROM districts
)
SELECT '<svg xmlns="http://www.w3.org/2000/svg" viewBox="' || min_x || ' ' || (-max_y) || ' ' || (max_x - min_x) || ' ' || (max_y - min_y) || '">' ||string_agg('<path d="' || path || '" fill="steelblue" stroke="white" stroke-width="0.01"/>', '') ||'</svg>'
FROM bounds, svg_paths
GROUP BY min_x, min_y, max_x, max_y;
8.7 其他输出格式
8.7.1 ST_AsEncodedPolyline
-- Google 编码折线(用于 Google Maps API)
SELECT ST_AsEncodedPolyline(ST_GeomFromText('LINESTRING(-120.2 38.5,-120.95 40.7,-126.453 43.252)', 4326)
);
-- _p~iF~ps|U_ulLnnqC_mqNvxq`@-- 控制精度(默认 5)
SELECT ST_AsEncodedPolyline(ST_GeomFromText('LINESTRING(-120.2 38.5,-120.95 40.7)', 4326),6 -- 6 位精度
);-- 解码
SELECT ST_AsText(ST_LineFromEncodedPolyline('_p~iF~ps|U_ulLnnqC_mqNvxq`@'));
8.7.2 ST_GeoHash
-- 生成 GeoHash
SELECT ST_GeoHash(ST_MakePoint(116.4074, 39.9042));
-- wx4g0ffe3d (默认完整精度)-- 控制精度
SELECT ST_GeoHash(ST_MakePoint(116.4074, 39.9042), 8);
-- wx4g0ffeSELECT ST_GeoHash(ST_MakePoint(116.4074, 39.9042), 5);
-- wx4g0-- 从 GeoHash 创建几何(边界框)
SELECT ST_AsText(ST_GeomFromGeoHash('wx4g0'));
-- POLYGON((116.3671875 39.8583984375, ...))-- 获取 GeoHash 的点(中心点)
SELECT ST_AsText(ST_PointFromGeoHash('wx4g0'));-- GeoHash 网格分析
SELECT ST_GeoHash(geom, 5) AS geohash,COUNT(*) AS poi_count
FROM poi
GROUP BY ST_GeoHash(geom, 5)
ORDER BY poi_count DESC;
8.7.3 ST_AsLatLonText
-- 度分秒格式
SELECT ST_AsLatLonText(ST_MakePoint(116.4074, 39.9042));
-- 39°54'15.120"N 116°24'26.640"E-- 自定义格式
SELECT ST_AsLatLonText(ST_MakePoint(116.4074, 39.9042), 'D°M''S.SSS"');
-- 39°54'15.120" 116°24'26.640"SELECT ST_AsLatLonText(ST_MakePoint(116.4074, 39.9042), 'D.DDD°');
-- 39.904° 116.407°
8.7.4 ST_AsX3D
-- X3D 格式(3D 可视化)
SELECT ST_AsX3D(ST_MakePoint(116.4, 39.9, 45.5));
-- 116.4 39.9 45.5SELECT ST_AsX3D(ST_GeomFromText('POLYGON Z((0 0 0, 1 0 0, 1 1 0, 0 1 0, 0 0 0))'));-- 控制精度
SELECT ST_AsX3D(ST_MakePoint(116.40740001, 39.90420001, 45.5), 4);
8.8 MVT 输出
8.8.1 ST_AsMVTGeom
-- 准备 MVT 几何(坐标转换到瓦片坐标系)
SELECT ST_AsMVTGeom(geom,ST_TileEnvelope(14, 13527, 6214), -- z/x/y 瓦片边界4096, -- extent(瓦片像素尺寸)0, -- buffertrue -- clip to tile
) AS mvt_geom
FROM poi
WHERE geom && ST_TileEnvelope(14, 13527, 6214);
8.8.2 ST_AsMVT
-- 生成 MVT 瓦片
SELECT ST_AsMVT(tile, 'poi') AS mvt
FROM (SELECT id,name,category,ST_AsMVTGeom(geom,ST_TileEnvelope(14, 13527, 6214),4096,64,true) AS geomFROM poiWHERE geom && ST_TileEnvelope(14, 13527, 6214)
) AS tile;-- 创建 MVT 瓦片函数
CREATE OR REPLACE FUNCTION get_mvt_tile(z INT, x INT, y INT)
RETURNS bytea AS $$
DECLAREtile_bounds geometry;mvt bytea;
BEGINtile_bounds := ST_TileEnvelope(z, x, y);SELECT ST_AsMVT(tile, 'poi', 4096, 'geom') INTO mvtFROM (SELECT id,name,category,ST_AsMVTGeom(geom,tile_bounds,4096,64,true) AS geomFROM poiWHERE geom && tile_bounds) AS tile;RETURN mvt;
END;
$$ LANGUAGE plpgsql STABLE PARALLEL SAFE;-- 使用
SELECT get_mvt_tile(14, 13527, 6214);-- 多图层 MVT
SELECT ST_AsMVT(poi_layer, 'poi') || ST_AsMVT(road_layer, 'roads') AS mvt
FROM (SELECT id, name, ST_AsMVTGeom(geom, bounds, 4096, 64, true) AS geomFROM poi, (SELECT ST_TileEnvelope(14, 13527, 6214) AS bounds) bWHERE geom && bounds
) poi_layer,
(SELECT id, name, ST_AsMVTGeom(geom, bounds, 4096, 64, true) AS geomFROM roads, (SELECT ST_TileEnvelope(14, 13527, 6214) AS bounds) bWHERE geom && bounds
) road_layer;
8.9 格式比较与选择
8.9.1 格式特性对比
| 格式 | 人类可读 | 大小 | SRID | 3D支持 | 用途 |
|---|---|---|---|---|---|
| WKT | ✓ | 大 | ✗ | ✓ | 调试、日志 |
| EWKT | ✓ | 大 | ✓ | ✓ | 数据交换 |
| WKB | ✗ | 中 | ✗ | ✓ | 程序间传输 |
| EWKB | ✗ | 中 | ✓ | ✓ | PostGIS 内部 |
| TWKB | ✗ | 小 | ✗ | ✓ | 网络传输 |
| GeoJSON | ✓ | 大 | 部分 | ✓ | Web API |
| GML | ✓ | 很大 | ✓ | ✓ | OGC 服务 |
| KML | ✓ | 大 | ✗ | ✓ | Google Earth |
| MVT | ✗ | 小 | ✗ | ✗ | 矢量瓦片 |
8.9.2 选择建议
-- Web API(推荐 GeoJSON)
SELECT json_build_object('type', 'FeatureCollection','features', json_agg(json_build_object('type', 'Feature','geometry', ST_AsGeoJSON(ST_Simplify(geom, 0.0001), 6)::json,'properties', json_build_object('id', id, 'name', name)))
)
FROM districts;-- 数据备份(推荐 EWKT 或 EWKB)
COPY (SELECT id, name, ST_AsEWKT(geom) AS geomFROM spatial_data
) TO '/tmp/backup.csv' WITH CSV HEADER;-- 移动应用(推荐 TWKB)
SELECT id, name, encode(ST_AsTWKB(geom, 5), 'base64') AS geom
FROM poi
WHERE category = '餐饮';-- 地图瓦片(推荐 MVT)
SELECT get_mvt_tile(z, x, y);-- Google Earth(推荐 KML)
SELECT ST_AsKML(geom) FROM poi;
8.10 本章小结
本章详细介绍了 PostGIS 的几何输出函数:
- WKT 输出:ST_AsText、ST_AsEWKT
- WKB 输出:ST_AsBinary、ST_AsEWKB、ST_AsTWKB
- GeoJSON 输出:ST_AsGeoJSON 及 FeatureCollection 构建
- GML/KML 输出:ST_AsGML、ST_AsKML
- SVG 输出:ST_AsSVG
- 其他格式:编码折线、GeoHash、X3D
- MVT 输出:矢量瓦片生成
8.11 下一步
在下一章中,我们将学习空间关系函数,包括:
- 拓扑关系判断
- 距离关系
- 方向关系
- DE-9IM 模型
相关资源:
- PostGIS 输出函数
- GeoJSON 规范
- KML 参考
- MVT 规范