postgresql中坐标转换(GCJ-02 to WGS84)

PostgreSQL 默认情况下没有提供直接将高德坐标(GCJ-02)转换为 WGS84 坐标的函数。在 PostGIS 扩展中,没有直接支持高德坐标转换的函数。要在 PostgreSQL 中实现高德坐标到 WGS84 坐标的转换,需要使用外部库或者自定义函数。这里通过 PostgreSQL 中的 plpgsql 扩展编写存储过程和函数实现坐标转换。

安装plpgsql扩展

1
CREATE EXTENSION plpgsql;

创建自定义函数

主函数

函数参数为高德坐标的纬度 lat 和经度 lon,返回转换后的 WGS84 坐标的纬度 wgs84_lat 和经度 wgs84_lon。

其中,先判断输入坐标是否在中国范围内(通过 out_of_china 变量),如果在中国范围外则直接返回原始坐标,否则进行坐标转换的计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
CREATE OR REPLACE FUNCTION gcj02towgs84(lat float8, lon float8)
RETURNS TABLE (wgs84_lat float8, wgs84_lon float8) AS $$
DECLARE
pi float8 := 3.1415926535897932384626;
ee float8 := 0.00669342162296594323;
a float8 := 6378245.0;
out_of_china BOOLEAN;
dLat float8;
dLon float8;
radLat float8;
magic float8;
sqrtMagic float8;
mgLat float8;
mgLon float8;
lontitude float8;
latitude float8;
BEGIN
out_of_china := (lat < 0.8293 OR lat > 55.8271 OR lon < 72.004 OR lon > 137.8347);

IF out_of_china THEN
wgs84_lat := lat;
wgs84_lon := lon;
ELSE
dLat := transformLat(lon - 105.0, lat - 35.0);
dLon := transformLon(lon - 105.0, lat - 35.0);
radLat := lat / 180.0 * pi;
magic := sin(radLat);
magic := 1 - ee * magic * magic;
sqrtMagic := sqrt(magic);
dLat := (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon := (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * pi);
mgLat := lat + dLat;
mgLon := lon + dLon;
lontitude := lon * 2 - mgLon;
latitude := lat * 2 - mgLat;

wgs84_lat := latitude;
wgs84_lon := lontitude;
END IF;

RETURN NEXT;
END;
$$ LANGUAGE plpgsql;

一些解释:

  • $$:表示函数体开始的标记。
  • RETURNS TABLE :指定了返回结果的列,并且将结果集作为表返回,包含了 wgs84_latwgs84_lon 两列。
  • LANGUAGE plpgsql:指定函数使用的语言为 PL/pgSQL,这是 PostgreSQL 的过程化 SQL 语言,支持编写存储过程和函数。

经纬度的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--将经度 x 和纬度 y 转换为新的纬度坐标,用于gcj02towgs84函数中的计算
CREATE OR REPLACE FUNCTION transformLat(x float8, y float8)
RETURNS float8 AS $$
DECLARE
ret float8;
pi float8 := 3.1415926535897932384626;
BEGIN
ret := -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(abs(x));
ret := ret + (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0;
ret := ret + (20.0 * sin(y * pi) + 40.0 * sin(y / 3.0 * pi)) * 2.0 / 3.0;
ret := ret + (160.0 * sin(y / 12.0 * pi) + 320 * sin(y * pi / 30.0)) * 2.0 / 3.0;
RETURN ret;
END;
$$ LANGUAGE plpgsql;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
--将经度 x 和纬度 y 转换为新的经度坐标,用于gcj02towgs84函数中的计算
CREATE OR REPLACE FUNCTION transformLon(x float8, y float8)
RETURNS float8 AS $$
DECLARE
ret float8;
pi float8 := 3.1415926535897932384626;
BEGIN
ret := 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(abs(x));
ret := ret + (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0;
ret := ret + (20.0 * sin(x * pi) + 40.0 * sin(x / 3.0 * pi)) * 2.0 / 3.0;
ret := ret + (150.0 * sin(x / 12.0 * pi) + 300.0 * sin(x / 30.0 * pi)) * 2.0 / 3.0;
RETURN ret;
END;
$$ LANGUAGE plpgsql;

坐标转换

使用自定义的存储过程gcj02towgs84转换高德坐标如下:

1
2
3
4
5
6
--更新表单的 wgs84 坐标
UPDATE table t
SET wgs84lat_cur = (SELECT (gcj02towgs84(t.gps_lat, t.gps_long)).wgs84_lat),
wgs84lng_cur = (SELECT (gcj02towgs84(t.gps_lat, t.gps_long)).wgs84_lon),
wgs84lat_data = (SELECT (gcj02towgs84(t.data_gps_lat, t.data_gps_long)).wgs84_lat),
wgs84lng_data = (SELECT (gcj02towgs84(t.data_gps_lat, t.data_gps_long)).wgs84_lon)

至此,高德坐标(GCJ-02)到 WGS84 坐标转换完成。