Carto-VL animation controls not showing up on map - widget

The sample Carto-VL animation control code doesn't appear on my animated map.
I'm creating an animation of tree population change since the last ice age in Carto VL, and I want the user to be able to change the duration and play/pause the animation. I've pasted the code from the example pages into my map.
const $progressRange = document.getElementById('js-progress-range');
const $playButton = document.getElementById('js-play-button');
const $pauseButton = document.getElementById('js-pause-button');
const $durationRange = document.getElementById('js-duration-range');
const $currentTime = document.getElementById('js-current-time');
// Listen to interaction events with the UI
$playButton.addEventListener('click', () => {
viz.variables.animation.play();
});
$pauseButton.addEventListener('click', () => {
viz.variables.animation.pause();
});
$durationRange.addEventListener('change', () => {
viz.variables.duration = parseInt($durationRange.value, 10);
});
// Update progress bar each 100 milliseconds
function updateProgress () {
$progressRange.value = viz.variables.animation.getProgressPct();
$currentTime.innerText = viz.variables.animation.getProgressValue();
}
setInterval(updateProgress, 100);
I expect the animation control box to show up on the map, but it only appears if there's an error in the code causing the map not to appear. Even then, I cannot interact with it.

Related

HTML 5 wait for drawImage to finish in a Vue SPA

I have been trying to debug something for a week and I now suspect the problem is that the drawImage function does not have time to finish. I have a for loop that composes a canvas element by stitching together two different canvas elements and then add that composedCanvas as a frame to a GIF.js object. The problem I keep running into is that the bottom stitched canvas does not appear or partially appears (the picture below started to draw but did not finish) in my output GIF file. My question is how do I ensure synchronous execution of drawImage in the context of a Vue SPA method. I have experimented with Promise, but I have not gotten it to work. Can anyone explain and help me with this, please?
EDIT : I have tried wrapping my drawImage in a promise and await but it raised type errors.
I managed to get it working by properly wrapping the drawImage step in a separate method and inside a promise the proper way. See the code below for an example of two methods that were the culprits but are now fixed.
async composeCanvas( gif , timeStep , visibleLayers , delayInput) {
const mapCnv = this.getMapCanvas();
await this.updateInfoCanvas( timeStep )
const numberVisibleLayers = visibleLayers.length;
const composedCnv = await this.stitchCanvases( mapCnv , numberVisibleLayers );
gif.addFrame(composedCnv, {copy:false, delay: delayInput});
},
async stitchCanvases( mapCanvas , numberVisibleLayers ) {
return new Promise(( resolve ) => {
var composedCnv = document.createElement('canvas');
var ctx = composedCnv.getContext('2d');
var ctx_w = mapCanvas.width;
var ctx_h = mapCanvas.height + ((numberVisibleLayers - 1) * 30) + 40;
composedCnv.width = ctx_w;
composedCnv.height = ctx_h;
[
{
cnv: mapCanvas,
y: 0
},
{
cnv: this.infoCanvas,
y: mapCanvas.height
}
].forEach( ( n ) => {
ctx.beginPath();
ctx.drawImage(n.cnv, 0, n.y, ctx_w, n.cnv.height);
});
resolve(composedCnv)
})
}

Implementing Three.js SSAOPass in AFrame

I was able to successfully integrate Threejs Effect composer in aframe as a component by exporting everything as THREE.Effectcomposer, THREE.SSAOPass etc. and adding the effect inside a aframe component and i tweaked the AFrame renderer to update the effects in the scene. OutlinePass from threejs worked fine in this code but SSAO is not working and i don't get any errors. Please someone help me figure out the problem. the code for SSAOPass looks like this
AFRAME.registerComponent('ssao', {
init: function () {
this.el.addEventListener('that', evt => this.onEnter());
this.el.addEventListener('mouseleave', evt => this.onLeave());
setTimeout(() => this.el.emit("that"), 2000);
},
onEnter: function () {
const scene = this.el.sceneEl.object3D;
const camera = this.el.sceneEl.camera;
const renderer = this.el.sceneEl.renderer;
const render = renderer.render;
const composer = new THREE.EffectComposer(renderer);
//let renderPass = new THREE.RenderPass(scene, camera);
//let outlinePass = new THREE.OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
const ssaoPass = new THREE.SSAOPass( scene, camera, window.innerWidth, window.innerHeight );
//composer.addPass(renderPass);
//composer.addPass(outlinePass);
ssaoPass.kernelRadius = 16;
composer.addPass( ssaoPass );
// let objects = [];
// this.el.object3D.traverse(node => {
// if (!node.isMesh) return;
// objects.push(node);
// });
// outlinePass.selectedObjects = objects;
// outlinePass.renderToScreen = true;
// outlinePass.edgeStrength = this.data.strength;
// outlinePass.edgeGlow = this.data.glow;
// outlinePass.visibleEdgeColor.set(this.data.color);
// HACK the AFRAME render method (a bit ugly)
const clock = new THREE.Clock();
this.originalRenderMethod = render;
let calledByComposer = false;
renderer.render = function () {
if (calledByComposer) {
render.apply(renderer, arguments);
} else {
calledByComposer = true;
composer.render(clock.getDelta());
calledByComposer = false;
}
};
},
onLeave: function () {
this.el.sceneEl.renderer.render = this.originalRenderMethod;
},
remove: function () {
this.onLeave();
}
});
I have also created a glitch project which i am sharing here. Please feel free to join and collaborate in my project
Edit link: https://glitch.com/edit/#!/accessible-torpid-partridge
Site link:https://accessible-torpid-partridge.glitch.me
Thanks in advance
The code is correct, all you need is to tweak the exposed SSAOShader uniforms: SSAOPass.kernelRadius, SSAOPass.minDistance, SSAOPass.maxDistance - like in the Three.js example.
Keep in mind - the scale in the example is huge, so the values will need to be different in a default aframe scene.
It's a good idea to be able to dynamically update a component (via setAttribute() if you properly handle updates), so you can see what's going on in realtime. Something like I did here - SSAO in a-frame (also based on Don McCurdys gist.
I've used some basic HTML elements, most threejs examples use dat.GUI - it is made for demo / debug tweaks.

HTML - refresh page after inactivity

I am having a touch screen with some very basic html pages. But if there is no activity for maybe 5 minutes, it should reload the main page again.
So it is not just a refresh of the site, but a load of the main page if there is no activity
just a question Where does the script know which site is the main page ?. If someone goes to "site B" - it should after some minutes without any movement move back to "site A"
Here is plain JavaScript implementation, without any dependencies:
var inactivityTime = function () {
var timer;
window.onload = timerReset;
document.onkeypress = timerReset;
document.onmousemove = timerReset;
document.onmousedown = timerReset;
document.ontouchstart = timerReset;
document.onclick = timerReset;
document.onscroll = timerReset;
document.onkeypress = timerReset;
function timerElapsed() {
console.log("Timer elapsed");
location.reload();
};
function timerReset() {
console.log("Reseting timer");
clearTimeout(timer);
timer = setTimeout(timerElapsed, 5 * 60 * 1000); // 5 mins
}
};
You can adjust list of events to listen to achieve best performance.
Complete list of DOM events.
You would need to use JavaScript. There is a The jQuery idletimer plugin at
http://paulirish.com/2009/jquery-idletimer-plugin/
Example:
// idleTimer() takes an optional argument that defines the idle timeout
// timeout is in milliseconds; defaults to 30000
$.idleTimer(10000);
$(document).bind("idle.idleTimer", function(){
// function you want to fire when the user goes idle
});
$(document).bind("active.idleTimer", function(){
// function you want to fire when the user becomes active again
});
// pass the string 'destroy' to stop the timer
$.idleTimer('destroy');
Reference:
How to change a page location after inactivity of 5 mins
Assuming no activity means you do not get click, scroll and movement events. You need to listen to these events in the body element of your page and set a range that calls the page refresh method.
var timeOut = null;
var activityMade = function(){
clearInterval(timeOut); //first clears the interval
timeOut = setInterval(function(){ console.log("Page Refreshed"); }, 3000); //logs to the console at every 3 seconds of inactivity
}
var bindEvents = function(){
var body = document.getElementById("app");
// bind click move and scroll event to body
body.addEventListener('click', activityMade);
body.addEventListener('mousemove', activityMade);
body.addEventListener('scroll', activityMade);
activityMade(); // assume activivity has done at page init
}
bindEvents();
You can bind as may events as you wish to ajust it to your needs.
Here is an working example https://jsfiddle.net/fspayfqt/
Please note that for this to work perfectly the body element will cover the whole window of the navigator and this can be solved with css using fixed position with height and width of 100%

How to dynamically change images using Metaio

I have just start learning Metaio. I am working on a simple development test project.
I have so far made the tracking sheet, put image and two arrows (buttons) which means next image and previous image.
For testing I made one of the buttons to display the image and the other for hiding the image. All this works fine so far.
My question is when I added extra images how can I shift the images dynamically back and forward using my next and previous buttons?
My testing code:
button2.onTouchStarted = function () {
image1.hide();
};
button1.onTouchStarted = function () {
image1.display();
};
It can be done different ways, I suggest you to use arel.Scene.getObject and put your image names inside array and each time you click next or previous you count the array key up or down.
I assume you are using Metaio Creator editor.
You have to added codes 3 different places:
Previous (left arrow button)
button1.onTouchStarted = function () {
if (currentImageIndex == firstImageIndex) {
return;
}
arel.Scene.getObject(imageArray[currentImageIndex]).hide();
currentImageIndex--;
arel.Scene.getObject(imageArray[currentImageIndex]).display();
globalsVar['currentImageIndex'] = currentImageIndex;
};
Next (right arrow button)
button2.onTouchStarted = function () {
if (currentImageIndex == lastImageIndex) {
return;
}
arel.Scene.getObject(imageArray[currentImageIndex]).hide();
currentImageIndex++;
arel.Scene.getObject(imageArray[currentImageIndex]).display();
globalsVar['currentImageIndex'] = currentImageIndex;
};
On your Global Script
var imageArray = ["image1", "image2"]; // and so on extra image names
var firstImageIndex = 0;
var lastImageIndex = imageArray.length - 1;
var currentImageIndex = firstImageIndex;
globalsVar = {};
arel.Scene.getObject(imageArray[currentImageIndex]).display();

OpenLayers - clear 'map' div

I am using OpenLayers to display dynamically loaded images. The code I am using is:
var map;
function init(strURL) {
map = new OpenLayers.Map('map');
var options = { numZoomLevels: 3,
isBaseLayer: true, };
var graphic = new OpenLayers.Layer.Image(
'City Lights',
strURL + "?sc=page",
new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
new OpenLayers.Size(580, 288),
options
);
// graphic.events.on({
// loadstart: function () {
// OpenLayers.Console.log("loadstart");
// },
// loadend: function () {
// OpenLayers.Console.log("loadend");
// }
// });
var jpl_wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
"http://t1.hypercube.telascience.org/cgi-bin/landsat7",
{ layers: "landsat7" }, options);
map.addLayers([graphic, jpl_wms]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.zoomToMaxExtent();
}
I am calling this function on a button click event and passing the strURL (sources of the images) at that time. The result is, with each click a different image is loaded and displayed on the web page but is not clearing the previous image. So I 5 different images on the webpage are shown with 5 clicks and so on.
My javascript knowledge is limited, so my apologies if this is a stupid question. How to stop this behavior? Thanks for any assistance.
Also, I didn't quite understand the lines:
var jpl_wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
"http://t1.hypercube.telascience.org/cgi-bin/landsat7",
{ layers: "landsat7" }, options);
But I know these lines are needed since I'm getting js error if I remove them.
So currently you are calling init(strURL) with each button click? Divide that code into two parts:
On page load, create map and layer objects
On button click, just update URL of existing image layer. Image layer has setUrl(url) method for that: http://dev.openlayers.org/apidocs/files/OpenLayers/Layer/Image-js.html#OpenLayers.Layer.Image.setUrl
Here is sample, that should explain it: http://jsfiddle.net/mEHrN/6/
About var jpl_wms = ... - it creates WMS layer, that should display Landsat imagery (but that URL doesn't seem to work). If you remove it, remember also to remove it from map.addLayers:
map.addLayers([graphic]);
Probably this was reason, why you got JS error.