Skip to content

Instantly share code, notes, and snippets.

@espinielli
Last active April 8, 2016 14:46
Show Gist options
  • Save espinielli/7bd09eabdacbb3670741 to your computer and use it in GitHub Desktop.
Save espinielli/7bd09eabdacbb3670741 to your computer and use it in GitHub Desktop.
FABs' En-route ATFM Delay Chroropleth

Average En-route delay per FAB in 2015.

TODOs:

  • automatic thresholding (quantization?). Use 5 classes instead of the 9 from Mike's bl.ock

  • proper legend, maybe using d3.legend

  • Investigate adding FAB labels, i.e. in the relevant centroids, and assign automatically a high contrast (compared to area color [depending on delay value]) font color. See this SO post or this blog post. Also see W3C material. The following simple function from the blog could suffice to decide between black or white label:

    ```javascript
    function getContrast50(hexcolor) {
      return (parseInt(hexcolor, 16) > 0xffffff/2) ? 'black':'white';
    }
    ```
    
  • better tooltips, maybe using d3.tip D3-tip has some benefits but:

    - not easy to have tooltip follow mouse, i.e. inside FAB area (see
      [issues #53](https://github.com/Caged/d3-tip/issues/53) for a possible solution)
    - no solution implemented to avoid cutoff at sides (see
      [issue #117](https://github.com/Caged/d3-tip/issues/117) for a possible fix not yet
      integrated in the official library)
    
    An alternative solution is the use of an helper, see
    [Chris Viau's gist](https://bl.ocks.org/biovisualize/2973775)
    or ([my fix](http://bl.ocks.org/espinielli/4d17fa15a7a5084e217992f985fba484) of
    [rveciana's one](http://bl.ocks.org/rveciana/5181105))
    

forked from espinielli's block: European FABs

(function(root,factory){if(typeof define==="function"&&define.amd){define(["d3"],factory)}else if(typeof module==="object"&&module.exports){module.exports=function(d3){d3.tip=factory(d3);return d3.tip}}else{root.d3.tip=factory(root.d3)}})(this,function(d3){return function(){var direction=d3_tip_direction,offset=d3_tip_offset,html=d3_tip_html,node=initNode(),svg=null,point=null,target=null;function tip(vis){svg=getSVGNode(vis);point=svg.createSVGPoint();document.body.appendChild(node)}tip.show=function(){var args=Array.prototype.slice.call(arguments);if(args[args.length-1]instanceof SVGElement)target=args.pop();var content=html.apply(this,args),poffset=offset.apply(this,args),dir=direction.apply(this,args),nodel=getNodeEl(),i=directions.length,coords,scrollTop=document.documentElement.scrollTop||document.body.scrollTop,scrollLeft=document.documentElement.scrollLeft||document.body.scrollLeft;nodel.html(content).style({opacity:1,"pointer-events":"all"});while(i--)nodel.classed(directions[i],false);coords=direction_callbacks.get(dir).apply(this);nodel.classed(dir,true).style({top:coords.top+poffset[0]+scrollTop+"px",left:coords.left+poffset[1]+scrollLeft+"px"});return tip};tip.hide=function(){var nodel=getNodeEl();nodel.style({opacity:0,"pointer-events":"none"});return tip};tip.attr=function(n,v){if(arguments.length<2&&typeof n==="string"){return getNodeEl().attr(n)}else{var args=Array.prototype.slice.call(arguments);d3.selection.prototype.attr.apply(getNodeEl(),args)}return tip};tip.style=function(n,v){if(arguments.length<2&&typeof n==="string"){return getNodeEl().style(n)}else{var args=Array.prototype.slice.call(arguments);d3.selection.prototype.style.apply(getNodeEl(),args)}return tip};tip.direction=function(v){if(!arguments.length)return direction;direction=v==null?v:d3.functor(v);return tip};tip.offset=function(v){if(!arguments.length)return offset;offset=v==null?v:d3.functor(v);return tip};tip.html=function(v){if(!arguments.length)return html;html=v==null?v:d3.functor(v);return tip};tip.destroy=function(){if(node){getNodeEl().remove();node=null}return tip};function d3_tip_direction(){return"n"}function d3_tip_offset(){return[0,0]}function d3_tip_html(){return" "}var direction_callbacks=d3.map({n:direction_n,s:direction_s,e:direction_e,w:direction_w,nw:direction_nw,ne:direction_ne,sw:direction_sw,se:direction_se}),directions=direction_callbacks.keys();function direction_n(){var bbox=getScreenBBox();return{top:bbox.n.y-node.offsetHeight,left:bbox.n.x-node.offsetWidth/2}}function direction_s(){var bbox=getScreenBBox();return{top:bbox.s.y,left:bbox.s.x-node.offsetWidth/2}}function direction_e(){var bbox=getScreenBBox();return{top:bbox.e.y-node.offsetHeight/2,left:bbox.e.x}}function direction_w(){var bbox=getScreenBBox();return{top:bbox.w.y-node.offsetHeight/2,left:bbox.w.x-node.offsetWidth}}function direction_nw(){var bbox=getScreenBBox();return{top:bbox.nw.y-node.offsetHeight,left:bbox.nw.x-node.offsetWidth}}function direction_ne(){var bbox=getScreenBBox();return{top:bbox.ne.y-node.offsetHeight,left:bbox.ne.x}}function direction_sw(){var bbox=getScreenBBox();return{top:bbox.sw.y,left:bbox.sw.x-node.offsetWidth}}function direction_se(){var bbox=getScreenBBox();return{top:bbox.se.y,left:bbox.e.x}}function initNode(){var node=d3.select(document.createElement("div"));node.style({position:"absolute",top:0,opacity:0,"pointer-events":"none","box-sizing":"border-box"});return node.node()}function getSVGNode(el){el=el.node();if(el.tagName.toLowerCase()==="svg")return el;return el.ownerSVGElement}function getNodeEl(){if(node===null){node=initNode();document.body.appendChild(node)}return d3.select(node)}function getScreenBBox(){var targetel=target||d3.event.target;while("undefined"===typeof targetel.getScreenCTM&&"undefined"===targetel.parentNode){targetel=targetel.parentNode}var bbox={},matrix=targetel.getScreenCTM(),tbbox=targetel.getBBox(),width=tbbox.width,height=tbbox.height,x=tbbox.x,y=tbbox.y;point.x=x;point.y=y;bbox.nw=point.matrixTransform(matrix);point.x+=width;bbox.ne=point.matrixTransform(matrix);point.y+=height;bbox.se=point.matrixTransform(matrix);point.x-=width;bbox.sw=point.matrixTransform(matrix);point.y-=height/2;bbox.w=point.matrixTransform(matrix);point.x+=width;bbox.e=point.matrixTransform(matrix);point.x-=width/2;point.y-=height/2;bbox.n=point.matrixTransform(matrix);point.y+=height;bbox.s=point.matrixTransform(matrix);return bbox}return tip}});
YEAR ENTITY_NAME FLT_ERT_1 DLY_ERT_1 DLY_ERT_A_1 DLY_ERT_C_1 DLY_ERT_D_1 DLY_ERT_E_1 DLY_ERT_G_1 DLY_ERT_I_1 DLY_ERT_M_1 DLY_ERT_N_1 DLY_ERT_O_1 DLY_ERT_P_1 DLY_ERT_R_1 DLY_ERT_S_1 DLY_ERT_T_1 DLY_ERT_V_1 DLY_ERT_W_1 DLY_ERT_NA_1
2015 NEFAB 1013252 38961 0 19602 0 182 0 0 0 0 193 5592 0 12190 0 0 1202 0
2016 UK-Ireland FAB 335984 64840 0 3466 0 0 0 0 0 0 674 57257 0 1225 0 0 2218 0
2015 Baltic FAB 789893 127978 0 108994 0 0 0 0 269 0 5409 0 0 5466 1940 0 5900 0
2015 UK-Ireland FAB 2355226 189796 0 34455 0 0 0 0 1954 0 25 0 0 50741 7432 0 95189 0
2016 NEFAB 149875 4260 0 3762 0 0 0 0 0 0 0 0 0 498 0 0 0 0
2016 FAB CE (SES RP2) 255671 23 0 0 0 0 0 0 0 0 23 0 0 0 0 0 0 0
2015 BLUE MED FAB 2326841 1481562 0 645336 0 0 0 5805 33576 500 4945 5840 161 736608 42615 0 6176 0
2016 DANUBE FAB 113568
2015 DK-SE FAB 1011225 12359 0 0 0 0 0 0 309 0 230 0 0 0 6738 0 5082 0
2015 SW FAB 1781608 821479 0 582039 0 67 461 13105 3557 48 59577 30101 0 27907 21419 722 82476 0
2016 FABEC 785443 492301 0 56366 0 0 1004 74883 5077 0 17188 311578 57 18441 1518 0 6189 0
2016 BLUE MED FAB 279354 16785 0 9023 0 0 0 0 0 0 131 577 0 7000 54 0 0 0
2015 DANUBE FAB 895457 23272 0 0 0 87 0 3259 0 0 446 0 0 0 15477 0 4003 0
2015 FABEC 5666716 3920847 0 1613384 0 0 11900 500055 110274 489 63834 672240 16 158006 125640 516 664493 0
2015 FAB CE (SES RP2) 1995408 415578 0 212659 0 0 0 1014 808 0 191 0 0 42942 29 0 157935 0
2016 Baltic FAB 111592 4732 0 4351 0 0 0 0 0 0 381 0 0 0 0 0 0 0
2016 DK-SE FAB 149147 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2016 SW FAB 238427 56821 0 34304 0 0 0 0 50 0 18076 0 0 2658 0 0 1733 0
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
.background {
fill: none;
pointer-events: all;
}
.fab {
stroke: yellow;
stroke-width: 1px;
opacity: 0.8;
}
.fir {
fill: yellow;
opacity: 0;
cursor: pointer;
}
.fir-boundary {
fill: none;
stroke: #888;
/*stroke-dasharray: 2,2;*/
stroke-linejoin: round;
stroke-linecap: round;
stroke-width: .5px;
}
.fir-boundary.ECTRL {
stroke: #8BF63D;
/*stroke-width: .5px;*/
}
.graticule {
fill: none;
stroke: #333;
/*stroke-width: .5px;*/
stroke-opacity: .5;
}
.boundary {
fill: none;
stroke: #000;
/*stroke-width: .5px;*/
}
.country {
fill: #ccc;
opacity: 0.4;
stroke: #000;
stroke-width: 0.5px;
}
#tooltip {
position: absolute;
width: auto;
height: auto;
padding: 2px 2px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4);
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4);
pointer-events: none;
background-color: white;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0 0 0 0;
padding: 2px 2px;
font-family: sans-serif;
font-size: 11px;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
pointer-events: none;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
position: absolute;
pointer-events: none;
}
/* Northward tooltips */
.d3-tip.n:after {
content: "\25BC";
margin: -1px 0 0 0;
top: 100%;
left: 0;
text-align: center;
}
/* Eastward tooltips */
.d3-tip.e:after {
content: "\25C0";
margin: -4px 0 0 0;
top: 50%;
left: -8px;
}
/* Southward tooltips */
.d3-tip.s:after {
content: "\25B2";
margin: 0 0 1px 0;
top: -8px;
left: 0;
text-align: center;
}
/* Westward tooltips */
.d3-tip.w:after {
content: "\25B6";
margin: -4px 0 0 -1px;
top: 50%;
left: 100%;
}
.hidden{
display: none;
}
svg {
background: none;
}
/* same as FAB opacity */
.swatch {
stroke: #000;
stroke-width: 0.5px;
opacity: 0.8;
}
.q0-5 {
fill:rgba(239,243,255, 1)
}
.q1-5 {
fill:rgba(189,215,231, 1)
}
.q2-5 {
fill:rgba(107,174,214, 1)
}
.q3-5 {
fill:rgba(49,130,189, 1)
}
.q4-5 {
fill:rgba(8,81,156, 1)
}
/*jslint browser: true, devel: true */
var d3, queue, vis, params, topojson;
(function () {
"use strict";
vis = {};
var width, height,
chart, svg, g, active, background,
path,
defs, style,
slider, step, maxStep, running, sv, timer,
button;
// general design from
// http://www.jeromecukier.net/blog/2013/11/20/getting-beyond-hello-world-with-d3/
vis.init = function (params) {
vis.params = params || {};
chart = d3.select(vis.params.chart || "#chart"); // placeholder div for svg
width = vis.params.width || 600;
height = vis.params.height || 600;
active = d3.select(null);
svg = chart.selectAll("svg")
.data([{width: width, height: height}]).enter()
.append("svg");
svg.attr({
width: function (d) { return d.width; },
height: function (d) { return d.height; }
});
background = svg.selectAll("rect.background")
.data([{}]).enter()
.append("rect")
.classed("background", true);
g = svg.selectAll("g.all")
.data([{}]).enter()
.append("g")
.classed("all", true);
// vis.init can be re-ran to pass different height/width values
// to the svg. this doesn't create new svg elements.
style = svg.selectAll("style")
.data([{}]).enter()
.append("style")
.attr("type", "text/css");
// this is where we can insert style that will affect the svg directly.
defs = svg.selectAll("defs").data([{}]).enter()
.append("defs");
// this is used if it's necessary to define gradients, patterns etc.
// the following will implement interaction around a slider and a
// button. repeat/remove as needed.
// note that this code won't cause errors if the corresponding elements
// do not exist in the HTML.
slider = d3.select(vis.params.slider || ".slider");
if (slider[0][0]) {
maxStep = slider.property("max");
step = slider.property("value");
slider.on("change", function () {
vis.stop();
step = this.value;
vis.draw(vis.params);
});
running = vis.params.running || 0; // autorunning off or manually set on
} else {
running = -1; // never attempt auto-running
}
button = d3.select(vis.params.button || ".button");
if (button[0][0] && running > -1) {
button.on("click", function () {
if (running) {
vis.stop();
} else {
vis.start();
}
});
}
vis.loaddata(vis.params);
};
function ready(error, firs, world, wnames, trafficdelays) {
if (error) {
console.error(error);
}
vis.firs = firs;
vis.world = world;
vis.wnames = wnames;
vis.trafficdelays = trafficdelays;
if (running > 0) {
vis.start();
} else {
vis.draw(vis.params);
}
}
vis.loaddata = function (params) {
if (!params) { params = {}; }
// if `params.refresh` is set/true forces the browser to reload the file
// and not use the cached version due to URL being different (but the filename is the same)
var topo = (params.topo || "ectrl-firs.json") + (params.refresh ? ("#" + Math.random()) : "");
var world = (params.world || "world-50m.json") + (params.refresh ? ("#" + Math.random()) : "");
var names = (params.worldnames || "world-country-names.tsv") + (params.refresh ? ("#" + Math.random()) : "");
var trafficdelays = (params.trafficdelays || "En-Route_Traffic_FAB_FIR.csv") + (params.refresh ? ("#" + Math.random()) : "");
queue()
.defer(d3.json, topo)
.defer(d3.json, world)
.defer(d3.tsv, names)
.defer(d3.csv, trafficdelays)
.await(ready);
};
vis.play = function () {
if (i === maxStep && !running) {
step = -1;
vis.stop();
}
if (i < maxStep) {
step = step + 1;
running = 1;
d3.select(".stop").html("Pause").on("click", vis.stop(params));
slider.property("value", sv);
vis.draw(params);
} else {
vis.stop();
}
};
vis.start = function (params) {
timer = setInterval(function () { vis.play(params); }, 50);
};
vis.stop = function (params) {
clearInterval(timer);
running = 0;
d3.select(".stop").html("Play").on("click", vis.start(params));
};
var zoom = d3.behavior.zoom()
.scaleExtent([1, 8000])
.on("zoom", zoomed);
function zoomed() {
g.style("stroke-width", 1.5 / d3.event.scale + "px");
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
// console.log("g.attr('transform') [zoomed]: " + g.attr("transform"));
// console.log("center [zoomed]: " + path.projection().center());
// console.log("translate [zoomed]: " + path.projection().translate());
// console.log("rotate [zoomed]: " + path.projection().rotate());
// console.log("scale [zoomed]: " + path.projection().scale());
}
// If the drag behavior prevents the default click,
// also stop propagation so we don’t click-to-zoom.
function stopped() {
if (d3.event.defaultPrevented) d3.event.stopPropagation();
}
function reset() {
active.classed("active", false);
active = d3.select(null);
svg.transition()
.duration(750)
.call(zoom.translate([0, 0]).scale(1).event);
// console.log("center: " + path.projection().center());
}
function clicked(d) {
if (active.node() === this) {
return reset();
}
active.classed("active", false);
active = d3.select(this).classed("active", true);
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = .9 / Math.max(dx / width, dy / height),
translate = [width / 2 - scale * x, height / 2 - scale * y];
svg.transition()
.duration(750)
.call(zoom.translate(translate).scale(scale).event);
// console.log("center [clicked]: " + path.projection().center());
}
vis.draw = function (params) {
// make stuff here!
var pars = params || {},
scale = pars.scale || 600,
cLon = pars.centerLon || 10,
cLat = pars.centerLat || 51.5,
projection = d3.geo.albers()
.center([cLon, cLat])
.rotate([4.4, 0])
.parallels([50, 60])
.scale(scale)
.translate([width / 2, height / 2]),
firs = topojson.feature(vis.firs, vis.firs.objects.firs).features,
tooltip = d3.select("#tooltip").classed("hidden", true),
countryname = d3.select("#countryname").append("tspan"),
graticule = d3.geo.graticule(),
land = topojson.feature(vis.world, vis.world.objects.land),
countries = topojson.feature(vis.world, vis.world.objects.countries).features,
borders = topojson.mesh(vis.world, vis.world.objects.countries, function (a, b) { return a.id !== b.id; }),
country, fir,
fabs = [
{"id": "BALTICFAB", "name": "Baltic FAB"},
{"id": "BLUMEDFAB", "name": "BLUE MED FAB"},
{"id": "DANUBEFAB", "name": "DANUBE FAB"},
{"id": "DKSEFAB", "name": "DK-SE FAB"},
{"id": "FABCE", "name": "FAB CE (SES RP2)"},
{"id": "FABEC", "name": "FABEC"},
{"id": "NEFAB", "name": "NEFAB"},
{"id":"SWFAB", "name": "SW FAB"},
{"id":"UKIRELANDFAB", "name": "UK-Ireland FAB"}
];
function idxForName(target) {
return function(idx, item, i) {
return item.name === target ? i : idx;
};
}
var rateById = {};
vis.trafficdelays.forEach(function(d) {
var obj_idx = fabs.reduce(idxForName(d.ENTITY_NAME), -1);
if (obj_idx === -1) {
console.log(d);
console.log("FAB name NOT found!!!!");
}
else {
//console.log(d);
if (d.YEAR === "2015") {
fabs[obj_idx].rate = d.FLT_ERT_1 == 0 ? 0 : d.DLY_ERT_1 / d.FLT_ERT_1;
fabs[obj_idx].year = +d.YEAR;
rateById[fabs[obj_idx].id] = fabs[obj_idx].rate;
}
}
});
console.log(fabs);
console.log(rateById);
var twodecimals = d3.format(".2n");
var rates = fabs.map(function(d) { return d.rate;});
console.log([d3.min(rates), d3.max(rates)]);
var qClasses = 5;
var quantize = d3.scale.quantize()
.domain([d3.min(rates), d3.max(rates)])
.range(d3.range(qClasses).map(function(i) { return "q" + i + "-" + qClasses; }));
// var color = d3.scale.threshold()
// .domain([.02, .03, .03, .05, .65])
// .range(["#f2f0f7", "#dadaeb", "#bcbddc", "#9e9ac8", "#756bb1", "#54278f"]);
path = d3.geo.path()
.projection(projection);
countries.forEach(function (d) {
vis.wnames.some(function (n) {
if (+d.id === +n.id) {
d.name = n.name;
return d.name;
}
});
});
// /* Initialize tooltip */
// var tip = d3.tip().attr('class', 'd3-tip').html(function(d) { return d; });
// tip.html(function(d) {
// return d.name + "<br><span>" + d.name +
// ": avg delay per flight: " + twodecimals(d.rate) + "</span>";
// });
// // use tip.offset to follow an hidden circle that follows the mouse when over the FAB
// /* Invoke the tip in the context of your visualization */
// svg.call(tip);
svg.on("click", stopped, true);
background.on("click", reset);
g.style("stroke-width", "0.5px");
svg
.call(zoom) // delete this line to disable free zooming
.call(zoom.event);
svg.on("mousemove", function () {
// update tooltip position
tooltip.style("top", (event.pageY + 16) + "px").style("left", (event.pageX + 10) + "px");
return true;
});
svg.selectAll(".path.graticule")
.data([graticule]).enter()
.append("path")
.classed("graticule", true)
.attr("d", path);
svg.append("g")
.attr("class", "legendQuant")
.attr("transform", "translate(20," + height/3 + ")");
var legend = d3.legend.color()
.labelFormat(d3.format(".2f"))
.useClass(true)
.scale(quantize);
svg.select(".legendQuant")
.call(legend);
country = g.selectAll(".country")
.data(countries)
.enter().insert("path", ".graticule")
.attr("class", function (d) {return "country country" + d.id; })
.attr("d", path)
.text(function (d) { return d.id; })
.on("mouseover", function (d, i) {
d3.select(this).style({'stroke-opacity': 1, 'stroke': '#F00'});
// http://stackoverflow.com/questions/17917072/#answer-17917341
// d3.select(this.parentNode.appendChild(this)).style({'stroke-opacity':1,'stroke':'#F00'});
if (d.id) {
tooltip.classed("hidden", false);
countryname.text(d.name);
}
})
.on("mouseout", function () {
this.style.stroke = "#000";
tooltip.classed("hidden", true);
})
.on("mousedown.log", function (d) {
console.log("id=" + d.id + "; name=" + d.name + "; centroid=[" + path.centroid(d) + "] px.");
});
// TODO: this seems not too much a D3 idiom...
fabs.forEach(function (f) {
// from http://stackoverflow.com/a/16093597/963575
var mmm = topojson.merge(
vis.firs,
vis.firs.objects.firs.geometries.filter(function (d) {
return d.properties.fab === f.id;
}));
mmm.id = f.id;
mmm.name = f.name;
mmm.rate = f.rate;
g.append("path")
.datum(mmm)
.attr("d", path)
// .attr("class", "fab " + f.id)
// .style("fill", function(d) { return color(d.rate); })
.attr("class", "fab " + f.id + " " + quantize(f.rate))
// .on("click", clicked)
.on("mouseover", function (d) {
d3.select(this).style("stroke", "red");
// tip.show(d);
tooltip.classed("hidden", false);
// countryname.text(d.name + ", avg delay per flight: " + twodecimals(d.rate));
countryname.html(d.name + "<br> avg delay per flight: " + twodecimals(d.rate));
})
.on("mouseleave", function (d) {
d3.select(this).style("stroke", "yellow");
tooltip.classed("hidden", true);
// tip.hide(d);
})
;
});
};
}());
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.10.0/d3-legend.min.js"></script>
<script src="d3-tip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js" type="text/javascript"></script>
<link href="firs.css" rel="stylesheet">
<script src="firs.js"></script>
<div id="tooltip" class="hidden">
<p id="countryname"></p>
</div><div id="chart"></div>
<script type="text/javascript">
(function () {
// general design from
// http://www.jeromecukier.net/blog/2013/11/20/getting-beyond-hello-world-with-d3/
var params = {
refresh: false, // REMOVE, i.e. `false`, for production
topo: "euctrl-firs.json",
world: "world-50m.json",
worldnames: "world-country-names.tsv"
};
var query = window.location.search.substring(1);
var vars = query.split("&");
vars.forEach(function(v) {
var p = v.split("=");
params[p[0]] = p[1];
})
console.log("params: " + JSON.stringify(params));
vis.init(params);
}());
</script>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment