A map of Eurocontrol member states. Hover with the mouse on members countries to get name, adhesion date and flag.
I tried various bounding boxes:
WOEID: 24865675
Centroid Location (lat/lon): 52.976181, 7.857840
Bounding Box:
NE 81.008797, 39.869301
SW 27.636311, -31.266001
I did decide to use
N = 71°
E = -11°
W = 35°
S = 34°
and then applied Mike's stackoverflow answer to find scale and translate values in order to center according to my map dimensions and above bounding box.
I tried some ogr2ogr
below but lacking time I resolved to use the data from Mike Bostock's World Atlas and reacheable via his TopoJSON Examples' gist
Countries in Europe (in bounding box):
ogr2ogr -f GeoJSON \
-where "SCALERANK = 0"
-spat -31.266001 27.636311 39.869301 81.008797 \
europe.json \
data/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp
And capitals:
ogr2ogr -f GeoJSON \
-where "FEATURECLA = 'Admin-0 capital'" \
-spat -31.266001 27.636311 39.869301 81.008797 \
data/ne_10m_populated_places/ne_10m_populated_places.shp
For now I stick with world list...
xxxxxxxxxx
<meta charset="utf-8">
<style>
.graticule {
fill: none;
stroke: #777;
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;
}
.country.eurocontrol {
fill: rgb(51,154,205);
}
.country-label {
fill: #fff;
fill-opacity: 1.0;
font-size: 20px;
font-weight: 300;
text-anchor: middle;
}
#loading {
text-align: center;
position: absolute;
padding-left: 200px;
padding-top: 100px;
height: 20px;
font-size: 30px;
}
#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: 14px;
}
#countryflag {
display: block;
max-width: 120px;
max-height: 100px;
width: auto;
height: auto;
}
#coordinates {
position: absolute;
text-align: left;
font-size: 12px;
font-family:Arial,Helvetica,sans-serif
}
.hidden{
display: none;
}
</style>
<body>
<div id="tooltip" class="hidden">
<p id="countryname"></p>
<p id="adhesiondate"></p>
<img id="countryflag">
</div>
<div id="map">
<div id="loading">Loading...</div>
</div>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://keithcirkel.co.uk/jwerty/jwerty.js"></script>
<script>
var width = 960,
height = 600;
// // ############### scaling and translating ###############
// from https://stackoverflow.com/questions/14492284/#answer-14691788
// var N = 71,
// E = -11,
// W = 35,
// S = 34,
// europe = { "type": "Polygon",
// "coordinates": [
// [
// [E, N],
// [W, N],
// [W, S],
// [E, S]
// ] ]
// };
// // find proper scale and translation from bounds of europe polygon
// var projection = d3.geo.azimuthalEqualArea().scale(1).translate([0,0]).clipAngle(180 - 1e-3).precision(0.1);
// var path = d3.geo.path().projection(projection);
// var b = path.bounds(europe),
// s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
// t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
// console.log("scale="+s);
// console.log("translate="+t);
// // #######################################################
var s = 987,
t = [262, 1187];
var projection = d3.geo.azimuthalEqualArea().scale(s).translate(t).clipAngle(180 - 1e-3).precision(1),
path = d3.geo.path().projection(projection);
var graticule = d3.geo.graticule();
var loading = d3.select("#loading");
var svg = d3.select("#map").append("svg")
.attr("width", width)
.attr("height", height);
var tooltip = d3.select("#tooltip").classed("hidden", true),
countryname = d3.select("#countryname"),
adhesiondate = d3.select("#adhesiondate"),
countryflag = d3.select("#countryflag"),
format = d3.format(" 2.2f");
svg.on("mousemove", function() {
// update tooltip position
tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");
return true;
});
var logo = svg.append("g");
logo.append("image")
.attr("xlink:href",
"https://upload.wikimedia.org/wikipedia/commons/b/b2/Eurocontrol_logo_2010.svg")
.attr("x", 40)
.attr("y", 160)
.attr("width", 100)
.attr("height", 100)
.attr("preserveAspectRatio", "xMinYMin");
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
queue()
.defer(d3.json, "/../../data/world-50m.json")
.defer(d3.tsv, "/../../data/world-country-names.tsv")
.defer(d3.tsv, "/espinielli/raw/5107491/world-country-flags.tsv")
.defer(d3.tsv, "eurocontrol_members.tsv")
.await(ready);
function ready(error, world, names, flags, members) {
if (error) return console.error(error);
loading.classed("hidden", true);
var land = topojson.feature(world, world.objects.land),
countries = topojson.feature(world, world.objects.countries).features,
borders = topojson.mesh(world, world.objects.countries,function(a, b) { return a.id !== b.id; }),
eurocontrol = d3.set();
for (var i = members.length - 1; i >= 0; i--) {
eurocontrol.add(members[i].id);
};
countries.forEach(function(d) {
names.some(function(n) {
if (d.id == n.id) return d.name = n.name;
});
});
countries.forEach(function(d) {
members.some(function(n) {
if (d.id == n.id) {
d.adhesion = n.date;
return d.iso2 = n.iso2;
}
});
});
countries.forEach(function(d) {
flags.some(function(n) {
if (d.id == n.id) return d.flag = n.url;
});
});
var country = svg.selectAll(".country")
.data(countries)
.enter().insert("path", ".graticule")
.attr("class", function(d) {return (eurocontrol.has(d.id)? "country eurocontrol": "country");})
.attr("d", path)
.text(function(d) { return d.id;})
.on("mouseover", function(d,i) {
d3.select(this).style({'stroke-opacity':1,'stroke':'#F00'});
// https://stackoverflow.com/questions/17917072/#answer-17917341
// d3.select(this.parentNode.appendChild(this)).style({'stroke-opacity':1,'stroke':'#F00'});
if (eurocontrol.has(d.id)) {
tooltip.classed("hidden", false);
countryname.text(d.name);
adhesiondate.text("(" + d.adhesion + ")");
countryflag.attr("src", d.flag);
}
})
.on("mouseout", function(d) {
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.");
});;
// add ISO2 label
// svg.selectAll(".eurocontrol")
// .append("text")
// .attr("class", function(d) { return "country-label " + d.iso2; })
// .attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
// .attr("z-index", 1000)
// .attr("dy", ".35em")
// .text(function(d) { return d.iso2;});
svg.insert("path", ".graticule")
.datum(land)
.attr("class", "land")
.attr("d", path);
svg.insert("path", ".graticule")
.datum(borders)
.attr("class", "boundary")
.attr("d", path);
};
d3.select(self.frameElement).style("height", height + "px");
</script>
Modified http://d3js.org/d3.v3.min.js to a secure url
Modified http://d3js.org/queue.v1.min.js to a secure url
Modified http://d3js.org/topojson.v1.min.js to a secure url
Modified http://keithcirkel.co.uk/jwerty/jwerty.js to a secure url
Changed /mbostock/raw/4090846/world-country-names.tsv to a local referenece
Changed /mbostock/raw/4090846/world-50m.json to a local referenece
https://d3js.org/d3.v3.min.js
https://d3js.org/queue.v1.min.js
https://d3js.org/topojson.v1.min.js
https://keithcirkel.co.uk/jwerty/jwerty.js