0

Using d3.js v4 I am trying to graph a time on the y-axis. The y-axis represents 24 hours of the clock.

The data is simply,

[{
    "start": "2016-11-14 20:00",
    "end": "2016-11-14 23:59"
  },
  {
    "start": "2016-11-16 01:00",
    "end": "2016-11-16 03:00"
  },
  {
    "start": "2016-11-15 00:00",
    "end": "2016-11-15 04:00"
  }
]

The first object should graph from 20 to 24 but it is going from 19 to 23. This would suggest a problem such as zero based counting.

Does anyone have an suggestions on how i might solve this?

Here is the full code snippet,

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title>d3 datetime graph</title>
  <script src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
  <svg width="800" height="400"></svg>
  </div>
  <div id="json">
  </div>
  <!-- <script type="text/javascript" src="script.js"></script> -->
  <script type="text/javascript">
    var svg = d3.select("svg"),
      margin = {
        top: 20,
        right: 60,
        bottom: 60,
        left: 40
      },
      width = +svg.attr("width") - margin.left - margin.right,
      height = +svg.attr("height") - margin.top - margin.bottom;
    var color = ["#2C93E8", "#838690", "#F56C4E", "#A60F2B", "#648C85", "#B3F2C9", "#528C18", "#C3F25C"];


    var data = [{
        "start": "2016-11-14 20:00",
        "end": "2016-11-14 23:59"
      },
      {
        "start": "2016-11-16 01:00",
        "end": "2016-11-16 03:00"
      },
      {
        "start": "2016-11-15 00:00",
        "end": "2016-11-15 04:00"
      }
    ]

    var parseTime = d3.timeParse("%Y-%m-%d %H:%M");
    var parseDay = d3.timeParse("%e %b %Y");
    var formatDay = d3.timeFormat("%e %b %Y");
    // data.forEach(function(d) {
    //   commence = parseTime(d.start);
    //   conclude = parseTime(d.end);
    //   if (commence.getDay() != conclude.getDay()) {
    //     // a check here in case they are more than a day apart
    //     // 2016-11-04 12:00
    //
    //     morning = conclude.getFullYear() + "-" + (conclude.getMonth() + 1) + "-" + conclude.getDate() + " 00:00";
    //     console.log("morning is " + morning);
    //     data.push({
    //       "start": morning,
    //       "end": d.end
    //     })
    //     midnight = commence.getFullYear() + "-" + (commence.getMonth() + 1) + "-" + commence.getDate() + " 23:59";
    //     console.log("midnight is " + midnight);
    //     d.end = midnight;
    //     //we need to remove current object
    //     //what element in the array is it, read the 'i'
    //     //use slice to remove that element data.slice(i,1) or soemthing like that
    //   }
    // });
    console.log(JSON.stringify(data, null, 2));

    data.forEach(function(d) {
        d.start = parseTime(d.start);
        d.end = parseTime(d.end);
        d.duration = ((d.end - d.start) / (60 * 1000) / 60); // session duration in hours
        d.starttime = d.start.getHours();
        d.endtime = d.end.getHours();
        d.daily = formatDay(d.start);
        d.day = parseDay(d.daily);

        return d;
      },
      function(error, data) {
        if (error) throw error;
      });

    document.getElementById("json").innerHTML = "<h5>data.json</h5>" + "<pre>" + JSON.stringify(data, null, 2) + "</pre>";

    var x = d3.scaleTime()
      .domain(d3.extent(data, function(d) {
        return d.day; //need to round to day
      }))
      .range([0, width]);

    var y = d3.scaleLinear()
      .domain([0, 24])
      .range([height, 0]);

    var group = svg.append("g");

    group.selectAll(".bar")
      .data(data)
      .enter()
      .append("rect")
      .style("fill", "blue")
      .attr("class", "bar")
      .attr("transform", "translate(80,0)")
      .attr("x", function(d) {
        return x(d.day) + 10;
      })
      .attr("y", function(d) {
        //if ((d.endtime + d.duration) > 24) {
        // console.log("oh no:")
        // console.log("d.starttime: " + d.starttime); //2
        // console.log("y(d.starttime): " + y(d.starttime)); //293.3333
        // console.log("d.endtime: " + d.endtime); //18
        // console.log("y(d.endtime): " + y(d.endtime)); //80
        // console.log("d.duration: " + d.duration); //16
        // console.log("y(d.duration): " + y(d.duration)); //106.6666
        // console.log("height: " + height); //320
        // console.log("y(16): " + y(16)); //106.6666
        // console.log("y(24): " + y(24)); //0
        // console.log("y(0): " + y(0)); //320
        // console.log("height - y(d.duration): " + (height - y(d.duration))); //320-106.666=213.333
        //  }
        return y(d.endtime);
      })
      .attr("width", 20)
      .attr("height", function(d) {
        return height - y(d.duration);
        // return y(d.starttime);
      })
      .style("fill", function(d, i) {
        return color[i];
      });

    svg.append("g")
      .attr("class", "axis axis--x")
      .attr("transform", "translate(100," + (height + 2) + ")")
      .call(d3.axisBottom(x).ticks(d3.timeDay.every(1)).tickFormat(d3.timeFormat("%e %b %Y")))
      // axis.ticks(d3.timeMinute.every(15));
      // .call(d3.axisBottom(x))
      .selectAll("text")
      .style("text-anchor", "end")
      .attr("dx", "-.8em")
      .attr("dy", ".15em")
      .attr("transform", "rotate(-35)");

    svg.append("g")
      .attr("class", "axis axis--y")
      .attr("transform", "translate(80,0)")
      .call(d3.axisLeft(y).ticks(24))
      .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 0)
      .attr("x", 0)
      .attr("dy", "0.71em")
      .attr("text-anchor", "end")
      .text("duration (hours)");
  </script>
</body>

</html>

Thanks

1 Answer 1

1

So the first bar, the one that you're having issues with, is based on this data:

{
  "start": "2016-11-14 20:00",
  "end": "2016-11-14 23:59"
},

And you're positioning it based on its endtime, which is derived like

d.endtime = d.end.getHours();

So, that's the issue: if you get the hour of 23:59, it's 23, not 24: JavaScript doesn't round to the nearest hour, it just takes the hour part. If you want a more accurate y-axis, you can:

  • Use something that isn't hours, like minutes or seconds.
  • Create a time scale for each bar with its domain set to midnight before / after the bar's time range.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.