2

そこで、d3.js、polymaps、Coffeescriptを使用するプロジェクトでマップマーカーのクラスタリングを実行しようとしています。

基になるデータに基づいてクラスターを計算し、クラスター化された配列を.data(clusters)としてd3に渡します。

クラスターの配置は問題ないようです。初期ズームレベルでのクラスタリングは問題ないようで、私の知識に基づくと100%正確です。ズームレベルを変更すると、一見すべて問題ないように見えますが、円にカーソルを合わせると、説明が現在の場所と一致していないように見え、以前のルートがどこにあったかが一致していないようです。今です。

完全なコードを含むこの例をhttp://bl.ocks.org/3161013で準備しました。

これには、クラスタリングとSVGの更新という2つの主要な障害点があります。

クラスタリングのコードはかなり単純で、 Mark Tuupolaのコードに基づいていますが、phpではなくcoffeescriptを使用しています。

  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にはかなり慣れていません。

4

1 に答える 1

1

既存のマーカー内の 'circle' 要素は、データが変更されても更新されません (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()
于 2012-07-24T05:33:29.713 に答える