web-dev-qa-db-ja.com

D3.jsを使用してSVGパス重心を計算する

プロジェクトで http://upload.wikimedia.org/wikipedia/commons/3/32/Blank_US_Map.svg にあるSVGを使用しており、d3.jsとやり取りしています。 http://bl.ocks.org/220659 のようなクリックしてズームする効果を作成したいのですが、その例では、JSONオブジェクトに格納されているパスデータに基づいて重心を計算しています。セントロイドを取得するために既存のSVGからd3のパスデータをロードする方法はありますか?

これまでの私の(ハックっぽい)試み:

  function get_centroid(sel){
    var coords = d3.select(sel).attr('d');
    coords = coords.replace(/ *[LC] */g,'],[').replace(/ *M */g,'[[[').replace(/ *z */g,']]]').replace(/ /g,'],[');
    return d3.geo.path().centroid({
      "type":"Feature",
      "geometry":{"type":"Polygon","coordinates":JSON.parse(coords)}
    });
  }

これはミズーリ州などの一部の州では機能しているようですが、ワシントンのような他の州では、私のSVGデータの解析が非常に初歩的であるために失敗します。 d3はこのようなものをネイティブにサポートしていますか?

21
Zikes

D3関数はすべて、GeoJSONから始めていることを前提としているようです。ただし、実際にはこれに図心が必要だとは思いません。実際に必要なのはバウンディングボックスです。幸い、これはSVGDOMインターフェイスから直接利用できます。

function getBoundingBoxCenter (selection) {
  // get the DOM element from a D3 selection
  // you could also use "this" inside .each()
  var element = selection.node();
  // use the native SVG interface to get the bounding box
  var bbox = element.getBBox();
  // return the center of the bounding box
  return [bbox.x + bbox.width/2, bbox.y + bbox.height/2];
}

これは、ズームの目的では、実際には実際の重心よりもわずかに優れています。これは、他の方法で遭遇する可能性のあるいくつかの投影の問題を回避するためです。

57
nrabinowitz

私がEdgeでテストするまで、受け入れられた答えは私にとってうまく機能していました。十分なカルマなどがないためコメントできませんが、このソリューションを使用していて、xまたはyを使用せず、上/左/下/右などを使用するMicrosoftEdgeの問題を見つけました。

したがって、上記のコードは次のようになります。

function getBoundingBoxCenter (selection) {
  // get the DOM element from a D3 selection
  // you could also use "this" inside .each()
  var element = selection.node();
  // use the native SVG interface to get the bounding box
  var bbox = element.getBBox();
  // return the center of the bounding box
  return [bbox.left + bbox.width/2, bbox.top + bbox.height/2];
}
1
Neil Ruud

から ここ

解決策は、選択に.datum()メソッドを使用することです。

var element = d3.select("#element");
var centroid = path.centroid(element.datum());
0
Matthew