Reputation: 569
I am trying to display data in a line graph within a 24 hr period.
I have created a d3 v4 line graph, and have successfully:
My issue is now trying to apply translateExtent to my d3.zoom. It seems to keep me from panning left/right before zooming, but when I zoom, it seems to cut off or add on a half hour or so to the end and beginning of my data set.
I think all I need to do is maybe add some code to my d3.zoom declaration, and maybe also add something into my zoom function.
I have tried adding .translateExtent([[0,0],[w,h]]) to my d3.zoom code and so many other variations that included subtracting/adding my margins, etc.
It looks like adding the w"2*margin like below takes away the jumping on initial click. (which confuses me because I would assume the first coordinate group should affect the left panning of my graph) but once I zoom in, it keeps me from panning left beyond the beginning of the day (yay) but allows me to pan a little past the end of the day (boo).
var zoomCall = d3.zoom()
.scaleExtent([1, 24]) // no zooming out (1x), only zoom to 1hr (24x)
.translateExtent([[0,0],[w+2*margin,h]])
.on("zoom", zoom);
I am also thinking in my zoom function of multiplying my width extent by d3.event.transform.k which should be the zoom level i believe, but it is not giving me what I want either:
function zoom() {
zoomCall.translateExtent([[0,0],[w+margin*2*d3.event.transform.k, h]]);
There also sometimes is a translation of my data left or right 20px just when clicking on the graph once after the translateExtent code is added.
window.onload = function() {
// parse the date / time functions
var formatTime = d3.timeFormat("%H:%M"),
formatMinutes = function(d) {
return formatTime(new Date(2012, 0, 1, 0, d));
};
// Get and Prepare Data for gap_array and range_array
var count_data = [
{
"date_time":"\/Date(1494864780000)\/",
"count":14
},
{
"date_time":"\/Date(1494864780000)\/",
"count":11
},
{
"date_time":"\/Date(1494864780000)\/",
"count":25
},
{
"date_time":"\/Date(1494864840000)\/",
"count":31
},
{
"date_time":"\/Date(1494864840000)\/",
"count":53
},
{
"date_time":"\/Date(1494864840000)\/",
"count":56
},
{
"date_time":"\/Date(1494864900000)\/",
"count":57
},
{
"date_time":"\/Date(1494864960000)\/",
"count":68
},
{
"date_time":"\/Date(1494865020000)\/",
"count":69
},
{
"date_time":"\/Date(1494865020000)\/",
"count":70
},
{
"date_time":"\/Date(1494865140000)\/",
"count":65
},
{
"date_time":"\/Date(1494865200000)\/",
"count":68
},
{
"date_time":"\/Date(1494865320000)\/",
"count":68
},
{
"date_time":"\/Date(1494865380000)\/",
"count":68
},
{
"date_time":"\/Date(1494865500000)\/",
"count":70
},
{
"date_time":"\/Date(1494865560000)\/",
"count":67
},
{
"date_time":"\/Date(1494865680000)\/",
"count":61
},
{
"date_time":"\/Date(1494865740000)\/",
"count":61
},
{
"date_time":"\/Date(1494865860000)\/",
"count":67
},
{
"date_time":"\/Date(1494865920000)\/",
"count":67
},
{
"date_time":"\/Date(1494866040000)\/",
"count":67
},
{
"date_time":"\/Date(1494866100000)\/",
"count":67
},
{
"date_time":"\/Date(1494866220000)\/",
"count":68
},
{
"date_time":"\/Date(1494866280000)\/",
"count":66
},
{
"date_time":"\/Date(1494866400000)\/",
"count":67
},
{
"date_time":"\/Date(1494866460000)\/",
"count":67
},
{
"date_time":"\/Date(1494866580000)\/",
"count":68
},
{
"date_time":"\/Date(1494866640000)\/",
"count":66
},
{
"date_time":"\/Date(1494866760000)\/",
"count":67
},
{
"date_time":"\/Date(1494866820000)\/",
"count":67
},
{
"date_time":"\/Date(1494866940000)\/",
"count":67
},
{
"date_time":"\/Date(1494867000000)\/",
"count":67
},
{
"date_time":"\/Date(1494867120000)\/",
"count":53
},
{
"date_time":"\/Date(1494867180000)\/",
"count":66
},
{
"date_time":"\/Date(1494867300000)\/",
"count":72
},
{
"date_time":"\/Date(1494867360000)\/",
"count":67
},
{
"date_time":"\/Date(1494867480000)\/",
"count":69
},
{
"date_time":"\/Date(1494867540000)\/",
"count":70
},
{
"date_time":"\/Date(1494867660000)\/",
"count":61
},
{
"date_time":"\/Date(1494867720000)\/",
"count":28
},
{
"date_time":"\/Date(1494867720000)\/",
"count":10
},
{
"date_time":"\/Date(1494867840000)\/",
"count":60
},
{
"date_time":"\/Date(1494867900000)\/",
"count":60
},
{
"date_time":"\/Date(1494868020000)\/",
"count":50
},
{
"date_time":"\/Date(1494868020000)\/",
"count":42
},
{
"date_time":"\/Date(1494868140000)\/",
"count":63
},
{
"date_time":"\/Date(1494868200000)\/",
"count":63
},
{
"date_time":"\/Date(1494868320000)\/",
"count":63
},
{
"date_time":"\/Date(1494868380000)\/",
"count":61
},
{
"date_time":"\/Date(1494868500000)\/",
"count":63
},
{
"date_time":"\/Date(1494868560000)\/",
"count":44
},
{
"date_time":"\/Date(1494868560000)\/",
"count":14
},
{
"date_time":"\/Date(1494868680000)\/",
"count":35
},
{
"date_time":"\/Date(1494868740000)\/",
"count":73
},
{
"date_time":"\/Date(1494868860000)\/",
"count":63
},
{
"date_time":"\/Date(1494868920000)\/",
"count":63
},
{
"date_time":"\/Date(1494869040000)\/",
"count":58
},
{
"date_time":"\/Date(1494869100000)\/",
"count":63
},
{
"date_time":"\/Date(1494869220000)\/",
"count":63
},
{
"date_time":"\/Date(1494869280000)\/",
"count":62
},
{
"date_time":"\/Date(1494869400000)\/",
"count":62
},
{
"date_time":"\/Date(1494869460000)\/",
"count":63
},
{
"date_time":"\/Date(1494869580000)\/",
"count":63
},
{
"date_time":"\/Date(1494869640000)\/",
"count":55
},
{
"date_time":"\/Date(1494869700000)\/",
"count":63
},
{
"date_time":"\/Date(1494869820000)\/",
"count":63
},
{
"date_time":"\/Date(1494869880000)\/",
"count":58
},
{
"date_time":"\/Date(1494869880000)\/",
"count":58
},
{
"date_time":"\/Date(1494869880000)\/",
"count":58
},
{
"date_time":"\/Date(1494870000000)\/",
"count":35
},
{
"date_time":"\/Date(1494870060000)\/",
"count":43
},
{
"date_time":"\/Date(1494870120000)\/",
"count":53
},
{
"date_time":"\/Date(1494870180000)\/",
"count":0
},
{
"date_time":"\/Date(1494870180000)\/",
"count":6
},
{
"date_time":"\/Date(1494870180000)\/",
"count":8
},
{
"date_time":"\/Date(1494870240000)\/",
"count":0
},
{
"date_time":"\/Date(1494870240000)\/",
"count":4
},
{
"date_time":"\/Date(1494870240000)\/",
"count":0
},
{
"date_time":"\/Date(1494870240000)\/",
"count":6
},
{
"date_time":"\/Date(1494870300000)\/",
"count":0
},
{
"date_time":"\/Date(1494870300000)\/",
"count":6
},
{
"date_time":"\/Date(1494870300000)\/",
"count":15
},
{
"date_time":"\/Date(1494870300000)\/",
"count":14
},
{
"date_time":"\/Date(1494870300000)\/",
"count":22
},
{
"date_time":"\/Date(1494870360000)\/",
"count":27
},
{
"date_time":"\/Date(1494870420000)\/",
"count":63
},
{
"date_time":"\/Date(1494870540000)\/",
"count":63
},
{
"date_time":"\/Date(1494870600000)\/",
"count":67
},
{
"date_time":"\/Date(1494870720000)\/",
"count":63
},
{
"date_time":"\/Date(1494870780000)\/",
"count":63
},
{
"date_time":"\/Date(1494870900000)\/",
"count":54
},
{
"date_time":"\/Date(1494870900000)\/",
"count":0
},
{
"date_time":"\/Date(1494870900000)\/",
"count":6
},
{
"date_time":"\/Date(1494870960000)\/",
"count":15
},
{
"date_time":"\/Date(1494871020000)\/",
"count":13
},
{
"date_time":"\/Date(1494871020000)\/",
"count":0
},
{
"date_time":"\/Date(1494871080000)\/",
"count":0
},
{
"date_time":"\/Date(1494871200000)\/",
"count":0
},
{
"date_time":"\/Date(1494871260000)\/",
"count":0
},
{
"date_time":"\/Date(1494871380000)\/",
"count":0
},
{
"date_time":"\/Date(1494871440000)\/",
"count":0
},
{
"date_time":"\/Date(1494871560000)\/",
"count":0
},
{
"date_time":"\/Date(1494871620000)\/",
"count":0
},
{
"date_time":"\/Date(1494888600000)\/",
"count":6
},
{
"date_time":"\/Date(1494888600000)\/",
"count":6
},
{
"date_time":"\/Date(1494888720000)\/",
"count":0
},
{
"date_time":"\/Date(1494888780000)\/",
"count":0
},
{
"date_time":"\/Date(1494888900000)\/",
"count":0
},
{
"date_time":"\/Date(1494888960000)\/",
"count":0
},
{
"date_time":"\/Date(1494889080000)\/",
"count":0
},
{
"date_time":"\/Date(1494889140000)\/",
"count":0
},
{
"date_time":"\/Date(1494889260000)\/",
"count":0
},
{
"date_time":"\/Date(1494889320000)\/",
"count":0
},
{
"date_time":"\/Date(1494889380000)\/",
"count":4
},
{
"date_time":"\/Date(1494889380000)\/",
"count":4
},
{
"date_time":"\/Date(1494889380000)\/",
"count":0
},
{
"date_time":"\/Date(1494889380000)\/",
"count":5
},
{
"date_time":"\/Date(1494889380000)\/",
"count":0
},
{
"date_time":"\/Date(1494889500000)\/",
"count":0
},
{
"date_time":"\/Date(1494889560000)\/",
"count":0
},
{
"date_time":"\/Date(1494889680000)\/",
"count":0
},
{
"date_time":"\/Date(1494889740000)\/",
"count":4
},
{
"date_time":"\/Date(1494889740000)\/",
"count":0
},
{
"date_time":"\/Date(1494889860000)\/",
"count":0
},
{
"date_time":"\/Date(1494889920000)\/",
"count":0
},
{
"date_time":"\/Date(1494889920000)\/",
"count":4
},
{
"date_time":"\/Date(1494889980000)\/",
"count":0
},
{
"date_time":"\/Date(1494889980000)\/",
"count":0
},
{
"date_time":"\/Date(1494890220000)\/",
"count":5
},
{
"date_time":"\/Date(1494890220000)\/",
"count":5
},
{
"date_time":"\/Date(1494890280000)\/",
"count":6
},
{
"date_time":"\/Date(1494890340000)\/",
"count":0
},
{
"date_time":"\/Date(1494890400000)\/",
"count":0
},
{
"date_time":"\/Date(1494890520000)\/",
"count":0
},
{
"date_time":"\/Date(1494890580000)\/",
"count":0
},
{
"date_time":"\/Date(1494890700000)\/",
"count":0
},
{
"date_time":"\/Date(1494890760000)\/",
"count":0
},
{
"date_time":"\/Date(1494890880000)\/",
"count":0
},
];
count_data.forEach(function(d) {
d.date_time = new Date(parseInt(d.date_time.replace(/\/Date\((-?\d+)\)\//gi, "$1")));
//d.date_time = formatTime(d.date_time);
});
var maxY = d3.max(count_data, function(d) {
return d.count;
});
var minY = 0;
var minX = new Date(count_data[0].date_time).setHours(0, 0, 0, 0);
var maxX = new Date(count_data[0].date_time).setHours(23, 59, 59, 999);
var gap_array = [];
var range_array = [];
var range_start = moment(new Date(firstDateTime)),
range_end,
range_start_position = 0,
range_end_position,
in_range = true;
var gap_start,
gap_end,
gap_start_position,
gap_end_position,
in_gap = false,
gap_min = 2;
var firstDateTime = count_data[0].date_time;
var lastDateTime = count_data[count_data.length - 1].date_time;
var date_previous = moment(new Date(firstDateTime));
for (i = 0; i < count_data.length; i++) {
var current = moment(new Date(count_data[i].date_time));
if (current.diff(date_previous, "m") > gap_min && !in_gap) {
range_end = date_previous;
range_end_position = i - 1;
in_range = !in_range;
range_array.push({
start: range_start.toDate(),
end: range_end.toDate(),
start_position: range_start_position,
end_position: range_end_position
});
gap_start = date_previous;
gap_start_position = i;
in_gap = !in_gap;
} else if (current.diff(date_previous, "m") <= gap_min && in_gap) {
range_start_position = i;
range_start = current;
in_range = !in_range;
gap_end_position = i;
gap_end = current;
in_gap = !in_gap;
gap_array.push({
start: gap_start.toDate(),
end: gap_end.toDate(),
start_position: gap_start_position,
end_position: gap_end_position
});
} else if (i == count_data.length - 1) {
if (in_gap) {
gap_end_position = i;
gap_end = current;
in_gap = !in_gap;
gap_array.push({
start: gap_start.toDate(),
end: gap_end.toDate(),
start_position: gap_start_position,
end_position: gap_end_position
});
} else {
range_end = current;
range_end_position = i;
in_range = !in_range;
range_array.push({
start: range_start.toDate(),
end: range_end.toDate(),
start_position: range_start_position,
end_position: range_end_position
});
}
}
date_previous = current;
}
gap_array.push({
start: minX,
end: firstDateTime,
start_position: null,
end_position: 0
}); // push initial gap
gap_array.push({
start: lastDateTime,
end: maxX,
start_position: count_data.length - 1,
end_position: null
}); // push final gap
var count_data_array = [];
for (i = 0; i < range_array.length; i++) {
count_data_array.push(count_data.slice(range_array[i].start_position, range_array[i].end_position + (range_array[i].end_position < count_data.length - 1 ? 1 : 0)));
}
//
var margin = 40;
var w = document.getElementById("d3-chart").offsetWidth - margin - margin,
h = 250 - margin - margin;
var x = d3.scaleTime().range([0, w]).domain([minX, maxX]);
var y = d3.scaleLinear().range([h, 0]).domain([minY, maxY]);
var zoomCall = d3.zoom()
.scaleExtent([1, 24]) // no zooming out (1x), only zoom to 1hr (24x)
.on("zoom", zoom);
var container = d3.select("#d3-chart");
var svg = container.append("svg")
.attr("width", w + margin + margin)
.attr("height", h + margin + margin);
var g = svg.append("g")
.attr("transform",
"translate(" + margin + "," + margin + ")")
.call(zoomCall);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", w)
.attr("height", h);
var xAxis = d3.axisBottom(x).ticks(20); //.tickFormat(formatMinutes);
var yAxis = d3.axisLeft(y);
function zoom() {
// re-scale y axis during zoom;
xAxisCall.transition()
.duration(50)
.call(xAxis.scale(d3.event.transform.rescaleX(x)));
// re-draw circles using new y-axis scale;
var new_xScale = d3.event.transform.rescaleX(x);
var new_valueline = d3.line()
.x(function(d) {
return new_xScale(d.date_time);
})
.y(function(d) {
return y(d.count);
});
gaps.attr("x", d => new_xScale(d.start)).attr("width", d => new_xScale(d.end) - new_xScale(d.start)).attr("clip-path", "url(#clip)");
for (i = 0; i < paths.length; i++) {
paths[i]
.attr("d", new_valueline).attr("clip-path", "url(#clip)");
}
}
var gaps = g.selectAll('.gap')
.data(gap_array)
.enter().append('rect')
.attr("class", "gap")
.attr("x", d => x(d.start))
.attr("y", 0)
.attr("width", d => x(d.end) - x(d.start))
.attr("height", h)
.attr("fill", "#efefef");
var xAxisCall = g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
var yAxisCall = g.append("g")
.attr("class", "axis")
.call(yAxis);
// define the line
var valueline = d3.line()
.x(function(d) {
return x(d.date_time);
})
.y(function(d) {
return y(d.count);
});
var paths = [];
// Add the valueline path.
for (i = 0; i < count_data_array.length; i++) {
paths.push(
g.append("path")
.data([count_data_array[i]])
.attr("class", "line")
.attr("d", valueline)
);
}
g.append("rect")
.attr("class", "overlay")
.attr("width", w)
.attr("height", h)
};
#d3-chart {
position: relative;
}
.axis {
font: 14px sans-serif;
}
.line {
fill: none;
stroke: #3988ff;
stroke-width: 2px;
}
.overlay {
fill: none;
pointer-events: all;;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.js"></script>
<h3>D3 Test</h3>
<div id="d3-chart"></div>
Upvotes: 1
Views: 1802
Reputation: 569
Not sure logically why, but the margin offset needs to be divided by the zoom factor.
function zoom() {
var zoomFactor = (d3.event != null ? d3.event.transform.k : 1);
zoomCall.translateExtent([[0,0],[w+((2*margin)/zoomFactor), h]]);
Upvotes: 0