I would like to create a dynamic graph with multiple (linear) axes. After the axes have been drawn, I would like (as new data arrives) to change the Data Domain and redraw/update the axes. Can I select the existing axis with D3 and do this or do I have to explicitly save each axis in my code? I hope my question is not confusing.
// init all Y-Axis
$.each(chart.YAxes, function (index) {
var yScale, yAxis;
yScale = d3.scale.linear().range([chartHeight, 0]);
yScale.domain([this.YMin, this.YMax]);
yAxis = d3.svg.axis()
.scale(yScale)
.ticks(10, this.Title)
.orient("left");
d3Chart.append("g")
.attr("class", "yAxis " + "y" + this.ID)
.call(yAxis);
......
// update Y-Axis (after new data arrives...)
var myYAxis = d3.select(".y" + yAxis.ID);
var myScale = myYAxis. **// get Scale ??**
myScale.domain([newYMin, newYMax]);
d3Chart.select(".y" + yAxis.ID)
.transition().duration(300).ease("sin-in-out")
.call(myYAxis);
thx...!
You need to keep references to the axis object so that you can call it again. Selecting the DOM element that contains it and calling that won't work. There are lots of examples on how to update axes, e.g. in this question.
Related
I have data that show both the number of items sold as well as the value of those items. I have a dynamic and interactive line chart that switches between the two data sets. I'd like to format the tick marks on the axis so that dollar signs appear on the axis where appropriate.
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y)
svg.append("g")
.attr("class","Yaxis")
y.domain([0, d3.max(data, function(d) { return d.number }) ]);
svg.selectAll(".Yaxis")
.transition()
.duration(3000)
.call(yAxis);`
The entire pen is available here: https://codepen.io/cyrusobrien/full/NWdPRLp
Replace D3 tickFormat with your custom one:
const d3Format = d3.format(".2s");
const customFormat = value => `$${d3Format(value)}`;
const yAxis = d3.axisLeft().scale(y)
.tickFormat(customFormat)
How can I replace below lines with the new version of D3 API?
I have already replaced scale.linear() with scaleLinear()
var xRange = d3.scaleLinear()
.domain([OIResults.min,OIResults.max]).range([40, 360]);
var yRange = d3.scaleLinear()
.domain(y_domain).range([360, 40]);
Below Lines need to be replaced according to the new API:
var xAxis = d3.svg.axis().scale(xRange).tickFormat(function(d) { return d.x;});
var yAxis = d3.svg.axis().scale(yRange).orient("left");
The D3 v4 API is here. According to the changelog:
D3 4.0 provides default styles and shorter syntax. In place of d3.svg.axis and axis.orient, D3 4.0 now provides four constructors for each orientation: d3.axisTop, d3.axisRight, d3.axisBottom, d3.axisLeft.
Therefore, those lines should be:
var xAxis = d3.axisBottom(xRange).tickFormat(function(d){ return d.x;});
var yAxis = d3.axisLeft(yRange);
PS: I'm assuming that you want the ticks to be below the axis, which is normally the case, since you didn't show the orient in your original lines.
PPS: At the time of the writing the linked documentation applies to D3 v4. Caveat, lector: that can change at any time (see comments below).
Is it possible to add labels to scatter plot points in c3.js like in this google charts example?
https://google-developers.appspot.com/chart/interactive/docs/gallery/bubblechart#javascript
c3 doesn't support this currently - https://github.com/masayuki0812/c3/issues/481. But you can easily add the functionality - just loop through the chart series and points and add the labels as necessary.
var labels = [
['AA', 'BB', 'CC', 'DD', 'EE', 'FF', 'GG', 'HH'],
['ZA', 'ZB', 'ZC', 'ZD', 'ZE', 'ZF', 'ZG', 'ZH']
];
// series
var series = chart.internal.main
.selectAll('.' + c3.chart.internal.fn.CLASS.circles)[0];
// text layers
var texts = chart.internal.main
.selectAll('.' + c3.chart.internal.fn.CLASS.chartTexts)
.selectAll('.' + c3.chart.internal.fn.CLASS.chartText)[0]
series.forEach(function (series, i) {
var points = d3.select(series).selectAll('.' + c3.chart.internal.fn.CLASS.circle)[0]
points.forEach(function (point, j) {
d3.select(texts[i])
.append('text')
.attr('text-anchor', 'middle')
.attr('dy', '0.3em')
.attr('x', d3.select(point).attr('cx'))
.attr('y', d3.select(point).attr('cy'))
.text(labels[i][j])
})
});
Fiddle - http://jsfiddle.net/6phuuans/
Currently C3.js doesnt provide us with the option to add labels to a scatter plot chart. But the following method can be used to add responsive data labels:
After the chart is rendered (in the "onrendered" property of the chart), identify the data points ( tags) and add tags with the x and y coodinates picked from the relevant circle, as the tags sibling.
Code Snippet:
onrendered: function(){
// get the parent of the the <circles> to add <text as siblings>
var g = d3.selectAll('.c3-circles');
//Get all circle tags
var circles = d3.selectAll('circle')[0];
//go to each circle and add a text label for it
for(var i = 0; i < circles.length; i++){
//fetch x-coordinate
var x = $(circles[i])[0].cx;
//fetch y-coordinate
var y = $(circles[i])[0].cy;
//create and append the text tag
g.append('text')
.attr('y', y.baseVal.value - 15) // (-15) places the tag above the circle, adjust it according to your need
.attr('x', x.baseVal.value)
.attr('text-anchor', 'middle')
.attr('class', 'c3-text c3-text-'+i)
.text(data[i].<data point key>) // the text that needs to be added can be hard coded or fetched for the original data.
//Since I am using a JSON to plot the data, I am referencing it and using the key of the value to be shown.
}
}
This will add the label, but on resize , multiple data labels will be plotted. To handle that, on the charts resize, we must remove the previous data labels (in the "onresize" property of the chart).
Code Snippet:
onresize: function () {
$('.c3-shapes.c3-circles text').remove();
}
Current code
/ Call method used
var svg = d3.select("#hoge").append("svg")
.attr("width", 600).attr("height", 400)
var xScale = d3.scale.linear()
.domain([0, 100])
.range([0, 400]);
svg.append("g")
.attr("class", "axis")
.call(d3.svg.axis()
.scale(xScale)
);
I want to know
/ Call method unused
var svg = d3.select("#hoge").append("svg")
.attr("width", 600).attr("height", 400)
var xScale = d3.scale.linear()
.domain([0, 100])
.range([0, 400]);
svg.append("g")
.attr("class", "axis");
d3.svg.axis(svg).scale(xScale);
If you do not use the call method
The x-axis can not be displayed?
d3.svg.axis() creates and returns a function that appends the SVG elements to display the axis. It does not actually append anything. If you don't call the function that is returned, those elements will not be added. This is a common pattern in d3.js so it is important to understand.
Let's say you create these two variables for the axis function and the axis group element:
var axisFunction = d3.svg.axis().scale(xScale);
var axisGroup = svg.append("g")
.attr("class", "axis");
The following two ways of calling the axis function are equivalent:
axisFunction(axisGroup);
axisGroup.call(axisFunction);
The call syntax exists simply to enable method chaining like you have in your current code example.
The call operator is identical to invoking a function by hand; but it makes it easier to use method chaining.
https://github.com/mbostock/d3/wiki/Selections#call
Mike Bostock's article Towards Reusable Charts does a good job of explaining this pattern. The pattern described in the article to create the reusable chart is used for a variety of items in d3.js such as d3.svg.axis.
I am using the trick of sizing the ticks on my axes in order to render a grid effect on my chart. The problem I have is that I am not able to work out how to resize the ticks on my y axis when the width of the chart changes due to a window resize.
The code that I have to initialize my chart is as follows:
this._xRange = d3.time.scale().range([0, chartWidth]);
this._yRange = d3.scale.linear().range([chartHeight, 0]);
this._xAxis = d3.svg.axis().scale(this._xRange)
.orient("bottom")
.tickSize(-chartHeight)
.tickSubdivide(true);
this._yAxis = d3.svg.axis().scale(this._yRange)
.orient("left")
.tickSize(-chartWidth)
.tickSubdivide(true);
When the chart is resized, I am recalculating the width and height of the chart and attempting to redraw the axes. For the y axis I am using the following code:
// Redraw the y axis
this._yAxis = d3.svg.axis().scale(this._yRange)
.orient("left")
.tickSize(-chartWidth)
.tickSubdivide(true);
this._chart.call(this._yAxis.orient("left"));
This is failing with the following exception in the browser console:
DOMException: Failed to execute 'insertBefore' on 'Node': The node before
which the new node is to be inserted is not a child of this node. "Error:
Failed to execute 'insertBefore' on 'Node': The node before which the new
node is to be inserted is not a child of this node.
I believe that I have hit upon a solution. It appears that I need to remove the existing y axis, create a new generator and then add the new generator to the chart. I am using the following code to do this:
d3.select(".y.ResourceStatsAxis").remove();
this._yAxis = d3.svg.axis().scale(this._yRange)
.orient("left")
.tickSize(-chartWidth)
.tickSubdivide(true);
this._chart.append("g")
.attr("class", "y ResourceStatsAxis")
.call(this._yAxis);