【问题标题】:Clustering of SVG Elements in d3.js yields unexpected resultsd3.js 中 SVG 元素的聚类产生了意想不到的结果
【发布时间】:2012-07-24 13:33:29
【问题描述】:

所以我正在尝试为使用 d3.js、polymaps 和 Coffeescript 的项目做一些地图标记聚类。

我根据底层数据计算集群,然后将集群数组作为 .data(clusters) 传递给 d3

集群的位置似乎没问题。初始缩放级别上的聚类似乎没问题,并且根据我的知识是 100% 准确的。当我更改缩放级别时,乍一看一切似乎都很好,但是当我将鼠标悬停在圆圈上时,描述似乎与它们所在的位置不匹配,而之前的路线所在的位置似乎与它们的位置不匹配现在。

我在http://bl.ocks.org/3161013 准备了一个包含完整代码的示例。

我认为这有两个主要的失败点:集群和更新 SVG。

集群的代码相当简单,基于Mark Tuupola 的代码,但是用的是coffeescript而不是php。

  cluster: (elements, distance) ->
    currentElements = elements.slice(0)
    pixelDistance = @pixelDistance()
    distLat = distance * pixelDistance.lat
    distLon = distance * pixelDistance.lon 

    clustered = []
    while currentElements.length > 0
      stop = currentElements.shift()

      cluster = []
      cluster.push stop

      i = 0
      while i < currentElements.length
        if Math.abs(currentElements[i].lat - stop.lat) < distLat and Math.abs(currentElements[i].lon - stop.lon) < distLon
          aStop = currentElements.splice i,1
          cluster.push aStop[0]
          i--
        i++
      clustered.push cluster  
    clustered   

用于 SVG 更新的代码似乎是相当直接的 d3 代码。每当移动地图时,都会调用此更新方法。如果缩放发生了变化,或者预聚类的数据发生了变化,我们重新聚类和布局,否则我们只是平移现有的点。

  update: ->
    if not @stops
      @stops = []

    if not @prevNumStops
      @prevNumStops = 0

    if not @prevZoom
      @prevZoom = 0


    if @zoomLevel() != @prevZoom or @prevNumStops != @stops.length
      @prevZoom = @zoomLevel()
      @prevNumStops = @stops.length 

      start = new Date()
      @clusters = @cluster(@stops,10)
      console.log @clusters
      console.log "clustering: " + ((new Date()) - start)

      start = new Date()
      marker = @selector.selectAll("g").data(@clusters)
      marker.enter().append("g")
      .append("circle")
      .attr("class", "stop no-tip")
      marker.exit().remove()
      @selector.selectAll("g").selectAll("circle")
      .attr('r', (cluster) -> if cluster.length > 1 then 5 else 3.5)
      .attr("text", (cluster) -> "<ul>" + ((("<li>" + route + "</li>") for route in stop.routes).join("") for stop in cluster).join("") + "</ul>")

    @selector.selectAll("g").attr("transform", (cluster) => 
      @transform cluster[0]
    )

我觉得这里可能缺少一些简单的东西,但我对 d3 还是很陌生。

【问题讨论】:

    标签: svg coffeescript d3.js


    【解决方案1】:

    当数据更改时,现有标记内的“圆圈”元素不会更新(d3 默认使用索引来确定新元素是添加(输入)、删除(退出)还是更新(默认))。这会导致文本成为该缩放级别上已存在的所有元素的先前缩放级别的旧文本。

    它应该与以下代码一起使用:

    marker = @selector.selectAll("g").data(@clusters)
    
    # update existing 'g' elements
    marker.select('circle')
    .attr('r', your_cluster_size_function)
    .attr("text", your_text_function)
    
    
    # add new 'g' elements
    marker.enter().append("g")
    .append("circle")
    .attr("class", "stop no-tip")
    .attr('r', your_cluster_size_function)
    .attr("text", your_text_function)
    
    # remove 'g' elements if there are too many
    marker.exit().remove()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多