Skip to content

Instantly share code, notes, and snippets.

@radbrt
Last active September 27, 2015 21:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save radbrt/e7728d5d94a77ccb6513 to your computer and use it in GitHub Desktop.
Save radbrt/e7728d5d94a77ccb6513 to your computer and use it in GitHub Desktop.
Earnings by occupation

#European earnings by occupation

This map shows earnings data for 2010. Choose a country to see average earnings for different occupational groups. Choose between nominal Euros or purchasing-power adjusted figures.

The selected country has to be re-clicked to update after switching between PPS and Euro. The occupational titles are unfortunately way to long to display below the bars, but they do appear upon hovering the bar for a second.

Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Diverse</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="json-stat.js" ></script>
<style type="text/css">
body {
background-color: #fff;
font-family: "Helvetica Neue", "sans-serif";
font-size: 11px;
}
svg.mainFrame {
background-color: #6baed6;
}
svg path {
stroke: #4499aa;
stroke-width: 0.5px;
}
svg path:hover {
fill: orange;
cursor: pointer;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: "Helvetica Neue", "sans-serif";
font-size: 11px;
}
#tooltip {
position: absolute;
top: 15px;
left: 520px;
/*z-index: 10;*/
padding: 5px;
width: 420px;
height: 250px;
color: #CC5D0E;
font-family: sans-serif;
font-size: 12px;
font-weight: bold;
text-align: center;
background-color: rgba(100, 110, 110, 0.75);
opacity: 0;
/*pointer-events: none;*/
}
.xlabel {
text-align: center;
}
.bar {
text-align: center;
}
#tooltip.visible {
opacity: 1;
}
.hoverthing {
font-size: 12px;
stroke: 1px #222;
color: #222;
}
</style>
</head>
<body>
<div id="tooltip"></div>
<form id="radio">
<input type="radio" name="group-stack" value="eur" checked>EUR<br>
<input type="radio" name="group-stack" value="pps">PPS
</form>
<script>
var bh = 250;
var bw = 420;
var bp = {t: 10, r: 50, b: 15, l: 10 };
var map;
var nFormat = d3.format(",");
var w = 960;
var h = 500;
d3.select("#radio")
.style("position", "absolute")
.style("left", "900px")
.style("top", "460px");
var projection = d3.geo.equirectangular()
.center([29, 52])
.translate([ w/2, h/2 ])
.scale([ 650 ])
;
var path = d3.geo.path()
.projection(projection);
d3.json("europe3.json", function(json) {
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("class", "mainFrame")
;
JSONstat("http://ec.europa.eu/eurostat/wdds/rest/data/v1.1/json/en/earn_ses_monthly?nace_r2=B-S_X_O&indic_se=ERN_MN_EUR&indic_se=ERN_MN_PPS&sex=T&precision=0&isco08=OC1&isco08=OC2&isco08=OC3&isco08=OC4&isco08=OC5&isco08=OC6&isco08=OC7&isco08=OC8&isco08=OC9&isco08=TOTAL&lastTimePeriod=1&age=TOTAL&groupedIndicators=1&worktime=FT", function() {
dt=this.Dataset(0).toTable({ type : "arrobj", content : "id" })
var ocids = this.Dataset(0).Dimension("isco08").id;
var o = [];
for (i=0; i<ocids.length; i++) {
o[ocids[i]]=this.Dataset(0).Dimension("isco08").Category(ocids[i]).label;
}
dt2=d3.nest()
.key(function(d) { return d.geo; })
.entries(dt);
for (i=0; i<dt2.length; i++) {
for (j=0; j<json.features.length; j++) {
if(json.features[j].id==dt2[i].key) {
json.features[j].ERN_MN_PPS=dt2[i].values.filter(function(d) { return d.indic_se=="ERN_MN_PPS"; }).pop().value;
json.features[j].ERN_MN_EUR=dt2[i].values.filter(function(d) { return d.indic_se=="ERN_MN_EUR"; }).pop().value;
json.features[j].pps=dt2[i].values.filter(function(d) { return d.indic_se=="ERN_MN_PPS"; }).slice(0,-1);
json.features[j].eur=dt2[i].values.filter(function(d) { return d.indic_se=="ERN_MN_EUR"; }).slice(0,-1);
}
}
}
var color=d3.scale.quantize()
.domain([
// d.values[d.values.length-1] pattern to get latest element, total (if correctly sorted...)
d3.min(dt2, function(d) { return d.values[d.values.length-1].value; }),
d3.max(dt2, function(d) { return d.values[d.values.length-1].value; })
])
.range(["#c7e9c0", "#a1d99b", "#74c476", "#41ab5d", "#238b45", "#005a32"]);
var groups = svg.selectAll("g")
.data(json.features)
.enter()
.append("g")
.attr("class", "gr");
var map = groups.append("path")
.attr("d", path)
.attr("class", "euCountries")
.attr("fill", function(d) {
if(d.eur) {
return color(d.ERN_MN_EUR);
} else {
return "#ccc";
}
});
//OK, this gets convoluted... to be improved. Needed to nest through countries and years.
var yScale = d3.scale.linear()
.domain([
0,
d3.max(dt2, function(d) {
return d3.max(d.values, function(d) { return d.value; }); })
])
.range([(bh-bp.b), bp.b]);
var xScale = d3.scale.ordinal()
.domain(d3.range(dt2[0].values.filter(function(d) { return d.indic_se=="ERN_MN_EUR" && d.isco08!='TOTAL'; }).length))
.rangeRoundBands([ bp.r, bw - bp.r ], 0.05);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("right")
;
var xAxis = d3.svg.axis()
.scale(xScale)
.ticks(10)
.tickFormat(function(i) { return o[i]; })
.orient("bottom");
var d0 = d3.select("#tooltip");
var divtip = d0
.append("svg")
.attr("width", bw)
.attr("height", bh)
;
var barGroups = divtip.selectAll("g")
.data(json.features[0].pps)
.enter()
.append("g")
.attr("class", "bar")
.attr("width", xScale.rangeBand());
// var divHover = d0.append("div")
// .attr("class", "hoverthing")
// .style("position", "absolute")
// .style("width", "200px")
// .style("height", "50px")
// .style("background-color", "#ddd")
// .style("opacity", 0)
// //.append("text")
// .text("")
// ;
var bar = barGroups
.append("rect")
.attr("class", "barGroups")
.style("fill", "#cc9900")
.attr("width", xScale.rangeBand() )
.attr("y", function(d) { return bh; })
.attr("height", function(d) { return 0; })
.attr("title", function(d) {
return o[d.isco08];
})
// .on("mousemove", function(d) {
// var cx = d3.mouse(this)[0];
// var cy = d3.mouse(this)[1];
// console.log(cy, cx);
// divHover
// .style("top", (cy-50))
// .style("left", (cx+20))
// .text("Hello World")
// .style("opacity", 1);
// // console.log("mouseovered");
// })
// .on("mouseout", function() {
// divHover.style("opacity", 0).text("");
// })
;
var yLine = divtip.append("g")
.attr("class", "y axis")
.call(yAxis)
.attr("transform", "translate(" + (bw-bp.r) + ", 0)");
yLine.append("text").text("Mean monthly earnings (€)")
.attr("transform", "rotate(-90), translate(-" + (bh/2) + ",50)")
.attr("text-anchor", "middle")
;
barGroups.append("text")
.attr("class", "xlabel")
.attr("x", function(d, i) { return xScale(i) + (xScale.rangeBand()/2); })
.attr("y", bh)
.attr("width", xScale.rangeBand() )
.text(function(d) { return d.isco08; })
.attr("text-anchor", "middle")
;
// barGroups.append("g")
// .call(xAxis)
// .attr("class", "x axis")
// .attr("transform", "translate(" + 0 + " , " + (bh - bp.b) + ")")
// .style("text-anchor", "middle")
// .attr("stroke", "none")
// //.attr("transform", function (d) { return "rotate(-10), translate(-100,140)"; });
// ;
var title = d0.append("div")
.attr("class", "xBoxClass")
.style("position", "absolute")
.style("color", "#333")
.style("top", "5px")
.style("left", "5px")
.style("font-size", "20px");
d3.select("#radio").on("change", function() {
if (d3.select('input[name="group-stack"]:checked').node().value=='eur'){
map
.transition()
.duration(500)
.delay(300)
.attr("fill", function(d) {
if(d.eur) {
return color(d.ERN_MN_EUR);
} else {
return "#ccc";
}
})
} else {
map
.transition()
.duration(500)
.delay(300)
.attr("fill", function(d) {
if(d.pps) {
return color(d.ERN_MN_PPS);
} else { return "#ccc"; }
})
}
d3.select("#tooltip")
.transition()
.duration(300)
.style("opacity", 0.5)
;
});
map.on("click", function(d) {
if (d3.select('input[name="group-stack"]:checked').node().value=='eur' && d.eur) {
// ONLY UPDATE IF HAZ DATA
d3.select("#tooltip")
.transition()
.duration(300)
.style("opacity", 1);
title.text(d.properties.name);
bar.data(d.eur)
.transition()
.duration(500)
.delay(function(d, i) { return i*100; })
.attr("y", function(d) { return yScale(d.value); })
.attr("x", function(d, i) { return xScale(i); })
.attr("height", function(d) { return (bh-bp.b)-yScale(d.value); });
} else if (d3.select('input[name="group-stack"]:checked').node().value=='pps' && d.pps) {
d3.select("#tooltip")
.transition()
.duration(300)
.style("opacity", 1);
title.text(d.properties.name);
bar.data(d.pps)
.transition()
.duration(500)
.delay(function(d, i) { return i*100; })
.attr("y", function(d) { return yScale(d.value); })
.attr("x", function(d, i) { return xScale(i); })
.attr("height", function(d) { return (bh-bp.b)-yScale(d.value); });
}
});
});
});
</script>
</body>
</html>
/*
JSON-stat Javascript Toolkit v. 0.8.3
http://json-stat.org
https://github.com/badosa/JSON-stat
Copyright 2015 Xavier Badosa (http://xavierbadosa.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
*/
function JSONstat(t,e){return window===this?new JSONstat.jsonstat(t,e):void 0}var JSONstat=JSONstat||{}
JSONstat.version="0.8.3",function(){"use strict"
function t(t){return"[object Array]"===Object.prototype.toString.call(t)}function e(e,n){var i,s,r,l,a=function(t,e){var n,i,s=e!==!1
if(window.XDomainRequest&&/^(http(s)?:)?\/\//.test(t)){if(!s)return
i=new XDomainRequest,i.onload=function(){n=JSON.parse(i.responseText),e.call(JSONstat(n))},i.open("GET",t),i.send()}else if(i=new XMLHttpRequest,i.onreadystatechange=function(){if(4===i.readyState){var t=i.status
n=t&&i.responseText&&(t>=200&&300>t||304===t)?JSON.parse(i.responseText):null,s&&e.call(JSONstat(n))}},i.open("GET",t,s),i.send(null),!s)return n},o=function(e,n){var i,s=[]
if("string"==typeof e&&(e=[e]),t(e)){if(e.length===n)return e
if(1===e.length){for(i=0;n>i;i++)s.push(e[0])
return s}}for(i=0;n>i;i++){var r=void 0===e[i]?null:e[i]
s.push(r)}return s}
if(this.length=0,this.id=[],null!==e&&void 0!==e)switch(this["class"]=e["class"]||"bundle",this["class"]){case"bundle":var u=[],h=0
if(this.error=null,this.length=0,"string"==typeof e&&e.length>0&&(e=a(e,"function"==typeof n?n:!1)),null===e||"object"!=typeof e)return this["class"]=null,void(void 0===e&&(delete this.id,delete this["class"],delete this.error,delete this.length))
if(e.hasOwnProperty("error"))return void(this.error=e.error)
if("dataset"===e["class"]||"collection"===e["class"])return JSONstat(e)
for(s in e)h++,u.push(s)
this.__tree__=e,this.length=h,this.id=u
break
case"dataset":e.hasOwnProperty("__tree__")||(delete e["class"],e={__tree__:e}),this.__tree__=i=e.__tree__,this.label=i.label||null,this.note=i.note||null,this.link=i.link||null,this.href=i.href||null,this.updated=i.updated||null,this.source=i.source||null,this.extension=i.extension||null
var f,c=0
if(i.hasOwnProperty("value")&&t(i.value))c=i.value.length
else if(i.hasOwnProperty("status")&&t(i.status))c=i.status.length
else if(i.hasOwnProperty("dimension")){var d=i.dimension.size,p=1
for(f=d.length;f--;)p*=d[f]
c=p}if(this.value=o(i.value,c),this.status=i.hasOwnProperty("status")?o(i.status,c):null,i.hasOwnProperty("dimension")){if(!t(i.dimension.id)||!t(i.dimension.size)||i.dimension.id.length!=i.dimension.size.length)return
var v=i.dimension,g=v.role||null,y=v.id,_=v.size.length,b=function(t){g.hasOwnProperty(t)||(g[t]=null)}
if(this.length=_,this.id=y,g&&(b("time"),b("geo"),b("metric"),b("classification")),g&&null===g.classification){var m=[],w=["time","geo","metric"],O=function(t,e){for(var n=e.length;n--;)if(t===e[n])return!0
return!1}
for(f=0;3>f;f++){var x=g[w[f]]
null!==x&&(m=m.concat(x))}for(g.classification=[],f=0;_>f;f++)O(y[f],m)||g.classification.push(y[f])
0===g.classification.length&&(g.classification=null)}this.role=g,this.n=c
for(var S=0,k=this.length;k>S;S++)if(v[v.id[S]].category.hasOwnProperty("index")){if(t(v[v.id[S]].category.index)){var P={},D=v[v.id[S]].category.index
for(r=D.length,l=0;r>l;l++)P[D[l]]=l
v[v.id[S]].category.index=P}}else{var j=0
v[v.id[S]].category.index={}
for(s in v[v.id[S]].category.label)v[v.id[S]].category.index[s]=j++}}else this.length=0
break
case"dimension":i=e.__tree__
var J=[],N=i.category
if(!e.hasOwnProperty("__tree__")||!i.hasOwnProperty("category"))return
if(!N.hasOwnProperty("label")){N.label={}
for(s in N.index)N.label[s]=s}for(s in N.index)J[N.index[s]]=s
this.__tree__=i,this.label=i.label||null,this.note=i.note||null,this.link=i.link||null,this.href=i.href||null,this.id=J,this.length=J.length,this.role=e.role,this.hierarchy=N.hasOwnProperty("child"),this.extension=i.extension||null
break
case"category":var T=e.child
this.id=T,this.length=null===T?0:T.length,this.index=e.index,this.label=e.label,this.note=e.note||null,this.unit=e.unit,this.coordinates=e.coord
break
case"collection":if(this.length=0,this.label=e.label||null,this.note=e.note||null,this.link=e.link||null,this.href=e.href||null,this.updated=e.updated||null,this.source=e.source||null,this.extension=e.extension||null,null!==this.link&&e.link.item){var z=e.link.item
if(this.length=t(z)?z.length:0,this.length)for(l=0;l<this.length;l++)this.id[l]=z[l].href}}}e.prototype.Item=function(t){if(null===this||"collection"!==this["class"]||!this.length)return null
if("number"==typeof t)return t>this.length||0>t?null:this.link.item[t]
var e,n=[]
if("object"==typeof t){if(!t["class"]&&!t.follow)return null
t["class"]?e=function(t,e,i){i["class"]===t.link.item[e]["class"]&&n.push(t.link.item[e])}:t.follow&&(e=function(t,e){n.push(JSONstat(t.id[e]))})}else e=function(t,e){n.push(t.link.item[e])}
for(var i=0;i<this.length;i++)e(this,i,t)
return n},e.prototype.Dataset=function(t){if(null===this)return null
if("dataset"===this["class"])return void 0!==t?this:[this]
if("bundle"!==this["class"])return null
if(void 0===t){for(var n=[],i=0,s=this.id.length;s>i;i++)n.push(this.Dataset(this.id[i]))
return n}if("number"==typeof t){var r=this.id[t]
return void 0!==r?this.Dataset(r):null}var l=this.__tree__[t]
return void 0===l?null:new e({"class":"dataset",__tree__:l})},e.prototype.Dimension=function(t){var n,i=[],s=this.id.length,r=function(t,e){var n=t.role
if(null!==n)for(var i in n)for(var s=null!==n[i]?n[i].length:0;s--;)if(n[i][s]===e)return i
return null}
if(null===this||"dataset"!==this["class"])return null
if(void 0===t){for(n=0;s>n;n++)i.push(this.Dimension(this.id[n]))
return i}if("number"==typeof t){var l=this.id[t]
return void 0!==l?this.Dimension(l):null}var a=this.__tree__.dimension
if(void 0===a)return null
if("object"==typeof t){if(t.hasOwnProperty("role")){for(n=0;s>n;n++){var o=this.id[n]
r(a,o)===t.role&&i.push(this.Dimension(o))}return void 0===i[0]?null:i}return null}var u=a[t]
return void 0===u?null:new e({"class":"dimension",__tree__:u,role:r(a,t)})},e.prototype.Category=function(t){if(null===this||"dimension"!==this["class"])return null
if(void 0===t){for(var n=[],i=0,s=this.id.length;s>i;i++)n.push(this.Category(this.id[i]))
return n}if("number"==typeof t){var r=this.id[t]
return void 0!==r?this.Category(r):null}var l=this.__tree__.category
if(void 0===l)return null
var a=l.index[t]
if(void 0===a)return null
var o=l.unit&&l.unit[t]||null,u=l.coordinates&&l.coordinates[t]||null,h=l.child&&l.child[t]||null,f=l.note&&l.note[t]||null
return new e({"class":"category",index:a,label:l.label[t],note:f,child:h,unit:o,coord:u})},e.prototype.Data=function(e){var n,i,s=[],r=function(t){for(var e in t)if(t.hasOwnProperty(e))return e},l=function(t,e){for(var n=[],i=t.dimension,s=i.id,l=0,a=s.length;a>l;l++){var o=s[l],u=e[o]
n.push("string"==typeof u?u:1===i.size[l]?r(i[o].category.index):null)}return n}
if(null===this||"dataset"!==this["class"])return null
if(void 0===e){for(i=this.value.length,n=0;i>n;n++)s.push(this.Data(n))
return s}if("number"==typeof e){var a=this.value[e]
return void 0!==a?{value:a,status:this.status?this.status[e]:null}:null}var o=this.__tree__,u=o.dimension.size,h=u.length
if(t(e)){if(this.length!==e.length)return null
var f=1,c=0,d=[],p=[]
for(n=0;h>n;n++)if(void 0!==e[n]){if("number"!=typeof e[n]||e[n]>=u[n])return null
f*=n>0?u[h-n]:1,c+=f*e[h-n-1]}else d.push(n),p.push(u[n])
if(d.length>1)return null
if(1===d.length){for(var v=0,g=p[0];g>v;v++){var y=[]
for(n=0;h>n;n++)y.push(n!==d[0]?e[n]:v)
s.push(this.Data(y))}return s}return{value:this.value[c],status:this.status?this.status[c]:null}}var _=l(o,e),b=[],m=o.dimension
for(i=_.length,n=0;i>n;n++)b.push(m[m.id[n]].category.index[_[n]])
return this.Data(b)},e.prototype.toTable=function(t,e){if(null===this||"dataset"!==this["class"])return null
1==arguments.length&&"function"==typeof t&&(e=t,t=null)
var n,i,s,r,l,a=this.__tree__
if(t=t||{field:"label",content:"label",vlabel:"Value",slabel:"Status",type:"array",status:!1},"function"==typeof e){n=this.toTable(t)
var o=[],u="array"!==t.type?0:1,h="object"!==t.type?n.slice(u):n.rows.slice(0)
for(l=h.length,i=0;l>i;i++){var f=e.call(this,h[i],i)
void 0!==f&&o.push(f)}return"object"===t.type?{cols:n.cols,rows:o}:("array"===t.type&&o.unshift(n[0]),o)}if("arrobj"===t.type){n=this.toTable({field:"id",content:t.content,status:t.status})
var c=[],d=n.shift()
for(l=n.length,i=0;l>i;i++){var p={}
for(s=n[i].length;s--;)p[d[s]]=n[i][s]
c.push(p)}return c}var v,g,y,_,b="id"===t.field
if("object"===t.type){var m="number"==typeof this.value[0]||null===this.value[0]?"number":"string"
v=function(t,e){var n=b&&t||e||t
z.push({id:t,label:n,type:"string"})},g=function(t,e,n){var i=b&&"value"||t||"Value",s=b&&"status"||e||"Status"
n&&z.push({id:"status",label:s,type:"string"}),z.push({id:"value",label:i,type:m})},y=function(t){B.push({v:t})},_=function(t){B.push({v:t}),V.push({c:B})}}else v=function(t,e){var n=b&&t||e||t
z.push(n)},g=function(t,e,n){var i=b&&"value"||t||"Value",s=b&&"status"||e||"Status"
n&&z.push(s),z.push(i),T.push(z)},y=function(t){B.push(t)},_=function(t){B.push(t),T.push(B)}
var w=a.dimension,O=w.id,x=O.length,S=w.size
if(x!=S.length)return!1
var k=[],P=1,D=1,j=[],J=[],N=[],T=[],z=[],V=[]
for(i=0;x>i;i++){var q=O[i],C=w[q].label
v(q,C),P*=S[i],D*=S[i]
var R=[]
for(s=0;s<S[i];s++)for(var X in w[O[i]].category.index)if(w[O[i]].category.index[X]===s){var E="id"!==t.content&&w[O[i]].category.label?w[O[i]].category.label[X]:X
R.push(E)}k.push(R),j.push(D)}for(g(t.vlabel,t.slabel,t.status),l=k.length,i=0;l>i;i++){for(var G=[],A=0,H=k[i].length;H>A;A++)for(var I=0;I<P/j[i];I++)G.push(k[i][A])
J.push(G)}for(l=J.length,i=0;l>i;i++){var L=[],M=0
for(r=0;P>r;r++)L.push(J[i][M]),M++,M===J[i].length&&(M=0)
N.push(L)}for(r=0;P>r;r++){var B=[]
l=J.length
for(var F=0;l>F;F++)y(N[F][r])
t.status&&y(this.status?this.status[r]:null),_(this.value[r])}return"object"===t.type?{cols:z,rows:V}:T},e.prototype.node=function(){return this.__tree__},e.prototype.toString=function(){return this["class"]},e.prototype.toValue=function(){return this.length},JSONstat.jsonstat=e}()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment