I am trying to build 2 vertical charts sharing the same data. So I created a refreshPrimary(top) and refreshStackedArea(bottom) that can be called to refresh the graphs depending on options the user selects.
The problem I am having it that when I invoke both refreshPrimary and refreshStackedArea the top graph does not show.if I only do refreshPrimary it display it's graph ok. Below are the refresh functions, the HTML and chart drawing functions.
Refresh function :
// Add refresh function to lm_pareto context
function refreshPrimary() {
//
function refreshGraph() {
console.log("DEBUG JS lm_pareto.refreshGraph entered");
d3.selectAll("#lm_pareto svg > *").remove();
d3.select('#lm_pareto svg')
.datum(lm_pareto.dataPrimary)
.call(chart);
//Update the chart when window resizes.
nv.utils.windowResize(function() { chart.update() });
return chart;
}
nv.addGraph(refreshGraph);
}
function refreshStackedArea() {
function refreshGraph_SArea() {
d3.selectAll("#lm_stackedArea svg > *").remove();
d3.select('#lm_stackedArea svg')
.datum(lm_pareto.dataStackedArea)
.call(chart); //Finally, render the chart!
//Update the chart when window resizes.
nv.utils.windowResize(function() { chart.update() });
return chart;
}
nv.addGraph(refreshGraph_SArea);
}
HTML DIV's
<div id="lm_pareto" class="lm_graph" style="border: 2px solid blue"></div>
<div id="lm_stackedArea" class="lm_graph" style="border: 2px solid blue"></div>
TOP CHART
var chart;
nv.addGraph(function () {
chart = nv.models.stackedLineChart()
.options({
reduceXTicks: false
})
.margin(margin)
// Get normalised data for chart
chart.lines1.forceY(0);
chart.lines2.forceY(0);
svg = d3.select("#lm_pareto")
.append("svg");
// chart is a temp object of nv.addGraph... not able to pass back.
return chart;
});
BOTTOM CHART
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 70
};
var chart;
nv.addGraph(function() {
chart = nv.models.stackedAreaChart()
.margin(margin)
.x(function(d) { return d[0] })
.y(function(d, x) { return d[1]; })
.useInteractiveGuideline(true)
.rightAlignYAxis(false) //y-axis to the right side.
// .transitionDuration(500)
.showControls(false)
.clipEdge(true)
.color(lm_pareto.ColorList);
chart.yAxis
.tickFormat(d3.format(',.2f'));
svg = d3.select("#lm_stackedArea")
.append("svg")
// OLD WAY
//svg.datum(lm_pareto.dataStackedArea)
// .call(chart);
//
//nv.utils.windowResize(chart.update);
return chart;
Related
I tried to erase negative ticks from my NVD3 line chart (with data includes 0 only)
but It seems impossible what I wanted to do...
I want to set the line bottom when I throw data what includes 0 only.
Is there another way? or just I had a mistake?
code:
nv.addGraph(function() {
//DATA.B includes only 0 data, the value must be "data>0"
data = DATA.A,DATA.B;
var chart = nv.models.linePlusBarChart()
.margin({top: 30, right: 60, bottom: 50, left: 70})
.x(function(d,i) { return i })
.y(function(d,i) { return d[1] });
chart.xAxis.tickFormat(function(d) {
var dx = data[0].values[d] && data[0].values[d][0] || 0;
return d3.time.format('%Y-%m-%d')(new Date(dx))
});
chart.y1Axis
.tickFormat(d3.format(',f'))
.tickSize(0,6);
chart.y2Axis
.tickFormat(function(d) { return d3.format(',f')(d) + '%' });
chart.bars.forceY([0]);
d3.select($node).datum(data).transition().duration(0).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
thanks
Used DC.js to create stacked bar chart with ordinal x-axis.
Versions used:
DC.js version 1.7.5
crossfilter.js version 1.3.12
D3.js version 3.5.17
The problem is that the chart's x-axis labels are not aligned with bars. They are actually shifted two ticks to right so last two labels have no bars above them.
Edit to remove - Also can't select the right most bar to filter data eg hover over bar doesn't show selector to click and activate cross filter. - it was just two chart margins overlapping blocking cursor.
Here is screenshot of chart indicating problems.
The x-axis is ordinal set using .xUnits(dc.units.ordinal)
I used a renderlet to change x-axis label orientation so they are vertical. If I remove renderlet it doesn't change the problems above.
Here is my chart div and javascript code.
<div id="month-chart"></div>
<script type="text/javascript">
d3.csv("merged_hostname.csv", function(data) {
var parseDate = d3.time.format("%Y-%m-%d").parse;
data.forEach(function(d) {
d.date = parseDate(d.date);
d.sessions = +d.sessions;
d.ad_requests = +d.ad_requests;
d.bounceRate = +d.bounceRate;
d.clicks = +d.clicks;
d.earnings = +d.earnings;
d.newUsers = +d.newUsers;
d.sessionDuration = +d.sessionDuration;
d.sessionsPerUser = +d.sessionsPerUser;
d.twitterSessions = +d.twitterSessions;
d.users = +d.users;
});
var ndx = crossfilter(data);
var yyyymmDim = ndx.dimension(function(d) { return d["yyyymm"]; });
var PPCCByYYYYMM = yyyymmDim.group().reduceSum(function(d) {
if (d.PPCC === "PPCC") {
return +d.sessions;
}else{
return 0;
}
});
var otherByYYYYMM = yyyymmDim.group().reduceSum(function(d) {
if (d.PPCC === "Other") {
return +d.sessions;
}else{
return 0;
}
});
monthChart = dc.barChart("#month-chart");
monthChart
.height(200)
.width(500)
.margins({top: 10, right: 10, bottom: 50, left: 40})
.dimension(yyyymmDim)
.group(PPCCByYYYYMM)
.stack(otherByYYYYMM)
.transitionDuration(500)
.brushOn(true)
.elasticY(true)
.yAxisLabel('sessions')
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.renderlet(function (chart) {
chart.selectAll("g.x text")
.attr('dx', '-30')
.attr('transform', "rotate(-90)");
});
dc.renderAll();
});
</script>
Any ideas what can causes these issues and how to resolve?
You can move the left position with this:
.attr('transform', "translate(-20,0) rotate(-90)");
Change 20 if its necessary
I have an area chart in nvd3:
var chart = nv.models.stackedAreaChart()
.x(function (d) { return d[0] })
.y(function (d) { return Math.round(d[1]) })
.clipEdge(true)
.showControls(true)
.useInteractiveGuideline(true);
As you can see, I have enabled showControls, which displays three small buttons (Stacked, Stream and Expanded) in the top left corner of the chart.
Since it was desired to select subsections of the chart by dragging the mouse over, I implemented the following solution by hooking up mouseup, mousedown and mousemove events on the SVG element that contains the chart.
var mouseDown = false;
var mouseDownCoords;
var rect = svg.append("rect")
.attr("x", 0).attr("y", 0)
.attr("width", 0).attr("height", 0)
.attr("fill", "rgba(43,48,87,0.3)");
svg.on('mousedown', function () {
var height = svg[0][0].height;
mouseDownCoords = d3.mouse(this);
mouseDown = true;
rect.attr("x", mouseDownCoords[0]);
rect.attr("height", height.animVal.value);
// Register mousemove when the mouse button is down
svg.on('mousemove', function () {
var coords = d3.mouse(this);
rect.attr("width", Math.max(coords[0] - mouseDownCoords[0], 0));
});
});
svg.on('mouseup', function () {
if (mouseDown) {
var coords = d3.mouse(this);
var width = Math.max(coords[0] - mouseDownCoords[0], 0);
mouseDown = false;
rect.attr("width", 0);
if (width > 0) {
var totalWidth = svg[0][0].width.animVal.value;
var totalPeriod = dateTo.getTime() - dateFrom.getTime();
var newDateFrom = new Date(Math.floor(dateFrom.getTime() + totalPeriod * mouseDownCoords[0] / totalWidth));
var newDateTo = new Date(Math.floor(newDateFrom.getTime() + totalPeriod * width / totalWidth));
window.setSearchTimeframe(newDateFrom, newDateTo);
}
}
// Unregister mousemove
svg.on('mousemove', null);
});
However, registering these event callbacks stops the control buttons from working. When I click on them, nothing happens, even if the pointer correctly changes when I hover them.
You're right, registering events on elements outside NVD3's built-in event system really seems to destroy things internally (which shouldn't be the case, in my opinion). You could work around this by positioning an invisible element over the part of the chart that needs custom behaviour.
Demo
The red rectangle is the part of the chart with custom behaviour (click it).
var chartElement = d3.select("#chart svg");
var chart;
nv.addGraph(function() {
chart = nv.models.pieChart()
.x(function(d) {
return d.label
})
.y(function(d) {
return d.value
})
.showLabels(true);
var chartData = [{
label: "Foo",
value: 67
}, {
label: "Bar",
value: 33
}];
chartElement
.datum(chartData)
.call(chart);
$("#customUI").on("mousedown", function() {
alert("Some custom behaviour...");
});
return chart;
});
#wrapper {
position: relative;
}
#chart {
position: absolute;
height: 500px;
}
#customUI {
position: absolute;
background: red;
opacity: 0.2;
width: 100px;
height: 100px;
left: 100px;
top: 200px;
}
#customUI:hover {
opacity: 0.5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.2/nv.d3.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.2/nv.d3.min.css" rel="stylesheet" />
<div id="wrapper">
<div id="chart">
<svg>
</svg>
</div>
<div id="customUI">
</div>
</div>
Plnkr example:
http://plnkr.co/edit/19D5cnrVYdUrMlblQARy?p=preview
Screenshot from my app:
I've tried modifying the CSS, however I was only able to remove the 2nd (right Y axis line), but not the first left Y axis line.
What I've tried:
.nv-y {
display: none;
}
As well as all the lines from this answer: Alter first vertical grid line in nvd3
d3.selectAll('.nv-y').attr('display','none')
d3.selectAll('.nv-y path').attr('opacity','0.1')
d3.selectAll('.nv-y path').attr('display','none')
My current drawChart function:
function drawChart(res) {
console.log(' ');
console.log('drawChart res = ',res);
nv.addGraph(function() {
var chart = nv.models.linePlusBarChart()
.margin({top: 30, right: 40, bottom: 50, left: 40})
.x(function(d,i) { return i })
.y(function(d) { return d[1] })
.color(d3.scale.category10().range());
chart.xAxis.tickFormat(function(d) {
var dx = res[0].values[d] && res[0].values[d][0] || 0;
return d3.time.format('%x')(new Date(dx))
});
chart.y1Axis
.tickFormat(d3.format(',f'));
chart.y2Axis
.tickFormat(function(d) { return '$' + d3.format(',f')(d) });
chart.bars.forceY([0]);
// https://stackoverflow.com/questions/23754188/nvd3-js-how-to-disable-tooltips-for-one-serie-only
chart.lines.interactive(false);
// http://nvd3-community.github.io/nvd3/examples/documentation.html#line
chart.height(280);
// If not chart data is avaliable to display:
chart.noData("There is no Data to display at the moment.");
// Remove legend:
chart.showLegend(false);
d3.select('#chart svg')
.datum(res)
.transition().duration(500)
.call(chart);
d3.selectAll('.nv-y path').attr('display','none');
nv.utils.windowResize(chart.update);
return chart;
});
}
if you want to hide the 1st y axis do:
.nv-y1{
display:none;
}
if you want to hide the 2nd y axis do:
.nv-y2{
display:none;
}
http://plnkr.co/edit/IgFh1rV4a6ubAMe0VqT2?p=preview
Use this :
.domain {
display: none;
}
I want to use d3.chart() for the charts I have written already. I found examples of d3.chart() for circle and barcharts but not for line charts. My charts are line charts, I need to use following code in d3.charts()
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
but am facing problem when try to use like this
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="d3.v3.min.js"></script>
<script type="text/javascript" src="d3.chart.min.js"></script>
</head>
<body>
<div id="vis"></div>
<script type="text/javascript">
d3.chart("linechart", {
initialize: function() {
// create a base scale we will use later.
var chart = this;
chart.w = chart.base.attr('width') || 200;
chart.h = chart.base.attr('height') || 150;
chart.w = +chart.w;
chart.h = +chart.h;
chart.x = d3.scale.linear()
.range([0, chart.w]);
chart.y = d3.scale.linear()
.range([chart.h,0]);
chart.base.classed('line', true);
this.areas = {};
chart.areas.lines = chart.base.append('g')
.classed('lines', true)
.attr('width', chart.w)
.attr('height', chart.h)
chart.line = d3.svg.line()
.x(function(d) { return chart.x(d.x);})
.y(function(d) { return chart.y(d.y);});
this.layer("lines", chart.areas.lines, {
dataBind: function(data) {
// update the domain of the xScale since it depends on the data
chart.y.domain([d3.min(data,function(d){return d.y}),d3.max(data,function(d){return d.y})])
chart.x.domain(d3.extent(data, function(d) { return d.x; }));
// return a data bound selection for the passed in data.
return this.append("path")
.datum(data)
.attr("d", chart.line)
.attr('stroke','#1ABC9C')
.attr('stroke-width','2')
.attr('fill','none');
},
insert: function() {
return null;
},
});
},
// configures the width of the chart.
// when called without arguments, returns the
// current width.
width: function(newWidth) {
if (arguments.length === 0) {
return this.w;
}
this.w = newWidth;
return this;
},
// configures the height of the chart.
// when called without arguments, returns the
// current height.
height: function(newHeight) {
if (arguments.length === 0) {
return this.h;
}
this.h = newHeight;
return this;
},
});
var data = [
{x: 0,y:190},
{x: 1,y:10},{x: 2,y:40},{x: 3,y:90},
{x: 4,y:30},{x: 5,y:20},{x: 6,y:10}
];
var chart1 = d3.select("#vis")
.append("svg")
.chart("linechart")
.width(720)
.height(320)
chart1.draw(data);
</script>
</body>
</html>
error:
Uncaught Error: [d3.chart] Layer selection not properly bound.
I have get the line and error as well.
Note: Get d3.chart.min.js from this link
Get d3.v3.min.js from this link
Updated: I got answer from #LarsKotthoff answer, but there is different in image. check this links Before apply D3 and After apply D3.
It looks like you have confused the insert and dataBind actions -- in the former, you're supposed to append the new elements while the latter only binds the data. With the modifications below, your code works fine for me.
dataBind: function(data) {
// update the domain of the xScale since it depends on the data
chart.y.domain([d3.min(data,function(d){return d.y}),d3.max(data,function(d){return d.y})])
chart.x.domain(d3.extent(data, function(d) { return d.x; }));
// return a data bound selection for the passed in data.
return this.selectAll("path").data([data]);
},
insert: function() {
return this.append("path")
.attr("d", chart.line)
.attr('stroke','#1ABC9C')
.attr('stroke-width','2')
.attr('fill','none');
}
Note that this won't work for several lines -- to do that, change .data([data]) to .data(data) and use a nested array, e.g. [[{x:0,y:0},...], [{x:1,y:1},...], ...].