mcpField
P
postgis

postgis

github.com/troykellyデータ・データベース1259 回インストール42 週間前に更新
リポジトリ

postgis について

PostGIS を扱う開発者向けに、空間データの設計、クエリ、インデックス、保守を案内するスキルです。PostGIS 3.6.1 に関連する機能を含み、型の選択、ジオメトリの検証・修復、3D 操作、トポロジ処理を扱います。

主な機能

  1. 01geometry と geography の選択指針
  2. 02空間インデックスとクエリのパターン
  3. 03ジオメトリの検証と修復
  4. 04ST_CoverageClean と SFCGAL の 3D 操作
  5. 05マイグレーションと GeoJSON のパターン

活用シーン

  1. 01空間カラムを持つデータベーステーブルの設計
  2. 02ポリゴンカバレッジのトポロジエラー修復
  3. 033D 交差演算や地形分析の実行
  4. 04地理クエリと GeoJSON API の構築

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/troykelly/claude-skills --skill postgis

インストール後、Claude Code で/postgisを入力して読み込まれたか確認します。

詳細

PostGIS 3.6.1 空間データベース

概要

PostGIS 3.6.1(GEOS 3.14 搭載)では、トポロジ修復用の ST_CoverageClean、強化された SFCGAL の 3D 操作、大規模データセット向けの bigint トポロジサポート、PostgreSQL 18 との統合の改善など、大幅な機能強化が行われています。このスキルを使うことで、これらの機能を正しく活用できます。

基本原則: 空間データは特殊です。一般的なデータベースのパターンは、地理データではうまく機能しないことがよくあります。

開始時に宣言: 「PostGIS 3.6.1 の空間データに関するベストプラクティスを適用するため、postgis を適用します。」

このスキルを適用する場合

次のパターンのいずれかに該当する場合、このスキルは必須です。

パターン例
**/*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

または、ファイルに次の内容が含まれる場合:

-- These patterns trigger this skill
ST_*
geography
geometry
SRID

PostGIS 3.6.1 の機能

1. ST_CoverageClean(3.6.1 の新機能)

カバレッジのクリーニングにより、ポリゴンコレクションのトポロジエラーを修復できます。GEOS 3.14 が必要です。

-- 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;

使用する場面:

  • トポロジエラーを含む GIS データをインポートする場合
  • 異なるソースのデータセットをマージする場合
  • 隙間や重なりのないシームレスなカバレッジを確保する場合
  • 地籍/区画データを管理する場合
2. SFCGAL 3D 関数

PostGIS 3.6.1 には、3D 操作をサポートする強化版の SFCGAL が含まれています。

-- 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);

使用例:

  • 建物/構造物のモデリング
  • 地下インフラ
  • 空域管理
  • 3D 地形分析
3. Bigint トポロジサポート

PostGIS 3.6.1 は、大規模データセット向けに bigint のトポロジ ID をサポートしています。

-- 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);

使用する場面:

  • 国全体/大陸規模のデータセット
  • 高解像度の区画データ
  • OpenStreetMap のインポート
  • トポロジのエッジ数が 20 億を超える場合
4. PostgreSQL 18 の割り込み処理

PostGIS 3.6.1 は、PostgreSQL 18 で改善されたクエリキャンセルを適切に処理します。

-- 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';

データ型

Geometry と Geography の比較
-- 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
SRID の選択
-- 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';

インデックス戦略

空間インデックス
-- 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);
インデックスのベストプラクティス
-- 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';
インデックスのクラスタリング
-- Cluster table by spatial index for range query performance
CLUSTER features USING idx_features_geom;

-- For large tables, recluster periodically
-- Schedule during maintenance window

クエリパターン

距離クエリ
-- 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
包含クエリ
-- 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);
集約
-- 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;

GeoJSON の統合

インポート/エクスポート
-- 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));
API レスポンスパターン
-- 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;

検証と修復

ジオメトリの検証
-- 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"
ジオメトリの修復
-- 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

パフォーマンスの最適化

クエリの最適化
-- 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;
空間データ向けのテーブル設計
-- 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);
複雑なクエリ向けのマテリアライズドビュー
-- 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;

移行パターン

空間カラムの追加
-- 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);
Geometry から Geography への変換
-- 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;

PostGIS アーティファクト

空間機能を実装する場合は、次のアーティファクトを投稿してください。


## PostGIS Implementation Summary

### Spatial Columns

| Table | Column | Type | SRID | Index |
|-------|--------|------|------|-------|
| locations | location | geography(Point) | 4326 | gist |
| parcels | boundary | geometry(Polygon) | 4326 | gist |

### PostGIS 3.6.1 Features Used

- [ ] ST_CoverageClean for topology repair
- [ ] SFCGAL 3D functions
- [ ] Bigint topology
- [ ] PostgreSQL 18 interrupt handling

### Spatial Queries

| Query Pattern | Index Used | Performance |
|---------------|------------|-------------|
| KNN distance | Yes (gist) | <10ms |
| ST_Within region | Yes (gist) | <50ms |
| ST_Intersects | Yes (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 の実装を完了する前に、次の項目を確認してください。

  • 適切なデータ型が選択されている(geometry または geography)
  • SRID に一貫性がある(保存には 4326 を推奨)
  • すべてのジオメトリカラムに空間インデックスが作成されている
  • 入力ジオメトリが検証されている(ST_IsValid)
  • GeoJSON のインポート/エクスポートがテストされている
  • EXPLAIN ANALYZE でクエリのパフォーマンスが確認されている
  • 適切な箇所で PostGIS 3.6.1 の機能が活用されている
  • アーティファクトが課題に投稿されている

統合

このスキルは、次のスキルと統合されます。

  • database-architecture - 空間カラムは一般的なスキーマパターンに従います
  • postgres-rls - RLS ポリシーでは空間述語を使用できます
  • timescaledb - 空間次元を持つ時系列データ

参考資料

コメント