关于 geospatial-data-pipeline
geospatial-data-pipeline 面向处理和可视化地理数据的工程师,覆盖无人机影像与 GPS 轨迹。内容侧重空间数据库、坐标系、空间查询及高效地图数据交付的实践选择。
主要功能
- 01无人机影像与 GPS 数据处理
- 02空间查询与索引指导
- 03坐标系转换
- 04GeoJSON 优化
- 05矢量瓦片生成
- 06地图数据可视化
适用场景
- 01搜索某点附近的位置
- 02分析并可视化 GPS 轨迹
- 03整理无人机测绘数据
- 04高效交付大型地图数据集
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ curiositech/ some_claude_skills --skill geospatial-data-pipeline安装后在 Claude Code 中输入/geospatial-data-pipeline确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.claude-plugin/plugin.jsonreferences/coordinate-systems.mdgeojson-optimization.mdpostgis-guide.mdscripts/geospatial_processor.tstile_generator.ts详细介绍
地理空间数据管道
擅长大规模处理、优化和可视化地理空间数据。
适用场景
✅ 适用于:
- 无人机影像处理和标注
- GPS 轨迹分析和可视化
- 基于位置的搜索(查找附近的 X)
- 为 Web/移动端生成地图瓦片
- 坐标系转换
- 地理围栏和空间查询
- 面向 Web 的 GeoJSON 优化
❌ 不适用于:
- 简单的地址验证(使用地址 API)
- 基本的距离计算(使用 Haversine 公式)
- 静态地图嵌入(使用 Mapbox Static API)
- 地理编码(使用 Nominatim 或 Google Geocoding API)
技术选型
数据库:PostGIS 与 MongoDB 地理空间功能对比
| 功能 | PostGIS | MongoDB |
|---|---|---|
| 空间索引 | GiST、SP-GiST | 2dsphere |
| 查询语言 | SQL + 空间函数 | 聚合管道 |
| 几何类型 | 20+(完整支持 OGC) | 基础类型(Point、Line、Polygon) |
| 坐标系 | 通过 EPSG 支持 6000+ 种 | 仅 WGS84 |
| 性能(1000 万个点) | <100ms | <200ms |
| 最适用于 | 复杂空间分析 | 以文档为中心的应用 |
时间线:
- 2005 年:PostGIS 1.0 发布
- 2012 年:MongoDB 添加地理空间索引
- 2020 年:PostGIS 3.0 提升性能
- 2024 年:PostGIS 仍是 GIS 工作负载的黄金标准
常见反模式
反模式 1:将坐标存储为字符串
新手想法:“我只要把经纬度存成文本就行,很简单。”
问题:无法使用空间索引,查询速度慢,也没有验证。
错误做法:
// ❌ String storage, no spatial features
interface Location {
id: string;
name: string;
latitude: string; // "37.7749"
longitude: string; // "-122.4194"
}
// Linear scan for "nearby" queries
async function findNearby(lat: string, lon: string): Promise<Location[]> {
const all = await db.locations.findAll();
return all.filter(loc => {
const distance = calculateDistance(
parseFloat(lat),
parseFloat(lon),
parseFloat(loc.latitude),
parseFloat(loc.longitude)
);
return distance < 5000; // 5km
});
}
错误原因:O(N) 线性扫描、没有空间索引,还需要承担字符串解析开销。
正确做法:
// ✅ PostGIS GEOGRAPHY type with spatial index
CREATE TABLE locations (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
location GEOGRAPHY(POINT, 4326) -- WGS84 coordinates
);
-- Spatial index (GiST)
CREATE INDEX idx_locations_geography ON locations USING GIST(location);
-- TypeScript query
async function findNearby(lat: number, lon: number, radiusMeters: number): Promise<Location[]> {
const query = `
SELECT id, name, ST_AsGeoJSON(location) as geojson
FROM locations
WHERE ST_DWithin(
location,
ST_SetSRID(ST_MakePoint($1, $2), 4326)::geography,
$3
)
ORDER BY location <-> ST_SetSRID(ST_MakePoint($1, $2), 4326)::geography
LIMIT 100
`;
return db.query(query, [lon, lat, radiusMeters]); // <10ms with index
}
时间线背景:
- 2000 年代:将经纬度存储为 FLOAT 列,并在应用代码中进行计算
- 2010 年代:PostGIS 普及,开始使用空间索引
- 2024 年:
GEOGRAPHY类型可自动处理地球曲率
反模式 2:不使用空间索引
问题:邻近查询会扫描整张表。
错误做法:
-- ❌ No index, sequential scan
CREATE TABLE drone_images (
id SERIAL PRIMARY KEY,
image_url VARCHAR(255),
location GEOGRAPHY(POINT, 4326)
);
-- This query scans ALL rows
SELECT * FROM drone_images
WHERE ST_DWithin(
location,
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326)::geography,
1000 -- 1km
);
EXPLAIN 输出:Seq Scan on drone_images (cost=0.00..1234.56 rows=1 width=123)
正确做法:
-- ✅ GiST index for spatial queries
CREATE INDEX idx_drone_images_location ON drone_images USING GIST(location);
-- Same query, now uses index
SELECT * FROM drone_images
WHERE ST_DWithin(
location,
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326)::geography,
1000
);
EXPLAIN 输出:Bitmap Index Scan on idx_drone_images_location (cost=4.30..78.30 rows=50 width=123)
性能影响:1000 万个点,查询半径为 5km
- 无索引:3.2 秒(全表扫描)
- 使用 GiST 索引:12ms(快 99.6%)
反模式 3:混用坐标系
新手想法:“坐标不过就是数字,我可以混着用。”
问题:距离不准确,地图要素无法对齐。
错误做法:
// ❌ Mixing EPSG:4326 (WGS84) and EPSG:3857 (Web Mercator)
const userLocation = {
lat: 37.7749, // WGS84
lon: -122.4194
};
const droneImage = {
x: -13634876, // Web Mercator (EPSG:3857)
y: 4545684
};
// Comparing apples to oranges!
const distance = Math.sqrt(
Math.pow(userLocation.lon - droneImage.x, 2) +
Math.pow(userLocation.lat - droneImage.y, 2)
);
结果:距离严重错误(数百万“单位”)。
正确做法:
-- ✅ Transform to common coordinate system
SELECT ST_Distance(
ST_Transform(
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326), -- WGS84
3857 -- Transform to Web Mercator
),
ST_SetSRID(ST_MakePoint(-13634876, 4545684), 3857) -- Already Web Mercator
) AS distance_meters;
更好的做法:始终使用一种坐标系(WGS84)存储,仅在显示时进行转换。
时间线:
- 2005 年:Google Maps 引入 Web Mercator(EPSG:3857)
- 2010 年:应用混用 WGS84 数据和 Web Mercator 瓦片,相关混淆达到顶峰
- 2024 年:最佳实践:使用 WGS84 存储,仅在渲染瓦片时转换为 3857
反模式 4:加载超大的 GeoJSON 文件
问题:50MB 的 GeoJSON 文件会导致浏览器崩溃。
错误做法:
// ❌ Load entire file into memory
const geoJson = await fetch('/drone-survey-data.geojson').then(r => r.json());
// 50MB of GeoJSON = browser freeze
map.addSource('drone-data', {
type: 'geojson',
data: geoJson // All 10,000 polygons loaded at once
});
正确做法 1:使用矢量瓦片(预先分块)
// ✅ Serve as vector tiles (MBTiles or PMTiles)
map.addSource('drone-data', {
type: 'vector',
tiles: ['https://api.example.com/tiles/{z}/{x}/{y}.pbf'],
minzoom: 10,
maxzoom: 18
});
// Browser only loads visible tiles
正确做法 2:简化 GeoJSON 并分块
# Simplify geometry (reduce points)
npm install -g @mapbox/geojson-precision
geojson-precision -p 5 input.geojson output.geojson
# Split into tiles
npm install -g geojson-vt
# Generate tiles programmatically (see scripts/tile_generator.ts)
正确做法 3:在服务端进行筛选
// ✅ Only fetch visible bounds
async function fetchVisibleFeatures(bounds: Bounds): Promise<GeoJSON> {
const response = await fetch(
`/api/features?bbox=${bounds.west},${bounds.south},${bounds.east},${bounds.north}`
);
return response.json();
}
map.on('moveend', async () => {
const bounds = map.getBounds();
const geojson = await fetchVisibleFeatures(bounds);
map.getSource('dynamic-data').setData(geojson);
});
反模式 5:在球形地球上使用欧几里得距离
新手想法:“距离就是勾股定理。”
问题:大范围测量时不准确,在极地附近误差更大。
错误做法:
// ❌ Flat Earth distance (wrong!)
function distanceKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
const dx = lon2 - lon1;
const dy = lat2 - lat1;
return Math.sqrt(dx * dx + dy * dy) * 111.32; // 111.32 km/degree (WRONG)
}
// Example: San Francisco to New York
const distance = distanceKm(37.7749, -122.4194, 40.7128, -74.0060);
// Returns: ~55 km (WRONG! Actual: ~4,130 km)
错误原因:地球是球体,不是平面。
正确做法 1:使用 Haversine 公式(大圆距离)
// ✅ Haversine formula (spherical Earth)
function haversineKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
const R = 6371; // Earth radius in km
const dLat = toRadians(lat2 - lat1);
const dLon = toRadians(lon2 - lon1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
// San Francisco to New York
const distance = haversineKm(37.7749, -122.4194, 40.7128, -74.0060);
// Returns: ~4,130 km ✅
正确做法 2:使用 PostGIS(自动处理曲率)
-- ✅ PostGIS ST_Distance with GEOGRAPHY
SELECT ST_Distance(
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326)::geography,
ST_SetSRID(ST_MakePoint(-74.0060, 40.7128), 4326)::geography
) / 1000 AS distance_km;
-- Returns: 4130.137 km ✅
精度对比:
| 方法 | SF 到 NYC | 误差 |
|---|---|---|
| 欧几里得距离(平面) | 55 km | 错误 98.7% |
| Haversine(球体) | 4,130 km | ✅ 正确 |
| PostGIS(椭球体) | 4,135 km | 最准确 |
生产环境检查清单
□ PostGIS extension installed and spatial indexes created
□ All coordinates stored in consistent SRID (recommend: 4326)
□ GeoJSON files optimized (<1MB) or served as vector tiles
□ Coordinate transformations use ST_Transform, not manual math
□ Distance calculations use ST_Distance with GEOGRAPHY type
□ Bounding box queries use ST_MakeEnvelope + ST_Intersects
□ Large geometries chunked (not >100KB per feature)
□ Map tiles pre-generated for common zoom levels
□ CORS configured for tile servers
□ Rate limiting on geocoding/reverse geocoding endpoints
适用与不适用的场景
| 场景 | 是否适用? |
|---|---|
| 无人机影像标注与搜索 | ✅ 是 - 处理测绘数据 |
| GPS 轨迹可视化 | ✅ 是 - 优化路径 |
| 查找最近的咖啡店 | ✅ 是 - 执行空间查询 |
| 行政区边界查询 | ✅ 是 - 执行点在多边形内判断 |
| 简单的地址自动补全 | ❌ 否 - 使用 Mapbox/Google |
| 在页面中嵌入静态地图 | ❌ 否 - 使用 Static API |
| 对单个地址进行地理编码 | ❌ 否 - 使用地理编码 API |
参考资料
/references/coordinate-systems.md- EPSG 代码、坐标转换、Web Mercator 与 WGS84/references/postgis-guide.md- PostGIS 设置、空间索引、常见查询/references/geojson-optimization.md- 简化、分块、矢量瓦片
脚本
scripts/geospatial_processor.ts- 处理无人机影像、GPS 轨迹并验证 GeoJSONscripts/tile_generator.ts- 从 GeoJSON 生成矢量瓦片(MBTiles/PMTiles)
此技能适用范围:地理空间数据 | PostGIS | GeoJSON | 地图瓦片 | 坐标系 | 无人机数据处理 | 空间查询