Openlayer3实现可拖动的Overlay

标签: Openlayers 分类: Gis 创建时间:2021-01-21 05:58:49 更新时间:2025-01-17 10:39:22

使用Overlay创建的标签,如何才能实现鼠标移动呢?除了实现可以随意拖动Overlay之外,还需要提供一条指示线,从图标指向Overlay,就像下面图示一样。

基本的思路如下:

  • (1) 捕捉overlay的dom的鼠标事件“mousedown”事件,以及地图map的“pointermove”、“pointerup”事件;
  • (2) 当触发“mousedown”时,记录下当前鼠标在元素中的相对于基点也就是overlay定位点的位置,_x,_y;
  • (3) 紧接着处理“pointermove”事件,通过改变overlay的停靠点的位置,来移动元素;
  • (4) 当触发“pointerup”事件时,终止拖动。

1.创建一个overlay

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
import OSM from 'ol/source/OSM';
import {fromLonLat} from 'ol/proj';
import TileLayer from 'ol/layer/Tile';
import View from 'ol/View';
import Map from 'ol/Map';
import Overlay from 'ol/Overlay';
import DragPan from 'ol/interaction/DragPan';

var pos = fromLonLat([16.3725, 48.208889]);
var layer = new TileLayer({
source: new OSM()
});
var map = new Map({
layers: [layer],
target: 'map',
view: new View({
center: pos,
zoom: 2
})
});
var overlayDom = document.getElementById('marker');
var overlay = new Overlay({
position: pos,
positioning: 'center-center',
element: overlayDom,
stopEvent: false,
dragging: false
});
map.addOverlay(marker);

2.实现可拖动

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
// overlayDom是overlay的dom
// overlay是overlay
// 绑定属性框移动事件
var dragPan;
let map=this.mainmap;
// 获取拖动对象
map.getInteractions().forEach(function(interaction){
if (interaction instanceof DragPan) {
dragPan = interaction;
}
});
// 监听鼠标点击事件
let offsetXY=[0,0];
overlayDom.addEventListener('mousedown', function(evt) {
dragPan.setActive(false);
overlay.set('dragging', true);
// 获取事件点击的坐标值
let evtCoord=map.getEventCoordinate(evt);
let domCoord=overlay.get("coordinate");
// 计算鼠标点击位置和基点坐标(可能是div的左上角)之间的偏移量
offsetXY=[evtCoord[0]-domCoord[0],evtCoord[1]-domCoord[1]];
});
// 监听地图鼠标移动事件
map.on('pointermove', function(evt) {
if (overlay.get('dragging') === true) {
// 将当前的鼠标位置减去偏移量,得到基点应该所处的位置
let coor=[evt.coordinate[0]-offsetXY[0],evt.coordinate[1]-offsetXY[1]];
overlay.setPosition(coor);
// 保存最新坐标,因为使用getPostion()方法无法获取overlay的坐标
overlay.set("coordinate",coor);

// 绘制虚线
}
});
// 监听鼠标释放事件
map.on('pointerup', function() {
if (overlay.get('dragging') === true) {
dragPan.setActive(true);
overlay.set('dragging', false);
}
});
参考文章:
1.openlayers 地图上绘制区域悬浮弹窗实现跟随鼠标移动 (这个代码也挺简单的,没有拖动一个div,而是实现了一个可以随着鼠标移动的overlay)
2.javascript - 我们可以在OpenLayers 3上使ol.Overlay可拖动吗? (这里有一点就是说,ol.Feature可以实现任何事情,所有不要用Overlay)
3.关于Openlayers Overlay事件监听的一个坑 (这里其实是一个进行标会的案例,说了自己在开发这么一个功能的时候,遇到的手机端和PC端的问题,对于简单的一个地图PC端拖动来说,其实还是不需要的。因为里面涉及到了一些源码的东西)
4.一个可以拖动的Overlay的实例 (这是实现了拖动Overlay的实例,强烈推荐,就是源码,除了没有属性框以及连线,其他的都包括了)
5.原生JavaScript实现div拖拽效果 (涉及到一个偏移量的问题,文章中还写道了进行原生拖动的原理)
6.用Javascript获取页面元素的位置 (阮一峰的讲解)
7.js实现可拖拽的div (代码虽小,但是功能都全了)
8.使用js拖拽div (大同小异)

3.绘制链接线

这个其实有些麻烦的,我感觉。

1
2
3
4
5
6
7
8
9
10
11
12
13
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';

// 添加连线图层
let vectorSource = new VectorSource({
name:"overlayLineLayer_source",
});
let vector = new VectorLayer({
source: vectorSource,
name:"overlayLineLayer_layer",
zIndex:15
});
map.addLayer(vector);

其中的feature其实就是对应了Overlayer对应的那个地图图标,就是一个箭头的地方。使用监听的方式,其实是因为地图缩放的时候,会出现的起始点连线不正确的问题,监听地图的rendercomplete,是因为不知道为什么,在第一绘制线段的时候,使用map.getPixelFromCoordinate(stationCoordinate);无法获取到正确的屏幕Pixel。

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
let stationGeom=feature.getGeometry();
let stationCoordinate=stationGeom.getCoordinates(); // 站点坐标
overlay.set("stationCoordinate",stationCoordinate); // 保存站点坐标
// 监听地图分辨率改变事件,重新计算指向线的起点位置
let mapview=map.getView();
mapview.on("change:resolution",function(){
// 计算图标顶点的坐标,也就是绘制的连线,应该指向图标的顶点
// 计算方法,也就是获取图标的旋转角度,获取站点原始坐标,图标的宽高为30x46
// 根据三角函数,已知斜边,求两个直角标的宽高,就可以使用sin和cos三角函数关系
// 其中36是图标的不含外边框的高度
let label=feature.get("label");
let radian = 0; // 角度转弧度
let picOffsetX=parseInt(36*Math.sin(radian));
let picOffsetY=parseInt(36*Math.cos(radian));
switch(label){
case 0:
picOffsetX=0;
picOffsetY=36;
break;
case 90:
picOffsetX=36;
picOffsetY=0;
break;
case 180:
picOffsetX=0;
picOffsetY=-36;
break;
case 270:
picOffsetX=-36;
picOffsetY=0;
break;
default:
radian = label * (Math.PI/180); // 角度转弧度
picOffsetX=parseInt(36*Math.sin(radian));
picOffsetY=parseInt(36*Math.cos(radian));
break;
}
// 获取站点坐标的屏幕像素值,
// 这里有一个问题,就是当地图第一次加载的时候,会出现stationPixel计算不正确的问题,通过设置地图渲染完成监听事件进行解决
let stationPixel=map.getPixelFromCoordinate(stationCoordinate);
// 计算屏幕像素偏移量
stationPixel=[stationPixel[0]+picOffsetX,stationPixel[1]-picOffsetY];
// if(feature.get("id")==26){
// console.log(stationPixel);
// }
let offsetCoordinate=map.getCoordinateFromPixel(stationPixel);
overlay.set("offsetCoordinate",offsetCoordinate); // 也就是偏移点坐标

// 删除已有的连线
let lineFeature=overlay.get("line");
let source=that.overlayLineLayer.getSource();
if(lineFeature){
source.removeFeature(lineFeature);
}
// 获取最新坐标
let coor=overlay.get("coordinate");
if(coor){
let tempOffset=map.getPixelFromCoordinate(coor);
tempOffset=[tempOffset[0]+offset[0],tempOffset[1]+offset[1]]
coor=map.getCoordinateFromPixel(tempOffset);
// 绘制虚线,从overlay的基点位置,到图标的末点位置
lineFeature = new Feature({
geometry: new LineString([coor,offsetCoordinate])
});
overlay.set("line",lineFeature);
source.addFeature(lineFeature);
}
});
// 地图渲染完成之后,绘制第一条连线
map.once("rendercomplete",function(){
// 计算图标顶点的坐标,也就是绘制的连线,应该指向图标的顶点
// 计算方法,也就是获取图标的旋转角度,获取站点原始坐标,图标的宽高为30x46
// 根据三角函数,已知斜边,求两个直角标的宽高,就可以使用sin和cos三角函数关系
let label=feature.get("label");
let radian = 0; // 角度转弧度
let picOffsetX=parseInt(36*Math.sin(radian));
let picOffsetY=parseInt(36*Math.cos(radian));
switch(label){
case 0:
picOffsetX=0;
picOffsetY=36;
break;
case 90:
picOffsetX=36;
picOffsetY=0;
break;
case 180:
picOffsetX=0;
picOffsetY=-36;
break;
case 270:
picOffsetX=-36;
picOffsetY=0;
break;
default:
radian = label * (Math.PI/180); // 角度转弧度
picOffsetX=parseInt(36*Math.sin(radian));
picOffsetY=parseInt(36*Math.cos(radian));
break;
}
// 获取站点坐标的屏幕像素值,
// 这里有一个问题,就是当地图第一次加载的时候,会出现stationPixel计算不正确的问题
let stationPixel=map.getPixelFromCoordinate(stationCoordinate);
// 计算屏幕像素偏移量
stationPixel=[stationPixel[0]+picOffsetX,stationPixel[1]-picOffsetY];
let offsetCoordinate=map.getCoordinateFromPixel(stationPixel);
overlay.set("offsetCoordinate",offsetCoordinate); // 也就是偏移点坐标

// 删除已有的连线
let lineFeature=overlay.get("line");
let source=that.overlayLineLayer.getSource();
if(lineFeature){
source.removeFeature(lineFeature);
}
// 获取最新坐标
let coor=overlay.get("coordinate");
if(coor){
let tempOffset=map.getPixelFromCoordinate(coor);
tempOffset=[tempOffset[0]+offset[0],tempOffset[1]+offset[1]]
coor=map.getCoordinateFromPixel(tempOffset);
// 绘制虚线,从overlay的基点位置,到图标的末点位置
lineFeature = new Feature({
geometry: new LineString([coor,offsetCoordinate])
});
overlay.set("line",lineFeature);
source.addFeature(lineFeature);
}
});

拖动绘制虚线的代码

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
// 监听地图鼠标移动事件
map.on('pointermove', function(evt) {
if (overlay.get('dragging') === true) {
// 将当前的鼠标位置减去偏移量,得到基点应该所处的位置
// 基点位置,即overlay停靠到图标的位置
let coor=[evt.coordinate[0]-offsetXY[0],evt.coordinate[1]-offsetXY[1]];
overlay.setPosition(coor);
// 保存最新坐标,因为使用getPostion()方法无法获取overlay的坐标
overlay.set("coordinate",coor);


// 删除已有的连线
let lineFeature=overlay.get("line");
let source=that.overlayLineLayer.getSource();
if(lineFeature){
source.removeFeature(lineFeature);
}
// 计算偏移量,从基点位置,反求停靠点位置
let tempOffset=map.getPixelFromCoordinate(coor);
tempOffset=[tempOffset[0]+offset[0],tempOffset[1]+offset[1]]
coor=map.getCoordinateFromPixel(tempOffset);
// 绘制虚线,从overlay的基点位置,到图标的末点位置
let offsetCoordinate=overlay.get("offsetCoordinate");

if(offsetCoordinate){
lineFeature = new Feature({
geometry: new LineString([coor,offsetCoordinate])
});
overlay.set("line",lineFeature);
source.addFeature(lineFeature);
}

}
});

这样就完成了整个拖动ovlayer并且添加指向线的功能。

参考文章:
1.Openlayers 3 linedash画虚线 (绘制虚线)
小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。