I'm building a cesium app and I need my points to indicate a rotational heading. I'm using billboards to display an image I created to show this heading; however, when I rotate the globe, the points don't rotate with it, making the heading indicators incorrect.
In other words, the rotation of my billboards stay constant with the screen space, but not with the globe.
Here's an aerial view of New York. The points in question are in the lower left corner of the image. Note that the heading indicators are pointing northeast.
Here's the same view, but rotated 180° so that up is South. Now, the heading indicators are still pointing to the northwest of the screen, but I need it to be pointing to the globe's northeast, i.e. towards Manhattan.
Here's the code that displays a point:
var entity = {
id: data.cluster_number + '_' + point.id,
name: point.id,
position: Cesium.Cartesian3.fromDegrees(point.lon, point.lat),
billboard: {
image: 'assets/img/point.png',
color: color,
rotation: -1 * toRadians(point.heading),
scale: point.size * 0.07
}
};
if (!angular.isDefined(viewer.entities.getById(entity.id))) {
viewer.entities.add(entity);
}
What's the easiest/most effective way to get the points to rotate with the globe/map?
You need to do a few things.
1 - Update the rotation property of the billboard to be a CesiumCallbackProperty.
rotation: new Cesium.CallbackProperty(
function () {
return -1 * toRadians(point.heading);
}, false);
2 - You'll need to update the heading property of the point based on the camera's heading.
point.heading = viewer.camera.heading;
3 - You'll want to update that point on an interval, either using the cesium clock:
mapContext.clock.onTick.addEventListener(function() {...});
or using JavaScripts setInterval
Once all that is set up here is how it works.
The billboard on the map continuosly pulls the rotation from the point, and if it changes, it will update the billboard on the map. Each time the interval or clock ticks, the value being used changes, based on the heading of the camera.
NOTE: The heading property on the camera is in radians.
Set the billboard's alignedAxis to Cesium.Cartesian3.UNIT_Z instead of the default screen up vector. Run following sample in SandCastle to demonstrate that it works:
var viewer = new Cesium.Viewer('cesiumContainer');
var position = Cesium.Cartesian3.fromDegrees(-111.572177, 40.582083);
var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider(
{ url : '//assets.agi.com/stk-terrain/world' }
);
//viewer.terrainProvider = cesiumTerrainProviderMeshes;
//viewer.scene.globe.depthTestAgainstTerrain = true;
var ellipsoid = viewer.scene.globe.ellipsoid;
var billboardCollection = viewer.scene.primitives.add(new Cesium.BillboardCollection(
{ scene : viewer.scene }
));
billboardCollection.add(
{ position : position, image : '../images/facility.gif', //heightReference : Cesium.HeightReference.CLAMP_TO_GROUND, rotation: Cesium.Math.PI/4, alignedAxis : Cesium.Cartesian3.UNIT_Z }
);
Related
I need to develop 2 animations on a polygon:
automatic rotation of each segments
the polygon follows the mouse movements
I'm trying to do that with this code:
// onMouseMove
tool.onMouseMove = function (event) {
mousePoint = event.point;
};
// onFrame
view.onFrame = function (event) {
// Animation 1: Automatic circular rotation of each segment
for (var i = 0; i < polygon.segments.length; i++) {
var offsetRotation = new Point(Math.cos(event.time + i * 2), Math.sin(event.time + i * 2)).multiply(10);
polygon.segments[i].point = polygonCached.segments[i].point.add(offsetRotation);
}
// Animation 2: the polygon moves following the mouse movements with transition
var delta = mousePoint.subtract(polygon.position).divide(15);
polygon.position = polygon.position.add(delta);
};
Here is the whole code: https://codepen.io/anon/pen/YMgEEe?editors=0010
With the above code the automatic rotation of each segments works and the polygon doesn't follow the mouse at all and also without the transition.
Instead, commenting the automatic rotation animation it correctly follows the mouse with transition.
To check if transition works I move the mouse cursor outside of browser window and then go back from another point. Now you'll see no transition when the polygon moves.
Where I'm wrong?
Just move the polygonCached as well.
polygonCached.position = polygonCached.position.add(delta);
https://codepen.io/arthursw/pen/LvKPXo
The cached version of the polygon was not moving, so each time you rotated your points, their position was reset.
I'm extending Leaflet's GridLayer to show canvas tiles on which I draw some data points - the problem is that after zooming in and out the other zoom layers are still visible, or are partially chopped off in random ways.
E.g. on the left is zoom level 13, on the right is after zooming out to 11 and back to 13 -
How can I show only the current zoom level, and prevent the canvas tiles from getting chopped off?
This is how I ended up fixing it, though there might be better ways...
Here's my GridCanvas class -
L.GridLayer.GridCanvas = L.GridLayer.extend({
onAdd: function (map) {
L.GridLayer.prototype.onAdd.call(this, map);
map.on('zoomend', e => this.onZoomEnd(e.target._zoom));
},
// on zoom end, want to hide all the tiles that are not on this zoom level,
// and show all the ones that are
onZoomEnd: function (zoom) {
Object.values(this._levels).forEach(level => {
level.el.style.display = (level.zoom === zoom) ? 'block' : 'none';
});
},
createTile,
});
The visibility of the tiles also gets set in seemingly random ways - this seemed to fix the problem - adding a class to the canvas elements and setting them to always be visible -
function createTile(coords, done) {
const canvas = L.DomUtil.create('canvas', 'grid-canvas-tile');
...
}
with styles.css:
.grid-canvas-tile {
visibility: visible !important;
}
I am using Cesium and am looking to visually represent multiple polylines between the same two entities. For example, a green polyline from entity A to entity B, and also a blue polyline from entity A to entity B. I would like them not to overlap or blend, so I am imagining a fanning out as more lines are drawn, so that each line and what it represents can be visualized. I've included a crude drawing of what I'm trying to explain with the fanning out rather than overlapping.
I have a functional data structure keeping track of the lines I want to represent, as well as a Cesium map that they are already being programatically drawn on. I guess at this point I'm looking for the technical explanation of how to programatically bend the polylines on the map, and also any suggestions for polyline management in order to recognize overlapping lines so I can apply the bends.
Thanks for any help!
Here's one method. This sample code will "spread" the lines along longitude only, so works best on North/South lines and not on East/West lines. But I think it should convey the right idea, you just have to figure out a more general-purpose way of "moving" the midpoint to a visually pleasing location.
I'm using time-based paths here, to gain access to Cesium's interpolation logic. But I've selected a reference time far in the past, and I'm only showing the finished paths on the viewer. So, the user is none the wiser that time is playing any role here.
var viewer = new Cesium.Viewer('cesiumContainer', {
navigationInstructionsInitiallyVisible: false,
animation: false,
timeline: false,
// These next 5 lines are just to avoid the Bing Key error message.
imageryProvider : Cesium.createTileMapServiceImageryProvider({
url : Cesium.buildModuleUrl('Assets/Textures/NaturalEarthII')
}),
baseLayerPicker : false,
geocoder : false,
// This next line fixes another Stack Snippet error, you may omit
// this setting from production code as well.
infoBox : false
});
var numberOfArcs = 5;
var startLon = -105;
var startLat = 39.7;
var stopLon = -98.4;
var stopLat = 29.4;
var spread = 5;
var referenceTime = Cesium.JulianDate.fromIso8601('2001-01-01T00:00:00Z');
var midTime = Cesium.JulianDate.addSeconds(referenceTime, 43200, new Cesium.JulianDate());
var stopTime = Cesium.JulianDate.addSeconds(referenceTime, 86400, new Cesium.JulianDate());
for (var i = 0; i < numberOfArcs; ++i) {
var color = Cesium.Color.fromRandom({
alpha : 1.0
});
// Create a straight-line path.
var property = new Cesium.SampledPositionProperty();
var startPosition = Cesium.Cartesian3.fromDegrees(startLon, startLat, 0);
property.addSample(referenceTime, startPosition);
var stopPosition = Cesium.Cartesian3.fromDegrees(stopLon, stopLat, 0);
property.addSample(stopTime, stopPosition);
// Find the midpoint of the straight path, and move it.
var spreadAmount = (spread / (numberOfArcs - 1)) * i - (spread / 2);
var midPoint = Cesium.Cartographic.fromCartesian(property.getValue(midTime));
midPoint.longitude += Cesium.Math.toRadians(spreadAmount);
var midPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(
midPoint, new Cesium.Cartesian3());
// Redo the path to be the new arc.
property = new Cesium.SampledPositionProperty();
property.addSample(referenceTime, startPosition);
property.addSample(midTime, midPosition);
property.addSample(stopTime, stopPosition);
// Create an Entity to show the arc.
var arcEntity = viewer.entities.add({
position : property,
// This path shows the arc as a polyline.
path : {
resolution : 1200,
material : new Cesium.PolylineGlowMaterialProperty({
glowPower : 0.16,
color : color
}),
width : 10,
leadTime: 1e11,
trailTime: 1e11
}
});
// This is where it becomes a smooth path.
arcEntity.position.setInterpolationOptions({
interpolationDegree : 5,
interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
});
}
// Optionally, add start and stop points, mostly for easy zoomTo().
viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(startLon, startLat),
point : {
pixelSize : 8,
color : Cesium.Color.WHITE
}
});
viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(stopLon, stopLat),
point : {
pixelSize : 8,
color : Cesium.Color.WHITE
}
});
viewer.zoomTo(viewer.entities);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
font-family: sans-serif;
}
<link href="http://cesiumjs.org/releases/1.30/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"/>
<script src="http://cesiumjs.org/releases/1.30/Build/Cesium/Cesium.js">
</script>
<div id="cesiumContainer"></div>
I'm using the jQuery transit plugin to rotate a block element with 4 navigation controls; each time a nav item is clicked, the rotated block should rotate to the custom data attribute of that nav item (either 0, 90, 180 or 270 degrees).
I can successfully do this (see my jsfiddle: http://jsfiddle.net/xkdgdhbk/, only tested in chrome), but I would like to restrict the rotation to ALWAYS go clockwise. As you can see, if you click 180deg it will go clockwise, but then if you click 90deg, it will go anti-clockwise. I would like the element to rotate clockwise at all times, if possible.
Is there a way to achieve this?
Here's my script from the jsfiddle:
$("span").click(function(){
$(".rotate").transition({ rotate: $(this).attr("data-rotate") + "deg"});
});
You can store the previous rotate value in the $(.rotate) element and use it for each rotation something like this
$().ready(function()
{
$("span").click(function()
{
var lastRotate = $(".rotate").attr("data-rotate");
if(lastRotate ==undefined)
{
lastRotate = 0;
}
lastRotate = parseInt(lastRotate);
var newRotate = lastRotate + parseInt($(this).attr("data-rotate"));
$(".rotate").transition({ rotate:newRotate + "deg"}).attr("data-rotate",newRotate);
});
});
OK, so I have a group of four elements rotating 90 degrees as I want them to, around an origin point in the middle of the four elements.
I would like to scale the top left block before and after spinning as well, outward from said origin point, but I'm having much difficulty doing so.
Here is a fiddle for my sample (read: overly simplified) progress so far:
http://jsfiddle.net/Vac2Q/2843/
The fiddle's javascript:
/* create an svg drawing */
var draw = SVG('drawing')
/* draw rectangle */
var dial = draw.circle(60)
.move(125,125)
.fill('#0099ff')
var rect_yellow = draw.rect(50,50)
.move(100,100)
.fill('gold')
var rect_blue = draw.rect(50,50)
.move(160,100)
.fill('navy')
var rect_black = draw.rect(50,50)
.move(160,160)
.fill('black')
var rect_green = draw.rect(50,50)
.move(100,160)
.fill('green')
var blades = draw.group()
.add(rect_yellow)
.add(rect_blue)
.add(rect_black)
.add(rect_green)
.back()
var angle = 0
var rotation = 90
var spin = document.getElementById('spin')
var spun = 0
/* make rectangle jump and change color on mouse over */
spin.addEventListener('click', function() {
/* calculate new ending orientation for blades */
angle += rotation
var new_rotate = angle
/* rotate the blade group */
blades.animate(1000, '>')
.rotate(new_rotate, 155, 155)
++spun
})
And here is a slightly more glamorous example of what I'm trying to do re: scaling:
The first issue is being able to determine which blade is in the top left position after a given rotation. The second issue is scaling itself; I've gotten the blade to scale, but then it goes crazy and moves off the screen at the same time. I'm not sure how to get it to scale properly from the specified origin point (the middle of the center circle).
You can use the .after() function to chain animations.
I'm not sure if I am using svg.js correctly, but here's what I did:
var rects = [rect_yellow, rect_green, rect_black, rect_blue];
// define the animations
var enlarge_blade = function() {
rects[spun % 4].animate(250, '<')
.scale(1.25, 1.25)
.translate(-38,-38);
};
function spin_anim() {
rects[spun % 4].animate(250, '>')
.scale(1, 1)
.translate(0,0)
.after(rotate_blades);
};
var rotate_blades = function() {
blades.animate(1000, '>')
.rotate(angle, 155, 155)
.after(function() {
++spun;
enlarge_blade();
title.text('spun ' + spun + ' times');
});
};
// Pre-enlarge the first (yellow) rect
enlarge_blade();
/* make rectangle jump and change color on mouse over */
spin.addEventListener('click', function() {
angle += rotation
spin_anim();
})
Demo here
I see you're using svg.js. I don't know how it treats transforms internally. But in any case. To scale an element, you typically use its center point as a reference. Therefore you should find the center point of each rect and scale it using that point. (I assume svg.js performs the required translation internally).