【问题标题】:MapBox - Cluster ZoomingMapBox - 集群缩放
【发布时间】:2018-01-12 18:32:35
【问题描述】:

是否可以像 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

【问题讨论】:

标签: javascript mapbox mapbox-gl


【解决方案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)

在我的页面上,我有 2 层基于相同的数据源但具有不同的属性。一种定义所有点(无簇),另一种定义点和簇。 对于我的显示,我将“markers_layer”与集群一起使用,而对于计算距离和其他内容,我使用另一个作为点数据库。

来源:

  @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】:

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

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

    getClusterExpansionZoom(clusterId)

    在给定集群的cluster_id 的情况下,返回集群展开为多个子节点的缩放比例(对“点击缩放”功能很有用)。

    编辑:实际上你可以在没有 Supercluster 的情况下做到这一点:

    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 就可以了。

    【讨论】:

      【解决方案3】:

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

      const handleClusterClick = React.useCallback(
          (event: LayerMouseEvent, sourceId: string) => {
            if (event && mapRef.current) {
              const properties = getFeaturePropertiesFromMapEvent(event);
              const coordinates = event.lngLat.toArray() as [number, number];
      
              // skip cluster logic, clicked no point
              if (properties?.type && coordinates) {
                handlePointClick(event);
                return;
              }
      
              const feature = event?.features?.[0];
              const source = mapRef.current.getSource(sourceId);
      
              if (
                feature &&
                feature.properties !== null &&
                // @ts-expect-error typings
                typeof source.getClusterExpansionZoom === "function"
              ) {
                source
                  // @ts-expect-error typings
                  .getClusterExpansionZoom(
                    feature.properties.cluster_id,
                    (err: Error, expansionZoom: number) => {
                      if (!err && mapRef.current) {
                        mapRef.current.flyTo({
                          // @ts-expect-error typings
                          center: feature.geometry.coordinates,
                          zoom: expansionZoom,
                          maxDuration: MAX_FLY_TO_DURATION_MS,
                        });
                      }
                    },
                  );
              } else {
                mapRef.current.flyTo({
                  maxDuration: MAX_FLY_TO_DURATION_MS,
                  center: event.lngLat,
                  zoom: mapRef.current.getZoom() + 3, // This is bad UX, requires multiple clicks
                });
              }
            }
          },
          [handlePointClick],
        );
      

      【讨论】:

        猜你喜欢
        • 2012-05-06
        • 1970-01-01
        • 2020-01-04
        • 1970-01-01
        • 1970-01-01
        • 2020-04-13
        • 2021-06-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多