let myChart = null;
function draw(){
const labels = [];
for (var i = 0; i < stats.length; i++) {
labels.push(legend[i]);
}
const data = {
labels: labels,
datasets: [{
backgroundColor: ['rgb(204,0,0)', 'rgb(241,194,50)', 'rgb(41,134,204)', 'rgb(106,168,79)', 'rgb(255,62,153)'],
data: stats,
}]
};
const config = {
type: 'doughnut',
data: data,
options: {
radius: 200,
hoverOffset: 30,
aspectRatio: 1,
maintainAspectRatio: false,
responsive: false,
}
};
if (myChart !== null) {
myChart.destroy();
}
myChart = new Chart(document.getElementById('defaultCanvas0'), config);
}
When i run this code my chart just keeps flickering and drawing itself over and over again with a new id each time. I need to be able to destroy my original chart before drawing a new one
This is what console.log displays:
Edit:
function HealthStudy() {
//name for the visualisation to appear in the menu bar
this.name = "Health study";
//each visualisation must have a unique ID with no special characters
this.id = "Health-study";
//property to represent whether stats has been loaded
this.loaded = false;
// Preload the stats. This function is called automatically by the
// gallery when a visualisation is added.
this.preload = function () {
var self = this;
this.stats = loadTable(
'./data/health/causes-of-death.csv', 'csv', 'header',
// Callback function to set the value
// this.loaded to true.
function (table) {
self.loaded = true;
});
};
this.setup = function () {
if (!this.loaded) {
console.log('stats not yet loaded');
return;
}
// Create a select DOM element.
this.select = createSelect();
this.select.position(800, 380);
// Fill the options with all country names.
var countries = this.stats.columns;
// First entry is empty.
for (let i = 1; i < countries.length; i++) {
this.select.option(countries[i]);
}
};
this.destroy = function () {
this.select.remove();
};
// Create a new donut object.
this.donut = new Donut(width / 2, height / 2, width * 0.4);
this.draw = function () {
if (!this.loaded) {
console.log('stats not yet loaded');
return;
}
// Get the value of the country we're interested in from the
// select item.
var country = this.select.value();
// Get the column of raw stats for country.
var col = this.stats.getColumn(country);
// Convert all stats strings to numbers.
col = stringsToNumbers(col);
// Copy the row labels from the table (the first item of each row).
var legend = this.stats.getColumn(0);
// Colour to use for each category.
var colours = ['#CC0000', '#55a5f2', '#4dff00', '#f4e410', '#6a329f'];
// Make a title.
var title = 'Top 5 causes of death in ' + country;
// Draw the Donut!
this.donut.draw(col, legend, colours, title);
Related
I'm putting a chart in the popup of my points. On the initial click, the popups display the chart and if I use the forward navigation arrow on the popup, the next chart renders. However, if I use the back arrow the chart will not render again. The method that generates the charts doesn't seem to be firing the second time. Any help is appreciated. My code is below. Popup:
popupTemplate: {
title: "{Location}",
content: [{
type: "text",
text: "Sample Location: {Type} </br> Survey: {Survey} </br> {Location:getSurveyInfo} <div id='chartDiv'><canvas id='chartArea{Location}'>{Location:createChart}</canvas></div>"
},
{
type: "attachments"
}]
}
Chart:
createChart = function (location) {
var date = new Date();
var chartArea = "chartArea" + location;
var sub = location.substring(0, 2);
getData(date.getFullYear(), [[location]], function (data) {
var maxScale = Math.max(...data[0].tData);
var colors = [["#34eb58", "#0000ff"]];
var chartData = buildChartData(name, maxScale, data, colors, null);
var ctx = document.getElementById(chartArea);
var chart = new Chart(ctx, chartData);
}, getChartDataError)
}
I think you should not use the text content type for the popuptemplate. Using a function that return HTML element would be easier in your case.
popupTemplate: {
title: "{Location}",
content: createPopup
}
here your construct your popupElement and insert chart in it:
function createPopup(graphic) {
try {
var location = graphic.getAttribute("Location");
var popupElement = document.createElement("div");
popupElement.innerHTML = "Sample Location: " + graphic.getAttribute("Type") + "</br> Survey: " + graphic.getAttribute("Survey") + "</br>" + getSurveyInfo(location) + "<div class='chartDiv'></div>"
var date = new Date();
var sub = location.substring(0, 2);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
getData(date.getFullYear(), [[location]], function(data) {
var maxScale = Math.max(...data[0].tData);
var colors = [["#34eb58", "#0000ff"]];
var chartData = buildChartData(name, maxScale, data, colors, null);
var chart = new Chart(ctx, chartData);
}, getChartDataError);
popupElement.querySelector(".chartDiv").appendChild(canvas);
return popupElement;
} catch(error) {
console.error(error)
}
}
See working demo: http://jsfiddle.net/bspa906m/3/
I am using the highstock charts library for a set of 4 charts. I used the options object and literal notation as described HERE. The 4 charts have the same set of options by default (the renderCharts() function in the code bellow is responsible for that) and there is a charts type picker (the setChatType() function ) with the help of which the user can change the chart type.
See all togheter HERE.
Can anyone please tell me the cause of and solution for this error in the console:
"Uncaught TypeError: Cannot read property 'addPoint' of undefined"?
Thank you!
/* ============ CHARTS OPTIONS BEGIN ============ */
var options = {
chart : {
zoomType: 'x',
events : {
load : function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime();
var y = Math.round(Math.random() * 100);
series.addPoint([x, y]);
}, 1000);
}
}
},
rangeSelector: {
buttons: [{
count: 1,
type: 'minute',
text: '1M'
}, {
count: 5,
type: 'minute',
text: '5M'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: false,
selected: 0
},
title : {
text: null
},
exporting: {
enabled: false
},
// Disable navigator
navigator : {
enabled : false
},
series : [{
name : '',
data : (function () {
// generate an array of random data
var data = [],
time = (new Date()).getTime(), i;
for (i = -999; i <= 0; i = i + 1) {
data.push([
time + i * 1000,
Math.round(Math.random() * 100)
]);
}
return data;
}())
}]
}
/* ============ CHARTS OPTIONS END ============ */
/* ============ DRAW CHARTS BEGIN ============ */
function renderCharts(){
$('div.chart-container').each(function(){
var chartId = $(this).attr('id');
var chartIdParts = chartId.split('-');
var chartIdentifier = chartIdParts[1];
//Set chart options dinamically
var chartId = "chart" + chartIdentifier;
var chart = $('#' + chartId);
var renderTo = "chartcontainer-" + chartIdentifier;
//Render Charts for each aech container
options.chart.renderTo = renderTo;
options.chart.type = 'line';
var chart = new Highcharts.StockChart(options);
});
}
function setChatType(){
// Show types list (piker)
$('.current-type').on('click', function(){
$(this).parents('div.chart-options').find('ul.type ul').addClass('clicked');
});
$('.chart-options ul ul li a').on('click', function(){
//Get piked chart type
var type = $(this).parent('li').attr('data-chart-type');
// For text and Title Capitalization
var textAndTitle = type.replace(/^[a-z]/, function(m){ return m.toUpperCase() });
// Show piked type in picker
var currSetClass = 'current-type ' + type;
$(this).parents('.chart-options').find('.current-type')
.text(textAndTitle)
.attr({
class : currSetClass,
title: textAndTitle
});
// Then Hide the types list
$('.chart-options ul ul').removeClass('clicked');
//Identify current chart container by ID
var chartCtnId= $(this).parents('div.chart').find('.chart-container').attr('id');
// Render chart again with new type
options.chart.renderTo = chartCtnId;
options.chart.type = type;
var chart = new Highcharts.StockChart(options);
});
}
/* ============ DRAW CHARTS END ============ */
$(document).ready(function(){
$("article.grid:even").addClass('left')
$("article.grid:odd").addClass('right');
// Draw charts
renderCharts();
// Set/change chart type
setChatType();
});
The solution, suggested by Pawel:
instead of
var chart = new Highcharts.StockChart(options);
use
var chart = new Highcharts.StockChart( $.extend(true, {}, options) );
I'm trying to change the default orientation in a space tree but can't figure out where to add:
st.switchPosition("top", "animate", {
onComplete: function() {
alert('completed!');
}
});
So that the tree will start from the top instead of the default of right.
In the examples i've seen, the switchPosition is only used with an event handler, which i do not intend to have.
So in the example (taken from the infovis site:Infovis - spacetree ), where should i add the code (or any code) in order to change the default orientation?
var labelType, useGradients, nativeTextSupport, animate;
(function() {
var ua = navigator.userAgent,
iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i),
typeOfCanvas = typeof HTMLCanvasElement,
nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'),
textSupport = nativeCanvasSupport
&& (typeof document.createElement('canvas').getContext('2d').fillText == 'function');
//I'm setting this based on the fact that ExCanvas provides text support for IE
//and that as of today iPhone/iPad current text support is lame
labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML';
nativeTextSupport = labelType == 'Native';
useGradients = nativeCanvasSupport;
animate = !(iStuff || !nativeCanvasSupport);
})();
var Log = {
elem: false,
write: function(text){
if (!this.elem)
this.elem = document.getElementById('log');
this.elem.innerHTML = text;
this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px';
}
};
function init(){
//init data
var json = {....removed due to space here in the group....}
//end
//A client-side tree generator
var getTree = (function() {
var i = 0;
return function(nodeId, level) {
var subtree = eval('(' + json.replace(/id:\"([a-zA-Z0-9]+)\"/g,
function(all, match) {
return "id:\"" + match + "_" + i + "\""
}) + ')');
$jit.json.prune(subtree, level); i++;
return {
'id': nodeId,
'children': subtree.children
};
};
})();
//Implement a node rendering function called 'nodeline' that plots a straight line
//when contracting or expanding a subtree.
$jit.ST.Plot.NodeTypes.implement({
'nodeline': {
'render': function(node, canvas, animating) {
if(animating === 'expand' || animating === 'contract') {
var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
var width = nconfig.width, height = nconfig.height;
var algnPos = this.getAlignedPos(pos, width, height);
var ctx = canvas.getCtx(), ort = this.config.orientation;
ctx.beginPath();
if(ort == 'left' || ort == 'right') {
ctx.moveTo(algnPos.x, algnPos.y + height / 2);
ctx.lineTo(algnPos.x + width, algnPos.y + height / 2);
} else {
ctx.moveTo(algnPos.x + width / 2, algnPos.y);
ctx.lineTo(algnPos.x + width / 2, algnPos.y + height);
}
ctx.stroke();
}
}
}
});
//init Spacetree
//Create a new ST instance
var st = new $jit.ST({
'injectInto': 'infovis',
//set duration for the animation
duration: 800,
//set animation transition type
transition: $jit.Trans.Quart.easeInOut,
//set distance between node and its children
levelDistance: 50,
//set max levels to show. Useful when used with
//the request method for requesting trees of specific depth
levelsToShow: 2,
//set node and edge styles
//set overridable=true for styling individual
//nodes or edges
Node: {
height: 20,
width: 40,
//use a custom
//node rendering function
type: 'nodeline',
color:'#23A4FF',
lineWidth: 2,
align:"center",
overridable: true
},
Edge: {
type: 'bezier',
lineWidth: 2,
color:'#23A4FF',
overridable: true
},
//Add a request method for requesting on-demand json trees.
//This method gets called when a node
//is clicked and its subtree has a smaller depth
//than the one specified by the levelsToShow parameter.
//In that case a subtree is requested and is added to the dataset.
//This method is asynchronous, so you can make an Ajax request for that
//subtree and then handle it to the onComplete callback.
//Here we just use a client-side tree generator (the getTree function).
request: function(nodeId, level, onComplete) {
var ans = getTree(nodeId, level);
onComplete.onComplete(nodeId, ans);
},
onBeforeCompute: function(node){
Log.write("loading " + node.name);
},
onAfterCompute: function(){
Log.write("done");
},
//This method is called on DOM label creation.
//Use this method to add event handlers and styles to
//your node.
onCreateLabel: function(label, node){
label.id = node.id;
label.innerHTML = node.name;
label.onclick = function(){
st.onClick(node.id);
};
//set label styles
var style = label.style;
style.width = 40 + 'px';
style.height = 17 + 'px';
style.cursor = 'pointer';
style.color = '#fff';
//style.backgroundColor = '#1a1a1a';
style.fontSize = '0.8em';
style.textAlign= 'center';
style.textDecoration = 'underline';
style.paddingTop = '3px';
},
//This method is called right before plotting
//a node. It's useful for changing an individual node
//style properties before plotting it.
//The data properties prefixed with a dollar
//sign will override the global node style properties.
onBeforePlotNode: function(node){
//add some color to the nodes in the path between the
//root node and the selected node.
if (node.selected) {
node.data.$color = "#ff7";
}
else {
delete node.data.$color;
}
},
//This method is called right before plotting
//an edge. It's useful for changing an individual edge
//style properties before plotting it.
//Edge data proprties prefixed with a dollar sign will
//override the Edge global style properties.
onBeforePlotLine: function(adj){
if (adj.nodeFrom.selected && adj.nodeTo.selected) {
adj.data.$color = "#eed";
adj.data.$lineWidth = 3;
}
else {
delete adj.data.$color;
delete adj.data.$lineWidth;
}
}
});
//load json data
st.loadJSON(eval( '(' + json + ')' ));
//compute node positions and layout
st.compute();
//emulate a click on the root node.
st.onClick(st.root);
//end
//Add event handlers to switch spacetree orientation. - Which i do not want...
// function get(id) {
// return document.getElementById(id);
// };
// var top = get('r-top'),
// left = get('r-left'),
// bottom = get('r-bottom'),
// right = get('r-right');
// function changeHandler() {
// if(this.checked) {
// top.disabled = bottom.disabled = right.disabled = left.disabled = true;
// st.switchPosition(this.value, "animate", {
// onComplete: function(){
// top.disabled = bottom.disabled = right.disabled = left.disabled = false;
// }
// });
// }
// };
// top.onchange = left.onchange = bottom.onchange = right.onchange = changeHandler;
//end
}
You can drop in orientation:'top', shortly into the new $jit.ST function, ie:
var st = new $jit.ST({
//id of viz container element
injectInto: 'infovis',
//SET THE TREE TO VERTICAL
orientation:"top",
//set duration for the animation
duration: 800,
Source: https://groups.google.com/forum/#!searchin/javascript-information-visualization-toolkit/top/javascript-information-visualization-toolkit/MhXSXJUmaIk/V5JNwSe359gJ
I have a flot chart that calculates the max Y-axis value based on the last 100 data points and then plots successfully...
BUT
Sometimes, the running total of an ongoing plot (5 second delay with new data point plotted) exceeds the current max limit.
Is there a way to have the Y-axis scale dynamically while plotting new points on the chart?
This is a valid question about how to dynamically scale the Y Axis of the chart if the current Y-axis is exceeded, since the chart is plotted over time with new points being added every 5 seconds, I was asking how to scale the Y-Axis to fit the NEW plot data if it reaches above the current Max Y Axis value..
UPDATE:
here is the code I use (Json returned data) as well as the plot update timer:
The "highY" takes the last 100 datapoints from a database and sets the max value to the highest count + 10%
<script type="text/javascript">
$(function () {
var str1 = [], totalPoints = 300;
var str2 = [], totalPoints = 300;
var pts1 = '';
var pts2 = '';
if (pts1 == "" || pts == null) { pts = '2012-10-02 17:17:02'; }
if (pts2 == "" || pts == null) { pts = '2012-10-02 17:17:02'; }
var maxYaxis = <?PHP echo $highY; ?>;
function getStr1() {
var ts1 = new Date().getTime();
var json1 = (function () {
var json1 = null;
var myURL = '<?PHP echo $updateURL; ?>?s=1&ts=' + ts1;
$.ajax({
'async': false,
'global': false,
'url': myURL,
'dataType': "json",
'success': function (data) {
json1 = data;
}
});
return json1;
})();
var y1 = json1['data']['avgpersec'];
var total_1 = json1['data']['running_total'];
document.getElementById('<?PHP echo $string1; ?>Total').innerHTML = total_1;
if (str1.length > 0) { str1 = str1.slice(1); }
while (str1.length < totalPoints) {
var prev = str1.length > 0 ? str1[str1.length - 1] : 50;
str1.push(y1);
}
// zip the generated y values with the x values
var res = [];
for (var i = 0; i < str1.length; ++i){ res.push([i, str1[i]]) }
return res;
}
function getStr2() {
var ts2 = new Date().getTime();
var json2 = (function () {
var json2 = null;
var myURL = '<?PHP echo $updateURL; ?>?s=2&ts=' + ts2;
$.ajax({
'async': false,
'global': false,
'url': myURL,
'dataType': "json",
'success': function (data) {
json2 = data;
}
});
return json2;
})();
var y2 = json2['data']['avgpersec'];
var total_2 = json2['data']['running_total'];
document.getElementById('<?PHP echo $string2; ?>Total').innerHTML = total_2;
if (str2.length > 0) { str2 = str2.slice(1); }
while (str2.length < totalPoints) {
var prev = str2.length > 0 ? str2[str2.length - 1] : 50;
str2.push(y2);
}
// zip the generated y values with the x values
var res = [];
for (var i = 0; i < str2.length; ++i){ res.push([i, str2[i]]) }
return res;
}
// setup control widget
var updateInterval = 5000;
$("#updateInterval").val(updateInterval).change(function () {
var v = $(this).val();
if (v && !isNaN(+v)) {
updateInterval = +v;
if (updateInterval < 1)
updateInterval = 1;
if (updateInterval > 2000)
updateInterval = 2000;
$(this).val("" + updateInterval);
}
});
// setup plot
var options = {
series: { shadowSize: 0 }, // drawing is faster without shadows
yaxis: { min: 0, max: maxYaxis},
xaxis: { show: false },
colors: ["<?PHP echo $string1Color; ?>","<?PHP echo $string2Color; ?>"]
};
var plot = $.plot($("#placeholder"), [ getStr1(), getStr2() ], options);
function update() {
plot.setData([ getStr1(), getStr2() ]);
plot.draw();
setTimeout(update, updateInterval);
}
update();
});
</script>
What i am hoping to accomplish is to adjust the "$highY" (Y-axis) value real time as i plot new data points so that the chart will scale if the value of the new data plot point exceeds the current "yaxis { max: # }" set in the chart options.
I'm assuming that right now you're using flot.setData and flot.draw?
The simplest solution is just to call $.plot with the new data each time you receive it. At various times, this has been recommended by the authors of the flot plugin as a reasonably efficient way of dealing with this situation. I've used this on graphs that refresh every second and found that it does not use an excessive amount of CPU on the user's computer, even with 3-4 graphs refreshing every second on one page.
EDIT based on the code you added (and your suggested edit), I would change the update function to look like this:
function update() {
var data = [ getStr1(), getStr2() ];
//modify options to set the y max to the new y max
options.yaxis.max = maxYaxis;
$.plot($("#placeholder"), data, options);
setTimeout(update, updateInterval);
}
Additionally, you would add code to getStr and getStr that keep the maxYaxis variable up to date.
I have two control panels; one is for the default draw features, the other for measure tools.
The problem is that since they are in different panels it is possible to run two controls simultaneously, one from each panel. (this didn't seem like much of a problem before, but I noticed that when the measure and default draw tools are activated together, they cancel out the line end function)
What I am trying to do is either place all controls in one panel and toggle them together or toggle the panels(e.g. when control from panel 1 is activated, deactivate all controls in panel 2)
Here is my code:
Default Controls Panel:
OpenLayers.Control.CustomNavToolbar = OpenLayers.Class(OpenLayers.Control.Panel,{
initialize: function(options){
OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
this.addControls([
new OpenLayers.Control.Navigation({displayClass: 'olControlNavigation', zoomBoxEnabled:false}),
new OpenLayers.Control.DrawFeature(vlayer, OpenLayers.Handler.Point, {displayClass: 'olControlDrawPoint'}),
new OpenLayers.Control.DrawFeature(vlayer, OpenLayers.Handler.Path, {displayClass: 'olControlDrawPath'}),
new OpenLayers.Control.DrawFeature(vlayer, OpenLayers.Handler.Polygon, {displayClass: 'olControlDrawPolygon'}),
new OpenLayers.Control.ZoomBox({displayClass: 'olControlZoomBox', alwaysZoom:true})
])
this.displayClass = 'olControlCustomNavToolbar'
},
draw: function(){
var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
this.defaultControl = this.controls[0];
return div;
}
});
var panel = new OpenLayers.Control.CustomNavToolbar({div:OpenLayers.Util.getElement('panel')});
map.addControl(panel);
Measure Controls Panel:
allControls = {
line: new OpenLayers.Control.Measure(OpenLayers.Handler.Path, {
persist: true,
handlerOptions: {
layerOptions: {
renderers: renderer,
styleMap: styleMap
}
},
textNodes: null,
callbacks:{
create:
function(){
this.textNodes = [];
// vlayer.destroyFeatures(vlayer.features);
mouseMovements = 0;
},
modify:
function(point, line){
if(mouseMovements++ < 5){
return;
}
var len = line.geometry.components.length;
var from = line.geometry.components[len -2];
var to = line.geometry.components[len -1];
var ls = new OpenLayers.Geometry.LineString([from, to]);
var dist = this.getBestLength(ls);
if(!dist[0]){
return;
}
var total = this.getBestLength(line.geometry);
var label = dist[0].toFixed(3) + " " + dist[1];
var textNode = this.textNodes[len -2] || null;
if(textNode && !textNode.layer){
this.textNodes.pop();
textNode = null;
}
if(!textNode){
var c = ls.getCentroid();
textNode = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(c.x, c.y), {}, {
label: "",
fontColor: "#800517",
fontSize: "13px",
fontFamily: "Tahoma",
fontWeight: "bold",
labelAlign: "cm"
});
this.textNodes.push(textNode);
vlayer.addFeatures([textNode]);
}
textNode.geometry.x = (from.x + to.x) / 2;
textNode.geometry.y = (from.y + to.y) / 2;
textNode.style.label = label;
textNode.layer.drawFeature(textNode);
this.events.triggerEvent("measuredynamic", {
measure: dist[0],
total: total[0],
units: dist[1],
order: 1,
geometry: ls
});
}
}
}),
polygon: new OpenLayers.Control.Measure(
OpenLayers.Handler.Polygon, {
persist: true,
immediate: true,
handlerOptions: {
layerOptions: {
renderers: renderer,
styleMap: styleMap
}
}
}
)
};
var control;
for(var key in allControls) {
control = allControls[key];
control.events.on({
"measure": handleMeasurements,
"measurepartial": handleMeasurements
});
map.addControl(control);
}
..and just for reference
function handleMeasurements(evt){
var geometry = evt.geometry;
var units = evt.units;
var order = evt.order;
var measure = evt.measure;
var element = document.getElementById('output');
var position = (map.getLonLatPxFromViewPortPx);
var out = "";
if(order == 1){
out += "Distance: " + measure.toFixed(3) + " " + units;
}
else{
out += "Area: " + measure.toFixed(3) + " " + units + "<sup>2</" + "sup>";
}
element.innerHTML = out;
}
function toggleControl(element){
vlayer.destroyFeatures(vlayer.features);
for(key in allControls){
var control = allControls[key];
if(element.value == key && element.click){
control.activate();
var label = document.getElementById('output');
var emptyOut = "";
label.innerHTML = emptyOut;
}
else{
control.deactivate();
}
}
}
Been looking for a way to do this and so far have not been able to find anything useful, if you could help me with some suggestions about how to go about this it would be very appreciated. Thanks
EDIT: I am not using GeoExt and am not considering using it. I'm looking for suggestions about how to go about this using only the OpenLayers 2.11 library. Thanks again.