From 2df20fb04bba0aa8ac3251c118f272797e667ce3 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 30 Nov 2015 15:34:15 +0100 Subject: [PATCH 1/6] Add client side raster reprojection support --- CHANGES.md | 5 ++ examples/reprojection.html | 17 ++++++ examples/reprojection.js | 56 ++++++++++++++++++ src/olcs/core.js | 7 ++- src/olcs/core/olimageryprovider.js | 93 ++++++++++++++++++++---------- 5 files changed, 144 insertions(+), 34 deletions(-) create mode 100644 examples/reprojection.html create mode 100644 examples/reprojection.js diff --git a/CHANGES.md b/CHANGES.md index e0d6febe5..28f98ced6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Changelog +# v 1.37 + +* Changes + * Add client side raster reprojection. + # v 1.36 * Changes diff --git a/examples/reprojection.html b/examples/reprojection.html new file mode 100644 index 000000000..1f8b9776c --- /dev/null +++ b/examples/reprojection.html @@ -0,0 +1,17 @@ + + + + + + + ol3cesium example + + + +
+ + + + + + diff --git a/examples/reprojection.js b/examples/reprojection.js new file mode 100644 index 000000000..6bebde8ec --- /dev/null +++ b/examples/reprojection.js @@ -0,0 +1,56 @@ +/* eslint googshift/valid-provide-and-module: 0 */ + +goog.provide('examples.reprojection'); + +goog.require('olcs.OLCesium'); +goog.require('ol.View'); +goog.require('ol.Map'); +goog.require('ol.Attribution'); +goog.require('ol.proj'); +goog.require('ol.layer.Tile'); +goog.require('ol.source.TileWMS'); + +proj4.defs('EPSG:21781', '+proj=somerc +lat_0=46.95240555555556 ' + + '+lon_0=7.439583333333333 +k_0=1 +x_0=600000 +y_0=200000 +ellps=bessel ' + + '+towgs84=674.4,15.1,405.3,0,0,0,0 +units=m +no_defs'); +const proj21781 = ol.proj.get('EPSG:21781'); +proj21781.setExtent([485071.54, 75346.36, 828515.78, 299941.84]); + +const source = new ol.source.TileWMS({ + attributions: [new ol.Attribution({ + html: '© ' + + '' + + 'Pixelmap 1:1000000 / geo.admin.ch' + })], + crossOrigin: 'anonymous', + params: { + 'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale', + 'FORMAT': 'image/jpeg' + }, + url: 'http://wms.geo.admin.ch/', + projection: 'EPSG:21781' +}); + + +const ol2d = new ol.Map({ + layers: [ + new ol.layer.Tile({ + source + }) + ], + target: 'map', + view: new ol.View({ + projection: 'EPSG:21781', + center: ol.proj.transform([6.56273, 46.51781], 'EPSG:4326', 'EPSG:21781'), + zoom: 6 + }) +}); + +const ol3d = new olcs.OLCesium({map: ol2d}); +const scene = ol3d.getCesiumScene(); +const terrainProvider = new Cesium.CesiumTerrainProvider({ + url: '//assets.agi.com/stk-terrain/world' +}); +scene.terrainProvider = terrainProvider; +ol3d.setEnabled(true); diff --git a/src/olcs/core.js b/src/olcs/core.js index f9625cd34..2d10d5d0e 100644 --- a/src/olcs/core.js +++ b/src/olcs/core.js @@ -1,7 +1,9 @@ goog.provide('olcs.core'); -goog.require('ol.easing'); goog.require('goog.asserts'); + +goog.require('ol'); +goog.require('ol.easing'); goog.require('ol.layer.Tile'); goog.require('ol.layer.Image'); goog.require('ol.proj'); @@ -9,6 +11,7 @@ goog.require('ol.source.Image'); goog.require('ol.source.ImageWMS'); goog.require('ol.source.TileImage'); goog.require('ol.source.TileWMS'); + goog.require('olcs.core.OLImageryProvider'); goog.require('olcs.util'); @@ -393,7 +396,7 @@ olcs.core.tileLayerToImageryLayer = function(olLayer, viewProj) { projection = viewProj; } - if (olcs.core.isCesiumProjection(projection)) { + if (olcs.core.isCesiumProjection(projection) || ol.ENABLE_RASTER_REPROJECTION) { provider = new olcs.core.OLImageryProvider(source, viewProj); } // Projection not supported by Cesium diff --git a/src/olcs/core/olimageryprovider.js b/src/olcs/core/olimageryprovider.js index d790aa6d3..ac5121a61 100644 --- a/src/olcs/core/olimageryprovider.js +++ b/src/olcs/core/olimageryprovider.js @@ -1,17 +1,18 @@ goog.provide('olcs.core.OLImageryProvider'); +goog.require('ol'); +goog.require('ol.events'); goog.require('ol.proj'); +goog.require('ol.TileState'); + goog.require('olcs.util'); /** - * Special class derived from Cesium.ImageryProvider - * that is connected to the given ol.source.TileImage. + * Special class derived from Cesium.ImageryProvider that is connected to the given ol.source.TileImage. * @param {!ol.source.TileImage} source - * @param {ol.proj.Projection=} opt_fallbackProj Projection to assume if the - * projection of the source - * is not defined. + * @param {ol.proj.Projection=} opt_fallbackProj Projection to assume if the projection of the source is not defined. * @constructor * @struct * @extends {Cesium.ImageryProvider} @@ -34,34 +35,34 @@ olcs.core.OLImageryProvider = function(source, opt_fallbackProj) { this.projection_ = null; /** - * @type {?ol.proj.Projection} + * @type {Cesium.Rectangle|undefined} * @private */ - this.fallbackProj_ = opt_fallbackProj || null; + this.rectangle_ = undefined; /** - * @type {boolean} + * @type {Cesium.TilingScheme|undefined} * @private */ - this.ready_ = false; + this.tilingScheme_ = undefined; /** - * @type {?Cesium.Credit} + * @type {Cesium.Credit|undefined} * @private */ - this.credit_ = null; + this.credit_ = undefined; /** - * @type {?Cesium.TilingScheme} + * @type {?ol.proj.Projection} * @private */ - this.tilingScheme_ = null; + this.fallbackProj_ = opt_fallbackProj || null; /** - * @type {?Cesium.Rectangle} + * @type {boolean} * @private */ - this.rectangle_ = null; + this.ready_ = false; const proxy = this.source_.get('olcs.proxy'); if (proxy) { @@ -182,9 +183,14 @@ olcs.core.OLImageryProvider.prototype.handleSourceChanged_ = function() { this.tilingScheme_ = new Cesium.GeographicTilingScheme(); } else if (this.projection_ == ol.proj.get('EPSG:3857')) { this.tilingScheme_ = new Cesium.WebMercatorTilingScheme(); + } else if (ol.ENABLE_RASTER_REPROJECTION) { + this.tilingScheme_ = new Cesium.GeographicTilingScheme(); + this.projection_ = ol.proj.get('EPSG:4326'); // reproject } else { return; } + + // FIXME: should intersect with the source extent this.rectangle_ = this.tilingScheme_.rectangle; this.credit_ = olcs.core.OLImageryProvider.createCreditForSource(this.source_); @@ -230,23 +236,46 @@ olcs.core.OLImageryProvider.prototype.getTileCredits = function(x, y, level) { * @override */ olcs.core.OLImageryProvider.prototype.requestImage = function(x, y, level) { - const tileUrlFunction = this.source_.getTileUrlFunction(); - if (tileUrlFunction && this.projection_) { - - // Perform mapping of Cesium tile coordinates to OpenLayers tile coordinates: - // 1) Cesium zoom level 0 is OpenLayers zoom level 1 for EPSG:4326 - const z_ = this.tilingScheme_ instanceof Cesium.GeographicTilingScheme ? level + 1 : level; - // 2) OpenLayers tile coordinates increase from bottom to top - const y_ = -y - 1; - - let url = tileUrlFunction.call(this.source_, - [z_, x, y_], 1, this.projection_); - if (this.proxy_) { - url = this.proxy_.getURL(url); - } - return url ? Cesium.ImageryProvider.loadImage(this, url) : this.emptyCanvas_; + // Perform mapping of Cesium tile coordinates to ol3 tile coordinates: + // 1) Cesium zoom level 0 is OpenLayers zoom level 1 for EPSG:4326 + const z_ = this.tilingScheme_ instanceof Cesium.GeographicTilingScheme ? level + 1 : level; + // 2) OpenLayers tile coordinates increase from bottom to top + const y_ = -y - 1; + + const tilegrid = this.source_.getTileGridForProjection(this.projection_); + if (z_ < tilegrid.getMinZoom() || z_ > tilegrid.getMaxZoom()) { + return this.emptyCanvas_; // no data + } + + const tile = this.source_.getTile(z_, x, y_, 1, this.projection_); + + tile.load(); + + // not yet loaded! + // const image = tile.getImage(); + // if (!image || !image.src) { + // return this.emptyCanvas_; // no data + // } + + + const state = tile.getState(); + if (state === ol.TileState.LOADED || state === ol.TileState.EMPTY) { + return tile.getImage() || undefined; + } else if (state === ol.TileState.ERROR) { + return undefined; // let Cesium continue retrieving later } else { - // return empty canvas to stop Cesium from retrying later - return this.emptyCanvas_; + const promise = new Promise((resolve, reject) => { + const unlisten = ol.events.listen(tile, 'change', (evt) => { + const state = tile.getState(); + if (state === ol.TileState.LOADED || state === ol.TileState.EMPTY) { + resolve(tile.getImage() || undefined); + ol.events.unlistenByKey(unlisten); + } else if (state === ol.TileState.ERROR) { + resolve(undefined); // let Cesium continue retrieving later + ol.events.unlistenByKey(unlisten); + } + }); + }); + return promise; } }; From fa910e11dc543962294d34c5147f8fec3b80b1ca Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 16 Apr 2018 15:08:25 +0200 Subject: [PATCH 2/6] Fix requestImagery call --- src/olcs/core/olimageryprovider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/olcs/core/olimageryprovider.js b/src/olcs/core/olimageryprovider.js index ac5121a61..710c793ef 100644 --- a/src/olcs/core/olimageryprovider.js +++ b/src/olcs/core/olimageryprovider.js @@ -244,7 +244,7 @@ olcs.core.OLImageryProvider.prototype.requestImage = function(x, y, level) { const tilegrid = this.source_.getTileGridForProjection(this.projection_); if (z_ < tilegrid.getMinZoom() || z_ > tilegrid.getMaxZoom()) { - return this.emptyCanvas_; // no data + return Promise.resolve(this.emptyCanvas_); // no data } const tile = this.source_.getTile(z_, x, y_, 1, this.projection_); @@ -254,7 +254,7 @@ olcs.core.OLImageryProvider.prototype.requestImage = function(x, y, level) { // not yet loaded! // const image = tile.getImage(); // if (!image || !image.src) { - // return this.emptyCanvas_; // no data + // return Promise.resolve(this.emptyCanvas_); // no data // } From 4904f1ea3ba97765cb62f7446f10db1e34f459c7 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 16 Apr 2018 15:10:30 +0200 Subject: [PATCH 3/6] Fix Cesium extern --- Cesium.externs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cesium.externs.js b/Cesium.externs.js index a2e648a0c..08a198d84 100644 --- a/Cesium.externs.js +++ b/Cesium.externs.js @@ -2133,7 +2133,7 @@ Cesium.ImageryProvider.prototype.getTileCredits = function(x, y, level) {}; * @param {number} x The tile X coordinate. * @param {number} y The tile Y coordinate. * @param {number} level The tile level. - * @return {Object|undefined} + * @return {Promise|undefined} */ Cesium.ImageryProvider.prototype.requestImage = function(x, y, level) {}; From bc6baa51745df6760018863e3cbd0b1c9523c4eb Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Tue, 17 Apr 2018 12:50:10 +0200 Subject: [PATCH 4/6] More fixes --- src/olcs/core/olimageryprovider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/olcs/core/olimageryprovider.js b/src/olcs/core/olimageryprovider.js index 710c793ef..8fbd24311 100644 --- a/src/olcs/core/olimageryprovider.js +++ b/src/olcs/core/olimageryprovider.js @@ -260,7 +260,7 @@ olcs.core.OLImageryProvider.prototype.requestImage = function(x, y, level) { const state = tile.getState(); if (state === ol.TileState.LOADED || state === ol.TileState.EMPTY) { - return tile.getImage() || undefined; + return Promise.resolve(tile.getImage()) || undefined; } else if (state === ol.TileState.ERROR) { return undefined; // let Cesium continue retrieving later } else { From ac724d4f92b5988f245dc950eb401974b2eb3dc3 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Tue, 1 May 2018 17:47:00 +0200 Subject: [PATCH 5/6] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 28f98ced6..4f954d911 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ * Changes * Add client side raster reprojection. -# v 1.36 +# v 1.36 - 2018-04-18 * Changes * Rework the autorenderloop using Cesium RenderMode. From 8a43c2eeace33ec090351a097fa281c82a82fa2d Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Tue, 1 May 2018 17:49:28 +0200 Subject: [PATCH 6/6] Hide feature behind a flag --- CHANGES.md | 2 +- examples/reprojection.js | 3 +++ src/olcs/core/olimageryprovider.js | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4f954d911..2b8913e6f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,7 +3,7 @@ # v 1.37 * Changes - * Add client side raster reprojection. + * Add client side raster reprojection, see the example to see how to enable it. # v 1.36 - 2018-04-18 diff --git a/examples/reprojection.js b/examples/reprojection.js index 6bebde8ec..8d7b67dff 100644 --- a/examples/reprojection.js +++ b/examples/reprojection.js @@ -9,6 +9,9 @@ goog.require('ol.Attribution'); goog.require('ol.proj'); goog.require('ol.layer.Tile'); goog.require('ol.source.TileWMS'); +goog.require('olcs.core.OLImageryProvider'); + +olcs.core.OLImageryProvider.ENABLE_RASTER_REPROJECTION = true; proj4.defs('EPSG:21781', '+proj=somerc +lat_0=46.95240555555556 ' + '+lon_0=7.439583333333333 +k_0=1 +x_0=600000 +y_0=200000 +ellps=bessel ' + diff --git a/src/olcs/core/olimageryprovider.js b/src/olcs/core/olimageryprovider.js index 8fbd24311..b8d4cf7ba 100644 --- a/src/olcs/core/olimageryprovider.js +++ b/src/olcs/core/olimageryprovider.js @@ -183,7 +183,7 @@ olcs.core.OLImageryProvider.prototype.handleSourceChanged_ = function() { this.tilingScheme_ = new Cesium.GeographicTilingScheme(); } else if (this.projection_ == ol.proj.get('EPSG:3857')) { this.tilingScheme_ = new Cesium.WebMercatorTilingScheme(); - } else if (ol.ENABLE_RASTER_REPROJECTION) { + } else if (ol.ENABLE_RASTER_REPROJECTION && olcs.core.OLImageryProvider.ENABLE_RASTER_REPROJECTION) { this.tilingScheme_ = new Cesium.GeographicTilingScheme(); this.projection_ = ol.proj.get('EPSG:4326'); // reproject } else { @@ -279,3 +279,9 @@ olcs.core.OLImageryProvider.prototype.requestImage = function(x, y, level) { return promise; } }; + +/** + * @type {boolean} + * @export + */ +olcs.core.OLImageryProvider.ENABLE_RASTER_REPROJECTION = false;