block by curran 716e5ed6b01e5bfa8a8c

Negative Relative Time Ticks

Full Screen

This example shows one solution for how to get d3 axis ticks to display relative time (durations) into the past by hour and minute [-01:30, -01:00, -00:30, 00:00, 00:30, 01:30].

forked from mbostock‘s block: Time of Day

web counter

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.dots path {
  fill: none;
  stroke: steelblue;
  stroke-width: 1.5px;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.axis--y line {
  stroke-opacity: 0.2;
}

.axis--y path {
  stroke: none;
}

.axis text {
  font: 10px sans-serif;
}

</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>

// This custom tick mark formatter displays signed hours and minutes
// relative to the given "zeroTime" date.
function relativeTimeFormatter(zeroTime){
  return function (d){
    
    var minutesNegative = d3.time.minutes(d, zeroTime).length;
    var minutesPositive = d3.time.minutes(zeroTime, d).length;
    var minutes = minutesNegative ? minutesNegative : minutesPositive;
    var hours = Math.floor(minutes / 60);
    var sign = minutesNegative ? "-" : "";
    
    hours = addLeadingZero(hours);
    minutes = (minutes % 60);
    minutes = addLeadingZero(minutes);
    
    return sign + hours + ":" + minutes;
  }
}
function addLeadingZero(number){
  return (number < 10 ? "0" : "") + number;
}

var parseTime = d3.time.format.utc("%H:%M").parse,
    zeroTime = parseTime("10:00");

var margin = {top: 30, right: 30, bottom: 30, left: 30},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var x = d3.time.scale.utc()
    .domain([
      
      // 3 hours before zero time.
      d3.time.hour.utc.offset(zeroTime, -3),
      
      // 2 hours after zero time.
      d3.time.hour.utc.offset(zeroTime, 2)
    ])
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  

d3.csv("tweets.csv", type, function(error, data) {
  if (error) throw error;

  y.domain([0, d3.max(data, function(d) { return d.rate; })]);

  svg.append("g")
      .attr("class", "axis axis--x")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.svg.axis()
          .scale(x)
          .orient("bottom")
          .tickFormat(relativeTimeFormatter(zeroTime)));

  svg.append("g")
      .attr("class", "dots")
    .selectAll("path")
      .data(data)
    .enter().append("path")
      .attr("transform", function(d) { return "translate(" + x(d.time) + "," + y(d.rate) + ")"; })
      .attr("d", d3.svg.symbol()
          .size(40));

  var tick = svg.append("g")
      .attr("class", "axis axis--y")
      .call(d3.svg.axis()
          .scale(y)
          .tickSize(-width)
          .orient("left"))
    .select(".tick:last-of-type");

  var title = tick.append("text")
      .attr("dy", ".32em")
      .text("tweets per hour");

  tick.select("line")
      .attr("x1", title.node().getBBox().width + 6);
});
  
function type(d) {
  d.rate = +d.count / 327 * 60; // January 8 to November 30
  d.time = parseTime(d.time);
  d.time.setUTCHours((d.time.getUTCHours() + 24 - 7) % 24);
  return d;
}

</script>

tweets.csv