This block shows an imaginary virtual network running on imaginary server farms across North America. Brush the graph to see where the physical machines reside. Zoom the map with shift-drag to see the virtual nodes running in those farms and the health of those nodes.
Admittedly, the main purpose for this block is to put dc.js, dc.leaflet.js, and dc.graph.js all in one block.
xxxxxxxxxx
<head>
<meta charset="utf-8">
<title>dc.js megamix</title>
<link rel="stylesheet" type="text/css" href="https://dc-js.github.io/dc.js/css/dc.css" />
<link rel="stylesheet" type="text/css" href="https://dc-js.github.io/dc.graph.js/css/dc.graph.css"/>
<link rel="stylesheet" type="text/css" href="https://dc-js.github.io/dc.leaflet.js/css/leaflet.css" />
<script src="https://dc-js.github.io/dc.js/js/d3.js"></script>
<script src="https://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script src="https://dc-js.github.io/dc.js/js/dc.js"></script>
<script src="https://dc-js.github.io/dc.graph.js/js/chart.registry.js"></script>
<script src="https://dc-js.github.io/dc.graph.js/js/dc.graph.js"></script>
<script src="https://dc-js.github.io/dc.graph.js/js/cola.js"></script>
<script src="https://dc-js.github.io/dc.leaflet.js/js/leaflet-src.js"></script>
<script src="https://dc-js.github.io/dc.leaflet.js/js/dc.leaflet.js"></script>
<style>
body { margin: 0 }
#scatter svg .axis { display: none; }
</style>
</head>
<body>
<div id="under-title" style="height: 215px"></div>
<div id="graph" style="float:left"></div>
<div id="pie"></div>
<div id="scatter"></div>
<div style="float: left">
<div id="bars"></div>
<div id="row" style="clear: both"></div>
</div>
<div id="map"></div>
<script>
let params = new URLSearchParams(document.location.search.substring(1));
var center = [39.8333333,-98.585522];
function normal(N) { // generate normal distribution of integers in [0,N)
var rand = d3.random.normal(N/2, N/6);
return function() {
var r = rand();
if(r < 0)
return 0;
else if(r > N-1)
return N-1;
else return Math.floor(r);
}
}
function remove_empty_bins(source_group, field) {
return {
all:function () {
return source_group.all().filter(function(d) {
//return Math.abs(d.value) > 0.00001; // if using floating-point numbers
return d.value[field] !== 0; // if integers only
});
}
};
}
var N = 5000, NW = 100, NE = 90, // number of data, number of (virtual) widgets, number of edges
rndVW = normal(NW);
var edges = d3.range(NE).map(function(i) {
return {key: i, value: {source: rndVW(), target: rndVW()}};
});
// data from https://www.geomidpoint.com/random/ - center 39.8333333,-98.585522, radius 1500mi
d3.csv('sites.csv', function(error, sites) {
var rndS = normal(sites.length), rndX = normal(5), rndY = normal(7), rndA = normal(100), rndB = normal(100);
sites = d3.shuffle(sites);
var data = [];
for(var i = 0; i < N; ++i) {
var s = rndS(),
site = sites[s];
data.push({
lat: +site.lat,
long: +site.long,
widget: rndVW(),
x: rndX(),
y: rndY(),
a: rndA(),
b: rndB(),
health: Math.random()
});
}
var cf = crossfilter(data);
var locDim = cf.dimension(function(d) { return d.lat + ',' + d.long; }),
locGroup = locDim.group();
var tiles=function(map) {
L.tileLayer('https://api.mapbox.com/styles/v1/mapbox/light-v9/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoiZm91cnRoYXJrIiwiYSI6ImNqNXNoMGNpODE3MDczMm11bGppejUydnYifQ.CbZ-bY4pwfUvkTncEoJWPA', {
attribution: '<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">© Mapbox</a> <a href=\"https://openstreetmap.org/about/\" target=\"_blank\">© OpenStreetMap</a>'
}).addTo(map);
};
var bubbles = dc_leaflet.bubbleChart('#map')
.width(600).height(500)
.center(center)
.zoom(4)
.tiles(tiles)
.dimension(locDim)
.group(locGroup)
.r(d3.scale.sqrt().domain([0,100]).range([5,15]))
.unselectedColor('#68e')
;
var widgetDim = cf.dimension(function(d) { return d.widget; }),
widgetGroup = widgetDim.group().reduce(
function(p, v) { // add
p.sumhealth += v.health;
++p.n;
return p;
},
function(p, v) { // remove
p.sumhealth -= v.health;
--p.n;
return p;
},
function() { // init
return {sumhealth: 0, n: 0};
}
),
nonemptyWidgets = remove_empty_bins(widgetGroup, 'n')
var edgeDim = {}, // static/unused
edgeGroup = {
all: function() {
return edges;
}
};
var healthColors = d3.scale.linear()
.domain([0.25, 0.5, 0.75]).clamp(true)
.range(["red", "white", "green"])
var engine = dc_graph.cola_layout()
.baseLength(15)
var diagram = dc_graph.diagram('#graph')
.width(700).height(600)
.nodeDimension(widgetDim).nodeGroup(nonemptyWidgets)
.edgeDimension(edgeDim).edgeGroup(edgeGroup)
.layoutEngine(engine)
.initLayoutOnRedraw(true)
.transitionDuration(2000)
.timeLimit(2000)
.nodeRadius(n => 2*Math.sqrt(n.value.n))
.nodeFill(n => n.value.sumhealth / n.value.n)
.nodeFillScale(healthColors)
.edgeKey(e => String(e.key))
.edgeSource(e => e.value.source)
.edgeTarget(e => e.value.target)
.nodeStrokeWidth(1)
.nodeOpacity(0.25)
.altKeyZoom(true)
.induceNodes(true);
var select_nodes = dc_graph.select_nodes({
nodeOpacity: 1
})
.noneIsAll(true)
.autoCropSelection(false);
diagram.child('select-nodes', select_nodes);
diagram.child('filter-selection', dc_graph.filter_selection());
var healthDim = cf.dimension(function(d) { return d.health; }),
healthGroup = healthDim.group(function(d) { return Math.floor(d*10)/10; })
var bars = dc.barChart('#bars')
.width(425).height(250)
.margins({top: 7, left: 30, right: 10, bottom: 20})
.dimension(healthDim)
.group(healthGroup)
.transitionDuration(2000)
.x(d3.scale.linear())
.xUnits(dc.units.fp.precision(0.1))
.colors(healthColors)
.colorAccessor(function(d) {
return d.key;
})
.elasticX(true).elasticY(true);
var xDim = cf.dimension(function(d) { return d.x; }),
xGroup = xDim.group();
var pie = dc.pieChart('#pie')
.width(300).height(300)
.dimension(xDim)
.group(xGroup)
.transitionDuration(2000)
.colors(d3.scale.ordinal().range(d3.shuffle(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00'])));
var yDim = cf.dimension(function(d) { return d.y; }),
yGroup = xDim.group();
var row = dc.rowChart('#row')
.width(425).height(250)
.margins({top: 0, left: 30, right: 10, bottom: 20})
.dimension(yDim)
.group(yGroup)
.transitionDuration(2000)
.colors(d3.scale.ordinal().range(d3.shuffle(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628'])));
var abDim = cf.dimension(function(d) { return [d.a, d.b]; }),
abGroup = abDim.group();
var scatter = dc.scatterPlot('#scatter')
.width(300).height(290)
.margins({top: 0, left: 0, right: 0, bottom: 0})
.dimension(abDim)
.group(abGroup)
.transitionDuration(2000)
.x(d3.scale.linear())
.y(d3.scale.linear())
.elasticX(true)
.elasticY(true)
.ordinalColors(['#4daf4a'])
.symbolSize(2)
dc.renderAll();
var map = bubbles.map();
map.on('boxzoomend', function(e) {
console.log(e.boxZoomBounds);
});
if(!params.get('nozoom')) {
var corner1 = L.latLng(49.993615462541136, -69.87304687500001),
corner2 = L.latLng(37.35269280367274, -94.21875),
bounds = L.latLngBounds(corner1, corner2);
window.setTimeout(function() {
map.fitBounds(bounds)
.fire('boxzoomend', {boxZoomBounds: bounds});
}, 2500);
}
});
</script>
</body>
Modified http://dc-js.github.io/dc.graph.js/js/chart.registry.js to a secure url
Modified http://dc-js.github.io/dc.graph.js/js/dc.graph.js to a secure url
Modified http://dc-js.github.io/dc.graph.js/js/cola.js to a secure url
Modified http://dc-js.github.io/dc.leaflet.js/js/leaflet-src.js to a secure url
Modified http://dc-js.github.io/dc.leaflet.js/js/dc.leaflet.js to a secure url
https://dc-js.github.io/dc.js/js/d3.js
https://dc-js.github.io/dc.js/js/crossfilter.js
https://dc-js.github.io/dc.js/js/dc.js
https://dc-js.github.io/dc.graph.js/js/chart.registry.js
https://dc-js.github.io/dc.graph.js/js/dc.graph.js
https://dc-js.github.io/dc.graph.js/js/cola.js
https://dc-js.github.io/dc.leaflet.js/js/leaflet-src.js
https://dc-js.github.io/dc.leaflet.js/js/dc.leaflet.js