第08章-几何输出函数
第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 -1
SELECT 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_y
FROM districts
),
svg_paths AS (
SELECT id, name, ST_AsSVG(geom, 0, 2) AS path
FROM 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);
-- wx4g0ffe
SELECT 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.5
SELECT 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, -- buffer
true -- 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 geom
FROM poi
WHERE 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 $$
DECLARE
tile_bounds geometry;
mvt bytea;
BEGIN
tile_bounds := ST_TileEnvelope(z, x, y);
SELECT ST_AsMVT(tile, 'poi', 4096, 'geom') INTO mvt
FROM (
SELECT
id,
name,
category,
ST_AsMVTGeom(
geom,
tile_bounds,
4096,
64,
true
) AS geom
FROM poi
WHERE 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 geom
FROM poi, (SELECT ST_TileEnvelope(14, 13527, 6214) AS bounds) b
WHERE geom && bounds
) poi_layer,
(
SELECT id, name, ST_AsMVTGeom(geom, bounds, 4096, 64, true) AS geom
FROM roads, (SELECT ST_TileEnvelope(14, 13527, 6214) AS bounds) b
WHERE 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 geom
FROM 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 模型
相关资源:

浙公网安备 33010602011771号