I have a basic line chart with two lines like this
I want to apply background in part where only blue line is to highlight part where both lines are. What is the best way to do this in D3.js?
Part of my code
x = d3.time.scale()
.domain([date_from, date_to])
.range([0, width])
y = d3.scale.linear()
.domain([15, 30])
.range([height, 0])
line = d3.svg.line()
.x((d, i) -> x(parseDate(d.date)))
.y((d) -> y(d.price))
svg = d3.select("#graph").append("svg:svg")
.attr("width", width + 50)
.attr("height", height + 50)
.append("svg:g")
.attr("transform", "translate(25, 25)")
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0, #{height})")
.call(xAxis)
svg.append("svg:g")
.attr("class", "grid")
.attr("transform", "translate(0, #{height})")
.call(xAxis.tickSize(-height, 0, 0).tickFormat("").ticks(80))
svg.append("svg:g")
.attr("class", "grid")
.call(yAxis.tickSize(-width, 0, 0).tickFormat("").ticks(40))
svg.append("svg:path")
.attr("class", "line")
.attr("d", line(data))
.style("stroke", (d) -> color("year"))
svg.append("svg:path")
.attr("class", "line")
.attr("d", line(data2))
.style("stroke", (d) -> color("month"))
Draw a rect element on that portion of the chart.
var rectangle = svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", x(firstXValueOfSecondLine))
.attr("height", chartHeight)
.style('opacity', 0.5)
.style('fill', '#ededed');
Related
I am using d3 js to draw a bar graph. I have x and y axis too.
The x axis would hold the 'names' and y axis the 'marks'. I am using ordinal scale for x axis.
In my json input dataset_rule_errors, I have 10 entries.
My code is
var svgd = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var x_domain = dataset_rule_errors.map(function(d) { return d.Rulename; })
var xScale = d3.scale.ordinal()
.domain(dataset_rule_errors.map(function (d) { return d.Rulename; }))
.rangeBands([padding_rule, wsvg]);
var xaxeScale = d3.scale.ordinal()
.domain(x_domain)
.rangePoints([padding_rule, wsvg]);
var xAxis = d3.svg.axis()
.scale(xaxeScale)
.tickValues(x_domain)
.orient("bottom");
//drawing rectangles
svgd.append("g")
.selectAll("rect") //based on the data in the dataset[] array, append rectangles s.t.
.data(dataset_rule_errors) //
.enter()
.append("rect")
.attr("x", function (d, i) {
return xScale(d.Rulename); // x position of rect as per i->0,1,2,3,...
})
.attr("y", function (d) {
return (h_rule - yScale(d.NumRuleFailed)); //y position of rect as per (h-value) to prevent inverted range
})
.attr("width", xScale.rangeBand())//"10") //depending upon domain->no of inputs - with of band is decided acc. to fit in svg
.attr("height", function (d) {
return yScale(d.NumRuleFailed); //depending upon domain->value of inputs - with of band is decided acc. to fit in svg
})
.attr("fill", function (d, i) { //colour based on values -> more errors - dark coloured bars
if(i%2==0)
return "rgb(" + 255 + "," + 255 + "," + 200 + ")";
else
return "rgb(" + 0 + "," + 0 + "," + 200 + ")";
})
.attr("stroke", "black");
//drawing x axis with ticks
svgd.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + 0 + "," + (h_rule) + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("text-anchor", "start")
.attr("transform", function (d) {
return "rotate(-90)"
})
.selectAll(".tick text")
.style("text-anchor", "start");
The problem I am facing is that my rectangles and the ticks of x-axis do not align with one another.
The reason is because I have 10 bars and therefore, I should be having 11 ticks including the one at the beginning and the end. But I have only 10 ticks, which distribute evenly along the axis length, so they do not coincide with the rectangle beginnings just like in this question Unable to align ticks with d3.js.
But the solution for this question did not work out for me. What can I do?
dataset_rule_errors = data I retrieve from my database
[{"Rulename":"A","NumRuleFailed":34321},{"Rulename":"B","NumRuleFailed":43},{"Rulename":"C","NumRuleFailed":45522},
{"Rulename":"D","NumRuleFailed":43643},{"Rulename":"E","NumRuleFailed":152},{"Rulename":"F","NumRuleFailed":152}]
I could not reproduce the issue you said you were having but I highly recommend using rangeRoundBands for a bar chart.
You can achieve the bar chart with the following setup:
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .2);
var y = d3.scale.linear()
.range([height, 0]);
// Finding domain of x (all our rulenames)
x.domain(data.map(function(d) {
return d.Rulename;
}));
// Finding domain of y (min and max values)
y.domain([d3.min(data, function(d) {
return d.NumRuleFailed;
}), d3.max(data, function(d) {
return d.NumRuleFailed;
})]);
var xAxis = d3.svg.axis()
.scale(x)
// no need yo specify ticks, x scale
// will take care of that
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
And the following for axis and rect rendering:
// Render xAxis
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", "-.50em")
.attr("text-anchor", "start")
.attr("transform", "rotate(-90)")
.selectAll(".tick text")
.style("text-anchor", "start")
// Render yAxis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("NumRuleFailed");
// Render rects
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.Rulename);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.NumRuleFailed);
})
.attr("fill", function(d, i) { //colour based on values -> more errors - dark coloured bars
return (i % 2) ? 'green' : 'red';
})
.attr("height", function(d) {
return height - y(d.NumRuleFailed);
});
Full Plnkr: https://plnkr.co/edit/jmtMGcRyT9hM5efwwTOb?p=preview
I'm trying to add text to a vertical line using d3. In principle the text is attached to the line but it is not showing. Any idea why? Here is my code and and image of the current state.Code problem
Thanks in advance!
var espanya = aggregatevalue(data)[0]['2015'];
var svg = d3.select("#UnemploymentRate")
.append("svg")
.attr("id", "chart")
.attr("width", w)
.attr("height", h);
svg.append("g")
.attr("class", "espanya line")
var lespanya = d3.select(".espanya.line")
.append("line")
.attr("class", "line")
.style("stroke-width", 2)
.style("stroke", "black")
.style("stroke-dasharray", ("3, 3"))
.style("fill", "none");
lespanya.attr("x1", padding.left + widthScale(espanya))
.attr("y1", heightScale(maximo))
.attr("x2", padding.left + widthScale(espanya))
.attr("y2", h - padding.bottom);
lespanya.append("text")
.attr("x", 486.78)
.attr("y", 300)
.text("Spain Average unemployment");
You should not append that group to the line element. You can append both text and line to .espanya.
It has to be more or less like this,
var svg = d3.select("#UnemploymentRate")
.append("svg")
.attr("id", "chart")
.attr("width", w)
.attr("height", h);
var container = svg.append("g")
.attr("class", "espanya")
var lespanya = container
.append("line")
.attr("class", "line")
.style("stroke-width", 2)
.style("stroke", "black")
.style("stroke-dasharray", ("3, 3"))
.style("fill", "none");
var text = container
.append("text")
.attr("x", 486.78)
.attr("y", 300)
.text("Spain Average unemployment");
Try this and provide a jsfiddle if it doesn't work.
I have created a bar chart using d3.js but I need a solid black line at 80 on the y axis going across the chart to indicate a 'maximum threshold'
I've figured out how to draw a line at the bottom but I need it to always appear at the value 80 on the chart. Is there anyway to do this?
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0])
.domain([0,1000]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
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 + ")");
d3.tsv("data.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.risk; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.name); })
.attr("width", x.rangeBand())
.attr("y", function(d) { console.log(d); return y(d.risk); })
.attr("height", function(d) { return height - y(d.risk); })
.style("fill", function(d) { // <== Add these
if (d.risk >= 80) {return "#e74c3c"} // <== Add these
else { return "#3498db" } // <== Add these
;})
.on("mouseover", function(d) {
// debugger
d3.select(this).transition().style({opacity:'0.6'});
console.log(d,this.x);
div.transition()
.duration(200)
.style("opacity", .9);
div .html(d.risk)
.style("left", (this.x.baseVal.value + (this.width.baseVal.value/2) + 15) + "px")
.style("top", (this.y.baseVal.value + (this.height.baseVal.value/2)) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
d3.select(this).transition().style({opacity:'1'});
});
var median = d3.median(svg.selectAll('.bar').data(),
function(d) { return d.risk; });
var median = svg.append("line")
.attr("x1", 0)
.attr("y1", (height/10)*0.8)
.attr("x2", width)
.attr("y2", (height/10)*0.8)
.attr("stroke-width", 2)
.attr("stroke", "black");
});
You have already built a scale function y which translates values on your input scale into your chart height output range. You use this when you calculate where to draw the top of each bar. You can simply use this function again when drawing the threshold line:
// not clear if you meant 80 as 80% or as literal value 80 on your input scale
var threshold = 80;
var median = svg.append("line")
.attr("x1", 0)
.attr("y1", y(threshold))
.attr("x2", width)
.attr("y2", y(threshold))
.attr("stroke-width", 2)
.attr("stroke", "black");
I have a line (& area) graph which works ok, when horizontal. But I really need it to be vertical, to stand up. I have tried all kinds of changes to the existing code. Mostly with some strange results.
Here’s the code (modified it a bit). Is there a way to change it to make the graph vertical?:
var x = d3.scale.linear().domain([1, itemCount]).range([0, width]);
var y = d3.scale.linear().domain([0, maxValue]).rangeRound([height, 0]);
// Set up linar x and y axis.
var xAxis = d3.svg.axis().scale(x).ticks(10);
var yAxis = d3.svg.axis().scale(y).ticks(2).orient("left");
// Line graph.
line = d3.svg.line()
.interpolate("basis")
.x(function (d) {
return x(d.x);
})
.y(function (d) {
return y(d.y);
});
// Create SVG element.
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// X-Axis, to bottom.
svg.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(1," + height + ")")
.call(xAxis);
//Y-Axis
svg.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(40, 1)")
.call(yAxis);
// Horizontal axis guide lines.
svg.selectAll("line.y")
.data(y.ticks(5))
.enter()
.append("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#000000")
.style("stroke-opacity", 0.1);
// Vertical axis guide lines.
svg.selectAll("line.x")
.data(y.ticks(5))
.enter()
.append("line")
.attr("x1", x)
.attr("x2", x)
.attr("y1", 0)
.attr("y2", this.heightInside - pushDown)
.style("stroke", "#000000")
.style("stroke-opacity", 0.1);
// Set up domains. Nice ensures the domains ends on nice round values.
x.domain([dataValues[0].x, dataValues[dataValues.length - 1].x]).nice();
y.domain([d3.min(dataValues, function (d) { return (d.y); }),
d3.max(dataValues, function (d) { return (d.y); })])
.nice();
// Draw line on graph.
svg.append("svg:path")
.attr("class", "line")
.attr("d", line(dataValues))
.style("stroke", function(d) { return colors[i]; });
// Marks.
svg.selectAll("circle_" + i)
.data(dataValues)
.enter()
.append("circle")
.style("fill", function(d) { return _this.colors[i]; })
.attr("r", 4)
.attr('cx', function (d) { return x(d.x); })
.attr('cy', function (d) { return y(d.y); });
I need some help with my following D3 line chart, where x is a time domain and y is linear scale value, currently in this fiddle, the transition of path occurs from right to left, but I want it to happen as left to right instead.
var data = [{"closedate":"2013-12-07T08:00:00.000Z","amount":60000}];
//Set Canvas Area properites
var margin = {top: 50, right: 50, bottom: 100, left: 100},
width = 500 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//X value transformation function - transforms close date into chart x position of canvas
var x = d3.time.scale()
.range([0, width])
.domain(d3.extent(data, function(d) { return new Date(d.closedate); }));
//Y Value transformation function - transforms amount into y position of canvas
var y = d3.scale.linear()
.range([height, 0])
.domain(d3.extent(data, function(d) { return d.amount; }));
//X Axis Function
var xAxis = d3.svg.axis()
.scale(x)
//.tickFormat(d3.time.format('%m/%y'))
.ticks(6)
.orient("bottom");
//Y Axis Function
var yAxis = d3.svg.axis().scale(y).ticks(6).orient("left");
//Line Function to draw SVG Line
var line = d3.svg.line()
.interpolate("cardinal")
.x(function(d) { return x(new Date(d.closedate)); })
.y(function(d) { return y(d.amount); });
//Create SVG canvas area with height and width properites
var svg = d3.select("#d3linechartid").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 + ")");
//console.log("data = "+JSON.stringify(data));
//Draw XAxis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", width )
.attr("y", "50")
.style("text-anchor", "middle")
.text("Close Date");
//Draw YAxis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "middle")
.text("Sales Amount ($)");
//Draw SVG Path
var path = svg.append("path")
.datum(data)
.attr("d", line)
.style('fill', 'none')
.style('stroke', 'steelblue')
.attr("stroke-width", "2");
//Do Transistion of Path
var totalLength = path.node().getTotalLength();
path
.attr("stroke-dasharray", totalLength+","+totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear-in-out")
.attr("stroke-dashoffset", 0);
You can sort the input data in ascending order, like so:
data.sort(function(a, b){ return d3.ascending(a.closedate, b.closedate); });