Reputation: 89
I am trying to stabilize this chart - I've tried to compare date objects - but then it ran into the issue of summer time differences - I am unsure why the change in data breaks the chart - I am getting an undefined issue.
//working js fiddle http://jsfiddle.net/Qh9X5/10379/
//broken js fiddle http://jsfiddle.net/Qh9X5/10380/
-- so when adding the extra bit of data to the chart - it creates an undefined error
, {
"text-start": "Text9",
"date-start": "2012-05-01",
"text-end": "Text10",
"date-end": "2012-05-03"
}
I thought at first that maybe the yAxis needs to be forced to show 1 day increments -- .ticks(d3.time.days(min, max).length) as if maybe the coordinates do not exist for in-between days.
here is the code for the working chart
var data = [{
"text-start": "Text1",
"date-start": "2012-04-21",
"text-end": "Text2",
"date-end": "2012-04-26"
}, {
"text-start": "Text3",
"date-start": "2012-04-22",
"text-end": "Text4",
"date-end": "2012-04-25"
}, {
"text-start": "Text5",
"date-start": "2012-04-26",
"text-end": "Text6",
"date-end": "2012-04-28"
}, {
"text-start": "Text7",
"date-start": "2012-04-21",
"text-end": "Text8",
"date-end": "2012-04-23"
}];
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 60
},
width = 600,
height = 500;
function colores_google(n) {
var colores_g = ["#e9168a", "#f8dd2f", "#448875", "#c3bd75", "#2b2d39", "#311854", "#553814", "#f7b363", "#89191d", "#c12f34", "#2b2a2c", "#c5b8a6", "#57585b"];
return colores_g[n % colores_g.length];
}
var y = d3.time.scale().range([height - margin.top - margin.bottom, 0]);
var arrayofDates = [];
$.each(data, function(index, value) {
arrayofDates.push(value["date-start"]);
arrayofDates.push(value["date-end"]);
});
var parseDate = d3.time.format("%m/%d/%Y").parse;
var min = new Date(d3.min(arrayofDates.map(d=>d)));
var max = new Date(d3.max(arrayofDates.map(d=>d)));
y.domain([min,max]);
var chart = d3.select('#timelines').append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
//http://stackoverflow.com/questions/22722952/how-can-i-get-the-d3-js-axis-ticks-and-positions-as-an-array
var axisArray = [];
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.tickPadding(1);
chart.append('g')
.attr('class', 'y axis')
.call(yAxis)
.selectAll(".tick").each(function(data) {
var tick = d3.select(this);
// pull the transform data out of the tick
var transform = d3.transform(tick.attr("transform")).translate;
// passed in "data" is the value of the tick, transform[0] holds the X value
//console.log("each tick", data, transform);
var month = data.getMonth() + 1;
if (month < 10) {
month = "0" + month;
}
var datestring = data.getFullYear() + "-" + month + "-" + data.getDate();
var obj = {
"data": datestring,
"transform": transform
}
axisArray.push(obj);
});
var links = chart.append('g')
.attr('class', 'links')
.attr("transform", "translate(0,0)");
function getRadius(d) {
var count = 2;
var ratio = count * 2.8;
if (count == 1) {
ratio = 10;
}
return ratio;
}
function getPos(date) {
var trans;
$.each(axisArray, function(index, value) {
var a = value.data;
var b = date;
//console.log("a", a)
//console.log("b", b)
if (a == b) {
//console.log("value", value.transform)
//if (a.getTime() === b.getTime()) {
trans = value.transform;
}
});
return trans;
}
var distanceBetween = 70;
var paths = links.selectAll("path")
.data(data);
paths.enter()
.append("path")
.attr("class", "link")
.attr("d", function(d, i) {
var sx = 0;
var tx = 0;
//console.log("d", d);
var posEnd = getPos(d["date-start"]);
var posStart = getPos(d["date-end"]);
var sy = posStart[1];
var ty = posEnd[1];
var dx = 0,
dy = getRadius(d) + 15 + (distanceBetween * i),
dr = 10 //Math.sqrt(dx * dx + dy * dy);
return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty;
})
.attr('stroke-width', 1.2)
.attr("stroke", function(d, i) {
//console.log("d", i);
return colores_google(i);
})
Upvotes: 2
Views: 296
Reputation: 1248
-- Stable fixed version http://jsfiddle.net/4v3Ldk93/2/
I managed to fix it: http://jsfiddle.net/4v3Ldk93/
The error is because:
console.log("pos", posEnd, posStart)
Returns:
pos undefined [0, 256.6666564941406]
posEnd is undefined. Hence, you cannot get posEnd[1];
of undefined
. And that is exactly the console error: cannot get attribute 1 of undefined.
This is because, in getPos, the smallest value of axisArray is: { data: "2012-04-22" }
This is not a problem with you date conversion, but because: .selectAll(".tick").each(function(data) {
is only receiving Apr 22 as the first date.
If you are OK with it, you could fix it by adding one day padding to both ends. Add these two lines after your min and max definition:
min.setDate(min.getDate() - 1);
max.setDate(max.getDate() + 1);
If you do not like the extra day at the end, you actually only need to fix this by for the min date. You could simply do:
min.setDate(min.getDate() - 1);
My guess is that domain is set as (min, max]; meaning it includes the upper boundary, but not the lower boundary. However not 100% certain on this, except for the fact that the change works.
Then, you were fixing the 0 in front of your months, but not doing the same for your days:
var day = data.getDate();
if (day < 10) day = "0" + day;
With these two changes, it is working for me.
While I am unsure why this worked on your "working version" and not on the "broken version", these changes also make the working version functional: http://jsfiddle.net/Ljrg9v2v/
Also add .ticks(d3.time.days(min, max).length) to the yAxis to ensure the timeline has 1 day increments.
I hope this helps.
Upvotes: 3