forked from yellowcap's block: spatialsankey.js - visualizing flows on a leaflet map
xxxxxxxxxx
<meta charset="utf-8">
<title>spatialsankey.js - sankey diagrams on a map</title>
<link rel="stylesheet" href="https://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="https://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="https://d3js.org/d3.v3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/geodesign/spatialsankey/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('https://{s}tile.stamen.com/toner-lite/{z}/{x}/{y}.png', {
subdomains: ['','a.','b.','c.','d.'],
attribution: 'Map tiles by <a href="https://stamen.com">Stamen Design</a>, under <a href="https://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="https://openstreetmap.org">OpenStreetMap</a>, under <a href="https://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>
Modified http://d3js.org/d3.v3.min.js to a secure url
Modified http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js to a secure url
Modified http://rawgit.com/geodesign/spatialsankey/master/spatialsankey.js to a secure url
https://d3js.org/d3.v3.min.js
https://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js
https://rawgit.com/geodesign/spatialsankey/master/spatialsankey.js