Rectangle following an object

Hey,
I am trying to get a rectangle to encircle my object as it move. I’ve included a picture in case its not clear what I meant. Right now I have my objects (basically taken from examples) and I have a rectangle that uses the position of the object to determine its own position, but I cannot get it to move! I am wondering if by piecing together bits of example code I missed something.

This is the code for my rectangle

// Draw the outline of a box.

var dimensions = new Cesium.Cartesian3(400000.0, 0, 400000);

// Box geometries are initially centered on the origin.

// We can use a model matrix to position the box on the

// globe surface.

var positionOnEllipsoid = czmlDataSource.entities.getById(“object1”).position.getValue(viewer.clock.currentTime);

var boxModelMatrix = Cesium.Matrix4.multiplyByTranslation(

Cesium.Transforms.eastNorthUpToFixedFrame(positionOnEllipsoid),

new Cesium.Cartesian3(0.0, 0.0, dimensions.z * 0.5), new Cesium.Matrix4());

// Create a box outline geometry.

var boxOutlineGeometry = Cesium.BoxOutlineGeometry.fromDimensions({

dimensions : dimensions

});

// Create a geometry instance using the geometry

// and model matrix created above.

var boxOutlineInstance = new Cesium.GeometryInstance({

geometry : boxOutlineGeometry,

modelMatrix : boxModelMatrix,

attributes : {

color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE)

}

});

//Multiple Boxes

var positionOnEllipsoid2 = czmlDataSource.entities.getById(“object2”).position.getValue(viewer.clock.currentTime);

var boxModelMatrix2 = Cesium.Matrix4.multiplyByTranslation(

Cesium.Transforms.eastNorthUpToFixedFrame(positionOnEllipsoid2),

new Cesium.Cartesian3(0.0, 0.0, dimensions.z * 0.5), new Cesium.Matrix4());

var boxOutlineGeometry2 = Cesium.BoxOutlineGeometry.fromDimensions({

dimensions : dimensions

});

var boxOutlineInstance2 = new Cesium.GeometryInstance({

geometry : boxOutlineGeometry2,

modelMatrix : boxModelMatrix2,

attributes : {

color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE)

}

});

// Add the geometry instance to primitives.

scene.primitives.add(new Cesium.Primitive({

geometryInstances : [boxOutlineInstance, boxOutlineInstance2],

appearance : new Cesium.PerInstanceColorAppearance({

flat : true,

renderState : {

depthTest : {

enabled : true

},

lineWidth : Math.min(2.0, scene.maximumAliasedLineWidth)

}

})

}));

``

Any suggestions would be really helpful

Hmm. Haven’t used the CZML/dynamic-data API yet myself, but… doesn’t that depend on your CZMl data having multiple position values for that entity over time? Seems like if you only have given it a single time/position value, or you gave it multiple position values and the current time is past the last of those, then the result of that .position.getValue() call won’t ever change.

Can you provide some more context as to what exactly you want to accomplish, and what code/data you’ve got so far?

Yeah, assuming I’m interpreting your code and data correctly, you’re seeing the second possibility I listed. When you use “viewer.clock.startTime”, I believe it’s going to be trying to look up the position of the object at the time you started your script, as that’s when the clock was initialized. Since you don’t have any positional data covering the time period of 2015-01-13:whateverYourCurrentTimeIs, it’ll just use the “last” value before that. Also, looking at https://github.com/AnalyticalGraphicsInc/cesium/wiki/CZML-Content, I’m not sure the contents of your “cartographicDegrees” array is correct. You’ve got multiple entries in there, but they’re in groups of 3, and I think you need them to be in groups of 4 (lat/lon/alt/timestamp). Also, now that I look at it… you’re always using viewer.clock.startTime rather than viewer.clock.currentTime, so that lookup will always use the same timestamp.

Basically, it looks like your code is trying to update positions based on position-value-at-time, but you don’t HAVE multiple position-values-at-time that match the timestamp, and you’re effectively using a constant timestamp rather than a varying one.

Can’t spend much further time on this myself, but hopefully that’ll point you in the right direction.

Ahhh… scratch what I said about the cartographicDegrees array. Misread that. You do have it in groups of 4. However, if I’m reading this and that wiki entry correctly, all the position sets have the exact same timestamp value, which is going to be 2012-08-04 + about 4.8 days, so 2012-08-08:somethingsomething. Meanwhile, viewer.clock.startTime is gonna be 2015-01-13:something. Try changing the “epoch” value to be 2015-01-13:something, make each seconds value of each position value be spread out, and use viewer.clock.currentTime instead of .startTime.

I’ll repeat my caveat that I have not worked with these APIs directly, so my advice could be entirely wrong. Having said that, this is what I think I know so far:

  • CZML allows you to supply varying data values with timestamps attached (for all fields)
  • When you use “.someDynamicField.getValue(someTimestamp)”, it tries to find an entry with a timestamp that exactly matches. If none found, it tries to interpolate what the value should be for that timestamp based on the nearest known values before and after that timestamp. Therefore, if the timestamp requested is after the last timestamp supplied, it will simply use that last supplied value.
  • When you create a Cesium.Viewer instance without passing in a specifically initialized Clock instance, it creates a new Clock instance initialized to right now
  • The “cartographicDegrees” array ought to be 4-value groups: lat, lon, alt, and seconds-since-“epoch”-value-provided-for-this-object.

Based on that info, what I think I’m seeing is:

  • You have an “epoch” value and timestamps for your “cartographicDegrees” position values, but those timestamps are all the same (422000 seconds, or 4.8 days). Therefore, there can be no position changing, because there’s no timestamp differences.
  • Even if they WERE different, the default Clock instance in the Viewer is going to be initialized with the current time when you run the script, so 2015-01-13:whatever. If you use that to try to retrieve position values, 2015-01-13 is way later than 2012-08-04, so it will only ever use the last timestamp entry provided, which is obviously going to be the same value each time.
  • I’m pretty sure the clock.startTime value doesn’t change once it’s created, while the .currentTime value does. So, even if you had a Clock initialized to 2012-08-04:something, using .startTime would retrieve the same timestamped value each time.

So, to fix this: make sure your position entries have varying timestamp values, you’ll probably need to create a custom Clock instance covering the time range you want (including minutes, which your last example didn’t look like it did), and you’ll want to use clock.currentTime as the timestamp passed to position.getValue() instead of clock.startTime.

Again, that’s all advice being given without having used these APIs directly myself. If I’ve said something wrong, I’d appreciate it if someone with more CZML experience could step in and correct things.

My bad, misread that entry. Yeah, time is first.

Okay, so, clock. Looking at the docs, you should be able to create a new Clock instance with a specific time range, and pass it in to the Viewer, like this:

var startTime = Cesium.JulianDate.fromIso8601(“2012-08-04T16:00:00Z”);

var viewer = new Cesium.Viewer(‘cesiumContainer’, {
clock : new Cesium.Clock({
startTime : startTime,
currentTime : startTime,
stopTime : Cesium.JulianDate.fromIso8601(“2012-08-04T18:00:00Z”)
})
});

``

That should create a Clock that covers the time range you’re interested in. Since the Cesium Viewer is already set up with a timeline that uses the clock, the CZML aspects should just work, I think.

Now, the other issue is getting YOUR code to work. I don’t yet see anything that shows your code running more than once. You’re going to want create your primitives once, but then you’ll need to continue retrieving the current position-at-time and updating the primitives’ position. Couple ways you could do that. You could add a callback to your Clock instance’s onTick() event,although that might be a bit too fine-grained. You could also set up a recursive timer loop using Javascript’s setTimeout() function, like this:

function updatePrimitivePositions() {
// get source object position at current time
// set primitive positions based on that position

setTimeout(updatePrimitivePositions, 1000);

}
updatePrimitivePositions();

``

In that function, you run your position retrieval and update code. As the last step in the function, you tell Javascript to re-run that same function N milliseconds later. Then, after defining the function, you run it manually the first time, starting the loop.

Out of curiosity, what’s the end goal for all this?

Emily, I’ve been really busy so I haven’t been able to keep up to date on all of the forum threads, so if this reply isn’t totally relevant, I apologize.

That being said, from a quick reason it looks like you just want to put a box around existing items. loading from CZML Here’s a complete example that does that; you can copy/paste this code below into Sandcastle This just sticks a box around the ISS and zooms to it. The code inside the if block will become even more trivial with the next release, if you haven’t seen it, I recommend you read the Entity API post from yesterday about where things are heading.

var gallery = ‘…/…/SampleData/’;

var viewer = new Cesium.Viewer(‘cesiumContainer’);

var czmlDataSource = new Cesium.CzmlDataSource();

viewer.dataSources.add(czmlDataSource);

czmlDataSource.loadUrl(gallery + ‘simple.czml’).then(function(){

var entities = czmlDataSource.entities.entities;

for(var i =0; i < entities.length; i++){

var entity = entities[i];

if(/ISS/.test(entity.id)) {

var box = new Cesium.BoxGraphics();

box.dimensions = new Cesium.ConstantProperty(new Cesium.Cartesian3(1500, 1500, 1));

box.outline = new Cesium.ConstantProperty(true);

box.outlineColor = new Cesium.ConstantProperty(Cesium.Color.YELLOW);

box.fill = new Cesium.ConstantProperty(false);

entity.box = box;

viewer.trackedEntity = entity;

}

}

});

Also, if you need to manage/orient the box as an independent entity around the object, then you can do that by just creating a new Entity and setting it’s position to the satelliteEntity.position and then providing your own orientation.

Hope this helps!

Matt

What version of Cesium are you using? Running this code in Sandcastle on our website works as expected. You can play with box.dimensions to change the sizing, right now it looks like a rectangle because the Z dimension is 1.

http://cesiumjs.org/Cesium/Apps/Sandcastle/

image.png

load does not return a Promise because it is synchronous. (i.e. all processing happens immediately) so “then” is not available and you can just run your code immediately. If you want to know more about Promises, I recommend this article on the subject.

The /ISS/.test is using a regular expression to just find any object with ISS as part of its id. If you have the id already, you are better off just calling entities.getById.

With the above two points in mind, your new code would look something like the below, just replace ‘’ with whatever the id of the object in the CZML file is

czmlDataSource.load(Object1, ‘Test’);

var entity = czmlDataSource.entities.getById(’’);

var box = new Cesium.BoxGraphics();

box.dimensions = new Cesium.ConstantProperty(new Cesium.Cartesian3(1500, 1500, 1));

box.outline = new Cesium.ConstantProperty(true);

box.outlineColor = new Cesium.ConstantProperty(Cesium.Color.YELLOW);

box.fill = new Cesium.ConstantProperty(false);

entity.box = box;

viewer.trackedEntity = entity;

Google might have mangled my last code snippet, here it is again just in case. Also, note that entity will be undefined if the id doesn’t actually exist in the loaded CZML document.

czmlDataSource.load(Object1, ‘Test’);

var entity = czmlDataSource.entities.getById(’’);

var box = new Cesium.BoxGraphics();

box.dimensions = new Cesium.ConstantProperty(new Cesium.Cartesian3(1500, 1500, 1));

box.outline = new Cesium.ConstantProperty(true);

box.outlineColor = new Cesium.ConstantProperty(Cesium.Color.YELLOW);

box.fill = new Cesium.ConstantProperty(false);

entity.box = box;

viewer.trackedEntity = entity;

Might be a good time to go read up on some relevant Javascript concepts, as well as parts of the Cesium API.

Cesium’s API has several functions that use a concept called “promises”. A promise is an object that represents “something that will finish happening in the future”. In this case, CzmlDataSource has a “loadUrl()” function that takes the URL for a CZML file, and returns a promise object. At some point in the future, Cesium finishes loading the CZML file, and it will “resolve” the promise. You can attach additional callbacks to the original promise, and when the first promise is resolved, the attached promises will run themselves.

Next, the “.test()” function is part of the Javascript RegExp regular expressions class, which does string searching. What his code is doing is looking at the string ID of each entity that was loaded from that CZML file, and trying to find the one that has the text “ISS” in it.

The changes you made won’t work, in a couple ways. First, the CzmlDataSource.load() function takes a JS object that matches the CZML schema, plus a URL. I don’t see a definition for the variable “Object1” in your code, and I’m not sure what URL “Test” is. So, based on that and the error message, it looks like czmlDataSource.load() is returning undefined, and of course you can’t call .then() on undefined.

The other issue is that I also don’t see a definition for a variable named “object1”, so the line “object1.test(entity.id)” is probably also going to fail.

Matt,
Do you have any suggestions on how to make multiple boxes do this? I duplicated the code and just changed the names and now I don’t get either box. Is there something to this functionality of BoxGeometry that it wont let me make more than one?
var czmlDataSource = new Cesium.CzmlDataSource();

viewer.dataSources.add(czmlDataSource);

czmlDataSource.load(Object1, ‘Test’);

var entity = czmlDataSource.entities.getById(‘object1’);

var box = new Cesium.BoxGraphics();

box.dimensions = new Cesium.ConstantProperty(new Cesium.Cartesian3(1500, 1500, 1));

box.outline = new Cesium.ConstantProperty(true);

box.outlineColor = new Cesium.ConstantProperty(Cesium.Color.YELLOW);

box.fill = new Cesium.ConstantProperty(false);

entity.box = box;

viewer.trackedEntity = entity;

czmlDataSource.process(Object2, ‘Test’);

var entity2 = czmlDataSource.entities.getById(‘object2’);

var box2 = new Cesium.BoxGraphics();

box2.dimensions = new Cesium.ConstantProperty(new Cesium.Cartesian3(1500, 1500, 1));

box2.outline = new Cesium.ConstantProperty(true);

box2.outlineColor = new Cesium.ConstantProperty(Cesium.Color.YELLOW);

box2.fill = new Cesium.ConstantProperty(false);

entity2.box2 = box2;

viewer.trackedEntity = entity2;

``

I think the line “entity2.box2 = box2;” should be “entity2.box = box2;”.

Scott

Scott,
Thanks. that worked to get the box around the second object, but for some reason it still wont let me have two boxes on two different objects. Any other suggestions?

var czmlDataSource = new Cesium.CzmlDataSource();

viewer.dataSources.add(czmlDataSource);

czmlDataSource.load(Object1, ‘Test’);

var entity = czmlDataSource.entities.getById(‘object1’);

var box = new Cesium.BoxGraphics();

box.dimensions = new Cesium.ConstantProperty(new Cesium.Cartesian3(1500, 1500, 1));

box.outline = new Cesium.ConstantProperty(true);

box.outlineColor = new Cesium.ConstantProperty(Cesium.Color.YELLOW);

box.fill = new Cesium.ConstantProperty(false);

entity.box = box;

viewer.trackedEntity = entity;

czmlDataSource.process(Object2, ‘Test’);

var entity2 = czmlDataSource.entities.getById(‘object2’);

var box2 = new Cesium.BoxGraphics();

box2.dimensions = new Cesium.ConstantProperty(new Cesium.Cartesian3(1500, 1500, 1));

box2.outline = new Cesium.ConstantProperty(true);

box2.outlineColor = new Cesium.ConstantProperty(Cesium.Color.YELLOW);

box2.fill = new Cesium.ConstantProperty(false);

entity2.box = box2;

viewer.trackedEntity = entity2;

``

Emily,

I’ve attached my example which I cobbled together from both your and Matt’s information. Maybe you can tell what’s different.

Scott

Example.js (7.41 KB)

Scott,
Thanks, I think I got it now!