diff --git a/CHANGES.md b/CHANGES.md
index e0d6febe5..2b8913e6f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,11 @@
# Changelog
-# v 1.36
+# v 1.37
+
+* Changes
+ * Add client side raster reprojection, see the example to see how to enable it.
+
+# v 1.36 - 2018-04-18
* Changes
* Rework the autorenderloop using Cesium RenderMode.
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) {};
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..8d7b67dff
--- /dev/null
+++ b/examples/reprojection.js
@@ -0,0 +1,59 @@
+/* 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');
+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 ' +
+ '+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..b8d4cf7ba 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 && olcs.core.OLImageryProvider.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,52 @@ 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 Promise.resolve(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 Promise.resolve(this.emptyCanvas_); // no data
+ // }
+
+
+ const state = tile.getState();
+ if (state === ol.TileState.LOADED || state === ol.TileState.EMPTY) {
+ return Promise.resolve(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;
}
};
+
+/**
+ * @type {boolean}
+ * @export
+ */
+olcs.core.OLImageryProvider.ENABLE_RASTER_REPROJECTION = false;