d3_map.css
.hero-image {
width: 910px;
font-style: "italic"
}
.hero-image #content {
width: 910px;
padding-left:0px;
}
#extra-content{
float:left;
width:680px;
padding:0px 0px 0px 25px;
}
.background {
fill: none;
pointer-events: all;
}
.country {
fill: #ccc;
stroke: #bbb;
stroke-width: .5px;
stroke-linejoin: round;
}
path.inactive.country {
fill-opacity: 0.3;
transition-duration: 0.25s;
}
path.inactive.country:hover {
fill-opacity: 0.75;
transition-duration: 0.25s;
}
path.active.country {
transition-duration: 0.25s;
}
#container .controls{
background-color: #fff;
}
.hidden {
display: none;
}
div.tooltip {
color: #222;
background: #fff;
padding: .5em;
text-shadow: #f5f5f5 0 1px 0;
border-radius: 2px;
box-shadow: 0px 0px 2px 0px #a6a6a6;
opacity: 0.95;
position: absolute;
z-index: 999;
}
.map-main #container {
border: 1px solid #ddd;
}
div.map-header {
height: 24px;
width: 100%;
background-color:#ccc;
padding-top:0.5em
}
div.map-header h3 {
color: #000;
margin: 0;
font-size: 0.95em;
padding-left: 8px;
padding-top: 1px;
line-height: 1.4em;
font-weight: normal;
float: left;
display: block;
height: 23px;
font-family: Arial, Helvetica, sans-serif;
}
div.map-header h3.menu {
height: 23px;
padding-right: 10px;
float: right;
}
div.map-header h3.menu span.menu-item {
padding-left: 8px;
padding-right: 8px;
display: block;
float: right;
}
div.map-header h3.menu span.menu-item a {
color: #5c5c5c;
}
div.map-header h3.menu span.selected {
height: 24px;
color:#fff;
}
div.map-key {
background-color: white;
padding: 2px 3px 3px 5px;
padding: 3px 3px 3px 15px;
border-bottom: 1px solid #DFDFDF;
}
div.map-key div.rhs {
float: right;
width: 150px;
padding-top: 1em;
}
#content div.map-key div.rhs p {
font-size: 80%;
text-align: right !important;
}
div.map-key div.rhs p {
text-align: right;
margin-right: 37px;
font-size: 75%;
line-height: 1em;
margin-top: 3px;
font-style: italic;
}
div.map-key div.rhs img {
float: right;
}
div.map-key div.range {
float: left;
height: 10px;
}
div.map-key div.range-label {
float: left;
width: 60px;
font-size: 0.7em;
line-height: 0.9em;
padding-top: 0.2em;
}
div.map-key div.range.first {
width: 64px;
}
div.map-key div.range-labels {
clear: left;
width: 400px;
height: 20px;
}
div.map-key div.title {
font-size: 75%;
font-weight: bold;
line-height: 1.6em;
padding-top: 1em;
}
div.map-key div.title span.subtitle {
font-weight: normal;
font-style: italic;
}
div.map-main {
background: #EEEEEE;
}
div.map-data-content {
padding: 12px 0;
border-top: 5px solid #DFDFDF;
}
.map-data-content > p{
padding-left:1em;
padding-right:1em;
}
.map-data-content .graphs{
margin-bottom: 1em;
margin-top: 1em;
margin-left:1em;
}
.map-data-content .highchart-chart{
width:552px;
border: 1px solid #e1e1e1;
margin-top:1em;
}
.country-jump {
background-color: #fff;
padding-top: 1em;
padding-bottom: 1em;
padding-left: 0;
}
.country-jump li{
padding: 1em;
margin: 4px;
font-size: 14px;
border-left: 1px solid #ddd;
background-color: #fff;
list-style: none;
cursor: pointer;
display:inline;
color: red;
text-decoration: underline;
}
.country-jump li:hover{
background-color: #ddd;
}
.country-jump li.hint{
border:none;
background-color: #fff;
cursor: default;
color: #111;
text-decoration: none;
font-family: 'AvenirLT-Roman';
}
.country-jump li.intro:hover{
display: inline;
padding:2em;
}
div.map-data-content h4{
font-family: 'AvenirLT-Roman', Arial, Helvetica, sans-serif;
font-size: 16px;
font-weight:normal;
}
div.map-data-content h3{
font-family: 'AvenirLT-Roman', Arial, Helvetica, sans-serif;
font-size: 18px;
font-weight:normal;
}
div.map-data-content ul li{
padding:0.3em;
}
div#map-data-area table {
border-collapse: separate;
border: 1px solid gray;
font-size: 95%;
width:90%;
margin-left:10px;
margin-bottom: 1em;
margin-top: 1em;
}
div#map-data-area table td,
div#map-data-area table th {
padding: 7px;
border: 1px solid gray;
}
div#map-data-area table th {
min-width: 10em;
background-color:#444;
color:#eee;
border: 1px solid gray;
}
div#map-data-area table tr.even td {
background-color:#eee;
}
div#map-data-area table td.intervention-link,
div#map-data-area table td.intervention-description{
font-family: 'AvenirLT-Roman';
}
div.notabene{
margin-left: 1em;
}
.notabene p,
div.map-data-content div.notabene p{
font-size: 14px;
}
div.definitions {
padding-top:1em;
padding-bottom:1em;
background-color: #f9f9f9;
}
div.definitions h4{
margin-top:0;
padding-left: 1em;
padding-right: 1em;
}
div.definitions p {
font-style: italic;
font-size: 95%;
margin-top: 1em;
line-height: 1.4em;
padding-left: 1em;
padding-right: 1em;
}
#content div.map-data-content h1,
#content div.map-data-content h2,
#content div.map-data-content h3,
#content div.map-data-content h4 {
margin-left:1em;
}
#content div.map-data-content .back-to-top{
font-size: 0.9em;
color: red;
text-decoration: underline;
float:right;
margin-right:3em;
margin-top:1.5em;
}
div.self-reported{
border-top: 1px solid rgb(227, 118, 90);
border-bottom: 1px solid rgb(227, 118, 90);
background-color: rgb(255, 234, 234);
width:560px;
margin-left:1em;
margin-bottom: 0.5em;
}
#content div.self-reported p{
margin: 0;
padding: 0.5em;
margin
}
div.self-reported:hover{
cursor: help;
}
div.self-reported .explanation{
display:none;
}
div.self-reported .explanation p{
font-size: 13px;
}
#content div.map-data-content .back_to_policy_map{
margin-left:1em;
font-size: 14px;
}
div.map-data-content .main_meta {
background-color: rgba(23, 176, 185, 0.8);
color: white;
padding: 0.5em 0em;
}
#content div.map-data-content .main_meta a{
color:white;
}
div.map-data-content .description,
div.map-data-content .reference,
div.map-data-content .disclaimer,
div.map-data-content .country-filter-status {
padding: 0.5em 0.5em;
overflow:auto;
}
div.map-data-content .reference{
background-color: rgb(200, 200, 200);
border-top: 1px solid #ddd;
}
div.map-data-content .disclaimer {
border-top: 1px solid #ddd;
}
div.map-data-content p{
font-family: 'AvenirLT-Light';
font-size: 16px;
}
p.image-graph-caption{
font-size: 0.7em;
font-style: italic;
padding-left:1em;
}
div.map-data-content .graphs img{
margin: 1em 1em 1em 0;
}
#content div.map-data-content .main_meta h3,
#content div.map-data-content .disclaimer h3,
#content div.map-data-content .description h3{
border-bottom: 3px solid #DFDFDF;
padding-left: 0;
margin-left: 0;
}
div.map-data-country{
border-bottom: 3px solid #DFDFDF;
border-left: 1px solid #e1e1e1;
border-right: 1px solid #e1e1e1;
}
div.map-data-country:nth-child(odd){
background-color: #fff;
}
div.map-data-country:nth-child(even){
background-color: #eFeFeF;
}
div.main_meta span.definition{
font-family: 'AvenirLT-Heavy';
margin-right: 1em;
width: 150px;
display:inline-block;
vertical-align: top;
}
div.main_meta span.definition-content{
width: 350px;
display:inline-block;
vertical-align: top;
overflow: hidden;
}
#content .import-from-spotlight,
#content .disclaimer p{
font-family: 'AvenirLT-LightOblique';
font-style: italic;
font-size:14px;
}
div.main_meta ul{
padding-left: 1em;
}
div.main_meta ul > li{
list-style: none;
font-family: AvenirLT-Light;
}
div.map-data-content thead {
background-color: #111;
color: white;
}
ul.policy-kinds .active{
font-family: 'AvenirLT-Heavy';
}
.map-data-content div.no-policy-interventions {
width: 90%;
border: 1px solid #ddd;
margin-left: 10px;
margin-top: 20px;
padding-top:2em;
padding-bottom:2em;
}
#content .map-data-content .no-policy-interventions h3{
margin-left: 4em;
color: #aaa;
}
#content .map-data-content .no-policy-interventions p{
margin-left: 4em;
color: #aaa;
}
d3_map.js
var problemCountries = [];
var goodcountries = [];
var nohoverCountries = [];
var WOF = WOF || {};
WOF.filterDetailPanel = function() {
WOF.tidyUpDefinitions();
if ($('.map-data-country').length > 1){
WOF.addCountryJumpLinks();
}
};
WOF.tidyUpDefinitions = function() {
var obesityDefinitions = $('div.definitions').first();
$('div.definitions').remove();
$("#content").append(obesityDefinitions);
}
WOF.addCountryJumpLinks = function() {
var jumper = $("<ul>").addClass("country-jump").prependTo($('#map-data-area'));
jumper.append("<li class='hint'>Click a country to jump to:</li>")
$('.map-data-country').each(function(index) {
var countryName = $(this).attr('data-country');
var scrollTop = $(this).offset().top - 10;
var item = $("<li>").text(countryName);
console.log("scrollTop for " + countryName + ": " + scrollTop)
$(item).click(function() {
jqscrollTo(scrollTop)
});
jumper.append(item);
});
$('.map-data-country h2').each(function (index) {
var topLink = $("<div>")
.text("(back to top)")
.addClass('back-to-top');
$(topLink).click(function () {
jqscrollTo(jumper.offset().top - 400);
})
$(this).before(topLink);
});
}
function jqscrollTo(scrollTop) {
log.debug(scrollTop)
$('html,body').animate({
scrollTop: scrollTop
});
}
var topo, projection, svg, g, country_info, width, height, tooltip,
ranges, clicked_country, zoom, zoomLevel, zoomTouch, data;
jQuery(document, WOF).ready(function($) {
width = $('#container').attr('offsetWidth');
height = width / 2;
active = d3.select(null);
var tooltip = d3.select("#container")
.append("div")
.attr("class", "tooltip hidden");
var tooltipOffsetLeft = $("#container")
.attr('offsetLeft') + 40;
var tooltipOffsetTop = $("#container")
.attr('offsetTop') + 20;
var control = d3.select("#container")
.append("div")
.attr("class", "controls")
.html("<button>Reset zoom</button>")
.on('click', reset);
control.style({
'top': $("#container").attr('offsetTop') + (height * 0.9) + 'px',
'left': $("#container").attr('offsetLeft') + 10 + 'px',
'position': 'absolute',
});
function setup(width, height) {
projection = d3.geo.robinson()
.scale(155)
.translate([width / 2, height / 1.7]);
path = d3.geo.path()
.projection(projection);
zoom = d3.behavior.zoom()
.translate([0, 0])
.scale(1)
.scaleExtent([1, 8])
.on("zoom", zoomed);
svg = d3.select("#container")
.append("svg")
.attr("width", width)
.attr("height", height)
.attr('overflow', 'hidden')
.on('click', stopped, true);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", reset);
svg.call(zoom);
if ($('#container').attr('data-map-centre')) {
startWithZoomedMap();
}
g = svg.append("g");
WOF.g = g;
}
function ready(error, world, country_data) {
var countries = topojson.feature(world, world.objects.countries).features;
for (var j = 0; j < countries.length; j++) {
country_values = [
"3 letter code: " + countries[j].id,
"2 letter code from IASO: " + countries[j].properties.id,
"name: " + countries[j].properties.name
]
if (!countries[j].properties.id) {
problemCountries.push(countries[j]);
}
if (countries[j].properties.id) {
goodcountries.push(countries[j]);
}
if (!countries[j].properties.hover) {
nohoverCountries.push(countries[j]);
}
}
WOF.allCountryData = country_data;
WOF.allCountryFeatures = countries;
WOF.draw = draw;
draw();
$('#map-data-container').appendTo('#extra-content');
$('#map-data-container').append("<div class='map-data-content'><h3 class='instructions'> Select a country above for further details</h3></div>");
if(window.location.hash) {
var countryId = window.location.hash.split('=')[1]
WOF.selectCountry(countryId);
}
}
WOF.findCountry = function(countryId) {
return _.find( WOF.annotatedCountries, function(country) {
return country.id == countryId;
});
}
WOF.resetMap = function(fillValueCallback) {
WOF.g.html('');
WOF.draw(fillValueCallback);
WOF.triggerZoom();
}
WOF.triggerZoom = function() {
zoom.event(svg);
}
function draw(fillValueCallback) {
ranges = buildRanges();
var countries = WOF.annotatedCountries = annotateTopography(WOF.allCountryFeatures, WOF.allCountryData, fillValueCallback);
var country_group = g.append('g')
.attr("id", countries)
.selectAll(".country")
.data(countries)
country_group.enter()
.insert("path")
.attr("class", "country")
.attr("d", path)
.attr("id", function(d, i) {
return d.id;
})
.style('fill', function(d) {
var col = fillBasedOnRange(d.properties.name, d.properties.data);
return col;
})
.call(zoom)
country_group
.on("mouseover", hovered)
.on("mouseout", function(d, i) {
tooltip.classed("hidden", true)
})
.on('click', clicked)
.on('touchstart', logTouchCoordsStart)
.on('touchstart.zoom', null)
.on('touchend', logTouchCoordsEnd)
WOF.setActiveClasses();
}
WOF.setActiveClasses = function() {
if(WOF.activeCountry) {
var selector = '#' + WOF.activeCountry;
d3.selectAll('svg .country').classed("inactive", true);
d3.select(selector).classed("inactive", false);
d3.select(selector).classed("active", true);
} else {
d3.selectAll('svg .country').classed("inactive", false);
d3.selectAll('svg .country').classed("active", false);
}
};
function annotateTopography(topoCountries, countryData, fillValueCallback) {
var countries = topoCountries;
for (var i = 0; i < countryData.length; i++) {
var cd = countryData[i]
for (var c = 0; c < countries.length; c++) {
if (cd.iso3166_a3 == countries[c].id) {
var data;
if(typeof fillValueCallback == 'function') {
data = fillValueCallback(countries[c].id)
} else {
data = cd.data;
}
countries[c].properties.hover = cd.hover;
countries[c].properties.id = cd.id;
countries[c].properties.name = cd.name;
countries[c].properties.data = data;
countries[c].properties.payload = cd.payload;
}
}
}
return topoCountries;
}
function logTouchCoordsStart(d) {
if (d3.event.targetTouches) {
zoomTouch = [d3.event.targetTouches[0].clientX, d3.event.targetTouches[0].clientY];
}
}
function logTouchCoordsEnd(d) {
if (zoomTouch[0] === d3.event.changedTouches[0].clientX) {
clicked(d)
}
}
function buildRanges(argument) {
var ranges = [];
var rangesDOM = d3.selectAll('.ranges div');
$.each(rangesDOM[0], function(index, range) {
var $r = $(range);
ranges.push({
value: $r.attr('data-value'),
color: $r.attr('data-colour')
});
});
return ranges
}
function fillBasedOnRange(name, value) {
if (!value) {
problemCountries.push([name, value]);
return "#ddd";
}
for (var i = 0; i < ranges.length; i++) {
var lowerBound = ranges[i].value;
if (i + 1 < ranges.length) {
var upperBound = ranges[i + 1].value
}
if (i === 0 && value < lowerBound) {
problemCountries.push([name, value])
return "#ddd";
}
if (i + 1 == ranges.length) {
if (value >= lowerBound) {
return "#" + ranges[i].color;
}
}
if (value >= lowerBound && value < upperBound) {
return "#" + ranges[i].color;
}
}
}
function startWithZoomedMap() {
var zoomOutCentre = $('#container').attr('data-map-centre');
var zoomOutScale = $('#container').attr('data-map-scale');
var scale_multiple = zoomOutScale / 100;
var zoomCoords = zoomOutCentre.split(',');
var projectedZoomCoords = projection(
[(zoomCoords[1]), (zoomCoords[0])]
);
var new_x_midpoint = width / 2 - scale_multiple * projectedZoomCoords[0];
var new_y_midpoint = height / 2 - scale_multiple * projectedZoomCoords[1];
var new_translate = [new_x_midpoint, new_y_midpoint];
WOF.new_translate = new_translate;
WOF.scale_multiple = scale_multiple;
svg.transition()
.duration(1400)
.call(zoom.translate(new_translate).scale(scale_multiple).event);
}
function hovered(d) {
function offScreenBottom() {
var distanceFromScreenBottom = window.innerHeight - d3.event.clientY
return distanceFromScreenBottom < tooltipOffsetTop;
}
var mouse = d3.mouse(svg.node()).map(function(d) {
return parseInt(d);
});
var mouseLeft = mouse[0] + tooltipOffsetLeft;
var mouseTop = mouse[1] + tooltipOffsetTop;
tooltip.classed("hidden", false)
.style({
left: +mouseLeft + "px",
top: +mouseTop + "px"
});
log.debug(d)
showPanelForTooltip(d, tooltip);
if (mouse[0] > (width / 2)) {
var tooltipWidth = parseInt(tooltip.style('width'));
tooltip.style({
left: tooltip.style({
'left': (mouseLeft - tooltipWidth) + 'px'
})
})
}
if(offScreenBottom()) {
var tooltipHeight = parseInt(tooltip.style('height'));
tooltip.style({
top: (mouseTop - tooltipHeight - 50) + 'px'
});
}
}
function chartingPayloadPresent(payload) {
if (typeof buildSummaryChart !== 'function'){
return false;
}
if (!payload){
return false;
}
if (!jQuery.isEmptyObject(payload[0])){
return true
}
}
function showPanelForTooltip(d, tooltip){
var payload = _.filter(d.properties.payload, function(p){
return !jQuery.isEmptyObject(p);
});
if(chartingPayloadPresent(payload)) {
log.debug("we have a payload!")
log.debug(d)
if(payload[0].chart_image_url) {
log.info("we have an uploaded chart");
tooltip.html('<h3>' + d.properties.name + ':</h3><img src="' + payload[0].chart_image_url + '" />')
return true
}
if(payload[0]){
log.info("we have a chart to build:", d.id)
tooltip.html("<div class='barchart'></div>")
tooltip.style('width', "400px");
selectionString = ".tooltip .barchart";
buildSummaryChart(selectionString, payload[0], d.properties.name);
return true
}
} else {
log.debug("No payload");
log.debug(d)
if(d.properties.hover) {
log.debug("But we have a hover")
if (typeof WOF.showPolicyMapPopup == 'function'){
WOF.showPolicyMapPopup(d, tooltip, payload)
return true
} else{
tooltip.html(d.properties.hover.split("\n").join('<br>'));
return true
}
}
if(d.properties.name) {
tooltip.html('<div class="no-data">' + d.properties.name + ': No information.</div>');
return true
} else {
log.debug("no name here")
tooltip.classed("hidden", true);
return false
}
}
}
function clicked(d) {
var t;
if (d3.event.type === 'touchend') {
t = d3.event.changedTouches[0].target;
}
if (d3.event.type === 'click') {
t = d3.event.target;
}
log.debug("Click registered:", d.id);
selectCountry(d.id);
WOF.setCountryHash();
}
function selectCountry(countryId) {
var d = WOF.findCountry(countryId);
WOF.activeCountry = countryId;
WOF.setActiveClasses();
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 = 0.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);
var map_id = $('h3.map-title').attr('data-map');
showStaticOverride(countryId, map_id)
}
WOF.selectCountry = selectCountry;
WOF.setCountryHash = function() {
window.location.hash = 'country=' + WOF.activeCountry;
}
function showStaticOverride(countryId, map_id){
$("div#map-data-area")
.load("/maps/ajax/" + map_id + "/" + countryId, function() {
$(".map-data-content .instructions").hide();
var scrollTop = $("div.map-container").offset().top - 10;
$('html,body').animate({
scrollTop: scrollTop
}, 500);
WOF.filterDetailPanel();
});
}
function reset() {
var new_translate = WOF.new_translate || 0;
var scale_multiple = WOF.scale_multiple || 1;
svg.transition()
.duration(750)
.call(zoom.translate(new_translate).scale(scale_multiple).event);
}
function zoomed() {
g.selectAll('.country').style("stroke-width", 1 / d3.event.scale + "px");
if (!isNaN(d3.event.translate[0])){
g.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
}
}
function stopped() {
if (d3.event.defaultPrevented) d3.event.stopPropagation();
}
function cleanString(hoverString) {
if (hoverString !== "" && hoverString) {
var str = hoverString.split('\n');
arr = $.map(str, function(val, i) {
return "<p>" + val + "</p>";
});
hoverString = arr.join().replace(/,/g, '');
}
if (!hoverString) {
hoverString = "";
}
return hoverString;
}
function addSelfReportDropDown(){
$('#map-data-container').click(function(ev){
if ($(ev.target).parent().hasClass('self-reported')) {
ev.stopPropagation();
ev.preventDefault();
$(ev.target).parent().find('.explanation').toggle('slow');
}
if ($(ev.target).parent().parent().hasClass('self-reported')) {
ev.stopPropagation();
ev.preventDefault();
$(ev.target).parent().parent().find('.explanation').toggle('slow');
}
});
}
setup(width, height);
var countryDataUrl = $('h3.map-title').attr('data-file-url');
log.setLevel(log.levels.DEBUG);
addSelfReportDropDown();
queue()
.defer(d3.json, "world-topo-smaller.json")
.defer(d3.json, countryDataUrl)
.await(ready);
});
loglevel.min.js
!function(a,b){"object"==typeof module&&module.exports&&"function"==typeof require?module.exports=b():"function"==typeof define&&"object"==typeof define.amd?define(b):a.log=b()}(this,function(){function a(a){return typeof console===i?!1:void 0!==console[a]?b(console,a):void 0!==console.log?b(console,"log"):h}function b(a,b){var c=a[b];if("function"==typeof c.bind)return c.bind(a);try{return Function.prototype.bind.call(c,a)}catch(d){return function(){return Function.prototype.apply.apply(c,[a,arguments])}}}function c(a,b){return function(){typeof console!==i&&(d(b),g[a].apply(g,arguments))}}function d(a){for(var b=0;b<j.length;b++){var c=j[b];g[c]=a>b?h:g.methodFactory(c,a)}}function e(a){var b=(j[a]||"silent").toUpperCase();try{return void(window.localStorage.loglevel=b)}catch(c){}try{window.document.cookie="loglevel="+b+";"}catch(c){}}function f(){var a;try{a=window.localStorage.loglevel}catch(b){}if(typeof a===i)try{a=/loglevel=([^;]+)/.exec(window.document.cookie)[1]}catch(b){}void 0===g.levels[a]&&(a="WARN"),g.setLevel(g.levels[a])}var g={},h=function(){},i="undefined",j=["trace","debug","info","warn","error"];g.levels={TRACE:0,DEBUG:1,INFO:2,WARN:3,ERROR:4,SILENT:5},g.methodFactory=function(b,d){return a(b)||c(b,d)},g.setLevel=function(a,b){if("string"==typeof a&&void 0!==g.levels[a.toUpperCase()]&&(a=g.levels[a.toUpperCase()]),!("number"==typeof a&&a>=0&&a<=g.levels.SILENT))throw"log.setLevel() called with invalid level: "+a;return b!==!1&&e(a),d(a),typeof console===i&&a<g.levels.SILENT?"No console available for logging":void 0},g.enableAll=function(a){g.setLevel(g.levels.TRACE,a)},g.disableAll=function(a){g.setLevel(g.levels.SILENT,a)};var k=typeof window!==i?window.log:void 0;return g.noConflict=function(){return typeof window!==i&&window.log===g&&(window.log=k),g},f(),g});