I am trying to use the pan and zoom functionality in kendo box plot chart, can this be achieved for box plot chart.
http://demos.telerik.com/kendo-ui/bar-charts/pan-and-zoom
You can apply the exact same methods to the boxplot.
DEMO
CODE:
// Minimum/maximum number of visible items
var MIN_SIZE = 6;
var MAX_SIZE = 18;
// Optional sort expression
// var SORT = { field: "val", dir: "asc" };
var SORT = {};
// Minimum distance in px to start dragging
var DRAG_THR = 50;
// State variables
var viewStart = 0;
var viewSize = MIN_SIZE;
var newStart;
// Drag handler
function onDrag(e) {
var chart = e.sender;
var ds = chart.dataSource;
var delta = Math.round(e.originalEvent.x.initialDelta / DRAG_THR);
if (delta != 0) {
newStart = Math.max(0, viewStart - delta);
newStart = Math.min(data.length - viewSize, newStart);
ds.query({
skip: newStart,
page: 0,
pageSize: viewSize,
sort: SORT
});
}
}
function onDragEnd() {
viewStart = newStart;
}
// Zoom handler
function onZoom(e) {
var chart = e.sender;
var ds = chart.dataSource;
viewSize = Math.min(Math.max(viewSize + e.delta, MIN_SIZE), MAX_SIZE);
ds.query({
skip: viewStart,
page: 0,
pageSize: viewSize,
sort: SORT
});
// Prevent document scrolling
e.originalEvent.preventDefault();
}
$("#chart").kendoChart({
dataSource: {
data: data,
pageSize: viewSize,
page: 0,
sort: { }
},
title: {
text: "Ozone Concentration (ppm)"
},
legend: {
visible: false
},
series: [{
type: "boxPlot",
lowerField: "lower",
q1Field: "q1",
medianField: "median",
q3Field: "q3",
upperField: "upper",
meanField: "mean",
outliersField: "outliers"
}],
categoryAxis: {
field: "year",
majorGridLines: {
visible: false
}
},
transitions: false,
drag: onDrag,
dragEnd: onDragEnd,
zoom: onZoom
});
Related
I am using Mapbox GL JS to capture frame by frame video of the animation of a geoJson (similar to what is described here: https://docs.mapbox.com/mapbox-gl-js/example/animate-a-line/).
The strategy for encoding mapbox animations into mp4 here are described here:
https://github.com/mapbox/mapbox-gl-js/issues/5297 and https://github.com/mapbox/mapbox-gl-js/pull/10172 .
I would like to show the distance the polyline has covered as I draw each frame. I need the distance to be in the GL itself (as opposed to, for example, an HTML element on top of the canvas), since that's where I'm capturing the video from.
Can someone help describe a performant strategy for doing this to me?
Aside from tearing apart the Mapbox GL JS, why not simply try drawing directly onto the mapbox canvas?
In this example, there are two canvases, created identically.
With the second, there is another requestAnimationFrame loop that adds an overlay.
I've also shown how it can be recorded with MediaRecorder.
const canvas1 = document.getElementById('canvas1')
const canvas2 = document.getElementById('canvas2')
const video = document.getElementById('output')
const status = document.getElementById('status')
let x = 0 // Value to be displayed
const setupCanvas = (canvas) => {
canvas.height = 300
canvas.width = 300
const canvas1Ctx = canvas.getContext('2d')
canvas1Ctx.fillStyle = 'black'
canvas1Ctx.fillRect(300, 100, 100, 100)
const animateCanvas = () => {
x += 2;
if (x > 300) x = 10
canvas1Ctx.fillStyle = 'rgba(20,20,20,1)'
canvas1Ctx.fillRect(0, 0, canvas.width, canvas.height)
canvas1Ctx.beginPath()
canvas1Ctx.arc(x, 100, 20, 0, 2 * Math.PI)
canvas1Ctx.fillStyle = 'rgba(250,0,0,1)'
canvas1Ctx.fill()
requestAnimationFrame(animateCanvas)
}
animateCanvas()
}
const addOverlay = (canvas) => {
const canvasCtx = canvas2.getContext('2d')
function animateOverlay() {
canvasCtx.font = '48px serif'
canvasCtx.fillStyle = 'white'
canvasCtx.fillText(`X: ${x}`, 10, 50)
requestAnimationFrame(animateOverlay)
}
animateOverlay()
}
const initVideoCapture = () => {
var videoStream = canvas2.captureStream(30)
var mediaRecorder = new MediaRecorder(videoStream)
var chunks = []
mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data)
}
mediaRecorder.onstop = function(e) {
var blob = new Blob(chunks, {
'type': 'video/mp4'
})
chunks = []
var videoURL = URL.createObjectURL(blob)
video.src = videoURL
}
mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data)
}
mediaRecorder.start()
status.textContent = 'Recording...'
setTimeout(function() {
mediaRecorder.stop()
status.textContent = 'Complete'
}, 5000)
}
setupCanvas(canvas1)
setupCanvas(canvas2)
addOverlay(canvas2)
initVideoCapture()
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
<video id="output" autoplay controls></video>
<p>Status: <span id="status">Loading</span></p>
Try this :
// show the distance a polyline has covered while animating it in mapbox gl js
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-74.5, 40],
zoom: 9,
})
var geojson = {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[-74.5, 40],
[-74.45, 40.7],
[-74.36, 40.8],
[-74.36, 41.2],
],
},
}
map.on('load', function () {
map.addLayer({
id: 'route',
type: 'line',
source: {
type: 'geojson',
data: geojson,
},
layout: {
'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': '#888',
'line-width': 8,
},
})
var lineDistance = turf.lineDistance(geojson, 'kilometers')
var arc = []
var maxTravelTime = 0
for (var i = 0; i < lineDistance; i += lineDistance / 200) {
var segment = turf.along(geojson, i, 'kilometers')
arc.push(segment.geometry.coordinates)
}
map.addLayer({
id: 'point',
type: 'symbol',
source: {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: arc[0],
},
},
],
},
},
layout: {
'icon-image': 'car',
'icon-rotate': ['get', 'bearing'],
'icon-rotation-alignment': 'map',
'icon-allow-overlap': true,
'icon-ignore-placement': true,
},
})
function animate() {
map.getSource('point').setData(geojson)
if (maxTravelTime < lineDistance) {
maxTravelTime += lineDistance / 200
} else {
maxTravelTime = 0
}
requestAnimationFrame(animate)
}
animate()
})
I am working with chartjs, I am trying to animate chart from right to left or left to right on load.
var canvas = document.getElementById('chart_canvas');
var ctx = canvas.getContext('2d');
// Generate random data to plot
var DATA_POINT_NUM = 10;
var data = {
labels: [],
datasets: [
{
data: [],
},
]
}
for (var i=0; i<DATA_POINT_NUM; i++) {
data.datasets[0].data.push(Math.random()*10);
data.labels.push(String.fromCharCode(65+i));
}
var oldDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function(animationFraction) {
var animationConfig = this.chart.options.animation;
if (animationConfig.xAxis === true) {
var ctx = this.chart.chart.ctx;
var hShift = (1-animationFraction)*ctx.canvas.width;
ctx.save();
ctx.setTransform(1, 0, 0, 1, hShift,0);
if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
ctx.restore();
} else if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: {
duration: 5000,
xAxis: true,
yAxis: true,
}
}
});
Example 1
The above code works fine on windows, but I'm facing issue on mac devices.While animating from left to right the data displays incorrectly means that the data moves to upward from x axis.How to fix this issue?
I am attaching screenshot.
Screenshot
Please change setTransform to transform.
Try the following code
var canvas = document.getElementById('chart_canvas');
var ctx = canvas.getContext('2d');
// Generate random data to plot
var DATA_POINT_NUM = 10;
var data = {
labels: [],
datasets: [
{
data: [],
},
]
}
for (var i=0; i<DATA_POINT_NUM; i++) {
data.datasets[0].data.push(Math.random()*10);
data.labels.push(String.fromCharCode(65+i));
}
var oldDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function(animationFraction) {
var animationConfig = this.chart.options.animation;
if (animationConfig.xAxis === true) {
var ctx = this.chart.chart.ctx;
var hShift = (1-animationFraction)*ctx.canvas.width;
ctx.save();
ctx.transform(1, 0, 0, 1, hShift,0);
if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
ctx.restore();
} else if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
}
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: {
duration: 5000,
xAxis: true,
yAxis: true,
}
}
});
Trying to display line chart using plotly.js, my data are collected per second. I fed my graph but the result looks strange even if I zoom in, to very low detail where it should be displayed per seconds.
Are there any methods I could use to preprocess the data so it would display well in different scales (as I zoom in and out)?
var gd = document.getElementById('tester');
var layout = {
xaxis: {
showgrid: true,
tickformat: "%H:%M:%S",
},
margin: {
l: 40,
b: 40,
r: 30,
t: 20
},
hovermode: 'x',
};
var draw = function(data, layout) {
Plotly.newPlot(gd, data, layout, {
showLink: false,
displaylogo: false
});
};
var dataurl = 'https://gist.githubusercontent.com/fhurta/6c53839fbc91a363d62966a972a5e4a2/raw/2cd735f0b024e496164dacec92fa4a7abcd5da2e/series.csv';
Plotly.d3.csv(dataurl, function(rows) {
var data = [{
type: 'scatter',
x: rows.map(function(row) {
return new Date(row['Time']);
}),
y: rows.map(function(row) {
return row['Value1'];
}),
line: {
width: 1
}
}];
draw(data, layout);
});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="tester" style="width:600px;height:300px;"></div>
I made a Highstock diagramm and got aproblem with zooming on the yAxis.
I have a Button and 2 textfield to get the wanted min/max values for the axis. With min:0, max: 100 it works well. With min:0, max:80 it doesn't (max will still be 100 in the Diagramm).
If I use the mouse for zooming it works well (even a min of: 3.7 and a max of 3.894 is possible). But using the mouse is not an Option, because in the later Diagramm there will be 3 yAxes with individual zoom.
$(function () {
var seriesOptions = [],
seriesCounter = 0,
names = ['MSFT', 'AAPL', 'GOOG'];
/**
* Create the chart when all data is loaded
* #returns {undefined}
*/
function createChart() {
$('#container').highcharts('StockChart', {
rangeSelector: {
selected: 4
},
chart:{
zoomType: 'xy'
},
yAxis: [
{
labels: {
format: '{value}',
},
height: '100%',
opposite: false,
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
],
plotOptions: {
series: {
compare: 'percent'
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
valueDecimals: 2
},
series: seriesOptions
},
function(chart){
$('#btn').click(function(){
var min = temp_min.value,
max = temp_max.value;
chart.yAxis[0].setExtremes((min),(max));
});
});
}
$.each(names, function (i, name) {
$.getJSON('https://www.highcharts.com/samples/data/jsonp.php?filename=' + name.toLowerCase() + '-c.json&callback=?', function (data) {
if(seriesCounter==0){
seriesOptions[i] = {
name: name,
data: data,
yAxis: 0
};
} else {
seriesOptions[i] = {
name: name,
data: data,
yAxis: 0
};
}
// As we're loading the data asynchronously, we don't know what order it will arrive. So
// we keep a counter and create the chart when all the data is loaded.
seriesCounter += 1;
if (seriesCounter === names.length) {
createChart();
}
});
});
});
JSFiddle
Another Question: Is it possible to set up a scrollbar for the yAxis as well?
Thanks for your help, Patrick
This is related with fact that tickInterval is not regular, so is rounded to value (like 100). The solution is using tickPositioner which calculates ticks, based on extremes which you define.
tickPositioner: function (min,max) {
var positions = [],
tick = Math.floor(min),
increment = Math.ceil((max - min) / 5);
for (tick; tick - increment <= max; tick += increment) {
positions.push(tick);
}
return positions;
},
http://jsfiddle.net/6s11kcwd/
The scrollbar is supported only for xAxis.
I'm working with a Kendo treelist widget, and disappointed to see there's no rowTemplate option as there is on the Kendo grid.
I see a columnTemplate option (i.e. http://docs.telerik.com/kendo-ui/api/javascript/ui/treelist#configuration-columns.template ), but this will affect the entire column.
However, I need to drill into each cell value and set a css color property based on a ratio ( i.e. If value/benchmark < .2, assign <span style='color:red;'> , but my color value is dynamic.
There's a dataBound: and dataBinding: event on the treelist, but I'm still trying to figure out how to intercept each cell value and set the color once I've done my calculation.
var treeOptions = {
dataSource: ds,
columns: colDefs,
selectable: true,
scrollable: true,
resizable: true,
reorderable: true,
height: 320,
change: function (e) {
// push selected dataItem
var selectedRow = this.select();
var row = this.dataItem(selectedRow);
},
dataBound: function (e) {
console.log("dataBinding");
var ds = e.sender.dataSource.data();
var rows = e.sender.table.find("tr");
}
};
and this is where I'm building out the `colDefs' object (column definitions):
function parseHeatMapColumns(data, dimId) {
// Creates the Column Headers of the heatmap treelist.
// typeId=0 is 1st Dimension; typeId=1 is 2nd Dimension
var column = [];
column.push({
"field": "field0",
"title": "Dimension",
headerAttributes: { style: "font-weight:" + 'bold' + ";" },
attributes : { style: "font-weight: bold;" }
});
var colIdx = 1; // start at column 1 to build col headers for the 2nd dimension grouping
_.each(data, function (item) {
if (item.typeId == dimId) {
// Dimension values are duplicated, so push unique values (i.e. trade types may have dupes, whereas a BkgLocation may not).
var found = _.find(column, { field0: item.field0 });
if (found == undefined) {
column.push({
field: "field2",
title: item.field0,
headerAttributes: {
style: "font-weight:" + 'bold'
}
,template: "<span style='color:red;'>#: field2 #</span>"
});
colIdx++;
}
}
});
return column;
}
**** UPDATE ****
In order to embed some logic within the template :
function configureHeatMapColumnDefs(jsonData, cols, model) {
var colDef = '';
var dimId = 0;
var colorProp;
var columns = kendoGridService.parseHeatMapColumns(jsonData, dimId);
// iterate columns and set color property; NB: columns[0] is the left-most "Dimension" column, so we start from i=1.
for (var i = 1; i <= columns.length-1; i++) {
columns[i]['template'] = function (data) {
var color = 'black';
if (data.field2 < 1000) {
color = 'red';
}
else if (data.field2 < 5000) {
color = 'green';
}
return "<span style='color:" + color + ";'>" + data.field2 + "</span>";
};
}
return columns;
}
Advice is appreciated.
Thanks,
Bob
In the databound event you can iterate through the rows. For each row you can get the dataItem associated with it using the dataitem() method (http://docs.telerik.com/kendo-ui/api/javascript/ui/treelist#methods-dataItem)
Once you have the dataitem, calculate your ration and if the row meets the criteria for color, change the cell DOM element:
dataBound: function (e) {
var that = e.sender;
var rows = e.sender.table.find("tr");
rows.each(function(idx, row){
var dataItem = that.dataItem(row);
var ageCell = $(row).find("td").eq(2);
if (dataItem.Age > 30) {
//mark in red
var ageText = ageCell.text();
ageCell.html('<span style="color:red;">' + ageText + '</span>');
}
}
DEMO
UPDATE: you can also do this with a template:
$("#treelist").kendoTreeList({
dataSource: dataSource,
height: 540,
selectable: true,
columns: [
{ field: "Position"},
{ field: "Name" },
{ field: "Age",
template: "# if ( data.Age > 30 ) { #<span style='color:red;'> #= data.Age # </span> #}else{# #= data.Age # #}#"
}
],
});
DEMO