需求
用户点击按钮,可以在地图上进行框选,框选结束后,返回框选区域内的数据。前端在用户框选结束后,获取到框选的范围:左上角经纬度和右下角经纬度,将经纬度信息发送给后台,后台返回区域内数据。
实现过程
通过OpenLayers的ol.interaction.DragBox
实现。在框选结束后,通过dragBox.getGeometry().getExtent()
获取框选区域的范围坐标。返回值是一个数组[左上角经度,左上角纬度,右下角经度,右下角纬度]
。但是经纬度值是投影坐标系(EPSG:3857),需要使用ol.proj.transform
转换为我们一般常见的EPSG4326
坐标系,然后发送给后台。
知识点
-
ol.source.OSM()
,使用OpenStreetMap提供的切片数据,等同于https://tile.openstreetmap.org/{z}/{x}/{y}.png
。国内访问可能有限制,可以使用高德地图或者天地图的瓦片服务,或者使用天地图的服务,天地图的需要注册并申请key,高德地图的无需,直接使用即可。const openStreetMap = new ol.layer.Tile({ source: new ol.source.OSM() }); // 等同于 const osmLayer = new ol.layer.Tile({ source: new ol.source.XYZ({ url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' }) }); // 高德矢量 http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}
-
天地图的矢量底图和矢量注记是分开的,需要添加两个图层,高德地图的是在一起的。
-
ol.proj.transform()
可实现不同的坐标的转换,([经度,纬度],坐标系1,坐标系2)
。 -
new ol.interaction.DragBox()
的condition
参数可以控制框选的触发时机。ol.events.condition.platformModifierKeyOnly
表示按住Ctrl+鼠标左键进行框选。ol.events.condition.noModifierKeys
表示不用其他辅助按键,直接鼠标左键框选。 -
map.removeInteraction(dragBox)
删除添加到地图上的交互。 -
可通过
ol.util.VERSION
查看openlayers
查看版本号。
代码HTML+CSS+JS
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>地图上框选范围</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/ol.min.css" integrity="sha512-bc9nJM5uKHN+wK7rtqMnzlGicwJBWR11SIDFJlYBe5fVOwjHGtXX8KMyYZ4sMgSL0CoUjo4GYgIBucOtqX/RUQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style type="text/css">
* {
padding: 0;
padding: 0;
box-sizing: border-box;
}
body, html {
width: 100%;
height: 100%;
margin: 0;
font-family: "Microsoft YaHei"
}
#map, #app {
width: 100vw;
height: 100vh;
}
#info {
position: fixed;
right: 10px;
top: 10px;
background-color: #fff;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
width: 250px;
padding: 10px;
text-align: center;
border-radius: 5px;
}
button {
font-size: 18px;
border: none;
padding: 12px 20px;
border-radius: 4px;
color: #fff;
background-color: #409eff;
border-color: #409eff;
cursor: pointer;
border: 1px solid #dcdfe6;
}
p {
position: fixed;
left: 10px;
top: 10px;
z-index: 2;
padding: 2px 10px;
color: #fff;
background-color: #07c170;
font-size: 12px;
border-radius: 3px;
}
button:disabled {
color: rgba(16, 16, 16, 0.3);
background-color: rgba(239, 239, 239, 0.3);
cursor: not-allowed;
}
.lonlat > div {
display: flex;
padding: 15px;
justify-content: space-between;
border-bottom: 1px solid rgba(7, 193, 96, 0.4);
}
.dragBox {
background-color: rgba(7, 193, 96, 0.4);
}
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<div id="map" @mousemove='handleMouseMoveOnMap' @mouseenter='handleMouseMoveOnMap' @mouseleave='handleMouseLeaveOnMap'></div>
<p :style='{left: clientX, top: clientY}'>按住鼠标左键拖拽框选</p>
<div id="info">
<button @click='handleClickSelectMap' :disabled='isDisabled'>地图上框选范围</button>
<div class="lonlat">
<div>
<span>左上角经度:</span>
<span>{{lonlat1[0]}}</span>
</div>
<div>
<span>左上角纬度:</span>
<span>{{lonlat1[1]}}</span>
</div>
<div>
<span>右下角经度:</span>
<span>{{lonlat2[0]}}</span>
</div>
<div>
<span>右下角纬度:</span>
<span>{{lonlat2[1]}}</span>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/dist/ol.min.js" integrity="sha512-+nvfloZUX7awRy1yslYBsicmHKh/qFW5w79+AiGiNcbewg0nBy7AS4G3+aK/Rm+eGPOKlO3tLuVphMxFXeKeOQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.14/vue.global.prod.min.js" integrity="sha512-huEQFMCpBzGkSDSPVAeQFMfvWuQJWs09DslYxQ1xHeaCGQlBiky9KKZuXX7zfb0ytmgvfpTIKKAmlCZT94TAlQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
const { createApp } = Vue;
const vm = createApp({
data() {
return {
map: {}, // 地图实例
isDisabled: false, // 按钮状态 框选中
dragBox: {}, // 拖拽框选实例
lonlat1: [], // 左上角经纬度
lonlat2: [], // 右下角经纬度
clientX: '-100%', // 提示信息left值
clientY: '-100%' // 提示信息top值
}
},
methods: {
// 初始化地图
initMap() {
// 高德地图瓦片地址
const amapVectorLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
})
});
this.map = new ol.Map({
target: 'map',
layers: [amapVectorLayer],
view: new ol.View({
center: [12129346.151443446, 4217286.994156892],
zoom: 5
})
});
// 绑定地图点击事件,打印经纬度
this.map.on('click', (e) => {
const point = ol.proj.transform(e.coordinate, 'EPSG:3857', 'EPSG:4326')
console.log(`经度:${point[0].toFixed(6)} 纬度:${point[1].toFixed(6)}`);
});
},
// 鼠标点击按钮,创建拖拽实例,绑定拖拽结束事件
handleClickSelectMap () {
this.isDisabled = true;
this.dragBox = new ol.interaction.DragBox({
className: 'dragBox', // 可自定义css样式
// condition: ol.events.condition.platformModifierKeyOnly, // ctrl
condition: ol.events.condition.noModifierKeys,
});
this.map.addInteraction(this.dragBox);
this.dragBox.on('boxend', (e) => {
this.isDisabled = false;
const boxExtent = this.dragBox.getGeometry().getExtent();
this.lonlat1 = ol.proj.transform([boxExtent[0], boxExtent[1]], 'EPSG:3857', 'EPSG:4326').map(e => e.toFixed(6));
this.lonlat2 = ol.proj.transform([boxExtent[2], boxExtent[3]], 'EPSG:3857', 'EPSG:4326').map(e => e.toFixed(6));
this.map.removeInteraction(this.dragBox); // 框选解释,删除拖拽款选交互
this.handleMouseLeaveOnMap(); // 框选结束,隐藏提示信息
});
dragBox.on('boxstart', function () {
console.log('===========开始框选===========');
});
},
// 地图绑定鼠标移动事件,提示拖拽框选方法
handleMouseMoveOnMap (e) {
if (!this.isDisabled) {
return;
}
this.clientX = e.clientX + 'px';
this.clientY = e.clientY + 'px';
},
// 鼠标离开地图,隐藏提示信息
handleMouseLeaveOnMap () {
this.clientX = '-100%';
this.clientY = '-100%';
}
},
mounted() {
this.initMap();
}
}).mount('#app');
</script>
</body>
</html>