index.html
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 12px sans-serif;
}
svg {
margin: 0 auto;
display: inherit;
}
.states path {
stroke-width: 1px;
stroke: white;
fill: #DBDBDB;
cursor: pointer;
}
.arcs path {
stroke-width: 2px;
stroke: tomato;
pointer-events: none;
fill: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var arcdata = [
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-106.503961875, 33.051502817366334]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-97.27544625, 34.29490081496779]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-92.793024375, 34.837711658059135]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-100.3076728125, 41.85852354782116]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-104.6143134375, 43.18636214435451]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-106.152399375, 45.57291634897]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-105.5811103125, 42.3800618087319]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-74.610651328125, 42.160561343227656]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-78.148248984375, 40.20112201100485]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-81.795709921875, 39.89836713516883]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-91.738336875, 42.1320516230261]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-93.902643515625, 39.89836713516886]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-146.68645699218752, 62.84587613514389]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-151.03704292968752, 62.3197734579205]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-150.50969917968752, 68.0575087745829]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-155.58278180000002, 19.896766200000002]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-155.41249371406252, 19.355435189875685]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-156.22204876777346, 20.77817385333129]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-156.08334637519533, 20.781383752662176]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-119.41793240000001, 36.77826099999999]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-111.73848904062501, 34.311442605956636]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-118.62691677500001, 39.80409417718468]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-115.56173122812501, 44.531552843807575]
},
{
sourceLocation: [-99.5606025, 41.068178502813595],
targetLocation: [-107.13521755625001, 43.90164233696157]
}
]
var width = 600,
height = 349;
var projection = d3.geo.albersUsa()
.scale(730.1630554896399)
.translate([width/2,height/2])
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var states = svg.append("g")
.attr("class","states");
var arcs = svg.append("g")
.attr("class","arcs");
var centered;
d3.json("us-states.topojson",function(error,geodata) {
if (error) return console.log(error);
states.selectAll("path")
.data(topojson.feature(geodata,geodata.objects.states).features)
.enter()
.append("path")
.attr("d",path)
.on("click",clicked);
arcs.selectAll("path")
.data(arcdata)
.enter()
.append("path")
.attr('d', function(d) {
return lngLatToArc(d, 'sourceLocation', 'targetLocation', 15);
});
});
function lngLatToArc(d, sourceName, targetName, bend){
bend = bend || 1;
var sourceLngLat = d[sourceName],
targetLngLat = d[targetName];
if (targetLngLat && sourceLngLat) {
var sourceXY = projection( sourceLngLat ),
targetXY = projection( targetLngLat );
var sourceX = sourceXY[0],
sourceY = sourceXY[1];
var targetX = targetXY[0],
targetY = targetXY[1];
var dx = targetX - sourceX,
dy = targetY - sourceY,
dr = Math.sqrt(dx * dx + dy * dy)*bend;
var west_of_source = (targetX - sourceX) < 0;
if (west_of_source) return "M" + targetX + "," + targetY + "A" + dr + "," + dr + " 0 0,1 " + sourceX + "," + sourceY;
return "M" + sourceX + "," + sourceY + "A" + dr + "," + dr + " 0 0,1 " + targetX + "," + targetY;
} else {
return "M0,0,l0,0z";
}
}
function clicked(d,i) {
var x, y, k;
if (d && centered !== d) {
var centroid = path.centroid(d);
var b = path.bounds(d);
x = centroid[0];
y = centroid[1];
k = .8 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height);
centered = d
} else {
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}
states.selectAll("path")
.classed("highlighted",function(d) {
return d === centered;
})
.style("stroke-width", 1 / k + "px");
svg
.transition()
.duration(500)
.attr("transform","translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")");
}
</script>
</body>
</html>