Skip to content

Instantly share code, notes, and snippets.

@mskeving
Last active December 8, 2016 05:35
Show Gist options
  • Save mskeving/5389fa9ed8ff6c0791073879f8d8495e to your computer and use it in GitHub Desktop.
Save mskeving/5389fa9ed8ff6c0791073879f8d8495e to your computer and use it in GitHub Desktop.
Emails by Timezone
tz count
0 45
+1 145
+2 147
+3 1
+5.5 76
+5.75 1
+7 1
+9.5 1
+12 1
+13 1
-3 1
-4 1263
-5 823
-6 123
-7 423
-8 227
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
body {
font-family: arial, sans;
font-size: 14px;
margin: 30px auto;
}
svg {
overflow: visible;
}
.tooltip {
background-color: #f7f7f7;
padding: 3px 12px;
font-family: sans-serif;
border: 1px solid #bbbbbb;
box-shadow: 1px 1px 4px #bbbbbb;
}
.tooltip_title {
font-weight: bold;
font-size: 14px;
max-width: 300px;
word-wrap: normal;
}
.tooltip_body {
font-weight: normal;
}
.title {
font-size: 36;
font-weight: bold;
}
</style>
<body>
</body>
<script src="http://d3js.org/d3.v4.js" charset="utf-8"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src='https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js'></script>
<script type="text/javascript">
// There are many abbreviations for each time zone.
// I took only one as a reference.
var tzToAbbrv = {
'0': 'UTC',
'+1': 'CET',
'+2': 'CAT',
'+3': 'TRT',
'+3.5': 'IRST',
'+4': 'GST',
'+4.5': 'AFT',
'+5': 'PKT',
'+5.5': 'IST',
'+5.75': 'NPT',
'+6': 'BST',
'+6.5': 'CCT',
'+7': 'THA',
'+8': 'MYT',
'+9': 'JST',
'+9.5': 'ACST',
'+10': 'PGT',
'+11': 'NCT',
'+12': 'NZST',
'+12.75': 'CHAST',
'+13': 'TOT',
'+14': 'LINT',
'-1': 'CVT',
'-2': 'FNT',
'-2.5': 'NDT',
'-3': 'ADT',
'-3.5': 'NST',
'-4': 'AMT',
'-5': 'EST',
'-6': 'CST',
'-7': 'MST',
'-8': 'PST',
'-9': 'GIT',
'-9.5': 'MIT',
'-10': 'CKT',
'-11': 'SST',
'-12': 'BIT',
}
var getTimeZone = function(name) {
return name.replace(/[A-Za-z ]/g, "")
}
var map_margin = {top: 110, right: 70, bottom: 30, left: 20};
var map_width = 800 - map_margin.left - map_margin.right;
var map_height = 400 - map_margin.top - map_margin.bottom;
var bar_margin = {top: 30, right: 40, bottom: 30, left: 50};
var bar_width = 550 - bar_margin.left - bar_margin.right;
var bar_height = 400 - bar_margin.top - bar_margin.bottom;
var color = d3.scaleSequential(function(t) {
t += .1; // darken the base color a tad.
return d3.interpolateBlues(t);
});
// set the ranges for bar chart
var x = d3.scaleBand()
.range([0, bar_width])
.padding(0.1);
var y = d3.scaleSqrt()
.range([bar_height, 0]);
var title = d3.select("body")
.append("div")
.attr("class", "title")
.style("font-size", "41px")
.style("font-weight", "bold")
.style("margin", "0 0 50px 72px")
.text("Emails by Timezone")
var bar_svg = d3.select("body").append("svg")
.attr('width', bar_width + bar_margin.left + bar_margin.right)
.attr('height', bar_height + bar_margin.top + bar_margin.bottom)
.append('g')
.attr('transform', 'translate(' + [bar_margin.left, bar_margin.top] + ')')
.classed("original", true)
var map_svg = d3.select("body").append("svg")
.attr('width', map_width + map_margin.left + map_margin.right)
.attr('height', map_height + map_margin.top + map_margin.bottom)
.append('g')
.attr('transform', 'translate(' + [map_margin.left, map_margin.top] + ')')
.classed("original", true)
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden");
var projection = d3.geoMercator()
.scale(135) // to make map fit inside svg.
.translate([400, 200]) // move up, right, down, or left
var path = d3.geoPath()
.projection(projection);
var dispatch = d3.dispatch('tzchange', 'leave');
d3.queue()
.defer(d3.json, "tz_data.json")
.defer(d3.csv, "email_data.csv")
.await(ready);
function ready(error, world, data) {
if (error) return console.warn(error);
// format email data into tz to count obj
var data_obj = {};
data.forEach(function(d) {
d.count = +d.count;
data_obj[d.tz] = +d.count
})
_.sortBy(data_obj, function(d) {
return d.tz;
})
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.tz; }));
y.domain([0.5, d3.max(data, function(d) { return d.count; })]);
color.domain([0, d3.max(data, function(d) { return +d.count; })]);
var world_data = topojson.feature(world, world.objects.timezones).features
// add bar chart
bar_svg .append("g")
.attr('width', bar_width)
.attr('height', bar_height)
.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function(d) {
abbrev = tzToAbbrv[d.tz];
return "bar " + abbrev;
})
.attr("x", function(d) { return x(d.tz); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.count); })
.attr("height", function(d) { return bar_height - y(d.count); })
.style("fill", function(d) { return color(d.count); })
.on("mouseover", function(d) {
var tz = getTimeZone(d.tz);
var count = data_obj[tz] || 0;
dispatch.call('tzchange', this, tz, count);
})
.on("mousemove", function() {
return tooltip.style("top", (d3.event.pageY-52) + "px").style("left", (d3.event.pageX+18) + "px");
})
.on("mouseout", function() {
dispatch.call('leave', this, world_data, data_obj)
});
// add the x Axis
bar_svg.append("g")
.attr("transform", "translate(0," + bar_height + ")")
.call(d3.axisBottom(x))
.classed('x-axis', true);
// add the y Axis
bar_svg.append("g")
// .call(d3.axisLeft(y).ticks(4, ".0s"))
.call(d3.axisLeft(y))
.classed('y-axis', true);
// add world map with mouse events
map_svg.append("g")
.attr("class", "world")
.attr('width', map_width)
.selectAll("path")
.data(world_data)
.enter().append("path")
.attr("class", function(d) {
tz = getTimeZone(d.properties.Name);
abbrev = tzToAbbrv[tz];
return "country " + abbrev;
})
.attr("d", path)
.style("fill", function(d, i) {
var tz = getTimeZone(d.properties.Name),
count = data_obj[tz] || 0;
return color(count);
})
.on("mouseover", function(d) {
var tz = getTimeZone(d.properties.Name),
email_count = data_obj[tz] || 0;
dispatch.call('tzchange', this, tz, email_count);
})
.on("mousemove", function() {
return tooltip.style("top", (d3.event.pageY-52) + "px").style("left", (d3.event.pageX+18) + "px");
})
.on("mouseout", function(d) {
dispatch.call('leave', this, d, data_obj)
});
}
dispatch.on('leave', function(data, data_obj) {
d3.selectAll('.country').style('opacity', 1)
.style('fill', function(d) {
var tz = getTimeZone(d.properties.Name),
count = data_obj[tz] || 0;
return color(count)
});
d3.selectAll('.bar').style('opacity', 1)
.style('fill', function(d) {
return color(d.count)
});
return tooltip.style("visibility", "hidden");
})
dispatch.on('tzchange', function(tz, count) {
var abbrev = tzToAbbrv[tz];
// decrease opacity for all non-selected timezones
d3.selectAll('.country').style('opacity', .2)
.style('fill', color(0));
d3.selectAll('.bar').style('opacity', .2)
.style('fill', color(0));
d3.selectAll('.' + abbrev).style('opacity', 1)
.style('fill', color(count));
tooltip.html("")
.append("pre").attr("class", "tooltip_body")
.text("Emails from " + tzToAbbrv[tz] + " " + tz + ": " + count);
return tooltip.style("visibility", "visible");
})
</script>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment