I am trying to put together Dygraphs charting library and AngularJS. I have downloaded https://github.com/cdjackson/angular-dygraphs and following the provided example. I was able to have it working with my code. My next step is to assign data to a graph in a callback after data is returned by the server. That is not working well. Have someone any sample of setting up data after it's been returned from the server?
Thanks
<div id="graphdiv" class="col-md-8">
<ng-dygraphs
options="graph.options"
legend="graph.legend"
data="graph.data"/>
</div>
charts.controller('dygraphController', function($scope, chartsService) {
$scope.graph = {
data: [
],
options: {
labels: ["x", "A", "B"]
},
legend: {
series: {
A: {
label: "Series A"
},
B: {
label: "Series B",
format: 3
}
}
}
};
// This function is a callback
function result(data) {
var base_time = Date.parse("2008/07/01");
var num = 24 * 0.25 * 365;
for (var i = 0; i < num; i++) {
$scope.graph.data.push([new Date(base_time + i * 3600 * 1000),
i + 50 * (i % 60), // line
i * (num - i) * 4.0 / num // parabola
]);
};
};
});
I am getting the following in the Console:
Can't plot empty data set
Heights null 320 320 320 320 0 0
Position [object Object]
Related
I'm working with React and ChartJS to draw a doughnut chart with a 3*Pi/2 circumference
and rounded corner.
I saw these two posts where they explain how to round corners for data sets and it is working as expected with a complete circle and with half a circle:
ChartJs - Round borders on a doughnut chart with multiple datasets
Chartjs doughnut chart rounded corners for half doghnut
One answer on this post is to change "y" or "x" translation by factor of n, for example 2 in the following case: ctx.translate(arc.round.x, arc.round.y*2);
With this in mind I started to change values for x and y but have not yet reach the correct set of values that will make it work.
For example I tried to use a factor of 3/2 on the translation of y and this is what I get.
ctx.translate(arc.round.x, (arc.round.y * 3) / 2);
with no factor I get the following:
ctx.translate(arc.round.x, arc.round.y);
The code to round the end corner is exactly the same as in the posts I refer. But here it is just in case:
let roundedEnd = {
// #ts-ignore
afterUpdate: function (chart) {
var a = chart.config.data.datasets.length - 1;
for (let i in chart.config.data.datasets) {
for (
var j = chart.config.data.datasets[i].data.length - 1;
j >= 0;
--j
) {
if (Number(j) == chart.config.data.datasets[i].data.length - 1)
continue;
var arc = chart.getDatasetMeta(i).data[j];
arc.round = {
x: (chart.chartArea.left + chart.chartArea.right) / 2,
y: (chart.chartArea.top + chart.chartArea.bottom) / 2,
radius:
chart.innerRadius +
chart.radiusLength / 2 +
a * chart.radiusLength,
thickness: (chart.radiusLength / 2 - 1) * 2.5,
backgroundColor: arc._model.backgroundColor,
};
}
a--;
}
},
// #ts-ignore
afterDraw: function (chart) {
var ctx = chart.chart.ctx;
for (let i in chart.config.data.datasets) {
for (
var j = chart.config.data.datasets[i].data.length - 1;
j >= 0;
--j
) {
if (Number(j) == chart.config.data.datasets[i].data.length - 1)
continue;
var arc = chart.getDatasetMeta(i).data[j];
var startAngle = Math.PI / 2 - arc._view.startAngle;
var endAngle = Math.PI / 2 - arc._view.endAngle;
ctx.save();
ctx.translate(arc.round.x, arc.round.y);
console.log(arc.round.startAngle);
ctx.fillStyle = arc.round.backgroundColor;
ctx.beginPath();
//ctx.arc(arc.round.radius * Math.sin(startAngle), arc.round.radius * Math.cos(startAngle), arc.round.thickness, 0, 2 * Math.PI);
ctx.arc(
arc.round.radius * Math.sin(endAngle),
arc.round.radius * Math.cos(endAngle),
arc.round.thickness,
0,
2 * Math.PI
);
ctx.closePath();
ctx.fill();
ctx.restore();
}
}
}, };
These are the options to configure the chart:
const chartJsOptions = useMemo<chartjs.ChartOptions>(() => {
if (data) {
return {
elements: {
center: {
text: `${data.impact > 0 ? "%"}`,
color: isDarkTheme ? darkText : greyAxis, // Default is #000000
fontStyle: "Open Sans Hebrew, sans-serif",
sidePadding: 20, // Default is 20 (as a percentage)
minFontSize: 15, // Default is 20 (in px), set to false and text will not wrap.
lineHeight: 20, // Default is 25 (in px), used for when text wraps
},
},
legend: {
display: false,
},
// rotation: Math.PI / 2,
rotation: (3 * Math.PI) / 4,
circumference: (3 * Math.PI) / 2,
maintainAspectRatio: false,
animation: {
duration: ANIMATION_DURATION,
},
plugins: {
datalabels: false,
labels: false,
},
cutoutPercentage: 90,
tooltips: {
enabled: false,
rtl: true,
},
};
} else {
return {};
} }, [data, isDarkTheme]);
Here is where I call the react component for the chart:
<Doughnut
data={chartJsData}
options={chartJsOptions}
plugins={[roundedEnd]} />
How can I correctly calculate the rounded edges on a 3*Pi/2 circumference or any other circumference between complete and half?
This issue may be more of a math than programing and my geometrical math is also a bit rusty.
I'm building a react native app where the user should be able to move an adjuster around a pie chart to adjust the start and end angles of the pie slices. I'm using 3 panResponders and used the idea by /users/681830/val:
react native circle transform translate animation
I'm calculating the shortest distance to either of the 36 snapshots then set the animated value to that point however it is super inefficient and laggy. Can anyone suggest any better performing solution?
'''
this._panResponder1 = PanResponder.create(
{
onStartShouldSetPanResponder: (evt, gesture) =>true,
onPanResponderMove: (evt, gesture) => {
//we need the distance between the points and get the index of the minimum distance
distances = [];
for(var i = 0; i < 36; i++){
var a = this.outputRangeX[i] - gesture.moveX;
var b = this.outputRangeY[i] - gesture.moveY + 120;
distances.push(Math.sqrt(a*a + b*b));
}
var minInd = distances.indexOf(Math.min(...distances));
this.setState({indexOfAdj1 : minInd});
this.adj1Anim.setValue((1/36)* minInd);
var isPos1 = minInd/36;
var isPos2 = (minInd)/36;
if(minInd>24){
isPos1 = -1 * ((36-minInd)/36);
isPos2 = minInd/36;
this.setState({data: [
{
number: 1,
startAngle: isPos1* Math.PI * 2,
endAngle: this.state.data[0].endAngle,
},
{
number: 30,
startAngle: this.state.data[1].startAngle,
endAngle: this.state.data[1].endAngle,
},
{
number: 1,
startAngle: this.state.data[1].endAngle,
endAngle: isPos2* Math.PI * 2,
},
]});
}else{
this.setState({data: [
{
number: 1,
startAngle: isPos1* Math.PI * 2,
endAngle: this.state.data[0].endAngle,
},
{
number: 30,
startAngle: this.state.data[1].startAngle,
endAngle: this.state.data[1].endAngle,
},
{
number: 1,
startAngle: -((Math.PI * 2)-this.state.data[1].endAngle),
endAngle: isPos2* Math.PI * 2,
},
]});
}
}
'''
Hi Guys according to the documentation of google charts a row type can either be :
The type can be one of the following: 'string', 'number', 'boolean',
'date', 'datetime', and 'timeofday'.
https://developers.google.com/chart/interactive/docs/datesandtimes
I need to have timespan type, so I can have vAxis with values 0 hour, 1 hour, 2 hours, ....up to any number. Which will mean between each two is 60 degrees like a timespan, but not 100 degree like numbers.
Is there a way to acheive this? timeofday will not work also when reaching 24 hours it turns it into 00:00
using the option --> hAxis.ticks
combined with object notation for values --> {v: value, f: formattedValue}
you could probably use just about any type (other than 'string')
see the following working snippet for a basic example...
a custom set of hAxis.ticks is built, one tick for each hour, in a 48 hour timespan
google.charts.load('current', {
callback: function () {
drawChart();
window.addEventListener('resize', drawChart, false);
},
packages:['corechart']
});
function drawChart() {
var dataTable = new google.visualization.DataTable();
dataTable.addColumn('date', 'Timespan');
dataTable.addColumn('number', 'Y');
var oneHour = (1000 * 60 * 60);
var startDate = new Date();
var endDate = new Date(startDate.getTime() + (oneHour * 24 * 2));
var ticksAxisH = [];
for (var i = startDate.getTime(); i < endDate.getTime(); i = i + oneHour) {
var tickValue = new Date(i);
var tickFormat = (tickValue.getTime() - startDate.getTime()) / oneHour;
var tick = {
v: tickValue,
f: tickFormat + 'h'
};
ticksAxisH.push(tick);
dataTable.addRow([tick, (2 * tickFormat) - 8]);
}
var container = document.getElementById('chart_div');
var chart = new google.visualization.LineChart(container);
chart.draw(dataTable, {
hAxis: {
ticks: ticksAxisH
}
});
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
im new in angular js and i need to use highchart in my angular page . the problem is that i must draw chart with dynamic data from json and the number of charts will be dynamic too , maybe it should draw 3 or 4 different chart from one json . I searched alot but couldnt solve my problem.
this code works but show the data in one chart in different series. I need to show each series in different charts, and in this case the json send 4 data but it will be changed .
1. List item
$scope.draw_chart = function(){
Highcharts.chart('container2', {
chart:{
type: 'spline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function () {
var this_chart = this;
$scope.ws = ngSocket('ws://#');
$scope.ws.onOpen(function () {
});
var k = 0 ;
var time=0;
$scope.points_avarage = [];
$scope.ws.onMessage(function (message) {
listener(JSON.parse(message.data));
var z = JSON.parse(message.data);
var line_to_draw = z.result.length;
var j = 0 ;
for(i=0 ; i < line_to_draw*2 ; i+=2)
{
$scope.data_to_draw[i] = {
name : z.result[j][0]['name'] ,
y : z.result[j][0]['rx-bits-per-second']
}
$scope.data_to_draw[i+1] = {
name : z.result[j][0]['name'] ,
y : z.result[j][0]['tx-bits-per-second']
}
j++;
}
this_chart.series[0].name= $scope.data_to_draw[0].name;
this_chart.series[1].name= $scope.data_to_draw[1].name;
this_chart.series[2].name= $scope.data_to_draw[2].name;
this_chart.series[3].name= $scope.data_to_draw[3].name;
for(i=0; i < line_to_draw*2; i++) {
var x = (new Date()).getTime(); // current time
var y = parseInt($scope.data_to_draw[i].y);
this_chart.series[i].addPoint([x, y], true, true);
}
});
var d = new Date().toTimeString();
}
}
},
global: {
useUTC: false
},
title: {
text: 'Live data'
},
xAxis: {
type: 'datetime'//,
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
width: 1,
color: '#808080'
}
]
}
plotOptions: {
series: {
marker: {
enabled: false
}
}
},
series: [{
data: (function () {
var data = [],
time = (new Date()).getTime(),
i;
for (i = -5; i <= 0; i += 1) {
data.push({
x: time ,
y: 0
});
}
return data;
}())
},
{
data: (function () {
var data = [],
time = (new Date()).getTime(),
i;
for (i = -5; i <= 0; i += 1) {
data.push({
x: time ,
y: 0
});
}
return data;
}())
},
{
data: (function () {
var data = [],
time = (new Date()).getTime(),
i;
for (i = -5; i <= 0; i += 1) {
data.push({
x: time ,
y: 0
});
}
return data;
}())
},
{
data: (function () {
var data = [],
time = (new Date()).getTime(),
i;
for (i = -5; i <= 0; i += 1) {
data.push({
x: time ,
y: 0
});
}
return data;
}())
}
]
});
};
<div id="containet" ng-init="draw_chart()"></div>
Condition
I have an array of HTML element constructed using ngRepeat.
<li ng-repeat="person in persons">Name: {{ person.name }}. Age {{ person.age }}</li>
In my controller I am trying to update (random) the person's "age" dynamically using Angular $interval.
$scope.persons = [
{name: "John Doe", age: 30},
{name: "Peter Parker", age: 21},
{name: "Clark Kent", age: 45},
{name: "John Wayne", age: 33}
];
var promiseOfYouth = [];
$scope.makeYoung = function() {
//since persons are store in array object.
//loop persons to update dynamically each person age
for(var x = 0; x < $scope.persons.length; x++) {
//check age randomizer already running
if ( angular.isDefined(promiseOfYouth[x]) ) return;
//make them young!
promiseOfYouth[x] = $interval(function() {
console.log('x value in $interval function: '+x);
try {
$scope.persons[x].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
} catch(err) {
console.log('"$scope.persons['+x+'].age" is not valid. Error message:'+err.message);
}
}, 100, 50);
}
}
Problem
In the makeYoung function the "x" value always return "4"
Question
How do I passed value of "x" (in this case it should 0,1,2,3) into the $interval function?
For further details please take a look at my jsFiddle example here
That's a classical JavaScript gotcha. You need to have x in a new scope to make this work. Otherwise, all the closures use the value of the same x variable, which is 4 at the end of the loop.
See the jsfiddle
function makePersonYoung(x) {
//check age randomizer already running
if ( angular.isDefined(promiseOfYouth[x]) ) return;
//make them young!
promiseOfYouth[x] = $interval(function() {
$scope.persons[x].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
}, 100, 50);
}
$scope.makeYoung = function() {
//since persons are store in array object.
//loop persons to update dynamically each person age
for(var x = 0; x < $scope.persons.length; x++) {
makePersonYoung(x);
}
}
This solution has the added advantage of making the code more readable.
This is a quirk of javascript. That blog post suggests several solutions. Copy the x variable as an immediately executing function:
(function(x2) {promiseOfYouth[x2] = $interval(function() {
console.log('x value in $interval function: '+x2);
try {
$scope.persons[x2].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
} catch(err) {
console.log('"$scope.persons['+x2+'].age" is not valid. Error message:'+err.message);
}
}, 100, 50)}(x);
Or bind the the function
promiseOfYouth[x] = $interval(function(x2) {
console.log('x value in $interval function: '+x2);
try {
$scope.persons[x2].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
} catch(err) {
console.log('"$scope.persons['+x2+'].age" is not valid. Error message:'+err.message);
}
}.bind(null, x), 100, 50);
you can inject it like a parameter
promiseOfYouth[x] = $interval(function(x) {
console.log('x value in $interval function: '+x);
try {
$scope.persons[x].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
} catch(err) {
console.log('"$scope.persons['+x+'].age" is not valid. Error message:'+err.message);
}
}, 100, 50);
fiddle example