From 37b369e0d7b7a76287f958857544ca42b480005c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 17 Jun 2013 15:24:37 +0200 Subject: [PATCH 1/5] Making extent optional for projections The projection validity extent is used to generate a sensible set of default resolutions and a sensible default tile grid. By making it optional, we can still generate defaults - with zoom levels that are similar to the default web mercator zoom levels (based on fitting the world on a single tile, even if the projection is not available for the whole world). --- src/objectliterals.jsdoc | 6 +++-- src/ol/proj/proj.js | 2 +- src/ol/tilegrid/tilegrid.js | 15 +++++++----- src/ol/view2d.js | 48 +++++++++++++++++++++++-------------- 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index f5be363af5..cf8dad720b 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -68,7 +68,7 @@ * Object literal with config options for the Proj4js projection. * @typedef {Object} ol.Proj4jsProjectionOptions * @property {string} code The SRS identifier code, e.g. 'EPSG:31256'. - * @property {ol.Extent} extent The validity extent for the SRS. + * @property {ol.Extent|undefined} extent The validity extent for the SRS. * @property {boolean|undefined} global Whether the projection is valid for the * whole globe. Default is false. */ @@ -78,7 +78,7 @@ * @typedef {Object} ol.ProjectionOptions * @property {string} code The SRS identifier code, e.g. 'EPSG:4326'. * @property {ol.ProjectionUnits} units Units. - * @property {ol.Extent} extent The validity extent for the SRS. + * @property {ol.Extent|undefined} extent The validity extent for the SRS. * @property {string|undefined} axisOrientation The axis orientation as * specified in Proj4. The default is 'enu'. * @property {boolean|undefined} global Whether the projection is valid for the @@ -89,6 +89,8 @@ * Object literal with config options for the view. * @typedef {Object} ol.View2DOptions * @property {ol.Coordinate|undefined} center The view center in map projection. + * @property {ol.Extent|undefined} maxExtent The maximum extent for the view. + * If configured, the view's center cannot be outside this extent. * @property {number|undefined} maxResolution The maximum resolution in map * units per pixel. * @property {number|undefined} maxZoom The maximum zoom level for this view. diff --git a/src/ol/proj/proj.js b/src/ol/proj/proj.js index f25d59a1aa..21d244f2be 100644 --- a/src/ol/proj/proj.js +++ b/src/ol/proj/proj.js @@ -74,7 +74,7 @@ ol.Projection = function(options) { * @private * @type {ol.Extent} */ - this.extent_ = options.extent; + this.extent_ = goog.isDef(options.extent) ? options.extent : null; /** * @private diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js index fabd8db081..5811beebce 100644 --- a/src/ol/tilegrid/tilegrid.js +++ b/src/ol/tilegrid/tilegrid.js @@ -4,6 +4,7 @@ goog.require('goog.array'); goog.require('goog.asserts'); goog.require('ol.Coordinate'); goog.require('ol.Projection'); +goog.require('ol.ProjectionUnits'); goog.require('ol.Size'); goog.require('ol.TileCoord'); goog.require('ol.TileRange'); @@ -410,21 +411,23 @@ ol.tilegrid.getForProjection = function(projection) { ol.tilegrid.createForProjection = function(projection, opt_maxZoom, opt_tileSize) { var projectionExtent = projection.getExtent(); - var size = Math.max( - projectionExtent[1] - projectionExtent[0], - projectionExtent[3] - projectionExtent[2]); + var size = goog.isNull(projectionExtent) ? + 360 * ol.METERS_PER_UNIT[ol.ProjectionUnits.DEGREES] / + ol.METERS_PER_UNIT[projection.getUnits()] : + Math.max(projectionExtent[1] - projectionExtent[0], + projectionExtent[3] - projectionExtent[2]); var maxZoom = goog.isDef(opt_maxZoom) ? opt_maxZoom : ol.DEFAULT_MAX_ZOOM; var tileSize = goog.isDef(opt_tileSize) ? opt_tileSize : [ol.DEFAULT_TILE_SIZE, ol.DEFAULT_TILE_SIZE]; var resolutions = new Array(maxZoom + 1); - goog.asserts.assert(tileSize[0] == tileSize[1]); - size = size / tileSize[0]; + size = size / Math.max(tileSize[0], tileSize[1]); for (var z = 0, zz = resolutions.length; z < zz; ++z) { resolutions[z] = size / Math.pow(2, z); } return new ol.tilegrid.TileGrid({ - origin: ol.extent.getBottomLeft(projectionExtent), + origin: goog.isNull(projectionExtent) ? [0, 0] : + ol.extent.getBottomLeft(projectionExtent), resolutions: resolutions, tileSize: tileSize }); diff --git a/src/ol/view2d.js b/src/ol/view2d.js index 5a52c18931..b807da9ad4 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -4,10 +4,12 @@ goog.provide('ol.View2D'); goog.provide('ol.View2DProperty'); goog.require('goog.asserts'); +goog.require('goog.math'); goog.require('ol.Constraints'); goog.require('ol.IView2D'); goog.require('ol.IView3D'); goog.require('ol.Projection'); +goog.require('ol.ProjectionUnits'); goog.require('ol.ResolutionConstraint'); goog.require('ol.RotationConstraint'); goog.require('ol.RotationConstraintType'); @@ -57,18 +59,12 @@ ol.View2D = function(opt_options) { options.center : null; values[ol.View2DProperty.PROJECTION] = ol.proj.createProjection( options.projection, 'EPSG:3857'); - if (goog.isDef(options.resolution)) { - values[ol.View2DProperty.RESOLUTION] = options.resolution; - } else if (goog.isDef(options.zoom)) { - var projectionExtent = values[ol.View2DProperty.PROJECTION].getExtent(); - var size = Math.max( - projectionExtent[1] - projectionExtent[0], - projectionExtent[3] - projectionExtent[2]); - values[ol.View2DProperty.RESOLUTION] = - size / (ol.DEFAULT_TILE_SIZE * Math.pow(2, options.zoom)); - } - values[ol.View2DProperty.ROTATION] = options.rotation; - this.setValues(values); + + /** + * @private + * @type {ol.Extent} + */ + this.maxExtent_ = goog.isDef(options.maxExtent) ? options.maxExtent : null; var parts = ol.View2D.createResolutionConstraint_(options); @@ -94,6 +90,14 @@ ol.View2D = function(opt_options) { this.constraints_ = new ol.Constraints(resolutionConstraint, rotationConstraint); + if (goog.isDef(options.resolution)) { + values[ol.View2DProperty.RESOLUTION] = options.resolution; + } else if (goog.isDef(options.zoom)) { + values[ol.View2DProperty.RESOLUTION] = resolutionConstraint( + this.maxResolution_, options.zoom); + } + values[ol.View2DProperty.ROTATION] = options.rotation; + this.setValues(values); }; goog.inherits(ol.View2D, ol.View); @@ -355,7 +359,10 @@ ol.View2D.prototype.isDef = function() { * @param {ol.Coordinate|undefined} center Center. */ ol.View2D.prototype.setCenter = function(center) { - this.set(ol.View2DProperty.CENTER, center); + this.set(ol.View2DProperty.CENTER, goog.isNull(this.maxExtent_) ? center : [ + goog.math.clamp(center[0], this.maxExtent_[0], this.maxExtent_[1]), + goog.math.clamp(center[1], this.maxExtent_[2], this.maxExtent_[3]) + ]); }; goog.exportProperty( ol.View2D.prototype, @@ -421,11 +428,16 @@ ol.View2D.createResolutionConstraint_ = function(options) { } else { maxResolution = options.maxResolution; if (!goog.isDef(maxResolution)) { - var projectionExtent = ol.proj.createProjection( - options.projection, 'EPSG:3857').getExtent(); - maxResolution = Math.max( - projectionExtent[1] - projectionExtent[0], - projectionExtent[3] - projectionExtent[2]) / ol.DEFAULT_TILE_SIZE; + var projection = options.projection; + var projectionExtent = ol.proj.createProjection(projection, 'EPSG:3857') + .getExtent(); + var size = goog.isNull(projectionExtent) ? + // use an extent that can fit the whole world if need be + 360 * ol.METERS_PER_UNIT[ol.ProjectionUnits.DEGREES] / + ol.METERS_PER_UNIT[projection.getUnits()] : + Math.max(projectionExtent[1] - projectionExtent[0], + projectionExtent[3] - projectionExtent[2]); + maxResolution = size / ol.DEFAULT_TILE_SIZE; } var maxZoom = options.maxZoom; if (!goog.isDef(maxZoom)) { From caefacac350e10429e6c52849db1bfcbcb5c6cf6 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 17 Jun 2013 15:26:23 +0200 Subject: [PATCH 2/5] Serve WMS tiles for sources without specified extent With this change, WMS sources do not need an extent configured. The result is that the WMS will be queried for tiles for any extent. --- src/ol/source/tiledwmssource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/source/tiledwmssource.js b/src/ol/source/tiledwmssource.js index 6597c64a4e..b2ee6562f7 100644 --- a/src/ol/source/tiledwmssource.js +++ b/src/ol/source/tiledwmssource.js @@ -64,7 +64,7 @@ ol.source.TiledWMS = function(options) { tileExtent = tileGrid.getTileCoordExtent( new ol.TileCoord(tileCoord.z, x, tileCoord.y)); } - if (!ol.extent.intersects(tileExtent, extent)) { + if (!goog.isNull(extent) && !ol.extent.intersects(tileExtent, extent)) { return null; } return new ol.TileCoord(tileCoord.z, x, tileCoord.y); From 29b5af9c87b6c421bd3b42a6c19624804382b14a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 17 Jun 2013 15:27:06 +0200 Subject: [PATCH 3/5] New example using a projection unknown to the client --- examples/wms-no-proj.html | 55 +++++++++++++++++++++++++++++++++++++ examples/wms-no-proj.js | 58 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 examples/wms-no-proj.html create mode 100644 examples/wms-no-proj.js diff --git a/examples/wms-no-proj.html b/examples/wms-no-proj.html new file mode 100644 index 0000000000..9f050ca19e --- /dev/null +++ b/examples/wms-no-proj.html @@ -0,0 +1,55 @@ + + + + + + + + + + WMS without client projection example + + + + + +
+ +
+
+
+
+
+ +
+ +
+

WMS without client projection example

+

Example of two WMS layers using the projection EPSG:21781, which is unknown to the client.

+
+

See the wms-no-proj.js source to see how this is done.

+
+
wms, projection
+
+ +
+ +
+ + + + + + diff --git a/examples/wms-no-proj.js b/examples/wms-no-proj.js new file mode 100644 index 0000000000..1bb50d5454 --- /dev/null +++ b/examples/wms-no-proj.js @@ -0,0 +1,58 @@ +goog.require('ol.Attribution'); +goog.require('ol.Map'); +goog.require('ol.Projection'); +goog.require('ol.ProjectionUnits'); +goog.require('ol.RendererHints'); +goog.require('ol.View2D'); +goog.require('ol.layer.ImageLayer'); +goog.require('ol.layer.TileLayer'); +goog.require('ol.source.SingleImageWMS'); +goog.require('ol.source.TiledWMS'); + + +var layers = [ + new ol.layer.TileLayer({ + source: new ol.source.TiledWMS({ + attributions: [new ol.Attribution( + '© ' + + '' + + '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/' + }) + }), + new ol.layer.ImageLayer({ + source: new ol.source.SingleImageWMS({ + attributions: [new ol.Attribution( + '© ' + + '' + + 'National parks / geo.admin.ch')], + crossOrigin: 'anonymous', + params: {'LAYERS': 'ch.bafu.schutzgebiete-paerke_nationaler_bedeutung'}, + url: 'http://wms.geo.admin.ch/' + }) + }) +]; + +// A minimal projection object is configured with only the SRS code and the map +// units. No client side coordinate transforms are possible with such a +// projection object. +var projection = new ol.Projection({ + code: 'EPSG:21781', + units: ol.ProjectionUnits.METERS +}); + +var map = new ol.Map({ + layers: layers, + renderers: ol.RendererHints.createFromQueryData(), + target: 'map', + view: new ol.View2D({ + center: [660000, 190000], + projection: projection, + zoom: 9 + }) +}); From 24953e5f0e9330c9bc7e844a01e530b2255a2684 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 17 Jun 2013 15:28:05 +0200 Subject: [PATCH 4/5] Making it clear that Proj4js is used --- examples/wms-custom-proj.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/wms-custom-proj.html b/examples/wms-custom-proj.html index c560e433bf..82f6e868e1 100644 --- a/examples/wms-custom-proj.html +++ b/examples/wms-custom-proj.html @@ -7,7 +7,7 @@ - Tiled WMS with custom projection example + Tiled WMS with Proj4js projection example @@ -36,12 +36,12 @@
-

Tiled WMS with custom projection example

+

Tiled WMS with Proj4js projection example

Example of two tiled WMS layers (Pixelmap 1:1'000'000 and national parks) using the projection EPSG:21781.

See the wms-custom-proj.js source to see how this is done.

-
wms, tile, tilelayer, projection
+
wms, tile, tilelayer, proj4js, projection
From b71bf9607c2081eb835edad54436469278a3c731 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 17 Jun 2013 16:46:47 +0200 Subject: [PATCH 5/5] Do not set center constraint This change slipped in accidently. Thanks @twpayne for catching this. --- src/objectliterals.jsdoc | 2 -- src/ol/view2d.js | 12 +----------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index cf8dad720b..cc20492def 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -89,8 +89,6 @@ * Object literal with config options for the view. * @typedef {Object} ol.View2DOptions * @property {ol.Coordinate|undefined} center The view center in map projection. - * @property {ol.Extent|undefined} maxExtent The maximum extent for the view. - * If configured, the view's center cannot be outside this extent. * @property {number|undefined} maxResolution The maximum resolution in map * units per pixel. * @property {number|undefined} maxZoom The maximum zoom level for this view. diff --git a/src/ol/view2d.js b/src/ol/view2d.js index b807da9ad4..68730a4dfe 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -4,7 +4,6 @@ goog.provide('ol.View2D'); goog.provide('ol.View2DProperty'); goog.require('goog.asserts'); -goog.require('goog.math'); goog.require('ol.Constraints'); goog.require('ol.IView2D'); goog.require('ol.IView3D'); @@ -60,12 +59,6 @@ ol.View2D = function(opt_options) { values[ol.View2DProperty.PROJECTION] = ol.proj.createProjection( options.projection, 'EPSG:3857'); - /** - * @private - * @type {ol.Extent} - */ - this.maxExtent_ = goog.isDef(options.maxExtent) ? options.maxExtent : null; - var parts = ol.View2D.createResolutionConstraint_(options); /** @@ -359,10 +352,7 @@ ol.View2D.prototype.isDef = function() { * @param {ol.Coordinate|undefined} center Center. */ ol.View2D.prototype.setCenter = function(center) { - this.set(ol.View2DProperty.CENTER, goog.isNull(this.maxExtent_) ? center : [ - goog.math.clamp(center[0], this.maxExtent_[0], this.maxExtent_[1]), - goog.math.clamp(center[1], this.maxExtent_[2], this.maxExtent_[3]) - ]); + this.set(ol.View2DProperty.CENTER, center); }; goog.exportProperty( ol.View2D.prototype,