Transition between paths with different number of data points - animation

Using d3 I am attempting to create an animation between two lines that have a different number of data points.
I have referenced Mike Bostock's post: https://bl.ocks.org/mbostock/3916621 which contains a pathTween method for two paths described in "M0..." format.
The data I am trying to plot is raw data points.
For instance:
var line = d3.svg.line().interpolate("monotone")
.x(function(d){ return d.x; })
.y(function(d){ return d.y; });
low_res = [
{x: 0 , y: 4 },
{x: 5 , y: 14 },
{x: 10 , y: 11 },
{x: 85 , y: 14 },
{x: 90 , y: 11 },
{x: 95 , y: 7 },
{x: 100 , y: 4 },
];
var high_res = [[
{x: 0 , y: 4 },
{x: 1 , y: 12 },
{x: 2 , y: 11 },
{x: 3 , y: 11 },
{x: 4 , y: 14 },
{x: 5 , y: 14 },
{x: 6 , y: 12 },
{x: 7 , y: 4 },
{x: 8 , y: 3 },
{x: 9 , y: 1 },
{x: 10 , y: 11 },
{x: 11 , y: 11 },
{x: 12 , y: 1 },
{x: 13 , y: 13 },
{x: 14 , y: 1 },
{x: 15 , y: 5 },
{x: 16 , y: 13 },
{x: 17 , y: 10 },
]]
var lines = svg.selectAll(".line").data(high_res)
.enter()
.append("path")
.attr("class","line")
.attr("d", line)
.style("stroke", "blue");
This works find for displaying the high_res plot. Mike Bostock's example is as follows:
var d0 = "M0,0c100,0 0,100 100,100c100,0 0,-100 100,-100",
d1 = "M0,0c100,0 0,100 100,100c100,0 0,-100 100,-100c100,0 0,100 100,100";
svg.append("path")
.attr("transform", "translate(180,150)scale(2,2)")
.attr("d", d0)
.call(transition, d0, d1);
function transition(path, d0, d1) {
path.transition()
.duration(2000)
.attrTween("d", pathTween(d1, 4))
.each("end", function() { d3.select(this).call(transition, d1, d0); });
}
function pathTween(d1, precision) {
return function() {
var path0 = this,
path1 = path0.cloneNode(),
n0 = path0.getTotalLength(),
n1 = (path1.setAttribute("d", d1), path1).getTotalLength();
// Uniform sampling of distance based on specified precision.
var distances = [0], i = 0, dt = precision / Math.max(n0, n1);
while ((i += dt) < 1) distances.push(i);
distances.push(1);
// Compute point-interpolators at each distance.
var points = distances.map(function(t) {
var p0 = path0.getPointAtLength(t * n0),
p1 = path1.getPointAtLength(t * n1);
return d3.interpolate([p0.x, p0.y], [p1.x, p1.y]);
});
return function(t) {
return t < 1 ? "M" + points.map(function(p) { return p(t); }).join("L") : d1;
};
};
}
I am struggling to figure out how to create a native path object, such as d0, d1 in the second example from my set of raw data which can be passed into the transition function ie:
.call(transition, d0, d1);
Many thanks in advance for any advice.

Your question seems to boil down to:
How can I generate a path for my datapoints?
This is actually what the line function is doing. It's as simple as:
var low_res = [
{x: 0 , y: 4 },
{x: 5 , y: 14 },
{x: 10 , y: 11 },
{x: 85 , y: 14 },
{x: 90 , y: 11 },
{x: 95 , y: 7 },
{x: 100 , y: 4 },
],
high_res = [
{x: 0 , y: 4 },
{x: 1 , y: 12 },
{x: 2 , y: 11 },
{x: 3 , y: 11 },
{x: 4 , y: 14 },
{x: 5 , y: 14 },
{x: 6 , y: 12 },
{x: 7 , y: 4 },
{x: 8 , y: 3 },
{x: 9 , y: 1 },
{x: 10 , y: 11 },
{x: 11 , y: 11 },
{x: 12 , y: 1 },
{x: 13 , y: 13 },
{x: 14 , y: 1 },
{x: 15 , y: 5 },
{x: 16 , y: 13 },
{x: 17 , y: 10 },
];
var line = d3.svg.line().interpolate("monotone")
.x(function(d){ return d.x; })
.y(function(d){ return d.y; });
var d0 = line(low_res),
d1 = line(high_res);
console.log(d0);
console.log(d1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Related

Lightningchart js - How i can rotate tick text?

Is it possible in lightningchart js to rotate ticks text on axis? Ticks text does not fit or overlaps adjacent text.
As of LightningChart JS v3.1.0 it's possible to set tick label rotation.
You can use axis.setTickStyle to set style settings for current tick strategy. Alternatively this can be done through axis.setTickStrategy's styler parameter.
The VisibleTicks class has setLabelRotation method which can be used to define tick rotation in degrees. Positive angle rotates clockwise and negative angle counter-clockwise. So to rotate labels on X axis to be rotated vertically.
chart.getDefaultAxisX()
.setTickStyle((s) => s
.setMajorTickStyle((m) => m.setLabelRotation(-90))
.setMinorTickStyle(m => m.setLabelRotation(-90)),
)
See full example below.
const {
lightningChart,
PointShape,
} = lcjs
const pointSize = 10
const chart = lightningChart().ChartXY()
chart.addPointLineSeries()
.setPointSize(pointSize)
.add([{
x: 0,
y: 0
},
{
x: 50,
y: 10
},
{
x: 80,
y: 20
},
{
x: 100,
y: 30
},
{
x: 150,
y: 40
},
{
x: 180,
y: 50
},
{
x: 230,
y: 60
},
{
x: 290,
y: 70
}
])
chart.addPointLineSeries({
pointShape: PointShape.Circle
})
.setPointSize(pointSize)
.add([{
x: 0,
y: 0
},
{
x: 100,
y: 10
},
{
x: 230,
y: 20
},
{
x: 390,
y: 30
},
{
x: 470,
y: 40
},
{
x: 540,
y: 50
},
{
x: 600,
y: 60
},
{
x: 800,
y: 70
}
])
chart.getDefaultAxisX()
.setInterval(0, 1000, false, true)
.setTickStyle((s) =>
s.setMajorTickStyle((m) => m.setLabelRotation(-90)).setMinorTickStyle(m => m.setLabelRotation(-90)),
)
chart.getDefaultAxisY()
.setInterval(0, 100, false, true)
<script src="https://unpkg.com/#arction/lcjs#3.1.0/dist/lcjs.iife.js"></script>
As of current version (v3.0.1) there is no API for configuring text.
I see this as a very potential feature improvement. It would help us in introducing it to the library, if you would describe your usage case for rotated tick text.
Screenshots are gold.

How to pass degree Celsius as symbol to YAxis title

How can I pass degree Celcius symbol for yAxis
I have tried all kinds
1. °
2. U+00B0 - unicode
3. ° - html code
but all of them get printed as given and do not print the degree symbol.
You can use suffix property to add suffix in axis labels to do this.
var chart = new CanvasJS.Chart("chartContainer", {
axisY: {
suffix: " °C"
},
data: [{
type: "spline",
yValueFormatString: "#0.## °C",
dataPoints: [
{ x: new Date(2017,6,24), y: 31 },
{ x: new Date(2017,6,25), y: 31 },
{ x: new Date(2017,6,26), y: 29 },
{ x: new Date(2017,6,27), y: 29 },
{ x: new Date(2017,6,28), y: 31 },
{ x: new Date(2017,6,29), y: 30 },
{ x: new Date(2017,6,30), y: 29 }
]
}]
});
chart.render();
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<div id="chartContainer" style="height: 300px; width: 100%;"></div>

nvd3 line chart xaxis with month names not working

I'm trying a line chart for several years with month names in the xaxis. xaxis seems to be an integer so I used the function from this answer: https://stackoverflow.com/a/30325918
The problem is that the chart is cut off by number 9 (which stands for "Okt") and I don't know why. So the 10 and 11 are missing. Also the tooltips are not shown, maybe that's a subsequent fault.
Style for the svg:
#chart svg {
height: 600px;
width: 1456px;
}
Code for the Chart:
nv.addGraph(function() {
var chart = nv.models.lineChart()
.margin({left: 50}) //Adjust chart margins to give the x-axis some breathing room.
.useInteractiveGuideline(true) //We want nice looking tooltips and a guideline!
.showLegend(true) //Show the legend, allowing users to turn on/off line series.
.showYAxis(true) //Show the y-axis
.showXAxis(true) //Show the x-axis
;
var Monate = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']
chart.xAxis //Chart x-axis settings
.axisLabel('Monat')
.tickValues([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]).tickFormat(function(d){
return Monate[d]
});
chart.forceY([0, 55000]);
d3.select('#chart svg').datum([{ values: [{ x:'0', y: '0'},{ x:'1', y: '0'},{ x:'2', y: '7147.75'},{ x:'3', y: '5567.75'},{ x:'4', y: '8307.75'},{ x:'5', y: '7680.75'},{ x:'6', y: '10416.75'},{ x:'7', y: '15119.25'},{ x:'8', y: '6654.75'},{ x:'9', y: '7670.75'},{ x:'10', y: '8535.75'},{ x:'11', y: '25978.5'}], key: '2012'},
{ values: [{ x:'0', y: '4397.5'},{ x:'1', y: '14542.5'},{ x:'2', y: '18652.5'},{ x:'3', y: '18027.5'},{ x:'4', y: '10123.5'},{ x:'5', y: '11686.25'},{ x:'6', y: '10451.5'},{ x:'7', y: '17918'},{ x:'8', y: '11013.25'},{ x:'9', y: '11598.25'},{ x:'10', y: '12179'},{ x:'11', y: '54450.75'}], key: '2013'},
{ values: [{ x:'0', y: '13257.75'},{ x:'1', y: '10050.25'},{ x:'2', y: '17530'},{ x:'3', y: '16684'},{ x:'4', y: '10437.25'},{ x:'5', y: '31956.5'},{ x:'6', y: '24343.75'},{ x:'7', y: '24026.5'},{ x:'8', y: '24754.5'},{ x:'9', y: '17423'},{ x:'10', y: '27284.75'},{ x:'11', y: '50121.25'}], key: '2014'},
...and so on...
{ values: [{ x:'0', y: '954'},{ x:'1', y: '0'},{ x:'2', y: '0'},{ x:'3', y: '0'},{ x:'4', y: '0'},{ x:'5', y: '0'},{ x:'6', y: '0'},{ x:'7', y: '0'},{ x:'8', y: '0'},{ x:'9', y: '0'},{ x:'10', y: '0'},{ x:'11', y: '0'}], key: '2019'}]).transition().duration(500).call(chart);
//Update the chart when window resizes.
nv.utils.windowResize(function() { chart.update() });
return chart;
});
All your x and y are strings.
Remove all the ' characters.
I have set the size style on the div#chart because nvd3.css has a style for the svg with width and height set to 100%.

How to pass a value to line generation function in D3

Following code is used to generate lines in D3:
var lineFn = d3.line()
.x((d) => this.base.xAxis.scale(d.x))
.y((d) => this.base.yAxes[0].scale(d.y));
// series is a collection of lines I want to plot
series = [
{
data: [{x: 10, y: 20}, {x: 20, y: 30}],
yAxis: 0, // this indicates which y-axis to use
color: red
},
...
];
_.forEach(series, (line) => {
this.base.chart.append("path")
.datum(line.data)
.attr("class", "line")
.attr("d", lineFn)
.style("stroke", line.color)
});
My chart uses dual y-axes using d3.axisLeft() and d3.axisRight().
Right now, I am hardcoding the value of which y-axis to use in the lineFn.
.y((d) => this.base.yAxes[0].scale(d.y)); // 0-left axis, 1-right axis
What I would like to do is pass that value when I call the line function, something like:
.attr("d", lineFn(line.yAxis))
Is there any way to achieve this?
Thanks.
The easiest way to achieve what you want is simply creating two different line generators.
However, since you asked (not verbatim) "is it possible to define the scale dynamically when calling the line generator?", the answer is: yes, it is possible. Let's see how to do it.
In this example, I'm using an object to store the different scales:
var scales = {
yScaleLeft: d3.scaleLinear()
.domain([0, 100])
.range([170, 30]),
yScaleRight: d3.scaleLinear()
.domain([0, 200])
.range([170, 30])
};
And, in the dataset, defining which scale and color should be used for each line, just as you did:
var data = [{
data: [{
x: 1,
y: 20
}, {
...
}, {
x: 8,
y: 50
}],
yAxis: "yScaleLeft",
color: "red"
}, {
data: [{
x: 3,
y: 120
}, {
...
}, {
x: 9,
y: 180
}],
yAxis: "yScaleRight",
color: "blue"
}];
Then, when calling the line generator, we set a variable (in this case, thisScale) to specify the scale:
var thisScale;
paths.attr("stroke", d => d.color)
.attr("d", d => {
thisScale = scales[d.yAxis]
return line(d.data);
})
.attr("fill", "none");
Here is the demo, the red line uses a scale going from 0 to 100, the blue line uses a scale going from 0 to 200:
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 200);
var thisScale;
var line = d3.line()
.x(d => xScale(d.x))
.y(d => thisScale(d.y))
.curve(d3.curveMonotoneX);
var data = [{
data: [{
x: 1,
y: 20
}, {
x: 2,
y: 30
}, {
x: 3,
y: 10
}, {
x: 4,
y: 60
}, {
x: 5,
y: 70
}, {
x: 6,
y: 80
}, {
x: 7,
y: 40
}, {
x: 8,
y: 50
}],
yAxis: "yScaleLeft",
color: "red"
}, {
data: [{
x: 3,
y: 120
}, {
x: 4,
y: 130
}, {
x: 5,
y: 10
}, {
x: 6,
y: 120
}, {
x: 7,
y: 40
}, {
x: 8,
y: 130
}, {
x: 9,
y: 180
}],
yAxis: "yScaleRight",
color: "blue"
}];
var scales = {
yScaleLeft: d3.scaleLinear()
.domain([0, 100])
.range([170, 30]),
yScaleRight: d3.scaleLinear()
.domain([0, 200])
.range([170, 30])
};
var xScale = d3.scalePoint()
.domain(d3.range(11))
.range([30, 470])
var paths = svg.selectAll("foo")
.data(data)
.enter()
.append("path");
paths.attr("stroke", d => d.color)
.attr("d", d => {
thisScale = scales[d.yAxis]
return line(d.data);
})
.attr("fill", "none");
var xAxis = d3.axisBottom(xScale);
var yAxisLeft = d3.axisLeft(scales.yScaleLeft);
var yAxisRight = d3.axisRight(scales.yScaleRight);
var gX = svg.append("g").attr("transform", "translate(0,170)").call(xAxis);
var gY = svg.append("g").attr("transform", "translate(30,0)").call(yAxisLeft);
var gY2 = svg.append("g").attr("transform", "translate(470,0)").call(yAxisRight);
<script src="https://d3js.org/d3.v4.min.js"></script>
And here the same solution, but using an array (instead of an object) to store the scales, as you asked in your question:
yAxis: 0//indicates the left axis
yAxis: 1//indicates the right axis
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 200);
var thisScale;
var line = d3.line()
.x(d => xScale(d.x))
.y(d => thisScale(d.y))
.curve(d3.curveMonotoneX);
var data = [{
data: [{
x: 1,
y: 20
}, {
x: 2,
y: 30
}, {
x: 3,
y: 10
}, {
x: 4,
y: 60
}, {
x: 5,
y: 70
}, {
x: 6,
y: 80
}, {
x: 7,
y: 40
}, {
x: 8,
y: 50
}],
yAxis: 0,
color: "red"
}, {
data: [{
x: 3,
y: 120
}, {
x: 4,
y: 130
}, {
x: 5,
y: 10
}, {
x: 6,
y: 120
}, {
x: 7,
y: 40
}, {
x: 8,
y: 130
}, {
x: 9,
y: 180
}],
yAxis: 1,
color: "blue"
}];
var scales = [d3.scaleLinear()
.domain([0, 100])
.range([170, 30]), d3.scaleLinear()
.domain([0, 200])
.range([170, 30])
];
var xScale = d3.scalePoint()
.domain(d3.range(11))
.range([30, 470])
var paths = svg.selectAll("foo")
.data(data)
.enter()
.append("path");
paths.attr("stroke", d => d.color)
.attr("d", d => {
thisScale = scales[d.yAxis]
return line(d.data);
})
.attr("fill", "none");
var xAxis = d3.axisBottom(xScale);
var yAxisLeft = d3.axisLeft(scales[0]);
var yAxisRight = d3.axisRight(scales[1]);
var gX = svg.append("g").attr("transform", "translate(0,170)").call(xAxis);
var gY = svg.append("g").attr("transform", "translate(30,0)").call(yAxisLeft);
var gY2 = svg.append("g").attr("transform", "translate(470,0)").call(yAxisRight);
<script src="https://d3js.org/d3.v4.min.js"></script>

change type of labels of x-axis in column chart

I am using 2 chart of canvasjs plugin piechart and columnchart.
In piechart labels of the elements are so fine it uses all the spaces into content free but column chart if one label is length is big it skipped. But I dont want it to skip.
so this is my question, is there any way using labels(X-Axis only) in column chart(barchart) like just like in piechart ?
Thank you.
Note: I parse it, change it etc. everything comes up another problem if I interrupt the plugin itself so I need though solution.
Charts automatically skips label when they are too close to avoid overlapping. You can override this behavior by setting interval to 1.
Refer Axis X Interval for more detail.
Here is an example:
var chart = new CanvasJS.Chart("chartContainer",
{
axisX:{
title: "Axis X with interval 1",
interval: 1
},
data: [
{
type: "column",
dataPoints: [
{ x: 1, y: 71 },
{ x: 2, y: 55 },
{ x: 3, y: 50 },
{ x: 4, y: 65 },
{ x: 5, y: 95 },
{ x: 6, y: 68 },
{ x: 7, y: 28 },
{ x: 8, y: 34 },
{ x: 9, y: 14 },
{ x: 10, y: 14 },
{ x: 11, y: 71 },
{ x: 12, y: 55 },
{ x: 13, y: 50 },
{ x: 14, y: 65 },
{ x: 15, y: 95 },
{ x: 16, y: 68 },
{ x: 17, y: 28 },
{ x: 18, y: 34 },
{ x: 19, y: 14 }
]
}
]
});
chart.render();
<script src="http://canvasjs.com/assets/script/canvasjs.min.js"></script>
<br/>
<div id="chartContainer" style="height: 360px; width: 100%;"></div>

Resources