Search code examples
openlayers-3cesiumjs

Integrating OpenLayers 3 with Cesium Viewer


I'm developing using Cesium built on top of Cesium.Viewer. Cesium lacks some features so I wish to integrate with OpenLayers. I'd like to add existing OpenLayers layers to the Cesium.Viewer as if they were "imagery layers".

I've found ol3-cesium, however this only allows an entire OpenLayers map instance to be visualized on a Cesium.Scene which it creates for you. Cesium.Viewer also creates an instance of Cesium.Scene targeted at a given DOM element.

How can I add OpenLayers layers to a Cesium.Viewer?

Some code snippets for illustration

var olLayer1= new ol.layer.Tile({
  source: new ol.source.MapQuest({layer: 'sat'})
});
var olLayer2= new ol.layer.Vector({
  source : ol.source.Vector();
});

var map = new ol.Map({
  layers: [olLayer1, olLayer2],
  target: 'map',
  view: new ol.View({
  })
});

Existing Cesium viewer

var viewer = new Cesium.Viewer('cesium-map', {});
// viewer setup code

ol3-cesium initialization - but this doesn't allow usage with existing viewer??

var ol3d = new olcs.OLCesium({map: map}); // map is the ol.Map instance

Solution

  • I just looked through the initialization code for OL3-Cesium and while it is essentially a wrapper on top of Cesium, the means that they decided to implement a Cesium environment is not going to play nicely if you want a hybrid Cesium.Viewer and an OL3 object.

    I'm not sure of your comfort level with modifying JS libraries, but what I personally would do is make my own ol3 cesium viewer class. Something like this gist I just threw together.

    Just a warning, I have not tested this code yet. You may have to make some additional modifications if you receive errors. There may be a reason that the OL3-Cesium developers choose not to use the Cesium widget or viewer to initialize Cesium in their library, but I see no reason this wouldn't work.

    Here is the constructor, but you would want the whole Gist as a separate file in your ol3-cesium library. Put it in the same directory as the ol3Cesium.js file.

    excerpt from https://gist.github.com/maikuru/9e650bf88aed84982667

    olcs.OLCesiumViewer = function(options) {
    
        /**
         * @type {!ol.Map}
         * @private
         */
        this.map_ = options.map;
    
        var fillArea = 'position:absolute;top:0;left:0;width:100%;height:100%;';
    
        /**
         * @type {!Element}
         * @private
         */
        this.container_ = goog.dom.createDom(goog.dom.TagName.DIV,
                {style: fillArea + 'visibility:hidden;'});
    
        var targetElement = goog.dom.getElement(options.target || null);
        if (targetElement) {
            goog.dom.appendChild(targetElement, this.container_);
        } else {
            var vp = this.map_.getViewport();
            var oc = goog.dom.getElementByClass('ol-overlaycontainer', vp);
            if (oc) {
                goog.dom.insertSiblingBefore(this.container_, oc);
            }
        }
    
        /**
         * Whether the Cesium container is placed over the ol map.
         * @type {boolean}
         * @private
         */
        this.isOverMap_ = !goog.isDefAndNotNull(targetElement);
    
        /**
         * @type {!HTMLCanvasElement}
         * @private
         */
        this.canvas_ = /** @type {!HTMLCanvasElement} */
                (goog.dom.createDom(goog.dom.TagName.CANVAS, {style: fillArea}));
        this.canvas_.oncontextmenu = function() { return false; };
        this.canvas_.onselectstart = function() { return false; };
    
        goog.dom.appendChild(this.container_, this.canvas_);
    
        /**
         * @type {boolean}
         * @private
         */
        this.enabled_ = false;
    
        /**
         * @type {!Array.<ol.interaction.Interaction>}
         * @private
         */
        this.pausedInteractions_ = [];
    
        /**
         * @type {?ol.layer.Group}
         * @private
         */
        this.hiddenRootGroup_ = null;
    
        /**
         * @type {!Object.<Cesium.Viewer.Options>}
         * @private
         */
        var cesiumViewerConfig_ = (options.viewer || {}).scene3DOnly = true;
    
        /**
         * @type {!Cesium.Viewer}
         * @private
         */
        this.viewer_ = new Cesium.Viewer(this.container_, cesiumViewerConfig_);
    
        /**
         * @type {!Cesium.Scene}
         * @private
         */
        this.scene_ = this.viewer_.scene;
    
        var sscc = this.scene_.screenSpaceCameraController;
        sscc.inertiaSpin = 0;
        sscc.ineartiaTranslate = 0;
        sscc.inertiaZoom = 0;
    
        sscc.tiltEventTypes.push({
            'eventType': Cesium.CameraEventType.LEFT_DRAG,
            'modifier': Cesium.KeyboardEventModifier.SHIFT
        });
    
        sscc.tiltEventTypes.push({
            'eventType': Cesium.CameraEventType.LEFT_DRAG,
            'modifier': Cesium.KeyboardEventModifier.ALT
        });
    
        sscc.enableLook = false;
    
        this.scene_.camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z;
    
        /**
         * @type {!olcs.Camera}
         * @private
         */
        this.camera_ = new olcs.Camera(this.scene_, this.map_);
    
        /**
         * @type {!Cesium.Globe}
         * @private
         */
        this.globe_ = this.scene_.globe;
        this.scene_.skyAtmosphere = new Cesium.SkyAtmosphere();
    
        var synchronizers = goog.isDef(options.createSynchronizers) ?
                options.createSynchronizers(this.map_, this.scene_) :
                [
                    new olcs.RasterSynchronizer(this.map_, this.scene_),
                    new olcs.VectorSynchronizer(this.map_, this.scene_)
                ];
    
        for (var i = synchronizers.length - 1; i >= 0; --i) {
            synchronizers[i].synchronize();
        }
    
        if (this.isOverMap_) {
            // if in "stacked mode", hide everything except canvas (including credits)
            var credits = goog.dom.getNextElementSibling(this.canvas_);
            if (goog.isDefAndNotNull(credits)) {
                credits.style.display = 'none';
            }
        }
    
        this.camera_.readFromView();
    
        this.cesiumRenderingDelay_ = new goog.async.AnimationDelay(function(time) {
            this.scene_.initializeFrame();
            this.handleResize_();
            this.scene_.render();
            this.enabled_ && this.camera_.checkCameraChange();
            this.cesiumRenderingDelay_.start();
        }, undefined, this);
    };