diff --git a/examples/reprojection.html b/examples/reprojection.html new file mode 100644 index 000000000..ce62651c6 --- /dev/null +++ b/examples/reprojection.html @@ -0,0 +1,16 @@ + + + + + + + Reprojection example + + + +
+ + + + + \ No newline at end of file diff --git a/examples/reprojection.js b/examples/reprojection.js new file mode 100644 index 000000000..c2b399f50 --- /dev/null +++ b/examples/reprojection.js @@ -0,0 +1,59 @@ +/** + * @module examples.reprojection + */ +const exports = {}; + +import proj4 from 'proj4'; +import {register} from 'ol/proj/proj4.js'; +import {get as getProjection} from 'ol/proj.js'; +import olTileWMS from 'ol/source/TileWMS.js'; +import olMap from 'ol/Map.js'; +import olLayerTile from 'ol/layer/Tile.js'; +import olView from 'ol/View.js'; +import OLCesium from 'olcs/OLCesium.js'; +import olSourceOSM from 'ol/source/OSM.js'; + +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'); +register(proj4); +const proj21781 = getProjection('EPSG:21781'); +proj21781.setExtent([485071.54, 75346.36, 828515.78, 299941.84]); +const source = new olTileWMS({ + attributions: ['© ' + + '' + + '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 olMap({ + layers: [ + new olLayerTile({source: new olSourceOSM()}), + new olLayerTile({ + source + }) + ], + target: 'map', + view: new olView({ + projection: 'EPSG:4326', + center: [6.56273, 46.51781], + zoom: 6 + }) +}); +const ol3d = new 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); + +document.getElementById('enable').addEventListener('click', () => ol3d.setEnabled(!ol3d.getEnabled())); + +export default exports; diff --git a/src/olcs/core.js b/src/olcs/core.js index a36cd97eb..294fc2982 100644 --- a/src/olcs/core.js +++ b/src/olcs/core.js @@ -12,6 +12,7 @@ import olSourceTileWMS from 'ol/source/TileWMS.js'; import {defaultImageLoadFunction} from 'ol/source/Image.js'; import olcsCoreOLImageryProvider from './core/OLImageryProvider.js'; import olcsUtil from './util.js'; +import {ENABLE_RASTER_REPROJECTION} from 'ol/reproj/common.js'; const exports = {}; @@ -421,7 +422,7 @@ exports.tileLayerToImageryLayer = function(olMap, olLayer, viewProj) { projection = viewProj; } - if (exports.isCesiumProjection(projection)) { + if (exports.isCesiumProjection(projection) || ENABLE_RASTER_REPROJECTION) { provider = new olcsCoreOLImageryProvider(olMap, source, viewProj); } // Projection not supported by Cesium diff --git a/src/olcs/core/OLImageryProvider.js b/src/olcs/core/OLImageryProvider.js index 46a4c947e..ba8286684 100644 --- a/src/olcs/core/OLImageryProvider.js +++ b/src/olcs/core/OLImageryProvider.js @@ -3,6 +3,9 @@ */ import {get as getProjection} from 'ol/proj.js'; import olcsUtil from '../util.js'; +import {ENABLE_RASTER_REPROJECTION} from 'ol/reproj/common.js'; +import olTileState from 'ol/TileState.js'; +import {listen, unlistenByKey} from 'ol/events.js'; import {Tile as TileSource} from 'ol/source.js'; @@ -110,6 +113,9 @@ class OLImageryProvider /* should not extend Cesium.ImageryProvider */ { this.tilingScheme_ = new Cesium.GeographicTilingScheme(); } else if (this.projection_ == getProjection('EPSG:3857')) { this.tilingScheme_ = new Cesium.WebMercatorTilingScheme(); + } else if (ENABLE_RASTER_REPROJECTION) { + this.tilingScheme_ = new Cesium.GeographicTilingScheme(); + this.projection_ = getProjection('EPSG:4326'); } else { return; } @@ -152,25 +158,52 @@ class OLImageryProvider /* should not extend Cesium.ImageryProvider */ { * @override */ requestImage(x, y, level) { - const tileUrlFunction = this.source_.getTileUrlFunction(); - if (tileUrlFunction && this.projection_) { + // 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 + + let y_ = y; + if (!olUseNewCoordinates) { + // OpenLayers version 3 to 5 tile coordinates increase from bottom to top + y_ = -y - 1; + } - // Cesium zoom level 0 is OpenLayers zoom level 1 for EPSG:4326 - const z_ = this.tilingScheme_ instanceof Cesium.GeographicTilingScheme ? level + 1 : level; + const tilegrid = this.source_.getTileGridForProjection(this.projection_); + if (z_ < tilegrid.getMinZoom() || z_ > tilegrid.getMaxZoom()) { + return Promise.resolve(this.emptyCanvas_); // no data + } - let y_ = y; - if (!olUseNewCoordinates) { - // OpenLayers version 3 to 5 tile coordinates increase from bottom to top - 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_; + 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 === olTileState.LOADED || state === olTileState.EMPTY) { + return Promise.resolve(tile.getImage()) || undefined; + } else if (state === olTileState.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 = listen(tile, 'change', (evt) => { + const state = tile.getState(); + if (state === olTileState.LOADED || state === olTileState.EMPTY) { + resolve(tile.getImage() || undefined); + unlistenByKey(unlisten); + } else if (state === olTileState.ERROR) { + resolve(undefined); // let Cesium continue retrieving later + unlistenByKey(unlisten); + } + }); + }); + return promise; } } } @@ -180,54 +213,54 @@ class OLImageryProvider /* should not extend Cesium.ImageryProvider */ { Object.defineProperties(OLImageryProvider.prototype, { 'ready': { 'get': /** @this {olcs.core.OLImageryProvider} */ - function() {return this.ready_;} + function() {return this.ready_;} }, 'rectangle': { 'get': /** @this {olcs.core.OLImageryProvider} */ - function() {return this.rectangle_;} + function() {return this.rectangle_;} }, 'tileWidth': { 'get': /** @this {olcs.core.OLImageryProvider} */ - function() { - const tg = this.source_.getTileGrid(); - return tg ? (Array.isArray(tg.getTileSize(0)) ? tg.getTileSize(0)[0] : tg.getTileSize(0)) : 256; - } + function() { + const tg = this.source_.getTileGrid(); + return tg ? (Array.isArray(tg.getTileSize(0)) ? tg.getTileSize(0)[0] : tg.getTileSize(0)) : 256; + } }, 'tileHeight': { 'get': /** @this {olcs.core.OLImageryProvider} */ - function() { - const tg = this.source_.getTileGrid(); - return tg ? (Array.isArray(tg.getTileSize(0)) ? tg.getTileSize(0)[1] : tg.getTileSize(0)) : 256; - } + function() { + const tg = this.source_.getTileGrid(); + return tg ? (Array.isArray(tg.getTileSize(0)) ? tg.getTileSize(0)[1] : tg.getTileSize(0)) : 256; + } }, 'maximumLevel': { 'get': /** @this {olcs.core.OLImageryProvider} */ - function() { - const tg = this.source_.getTileGrid(); - return tg ? tg.getMaxZoom() : 18; - } + function() { + const tg = this.source_.getTileGrid(); + return tg ? tg.getMaxZoom() : 18; + } }, 'minimumLevel': { 'get': /** @this {olcs.core.OLImageryProvider} */ - function() { - // WARNING: Do not use the minimum level (at least until the extent is - // properly set). Cesium assumes the minimumLevel to contain only - // a few tiles and tries to load them all at once -- this can - // freeze and/or crash the browser ! - return 0; - //var tg = this.source_.getTileGrid(); - //return tg ? tg.getMinZoom() : 0; - } + function() { + // WARNING: Do not use the minimum level (at least until the extent is + // properly set). Cesium assumes the minimumLevel to contain only + // a few tiles and tries to load them all at once -- this can + // freeze and/or crash the browser ! + return 0; + //var tg = this.source_.getTileGrid(); + //return tg ? tg.getMinZoom() : 0; + } }, 'tilingScheme': { 'get': /** @this {olcs.core.OLImageryProvider} */ - function() {return this.tilingScheme_;} + function() {return this.tilingScheme_;} }, 'tileDiscardPolicy': { @@ -236,12 +269,12 @@ Object.defineProperties(OLImageryProvider.prototype, { 'errorEvent': { 'get': /** @this {olcs.core.OLImageryProvider} */ - function() {return this.errorEvent_;} + function() {return this.errorEvent_;} }, 'proxy': { 'get': /** @this {olcs.core.OLImageryProvider} */ - function() {return this.proxy_;} + function() {return this.proxy_;} }, 'hasAlphaChannel': {