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>
Related
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))
How can I assign id base on its name?
the link of the screenshot of the console is down below.
Thanks
serie.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("class","bar")
.attr("x", function(d) { return x(d.data.Company); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth())
.attr("id",function(d,i){
return "id"+ d.name;
})
I discourage assigning ids to every bar as ids need to be unique. Its better to have a class instead. But as requested, I have posted a fiddle below. Hover on the bars to get the id. And you also said you need the id for animating so I've also added some animations when the chart loads (If that's what you meant by animation).
// Setup svg using Bostock's margin convention
var margin = {
top: 20,
right: 160,
bottom: 35,
left: 30
};
var width = 700 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var svg = d3.select("body")
.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 + ")");
/* Data in strings like it would be if imported from a csv */
var data = [{
mystate: "Intuit",
name: "Asian",
y0: 53,
y1: 1824,
value: "1771"
}, {
mystate: "Intuit",
name: "Latino",
y0: 2707,
y1: 1231,
value: "1771"
}, {
mystate: "Intuit",
name: "Black_or_African_American",
y0: 2060,
y1: 1824,
value: "1771"
}, {
mystate: "Intuit",
name: "Caucasian",
y0: 355,
y1: 1024,
value: "1771"
}];
// Transpose the data into layers
var dataset = d3.layout.stack()(['y0', 'y1'].map(function(values) {
return data.map(function(d) {
return {
x: d.name,
y: +d[values],
};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([10, width - 10], 0.02);
var y = 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 = ["b33040", "#d25c4d", "#f2b447", "#d9d574"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat(function(d) {
return d
});
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
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.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) {
return colors[i];
});
var rect = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return height;
})
.attr("height", function(d) {
return 0;
})
.attr("width", x.rangeBand())
.attr('id', function(d) {
return 'id' + d.x;
})
.on("mouseover", function() {
console.log(d3.select(this).attr('id'));
tooltip.style("display", null);
})
.on("mouseout", function() {
tooltip.style("display", "none");
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
}).transition().duration(1000)
.delay(function(d, i) {
return i * 300;
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
}).attr("y", function(d) {
return y(d.y0 + d.y);
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.13/d3.min.js"></script>
Now coming back to your code, when you transform the data by using d3.layout.stack() there is no property called name in your new data (I suppose) and hence your ids are idundefined
Hi I am trying to adapt Matthew Izanuk's Unit Bar Chart with Brush and Zoom to a randomly generated bar chart I have already created.
I get an error with the zoom function, on line 261
line 261 is
x.domain(t.rescaleX(x2).domain());
Here is the code and jsFiddle
<!DOCTYPE html>
<body>
<style>
div {
display: inline-block;
vertical-align: top;
}
#bar_chart {
border: 2px solid lightgray;
border-radius: 15px;
}
#json {
max-height: 600px;
width: 200px;
overflow: scroll;
border: 2px solid gray;
border-radius: 15px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
<div id="bar_chart">
</div>
<div id="json"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//************* generate data ************
var data = [];
var space = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var spaceLength = space.length;
makedata();
function makedata() {
var obj = {};
for (var i = 0; i < spaceLength; i++) {
obj = {};
value = Math.floor(Math.random() * 500);
rand = Math.floor(Math.random() * space.length)
name = space.charAt(rand);
obj["name"] = name;
obj["val"] = value;
data.push(obj);
space = space.slice(0, rand) + space.slice(rand + 1, space.length)
}
}
// To display json in html page
document.getElementById("json").innerHTML = "<pre>" + JSON.stringify(data, null, 4) + "</pre>";
var margin = {
top: 50,
right: 20,
bottom: 90,
left: 50
},
margin2 = {
top: 530,
right: 20,
bottom: 30,
left: 50
},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom,
height2 = 600 - margin2.top - margin2.bottom;
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var x2 = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height, 0]);
var y2 = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height2, 0]);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var svg = d3.select("#bar_chart")
// .data(data)
.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("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var g0 = focus.append("g")
.attr("class", "focus")
.attr("transform", "translate(0,0)");
var xAxis = d3.axisBottom(x);
var xAxis2 = d3.axisBottom(x2);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
focus.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(7));
var tooltip = d3.select("#info")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var focus_group = focus.append("g");
focus_group.attr("clip-path", "url(#clip)");
var rects = focus_group.selectAll('rect')
.data(data);
//********* Bar Chart 1 ****************
var newRects1 = rects.enter();
newRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x(d.name);
})
.attr('y', function(d, i) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.attr("transform", "translate(" + 4 + ",0)")
.style('fill', 'lightblue')
.style('stroke', 'gray');
var focus_group = context.append("g");
focus_group.attr("clip-path", "url(#clip)");
var brushRects = focus_group.selectAll('rect')
.data(data);
//********* Brush Bar Chart ****************
var brushRects1 = brushRects.enter();
brushRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x2(d.name);
})
.attr('y', function(d, i) {
return y2(d.val);
})
.attr('height', function(d, i) {
return height2 - y2(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.attr("transform", "translate(" + 4 + ",0)")
.style('fill', 'lightblue')
.style('stroke', 'gray');
//append brush xAxis2
context.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
//create brush function redraw scatterplot with selection
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.selectAll(".mainBars")
.attr("x", function(d) {
return x(d.name);
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x-axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll(".mainBars")
.attr("x", function(d) {
return x(d.name);
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x-axis").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
</script>
Any help would be greatly appreciated,
thanks
As mentioned in the comments, invert only exists for continuous ranges. Your scaleBand is a ordinal scale composed of discreet values. One way I've worked around this is to simply figure which values in my domain fall in the selection:
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return;
// ignore brush-by-zoom
// get bounds of selection
var s = d3.event.selection,
nD = [];
// for each "tick" in our domain is it in selection
x2.domain().forEach((d)=>{
var pos = x2(d) + x2.bandwidth()/2;
if (pos > s[0] && pos < s[1]){
nD.push(d);
}
});
// set new domain
x.domain(nD);
// redraw
focus.selectAll(".mainBars")
// hide bars not in domain
.style("opacity", function(d){
return x.domain().indexOf(d.name) === -1 ? 0 : 100;
})
.attr("x", function(d) {
return x(d.name)+ x.bandwidth()/2 - 5;
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x.axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
Running code:
<!DOCTYPE html>
<head>
<style>
div {
display: inline-block;
vertical-align: top;
}
#bar_chart {
border: 2px solid lightgray;
border-radius: 15px;
}
#json {
max-height: 600px;
width: 200px;
overflow: scroll;
border: 2px solid gray;
border-radius: 15px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
</head>
<body>
<div id="bar_chart">
</div>
<div id="json"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//************* generate data ************
var data = [];
var space = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var spaceLength = space.length;
makedata();
function makedata() {
var obj = {};
for (var i = 0; i < spaceLength; i++) {
obj = {};
value = Math.floor(Math.random() * 500);
rand = Math.floor(Math.random() * space.length)
name = space.charAt(rand);
obj["name"] = name;
obj["val"] = value;
data.push(obj);
space = space.slice(0, rand) + space.slice(rand + 1, space.length)
}
}
// To display json in html page
document.getElementById("json").innerHTML = "<pre>" + JSON.stringify(data, null, 4) + "</pre>";
var margin = {
top: 50,
right: 20,
bottom: 90,
left: 50
},
margin2 = {
top: 530,
right: 20,
bottom: 30,
left: 50
},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom,
height2 = 600 - margin2.top - margin2.bottom;
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var x2 = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height, 0]);
var y2 = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height2, 0]);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var svg = d3.select("#bar_chart")
// .data(data)
.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("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var xAxis = d3.axisBottom(x);
var xAxis2 = d3.axisBottom(x2);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
focus.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(7));
var tooltip = d3.select("#info")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var focus_group = focus.append("g");
focus_group.attr("clip-path", "url(#clip)");
var rects = focus_group.selectAll('rect')
.data(data);
//********* Bar Chart 1 ****************
var newRects1 = rects.enter();
newRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x(d.name) + x.bandwidth()/2;
})
.attr('y', function(d, i) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.style('fill', 'lightblue')
.style('stroke', 'gray');
var focus_group = context.append("g");
focus_group.attr("clip-path", "url(#clip)");
var brushRects = focus_group.selectAll('rect')
.data(data);
//********* Brush Bar Chart ****************
var brushRects1 = brushRects.enter();
brushRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x2(d.name);
})
.attr('y', function(d, i) {
return y2(d.val);
})
.attr('height', function(d, i) {
return height2 - y2(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.attr("transform", "translate(" + 4 + ",0)")
.style('fill', 'lightblue')
.style('stroke', 'gray');
//append brush xAxis2
context.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
//create brush function redraw scatterplot with selection
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
// get bounds of selection
var s = d3.event.selection,
nD = [];
x2.domain().forEach((d)=>{
var pos = x2(d) + x2.bandwidth()/2;
if (pos > s[0] && pos < s[1]){
nD.push(d);
}
});
x.domain(nD);
focus.selectAll(".mainBars")
.style("opacity", function(d){
return x.domain().indexOf(d.name) === -1 ? 0 : 100;
})
.attr("x", function(d) {
return x(d.name)+ x.bandwidth()/2 - 5;
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x.axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
/*
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll(".mainBars")
.attr("x", function(d) {
return x(d.name) + x.bandwidth()/2;
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x-axis").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
*/
}
</script>
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>
This question is pretty such similar to this D3JS question but I am using latest D3 version(//d3js.org/d3.v4.min.js).
I am trying make this Normalized Stacked Bar Chart
chart horizontal. Is there any optimized way in latest version to achieve this?
I have swapped the x axis and y axis as below
var svg = d3.select("svg"),
margin = {top: 20, right: 60, bottom: 30, left: 40},
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 + ")");
var y = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
.align(0.1);
var x = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var stack = d3.stack()
.offset(d3.stackOffsetExpand);
d3.csv("data.csv", type, function(error, data) {
if (error) throw error;
data.sort(function(a, b) { return b[data.columns[1]] / b.total - a[data.columns[1]] / a.total; });
y.domain(data.map(function(d) { return d.State; }));
z.domain(data.columns.slice(1));
var serie = g.selectAll(".serie")
.data(stack.keys(data.columns.slice(1))(data))
.enter().append("g")
.attr("class", "serie")
.attr("fill", function(d) { return z(d.key); });
serie.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.data.State); })
.attr("x", function(d) { return x(d[1]); })
.attr("height", function(d) { return x(d[0]) - x(d[1]); })
.attr("width", y.bandwidth());
g.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(y));
g.append("g")
.attr("class", "axis axis--x")
.call(d3.axisLeft(x).ticks(10, "%"));
var legend = serie.append("g")
.attr("class", "legend")
.attr("transform", function(d) { var d = d[d.length - 1]; return "translate(" + (y(d.data.State) + y.bandwidth()) + "," + ((x(d[0]) + x(d[1])) / 2) + ")"; });
legend.append("line")
.attr("x1", -6)
.attr("x2", 6)
.attr("stroke", "#000");
legend.append("text")
.attr("y", 9)
.attr("dy", "0.35em")
.attr("fill", "#000")
.style("font", "10px sans-serif")
.text(function(d) { return d.key; });
});
function type(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}
Referring the example
You will need to reverse the domains:
var y = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
.align(0.1);
var x = d3.scaleLinear()
.rangeRound([height, 0]);
Swap the x to y as the domains are reverse so x will become y and y will become x, when you create the rectangles.
serie.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("y", function(d) {
return y(d.data.State);
})
.attr("x", function(d) {
return x(d[1]);
})
.attr("width", function(d) {
return x(d[0]) - x(d[1]);
})
.attr("height", y.bandwidth());
Change the legend position accordingly to position it on the top bar.
var legend = serie.append("g")
.attr("class", "legend")
.attr("transform", function(d) {
var d = d[0];//get the top data for placing legends on that.
return "translate(" + ((x(d[0]) + x(d[1])) / 2) + ", " +(y(d.data.State) - y.bandwidth())+ ")";
});
Finally position the legend lines:
legend.append("line")
.attr("y1", 5)
.attr("x1", 15)
.attr("x2", 15)
.attr("y2", 12)
.attr("stroke", "#000");
working code here
Below example also will help you
var initStackedBarChart = {
draw: function(config) {
me = this,
domEle = config.element,
stackKey = config.key,
data = config.data,
margin = {top: 20, right: 20, bottom: 30, left: 50},
parseDate = d3.timeParse("%m/%Y"),
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
xScale = d3.scaleLinear().rangeRound([0, width]),
yScale = d3.scaleBand().rangeRound([height, 0]).padding(0.1),
color = d3.scaleOrdinal(d3.schemeCategory20),
xAxis = d3.axisBottom(xScale),
yAxis = d3.axisLeft(yScale).tickFormat(d3.timeFormat("%b")),
svg = d3.select("#"+domEle).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 + ")");
var stack = d3.stack()
.keys(stackKey)
/*.order(d3.stackOrder)*/
.offset(d3.stackOffsetNone);
var layers= stack(data);
data.sort(function(a, b) { return b.total - a.total; });
yScale.domain(data.map(function(d) { return parseDate(d.date); }));
xScale.domain([0, d3.max(layers[layers.length - 1], function(d) { return d[0] + d[1]; }) ]).nice();
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return yScale(parseDate(d.data.date)); })
.attr("x", function(d) { return xScale(d[0]); })
.attr("height", yScale.bandwidth())
.attr("width", function(d) { return xScale(d[1]) - xScale(d[0]) });
svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + (height+5) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(0,0)")
.call(yAxis);
}
}
var data = [{"date":"4/1854","total":8571,"disease":1,"wounds":0,"other":5},{"date":"5/1854","total":23333,"disease":12,"wounds":0,"other":9},{"date":"6/1854","total":28333,"disease":11,"wounds":0,"other":6},{"date":"7/1854","total":28772,"disease":359,"wounds":0,"other":23},{"date":"8/1854","total":30246,"disease":828,"wounds":1,"other":30},{"date":"9/1854","total":30290,"disease":788,"wounds":81,"other":70},{"date":"10/1854","total":30643,"disease":503,"wounds":132,"other":128},{"date":"11/1854","total":29736,"disease":844,"wounds":287,"other":106},{"date":"12/1854","total":32779,"disease":1725,"wounds":114,"other":131},{"date":"1/1855","total":32393,"disease":2761,"wounds":83,"other":324},{"date":"2/1855","total":30919,"disease":2120,"wounds":42,"other":361},{"date":"3/1855","total":30107,"disease":1205,"wounds":32,"other":172},{"date":"4/1855","total":32252,"disease":477,"wounds":48,"other":57},{"date":"5/1855","total":35473,"disease":508,"wounds":49,"other":37},{"date":"6/1855","total":38863,"disease":802,"wounds":209,"other":31},{"date":"7/1855","total":42647,"disease":382,"wounds":134,"other":33},{"date":"8/1855","total":44614,"disease":483,"wounds":164,"other":25},{"date":"9/1855","total":47751,"disease":189,"wounds":276,"other":20},{"date":"10/1855","total":46852,"disease":128,"wounds":53,"other":18},{"date":"11/1855","total":37853,"disease":178,"wounds":33,"other":32},{"date":"12/1855","total":43217,"disease":91,"wounds":18,"other":28},{"date":"1/1856","total":44212,"disease":42,"wounds":2,"other":48},{"date":"2/1856","total":43485,"disease":24,"wounds":0,"other":19},{"date":"3/1856","total":46140,"disease":15,"wounds":0,"other":35}];
var key = ["wounds", "other", "disease"];
initStackedBarChart.draw({
data: data,
key: key,
element: 'stacked-bar'
});
.axis text {
font: 10px sans-serif;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.path-line {
fill: none;
stroke: yellow;
stroke-width: 1.5px;
}
svg {
background: #f0f0f0;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id='stacked-bar'></div>