<!DOCTYPE html>
<html>

  <head>
    <script data-require="d3@*" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
    <link rel="stylesheet" href="style.css" />
  </head>

  <body>
    <h1>Histogram</h1>
    <div class="charts"></div>
    
    <script src="script.js"></script>
  </body>

</html>
var data = [61, 139, 218, 258, 282, 284, 285, 285, 291, 311, 319, 320, 336, 341, 345, 345, 371, 380, 381, 384, 412, 421, 488, 511, 512, 522, 598, 613, 684, 730, 780, 902, 1015, 1219, 1479, 1533, 1939, 83168, 1261736, 1280703];
// var data = data.slice(0, Math.floor((data.length-1)*.90));

var tripDurationHistogram = histogramChart();

var container = d3.select('.charts');
container.append('svg').datum(data).call(tripDurationHistogram);

// https://bl.ocks.org/mbostock/3048450
// https://bl.ocks.org/mbostock/3048166
// https://bl.ocks.org/mbostock/b2fee5dae98555cf78c9e4c5074b87c3
function histogramChart() {
  var margin = {top: 10, right: 30, bottom: 30, left: 30},
      width = 460 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom,
      title = '';

  var preprocessor = function(d) { return d };

  function chart(selection) {
    selection.each(function(raw, i) {
      // generate chart here; `raw` is the data and `this` is the element

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

      // Title
      d3.select(this).append('g')
          .attr("transform", "translate(" + width/2 + ", " + (margin.top - 5) + ")")
          .append('text')
          .attr('class', 'title')
          .text(title);

      // preparing data
      var data = preprocessor(raw);
      // var data = d3.range(1000).map(d3.randomBates(10));
      var formatCount = d3.format(",.0f");

// 
      // var x = d3.scaleLinear()
      //   .domain([0, d3.quantile(data, 0.95)])
      //   .rangeRound([0, width]);

      // var bins = d3.histogram()
      //   .domain(x.domain())
      //   .thresholds(x.ticks(20))(data);
// 
      var MIN = 60;
      var HR = MIN * 60;
      var tripDomain = [0, 15*MIN, 30*MIN, 45*MIN, 60*MIN, 90*MIN, Math.max(24*HR, d3.max(data))];

      var bins = d3.histogram()
        .thresholds(tripDomain)(data);

      var x = d3.scaleOrdinal()
        .domain(tripDomain)
        .range(tripDomain.map((_,i) => (i/(tripDomain.length-1))*width));

// console.debug(data, bins, x.domain());

      var y = d3.scaleLinear()
        .domain([0, d3.max(bins, function(d) { return d.length; })])
        .range([height, 0]);

      // bars
      var bar = chart.selectAll(".bar")
          .data(bins);

      bar.exit().remove();

      var newBar = bar.enter()
          .append("g")
            .attr('class', 'bar')
            .attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; });

      newBar.append('rect');
      newBar.append('text');

      var all = newBar.merge(bar);

      all.select('rect')
        .attr("x", 1)
        .attr("width", x(bins[0].x1) - x(bins[0].x0) - 1)
        .attr("height", function(d) { return height - y(d.length); });

      all.select("text")
        .attr("dy", ".75em")
        .attr("y", 6)
        .attr("x", (x(bins[0].x1) - x(bins[0].x0)) / 2)
        .attr("text-anchor", "middle")
        .text(function(d) { return formatCount(d.length); });

      chart.append("g")
        .attr("class", "axis axis--x")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(x));
    });
  }

  chart.title = function(_) {
    return arguments.length ? (title = _, chart) : title;
  };

  // https://bost.ocks.org/mike/chart/
  chart.preprocessor = function(_) {
    return arguments.length ? (preprocessor = _, chart) : preprocessor;
  };

  chart.width = function(_) {
    return arguments.length ? (width = _, chart) : width;
  };

  chart.height = function(_) {
    return arguments.length ? (height = _, chart) : height;
  };

  return chart;
}
.bar text {
  fill: white;
  font: 10px sans-serif;
  text-anchor: end;
}

.bar {
  fill: steelblue;
}

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

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

.x.axis path {
  display: none;
}