Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate SVG Path Centroid with D3.js

Tags:

I'm using the SVG located at http://upload.wikimedia.org/wikipedia/commons/3/32/Blank_US_Map.svg in a project and interacting with it with d3.js. I'd like to create a click to zoom effect like http://bl.ocks.org/2206590, however that example relies on path data stored in a JSON object to calculate the centroid. Is there any way to load path data in d3 from an existing SVG to get the centroid?

My (hackish) attempt so far:

  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)}     });   } 

This seems to work on some states, such as Missouri, but others like Washington fail because my SVG data parsing is so rudimentary. Does d3 support something like this natively?

like image 358
Zikes Avatar asked Aug 21 '12 20:08

Zikes


Video Answer


2 Answers

The D3 functions all seem to assume you're starting with GeoJSON. However, I don't actually think you need the centroid for this - what you really need is the bounding box, and fortunately this is available directly from the SVG DOM interface:

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]; } 

This is actually slightly better than the true centroid for the purpose of zooming, as it avoids some projection issues you might otherwise run into.

like image 134
nrabinowitz Avatar answered Oct 04 '22 01:10

nrabinowitz


The accepted answer was working great for me until I tested in Edge. I can't comment since I don't have enough karma or whatever but was using this solution and found an issue with Microsoft Edge, which does not use x or y, just top/left/bottom/right, etc.

So the above code should be:

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]; } 
like image 33
Neil Ruud Avatar answered Oct 04 '22 00:10

Neil Ruud