Rickshaw Stacked Multi-graph - coffeescript

I am working with this multi-graph Dashing widget. It works correctly, but it uses only two data series and I want to add one more. I have modified the .coffee file to
class Dashing.Mgraph extends Dashing.Widget
#accessor 'current', ->
return #get('displayedValue') if #get('displayedValue')
points = #get('points')
if points
points[0][points[0].length - 1].y + ' / ' + points[1][points[1].length - 1].y ' / ' + points[2][points[2].length - 1].y
ready: ->
container = $(#node).parent()
# Gross hacks. Let's fix this.
width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1)
height = (Dashing.widget_base_dimensions[1] * container.data("sizey"))
#graph = new Rickshaw.Graph(
element: #node
width: width
height: height
renderer: 'area'
stroke: false
series: [
{
color: "#fff",
data: [{x:0, y:0}]
},
{
color: "#222",
data: [{x:0, y:0}]
},
{
color: "#333",
data: [{x:0, y:0}]
}
]
)
#graph.series[0].data = #get('points') if #get('points')
x_axis = new Rickshaw.Graph.Axis.Time(graph: #graph)
y_axis = new Rickshaw.Graph.Axis.Y(graph: #graph, tickFormat: Rickshaw.Fixtures.Number.formatKMBT)
#graph.renderer.unstack = true
#graph.render()
onData: (data) ->
if #graph
#graph.series[0].data = data.points[0]
#graph.series[1].data = data.points[1]
#graph.series[2].data = data.points[2]
#graph.render()
However, when I run dashing, nothing it displayed (not even my other widgets). It is just a blank screen. Can anyone tell me what is going on here?
EDIT:
I have isolated the problem more. It seems that everything works until I add the third data series in series: It seems to be this that causes it to not work.

Here, try using rickshawgraph widget https://gist.github.com/jwalton/6614023
Here is my .rb
points1 = []
points2 = []
points3 = []
(1..10).each do |i|
points1 << { x: i, y: 10 }
points2 << { x: i, y: 10 }
points3 << { x: i, y: 10 }
end
last_x = points1.last[:x]
SCHEDULER.every '2s' do
points1.shift
points2.shift
points3.shift
last_x += 1
points1 << { x: last_x, y: rand(50) }
points2 << { x: last_x, y: rand(10) }
points3 << { x: last_x, y: rand(100) }
series = [
{
name: "set1",
data: points1
},
{
name: "set2",
data: points2
},
{
name: "set3",
data: points3
}
]
send_event('convergence', series: series)
end
Here is my .erb
% content_for :title do %>My super sweet dashboard<% end %>
<div class="gridster">
<ul>
<li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
<div data-id="convergence" data-view="Rickshawgraph" data-title="Convergence" data-unstack="true" data-stroke="true" data-default-alpha="0.5" data-color-scheme="compliment" data-legend="true" data-summary-method="last"></div>
</li>
</ul>
</div>

Related

Mapbox GL JS: How to draw an horizontal separator inside a circle point

I'm trying to display two values separated by a horizontal separator inside a cluster point rendered using Mapbox GL JS.
Example (using leaflet) :
So far I've achieved to have this kind of point but I'm missing the 1px bar in the center.
How would you do this?
The code I'm using:
this.map.addLayer({
id: 'clusters',
type: 'circle',
source: 'markers',
filter: ['has', 'point_count'],
paint: {
'circle-color': '#ffffff',
'circle-radius': 20,
'circle-stroke-width': 3,
'circle-stroke-color': '#5eb3e4',
}
});
this.map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'markers',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count}\n{sum}',
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12,
},
paint: {
'text-color': '#00214e'
}
});
So I've managed to do this using a generated image, added as an icon to the layer:
const createLineImage = (width) => {
const bytesPerPixel = 4; // Each pixel is 4 bytes: red, green, blue, and alpha.
const data = new Uint8Array(width * bytesPerPixel);
for (let x = 0; x < width; x++) {
const offset = x * bytesPerPixel;
data[offset] = 0; // red
data[offset + 1] = 0; // green
data[offset + 2] = 0; // blue
data[offset + 3] = 255; // alpha
}
return { data, width, height: 1 };
};
this.map.addImage('line', createLineImage(25));
this.map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'markers',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count}\n{sum}',
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12,
'text-line-height': 1.5,
'icon-image': 'line',
},
});
Result is

Google Chart mouse over skipping data point [duplicate]

I want to add a custom tooltip to my charts by using the default one and for example just append some text to it.
Is this even possible, or to i have to create it all by myself with html?
data= google.visualization.arrayToDataTable([
["Element", "Duration ", { role: "style" }, { role: 'tooltip' }],
["Count", 23515, "orange", ???],
]);
How it is (Default Tooltip):
How i want it:
Append the duration as readable time, but still keep the default tooltip
it's not possible to add content to the default tooltip via standard functionality
to do so requires manipulating the tooltip directly when it is shown
the following working snippet listens for the 'onmouseover' event on the chart
then modifies the tooltip (if found)
using the row # passed as a property of the event argument
keep in mind, the style (font-size) will change according to the size of the chart
the snippet copies the style from the existing lines
google.charts.load('current', {
callback: function () {
var dataTable = new google.visualization.DataTable({
cols: [
{label: 'Element', type: 'string'},
{label: 'Duration', type: 'number'},
{role: 'style', type: 'string'}
],
rows: [
{c:[{v: 'Amazon Elastic Transcoder'}, {v: 3116, f: '3,116 s'}, {v: 'orange'}]},
{c:[{v: 'Amazon Elastic Transcoder'}, {v: 8523, f: '8,523 s'}, {v: 'cyan'}]}
]
});
var options = {
backgroundColor: 'transparent',
legend: 'none',
theme: 'maximized',
hAxis: {
textPosition: 'none'
},
tooltip: {
isHtml: true
}
};
var container = document.getElementById('chart_div');
var chart = new google.visualization.ColumnChart(container);
google.visualization.events.addListener(chart, 'onmouseover', function (props) {
var duration = dataTable.getValue(props.row, 1);
var hours = parseInt( duration / 3600 ) % 24;
var minutes = parseInt( duration / 60 ) % 60;
var seconds = duration % 60;
var tooltip = container.getElementsByTagName('ul');
var tooltipLabel = container.getElementsByTagName('span');
if (tooltip.length > 0) {
// increase tooltip height
tooltip[0].parentNode.style.height = '95px';
// add new li element
var newLine = tooltip[0].appendChild(document.createElement('li'));
newLine.className = 'google-visualization-tooltip-item';
// add span for label
var lineLabel = newLine.appendChild(document.createElement('span'));
lineLabel.style.fontFamily = tooltipLabel[0].style.fontFamily;
lineLabel.style.fontSize = tooltipLabel[0].style.fontSize;
lineLabel.style.color = tooltipLabel[0].style.color;
lineLabel.style.margin = tooltipLabel[0].style.margin;
lineLabel.style.textDecoration = tooltipLabel[0].style.textDecoration;
lineLabel.innerHTML = dataTable.getColumnLabel(1) + ': ';
// add span for value
var lineValue = newLine.appendChild(document.createElement('span'));
lineValue.style.fontFamily = tooltipLabel[0].style.fontFamily;
lineValue.style.fontSize = tooltipLabel[0].style.fontSize;
lineValue.style.fontWeight = tooltipLabel[0].style.fontWeight;
lineValue.style.color = tooltipLabel[0].style.color;
lineValue.style.margin = tooltipLabel[0].style.margin;
lineValue.style.textDecoration = tooltipLabel[0].style.textDecoration;
lineValue.innerHTML = hours + 'h ' + minutes + 'm ' + seconds + 's';
}
});
chart.draw(dataTable, options);
},
packages:['corechart']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
to add content to the tooltip using standard functionality requires replacing the tooltip altogether
the best result will be using html tooltips
to use html tooltips, two things must be in place
first, need html column property on tooltip column
{role: 'tooltip', type: 'string', p: {html: true}}
next, need tooltip.isHtml: true in the config options
the tooltip can be provided directly in the data,
or add dynamically, as in the following snippet...
google.charts.load('current', {
callback: function () {
var dataTable = new google.visualization.DataTable({
cols: [
{label: 'Element', type: 'string'},
{label: 'Duration', type: 'number'},
{role: 'style', type: 'string'}
],
rows: [
{c:[{v: 'Amazon Elastic Transcoder'}, {v: 3116, f: '3,116 s'}, {v: 'orange'}]},
{c:[{v: 'Amazon Elastic Transcoder'}, {v: 8523, f: '8,523 s'}, {v: 'cyan'}]}
]
});
dataTable.addColumn({role: 'tooltip', type: 'string', p: {html: true}});
for (var i = 0; i < dataTable.getNumberOfRows(); i++) {
var duration = dataTable.getValue(i, 1);
var hours = parseInt( duration / 3600 ) % 24;
var minutes = parseInt( duration / 60 ) % 60;
var seconds = duration % 60;
var tooltip = '<div class="ggl-tooltip"><span>' +
dataTable.getValue(i, 0) + '</span><div>' +
dataTable.getColumnLabel(1) + ': <span>' +
dataTable.getFormattedValue(i, 1) + '</span></div><div>' +
dataTable.getColumnLabel(1) + ': <span>' +
hours + 'h ' + minutes + 'm ' + seconds + 's</span></div></div>';
dataTable.setValue(i, 3, tooltip);
}
var options = {
backgroundColor: 'transparent',
legend: 'none',
theme: 'maximized',
hAxis: {
textPosition: 'none'
},
tooltip: {
//trigger: 'selection',
isHtml: true
}
};
var container = document.getElementById('chart_div');
var chart = new google.visualization.ColumnChart(container);
chart.draw(dataTable, options);
},
packages:['corechart']
});
.ggl-tooltip {
border: 1px solid #E0E0E0;
font-family: Arial, Helvetica;
font-size: 10pt;
padding: 12px 12px 12px 12px;
}
.ggl-tooltip div {
padding-top: 6px;
}
.ggl-tooltip span {
font-weight: bold;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

How does google-visualization-tooltip calculate the left and top values?

How does google-visualization-tooltip calculate the left and top values for style ?
example - on debugging i can see that :-
<div class="google-visualization-tooltip" style="width: 1px; height: 0px; left: 453.25px; top: 44.91px;"</div>
How can i calculate this relatively, so that the tooltip shows on top and is center aligned to the point that is hovered upon, irrespective of the width of the element?
most charts in the 'corechart' package,
have methods for getting the position of various chart elements.
first, get the chart's layout interface...
var chartLayout = chart.getChartLayoutInterface();
the layout interface has method --> getBoundingBox(id)
where id is a string id of the chart element.
to find the position of a point, use this format for id --> point#series#row -- point#0#0
we can use chart event onmouseover to know when a point has been hovered,
and the tooltip is being shown.
the onmouseover event sends an argument with the row and column from the data table,
of the point that is being hovered.
as such, we can get the layout, find the point, and position the tooltip.
google.visualization.events.addListener(chart, 'onmouseover', function (sender) {
// ensure point is hovered
if (sender.row !== null) {
var padding = 16;
var chartLayout = chart.getChartLayoutInterface();
var pointBounds = chartLayout.getBoundingBox('point#' + (sender.column - 1) + '#' + sender.row);
var tooltip = chart.getContainer().getElementsByClassName('google-visualization-tooltip');
if (tooltip.length > 0) {
var tooltipBounds = tooltip[0].getBoundingClientRect();
tooltip[0].style.top = (pointBounds.top - tooltipBounds.height - padding) + 'px';
tooltip[0].style.left = ((pointBounds.left + (pointBounds.width / 2)) - (tooltipBounds.width / 2)) + 'px';
}
}
});
see following working snippet...
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = new google.visualization.DataTable({
"cols": [
{"label": "x", "type": "number"},
{"label": "y", "type": "number"}
],
"rows": [
{"c":[{"v": 2015}, {"v": 1}]},
{"c":[{"v": 2016}, {"v": 2}]},
{"c":[{"v": 2017}, {"v": 3}]},
{"c":[{"v": 2018}, {"v": 4}]},
{"c":[{"v": 2019}, {"v": 5}]},
{"c":[{"v": 2020}, {"v": 6}]}
]
});
var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
google.visualization.events.addListener(chart, 'onmouseover', function (sender) {
// ensure point is hovered
if (sender.row !== null) {
var padding = 16;
var chartLayout = chart.getChartLayoutInterface();
var pointBounds = chartLayout.getBoundingBox('point#' + (sender.column - 1) + '#' + sender.row);
var tooltip = chart.getContainer().getElementsByClassName('google-visualization-tooltip');
if (tooltip.length > 0) {
var tooltipBounds = tooltip[0].getBoundingClientRect();
tooltip[0].style.top = (pointBounds.top - tooltipBounds.height - padding) + 'px';
tooltip[0].style.left = ((pointBounds.left + (pointBounds.width / 2)) - (tooltipBounds.width / 2)) + 'px';
}
}
});
var options = {
chartArea: {
bottom: 32,
left: 32,
right: 32,
top: 48,
width: '100%',
height: '100%'
},
hAxis: {
format: '0',
ticks: data.getDistinctValues(0)
},
legend: {
position: 'top'
},
pointSize: 4,
tooltip: {
isHtml: true,
trigger: 'both'
}
};
chart.draw(data, options);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Google Charts - How to append text to default tooltip

I want to add a custom tooltip to my charts by using the default one and for example just append some text to it.
Is this even possible, or to i have to create it all by myself with html?
data= google.visualization.arrayToDataTable([
["Element", "Duration ", { role: "style" }, { role: 'tooltip' }],
["Count", 23515, "orange", ???],
]);
How it is (Default Tooltip):
How i want it:
Append the duration as readable time, but still keep the default tooltip
it's not possible to add content to the default tooltip via standard functionality
to do so requires manipulating the tooltip directly when it is shown
the following working snippet listens for the 'onmouseover' event on the chart
then modifies the tooltip (if found)
using the row # passed as a property of the event argument
keep in mind, the style (font-size) will change according to the size of the chart
the snippet copies the style from the existing lines
google.charts.load('current', {
callback: function () {
var dataTable = new google.visualization.DataTable({
cols: [
{label: 'Element', type: 'string'},
{label: 'Duration', type: 'number'},
{role: 'style', type: 'string'}
],
rows: [
{c:[{v: 'Amazon Elastic Transcoder'}, {v: 3116, f: '3,116 s'}, {v: 'orange'}]},
{c:[{v: 'Amazon Elastic Transcoder'}, {v: 8523, f: '8,523 s'}, {v: 'cyan'}]}
]
});
var options = {
backgroundColor: 'transparent',
legend: 'none',
theme: 'maximized',
hAxis: {
textPosition: 'none'
},
tooltip: {
isHtml: true
}
};
var container = document.getElementById('chart_div');
var chart = new google.visualization.ColumnChart(container);
google.visualization.events.addListener(chart, 'onmouseover', function (props) {
var duration = dataTable.getValue(props.row, 1);
var hours = parseInt( duration / 3600 ) % 24;
var minutes = parseInt( duration / 60 ) % 60;
var seconds = duration % 60;
var tooltip = container.getElementsByTagName('ul');
var tooltipLabel = container.getElementsByTagName('span');
if (tooltip.length > 0) {
// increase tooltip height
tooltip[0].parentNode.style.height = '95px';
// add new li element
var newLine = tooltip[0].appendChild(document.createElement('li'));
newLine.className = 'google-visualization-tooltip-item';
// add span for label
var lineLabel = newLine.appendChild(document.createElement('span'));
lineLabel.style.fontFamily = tooltipLabel[0].style.fontFamily;
lineLabel.style.fontSize = tooltipLabel[0].style.fontSize;
lineLabel.style.color = tooltipLabel[0].style.color;
lineLabel.style.margin = tooltipLabel[0].style.margin;
lineLabel.style.textDecoration = tooltipLabel[0].style.textDecoration;
lineLabel.innerHTML = dataTable.getColumnLabel(1) + ': ';
// add span for value
var lineValue = newLine.appendChild(document.createElement('span'));
lineValue.style.fontFamily = tooltipLabel[0].style.fontFamily;
lineValue.style.fontSize = tooltipLabel[0].style.fontSize;
lineValue.style.fontWeight = tooltipLabel[0].style.fontWeight;
lineValue.style.color = tooltipLabel[0].style.color;
lineValue.style.margin = tooltipLabel[0].style.margin;
lineValue.style.textDecoration = tooltipLabel[0].style.textDecoration;
lineValue.innerHTML = hours + 'h ' + minutes + 'm ' + seconds + 's';
}
});
chart.draw(dataTable, options);
},
packages:['corechart']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
to add content to the tooltip using standard functionality requires replacing the tooltip altogether
the best result will be using html tooltips
to use html tooltips, two things must be in place
first, need html column property on tooltip column
{role: 'tooltip', type: 'string', p: {html: true}}
next, need tooltip.isHtml: true in the config options
the tooltip can be provided directly in the data,
or add dynamically, as in the following snippet...
google.charts.load('current', {
callback: function () {
var dataTable = new google.visualization.DataTable({
cols: [
{label: 'Element', type: 'string'},
{label: 'Duration', type: 'number'},
{role: 'style', type: 'string'}
],
rows: [
{c:[{v: 'Amazon Elastic Transcoder'}, {v: 3116, f: '3,116 s'}, {v: 'orange'}]},
{c:[{v: 'Amazon Elastic Transcoder'}, {v: 8523, f: '8,523 s'}, {v: 'cyan'}]}
]
});
dataTable.addColumn({role: 'tooltip', type: 'string', p: {html: true}});
for (var i = 0; i < dataTable.getNumberOfRows(); i++) {
var duration = dataTable.getValue(i, 1);
var hours = parseInt( duration / 3600 ) % 24;
var minutes = parseInt( duration / 60 ) % 60;
var seconds = duration % 60;
var tooltip = '<div class="ggl-tooltip"><span>' +
dataTable.getValue(i, 0) + '</span><div>' +
dataTable.getColumnLabel(1) + ': <span>' +
dataTable.getFormattedValue(i, 1) + '</span></div><div>' +
dataTable.getColumnLabel(1) + ': <span>' +
hours + 'h ' + minutes + 'm ' + seconds + 's</span></div></div>';
dataTable.setValue(i, 3, tooltip);
}
var options = {
backgroundColor: 'transparent',
legend: 'none',
theme: 'maximized',
hAxis: {
textPosition: 'none'
},
tooltip: {
//trigger: 'selection',
isHtml: true
}
};
var container = document.getElementById('chart_div');
var chart = new google.visualization.ColumnChart(container);
chart.draw(dataTable, options);
},
packages:['corechart']
});
.ggl-tooltip {
border: 1px solid #E0E0E0;
font-family: Arial, Helvetica;
font-size: 10pt;
padding: 12px 12px 12px 12px;
}
.ggl-tooltip div {
padding-top: 6px;
}
.ggl-tooltip span {
font-weight: bold;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

How to add comma in stacked column highchart in indian format?

I am using stacked column highchart. I am getting few value in column and tooltip. Now I want to show this value in Indian format with comma separator. Suppose I have a value like 123456789.So I want to show this value in 12,34,56,789 format. How can I do this? Please share with me if any body has any idea.
I tried this below code.
Highcharts.setOptions({
lang: {
thousandsSep: ','
}
});
But It gives 123,456,789 format, I want something like 12,34,56,789. The Indian format.
My codes are below:
function draw_charts(amount, interest , year)
{
/*Highcharts.setOptions({
lang: {
thousandsSep: ','
}
});*/
$('#chart_area').highcharts({
chart: {
type: 'column',
backgroundColor: 'transparent'
},
title: {
text: 'Year wise break-up'
},
xAxis: {
categories: year,
title: {
text: 'Year'
}
},
yAxis: {
min: 0,
title: {
text: 'Amount'
},
stackLabels: {
enabled: true,
style: {
fontWeight: 'bold',
color: (Highcharts.theme && Highcharts.theme.textColor) || 'gray'
}
}
},
legend: {
align: 'right',
x: -30,
verticalAlign: 'top',
y: -5,
floating: true,
backgroundColor: (Highcharts.theme && Highcharts.theme.background2) || 'white',
borderColor: '#CCC',
borderWidth: 1,
shadow: false
},
tooltip: {
headerFormat: '<b>{point.x}</b><br/>',
pointFormat: '{series.name}: {point.y}<br/>Total: {point.stackTotal}'
},
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
style: {
textShadow: '0 0 3px black'
}
}
}
},
series: [{
name: 'Interest',
data: interest, color: '#7fb801'
},{
name: 'Principal',
data: amount, color: '#4fc1e9'
}],
exporting: { enabled: false },
credits: { enabled: false },
});
}
You can slightly modify the numberFormat function, changing from the lines (from source):
thousands = strinteger.length > 3 ? strinteger.length % 3 : 0;
// ...
ret += strinteger.substr(thousands).replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep);
To these lines:
thousands = strinteger.length > 3 ? (strinteger.length - 1) % 2 : 0;
// ...
ret += strinteger.substr(thousands).replace(/(\d{2})(?=\d{3})/g, '$1' + thousandsSep);
Ending up with this function:
Highcharts.numberFormat = function (number, decimals, decimalPoint, thousandsSep) {
number = +number || 0;
decimals = +decimals;
var lang = Highcharts.getOptions().lang,
origDec = (number.toString().split('.')[1] || '').length,
decimalComponent,
strinteger,
thousands,
absNumber = Math.abs(number),
ret;
if (decimals === -1) {
decimals = Math.min(origDec, 20); // Preserve decimals. Not huge numbers (#3793).
} else if (!isNumber(decimals)) {
decimals = 2;
}
// A string containing the positive integer component of the number
strinteger = String(Highcharts.pInt(absNumber.toFixed(decimals)));
// Leftover after grouping into thousands. Can be 0, 1 or 3.
thousands = strinteger.length > 3 ? (strinteger.length - 1) % 2 : 0;
// Language
decimalPoint = Highcharts.pick(decimalPoint, lang.decimalPoint);
thousandsSep = Highcharts.pick(thousandsSep, lang.thousandsSep);
// Start building the return
ret = number < 0 ? '-' : '';
// Add the leftover after grouping into thousands. For example, in the number 42 000 000,
// this line adds 42.
ret += thousands ? strinteger.substr(0, thousands) + thousandsSep : '';
// Add the remaining thousands groups, joined by the thousands separator
ret += strinteger.substr(thousands).replace(/(\d{2})(?=\d{3})/g, '$1' + thousandsSep);
// Add the decimal point and the decimal component
if (decimals) {
// Get the decimal component, and add power to avoid rounding errors with float numbers (#4573)
decimalComponent = Math.abs(absNumber - strinteger + Math.pow(10, -Math.max(decimals, origDec) - 1));
ret += decimalPoint + decimalComponent.toFixed(decimals).slice(2);
}
return ret;
};
I've prefixed some function calls with Highcharts to make it work without a custom js-file.
See this JSFiddle demonstration of it in use.