Adapting a histogram with D3 (v3) I find two problems to solve (original code here):
<script type="text/javascript">
var faithfulData = [20,21,26,18,24,24,25,25,21,20,20,18,28,23,17,26,27,27,20,28,23,26,];
var datos_unicos = Array.from(new Set(faithfulData))
var margin = {top: 4, right: 10, bottom: 40, left: 40},
width = 360 - margin.left - margin.right,
height = 180 - margin.top - margin.bottom;
var cant_ticks = datos_unicos.length;
var edad_min = Math.min.apply(Math, datos_unicos) - 3;
var edad_max = Math.max.apply(Math, datos_unicos) + 3;
var vartickValues = []
var tope = (edad_max)+1;
for (var i =edad_min; i< tope; i++) {
vartickValues.push(i);
}
var x = d3.scale.linear()
.domain([edad_min, edad_max])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, .1])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickValues(vartickValues)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format("%"));
var line = d3.svg.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
var histogram = d3.layout.histogram()
.frequency(false)
.bins(cant_ticks);
var svg = d3.select("#plantel_distribucion").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x plantel_axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", 34)
.style("text-anchor", "end")
.text("Edad de las jugadoras");
svg.append("g")
.attr("class", "y plantel_axis")
.call(yAxis);
var data = histogram(faithfulData),
kde = kernelDensityEstimator(epanechnikovKernel(7), x.ticks(100));
svg.selectAll(".plantel_bar")
.data(data)
.enter().insert("rect", ".axis")
.attr("class", "plantel_bar")
.attr("x", function(d) { return x(d.x) + 1; })
.attr("y", function(d) { return y(d.y); })
.attr("width", x(data[0].dx + data[0].x) - x(data[0].x) - 1)
.attr("height", function(d) { return height - y(d.y); });
svg.append("path")
.datum(kde(faithfulData))
.attr("class", "plantel_line")
.attr("d", line);
//});
function kernelDensityEstimator(kernel, x) {
return function(sample) {
return x.map(function(x) {
return [x, d3.mean(sample, function(v) { return kernel(x - v); })];
});
};
}
function epanechnikovKernel(scale) {
return function(u) {
return Math.abs(u /= scale) <= 1 ? .75 * (1 - u * u) / scale : 0;
};
}
</script>
1) How to place the labels on the x axis in the center of the bin? In other words, the tick mark and its label on the center of the bar.
2) How do I place the quantity (frequency) of each bin above its bar?
I appreciate your comments and leave an image with the current development:
Thanks
For question1, if you just want to add some ticks at the center of each bin, there are x and dx attributes in histogram that indicate the position and step of each bin. You can compute the x tick by xtick = x + dx / 2.
For question2, I think you can draw a line chart above the histogram, and set the z-index to be 2.
I hope the above helps. :)
I have been searching for a while about how to handle the X axis in a stacked bar chart (since dataset is a little different from a single bar chart).
Basically, I have data for a 24hr period in 15 minute intervals. However, I only want to display the x-axis in 2hr ticks.
Existing Fiddle: [https://jsfiddle.net/lucksp/crwb4v5u/][1]
It currently prints all the intervals.
I have tried various scale options with time but something doesn't translate with the way I have this setup.
var xScale = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([0, width - margin.left]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.tickSize(0)
.ticks(12)
.tickFormat(function(d) {
return d;
});
var rect = groups.selectAll('rect')
.data(function(d) {
return d;
})
.enter()
.append('rect')
.attr('class', function(d, i) {
return 'stacks ' + d.type;
})
.classed('stacks', true)
.attr('id', function(d, i) {
return d.type + '_' + i;
})
.attr('x', function(d) {
return xScale(d.x);
})
.attr('y', function(d) {
return yScale(d.y0 + d.y);
})
.attr('height', function(d) {
return yScale(d.y0) - yScale(d.y0 + d.y);
})
.attr('width', xScale.rangeBand());
[1]: https://jsfiddle.net/lucksp/crwb4v5u/
I know it's user error, but after looking at this for the last 2 days, I am resorting to asking this question now. Thanks!
You are currently trying to use .ticks which will only work if the scale you're using has an inbuilt ticks function. Your ordinal scale in this case does not. It will by default use all values in the domain.
To go around it, we can manually set the ticks using xAxis.tickValues(["custom tick values that match domain vals"]). Check the snippet below.
var data = [{"hour":"0:00","inProgress":3,"inQueue":0},{"hour":"0:15","inProgress":5,"inQueue":3},{"hour":"0:30","inProgress":1,"inQueue":1},{"hour":"0:45","inProgress":1,"inQueue":0},{"hour":"1:00","inProgress":2,"inQueue":0},{"hour":"1:15","inProgress":8,"inQueue":2},{"hour":"1:30","inProgress":5,"inQueue":3},{"hour":"1:45","inProgress":5,"inQueue":1},{"hour":"2:00","inProgress":6,"inQueue":0},{"hour":"2:15","inProgress":6,"inQueue":0},{"hour":"2:30","inProgress":7,"inQueue":0},{"hour":"2:45","inProgress":7,"inQueue":0},{"hour":"3:00","inProgress":8,"inQueue":0},{"hour":"3:15","inProgress":8,"inQueue":0},{"hour":"3:30","inProgress":9,"inQueue":1},{"hour":"3:45","inProgress":9,"inQueue":4},{"hour":"4:00","inProgress":10,"inQueue":6},{"hour":"4:15","inProgress":10,"inQueue":2},{"hour":"4:30","inProgress":10,"inQueue":1},{"hour":"4:45","inProgress":11,"inQueue":0},{"hour":"5:00","inProgress":11,"inQueue":0},{"hour":"5:15","inProgress":12,"inQueue":0},{"hour":"5:30","inProgress":12,"inQueue":0},{"hour":"5:45","inProgress":13,"inQueue":0},{"hour":"6:00","inProgress":13,"inQueue":0},{"hour":"6:15","inProgress":14,"inQueue":0},{"hour":"6:30","inProgress":14,"inQueue":0},{"hour":"6:45","inProgress":15,"inQueue":0},{"hour":"7:00","inProgress":15,"inQueue":3},{"hour":"7:15","inProgress":15,"inQueue":1},{"hour":"7:30","inProgress":16,"inQueue":0},{"hour":"7:45","inProgress":16,"inQueue":0},{"hour":"8:00","inProgress":17,"inQueue":2},{"hour":"8:15","inProgress":17,"inQueue":3},{"hour":"8:30","inProgress":18,"inQueue":1},{"hour":"8:45","inProgress":18,"inQueue":0},{"hour":"9:00","inProgress":19,"inQueue":0},{"hour":"9:15","inProgress":19,"inQueue":0},{"hour":"9:30","inProgress":20,"inQueue":0},{"hour":"9:45","inProgress":20,"inQueue":0},{"hour":"10:00","inProgress":20,"inQueue":0},{"hour":"10:15","inProgress":21,"inQueue":1},{"hour":"10:30","inProgress":21,"inQueue":4},{"hour":"10:45","inProgress":22,"inQueue":6},{"hour":"11:00","inProgress":22,"inQueue":2},{"hour":"11:15","inProgress":23,"inQueue":1},{"hour":"11:30","inProgress":23,"inQueue":0},{"hour":"11:45","inProgress":3,"inQueue":0},{"hour":"12:00","inProgress":5,"inQueue":0},{"hour":"12:15","inProgress":1,"inQueue":0},{"hour":"12:30","inProgress":1,"inQueue":0},{"hour":"12:45","inProgress":2,"inQueue":0},{"hour":"13:00","inProgress":8,"inQueue":0},{"hour":"13:15","inProgress":5,"inQueue":0},{"hour":"13:30","inProgress":5,"inQueue":0},{"hour":"13:45","inProgress":6,"inQueue":3},{"hour":"14:00","inProgress":6,"inQueue":1},{"hour":"14:15","inProgress":7,"inQueue":0},{"hour":"14:30","inProgress":7,"inQueue":0},{"hour":"14:45","inProgress":8,"inQueue":2},{"hour":"15:00","inProgress":8,"inQueue":3},{"hour":"15:15","inProgress":9,"inQueue":1},{"hour":"15:30","inProgress":9,"inQueue":0},{"hour":"15:45","inProgress":10,"inQueue":0},{"hour":"16:00","inProgress":10,"inQueue":0},{"hour":"16:15","inProgress":10,"inQueue":0},{"hour":"16:30","inProgress":11,"inQueue":0},{"hour":"16:45","inProgress":11,"inQueue":0},{"hour":"17:00","inProgress":12,"inQueue":1},{"hour":"17:15","inProgress":12,"inQueue":4},{"hour":"17:30","inProgress":13,"inQueue":6},{"hour":"17:45","inProgress":13,"inQueue":2},{"hour":"18:00","inProgress":14,"inQueue":1},{"hour":"18:15","inProgress":14,"inQueue":0},{"hour":"18:30","inProgress":15,"inQueue":0},{"hour":"18:45","inProgress":15,"inQueue":0},{"hour":"19:00","inProgress":15,"inQueue":0},{"hour":"19:15","inProgress":16,"inQueue":0},{"hour":"19:30","inProgress":16,"inQueue":0},{"hour":"19:45","inProgress":17,"inQueue":0},{"hour":"20:00","inProgress":17,"inQueue":0},{"hour":"20:15","inProgress":18,"inQueue":0},{"hour":"20:30","inProgress":18,"inQueue":3},{"hour":"20:45","inProgress":19,"inQueue":1},{"hour":"21:00","inProgress":19,"inQueue":0},{"hour":"21:15","inProgress":20,"inQueue":0},{"hour":"21:30","inProgress":20,"inQueue":2},{"hour":"21:45","inProgress":20,"inQueue":3},{"hour":"22:00","inProgress":21,"inQueue":1},{"hour":"22:15","inProgress":21,"inQueue":0},{"hour":"22:30","inProgress":22,"inQueue":0},{"hour":"22:45","inProgress":22,"inQueue":0},{"hour":"23:00","inProgress":23,"inQueue":0},{"hour":"23:15","inProgress":23,"inQueue":0},{"hour":"23:30","inProgress":1,"inQueue":0},{"hour":"23:45","inProgress":2,"inQueue":1}];
var margin = {top: 20, right: 50, bottom: 30, left: 20},
width = 500,
height = 300;
// Transpose the data into layers
var dataset = d3.layout.stack()(['inProgress', 'inQueue'].map(function(types) {
return data.map(function(d) {
return {
x: d.hour,
y: +d[types],
type: types
};
});
}));
var svg = d3.select('svg'),
margin = {top: 40, right: 10, bottom: 20, left: 10},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Set x, y and colors
var xScale = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([0, width - margin.left]);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})])
.range([height, 0]);
var colors = ['#56a8f8', '#c34434'];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.ticks(5)
.tickSize(0)
.tickFormat(function(d) {
return d;
});
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.tickSize(0)
.ticks(12) // this
.tickFormat(function(d) {
return d; // and this will not work with an ordinal scale
});
xAxis.tickValues(["0:00", "2:00", "4:00", "6:00", "8:00", "10:00", "12:00", "14:00", "16:00", "18:00", "20:00", "22:00"]);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll('g.bar-stacks')
.data(dataset)
.enter().append('g')
.attr('class', function(d, i) {
return 'bar-stacks ' + d[i].type;
})
.classed('bar-stacks', true)
.style('fill', function(d, i) {
return colors[i];
});
var rect = groups.selectAll('rect')
.data(function(d) {
return d;
})
.enter()
.append('rect')
.attr('class', function(d, i) {
return 'stacks ' + d.type;
})
.classed('stacks', true)
.attr('id', function(d, i) {
return d.type + '_' + i;
})
.attr('x', function(d) {
return xScale(d.x);
})
.attr('y', function(d) {
return yScale(d.y0 + d.y);
})
.attr('height', function(d) {
return yScale(d.y0) - yScale(d.y0 + d.y);
})
.attr('width', xScale.rangeBand());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div>
<svg width="600" height="300"></svg>
</div>
The graphic below is the current output of my program, which is basically working. The problem is, it draws the graph from left to right, plotting the JSON data it receives, but then goes off the edge of the screen (or into the bit bucket), when what I want is for the graph to scroll to the left as the JSON data is consumed. You can see the data is cutoff past the "9" label.
I'm not sure where to begin making that happen and would appreciate some advice.
Current output:
function createLineChart(data, number) {
var xy_chart = d3_xy_chart()
.width(960)
.height(500)
.xlabel("TCS")
.ylabel("STATUS");
var svg = d3.select(".lineChart" + number).append("svg")
.datum(data)
.call(xy_chart);
function d3_xy_chart() {
var width = 640,
height = 480,
xlabel = "X Axis Label",
ylabel = "Y Axis Label";
function chart(selection, svg) {
selection.each(function (datasets) {
//
// Create the plot.
var margin = {top: 20, right: 80, bottom: 30, left: 50},
innerwidth = width - margin.left - margin.right,
innerheight = height - margin.top - margin.bottom;
var x_scale = d3.scale.linear()
.range([0, innerwidth])
.domain([d3.min(datasets, function (d) {
return d3.min(d.x);
}),
d3.max(datasets, function (d) {
return d3.max(d.x);
})]);
// Set y scale
var y_scale = d3.scale.linear()
.range([innerheight, 0])
.domain([d3.min(datasets, function (d) {
return d3.min(d.y);
}),
d3.max(datasets, function (d) {
return d3.max(d.y);
})]);
var color_scale = d3.scale.category10()
.domain(d3.range(datasets.length));
var x_axis = d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.tickFormat(function (d, i) {
// Remove remove decimal points and set values again to the x axis
if (d % 1 == 0) {
return parseInt(d)
} else {
return " "
}
});
var y_axis = d3.svg.axis()
.scale(y_scale)
.orient("left")
.tickFormat(function (d, i) {
if (d == "1") {
return "NOT EXECUTED"
} else if (d == "2") {
return "FAILED"
} else if (d == "3") {
return "PASSED"
} else {
return " "
}
});
var x_grid = d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.tickSize(-innerheight)
.tickFormat("");
var y_grid = d3.svg.axis()
.scale(y_scale)
.orient("left")
.tickSize(-innerwidth)
.tickFormat("");
// Draw the line
var draw_line = d3.svg.line()
.interpolate("linear")
.x(function (d) {
return x_scale(d[0]);
})
.y(function (d) {
return y_scale(d[1]);
});
var svg = d3.select(this)
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Append g as x grid to svg
svg.append("g")
.attr("class", "x grid")
.attr("transform", "translate(0," + innerheight + ")")
.call(x_grid);
svg.append("g")
.attr("class", "y grid")
.call(y_grid);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + innerheight + ")")
.call(x_axis)
.append("text")
.attr("dy", "-.71em")
.attr("x", innerwidth)
.style("text-anchor", "end")
.text(xlabel);
// Append g as x axis to svg
svg.append("g")
.attr("class", "y axis")
.call(y_axis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.style("text-anchor", "end")
.text(ylabel);
var data_lines = svg.selectAll(".d3_xy_chart_line")
.data(datasets.map(function (d) {
return d3.zip(d.x, d.y);
}))
.enter().append("g")
.attr("class", "d3_xy_chart_line");
data_lines.append("path")
.attr("class", "line")
.attr("d", function (d) {
return draw_line(d);
})
.attr("stroke", function (_, i) {
return color_scale(i);
});
// Set label texts
data_lines.append("text")
.datum(function (d, i) {
return {name: datasets[i].label, final: d[d.length - 1]};
})
.attr("transform", function (d) {
return ( "translate(" + x_scale(d.final[0]) + "," +
y_scale(d.final[1]) + ")" );
})
.attr("x", 3)
.attr("dy", ".35em")
.attr("fill", function (_, i) {
return color_scale(i);
})
.text(function (d) {
return d.name;
});
});
}
chart.width = function (value) {
if (!arguments.length) return width;
width = value;
return chart;
};
chart.height = function (value) {
if (!arguments.length) return height;
height = value;
return chart;
};
chart.xlabel = function (value) {
if (!arguments.length) return xlabel;
xlabel = value;
return chart;
};
chart.ylabel = function (value) {
if (!arguments.length) return ylabel;
ylabel = value;
return chart;
};
return chart;
}
}
I'm trying to adapt this D3js line chart example of mouse-over usage to my multi-line chart.
It seems that d3.mouse(this)[0] on the mousemove function generate the following error: "Cannot read property 'sourceEvent' of null".
1 Any idea why I get the null sourceEvent error ?
2 Any tips on how to adapt the mouse over example from a single line chart to a multi (n) line chart ?
Here is a jsfiddle to demonstrate the issue.
( And the solution )
var myApp = angular.module('app', []);
myApp.directive("lineChart", function() {
return {
restrict: 'E',
scope: {
data: '=',
id: '#'
},
link: function (scope, element, attrs) {
scope.$watch( 'data', function ( data ) {
d3.select("#"+attrs.id).select("svg").remove();
if (data) {
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = element[ 0 ].parentElement.offsetWidth - margin.left - margin.right,
height = element[ 0 ].parentElement.offsetHeight - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.ticks(4)
.outerTickSize(0)
.tickPadding(5)
.tickFormat(function(d) { return d3.time.format('%d/%m %H:%M')(new Date(d)); });
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var line = d3.svg.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
var svg = d3.select(element[0]).append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+ element[ 0 ].parentElement.offsetWidth +' '+ element[ 0 ].parentElement.offsetHeight )
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var minX = d3.min(data, function (item) { return d3.min(item.values, function (d) { return d[0]; }) });
var maxX = d3.max(data, function (item) { return d3.max(item.values, function (d) { return d[0]; }) });
var minY = d3.min(data, function (item) { return d3.min(item.values, function (d) { return d[1]; }) });
var maxY = d3.max(data, function (item) { return d3.max(item.values, function (d) { return d[1]; }) });
x.domain([minX, maxX]);
y.domain([0, maxY]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var domaine = svg.selectAll(".domaine")
.data(data)
.enter().append("g")
.attr("class", "domaine");
domaine.append("path")
.attr("class", "line")
.attr("d", function (d) {
return line(d.values);
})
.style("stroke", function (d) {
return d.color;
});
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove());
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i];/*
To adapt for multi line
,
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")");
focus.select("text").text(formatCurrency(d.close));*/
}
}
});
}
};
});
function MainCtrl($scope) {
$scope.lineData = [{"key": "users","color": "#16a085",
"values": [[1413814800000,4034.418],[1413815400000,5604.155000000001]]},
{"key": "users 2","color": "#d95600",
"values": [[1413814800000,3168.183],[1413815400000,1530.8435]]}];
}
When you write
.on("mousemove", mousemove());
it is immediately call the mousemove function and passes its return value as the listener function to the "mousemove" event. Because you are not setting correctly the this that is why d3.mouse(this) returns null.
The fix is very easy: just pass in your mousemove function as reference and don't call it:
.on("mousemove", mousemove);
However even after this fix you will still get an error in the fiddle because your bisectDate function is missing...
I have a line chart with several axes, using zooming and panning. Works fine, however when I pan or zoom, my lines extend outside the 'graph' component. I've tried following this example, adding a clipPath to my chart.
When highlighting the code in chromes code inspector, I see the following (which at least indicates that I've got a path present I guess)
Here's my grid:
<script>
/* d3 vars */
var x;
var y1;
var y2;
var y3;
var graph;
var m = [];
var w;
var h;
/* d3 axes */
var xAxis;
var yAxisLeft;
var yAxisLeftLeft;
var yAxisRight;
/* d3 lines */
var line1;
var line2;
var line3;
/* d3 zoom */
var zoom;
var zoomLeftLeft;
var zoomRight;
/* Data */
var speed = [];
var depth = [];
var weight = [];
var timestamp = [];
var url = '#Url.Action("DataBlob", "Trend", new {id = Model.Unit.UnitId, runId = Request.Params["runId"]})';
var data = $.getJSON(url, null, function(data) {
var list = JSON.parse(data);
var format = d3.time.format("%Y-%m-%dT%H:%M:%S").parse;
list.forEach(function(d) {
speed.push(d.Speed);
depth.push(d.Depth);
weight.push(d.Weight);
var date = format(d.Time);
d.Time = date;
timestamp.push(d.Time);
});
m = [10, 80, 30, 100]; // margins: top, right, bottom, left
w = $("#trendcontainer").width() - m[1] - m[3]; // width
h = 550 - m[0] - m[2]; // height
x = d3.time.scale().domain(d3.extent(timestamp, function (d) {
return d;
})).range([0, w]);
y1 = d3.scale.linear().domain([0, d3.max(speed)]).range([h, 0]);
y2 = d3.scale.linear().domain([0, d3.max(depth)]).range([h, 0]);
y3 = d3.scale.linear().domain([0, d3.max(weight)]).range([h, 0]);
line1 = d3.svg.line()
.interpolate("basis")
.x(function (d, i) {
return x(timestamp[i]);
})
.y(function (d) {
return y1(d);
});
line2 = d3.svg.line()
.interpolate("basis")
.x(function (d, i) {
return x(timestamp[i]);
})
.y(function (d) {
return y2(d);
});
line3 = d3.svg.line()
.interpolate("basis")
.x(function (d, i) {
return x(timestamp[i]);
})
.y(function (d) {
return y3(d);
});
zoom = d3.behavior.zoom()
.x(x)
.y(y1)
.scaleExtent([1, 10])
.on("zoom", zoomed);
zoomLeftLeft = d3.behavior.zoom()
.x(x)
.y(y3)
.scaleExtent([1, 10]);
zoomRight = d3.behavior.zoom()
.x(x)
.y(y2)
.scaleExtent([1, 10]);
// Add an SVG element with the desired dimensions and margin.
graph = d3.select(".panel-body").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.call(zoom)
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
graph.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", w)
.attr("height", h);
// create xAxis
xAxis = d3.svg.axis().scale(x).tickSize(-h).tickSubdivide(false);
// Add the x-axis.
graph.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
// create left yAxis
yAxisLeft = d3.svg.axis().scale(y1).ticks(10).orient("left");
// Add the y-axis to the left
graph.append("svg:g")
.attr("class", "y axis axisLeft")
.attr("transform", "translate(-15,0)")
.call(yAxisLeft);
// create leftleft yAxis
yAxisLeftLeft = d3.svg.axis().scale(y3).ticks(10).orient("left");
// Add the y-axis to the left
graph.append("svg:g")
.attr("class", "y axis axisLeftLeft")
.attr("transform", "translate(-50,0)")
.call(yAxisLeftLeft);
// create right yAxis
yAxisRight = d3.svg.axis().scale(y2).ticks(10).orient("right");
// Add the y-axis to the right
graph.append("svg:g")
.attr("class", "y axis axisRight")
.attr("transform", "translate(" + (w + 15) + ",0)")
.call(yAxisRight);
// add lines
// do this AFTER the axes above so that the line is above the tick-lines
graph.append("svg:path").attr("d", line1(speed)).attr("class", "y1");
graph.append("svg:path").attr("d", line2(depth)).attr("class", "y2");
graph.append("svg:path").attr("d", line3(weight)).attr("class", "y3");
});
function zoomed() {
zoomRight.scale(zoom.scale()).translate(zoom.translate());
zoomLeftLeft.scale(zoom.scale()).translate(zoom.translate());
graph.select(".x.axis").call(xAxis);
graph.select(".y.axisLeft").call(yAxisLeft);
graph.select(".y.axisLeftLeft").call(yAxisLeftLeft);
graph.select(".y.axisRight").call(yAxisRight);
graph.select(".x.grid")
.call(make_x_axis()
.tickFormat(""));
graph.select(".y.axis")
.call(make_y_axis()
.tickSize(5, 0, 0));
graph.selectAll(".y1")
.attr("d", line1(speed));
graph.selectAll(".y2")
.attr("d", line2(depth));
graph.selectAll(".y3")
.attr("d", line3(weight));
};
var make_x_axis = function () {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5);
};
var make_y_axis = function () {
return d3.svg.axis()
.scale(y1)
.orient("left")
.ticks(5);
};
</script>
Any suggestions on what I'm missing here?
Solved by appending the following to the lines being drawn:
graph.append("svg:path").attr("d", line1(speed)).attr("class", "y1").attr("clip-path", "url(#clip)");
graph.append("svg:path").attr("d", line2(depth)).attr("class", "y2").attr("clip-path", "url(#clip)");
graph.append("svg:path").attr("d", line3(weight)).attr("class", "y3").attr("clip-path", "url(#clip)");