2

Using d3.js the x coordinates of the graph are displaying at x=0 or on the y axis. The x axis represents a date and time and the y axis is the temperature. But this is only on an ipad or iphone. On my own machine, Linux, it displays correctly.

The graphs and all file can be seen at, http://shanespi.no-ip.biz

The ipad/iphone display

enter image description here

While the correct graph is,

enter image description here

Here is the javascript,

 var xScale = d3.scaleTime()
      .domain([new Date(datahourly[0].date), d3.max(datahourly, function(d) {return new Date(d.date)})])
  .range([0, (w-2*padding)]); // max x screen space is width - twice padding


  var yScale = d3.scaleLinear()
      .domain([0, d3.max(datahourly, function(d) {return d.temp})])
      .range([(h-2*padding), 0]); // max y screen space is height - twice padding

  var xAxis =  d3.svg.axis(xScale) // d3 v.4
      .ticks(9) // specify the number of ticks 
    /*.ticks(d3.time.days, 1) */ 
      .tickFormat(d3.time.format('%H:00')) 
      .scale(xScale)
      .orient("bottom");

  var yAxis = d3.svg.axis(yScale)
      .ticks(7)
      .scale(yScale)
      .orient("left");

  var svg = d3.select('#hourly-readings')
      .append('svg') // create an <svg> element
  .attr('id', 'svgDaily')
      .attr('width', w) // set its dimensions
      .attr('height', h);

    svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(" + (2*padding - 15) + "," + (padding - 15) + ")")
    .call(yAxis);

svg.append('g')            // create a <g> element
          .attr('class', 'axis')   // specify classes
  .attr("transform", "translate(" + (2*padding - 15) + "," + (h - padding - 15) + ")")
      .call(xAxis);            // let the axis do its thing

  var lineFunctionStart = d3.svg.line()
  .x(function(d) {return xScale(new Date(d.date)); })
  .y(h - 2*padding - 5)
  .interpolate("cardinal");

  var lineFunction = d3.svg.line()
  .x(function(d) {return xScale(new Date(d.date)); })
  .y(function(d) {return yScale(d.temp); })
  .interpolate("cardinal");

  svg.append("path")
      .attr('d', lineFunctionStart(datahourly))
  .attr('stroke', "grey")
  .attr('stroke-width', 1)
  .style('fill', "white")
  .attr("transform","translate(" + (2*padding - 13) + "," + (padding - 10) + ")")
  .transition()
  .duration(3000)
      .attr('d', lineFunction(datahourly));

  //var svg = d3.select('svg');
  var svg = d3.select('#svgDaily');


      svg.append("text")      // text label for the x axis
  .attr("x", 310)
.attr("y", h)
.style("font-size", "12")
  .style("text-anchor", "middle")
  .text("Time (1 hr. intervals)");

svg.append("text")      // text label for the x axis
.attr('transform', 'rotate(-90)')
.attr("x", -85) // Because rotate is first x and y coordinates are transaposed
.attr("y", padding-17)
.style("font-size","10")
  .style("text-anchor", "middle")
  .text("Temp. Celcius");


  var rects = svg.selectAll('circle')
      .data(datahourly);

  var newRects = rects.enter();
      newRects.append('circle')
      .attr('cx', function(d, i) { return (Math.random() * (w - 2*padding)) })
      //.attr('cx', function(d, i) {
          //return (5 + xScale(new Date(d.date)));
          //})
      .attr('cy', (h - (2*padding)))
      .attr('r', 5)
      .style('fill', "lightblue")
      .attr("transform","translate(" + (2*padding - 18) + "," + (padding - 20) + ")")
      .transition()
      .duration(3000)
      .delay(function(d, i) {return i * 300})
      .attr('cx', function(d, i) {
          return (5 + xScale(new Date(d.date)));
          })
      .attr('cy', function(d, i) {
          return 10 + yScale(d.temp);
  });

Here is the 'datahourly' data,

  [  
   {  
      "date":"2016-12-14 22:01:01.799830",
      "temp":"24.04"
   },
   {  
      "date":"2016-12-15 00:01:02.362875",
      "temp":"23.03"
   },

......................

   {  
      "date":"2016-12-15 21:01:01.868593",
      "temp":"21.93"
   },
   {  
      "date":"2016-12-15 22:01:02.278817",
      "temp":"15.9"
   },
   {  
      "date":"2016-12-15 23:01:01.963714",
      "temp":"21.63"
   }
]

I am using Chrome on Linux and Safari on the ipad and iphone. But I did install chrome on the iphone and the graph is still incorrect.

Are there svg issues with iOS?

EDIT: The main issue was that the time data was not parsed correctly,

This is the correct solution,

var data = [];
      $.getJSON("data/data.json",
      function(info){
      data = info[0].fiveMinReadings;
      //console.log(data);
      var parseTime = d3.timeParse("%Y-%m-%d %H:%M:%S.%L");
      data.forEach(function(d) {
          d.date = d.date.slice(0,-3);// remove microseconds
          d.date = parseTime(d.date);
          d.temp = +d.temp;
      });

      // Beginning of graph for 5 minute readings
          var padding = 25;
          var w = 600;
      var h = 300;

      var xScale = d3.scaleTime()
      .domain(d3.extent(data, function(d) { return d.date; }))
              .range([0, (w-2*padding)]); // max x screen space is width - twice padding

      var yScale = d3.scaleLinear()
          .domain([0,d3.max(data, function(d) {return d.temp})])
          .range([(h-2*padding), 0]); // max y screen space is height - twice padding

      var xAxis =  d3.axisBottom(xScale) // d3 v.4
          .tickFormat(d3.timeFormat('%H:%M ')) 
              .scale(xScale);

      var yAxis = d3.axisLeft(yScale)
          .scale(yScale);

      var svg = d3.select('#five-min-readings')
          .append('svg') // create an <svg> element
          .attr('id','svgHourly')
          .attr("align","center")
          .attr('width', w) // set its dimensions
          .attr('height', h);

      var valueline = d3.line()
          .curve(d3.curveCardinal)
          .x(function(d) { return xScale(d.date); })
          .y(h - 2*padding - 4);

      var valueline2 = d3.line()
          .curve(d3.curveCardinal)
          .x(function(d) { return xScale(d.date); })
          .y(function(d) {return yScale(d.temp); });

      svg.append("text")      // text label for the x axis
          .attr("x", 310)
          .attr("y", h)
          .style("font-size", "12")
          .style("text-anchor", "middle")
          .text("Time (5 min. intervals)");

      svg.append("text")      // text label for the x axis
          .attr('transform', 'rotate(-90)')
          .attr("x", -85) // Because rotate is first, x and y coordinates are transaposed
          .attr("y", padding-17)
          .style("font-size","10")
          .style("text-anchor", "middle")
          .text("Temp. Celcius");

          svg.append("g")
          .attr("class", "axis")
          .attr("transform", "translate(" + (2*padding-15) + "," + (padding-15) + ")")
          .call(yAxis);

      svg.append('g')            // create a <g> element
          .attr('class', 'axis')   // specify class
          .attr("transform", "translate(" + (2*padding-15) + "," + (h - padding - 15) + ")")
              .call(xAxis);            // let the axis do its thing

      svg.append('path')
          .data([data])
          .attr("class","line")
          .attr('d', valueline)
          .attr('stroke', "grey")
          .attr('stroke-width', 1)
          .style('fill', "white")
          .attr("transform","translate(" + (2*padding - 13) + "," + (padding -10) + ")")
          .transition()
          .duration(3000)
          .attr('d', valueline2 );    

      var svg = d3.select('#svgHourly');

      var rects = svg.selectAll('circle')
          .data(data);

      var newRects = rects.enter();

      newRects.append('circle')
          .attr('cx', function(d, i) { return (Math.random() * (w - 2*padding)) })
          .attr('cy', h - 2*padding)      
          .attr('r', 5)
          .attr("id", function(d,i){return "circle" + i})
          .style('fill', "lightblue")
          .attr("transform","translate(" + (2*padding - 18) + "," + (padding - 20) + ")")
          .transition()
          .duration(3000)
          .delay(function(d, i) {return i * 300})
              .attr('cx', function(d, i) {  return (5 + xScale(d.date)); })
          .attr('cy', function(d, i) { return 10 + yScale(d.temp); });        
       }); // closes getJSON()
Shane G
  • 3,129
  • 10
  • 43
  • 85

3 Answers3

3

You can clearly see that your circles are getting the correct y ("cy") value, the error lies in the x ("cx") value.

The problem seems to be the use of new Date() in Safari with this pattern: yyyy-MM-dd.

In your code, given your data structure, you'll end up having something like this in the line generator:

.x(function(d) {
    return xScale(new Date("2016-12-15 23:01:01.963714")); 
})//the first date in your data ---^

And the same for your circles:

.attr('cx', function(d, i) {
    return (5 + xScale(new Date("2016-12-15 23:01:01.963714")));
})

Apparently, this is supported by Chrome and Firefox, but not by Safari. Curiously, the pattern (yyyy-MM-dd) is included in the ECMA standard, so this is probably a Safari specific issue.

According to this answer, it will work if you include a T (I didn't test it):

.x(function(d) {
    return xScale(new Date("2016-12-15T23:01:01.963714")); 
})

Alternatively, remove the new Date() and parse the dates using D3 (d3.timeParse() in d3 v4.x and format.parse() in d3 v3.x).

EDIT: Summarising, you have two possible solutions:

Solution 1: Remove all new Date functions (both on the line generator and in all scales that use it) and parse the date using d3. You said that you're using d3 v3.x, but your code is using d3 v4.x instead. Nevertheless, here is how to do it using d3 v3.x:

var date = "2016-12-14 22:01:01.799830".slice(0,-3);
var format = d3.time.format("%Y-%m-%d %H:%M:%S.%L");
var myDate = format.parse(date);

console.log(myDate);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Note: I'm removing the last 3 digits because d3 can parse onli miliseconds, not microseconds.

Solution 2: Keep your new Date functions, but add a T as already discussed above:

var date = "2016-12-14 22:01:01.799830".replace(/\s/, 'T');
console.log(date);
console.log(new Date(date));
Community
  • 1
  • 1
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • 1
    @ofey This is a guess, I cannot test it because I use Chrome on Windows. If this is not the problem, please tell me so I can delete the answer. – Gerardo Furtado Dec 16 '16 at 03:22
  • I also don't use iOS so I've to get a friend to check it. How do I get a T into the date-time? That json data is generated in a python file data[1]["hourlyAverages"].append({"date": str(now),"temp": str(temp)}) – Shane G Dec 16 '16 at 15:31
  • I thought I saw a comment here earlier where someone suggested using the d3 time format. I ma going to try the d3.timeParse() first. Thanks – Shane G Dec 16 '16 at 15:35
  • I will try to get a 'T' in in the python. – Shane G Dec 16 '16 at 15:50
  • Got a T in the time date, dateis = str(now) dateNew = bytearray(dateis) dateNew[10] = "T" dateis = str(dateNew) print dateis – Shane G Dec 16 '16 at 16:09
  • Hoping this might work, .domain([new Date(data[0].date), d3.max(data, function(d) {return new Date(Date(d.date).replace(/\s/, 'T'))})]) – Shane G Dec 16 '16 at 16:13
  • 1
    Take care of adding the T in the domain also (it has the same `new Date()`). Alternatively, as I said, just abandon the `new Date()` and use `de.timeParse`. – Gerardo Furtado Dec 17 '16 at 03:16
  • This isn't working var parseTime = d3.timeParse("%d-%b-%y %H:%M"); var xScale = d3.scaleTime() .domain([d3.parseTime(data[0].date), d3.max(data, function(d) {return parseTime(d.date)})]) .range([0, (w-2*padding)]); – Shane G Dec 20 '16 at 17:10
  • I have also made the same change for the circles and the lines. – Shane G Dec 20 '16 at 17:13
  • I am using d3.v3.min.js Interesting sight for comparing the two versions https://keithpblog.wordpress.com/2016/07/31/upgrading-d3-from-v3-to-v4/ – Shane G Dec 20 '16 at 17:15
  • I am not sure why d3.timeParse() works as this is v.4 and I am using v.3. But I get errors if I replace it with v.3's d3.time.format() – Shane G Dec 20 '16 at 17:22
  • 1
    Try to create a working fiddle or similar, and I'll have a look at it. – Gerardo Furtado Dec 21 '16 at 04:20
  • http://shanespi.no-ip.biz/ from there you will be able to see all the files. thank you – Shane G Dec 21 '16 at 21:20
0

Seems there's a issue with compatibility between dc.js 1.7.5 the stable version and d3.js v4. dc.js compatibility with v4 of d3.js and my new post on this https://stackoverflow.com/questions/41389307/dc-js-1-7-5-not-compatible-with-d3-js-v4

Community
  • 1
  • 1
Shane G
  • 3,129
  • 10
  • 43
  • 85
  • 3
    dc.js is not compatible with d3 v4.x, this is a fact. But stop to think about your question: you're questioning about a code that **works** in Chrome, Firefox etc... but that **doesn't work** in Safari. If that's a dc.js compatibility problem, how do you explain that? dc.js is compatible with d3 on Chrome, but suddenly it's not on Safari? Can you see that this doesn't make sense? – Gerardo Furtado Dec 30 '16 at 04:27
  • Dc does become a problem in all browsers with d3 v4. Why can't I simply graph these dates? http://shanespi.no-ip.biz – Shane G Dec 30 '16 at 08:37
  • 1
    Well, you changed your code, which not only makes my answer useless, but the whole question a completely *different* question. It *was* working before you changed it. – Gerardo Furtado Dec 30 '16 at 08:39
  • I'm not understanding this. When you posted the question, before any edit, on Dec 15, **it was v4** already! And it was working on Chrome, the only problem was Safari. Am I missing something here? – Gerardo Furtado Dec 30 '16 at 08:46
  • 1
    I did switch to v3 and then back to v4. Sorry about that. The issues were, parts of the code used v3 syntax and other parts used v4. Also eliminating the microseconds was needed. The date was not parsed correctly. Works now, thanks for your help. – Shane G Dec 30 '16 at 21:29
0

Wow, this method works commonly in both android and iOS, saved my hours of browsing about this issue

var commonDate = item.created_at.replace(/\s/, "T");
var date = new Date(commonDate);

Long story short: I have used d3 in the ionic angular version and had the same issue in iOS alone

SWAROOP
  • 1
  • 2