From eb4a8683e255d97df1ca0243286abe9a8d3d0e60 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 31 Oct 2012 13:17:58 +0100 Subject: [PATCH] Projection handling improvements * The Map's private createProjection_ method is now a public method of the Projection. It is also used by TiledWMSSource to calculate the appropriate projection from the user configuration. * ol.Projection.addProjection now adds the transformation for the source == target case. This makes it easy for the user to create custom projection maps without including proj4js and without adding a custom transformation, as long as the userProjection equals the projection. * The TiledWMSSource now uses the same default resolutions as the map (i.e. the OSM resolutions) * The modulo calculation for wrapping extents now works for all grid configurations, by not calculating the number of columns based on an assumption about the way the zoom levels relate to resolutions. * The wms-custom-proj example now shows how to properly configure a custom resolution, i.e. by using the validity extent of the projection as its extent. --- examples/wms-custom-proj.js | 18 ++++++++++++------ src/ol/map.js | 22 ++-------------------- src/ol/projection.js | 25 +++++++++++++++++++++++-- src/ol/source/tiledwmssource.js | 17 ++++++++++------- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/examples/wms-custom-proj.js b/examples/wms-custom-proj.js index 357235d5c5..d6da12b1e4 100644 --- a/examples/wms-custom-proj.js +++ b/examples/wms-custom-proj.js @@ -13,11 +13,12 @@ if (goog.DEBUG) { goog.debug.Logger.getLogger('ol').setLevel(goog.debug.Logger.Level.INFO); } +var epsg21781 = new ol.Projection('EPSG:21781', ol.ProjectionUnits.METERS, + // Validity extent from http://spatialreference.org + new ol.Extent(485869.5728, 76443.1884, 837076.5648, 299941.7864)); +ol.Projection.addProjection(epsg21781); + var extent = new ol.Extent(420000, 30000, 900000, 350000); - -var epsg21781 = new ol.Projection('EPSG:21781', - ol.ProjectionUnits.METERS, extent); - var layers = new ol.Collection([ new ol.layer.TileLayer({ source: new ol.source.TiledWMS({ @@ -28,7 +29,8 @@ var layers = new ol.Collection([ 'Pixelmap 1:1000000 / geo.admin.ch')], crossOrigin: null, params: {'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale'}, - projection: epsg21781 + projection: epsg21781, + extent: extent }) }), new ol.layer.TileLayer({ @@ -40,7 +42,8 @@ var layers = new ol.Collection([ 'National parks / geo.admin.ch')], crossOrigin: null, params: {'LAYERS': 'ch.bafu.schutzgebiete-paerke_nationaler_bedeutung'}, - projection: epsg21781 + projection: epsg21781, + extent: extent }) }) ]); @@ -49,6 +52,9 @@ var map = new ol.Map({ renderer: ol.RendererHint.DOM, center: new ol.Coordinate(660000, 190000), projection: epsg21781, + // By setting userProjection to the same as projection, we do not need + // proj4js because we do not need any transforms. + userProjection: epsg21781, layers: layers, target: 'map', zoom: 9 diff --git a/src/ol/map.js b/src/ol/map.js index b3242ff03b..6ad2e77d52 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -980,7 +980,7 @@ ol.Map.createOptionsInternal = function(mapOptions) { values[ol.MapProperty.LAYERS] = goog.isDef(mapOptions.layers) ? mapOptions.layers : new ol.Collection(); - values[ol.MapProperty.PROJECTION] = ol.Map.createProjection_( + values[ol.MapProperty.PROJECTION] = ol.Projection.createProjection( mapOptions.projection, 'EPSG:3857'); if (goog.isDef(mapOptions.resolution)) { @@ -990,7 +990,7 @@ ol.Map.createOptionsInternal = function(mapOptions) { ol.Projection.EPSG_3857_HALF_SIZE / (128 << mapOptions.zoom); } - values[ol.MapProperty.USER_PROJECTION] = ol.Map.createProjection_( + values[ol.MapProperty.USER_PROJECTION] = ol.Projection.createProjection( mapOptions.userProjection, 'EPSG:4326'); /** @@ -1183,21 +1183,3 @@ ol.Map.createInteractions_ = function(mapOptions) { return interactions; }; - - -/** - * @private - * @param {ol.Projection|string|undefined} projection Projection. - * @param {string} defaultCode Default code. - * @return {ol.Projection} Projection. - */ -ol.Map.createProjection_ = function(projection, defaultCode) { - if (!goog.isDefAndNotNull(projection)) { - return ol.Projection.getFromCode(defaultCode); - } else if (goog.isString(projection)) { - return ol.Projection.getFromCode(projection); - } else { - goog.asserts.assert(projection instanceof ol.Projection); - return projection; - } -}; diff --git a/src/ol/projection.js b/src/ol/projection.js index 2dee26c18c..d64c2d68e0 100644 --- a/src/ol/projection.js +++ b/src/ol/projection.js @@ -137,8 +137,10 @@ ol.Projection.addEquivalentProjections_ = function(projections) { ol.Projection.addProjections(projections); goog.array.forEach(projections, function(source) { goog.array.forEach(projections, function(destination) { - ol.Projection.addTransform( - source, destination, ol.Projection.cloneTransform); + if (source !== destination) { + ol.Projection.addTransform( + source, destination, ol.Projection.cloneTransform); + } }); }); }; @@ -181,6 +183,8 @@ ol.Projection.addProjection = function(projection) { var code = projection.getCode(); goog.asserts.assert(!goog.object.containsKey(projections, code)); projections[code] = projection; + ol.Projection.addTransform( + projection, projection, ol.Projection.cloneTransform); }; @@ -194,6 +198,23 @@ ol.Projection.addProjections = function(projections) { }; +/** + * @param {ol.Projection|string|undefined} projection Projection. + * @param {string} defaultCode Default code. + * @return {ol.Projection} Projection. + */ +ol.Projection.createProjection = function(projection, defaultCode) { + if (!goog.isDefAndNotNull(projection)) { + return ol.Projection.getFromCode(defaultCode); + } else if (goog.isString(projection)) { + return ol.Projection.getFromCode(projection); + } else { + goog.asserts.assert(projection instanceof ol.Projection); + return projection; + } +}; + + /** * @param {ol.Projection} source Source. * @param {ol.Projection} destination Destination. diff --git a/src/ol/source/tiledwmssource.js b/src/ol/source/tiledwmssource.js index 37f9156928..0e1e43b42f 100644 --- a/src/ol/source/tiledwmssource.js +++ b/src/ol/source/tiledwmssource.js @@ -2,6 +2,7 @@ goog.provide('ol.source.TiledWMS'); + goog.require('goog.asserts'); goog.require('goog.object'); goog.require('ol.Attribution'); @@ -19,8 +20,8 @@ goog.require('ol.tilegrid.TileGrid'); * @param {ol.source.TiledWMSOptions} tiledWMSOptions options. */ ol.source.TiledWMS = function(tiledWMSOptions) { - var projection = goog.isDef(tiledWMSOptions.projection) ? - tiledWMSOptions.projection : ol.Projection.getFromCode('EPSG:3857'); + var projection = ol.Projection.createProjection( + tiledWMSOptions.projection, 'EPSG:3857'); var projectionExtent = projection.getExtent(); var extent = goog.isDef(tiledWMSOptions.extent) ? @@ -39,7 +40,7 @@ ol.source.TiledWMS = function(tiledWMSOptions) { tiledWMSOptions.maxZoom : 18; var resolutions = new Array(maxZoom + 1); for (var z = 0, zz = resolutions.length; z < zz; ++z) { - resolutions[z] = size / (256 << z); + resolutions[z] = ol.Projection.EPSG_3857_HALF_SIZE / (128 << z); } tileGrid = new ol.tilegrid.TileGrid({ origin: projectionExtent.getTopLeft(), @@ -85,15 +86,17 @@ ol.source.TiledWMS = function(tiledWMSOptions) { return null; } var x = tileCoord.x; + var tileExtent = tileGrid.getTileCoordExtent(tileCoord); // FIXME do we want a wrapDateLine param? The code below will break maps // with projections that do not span the whole world width. if (extent.minX === projectionExtent.minX && extent.maxX === projectionExtent.maxX) { - var n = 1 << tileCoord.z; - x = goog.math.modulo(x, n); + var numCols = Math.ceil( + (extent.maxX - extent.minX) / (tileExtent.maxX - tileExtent.minX)); + x = goog.math.modulo(x, numCols); + tileExtent = tileGrid.getTileCoordExtent( + new ol.TileCoord(tileCoord.z, x, tileCoord.y)); } - var tileExtent = tileGrid.getTileCoordExtent( - new ol.TileCoord(tileCoord.z, x, tileCoord.y)); // FIXME We shouldn't need a typecast here. if (!tileExtent.intersects(/** @type {ol.Extent} */ (extent))) { return null;