第03章:空间数据类型详解
3.1 PostGIS 数据类型概述
PostGIS 提供了多种空间数据类型来存储和处理地理信息。理解这些数据类型是使用 PostGIS 的基础。
3.1.1 数据类型分类
┌─────────────────────────────────────────────────────────────┐
│ PostGIS 空间数据类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 矢量数据类型 │
│ ├── Geometry (几何类型) │
│ │ ├── 平面坐标系统 │
│ │ ├── 笛卡尔运算 │
│ │ └── 更多函数支持 │
│ │ │
│ └── Geography (地理类型) │
│ ├── 球面坐标系统 │
│ ├── 大地测量计算 │
│ └── 经纬度坐标 │
│ │
│ 栅格数据类型 │
│ └── Raster (栅格类型) │
│ ├── 像素网格数据 │
│ ├── 多波段支持 │
│ └── 空间分析功能 │
│ │
│ 拓扑数据类型 │
│ └── Topology (拓扑类型) │
│ ├── 节点、边、面 │
│ ├── 拓扑规则 │
│ └── 数据完整性 │
│ │
└─────────────────────────────────────────────────────────────┘
3.1.2 几何类型层次结构
PostGIS 的几何类型遵循 OGC Simple Features 规范:
Geometry│┌────────────────────┼────────────────────┐│ │ │Point Curve Surface│ │ ││ ┌─────────────┼─────────────┐ ││ │ │ │ ││ LineString CircularString CompoundCurve│ │ ││ │ Polygon│ │ ││ │ CurvePolygon│ │MultiPoint ││MultiLineString│MultiPolygon│GeometryCollection
3.1.3 类型定义语法
-- 完整的类型定义语法
GEOMETRY(geometry_type, srid)
GEOGRAPHY(geometry_type, srid)-- geometry_type 可选值:
-- POINT, LINESTRING, POLYGON
-- MULTIPOINT, MULTILINESTRING, MULTIPOLYGON
-- GEOMETRYCOLLECTION
-- POINTZ, LINESTRINGZ, POLYGONZ (带 Z 值)
-- POINTM, LINESTRINGM, POLYGONM (带 M 值)
-- POINTZM, LINESTRINGZM, POLYGONZM (带 ZM 值)-- 示例
CREATE TABLE points (id SERIAL PRIMARY KEY,name VARCHAR(100),geom GEOMETRY(POINT, 4326)
);CREATE TABLE lines (id SERIAL PRIMARY KEY,name VARCHAR(100),geom GEOMETRY(LINESTRING, 4326)
);CREATE TABLE polygons (id SERIAL PRIMARY KEY,name VARCHAR(100),geom GEOMETRY(POLYGON, 4326)
);-- 不指定类型(可存储任意几何类型)
CREATE TABLE mixed_geometries (id SERIAL PRIMARY KEY,name VARCHAR(100),geom GEOMETRY
);
3.2 Geometry 类型详解
3.2.1 Point(点)
点是最简单的几何类型,表示空间中的一个位置。
结构定义
-- 2D 点
POINT(x y)
-- 例如:POINT(116.4074 39.9042)-- 3D 点(带高程)
POINT Z(x y z)
-- 例如:POINT Z(116.4074 39.9042 45.5)-- 带测量值的点
POINT M(x y m)
-- 例如:POINT M(116.4074 39.9042 100.0)-- 带高程和测量值的点
POINT ZM(x y z m)
-- 例如:POINT ZM(116.4074 39.9042 45.5 100.0)
创建点的方法
-- 使用 ST_MakePoint
SELECT ST_MakePoint(116.4074, 39.9042);
SELECT ST_MakePoint(116.4074, 39.9042, 45.5); -- 3D
SELECT ST_MakePointM(116.4074, 39.9042, 100.0); -- 带 M 值-- 使用 ST_GeomFromText (WKT)
SELECT ST_GeomFromText('POINT(116.4074 39.9042)', 4326);
SELECT ST_GeomFromText('POINT Z(116.4074 39.9042 45.5)', 4326);-- 使用 ST_SetSRID 设置空间参考
SELECT ST_SetSRID(ST_MakePoint(116.4074, 39.9042), 4326);-- 使用 ST_Point(PostGIS 3.0+)
SELECT ST_Point(116.4074, 39.9042, 4326);-- 从经纬度创建(注意经度在前)
SELECT ST_SetSRID(ST_MakePoint(116.4074, 39.9042), 4326);
点的常用操作
-- 创建点表
CREATE TABLE poi (id SERIAL PRIMARY KEY,name VARCHAR(100),category VARCHAR(50),geom GEOMETRY(POINT, 4326)
);-- 插入数据
INSERT INTO poi (name, category, geom) VALUES('故宫', '景点', ST_SetSRID(ST_MakePoint(116.3972, 39.9169), 4326)),('天安门', '景点', ST_SetSRID(ST_MakePoint(116.3912, 39.9055), 4326)),('王府井', '商业', ST_SetSRID(ST_MakePoint(116.4104, 39.9142), 4326));-- 获取点坐标
SELECT name, ST_X(geom) AS lng, ST_Y(geom) AS lat FROM poi;-- 计算两点距离(米)
SELECT a.name AS from_poi,b.name AS to_poi,ST_Distance(a.geom::geography, b.geom::geography) AS distance_m
FROM poi a, poi b
WHERE a.id < b.id;-- 查找某点附近的 POI
SELECT name, ST_Distance(geom::geography, ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)::geography) AS distance
FROM poi
WHERE ST_DWithin(geom::geography, ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)::geography, 1000)
ORDER BY distance;
3.2.2 LineString(线)
线由有序的点序列组成,表示路径或边界。
结构定义
-- 2D 线
LINESTRING(x1 y1, x2 y2, ..., xn yn)
-- 例如:LINESTRING(116.3 39.9, 116.4 39.95, 116.5 39.9)-- 3D 线
LINESTRING Z(x1 y1 z1, x2 y2 z2, ..., xn yn zn)-- 环形线(首尾相连)
LINESTRING(x1 y1, x2 y2, x3 y3, x1 y1)
创建线的方法
-- 使用 ST_MakeLine(从点创建)
SELECT ST_MakeLine(ST_MakePoint(116.3, 39.9),ST_MakePoint(116.5, 39.95)
);-- 从点数组创建
SELECT ST_MakeLine(ARRAY[ST_MakePoint(116.3, 39.9),ST_MakePoint(116.4, 39.95),ST_MakePoint(116.5, 39.9)
]);-- 使用 ST_GeomFromText (WKT)
SELECT ST_GeomFromText('LINESTRING(116.3 39.9, 116.4 39.95, 116.5 39.9)', 4326);-- 使用 ST_GeomFromGeoJSON
SELECT ST_GeomFromGeoJSON('{"type":"LineString","coordinates":[[116.3,39.9],[116.4,39.95],[116.5,39.9]]}');
线的常用操作
-- 创建道路表
CREATE TABLE roads (id SERIAL PRIMARY KEY,name VARCHAR(100),road_type VARCHAR(50),geom GEOMETRY(LINESTRING, 4326)
);-- 插入数据
INSERT INTO roads (name, road_type, geom) VALUES('长安街', '主干道', ST_GeomFromText('LINESTRING(116.28 39.9055, 116.32 39.9055, 116.39 39.9055, 116.46 39.9055)', 4326)),('建国门外大街', '主干道', ST_GeomFromText('LINESTRING(116.39 39.9055, 116.46 39.906, 116.52 39.906)', 4326));-- 计算线长度(米)
SELECT name, ST_Length(geom::geography) AS length_m FROM roads;-- 获取起点和终点
SELECT name, ST_AsText(ST_StartPoint(geom)) AS start_point,ST_AsText(ST_EndPoint(geom)) AS end_point
FROM roads;-- 获取线上的点数
SELECT name, ST_NPoints(geom) AS point_count FROM roads;-- 获取线的中点
SELECT name, ST_AsText(ST_LineInterpolatePoint(geom, 0.5)) AS mid_point FROM roads;-- 线段分割
SELECT name, ST_AsText((ST_Dump(ST_Segmentize(geom::geography, 1000)::geometry)).geom) AS segments
FROM roads;-- 合并多条线
SELECT ST_AsText(ST_LineMerge(ST_Collect(geom))) AS merged_line FROM roads;-- 判断线是否闭合
SELECT name, ST_IsClosed(geom) AS is_closed FROM roads;-- 简化线
SELECT name, ST_AsText(ST_Simplify(geom, 0.001)) AS simplified FROM roads;
3.2.3 Polygon(多边形)
多边形由一个外环和零个或多个内环(孔洞)组成。
结构定义
-- 简单多边形(无孔洞)
POLYGON((x1 y1, x2 y2, x3 y3, x1 y1))
-- 例如:POLYGON((116.3 39.8, 116.5 39.8, 116.5 40.0, 116.3 40.0, 116.3 39.8))-- 带孔洞的多边形
POLYGON((外环坐标), (内环1坐标), (内环2坐标), ...)
-- 例如:POLYGON((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 8 2, 8 8, 2 8, 2 2))-- 3D 多边形
POLYGON Z((x1 y1 z1, x2 y2 z2, x3 y3 z3, x1 y1 z1))
创建多边形的方法
-- 使用 ST_GeomFromText (WKT)
SELECT ST_GeomFromText('POLYGON((116.3 39.8, 116.5 39.8, 116.5 40.0, 116.3 40.0, 116.3 39.8))', 4326);-- 使用 ST_MakePolygon(从闭合线创建)
SELECT ST_MakePolygon(ST_GeomFromText('LINESTRING(116.3 39.8, 116.5 39.8, 116.5 40.0, 116.3 40.0, 116.3 39.8)', 4326)
);-- 带孔洞的多边形
SELECT ST_MakePolygon(ST_GeomFromText('LINESTRING(0 0, 10 0, 10 10, 0 10, 0 0)', 4326),ARRAY[ST_GeomFromText('LINESTRING(2 2, 8 2, 8 8, 2 8, 2 2)', 4326)]
);-- 使用 ST_MakeEnvelope 创建矩形
SELECT ST_MakeEnvelope(116.3, 39.8, 116.5, 40.0, 4326);-- 使用 ST_Buffer 从点创建圆形
SELECT ST_Buffer(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)::geography, 1000)::geometry;-- 使用 ST_GeomFromGeoJSON
SELECT ST_GeomFromGeoJSON('{"type":"Polygon","coordinates":[[[116.3,39.8],[116.5,39.8],[116.5,40.0],[116.3,40.0],[116.3,39.8]]]}');
多边形的常用操作
-- 创建行政区表
CREATE TABLE districts (id SERIAL PRIMARY KEY,name VARCHAR(100),code VARCHAR(20),geom GEOMETRY(POLYGON, 4326)
);-- 插入数据
INSERT INTO districts (name, code, geom) VALUES('东城区', '110101', ST_GeomFromText('POLYGON((116.38 39.88, 116.44 39.88, 116.44 39.95, 116.38 39.95, 116.38 39.88))', 4326)),('西城区', '110102', ST_GeomFromText('POLYGON((116.32 39.88, 116.38 39.88, 116.38 39.95, 116.32 39.95, 116.32 39.88))', 4326));-- 计算面积(平方米)
SELECT name, ST_Area(geom::geography) AS area_m2 FROM districts;-- 计算面积(平方公里)
SELECT name, ST_Area(geom::geography) / 1000000.0 AS area_km2 FROM districts;-- 获取边界周长
SELECT name, ST_Perimeter(geom::geography) AS perimeter_m FROM districts;-- 获取边界
SELECT name, ST_AsText(ST_Boundary(geom)) AS boundary FROM districts;-- 获取外环
SELECT name, ST_AsText(ST_ExteriorRing(geom)) AS exterior_ring FROM districts;-- 获取质心
SELECT name, ST_AsText(ST_Centroid(geom)) AS centroid FROM districts;-- 获取内部点(保证在多边形内)
SELECT name, ST_AsText(ST_PointOnSurface(geom)) AS point_on_surface FROM districts;-- 判断是否有效
SELECT name, ST_IsValid(geom) AS is_valid FROM districts;-- 修复无效多边形
UPDATE districts SET geom = ST_MakeValid(geom) WHERE NOT ST_IsValid(geom);-- 简化多边形
SELECT name, ST_AsText(ST_Simplify(geom, 0.001)) AS simplified FROM districts;-- 判断点是否在多边形内
SELECT d.name
FROM districts d
WHERE ST_Contains(d.geom, ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326));
3.2.4 MultiPoint(多点)
-- 创建多点
SELECT ST_GeomFromText('MULTIPOINT((116.3 39.9), (116.4 39.95), (116.5 39.9))', 4326);-- 使用 ST_Collect 合并点
SELECT ST_Collect(geom) FROM poi;-- 从多点获取单个点
SELECT ST_AsText((ST_Dump(ST_GeomFromText('MULTIPOINT((116.3 39.9), (116.4 39.95))', 4326))).geom);-- 获取点数量
SELECT ST_NumGeometries(ST_GeomFromText('MULTIPOINT((116.3 39.9), (116.4 39.95))', 4326));
3.2.5 MultiLineString(多线)
-- 创建多线
SELECT ST_GeomFromText('MULTILINESTRING((116.3 39.9, 116.4 39.95), (116.4 39.95, 116.5 39.9))', 4326);-- 合并线
SELECT ST_LineMerge(ST_Collect(geom)) FROM roads;-- 计算总长度
SELECT ST_Length(ST_Collect(geom)::geography) FROM roads;
3.2.6 MultiPolygon(多面)
-- 创建多面
SELECT ST_GeomFromText('MULTIPOLYGON(((0 0, 1 0, 1 1, 0 1, 0 0)), ((2 2, 3 2, 3 3, 2 3, 2 2)))', 4326);-- 合并多边形
SELECT ST_Union(geom) FROM districts;-- 计算总面积
SELECT ST_Area(ST_Union(geom)::geography) FROM districts;
3.2.7 GeometryCollection(几何集合)
-- 创建几何集合
SELECT ST_GeomFromText('GEOMETRYCOLLECTION(POINT(116.4 39.9), LINESTRING(116.3 39.8, 116.5 39.8))', 4326);-- 收集不同类型的几何
SELECT ST_Collect(ARRAY[ST_MakePoint(116.4, 39.9),ST_MakeLine(ST_MakePoint(116.3, 39.8), ST_MakePoint(116.5, 39.8))
]);-- 获取集合中的几何数量
SELECT ST_NumGeometries(ST_GeomFromText('GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(1 1, 2 2))', 4326));-- 获取特定索引的几何
SELECT ST_AsText(ST_GeometryN(ST_GeomFromText('GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(1 1, 2 2))', 4326),1
));
3.3 Geography 类型详解
3.3.1 Geography 与 Geometry 的区别
| 特性 | Geometry | Geography |
|---|---|---|
| 坐标系 | 平面(笛卡尔) | 球面(大地) |
| 单位 | 度或投影单位 | 度(经纬度) |
| 距离计算 | 平面距离 | 大地测量距离 |
| 面积计算 | 平面面积 | 球面面积 |
| 函数数量 | 1000+ | 100+ |
| 性能 | 较快 | 较慢 |
| 适用范围 | 小区域 | 大区域/全球 |
| SRID 支持 | 任意 | 4326 等 |
3.3.2 使用 Geography 类型
-- 创建使用 Geography 的表
CREATE TABLE cities_geo (id SERIAL PRIMARY KEY,name VARCHAR(100),population INTEGER,geog GEOGRAPHY(POINT, 4326)
);-- 插入数据
INSERT INTO cities_geo (name, population, geog) VALUES('北京', 21540000, ST_GeographyFromText('POINT(116.4074 39.9042)')),('上海', 24870000, ST_GeographyFromText('POINT(121.4737 31.2304)')),('纽约', 8336817, ST_GeographyFromText('POINT(-74.0060 40.7128)'));-- Geography 类型自动使用球面计算
-- 计算北京到上海的距离(米)
SELECT ST_Distance((SELECT geog FROM cities_geo WHERE name = '北京'),(SELECT geog FROM cities_geo WHERE name = '上海')
) AS distance_m;-- 计算北京到纽约的距离(公里)
SELECT ST_Distance((SELECT geog FROM cities_geo WHERE name = '北京'),(SELECT geog FROM cities_geo WHERE name = '纽约')
) / 1000.0 AS distance_km;
3.3.3 Geometry 与 Geography 转换
-- Geometry 转 Geography
SELECT geom::geography FROM poi;-- Geography 转 Geometry
SELECT geog::geometry FROM cities_geo;-- 使用 ST_Transform 进行投影转换后再转换
SELECT ST_Transform(geom, 3857)::geography FROM poi WHERE ST_SRID(geom) = 4326;
3.3.4 选择 Geometry 还是 Geography
使用 Geography 的场景:
- 需要精确的大地测量计算
- 数据跨越大范围区域(跨省、跨国)
- 需要计算真实的地球表面距离
- 数据使用经纬度坐标(WGS84)
使用 Geometry 的场景:
- 需要更多空间函数支持
- 数据范围较小(城市级别)
- 已有投影坐标数据
- 性能要求较高
-- 小范围数据示例(北京市内)
-- 使用 Geometry 配合投影坐标
CREATE TABLE beijing_poi (id SERIAL PRIMARY KEY,name VARCHAR(100),geom GEOMETRY(POINT, 4527) -- CGCS2000 / 3-degree Gauss-Kruger zone 39
);-- 大范围数据示例(全球城市)
-- 使用 Geography
CREATE TABLE world_cities (id SERIAL PRIMARY KEY,name VARCHAR(100),country VARCHAR(100),geog GEOGRAPHY(POINT, 4326)
);
3.4 坐标维度与测量值
3.4.1 二维几何(XY)
-- 标准 2D 几何
CREATE TABLE table_2d (id SERIAL PRIMARY KEY,geom GEOMETRY(POINT, 4326)
);INSERT INTO table_2d (geom) VALUES(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326));-- 检查维度
SELECT ST_NDims(geom), ST_CoordDim(geom) FROM table_2d;
-- 结果: 2, 2
3.4.2 三维几何(XYZ)
-- 带高程的 3D 几何
CREATE TABLE table_3d (id SERIAL PRIMARY KEY,geom GEOMETRY(POINTZ, 4326)
);INSERT INTO table_3d (geom) VALUES(ST_SetSRID(ST_MakePoint(116.4, 39.9, 45.5), 4326));-- 获取 Z 值
SELECT ST_Z(geom) AS elevation FROM table_3d;-- 3D 距离计算
SELECT ST_3DDistance(ST_SetSRID(ST_MakePoint(0, 0, 0), 4326),ST_SetSRID(ST_MakePoint(1, 1, 1), 4326)
) AS distance_3d;-- 3D 长度计算
SELECT ST_3DLength(ST_SetSRID(ST_GeomFromText('LINESTRING Z(0 0 0, 1 1 1, 2 2 2)'), 4326)
) AS length_3d;
3.4.3 带测量值的几何(XYM)
-- 带测量值的几何(如里程标记)
CREATE TABLE table_m (id SERIAL PRIMARY KEY,geom GEOMETRY(POINTM, 4326)
);INSERT INTO table_m (geom) VALUES(ST_SetSRID(ST_MakePointM(116.4, 39.9, 1000.0), 4326));-- 获取 M 值
SELECT ST_M(geom) AS measure FROM table_m;-- 线性参考示例
-- 创建带里程的道路
CREATE TABLE road_with_measures (id SERIAL PRIMARY KEY,name VARCHAR(100),geom GEOMETRY(LINESTRINGM, 4326)
);INSERT INTO road_with_measures (name, geom) VALUES('测试路', ST_SetSRID(ST_GeomFromText('LINESTRING M(0 0 0, 1 0 500, 2 0 1000)'), 4326));-- 根据 M 值定位点
SELECT ST_AsText(ST_LocateAlong(geom, 500)) FROM road_with_measures;
3.4.4 四维几何(XYZM)
-- 完整的 4D 几何
CREATE TABLE table_4d (id SERIAL PRIMARY KEY,geom GEOMETRY(POINTZM, 4326)
);INSERT INTO table_4d (geom) VALUES(ST_SetSRID(ST_GeomFromText('POINT ZM(116.4 39.9 45.5 1000.0)'), 4326));-- 获取所有坐标值
SELECT ST_X(geom) AS x,ST_Y(geom) AS y,ST_Z(geom) AS z,ST_M(geom) AS m
FROM table_4d;
3.5 类型修饰符与约束
3.5.1 几何类型约束
-- 使用类型修饰符
CREATE TABLE constrained_table (id SERIAL PRIMARY KEY,-- 只允许 Point 类型point_geom GEOMETRY(POINT, 4326),-- 只允许 Polygon 类型polygon_geom GEOMETRY(POLYGON, 4326),-- 允许任意几何类型any_geom GEOMETRY(GEOMETRY, 4326)
);-- 使用 CHECK 约束
CREATE TABLE check_constrained (id SERIAL PRIMARY KEY,geom GEOMETRY(GEOMETRY, 4326),CONSTRAINT enforce_geom_type CHECK (GeometryType(geom) IN ('POINT', 'LINESTRING'))
);-- 使用 AddGeometryColumn(传统方式)
CREATE TABLE old_style (id SERIAL PRIMARY KEY);
SELECT AddGeometryColumn('old_style', 'geom', 4326, 'POINT', 2);
3.5.2 SRID 约束
-- 强制 SRID
CREATE TABLE srid_constrained (id SERIAL PRIMARY KEY,geom GEOMETRY(POINT, 4326),-- 自动添加 SRID 约束CONSTRAINT enforce_srid CHECK (ST_SRID(geom) = 4326)
);-- 插入数据时会自动验证 SRID
INSERT INTO srid_constrained (geom) VALUES(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)); -- 成功-- 以下会失败
-- INSERT INTO srid_constrained (geom) VALUES
-- (ST_SetSRID(ST_MakePoint(116.4, 39.9), 3857)); -- 错误:SRID 不匹配
3.5.3 有效性约束
-- 确保几何有效性
CREATE TABLE valid_geometries (id SERIAL PRIMARY KEY,geom GEOMETRY(POLYGON, 4326),CONSTRAINT enforce_valid CHECK (ST_IsValid(geom))
);-- 自动修复触发器
CREATE OR REPLACE FUNCTION fix_geometry()
RETURNS TRIGGER AS $$
BEGINIF NOT ST_IsValid(NEW.geom) THENNEW.geom = ST_MakeValid(NEW.geom);END IF;RETURN NEW;
END;
$$ LANGUAGE plpgsql;CREATE TRIGGER trigger_fix_geometry
BEFORE INSERT OR UPDATE ON valid_geometries
FOR EACH ROW EXECUTE FUNCTION fix_geometry();
3.6 空间数据存储
3.6.1 内部存储格式
PostGIS 使用 EWKB(Extended Well-Known Binary)格式存储几何数据:
-- 查看内部存储格式
SELECT geom FROM poi LIMIT 1;
-- 返回十六进制的 EWKB 格式-- 查看 WKB
SELECT ST_AsBinary(geom) FROM poi LIMIT 1;-- 查看 EWKB(包含 SRID)
SELECT ST_AsEWKB(geom) FROM poi LIMIT 1;-- 查看存储大小
SELECT pg_column_size(geom) AS bytes,ST_MemSize(geom) AS memory_size
FROM poi LIMIT 1;-- 估算表的几何数据大小
SELECT pg_size_pretty(sum(pg_column_size(geom))) AS total_geom_size,pg_size_pretty(pg_total_relation_size('poi')) AS total_table_size
FROM poi;
3.6.2 存储优化
-- 简化几何以减少存储
UPDATE large_polygons
SET geom = ST_Simplify(geom, 0.0001)
WHERE ST_NPoints(geom) > 1000;-- 压缩几何
-- PostGIS 默认使用 PostgreSQL 的 TOAST 压缩-- 设置列的存储策略
ALTER TABLE large_polygons ALTER COLUMN geom SET STORAGE EXTENDED;
-- PLAIN: 不压缩,不使用 TOAST
-- EXTENDED: 压缩后使用 TOAST(默认)
-- EXTERNAL: 不压缩,使用 TOAST
-- MAIN: 尽量不使用 TOAST-- 查看存储策略
SELECT attname, attstorage
FROM pg_attribute
WHERE attrelid = 'large_polygons'::regclass AND attname = 'geom';
3.7 空间元数据
3.7.1 geometry_columns 视图
-- 查看所有空间表
SELECT * FROM geometry_columns;-- 查看特定表的空间列信息
SELECT f_table_schema, f_table_name, f_geometry_column, coord_dimension, srid, type
FROM geometry_columns
WHERE f_table_name = 'poi';-- 手动更新元数据(不建议)
-- SELECT Populate_Geometry_Columns();
3.7.2 spatial_ref_sys 表
-- 查看可用的空间参考系统
SELECT srid, auth_name, auth_srid, srtext
FROM spatial_ref_sys
WHERE srid IN (4326, 3857, 4490);-- 搜索特定投影
SELECT srid, auth_name || ':' || auth_srid AS code, srtext
FROM spatial_ref_sys
WHERE srtext LIKE '%China%' OR srtext LIKE '%Beijing%'
LIMIT 10;-- 添加自定义空间参考系统
INSERT INTO spatial_ref_sys (srid, auth_name, auth_srid, srtext, proj4text)
VALUES (990000,'CUSTOM',990000,'PROJCS["Custom CRS", ...]','+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs'
);
3.8 类型转换最佳实践
3.8.1 类型转换函数
-- 几何类型转换
-- 点转多点
SELECT ST_Multi(ST_MakePoint(116.4, 39.9));-- 线转多线
SELECT ST_Multi(ST_MakeLine(ST_MakePoint(0, 0), ST_MakePoint(1, 1)));-- 强制转换为 2D
SELECT ST_Force2D(ST_SetSRID(ST_MakePoint(116.4, 39.9, 45.5), 4326));-- 强制转换为 3D
SELECT ST_Force3D(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326));-- 强制转换为 4D
SELECT ST_Force4D(ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326));-- 转换几何类型
SELECT ST_CurveToLine(ST_GeomFromText('CIRCULARSTRING(0 0, 1 1, 2 0)', 4326));
3.8.2 安全转换
-- 安全的类型转换(带错误处理)
CREATE OR REPLACE FUNCTION safe_geom_cast(geom geometry, target_type text)
RETURNS geometry AS $$
DECLAREresult geometry;
BEGINIF GeometryType(geom) = upper(target_type) THENRETURN geom;END IF;CASE upper(target_type)WHEN 'POINT' THENresult := ST_Centroid(geom);WHEN 'LINESTRING' THENresult := ST_Boundary(geom);WHEN 'POLYGON' THENIF GeometryType(geom) = 'LINESTRING' AND ST_IsClosed(geom) THENresult := ST_MakePolygon(geom);ELSEresult := ST_ConvexHull(geom);END IF;WHEN 'MULTIPOINT' THENresult := ST_Multi(ST_Centroid(geom));WHEN 'MULTILINESTRING' THENresult := ST_Multi(ST_Boundary(geom));WHEN 'MULTIPOLYGON' THENresult := ST_Multi(ST_ConvexHull(geom));ELSERAISE EXCEPTION 'Unsupported target type: %', target_type;END CASE;RETURN result;
END;
$$ LANGUAGE plpgsql;
3.9 本章小结
本章详细介绍了 PostGIS 的空间数据类型:
- 类型概述:了解了 Geometry、Geography、Raster 等类型
- Geometry 类型:深入学习了 Point、LineString、Polygon 等几何类型
- Geography 类型:理解了球面计算的优势和使用场景
- 坐标维度:掌握了 2D、3D、4D 几何的使用方法
- 类型约束:学习了几何类型和 SRID 约束的实现
- 存储格式:了解了 EWKB 格式和存储优化方法
- 空间元数据:掌握了 geometry_columns 和 spatial_ref_sys 的使用
3.10 下一步
在下一章中,我们将学习空间参考系统,包括:
- SRID 详解
- 常用坐标系统
- 坐标转换方法
- 中国常用坐标系
相关资源:
- PostGIS 几何类型文档
- OGC Simple Features 规范
- EPSG 坐标系统注册