Openlayers: move, scale control, load corresponding range data, asynchronously load data to update map coordinates

Foreword:

In the company's project, it is necessary to obtain the information of nearby charging piles, display them, and be able to punctuate the map. Click on the point to enter the corresponding details;

Choose Openlayers Technology Stack

OpenLayers is an open source project, which is designed to provide powerful map display functions for Internet clients, including map data display and related operations, and has a flexible expansion mechanism.

Here is a record of the technical points I used in the project to solve the problem.

Initial map

//初始地图前需要引入模块
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)
    }
  })
}

Problem analysis and treatment

Monitor the map zoom for data loading,

Question 1. When the map needs to be zoomed, load the data within the corresponding range;

Solution:
Listen to the mouse movement event
to obtain the coordinates of the corresponding map's visible range
Usage method:
map.getView().calculateExtent(map.getSize())
Corresponding code:

 //监听地图拖拽事件结束,获取地图中心点坐标
  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],
    }
  })

Problem 2. When the zoom range of the map is large, the loaded data is relatively large, and the rendered map appears stuck.

Solution:
Get the zoom ratio of the map
to control the data not to be loaded when the ratio value reaches the specified range;
use method:
map.getView().getZoom()
This method gets the initial map from the maximum range to the minimum range of the set zoom level The value within;
here is set to 10-16;
corresponding code:

 //监听地图拖拽事件结束,获取地图中心点坐标
  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)
    }
  })

Question 3. When the map is zoomed out, when there are a lot of corresponding location data, it needs to be aggregated into one icon; click the collection icon to expand

Main code:

// 点击聚合点展开
    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)
        }
      }
    })

Question 4. How to update the coordinates on the map after loading data asynchronously

How to useFeature

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
        })
      )
    })

}

Question 5. Zoom in, zoom out, and call methods for my location operations on the map layer

//显示自己的定位点  传入自己的坐标数据
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)
}

Need to know more about Openlayers

Guess you like

Origin blog.csdn.net/weixin_47659945/article/details/132584747