PostGIS geometry(Point,4326) 经纬度与 EWKB 十六进制互相转换实战

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 类型,存点位
  • lonlat: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'))

五、避坑要点

  1. ST_MakePoint(经度,纬度),顺序不能颠倒,颠倒点位会跑到地球另一边。
  2. ST_GeomFromEWKB接收 bytea,不能直接传入十六进制字符串,必须 decode(xxx,'hex')
  3. DBeaver/Navicat 看到 geometry 展示的十六进制,只是客户端渲染,不是 text 字符串,不能直接复制解析。
  4. 浮点数双精度存储,十进制小数转二进制会存在微小尾数误差,生成的 hex 字符串不一定和原始完全一致,但坐标数值几乎等价。
  5. 区分 2D 点和 Z/M 点,业务数据头部前缀为 0101000020E6100000,不要使用 ST_MakePointZ带高程。