WebGis技术汇总、坐标体系转换

前言

WebGIS系统通常都围绕地图进行内容表达,但并不是有地图就一定是WebGIS,所以有必要讨论下基于Web的地图API分类及应用场景。

Web上的Map API主要分类如下几类:

  • Charts:以D3.js,Echarts等为代表。
  • LBS:以高德/谷歌/百度地图等为代表。
  • WebGIS商业API:ESRI的ArcGIS API For JS,超图的IClient。
  • WebGIS开源API:Leaflet,OpenLayers,Cesium,MapboxGL等。

建模工具Blender

https://zhuanlan.zhihu.com/p/628531907

Leaflet

https://leafletjs.com/

OpenLayers

https://openlayers.org/doc/quickstart.html

https://blog.csdn.net/qq_29602347/article/details/99623968

https://viglino.github.io/ol-ext/examples/map/map.layer.3D.html

Cesium

https://github.com/CesiumGS/cesium

https://sandcastle.cesium.com/?src=3D%20Tiles%20Photogrammetry.html

使用OpenLayers

创建项目

1
vue create z-webgis-ol

添加依赖

1
2
3
cd z-webgis-ol

npm i -S ol

地图页面

添加页面

OlMap.vue

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// example
<template>
<div style="height: 100vh; width: 100vw">
<div id="map" ref="map" style="height: 100%; width: 100%"></div>
</div>
</template>

<script>
import "ol/ol.css";
import { Map, View } from "ol";
import TileLayer from "ol/layer/Tile";
import OSM from "ol/source/OSM";

export default {
data() {
return {
map: {},
};
},
mounted() {
this.initMap();
},
methods: {
initMap() {
this.map = new Map({
target: "map",
layers: [
new TileLayer({
source: new OSM({
url: "http://webst0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}",
crossOrigin: "anonymous",
maxZoom: 19,
}),
}),
],
view: new View({
center: [121.5025, 31.237015], // 中心点, 填的是经纬度
projection: "EPSG:4326", // EPSG:4326格式的经纬度
zoom: 10, // 地图默认缩放级别
maxZoom: 15, // 地图最大缩放级别
// minZoom: 5, // 地图最小缩放级别
}),
});
},
},
};
</script>

<style>
#map {
height: 100%;
}

/*隐藏ol的一些自带元素*/
.ol-attribution,
.ol-zoom {
display: none;
}
</style>

瓦片地址

瓦片地址配置

1
2
3
new TileLayer({
source: new OSM(),
}),

更换为

1
2
3
4
5
6
7
new TileLayer({
source: new OSM({
url: "https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png",
crossOrigin: "anonymous",
maxZoom: 19,
}),
}),

高德

1
2
3
4
5
6
7
new TileLayer({
source: new OSM({
url: "http://webst0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}",
crossOrigin: "anonymous",
maxZoom: 19,
}),
}),

openstreetmap地址

1
https://a.tile.openstreetmap.org/10/809/421.png

格式为

1
https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png

高德的地址

http://webst01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x=809&y=421&z=10

格式为

1
http://webst0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}

经纬度

1
2
3
4
5
6
7
view: new View({
center: [121.5025, 31.237015], // 中心点, 填的是经纬度
projection: "EPSG:4326", // EPSG:4326格式的经纬度
zoom: 10, // 地图默认缩放级别
maxZoom: 15, // 地图最大缩放级别
minZoom: 5, // 地图最小缩放级别
}),

引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div class="home">
<olmap />
</div>
</template>

<script>
// @ is an alias to /src
import olmap from "@/components/OlMap.vue";

export default {
name: "HomeView",
components: {
olmap,
},
};
</script>

百度坐标拾取器

https://lbs.qq.com/getPoint/

经纬度转换

概念

投影方式

  • Google的墨卡托坐标,也就是我们经常看到的 EPSG:3857 坐标系。
    EPSG:3857 的数据一般是这种的。[12914838.35,4814529.9],看上去相对数值较大。不利于存储,比较占内存。

  • EPSG:4326/WGS-84:是国际标准,GPS坐标(Google Earth使用、或者GPS模块)。
    EPSG:4326 的数据一般是这种的。[22.37,114.05]。利于存储,可读性高。
    所以我们常常看到和用到的坐标系数据往往不是墨卡托坐标,而是EPSG:4326坐标系下的坐标数据。

讲完投影方式,我们再聊一下坐标系,WGS84、GCJ-02、BD-09

WGS84/EPSG:4326:也叫大地坐标系,它是原始坐标系统。

GCJ02/火星坐标:是由中国国家测绘局(G表⽰Guojia国家,C表⽰Cehui测绘,J表⽰Ju局)制订的地理信息系统的坐标系统,是在WGS84经纬度的基础上执⾏加密算法⽽成。因为GPS得到的经纬度直接在 GCJ-02 坐标系下会定位到错误的地点,有种到了⽕星的感觉,因此在坊间也将 GCJ-02 戏称为火星坐标系。

BD09:只有百度地图没有使用这种加密算法,而是使用的是BD09,从名字上可以看出,GCJ02是2002年提出来的算法,BD09则是2009年提出来的,虽然百度地图没有使用GCJ02加密算法,但是他却是在GCJ02基础上做了一个二次加密,所以说,从WGS84坐标系不能直接转BD09,中间需要跨越一个GCJ02,反过来,需要将GCJ02或者BD09转为WGS84就是纠偏算法,相当于逆向解密,同样的DB09直接到不了WGS84,中间还需要转为GCJ02,所以现在的很多算法,如果你看到有百度坐标转大地坐标,基本上需要借助火星坐标来计算。

使用对象

高德地图、腾讯地图以及谷歌中国区地图使用的是GCJ-02坐标系

百度地图使用的是BD-09坐标系

底层接口(HTML5 Geolocation或iOS、安卓API)通过GPS设备获取的坐标使用的是EPSG:4326/WGS-84坐标系

在Openlayers 中默认的坐标就是Google的摩卡托坐标,也就是我们经常看到的 EPSG:3857 坐标系。

不过我们可以设置为EPSG:4326/WGS-84

1
2
3
4
5
6
7
view: new View({
center: [121.5025, 31.237015], // 中心点, 填的是经纬度
projection: "EPSG:4326", // EPSG:4326格式的经纬度
zoom: 10, // 地图默认缩放级别
maxZoom: 15, // 地图最大缩放级别
minZoom: 5, // 地图最小缩放级别
}),

坐标示例:

  • EPSG:3857 的数据一般是这种的:[12914838.35,4814529.9],看上去相对数值较大。不利于存储,比较占内存。

  • EPSG:4326/WGS-84 数据一般是这种的:[121.5025, 31.237015]

  • BD-09的数据一般是这种的[113.65523,34.79977]

EPSG:4326/WGS-84:是国际标准,GPS坐标(Google Earth使用、或者GPS模块)EPSG:4326 的数据一般是这种的:[22.37,114.05]。利于存储,可读性高。

EPSG:4326和EPSG:3857区别(重点)

EPSG:3857(投影):数据的可读性差和数值大存储比较占用内存。
EPSG:4326(地理):使用此坐标系会导致页面变形。

经纬度存储计算用EPSG:4326也就是WGS84,数据展示用EPSG:3857。

百度地图采用 BD09 百度坐标系,叠加到 Leaflet 和 Cesium 默认的 WGS84 通用坐标系需要进行纠偏。

2个格式的数据怎么互转呢?

JS转换

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
var GPS = {
PI: 3.14159265358979324,
x_pi: 3.14159265358979324 * 3000.0 / 180.0,
delta: function (lat, lon) {
// Krasovsky 1940
//
// a = 6378245.0, 1/f = 298.3
// b = a * (1 - f)
// ee = (a^2 - b^2) / a^2;
var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
var dLat = this.transformLat(lon - 105.0, lat - 35.0);
var dLon = this.transformLon(lon - 105.0, lat - 35.0);
var radLat = lat / 180.0 * this.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) * this.PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI);
return { 'lat': dLat, 'lon': dLon };
},

//WGS-84 to GCJ-02
gcj_encrypt: function (wgsLat, wgsLon) {
if (this.outOfChina(wgsLat, wgsLon))
return { 'lat': wgsLat, 'lon': wgsLon };

var d = this.delta(wgsLat, wgsLon);
return { 'lat': wgsLat + d.lat, 'lon': wgsLon + d.lon };
},
//GCJ-02 to WGS-84 (粗略)
gcj_decrypt: function (gcjLat, gcjLon) {
if (this.outOfChina(gcjLat, gcjLon))
return { 'lat': gcjLat, 'lon': gcjLon };

var d = this.delta(gcjLat, gcjLon);
return { 'lat': gcjLat - d.lat, 'lon': gcjLon - d.lon };
},
//GCJ-02 to WGS-84 exactly (精确(二分极限法))
gcj_decrypt_exact: function (gcjLat, gcjLon) {
var initDelta = 0.01;
var threshold = 0.000000001;
var dLat = initDelta, dLon = initDelta;
var mLat = gcjLat - dLat, mLon = gcjLon - dLon;
var pLat = gcjLat + dLat, pLon = gcjLon + dLon;
var wgsLat, wgsLon, i = 0;
while (1) {
wgsLat = (mLat + pLat) / 2;
wgsLon = (mLon + pLon) / 2;
var tmp = this.gcj_encrypt(wgsLat, wgsLon)
dLat = tmp.lat - gcjLat;
dLon = tmp.lon - gcjLon;
if ((Math.abs(dLat) < threshold) && (Math.abs(dLon) < threshold))
break;

if (dLat > 0) pLat = wgsLat; else mLat = wgsLat;
if (dLon > 0) pLon = wgsLon; else mLon = wgsLon;

if (++i > 10000) break;
}
//console.log(i);
return { 'lat': wgsLat, 'lon': wgsLon };
},
//GCJ-02 to BD-09
bd_encrypt: function (gcjLat, gcjLon) {
var x = gcjLon, y = gcjLat;
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);
bdLon = z * Math.cos(theta) + 0.0065;
bdLat = z * Math.sin(theta) + 0.006;
return { 'lat': bdLat, 'lon': bdLon };
},
//BD-09 to GCJ-02
bd_decrypt: function (bdLat, bdLon) {
var x = bdLon - 0.0065, y = bdLat - 0.006;
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);
var gcjLon = z * Math.cos(theta);
var gcjLat = z * Math.sin(theta);
return { 'lat': gcjLat, 'lon': gcjLon };
},
//WGS-84 to Web mercator
//mercatorLat -> y mercatorLon -> x
mercator_encrypt: function (wgsLat, wgsLon) {
var x = wgsLon * 20037508.34 / 180.;
var y = Math.log(Math.tan((90. + wgsLat) * this.PI / 360.)) / (this.PI / 180.);
y = y * 20037508.34 / 180.;
return { 'lat': y, 'lon': x };
/*
if ((Math.abs(wgsLon) > 180 || Math.abs(wgsLat) > 90))
return null;
var x = 6378137.0 * wgsLon * 0.017453292519943295;
var a = wgsLat * 0.017453292519943295;
var y = 3189068.5 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)));
return {'lat' : y, 'lon' : x};
//*/
},
// Web mercator to WGS-84
// mercatorLat -> y mercatorLon -> x
mercator_decrypt: function (mercatorLat, mercatorLon) {
var x = mercatorLon / 20037508.34 * 180.;
var y = mercatorLat / 20037508.34 * 180.;
y = 180 / this.PI * (2 * Math.atan(Math.exp(y * this.PI / 180.)) - this.PI / 2);
return { 'lat': y, 'lon': x };
},
//两点的距离
distance: function (latA, lonA, latB, lonB) {
var earthR = 6371000.;
var x = Math.cos(latA * this.PI / 180.) * Math.cos(latB * this.PI / 180.) * Math.cos((lonA - lonB) * this.PI / 180);
var y = Math.sin(latA * this.PI / 180.) * Math.sin(latB * this.PI / 180.);
var s = x + y;
if (s > 1) s = 1;
if (s < -1) s = -1;
var alpha = Math.acos(s);
var distance = alpha * earthR;
return distance;
},
outOfChina: function (lat, lon) {
if (lon < 72.004 || lon > 137.8347)
return true;
if (lat < 0.8293 || lat > 55.8271)
return true;
return false;
},
transformLat: function (x, y) {
var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0;
return ret;
},
transformLon: function (x, y) {
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0;
return ret;
}
};

上面这段代码,没有对百度坐标bd09转大地坐标wgs84、大地坐标wgs84转百度坐标bd09进行书写,

如果你了解坐标之间的关系,你大概就能猜到:

只需要借助wgs84->gcj02->bd09就能实现wgs84->bd09的实现。

相反,借助bd09->gcj02->wgs84就能实现bd09->wgs84的转换了。

另一个封装

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/**
* Created by Wandergis on 2015/7/8.
* 提供了百度坐标(BD-09)、国测局坐标(火星坐标,GCJ-02)、和 WGS-84 坐标系之间的转换
*/
// 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_lng
* @param bd_lat
* @returns {*[]}
*/
var bd09togcj02 = function bd09togcj02(bd_lng, bd_lat) {
var bd_lng = +bd_lng;
var bd_lat = +bd_lat;
var x = bd_lng - 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]
};

/**
* WGS-84 转 GCJ-02
* @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]
}
};

/**
* GCJ-02 转换为 WGS-84
* @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
}
}));

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//国测局坐标(火星坐标,比如高德地图在用),百度坐标,wgs84坐标(谷歌国外以及绝大部分国外在线地图使用的坐标)
var coordtransform = require('../index');
//百度经纬度坐标转国测局坐标
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 ]

自带transform

可以通过ol/proj/transform这个方法
例:

1
transform([121.501842, 31.239204], ‘EPSG:4326’, ‘EPSG:3857’)

经纬度存储计算用EPSG:4326也就是WGS84,数据展示用EPSG:3857。

用 fromLonLat() 方法把4326的坐标转换为3857的坐标。

1
2
3
4
5
6
7
8
9
10
11
12
import { fromLonLat } from 'ol/proj'
this.centerMap = new Map({
target: 'centerMap',
layers: this.baseLayers,
view: new View({
projection: 'EPSG:3857',
center: fromLonLat([117.691603, 39.014074]),
zoom: 11.5,
maxZoom: 18,
minZoom: 5
})
})

在实际开发中,因为map源数据大部分都是EPSG:4326的数据源格式的数据,但是使用EPSG:4326的坐标系地图会出现被压缩的感觉。

所以我们都是采用 EPSG:3857的坐标系类型,把数据源转换位 EPSG:3857的数据源即可。

但是这个EPSG:3857数据源不易读取和值占内存原因,所有结合两者的缺点,我们采用坐标转换,即 EPSG:4326转 EPSG:3857。

所有请理解这句话:

通常:数据存储在EPSG:4326中,显示在EPSG:3857中 如下所示:

1
2
3
4
5
6
7
8
function anmiteCenter(map, attr, zoom) {
let pos = [parseFloat(attr.lon), parseFloat(attr.lat)];
pos = ol.proj.transform(pos, 'EPSG:4326', 'EPSG:3857');
map.getView().animate({
center: pos,
zoom: zoom
});
}

三方库gcoord

使用gcoord这个库

1
npm install gcoord --save

使用

1
2
3
4
5
6
import gcoord from 'gcoord'
const xy = gcoord.transform(
[经度, 纬度],
gcoord.EPSG4326,
gcoord.EPSG3857
)

瓦片地址

高德(可用)

高德瓦片地图下载地址

矢量图

矢量图(含路网、含注记)

1
http://wprd0{1,4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7

示例

http://wprd01.is.autonavi.com/appmaptile?x=809&y=421&z=10&lang=zh_cn&size=1&scl=1&style=7

http://wprd04.is.autonavi.com/appmaptile?x=809&y=421&z=10&lang=zh_cn&size=1&scl=1&style=7

矢量图(含路网,不含注记)

1
http://wprd0{1,4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=2&style=7

示例

http://wprd01.is.autonavi.com/appmaptile?x=809&y=421&z=10&lang=zh_cn&size=1&scl=2&style=7

http://wprd04.is.autonavi.com/appmaptile?x=809&y=421&z=10&lang=zh_cn&size=1&scl=2&style=7

影像

高德影像:

1
https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}

https://webst01.is.autonavi.com/appmaptile?style=6&x=809&y=421&z=10

参数说明:

  1. {1,4}选一个数字,

  2. {x}、{y}、{z}需要替换为相应的层级。

  3. lang=zh_cn 标注语言为中文。

ArcGIS(可用)

ArcGIS上对外开放的瓦片地址网站

中文

1
https://map.geoq.cn/arcgis/rest/services/ChinaOnlineCommunity_Mobile/MapServer/tile/{z}/{y}/{x}

示例

https://map.geoq.cn/arcgis/rest/services/ChinaOnlineCommunity_Mobile/MapServer/tile/10/421/809

英文

1
https://map.geoq.cn/arcgis/rest/services/ChinaOnlineCommunityENG/MapServer/tile/{z}/{y}/{x}

示例

https://map.geoq.cn/arcgis/rest/services/ChinaOnlineCommunityENG/MapServer/tile/10/421/809

中文灰色

1
https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetGray/MapServer/tile/{z}/{y}/{x}

示例

https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetGray/MapServer/tile/10/421/809

中文蓝色

1
https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}

示例

https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/10/421/809

中文暖色

1
https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetWarm/MapServer/tile/{z}/{z}/{y}/{x}

示例

https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetWarm/MapServer/tile/10/421/809

百度(不推荐)

不推荐使用百度的,原因:

百度使用的坐标体系不是标准的WGS84,所以瓦片的XY坐标也不一致。

百度地图瓦片地址:
黄色底图

1
http://api0.map.bdimg.com/customimage/tile?&x={x}&y={y}&z={z}&udt=20191205&scale=1&ak=5ieMMexWmzB9jivTq6oCRX9j1

示例地址:

http://api0.map.bdimg.com/customimage/tile?&x=100&y=100&z=10&udt=20230704&scale=1&ak=gBd1Zf7VGjYj5G9YvWDKtHOniPvs1WGd

夜深色地图,蓝色地图

1
http://api0.map.bdimg.com/customimage/tile?&x={x}&y={y}&z={z}&udt=20191205&scale=1&ak=5ieMMexWmzB9jivTq6oCRX9j&customid=midnight

http://api0.map.bdimg.com/customimage/tile?&x=100&y=100&z=10&udt=20191205&scale=1&ak=5ieMMexWmzB9jivTq6oCRX9j&customid=midnight

百度个性化地图模板
只需要修改customid参数分别为:midnight、light、normal、grassgreen等。

腾讯(已失效)

1
2
3
4
5
6
7
8
9
vector = "http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={reverseY}&type=vector&style=0&scene=0",
terrain = "http://p{s}.map.gtimg.com/demTiles/{z}/{sx}/{sy}/{x}{reverseY}.jpg&scene=0",
img = "https://p{s}.map.gtimg.com/sateTiles/{z}/{sx}/{sy}/{x}{reverseY}.jpg&scene=0”,
label = "https://rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&type=vector&styleid=3”,
vectorSimple = "https://rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&type=vector&styleid=1”,
vectorGrey = "https://rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&type=vector&styleid=2”,
vectorGreyExt = "https://rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&type=vector&styleid=3”,
vectorBlue = "https://rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&type=vector&styleid=4”,
vectorNormal = "https://rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&type=vector&styleid=8”,

示例

http://rts.map.gtimg.com/realtimerender?z=10&x=809&y=421&type=vector&style=0&scene=0

新版本返回格式

https://rt2.map.gtimg.com/vector/?z=11&x=1674&y=1232&type=jsonp&version=1346&compress=1&enc=simon&key=4VQBZ-ZGO3G-VGSQE-ILN4G-LWFUK-5WB7H&output=jsonp&pf=jsapi&ref=jsapi&cb=qq.maps._svcb3.td1674_815_11