I am using a javascript library called Highcharts https://www.highcharts.com/
to plot JSON data on a stock/line graph.
Reference the image below to get an understanding of the terms I am using in this post.
https://www.highcharts.com/images/docs/understanding_highstock.png
So everything works fine when clicking on the range selector buttons to zoom in. But when I use the navigator to specify a huge range (years), the final line plot is incorrect. I have created some images to help you understand what I mean.
Using the range selector buttons to specify a smaller range works perfectly when trying to plot [1519162460000,246.39649367343821] as my final price:
But now, let me resize the navigator in order to show the entire price history:
If you look carefully at the image you should be able to see that the last plot on the chart is incorrect and shows $230.72 when it should display $246.40 246.39649367343821 <- rounded
As you can probably tell, this is a major issue for someone who is trying to display relevant price information on my website.
If I do not restrict the width of the chart div or resize the browser window the plots display fine but I still dont know the issue. (note: what I just described does not work in codepen because the viewport width is restricted so the plots mess up regardless when I navigate a big enough range)
Below is a snippet of my js and html along with a link to the codepen I created.
JS
Highcharts.setOptions({
lang: {
thousandsSep: ","
}
});
Highcharts.stockChart("chart", {
chart: {
width: 290,
padding: [5, 5, 5, 5]
},
title: {
text: "Litecoin Price" // make dynamic
},
series: [
{
name: "Price",
data: JSON.parse(data)["stats"],
tooltip: {
valueDecimals: 2,
valuePrefix: "$",
valueSuffix: " USD"
}
}
],
responsive: {
rules: [
{
condition: {
maxWidth: 500
},
chartOptions: {
chart: {
zoomType: "none"
},
credits: {
enabled: false
},
scrollbar: {
enabled: false
} //,
//navigator: {
//enabled: false
//},
//rangeSelector: {
//enabled: false
//}
}
}
]
},
scrollbar: {
liveRedraw: false
},
rangeSelector: {
allButtonsEnabled: false,
buttons: [
{
type: "month",
count: 1,
text: "1m"
},
{
type: "month",
count: 3,
text: "3m"
},
{
type: "month",
count: 6,
text: "6m"
},
{
type: "year",
count: 1,
text: "1y"
},
{
type: "ytd",
count: 1,
text: "YTD"
}
],
selected: 4,
inputEnabled: true,
enabled: true,
inputStyle: {
color: "#757575"
},
labelStyle: {
color: "#757575"
}
}
});
HTML
<div class="card" style="padding: 10px; min-width: 330px;">
<div class="card chart-card" style="height: 350px; overflow: hidden; width: 300px;">
<div id="chart" style="height: 350px; min-width: 300px;">
<div id="highcharts-loading">
<div style="display: block; margin: 0 auto; position: absolute; top: calc(50% - 24px); left: calc(50% - 24px);"><span class="loading1"></span></div>
</div>
<div id="highcharts-nodata" style="display:none; text-align: center;">No chart data found</div>
</div>
</div>
</div>
That's because when you zoom out enough, Highcharts groups the data and displays the average value. If you look at the tooltip it says "Week from Monday, Feb 19, 2018", which means the value that is being displayed is the average for that week. I believe there are some settings that allow you to disable that grouping, but at the expense of the graph interaction possibly becoming laggy due to rendering so many data points.
Data grouping is the concept of sampling the data values into larger
blocks in order to ease readability and increase performance of the
JavaScript charts. Highstock by default applies data grouping when the
points become closer than a certain pixel value, determined by the
groupPixelWidth option.
The settings for data grouping on line charts are here: series.line.dataGrouping
Related
I'm using ApexCharts to display a rangeBar. If I slide the rangeBar from side to side, the date on the xaxis is showing inconsistently. For instance on this image, I have two days - the 26th and the 27th, but the day label is hidden.
If I zoom out and zoom in, the day-label is showing.
I've been looking at ApexCharts docs several times, and have tried many different things to enforce this day label to always show, but without any luck.
Here is my configuration
var options = {
series: [
{
data: arr
}
],
colors: [
"#00be00",
],
chart: {
height: 150,
width: '100%',
type: 'rangeBar',
toolbar: {
tools: {
download: false,
// pan: false,
},
},
},
plotOptions: {
bar: {
horizontal: true
}
},
xaxis: {
type: 'datetime',
},
};
Can I somehow force the day-label to always show?
I have line-time-series chart (in ECharts). I need to keep a constant number of points, which moves from left to right with new data. However, when I add data using setOption, there is no shift, but each point just changes its position in the y direction.
Can someone please advise me what I'm doing wrong?
This example does exactly what I need. I think I have it the same, but it still behaves differently.
Thank you
<script src="https://unpkg.com/vue#2.6.12/dist/vue.js"></script>
<script src="https://unpkg.com/echarts#4.9.0/dist/echarts.js"></script>
<div id="app" style="width: 700px; height: 400px;">
<div ref="echart" style="width: 100%; height: 100%;" ></div>
<button type="button" #click="onClick"> Push data < /button></div>
new Vue({
el: '#app',
mounted() {
this.chart_data=[
['2020-09-01 15:14:13.0', 1],
['2020-09-01 15:14:14.0', 1.5],
['2020-09-01 15:14:15.0', 0.7],
['2020-09-01 15:14:16.0', 0.8],
['2020-09-01 15:14:17.0', 1.7]
]
this.chart=echarts.init(this.$refs.echart)
var option={
color: '#3283bb',
xAxis: {
type: 'time',
splitLine: {
show: false
}
},
yAxis: {
type: 'value',
splitLine: {
show: false
}
},
series: [{
showSymbol: false,
type: 'line',
areaStyle: {
opacity: 0.2
},
lineStyle: {
width: 1.5
},
data: this.chart_data,
hoverAnimation: false
}]
}
this.chart.setOption(option)
},
methods: {
onClick: function() {
this.chart_data.shift()
this.chart_data.shift()
this.chart_data.push(['2020-09-01 15:14:18.0', 2.5])
this.chart_data.push(['2020-09-01 15:14:19.0', 2.0])
this.chart.setOption({
series: [{
data: this.chart_data
}]
})
}
},
})
JSFiddle example
I was able to find out the answer directly from the ECharts developers. Therefore, if someone has a similar problem, the solution is as follows:
ECharts performs animates "shift" if it recognizes part of the old data in the newly added data. Currently, this can be done by adding a unique name to each value. Each value is then an object with a name and value property.
{name: '2020-09-01 15:14:18.0', value: ['2020-09-01 15:14:18.0', 2.5]}
I am attaching a modified jsfiddle https://jsfiddle.net/Clyde256/b2d4juzt/44/ from the previous post, where the shift already works.
I would like to add, that if you are using dataset functionality, you need to name your dimension and then in the series property use the encode block and set itemId to any dimension name that has unique values like so:
dataset: {
dimensions: ['value', 'ts'],
source: [],
},
series: [
{
name: 'some line',
type: 'line',
encode: {
x: 'ts',
y: 'value',
itemId: 'ts',
},
},
],
Then the chart renders with correct 'shift left' animation when new values are added and old ones are removed.
I'm creating gauge charts using c3.js. I'm setting the height and width both to 75 because that's the right size of the gauge that I'm wanting, however when they get generated, there's always extra whitespace in the container that's messing me up.
I really want the svg that gets created to have a height of 60 in order to move the label up properly. The problem, is that when I set the height/width of the chart to 60, the size of the gauge itself gets way too small because of this extra whitespace.
I've tried setting the padding of everything that I know of to 0. I've searched through the documentation, there's always a chance that I've overlooked something. I can always try to do some hacky css to get around it, but before I do that, I'd like to change something in the configuration if I can.
Essentially, I want the chart to take up the full size that I specify. It seems that the legend, that I've specified to not show, is still taking up space that the chart should use.
http://jsfiddle.net/kLsox4ya/1/
<div class='row'>
<div class="col-12" id="chart"></div>
<p class="col-12 f-small">PERFECT</p>
</div>
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [['data', 0]],
type: 'gauge'
},
gauge: {
fullCircle: true,
startingAngle: 2 * Math.PI,
width: 3,
expand: false,
label: {
show: false
}
},
size: {
height: 75,
width: 75
},
legend: {
show: false
},
interaction: {
enabled: false
}
});
You are going to have a hard time getting that much control over c3. It's doing a lot under the hood to calculate positions for axis, legends, etc... that you aren't even using.
I think you have two options:
Code it yourself using straight d3
Resort to a little hackery. For instance here, I've manually adjust the height after it renders.
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [['data', 90]],
type: 'gauge'
},
tooltip: {
show: false
},
color: {
pattern: ['#565656', '#cfd628', '#e8b532', '#28d632'],
threshold: {
values: [40, 80, 90, 100]
}
},
gauge: {
fullCircle: true,
startingAngle: 2 * Math.PI,
width: 3,
label: {
format: function (value, ratio) {
return '';
},
extents: function (value) {
return '';
}
}
},
size: {
height: 75,
width: 75
},
legend: {
show: false
},
interaction: {
enabled: false
},
axis: {
x: {
show: false
},
y: {
show: false
}
},
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
onrendered: function(){
this.svg.attr('height', 55);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.12/c3.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.12/c3.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class='row'>
<div class="col-12" id="chart"></div>
<p class="col-12 f-small">PERFECT</p>
</div>
I've always use flot.js for common charting requirements, but I'd like to explore new ways to visualize data that might be beyond this charting library. I'd appreciate any advice or recommendations as to how others might programmatically render the custom display chart below. Maybe CSS?
I know you probably weren't looking for an answer spelling out how to implement this in flot, but it was a fun exercise.
Getting creative with hiding and stacking bars with the stack plugin allows you to represent your bounds. For each set of bounds, you'll need to create a hidden bar with your lower bound value and then create a visible bar with your upper bound value (and stack the two). Specifying which bars should stack on each other is easy by setting the stack option to the same key.
Once the bars are setup, the next step is to set the options of the chart. A grid marking handles displaying the current value. Hiding both axes effectively hides the grid.
The rest comes down to creating methods to append div elements to placeholder to show the bar values, labels, and marking value.
This is a basic implementation of your example image that really focuses on the flot component of the chart. With a bit more time, the extra appended div elements could be styled in such a way to more closely match your example.
This JSFiddle contains the code below for easier review.
$(function() {
var data = [{
data: [ [0, 21.51] ],
lines: { show: false },
bars: { show: false },
stack: 0,
label: 'Hidden'
},{
data: [ [1, 32.50] ],
lines: { show: false },
bars: { show: false },
stack: 1,
label: 'Hidden'
},{
data: [ [2, 47.14] ],
lines: { show: false },
bars: { show: false },
stack: 2,
label: 'Hidden'
},{
data: [ [0, 37.77] ],
stack: 0,
label: 'Last Year'
},{
data: [ [1, 24.65] ],
stack: 1,
label: 'Last Month'
}, {
data: [ [2, 7.67] ],
stack: 2,
label: 'Last Week'
}];
var options = {
series: {
bars: { show: true },
points: { show: false }
},
xaxis: { show: false },
yaxis: { show: false },
grid: {
show: true,
borderWidth: 0,
backgroundColor: null,
markings: [{
xaxis: { from: 0, to: 3 },
yaxis: { from: 48.01, to: 48.01 },
color: "#000000"
}]
},
legend: { show: false }
};
var plot = $.plot($('#graph'), data, options);
var plotData = plot.getData();
var markings = plot.getOptions().grid.markings;
displayBarValues(plotData);
displayBarLabels(plotData);
displayMarkingValues(markings);
// display values next to bars
function displayBarValues(plotData) {
$.each(plotData, function(i, data) {
var stackedValue = data.data[0][1];
if (data.bars.show) {
stackedValue = findStackedValue(plotData, data.stack);
}
var offset = plot.pointOffset({x: data.data[0][0], y: stackedValue});
$('<div class="data-point-value">-- $' + stackedValue + '</div>').css( {
left: offset.left + 30,
top: offset.top - 8,
}).appendTo(plot.getPlaceholder());
});
}
function findStackedValue(dataSeries, stackNumber) {
var stackedValue = 0;
for (var i =0; i < dataSeries.length; i++) {
if (dataSeries[i].stack === stackNumber) {
stackedValue = stackedValue + dataSeries[i].data[0][1];
}
}
return stackedValue;
}
// display a marking value
function displayMarkingValues(markings) {
$.each(markings, function(i, marking) {
var offset = plot.pointOffset({x: marking.xaxis.to, y: marking.yaxis.to });
$('<div class="data-point-value">------ $' + marking.yaxis.to + '</div>').css( {
left: offset.left,
top: offset.top - 8,
}).appendTo(plot.getPlaceholder());
});
}
function displayBarLabels(plotData) {
$.each(plotData, function(i, data) {
if (data.bars.show) {
var stackedValue = findStackedValue(plotData, data.stack);
var offset = plot.pointOffset({x: data.data[0][0], y: stackedValue});
$('<div class="data-point-label">' + data.label + '</div>').css({
left: offset.left - 35,
top: offset.top + 50,
}).appendTo(plot.getPlaceholder());
}
});
}
});
#graph {
margin: 0 auto;
text-align: center;
width: 100px;
height: 600px;
}
.data-point-value {
position: absolute;
white-space: nowrap;
font-size: 11px;
}
.data-point-label {
position: absolute;
white-space: nowrap;
width: 100px;
font-size: 11px;
text-align: right;
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
-o-transform: rotate(-90deg);
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.js"></script>
<script src="https://rawgit.com/emmerich/flot-orderBars/master/js/jquery.flot.orderBars.js"></script>
<script src="https://rawgit.com/flot/flot/master/source/jquery.flot.stack.js"></script>
<div id="graph"></div>
I have used Fusion Charts. Its pretty easy to customize and it works well in all browsers. Also have a look at Chart JS. Its pretty cool and its open source
I'v been using Chart.js for a while. While it is sweet at first glance, and you really can do super awesome stuff with it out of the box, it can be really limiting later on when you come across the need to do some simple/custom details that are not in documentation. For example: label outside the chart; turning off background for bar chart, doughnut bar with 1 value (for example - i have doughnut chart, with value 20%, i want the difference to be colored - you cant do that).
Of course all these stuff can be addressed and made with customizing .js file, and extending it but if you don't have time for that and you want out of the box solution, you can be stuck on simple detail so i suggest reading full docs to see if it is up to your expetations.
I would recommend D3.js with the caveat the the learning curve is steep; let me try to explain:
In something like chart.js, chartist,etc you provide the data, chart type and some configuration and you have your chart. In D3, it is a bit more complicated in the sense that D3 provides the framework for displaying and interacting with data via the Dom elements you design and specify.( Mostly svg's although you can also use divs,spans etc)
While it feels daunting at first and the documentation is basically an API Reference, there are hundreds of examples you can use as base or inspiration.
I'v been using (ChartJS, Morris, Inline Charts) for dashboard. It may help you much to customize the chart
I would recommend Chartist.js. It is fully responsive with great flexibility and DPI dependent.
you're able to style your charts with CSS in #media queries and lot of animation options. they have given examples for line chart, bar chart, pie chart with code. So it will definitely help you.
I like amCharts.
Can do all kinds of stuff and is free to use.
Google gives you an interesting charting library.
May want to try that but it requires you to be connected to Google for it to work (can't run it offline).
Here's a shot at it with HighCharts. I adapted the 'Stacked and grouped column' example at http://www.highcharts.com/demo/column-stacked-and-grouped.
Obviously there's work to be done in getting the labels and axes right, but I think this is a good start.
jsFiddle at http://jsfiddle.net/saevj2n4/1/
HTML:
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
JavaScript
$(function () {
Highcharts.chart('container', {
chart: {
type: 'column',
width: 200,
height: 1000
},
title: {
text: 'Total fruit consumtion, grouped by gender'
},
xAxis: {
categories: ['Profit']
},
yAxis: {
allowDecimals: false,
min: 0,
title: {
text: 'Number of fruits'
}
},
tooltip: {
formatter: function () {
return '<b>' + this.x + '</b><br/>' +
this.series.name + ': ' + this.y + '<br/>' +
'Total: ' + this.point.stackTotal;
}
},
plotOptions: {
column: {
stacking: 'normal',
//groupPadding: .45,
pointPadding: 0,
//pointWidth: 40,
}
},
series: [{
name: 'Min',
data: [59.28 - 21.58 ],
stack: 'Last Year',
color: "#919191"
}, {
name: 'Max',
data: [21.58 ],
stack: 'Last Year',
color: "transparent"
}, {
name: 'Min',
data: [ 57.15 - 32.5 ],
stack: 'Last Month',
color: "#6095c9"
}, {
name: 'Max',
data: [32.50],
stack: 'Last Month',
color: "transparent"
}, {
name: 'Min',
data: [54.81 - 47.14 ],
stack: 'Last Week',
color: "#745f8e"
}, {
name: 'Max',
data: [47.14],
stack: 'Last Week',
color: "transparent"
}]
});
});
I'm using bar chart with Highcharts 4.0.4.
When categories labels are to close to each other half of them disappear. I want to disable this behaviour because there is plenty of space.
I've tried to search Highcharts documentations with: overflow, crop, clip, hide and so on. Nothing seems to fit in.
Examples:
height: 260px;
http://jsfiddle.net/t1wtLLnc/3/
height: 230px;
http://jsfiddle.net/t1wtLLnc/2/
As you can see in second example only 5 of 10 categories are shown.
In order to force the chart to show all categories regardless of available space set the xAxis.labels.step to 1.
Here is code:
$(function () {
$('#container').highcharts({
chart: {
type: 'bar'
},
title: {
text: ''
},
subtitle: {
text: ''
},
xAxis: {
categories: ['cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6', 'cat7', 'cat8', 'cat9', 'cat10'],
title: {
text: null
},
labels: {
step: 1
}
},
series: [{
name: 'ser1',
data: [107, 31, 635, 203, 2, 34, 54, 41, 105, 20]
}, ]
});
});
And here is sample fiddle.
Note that you can run into issues with cramping up of text. We manipulate the spacingLeft and margins to accommodate long titles. In some cases we are forced to increase height of chart.