Reading Files into Cesium/Custom CZML properties

Hello,

I am trying to figure out how to parse a file from the file system in Cesium. I know there are limitations in Javascript for accessing local files because of security issues. Is there a way to read in and parse files built into Cesium?

Ideally, I would like to store this data in CZML, but I am having a hard time figuring out how to do this, as it seems to be limited to the properties that are defined in CZML content. I have heard that it may be possible to create custom CZML properties. Would I be able to say, create a property which is an array that holds floating point values? And access that value easily in Cesium through CZML?

I found this article from over a year ago that describes a process of creating a custom CZML property, though it seems rarely complicated/cumbersome:

https://groups.google.com/forum/embed/?place=forum/cesium-dev&showsearch=true&showpopout=true&hideforumtitle=true&parenturl=http%3A%2F%2Fcesiumjs.org%2Fforum.html#!searchin/cesium-dev/custom$20czml/cesium-dev/79DMA7msqoI/_zajBujnliYJ

Has this been implemented yet?

Thanks a lot,

-Brian

Automatic handling of custom properties is still planned. The code to handle it yourself is somewhat simpler than what I wrote last year. Here’s a Sandcastle example that you can paste into here:

http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Cesium%20Viewer%20Widget.html

The main addition is processCustomProperty, which uses DynamicObject.addProperty to install the custom property on each dynamic object, and then uses processPacketData to fill that property with the appropriate type of Property (constant, time-interval, or sampled) based on the CZML. This function is installed into the global list of functions in CzmlDataSource.updaters.

If you run this, you can see that when you drag the timeline back and forth, the value logged to the console changes from 5 to 10 depending on the clock time, because the value of this particular custom property is defined using time intervals.

require([‘Cesium’], function(Cesium) {

“use strict”;

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

var czml = [{

“id” : “Vehicle”,

“availability” : “2012-08-04T16:00:00Z/2012-08-04T17:04:54.9962195740191Z”,

“customProperty” : [{

“interval” : “2012-08-04T16:00:00Z/2012-08-04T16:30:00Z”,

“number” : 5.0

},{

“interval” : “2012-08-04T16:30:00Z/2012-08-04T17:00:00Z”,

“number” : 10.0

}]

}];

function processCustomProperty(dynamicObject, packet, dynamicObjectCollection, sourceUri) {

var customPropertyData = packet.customProperty;

var customProperty = dynamicObject.customProperty;

if (customProperty === undefined) {

dynamicObject.addProperty(‘customProperty’);

}

Cesium.CzmlDataSource.processPacketData(Number, dynamicObject, ‘customProperty’, customPropertyData, undefined, sourceUri);

}

Cesium.CzmlDataSource.updaters.push(processCustomProperty);

var czmlDataSource = new Cesium.CzmlDataSource();

czmlDataSource.load(czml);

viewer.dataSources.add(czmlDataSource);

var dynamicObject = czmlDataSource.dynamicObjects.getById(‘Vehicle’);

var lastCustomPropertyValue;

viewer.clock.onTick.addEventListener(function(clock) {

var customPropertyValue = dynamicObject.customProperty.getValue(clock.currentTime);

if (lastCustomPropertyValue !== customPropertyValue) {

console.log(customPropertyValue);

lastCustomPropertyValue = customPropertyValue;

}

});

Sandcastle.finishedLoading();

});

Thanks for the response Scott.

I've updated to build 28 and was able to successfully run this example locally. I am also able to create my own custom properties for simple numbers. I am having trouble creating a custom property that is an array holding multiple values.

"azimuths":[1,2,3]

The error occurs on
"Cesium.CzmlDataSource.processPacketData(Array, dynamicObject, 'azimuths', customPropertyData4, undefined, sourceUri)"

Where I pass in Array as the type, etc...

The error I get is "Uncaught TypeError: Cannot read property 'length' of undefined (on line 444 of http://localhost:8080/Source/DynamicScene/CzmlDataSource.js)",

I'm not sure why this is undefined. Is there any way to declare a custom property that is an array of multiple values. I have no need of time intervals.

Scratch that. Maybe it was my ignorance of JSON/CZML getting in the way.

I was able to define the property as

        "azimuths":
        [
          {
              "array":[1,2,3,4]
          }
        ],

inside of my CZML object and it worked correctly. Thanks a lot for the help with the custom properties!

Yes, unlike most other types, CZML properties of type ‘Array’ require the type to specified, as you discovered, because the array syntax is used to specify time interval-based property values.

Hello Scott,

is there any way to load a bunch of properties at once? I.e.

var czml = [{

“id” : “Vehicle”,

“properties” : {
“myPuppet” : “cat”

“myName” : “test”

}

}];

I’d like to have all the properties in “properties” to be loaded as properties of the czml object in my scene :expressionless:

Yes. Basically, you need to call processPacketData for each property. This example also shows how to group properties together into subobjects. You can paste this into the CZML Sandcastle example to try it:

require([‘Cesium’], function(Cesium) {

“use strict”;

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

var czml = [{

“id” : “Vehicle”,

“availability” : “2012-08-04T16:00:00Z/2012-08-04T17:04:54.9962195740191Z”,

“properties” : {

“myPuppet” : “cat”,

“myName” : “test”

}

}];

var CustomProperties = function() {

this._definitionChanged = new Cesium.Event();

};

Object.defineProperties(CustomProperties.prototype, {

definitionChanged : {

get : function() {

return this._definitionChanged;

}

},

myPuppet : Cesium.createDynamicPropertyDescriptor(‘myPuppet’),

myName : Cesium.createDynamicPropertyDescriptor(‘myName’)

});

function processCustomProperties(dynamicObject, packet, dynamicObjectCollection, sourceUri) {

var propertiesData = packet.properties;

if (propertiesData === undefined) {

return;

}

var interval = propertiesData.interval;

if (interval !== undefined) {

interval = TimeInterval.fromIso8601(interval);

}

var properties = dynamicObject.properties;

if (properties === undefined) {

dynamicObject.addProperty(‘properties’);

dynamicObject.properties = properties = new CustomProperties();

}

Cesium.CzmlDataSource.processPacketData(String, properties, ‘myPuppet’, propertiesData.myPuppet, interval, sourceUri);

Cesium.CzmlDataSource.processPacketData(String, properties, ‘myName’, propertiesData.myName, interval, sourceUri);

}

Cesium.CzmlDataSource.updaters.push(processCustomProperties);

var czmlDataSource = new Cesium.CzmlDataSource();

czmlDataSource.load(czml);

viewer.dataSources.add(czmlDataSource);

var dynamicObject = czmlDataSource.dynamicObjects.getById(‘Vehicle’);

var lastValue;

viewer.clock.onTick.addEventListener(function(clock) {

var value = dynamicObject.properties.myName.getValue(clock.currentTime);

if (lastValue !== value) {

console.log(value);

lastValue = value;

}

});

Sandcastle.finishedLoading();

});

If you arrived at this thread looking for a simple way to embed data into a CZML file and retrieve it later, and you don't care about time-varying data, here is the simplest way I could find to do that.

You can paste this into a Sandbox to try it out:

[code]
var czml = [{
    "id" : "document",
    "name" : "CZML Extension",
    "version" : "1.0"
}, {
    // This entity will be invisible, since it does
    // not contain any renderable subobjects. Also if
    // you later find this entity with findById, the
    // "extraData" will be gone. We intercept the
    // extra date *before* the entity is created.
    "id": "myExtraDataEntity",
    "extraData": {
        "message": "Some string data",
        "list": [ "Some", "array", "data" ],
        "obj": { "some": "more", "stuff": 42 },
    }
}, {
    "id" : "rgba",
    "name" : "Rectangle using RGBA Colors",
    "rectangle" : {
        "coordinates" : {
            "wsenDegrees" : [-120, 40, -110, 50]
        },
        "fill" : true,
        "material" : {
            "solidColor" : {
                "color": {
                    "rgba" : [255, 0, 0, 100]
                }
            }
        },
        "outline" : false
    }
}];

var myExtraData = null;
var viewer = new Cesium.Viewer('cesiumContainer');
Cesium.CzmlDataSource.updaters.push(
    function(dynamicObject, packet, dynamicObjectCollection, sourceUri)
    {
        if ("extraData" in packet)
        {
            myExtraData = packet.extraData;
        }
    });
var dataSource = Cesium.CzmlDataSource.load(czml);
viewer.dataSources.add(dataSource);
viewer.zoomTo(dataSource).then(
    function()
    {
        console.log("message: " + myExtraData.message);
        console.log("list: "); console.log(myExtraData.list);
        console.log("obj: " + myExtraData.obj);
    });
[/code]

When you run this, you'll see in the Console tab:

message: Some string data
list:
Some,array,data
obj: [object Object]

The data will not be stored in the entity at all. This simple solution will probably work for many applications.

-Bryan
Institute for Disease Modeling

Four years later, I found this discussion is still very valuable. I copied and ran Bryan_Ressler code in SandCastle, and it worked perfectly.

Big thanks to Bryan, everyone involved in this “Reading Files into Cesium…” topic, and the wonderful 3D mapping of Cesium.

Cheers!
henryqnguyen