Get values from D3 line interpolator functions - javascript

I am building a D3 graph which contains a shaded region in the middle of it. The shaded regions top part matches the line graph.
The data looks something like this:
line = [
{x: 0, y:1},
{x: 1, y:2},
{x: 2, y:4},
{x: 3, y:6},
{x: 4, y:3},
{x: 5, y:1}]
And the graph uses a 'cardinal' interpolation function:
g = d3.svg.line()
.interpolate('cardinal')
In order to shade the area under the graph, I just create a d3.svg.area() and provide it with the equivalent data for the shaded area, e.g (to shade between x=2 and x=4):
area = [
{x: 2, y:4},
{x: 3, y:6},
{x: 4, y:3}]
a = d3.svg.area()
.interpolate('cardinal')
, but my problem is that I want to shade the area under the graph between 1.5 and 3.5 for which I dont know the values after the 'cardinal' interpolation has taken place.
So, I need to work out how to either a) apply a cardinal interpolation over the data and then pick out the values for 1.5 and 3.5 or b) extract the line data from d3 and use that to build up the data for the area graph.
for a) I have looked at d3.interpolate, but it looks like its different from d3.line.interpolate() and does not allow you to pass the interpolation method.
for b) I cant work out how to extract the data from the line once it has been interpolated... its not documented anywhere in D3.

Consider approaching this in a different way: you can use svg clipPath elements to arbitrarily clip a path, allowing you to forego any calculation of interpolated values.
Draw two versions of your area, one with the background styling, the other with the foreground, then create a rectangle clipPath element to bound the 'foreground' area:
js:
svg.append("clipPath")
.attr("id", "clipRect")
.append("rect")
.attr("x", x(1.5))
.attr("width", x(2))
.attr("height", height);
css:
.foreArea {clip-path: url(#clipRect);}
See, for example: http://bl.ocks.org/jsl6906/89ef40de1d8808d04f42

Related

d3.geoPath().projection is rendering black rectangle instead of map

I want to create a US map based on county data. I'm using this JSON topology data to create the graph: https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/counties.json
In the first step, I created the map like this, and it works fine:
var path = d3.geoPath();
svgContainer.selectAll("path")
.data(topojson.feature(countyData, countyData.objects.counties).features)
.enter()
.append("path")
.attr("d", path)
Picture: US map renders OK but too large
However, it is too large for my purpose so I'm trying to scale it down. I tried projections which I saw in several other projects (for example here: https://www.d3-graph-gallery.com/graph/choropleth_basic.html). Unfortunately it just renders a black rectangle. I also tried geoAlbersUsa() and some other projections, but it did not help. How can I get the map data to scale?
var projection = d3.geoAlbersUsa() // geoMercator() also does not work
.scale(200)
.translate([width / 2, height / 2]);
var path = d3.geoPath().projection(projection);
svgContainer.selectAll("path")
.data(topojson.feature(countyData, countyData.objects.counties).features)
.enter()
.append("path")
.attr("d", path)
Picture: projection renders black rectangle
What am I doing wrong here?
Everything looks good in the second block of code (using d3.geoAlbersUSA()) except I think you are zoomed in too close with .scale(200) and only seeing the middle of a county. As explained in this post, if you zoom out with smaller scale value you may start to see more of your map.(What does it mean to scale a projection in d3?)
You may be better off using .fitSize() instead of .scale since you seem to be trying to fit the whole topojson data set inside an area rather than zooming into part of it. Updated your example below using a variable margin.
var margin = 20; //amount of whitespace you want around the map
var projection = d3.geoAlbersUsa()
.translate([width / 2, height / 2]);
var path = d3.geoPath().projection(projection);
var countiesFeatureCollection = topojson.feature(countyData, countyData.objects.counties);
//make the map projection fit into size of screen minus margin on all sides
projection.fitSize([width - margin*2, height - margin*2], countiesFeatureCollection);
svgContainer.selectAll("path")
.data(countiesFeatureCollection.features)
.enter()
.append("path")
.attr("d", path)

What's the most effective way to implement a radar plot with 50 points at arbitrary locations using chart.js

Consider a sequence of data along the following lines:
data = [{angle:1.2,value:1.2},...,{angle:355.2: value:5.6}];
I'd like to display this data on a radially scaled plot (i.e. circular bands indicating how high the value of each point is) to show angle vs value. Angles will change by a small but uncontrollable quantity for each data set but there will always be ~50 of them spaced fairly evenly around the chart.
It looks like chart.js has two options which don't quite fit the bill:
A Radar plot which appears to require a label per point but without an obvious way to control where those labels are applied.
An x-y scatter which I could calculate x/y co-ordinates for but which doesn't have the radial scale to help visualise the value of each point.
Is there a way to combine the two perhaps or some option I've missed to control them to achieve the result I'm looking for here?
Edit - for example, this shows the data but lacks a radial scale:
https://jsfiddle.net/7d7ghaxx/4/
**Edit2 - This is the sort of thing I Would expect to see as a result:
Demo & Code :
https://stackblitz.com/edit/js-jp4xm4?file=index.js
Explanation:
Used a scatter chart to plot points
Added (wrote) a chartjs plugin that converts points from polar to cartesian on beforeUpdate so you don't have to worry about converting before every update
Made x & y grid (not axes through origin) hide by adding gridLines: { color: 'transparent' } and ticks: { display: false }
Made min and max (options in ticks) of both axes equal so that the orgin is at the center
Added a radialLinear scale for the polar grid
(Update 1)
Added a tooltip label callback to show tooltip as (r,θ) instead of default (x,y)
(Update 2)
Added (wrote) a beforeDraw plugin to fill the ctx with light blue color as the OP wanted
PS: (Pointing out just to be a little competitive) I have used chartjs (unlike other answers) because the OP wants a chartjs solution as it's clearly written in the question: "using chart.js". There might be solutions better than chartjs but that's irrelevant.
You can use D3 js Charts is usefull for radar chart check the example link bellow :
//////////////////////////////////////////////////////////////
//////////////////////// Set-Up //////////////////////////////
//////////////////////////////////////////////////////////////
var margin = {top: 100, right: 100, bottom: 100, left: 100},
width = Math.min(700, window.innerWidth - 10) - margin.left - margin.right,
height = Math.min(width, window.innerHeight - margin.top - margin.bottom - 20);
//////////////////////////////////////////////////////////////
////////////////////////// Data //////////////////////////////
//////////////////////////////////////////////////////////////
var data = [
[//Yourchart values
{axis:"",value:0.052},
{axis:"",value:0.052},
{axis:"",value:0.012},
{axis:"",value:0.012},
{axis:"",value:0.022},
{axis:"",value:0.052},
{axis:"",value:0.052},
{axis:"",value:0.021}
]
];
//////////////////////////////////////////////////////////////
//////////////////// Draw the Chart //////////////////////////
//////////////////////////////////////////////////////////////
var color = d3.scale.ordinal()
.range(["#6cbb69","#CC333F","#00A0B0"]);
var radarChartOptions = {
w: 500,
h: 300,
maxValue: 0.15,
levels: 5,
roundStrokes: true,
color: color
};
//Call function to draw the Radar chart
RadarChart(".radarChart", data, radarChartOptions);
https://codepen.io/Nighamza/pen/bKmjGE

How to connect dots in D3 with a line?

I've pulled out some data and after scaling, I draw a bunch of circles. This works just great.
var gSet = graph1.selectAll("g").data(data).enter().append("g");
gSet.append("circle")
.attr({ cx: posX, cy: posY, r: dotSize })
.attr("class", "dataPoint");
Now, I'd like to connect the dots. Most examples I've seen are about bars, not lines so I've googled some more line charts and decided to use path element, like so.
var gSet = graph1.selectAll("g").data(data).enter().append("g");
gSet.append("circle")
.attr({ cx: posX, cy: posY, r: dotSize })
.attr("class", "dataPoint");
gSet.append("path")
.attr("d", d3.svg.line().x(posX).y(posY))
.attr({ "stroke": "yellow", "stroke-width": "1" });
Nothing new appears on the screen and due to ignorance, I'm not sure where to poke to see what went wrong.
Should I be using path (or is line, polyline etc. a better choice)?
Should I be working with the d attribute or is there a more suited one?
Should I be applying the d3.svg.line() function or is there a smoother way?
I happen to have done something similar to what I think you intend, so I'll just paste it as-is in this jsfiddle, and it will show you one possible way to connect your lines. This one is more of a parent/child graph, but that is just based on the data. you could remove the parent property and just link each node to the previous node instead.
to answer your questions:
you could use path, but it's a bit overkill if you just want straight lines. if your case is simple, then I recommend line.
if using a path object, the d property will describe the shape of your path. with D3js people often use d as one of two optional parameters into anonymous methods (with d being the data, and i being the incrementing counter).
you could use the d3.svg.line() function to describe the shape and position of your line, but if your data is as simple as it sounds then that might be overkill - consider just appending line objects as shown in my code below. my recommendation would be to go with a path if you need "fancy lines", but that's just my comfort area and there are likely other ways.
for my approach, the code ends up looking like this
var items = svg.selectAll("g").data(srcData).enter().append("g");
items.each(function(d, i){
d3.select(this).attr("transform","translate("+d.posx+","+d.posy+")");
if(d.parentid > 0)
d3.select(this).append("line")
.attr("x",0)
.attr("y",0)
.attr("x1",function(d){ return -1*(d.posx - parent(d.parentid).posx); })
.attr("y1",function(d){ return -1*(d.posy - parent(d.parentid).posy); })
.style("stroke",d.color)
.style("stroke-width",2);
d3.select(this).append("circle")
.attr("r",5)
.style("fill",d.color);
});

D3 pie chart transition

I'm studying transitions in D3js, trying to get them working with a simple pie chart that is derived from a Mike Bostock example.
I want to transition the data in the pie from data to data2, but the chart does not update.
This is the transition statement:
path.selectAll("path").data(pie(data2)).transition().duration(2000);
What am I missing?
EDIT
I've got it working with the below. Now I'd like to understand why this is working. I understand the .attr("d",arc) part, but why do I need to selectAll("path")?
path.selectAll("path").data(pie(data2)).transition().duration(2000).attr("d",arc);
END EDIT
The complete code (JSFiddle here):
var width = 200,
height = 200,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var data = [2, 19, 18, 99, 100];
var data2 = [100, 1200, 20, 88, 12];
var pie, arc, svg, path, data;
var chartCanvas = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height);
var path = chartCanvas
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
function setupPieChart() {
pie = d3.layout.pie()
.value(function (d) {
return d;
})
.sort(null);
arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 20)
path
.selectAll("path")
.data(pie(data))
.enter()
.append("path")
.attr("fill", function (d, i) {
return color(i);
})
.attr("d", arc)
.each(function (d) {
this._current = d;
}); // store the initial angles
console.log(path);
}
setupPieChart();
console.log(path);
path.selectAll("path").data(pie(data2)).transition().duration(2000);
.transition starts a transition for the attributes change(s) declared after it. You don't set / do anything after .transition, so there is nothing to interpolate over. In the original example from Mike, you'll see he sets the d attribute after starting the transition, specifying a custom interpolator (arcTween)
I immediately see you are missing some important part of the update process. You copied the original code, but you forgot the update part :-). I can tell, because I see in your code you store the initial angles.
Look again at the code here and try to understand the function arcTween. More information can be found here. You need an arc tween function to calculate the new angles based on the initial angles (which is why you stored the initial angles in the first place :-).
I won't do the fiddle at the moment, cause in my experience, you learn more if you try to understand the arc tween function (as I did here . This is a link to a personal project of mine, but feel free to copy code as you see fit).
You need to .selectAll("path") as those are the actual elements that will update. When doing d3, try to think of the chart elements as following: Elements that are not visible yet (which is enter collection), elements that are visible now (which can be seen as the update collection) and elements that can be removed (the exit collection).
You need to see those elements based on the data you want to visualize. D3 is data driven documents, so everything is in relation to the data you want to show on the screen: if you have data but no elements yet, you do an "enter" of elements. So you do a selection of elements that are not in the DOM yet, but will soon be, because you will bind them to the data you have.
If you already have elements on the screen, and the number of elements matches the data you need to show (for example: var data = [20 ,30 , 40], you got 3 pieces of data here for 3 div's on the screen, you got a matching amount), then d3 will update your selection (hence the update collection or update selection) so the elements properties match the data.
If you have more elements on the screen then there is data to show, then you can do an exit of elements (again, hence the exit selection or collection). I hope that made it a bit more clear and that it made sense as well :-)

How to draw growth rate line in dc.js barChart?

I have Statewise, sectorwise(Agriculture, Manufacturing, Mining etc.) & yearwise GDP data of India. I have created a dashboard that can be found at India GDP. Now in the barchart I want to draw a line indicating growth rate at each year. I think it can be done by composite chart but I don't know how to calculate dynamic growth rates each time a filter is applied. Can anybody provide a guidance.
You are right that there should be a standard way to do this. The problem is that it doesn't fit into dc.js's way of dealing with data through crossfilter, and there are any number of graphical objects you might want to overlay on a chart.
So, for now we have renderlets.
The idea of using a renderlet is to write a function that drops into lower-level d3 code once the chart has rendered. You might search around for the exact d3 code you want, but here is something which adds a single red line on top of the bars:
.on('renderlet', function(chart) {
var extra_data = [{x: chart.x().range()[0], y: chart.y()(10)},
{x: chart.x().range()[1], y: chart.y()(70)}];
var line = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate('linear');
var path = chart.select('g.chart-body').selectAll('path.extra').data([extra_data]);
path.enter().append('path').attr('class', 'extra').attr('stroke', 'red');
path.attr('d', line);
});
This draws the line from the left side to the right side, from Y value 10 to Y value 70, using the x and y scales to obtain the SVG coordinates.
I created a bar with extra line example.
Hopefully this will get you started!

Categories