MapBox - 群集缩放

时间:2018-01-12 18:32:36

标签: javascript mapbox mapbox-gl

是否有可能重新创建点击以放大像Craigslist Mapview一样的MapBox集群?单击时,它会缩放到包含这些位置点的区域。

这是我的群集代码:

map.on('load', function() {
map.addSource("location", {
    type: "geojson",
    data: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/73873/test.geojson",
    cluster: true,
    clusterMaxZoom: 14,
    clusterRadius: 100
});

    map.addLayer({
    id: "clusters",
    type: "circle",
    source: "location",
    filter: ["has", "point_count"],
    paint: {
        "circle-color": {
            property: "point_count",
            type: "interval",
            stops: [
                [0, "#71AAC6"],
                [100, "#71AAC6"],
                [750, "#71AAC6"],
            ]
        },
        "circle-radius": {
            property: "point_count",
            type: "interval",
            stops: [
                [0, 20],
                [100, 30],
                [750, 40]
            ]
        }
    }
});

这是我的演示

Codepen Demo

3 个答案:

答案 0 :(得分:1)

我认为你不能在纯Mapbox中做到这一点,至少不容易。

但对于那些特定于群集的用法,有Supercluster(一个官方的Mapbox地理空间点聚类库),它正是您想要的:

  

getClusterExpansionZoom(clusterId)

     

给定群集的cluster_id,返回群集扩展为多个子项的缩放(对“单击缩放”功能很有用)。

编辑:实际上你可以在没有超级集群的情况下完成:

  1. 使用此JsFiddle example,您可以检索所点击的群集的点数。

    map.on('click', function(e) {
        const cluster = map.queryRenderedFeatures(e.point, { layers: ["cluster"] });
    
        if (cluster[0]) {
        // features: from the added source that are clustered
        const pointsInCluster = features.filter(f => {
            const pointPixels = map.project(f.geometry.coordinates)
          const pixelDistance = Math.sqrt(
            Math.pow(e.point.x - pointPixels.x, 2) + 
            Math.pow(e.point.y - pointPixels.y, 2) 
          );
          return Math.abs(pixelDistance) <= clusterRadius;
        });
        console.log(cluster, pointsInCluster);
      }
    });
    
  2. 然后,您可以使用所有这些点创建mapboxgl.LngLatBoundsextend

  3. 您将获得包含所有积分的LngLatBounds,这样您就可以在其上拨打fitBounds,这样就可以了。

答案 1 :(得分:1)

正如@MeltedPenguin所说。您无需使用SuperCluster就可以做到。我搜索了几个答案,最后使用coffeescript做了自己的解决方案(您可以使用http://js2.coffee/之类的工具将其转换回JS):

    @clusterRadius = 30
    @map.on 'click', (e) =>
          features = @map.queryRenderedFeatures(e.point, { layers: ['markers_layer'] });
          if features && features.length > 0
            if features[0].properties.cluster
              cluster = features[0].properties

              allMarkers = @map.queryRenderedFeatures(layers:['markers_layer_dot']);
              self = @ #just a way to use 'this' un a function, its more verbose then =>    

              #Get all Points of a Specific Cluster
              pointsInCluster = allMarkers.filter((mk) ->
                pointPixels = self.map.project(mk.geometry.coordinates) #get the point pixel
                #Get the distance between the Click Point and the Point we are evaluating from the Matrix of All point
                pixelDistance = Math.sqrt((e.point.x - (pointPixels.x)) ** 2 + (e.point.y - (pointPixels.y)) ** 2)

                #If the distant is greater then the disance that define a cluster,  then the point si in the cluster
                # add it to the boundaries
                Math.abs(pixelDistance) <= self.clusterRadius
              )

              #build the bounding box with the selected points coordinates
              bounds = new (mapboxgl.LngLatBounds)
              pointsInCluster.forEach (feature) ->
                bounds.extend feature.geometry.coordinates
                return

              #Move the map to fit the Bounding Box (BBox)
              @map.fitBounds bounds, {padding:45, maxZoom: 16}

            else
              window.open('/en/ad/' + features[0].properties.propertyId)

在我的页面上,我有两个基于相同数据源但具有不同属性的图层。一个定义所有点(无簇),另一个定义点和簇。 对于我的显示,我使用带有簇的“ markers_layer”,为了计算距离和东西,我使用另一个作为点的DB。

来源:

  @map.addSource "markers_source_wo_cluster",
    type: "geojson"
    data:
      type: "FeatureCollection"
      features: []
    cluster: false
    clusterMaxZoom: 10
    clusterRadius: @clusterRadius

  @map.addSource "markers_source",
    type: "geojson"
    data:
      type: "FeatureCollection"
      features: []
    cluster: true
    clusterMaxZoom: 60
    clusterRadius: @clusterRadius

图层:

##============================================================##
## Add marker layer (Layer for QueryRender all dot without cluster)
##============================================================##
@map.addLayer
  id: 'markers_layer_dot'
  source: 'markers_source_wo_cluster'
  type: "circle"
  paint:
    "circle-radius": 0 #This are 1 pixel dot for ref only

##============================================================##
## Add marker layer
##============================================================##
@map.addLayer
  id: 'markers_layer'
  source: 'markers_source'
  type: 'symbol'
  layout:
    'icon-allow-overlap': true
    'icon-image':'pin_map'
    'icon-size':
      stops: [[0,0.4], [40,0.4]]

答案 2 :(得分:0)

我这样解决https://github.com/mapbox/mapbox-gl-js/issues/9707

const handleClusterOnClick = React.useCallback(
    (event: LayerMouseEvent) => {
      if (event && rawMap) {
        const feature = event?.features?.[0];
        const source = rawMap.getSource(SOURCE_ID);

        if (
          feature &&
          feature.properties !== null &&
          // @ts-ignore
          typeof source.getClusterExpansionZoom === "function"
        ) {
          rawMap
            .getSource(SOURCE_ID)
            // @ts-ignore
            .getClusterExpansionZoom(
              feature.properties.cluster_id,
              (err: Error, expansionZoom: number) => {
                if (!err) {
                  rawMap.flyTo({
                    // @ts-ignore
                    center: feature.geometry.coordinates,
                    zoom: expansionZoom,
                    animate: true,
                    essential: true,
                  });
                }
              },
            );
        } else {
          rawMap.flyTo({
            animate: true,
            essential: true,
            center: event.lngLat,
            zoom: rawMap.getZoom() + 3, // This is bad UX, requires multiple clicks
          });
        }
      }
    },
    [rawMap],
  );