openlayer中批量点位高亮,批量高亮时解除聚合状态,如何实现?数据量大时,如何解决性能卡顿问题?

需求:外部指定多个点位进行跳转到地图并高亮。

1.如果存在聚合状态下的点位,需要放大地图到所有高亮点均不处于聚合(或者放大到最大层级依然聚合,这种情况不去考虑,无实际物理意义)

2.大量高亮点要求不卡顿。

3.其他业务逻辑不再展开说明。

先不比比,先上图

分析:

1.只分析这批设备的点位是当前地图的情况下,并且解决卡顿的思路,其他业务逻辑不必赘述。

2. 思路:用当前层级判断点位是否聚合,如果聚合,则放大地图层级,再次验证,直至不聚合或者到达最大缩放层级。

(能实现但是解决不了点位多时卡顿问题,而且这种方式比较挫,属于赶时间强行实现,不需要的同学可以去后面看方案优化!!!!!)

Day1实现:

/*
     * @param devList Array 设备数组 包含deviceId
     * @param mapHierarchy Number|String 地图层级
     * */
    async locateToCamera(devList, mapHierarchy){
       // 定义检查某个设备是否为聚合状态
       const isDeviceNoClustered = item => {
           const cluster = this.findClusterByDeviceId(item.deviceId)
           // 非聚合状态时 features 长度为 1
           return !cluster || cluster.get('features').length === 1 
       }
       // 等待渲染稳定的函数
       const waitForStability = () => new Promise(resolve => setTimeout(resolve, 500))
    
       // 更新视图并等待渲染完成的函数
       const updateViewAndWaitForRender = (zoomLevel, devList) => {
        return new Promise(resolve => {
          // 随便用一个点位更新视图,放大过程中用任意点即可
          const firstDeviceId = devList[0].deviceId
          const marker = this.cadMap.getMarkerById(firstDeviceId)
          if (marker) {
            const { pointX, pointY } = marker.get('payload')
            this.cadMap.viewAnimate({
              center: [pointX, pointY],
              zoom: zoomLevel
            })

            // 使用 requestAnimationFrame 确保渲染完成
            const checkRenderComplete = () => {
              requestAnimationFrame(() => {
                this.cadMap.map.once('rendercomplete', resolve)
              })
            }

            checkRenderComplete()
          }
        })
      }
      let allDevicesNoClustered = false
      let zoom
      let currentZoom = this.cadMap.getView().getZoom()
      let defaultZoom = parseFloat(mapHierarchy)
      if (devList.length > 0) {
        // 确保默认层级小于当前层级时,直接跳转到当前层级
        zoom = currentZoom >= defaultZoom ? currentZoom : defaultZoom
        // 循环增加层级,直到所有点位都非聚合
        while (!allDevicesNoClustered && zoom < 16) {
          // 更新视图并等待渲染完成
          await updateViewAndWaitForRender(zoom, devList)
          // 等待视图稳定
          await waitForStability()
          // 检查是否所有点位已经非聚合
          allDevicesNoClustered = devList.every(isDeviceNoClustered)
          if (!allDevicesNoClustered) {
            zoom += 1 // 放大层级
          }
        }
        // 查找第一个达到非聚合状态的点位
        const nonClusteredDeviceId = devList.find(isDeviceNoClustered)

        // 最终定位到该点位
        if (nonClusteredDeviceId) {
          const marker = this.cadMap.getMarkerById(nonClusteredDeviceId)
          if (marker) {
            const { pointX, pointY } = marker.get('payload')
            this.cadMap.viewAnimate({
              center: [pointX, pointY],
              zoom
            })
          }
        }
      }
     }

其中cadMap是 CadMap类生成的对象,部分方法如下:

export default class CadMap extends Emitter {
  constructor(url, options = {}) {
    super()
    if (!url) {
      throw new Error('ImageMap need url option')
    }
    this._mapServiceUrl = url
    this._options = {
      cluster: true,
      ...options
    }
    this.map = null
    this.layer = null
    this.source = null
    this.marker = null
    this.popup = null
    this.overviewMapControl = null
    this.featureDraggable = false
    this.clickTimeout = null
  }

    ...
    // 通过设备id获取到指定设备对应的要素feature
    getMarkerById(id) {
        const source = this.getSource()
        return source.getFeatureById(id)
    }
    // 获取数据源
    getSource() {
        // isCluster 代表是否开启聚合
        const { cluster: isCluster } = this.options_
        if (isCluster && this.clusterLayer.getVisible()) {
          return this.clusterLayer.getSource().getSource()
        } else {
          return this.vectorLayer.getSource()
        }
    }
    // 动画
    viewAnimate(AnimationOptions) {
        this.map.getView().animate({
          ...AnimationOptions,
          duration: 500
        })
    }
    // 设置点位高亮状态
    setActive(value, changeImmediate = true) {
        this.active_ = value
        changeImmediate && this.changed()
    }
    ...
}

当数据量正常时正常展示,当数据量大时这个具体的卡顿程度就不展示了,大概需要几分钟甚至更久。

对需求时只是说上限50个点位进行跳转,本机测试大概3-4s,还能接受,但是现网上面100甚至更多点位,这个时长就非常感人了

就着手分析一下卡顿原因

通过定位可知是 监听设置需要高亮的点位会设置每个要素的active,然后地图上每一个点位变更都会引起重绘,也就是openlayer内部的changed方法,所以我们可以试着最后一次性再设置acitve

activeDeviceList: {
      handler(newVal, oldVal) {
        const toggleHighlight = (info, isActive) => {
          info.forEach((item, index) => {
            const feature = this.cadMap.getMarkerById(item.deviceId)
            feature && feature.setActive(isActive, index === info.length - 1)
          })
        }
        if (!this.cadMap?.map) return
        // 如果旧值为空,则只需要高亮新值
        if (!oldVal.length) {
          if (newVal.length) toggleHighlight(newVal, true)
        }
        // 如果有旧值,则取消旧值的高亮,再高亮新值
        else {
          toggleHighlight(oldVal, false)
          if (newVal.length) toggleHighlight(newVal, true)
        }
      },
      deep: true,
      immediate: true
    }

解决了卡顿问题,然后还有高亮点在聚合内部该如何展现呢?

当前的聚合代码如下:

... 
import { Cluster as ClusterSource, Vector as VectorSource } from 'ol/source'
...
if (isCluster) {
      const clusterSource = new ClusterSource({
        distance: 40,
        source: new VectorSource()
      })
      this.clusterLayer = new VectorLayer({
        source: clusterSource,
        zIndex: 100,
        style: clusterStyle
      })
      this.vectorLayer.setVisible(true)
      this.map.addLayer(this.clusterLayer)
    }

试着从样式回调

function clusterStyle(feature) {
  const features = feature.get('features')
  const size = features.length
  if (size > 1) {
    const activeFeatures = features.filter(f => f.getActive())
    return new Style({
      image: new Icon({
        crossOrigin: 'anonymous',
        src: require('@/assets/img/cadMap/cluster_bg.png'),
        size: [40, 40]
      }),
      text: new Text({
        text: (size - activeFeatures.length).toString(),
        fill: new Fill({
          color: '#ffffff'
        }),
        font: '16px Inter-Regular'
      })
    })
  } else {
    const originalFeature = feature.get('features')[0]
    const active = originalFeature.getActive()
    return createStyle(originalFeature.get('payload'), {
      alarm: false,
      active
    })
  }
}

在这边可以按照聚合点内部的高亮点位的数量,去计算剩余聚合的数量,但是发现好像不生效,聚合点仍然包含高亮点,只是数量对上了,所以就想办法改造一下这个方法,结果发现该方法只支持返回一种类型,要么正常点位样式,要么聚合点位样式,不支持组合点位,所以想办法从openlayer的Cluster 入手

import { Cluster } from 'ol/source'
import { createEmpty, createOrUpdateFromCoordinate, buffer } from 'ol/extent'
import { getUid } from 'ol'

export default class CustomCluster extends Cluster {
  /**
   * @param {Options} options Cluster options, with an additional canCluster function.
   * @param {function(Feature): boolean} options.canCluster A function that determines whether
   * the feature should be clustered. It should return true to allow clustering and false to prevent it.
   */
  constructor(options) {
    super(options)

    /**
     * @type {function(Feature): boolean}
     * @private
     */
    this.canCluster =
      options.canCluster ||
      function () {
        return true // Default is to allow clustering.
      }
  }

  /**
   * @protected
   */
  cluster() {
    if (this.resolution === undefined || !this.source) {
      return
    }
    const extent = createEmpty()
    const mapDistance = this.distance * this.resolution
    const features = this.source.getFeatures()

    /** @type {Object<string, true>} */
    const clustered = {}

    for (let i = 0, ii = features.length; i < ii; i++) {
      const feature = features[i]
      if (!(getUid(feature) in clustered)) {
        const geometry = this.geometryFunction(feature)
        if (geometry) {
          const coordinates = geometry.getCoordinates()
          createOrUpdateFromCoordinate(coordinates, extent)
          buffer(extent, mapDistance, extent)
          if (this.canCluster(feature)) {
            const neighbors = this.source.getFeaturesInExtent(extent).filter(neighbor => {
              if (!this.canCluster(neighbor)) {
                return false
              }
              const uid = getUid(neighbor)
              if (uid in clustered) {
                return false
              }
              clustered[uid] = true
              return true
            })
            this.features.push(this.createCluster(neighbors, extent))
          } else {
            const uid = getUid(feature)
            clustered[uid] = true
            this.features.push(this.createCluster([feature], extent))
          }
        }
      }
    }
  }
}

继承一下原来的Cluster类,扩展 canCluster 方法,代表是否聚合,此时外部生成聚合图层时加上刚刚判断的高亮状态即可

const clusterSource = new CustomCluster({
        distance: 40,
        source: new VectorSource(),
        canCluster: feature => {
          return !feature.getActive()
        }
      })

此时当我处于高亮状态时,无论缩放地图,高亮点不会进入聚合点。

到此功能已经完全实现。

请问在openlayers中如何删除Layer中的marker 3 在层中加了一个标记怎么永久的存到地图上 3 OpenLayers 如何设死地图边界 3 谁写过openlayers解析xml 3 openlayer.popup 3 OpenLayers 怎么做字段标注 4 字段标注不是用OL做的,而是你在SLD中定义用数据表的哪个字段做标签就可以了 4 OpenLayers 中画最短轨迹 4 使用TileCache配合OpenLayers 4 openlayers如何准确测距 9 openlayers简单实用例子 9 关于TileCache函数 13 openlayers 有函数说明的类图 14 go 14 Maker 一多,客户端就死掉 15 OpenLayers 怎么实现 AJAX 16 openlayer 要怎么研究才会应用得比较自如 16 改良OpenLayers的Popup 16 在openlayers的地图中要添加地图搜索功能一般采用什么方法 17 openlayers中如何访问geoserver发布的图片 18 什么是TMS? 18 怎样设置瓦片的18 画一条线,数据量,怎么办? 18 怎么在图层上动态画 20 GeoExt集成google map ,地图偏移 21JavaScriptOpenLayers.Marker的加上单击显示详细信息的功能 21 改良OpenLayers的MousePostion 25 使用OpenLayers实现一个在鼠标击处添加标记的效果 27 openlayers 利用google maps的卫星地图 29 openLayers集成google map ,标注错位 30 openlayers可以做出谷歌效果的地图吗? 31OpenLayers API和类来画、线、面 32 OGC标准术语介绍WMS、WFS、WCS 35 如何控制地图的放缩 37 openLayers链接WMS的代码探讨 37 openlayers 2.5 矢量层在ie下闪烁的问题 38 openLayers 在地图上添加一个并保存 39 openLayers 各个参数的意义 42 geoserver能搞出这种风格的图来吗? 43 关于SLD的线切割后的设置 43 GEOSERVE 标注铁路,使用 SLD 44 geoserver 发布.shp 中文乱码问题 怎么解决啊 45 Geoserver怎么连postGreSql 数据库 48 Geoserver连Oracle Spatial 52 GeoServer架构浅谈 53 Geoserver发布地图无法显示问题 57 WebGIS相关的OpenGIS规范 58 geoserver中地图以外的区域以自定义图片填充 62 怎样修改 geoServer 的用户名密码 65 GeoServer中的WMS服务生成的png图片背景色为透明 65 比例尺 65 需不需要一个layer对应一个store 66 如何部署shp 66 用GeoWebCache后Marker错位 66 标签太导致不能显示? 67 geoserver把两个shapefile格式的图叠 67 GeoServer 能够集成 Google Map? 68 gwc地图切片的缓存路径在web.xml中设置么 68 如何实现地图查询? 68 sld文件怎么用 69 在sld中怎么控制路名的间隔? 69
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值