diff --git a/GPSTrans.js b/GPSTrans.js new file mode 100644 index 0000000..83bcdc8 --- /dev/null +++ b/GPSTrans.js @@ -0,0 +1,210 @@ +/** + * Created by Wandergis on 2015/7/8. + * 提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换 + * modify by ipcjs on Nov 22, 2018 + * 修改了中国区域范围 规范了部分代码,取消了部分重叠代码 + * https://github.com/ipcjs/coordtransform + * modify by rongzedong 2020/2/28 + * 增加了 gps2bd 和 bd2gps 传入和传出用了 point对象,避免 lat 和lng 传参错误的问题,简化了使用。 add short name GPSTrans + * https://github.com/rongzedong/coordtransform/ + */ +//UMD魔法代码 +// if the module has no dependencies, the above pattern can be simplified to +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.coordtransform = factory(); + root.GPSTrans = factory(); + } +}(this, function () { + //定义一些常量 + var x_PI = 3.14159265358979324 * 3000.0 / 180.0; + var PI = 3.1415926535897932384626; + var a = 6378245.0; + var ee = 0.00669342162296594323; + + function Rectangle(lng1, lat1, lng2, lat2) { + this.west = Math.min(lng1, lng2); + this.north = Math.max(lat1, lat2); + this.east = Math.max(lng1, lng2); + this.south = Math.min(lat1, lat2); + } + /** + * @param {number} lon + * @param {number} lat + */ + Rectangle.prototype.contain = function (lon, lat) { + return this.west <= lon && this.east >= lon && this.north >= lat && this.south <= lat; + } + + //China region - raw data + var region = [ + new Rectangle(79.446200, 49.220400, 96.330000, 42.889900), + new Rectangle(109.687200, 54.141500, 135.000200, 39.374200), + new Rectangle(73.124600, 42.889900, 124.143255, 29.529700), + new Rectangle(82.968400, 29.529700, 97.035200, 26.718600), + new Rectangle(97.025300, 29.529700, 124.367395, 20.414096), + new Rectangle(107.975793, 20.414096, 111.744104, 17.871542) + ]; + //China excluded region - raw data + var exclude = [ + new Rectangle(119.921265, 25.398623, 122.497559, 21.785006), + new Rectangle(101.865200, 22.284000, 106.665000, 20.098800), + new Rectangle(106.452500, 21.542200, 108.051000, 20.487800), + new Rectangle(109.032300, 55.817500, 119.127000, 50.325700), + new Rectangle(127.456800, 55.817500, 137.022700, 49.557400), + new Rectangle(131.266200, 44.892200, 137.022700, 42.569200), + new Rectangle(73.124600, 35.398637, 77.948114, 29.529700) + ]; + + function isInChina(lon, lat) { + for (var i = 0; i < region.length; i++) { + if (region[i].contain(lon, lat)) { + for (var j = 0; j < exclude.length; j++) { + if (exclude[j].contain(lon, lat)) { + return false; + } + } + return true; + } + } + return false; + } + + /** + * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换 + * 即 百度 转 谷歌、高德 + * @param {number} bd_lon + * @param {number} bd_lat + * @returns {*[]} + */ + function bd09togcj02(bd_lon, bd_lat) { + var x = bd_lon - 0.0065; + var y = bd_lat - 0.006; + var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI); + var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI); + var gg_lng = z * Math.cos(theta); + var gg_lat = z * Math.sin(theta); + return [gg_lng, gg_lat] + }; + + /** + * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换 + * 即谷歌、高德 转 百度 + * @param {number} lng + * @param {number} lat + * @returns {*[]} + */ + function gcj02tobd09(lng, lat) { + var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI); + var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI); + var bd_lng = z * Math.cos(theta) + 0.0065; + var bd_lat = z * Math.sin(theta) + 0.006; + return [bd_lng, bd_lat] + }; + + /** + * WGS84转GCj02 + * @param {number} lng + * @param {number} lat + * @returns {*[]} + */ + function wgs84togcj02(lng, lat) { + if (!isInChina(lng, lat)) { + return [lng, lat] + } + return transform(lng, lat) + }; + + /** + * GCJ02 转换为 WGS84 + * @param {number} lng + * @param {number} lat + * @returns {*[]} + */ + function gcj02towgs84(lng, lat) { + if (!isInChina(lng, lat)) { + return [lng, lat] + } + var out = transform(lng, lat) + return [lng * 2 - out[0], lat * 2 - out[1]] + }; + + /** + * WGS84 转换为 bd09 + * @param point {lng, lat} + * @returns {*[]} + */ + var gps2bd = function gps2bd(point) + { + var tmp = wgs84togcj02(point.lng, point.lat); + var r = gcj02tobd09(tmp[0],tmp[1]); + return {'lng':r[0],'lat':r[1]}; + } + + /** + * bd09 转换为 WGS84 + * @param point {lng, lat} + * @returns {*[]} + */ + var bd2gps = function bd2gps(point) + { + var tmp = bd09togcj02(point.lng, point.lat); + var r = gcj02towgs84(tmp[0],tmp[1]); + return {'lng':r[0],'lat':r[1]}; + } + + function transform(lng, lat) { + var dlat = transformlat(lng - 105.0, lat - 35.0); + var dlng = transformlng(lng - 105.0, lat - 35.0); + var radlat = lat / 180.0 * PI; + var magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + var sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + var mglat = lat + dlat; + var mglng = lng + dlng; + return [mglng, mglat] + } + + function transformlat(lng, lat) { + var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0; + return ret + }; + + function transformlng(lng, lat) { + var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0; + return ret + }; + + return { + // 向前兼容 + bd09togcj02: bd09togcj02, + gcj02tobd09: gcj02tobd09, + wgs84togcj02: wgs84togcj02, + gcj02towgs84: gcj02towgs84, + // 小驼峰命名风格 + bd09ToGcj02: bd09togcj02, + gcj02ToBd09: gcj02tobd09, + wgs84ToGcj02: wgs84togcj02, + gcj02ToWgs84: gcj02towgs84, + // gps2bd(bd09) + gps2bd: gps2bd, + bd2gps: bd2gps + } +})); diff --git a/README.md b/README.md index 1512c34..77f19d7 100644 --- a/README.md +++ b/README.md @@ -1,133 +1,20 @@ -# coordtransform 坐标转换 +# GPSTrans 坐标转换 **** -一个提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的工具模块。 +基于 coordtransform , 一个提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的工具模块。 -python版本:https://github.com/wandergis/coordTransform_py - -命令行版本(支持模块或在命令行直接转换geojson数据):https://github.com/wandergis/coordtransform-cli - -go语言社区版本:https://github.com/qichengzx/coordtransform +forked from https://github.com/wandergis/coordtransform (原版请访问这个网址) **** -## **支持node、浏览器(AMD方式和直接引用方式)** +## **原版支持node、浏览器(AMD方式和直接引用方式)** - GitHub地址:https://github.com/wandergis/coordtransform - npm地址:https://www.npmjs.com/package/coordtransform - 项目主页:http://wandergis.github.io/coordtransform/ -## 为什么写这个模块 - -随着移动互联网的兴起,几乎每一个app都会去收集用户位置,如果恰好你在处理与地理定位相关的代码,并且不了解地理坐标系的话,肯定要被我大天朝各种坐标系搞晕。写这个模块的目的也是因为项目中app获取的坐标是百度sdk获取的,在做webgis可视化的时候各种偏,各种坐标不对,叠加错位。 - -## 当前互联网地图的坐标系现状 -### 地球坐标 (WGS84) -- 国际标准,从 GPS 设备中取出的数据的坐标系 -- 国际地图提供商使用的坐标系 - -### 火星坐标 (GCJ-02)也叫国测局坐标系 -- 中国标准,从国行移动设备中定位获取的坐标数据使用这个坐标系 -- 国家规定: 国内出版的各种地图系统(包括电子形式),必须至少采用GCJ-02对地理位置进行首次加密。 - -### 百度坐标 (BD-09) -- 百度标准,百度 SDK,百度地图,Geocoding 使用 -- (本来就乱了,百度又在火星坐标上来个二次加密) - -## 开发过程需要注意的事 -- 从设备获取经纬度(GPS)坐标 - - 如果使用的是百度sdk那么可以获得百度坐标(bd09)或者火星坐标(GCJ02),默认是bd09 - 如果使用的是ios的原生定位库,那么获得的坐标是WGS84 - 如果使用的是高德sdk,那么获取的坐标是GCJ02 -- 互联网在线地图使用的坐标系 - - 火星坐标系: - iOS 地图(其实是高德) - Google国内地图(.cn域名下) - 搜搜、阿里云、高德地图、腾讯 - 百度坐标系: - 当然只有百度地图 - WGS84坐标系: - 国际标准,谷歌国外地图、osm地图等国外的地图一般都是这个 -# 举个例子 -笔者所在的公司app使用的是百度的sdk,需要对定位坐标做web可视化效果,百度地图提供的js api满足不了需求,选用leaflet来做可视化,这里要说到百度地图了,它使用的坐标系和切图的原点都不一致,并且其加偏还是非线性的,因此无法利用常用的加载方法去加载,放弃使用它的底图,选用了符合标准的高德底图,高德底图使用的是国测局坐标也就是GCJ02坐标系,如果简单的将app获取的经纬度叠加上去,就有可能你本来在百度大厦的位置就显示在西二旗地铁站了甚至更远,因此需要将bd09转成gcj02坐标系,这个时候这个库就有了用武之地,对点批量转换再加载到底图上,就可以让点显示在本应该出现的位置。 - - 另外如果你拿到了一些WGS84的坐标,想加载到各种底图上就可以根据这个库在底图坐标系和你的数据坐标系之间进行转换。希望对大家有用吧。 - -**** - - -### 安装(install) - -``` -npm install coordtransform -``` - - -### 示例用法(Example&Usage) -1 NodeJs用法 - -``` -//国测局坐标(火星坐标,比如高德地图在用),百度坐标,wgs84坐标(谷歌国外以及绝大部分国外在线地图使用的坐标) -var coordtransform=require('coordtransform'); -//百度经纬度坐标转国测局坐标 -var bd09togcj02=coordtransform.bd09togcj02(116.404, 39.915); -//国测局坐标转百度经纬度坐标 -var gcj02tobd09=coordtransform.gcj02tobd09(116.404, 39.915); -//wgs84转国测局坐标 -var wgs84togcj02=coordtransform.wgs84togcj02(116.404, 39.915); -//国测局坐标转wgs84坐标 -var gcj02towgs84=coordtransform.gcj02towgs84(116.404, 39.915); -console.log(bd09togcj02); -console.log(gcj02tobd09); -console.log(wgs84togcj02); -console.log(gcj02towgs84); -//result -//bd09togcj02: [ 116.39762729119315, 39.90865673957631 ] -//gcj02tobd09: [ 116.41036949371029, 39.92133699351021 ] -//wgs84togcj02: [ 116.41024449916938, 39.91640428150164 ] -//gcj02towgs84: [ 116.39775550083061, 39.91359571849836 ] -``` -2 浏览器用法 -直接引用目录内的index.js,会有一个coordtransform的全局对象暴露出来,也支持用AMD加载器加载 - -``` - - - - - coordTransform - - -

请按F12打开控制台查看结果

- - - - -``` -### todos -- 墨卡托坐标 -- geojson转换 -- 批量转换 -- turf插件 -- leaflet插件 +浏览器用法 +直接引用目录内的GPSTrans.js,会有一个GPSTrans的全局对象暴露出来,也支持用AMD加载器加载 +为什么改名? +原来名字有点长,仅此而已。 请 fork原版, star 原版。 感谢原作者 wandergis的无私奉献 和 ipcjs(https://github.com/ipcjs/coordtransform/) 的改进。 + ### sometipes -对于做GIS的人来说,底图对我们还是很重要的,有时候看国外的底图很好看,换上之后发现坐标位置全部不对,因此写了这个包帮助大家完成坐标的转换,也准备写成一个leaflet的扩展,方便大家的使用,喜欢的童鞋请star,O(∩_∩)O +对于做GIS的人来说,底图对我们还是很重要的,有时候看国外的底图很好看,换上之后发现坐标位置全部不对,因此写了这个包帮助大家完成坐标的转换,也准备写成一个leaflet的扩展,方便大家的使用,喜欢的童鞋请去 star 原版 ,O(∩_∩)O diff --git a/index.js b/index.js deleted file mode 100644 index be1a7d4..0000000 --- a/index.js +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Created by Wandergis on 2015/7/8. - * 提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换 - */ -//UMD魔法代码 -// if the module has no dependencies, the above pattern can be simplified to -(function (root, factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define([], factory); - } else if (typeof module === 'object' && module.exports) { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like environments that support module.exports, - // like Node. - module.exports = factory(); - } else { - // Browser globals (root is window) - root.coordtransform = factory(); - } -}(this, function () { - //定义一些常量 - var x_PI = 3.14159265358979324 * 3000.0 / 180.0; - var PI = 3.1415926535897932384626; - var a = 6378245.0; - var ee = 0.00669342162296594323; - /** - * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换 - * 即 百度 转 谷歌、高德 - * @param bd_lon - * @param bd_lat - * @returns {*[]} - */ - var bd09togcj02 = function bd09togcj02(bd_lon, bd_lat) { - var bd_lon = +bd_lon; - var bd_lat = +bd_lat; - var x = bd_lon - 0.0065; - var y = bd_lat - 0.006; - var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI); - var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI); - var gg_lng = z * Math.cos(theta); - var gg_lat = z * Math.sin(theta); - return [gg_lng, gg_lat] - }; - - /** - * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换 - * 即谷歌、高德 转 百度 - * @param lng - * @param lat - * @returns {*[]} - */ - var gcj02tobd09 = function gcj02tobd09(lng, lat) { - var lat = +lat; - var lng = +lng; - var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI); - var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI); - var bd_lng = z * Math.cos(theta) + 0.0065; - var bd_lat = z * Math.sin(theta) + 0.006; - return [bd_lng, bd_lat] - }; - - /** - * WGS84转GCj02 - * @param lng - * @param lat - * @returns {*[]} - */ - var wgs84togcj02 = function wgs84togcj02(lng, lat) { - var lat = +lat; - var lng = +lng; - if (out_of_china(lng, lat)) { - return [lng, lat] - } else { - var dlat = transformlat(lng - 105.0, lat - 35.0); - var dlng = transformlng(lng - 105.0, lat - 35.0); - var radlat = lat / 180.0 * PI; - var magic = Math.sin(radlat); - magic = 1 - ee * magic * magic; - var sqrtmagic = Math.sqrt(magic); - dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); - dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); - var mglat = lat + dlat; - var mglng = lng + dlng; - return [mglng, mglat] - } - }; - - /** - * GCJ02 转换为 WGS84 - * @param lng - * @param lat - * @returns {*[]} - */ - var gcj02towgs84 = function gcj02towgs84(lng, lat) { - var lat = +lat; - var lng = +lng; - if (out_of_china(lng, lat)) { - return [lng, lat] - } else { - var dlat = transformlat(lng - 105.0, lat - 35.0); - var dlng = transformlng(lng - 105.0, lat - 35.0); - var radlat = lat / 180.0 * PI; - var magic = Math.sin(radlat); - magic = 1 - ee * magic * magic; - var sqrtmagic = Math.sqrt(magic); - dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); - dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); - var mglat = lat + dlat; - var mglng = lng + dlng; - return [lng * 2 - mglng, lat * 2 - mglat] - } - }; - - var transformlat = function transformlat(lng, lat) { - var lat = +lat; - var lng = +lng; - var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); - ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; - ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; - ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0; - return ret - }; - - var transformlng = function transformlng(lng, lat) { - var lat = +lat; - var lng = +lng; - var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); - ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; - ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; - ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0; - return ret - }; - - /** - * 判断是否在国内,不在国内则不做偏移 - * @param lng - * @param lat - * @returns {boolean} - */ - var out_of_china = function out_of_china(lng, lat) { - var lat = +lat; - var lng = +lng; - // 纬度3.86~53.55,经度73.66~135.05 - return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55); - }; - - return { - bd09togcj02: bd09togcj02, - gcj02tobd09: gcj02tobd09, - wgs84togcj02: wgs84togcj02, - gcj02towgs84: gcj02towgs84 - } -})); diff --git a/test/app.js b/test/app.js index 0dcd2ef..5d76cdb 100644 --- a/test/app.js +++ b/test/app.js @@ -2,15 +2,15 @@ * Created by wandergis on 15/11/17. */ //国测局坐标(火星坐标,比如高德地图在用),百度坐标,wgs84坐标(谷歌国外以及绝大部分国外在线地图使用的坐标) -var coordtransform = require('../index'); +var coordtransform = require('../GPSTrans'); //百度经纬度坐标转国测局坐标 -var bd09togcj02 = coordtransform.bd09togcj02(116.404, 39.915); +var bd09togcj02 = GPSTrans.bd09togcj02(116.404, 39.915); //国测局坐标转百度经纬度坐标 -var gcj02tobd09 = coordtransform.gcj02tobd09(116.404, 39.915); +var gcj02tobd09 = GPSTrans.gcj02tobd09(116.404, 39.915); //wgs84转国测局坐标 -var wgs84togcj02 = coordtransform.wgs84togcj02(116.404, 39.915); +var wgs84togcj02 = GPSTrans.wgs84togcj02(116.404, 39.915); //国测局坐标转wgs84坐标 -var gcj02towgs84 = coordtransform.gcj02towgs84(116.404, 39.915); +var gcj02towgs84 = GPSTrans.gcj02towgs84(116.404, 39.915); console.log(bd09togcj02); console.log(gcj02tobd09); console.log(wgs84togcj02); @@ -19,4 +19,4 @@ console.log(gcj02towgs84); //bd09togcj02: [ 116.39762729119315, 39.90865673957631 ] //gcj02tobd09: [ 116.41036949371029, 39.92133699351021 ] //wgs84togcj02: [ 116.41024449916938, 39.91640428150164 ] -//gcj02towgs84: [ 116.39775550083061, 39.91359571849836 ] \ No newline at end of file +//gcj02towgs84: [ 116.39775550083061, 39.91359571849836 ] diff --git a/test/index.html b/test/index.html index e7cc7bd..f13a709 100644 --- a/test/index.html +++ b/test/index.html @@ -6,17 +6,17 @@

请按F12打开控制台查看结果!(Please open console by F12!)

- + - \ No newline at end of file +