第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 的几何输出函数:

  1. WKT 输出:ST_AsText、ST_AsEWKT
  2. WKB 输出:ST_AsBinary、ST_AsEWKB、ST_AsTWKB
  3. GeoJSON 输出:ST_AsGeoJSON 及 FeatureCollection 构建
  4. GML/KML 输出:ST_AsGML、ST_AsKML
  5. SVG 输出:ST_AsSVG
  6. 其他格式:编码折线、GeoHash、X3D
  7. MVT 输出:矢量瓦片生成

8.11 下一步

在下一章中,我们将学习空间关系函数,包括:

  • 拓扑关系判断
  • 距离关系
  • 方向关系
  • DE-9IM 模型

相关资源

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