用floor()缩放法比round()更可靠:先放大经纬度为整数,floor取整后再缩回,避免浮点误差;高纬度需用余弦校正或st_area计算真实面积以准确反映密度。

用 ROUND() 对经纬度做固定精度截断实现网格划分
网格化本质是把连续的经纬度值映射到离散的格子编号上,最直接的方式就是对 longitude 和 latitude 分别做四舍五入。比如保留 2 位小数,相当于约 1.1 km × 1.1 km 的网格(赤道附近);保留 3 位小数则缩至约 110 m × 110 m。
注意:不能直接 ROUND(longitude, 2) 后分组再统计,因为浮点精度可能导致本该同属一格的值被分到相邻格子(如 116.405 和 116.414 都应进 116.41,但 ROUND(116.405, 2) 在某些数据库里可能得 116.4)。稳妥做法是先偏移再截断:
SELECT ROUND(longitude * 100) / 100.0 AS grid_lon, ROUND(latitude * 100) / 100.0 AS grid_lat, COUNT(*) AS cnt FROM locations GROUP BY grid_lon, grid_lat;
用 FLOOR() + 缩放避免浮点误差更可靠
FLOOR() 是整数截断,比 ROUND() 更稳定,尤其在边界值处理上。核心思路是把经纬度放大成整数,取整后再缩回原单位:
-
FLOOR(longitude * 100)得到以 0.01° 为单位的整数格子 ID,再除以100.0还原为小数 - 这样
116.405→11640→116.40,116.414→11641→116.41,无歧义 - 若需按 0.001°(≈110 m)分格,就用
FLOOR(longitude * 1000) / 1000.0
PostgreSQL、MySQL、SQL Server 均支持该写法;SQLite 需显式转 REAL 防整除问题。
地理围栏场景下用 ST_Envelope() 或 ST_SnapToGrid()(PostGIS)
当数据量大、且需要与真实地理范围(如行政区、热力图瓦片)对齐时,纯数值截断不够用。PostGIS 提供了空间感知的网格工具:
-
ST_SnapToGrid(geom, 0.01)将点几何对象吸附到 0.01° 网格顶点,返回新POINT,可直接分组 -
ST_Envelope(ST_Buffer(geom, 0.005))模拟“以点为中心、半径 0.005° 的矩形框”,适合表达模糊归属 - 注意:
ST_SnapToGrid()默认按坐标系单位缩放,WGS84 下单位是度,不是米
非 PostGIS 环境硬要模拟类似行为,就得自己算格子左下角:FLOOR(longitude / 0.01) * 0.01,但不如原生函数健壮。
密度计算必须考虑网格面积差异
纬度越高,相同经度差对应的实际距离越短。例如在北纬 60°,1° 经度 ≈ 55 km,而在赤道是 ≈ 111 km。如果只用 COUNT(*) 当密度,高纬度网格会系统性偏低。
粗略校正可用余弦因子:COUNT(*) / COS(RADIANS(latitude))(要求 latitude 用平均值或中心值代入);更准的做法是用 ST_Area()(PostGIS)计算每个网格在球面上的真实面积(需先转 GEOMETRY 再用 ST_Transform() 到等积投影)。
多数业务场景中,只要网格足够细(如 0.001°),且覆盖区域纬度跨度不大(











