meface/docs/article/gis/openlayers/ol_cluster.md

4.8 KiB
Raw Blame History

title date author tags categories
Openlayer聚合 2020-12-25 ac
Openlayers
cluster
GIS

首先需要意识到一件事情,地图上渲染描述的点是带有主观因数的,是一个观点问题,它取决于比例尺或分辨率。例如,对于一栋房子,在小比例尺(大范围)下可以使用点要素来进行描述,这样可以清晰的描述房子在大范围的区域内的位置信息,而当在大比例尺(小范围)下为了能更加精确的描述房子的形状,使用面要素来渲染。

聚合

当面对海量数据,在地图缩放到小比例尺时,矢量数据会比较密集,所以渲染的样式可能会重叠,影响信息的查看。同时由于数据量较大,渲染的效率和性能也会受到影响。解决方法是根据地图当前的分辨率resolution,选择一个固定的像素尺度distance作为buffer缓冲距离(distance*resolution),将要素进行分类统计(聚合),渲染统计后的要素。

ol中为我们提供了ol.source.Cluster来实现【点要素】的聚合功能。常用的构造参数options:

  • distance:聚合的像素距离,单位:px默认20

  • source:需要聚合的矢量数据源

  • geometryFunction:几何函数,可以用于过滤或处理几何对象返回Point对象(中心点)

    //`geometryFunction`默认值只处理点要素
    function(feature) {
        const geometry = feature.getGeometry();
        assert(geometry.getType() == GeometryType.POINT,10); 
        return geometry;
    };
    

通过查看Cluster.js可以知道,其内部的cluster()createCluster()方法实现了上述的解决方法。

 cluster() {
    if (this.resolution === undefined) {
      return;
    }
    this.features.length = 0;
    const extent = createEmpty();
    //获取地图单位下的实际距离
    const mapDistance = this.distance * this.resolution;
    const features = this.source.getFeatures();

    /**
     * @type {!Object<string, boolean>}
     * 缓存已经参与聚合的点
     */
    const clustered = {};

    for (let i = 0, ii = features.length; i < ii; i++) {
      const feature = features[i];
      if (!(getUid(feature) in clustered)) {
        //构造参数或默认的geometryFunction
        const geometry = this.geometryFunction(feature);
        if (geometry) {
          const coordinates = geometry.getCoordinates();
          createOrUpdateFromCoordinate(coordinates, extent);
          buffer(extent, mapDistance, extent);

          let neighbors = this.source.getFeaturesInExtent(extent);
          neighbors = neighbors.filter(function(neighbor) {
            const uid = getUid(neighbor);
            if (!(uid in clustered)) {
              clustered[uid] = true;
              return true;
            } else {
              return false;
            }
          });
          this.features.push(this.createCluster(neighbors));
        }
      }
    }
  }

  /**
   * @param {Array<Feature>} features Features
   * @return {Feature} The cluster feature.
   * @protected
   */
  createCluster(features) {
    const centroid = [0, 0];
    for (let i = features.length - 1; i >= 0; --i) {
      const geometry = this.geometryFunction(features[i]);
      if (geometry) {
        addCoordinate(centroid, geometry.getCoordinates());
      } else {
        features.splice(i, 1);
      }
    }
    //计算要素集features的中点centroid
    scaleCoordinate(centroid, 1 / features.length);

    const cluster = new Feature(new Point(centroid));
    cluster.set('features', features);
    return cluster;
  }

ol.source.Cluster只是作为ol.layer.Vector的数据源,帮我们计算好地图每个层级的聚合要素集。其中每个聚合后的要素都包含一个表示像素尺度范围内被聚合的要素集合的features属性和一个代表被聚合要素集合的中心点的geometry属性。可以根据features属性的大小,设置不同样式,如官网地震点聚合的示例:

function styleFunction(feature, resolution) {
  if (resolution != currentResolution) {
    calculateClusterInfo(resolution);
    currentResolution = resolution;
  }
  var style;
  var size = feature.get('features').length;
  if (size > 1) {
    style = new Style({
      image: new CircleStyle({
        radius: feature.get('radius'),
        fill: new Fill({
          color: [255, 153, 0, Math.min(0.8, 0.4 + size / maxFeatureCount)],
        }),
      }),
      text: new Text({
        text: size.toString(),
        fill: textFill,
        stroke: textStroke,
      }),
    });
  } else {
    var originalFeature = feature.get('features')[0];
    style = createEarthquakeStyle(originalFeature);
  }
  return style;
}