Added lineChartDaily example, and modified line and lineWithLegend models to feed getX and getY x,i not just i

master-patched
Bob Monteverde 12 years ago
parent 4737ead469
commit 83d56ef755

File diff suppressed because one or more lines are too long

@ -631,7 +631,11 @@ nv.models.line = function() {
function chart(selection) {
selection.each(function(data) {
var seriesData = data.map(function(d) { return d.values }),
var seriesData = data.map(function(d) {
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i) }
})
}),
availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom;
@ -639,10 +643,10 @@ nv.models.line = function() {
y0 = y0 || y;
x .domain(xDomain || d3.extent(d3.merge(seriesData), getX ))
x .domain(xDomain || d3.extent(d3.merge(seriesData), function(d) { return d.x } ))
.range([0, availableWidth]);
y .domain(yDomain || d3.extent(d3.merge(seriesData), getY ))
y .domain(yDomain || d3.extent(d3.merge(seriesData), function(d) { return d.y } ))
.range([availableHeight, 0]);
@ -685,7 +689,7 @@ nv.models.line = function() {
var vertices = d3.merge(data.map(function(line, lineIndex) {
return line.values.map(function(point, pointIndex) {
//return [x(getX(point)), y(getY(point)), lineIndex, pointIndex]; //inject series and point index for reference into voronoi
return [x(getX(point)) * (Math.random() / 1e12 + 1) , y(getY(point)) * (Math.random() / 1e12 + 1), lineIndex, pointIndex]; //temp hack to add noise untill I think of a better way so there are no duplicates
return [x(getX(point, pointIndex)) * (Math.random() / 1e12 + 1) , y(getY(point, pointIndex)) * (Math.random() / 1e12 + 1), lineIndex, pointIndex]; //temp hack to add noise untill I think of a better way so there are no duplicates
})
})
);
@ -725,7 +729,7 @@ nv.models.line = function() {
dispatch.pointMouseover({
point: point,
series:series,
pos: [x(getX(point)) + margin.left, y(getY(point)) + margin.top],
pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
seriesIndex: d.series,
pointIndex: d.point
});
@ -778,40 +782,40 @@ nv.models.line = function() {
.data(function(d, i) { return [d.values] });
paths.enter().append('path')
.attr('d', d3.svg.line()
.x(function(d) { return x0(getX(d)) })
.y(function(d) { return y0(getY(d)) })
.x(function(d,i) { return x0(getX(d,i)) })
.y(function(d,i) { return y0(getY(d,i)) })
);
//d3.transition(paths.exit())
d3.transition(lines.exit().selectAll('path'))
.attr('d', d3.svg.line()
.x(function(d) { return x(getX(d)) })
.y(function(d) { return y(getY(d)) })
.x(function(d,i) { return x(getX(d,i)) })
.y(function(d,i) { return y(getY(d,i)) })
)
.remove();
d3.transition(paths)
.attr('d', d3.svg.line()
.x(function(d) { return x(getX(d)) })
.y(function(d) { return y(getY(d)) })
.x(function(d,i) { return x(getX(d,i)) })
.y(function(d,i) { return y(getY(d,i)) })
);
var points = lines.selectAll('circle.point')
.data(function(d) { return d.values });
points.enter().append('circle')
.attr('cx', function(d) { return x0(getX(d)) })
.attr('cy', function(d) { return y0(getY(d)) });
.attr('cx', function(d,i) { return x0(getX(d,i)) })
.attr('cy', function(d,i) { return y0(getY(d,i)) });
d3.transition(points.exit())
.attr('cx', function(d) { return x(getX(d)) })
.attr('cy', function(d) { return y(getY(d)) })
.attr('cx', function(d,i) { return x(getX(d,i)) })
.attr('cy', function(d,i) { return y(getY(d,i)) })
.remove();
d3.transition(lines.exit().selectAll('circle.point'))
.attr('cx', function(d) { return x(getX(d)) })
.attr('cy', function(d) { return y(getY(d)) })
.attr('cx', function(d,i) { return x(getX(d,i)) })
.attr('cy', function(d,i) { return y(getY(d,i)) })
.remove();
points.attr('class', function(d,i) { return 'point point-' + i });
d3.transition(points)
.attr('cx', function(d) { return x(getX(d)) })
.attr('cy', function(d) { return y(getY(d)) })
.attr('cx', function(d,i) { return x(getX(d,i)) })
.attr('cy', function(d,i) { return y(getY(d,i)) })
.attr('r', dotRadius);
@ -1273,13 +1277,13 @@ nv.models.lineWithLegend = function() {
getWidth = function() { return 960 },
getHeight = function() { return 500 },
dotRadius = function() { return 2.5 },
getX = function(d) { return d.x },
getY = function(d) { return d.y },
color = d3.scale.category10().range(),
dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
var x = d3.scale.linear(),
y = d3.scale.linear(),
getX = function(d) { return d.x },
getY = function(d) { return d.y },
xAxis = nv.models.xaxis().scale(x),
yAxis = nv.models.yaxis().scale(y),
legend = nv.models.legend().height(30),
@ -1294,12 +1298,16 @@ nv.models.lineWithLegend = function() {
availableHeight = height - margin.top - margin.bottom;
var series = data.filter(function(d) { return !d.disabled })
.map(function(d) { return d.values });
.map(function(d) {
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i) }
})
});
x .domain(d3.extent(d3.merge(series), getX ))
x .domain(d3.extent(d3.merge(series), function(d) { return d.x } ))
.range([0, availableWidth]);
y .domain(d3.extent(d3.merge(series), getY ))
y .domain(d3.extent(d3.merge(series), function(d) { return d.y } ))
.range([availableHeight, 0]);
lines
@ -1413,6 +1421,9 @@ nv.models.lineWithLegend = function() {
chart.xAxis = xAxis;
chart.yAxis = yAxis;
d3.rebind(chart, lines, 'interactive');
//consider rebinding x and y as well
chart.x = function(_) {
if (!arguments.length) return getX;
getX = _;
@ -2635,6 +2646,7 @@ nv.models.stackedAreaWithLegend = function() {
stacked.dispatch.on('pointMouseover.tooltip', function(e) {
//disable tooltips when value ~= 0
//// TODO: consider removing points from voronoi that have 0 value instead of this hack
if (!Math.round(getY(e.point) * 100)) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
return false;

4
nv.d3.min.js vendored

File diff suppressed because one or more lines are too long

@ -0,0 +1,166 @@
// This is an attempt to make an extremely easy to use chart that is ready to go,
// basically the chart models with the extra glue... Queuing, tooltips, automatic resize, etc.
// I may make these more specific, like 'time series line with month end data points', etc.
// or may make yet another layer of abstraction... common settings.
nv.charts.lineChartDaily = function() {
var selector = null,
data = [],
duration = 500,
tooltip = function(key, x, y, e, graph) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>'
};
var graph = nv.models.lineWithLegend()
.x(function(d,i) { return i }),
showTooltip = function(e) {
var offset = $(selector).offset(),
left = e.pos[0] + offset.left,
top = e.pos[1] + offset.top,
formatX = graph.xAxis.tickFormat(),
formatY = graph.yAxis.tickFormat(),
x = formatX(graph.x()(e, e.pointIndex)),
//x = formatX(graph.x()(e.point)),
y = formatY(graph.y()(e.point)),
content = tooltip(e.series.key, x, y, e, graph);
nvtooltip.show([left, top], content);
};
//setting component defaults
//graph.xAxis.tickFormat(d3.format(',r'));
graph.xAxis.tickFormat(function(d) {
//return d3.time.format('%x')(new Date(d))
return d3.time.format('%x')(new Date(data[0].values[d].x))
});
//graph.yAxis.tickFormat(d3.format(',.2f'));
graph.yAxis.tickFormat(d3.format(',.2%'));
//TODO: consider a method more similar to how the models are built
function chart() {
if (!selector || !data.length) return chart; //do nothing if you have nothing to work with
d3.select(selector).select('svg')
.datum(data)
.transition().duration(duration).call(graph); //consider using transition chaining like in the models
return chart;
}
// This should always only be called once, then update should be used after,
// in which case should consider the 'd3 way' and merge this with update,
// but simply do this on enter... should try anoter example that way
chart.build = function() {
if (!selector || !data.length) return chart; //do nothing if you have nothing to work with
nv.addGraph({
generate: function() {
var container = d3.select(selector),
width = function() { return parseInt(container.style('width')) },
height = function() { return parseInt(container.style('height')) },
svg = container.append('svg');
graph
.width(width)
.height(height);
svg
.attr('width', width())
.attr('height', height())
.datum(data)
.transition().duration(duration).call(graph);
return graph;
},
callback: function(graph) {
graph.dispatch.on('tooltipShow', showTooltip);
graph.dispatch.on('tooltipHide', nvtooltip.cleanup);
//TODO: create resize queue and have nv core handle resize instead of binding all to window resize
$(window).resize(function() {
// now that width and height are functions, should be automatic..of course you can always override them
d3.select(selector + ' svg')
.attr('width', graph.width()()) //need to set SVG dimensions, chart is not aware of the SVG component
.attr('height', graph.height()())
.call(graph);
});
}
});
return chart;
};
/*
// moved to chart()
chart.update = function() {
if (!selector || !data.length) return chart; //do nothing if you have nothing to work with
d3.select(selector).select('svg')
.datum(data)
.transition().duration(duration).call(graph);
return chart;
};
*/
chart.data = function(_) {
if (!arguments.length) return data;
data = _;
return chart;
};
chart.selector = function(_) {
if (!arguments.length) return selector;
selector = _;
return chart;
};
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
return chart;
};
chart.tooltip = function(_) {
if (!arguments.length) return tooltip;
tooltip = _;
return chart;
};
chart.xTickFormat = function(_) {
if (!arguments.length) return graph.xAxis.tickFormat();
graph.xAxis.tickFormat(typeof _ === 'function' ? _ : d3.format(_));
return chart;
};
chart.yTickFormat = function(_) {
if (!arguments.length) return graph.yAxis.tickFormat();
graph.yAxis.tickFormat(typeof _ === 'function' ? _ : d3.format(_));
return chart;
};
chart.xAxisLabel = function(_) {
if (!arguments.length) return graph.xAxis.axisLabel();
graph.xAxis.axisLabel(_);
return chart;
};
chart.yAxisLabel = function(_) {
if (!arguments.length) return graph.yAxis.axisLabel();
graph.yAxis.axisLabel(_);
return chart;
};
d3.rebind(chart, graph, 'x', 'y');
chart.graph = graph; // Give direct access for getter/setters, and dispatchers
return chart;
};

@ -24,7 +24,11 @@ nv.models.line = function() {
function chart(selection) {
selection.each(function(data) {
var seriesData = data.map(function(d) { return d.values }),
var seriesData = data.map(function(d) {
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i) }
})
}),
availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom;
@ -32,10 +36,10 @@ nv.models.line = function() {
y0 = y0 || y;
x .domain(xDomain || d3.extent(d3.merge(seriesData), getX ))
x .domain(xDomain || d3.extent(d3.merge(seriesData), function(d) { return d.x } ))
.range([0, availableWidth]);
y .domain(yDomain || d3.extent(d3.merge(seriesData), getY ))
y .domain(yDomain || d3.extent(d3.merge(seriesData), function(d) { return d.y } ))
.range([availableHeight, 0]);
@ -78,7 +82,7 @@ nv.models.line = function() {
var vertices = d3.merge(data.map(function(line, lineIndex) {
return line.values.map(function(point, pointIndex) {
//return [x(getX(point)), y(getY(point)), lineIndex, pointIndex]; //inject series and point index for reference into voronoi
return [x(getX(point)) * (Math.random() / 1e12 + 1) , y(getY(point)) * (Math.random() / 1e12 + 1), lineIndex, pointIndex]; //temp hack to add noise untill I think of a better way so there are no duplicates
return [x(getX(point, pointIndex)) * (Math.random() / 1e12 + 1) , y(getY(point, pointIndex)) * (Math.random() / 1e12 + 1), lineIndex, pointIndex]; //temp hack to add noise untill I think of a better way so there are no duplicates
})
})
);
@ -118,7 +122,7 @@ nv.models.line = function() {
dispatch.pointMouseover({
point: point,
series:series,
pos: [x(getX(point)) + margin.left, y(getY(point)) + margin.top],
pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
seriesIndex: d.series,
pointIndex: d.point
});
@ -171,40 +175,40 @@ nv.models.line = function() {
.data(function(d, i) { return [d.values] });
paths.enter().append('path')
.attr('d', d3.svg.line()
.x(function(d) { return x0(getX(d)) })
.y(function(d) { return y0(getY(d)) })
.x(function(d,i) { return x0(getX(d,i)) })
.y(function(d,i) { return y0(getY(d,i)) })
);
//d3.transition(paths.exit())
d3.transition(lines.exit().selectAll('path'))
.attr('d', d3.svg.line()
.x(function(d) { return x(getX(d)) })
.y(function(d) { return y(getY(d)) })
.x(function(d,i) { return x(getX(d,i)) })
.y(function(d,i) { return y(getY(d,i)) })
)
.remove();
d3.transition(paths)
.attr('d', d3.svg.line()
.x(function(d) { return x(getX(d)) })
.y(function(d) { return y(getY(d)) })
.x(function(d,i) { return x(getX(d,i)) })
.y(function(d,i) { return y(getY(d,i)) })
);
var points = lines.selectAll('circle.point')
.data(function(d) { return d.values });
points.enter().append('circle')
.attr('cx', function(d) { return x0(getX(d)) })
.attr('cy', function(d) { return y0(getY(d)) });
.attr('cx', function(d,i) { return x0(getX(d,i)) })
.attr('cy', function(d,i) { return y0(getY(d,i)) });
d3.transition(points.exit())
.attr('cx', function(d) { return x(getX(d)) })
.attr('cy', function(d) { return y(getY(d)) })
.attr('cx', function(d,i) { return x(getX(d,i)) })
.attr('cy', function(d,i) { return y(getY(d,i)) })
.remove();
d3.transition(lines.exit().selectAll('circle.point'))
.attr('cx', function(d) { return x(getX(d)) })
.attr('cy', function(d) { return y(getY(d)) })
.attr('cx', function(d,i) { return x(getX(d,i)) })
.attr('cy', function(d,i) { return y(getY(d,i)) })
.remove();
points.attr('class', function(d,i) { return 'point point-' + i });
d3.transition(points)
.attr('cx', function(d) { return x(getX(d)) })
.attr('cy', function(d) { return y(getY(d)) })
.attr('cx', function(d,i) { return x(getX(d,i)) })
.attr('cy', function(d,i) { return y(getY(d,i)) })
.attr('r', dotRadius);

@ -4,13 +4,13 @@ nv.models.lineWithLegend = function() {
getWidth = function() { return 960 },
getHeight = function() { return 500 },
dotRadius = function() { return 2.5 },
getX = function(d) { return d.x },
getY = function(d) { return d.y },
color = d3.scale.category10().range(),
dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
var x = d3.scale.linear(),
y = d3.scale.linear(),
getX = function(d) { return d.x },
getY = function(d) { return d.y },
xAxis = nv.models.xaxis().scale(x),
yAxis = nv.models.yaxis().scale(y),
legend = nv.models.legend().height(30),
@ -25,12 +25,16 @@ nv.models.lineWithLegend = function() {
availableHeight = height - margin.top - margin.bottom;
var series = data.filter(function(d) { return !d.disabled })
.map(function(d) { return d.values });
.map(function(d) {
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i) }
})
});
x .domain(d3.extent(d3.merge(series), getX ))
x .domain(d3.extent(d3.merge(series), function(d) { return d.x } ))
.range([0, availableWidth]);
y .domain(d3.extent(d3.merge(series), getY ))
y .domain(d3.extent(d3.merge(series), function(d) { return d.y } ))
.range([availableHeight, 0]);
lines
@ -144,6 +148,9 @@ nv.models.lineWithLegend = function() {
chart.xAxis = xAxis;
chart.yAxis = yAxis;
d3.rebind(chart, lines, 'interactive');
//consider rebinding x and y as well
chart.x = function(_) {
if (!arguments.length) return getX;
getX = _;

@ -125,6 +125,7 @@ nv.models.stackedAreaWithLegend = function() {
stacked.dispatch.on('pointMouseover.tooltip', function(e) {
//disable tooltips when value ~= 0
//// TODO: consider removing points from voronoi that have 0 value instead of this hack
if (!Math.round(getY(e.point) * 100)) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
return false;

Loading…
Cancel
Save