Chart.js not drawing on mobile (safari/chrome) fine on desktop - javascript

I've got a piechart that works perfectly fine on desktop. It retrieves data from an AJAX request and stores the data/json it gets. Selected data is then pushed into the chart. I am using Thymeleaf & Spring in but thats not relevant here I believe.
Everything is rendered fine on my page when I access it through Safari or chrome on mobile, however the graph is not present.
I've tried changing responsive true/false, maintaingAspectRatio false/true, playing with other options provided in the chart.js documentation. Changed the viewport, set my width on the container of the canvas rather than the canvas it self and a whole bunch of other stuff.
Could it be due to the load order? i.e the page is loaded before it can actually get the information from the request? However, that would mean that it shouldn't be working on desktop either.
Here is some code
myGraph.js
$(document).ready(function () {
var id = $.url(2);
$.ajax({
url: "hosturl/a/b/" + id + "/c/d",
type: "GET",
dataType: "json",
success: function (data) {
console.log(data);
var count = [];
var days = [];
for (var i in data) {
days.push(data[i][1]);
count.push(data[i][0]);
}
var chartdata = {
labels: days,
datasets: [
{
label: "By day",
backgroundColor: "#4dc3ff",
borderWidth: 2,
hoverBackgroundColor: "#009",
hoverBorderColor: "#099",
hoverBorderWidth: 5,
data: count
}
]
};
var ctx = $("#daysGraph");
var pieChart = new Chart(ctx, {
type: 'pie',
data: chartdata,
options: {
responsive: true,
legend: {
display: true,
position: 'right',
labels: {
fontColor: "#000"
}
}
}
});
},
error: function (data) {
}
});
graph.html (most of the other html is cut out out, but this on its own doesn't work)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-
scale=1,maximum-scale=7"/>
<title>Hmm</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css"/>
<script src="../../javascript/Chart.min.js" th:href="#{/Chart.min.js}"/>
<script src="../../javascript/js-url-2.5.0/url.js" th:href="#{/url.js}" ></script >
<link rel="stylesheet" href="../static/css/graph.css" th:href="#{/css/graph.css}"/>
<script src="../../javascript/myGraph.js" th:href="#{/myGraph.js}" />
</head>
<body>
<div class="chart-container">
<canvas id="daysGraph" ></canvas>
</div>
</body>
</html>
graph.css
.chart-container {
position: relative;
margin: auto;
height: 80vh;
width: 80vw;
}
On the live server it looks like this
Any advice is welcome

Turned out there was something wrong with the api-route on mobile vs dektop relating to localhost. Therefore my data wasn't being fetched from the api and thus wouldn't populate the chart which is why it is not displaying on mobile. In other words, I was looking in the wrong places for an answer.

I've had similar problems myself, I resolved my issue by playing around with the borderWidth in order to get it working across all devices. Try changing it several times to see if it has any impact at all.

Related

Remove/Hide/Delete an array of features from Azure Map

This is a follow on questions from Azure Maps (with Turf.js) - can't get the 'points' from the SymbolLayer
I now have a function that loads the data "onclick" from a JSON location.
What I am trying to do is now Remove the data. A toggle-on / toggle-off function.
Full code is: https://espiritos.projectwebdev.co.uk/index-v1.0.8a.html
But the key function I am concerned with is:
$(document).on("click", ".toggleCounty.on", function(event) {
if ($(this).hasClass("on")) {
var elementsToRemove = "";
$(this).toggleClass("off");
$(this).toggleClass("on");
var countyID = $(this).attr("data-countyID");
var url = geoJsonData[countyID].url;
$.getJSON(url, function(data) {
var noOfFeatures = Object.keys(data.features).length;
console.log(noOfFeatures);
for (var i = 0; i < noOfFeatures; i++) {
console.log("[" + i + "]");
console.log(data.features[i]);
datasource.remove(data.features[i]);
// map.data.remove(data.features[i]);
debugger;
}
});
}
});
I do an API lookup from a variable "geoJsonData":
var geoJsonData = [
{
name: "Hertfordshire",
shown: "off",
url:
"https://www.naptan.projectwebdev.co.uk/api/v1/get/stoppoints?fields=all&format=geojson&filter=NptgLocalityRef|E0000708",
geomType: "points"
},
{
name: "Hampshire",
shown: "off",
url:
"https://www.naptan.projectwebdev.co.uk/api/v1/get/stoppoints?fields=all&format=geojson&filter=NptgLocalityRef|E0044074",
geomType: "points"
},
{
name: "Oxfordshire",
shown: "off",
url:
"https://www.naptan.projectwebdev.co.uk/api/v1/get/stoppoints?fields=all&format=geojson&filter=NptgLocalityRef|E0000708",
geomType: "points"
}
];
I load in the "features" from the JSON...
And then I just can't work out how to remove those features from the map.
I have popped a "debugger;" where the issue is.
I am extremely grateful for any guidance. Thank you.
It looks like in the first code block you are loading GeoJSON data from the web and then trying to remove it from a data source, but you never add it to the data source. That won't work. If you previously loaded the same data into the data source, I don't think that would work either since these are fundamentally different JSON object instances. The one way you can get this to work is to add unique ID's to each feature in your GeoJSON files. Then the data source can do a match on that. For example:
{
"type": "Feature",
"id": 1,
"geometry": {
"type": "Point",
"coordinates": [-0.61827807482, 51.537852954]
},
"properties": {
"AtcoCode": "040000000127",
"NaptanCode": "bucgjtjt",
"CommonName": "Farnham House",
"Street": "Farnham Lane",
"Indicator": "adj",
"NptgLocalityRef": "E0044074",
"GridType": "UKOS",
"Easting": "495927",
"Northing": "183010",
"StopType": "BCT",
"BusStopType": "MKD",
"TimingStatus": "OTH",
"CompassPoint": "SE",
"Degrees": "135",
"StopAreaRef": "",
"AdministrativeAreaRef": "70"
}
},
Note you will need to make sure all id's across all your files are unique.
Some other approaches you can consider;
If there is a property in each feature that can relate a feature back to its original file (i.e. "source": "Hertfordshire"), then you could us a filter in the layers to control what is rendered based on this property.
If you only ever load the data for one file at a time and remove all other data, then consider using the datasource.setShapes function. This will overwrite all data in the data source.
Update
I have put together a more in depth sample that shows toggling between different data sets. It looks like these data sets are small, so I load them the first time they are needed into the data source and leave them in there. Then to toggle I simply use a filter at the layer level. This makes it much faster to reload a data set a second time. This will work fine for data sets that are tens of thousands in size. If you are working with larger data sets, then removing the data might be something to consider. I also noticed when putting this sample together, the URL for the first and last data set you provided are the same (assuming this is an error).
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
<link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" type="text/css" />
<script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>
<script type='text/javascript'>
var map, datasource, layer;
var geoJsonData = [
{
name: "Hertfordshire",
shown: "off",
url:
"https://www.naptan.projectwebdev.co.uk/api/v1/get/stoppoints?fields=all&format=geojson&filter=NptgLocalityRef|E0000708",
geomType: "points"
},
{
name: "Hampshire",
shown: "off",
url:
"https://www.naptan.projectwebdev.co.uk/api/v1/get/stoppoints?fields=all&format=geojson&filter=NptgLocalityRef|E0044074",
geomType: "points"
},
{
name: "Oxfordshire",
shown: "off",
url:
"https://www.naptan.projectwebdev.co.uk/api/v1/get/stoppoints?fields=all&format=geojson&filter=NptgLocalityRef|E0000708",
geomType: "points"
}
];
function GetMap() {
//Initialize a map instance.
map = new atlas.Map('myMap', {
center: [-0.76, 51.6],
zoom: 10,
view: 'Auto',
//Add your Azure Maps key to the map SDK. Get an Azure Maps key at https://azure.com/maps. NOTE: The primary key should be used as the key.
authOptions: {
authType: 'subscriptionKey',
subscriptionKey: '<Your Azure Maps Key>'
}
});
//Wait until the map resources are ready.
map.events.add('ready', function () {
//Create a data source and add it to the map.
datasource = new atlas.source.DataSource();
map.sources.add(datasource);
//Create a layer to render the points.
layer = new atlas.layer.BubbleLayer(datasource);
map.layers.add(layer);
loadDataSetPanel();
});
}
function loadDataSetPanel(){
var html = [];
for(var i=0;i<geoJsonData.length;i++){
html.push(`<input type="checkbox" onclick="toggleDataSet('${geoJsonData[i].name}')"> ${geoJsonData[i].name}<br/>`);
}
document.getElementById('dataSetPanel').innerHTML = html.join('');
}
function toggleDataSet(name){
var ds = getDataSetByName(name);
if(ds){
//Toggle the shown state of the data set.
ds.show = (typeof ds.show === undefined)? true : !ds.show;
//Update filter on data source.
updateFilter();
//Check to see if data set is loaded, if not, load it.
if(!ds.loaded){
loadDataSet(ds);
}
}
}
function loadDataSet(ds) {
//Fetch the data directly as we want to modify it before it is entered into the data source.
fetch(ds.url)
.then(r => r.json())
.then(r => {
var f = r.features;
//Enrich the features.
for(var i=0;i<f.length;i++){
//Make the AtcoCode the unique ID for each feature. This will allow for lookups and removal by ID.
f[i].id = f[i].properties.AtcoCode;
//Add the data set name as a property for each feature for filtering purposes.
f[i].properties.dataset = ds.name;
}
//Add the features to the data source.
datasource.add(f);
});
}
function updateFilter(){
var dataSetsToShow = [];
//Get the name of each data set that should be shown.
for(var i=0;i<geoJsonData.length;i++){
if(geoJsonData[i].show){
dataSetsToShow.push(geoJsonData[i].name);
}
}
if(dataSetsToShow.length === 0){
//If there is no layers to show, set filter to false.
layer.setOptions({ filter: ['literal', false] });
} else {
//Create a data driven filter to hide the
var filter = [
'match',
//Get the data set property.
['get', 'dataset'],
//See if it matches any of the data set names to show.
dataSetsToShow,
//If so, return true, to indicate the feature should be shown.
true,
//Else, return false to not render feature.
false
];
//Update the filter on the layer.
layer.setOptions({ filter: filter });
}
}
function getDataSetByName(name){
for(var i=0;i<geoJsonData.length;i++){
if(geoJsonData[i].name === name){
return geoJsonData[i];
}
}
return null;
}
</script>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#myMap {
position: relative;
width: 100%;
height: 100%;
}
#dataSetPanel {
position: absolute;
left:10px;
top:10px;
background-color: white;
padding: 10px;
border-radius: 5px;
}
</style>
</head>
<body onload="GetMap()">
<div id="myMap"></div>
<div id="dataSetPanel"></div>
</body>
</html>

Problem Rendering Multiple CanvasJS charts on one page

I have a page (stats.php) that calls php pages containing canvasjs scripts for rendering charts on them. I can get the pages (top_airlines.php and top_aircraft.php) that contain each individual chart to render, however, when I try to get them on the single stats.php page only one of them will actually render.
It utilizes JSON, which I am not at all familiar with and am using the example that was given to me by their help desk a couple of years ago. I've attempted to modify the code so that it should, in theory, load the chart. Again, they'll load on their independent pages it's just when I try to call them on a single page together that all code breaks loose.
I am curious to think that maybe it is related to the javascript code for
$(document).ready(function ()`
TOP AIRLINES (top_airlines.php)
<script src=”http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js”></script>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js”></script>
<script src=”http://globe-trekking.com/vg/includes/js/jquery.canvasjs.min.js”></script>
<script type=”text/javascript”>
window.onload = function () {
CanvasJS.addColorSet(“blueShades2”,
[//colorSet Array
“#074b83”,
“#085a9d”,
“#0a69b7”,
“#0b78d1”,
“#0c87eb”,
“#2196f3”,
“#4daaf6”,
“#79bff8”,
“#a6d4fa”,
“#d2eafd”
]);
$(document).ready(function () {
$.getJSON(“http://globe-trekking.com/vg/charts/top_airlines_data.php”, function (result) {
var chartAirlines = new CanvasJS.Chart(“top_10_airlines_chart”, {
animationEnabled: false,
colorSet: “blueShades2”,
toolTip:{content: “{name}”},
data: [
{
type: “bar”,
indexLabelFontSize: 22,
dataPoints: result
}
]
});
chartAirlines.render();
});
});
}
</script>
<div id=”top_10_airlines_chart” style=”width: 100%; height: 300px;”></div>
TOP AIRCRAFT (top_aircraft.php)
<script src=”http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js”></script>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js”></script>
<script src=”http://globe-trekking.com/vg/includes/js/jquery.canvasjs.min.js”></script>
<script type=”text/javascript”>
window.onload = function () {
CanvasJS.addColorSet(“blueShades”,
[//colorSet Array
“#074b83”,
“#085a9d”,
“#0a69b7”,
“#0b78d1”,
“#0c87eb”,
“#2196f3”,
“#4daaf6”,
“#79bff8”,
“#a6d4fa”,
“#d2eafd”
]);
$(document).ready(function () {
$.getJSON(“http://globe-trekking.com/vg/charts/top_aircraft_data.php”, function (result) {
var chartAircraft = new CanvasJS.Chart(“top_10_airplanes_chart”, {
animationEnabled: false,
colorSet: “blueShades”,
toolTip:{content: “{name}”},
data: [
{
type: “bar”,
indexLabelFontSize: 22,
dataPoints: result
}
]
});
chartAircraft.render();
});
});
}
</script>
<div id=”top_10_airplanes_chart” style=”width: 100%; height: 300px;”></div>
I'm calling them on the stats.php page by using the following located in a certain location on the stats.php page.
The issue seems to happen due to overriding of window.onload event. Changing it to jQuery ready / load should work fine in your case. Here is the sample project.
You can also try out the ways suggested in this stackoverflow thread.
$(document).ready(function () {
CanvasJS.addColorSet(“blueShades”,
[//colorSet Array
“#074b83”,
“#085a9d”,
“#0a69b7”,
“#0b78d1”,
“#0c87eb”,
“#2196f3”,
“#4daaf6”,
“#79bff8”,
“#a6d4fa”,
“#d2eafd”
]);
});
PS: dataPoints are hardcoded in sample-project.

use specific kendo chart library files instead of kendo.all.min.js

I am using Kendo chart in my application, for this "kendo.all.min.js" is used
but size of this file is 2.5 MB, and i want to optimize the speed performance of application therfore i want only specific kendo chart library,
I am using following library instead of kendo.all.min.js.
<script src="scripts/kendo/kendo.core.min.js"></script>
<script src="scripts/kendo/kendo.data.min.js"></script>
<script src="scripts/kendo/kendo.userevents.min.js"></script>
<script src="scripts/kendo/kendo.color.min.js"></script>
<script src="scripts/kendo/kendo.pdf.min.js"></script>
<script src="scripts/kendo/kendo.drawing.min.js"></script>
<script src="scripts/kendo/kendo.dataviz.core.min.js"></script>
<script src="scripts/kendo/kendo.dataviz.themes.min.js"></script>
<script src="scripts/kendo/kendo.dataviz.chart.min.js"></script>
<script src="scripts/kendo/kendo.dataviz.chart.polar.min.js"></script>
<script src="scripts/kendo/kendo.dataviz.chart.funnel.min.js"></script>
And When i run application it throws error "Origin are not define"
$("#chart").kendoChart({
legend: {
position: "bottom",
item: {
visual: createLegendItem
}
},
seriesDefaults: {
type: "line",
style: "smooth",
visual: function (e) {
return createColumn(e.rect, "#6e6e78");//#0099CC")// e.options.color);
}
},
series: [{
type: "column",
data: a,b,c,d,
labels: {
visible: true,
position: "outsideEnd",
visual: function (e) {
console.log(e);
var rect;
//if (checkScreenSize()) {
// rect = new kendo.geometry.Rect(
// [e.rect.origin.x, e.rect.origin.y], // Position of the top left corner
// [25,25] // Size of the rectangle
// );
//} else {
rect = new kendo.geometry.Rect(
[e.rect.origin.x - position, e.rect.origin.y], // Position of the top left corner
[size, size] // Size of the rectangle
);....
Please let me know where i am wrong and which files i missed to include.
use kendo.custom.min.js file before above files, it will work

jqPlot EnhancedLegendRenderer plugin does not toggle series for Pie charts

I use EnhancedLegendRenderer plugin for my jqPlot charts, however I am unable to get it working for Pie charts.
If I click on the legend labels, they don't show or hide series. Here is jsFiddle example.
legend: {
renderer: $.jqplot.EnhancedLegendRenderer,
rendererOptions: {
numberColumns: 3,
seriesToggle: true
},
show: true
}
Has anybody came across and found a solution?
The answer of Merrily somehow correct, ZingChart looks good and have such functionality out of the box, but jqPlot is free and open source.
I rewrote jqPlot Pie Chart plugins and now the Pie chart from your example will work. Here is my blog post with explanation what I changed.
Download these 2 files:
extendedPieRenderer.js (it replaces jqplot.pieRenderer.js)
enhancedPieLegendRenderer.js (it replaces
jqplot.enhancedLegendRenderer.js)
And use them like this code:
<script type="text/javascript" src="jquery.jqplot.js"></script>
<script type="text/javascript" src="extendedPieRenderer.js"></script>
<script type="text/javascript" src="enhancedPieLegendRenderer.js"></script>
<script type="text/javascript">
...
var plot = $.jqplot('chart', data, {
seriesDefaults: {
renderer: $.jqplot.PieRenderer
},
legend: {
renderer: $.jqplot.EnhancedPieLegendRenderer
}
});
...
</script>
I also created this jsFiddle which you can open and verify that showing and hiding works: http://jsfiddle.net/19vzL5h2/1/
I'm not sure how tied to jqPlot you are, but many libraries have this sort of option baked in. Highcharts has it (and is free in most cases if that is your concern) and I saw it in AmCharts recently too.
It is also available through the ZingChart JavaScript charting library. I've made a demo with the toggle legend for you to try.
<html>
<head>
<script src="https://blog.pint.com/include_files/zingchart-html5-min.js"></script>
<script src="http://cdn.zingchart.com/zingchart-core.min.js"></script>
<script>zingchart.MODULESDIR="http://cdn.zingchart.com/modules/";</script>
<meta charset="utf-8">
<title>Pie chart with legend</title>
</head>
<div id="zc"></div>
<script>
var piedemo ={
"type":"pie",
"plot":{
"value-box":{
"text":"%v"
}
},
"series":[
{
"text":"Apples",
"values":[5]
},
{
"text":"Oranges",
"values":[8]
},
{
"text":"Bananas",
"values":[22]
},
{
"text":"Grapes",
"values":[16]
}
],
"legend":{
"header":{
"text":"Click an item to toggle"
},
"layout":"x4",
"marker":{
"type":"circle",
"size":4,
"border-color":"#333"
}
}
};
zingchart.render({
id: 'zc',
data: piedemo,
height: 400,
width: 400
});
</script>
</body>
</html>
I'm on the ZingChart team so if you have any questions on this demo, please feel free to reach out.

Using Flot with multiple bars with orderbars.js and categories

I am having trouble creating multiple bars with flot. There is a plugin that can be downloaded here: http://www.benjaminbuffet.com/public/js/jquery.flot.orderBars.js that makes graphs with multiple bars per x category like this: http://www.pikemere.co.uk/blog/tutorial-flot-how-to-create-bar-charts/ (see under the customized bar charts). However, his example is a bit different in that it uses the time function rather than categories.
Here is my code:
<!doctype html>
<head>
<script language="javascript" type="text/javascript" src="/flot/jquery.js"></script>
<script language="javascript" type="text/javascript" src="/flot/jquery.flot.js"> </script>
<script language="javascript" type="text/javascript" src="/flot/jquery.flot.categories.js"></script>
<script language="javascript" type="text/javascript" src="/flot/jquery.flot.orderBars.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var data1 = [
{
label: "Male" ,
data: [["True", 1],["False", 2]] ,
bars: {
show: true,
barWidth: 0.13,
order: 1
}
},
{
label: "Female" ,
data: [["True", 3],["False", 4]],
bars: {
show: true,
barWidth: 0.13,
order: 2
}
}
];
$.plot($("#placeholder"), data1, {
xaxis: {
mode: "categories"
},
});
});
</script>
<title>Test</title>
</head>
<body>
<div id="placeholder" style="width:600px;height:300px"></div>
</body>
</html>
With the above code, the graph displays, but without any bars. If I remove the order:1 and order:2, it displays correctly, except with the bars overlapping each other rather than being offset by each other (I think it just ignores the orderbars plugin).
This is a very simplified example of what I really want to do, but if someone knows how I can get it to do what I want fairly simply, I would be very much appreciative.
To sum up, what I want is to have two sets of two bars. The first set with "True" under them and the second second set with "False" under them. I do not want to use numbers to represent the values, if possible as it will greatly complicate my more complex situation. But if I must, I would still like to know how to do it that way.
change the function getAxeMinMaxValues in orderBars.js
function getAxeMinMaxValues(series, AxeIdx) {
var minMaxValues = new Array();
for (var i = 0; i < series.length; i++) {
series[i].data[series[i].data.length - 1][AxeIdx];
minMaxValues[0] = 0;
minMaxValues[1] = series[i].data.length - 1;
}
return minMaxValues;
}
hope this will help

Categories

Resources