Visualization of European FIRs (those starting w/ 'E', 'L' or 'UK', 'UG', 'UD', 'BK', 'GC'.
Click on FIR to zoom in/out.
Geometries from the Network Manager and available in this repo.
Built with blockbuilder.org
Visualization of European FIRs (those starting w/ 'E', 'L' or 'UK', 'UG', 'UD', 'BK', 'GC'.
Click on FIR to zoom in/out.
Geometries from the Network Manager and available in this repo.
Built with blockbuilder.org
.background { | |
fill: none; | |
pointer-events: all; | |
} | |
.fab { | |
fill: #ddd; | |
opacity: 0.5; | |
} | |
.fir { | |
fill: #ddc; | |
opacity: 0.5; | |
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; | |
} | |
.land { | |
fill: none; | |
/*stroke-width: .5px;*/ | |
stroke-opacity: .5; | |
} | |
.boundary { | |
fill: none; | |
stroke: #fff; | |
/*stroke-width: .5px;*/ | |
} | |
.country { | |
fill: #ccc; | |
} | |
#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; | |
} | |
svg { | |
background: #eee; | |
} |
/*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) { | |
console.log("in init, params: " + JSON.stringify(params)); | |
vis.params = params || {}; | |
chart = d3.select(vis.params.chart || "#chart"); // placeholder div for svg | |
width = vis.params.width || 960; | |
height = vis.params.height || 500; | |
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) { | |
if (error) { | |
console.error(error); | |
} | |
vis.firs = firs; | |
vis.world = world; | |
vis.wnames = wnames; | |
if (running > 0) { | |
vis.start(); | |
} else { | |
vis.draw(vis.params); | |
} | |
} | |
vis.loaddata = function (params) { | |
console.log("in loaddata, params: " + JSON.stringify(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()) : ""); | |
queue() | |
.defer(d3.json, topo) | |
.defer(d3.json, world) | |
.defer(d3.tsv, names) | |
.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 + ")"); | |
} | |
// 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); | |
} | |
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); | |
} | |
vis.draw = function (params) { | |
// make stuff here! | |
console.log("in draw, params: " + JSON.stringify(params)); | |
var pars = params || {}, | |
scale = pars.scale || 600, | |
cLon = pars.centerLon || 0, | |
cLat = pars.centerLan || 55.4, | |
projection = d3.geo.albers() | |
.center([cLon, cLat]) | |
.rotate([4.4, 0]) | |
.parallels([50, 60]) | |
.scale(scale) | |
.translate([width / 2, height / 2]), | |
fff = vis.firs.objects.firs, | |
firs = topojson.feature(vis.firs, fff), | |
tooltip = d3.select("#tooltip").classed("hidden", true), | |
countryname = d3.select("#countryname"), | |
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, | |
uir; | |
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; | |
} | |
}); | |
}); | |
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); | |
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 = "none"; | |
tooltip.classed("hidden", true); | |
}) | |
.on("mousedown.log", function (d) { | |
console.log("id=" + d.id + "; name=" + d.name + "; centroid=[" + path.centroid(d) + "] px."); | |
}); | |
// FIRs | |
fir = g.selectAll(".fir") | |
.data(firs.features) | |
.enter().insert("path", ".graticule") | |
.attr("class", function (d) { return "fir " + d.id; }) | |
.attr("d", path) | |
.on("click", clicked) | |
.on("mouseover", function (d) { | |
d3.select(this).style("fill", "red"); | |
tooltip.classed("hidden", false); | |
countryname.html("<h4>" + (d.properties.name || "- no name -") + "</h4>" + | |
"<b>ID</b>: " +d.properties.id + "<br>" + | |
"<b>FL</b>: [" + d.properties.minfl + ", " + d.properties.maxfl + "]<br>" + | |
"<b>Fab</b>: " + (d.properties.fab || "-" ) + "</p>"); | |
}) | |
.on("mouseleave", function () { | |
d3.select(this).style("fill", "#ddc"); | |
tooltip.classed("hidden", true); | |
}); | |
// intra FIR borders | |
g.selectAll(".fir-boundary") | |
.data([topojson.mesh(vis.firs, fff, function (a, b) { | |
return a !== b; | |
})]) | |
.enter().insert("path", ".graticule") | |
.attr("d", path) | |
.attr("class", "fir-boundary"); | |
// external borders | |
g.selectAll("fir-boundary ECTRL") | |
.data([topojson.mesh(vis.firs, fff, function (a, b) { | |
return a === b; | |
})]) | |
.enter().insert("path", ".graticule") | |
.attr("d", path) | |
.attr("class", "fir-boundary ECTRL"); | |
}; | |
}()); |
<!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/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="fir.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 | |
width: 960, | |
height:500, | |
scale: 470, | |
centerLat:65.4, | |
centerLon: 0, | |
topo: "euctrl-firs.json", | |
world: "world-110m.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> |
id | name | |
---|---|---|
-1 | Northern Cyprus | |
-2 | Kosovo | |
-3 | Somaliland | |
4 | Afghanistan | |
8 | Albania | |
10 | Antarctica | |
12 | Algeria | |
16 | American Samoa | |
20 | Andorra | |
24 | Angola | |
28 | Antigua and Barbuda | |
31 | Azerbaijan | |
32 | Argentina | |
36 | Australia | |
40 | Austria | |
44 | Bahamas | |
48 | Bahrain | |
50 | Bangladesh | |
51 | Armenia | |
52 | Barbados | |
56 | Belgium | |
60 | Bermuda | |
64 | Bhutan | |
68 | Bolivia, Plurinational State of | |
70 | Bosnia and Herzegovina | |
72 | Botswana | |
74 | Bouvet Island | |
76 | Brazil | |
84 | Belize | |
86 | British Indian Ocean Territory | |
90 | Solomon Islands | |
92 | Virgin Islands, British | |
96 | Brunei Darussalam | |
100 | Bulgaria | |
104 | Myanmar | |
108 | Burundi | |
112 | Belarus | |
116 | Cambodia | |
120 | Cameroon | |
124 | Canada | |
132 | Cape Verde | |
136 | Cayman Islands | |
140 | Central African Republic | |
144 | Sri Lanka | |
148 | Chad | |
152 | Chile | |
156 | China | |
158 | Taiwan, Province of China | |
162 | Christmas Island | |
166 | Cocos (Keeling) Islands | |
170 | Colombia | |
174 | Comoros | |
175 | Mayotte | |
178 | Congo | |
180 | Congo, the Democratic Republic of the | |
184 | Cook Islands | |
188 | Costa Rica | |
191 | Croatia | |
192 | Cuba | |
196 | Cyprus | |
203 | Czech Republic | |
204 | Benin | |
208 | Denmark | |
212 | Dominica | |
214 | Dominican Republic | |
218 | Ecuador | |
222 | El Salvador | |
226 | Equatorial Guinea | |
231 | Ethiopia | |
232 | Eritrea | |
233 | Estonia | |
234 | Faroe Islands | |
238 | Falkland Islands (Malvinas) | |
239 | South Georgia and the South Sandwich Islands | |
242 | Fiji | |
246 | Finland | |
248 | Åland Islands | |
250 | France | |
254 | French Guiana | |
258 | French Polynesia | |
260 | French Southern Territories | |
262 | Djibouti | |
266 | Gabon | |
268 | Georgia | |
270 | Gambia | |
275 | Palestinian Territory, Occupied | |
276 | Germany | |
288 | Ghana | |
292 | Gibraltar | |
296 | Kiribati | |
300 | Greece | |
304 | Greenland | |
308 | Grenada | |
312 | Guadeloupe | |
316 | Guam | |
320 | Guatemala | |
324 | Guinea | |
328 | Guyana | |
332 | Haiti | |
334 | Heard Island and McDonald Islands | |
336 | Holy See (Vatican City State) | |
340 | Honduras | |
344 | Hong Kong | |
348 | Hungary | |
352 | Iceland | |
356 | India | |
360 | Indonesia | |
364 | Iran, Islamic Republic of | |
368 | Iraq | |
372 | Ireland | |
376 | Israel | |
380 | Italy | |
384 | Côte d'Ivoire | |
388 | Jamaica | |
392 | Japan | |
398 | Kazakhstan | |
400 | Jordan | |
404 | Kenya | |
408 | Korea, Democratic People's Republic of | |
410 | Korea, Republic of | |
414 | Kuwait | |
417 | Kyrgyzstan | |
418 | Lao People's Democratic Republic | |
422 | Lebanon | |
426 | Lesotho | |
428 | Latvia | |
430 | Liberia | |
434 | Libya | |
438 | Liechtenstein | |
440 | Lithuania | |
442 | Luxembourg | |
446 | Macao | |
450 | Madagascar | |
454 | Malawi | |
458 | Malaysia | |
462 | Maldives | |
466 | Mali | |
470 | Malta | |
474 | Martinique | |
478 | Mauritania | |
480 | Mauritius | |
484 | Mexico | |
492 | Monaco | |
496 | Mongolia | |
498 | Moldova, Republic of | |
499 | Montenegro | |
500 | Montserrat | |
504 | Morocco | |
508 | Mozambique | |
512 | Oman | |
516 | Namibia | |
520 | Nauru | |
524 | Nepal | |
528 | Netherlands | |
531 | Curaçao | |
533 | Aruba | |
534 | Sint Maarten (Dutch part) | |
535 | Bonaire, Sint Eustatius and Saba | |
540 | New Caledonia | |
548 | Vanuatu | |
554 | New Zealand | |
558 | Nicaragua | |
562 | Niger | |
566 | Nigeria | |
570 | Niue | |
574 | Norfolk Island | |
578 | Norway | |
580 | Northern Mariana Islands | |
581 | United States Minor Outlying Islands | |
583 | Micronesia, Federated States of | |
584 | Marshall Islands | |
585 | Palau | |
586 | Pakistan | |
591 | Panama | |
598 | Papua New Guinea | |
600 | Paraguay | |
604 | Peru | |
608 | Philippines | |
612 | Pitcairn | |
616 | Poland | |
620 | Portugal | |
624 | Guinea-Bissau | |
626 | Timor-Leste | |
630 | Puerto Rico | |
634 | Qatar | |
638 | Réunion | |
642 | Romania | |
643 | Russian Federation | |
646 | Rwanda | |
652 | Saint Barthélemy | |
654 | Saint Helena, Ascension and Tristan da Cunha | |
659 | Saint Kitts and Nevis | |
660 | Anguilla | |
662 | Saint Lucia | |
663 | Saint Martin (French part) | |
666 | Saint Pierre and Miquelon | |
670 | Saint Vincent and the Grenadines | |
674 | San Marino | |
678 | Sao Tome and Principe | |
682 | Saudi Arabia | |
686 | Senegal | |
688 | Serbia | |
690 | Seychelles | |
694 | Sierra Leone | |
702 | Singapore | |
703 | Slovakia | |
704 | Viet Nam | |
705 | Slovenia | |
706 | Somalia | |
710 | South Africa | |
716 | Zimbabwe | |
724 | Spain | |
728 | South Sudan | |
729 | Sudan | |
732 | Western Sahara | |
740 | Suriname | |
744 | Svalbard and Jan Mayen | |
748 | Swaziland | |
752 | Sweden | |
756 | Switzerland | |
760 | Syrian Arab Republic | |
762 | Tajikistan | |
764 | Thailand | |
768 | Togo | |
772 | Tokelau | |
776 | Tonga | |
780 | Trinidad and Tobago | |
784 | United Arab Emirates | |
788 | Tunisia | |
792 | Turkey | |
795 | Turkmenistan | |
796 | Turks and Caicos Islands | |
798 | Tuvalu | |
800 | Uganda | |
804 | Ukraine | |
807 | Macedonia, the former Yugoslav Republic of | |
818 | Egypt | |
826 | United Kingdom | |
831 | Guernsey | |
832 | Jersey | |
833 | Isle of Man | |
834 | Tanzania, United Republic of | |
840 | United States | |
850 | Virgin Islands, U.S. | |
854 | Burkina Faso | |
858 | Uruguay | |
860 | Uzbekistan | |
862 | Venezuela, Bolivarian Republic of | |
876 | Wallis and Futuna | |
882 | Samoa | |
887 | Yemen | |
894 | Zambia |