Adam
Adam

Reputation: 36703

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

Upvotes: 0

Views: 3812

Answers (2)

Mike LP
Mike LP

Reputation: 709

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);
};

Upvotes: 2

Adam
Adam

Reputation: 36703

There are some internal classes which emulate layers using the Cesium API calls. These are in the documentation so I'm assuming "public" API.

The layers must be added to a map and cannot be used in isolation. This is required as the map defines the projection etc.

For example VectorSynchronizer creates listeners on the underlying layers and keeps the Cesium scene in sync...

new olcs.VectorSynchronizer(map, viewer.scene);

Upvotes: 0

Related Questions