How can I show the distance a polyline has covered while animating it in Mapbox GL JS? - html5-canvas

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()
})

Related

Phaser 3.17 doesn't setect collision between tilemap layer and player sprite

No collisions between map layer and player sprite. But collisions between world bounds work. What is wrong?
I tried different workarounds that could find online and none of them worked.
Game config
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: "#f",
physics: {
default: 'arcade',
arcade: {
gravity: { y: gameState.gravity },
debug: true
}
},
scene: {
preload,
create,
update
}
};
Code in create() regarding the tilemap and its layers and the character
gameState.map = this.add.tilemap('map');
gameState.dungeonTileset = gameState.map.addTilesetImage('dungeon', 'dungeonTiles');
gameState.backgroundLayer = gameState.map.createStaticLayer('Background', gameState.dungeonTileset);
gameState.mapLayer = gameState.map.createStaticLayer('Map', gameState.dungeonTileset);
gameState.miscLayer = gameState.map.createStaticLayer('Misc', gameState.dungeonTileset);
gameState.mapLayer.setCollisionByExclusion([-1]);
this.physics.world.bounds.width = gameState.mapLayer.width;
this.physics.world.bounds.height = gameState.mapLayer.height;
gameState.player = this.physics.add.sprite(73, 398, 'player', 0);
gameState.player.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player, gameState.mapLayer);
No warning and no errors are coming up in the console. I don't know what to do anymore.
Thanks in advance!
I have addited a little bit the original example so you can see how it looks, I think the problem in your code is the line concerning gameState.mapLayer.setCollisionByExclusion([-1]); but am not sure because I can't see how the tilemap is created
var config = {
type: Phaser.WEBGL,
width: 800,
height: 576,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
loader: {
baseURL: 'https://labs.phaser.io',
crossOrigin: 'anonymous'
},
pixelArt: true,
physics: {
default: 'arcade',
arcade: { gravity: { y: 300 } }
},
scene: {
preload: preload,
create: create,
update: update
}
};
var game = new Phaser.Game(config);
var map;
var cursors;
var player;
var groundLayer;
function preload ()
{
this.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/tile-collision-test.json');
this.load.image('player', 'assets/sprites/phaser-dude.png');
}
function create ()
{
map = this.make.tilemap({ key: 'map' });
var groundTiles = map.addTilesetImage('ground_1x1');
map.createDynamicLayer('Background Layer', groundTiles, 0, 0);
groundLayer = map.createDynamicLayer('Ground Layer', groundTiles, 0, 0);
groundLayer.setCollisionBetween(1, 25);//here
player = this.physics.add.sprite(80, 70, 'player')
.setBounce(0.1);
this.physics.add.collider(player, groundLayer);
cursors = this.input.keyboard.createCursorKeys();
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.startFollow(player);
}
function update ()
{
player.body.setVelocityX(0);
if (cursors.left.isDown)
{
player.body.setVelocityX(-200);
}
else if (cursors.right.isDown)
{
player.body.setVelocityX(200);
}
if (cursors.up.isDown && player.body.onFloor())
{
player.body.setVelocityY(-300);
}
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.17.0/dist/phaser.min.js"></script>

Chartjs animate x-axis displaying data incorrectly in mac device

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,
}
}
});

Marker clustering not clustering on zoom out

I have a dataset of 100000 and i am sending in the patch of 10000 each while fetching the data i am adding the markers and redrawing the cluster so in the end i get a single cluster of 100000. But once i zoom in and try to zoom out again they overlap in patches of 10000 each instead reforming the single cluster of 100000.
var mapDiv = document.getElementById('newmap');
map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(latitude, longitude),
zoom: 3,
panControl: true,
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
},
zoomControl: true,
zoomControlOptions: {
position: google.maps.ControlPosition.LEFT_TOP,
},
streetViewControl: true,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
function addMarker1(locations,outletname,outletData)
{
var infoWindow = new google.maps.InfoWindow();
markers = locations.map(function(location, i)
{
return new google.maps.Marker
({
position: location,
});
});
markerCluster = new MarkerClusterer(map, markers,{imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});
new_arr=new_arr.concat(markers);
markerCluster.clearMarkers()
markerCluster.addMarkers(new_arr);
markerCluster.redraw();
}
// this is sending data 10000 each
for (var i = 0; i < outletDataLen; i++) {
outletArray.push(outletData[i]['Outletview']['name']);
j.push({
lat: parseFloat(outletData[i]['Outletview']['latitude']),
lng: parseFloat(outletData[i]['Outletview']['longitude'])
});
outletname.push(outletData[i]['Outletview']['name']);
}
addMarker1(j, outletname, outletData);
The solution is to clear the data before looping through the markers
if (markerCluster)
{
markerCluster.clearMarkers();
markerCluster.resetViewport();
markers = [];
markerCluster.removeMarker(new_arr);
}

Transform too dense data

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>

Pan and zoom in kendo boxplot chart

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
});

Resources