首页 / 实战手册 / 坐标与投影
经纬度只存两位小数,热力图整体错开 1.1 公里
先看结论
数据库字段是 decimal(9,2),存进去的点全被吸到 0.01 度的网格上。
现象
前端录入 116.397428, 39.909230,写入数据库后读出来是
116.40, 39.91。单点在地图上看起来只差一点点,
但同一商圈的两三百个点被压到同一格,热力图出现规则的方格状色块,
整体位置与真实路网错开约 1.1 公里。
原因
decimal(9,2) 表示总共 9 位、小数 2 位,步长是 0.01。
1 度纬度约 111.3 公里,0.01 度约 1.11 公里;
经度在北纬 40° 处约 85.3 公里/度,0.01 度约 0.85 公里。
四舍五入后最坏误差是半个步长,即 0.55 公里。
| 小数位 | 纬度步长 | 实地步长(北纬 40°) | 适用场景 |
|---|---|---|---|
| 2 | 0.01° | 约 1.1 km | 省级示意 |
| 4 | 0.0001° | 约 11 m | 街区级 |
| 6 | 0.000001° | 约 0.11 m | POI、导航 |
| 7 | 0.0000001° | 约 1.1 cm | 测绘成果 |
排查过程
- 执行
SHOW CREATE TABLE或\d+ poi,看经纬度列的实际类型。 - 取一个已知点,写入再读回,比较差值的量级。差值恰好是 0.005 的整数倍,就是精度截断。
- 用
COUNT(DISTINCT lon, lat)与总行数比较,若远小于总行数,说明大量点被量化到同一格。 - 确认 ORM 层有没有把
double映射成numeric(9,2)。
-- 量化痕迹:不同坐标数远少于总点数
SELECT COUNT(*) AS total, COUNT(DISTINCT lon, lat) AS distinct_pts
FROM poi;
-- total 12840 | distinct_pts 913 -> 大量点被压到同一格
解决方案
经纬度用双精度浮点或 7 位小数的 numeric。改字段后必须从原始数据重导, 被截断的值无法还原。
-- MySQL 8.0
ALTER TABLE poi
MODIFY lon DECIMAL(10,7) NOT NULL,
MODIFY lat DECIMAL(10,7) NOT NULL;
-- PostgreSQL + PostGIS:直接存几何,精度由 double 保证
ALTER TABLE poi ADD COLUMN geom geometry(Point, 4326);
UPDATE poi SET geom = ST_SetSRID(ST_MakePoint(lon, lat), 4326);
CREATE INDEX poi_geom_idx ON poi USING GIST (geom);
-- 新表建议
CREATE TABLE poi (
id bigserial PRIMARY KEY,
name text,
lon double precision NOT NULL CHECK (lon BETWEEN -180 AND 180),
lat double precision NOT NULL CHECK (lat BETWEEN -90 AND 90)
);
验证
写入 116.397428, 39.909230 后读回应完全一致(浮点允许 1e-7 误差);
热力图不再出现方格,点密度分布与卫星底图上的建筑轮廓吻合。
SELECT lon, lat FROM poi WHERE id = 1;
-- 116.397428 | 39.909230
SELECT count(*) FROM poi
WHERE lon = round(lon::numeric, 2) AND lat = round(lat::numeric, 2);
-- 应为 0,说明不再有量化痕迹
适用版本
| 数据库 | 版本 | 建议类型 |
|---|---|---|
| MySQL | 8.0 及以上 | DECIMAL(10,7) 或 DOUBLE |
| PostgreSQL | 14 及以上 | double precision + PostGIS geometry(Point,4326) |
| SQL Server | 2019 及以上 | geography 类型 |
经纬志(2026)。《经纬度只存两位小数,热力图整体错开 1.1 公里》。https://www.jingweizhi.com/cookbook/coordinate-precision.html(访问日期 2026-11-03)。