proj4js是一个用于地理空间坐标转换的JavaScript库。可以很方便的在浏览器和nodejs环境下完成坐标转换,能在一定程度上弥补当前很多web gis引擎对坐标系支持能力不足的短板(大多数web端的gis引擎只支持WGS 84和Web墨卡托两种坐标系)。
proj4js源于另一个开源项目PROJ,虽然目前功能已经相对完善,开源社区也比较活跃,但是文档还有不少欠缺,想要了解完整的参数和用法还是得查阅PROJ的文档。
坐标系描述
在proj4中可以通过下表所列的常用参数组合成一个描述坐标系信息的字符串,在proj4js中称为proj-string。本文对其中几个重要的参数进行介绍。
参数 | 描述 |
+a | 椭球长轴的半径 |
+axis | 轴的方向 |
+b | 椭球短轴的半径 |
+ellps | 地球椭球 |
+datum | 大地基准面 |
+k | 缩放系数(已废弃) |
+k_0 | 缩放系数 |
+lat_0 | 纬度的起算点(默认以度为单位) |
+lat_ts | 有效纬度范围 |
+lon_0 | 中央经线(默认以度为单位) |
+lon_wrap | 定义经度范围的一种方式(+lon_wrap=180 表示 [ 0, 360] ) |
+over | 允许经度超出[ -180, 180 ]的范围 |
+pm | 自定义子午线(通常是城市名称) |
+proj | 投影名称 |
+units | 水平坐标系单位(米、美制测量英尺等) |
+vunits | 垂直坐标系单位 |
+x_0 | 东偏移量(始终以米为单位) |
+y_0 | 北偏移量(始终以米为单位) |
+no_defs | 不从默认的proj_def.dat读取默认参数 |
+zone | 指定UTM区域 |
常用坐标系定义参数
投影名称
使用+proj指定投影名称,常用可选值如下
坐标系名称 | 意义 |
merc | 墨卡托投影 |
utm | UTM(横轴墨卡托)投影 |
longlat | 坐标值输入为 [ 经度, 纬度 ] 的地理坐标系 |
latlong | 坐标值输入为 [ 纬度, 经度 ] 的地理坐标系 |
常用投影名称
示例:
+proj=merc +lat_ts=56.5 +ellps=GRS80
+proj=utm +zone=50 +datum=WGS84 +units=m +no_defs +type=crs
单位
坐标系水平方向单位用+units参数定义,垂直方向的单位通过+vunits参数定义。
示例:
坐标单位为度
+proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees
坐标单位为米(投影坐标系默认单位是米)
+proj=gnom +lat_0=90 +lon_0=0 +x_0=6300000 +y_0=6300000 +ellps=WGS84 +datum=WGS84 +units=m +no_defs
偏移量
坐标系允许存在东偏移量 (+x_0) 和北偏移 (+y_0)。即使坐标系是其他单位,偏移量也始终以米表示。
示例:
+proj=tmerc +lat_0=0 +lon_0=75 +k=1 +x_0=13500000 +y_0=0 +a=6378140 +b=6356755.288157528 +units=m +no_defs
经度范围
经度范围默认被限制在 [ -180, 180 ],如果使用了+over参数,则经度范围可以忽略这个限制
示例:
+proj=merc +a=6371200 +b=6371200 +units=meters +no_defs + lon_1=110 +lon_2=-109.129 +lat_ts=20 +lon_wrap=180 +over
本初子午线
使用+pm参数可以自定义坐标系的本初子午线。proj4js有一组预定义的子午线名称。
子午线 | 经度 |
greenwich(默认) | 0dE |
lisbon | 9d07'54.862"W |
paris | 2d20'14.025"E |
bogota | 74d04'51.3"E |
madrid | 3d41'16.48"W |
rome | 12d27'8.4"E |
bern | 7d26'22.5"E |
jakarta | 106d48'27.79"E |
ferro | 17d40'W |
brussels | 4d22'4.71"E |
stockholm | 18d3'29.8"E |
athens | 23d42'58.815"E |
oslo | 10d43'22.5"E |
proj4预定义子午线
示例:
+proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=0 +k_0=0.99987742 +x_0=600000 +y_0=2200000 +a=6378249.2 +b=6356515 +towgs84=-168,-60,320,0,0,0,0 +pm=paris +units=m +no_defs
+proj=latlong +datum=WGS84 +pm=madrid
坐标轴方向
+axis参数用于控制坐标系的轴方向,默认方向是“东、北、上”,或者由以下字母自由组合
- "e" - 东
- "w" - 西
- "n" - 北
- "s" - 南
- "u" - 上
- "d" - 下
例如以下几种组合方式
- +axis=enu :默认的ENU(东、北、上)坐标系;
- +axis=neu :通常用于 "纬度 / 经度" 形式的地理坐标或者南向横轴墨卡托投影(south orientated transverse mercator);
- +axis=wnu :用于一些以西为正方向的行星坐标系
椭球体
椭球体是一个数学意义上的表面,它近似于大地水准面。由于地球是一个不规则的椭球体,于是地图投影中产生了多种多样的椭球体定义。每个地区都有最适合当地地形的地球椭球体。
椭球体包含尺寸(size)和形状(shape )两部分概念。具体由尺寸参数、形状参数和球谐参数指定。(如果使用了+R参数指定为标准球体,那么形状参数和球谐参数都会被忽略)
参数 | 意义 |
+R=<value> | 球体的半径(定义为球体时使用) |
+a=<value> | 椭球的长半轴 |
尺寸参数
参数 | 意义 |
+rf=<value> | 椭球扁率的倒数(Reverse flattening) |
+f=<value> | 椭球扁率 |
+es=<value> | 偏心率的平方 |
+e=<value> | 偏心率 |
+b=<value> | 短半轴 |
形状参数
参数 | 意义 |
+R_A | 与椭球体表面积相同的球体 |
+R_V | 与椭球体体积相同的球体 |
+R_C | 半径为共形球体(conformal sphere)处半径的球体 |
+R_a | 半径R为 (a + b) / 2 的球体(算数平均数) |
+R_g | 半径R为 的球体(几何平均数) |
+R_h | 半径R为 2ab / (a + b)的球体(调和平均数) |
+R_lat_a=<phi> | 半径为相应椭球在纬度处的算数平均数的球体 |
+R_lat_g=<phi> | 半径为相应椭球在纬度处的几何平均数的球体 |
球谐参数
用户可以自定义椭球体,但更多的还是使用+ellps参数选用proj4内置的椭球体
椭球 | 参数 | 测量基准 |
GRS80(默认) | a=6378137.0 rf=298.257222101 | GRS 1980(IUGG, 1980) |
airy | a=6377563.396 b=6356256.910 | Airy 1830 |
bessel | a=6377397.155 rf=299.1528128 | Bessel 1841 |
clrk66 | a=6378206.4 b=6356583.8 | Clarke 1866 |
intl | a=6378388.0 rf=297. | International 1909 (Hayford) |
WGS60 | a=6378165.0 rf=298.3 | WGS 60 |
WGS66 | a=6378145.0 rf=298.25 | WGS 66 |
WGS72 | a=6378135.0 rf=298.26 | WGS 72 |
WGS84 | a=6378137.0 rf=298.257223563 | WGS 84 |
sphere | a=6370997.0 b=6370997.0 | Normal Sphere (r=6370997) |
proj4预定义椭球体
示例:
GRS80椭球
+proj=merc +lat_ts=56.5 +ellps=GRS80
通过半长轴和扁率的倒数自定义椭球
+proj=latlon +a=6378137.0 +rf=298.25
和定义的椭球体体积相同的球体
+proj=latlon +a=6378137.0 +rf=298.25 +R_V
常用坐标系定义
WGS 84地理坐标系统 (EPSG:4326)
+proj=longlat +datum=WGS84 +no_defs
Web Mercator (EPSG:3857)
+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs
中国国家2000地理坐标系统 (EPSG:4490)
定义和WGS 84一样(CGCS2000椭球和WGS84椭球极为相似,偏差仅有0.11mm,完全可以兼容使用)
+proj=longlat +ellps=GRS80 +no_defs
北京54坐标系 (EPSG:4214)
+proj=longlat +ellps=krass +towgs84=15.8,-154.4,-82.3,0,0,0,0 +no_defs
西安80坐标系 (EPSG:4610)
+proj=longlat +a=6378140 +b=6356755.288157528 +no_defs
UTM 50号带 / UTM zone 50N (EPSG:32650)
中国国境跨UTM带号为43-53,根据需要更改+zone后面的数字即可
+proj=utm +zone=50 +datum=WGS84 +units=m +no_defs
API使用介绍
proj4js主要的函数是proj4,函数签名如下所示:
proj4([fromProjection, ]toProjection[, coordinates])
应用举例
下面的用例用到两个坐标系定义
var firstProjection = 'PROJCS["NAD83 / Massachusetts Mainland",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",42.68333333333333],PARAMETER["standard_parallel_2",41.71666666666667],PARAMETER["latitude_of_origin",41],PARAMETER["central_meridian",-71.5],PARAMETER["false_easting",200000],PARAMETER["false_northing",750000],AUTHORITY["EPSG","26986"],AXIS["X",EAST],AXIS["Y",NORTH]]';
var secondProjection = "+proj=gnom +lat_0=90 +lon_0=0 +x_0=6300000 +y_0=6300000 +ellps=WGS84 +datum=WGS84 +units=m +no_defs";
1、将一个坐标点从一个坐标系转换到另一个坐标系
// ...
// [-2690575.447893817, 36622916.8071244564]
proj4(firstProjection, secondProjection, [-122.305887, 58.9465872]);
2、转换带高程值的坐标(高程值会原样保留)
// ...
// [-2690575.447893817, 36622916.8071244564, 10]
proj4(firstProjection,secondProjection,[-122.305887, 58.9465872,10]);
3、如果只提供一个坐标系,这个坐标系会被认为是目标坐标系(toProjection),源坐标系(fromProjection)默认为WGS84
// ...
// 坐标从WGS 84转换到 firstProjection
// [242075.00535055372, 750123.32090043]
proj4(firstProjection, [-71, 41]);
4、如果proj4函数中只传入了坐标系,没有传入待转换的坐标。那么proj4函数返回一个包含 forward 和 inverse 两个函数的对象。
- forward :从第一个坐标系参数转换到第二个坐标系参数
- inverse :从第二个坐标系参数转换到第一个坐标系参数
// firstProjection 转换到 secondProjection
// [-2690575.447893817, 36622916.8071244564]
proj4(firstProjection, secondProjection).forward([-122.305887, 58.9465872]);
// firstProjection 转换到 secondProjection
// [-2690575.447893817, 36622916.8071244564]
proj4(secondProjection, firstProjection).inverse([-122.305887, 58.9465872]);
5、只给出一个坐标系,并且没有坐标,那么原始坐标系被认为是wgs84
// wgs84转换到firstProjection
// [242075.00535055372, 750123.32090043]
proj4(firstProjection).forward([-71,41])
// 因为是inverse,所以把坐标从firstProjection转换到wgs84
// [-71, 40.99999999999986]
proj4(firstProjection).inverse([242075.00535055372, 750123.32090043]);
投影命名
proj4js允许用户通过defs函数自定义坐标系名称,并在后续引用
proj4.defs([
[
'EPSG:4326',
'+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees'],
[
'EPSG:4269',
'+title=NAD83 (long/lat) +proj=longlat +a=6378137.0 +b=6356752.31414036 +ellps=GRS80 +datum=NAD83 +units=degrees'
]
]);
// 从secondProjection转换到自定义的'EPSG:4326'
// [-163.48520399669096, 11.401530800453495, 10]
proj4(secondProjection, 'EPSG:4326', [-2690575.447893817, 36622916.8071244564, 10]);
proj4js内置了一些预定义的坐标系名称
坐标系名称 | 别名 |
EPSG:4326 | WGS84 |
EPSG:4269 | |
EPSG:3857 | EPSG:3785 或 GOOGLE 或 EPSG:900913 或 EPSG:102113 |
proj4预定义坐标系及别名
// 使用预定义坐标系
proj4(secondProjection, 'EPSG:3785', [-2690575.447893817, 36622916.8071244564, 10]);
proj4(secondProjection, 'EPSG:900913', [-2690575.447893817, 36622916.8071244564, 10])
使用proj4js预定义的坐标系
反转坐标轴顺序
默认情况下,proj4中投影坐标系使用 [x, y] 的坐标轴顺序,地理坐标系使用 [x = longitude, y = latitude] 的坐标轴顺序。可以按以下方式设置enforceAxis参数以反转默认的坐标轴顺序。
proj4(fromProjection, toProjection).forward(coordinate, enforceAxis);
proj4(fromProjection, toProjection).inverse(coordinate, enforceAxis);
// enforceAxis默认值为false
// [-71, 40.99999999999986]
proj4('+proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees +axis=neu', firstProjection).inverse([242075.00535055372, 750123.32090043]);
// 显示指定enforceAxis为true,反转坐标轴
// [40.99999999999986, -71]
proj4('+proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees +axis=neu', firstProjection).inverse([242075.00535055372, 750123.32090043], true);
// 不设置enforceAxis
// [39078917.71700995, -2076788.7936424324]
proj4('+proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees +axis=neu', firstProjection).forward([41, -71]);
// enforceAxis设置为true
// [242075.00535055372, 750123.32090043]
proj4('+proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees +axis=neu', firstProjection).forward([41, -71], true);
结语
这篇文章只是总结了proj4js涉及的基本概念以及使用方法,并不能囊括所有proj4js的细节。同时地图投影的内容很多,还有很多理论和概念是我不了解的,有待后续不断学习和总结!