如何从坐标数组创建PostgreSQL兼容MultiPolygon并实现PHP空间查询
问题背景
现有结构如下的坐标数组,共包含11组经纬度坐标,首尾坐标重合构成闭合环:
Array(11) [ (2) […], (2) […], (2) […], (2) […], (2) […], (2) […], (2) […], (2) […], (2) […], (2) […], … ] 0: Array [ 21.0560137444799, 34.739720318378 ] 1: Array [ 21.0560779456378, 34.7397100033393 ] 2: Array [ 21.056080278408, 34.7397458213187 ] 3: Array [ 21.0561253817111, 34.7397439687819 ] 4: Array [ 21.0561232577343, 34.739797555191 ] 5: Array [ 21.0562660652913, 34.7397921848872 ] 6: Array [ 21.0562570608315, 34.7397353403419 ] 7: Array [ 21.0562048139691, 34.7397280744531 ] 8: Array [ 21.0561893739781, 34.7396442492899 ] 9: Array [ 21.0560014634615, 34.7396514720213 ] 10: Array [ 21.0560137444799, 34.739720318378 ] length: 11
需要基于这11个点创建PostgreSQL兼容的$multipolygon对象,解决两个问题:
- 如何创建符合要求的MultiPolygon?
- 创建完成后如何在PHP请求中传入该多边形,编写SQL查询筛选出落在该多边形内部、与多边形相交的要素?已知
ST_MakeEnvelope仅支持4个点,无法满足当前需求,初始SQL写法如下:
$sql="SELECT * from $table where $coverage in ($multipolygon)";
解决方案
一、构造PostGIS兼容的MultiPolygon
你持有的是单闭合环坐标,本质是单个Polygon,包装为MultiPolygon即可,PostGIS对WKT格式的MultiPolygon有固定规则:
- 坐标顺序保持
经度 纬度,和现有数组顺序一致 - 单环Polygon需要两层括号包裹,MultiPolygon需要额外再加一层括号
- 现有数组已经首尾闭合,不需要额外补点
- 坐标为WGS84经纬度时,指定SRID为4326即可
PHP端坐标转WKT格式示例代码
// 原始坐标数组 $coords = [ [21.0560137444799, 34.739720318378], [21.0560779456378, 34.7397100033393], [21.056080278408, 34.7397458213187], [21.0561253817111, 34.7397439687819], [21.0561232577343, 34.739797555191], [21.0562660652913, 34.7397921848872], [21.0562570608315, 34.7397353403419], [21.0562048139691, 34.7397280744531], [21.0561893739781, 34.7396442492899], [21.0560014634615, 34.7396514720213], [21.0560137444799, 34.739720318378] ]; // 拼接坐标串 $coordStr = implode(',', array_map(function($point){ return $point[0] . ' ' . $point[1]; }, $coords)); // 生成纯WKT格式的MultiPolygon字符串,用于参数绑定 $multipolygonWkt = "MULTIPOLYGON((($coordStr)))";
二、编写正确的空间查询SQL
原写法里的in是普通值匹配语法,完全不适用空间关系判断,需要替换为PostGIS原生空间关系函数:
- 筛选完全落在多边形内部的要素,用
ST_Within - 筛选**和多边形相交(含边界接触、重叠、被包含)**的要素,用
ST_Intersects
安全的PHP预处理写法(避免SQL注入)
禁止直接拼接字符串到SQL语句中,用PDO预处理传参:
// 初始化PDO连接 $pdo = new PDO('pgsql:host=数据库地址;dbname=库名', '用户名', '密码'); // 预处理SQL,若业务字段SRID不是4326,通过ST_Transform统一坐标系即可 $stmt = $pdo->prepare(" SELECT * FROM $table WHERE ST_Intersects( ST_Transform($coverage, 4326), ST_Multi(ST_GeomFromText(?, 4326)) ) "); // 传入WKT参数执行查询 $stmt->execute([$multipolygonWkt]); $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
如果需要查询完全落在多边形内部的要素,把SQL里的ST_Intersects替换为ST_Within即可。
优化注意事项
- 提前确认空间字段
$coverage的坐标系SRID,保证查询两边坐标系一致,否则结果会出现偏差 - 数据量较大时,给空间字段创建GIST索引可大幅提升查询速度,建索引语句:
CREATE INDEX idx_对应表名_coverage ON 对应表名 USING GIST($coverage);
ST_MakeEnvelope仅支持生成矩形范围,任意多边形查询统一用ST_GeomFromText解析WKT是标准方案。
内容的提问来源于stack exchange,提问作者lekythos
相关产品推荐
相关产品推荐

