postgis

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

PostGIS 3.6.1 Spatial Database

PostGIS 3.6.1 空间数据库

Overview

概述

PostGIS 3.6.1 (with GEOS 3.14) brings significant improvements: ST_CoverageClean for topology repair, enhanced SFCGAL 3D operations, bigint topology support for massive datasets, and improved PostgreSQL 18 integration. This skill ensures you leverage these capabilities correctly.
Core principle: Spatial is special. Generic database patterns often fail with geographic data.
Announce at start: "I'm applying postgis to ensure PostGIS 3.6.1 spatial best practices."
PostGIS 3.6.1(搭配GEOS 3.14)带来了多项重大改进:用于拓扑修复的ST_CoverageClean、增强的SFCGAL 3D运算、支持海量数据集的bigint拓扑,以及与PostgreSQL 18的集成优化。本规范确保你能正确利用这些功能。
核心原则:空间数据具有特殊性。通用的数据库模式在处理地理数据时常会失效。
启动时声明:"我正在应用postgis规范以确保遵循PostGIS 3.6.1的空间最佳实践。"

When This Skill Applies

本规范的适用场景

This skill is MANDATORY when ANY of these patterns are touched:
PatternExamples
**/*geo*
models/geography.ts, geo_utils.py
**/*spatial*
lib/spatial.ts
**/*location*
services/locationService.ts
**/*coordinate*
types/coordinates.ts
**/*polygon*
db/polygons.sql
**/*geometry*
migrations/add_geometry.sql
**/*postgis*
setup/postgis.sql
**/*gis*
utils/gis.ts
Or when files contain:
sql
-- These patterns trigger this skill
ST_*
geography
geometry
SRID
当涉及以下任何模式时,必须遵循本规范:
模式示例
**/*geo*
models/geography.ts, geo_utils.py
**/*spatial*
lib/spatial.ts
**/*location*
services/locationService.ts
**/*coordinate*
types/coordinates.ts
**/*polygon*
db/polygons.sql
**/*geometry*
migrations/add_geometry.sql
**/*postgis*
setup/postgis.sql
**/*gis*
utils/gis.ts
或者当文件包含以下内容时:
sql
-- 这些模式会触发本规范
ST_*
geography
geometry
SRID

PostGIS 3.6.1 Features

PostGIS 3.6.1 功能特性

1. ST_CoverageClean (New in 3.6.1)

1. ST_CoverageClean(3.6.1新增)

Coverage cleaning repairs topological errors in polygon collections. Requires GEOS 3.14:
sql
-- Clean a set of polygons that should form a seamless coverage
-- Fixes: overlaps, gaps, edge inconsistencies
SELECT ST_CoverageClean(
  ARRAY[polygon1, polygon2, polygon3]::geometry[]
) AS cleaned_polygons;

-- Use case: Administrative boundaries, parcels, zones
-- Before: Manual repair with ST_MakeValid, ST_SnapToGrid
-- After: Single function handles entire coverage

-- Example: Clean municipal boundaries
WITH boundaries AS (
  SELECT geom FROM municipalities
)
SELECT ST_CoverageClean(array_agg(geom))
FROM boundaries;
When to use:
  • Importing GIS data with topological errors
  • Merging datasets from different sources
  • Ensuring seamless coverage (no gaps/overlaps)
  • Cadastral/parcel data management
Coverage清理可修复多边形集合中的拓扑错误,需要搭配GEOS 3.14:
sql
-- 清理应形成无缝覆盖的多边形集合
-- 修复:重叠、间隙、边不一致等问题
SELECT ST_CoverageClean(
  ARRAY[polygon1, polygon2, polygon3]::geometry[]
) AS cleaned_polygons;

-- 适用场景:行政边界、地块、区域
-- 之前:使用ST_MakeValid、ST_SnapToGrid手动修复
-- 现在:单个函数即可处理整个覆盖区域

-- 示例:清理市政边界
WITH boundaries AS (
  SELECT geom FROM municipalities
)
SELECT ST_CoverageClean(array_agg(geom))
FROM boundaries;
适用时机:
  • 导入存在拓扑错误的GIS数据
  • 合并来自不同数据源的数据集
  • 确保无缝覆盖(无间隙/重叠)
  • 地籍/地块数据管理

2. SFCGAL 3D Functions

2. SFCGAL 3D函数

PostGIS 3.6.1 includes enhanced SFCGAL support for 3D operations:
sql
-- Enable SFCGAL (if not already enabled)
CREATE EXTENSION IF NOT EXISTS postgis_sfcgal;

-- 3D intersection (true 3D, not projection)
SELECT ST_3DIntersection(
  ST_GeomFromText('POLYHEDRALSURFACE Z (...)'),
  ST_GeomFromText('POLYHEDRALSURFACE Z (...)')
);

-- 3D union
SELECT ST_3DUnion(geom1, geom2);

-- 3D area (actual surface area in 3D)
SELECT ST_3DArea(polyhedral_surface);

-- Minkowski sum (for buffer-like operations in 3D)
SELECT ST_MinkowskiSum(geometry1, geometry2);

-- Straight skeleton (for building roofs, etc.)
SELECT ST_StraightSkeleton(polygon);

-- Extrude 2D to 3D
SELECT ST_Extrude(polygon, 0, 0, height);
Use cases:
  • Building/structure modeling
  • Underground infrastructure
  • Airspace management
  • 3D terrain analysis
PostGIS 3.6.1增强了对SFCGAL的支持,可实现3D运算:
sql
-- 启用SFCGAL(如果尚未启用)
CREATE EXTENSION IF NOT EXISTS postgis_sfcgal;

-- 3D交集(真正的3D运算,而非投影)
SELECT ST_3DIntersection(
  ST_GeomFromText('POLYHEDRALSURFACE Z (...)'),
  ST_GeomFromText('POLYHEDRALSURFACE Z (...)')
);

-- 3D并集
SELECT ST_3DUnion(geom1, geom2);

-- 3D面积(3D空间中的实际表面积)
SELECT ST_3DArea(polyhedral_surface);

-- 闵可夫斯基和(用于3D中的类缓冲区运算)
SELECT ST_MinkowskiSum(geometry1, geometry2);

-- 直线骨架(用于建筑屋顶建模等)
SELECT ST_StraightSkeleton(polygon);

-- 将2D几何拉伸为3D
SELECT ST_Extrude(polygon, 0, 0, height);
适用场景:
  • 建筑/结构建模
  • 地下基础设施
  • 空域管理
  • 3D地形分析

3. Bigint Topology Support

3. Bigint拓扑支持

PostGIS 3.6.1 supports bigint topology IDs for massive datasets:
sql
-- Create topology with bigint IDs (new in 3.6.1)
SELECT CreateTopology('massive_parcels', 4326, 0.0000001, true);
-- Last parameter: use_bigint = true

-- Supports > 2 billion features per topology
-- Previous limit: ~2 billion (int4 max)

-- Add layer
SELECT AddTopoGeometryColumn('massive_parcels', 'public', 'parcels', 'topogeom', 'POLYGON');

-- TopoGeometry operations work the same
SELECT ST_CreateTopoGeo('massive_parcels', geom);
When to use:
  • National/continental scale datasets
  • High-resolution parcel data
  • OpenStreetMap imports
  • Any topology > 2 billion edges
PostGIS 3.6.1支持使用bigint拓扑ID处理海量数据集:
sql
-- 创建使用bigint ID的拓扑(3.6.1新增)
SELECT CreateTopology('massive_parcels', 4326, 0.0000001, true);
-- 最后一个参数:use_bigint = true

-- 支持每个拓扑超过20亿个要素
-- 之前的限制:约20亿(int4最大值)

-- 添加图层
SELECT AddTopoGeometryColumn('massive_parcels', 'public', 'parcels', 'topogeom', 'POLYGON');

-- TopoGeometry运算方式保持不变
SELECT ST_CreateTopoGeo('massive_parcels', geom);
适用时机:
  • 国家/大陆级数据集
  • 高分辨率地块数据
  • OpenStreetMap导入
  • 任何拓扑要素超过20亿条边的场景

4. PostgreSQL 18 Interrupt Handling

4. PostgreSQL 18中断处理

PostGIS 3.6.1 properly handles PostgreSQL 18's improved query cancellation:
sql
-- Long-running spatial operations can now be cancelled cleanly
-- No more orphaned locks or corrupted state

-- Example: Cancellable heavy operation
SELECT ST_Union(geom)
FROM very_large_table
GROUP BY region;
-- ^C now works properly

-- COPY operations with PostGIS also respect cancellation
COPY (SELECT id, ST_AsGeoJSON(geom) FROM features) TO '/tmp/export.json';
PostGIS 3.6.1可正确处理PostgreSQL 18改进后的查询取消机制:
sql
-- 长时间运行的空间运算现在可被正常取消
-- 不再出现孤立锁或状态损坏

-- 示例:可取消的重型运算
SELECT ST_Union(geom)
FROM very_large_table
GROUP BY region;
-- 现在按^C可正常取消

-- 搭配PostGIS的COPY运算也支持取消
COPY (SELECT id, ST_AsGeoJSON(geom) FROM features) TO '/tmp/export.json';

Data Types

数据类型

Geometry vs Geography

Geometry vs Geography

sql
-- GEOMETRY: Planar coordinates, any SRID
-- Faster computations, less accurate over large distances
CREATE TABLE places_geometry (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  location geometry(Point, 4326)  -- WGS84
);

-- GEOGRAPHY: Spherical coordinates, always WGS84
-- Accurate distances/areas, slower computations
CREATE TABLE places_geography (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  location geography(Point, 4326)  -- Always WGS84
);

-- When to use GEOMETRY:
-- - Local/city-scale applications
-- - Need complex operations (union, intersection)
-- - Performance critical
-- - Non-earth data (game maps, floor plans)

-- When to use GEOGRAPHY:
-- - Global applications
-- - Distance/area accuracy matters
-- - Simple operations (distance, contains)
-- - User-facing distance calculations
sql
-- GEOMETRY:平面坐标,支持任意SRID
-- 运算速度更快,大距离下精度较低
CREATE TABLE places_geometry (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  location geometry(Point, 4326)  -- WGS84坐标系
);

-- GEOGRAPHY:球面坐标,始终使用WGS84
-- 距离/面积计算准确,运算速度较慢
CREATE TABLE places_geography (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  location geography(Point, 4326)  -- 始终为WGS84
);

-- 何时使用GEOMETRY:
-- - 本地/城市级应用
-- - 需要复杂运算(并集、交集)
-- - 性能优先
-- - 非地球数据(游戏地图、楼层平面图)

-- 何时使用GEOGRAPHY:
-- - 全球级应用
-- - 距离/面积精度要求高
-- - 简单运算(距离、包含)
-- - 用户可见的距离计算

Choosing SRID

SRID选择

sql
-- Common SRIDs:
-- 4326: WGS84 (GPS coordinates, web maps)
-- 3857: Web Mercator (tile-based web maps, display only)
-- Local projections for accurate measurements

-- ALWAYS store in 4326 (WGS84) as source of truth
-- Transform for calculations when needed

CREATE TABLE locations (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  name text NOT NULL,
  location geography(Point, 4326),  -- Storage
  location_local geometry(Point)     -- NULL, computed as needed
);

-- Transform for local calculations
SELECT ST_Transform(
  location::geometry,
  32610  -- UTM Zone 10N (California)
) FROM locations WHERE name = 'San Francisco';
sql
-- 常见SRID:
-- 4326:WGS84(GPS坐标、Web地图)
-- 3857:Web墨卡托(瓦片式Web地图,仅用于展示)
-- 本地投影坐标系(用于精确测量)

-- 始终以4326(WGS84)作为存储的基准坐标系
-- 必要时转换坐标系进行运算

CREATE TABLE locations (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  name text NOT NULL,
  location geography(Point, 4326),  -- 存储用
  location_local geometry(Point)     -- 初始为NULL,按需计算
);

-- 转换为本地坐标系进行计算
SELECT ST_Transform(
  location::geometry,
  32610  -- UTM 10N区(加利福尼亚州)
) FROM locations WHERE name = 'San Francisco';

Index Strategy

索引策略

Spatial Indexes

空间索引

sql
-- GiST index: Default for most spatial queries
CREATE INDEX idx_locations_geom ON locations USING gist(location);

-- BRIN index: For very large, naturally ordered datasets
-- (e.g., GPS tracks ordered by time)
CREATE INDEX idx_tracks_geom ON gps_tracks USING brin(location);

-- SP-GiST: For non-overlapping data (points, IP ranges)
CREATE INDEX idx_points_spgist ON points USING spgist(location);
sql
-- GiST索引:大多数空间查询的默认选择
CREATE INDEX idx_locations_geom ON locations USING gist(location);

-- BRIN索引:适用于超大且自然有序的数据集
-- (例如:按时间排序的GPS轨迹)
CREATE INDEX idx_tracks_geom ON gps_tracks USING brin(location);

-- SP-GiST索引:适用于非重叠数据(点、IP范围)
CREATE INDEX idx_points_spgist ON points USING spgist(location);

Index Best Practices

索引最佳实践

sql
-- Always include spatial index
CREATE TABLE features (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  geom geometry(Polygon, 4326),
  created_at timestamptz DEFAULT now()
);
CREATE INDEX idx_features_geom ON features USING gist(geom);

-- Partial spatial index for active records
CREATE INDEX idx_features_geom_active ON features USING gist(geom)
  WHERE deleted_at IS NULL;

-- Composite index for common query patterns
CREATE INDEX idx_features_type_geom ON features USING gist(geom)
  WHERE feature_type = 'building';
sql
-- 始终为几何列创建空间索引
CREATE TABLE features (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  geom geometry(Polygon, 4326),
  created_at timestamptz DEFAULT now()
);
CREATE INDEX idx_features_geom ON features USING gist(geom);

-- 为活跃记录创建部分空间索引
CREATE INDEX idx_features_geom_active ON features USING gist(geom)
  WHERE deleted_at IS NULL;

-- 为常见查询模式创建复合索引
CREATE INDEX idx_features_type_geom ON features USING gist(geom)
  WHERE feature_type = 'building';

Index Clustering

索引聚类

sql
-- Cluster table by spatial index for range query performance
CLUSTER features USING idx_features_geom;

-- For large tables, recluster periodically
-- Schedule during maintenance window
sql
-- 按空间索引聚类表以提升范围查询性能
CLUSTER features USING idx_features_geom;

-- 对于大表,定期重新聚类
-- 在维护窗口执行

Query Patterns

查询模式

Distance Queries

距离查询

sql
-- Find points within distance (geography, in meters)
SELECT * FROM locations
WHERE ST_DWithin(
  location,
  ST_MakePoint(-122.4194, 37.7749)::geography,
  1000  -- 1km radius
);

-- Find points within distance (geometry, in SRID units)
SELECT * FROM locations
WHERE ST_DWithin(
  location,
  ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326),
  0.01  -- ~1km at this latitude (degrees)
);

-- K-nearest neighbors (KNN)
SELECT *, location <-> ST_MakePoint(-122.4194, 37.7749)::geography AS distance
FROM locations
ORDER BY location <-> ST_MakePoint(-122.4194, 37.7749)::geography
LIMIT 10;
-- Uses index for efficient KNN
sql
-- 查找指定距离内的点(使用geography,单位为米)
SELECT * FROM locations
WHERE ST_DWithin(
  location,
  ST_MakePoint(-122.4194, 37.7749)::geography,
  1000  -- 1公里半径
);

-- 查找指定距离内的点(使用geometry,单位为SRID对应单位)
SELECT * FROM locations
WHERE ST_DWithin(
  location,
  ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326),
  0.01  -- 此纬度下约1公里(度数)
);

-- K近邻查询(KNN)
SELECT *, location <-> ST_MakePoint(-122.4194, 37.7749)::geography AS distance
FROM locations
ORDER BY location <-> ST_MakePoint(-122.4194, 37.7749)::geography
LIMIT 10;
-- 利用索引实现高效KNN查询

Containment Queries

包含查询

sql
-- Points within polygon
SELECT * FROM points
WHERE ST_Within(location, (
  SELECT boundary FROM regions WHERE name = 'California'
));

-- Polygon contains point
SELECT * FROM regions
WHERE ST_Contains(boundary, ST_MakePoint(-122.4194, 37.7749));

-- Intersects (overlaps in any way)
SELECT * FROM features
WHERE ST_Intersects(geom, query_polygon);
sql
-- 多边形内的点
SELECT * FROM points
WHERE ST_Within(location, (
  SELECT boundary FROM regions WHERE name = 'California'
));

-- 包含点的多边形
SELECT * FROM regions
WHERE ST_Contains(boundary, ST_MakePoint(-122.4194, 37.7749));

-- 相交查询(任意方式重叠)
SELECT * FROM features
WHERE ST_Intersects(geom, query_polygon);

Aggregation

聚合运算

sql
-- Union all geometries
SELECT ST_Union(geom) FROM parcels WHERE owner = 'City';

-- Collect without merging (faster, preserves individual geometries)
SELECT ST_Collect(geom) FROM parcels WHERE owner = 'City';

-- Extent (bounding box)
SELECT ST_Extent(geom) FROM features;

-- Centroid of all points
SELECT ST_Centroid(ST_Collect(location)) FROM locations;
sql
-- 合并所有几何
SELECT ST_Union(geom) FROM parcels WHERE owner = 'City';

-- 收集几何但不合并(速度更快,保留单个几何)
SELECT ST_Collect(geom) FROM parcels WHERE owner = 'City';

-- 范围(边界框)
SELECT ST_Extent(geom) FROM features;

-- 所有点的质心
SELECT ST_Centroid(ST_Collect(location)) FROM locations;

GeoJSON Integration

GeoJSON集成

Import/Export

导入/导出

sql
-- Geometry to GeoJSON
SELECT ST_AsGeoJSON(location) FROM locations WHERE id = $1;

-- Geometry with properties to Feature
SELECT jsonb_build_object(
  'type', 'Feature',
  'geometry', ST_AsGeoJSON(location)::jsonb,
  'properties', jsonb_build_object(
    'id', id,
    'name', name
  )
) FROM locations WHERE id = $1;

-- FeatureCollection
SELECT jsonb_build_object(
  'type', 'FeatureCollection',
  'features', jsonb_agg(
    jsonb_build_object(
      'type', 'Feature',
      'geometry', ST_AsGeoJSON(location)::jsonb,
      'properties', jsonb_build_object('id', id, 'name', name)
    )
  )
) FROM locations;

-- GeoJSON to Geometry
INSERT INTO locations (name, location)
VALUES ('New Place', ST_GeomFromGeoJSON($1));

-- With SRID enforcement
INSERT INTO locations (name, location)
VALUES ('New Place', ST_SetSRID(ST_GeomFromGeoJSON($1), 4326));
sql
-- 几何转GeoJSON
SELECT ST_AsGeoJSON(location) FROM locations WHERE id = $1;

-- 带属性的几何转Feature
SELECT jsonb_build_object(
  'type', 'Feature',
  'geometry', ST_AsGeoJSON(location)::jsonb,
  'properties', jsonb_build_object(
    'id', id,
    'name', name
  )
) FROM locations WHERE id = $1;

-- FeatureCollection
SELECT jsonb_build_object(
  'type', 'FeatureCollection',
  'features', jsonb_agg(
    jsonb_build_object(
      'type', 'Feature',
      'geometry', ST_AsGeoJSON(location)::jsonb,
      'properties', jsonb_build_object('id', id, 'name', name)
    )
  )
) FROM locations;

-- GeoJSON转几何
INSERT INTO locations (name, location)
VALUES ('New Place', ST_GeomFromGeoJSON($1));

-- 强制指定SRID
INSERT INTO locations (name, location)
VALUES ('New Place', ST_SetSRID(ST_GeomFromGeoJSON($1), 4326));

API Response Pattern

API响应模式

sql
-- Function for API endpoints
CREATE OR REPLACE FUNCTION get_locations_geojson(
  bounds geometry DEFAULT NULL
)
RETURNS jsonb AS $$
SELECT jsonb_build_object(
  'type', 'FeatureCollection',
  'features', COALESCE(jsonb_agg(
    jsonb_build_object(
      'type', 'Feature',
      'id', id,
      'geometry', ST_AsGeoJSON(location, 6)::jsonb,  -- 6 decimal places
      'properties', jsonb_build_object(
        'name', name,
        'created_at', created_at
      )
    )
  ), '[]'::jsonb)
)
FROM locations
WHERE bounds IS NULL OR ST_Intersects(location::geometry, bounds);
$$ LANGUAGE sql STABLE;
sql
-- 用于API端点的函数
CREATE OR REPLACE FUNCTION get_locations_geojson(
  bounds geometry DEFAULT NULL
)
RETURNS jsonb AS $$
SELECT jsonb_build_object(
  'type', 'FeatureCollection',
  'features', COALESCE(jsonb_agg(
    jsonb_build_object(
      'type', 'Feature',
      'id', id,
      'geometry', ST_AsGeoJSON(location, 6)::jsonb,  -- 6位小数
      'properties', jsonb_build_object(
        'name', name,
        'created_at', created_at
      )
    )
  ), '[]'::jsonb)
)
FROM locations
WHERE bounds IS NULL OR ST_Intersects(location::geometry, bounds);
$$ LANGUAGE sql STABLE;

Validation and Repair

验证与修复

Validate Geometries

验证几何有效性

sql
-- Check validity
SELECT id, ST_IsValid(geom), ST_IsValidReason(geom)
FROM features
WHERE NOT ST_IsValid(geom);

-- Common issues:
-- "Self-intersection"
-- "Ring Self-intersection"
-- "Too few points in geometry component"
-- "Hole lies outside shell"
sql
-- 检查有效性
SELECT id, ST_IsValid(geom), ST_IsValidReason(geom)
FROM features
WHERE NOT ST_IsValid(geom);

-- 常见问题:
-- "Self-intersection"(自相交)
-- "Ring Self-intersection"(环自相交)
-- "Too few points in geometry component"(几何组件点数不足)
-- "Hole lies outside shell"(洞位于外壳外部)

Repair Geometries

修复几何

sql
-- Simple repair (handles most issues)
UPDATE features
SET geom = ST_MakeValid(geom)
WHERE NOT ST_IsValid(geom);

-- Repair with specific strategy
UPDATE features
SET geom = ST_MakeValid(geom, 'method=structure')
WHERE NOT ST_IsValid(geom);

-- Coverage clean for polygon sets (3.6.1)
WITH cleaned AS (
  SELECT unnest(ST_CoverageClean(array_agg(geom ORDER BY id))) AS geom
  FROM parcels
)
UPDATE parcels p
SET geom = c.geom
FROM cleaned c
WHERE ST_Intersects(p.geom, c.geom);

-- Snap to grid for precision issues
UPDATE features
SET geom = ST_SnapToGrid(geom, 0.000001)
WHERE ST_NPoints(geom) > 1000;  -- High-detail features
sql
-- 简单修复(处理大多数问题)
UPDATE features
SET geom = ST_MakeValid(geom)
WHERE NOT ST_IsValid(geom);

-- 使用特定策略修复
UPDATE features
SET geom = ST_MakeValid(geom, 'method=structure')
WHERE NOT ST_IsValid(geom);

-- 对多边形集合进行Coverage清理(3.6.1)
WITH cleaned AS (
  SELECT unnest(ST_CoverageClean(array_agg(geom ORDER BY id))) AS geom
  FROM parcels
)
UPDATE parcels p
SET geom = c.geom
FROM cleaned c
WHERE ST_Intersects(p.geom, c.geom);

-- 对齐到网格以解决精度问题
UPDATE features
SET geom = ST_SnapToGrid(geom, 0.000001)
WHERE ST_NPoints(geom) > 1000;  -- 高细节要素

Performance Optimization

性能优化

Query Optimization

查询优化

sql
-- Use && for bounding box pre-filter
SELECT * FROM features
WHERE geom && ST_MakeEnvelope(-122.5, 37.7, -122.4, 37.8, 4326)
  AND ST_Intersects(geom, query_polygon);

-- Simplify for display (reduces transfer size)
SELECT id, ST_Simplify(geom, 0.0001) AS geom_display
FROM features;

-- Viewport-aware simplification
SELECT id,
  CASE
    WHEN zoom < 10 THEN ST_Simplify(geom, 0.01)
    WHEN zoom < 14 THEN ST_Simplify(geom, 0.001)
    ELSE geom
  END AS geom
FROM features
WHERE geom && viewport_bounds;
sql
-- 使用&&进行边界框预过滤
SELECT * FROM features
WHERE geom && ST_MakeEnvelope(-122.5, 37.7, -122.4, 37.8, 4326)
  AND ST_Intersects(geom, query_polygon);

-- 简化几何用于展示(减少传输数据量)
SELECT id, ST_Simplify(geom, 0.0001) AS geom_display
FROM features;

-- 视口感知的简化
SELECT id,
  CASE
    WHEN zoom < 10 THEN ST_Simplify(geom, 0.01)
    WHEN zoom < 14 THEN ST_Simplify(geom, 0.001)
    ELSE geom
  END AS geom
FROM features
WHERE geom && viewport_bounds;

Table Design for Spatial

空间数据表设计

sql
-- Separate geometry from attributes for large tables
CREATE TABLE features (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  name text NOT NULL,
  category text,
  metadata jsonb DEFAULT '{}',
  created_at timestamptz DEFAULT now()
);

CREATE TABLE feature_geometries (
  feature_id uuid PRIMARY KEY REFERENCES features(id) ON DELETE CASCADE,
  geom geometry(Geometry, 4326),
  geom_simplified geometry(Geometry, 4326)  -- Pre-computed simplification
);

CREATE INDEX idx_feature_geom ON feature_geometries USING gist(geom);
CREATE INDEX idx_feature_geom_simple ON feature_geometries USING gist(geom_simplified);
sql
-- 对于大表,将几何与属性分离
CREATE TABLE features (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  name text NOT NULL,
  category text,
  metadata jsonb DEFAULT '{}',
  created_at timestamptz DEFAULT now()
);

CREATE TABLE feature_geometries (
  feature_id uuid PRIMARY KEY REFERENCES features(id) ON DELETE CASCADE,
  geom geometry(Geometry, 4326),
  geom_simplified geometry(Geometry, 4326)  -- 预计算的简化几何
);

CREATE INDEX idx_feature_geom ON feature_geometries USING gist(geom);
CREATE INDEX idx_feature_geom_simple ON feature_geometries USING gist(geom_simplified);

Materialized Views for Complex Queries

复杂查询的物化视图

sql
-- Pre-computed spatial joins
CREATE MATERIALIZED VIEW feature_regions AS
SELECT f.id AS feature_id, r.id AS region_id, r.name AS region_name
FROM features f
JOIN regions r ON ST_Within(f.location, r.boundary);

CREATE UNIQUE INDEX idx_feature_regions ON feature_regions(feature_id);

-- Refresh periodically
REFRESH MATERIALIZED VIEW CONCURRENTLY feature_regions;
sql
-- 预计算空间连接
CREATE MATERIALIZED VIEW feature_regions AS
SELECT f.id AS feature_id, r.id AS region_id, r.name AS region_name
FROM features f
JOIN regions r ON ST_Within(f.location, r.boundary);

CREATE UNIQUE INDEX idx_feature_regions ON feature_regions(feature_id);

-- 定期刷新
REFRESH MATERIALIZED VIEW CONCURRENTLY feature_regions;

Migration Patterns

迁移模式

Adding Spatial Column

添加空间列

sql
-- Step 1: Add column
ALTER TABLE locations ADD COLUMN geom geometry(Point, 4326);

-- Step 2: Create index
CREATE INDEX CONCURRENTLY idx_locations_geom ON locations USING gist(geom);

-- Step 3: Backfill from lat/lng
UPDATE locations
SET geom = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)
WHERE geom IS NULL AND latitude IS NOT NULL;

-- Step 4: Add constraint if needed
ALTER TABLE locations ADD CONSTRAINT locations_geom_4326
  CHECK (ST_SRID(geom) = 4326);
sql
-- 步骤1:添加列
ALTER TABLE locations ADD COLUMN geom geometry(Point, 4326);

-- 步骤2:创建索引
CREATE INDEX CONCURRENTLY idx_locations_geom ON locations USING gist(geom);

-- 步骤3:从经纬度回填数据
UPDATE locations
SET geom = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)
WHERE geom IS NULL AND latitude IS NOT NULL;

-- 步骤4:按需添加约束
ALTER TABLE locations ADD CONSTRAINT locations_geom_4326
  CHECK (ST_SRID(geom) = 4326);

Converting Geometry to Geography

将Geometry转换为Geography

sql
-- Create new column
ALTER TABLE locations ADD COLUMN location_geo geography(Point, 4326);

-- Migrate data
UPDATE locations
SET location_geo = location::geography
WHERE location_geo IS NULL;

-- Create index on new column
CREATE INDEX CONCURRENTLY idx_locations_geo ON locations USING gist(location_geo);

-- Update application, then drop old column
ALTER TABLE locations DROP COLUMN location;
ALTER TABLE locations RENAME COLUMN location_geo TO location;
sql
-- 创建新列
ALTER TABLE locations ADD COLUMN location_geo geography(Point, 4326);

-- 迁移数据
UPDATE locations
SET location_geo = location::geography
WHERE location_geo IS NULL;

-- 为新列创建索引
CREATE INDEX CONCURRENTLY idx_locations_geo ON locations USING gist(location_geo);

-- 更新应用,然后删除旧列
ALTER TABLE locations DROP COLUMN location;
ALTER TABLE locations RENAME COLUMN location_geo TO location;

PostGIS Artifact

PostGIS 实现文档

When implementing spatial features, post this artifact:
markdown
<!-- POSTGIS_IMPLEMENTATION:START -->
在实现空间功能时,需添加以下文档:
markdown
<!-- POSTGIS_IMPLEMENTATION:START -->

PostGIS Implementation Summary

PostGIS 实现摘要

Spatial Columns

空间列

TableColumnTypeSRIDIndex
locationslocationgeography(Point)4326gist
parcelsboundarygeometry(Polygon)4326gist
表名列名类型SRID索引
locationslocationgeography(Point)4326gist
parcelsboundarygeometry(Polygon)4326gist

PostGIS 3.6.1 Features Used

使用的PostGIS 3.6.1特性

  • ST_CoverageClean for topology repair
  • SFCGAL 3D functions
  • Bigint topology
  • PostgreSQL 18 interrupt handling
  • 用于拓扑修复的ST_CoverageClean
  • SFCGAL 3D函数
  • Bigint拓扑
  • PostgreSQL 18中断处理

Spatial Queries

空间查询

Query PatternIndex UsedPerformance
KNN distanceYes (gist)<10ms
ST_Within regionYes (gist)<50ms
ST_IntersectsYes (gist)<100ms
查询模式使用索引性能
KNN距离查询是(gist)<10ms
ST_Within区域查询是(gist)<50ms
ST_Intersects相交查询是(gist)<100ms

Validation

验证

  • All geometries pass ST_IsValid
  • SRID constraints enforced
  • Spatial indexes created
  • Query patterns tested with EXPLAIN ANALYZE
PostGIS Version: 3.6.1 GEOS Version: 3.14.x Verified At: [timestamp]
<!-- POSTGIS_IMPLEMENTATION:END -->
undefined
  • 所有几何通过ST_IsValid验证
  • 强制SRID约束
  • 创建空间索引
  • 使用EXPLAIN ANALYZE测试查询模式
PostGIS版本: 3.6.1 GEOS版本: 3.14.x 验证时间: [时间戳]
<!-- POSTGIS_IMPLEMENTATION:END -->
undefined

Checklist

检查清单

Before completing PostGIS implementation:
  • Correct data type chosen (geometry vs geography)
  • SRID is consistent (4326 recommended for storage)
  • Spatial indexes created on all geometry columns
  • Input geometries validated (ST_IsValid)
  • GeoJSON import/export tested
  • Query performance verified with EXPLAIN ANALYZE
  • PostGIS 3.6.1 features leveraged where appropriate
  • Artifact posted to issue
完成PostGIS实现前需确认:
  • 选择了正确的数据类型(geometry vs geography)
  • SRID保持一致(推荐存储使用4326)
  • 所有几何列均已创建空间索引
  • 输入几何已通过ST_IsValid验证
  • GeoJSON导入/导出已测试
  • 使用EXPLAIN ANALYZE验证查询性能
  • 已按需利用PostGIS 3.6.1的特性
  • 已在问题中添加实现文档

Integration

集成

This skill integrates with:
  • database-architecture
    - Spatial columns follow general schema patterns
  • postgres-rls
    - RLS policies can use spatial predicates
  • timescaledb
    - Time-series with spatial dimensions
本规范可与以下规范集成:
  • database-architecture
    - 空间列遵循通用 schema 模式
  • postgres-rls
    - RLS策略可使用空间谓词
  • timescaledb
    - 带空间维度的时间序列数据

References

参考资料