Reproducing the map from Alessandro Sorichetta et al., Mapping internal connectivity through human migration in malaria endemic countries, Nature Scientific Data 3, Article number: 160066 (2016) doi:10.1038/sdata.2016.66.
We use a Force‐Directed Edge Bundling (FDEB) algorithm to make the graph less cluttered.
See Holten, Danny, and Jarke J. Van Wijk. “Force‐Directed Edge Bundling for Graph Visualization” Computer Graphics Forum (Blackwell Publishing Ltd) 28, no. 3 (2009): 983-990. (PDF)
Javascript implementation by Corneliu Sugar.
Compare with the graph without bundling: bolivia internal migrations.
xxxxxxxxxx
<meta charset="utf-8">
<style>
.stroke {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #fff;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: 0.5px;
stroke-opacity: 0.5;
}
.land {
fill: #ccc;
}
.boundary {
fill: none;
stroke: #555;
stroke-width: 0.5px;
}
</style>
<svg width="960" height="484"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="//d3js.org/d3-geo-projection.v1.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script type="text/javascript" src="d3-ForceEdgeBundling.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var projection = d3.geoRobinson()
.rotate([66,16.4])
.center([0,0])
.scale(1800)
.translate([width / 2, height / 2])
.precision(0.1);
var graticule = d3.geoGraticule();
var path = d3.geoPath()
.projection(projection);
var defs = svg.append("defs");
defs.append("path")
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path);
defs.append("clipPath")
.attr("id", "clip")
.append("use")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("clip-path", "url(#clip)")
.attr("d", path);
var lines = svg.append("g"),
circles = svg.append("g");
var color = d3.scaleQuantize()
.domain([0, 1])
.range(d3.range(6).map(function(i) { return d3.interpolateSpectral(1-i/6)}));
d3.json("https://unpkg.com/visionscarto-world-atlas@0.0.4/world/110m.json" // "https://rawgit.com/visionscarto/some-geo-data/master/ne_50m_admin_0_countries/countries.topo.json"
, function(error, world) {
if (error) throw error;
if (true)
svg.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.countries))
.attr("class", "land")
.attr("clip-path", "url(#clip)")
.attr("d", path);
if (true)
svg.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("clip-path", "url(#clip)")
.attr("d", path);
var w = topojson.feature(world, world.objects.countries);
w.features.forEach(function(d, i){
});
d3.csv('BOL_5yrs_InternalMigFlows_2010.csv', function(err, migrations){
var sites = [];
migrations.map(function(l) {
var p = projection([+l.LONFR,+l.LATFR]);
sites[+l.NODEI] = { index: +l.NODEI, x: p[0], y: p[1] };
});
migrations = migrations.map(function(l){
l.source = +l.NODEI; //sites[+l.NODEI];
l.target = +l.NODEJ; //sites[+l.NODEJ];
l.i = Math.log(+l.PrdMIG);
return l;
})
migrations
.sort(function(a,b){
return d3.ascending(a.i, b.i);
})
//console.log(migrations);
color.domain(d3.extent(migrations, function(d){ return d.i}))
var d3line = d3.line()
.x(function(d){return d.x;})
.y(function(d){return d.y;});
var lines = svg.append('g');
lines
.selectAll('path')
.data(migrations
.map(d => [sites[d.source], sites[d.target]])
)
.enter()
.append('path')
.attr('d', d3line)
.attr('stroke', function(d,i) { return color(migrations[i].i); })
.attr('stroke-width', function(d,i) { return (migrations[i].i)/2; })
.attr('opacity', 0.4)
.attr('fill', 'none')
svg.append('g')
.selectAll('circle')
.data(sites.filter(d => d.index))
.enter()
.append('circle')
.attr('transform', function(d){ return 'translate('+ [d.x, d.y] +')';})
.attr('r', 3)
.attr('fill', 'white')
.attr('stroke', '#777');
var fbundling = d3.ForceEdgeBundling().nodes(sites).edges(migrations);
setTimeout(function() {
var results = fbundling();
console.log(results);
lines
.selectAll('path')
.data(results)
.attr('d', d3line)
},10);
});
});
</script>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-geo-projection.v1.min.js
https://d3js.org/topojson.v1.min.js
https://d3js.org/d3-scale-chromatic.v1.min.js