so here... http://codepen.io/dwilbank68/pen/VagOKd?editors=0010
I have the same exact array of data creating the right number of dots, but not enough number of text elements.
It's not a margin issue obscuring the names... the elements are not even in the DOM.
I even appended the index to the name, to prove that the graphData array has the right number of elements.
What else could be wrong?
svg.selectAll('.dot') // creates the correct number of dots
.data(graphData)
.enter()
.append('circle')
.attr('class', 'dot')
.attr('r', 5)
.attr('cx', (d)=> xScale(d.secondsBehind) )
.attr('cy', (d)=> yScale(d.place) )
.style('fill', (d)=> colorScale(d.dopingAllegations) );
svg.selectAll('.label') // does not create the last two text elements
.data(graphData)
.enter()
.append('text')
.attr('class', 'label')
.attr('x', (d)=> xScale(d.secondsBehind) + 10)
.attr('y', (d)=> yScale(d.place) + 4)
.text( (d)=> d.name );
var url = "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/cyclist-data.json";
var m = {t: 20, r: 120, b: 30, l: 40},
width = 800 - m.l - m.r,
height = 700 - m.t - m.b;
var svg = d3.select("body").append("svg")
.attr("width", width + m.l + m.r)
.attr("height", height + m.t + m.b)
.append("g")
.attr("transform", "translate(" + m.l + "," + m.t + ")");
var div = d3.select('body')
.append('div')
.style({
'position':'absolute',
'text-align':'center',
'width':'240px',
'height':'2.5em',
'font':'1.5em sans-serif',
'color':'yellow',
'background':'black',
'border-radius':'8px',
'border':'solid 1px green',
'opacity':0
});
var colorScale = d3.scale.ordinal()
.range(["#FF0000", "#009933"]);
var xScale = d3.scale.linear()
.range([width, 0]);
var yScale = d3.scale.linear()
.range([0, height]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickFormat(formatMinSec);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
d3.json(url, callback);
function callback (error, data) {
if (error) throw error;
var bestTime = _.sortBy(data, 'Place')[0].Seconds;
var graphData = _.map(data, (d)=> ({
'secondsBehind': Math.abs(bestTime - d.Seconds),
'year': d.Year,
'nationality': d.Nationality,
'doping': d.Doping,
'dopingAllegations': d.Doping.length > 0 ? "Doping Allegations":"No Doping Allegations",
'name': d.Name,
'place': d.Place,
'time': d.Time
}) )
var timeRange = d3.extent(graphData, (d) => d.secondsBehind );
xScale.domain([timeRange[0]-15, timeRange[1]]);
var rankRange = d3.extent(graphData, (d) => d.place );
yScale.domain([rankRange[0], rankRange[1]+1]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.text("Minutes : Seconds Behind Fastest Time")
.attr({
'class': 'label',
'x': width,
'y': -6
})
.style("text-anchor", "end");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.text("Ranking")
.attr({
'class': 'label',
"transform": "rotate(-90)",
"y": 6,
"dy": ".71em"
})
.style("text-anchor", "end");
svg.selectAll('.dot')
.data(graphData)
.enter()
.append('circle')
.attr('class', 'dot')
.attr('r', 5)
.attr('cx', (d)=> xScale(d.secondsBehind) )
.attr('cy', (d)=> yScale(d.place) )
.style('fill', (d)=> colorScale(d.dopingAllegations) );
svg.selectAll('.label')
.data(graphData)
.enter()
.append('text')
.attr('class', 'label')
.attr('x', (d)=> xScale(d.secondsBehind) + 10)
.attr('y', (d)=> yScale(d.place) + 4)
.text( (d)=> d.name );
// d3.selectAll('.dot')
// .on('mouseover', mouseover)
// .on('mouseout', mouseout);
var legend = svg.selectAll('.legend')
.data(colorScale.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d,i){return 'translate(0,' +i*20+')';});
legend.append('rect')
.attr('x', width)
.attr('y', 100)
.attr('width', 18)
.attr('height', 18)
.style('fill', colorScale);
legend.append('text')
.text((d)=> d)
.attr('x', width - 18)
.attr('y', 108)
.attr('dy', '.35em')
.style('text-anchor', 'end');
};
// function mouseover(d){
// div.html('Sepal Width: ' + d.sepalWidth +
// '<br/>' +
// 'Sepal Length: ' + d.sepalLength)
// .style('left', (d3.event.pageX + 9) +'px')
// .style('top', (d3.event.pageY - 43) +'px')
// .style('opacity', 1);
// }
// function mouseout(){
// div.style('opacity', 1e-6);
// }
function formatMinSec(d){
if( d % 60 > 9){
return Math.floor(d/60) +':'+ d%60
} else {
return Math.floor(d/60) +':0'+ d%60
}
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.min.js"></script>
you add two labels to your axes with a class label
your selection svg.selectAll('.label') searches the whole svg and is based on .label.
That picks up those two labels, counting them as already created and thus irrelevant in the enter phase
The simplest fix is to wrap your selection in a g element, something like
var graph = svg.append("g");
graph.selectAll('.dot')
.data(graphData)
// ...
graph.selectAll('.label')
.data(graphData)
// ...
And a demo
var url = "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/cyclist-data.json";
var m = {t: 20, r: 120, b: 30, l: 40},
width = 800 - m.l - m.r,
height = 700 - m.t - m.b;
var svg = d3.select("body").append("svg")
.attr("width", width + m.l + m.r)
.attr("height", height + m.t + m.b)
.append("g")
.attr("transform", "translate(" + m.l + "," + m.t + ")");
var div = d3.select('body')
.append('div')
.style({
'position':'absolute',
'text-align':'center',
'width':'240px',
'height':'2.5em',
'font':'1.5em sans-serif',
'color':'yellow',
'background':'black',
'border-radius':'8px',
'border':'solid 1px green',
'opacity':0
});
var colorScale = d3.scale.ordinal()
.range(["#FF0000", "#009933"]);
var xScale = d3.scale.linear()
.range([width, 0]);
var yScale = d3.scale.linear()
.range([0, height]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickFormat(formatMinSec);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
d3.json(url, callback);
function callback (error, data) {
if (error) throw error;
var bestTime = _.sortBy(data, 'Place')[0].Seconds;
var graphData = _.map(data, (d)=> ({
'secondsBehind': Math.abs(bestTime - d.Seconds),
'year': d.Year,
'nationality': d.Nationality,
'doping': d.Doping,
'dopingAllegations': d.Doping.length > 0 ? "Doping Allegations":"No Doping Allegations",
'name': d.Name,
'place': d.Place,
'time': d.Time
}) )
var timeRange = d3.extent(graphData, (d) => d.secondsBehind );
xScale.domain([timeRange[0]-15, timeRange[1]]);
var rankRange = d3.extent(graphData, (d) => d.place );
yScale.domain([rankRange[0], rankRange[1]+1]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.text("Minutes : Seconds Behind Fastest Time")
.attr({
'class': 'label',
'x': width,
'y': -6
})
.style("text-anchor", "end");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.text("Ranking")
.attr({
'class': 'label',
"transform": "rotate(-90)",
"y": 6,
"dy": ".71em"
})
.style("text-anchor", "end");
var graph = svg.append("g");
graph.selectAll('.dot')
.data(graphData)
.enter()
.append('circle')
.attr('class', 'dot')
.attr('r', 5)
.attr('cx', (d)=> xScale(d.secondsBehind) )
.attr('cy', (d)=> yScale(d.place) )
.style('fill', (d)=> colorScale(d.dopingAllegations) );
graph.selectAll('.label')
.data(graphData)
.enter()
.append('text')
.attr('class', 'label')
.attr('x', (d)=> xScale(d.secondsBehind) + 10)
.attr('y', (d)=> yScale(d.place) + 4)
.text( (d)=> d.name );
// d3.selectAll('.dot')
// .on('mouseover', mouseover)
// .on('mouseout', mouseout);
var legend = svg.selectAll('.legend')
.data(colorScale.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d,i){return 'translate(0,' +i*20+')';});
legend.append('rect')
.attr('x', width)
.attr('y', 100)
.attr('width', 18)
.attr('height', 18)
.style('fill', colorScale);
legend.append('text')
.text((d)=> d)
.attr('x', width - 18)
.attr('y', 108)
.attr('dy', '.35em')
.style('text-anchor', 'end');
};
// function mouseout(){
// div.style('opacity', 1e-6);
// }
function formatMinSec(d){
if( d % 60 > 9){
return Math.floor(d/60) +':'+ d%60
} else {
return Math.floor(d/60) +':0'+ d%60
}
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.min.js"></script>
Instead of this:
svg.selectAll('.dot') // creates the correct number of dots
.data(graphData)
.enter()
.append('circle')
.attr('class', 'dot')
.attr('r', 5)
.attr('cx', (d)=> xScale(d.secondsBehind) )
.attr('cy', (d)=> yScale(d.place) )
.style('fill', (d)=> colorScale(d.dopingAllegations) );
svg.selectAll('.label') // does not create the last two text elements
.data(graphData)
.enter()
.append('text')
.attr('class', 'label')
.attr('x', (d)=> xScale(d.secondsBehind) + 10)
.attr('y', (d)=> yScale(d.place) + 4)
.text( (d)=> d.name );
Do it like this:
var gs = svg.selectAll('.dot')
.data(graphData)
.enter();
gs.append('circle')
.attr('class', 'dot')
.attr('r', 5)
.attr('cx', (d)=> xScale(d.secondsBehind) )
.attr('cy', (d)=> yScale(d.place) )
.style('fill', (d)=> colorScale(d.dopingAllegations) );
gs
.append('text')
.attr('class', 'label')
.attr('x', (d)=> xScale(d.secondsBehind) + 10)
.attr('y', (d)=> yScale(d.place) + 4)
.text( (d)=> { return d.name; } );
working code here
Other option is that instead of
svg.selectAll('.label') // does not create the last two text elements
.data(graphData)
.enter()
.append('text')
.attr('class', 'label')
.attr('x', (d)=> xScale(d.secondsBehind) + 10)
.attr('y', (d)=> yScale(d.place) + 4)
.text( (d)=> d.name );
Do this:
svg.selectAll('.label')
.data(graphData, function(d) {
if (d) {
return d.place; //unique identifier of the data, otherwise Marco Pantani will come only once.
}
})
.enter()
.append('text')
.attr('class', 'label')
.attr('x', (d) => xScale(d.secondsBehind) + 10)
.attr('y', (d) => yScale(d.place) + 4)
.text((d) => d.name);
Working code here
Related
I have implemented one scatter chart using d3.js. I want to convert this chart to line chart, but i am not able to do so. I have tried to follow ( http://embed.plnkr.co/wJDcZmkEzXaLVhuLZmcQ/ ) but it didn't helped me.
This is the code for scatter chart.
var data = [{"buildName":"otfa_R5-10_a1","build":"Build 1","value":"19628"},{"buildName":"otfa_R5-91_a1","build":"Build 2","value":"19628"},{"buildName":"otfa_R5-9_a1","build":"Build 3","value":"19628"}]
var yValues = [], responseData = [];
data.map(function(key) {
var test = [];
test[0] = key.build;
test[1] = key.value;
responseData.push(test);
yValues = key.value;
})
var margin = {
top: 20,
right: 15,
bottom: 60,
left: 60
},
width = 300 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(responseData.map(function(d) {
return d[0];
}))
.rangePoints([0, width], 0.5)
var y = d3.scale.linear()
.domain([5000,20000])
.range([height, 0]);
var chart = d3.select(divId)
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var colors = d3.scale.linear()
.domain([5, 20])
.range(['#4577bc', '#4577bc'])
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)" );
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var g = main.append("svg:g");
g.selectAll("scatter-dots")
.data(responseData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 6)
.style('stroke', function(d, i) {
return colors(i);
})
.style('fill', function(d, i) {
return colors(i);
})
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#fff8ee");
div.transition()
.duration(200)
.style("opacity", 2.9);
div .html((d[1]))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#4577bc");
div.transition()
.duration(500)
.style("opacity", 0);
});
How we can add a line connecting these points ?
Please help me !!
To add a line to your existing chart, just add it using path generators.
Line generator:
var line = d3.svg.line()
.x(function (d) { return x(d[0]); })
.y(function (d) { return y(d[1]); });
Append the line to the svg:
g.append('path').classed('line', true)
.style( { fill: 'none', 'stroke': 'steelblue'} )
.attr('d', line(responseData));
Snippet with the above code included and a few CSS styles to make it look better:
var data = [{"buildName":"otfa_R5-10_a1","build":"Build 1","value":"19628"},{"buildName":"otfa_R5-91_a1","build":"Build 2","value":"10628"},{"buildName":"otfa_R5-9_a1","build":"Build 3","value":"17628"}]
var yValues = [], responseData = [];
data.map(function(key) {
var test = [];
test[0] = key.build;
test[1] = key.value;
responseData.push(test);
yValues = key.value;
})
var margin = {
top: 20,
right: 15,
bottom: 60,
left: 60
},
width = 300 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(responseData.map(function(d) {
return d[0];
}))
.rangePoints([0, width], 0.5)
var y = d3.scale.linear()
.domain([5000,20000])
.range([height, 0]);
var chart = d3.select('body')
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var colors = d3.scale.linear()
.domain([5, 20])
.range(['#4577bc', '#4577bc'])
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)" );
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var g = main.append("svg:g");
g.selectAll("scatter-dots")
.data(responseData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 6)
.style('stroke', function(d, i) {
return colors(i);
})
.style('fill', function(d, i) {
return colors(i);
})
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#fff8ee");
div.transition()
.duration(200)
.style("opacity", 2.9);
div .html((d[1]))
.style("left", (d3.event.pageX+4) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#4577bc");
div.transition()
.duration(500)
.style("opacity", 0);
});
var line = d3.svg.line()
.x(function (d) { return x(d[0]); })
.y(function (d) { return y(d[1]); });
g.append('path').classed('line', true)
.style( { fill: 'none', 'stroke': 'steelblue'} )
.attr('d', line(responseData));
path.domain {
fill: none;
stroke: #000;
}
.axis text {
font-size: 12px;
}
div.tooltip {
position: absolute;
background: #FFF;
padding: 5px;
border: 1px solid #DDD;
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.0/d3.min.js"></script>
I am trying to make the angles on this chart smoother, D3.js is being used and I already tried to apply a few ideas as solution, like adding .interpolate("basis") on the code, but for some reason the chart disappear when I do it.
Do you have any clue on what am I doing wrong? The dots are draggable and this is the intended behavior.
Here's a sample to the code: https://codepen.io/A8-XPs/pen/ePWRxZ?editors=1010
HTML:
<svg width="500" height="350"></svg>
JS:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
let points = d3.range(1, 10).map(function(i) {
return [i * width / 10, 50 + Math.random() * (height - 100)];
});
var x = d3.scaleLinear()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
let drag = d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
svg.append('rect')
.attr('class', 'zoom')
.attr('cursor', 'move')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(points, function(d) { return d[0]; }));
y.domain(d3.extent(points, function(d) { return d[1]; }));
focus.append("path")
.datum(points)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
focus.selectAll('circle')
.data(points)
.enter()
.append('circle')
.attr('r', 5.0)
.attr('cx', function(d) { return x(d[0]); })
.attr('cy', function(d) { return y(d[1]); })
.style('cursor', 'pointer')
.style('fill', 'steelblue');
focus.selectAll('circle')
.call(drag);
focus.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
focus.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
function dragstarted(d) {
d3.select(this).raise().classed('active', true);
}
function dragged(d) {
d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
.attr('cx', x(d[0]))
.attr('cy', y(d[1]))
focus.select('path').attr('d', line);
}
function dragended(d) {
d3.select(this).classed('active', false);
}
Thank you!
To get basic interpolation use
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); })
.curve(d3.curveBasis);
or
.curve(d3.curveCatmullRom.alpha(0.5))
I create a line chart like that.
However, I added the possibility to update data. The "darker line" is updated correctly, the area no.
Here is a piece of code:
var margin = {top: 10, right: 10, bottom: 35, left: 30};
var width = 500 - margin.left - margin.right;
var height = 220 - margin.top - margin.bottom;
var x = d3.scalePoint().range([width, 0]);
var y = d3.scaleLinear().range([height, 0]);
x.domain([...new Set(dataFilter.map(function(d) {
return d.year;
}))]);
y.domain([minX, 100]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var valueline = d3.line()
.x(function(d) {
return x(d.year);
})
.y(function(d) {
return y(d.euro);
})
.defined(function(d) {
return (d.euro !== 0 && !isNaN(d.euro));
});
var area = d3.area()
.x(function(d) {
return x(d.year);
})
.y0(function(d) {
return y(d.euro);
})
.y1(height)
.defined(function(d) {
return (d.euro !== 0 && !isNaN(d.euro));
});
var svg = d3.select("#chart")
.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("path")
.attr("class", "linePath")
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 1)
.attr("d", valueline(dataFilter));
svg.append('path')
.datum(dataFilter)
.attr('d', area)
.attr('fill', 'steelblue')
.attr('stroke', 'none')
.attr('opacity', '0.1')
.attr('class', 'areaLines');
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-65)');
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
function updateData() {
dataFilter = ...; // other data
// Scale the range of the data again minX is now different
x.domain([...new Set(dataFilter.map(function(d) {
return d.year;
}))]);
y.domain([minX, 100]);
var svg = d3.select("#chart").transition();
svg.select(".linePath")
.duration(1000)
.attr("d", valueline(dataFilter));
svg.select(".x.axis")
.duration(1000)
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-65)');
// update area (doesn't work): TypeError: svg.select(...).datum is not a function
svg.select('.areaLines')
.datum(dataFilter)
.attr('d', area)
.duration(1000);
svg.select(".y.axis")
.duration(1000)
.call(yAxis);
}
When I run it I get: TypeError: svg.select(...).datum is not a function.
Why?
I found this question but I'cant able to solve the problem. Thanks a lot!!
You cannot transition the datum, it makes no sense. That's why datum() is not a transformation method.
Instead of that, transition the d attribute:
svg.select('.areaLines')
.attr('d', area(dataFilter))
.duration(1000);
I'm relatively new to D3 and trying to add labels to a grouped bar chart.. With below implementation, I'm only able to see them under one group instead of both.
Below is how the data looks in the db:
category,Exceed,Fully Meets,Partially Meets,Does not meet
business,10,20,30,30
leadership,15,5,30,50
Below is the code:
var chart1 = d3.select("#svgarea2"),
margin = { top: 70, right: 0, bottom: 30, left: 40 },
width = +chart1.attr("width") - margin.left - margin.right,
height = +chart1.attr("height") - margin.top - margin.bottom,
g = chart1.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//chart background color
var bg = d3.select("g").append("svg")
// .attr("width", width + margin.right + margin.left)
.attr("width", 510 + "px")
// .attr("height", height + margin.top + margin.bottom);
.attr("height", 310 + "px");
bg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "#f8f8ff");
bg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//scale chart
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.4);
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal() //d3.schemeCategory20
// .range(["#0000ff", "#dcdcdc", "#696969", "#00008b"]);
.range(["#00008b", "#696969", "#dcdcdc", "#0000ff"]);
var columns = ['category', 'Does Not Meet', 'Partially Meets', 'Fully Meets', 'Exceed'];
var keys = columns.slice(1);
var color = d3.scaleOrdinal()
.range(["#00008b", "#696969", "#dcdcdc", "#0000ff"]);
x0.domain(data.map(function (d) { return d.category; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, 100]).nice();
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function (d) { return "translate(" + x0(d.category) + ",0)"; })
.selectAll("rect")
.data(function (d) { return keys.map(function (key) { return { key: key, value: d[key] }; }); })
.enter().append("rect")
.attr("x", function (d) { return x1(d.key); })
.attr("y", function (d) {
return y(d.value);
})
.attr("width", x1.bandwidth() - 7)
.attr("height", function (d) { return height - y(d.value); })
.attr("fill", function (d) { return z(d.key); })
.on("mousemove", function(d){
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html((d.key) + "<br>" + (d.value) + "%");
});
g.append("g")
.selectAll("g")
.data(data).enter()
.append("g")
.attr("transform", function (d) { return keys })
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.attr("x", function (d) { return x0(d.category); })
.call(d3.axisBottom(x1))
.selectAll("text")
.attr("y", 15)
.attr("x", 0)
.attr("dy", ".35em")
.attr("transform", "rotate(50)")
.style("text-anchor", "start");;
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) - 5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Employees (%)");
//chart title
g.append("text")
.attr("x", (width / 2) + 30)
.attr("y", 1 - (margin.top / 2) + 20)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-weight", "bold")
.style("text-decoration", "underline")
.attr("font-family", "sans-serif")
.text("Performance Distribution");
};
Any help is appreciated!
It will work if you create a g element for each barchart, and then add you axes, bars etc to each. See here for example:
http://blockbuilder.org/tomshanley/aa5471a3ecaf9e41283d68188aecf042
Relevant code:
var chart = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g");
chart.attr("transform", function (d) { return "translate(" + x0(d.category) + ",0)"; })
.selectAll("rect")
.data(function (d) { return keys.map(function (key) { return { key: key, value: d[key] }; }); })
.enter().append("rect")
.attr("x", function (d) { return x1(d.key); })
.attr("y", function (d) {
return y(d.value);
})
.attr("width", x1.bandwidth() - 7)
.attr("height", function (d) { return height - y(d.value); })
.attr("fill", function (d) { return z(d.key); })
//APPEND AN AXIS TO THE CHART G'S
chart.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.attr("x", function (d) { return x0(d.category); })
.call(d3.axisBottom(x1))
.selectAll("text")
.attr("y", 15)
.attr("x", 0)
.attr("dy", ".35em")
.attr("transform", "rotate(50)")
.style("text-anchor", "start");
I have the following segment in one of my d3 graph.
What I want is to display some text in a rectangle.
var values = $('#<%=hdnDtArray.ClientID%>').val();
var data = JSON.parse(values);
margin = {
top: 20,
right: 60,
bottom: 20,
left: 100
};
var width = 900,
height = 350;
var vis = d3.select("#line_chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var parseTime = d3.time.format("%Y.%m.%d").parse;
max_y = 0;
min_y = data[0].close;
var extent = d3.extent(data.map(function(d) {
return d.date
}));
max_x = extent[1];
min = extent[0];
for (i = 0; i < data.length; i++) {
max_y = Math.max(max_y, data[i].close);
min_y = Math.min(min_y, data[i].close);
}
var x = d3.time.scale()
.rangeRound([margin.left, width]);
xScale = x.domain(d3.extent(data, function(d) {
return parseTime(d.date);
}));
yScale = d3.scale.linear().range([height - margin.top, margin.bottom]).domain([0, max_y]),
xAxis = d3.svg.axis()
.scale(xScale),
yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.innerTickSize(-width + margin.left)
.outerTickSize(0)
.tickPadding(10);
vis.append("svg:g")
.attr("class", "x axis")
.style({
'stroke': 'Black',
'fill': 'none',
"stroke-width": 1,
"font-size": "13px"
})
.attr("transform", "translate(0," + (height - margin.bottom) + ")")
.call(xAxis)
.selectAll("text")
.attr("transform", "translate(-10,0) rotate(-40)")
.style("text-anchor", "end");
vis.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width + 120)
.attr("y", height - 10)
.attr("font-weight", "bold");
vis.append("svg:g")
.attr("class", "y axis")
.style({
'stroke': 'Black',
'fill': 'none',
'stroke-width': 1,
"font-size": "13px"
})
.attr("transform", "translate(" + (margin.left) + ",0)")
.call(yAxis);
vis.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("x", margin.left + 5)
.attr("y", margin.top - 2)
.attr("font-weight", "bold");
var line = d3.svg.line()
.x(function(d) {
return xScale(parseTime(d.date));
})
.y(function(d) {
return yScale(d.close);
})
.interpolate("basis");
vis.append('svg:path')
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
var hoverLineGroup = vis.append("g")
.attr("class", "hover-line");
var hoverLine = hoverLineGroup.append("line")
.attr("stroke", "#000000")
.attr("stroke-width", "1px")
.attr("x1", 10).attr("x2", 10)
.attr("y1", 20).attr("y2", height - 20);
var hoverTT = hoverLineGroup.append('text')
.attr("class", "hover-tex capo")
.attr('dy', "0.35em");
var cle = hoverLineGroup.append("circle")
.attr("r", 4.5);
var hoverTT2 = hoverLineGroup.append('text')
.attr("class", "hover-text capo")
.attr('dy', "0.55em");
hoverLineGroup.style("opacity", 1e-6);
var rectHover = vis.append("rect")
.data(data)
.attr("fill", "none")
.attr("width", width)
.attr("height", height);
var hoverCircle = hoverLineGroup.append("circle");
var hoverRect = hoverLineGroup
.append("rect");
vis.on("mouseout", hoverMouseOff)
.on("mousemove", hoverMouseOn);
var bisectDate = d3.bisector(function(d) {
return parseTime(d.date);
}).left;
function hoverMouseOn() {
var mouse_x = d3.mouse(this)[0];
var mouse_y = d3.mouse(this)[1];
var graph_y = yScale.invert(mouse_y);
var graph_x = xScale.invert(mouse_x);
var mouseDate = xScale.invert(mouse_x);
var i = bisectDate(data, mouseDate);
var d0 = data[i - 1]
var d1 = data[i];
var d = mouseDate - d0[0] > d1[0] - mouseDate ? d1 : d0;
hoverRect.attr("class", "y")
.style("fill", "none")
.style("stroke", "black")
.attr('x', mouse_x + 8)
.attr('y', yScale(d.close) - 20)
.attr("width", 200)
.attr("height", 50);
hoverTT.text("Test Text")
.attr("opacity", "1");
hoverTT.attr('x', mouse_x + 23);
hoverTT.attr('y', yScale(d.close));
/*
hoverTT.text("Date: " + d.date);
hoverTT.attr('x', mouse_x + 23);
hoverTT.attr('y', yScale(d.close));
hoverTT2.text("Portfolio Value: " + Math.round(d.close * 100) / 100)
.attr('x', mouse_x + 23)
.attr('y', yScale(d.close) + 10);
*/
hoverLine.attr("x1", mouse_x).attr("x2", mouse_x)
hoverLineGroup.style({
'font-weight': 'bold',
'opacity': 1
});
hoverCircle.attr("class", "y")
.style("fill", "blue")
.style("stroke", "blue")
.attr("r", 4)
.attr('cx', mouse_x)
.attr('cy', yScale(d.close));
}
function hoverMouseOff() {
hoverLineGroup.style("opacity", 1e-6);
}
The text is not visible now. But if I set the "fill" property to "none", then the text becomes visible.
What I want is the background to be non transparent, that's why I made it white.
Still I want the text to be visible.
The problem with your code is the order of the selections.
In an SVG, just like a real painter using ink in a real canvas, what is painted later remains on top. So, if you want the text to be on top of the rectangle (with any fill you want), set the text's selection after the rectangle's selection.
Therefore, in your case, this...
var hoverTT = hoverLineGroup.append('text')
.attr("class", "hover-tex capo")
.attr('dy', "0.35em");
... has to be after this:
var hoverRect = hoverLineGroup
.append("rect");
Here is a demo, the rectangle has a solid white fill. Have a look at the order of the selections:
var svg = d3.select("svg");
var hoverRect = svg.append("rect")
.attr("fill", "white")
.attr("stroke", "firebrick")
.attr("width", 40)
.attr("height", 30)
.attr("opacity", 0);
var hoverText = svg.append("text")
.text("foo")
svg.on("mousemove", function() {
var coords = d3.mouse(this);
hoverRect.attr("x", coords[0] + 15)
.attr("y", coords[1])
.attr("opacity", 1)
hoverText.attr("x", coords[0] + 25)
.attr("y", coords[1] + 20)
})
svg {
border: 1px solid gray;
background-color: gainsboro;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>