I have two entities on my page; a satellite and its "ground position", both of which move as time passes in Cesium. I'd like to connect the two with a straight line that moves with them.
The CZML Showcase seems to demonstrate similar functionality if you're using a CZML file, but I would like to know how to do this in code. Their demo contains several lines between satellites and ground positions, and in fact they go one step further and only show the line if it doesn't intersect the earth (if line-of-sight exists between the two entities). I don't need anything quite that fancy.
Are there any good examples of this, or docs that someone could point me to? Thanks!
Figured it out: #emackey started me on the right track by pointing me at this section of simple.czml. The part I had trouble translating from CZML to javascript was this section that dynamically specifies where the line should begin and end:
"positions":{
"references":[
"Facility/AGI#position","Satellite/ISS#position"
]
}
It turns out the classes I needed for that were PositionPropertyArray and ReferenceProperty. With those two I can add a dynamic line to either of my entities like this:
var groundTrackEntity = cesiumViewer.entities.add({
id: "groundTrackEntity",
position: groundTrackPosition,
point: /* ... */,
path: /* ... */,
polyline: {
followSurface: false,
positions: new Cesium.PositionPropertyArray([
new Cesium.ReferenceProperty(
cesiumViewer.entities,
'orbitEntity',
[ 'position' ]
),
new Cesium.ReferenceProperty(
cesiumViewer.entities,
'groundTrackEntity',
[ 'position' ]
)
]),
material: new Cesium.ColorMaterialProperty(
Cesium.Color.YELLOW.withAlpha( 0.25 )
)
}
});
This is done by adding PolylineGraphics to one of your entities. Make sure to set "followSurface": false for this, since you don't want the line to bend with the curvature of the Earth. The options here are similar to what you see in simple.czml, except you don't need the list of visibility intervals, and can simply set "show": true here.
Related
I'm making a Sheet to be used by elementary students, to track some energy "usage" in their class, based on a date. To make it dead easy, I've created dropdowns for their choices (text).
In order to make a graph, I've changed the choices into numbers ("All"=3, "Some"=2, "None"=1, "N/A"=0) onto another tab (using Apps Script). This makes a nice graph, but the vertical axis of course shows the numbers. I'm hoping there is a way to swap them out for the text.
I've tried the 'ticks' option, but nothing changes:
var vAxisOptions = {
0: {
ticks: [{v:0, f:'N/A'}, {v:1, f:'None'}, {v:2, f:'Some'}, {v:3, f:'All'}, {v:4, f:''}],
maxValue: 4,
gridlines: {count: 5} //add an extra line of space to see the lines better
}
};
And then apply it by .setOption('vAxes', vAxisOptions).
I suspect this just isn't possible, but is it? Thanks!!
Example: https://docs.google.com/spreadsheets/d/1zOeXJy92LdCmhdLW6MmLA0JCNVk34kk50B3tQGACwlY/edit?usp=sharing
p.s. Click the "View Results" button to make the graph if you make data changes
There is this Google Issue tracker issue on this matter that it is being worked on.
You can go there and click on the star next to the title of the issue so you will get updates on the issue.
I'm using the Cesium library to simulate the satellite motion. This code is used to show the path:
path:{
leadTime:data.data_list[0].period/2,
trailTime:data.data_list[0].period/2,
width:1.5,
material: color
}
Is there a way to make the trailing path thinner?
As an example, the SpaceX video: http://youtu.be/rQEqKZ7CJlk?t=47m40s
Couple of comments here. First, with a width of only 1.5, altering the width is likely not the effect you want. Try using a solid color vs a faded color, such as by changing the alpha value. This should be more similar to what you're seeing in that video link.
But, currently Cesium does not support separate path materials for lead and trail times. If you really need both lead and trail paths shown with different colors/widths/etc, you have to insert a duplicate entity in your CZML (whose position can just be a reference to the primary entity position), such that one entity can have only a leadtime and the other has only a trailtime, and they use different path materials.
But if you have a satellite in a stable orbit, you there's a different approach you can take, because the thing keeps circling around the same path. You can use the StripeMaterial to make the orbit line fade from one side to the other.
Here's a live demo that loads simple.czml and replaces the Molniya's yellow orbit with a faded line. The solid part is the most recent trail of the satellite, and the faded part is much further back but serves to show where the satellite is headed next.
Cesium.Camera.DEFAULT_VIEW_FACTOR = 5.5;
var viewer = new Cesium.Viewer('cesiumContainer', {
shouldAnimate : true
});
Cesium.CzmlDataSource.load('../../../../Apps/SampleData/simple.czml').then(function(dataSource) {
viewer.dataSources.add(dataSource);
viewer.camera.flyHome(0);
viewer.clock.multiplier = 1800;
var entity = dataSource.entities.getById('Satellite/Molniya_1-92');
var fadedLine = new Cesium.StripeMaterialProperty({
// The newest part of the line is bright yellow.
evenColor: Cesium.Color.YELLOW,
// The oldest part of the line is yellow with a low alpha value.
oddColor: Cesium.Color.YELLOW.withAlpha(0.2),
repeat: 1,
offset: 0.25,
orientation: Cesium.StripeOrientation.VERTICAL
});
entity.path.material = fadedLine;
entity.path.leadTime = new Cesium.ConstantProperty(0);
entity.path.trailTime = new Cesium.ConstantProperty(3600 * 12);
});
Here you can play with the color, of course, but I would recommend keeping the same base color for even and odd. The withAlpha value of 0.2 on the oddColor here controls how faded the line gets, range is 0 to 1. The offsetvalue here can also be tweaked, to control the placement of the fade range. leadTime should be zero, and trailTime should be the orbit's period.
I am currently working an application that is using a Cesium Viewer. I need to be able to display a collection of shapes that will be updated dynamically. I am having trouble understanding the best way to do this.
I currently am using Entities and using CallbackProperties to allow for the updating of shapes.
You can through this into a sandcastle to get an idea of how I am doing this. There is a polygon object that is being used as the basis for the cesiumCallback, and it is getting edited by another piece of code. (simulated with the setTimeout)
var viewer = new Cesium.Viewer('cesiumContainer', {});
var polygon = {};
polygon.coordinates = [
{longitude: 0, latitude: 0, altitude: 0},
{longitude: 10, latitude: 10, altitude: 0},
{longitude: 10, latitude: 0, altitude: 0}
];
// converts generic style options to cesium one (aka color -> material)
var polOpts = {};
// function for getting location
polOpts.hierarchy = new Cesium.CallbackProperty(function() {
var hierarchy = [];
for (var i = 0; i < polygon.coordinates.length; i++) {
var coordinate = polygon.coordinates[i];
hierarchy.push(Cesium.Cartesian3.fromDegrees(coordinate.longitude, coordinate.latitude, coordinate.altitude));
}
return hierarchy;
}, false);
viewer.entities.add({polygon: polOpts});
setInterval(function(polygon){
polygon.coordinates[0].longitude--;
}.bind(this, polygon), 1000);
The polygon being passed in is a class that generically describes a polygon, so it has an array of coordinates and style options, as well as a render method that calls this method renderPolygon passing in itself.
This method of rendering shapes works for everything I need it to, but it is not very performant. There are two cases for shapes updating, one type of shape will be updated over a long period of time, as a slow rate like once every few seconds. The other is shapes that will will get updated many times, like thousands, in a few seconds, then not change again for a long time, if ever.
I had two ideas for how to fix this.
Idea 1:
Have two methods, a renderDynamicPolygon and a renderStaticPolygon.
The renderDynamicPolygon method would do the above functionality, using the cesiumCallbackProperties. This would be used for shapes that are getting updated many times during the short time they are being updated.
The renderStaticPolygon method would replace the entities properties that are using callbackProperties with constant values, once the updating is done.
This creates a lot of other work to make sure shapes are in the right state, and doesn't help the shapes that are being updated slowly over a long period of time.
Idea 2:
Similarly to how the primitives work, I tried removing the old entity and adding it again with its updated properties each time its need to be updated, but this resulted in flickering, and unlike primitives, i could not find a async property for entities.
I also tried using primitives. It worked great for polylines, I would simply remove the old one and add a new one with the updated properties. I was also using the async = false to ensure there was no flickering. This issue I ran into here was not all shapes can be created using primitives. (Is this true?)
The other thing I tried was using the geometry instance using the geometry and appearance. After going through the tutorial on the cesium website I was able to render a few shapes, and could update the appearance, but found it close to impossible to figure out how to update the shapes correctly, and also have a very hard time getting them to look correct. Shapes need to have the right shape, a fill color and opacity and a stroke color, opacity and weight. I tried to use the polygonOutlineGeometry, but had not luck.
What would be the best way to implement this? Are one of these options headed the right way or is there some other method of doing this I have not uncovered yet?
[Edit] I added an answer of where I have gotten, but still not complete and looking for answers.
I have came up with a pretty good solution to this, but it still has one small issue.
I made too ways of showing entities. I am calling one render and one paint. Render uses the the Cesium.CallbackProperty with the isConstant property true, and paint with the isConstantProperty false.
Then I created a function to change the an entity from render to paint and vice vera. It goes through the entities callback properties an uses the setCallback property to overwrite the property with a the correct function and isConstant value.
Example:
I create a ellipse based on a circle object I have defined.
// isConst is True if it is being "painted" and false if it is being "rendered"
ellipse: lenz.util.extend(this._getStyleOptions(circle), {
semiMinorAxis: new Cesium.CallbackProperty(
this._getRadius.bind(this, circle),
isConst
),
semiMajorAxis: new Cesium.CallbackProperty(
this._getRadius.bind(this, circle),
isConst
),
})
So when the shape is being updated (while the user is drawing a shape) the shape is rendered with the isConstant being false.
Then when the drawing is complete it is converted to the painted version using some code like this:
existingEntity.ellipse.semiMinorAxis.setCallback(
this._getRadius.bind(this, circle),
isConst
);
existingEntity.ellipse.semiMajorAxis.setCallback(
this._getRadius.bind(this, circle, 1),
isConst
);
This works great performance wise. I am able to draw hundreds of shapes without the frame dropping much at all. I have attached a screen shot of the cesium map with 612 entities before and after my changes, the frame rate is in the upper right using the chrome render tool.
Before: Locked up at fps 0.9
Note: I redacted the rest of the ui, witch makes the globe look cut off, sorry
And after the changes: The fps remains at 59.9, almost perfect!
Whenever the entity is 'converted' from using constant to not constant callback properties, it and all other entities of the same type flash off then on again. I cannot find a better way to do this conversion. I feel as thought there must still be some thing I am missing.
You could try using a PositionPropertyArray as the polygon's hierarchy with SampledPositionProperty for any dynamic positions and ConstantPositionProperty for any static positions. I'm not sure if it would perform any better than your solution, but it might be worth testing. Here is an example of how it might work that you can paste into the Cesium Sandcastle:
var viewer = new Cesium.Viewer('cesiumContainer', {});
// required if you want no interpolation of position between times
var noInterpolation = {
type: 'No Interpolation',
getRequiredDataPoints: function (degree) {
return 2;
},
interpolateOrderZero: function (x, xTable, yTable, yStride, result) {
if (!Cesium.defined(result)) {
result = new Array(yStride);
}
for (var i = 0; i < yStride; i++) {
result[i] = yTable[i];
}
return result;
}
};
var start = viewer.clock.currentTime;
// set up the sampled position property
var sampledPositionProperty = new Cesium.SampledPositionProperty();
sampledPositionProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD;
sampledPositionProperty.addSample(start, new Cesium.Cartesian3.fromDegrees(0, 0)); // initial position
sampledPositionProperty.setInterpolationOptions({
interpolationAlgorithm: noInterpolation
});
// set up the sampled position property array
var positions = [
sampledPositionProperty,
new Cesium.ConstantPositionProperty(new Cesium.Cartesian3.fromDegrees(10, 10)),
new Cesium.ConstantPositionProperty(new Cesium.Cartesian3.fromDegrees(10, 0))
];
// add the polygon to Cesium viewer
var polygonEntity = new Cesium.Entity({
polygon: {
hierarchy: new Cesium.PositionPropertyArray(positions)
}
});
viewer.zoomTo(viewer.entities.add(polygonEntity));
// add a sample every second
var counter = 1;
setInterval(function(positionArray) {
var time = new Cesium.JulianDate.addSeconds(start, counter, new Cesium.JulianDate());
var position = new Cesium.Cartesian3.fromDegrees(-counter, 0);
positionArray[0].addSample(time, position);
counter++;
}.bind(this, positions), 1000);
One nice thing about this is you can set the timeline start/end time to a reasonable range and use it to see your polygon at any time within the sample range so you can see the history of your polygons through time (See here for how to change the timeline start/end time). Additionally, you don't need to use timers to set the positions, the time is built in to the SampledPositionProperty (although you can still add samples asynchronously).
However, this also means that the position depends on the current time in the timeline instead of a real-time array value. And you might need to keep track of a time somewhere if you aren't adding all the samples at once.
I've also never done this using ellipses before, but the semiMinorAxis and semiMajorAxis are properties, so you might still be able to use a SampledProperty.
Of course, this doesn't really matter if there are still performance issues. Hopefully it will improve as you don't need to recreate the array from scratch each callback and, depending on how you're getting the data to update the polygons, you might be able to add multiple samples at once. This is just speculation, but it's something to consider.
EDIT
Cesium can handle quite a bit of samples added to a sampled position, for example in the above code if you add a million samples to the position it takes a few seconds to load them all, but renders the polygon at any time without any performance issues. To test this, instead of adding samples using a timer, just add them all directly to the property.
for (var i = 0; i < 1000000; i++) {
var time = new Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
var position = new Cesium.Cartesian3.fromDegrees(-(i % 2), 0);
positions[0].addSample(time, position);
}
However, if you run into memory problems currently there is no way to remove samples from a position property without accessing private variables. A work around would be to periodically create a new array containing new position properties and use the previous position property array's setValue() method to clear previous values or perhaps to use a TimeIntervalCollectionProperty as in this answer and remove time intervals with the removeInterval method.
I imagine this is a simple problem for anyone really familiar with Cesium's CZML files. I'm just trying to display a series of lat/long/alt points as a flight path using Cesium. Can someone tell me what the "position" tag should look like?
Unless I'm looking in the wrong places, I don't see a lot of examples for CZML. So it's hard to know what tags can be used and how to use them (and the Java console doesn't show the errors if you get them wrong).
In the Sandcastle CZML example on the Cesium website, the relevant section looks like this:
"position" : {
"interpolationAlgorithm" : "LAGRANGE",
"interpolationDegree" : 1,
"epoch" : "2012-08-04T16:00:00Z",
// Trimmed to just 2 points
"cartesian" : [0.0, -2379754.6637012, -4665332.88013588, 3628133.68924173,
3894.996219574019, -2291336.52323822, -4682359.21232197, 3662718.52171165]
}
If it's two points, why are there 8 values? If it was ECEF coordinates, I would expect only three per point...
For example, when I tried this, I got an "uncaught error" message in the console... which isn't very helpful:
"cartographic" : [-1.472853549, 0.589580778, 1000,
-1.472962668, 0.589739552, 1000 ]
According to the documentation, cartographic takes (long, lat, height) where long and lat are in radians and height is in meters.
The first coordinate is in each set of 4 is time, so it's actually (t, x, y, z). In the example you posted, t is the number of seconds after the specified epoch that the waypoint exists.
You can also use cartographicRadians or cartographicDegrees, but they would still be specified as (t, lon, lat, alt).
If you want to draw a route that is not time-dynamic (i.e. just a static line) you can use the polyline CZML object instead; which has a list of x/y/z positions without time.
Matthews answer is correct, took a littke bit of tweaking to get it working so for others looking at this here is an example showing cartographicDegrees in use.
"position": {
"interpolationAlgorithm": "LAGRANGE",
"interpolationDegree": 1,
"epoch": "2012-08-04T16:00:00Z",
"cartographicDegrees": [
//time, lat, long, alt
0,-116.52,35.02,80,
300,-116.52,35.04,4000,
600,-116.52,35.08,9000,
900,-116.52,35.02,3000,
1100,-116.52,35.02,1000,
1400,-116.52,35.02,100
]
}
Following up on a previous question... I've got my minimal horizon chart example much more minimaler than before ( minimal cubism.js horizon chart example (TypeError: callback is not a function) )
<body>
<div class="mag"></div>
<script type="text/javascript">
var myContext = cubism.context();
var myMetr = myContext.metric(function(start, stop, step, callback) {
d3.json("../json/600s.json.php?t0=" + start/1000 + "&t1=" + stop/1000 + "&ss=" + step/1000, function(er, dt) {
if (!dt) return callback(new Error("unable to load data, or has NaNs"));
callback(null, dt.val);
});
});
var myHoriz = myContext.horizon()
.metric(myMetr);
d3.select(".mag")
.call(myHoriz);
</script>
</body>
The d3.json() bit calls a server side .php that I've written that returns a .json version of my measurements. The .php takes the start, stop, step (which cubism's context.metric() uses) as the t0, t1, and ss items in its http query string and sends back a .json file. The divides by 1000 are because I made my .php expect parameters in s, not ms. And the dt.val is because the actual array of my measurements is in the "val" member of the json output, e.g.
{
"other":"unused members...",
"n":5,
"val":[
22292.078125,
22292.03515625,
22292.005859375,
22292.02734375,
22292.021484375
]
}
The problem is, now that I've got it pared down to (I think) the bare minimum, AND I actually understand all of it instead of just pasting from other examples and hoping for the best (in which scenario, most things I try to change just break things instead of improving them), I need to start adding parameters and functions back to make it visually more useful.
Two problems first of all are, this measurement hovers all day around 22,300, and only varies +/- 10 maybe all day, so the graph is just a solid green rectangle, AND the label just says constantly "22k".
I've fixed the label with .format(d3.format(".3f")) (versus the default .2s which uses SI metric prefixes, thus the "22k" above).
What I can't figure out is how to use either axis, scale, extent, or what, so that this only shows a range of numbers that are relevant to the viewer. I don't actually care about the positive-green and negative-blue and darkening colours aspects of the horizon chart. I just used it as proof-of-concept to get the constantly-shifting window of measurements from my .json data source, but the part I really need to keep is the serverDelay, step, size, and such features of cubism.js that intelligently grab the initial window of data, and incrementally grab more via the .json requests.
So how do I keep the cubism bits I need, but usefully change my all-22300s graph to show the important +/- 10 units?
update re Scott Cameron's suggestion of horizon.extent([22315, 22320])... yes I had tried that and it had zero effect. Other things I've changed so far from "minimal" above...
var myHoriz = myContext.horizon()
.metric(myMetr)
.format(d3.format(".2f"))
.height(100)
.title("base1 (m): ")
.colors(["#08519c", "#006d2c"])
// .extent([22315, 22320]) // no effect with or without this line
;
I was able to improve the graph by using metric.subtract inserting it above the myHoriz line like so: (but it made the numerical label useless now):
var myMetr2 = myMetr.subtract(22315);
var myHoriz = myContext.horizon()
.metric(myMetr2)
.format...(continue as above)
All the examples seem so concise and expressive and work fine verbatim but so many of the tweaks I try to make to them seem to backfire, I'm not sure why that is. And similarly when I refer to the API wiki... maybe 4 out of 5 things I use from the API work immediately, but then I always seem to hit one that seems to have no effect, or breaks the chart completely. I'm not sure I've wrapped my head around how so many of the parameters being passed around are actually functions, for one thing.
Next hurdles after this scale/extent question, will be getting the horizontal time axis back (after having chopped it out to make things more minimal and easier to understand), and switching this from an area-looking graph to more of a line graph.
Anyway, all direction and suggestion appreciated.
Here's the one with the better vertical scale, but now the numerical label isn't what I want:
Have you tried horizon.extent? It lets you specify the [min, max] value for the horizon chart. By default, a linear scale will be created to map values within the extent to the pixels within the chart's height (specified with `horizon.height or default to 30 pixels).