PostGIS geometry (Point,4326) 经纬度与 EWKB 十六进制互相转换实战
环境:PostgreSQL12 + PostGIS,业务表存储地震点位数据,geom 字段为原生 geometry 类型,不是 varchar 存储十六进制字符串。很多网上示例混淆 geometry 对象与 EWKB‑hex 文本,踩坑Invalid endian flag value encountered,本文记录完整踩坑与双向转换。
一、概念区分(最容易踩坑)
- geometry :PostGIS 原生几何对象,数据库字段直接存这个类型,不是字符串。Navicat/DBeaver 客户端展示出来的一串
0101000020E6100000...只是客户端渲染,不是真正文本字符串,不能直接拿去解析。 - EWKB‑hex:十六进制文本字符串,用于导出、接口传输;类型是 text/varchar。
decode(hex_text, 'hex'):十六进制文本 → bytea 二进制。encode(bytea, 'hex'):bytea 二进制 → 可读十六进制文本。
报错:Invalid endian flag value encountered.
原因:直接把 EWKB 十六进制文本字符串传给ST_GeomFromEWKB,该函数入参必须是 bytea,需要先用decode()转换。
二、双向转换完整 SQL
2.1 经纬度 → geometry 对象(写入 geometry 字段)
⚠️顺序:ST_MakePoint(经度lon,纬度lat),X 在前 Y 在后,不要写反!
-- 生成geometry点,SRID=4326 WGS84
SELECT ST_SetSRID(ST_MakePoint(95.56, 37.80), 4326) AS geom_val;
2.2 geometry 对象 → 提取经纬度
SELECT
ST_X(geom) AS lon,
ST_Y(geom) AS lat
FROM mapserver_data.gsyj_lszh;
2.3 geometry 对象 → EWKB 十六进制文本(导出 hex 字符串)
需要对外输出那串 0101000020E6100000... 才需要这一层
SELECT encode(ST_AsEWKB(geom), 'hex') AS ewkb_hex
FROM mapserver_data.gsyj_lszh;
2.4 EWKB‑hex 文本 → geometry 对象(写入 geom 字段)
流程:hex 文本 → decode 转 bytea → ST_GeomFromEWKB 得到 geometry 对象 → 直接存入 geom 字段
SELECT ST_GeomFromEWKB(decode('0101000020E6100000C3F5285C8F525840E17A14AE47A14340','hex')) AS geom_from_hex;
三、业务表实战示例(地震点位表 gsyj_lszh)
表关键点:
geom:public.geometry 类型,存点位lon、lat:float8,冗余存储经纬度- 其他业务字段:震级、时间、人员伤亡等
3.1 插入 SQL
INSERT INTO "mapserver_data"."gsyj_lszh" (
"id",
"geom",
"economy_wy",
"mapname",
"eq_date",
"name",
"display_name",
"source_type",
"eq_id",
"location",
"riqi",
"m",
"eq_date0",
"code",
"lon",
"lat",
"province",
"collapse",
"damage",
"maxintensity",
"population",
"year",
"gname",
"wound",
"hurt",
"death",
"intensity",
"liedu",
"eq_time",
"depth",
"economy",
"magnitude",
"objectid",
"bz"
) VALUES (
571,
ST_SetSRID(ST_MakePoint(95.56, 37.80), 4326),
'暂不详',
'gsyj_lszh',
'2026-06-16',
'青海海西',
'青海海西',
NULL,
0,
'青海省海西州直辖区',
'2026-06-16',
6.3,
'2026年6月16日',
632800,
95.56,
37.80,
'青海省',
0,
0,
'8',
0,
'2026',
'海西州直辖区',
'0',
'0',
'1',
'Ⅷ',
'8',
'17:06:00',
'10',
'暂不详',
6.3,
0,
'2026/06/16,青海海西M6.3,亡1人/伤0人'
);
3.2 更新 SQL(id 冲突时使用)
UPDATE "mapserver_data"."gsyj_lszh"
SET
geom = ST_SetSRID(ST_MakePoint(95.56, 37.80), 4326),
economy_wy = '暂不详',
mapname = 'gsyj_lszh',
eq_date = '2026-06-16',
name = '青海海西',
display_name = '青海海西',
source_type = NULL,
eq_id = 0,
location = '青海省海西州直辖区',
riqi = '2026-06-16',
m = 6.3,
eq_date0 = '2026年6月16日',
code = 632800,
lon = 95.56,
lat = 37.80,
province = '青海省',
collapse = 0,
damage = 0,
maxintensity = '8',
population = 0,
year = '2026',
gname = '海西州直辖区',
wound = '0',
hurt = '0',
death = '1',
intensity = 'Ⅷ',
liedu = '8',
eq_time = '17:06:00',
depth = '10',
economy = '暂不详',
magnitude = 6.3,
objectid = 0,
bz = '2026/06/16,青海海西M6.3,亡1人/伤0人'
WHERE id = 571;
3.3 插入后校验 SQL,核对 geom 与 lon/lat 是否一致
SELECT
id,
lon,
lat,
ST_X(geom) AS geom_lon,
ST_Y(geom) AS geom_lat,
encode(ST_AsEWKB(geom),'hex') AS geom_ewkb_hex
FROM "mapserver_data"."gsyj_lszh"
WHERE id = 571;
四、速查表
| 业务场景 | 使用函数 |
|---|---|
| 经纬度写入 geometry 字段 | ST_SetSRID(ST_MakePoint(lng,lat),4326) |
| geometry 读取经纬度 | ST_X(geom), ST_Y(geom) |
| geometry 导出 EWKB 十六进制文本 | encode(ST_AsEWKB(geom),'hex') |
| EWKB‑hex 文本写入 geometry 字段 | ST_GeomFromEWKB(decode(hex_str,'hex')) |
五、避坑要点
ST_MakePoint(经度,纬度),顺序不能颠倒,颠倒点位会跑到地球另一边。ST_GeomFromEWKB接收 bytea,不能直接传入十六进制字符串,必须decode(xxx,'hex')。- DBeaver/Navicat 看到 geometry 展示的十六进制,只是客户端渲染,不是 text 字符串,不能直接复制解析。
- 浮点数双精度存储,十进制小数转二进制会存在微小尾数误差,生成的 hex 字符串不一定和原始完全一致,但坐标数值几乎等价。
- 区分 2D 点和 Z/M 点,业务数据头部前缀为
0101000020E6100000,不要使用ST_MakePointZ带高程。