直接用 round(lat, 2) 做网格会出错,因为经纬度每度对应的实际距离不恒定——纬度方向约111km/度,经度方向则随纬度升高而急剧收缩(如北纬60°仅约55km/度),导致同一小数位精度在不同地区代表的物理面积差异可达一倍,统计结果不可比。

为什么直接用 ROUND(lat, 2) 做网格会出错
很多人第一反应是把经纬度各自 ROUND() 到小数点后几位,比如 ROUND(lat, 2) 和 ROUND(lng, 2),再组合成分组键。但这样做的实际网格边长不恒定——纬度方向每度约111km,经度方向则随纬度升高急剧收缩(赤道约111km,北纬60°只剩约55km)。同一 ROUND 精度在不同地区代表的物理面积可能差一倍,统计结果不可比。
真正可控的网格需基于固定距离(如5km×5km),而非固定小数位数。
- 避免用
ROUND(lat, n)/ROUND(lng, n)直接分组 - 优先采用「墨卡托投影 + 整数取整」或「geohash前缀截断」两类可靠方案
- 若数据库不支持地理函数(如MySQL旧版本),必须先用应用层预计算网格ID
PostgreSQL + PostGIS:用 ST_SnapToGrid() 生成等距网格
PostGIS 提供了真正按米级精度划分平面网格的能力。关键不是对原始经纬度四舍五入,而是先将地理坐标转为 Web Mercator 投影(SRID 3857),再用 ST_SnapToGrid() 对 X/Y 坐标做等距对齐。
SELECT
ST_AsText(ST_SnapToGrid(
ST_Transform(ST_SetSRID(ST_MakePoint(lng, lat), 4326), 3857),
500 -- 网格边长(单位:米)
)) AS grid_geom,
COUNT(*) AS cnt
FROM locations
GROUP BY grid_geom;
注意:ST_SnapToGrid() 返回的是几何对象,若只想存网格ID便于后续关联,可改用 ST_XMin() 和 ST_YMin() 提取左下角坐标并拼接:
-
ST_Transform(..., 3857)是必须步骤,否则ST_SnapToGrid在球面坐标上无意义 - 500 表示 500 米边长正方形,数值越大网格越粗
- 输出的
grid_geom可直接用于空间连接,也支持ST_Contains查询某点归属
MySQL 8.0+:用 ST_GeomFromText() 搭配自定义网格函数
MySQL 原生不提供 ST_SnapToGrid(),但可通过计算墨卡托坐标后手动取整模拟。核心是把经纬度转为 Web Mercator 的 XY(单位:米),再除以目标网格尺寸后取整:
SELECT FLOOR((lng * PI() / 180) * 6378137) DIV 500 AS x_grid, FLOOR(LN(TAN((90 + lat) * PI() / 360)) * 6378137) DIV 500 AS y_grid, COUNT(*) AS cnt FROM locations GROUP BY x_grid, y_grid;
这里用了近似墨卡托公式(WGS84椭球简化为球体),误差在城市级分析中可接受;若需更高精度,应改用 ST_Transform()(MySQL 8.0.33+ 支持)或移至应用层计算。
- 公式中
6378137是地球赤道半径(米),不可替换成其他常量 -
lat必须用度数,且范围限定在-85.0511~85.0511(墨卡托极点截断值) - 若数据含极地坐标,此公式会溢出,需提前过滤或换用 geohash
通用 fallback 方案:用 ST_GeoHash() 截断长度控制网格粒度
几乎所有支持地理扩展的数据库(PostgreSQL、MySQL、ClickHouse)都内置 ST_GeoHash()。它本质是空间填充曲线编码,截断长度即可控制精度:长度为 6 的 geohash 约对应 1.2km × 1.2km,长度为 5 约对应 4.9km × 4.9km。
SELECT SUBSTRING(ST_GeoHash(POINT(lng, lat)), 1, 6) AS gh6, COUNT(*) AS cnt FROM locations GROUP BY gh6;
优点是无需投影、跨库兼容性好;缺点是网格非正方形(呈矩形且随纬度变形),且相邻 geohash 并不保证空间邻近(存在“断裂带”)。适合快速探索性分析,但不适合要求严格空间连续性的场景。
- 不要用
LEFT(geohash, n)以外的方式截断,否则破坏编码结构 - geohash 长度与实际尺寸是非线性关系,查表确认目标精度对应长度(如长度7 ≈ 150m)
- PostgreSQL 中函数名为
ST_GeoHash(geom, precision),MySQL 中为ST_GeoHash(x, y, precision)
地理网格化最易被忽略的点是:没有统一坐标系就谈“等距”毫无意义。哪怕只差一个 ST_Transform() 调用,结果就可能从可用变成误导。











