I'm making an app with highcharts, after having many problems I finally make plotted but now takes the correct data but I need to make some adjustments.
for example if an user registers a new log on the same day it should take the cost of the log and add it to the logs of the same day or simply show if there are no more, I follow the railcast episode 223 and helpme a little. but my issue is: when I add a new log it create a new bar:
(there are only 2 logs, I'm gonna create a new log)
here is that my app do.
also I need to fix the datetime, here is my code:
$(function () {
new Highcharts.Chart({
chart: { renderTo: 'foo_chart',defaultSeriesType: 'column' },
title: { text: 'tanking costs daily' },
xAxis: { type: 'datetime' },
yAxis: {
title: { text: 'cost' }
},
tooltip: {
formatter: function () {
return Highcharts.dateFormat("%B %e %Y", this.x) + ': ' + '$' + Highcharts.numberFormat(this.y, 2);
}
},
series: [{
name: 'Days',
pointInterval: <%= 1.day * 1000 %>,
pointStart: <%= 1.weeks.ago.at_midnight.to_i * 1000 %>,
data:[
<% for tankinglog in #tankinglog %>
<%= "(" + tankinglog.cost.to_f.round(2).to_s + "),"%>
<% end %>
]
}]
});
});
if you see the list on the picture I have 3 logs...the last 2 logs have the same date these must appear in the same bar with the sum of their costs
Your question is very confusing. You want all the data summed into one bar? A new bar is created for each number you have in the data list. So your for-each loop is creating a new bar for each item in #tankinglog. The solution is to sum what's required into one number, then add it to the list.
I'm not sure exactly what you're asking because your question is poor but here are all the values summed into one bar.
$(function () {
new Highcharts.Chart({
chart: { renderTo: 'foo_chart',defaultSeriesType: 'column' },
title: { text: 'tanking costs daily' },
xAxis: { type: 'datetime' },
yAxis: {
title: { text: 'cost' }
},
tooltip: {
formatter: function () {
return Highcharts.dateFormat("%B %e %Y", this.x) + ': ' + '$' + Highcharts.numberFormat(this.y, 2);
}
},
series: [{
name: 'Days',
pointInterval: <%= 1.day * 1000 %>,
pointStart: <%= 1.weeks.ago.at_midnight.to_i * 1000 %>,
data:[
<%
result = 0;
for tankinglog in #tankinglog
result += tankinglog.cost.to_f
end %>
<%= result.round(2).to_s %>
]
}]
});
});
Related
I've built something like this. I get my data from the server, put it in an object called series, and pass it to 'series' in Highcharts code block. Basically, for every staff, there will be a date, and my default value(Y-Axis) is '1' for now. However, I can't get dates on the chart as expected even if it looks that I had correct data and did correct parsing. Unexpectedly, I get my millisecond values as Y-axis values, which does not make any sense, and every staff has a default date, which is 1 January. (For ex., staff 1, 1 January, x-axis value = 1554422400000)
I get dates like this, 19-02-2019 17:32. Then I split them, and use it like this,
([Date.UTC(parseInt(yearsplit[0]), datesplit[1]-1, parseInt(datesplit[0])), 1])
which looks exactly the same format in Highcharts, ([Date.UTC(1971, 2, 16), 0.86])
var responsePromise = $http.post('statistics/getAllProtocolRecords', data, null);
responsePromise.success(function (dataFromServer, status, headers, config) {
var series = [{
name: "",
data: []
}];
dataFromServer.protocolRecords.forEach((data) => {
var datesplit = data.checkupDate.split("-");
var yearsplit = datesplit[2].split(" ");
series.push({
name: data.staff,
data: [Date.UTC(parseInt(yearsplit[0]), datesplit[1]-1, parseInt(datesplit[0])), 1]
})
});
series.shift();
Highcharts.chart('container', {
chart: {
type: 'spline'
},
title: {
text: 'Toplam Muayene Kaydı (' + sysrefHcCheckupType + ')'
},
subtitle: {
text: ''
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { // don't display the dummy year
month: '%e. %b',
year: '%b'
},
title: {
text: 'Tarih'
}
},
yAxis: {
title: {
text: 'Toplam Muayene (Gün)'
},
min: 0
},
tooltip: {
headerFormat: '<b>{series.name}</b><br>',
pointFormat: '{point.x:%e. %b}: {point.y:%f} '
},
plotOptions: {
spline: {
marker: {
enabled: true
}
}
},
colors: ['#00bdff', '#FF0700', '#df0300', '#ff0700', '#c0df00'],
series: series
});
});
I've just realized that I'd made a little mistake in push function. In series.push, 'data' should like this, surrounded by array brackets:
data: [
[ Date.UTC(parseInt(yearsplit[0]), parseInt(datesplit[1])-1, parseInt(datesplit[0])), 1]
]
I am a bit out of my comfort zone, since I normally do analytics and not fancy front-ends. However, I would like to have a real-time demo of some of my work, so it becomes easier to understand and not just numbers in a matrix. I have looked around and found something semi-relevant and come this far:
(It has four series like I want to and it iterates - to some degree)
https://jsfiddle.net/023sre9r/
var series1 = this.series[0],
series2 = this.series[1],
series3 = this.series[2],
series4 = this.series[3];
But I am totally lost on how to remove the random number generators without loosing nice things like the number of data points in a view (seems to depend on the for loop?!). Remove the extra title "Values" right next to my real y-axis title. And of cause how to get a new data point from a XML-file every second.
Ideally I want to have an XML-file containing 4 values, which I update approximately every 200ms in MATLAB. And every second I would like my 4 series chart to update. Is it not relatively easy, if you know what you are doing?! :-)
Thanks in advance!
I simplified your example and added clear code showing how to fetch data from server and append it to your chart using series.addPoint method. Also if you want to use XML, just convert it to JS object / JSON.
const randomData = () => [...Array(12)]
.map((u, i) => [new Date().getTime() + i * 1000, Math.random()])
Highcharts.chart('container', {
chart: {
renderTo: 'container',
type: 'spline',
backgroundColor: null,
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load () {
const chart = this
setInterval(() => {
// Fetch example below (working example: https://github.com/stpoa/live-btc-chart/blob/master/app.js)
// window.fetch('https://api.cryptonator.com/api/ticker/btc-usd').then((response) => {
// return response.json()
// }).then((data) => {
// chart.series[0].addPoint({ x: data.timestamp * 1000, y: Number(data.ticker.price) })
// })
chart.series.forEach((series) => series.addPoint([new Date().getTime(), Math.random()], true, true))
}, 3000)
}
}
},
title: {
text: null
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: [{
title: {
text: 'Temperature [°C]',
margin: 30
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
}, {
}],
tooltip: {
formatter: function() {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' + Highcharts.numberFormat(this.y, 4);
}
},
legend: {
enabled: true
},
exporting: {
enabled: false
},
rangeSelector: {
enabled: false
},
navigator: {
enabled: false
},
scrollbar: {
enabled: false
},
series: [{
name: 'Setpoint',
data: randomData()
}, {
name: 'Return',
data: randomData()
}, {
name: 'Supply',
data: randomData()
}, {
name: 'Output',
data: randomData()
}]
})
Live example: https://jsfiddle.net/9gw4ttnt/
Working one with external data source: https://jsfiddle.net/111u7nxs/
I need some tips from you out there to come over a good solution on my problem with JavaScript, AJAX and JSON data. I want to fill a generic set with barcharts (I am using HighCharts) on my web page. The data is in JSON format which from the start I only used date and value as pair data set. The solution works fine of I had only one bar chart it, but I have a lot of charts on my page and I need to show all of them (up to twelve).
Now I want to adjust for displaying more than one graph. In the code below the DataMacro array works fine with the chart. It also has a hard coded ID matching a . Now I have a series of in the page like id=barchart11, id=barchar21, and so on. In the dataset I have made a tag called PanelCodeUI that I am going to use looping through the dataset. The problem is how to do that. The each-loop will now fill in all date,value for all vessels.
And further it I need to restructure the function which is displaying the barchart. The best thing would be to call a function with a data array and panelCodeUI id just replacing the name of the barchart and set in the datamacro as is. But I don’t know how to do this. The data is mixed between all vessels and I need to collect all data before sending to a function. So is the problem with AJAX and JavaScript with is asynchron. I need to ensure that it behaves correctly and fast.
Maybe I need to change my dataset, or I need to do this in several step like finding all vessel IDs then do another AJAX call to get date,value pair from a vessel and then displaying. I hope there is a way to do this with this data set and hope somebody can help me on this
Here is a bit of the JSON data set:
[
{"__type":"Demo.Entities.OilProductionLast5DaysEntity","Date":1465084800000,"Value":844,"VesselId":1,"SectorId":2,"PanelCodeUI":"21","VesselCodeUI":"21","VesselSorting":1},
{"__type":"Demo.Entities.OilProductionLast5DaysEntity","Date":1465084800000,"Value":8720,"VesselId":4,"SectorId":1,"PanelCodeUI":"11","VesselCodeUI":"12","VesselSorting":2},
{"__type":"Demo.Entities.OilProductionLast5DaysEntity","Date":1465084800000,"Value":948,"VesselId":5,"SectorId":1,"PanelCodeUI":"11","VesselCodeUI":"11","VesselSorting":1},
{"__type":"Demo.Entities.OilProductionLast5DaysEntity","Date":1465084800000,"Value":0,"VesselId":6,"SectorId":3,"PanelCodeUI":"31","VesselCodeUI":"31","VesselSorting":1},
{"__type":"Demo.Entities.OilProductionLast5DaysEntity","Date":1465171200000,"Value":2067,"VesselId":1,"SectorId":2,"PanelCodeUI":"21","VesselCodeUI":"21","VesselSorting":1}
]
And here is the JavaScript code so far:
$(function () {
var datamacro = [];
$.ajax({
type: "POST",
url: '../Services/HighChartService.asmx/GetOilProductionLast5DaysByActiveVessels',
data: '',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (seriedata) {
console.log(JSON.stringify(seriedata.d));
var productions = seriedata.d;
$.each(productions, function (index, productions) {
var yval = productions.Value;
var xval = productions.Date;
var x = [xval, yval];
datamacro.push(x);
//alert("productions Name: " + productions.Date + "\nID: " + productions.Value);
});
$(function () {
//var bchart = '#barchart' + vesselindex.toString();
// want this to be looped with generic names like #barchart11, #barchart21, #barchart31 and so on
$('#barchart11').highcharts({
chart: {
type: 'column'
},
title: {
text: 'LAST FIVE DAYS'
},
subtitle: {
text: ''
},
xAxis: {
type: "datetime",
tickInterval: 24 * 3600 * 1000,
labels: {
rotation: -45,
align: 'right'
},
dateTimeLabelFormats: { // don't display the dummy year
day: '%e. %b',
},
//crosshair: true
},
credits: {
enabled: false
},
yAxis: {
labels: {
enabled: false
},
title: {
text: null
}
},
tooltip: {
formatter: function () {
return Highcharts.dateFormat('%d/%m/%Y', new Date(this.x)) + '<br/>' + ' in barrels: ' + this.y;
}
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
}, series: {
pointRange: 24 * 3600 * 1000, // one day
pointInterval: 3600 * 1000
}
},
series: [{
//name: '',
showInLegend: false,
data: datamacro,
dataLabels: {
enabled: true,
rotation: -90,
color: '#FFFFFF',
align: 'right',
format: '{point.y:.1f}', // one decimal
y: 10, // 10 pixels down from the top
style: {
fontSize: '13px',
fontFamily: 'Verdana, sans-serif'
}
}
}]
});
});
},
error: function (r) {
alert(r.responseText);
},
failure: function (r) {
alert(r.responseText);
}
});
});
If i understand correctly, you would like to draw a chart for each different panelCodeUI ?
If that's the case, change your code after AJAX success with that :
var productions = seriedata.d;
var listPanelCodeUI = productions.map(function(p){return p.PanelCodeUI}).filter(function(item, pos, self) {
return self.indexOf(item) == pos;
});
//listPanelCodeUI : [21,11,31]
listPanelCodeUI.sort();
listPanelCodeUI.forEach(function(e){
datamacro = [];
//Create a div for each panelCodeUI
$("body").append("<div id='barchart" + e + "'></div>");
var divId = "#barchart"+e;
//Filter productions for specific panelCodeUI
var data = productions.filter(function(p){return p.panelCodeUI === e});
data.forEach(function(d){
var yval = d.Value;
var xval = d.Date;
var x = [xval, yval];
datamacro.push(x);
});
$(function () {
$(divId).highcharts({
...
})
})
}
That's what you need to parse your data:
charts = [];
$.each(productions.map(function(el) {
return el.PanelCodeUI;
}).filter(function(el, index, arr) {
return arr.indexOf(el) === index;
}), function(index,PanelCodeUI) {
var serie = productions.filter(function(el) {
return el.PanelCodeUI === PanelCodeUI;
});
$.each(serie, function(index, production) {
datamacro.push([production.Value, production.Date]);
});
drawChart('#barchart' + PanelCodeUI, 'LAST FIVE DAYS', datamacro);
});
Also i made this helper function to create the charts:
function drawChart(containerID, chartTitle, data) {
charts.push(new Highchart.Chart({
chart: {
type: 'column',
renderTo: containerID
},
title: {
text: chartTitle
},
subtitle: {
text: ''
},
xAxis: {
type: "datetime",
tickInterval: 24 * 3600 * 1000,
labels: {
rotation: -45,
align: 'right'
},
dateTimeLabelFormats: { // don't display the dummy year
day: '%e. %b',
},
//crosshair: true
},
credits: {
enabled: false
},
yAxis: {
labels: {
enabled: false
},
title: {
text: null
}
},
tooltip: {
formatter: function() {
return Highcharts.dateFormat('%d/%m/%Y', new Date(this.x)) + '<br/>' + ' in barrels: ' + this.y;
}
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
},
series: {
pointRange: 24 * 3600 * 1000, // one day
pointInterval: 3600 * 1000
}
},
series: [{
//name: '',
showInLegend: false,
data: data,
dataLabels: {
enabled: true,
rotation: -90,
color: '#FFFFFF',
align: 'right',
format: '{point.y:.1f}', // one decimal
y: 10, // 10 pixels down from the top
style: {
fontSize: '13px',
fontFamily: 'Verdana, sans-serif'
}
}
}]
}));
}
https://jsfiddle.net/zv6ymqpL/3/
series: [{
type: 'column',
name: 'Columns',
data: data
}, {
type: 'line',
name: 'Lines',
data: line,
step: true
}]
When hovering over the chart the tooltip will display both series or just the columns this is because the column series has a higher resolution (1 hour) compared to the line (2 hour).
The chart I'm using has a line series with step enabled, I want the tooltip when hovering over a data point to include the current step value (as it's all the same value from the last point until the next point).
Is this possible to do in Highstock?
You will need to use a formatter to calculate the values in the expanded step as to account for the lower resolution.
docs: http://api.highcharts.com/highstock#tooltip.formatter
Example:
$(function () {
$('#container').highcharts('StockChart', {
tooltip: {
formatter: function () {
var s = '<b>' + Highcharts.dateFormat('%A, %b %e, %Y', this.x) + '</b>';
$.each(this.points, function () {
s += '<br/>1 USD = ' + this.y + ' EUR';
});
return s;
}
},
rangeSelector: {
selected: 1
},
series: [{
name: 'USD to EUR',
data: usdeur
}]
});
});
P.S. - If I could see a sample data set (In JSON), I'll be able to calculate this.
In my app users record their weight and the date that weight belongs to. I want that data displayed in a HighStock line graph. Should be very simple, but I've been working on this (partially) over the past 2 months. I've looked at a bunch of different things and I can't get this to work.
The exact data I'd like displayed is the actual line in the line graph to be their weight, y-axis will be their weight, x-axis will be the date that the user entered for that weight.
i.e. user enters their weight in the form for yesterday as 135 and the date they put in with the form is 12/3/2012.
Don't know if it matters but a User has_many weights, a weight belongs_to a user.
Here is what I have and I'm getting a bunch of different errors. I'm definitely missing/not understanding something here:
Weight Model:
class Weight < ActiveRecord::Base
def user_weight_series(user, weight)
weights_by_weight_date = Weights.select("date(weight_date) as dater, content as weights")
weights_by_weight_date.map do |record|
parts = %w[%Y %m %d].map{ |s| record.dater.send(s) }
"[Date.UTC(#{parts.join(',')}), #{record.weights}]"
end
end
application.html.erb
<script type="text/javascript">
$(function () {
var chart;
$(document).ready(function() {
window.chart = new Highcharts.StockChart({
chart: {
renderTo: 'weight_chart',
type: 'line',
marginRight: 20,
marginBottom: 25
},
title: {
text: 'Your Weight Over Time',
x: -20 //center
},
yAxis: {
title: {
text: 'Weight'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
legend: {
layout: 'vertical',
align: 'center',
verticalAlign: 'bottom',
x: -10,
y: 100,
borderWidth: 0
},
scrollbar: {
enabled: true
},
rangeSelector : {
selected : 1
},
credits: {
enabled: false
},
series: [{
tickInterval: <%= 1.day * 1000 %>,
pointStart: <%= 3.weeks.ago.at_midnight.to_i * 1000 %>,
name: 'Weight',
data: [ <%= current_user.user_weight_series(user, weight).join(", ") %> ],
}]
},
function(chart){
// apply the date pickers
setTimeout(function(){
$('input.highcharts-range-selector', $('#'+chart.options.chart.renderTo))
.datepicker()
},0)
});
});
});
What's the best way to do this? Thank you in advance!
Here is the process I use, along with some tips to get you started:
Work backwards from the desired display you want to create, using dummy json data to populate the chart.
Figure out how to create a ruby hash of your real database data that matches the dummy json data series. For the timestamps, convert them to a highcharts-readable format like this:
SOME_DATE.to_time.to_i * 1000
Have one of your controllers render the resulting ruby hash as json.
def some_controller_method
#chart_data_series = [] // your desired series here
respond_to do |format|
format.json { render :json => #chart_data_series }
end
end
Read this json from its appropriate page and pass it into your chart series. One way is to use d3:
function buildChart(){
d3.json("/path/to/your/data.json", function(error, json_data) {
$('#chart_container').highcharts({
chart: {type: 'column'},
// options, options, etc
series: json_data,
// options, options, etc
});
});
});