I am trying to create a simple line graph using d3 which segments the curve and paint each segment with a different colour. Currently I am only able to colour the whole area under the curve.
Current:
Attempting to achieve (Pardon me for the terrible colouring. In a rush for time):
This is bits of relevant code. Please help!
var x = d3.scaleLinear();
var y = d3.scaleLinear();
//Set the range of the data
x.domain([0, Math.max(endGrowth, endPlateau) ,maxX]).range([0, width*0.8, width]);
y.domain([0, maxY]).range([height, 0]);
//Define the area under the Graph
var area1 = d3.area()
.x0(function(d){
return x(d.Rank);
})
.y1(function(d){ return y(d.Elements);})
.y0(height);
//Add the colored regions
svg.append("path")
.data([data])
.attr("class", "areaUnderGraph")
.attr("fill", "blue")
.attr("transform", "translate(" + leftMarginLabel + ",0)")
.attr("d", area1);
Right now, the area under the curve is one path, so it can only be colored one color. The simplest way to color different portions under the curve different colors is to split them up in data. It's not clear where data is coming from, but you'd take sub-sections of the array, like
var segments = [data.slice(0, 2), data.slice(2)];
svg.selectAll("path")
.data(segments)
.enter()
.append("path")
.attr("fill", function(d) { /* use d to choose a color */ })
That's the gist: you'd have multiple slices of the data, and instead of one path, you'd create multiple paths that you can color as you wish.
Related
I am trying to do a radial area plot with a colour gradient to reflect this. However the gradient does not appear to ever want to sit perfectly in line / at the correct side with the circle (see image below).
I have tried constraining the width and hight of the svg so that they are equal, but with no avail.Every refresh (with random data), the central ring for the gradient will shift and warp into a different shape, but never lie on the mean line where it should be.
I have ensured that the svg is square
var width = window.innerWidth , height = window.innerHeight;
width = height = d3.min([width,height])
var data = d3.range(0,100).map(d=>Math.random())
Given the gradient the following properties
var linearGradient = defs.append("radialGradient")
.attr("id", "rad-gradient")
.attr("cx", "50%") //The x-center of the gradient, same as a typical SVG circle
.attr("cy", "50%") //The y-center of the gradient
.style("r", "50%");
and my area mathematically as
var area = d3.area()
.x1(function(d,i) { return width/2+Math.cos(i\*angle)*(h(d)) }) .x0(function(d,i) { return > width/2+Math.cos(i\*angle)*(h(mean)) }) .y0(function(d,i) {> return height/2+Math.sin(i\*angle)*(h(mean)) }) .y1(function(d,i) { return height/2+Math.sin(i\*angle)*(h(d)) })
And appended the created gradient to the svg path
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", area)
.style("fill", "url(#rad-gradient)")
The solution lies in creating a circle and filling it with the correct gradient. This ensures that the radial gradient will always be centered around the origin.
You can then use clipPath to extract only the path you wish to use from the circle. After a little fiddling to align the gradient radius, we get the desired effect.
// previous definition for area path appended as a clip path
clip = defs.append("clipPath")
.attr('id','clip')
.append("path")
.data([data])
.attr("class", "area")
.attr("d", area)
//extracting this path from a circle with the desired gradient
svg.append("circle")
.attr("cx", width/2)
.attr("cy", height/2)
.attr("r", rmax)
.attr("clip-path","url(#clip)")
.style("fill", "url(#linear-gradient)")
I have already a code parallel coordinates graph, everything works fine. Now i'm trying to use colors to color-code the parallel coordinates visualization, but something is wrong. In dataset (http://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data) i've got different names of wine species (1st column is class identifier (1-3)), but in graph draws only one color. Could anybody help me?
Graph :
enter code here
// CREATE A COLOR SCALE
var color = d3.scale.ordinal()
.domain(['1','2','3'])
.range(['red','blue','green'])
d3.csv("wine.csv", function(error, wine) {
// Extract the list of dimensions and create a scale for each.
x.domain(dimensions = d3.keys(wine[0]).filter(function(d) {
return d != "name" && (y[d] = d3.scale.linear()
.domain(d3.extent(wine, function(p) { return +p[d]; }))
.range([h, 0]));
}));
// Add grey background lines for context.
background = svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(wine)
.enter().append("path")
.attr("d", path);
// USE THE COLOR SCALE TO SET THE STROKE BASED ON THE DATA
foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(wine)
.enter().append("path")
.attr("d", path)
.attr("stroke", function(d) {
var species = d.name.slice(0,d.name.indexOf(' '));
return color(species);
})
Once you already have your ordinal scale for the colors with the domain and range defined, you only need to color your lines according to d.name:
.attr("stroke", function(d) {
return color(d.name);
});
I have a simple pie chart made with d3js and I would like to add transparent gap between each path.
paths = pieWrap.selectAll("path")
.data(pie(data)).enter()
.append("path")
.style("fill", "rgba(90, 168, 217, 1)")
.style("stroke", "#FFF")
.style("stroke-width", "1")
.style("stroke-opacity", "0")
.attr("d", arc);
Example here : http://jsfiddle.net/x4p0eLmL/2/
Just to know, in my case the background is an image so I can't use its color.
I tried stroke-opacity but it doesn't seem to work.
Is there a proper way to do that with d3js?
Thanks
I have had the same idea as #redress suggested: http://jsfiddle.net/x4p0eLmL/9/. The added part is as follows:
.attr("transform", function(path) {
middleAngle = -Math.PI/2 + (path.startAngle+path.endAngle)/2;
dx = 3 * Math.cos(middleAngle);
dy = 3 * Math.sin(middleAngle);
return "translate("+dx+", "+dy+")";
})
path has the attributes startAngle and endAngle. There is computed middle angle and translated. It is suitable for smaller gaps between the paths. Each path is translated to outer circle with radius+3 in the current example. There is the problem with "wider" gaps where the circle may be "distorted"
I have a choropleth map of the united states showing total population. I would like to add a legend to the map showing the quantile range values.I’ve seen other similar questions about this topic but can’t seem to get it to work for my specific case. I know I need to include the color range or color domain but just not sure if this is the correct way. As of right now just one feature shows up in the legend, could it be that all the legend features are stacked on top of each other. How can I know for sure and how can I fix this.
//Define default colorbrewer scheme
var colorSchemeSelect = "Greens";
var colorScheme = colorbrewer[colorSchemeSelect];
//define default number of quantiles
var quantiles = 5;
//Define quantile scale to sort data values into buckets of color
var color = d3.scale.quantile()
.range(colorScheme[quantiles]);
d3.csv(data, function (data) {
color.domain([
d3.min(data, function (d) {
return d.value;
}),
d3.max(data, function (d
return d.value
})
]);
//legend
var legend = svg.selectAll('rect')
.data(color.domain().reverse())
.enter()
.append('rect')
.attr("x", width - 780)
.attr("y", function(d, i) {
return i * 20;
})
.attr("width", 10)
.attr("height", 10)
.style("fill", color);
The legend code that you're using would work perfectly well if you had an ordinal scale, where the domain is made up of discrete values that correlate to the range of colours on a one-to-one basis. But you're using a quantile scale, and so need a different approach.
For a d3 quantile scale, the domain is the list of all possible input values, and the range is a list of discrete output values. The domain list is sorted in ascending order and then divided into equal-sized groups, which are assigned to each output value from the range. The number of groups is determined by the number of output values.
With that in mind, in order to get one legend entry for each colour, you're going to need to use your colour scale's range, not the domain, as the data for your legend. Then you can use the quantileScale.invertExtent() method to find the minimum and maximum input values that are getting drawn with that colour.
Sample code, making each legend entry a <g> containing both the coloured rectangle and a text label showing the corresponding values.
var legend = svg.selectAll('g.legendEntry')
.data(color.range().reverse())
.enter()
.append('g').attr('class', 'legendEntry');
legend
.append('rect')
.attr("x", width - 780)
.attr("y", function(d, i) {
return i * 20;
})
.attr("width", 10)
.attr("height", 10)
.style("stroke", "black")
.style("stroke-width", 1)
.style("fill", function(d){return d;});
//the data objects are the fill colors
legend
.append('text')
.attr("x", width - 765) //leave 5 pixel space after the <rect>
.attr("y", function(d, i) {
return i * 20;
})
.attr("dy", "0.8em") //place text one line *below* the x,y point
.text(function(d,i) {
var extent = color.invertExtent(d);
//extent will be a two-element array, format it however you want:
var format = d3.format("0.2f");
return format(+extent[0]) + " - " + format(+extent[1]);
});
I wanted to create a doughnut chart with d3.js and I want to put a navigation button just exactly on the middle of it, maybe when clicked shrinks the whole chart into a smaller version (expand probably). But that is not how complex my question is, but if you can find an example that probably exactly what I wanted to have, its better.
The question now is just position a circle, I don't know if there is a better alternative for it, but this is how it goes.
var height=800,
width=800;
var data = [10,50,80];
var color = d3.scale.ordinal()
.range(["red", "blue", "yellow"]);
// the graph
var radius=300;
var canvas = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var group = canvas.append("g")
.attr("transform", "translate(300,300)");
var arc = d3.svg.arc()
.innerRadius(100)
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function(d){
return d;
});
var arcs = group.selectAll(".arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.attr("fill", function(d){
return color(d.data);
});
canvas.append("g")
.attr("class", "collapse")
.append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 100);
I just a did a dirty append of circle, because I don't want to go on, if there is a better way to do it, that is why I asked. How would one make it to the center, and if you are to generous, how to add events on it, probably using on, and then scale it down to about 100px and make it the button itself, so when toggled again, it collapses
EDIT
Guess I'm a bit asking to much, how about:
Center the button without hard coding 300px to it? and on your opinion , would an svg circle can do fine as a button?