I want to display cesium clock and timeline widget inside a different div container outside of the cesium container. With the help of this link I've created a separate clock widget and applied animation on an entity. Animation is working but the clock widget is not working. It seems like default clock widget is working not the newly created. Sandcastle link
var viewer = new Cesium.Viewer("cesiumContainer", {
infoBox: false, //Disable InfoBox widget
selectionIndicator: false, //Disable selection indicator
shouldAnimate: true, // Enable animations
terrainProvider: Cesium.createWorldTerrain(),
});
//Enable lighting based on the sun position
viewer.scene.globe.enableLighting = true;
//Enable depth testing so things behind the terrain disappear.
viewer.scene.globe.depthTestAgainstTerrain = true;
var _currentSysDT = new Date();
function onTimelineScrubfunction(e) {
clock = e.clock;
clock.currentTime = e.timeJulian;
clock.shouldAnimate = false;
}
var timeControlsContainer = document.getElementById('timeControlsContainer');
// viewer.animation.viewModel.timeFormatter = LocalFormatter;
var clock = new Cesium.Clock();
clock.startTime = Cesium.JulianDate.fromDate(_currentSysDT);
clock.currentTime = Cesium.JulianDate.fromDate(_currentSysDT);
clock.clockRange = Cesium.ClockRange.LOOP_STOP;
var clockViewModel = new Cesium.ClockViewModel(clock);
clockViewModel.startTime = Cesium.JulianDate.fromDate(_currentSysDT);
clockViewModel.currentTime = Cesium.JulianDate.fromDate(_currentSysDT);
var animationContainer = document.createElement('div');
animationContainer.className = 'cesium-viewer-animationContainer';
timeControlsContainer.appendChild(animationContainer);
var animViewModel = new Cesium.AnimationViewModel(clockViewModel);
var animation = new Cesium.Animation(animationContainer, animViewModel);
var timelineContainer = document.createElement('div');
timelineContainer.className = 'cesium-viewer-timelineContainer';
timeControlsContainer.appendChild(timelineContainer);
var timeline = new Cesium.Timeline(timelineContainer, clock);
timeline.addEventListener('settime', onTimelineScrubfunction, false);
timeline.zoomTo(clock.startTime, clock.stopTime);
clockViewModel.shouldAnimate = true;
animViewModel.snapToTicks = false;
animViewModel.pauseViewModel.command(); //comment this for default play
timeline.zoomTo(clock.startTime, Cesium.JulianDate.addSeconds(clock.startTime, 60, new Cesium.JulianDate()));
clock.onTick.addEventListener(function (clock) {
});
window.setInterval(function () {
clock.tick();
}, 32);
var start = Cesium.JulianDate.addSeconds(Cesium.JulianDate.fromDate(_currentSysDT), 0, new Cesium.JulianDate());
var stop = Cesium.JulianDate.addSeconds(Cesium.JulianDate.fromDate(_currentSysDT), 120, new Cesium.JulianDate());
var positions = [{"lat":"23.14291673","lon":"73.60544359","alt":"33.79739465869949"},{"lat":"23.14291736","lon":"73.60558935","alt":"33.705623697852786"},{"lat":"23.14284330","lon":"73.60553133","alt":"33.280035949546644"},{"lat":"23.14284640","lon":"73.60546898","alt":"33.219775982790594"}];
var positionProperty = new Cesium.SampledPositionProperty();
positions.forEach((p,i)=>{
let _pos = Cesium.Cartesian3.fromDegrees(parseFloat(p.lon), parseFloat(p.lat), parseFloat(p.alt));
let _time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
positionProperty.addSample(_time, _pos);
});
var entity = viewer.entities.add({
//Set the entity availability to the same interval as the simulation time.
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: start,
stop: stop,
}),
]),
//Use our computed positions
position: positionProperty,
//Automatically compute orientation based on position movement.
orientation: new Cesium.VelocityOrientationProperty(positionProperty),
//Load the Cesium plane model to represent the entity
model: {
uri: "../SampleData/models/CesiumMan/Cesium_Man.glb",
minimumPixelSize: 64,
},
//Show the path as a yellow line sampled in 1 second increments.
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.1,
color: Cesium.Color.YELLOW,
}),
width: 10,
},
});
viewer.trackedEntity = entity;
You need to create the viewer after the clockViewModel, and pass in the clockViewModel as a constructor option. For example:
var viewer = new Cesium.Viewer("cesiumContainer", {
infoBox: false, //Disable InfoBox widget
selectionIndicator: false, //Disable selection indicator
terrainProvider: Cesium.createWorldTerrain(),
// Construct this viewer using your previously constructed clockViewModel.
clockViewModel: clockViewModel,
// Don't let this viewer build its own animation widget.
animation: false,
// Don't let this viewer build its own timeline widget.
timeline: false
});
Here's the Updated Sandcastle Demo.
Related
I'm using Pixi with PixiOverlay on leaflet. I have the following jsfiddle for a dummy simulation. The objective: once you click Add Image 2 - it adds a picture of a hamster randomly on the map.
It (almost) work.
the problem:
Error message: "BaseTexture added to the cache with an id [hamster] that already had an entry"
I couldn't figure our where to put the loader and how to integrate it properly in terms of code organization: (do I need to use it only once?) what if I have other layers to add? So I assume my challenge is here:
this.loader.load((loader, resources) => {...}
Minor: how to reduce the size of the hamster :-)
my JS code (also on jsfiddle)
class Simulation
{
constructor()
{
// center of the map
var center = [1.8650, 51.2094];
// Create the map
this.map = L.map('map').setView(center, 2);
// Set up the OSM layer
L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(this.map);
this.imagesLayer = new L.layerGroup();
this.imagesLayer.addTo(this.map);
}
_getRandomCoord()
{
var randLat = Math.floor(Math.random() * 90);
randLat *= Math.round(Math.random()) ? 1 : -1;
var randLon = Math.floor(Math.random() * 180);
randLon *= Math.round(Math.random()) ? 1 : -1;
return [randLat,randLon]
}
addImage2()
{
this.loader = new PIXI.Loader()
this.loader.add('hamster', 'https://cdn-icons-png.flaticon.com/512/196/196817.png')
this.loader.load((loader, resources) => {
let markerTexture = resources.hamster.texture
let markerLatLng = this._getRandomCoord()
let marker = new PIXI.Sprite(markerTexture)
marker.anchor.set(0.5, 1)
let pixiContainer = new PIXI.Container()
pixiContainer.addChild(marker)
let firstDraw = true
let prevZoom
let pixiOverlay = L.pixiOverlay(utils => {
let zoom = utils.getMap().getZoom()
let container = utils.getContainer()
let renderer = utils.getRenderer()
let project = utils.latLngToLayerPoint
let scale = utils.getScale()
if (firstDraw) {
let markerCoords = project(markerLatLng)
marker.x = markerCoords.x
marker.y = markerCoords.y
}
if (firstDraw || prevZoom !== zoom) {
marker.scale.set(1 / scale)
}
firstDraw = true
prevZoom = zoom
renderer.render(container)
}, pixiContainer)
this.imagesLayer.addLayer(pixiOverlay);
})
}
addTriangle()
{
console.log("Trinalge")
var polygonLatLngs = [
[51.509, -0.08],
[51.503, -0.06],
[51.51, -15.047],
[21.509, -0.08]
];
var projectedPolygon;
var triangle = new PIXI.Graphics();
var pixiContainer = new PIXI.Container();
pixiContainer.addChild(triangle);
var firstDraw = true;
var prevZoom;
var pixiOverlay = L.pixiOverlay(function(utils) {
var zoom = utils.getMap().getZoom();
var container = utils.getContainer();
var renderer = utils.getRenderer();
var project = utils.latLngToLayerPoint;
var scale = utils.getScale();
if (firstDraw) {
projectedPolygon = polygonLatLngs.map(function(coords) {return project(coords);});
}
if (firstDraw || prevZoom !== zoom) {
triangle.clear();
triangle.lineStyle(3 / scale, 0x3388ff, 1);
triangle.beginFill(0x3388ff, 0.2);
projectedPolygon.forEach(function(coords, index) {
if (index == 0) triangle.moveTo(coords.x, coords.y);
else triangle.lineTo(coords.x, coords.y);
});
triangle.endFill();
}
firstDraw = false;
prevZoom = zoom;
renderer.render(container);
}.bind(this), pixiContainer);
this.imagesLayer.addLayer(pixiOverlay)
}
removeLayer()
{
this.imagesLayer.clearLayers();
}
}
var simulation = new Simulation();
TLDR: Updated jsfiddle:
https://jsfiddle.net/gbsdfm97/
more info below:
First problem: loading resources (textures)
There was error in console because you loaded hamster image on each click:
addImage2()
{
this.loader = new PIXI.Loader()
this.loader.add('hamster', 'https://cdn-icons-png.flaticon.com/512/196/196817.png')
this.loader.load((loader, resources) => {
...
Better approach is to load image (resource) once at beginning and then just reuse what is loaded in memory:
constructor()
{
...
this.markerTexture = null;
this._loadPixiResources();
}
...
_loadPixiResources()
{
this.loader = new PIXI.Loader()
this.loader.add('hamster', 'https://cdn-icons-png.flaticon.com/512/196/196817.png')
this.loader.load((loader, resources) => {
this.markerTexture = resources.hamster.texture;
})
}
...
addImage2()
{
...
let marker = new PIXI.Sprite(this.markerTexture);
Second problem: size of hamsters :)
Scale was set like this:
marker.scale.set(1 / scale)
Which was too big - so changed it to:
// affects size of hamsters:
this.scaleFactor = 0.05;
...
marker.scale.set(this.scaleFactor / scale);
Scale of hamsters (not triangles!) is now updated when zoom changes - so when user uses mouse scroll wheel etc.
Third problem: too many layers in pixiOverlay
Previously on each click on Add Image 2 or Add Triangle button there was added new pixiContainer and new pixiOverlay which was added as new layer: this.imagesLayer.addLayer(pixiOverlay);
New version is a bit simplified: there is only one pixiContainer and one pixiOverlay created at beginning:
constructor()
{
...
// Create one Pixi container for pixiOverlay in which we will keep hamsters and triangles:
this.pixiContainer = new PIXI.Container();
let prevZoom;
// Create one pixiOverlay:
this.pixiOverlay = L.pixiOverlay((utils, data) => {
...
}, this.pixiContainer)
this.imagesLayer.addLayer(this.pixiOverlay);
}
this.pixiOverlay is added as one layer
then in rest of program we reuse this.pixiOverlay
also we reuse this.pixiContainer because it is returned from utils - see:
let container = utils.getContainer() // <-- this is our "this.pixiContainer"
...
container.addChild(marker)
renderer.render(container)
Bonus: Triangles
Now you can add many triangles - one per each click.
Note: triangles do not change scale - this is a difference compared to hamsters.
I'm trying to implement a zoom bar in mapbox gl js but the only thing I found is this code from their documentation which adds an +, - and a reset. Is there anyway I can add a slider zoom level bar ? (A bar like this https://www.w3schools.com/howto/howto_js_rangeslider.asp )
var nav = new mapboxgl.NavigationControl();
map.addControl(nav, 'bottom-left');
Yes, you can, it requires to create a custom control and add manually the event to update the map zoom... but it's only a few lines of code. I didn't work too much in the css styling.
here's a fiddle I have created with an example How to create a custom zoom control
And here's the relevant scripting code
<script>
mapboxgl.accessToken = 'PUT HERE YOUR MAPBOX TOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11', // stylesheet location
zoom: 3, // starting zoom
center: [-95, 40], // starting position [lng, lat]
});
map.on('load', function () {
let zoomControl = new CustomZoomControl();
map.addControl(zoomControl, 'top-right');
map.on('zoom', function () {
zoomControl.update();
});
});
class CustomZoomControl {
onAdd(map) {
this.map = map;
this.container = document.createElement('div');
this.container.className = " mapboxgl-ctrl mapboxgl-ctrl-group";
this.input = document.createElement('input');
this.input.type = "range"
this.input.min = 1;
this.input.max = 220;
this.createAttribute(this.input , "value", map.getZoom()*10)
this.input.className = "slider";
this.input.id = "myRange";
this.container.appendChild(this.input);
// Update the current slider value (each time you drag the slider handle)
this.input.oninput = function () {
map.setZoom(this.value/10);
}
return this.container;
}
onRemove() {
this.container.parentNode.removeChild(this.container);
this.map = undefined;
}
createAttribute(obj, attrName, attrValue) {
var att = document.createAttribute(attrName);
att.value = attrValue;
obj.setAttributeNode(att);
}
update() {
let zoom = map.getZoom() * 10;
if (this.input.value != zoom) this.input.value = zoom;
}
}
</script>
I'm creating my own drawer online with mxGraph. I want to make my ConnectionConstraints always visibles on the sheet after the creation of my cell. I tried to edit mxConstraintHandler but it didn't worked. This is what I want, even after the mouse left the shape --> enter image description here. The 'x' on the shape have to be always persistent.
Instead you can try ports. that's Using the isPort hook for visually connecting to another cell. And for 'x' shape use offset of geometry.
Please check Hello Port example
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
function main(container)
{
// Checks if the browser is supported
if (!mxClient.isBrowserSupported())
{
// Displays an error message if the browser is not supported.
mxUtils.error('Browser is not supported!', 200, false);
}
else
{
// Creates the graph inside the given container
var graph = new mxGraph(container);
graph.setConnectable(true);
graph.setTooltips(true);
// Sets the default edge style
var style = graph.getStylesheet().getDefaultEdgeStyle();
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;
// Ports are not used as terminals for edges, they are
// only used to compute the graphical connection point
graph.isPort = function(cell)
{
var geo = this.getCellGeometry(cell);
return (geo != null) ? geo.relative : false;
};
// Implements a tooltip that shows the actual
// source and target of an edge
graph.getTooltipForCell = function(cell)
{
if (this.model.isEdge(cell))
{
return this.convertValueToString(this.model.getTerminal(cell, true)) + ' => ' +
this.convertValueToString(this.model.getTerminal(cell, false))
}
return mxGraph.prototype.getTooltipForCell.apply(this, arguments);
};
// Removes the folding icon and disables any folding
graph.isCellFoldable = function(cell)
{
return false;
};
// Enables rubberband selection
new mxRubberband(graph);
// Gets the default parent for inserting new cells. This
// is normally the first child of the root (ie. layer 0).
var parent = graph.getDefaultParent();
// Adds cells to the model in a single step
graph.getModel().beginUpdate();
try
{
var v1 = graph.insertVertex(parent, null, 'Hello', 20, 80, 80, 30);
v1.setConnectable(false);
var v11 = graph.insertVertex(v1, null, '', 1, 1, 10, 10);
v11.geometry.offset = new mxPoint(-5, -5);
v11.geometry.relative = true;
var v12 = graph.insertVertex(v1, null, '', 1, 0, 10, 10);
v12.geometry.offset = new mxPoint(-5, -5);
v12.geometry.relative = true;
var v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);
var v3 = graph.insertVertex(parent, null, 'World2', 200, 20, 80, 30);
var e1 = graph.insertEdge(parent, null, '', v11, v2);
var e1 = graph.insertEdge(parent, null, '', v12, v3);
}
finally
{
// Updates the display
graph.getModel().endUpdate();
}
var button = mxUtils.button('View XML', function()
{
var encoder = new mxCodec();
var node = encoder.encode(graph.getModel());
mxUtils.popup(mxUtils.getPrettyXml(node), true);
});
document.body.insertBefore(button, container.nextSibling);
}
};
I am trying to "move" renderables around the web worldwind globe on an interval. To illustrate the issue I am having, I made a small example.
This works (but is inefficient):
var myVar = setInterval(myTimer, 5000);
function myTimer() {
shapesLayer.removeRenderable(shape);
shape = new WorldWind.SurfaceCircle(new WorldWind.Location(shape.center.latitude+1, shape.center.longitude), 200e3, attributes);
shapesLayer.addRenderable(shape);
console.log(" new pos "+shape.center.latitude + " "+shape.center.longitude);
wwd.redraw();
}
This is what I'd like to do, but the shape doesn't move:
var myVar = setInterval(myTimer, 5000);
function myTimer() {
shape.center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
console.log(" new pos "+shape.center.latitude + " "+shape.center.longitude);
wwd.redraw();
}
Is there a flag I need to set on the renderable to make it refresh?
Here is the full SurfaceShapes.js file I've been playing with (based on this http://worldwindserver.net/webworldwind/examples/SurfaceShapes.html):
/*
* Copyright (C) 2014 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration. All Rights Reserved.
*/
/**
* Illustrates how to display SurfaceShapes.
*
* #version $Id: SurfaceShapes.js 3320 2015-07-15 20:53:05Z dcollins $
*/
requirejs(['../src/WorldWind',
'./LayerManager'],
function (ww,
LayerManager) {
"use strict";
// Tell World Wind to log only warnings.
WorldWind.Logger.setLoggingLevel(WorldWind.Logger.LEVEL_WARNING);
// Create the World Window.
var wwd = new WorldWind.WorldWindow("canvasOne");
/**
* Added imagery layers.
*/
var layers = [
{layer: new WorldWind.BMNGLayer(), enabled: true},
{layer: new WorldWind.BingAerialWithLabelsLayer(null), enabled: true},
{layer: new WorldWind.CompassLayer(), enabled: true},
{layer: new WorldWind.CoordinatesDisplayLayer(wwd), enabled: true},
{layer: new WorldWind.ViewControlsLayer(wwd), enabled: true}
];
for (var l = 0; l < layers.length; l++) {
layers[l].layer.enabled = layers[l].enabled;
wwd.addLayer(layers[l].layer);
}
// Create a layer to hold the surface shapes.
var shapesLayer = new WorldWind.RenderableLayer("Surface Shapes");
wwd.addLayer(shapesLayer);
// Create and set attributes for it. The shapes below except the surface polyline use this same attributes
// object. Real apps typically create new attributes objects for each shape unless they know the attributes
// can be shared among shapes.
var attributes = new WorldWind.ShapeAttributes(null);
attributes.outlineColor = WorldWind.Color.BLUE;
attributes.drawInterior = false;
attributes.outlineWidth = 4;
attributes.outlineStippleFactor = 1;
attributes.outlineStipplePattern = 0xF0F0;
var highlightAttributes = new WorldWind.ShapeAttributes(attributes);
highlightAttributes.interiorColor = new WorldWind.Color(1, 1, 1, 1);
// Create a surface circle with a radius of 200 km.
var shape = new WorldWind.SurfaceCircle(new WorldWind.Location(35, -120), 200e3, attributes);
shape.highlightAttributes = highlightAttributes;
shapesLayer.addRenderable(shape);
// Create a layer manager for controlling layer visibility.
var layerManger = new LayerManager(wwd);
// Now set up to handle highlighting.
var highlightController = new WorldWind.HighlightController(wwd);
var myVar = setInterval(myTimer, 5000);
function myTimer() {
//Shape doesn't move
shape.center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
//Shape "moves" but is inefficient
//shapesLayer.removeRenderable(shape);
//shape = new WorldWind.SurfaceCircle(new WorldWind.Location(shape.center.latitude+1, shape.center.longitude), 200e3, attributes);
//shapesLayer.addRenderable(shape);
console.log(" new pos "+shape.center.latitude + " "+shape.center.longitude);
wwd.redraw();
}
}
);
Document says renderables variable is readonly but I think it can be possible.
change code
shape.center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
To
index = ShapesLayer.renderables.indexOf(shape);
ShapesLayer.renderables[index].center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
I think ShapesLayer.addRenderable create anther shape.
If you think it's not good way to do you can use this way
RenderableLayer.prototype.changeRenderable= function (prevRenderable, nextRenderable) {
index = ShapesLayer.renderables.indexOf(prevRenderable);
ShapesLayer.renderables[index].center = nextRenderable;
};
main code
ShapesLayer.changeRenderable(shape, new WorldWind.Location(shape.center.latitude+1, shape.center.longitude));
Document : https://nasaworldwind.github.io/WebWorldWind/layer_RenderableLayer.js.html
If anyone else is looking at this, I found a better solution to force it to recompute the center position by setting isPrepared to false and _boundaries to undefined.
var myVar = setInterval(myTimer, 5000);
function myTimer() {
shape.isPrepared = false;
shape._boundaries = undefined;
shape.center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
console.log(" new pos "+shape.center.latitude + " "+shape.center.longitude);
wwd.redraw();
}
I am adding a plane and a grid using Gridhelper to my 3d scene:
// PLANE XY static
var gridplaneSize = 20;
var color = 0xFFDCBB;
var plGeometry = new THREE.PlaneGeometry(gridplaneSize, gridplaneSize);
var plMaterial = new THREE.MeshBasicMaterial( {color:color, ambient:color, side:THREE.DoubleSide, opacity:0.5, transparent:true, depthWrite: false } );
var planeMesh_xy = new THREE.Mesh(plGeometry, plMaterial);
planeMesh_xy.rotation.x = -Math.PI/2;
scene.add(planeMesh_xy);
planeMesh_xy.receiveShadow = true;
// GRID XY static
var gridstep = 1;
var gridcolor = 0xCCCCCC;
var gridHelper_xy = new THREE.GridHelper(gridplaneSize/2, gridstep);
gridHelper_xy.position = new THREE.Vector3(0, 0, 0);
gridHelper_xy.setColors( new THREE.Color(gridcolor), new THREE.Color(gridcolor) );
scene.add(gridHelper_xy);
Then I would like to decide if the plane or the grid is visible.
This works for the plane:
planeMesh_xy.visible = false;
But not for the grid:
gridHelper_xy.visible = false; // not working
I also tried workarounds, not working:
gridHelper_xy.material.transparent = true;
gridHelper_xy.material.opacity = 0;
gridHelper_xy.parent.visible = false;
Can anyone tell me how to hide the grid then?
What should happen when you set the visible flag of an object is that all of its children should inherit the flag.
In r71 this should work out of the box (look at issue https://github.com/mrdoob/three.js/issues/1524)
In r65 you would have to traverse your hierarchy setting the flag on all the sub-parts:
object.traverse ( function (child) {
child.visible = false;
}