使用OpenLayers进行地图开发
之前使用openLayers开发了离线瓦片地图,做了下整理
使用OpenLayers进行地图开发
需要了解的前置知识
坐标系
常见坐标系
坐标系分为:地理坐标系
和投影坐标系
。通过第一次抽象,将地表物体抽象成规则的椭球面,即是地理坐标系;在此基础上,再进行一次,将椭球体上的物体抽象到平面,即是投影坐标系;所以对于一个具体的地图服务提供商,它的地理坐标系和投影坐标系一般是配合的,如高德采用GCJ02地理坐标系和GCJ02Web墨卡托投影坐标系;
坐标系名 | 坐标系类型 | EPSG代号 | 备注 |
---|---|---|---|
WGS84 | 地理坐标系 | EPSG:4326 | 世界大地坐标系(国际上标准的经纬度坐标系,如GPS定位、国外谷歌地图、Open Street Map 使用此坐标系) |
CGCS2000 | 地理坐标系 | EPSG4490 | 中国大地坐标系,天地图使用此坐标系 |
GCJ02 | 地理坐标系 | 无具体代号 | 国测局坐标系,又称火星坐标系,在WGS84坐标系上加密得来;(国内高德使用此坐标系) |
BD09 | 地理坐标系 | 无具体代号 | 百度坐标系,在GCJ02坐标系基础上再次加密(国内百度使用此坐标系) |
Web墨卡托投影坐标系 | 投影坐标系 | EPSG:3857 | 很多其他投影坐标系是基于此,BingMap使用此坐标系 |
坐标系的转换
- 由于我们使用的OpenLayers框架是国外产品, 默认不支持国内坐标系的,所以就涉及坐标系的转换问题;
- 由于坐标系涉及地理知识,对于我们这些一般不会接触地理的web前端开发不是很友好,所以坐标系的转换都是查找网上已经写好的转换方案或者使用别人封装好的库(如gcoord)
离线瓦片地图
什么是瓦片地图
瓦片地图金字塔模型是一种多分辨率层次
模型,从瓦片金字塔的底层到顶层,分辨率越来越低,但表示的地理范围不变。首先确定地图服务平台所要提供的缩放级别的数量N,把缩放级别最高、地图比例尺最大的地图图片作为金字塔的底层,即第0层
,并对其进行分块,从地图图片的左上角开始,从左至右、从上到下进行切割,分割成相同大小(比如256x256像素)的正方形地图瓦片,形成第0层瓦片矩阵;在第0层地图图片的基础上,按每像素分割为2×2个像素的方法生成第1层地图图片,并对其进行分块,分割成与下一层相同大小的正方形地图瓦片,形成第1层
瓦片矩阵;采用同样的方法生成第2层
瓦片矩阵;…;如此下去,直到第N-1
层,构成整个瓦片金字塔。简单概括如下:
- 具有唯一的瓦片等级(Level)和瓦片坐标编号(tileX, tileY)
- 瓦片图片分辨率为256*256(单位像素)
- 最小的地图等级是0,此时世界地图只由一张瓦片组成。
- 瓦片等级越高,组成世界地图的瓦片数越多,可以展示的地图越详细。
- 某一瓦片等级地图的瓦片是由低一级的各瓦片切割成的4个瓦片组成,遂形成了瓦片金字塔。
瓦片地图分类
特性 | 矢量瓦片 | 栅格瓦片 |
---|---|---|
使用方式 | 将矢量数据通过不同的描述文件来组织和定义,在客户端实时解析数据完成绘制 | 预先在服务端绘制好固定的PNG或其他格式的图片集合 |
瓦片体量 | 小 | 大 |
瓦片生成效率 | 高 | 低 |
分辨率 | 高 | 低 |
样式修改 | 支持 | 不支持 |
技术成熟度 | 差,数据不通用 | 好,图片可通用 |
交互性 | 很好 | 一般 |
对客户端要求 | 要求高,存在兼容性问题 | 要求低,性能稳定 |
瓦片地图分辨率
以百度地图为例:百度地图的瓦片定义的方式比较独特,原点的位置在经纬度都为0的地方,X向左为正,向右为负;Y向上为正,向下为负。切分的方式不是在每一级进行二等分,而是通过定义每一级的地图分辨率 ,确定每一级应该划分的行列数。地图分辨率的表达式为:Math.pow(2, 18 - z) z取值0~18
,其含义是每个像素所对应的实际长度。由此,可得每一级应该划分的行列数为:2πR/(256*(2^18-z )),其中R为地球的半径,单位是米,z是地图缩放层级。也就是说,当瓦片地图放大到18级时,图片每单位像素表示1米;
瓦片坐标与瓦片像素坐标
通常情况下,我们是知道一个点的地理坐标,然后标注到地图上。但是瓦片图就只是图片,并没有携带地理位置信息,那么是如何定位到具体某一个瓦片的某个像素点来做标注的呢?仍然以百度地图为例。
首先,瓦片坐标和瓦片像素坐标都应该放在具体地图缩放级别来讨论;当瓦片尺寸为256*256pixel的时候:
瓦片坐标(tileX,tileY) | 瓦片像素坐标(pixelX,pixelY) | 平面投影坐标(x,y) |
---|---|---|
tileX=int(x2^(z-18)/256) | pixelX=x * 2^(z-18)-int(x * 2^(z-18)/256) * 256 | x=(tileX * 256+pixelX) * 2^(18-z) |
tileY=int(y2^(z-18)/256) | pixelY=y * 2^(z-18)-int(y * 2^(z-18)/256) * 256 | y=(tileY * 256+pixelY) * 2^(18-z) |
x和y是平面投影坐标,百度地图瓦片平面投影坐标x最大值为20037726m, y最大值为12474104m。z是缩放级别(0~18),tileX,tileY表示当前坐标(x,y)定位的具体的这一张瓦片图片在地图中的什么位置;pixelX,pixelY 表示当前坐标(x,y)在这张瓦片图的哪个像素点上;
OpenLayers介绍
什么是OpenLayers
OpenLayers是一个高性能、功能丰富的库,用于在网络上创建交互式地图。它可以在任何网页上显示从任何来源加载的地图图块、矢量数据和标记。
OpenLayers可以实现哪些功能
- 从 OSM、Bing、MapBox、Stamen 以及您能找到的任何其他 XYZ 源中提取图块。还支持 OGC 地图服务和未耕图层。
- 渲染来自 GeoJSON、TopoJSON、KML、GML、Mapbox 矢量切片和其他格式的矢量数据。
- 利用 Canvas 2D、WebGL 以及 HTML5 的所有最新功能。开箱即用的移动支持。仅使用您需要的组件构建轻量级自定义配置文件。
- 使用简单的 CSS 设计您的地图控件。连接不同级别的 API 或使用第三方库来自定义和扩展功能。
OpenLayers的简单使用
例如一个功能(生成地图,绘制区域边界,添加指定坐标标点),还是以百度地图为例:
生成地图
获取瓦片图数据(网络搜索,例如:望远网)
瓦片图的投影与openlayers 默认的球面墨卡托投影 (EPSG:3857)不一致,需要重新定义BD:09的投影
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20const confineProj = () => {
/* 定义百度投影,这是实现无偏移加载百度地图离线瓦片核心所在。
网上很多相关资料在用OpenLayers加载百度地图离线瓦片时都认为投影就是EPSG:3857(也就是Web墨卡托投影)。
事实上这是错误的,因此无法做到无偏移加载。
百度地图有自己独特的投影体系,必须在OpenLayers中自定义百度投影,才能实现无偏移加载。
百度投影实现的核心文件为bd09.js,在迈高图官网可以找到查看这个文件。 */
const projBD09 = new Projection({
code: 'BD:09',
extent: [-20037726.37, -11708041.66, 20037726.37, 12474104.17],
units: 'm',
axisOrientation: 'neu',
global: false,
});
addProjection(projBD09);
addCoordinateTransforms('EPSG:4326', 'BD:09',
(coordinate) => lngLatToMercator(coordinate),
(coordinate) => mercatorToLngLat(coordinate),
);
};设置地图数据分辨率
1
2
3
4
5
6
7
8
9// 定义百度分辨率
const resolutions = [];
for (let i = 0; i <= 18; i++) {
resolutions[i] = Math.pow(2, 18 - i);
}
const tilegrid = new TileGrid({
origin: [0, 0],
resolutions,
});添加图层,在指定元素渲染地图
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
57const source = new TileImage({
projection: 'BD:09',
tileGrid: tilegrid,
tilePixelRatio: 2,
cacheSize: 10000,
tileUrlFunction(tileCoord, pixelRatio, proj) {
if (!tileCoord) {
return "";
}
let z = tileCoord[0];
let x = tileCoord[1];
let y = -tileCoord[2] - 1;//ol6需要此处减一,否则缩放有偏移
return `/baidu/tiles/fulin/${z}/${x}/${y}.png`;
},
});
const mapLayer = new TileLayer({
source,
});
myMap = new Map({
layers: [
mapLayer,
vectorTL,
],
view: new View({
//将坐标从源投影转换为目标投影。这将返回一个新坐标(并且不会修改原始坐标)
center: ProjTransform([
107.37492826262412,
29.689381110571663,
], 'EPSG:4326', 'BD:09'),
// 投影。默认为球面墨卡托。指定百度的坐标系
projection: 'BD:09',
// 层级 从 0 开始,zoom:10 对应的文件夹11
zoom: 10,
minZoom: 10,
maxZoom: 16,
constrainOnlyCenter: true,
extent: [
...ProjTransform([
106.66490758777209,
29.312665938287,
], 'EPSG:4326', 'BD:09'),
...ProjTransform([
107.91822344761631,
30.067675651437312,
], 'EPSG:4326', 'BD:09'),
],
}),
target: 'map',
//对地图容器中的控件设置
controls: defaultControls({
zoom: false,
rotate: false,
attribution: false,
}),
});
绘制区域边界
获取区域边界数据(从高德地图获取行政区域边界坐标后再通过
bd09.js
中的gcj02tobd09
方法转换为百度坐标系的坐标)设置矢量图层数据格式
1
2
3
4
5
6
7
8
9
10
11
12const vectorTL = new LayerVector({
source: new SourceVector({
url: '/fulin_geo_baidu.json',
format: new GeoJSON({
extractStyles: false,
}),
}),
style(feature) {
style.getText().setText(feature.get('name'));
return style;
},
});给边界矢量图层添加样式、文字等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const style = new Style({
fill: new Fill({
color: [124, 201, 245, 0.1],
}),
stroke: new Stroke({
color: '#7cc9f5',
width: 3,
}),
text: new Text({
font: '12px Calibri,sans-serif',
fill: new Fill({
color: '#fff',
}),
}),
});创建矢量图层,加载到地图中
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
37myMap = new Map({
layers: [
mapLayer,
vectorTL,
],
view: new View({
//将坐标从源投影转换为目标投影。这将返回一个新坐标(并且不会修改原始坐标)。
center: ProjTransform([
107.37492826262412,
29.689381110571663,
], 'EPSG:4326', 'BD:09'),
// 投影。默认为球面墨卡托。指定百度的坐标系
projection: 'BD:09',
// 层级 从 0 开始,zoom:10 对应的文件夹11
zoom: 10,
minZoom: 10,
maxZoom: 16,
constrainOnlyCenter: true,
extent: [
...ProjTransform([
106.66490758777209,
29.312665938287,
], 'EPSG:4326', 'BD:09'),
...ProjTransform([
107.91822344761631,
30.067675651437312,
], 'EPSG:4326', 'BD:09'),
],
}),
target: 'map',
//对地图容器中的控件设置
controls: defaultControls({
zoom: false,
rotate: false,
attribution: false,
}),
});
添加指定坐标标点
- 从后端获取坐标数据
- 转换为百度BD09坐标系(上面已定义)的坐标点
- 添加矢量图层
OpenLayers使用示例
安装依赖
1 | npm i ol |
在vue3中使用
以上面的功能为例,完整代码如下:
1 | <script setup> |
1 | //bd09.js |
参考文档:
使用OpenLayers进行地图开发
1.docker部署jenkins
2.云服务器上前端自动化部署一条龙
3.服务器docker安装nginx
4.hexo设置永久链接地址为数字编号
5.自动部署hexo博客
6.服务器上安装jenkins