openlayers中网格聚类的实现

2023-06-10 11:20:26 浏览数 (3)

概述

聚类是根据一定的规则将数据进行分类统计,常见的聚类方式有:1、基于行政区划;2、基于空间距离;3、基于业务字段。本文实现了基于固定大小的网格的聚类。

效果

实现

1. 数据源

本文的数据源为cesium示例中的全球的机场数据。

2. 实现代码

  1. 网格大小可配置,默认为64;
  2. 根据聚类的数量进行了分级颜色渲染;
代码语言:javascript复制
class GridCluster {
  constructor(map, data, size = 64, showPoint = true) {
    this.map = map
    this.data = data.map(d => {
      d.coords = ol.proj.fromLonLat([d.lon, d.lat])
      return d
    })
    this.size = size
    this.val = 20037508.34
    this.showPoint = showPoint
    const source = new ol.source.Vector({
      features: []
    });
    const vector = new ol.layer.Vector({
      source: source,
      style: (feat) => {
        const count = feat.get('count')
        let color = `68, 149, 247`
        if(count > 20) {
          color = '165,0,179'
        } else if(count <= 20 && count > 15) {
          color = '255,10,10'
        } else if(count <= 15 &&count > 10) {
          color = '255,138,5'
        }
        else if(count <= 10 &&count > 5) {
          color = '247,176,76'
        }
        const colorPoint = '255,0,0'
        const stroke = new ol.style.Stroke({
          color: `rgba(${color}, 1)`,
          width: 1
        })
        const style = count && count > 0 ? {
          fill: new ol.style.Fill({
            color: `rgba(${color}, 0.35)`
          }),
          stroke: stroke,
          text: new ol.style.Text({
            text: count.toString(),
            font: 'bold 16px sans-serif',
            fill: new ol.style.Fill({
              color: '#000'
            }),
            stroke: new ol.style.Stroke({
              color: `rgba(255,255,255, 1)`,
              width: 1
            })
          })
        } : {
          image: new ol.style.Circle({
            fill: new ol.style.Fill({
              color: `rgba(${colorPoint}, 0.7)`
            }),
            stroke: new ol.style.Stroke({
              color: `rgba(${colorPoint}, 1)`,
              width: 1
            }),
            radius: 2
          }),
        }
        return new ol.style.Style(style)
      }
    });
    map.addLayer(vector)
    this.source = source
    const that = this
    this.map.on('movestart', () => {
      this.source.clear()
    })
    this.map.on('moveend', () => {
      that.addCluster()
    })
    that.addCluster()
  }

  addCluster() {
    const that = this
    const zoom = that.map.getView().getZoom()
    const res = that.map.getView().getResolutionForZoom(zoom) * that.size
    const [x1, y1, x2, y2] = that.map.getView().calculateExtent()
    const count = Math.ceil(this.val * 2 / res)
    let features = []
    const data = that.data.filter(({coords}) => {
      const [x, y] = coords
      return x >= x1 && x <= x2 && y >= y1 && y <= y2
    })
    for(let i = 0; i < count; i  ) {
      const xmin = i * res - that.val
      const xmax = (i 1) * res - that.val
      for(let j = 0; j < count; j  ) {
        const ymax = that.val - j * res
        const ymin = that.val - (j 1) * res
        const isInExtent = xmin >= x1 && xmin <= x2 && ymin >= y1 && ymin <= y2
          ||  xmax >= x1 && xmax <= x2 && ymin >= y1 && ymin <= y2
          ||  xmin >= x1 && xmin <= x2 && ymax >= y1 && ymax <= y2
          ||  xmax >= x1 && xmax <= x2 && ymax >= y1 && ymax <= y2
        if(isInExtent) {
          const dataFilter = [...data].filter(({coords}) => {
            const [x, y] = coords
            return x >= xmin && x <= xmax && y >= ymin && y <= ymax
          })
          const count = dataFilter.length
          if(count > 1) {
            features.push(new ol.Feature({
              geometry: ol.geom.Polygon.fromExtent([xmin, ymin, xmax, ymax]),
              count: count,
              data: dataFilter
            }))
          }
        }
      }
    }
    that.source.addFeatures(features)
    if(that.showPoint) {
      const features = data.map(d => {
        return new ol.Feature({
          geometry: new ol.geom.Point(d.coords),
          data: d
        })
      })
      that.source.addFeatures(features)
    }
  }
}

fetch('./flights.json').then(res => res.json()).then(res => {
  let {airports} = res
  airports = airports.map(([name, city, country, lon, lat]) => {
    return {name, city, country, lon, lat}
  })
  new GridCluster(map, airports)
})

0 人点赞