Skip to content

Instantly share code, notes, and snippets.

@brandonhaydu
Created March 15, 2018 02:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brandonhaydu/b61f5da2353116ca94914b25c57c9561 to your computer and use it in GitHub Desktop.
Save brandonhaydu/b61f5da2353116ca94914b25c57c9561 to your computer and use it in GitHub Desktop.
spatialsankey.js - visualizing flows on a leaflet map
license: mit
<!DOCTYPE html>
<meta charset="utf-8">
<title>spatialsankey.js - sankey diagrams on a map</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<style>
body {
position: absolute;
width:100%;
height: 100%;
margin: 0px;
font-family: sans-serif;
}
#map {
top:0px;
left:0px;
right:0px;
height: 100%;
}
path {
fill: none;
stroke: #4682B4;
stroke-opacity: 0.6;
stroke-linecap: round;
cursor: pointer;
}
path:hover {
stroke-opacity: 0.8;
stroke: #315B7E;
}
.curvesettings {
position: absolute;
right: 10px;
top:6px;
}
.box {
border: 1px solid #EEE;
margin: 3px;
padding: 5px;
background-color: white;
font-family: sans-serif;
font-size: 12px;
}
.title {
font-weight: 600;
}
.source {
position: absolute;
width: 50%;
top: 6px;
left: 50px;
}
</style>
<body>
<div id="map"></div>
<form class="curvesettings">
<div class="box">
<div class="title">Curve settings</div>
<div>Hover over nodes<br> to see links.</div>
<div>Change styles using<br> radio buttons.</div>
</div>
<div class="box">
<div class="title">Curve shape</div>
<input type="radio" name="use_arcs" value="0" checked>Beziers<br>
<input type="radio" name="use_arcs" value="1">Arcs<br>
</div>
<div class="box">
<div class="title">Flip at horizontal axis</div>
<input type="radio" name="flip" value="1" checked>Flip<br>
<input type="radio" name="flip" value="0">NoFlip<br>
</div>
<div class="box">
<div class="title">Set curve steepness X</div>
<div>(bezier control point)</div>
<input type="radio" name="xshift" value="0.1">xshift 0.1<br>
<input type="radio" name="xshift" value="0.4" checked>xshift 0.4<br>
<input type="radio" name="xshift" value="0.8">xshift 0.8<br>
<input type="radio" name="xshift" value="1.6">xshift 1.6<br>
</div>
<div class="box">
<div class="title">Set curve steepness Y</div>
<div>(bezier control point)</div>
<input type="radio" name="yshift" value="0.1" checked>yshift 0.1<br>
<input type="radio" name="yshift" value="0.4">yshift 0.4<br>
<input type="radio" name="yshift" value="0.8">yshift 0.8<br>
<input type="radio" name="yshift" value="1.6">yshift 1.6<br>
</div>
</form>
<div class="source box">
<div class="title">European energy imports</div>
This graph shows how much petroleum products in thousands of tonnes are imported by each EU28 country. The <a href="http://appsso.eurostat.ec.europa.eu/nui/show.do?dataset=nrg_123a&lang=en">datasource</a> is the Eurostat database table nrg123a and for the year 2012.
</div>
</body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script src="http://rawgit.com/geodesign/spatialsankey/master/spatialsankey.js"></script>
<script type="text/javascript">
// Set leaflet map
var map = new L.map('map', {
center: new L.LatLng(50,15),
zoom: 4,
layers: [
new L.tileLayer('http://{s}tile.stamen.com/toner-lite/{z}/{x}/{y}.png', {
subdomains: ['','a.','b.','c.','d.'],
attribution: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>'
})
]
});
// Initialize the SVG layer
map._initPathRoot()
// Setup svg element to work with
var svg = d3.select("#map").select("svg"),
linklayer = svg.append("g"),
nodelayer = svg.append("g");
// Load data asynchronosuly
d3.json("nodes.geojson", function(nodes) {
d3.csv("links.csv", function(links) {
// Setup spatialsankey object
var spatialsankey = d3.spatialsankey()
.lmap(map)
.nodes(nodes.features)
.links(links);
var mouseover = function(d){
// Get link data for this node
var nodelinks = spatialsankey.links().filter(function(link){
return link.source == d.id;
});
// Add data to link layer
var beziers = linklayer.selectAll("path").data(nodelinks);
link = spatialsankey.link(options);
// Draw new links
beziers.enter()
.append("path")
.attr("d", link)
.attr('id', function(d){return d.id})
.style("stroke-width", spatialsankey.link().width());
// Remove old links
beziers.exit().remove();
// Hide inactive nodes
var circleUnderMouse = this;
circs.transition().style('opacity',function () {
return (this === circleUnderMouse) ? 0.7 : 0;
});
};
var mouseout = function(d) {
// Remove links
linklayer.selectAll("path").remove();
// Show all nodes
circs.transition().style('opacity', 0.7);
};
// Draw nodes
var node = spatialsankey.node()
var circs = nodelayer.selectAll("circle")
.data(spatialsankey.nodes())
.enter()
.append("circle")
.attr("cx", node.cx)
.attr("cy", node.cy)
.attr("r", node.r)
.style("fill", node.fill)
.attr("opacity", 0.7)
.on('mouseover', mouseover)
.on('mouseout', mouseout);
// Adopt size of drawn objects after leaflet zoom reset
var zoomend = function(){
linklayer.selectAll("path").attr("d", spatialsankey.link());
circs.attr("cx", node.cx)
.attr("cy", node.cy);
};
map.on("zoomend", zoomend);
});
});
var options = {'use_arcs': false, 'flip': false};
d3.selectAll("input").forEach(function(x){
options[x.name] = parseFloat(x.value);
})
d3.selectAll("input").on("click", function(){
options[this.name] = parseFloat(this.value);
});
</script>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment