A minimalistic visualization of SES RP2 FABs.
Geo data from Eurocontrol Atlas.
A minimalistic visualization of SES RP2 FABs.
Geo data from Eurocontrol Atlas.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
svg { | |
background: #00FFFF; | |
} | |
.graticule { | |
fill: none; | |
stroke: #777; | |
stroke-width: .5px; | |
stroke-opacity: .5; | |
} | |
.land { | |
fill: #ddd; | |
opacity: 0.4; | |
stroke: #aaa; | |
} | |
.countryboundary { | |
fill: none; | |
stroke: #aaa; | |
stroke-width: .5px; | |
} | |
.firboundary { | |
stroke: black; | |
fill: none; | |
stroke-width: .5px; | |
} | |
.fabboundary { | |
stroke: rgb(206,0,18); | |
fill: none; | |
stroke-width: .5px; | |
} | |
.SWFAB { | |
fill: rgb(247,243,38); | |
} | |
.NEFAB { | |
fill: rgb(13,24,73); | |
} | |
.FABEC { | |
fill: rgb(238,138,27); | |
} | |
.FABCE { | |
fill: rgb(169,0,25); | |
} | |
.UKEI { | |
fill: rgb(125,59,148); | |
} | |
.BALTIC { | |
fill: rgb(238,234,49); | |
} | |
.DKSE { | |
fill: orange; | |
} | |
.DANUBE { | |
fill: rgb(179,179,179); | |
} | |
.BLUEMED { | |
fill: rgb(30,37,108); | |
} | |
.fab { | |
opacity: 0.3; | |
pointer-events: none; | |
} | |
.fir { | |
opacity: 0.1; | |
} | |
#tooltip { | |
position: absolute; | |
width: auto; | |
height: auto; | |
padding: 2px 2px; | |
-webkit-border-radius: 3px; | |
-moz-border-radius: 3px; | |
border-radius: 3px; | |
-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: #bbb; | |
} | |
#tooltip.hidden { | |
display: none; | |
} | |
#tooltip p { | |
margin: 0 0 0 0; | |
padding: 2px 2px; | |
font-family: sans-serif; | |
font-size: 11px; | |
} | |
.hidden{ | |
display: none; | |
} | |
</style> | |
<body> | |
<div id="tooltip" class="hidden"> | |
<p id="info"></p> | |
</div> | |
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script src="//d3js.org/topojson.v1.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script> | |
<script> | |
var width = 960, | |
height = 630; | |
var scale = 570, | |
cLat = 52.4, | |
cLon = 0; | |
var color = d3.scale.category10(); | |
var tooltip = d3.select("#tooltip").classed("hidden", true); | |
var info = d3.select("#info"); | |
var projection = d3.geo.albers() | |
.center([cLon, cLat]) | |
.rotate([4.4, 0]) | |
.parallels([50, 60]) | |
.scale(scale) | |
.translate([width / 2, height / 2]); | |
var path = d3.geo.path() | |
.projection(projection); | |
var graticule = d3.geo.graticule() | |
.majorStep([5, 5]) | |
.precision(2.5); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
svg.append("path") | |
.datum(graticule) | |
.attr("class", "graticule") | |
.attr("d", path); | |
svg.on("mousemove", function () { | |
// update tooltip position | |
tooltip.style("top", (event.pageY + 8) + "px").style("left", (event.pageX + 8) + "px"); | |
return true; | |
}); | |
queue() | |
.defer(d3.json, "rp2.json") | |
.defer(d3.json, "world-50m.json") | |
.await(ready); | |
function ready(error, rp2, world) { | |
if (error) return console.error(error); | |
var firs = topojson.feature(rp2, rp2.objects.firs); | |
var firborders = topojson.mesh(rp2, rp2.objects.firs, function (a, b) { return (a !== b) || (a === b); }); | |
var fabs = topojson.feature(rp2, rp2.objects.fabs); | |
// FAB borders (interior [a !== b] and exterior [a === b]) | |
var fabborders = topojson.mesh(rp2, rp2.objects.fabs, function (a, b) { return (a !== b) || (a === b); }); | |
var land = topojson.feature(world, world.objects.land); | |
var countryborders = topojson.mesh(world, world.objects.countries, function (a, b) { return (a !== b) || (a === b); }); | |
svg.insert("path", ".graticule") | |
.datum(topojson.feature(world, world.objects.land)) | |
.attr("class", "land") | |
.attr("d", path); | |
svg.insert("path", ".graticule") | |
.datum(countryborders) | |
.attr("class", "countryboundary") | |
.attr("d", path); | |
svg.selectAll(".fir") | |
.data(firs.features) | |
.enter().insert("path", ".graticule") | |
.attr("class", function(d) { return "fir " + d.id + " " + d.properties.fab; }) | |
.attr("d", path) | |
.on("mouseover", function (d, i) { | |
d3.select(this).style({'stroke-opacity': 1, 'stroke': '#F00'}); | |
// http://stackoverflow.com/questions/17917072/#answer-17917341 | |
tooltip.classed("hidden", false); | |
info.text("FIR: " + d.id + ". " + d.properties.fab); | |
}) | |
.on("mouseout", function () { | |
this.style.stroke = "none"; | |
tooltip.classed("hidden", true); | |
}); | |
svg.insert("path", ".graticule") | |
.datum(firborders) | |
.attr("class", "firboundary") | |
.attr("d", path); | |
svg.selectAll(".fab") | |
.data(fabs.features) | |
.enter().insert("path", ".graticule") | |
.attr("class", function(d) { return "fab " + d.properties.id; }) | |
.attr("d", path); | |
svg.insert("path", ".graticule") | |
.datum(fabborders) | |
.attr("class", "fabboundary") | |
.attr("d", path); | |
} | |
</script> |