I was using a dc.js rowChart to display Index averages calculated in custom reduceAdd/reduceRemove functions and it all works fine. Now, I want to change the rowChart to a barChart, and for some reason, the valueAccessor returns [object object] when hovering over the bars (representing the Index column) in the chart, instead of showing the average value. If I print out the avg value in the console though, it is defined correctly.
My csv file is structured like this:
Index,Model,Year,Season
Heat,Model1,1974,0
Heat,Model1,1974,1
Heat,Model1,1974,2
Heat,Model2,1974,3
Heat,Model2,1994,0
Heat,Model2,1994,1
Heat,Model2,1994,2
Heat,Model2,1994,3
Rain,Model1,1974,0
Rain,Model1,1974,1
Rain,Model1,1974,2
Rain,Model2,1974,3
Rain,Model2,1994,0
Rain,Model2,1994,1
Rain,Model2,1994,2
Rain,Model2,1994,3
These are the versions of the libraries:
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/dc/1.7.0/dc.js"></script>
<script type="text/javascript" src="https://rawgithub.com/NickQiZhu/dc.js/master/web/js/crossfilter.js"></script>
<link rel="stylesheet" type="text/css" href="http://tvinci.github.io/webs/css/dc.css"/>
Here is the barChart and the filter definitions:
indexChart = dc.barChart("#chart-index");
var indexDimension = filter.dimension(function(d) { return d.Index; });
var indexGroup = indexDimension.group();
var avgIndexGroup = indexDimension.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
++p.count;
p.total += v.value;
p.average = p.total / p.count
return p;
}
function reduceRemove(p, v) {
--p.count;
p.total -= v.value;
p.average = p.total / p.count
return p;
}
function reduceInitial() {
return {count: 0, total: 0, average: 0};
}
And finally, the chart parameters:
indexChart
.width(300).height(200)
.margins({ top: 10, right: 30, bottom: 30, left: 10 })
.dimension(indexDimension)
.group(avgIndexGroup)
.valueAccessor(function(p) {
//console.log("p.value.average: ", p.value.average) //displays the avg fine
return p.value.average;
})
.elasticY(true)
.gap(0)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal); // Tell dc.js that we're using an ordinal x-axis;
indexChart.yAxis().ticks(4);
Any suggestions are welcome. Thanks in advance!
Related
Why is dc.js boxplot not creating any outlines? I am just created a simplified code based on http://dc-js.github.io/dc.js/examples/boxplot-basic.html
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<body>
<div id = 'boxch1'><br></div>
<script src="https://unpkg.com/crossfilter2#1.4.7/crossfilter.min.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="http://unpkg.com/dc#3/dc.js"></script>
<script>
let exp = [
{"Expt":1, "Speed":8},
{"Expt":1, "Speed":7},
{"Expt":1, "Speed":9},
{"Expt":1, "Speed":1},
{"Expt":2, "Speed":8},
{"Expt":2, "Speed":6},
{"Expt":2, "Speed":8}];
dc.config.defaultColors(d3.schemeSet1);
var ndx = crossfilter(exp)
runDimension = ndx.dimension(function(d) {return +d.Run;});
runGroup = runDimension.group();
experimentDimension = ndx.dimension(function(d) {return "exp-" + d.Expt;});
speedArrayGroup = experimentDimension.group().reduce(
function(p,v) {
// keep array sorted for efficiency
p.splice(d3.bisectLeft(p, v.Speed), 0, v.Speed);
return p;
},
function(p,v) {
p.splice(d3.bisectLeft(p, v.Speed), 1);
return p;
},
function() {
return [];
}
);
var bp01 = dc.boxPlot("#boxch1");
bp01
.width(768)
.height(480)
.margins({top: 10, right: 50, bottom: 30, left: 50})
.dimension(experimentDimension)
.group(speedArrayGroup)
.elasticY(true)
.elasticX(true);
bp01.render()
</script>
</body>
</html>
I am not getting the box outlines as in the chart drawn below.
https://imgur.com/a/ELH5xlX
Thanks for including a complete example.
These kinds of problems are usually because you are missing dc.css.
In dc.js, and D3 code in general, any static attributes of SVG elements that are not controlled by data can be specified in either JS code or in CSS. This includes axis lines, outlines, fonts, etc.
Generally it makes customization easier to use classnames and leave the static attributes to CSS. But you do need to include dc.css.
My chart needs to have tick values in multiples of 3. For example, if I have 5 data points, my x-axis should show (3, 6, 9, 12, 15). How do I get nvd3 to display all tick numbers? I pasted my code below for reference
var chart = nv.models.lineChart()
.options({
margin: {left: 100, bottom: 100},
x: function(d,i) { return i*3},
showXAxis: true,
showYAxis: true,
showLegend: true,
reduceXTicks: false,
showMaxMin: false,
//values: $scope.data.values.map( function(d,i) { return i*3; }),
useInteractiveGuideline: true,
transitionDuration: 2500,
showControls : true
})
;
chart.xAxis //Chart x-axis settings
.axisLabel('Ager')
.orient('bottom')
//.tickValues( function(d,i) { return i*3;})
.tickFormat(d3.format(',r'));
chart.yAxis //Chart y-axis settings
.axisLabel('Voltage (v)')
.tickFormat(d3.format('.02f')); nv.utils.windowResize(chart.update);
I've tried everything I can think of and read online to get nvd3 show all ticks in multiples of 3. Please help me out of this tricky situation.
Thanks in advance!
You could have read the d3 docs and see that .tickValues does not accept a function.
Construct the array of tick values based on the domain of the x-axis. Because the domain is not yet set you have to construct it yourself based on the data.
Using the SinCos example from the nvd3 site
var myData = sinAndCos();
var xExtent = d3.extent(myData[0].values, d=>d.x);
xExtent = [Math.floor(xExtent[0]), Math.ceil(xExtent[1])+1];
var xTicks = d3.range(xExtent[0], xExtent[1]).filter(n => n%3===0);
chart.xAxis
.axisLabel('Time (ms)')
.tickValues(xTicks)
.tickFormat(d3.format(',r'));
Complete example. It will not run directly from the browser (will not load nvd3 inside an iframe(??)). Copy it to a local file and run it from there.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link href="https://nvd3.org/assets/css/nv.d3.css" rel="stylesheet"/>
<script src="https://nvd3.org/assets/lib/d3.v3.js"></script>
<script src="https://nvd3.org/assets/js/nv.d3.js"></script>
</head>
<body>
<div id="chart">
<svg style="width:800px;height:500px;"></svg>
</div>
<script>
/*These lines are all chart setup. Pick and choose which chart features you want to utilize. */
nv.addGraph(function() {
var chart = nv.models.lineChart()
.margin({left: 100}) //Adjust chart margins to give the x-axis some breathing room.
.useInteractiveGuideline(true) //We want nice looking tooltips and a guideline!
.transitionDuration(350) //how fast do you want the lines to transition?
.showLegend(true) //Show the legend, allowing users to turn on/off line series.
.showYAxis(true) //Show the y-axis
.showXAxis(true) //Show the x-axis
;
var myData = sinAndCos();
var xExtent = d3.extent(myData[0].values, d=>d.x);
xExtent = [Math.floor(xExtent[0]), Math.ceil(xExtent[1])+1];
var xTicks = d3.range(xExtent[0], xExtent[1]).filter(n => n%3===0);
chart.xAxis //Chart x-axis settings
.axisLabel('Time (ms)')
.tickValues(xTicks)
.tickFormat(d3.format(',r'));
chart.yAxis //Chart y-axis settings
.axisLabel('Voltage (v)')
.tickFormat(d3.format('.02f'));
d3.select('#chart svg') //Select the <svg> element you want to render the chart in.
.datum(myData) //Populate the <svg> element with chart data...
.call(chart); //Finally, render the chart!
//Update the chart when window resizes.
nv.utils.windowResize(function() { chart.update() });
return chart;
});
/**************************************
* Simple test data generator
*/
function sinAndCos() {
var sin = [],sin2 = [],
cos = [];
//Data is represented as an array of {x,y} pairs.
for (var i = 0; i < 100; i++) {
sin.push({x: i, y: Math.sin(i/10)});
sin2.push({x: i, y: Math.sin(i/10) *0.25 + 0.5});
cos.push({x: i, y: .5 * Math.cos(i/10)});
}
//Line chart data should be sent as an array of series objects.
return [
{
values: sin, //values - represents the array of {x,y} data points
key: 'Sine Wave', //key - the name of the series.
color: '#ff7f0e' //color - optional: choose your own line color.
},
{
values: cos,
key: 'Cosine Wave',
color: '#2ca02c'
},
{
values: sin2,
key: 'Another sine wave',
color: '#7777ff',
area: true //area - set to true if you want this line to turn into a filled area chart.
}
];
}
</script>
</body>
</html>
I need insert a mouseclick event on graph nodes, but all ways don't work with me.
My code that done the graph is this:
<!DOCTYPE html>
<html>
<head>
<title>01. Create Graph. Vivagraph SVG tutorial.</title>
<script type="text/javascript" src="VivaGraphJS-master/dist/vivagraph.js"></script>
<script type="text/javascript">
function main () {
// 1- criar um projeto de grafo
var graph = Viva.Graph.graph();
// 2 - adicionando nós e arestas
graph.addNode('natalia', 'ledlightblue.png');
graph.addNode('jessica', 'krec_record.png');
graph.addNode('lucas', 'ledyellow.png');
graph.addNode('leo', 'ledgreen.png');
graph.addNode('hcii', 'ledpurple.png');
graph.addNode('evento', 'krec_record.png');
graph.addLink('natalia', 'hcii');
graph.addLink('jessica', 'hcii');
graph.addLink('lucas', 'hcii');
graph.addLink('leo', 'hcii');
graph.addLink('jessica', 'evento');
var graphics = Viva.Graph.View.svgGraphics();
//var renderer = Viva.Graph.View.renderer(graph);
graphics.node(function(node) {
var ui = Viva.Graph.svg('image')
.attr('width', 32)
.attr('height', 32)
.link('https://cdn1.iconfinder.com/data/icons/nuvola2/32x32/actions/' + node.data);
return(ui);
});
graphics.link(function(link){
return Viva.Graph.svg('path')
.attr('stroke', 'black')
.attr('stroke-width',2);
}).placeLink(function(linkUI, fromPos, toPos) {
var data = 'M' + fromPos.x + ',' + fromPos.y +
'L' + toPos.x + ',' + toPos.y;
linkUI.attr("d", data);
})
var renderer = Viva.Graph.View.renderer(graph, {
graphics : graphics
});
renderer.run();
}
</script>
<style type="text/css" media="screen">
html, body, svg { width: 100%; height: 100%;}
</style>
I tried many ways like this that is used to do mouseover event
I'm using VivaGraph.js to do the graph
Some solution?
D3js and VivaGraphJS are two different libraries, and your question is only about VivaGraphJs (in the code D3 is not even imported), "d3.js" tag should be removed.
A possible solution is to import JQuery and change this portion of code:
graphics.node(function(node) {
var ui = Viva.Graph.svg('image')
.attr('width', 32)
.attr('height', 32)
.link('https://cdn1.iconfinder.com/data/icons/nuvola2/32x32/actions/' + node.data);
return(ui);
});
Into:
graphics.node(function(node) {
var ui = Viva.Graph.svg('image')
.attr('width', 32)
.attr('height', 32)
.link('https://cdn1.iconfinder.com/data/icons/nuvola2/32x32/actions/' + node.data);
$(ui).hover(function() { // mouse over
console.log("hovering in.", node.id);
}, function() { // mouse out
console.log("hovering out.", node.id);
});
$(ui).click(function() { // mouse click
console.log("click.", node.id);
});
return(ui);
});
$(ui).hover and $(ui).click come from JQuery.
How do I create multiple graphs that graph one dimension such that they react to filters placed on the other graph. Example here: http://bl.ocks.org/pbutler/9356548
<html>
<head>
<link href="style.css" rel="stylesheet">
<link href="dc.css" rel="stylesheet">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="crossfilter.js"></script>
<script src="dc.js"></script>
</head>
<body>
<div id="chart1">
</div>
<div id="chart2">
</div>
<script>
var data = [];
for (var i = 0; i < 10; i++) {
data.push({'val' : 'a'})
}
for (var i = 0; i < 15; i++) {
data.push({'val' : 'b'})
}
var ndx = crossfilter(data);
var all = ndx.groupAll();
var val = ndx.dimension(function (d) { return d.val; });
var valGroup = val.group()
var chart1 = dc.pieChart("#chart1");
var chart2 = dc.pieChart("#chart2");
chart1.dimension(val)
.group(valGroup);
chart2.dimension(val)
.group(valGroup);
dc.renderAll();
</script>
</body>
</html>
In short the graphs seem to ignore each other in this example.
If you create a second dimension using the same property, the filtering will reflect across charts.
Here is an example of that: http://jsfiddle.net/djmartin_umich/nw8EV/.
// build charts
teamMemberChart
.width(270)
.height(220)
.dimension(teamMemberDimension)
.group(teamMemberGroup)
.valueAccessor(function (d) {
return d.value.projectCount;
})
.elasticX(true);
teamMemberChart2
.width(270)
.height(220)
.dimension(teamMemberDimension)
.group(teamMemberGroup)
.valueAccessor(function (d) {
return d.value.projectCount;
})
.elasticX(true);
teamMemberChart3
.width(270)
.height(220)
.dimension(teamMemberDimension2)
.group(teamMemberGroup2)
.valueAccessor(function (d) {
return d.value.projectCount;
})
.elasticX(true);
The first two charts use the same dimension - picking one option does not reflect the other. The third chart uses a different dimension on the same property - choosing an option on this chart updates the other two charts.
It seems the answer can be found here: https://groups.google.com/forum/#!topic/dc-js-user-group/1nIScnbY0vs . There are two possible ways, add another dimension as suggested by DJ Martin or use an on("click", ...) handler to call the filter on the second chart.
I think I'm almost there, but I could be wrong. I'm trying to get the grouped arc sectors to animate as a whole circle in one direction. How do I reference the createWheel function inside the animateWheel function?
Here is the code:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Circle - Arc 3</title>
<style>
h1 {
font-family:Arial, Helvetica, sans-serif;
font-size: 1.5em;
color:#333;
}
#canvas1{ background-color:#699;}
</style>
</head>
<body>
<h1>Circle - Arc</h1>
<canvas id="canvas1" width="600" height="600"> Your Browser does not support HTML 5
</canvas>
<script>
// arc sectors vars
var centerX = 200;
var centerY = 200;
var radius = 100;
var fullCircleDegree = 360;
// Closure Function to ceate dynamic arc sectors
var arcSectors = function(num) { // The outer function defines a variable called "num"
var getNum = function() {
return 360 / num; // The inner function has access to the "num" variable of the outer function
}
return getNum; // Return the inner function, thereby exposing it to outer scopes
},
createArcSectors = arcSectors(7);
var rotateAngle = createArcSectors() * Math.PI/180;
var startAngle = 0 * Math.PI/180;
var endAngle = createArcSectors() * Math.PI/180;
var animateRot = 0;
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
window.onload = function () {
createWheel();
}
function createWheel() {
var theCanvas = document.getElementById('canvas1');
var context = theCanvas.getContext('2d');
context.clearRect(0, 0, canvas1.width, canvas1.height);
context.arc(centerX, centerY, radius, startAngle, endAngle, false);
// create arc sectors
for (i = 0; i < 7; i++) {
context.translate(centerX, centerY);
context.rotate(rotateAngle);
context.translate(-centerX, -centerY);
context.beginPath();
context.moveTo(centerX, centerY);
context.lineTo(centerX + radius, centerY);
context.arc(centerX, centerY, radius, startAngle, endAngle, false);
context.closePath();
context.stroke();
}
animateWheel();
}
function animateWheel() {
var theCanvas = document.getElementById('canvas1');
var context = theCanvas.getContext('2d');
//rotateAngle = animateRot * Math.PI / 180;
rotateAngle = .002;
console.log(rotateAngle);
animateRot += .002;
if (rotateAngle > 360) {
animateRot -= 1;
}
requestAnimFrame(function() {
animateWheel();
});
}
</script>
</body>
</html>
The requestAnimationFrame design pattern looks like this:
function animate() {
// request a new animation frame as soon as possible
requestAnimFrame(animate);
// reset any values that need to change with every frame
rotation+=PI2/120;
// do the drawing
drawWheel(cx,cy,rotation);
}
Here's a demo of your code (slightly refactored): http://jsfiddle.net/m1erickson/LydNg/
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var PI2=Math.PI*2;
var cx=100;
var cy=100;
var radius=30;
var rotation=-Math.PI/2;
animate();
function drawWheel(cx,cy,rotation){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(cx,cy);
ctx.rotate(rotation);
ctx.beginPath();
ctx.arc(0,0,radius,0,PI2,false);
ctx.closePath();
for(var i=0;i<7;i++){
var r=PI2/7*i;
ctx.moveTo(0,0);
ctx.lineTo(radius*Math.cos(r),radius*Math.sin(r));
}
ctx.stroke();
ctx.restore();
}
var fps = 60;
function animate() {
setTimeout(function() {
requestAnimFrame(animate);
// Drawing code goes here
rotation+=PI2/120;
drawWheel(cx,cy,rotation);
}, 1000 / fps);
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=350 height=350></canvas>
</body>
</html>