前言:
公司项目中使用 需要实现获取附近充电桩信息,进行展示,并且在能对地图上进行标点,点击描点可以进入到对应详情;
选择 Openlayers 技术栈
OpenLayers是一个开源的项目,其设计之意是为互联网客户端提供强大的地图展示功能,包括地图数据显示与相关操作,并具有灵活的扩展机制。
这里记录一下自己在项目中使用的技术点,解决问题。
初始地图
//初始地图前需要引入模块
import { Map, View, Feature } from 'ol'
/ 地图对象
let map: any = null
// 充电站数据源
let clusterSource = new Vector()
//我的位置标记点
let myClusterSource = new Vector()
// 初始化地图
function initMap() {
// 基础图层
let baseLayer = new TileLayer({
source: new XYZ({
//iOS需要使用https
url: 'https://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=2&style=7&x={x}&y={y}&z={z}',
crossOrigin: 'anonymous',
projection: 'EPSG:3857'
})
})
// 充电站图层
let clusterLayer = new VectorLayer({
source: new Cluster({
distance: 40,
source: clusterSource
}),
style: function (feature, resolution) {
let features = feature.get('features')
// 空闲充电桩数目
let freeGunCount = 0
features.forEach((item: any) => {
let data = item.get('data')
if (data.freeGunCount > 0) {
freeGunCount += data.freeGunCount
}
})
return new Style({
image: new Icon({
src: freeGunCount > 0 ? icFree : icFreeNone,
width: 80,
height: 50
}),
text: new Text({
text: freeGunCount.toString(),
fill: new Fill({
color: freeGunCount > 0 ? '#018BD9' : '#969696'
}),
font: "16px 'PingFang SC'",
offsetY: -5,
offsetX: 10
})
})
}
})
// 我的位置图层
let myClusterLayer = new VectorLayer({
source: new Cluster({
distance: 40,
source: myClusterSource
}),
style: new Style({
image: new Icon({
src: icMyLocMark,
width: 42,
height: 50
})
})
})
// 初始化地图
map = new Map({
target: 'map',
controls: defaultControls({
rotate: false,
attribution: false,
zoom: false
}),
layers: [baseLayer, clusterLayer, myClusterLayer],
view: new View({
// center: [113.347185, 23.144157], // 地图中心点/华师
center: [113.92768, 22.551535], // 地图视图
minZoom: 10, // 地图缩放最小级别
zoom: 16, // 地图缩放级别(打开页面时默认级别)
projection: 'EPSG:4326' // 地图投影
})
})
// 点击获取坐标
map.on('click', function (e: any) {
// 打印坐标
console.log('点击坐标点['+ e.coordinate[0] + ',' + e.coordinate[1] +']')
// 点击聚合点展开
clusterLayer.getFeatures(e.pixel).then((features) => {
if (features.length > 0) {
const clusterMembers = features[0].get('features')
// 点击单个充电站
if (clusterMembers.length === 1) {
let data = features[0].get('features')[0].get('data')
clickByMap(data)
} else if (clusterMembers.length > 1) {
// 点击聚合点展开充电站
// 计算集群内部的范围,以便将视图缩放到该范围。
const extent = createEmpty()
clusterMembers.forEach((feature: any) =>
extend(extent, feature.getGeometry().getExtent())
)
const view = map.getView()
const resolution = map.getView().getResolution()
if (
view.getZoom() !== view.getMaxZoom() &&
getWidth(extent) > resolution &&
getHeight(extent) > resolution
) {
view.fit(extent, { duration: 500, padding: [50, 50, 200, 50] })
}
let data = features[0].get('features')[0].get('data')
clickByMap(data)
}
}
})
})
//监听地图拖拽事件结束,获取地图中心点坐标
map.on('moveend', (e: any) => {
let center = map.getView().calculateExtent(map.getSize())
const params = {
ymin: center[1],
ymax: center[3],
xmin: center[0],
xmax: center[2],
}
if(map.getView().getZoom() >= 14.5){
antiShake(getMoveMapStation(params), 800)
}
})
}
问题分析及其处理
监听地图缩放进行 数据加载,
问题1、需要地图缩放的时候去加载对应范围内的数据;
解决方法:
监听鼠标移动事件
获取对应地图可视范围坐标
使用方法:
map.getView().calculateExtent(map.getSize())
对应代码:
//监听地图拖拽事件结束,获取地图中心点坐标
map.on('moveend', (e: any) => {
let center = map.getView().calculateExtent(map.getSize())
console.log('minZoom>>>', map.getView().getZoom())
const params = {
ymin: center[1],
ymax: center[3],
xmin: center[0],
xmax: center[2],
}
})
问题2、地图缩放范围很大时加载出的数据比较多,渲染地图出现卡顿,
解决方法:
获取地图缩放比例
控制当比例值达到指定范围不在加载数据;
使用方法:
map.getView().getZoom()
该方法获取到的是初始地图是所设置的缩放级别的最大范围到最小范围内的值;
这里设置的为 10-16;
对应代码:
//监听地图拖拽事件结束,获取地图中心点坐标
map.on('moveend', (e: any) => {
let center = map.getView().calculateExtent(map.getSize())
console.log('minZoom>>>', map.getView().getZoom())
const params = {
ymin: center[1],
ymax: center[3],
xmin: center[0],
xmax: center[2],
}
//控制地图的缩放值来加载对应数据,当小于 14.5 时不在加载数据;
if(map.getView().getZoom() >= 14.5){
//这里调用加载数据的方法 这里需要做防抖处理,
// antiShake(getMoveMapStation(params), 800)
}
})
问题三、地图缩小过程中,对应位置数据比较多时需要聚合为一个图标;点击集合图标需要展开
主要代码:
// 点击聚合点展开
clusterLayer.getFeatures(e.pixel).then((features) => {
if (features.length > 0) {
const clusterMembers = features[0].get('features')
// 点击单个充电站
if (clusterMembers.length === 1) {
let data = features[0].get('features')[0].get('data')
clickByMap(data)
} else if (clusterMembers.length > 1) {
// 点击聚合点展开充电站
// 计算集群内部的范围,以便将视图缩放到该范围。
const extent = createEmpty()
clusterMembers.forEach((feature: any) =>
extend(extent, feature.getGeometry().getExtent())
)
const view = map.getView()
const resolution = map.getView().getResolution()
if (
view.getZoom() !== view.getMaxZoom() &&
getWidth(extent) > resolution &&
getHeight(extent) > resolution
) {
view.fit(extent, { duration: 500, padding: [50, 50, 200, 50] })
}
let data = features[0].get('features')[0].get('data')
clickByMap(data)
}
}
})
问题四、 异步加载数据后怎么更新坐标的到地图上
使用方法 Feature
const reqMapPosition = (res: any) => {
mapPosition.value = res.data
// 清空数据
clusterSource.clear()
// 添加充电站数据
mapPosition.value.forEach((item: any) => {
clusterSource.addFeature(
new Feature({
geometry: new Point([item.stationLng, item.stationLat]),
data: item
})
)
})
}
问题五、 地图图层上放大、缩小、我的位置 操作调用方法
//显示自己的定位点 传入自己的坐标数据
const showMyLoc = (lon: number, lat: number) => {
originData.isInitMyLoc = true
map.getView().animate({
center: [lon, lat], // 中心点
zoom: 17, // 缩放级别
rotation: undefined, // 缩放完成view视图旋转弧度
duration: 800 // 缩放持续时间,默认不需要设置
})
}
//增大缩放
const clickAddZoom = () => {
const view = map.getView()
const zoom = view.getZoom()
if (zoom < 19) {
//超过19就缩放显示不出来了
view.setZoom(zoom + 1)
}
}
//减小缩放
const clickSubZoom = () => {
const view = map.getView()
const zoom = view.getZoom()
view.setZoom(zoom - 1)
}