How do I properly replace data sorces, or data in a data source when new data is fetched?

1. A concise explanation of the problem you’re experiencing.

When I replace my custom data source entities, or the entire data source, I get an onTick error ( see below ). What is happening is the hermite spline points are undefined.

If I put a check in https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Core/Cartesian3.js#L537, cartesian is undefined, so it should throw the error.

If I put a check in https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Core/HermiteSpline.js#L541, I can check if ( !defined( points[ i ] ) return result; which short circuits the evaluation. This will happen a handful of times, and then rendering resumes proper execution.

My issue with this is I have to modify CesiumJS in order to prevent my inherited code error when reloading new data.

I’ve looked around and the way we were loading data was not recommended. I’ve updated the code to suspend events, and rather than remove the data source and add a new one just to populate it, I instead remove all entities and then add the new entities from the new data. Once all the new entities are drawn, I resume events on the data source. However, the issue remains. Whenever I try to load a new data set, I get a render error.

2. A minimal code example. If you’ve found a bug, this helps us reproduce and repair it.

let cesiumViewer = null;

let arcsDataSource = null;

const conversations = [ {

‘src_country’:‘CA’,

‘dst_country’:‘US’,

‘data’:12345

} ];

renderViewer() {

//Render viewer if we haven’t already done so

if (! cesiumViewer ) {

cesiumViewer = new Cesium.Viewer(this.cesiumContainer, {

navigationInstructionsInitiallyVisible: false,

timeline : false,

navigationHelpButton: false,

homeButton: false,

animation: false,

geocoder: false,

requestRenderMode: true, //not available in 1.44

baseLayerPicker: false,

/showRenderLoopErrors: false,///TODO: Uncomment for production. It’d be nice if errors went to console instead.

projectionPicker: true,

imageryProvider: new Cesium.UrlTemplateImageryProvider({

url : ‘/tiles/{z}/{x}/{y}.png’,

minimumLevel: 3,

maximumLevel:9,

customTags : {

Time: function(imageryProvider, x, y, level) {

return ‘Mimir Networks Custom Tag’

}

}

}),

mapProjection : new Cesium.WebMercatorProjection()

//infoBox: false,

});

//Synchronize clock to actual time

this.cesiumViewer.clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK;

//Add data source for arcs

this.arcsDataSource = new Cesium.CustomDataSource(‘arcs’);

this.cesiumViewer.dataSources.add(this.arcsDataSource);

//Add event handlers

const cesiumMapComponent = this;

cesiumViewer.scene.morphStart.addEventListener(function(sceneTransitioner,fromMode, toMode){

console.log( ‘morph start’);

clearArcs();

});

const props = this.props;

cesiumViewer.scene.morphComplete.addEventListener(function(sceneTransitioner,fromMode, toMode){

renderArcs( props );

});

cesiumViewer.selectedEntityChanged.addEventListener(function(selectedEntity){

//console.log( ‘selected entity’, selectedEntity );

if ( selectedEntity && selectedEntity.properties ) {

//console.log( selectedEntity )

const entityType = selectedEntity.properties.entityType.getValue();

switch( entityType ){

case ‘locality’:

const selected_locality = selectedEntity.properties.locality.getValue();

arcsDataSource.entities.values.forEach( entity => {

if( entity

&& entity.polyline

&& entity.polyline.material

&& entity.polyline.material.color ){

/* *

  • TODO: make changing polyline colours not

  • cause the rendering process to cresh

*/

//const color = cesiumMapComponent.selectConversationColor( entity.properties.arcId.getValue() , cesiumMapComponent )

//console.log( ‘setting this color’, color, ‘replacing this color’, entity.polyline.material.color.getValue() )

//entity.polyline.material.color.setValue( color );// = cesiumMapComponent.selectConversationColor( entity.properties.arcId.getValue() , cesiumMapComponent );

}

});

break;

}

}

if (selectedEntity && selectedEntity.properties && selectedEntity.properties.arcId) {

const arcId = selectedEntity.properties.arcId.getValue();

if ( arcId && selectedEntity.id !== arcId) {

const arcEntity = arcsDataSource.entities.getById( arcId );

cesiumViewer.selectedEntity = arcEntity;

}

}

});

}

}

clearArcs() {

//Remove the arcsDataSource entities if the datasource exists exists

//The false argument means don’t actually ‘destroy’ the objects.

//See https://groups.google.com/forum/#!topic/cesium-dev/qEqVH1j9k_k for the reasoning

if (this.arcsDataSource !== null) {

const entities = arcsDataSource._entityCollection;

console.log( arcsDataSource );

/* *

  • events should be suspended for performance, and it may prevent

  • render errors…maybe. Resume when all arcs are rendered.

*/

entities.suspendEvents();

entities.removeAll();

}

initArcs();

}

initArcs(){

if( arcsDataSource === null ){

arcsDataSource = new Cesium.CustomDataSource( ‘arcs’ );

arcsDataSource.errorEvent.addEventListener( e => { console.log( " data source event error. THIS NEVER TRIGGERS", e ) } );

console.log( ‘adding arcs data source’, arcsDataSource );

cesiumViewer.dataSources.add( arcsDataSource );

}

}

generateArcId( fromLocality, toLocality ){

return fromLocality + ‘>’ + toLocality;

}

renderSplineArc( arcId ){

/* I can provide this code if required. It’s 200+ lines of getting a midpoint and creating a hermite spline and drawing billboards which move along the arcs at a variable speed, based on “data” */

}

renderArcs( props ) {

conversations.forEach( conversation => {

const arcId = generateArcId( conversation[ focusField ], conversation[ correlatedField ] )

/* *

  • Rerender arcs when switching between 2D and 3D due to the arcs

  • not looking proper when projected when switching between

  • projections.

*/

renderSplineArc( arcId );

} )

console.log( ‘resume events’ )

arcsDataSource._entityCollection.resumeEvents();

}

clearArcs();//init called in clear

renderArcs( props );

https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Core/HermiteSpline.js#L541

if ( ! defined( points[ i ] ) return result;

3. Context. Why do you need to do this? We might know a better way to accomplish your goal.

So the user can provide parameters for fetching new data from the server, which goes back years.

4. The Cesium version you’re using, your operating system and browser.

CesiumJS 1.44, from npm ( going to upgrade to 1.46.1 to see if issue is resolved ).

I found the actual line of code and I’ve been able to put in a try catch in order to mitigate the crash.

The previous developer had leveraged closures. How can I make this so it doesn’t crash, because now I get some missing arcs with the try…catch.

let arcSpline = new Cesium.HermiteSpline.createNaturalCubic( {

points: [ fromVertex, midpointVertex, toVertex ],

times: [ 0.0, 0.5, 1.0 ]

});

(function( arcId, arcSpline, startTime, endTime, offset, color, scale){

// Add billboard along spline at point between 0 and 1

//console.log( ‘FOUR’, arcSpline )

//console.log( ‘FOUR.1’, ‘arcId’, arcId, ‘arcSpline’, arcSpline, ‘startTime’, startTime, ‘endTime’, endTime, ‘offset’, offset, ‘color’, color, ‘scale’, scale)

const cb = new Cesium.CallbackProperty( function(time, result) {

//console.log ( ‘FIVE’ );

//console.log( endTime, time )

if ( endTime < time ) {

var speed = Cesium.JulianDate.secondsDifference( endTime, startTime );

startTime = time;

endTime = Cesium.JulianDate.addSeconds( startTime, speed, new Cesium.JulianDate() );

}

//console.log ( ‘SIX’ );

var ilerpTime = inverseLerpCesiumJulianDate( startTime, endTime, time );

//console.log( ilerpTime );

var billboardOffset = ( ilerpTime + offset ) % 1;

//console.log ( ‘SEVEN’ );

try{

var newBillboardPosition = arcSpline.evaluate( billboardOffset );

}catch( error ){

return new Cesium.Cartesian3();

}

//console.log("ilerpTime: ", ilerpTime, ", billboardOffset: ", billboardOffset, "offset: ", offset, "i: ", i);

//console.log ( ‘EIGHT’ );

return newBillboardPosition;

//return arcSpline.evaluate(0.5);

}, false);

cesiumMapComponent.arcsDataSource.entities.add({

position: cb,

billboard: {

image: ‘/static/map_arc_particle.png’,

color: new Cesium.ReferenceProperty.fromString( cesiumMapComponent.arcsDataSource.entities, arcId + colorReferencePropertySuffix ),

width:32,

height:32,

scale: 1,

},

properties: {

‘entityType’ : ‘particle’,

‘arcId’ : arcId

}

});

} ) (arcId, arcSpline, startTime,endTime,offset,color,scale);

The evaluate function will throw if the time is outside of the range. Use either the clampTime or wrapTime functions to ensure the time you’re passing to that function is inside that range.