Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cesium in Drupal: Load Viewer on a Page in Drupal

IMPORTANT NOTE When instantiating the Cesium Object in a normal project, it does not have any properties that are prefixed with underscores (_dataSourceCollection, _dataSourceDisplay, etc). When instantiated in Drupal, however, about 40-45 properties are set on the object (all prefixed with underscores) in addition to the usual properties. This occurs in Drupal 7 or 8, and though I'm not sure if this is relevant to the problem I'm having, it was a noticeable discrepancy and thus I thought it should be shared.

I have added the Cesium library to a Drupal project by placing the file in sites/all/libraries/cesium/Cesium.js along with the Assets and Widgets folders and then also calling hook_library_info in a custom module

function cesium_library_info() {
  $libraries['cesium'] = array(
    'files' => array(
      'js' => 'Cesium.js',
    ),
    'path' => 'js',
    'library path' => libraries_get_path('cesium'),
    'version' => '1'
  );
  return $libraries;
}

I am then using hook_menu to return the following page callback:

function cesium_page()  {
  drupal_add_js(libraries_get_path('cesium') . '/Cesium.js');
  drupal_add_js(drupal_get_path('module', 'cesium') . '/js/mCesium.js');
  drupal_add_css(libraries_get_path('cesium') . '/Widgets/widgets.css');

  $page = array();

  $page['ces-container'] = array(
    '#prefix' => '<div id="myApp-cesium">',
    '#suffix' => '</div>',
    '#markup' => '<h1>Welcome to Cesium!',
  );
  return $page;
}

mCesium.js contains the code where Cesium is called from within Drupal.behaviors to attach a Viewer to my #myApp-cesium element.

Drupal.behaviors.cesium = {
  attach: function (context, settings) {

    var viewer = new Cesium.Viewer('myApp-cesium', {
        imageryProvider : new Cesium.createOpenStreetMapImageryProvider({
            url : 'http://thebestmaptiles.map.tile.com/',
        }),
        baseLayerPicker : false
    });
  }
}

This successfully creates the viewer on the appropriate element inside the main container of a Drupal page. The viewer is completely void of content, however, and the following error is found in the console:

Cesium.js:169769 Uncaught TypeError: this._dataSourceAdded is not a function

The value of "this" is not null or undefined, but seems to be the structure suggesting an incompletely formed Viewer object. This object is missing all the properties which would be prefixed with underscores, such as _dataSourceAdded.

Does anyone have any insight into why this might be?

Lastly, just prior to posting this, I saw that there is a Drupal module for Cesium which has something interesting happening in hook_libraries_info_alter where the Cesium codebase is being saved as public://cesium_base_url.js.

See lines 50-59 in cesium.module found at https://www.drupal.org/project/cesium

 47 function cesium_libraries_info_alter(&$libraries) {                                                                             
 48   $library = libraries_detect('cesium');                                                                                        
 49                                                                                                                                 
 50   if ($library['installed'] == TRUE) {                                                                                          
 51     $data = "var CESIUM_BASE_URL = '" . url($library['library path'] . '/Build/Cesium/') . "';";                                
 52     $jsfile = file_unmanaged_save_data($data, 'public://cesium_base_url.js', FILE_EXISTS_REPLACE);                              
 53                                                                                                                                 
 54     $libraries['cesium']['files']['js'][$jsfile] = array(                                                                       
 55       'data' => $jsfile,                                                                                                        
 56       'weight' => 0,                                                                                                            
 57       'group' => JS_LIBRARY,                                                                                                    
 58     );                                                                                                                          
 59   }                                                                                                                             
 60 }            

I'm unsure if that approach is related whatsoever, but I thought it was worth mentioning for the off chance that it is. In any case, I should also mention that I tried a fresh project to test if that module would work, but I believe the code to download the library has to be updated.

EDIT

I'd also like to mention that I've replicated the same situation, with the same error, on both Drupal 7 and Drupal 8.

Here is the code, as loaded without Drupal.behaviors, which, again, creates the same situation with an incompetely loaded Cesium.Viewer object:

setTimeout(function() {

    var viewer = new Cesium.Viewer('myApp-cesium', {
        imageryProvider : new Cesium.createOpenStreetMapImageryProvider({
            url : 'http://thebestmaptiles.map.tile.com/',
        }),
        baseLayerPicker : false
    });
}, 5000);

ANOTHER UPDATE:

Placing the javascript inside a jQuery wrapper doesn't seem to offer any benefit.

I've also tried to see if adding the libraries in a hook_init() function would help, but it does not.

ANOTHER UPDATE:

Notable Discrepancies - Viewer Object in HelloWorld App vs Drupal

Environment Variables for Observations:

I am sending "this" to console.log inside the Viewer function of Cesium.js, AFTER all the private properties have been set (properties prefixed with an _underscore), and just prior to calling the _dataSourceAdded function on the Viewer object (line 169769 on my modified, unminified Cesium.js)

console.log(this);
var dataSourceLength = dataSourceCollection.length;
for (var i = 0; i < dataSourceLength; i++) {
    this._dataSourceAdded(dataSourceCollection, dataSourceCollection.get
   (i));          

Here are the main differences:

HelloWorld App - 41 properties (according to console.log(Object.keys(this).length);)

  • Recognized as a "Viewer" object

  • Looping over "key in obj" found 61 properties

  • 53 of these were private

Drupal App - 41 properties (Object.keys(this).length))

  • Recognized as "Object" object

  • Looping over "key in obj" found 61 properties

  • 0 of these were private

like image 818
Emmanuel Buckshi Avatar asked Sep 10 '16 17:09

Emmanuel Buckshi


2 Answers

The code above originally had this line to create a viewer:

var viewer = Cesium.Viewer('myApp-cesium',

It was missing the new keyword:

var viewer = new Cesium.Viewer('myApp-cesium',

The new keyword is needed to correctly construct a new instance of the Cesium viewer. For details on what new does exactly, see this answer on Stack Overflow.

like image 117
emackey Avatar answered Oct 21 '22 05:10

emackey


Some progress made on this. It turns out, each of the Assets/Widgets/Workers directories had to be located within libraries/cesium/Build/Cesium/

Now, the globe appears normally and seems to be functioning (need to test to see if there are limitations). The "this._dataSourceAdded is not a function" error still remains, however, though it doesn't seem to be causing any issues as far as I can yet tell.

I will update after more testing.

like image 44
Emmanuel Buckshi Avatar answered Oct 21 '22 05:10

Emmanuel Buckshi