Is it possible to draw a target line on pie chart?
For example, goal is 90% but installed is 70% and not-installed is 30%. I need to show the goal as dotted line as shown in the image below.
You can use the render event of the grid to draw the dotted line on the chart. I would use the visual property of the series to get the center point and radius of the pie, then in the render, draw a path from the pie center at the correct angle.
WORKING DEMO
var center;
var radius;
$("#chart").kendoChart({
theme: "Bootstrap",
legend: {visible: true,position: "bottom"},
seriesDefaults: { labels: {visible: false, }},
series: [{
type: "pie",
data: [{
category: "Installed",value: 45,color: "#52B84D"
}, {
category: "Not Installed",value: 25,color: "#E64F49"
}],
visual: function(e) {
//use this function to get the center and radius
//for use in the render function
center = e.center;
radius = e.radius;
// return the default visual element
return e.createVisual();
},
}],
render: function(e){
var draw = kendo.drawing;
var geom = kendo.geometry;
var chart = e.sender;
//angle is 90% of 270 because 0 is horizontal
var cornerRad = (0.9 * 270) * Math.PI / 180;
var nx = Math.cos(cornerRad)*radius + center.x;
var ny = Math.sin(cornerRad)*radius + center.y;
// The center and radius are populated by now.
var path = new draw.Path({
stroke: {
color: "#000",
width: 2,
dashType: "dash"
}
});
path.moveTo(center).lineTo(nx, ny, 0).close();
// Draw it on the Chart drawing surface
chart.surface.draw(path);
}
});
Related
I built a map with Highmaps that initially zooms in to a lat lon position: see fiddle
Here is my code:
parsed: function() {
var chart = this.chart,
center = chart.fromLatLonToPoint({
lat: 50,
lon: 10
});
setTimeout(function() {
chart.mapZoom(.2, center.x, center.y);
When the map is loaded and when the time slider is started the map zooms in. How can I prevent this animation?
You can disable animation on the chart level and restore it after initial zoom:
chart: {
animation: false,
...
}
chart.update({
chart: {
animation: true
}
});
Live demo: https://jsfiddle.net/BlackLabel/xarg940y/
API Reference: https://api.highcharts.com/highmaps/chart.animation
I use chart.js 2.8.0 to create mainly pie and bar charts. The clickable legend on pie charts is really useful, filtering out unwanted data from the result.
When creating a chart there are two kinds of labels:
* An array of labels on chart level, label 1 corresponding to item 1 in each dataset.
* Dataset labels, one for each dataset.
A pie chart as standard get the chart label array turned into a legend with clickable labels, click on a label and that item is filtered out from the chart.
A bar chart, on the other hand, gets the labels shown below the bar but not clickable. Instead the legend here is made out of the dataset label. If you have more than one dataset, a whole dataset is filtered out if you click on that label.
Since I sometimes have several datasets I can not use the "trick" that consists of putting data item into a separate dataset (that was otherwise the closest to what I wanted that I found in my search, the "extra" clickable legend that would create would work as well). The situation is also that the end user should get a drop-down (or similar) so he, from the same data, can select chart type. So the soultion need to work both for pie and bar charts. the same data and (standard) code creates the two shown charts (except for the colors).
The question is now, as stated in the title: Is it possible to get clickable labels for a bar chart with the same filtering functionality as when the chart is of pie type?
I understand that it isn't doable by just setting some options, it would probably have to be done by creating a plugin, but is it at all doable? if so, any pointers for help?
If not clickable labels, maybe make the bars themselves clickable (with the same result)...?
With a slight change to the fiddle given by https://stackoverflow.com/users/3963330/tob%c3%adas in his answer here: Click events on Pie Charts in Chart.js I get a fiddle that also can handle multiple datasets, and on my second try I managed to hide a segment when I clicked on it. And then I relized that if it wasn't a pie chart there would be no clickable legend to use for unhiding that element - so that's not a solution for my bar charts.
Tried combining a couple of SO questions/answers (generating labels by #GRUNT : Bar labels in Legend) but can't get legend labels for bar charts to filter out segments instead of datasets.
Fiddle: https://jsfiddle.net/tommypeters/24ra6egy/9/
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="canvas"></canvas>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
var myNewChart = new Chart(ctx, {
type: 'bar',
data: {
datasets: [{
data: [300, 50, 100],
backgroundColor: [
"#F7464A",
"#46BFBD",
"#FDB45C"
]
},
{
data: [400, 60, 101],
backgroundColor: [
"#F7464A",
"#46BFBD",
"#FDB45C"
]
}
],
labels: [
"Red",
"Green",
"Yellow"
]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
legend: {
labels: {
generateLabels: function(chart) {
var labels = chart.data.labels;
var dataset = chart.data.datasets[0];
var legend = labels.map(function(label, index) {
return {
datasetIndex: 0,
fillStyle: dataset.backgroundColor && dataset.backgroundColor[index],
strokeStyle: dataset.borderColor && dataset.borderColor[index],
lineWidth: dataset.borderWidth,
text: label
}
});
return legend;
}
}
}
}
});
canvas.onclick = function(evt) {
var activePoints = myNewChart.getElementsAtEvent(evt);
if (activePoints[0]) {
var chartData = activePoints[0]['_chart'].config.data;
var idx = activePoints[0]['_index'];
var dIndex = myNewChart.getDatasetAtEvent(evt)[0]._datasetIndex;
var label = chartData.labels[idx];
var value = chartData.datasets[dIndex].data[idx];
// Doesn't hide a slice but a whole dataset...
// var meta = myNewChart.getDatasetMeta(dIndex);
// meta.hidden = meta.hidden === null ? !myNewChart.data.datasets[dIndex].hidden : null;
// myNewChart.update();
var i, ilen, meta;
for (i = 0, ilen = (myNewChart.data.datasets || []).length; i < ilen; ++i) {
meta = myNewChart.getDatasetMeta(i);
if (meta.data[idx]) {
meta.data[idx].hidden = !meta.data[idx].hidden;
}
}
myNewChart.update();
var url = "http://example.com/?label=" + label + "&value=" + value;
console.log(url);
alert(url);
}
}
I am working on a Gantt-like task display using HTML5 canvas, and the Konvajs canvas library.
Deconstructing a Gantt chart into its components leads me currently to a view as below. Here 1 is the list of tasks, 2 is the task bar area, 3 is a task bar, and 4 is a text cell.
For this question I am seeking code to construct 1 & 4. The data to be displayed will be delivered in plain JS objects with a nested list of tasks where each task has a number, name, assigned-to person name, start date, end date, days duration, and % complete.
So the requirement is to be able to construct a spreadsheet-like panel such as is seen on the left hand side of a Gantt chart.
I have something part developed which I shall post as an answer. However this seems like such as common need that I am hoping there is someone out there with code they can cut & paste into SO to lead the way.
Note: Gantt in sample image is from Vertex42.
So here is my own fumbling attempt at an approach. Can anyone improve upon it or am I going down the wrong road.
EDIT: I now have a fledgling component for drawing text into the spreadsheet-like cells, including the percent complete shading. To keep this answer uncluttered, this additional component is in this codepen.
// this is the object that defines our grid
//
var gridData = { name: 'grid1', width: 350, height: 400, rowHeight: 24, padding: 4, fill: 'azure', gridLineColor: '#ccc', header: {size: 16, fill: 'black', color: 'white' }, data: {size: 16, fill: 'azure', color: 'black' },
row: [
{ cells: // row 1
[
{ width: 50, text: 'Item', style: 'header'},
{ width: 240, text: 'Name', style: 'header'},
{ width: 60, text: 'Status', style: 'header'},
]
},
{ cells: // row 2
[
{ text: '1'},
{ text: 'Find tea pot'},
{ text: '100%'},
]
},
{ cells: // row 3
[
{ text: '2'},
{ text: 'Boil water'},
{ text: '60%'},
]
}
]
}
// From here on could be wrapped into a component that churns out grids. Maybe you pass in the id of the stage container
// and the data model you want to produce, etc.
// Set up the canvas / stage
var stage = new Konva.Stage({container: 'container1', width: 600, height: 300});
// Add a layer
var layer = new Konva.Layer({draggable: false});
stage.add(layer);
// make a main group for the grid, call it a panel. Assigning a name may be handy later
var panel = new Konva.Group({name: gridData.name});
layer.add(panel); // Add the panel to the layer
// a group has no visual properties. Add a background rect to hold the colour fill
var panelRect = new Konva.Rect({width: gridData.width, height: gridData.height, fill: gridData.fill})
panel.add(panelRect);
var topLeft = {x: 0, y: 0}; // Since we are drawing a grid, we need to compute the position of each cell
for (var i = 0; i < gridData.row.length; i = i + 1){
topLeft.x = 0; // reset x at start of each row
// iterate for each cell on the row
for (var j = 0; j < gridData.row[i].cells.length; j = j + 1){
var cell = new Konva.Rect({name: 'cellBg', // assign a name for later searching
x: topLeft.x, y: topLeft.y, // position as computed
width: gridData.row[0].cells[j].width, // use the first row from celldate to get the cell width
height: gridData.rowHeight, // grid has a uniform row height
stroke: gridData.gridLineColor, // and line colour
strokeWidth: 1, // use a set line width but you can add to the gridData object as needed.
fill: (i === 0 ? gridData.header.fill : gridData.data.fill), // use the given header text color
});
panel.add(cell);
// Add text to the cell. Note that if you wanted to be using alignments you would need to draw the text off-screen and
// get width/height of that text then use those values for positioning calculations. Once you have the rect size of the
// text, all the alignments are simple math.
var text = new Konva.Text({ x: topLeft.x + gridData.padding, // add padding to locate the text nicely into the cell
y: topLeft.y + gridData.padding,
// use the given text size
fontSize: (i === 0 ? gridData.header.size : gridData.data.size),
// use the given header text color
fill: (i === 0 ? gridData.header.color : gridData.data.color),
text: gridData.row[i].cells[j].text, // set the text value.
listening: false // stop text interfering with mouse events
});
panel.add(text);
cell.on('mouseover', function(evt){
var shape = evt.target;
$(shape).data('bgColor', shape.fill());
shape.fill('lime');
layer.draw();
})
cell.on('mouseout', function(evt){
var shape = evt.target;
shape.fill($(shape).data('bgColor'));
layer.draw();
})
topLeft.x = topLeft.x + gridData.row[0].cells[j].width; // offset the computed next cell x value by the width of the cell
}
topLeft.y = topLeft.y + gridData.rowHeight; // offset the computed next cell y value by the height of the row
}
layer.draw();
stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.min.js"></script>
<div id='container1' style="width: 300px, height: 200px; background-color: silver;"></div>
I'm trying to show an icon on the center of a circle.
Here is my code :
jsFiddle : http://jsfiddle.net/61dkv8tr/2/
(function(){
var base64img = "data:image/gif;base64,R0lGODlhPQBEAPeoAJ[...]==";
var extent = [0, 0, 400, 400];
var sourceV = new ol.source.Vector({ wrapX: false });
var map = new ol.Map({
renderer: 'canvas',
target: 'divMap',
layers: [
new ol.layer.Vector({
source: sourceV
})
],
restrictedExtent: extent,
view: new ol.View({
center: ol.extent.getCenter(extent),
extent: extent, //world limit drag map
resolution : 1
})
});
var radius = 50;
var x = 200;
var y = 200;
var circleGeom = new ol.geom.Circle([x, y], radius);
var feature = new ol.Feature(circleGeom);
feature.setStyle(new ol.style.Style ({
stroke: new ol.style.Stroke({
color: 'black',
width: 1
}),
image: new ol.style.Icon({
src: base64img,
color: '#4271AE',
crossOrigin: 'anonymous',
})
}));
sourceV.addFeature(feature);
})();
The render is just the stroke of the circle. Do I miss something ?
The icon is a small red bus.
PS : I also tried with a relative URL, an absolute URL, a canvas...
Thanks !
OK I found the solution. style.Icon only works if its property 'geometry' is of type geom.Point (or if the feature owns a point as geometry type).
To get around with any type of geometry I use the method getExtent() to calculate the center of the geometry and I create a new one of type Point.
hFollowing is the code for barclustered jqplot. can anyone please guide me gow to create a highlighted array in following code dynamically
$(document).ready(function(){
// For horizontal bar charts, x an y values must will be "flipped"
// from their vertical bar counterpart.
var plot2 = $.jqplot('chart2', [
[[2,1], [4,2], [6,3], [3,4]],
[[5,1], [1,2], [3,3], [4,4]],
[[4,1], [7,2], [1,3], [2,4]]], {
seriesDefaults: {
renderer:$.jqplot.BarRenderer,
// Show point labels to the right ('e'ast) of each bar.
// edgeTolerance of -15 allows labels flow outside the grid
// up to 15 pixels. If they flow out more than that, they
// will be hidden.
pointLabels: { show: true, location: 'e', edgeTolerance: -15 },
// Rotate the bar shadow as if bar is lit from top right.
shadowAngle: 135,
// Here's where we tell the chart it is oriented horizontally.
rendererOptions: {
barDirection: 'horizontal'
}
},
axes: {
yaxis: {
renderer: $.jqplot.CategoryAxisRenderer
}
}
});
});
You have a problem with your Javascript syntax and algorithm. The loop should looks like :
VData="9,453,470,232|488,378,375,142|365,275,255,434|217,317,479,89";
var a = new Array();
var split_fst = VData.split("|")
for(m=0;m<split_fst.length;m++) {
var split_snd = split_fst[m].split(",");
a[m] = new Array();
for(j=0;j<split_snd.length;j++){
a[m][j]=split_snd[j];
}
}
Your a variable now looks like : `[["9","453","470","232"],["488","378","375","142"],["365","275","255","434"],["217","317","479","89"]]