From 0d6c54847bb6d3556455dd57c6016a9d5ffac74b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 15:55:27 -0700 Subject: [PATCH 1/9] Removing logic that assumes occasionally stretched tiles When the dom renderer included logic to stretch tiles so that gaps were properly filled for fractional zoom levels, we needed to take this into account when getting a tile coordinate for a given coordinate and resolution. This was never the proper logic for a renderer that wasn't stretching occassional tiles (e.g. the WebGL renderer, the Canvas renderer, or the new DOM renderer). --- src/ol/tilegrid/tilegrid.js | 24 +------- test/spec/ol/tilegrid.test.js | 100 ---------------------------------- 2 files changed, 3 insertions(+), 121 deletions(-) diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js index 58dfaacfda..ba9e6ff441 100644 --- a/src/ol/tilegrid/tilegrid.js +++ b/src/ol/tilegrid/tilegrid.js @@ -251,28 +251,10 @@ ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function( tileSize = new ol.Size(tileSize.width / scale, tileSize.height / scale); - var x, y; - x = Math.floor(offsetFromOrigin.x / tileSize.width); - y = Math.floor(offsetFromOrigin.y / tileSize.height); + var x = Math.floor(offsetFromOrigin.x / tileSize.width); + var y = Math.floor(offsetFromOrigin.y / tileSize.height); - var tileCoord = new ol.TileCoord(z, x, y); - var tileCoordPixelBounds = this.getPixelBoundsForTileCoordAndResolution( - tileCoord, resolution); - - // adjust x to allow for stretched tiles - if (offsetFromOrigin.x < tileCoordPixelBounds.minX) { - tileCoord.x -= 1; - } else if (offsetFromOrigin.x >= tileCoordPixelBounds.maxX) { - tileCoord.x += 1; - } - // adjust y to allow for stretched tiles - if (offsetFromOrigin.y < tileCoordPixelBounds.minY) { - tileCoord.y -= 1; - } else if (offsetFromOrigin.y >= tileCoordPixelBounds.maxY) { - tileCoord.y += 1; - } - - return tileCoord; + return new ol.TileCoord(z, x, y); }; diff --git a/test/spec/ol/tilegrid.test.js b/test/spec/ol/tilegrid.test.js index bcaba344f6..63451a1668 100644 --- a/test/spec/ol/tilegrid.test.js +++ b/test/spec/ol/tilegrid.test.js @@ -258,106 +258,6 @@ describe('ol.tilegrid.TileGrid', function() { }); }); - describe('getTileCoordForCoordAndResolution fractional', function() { - it('returns the expected TileCoord', function() { - var tileSize = new ol.Size(256, 256); - var tileGrid = new ol.tilegrid.TileGrid({ - resolutions: [1 / 3], - extent: extent, - origin: origin, - tileSize: tileSize - }); - - var coordinate; - var tileCoord; - - // These tests render at a resolution of 1. Because the layer's - // closest resolution is 1/3, the images are scaled by 1/3. - // In this scenario, every third tile will be one pixel wider when - // rendered (0,0 is normal; 1,0 is wider; 0,1 is taller; etc.) - - // gets the first tile at the origin - coordinate = new ol.Coordinate(0, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(0); - expect(tileCoord.y).toEqual(0); - - // gets the 1,0 tile at 256/3,0 - coordinate = new ol.Coordinate(256 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(1); - expect(tileCoord.y).toEqual(0); - - // still gets the 1,0 tile at 512/3,0 - wider tile - coordinate = new ol.Coordinate(512 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(1); - expect(tileCoord.y).toEqual(0); - - // gets the 2,0 tile at 513/3,0 - coordinate = new ol.Coordinate(513 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(2); - expect(tileCoord.y).toEqual(0); - - // gets the 3,0 tile at 768/3,0 - coordinate = new ol.Coordinate(768 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(3); - expect(tileCoord.y).toEqual(0); - - // gets the 4,0 tile at 1024/3,0 - coordinate = new ol.Coordinate(1024 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(4); - expect(tileCoord.y).toEqual(0); - - // still gets the 4,0 tile at 1280/3,0 - wider tile - coordinate = new ol.Coordinate(1280 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(4); - expect(tileCoord.y).toEqual(0); - - // gets the 5,0 tile at 1281/3,0 - coordinate = new ol.Coordinate(1281 / 3, 0); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(5); - expect(tileCoord.y).toEqual(0); - - // gets the 0,1 tile at 0,-256/3 - coordinate = new ol.Coordinate(0, -256 / 3); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(0); - expect(tileCoord.y).toEqual(-2); - - // still gets the 0,1 tile at 0,-512/3 - taller tile - coordinate = new ol.Coordinate(0, -512 / 3); - tileCoord = tileGrid.getTileCoordForCoordAndResolution( - coordinate, 1); - expect(tileCoord.z).toEqual(0); - expect(tileCoord.x).toEqual(0); - expect(tileCoord.y).toEqual(-2); - }); - }); - describe('getTileCoordCenter', function() { it('returns the expected center', function() { var tileGrid = new ol.tilegrid.TileGrid({ From 8b1975126401dd9ea97c34c500be7be8907d1ca6 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 17:07:31 -0700 Subject: [PATCH 2/9] Making it explicit that this is a range of tiles With tile range extending rectangle, a tile range that represented a single tile was not allowed. --- src/ol/tilerange.js | 75 +++++++++++++++++++++++++++++++--- test/spec/ol/tilerange.test.js | 67 ++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 6 deletions(-) diff --git a/src/ol/tilerange.js b/src/ol/tilerange.js index e1baa169f8..752716a4dc 100644 --- a/src/ol/tilerange.js +++ b/src/ol/tilerange.js @@ -1,23 +1,44 @@ goog.provide('ol.TileRange'); goog.require('goog.asserts'); -goog.require('ol.Rectangle'); +goog.require('ol.Size'); goog.require('ol.TileCoord'); /** + * A representation of a contiguous block of tiles. A tile range is specified + * by its min/max tile coordinates and is inclusive of coordinates. + * * @constructor - * @extends {ol.Rectangle} * @param {number} minX Minimum X. * @param {number} minY Minimum Y. * @param {number} maxX Maximum X. * @param {number} maxY Maximum Y. */ ol.TileRange = function(minX, minY, maxX, maxY) { - goog.base(this, minX, minY, maxX, maxY); + + /** + * @type {number} + */ + this.minX = minX; + + /** + * @type {number} + */ + this.minY = minY; + + /** + * @type {number} + */ + this.maxX = maxX; + + /** + * @type {number} + */ + this.maxY = maxY; + }; -goog.inherits(ol.TileRange, ol.Rectangle); /** @@ -62,7 +83,28 @@ ol.TileRange.prototype.containsTileRange = function(tileRange) { /** - * @inheritDoc + * @param {ol.TileRange} range Other range. + * @return {boolean} The two ranges are equivalent. + */ +ol.TileRange.prototype.equals = function(range) { + return this.minX == range.minX && this.minY == range.minY && + this.maxX == range.maxX && this.maxY == range.maxY; +}; + + +/** + * Extend this range so it includes the other. + * @param {ol.TileRange} other Other range. + */ +ol.TileRange.prototype.extend = function(other) { + this.minX = Math.min(this.minX, other.minX); + this.minY = Math.min(this.minY, other.minY); + this.maxX = Math.max(this.maxX, other.maxX); + this.maxY = Math.max(this.maxY, other.maxY); +}; + + +/** * @return {number} Height. */ ol.TileRange.prototype.getHeight = function() { @@ -71,9 +113,30 @@ ol.TileRange.prototype.getHeight = function() { /** - * @inheritDoc + * @return {ol.Size} Size. + */ +ol.TileRange.prototype.getSize = function() { + return new ol.Size(this.getWidth(), this.getHeight()); +}; + + +/** * @return {number} Width. */ ol.TileRange.prototype.getWidth = function() { return this.maxX - this.minX + 1; }; + + +/** + * Test for range intersection. + * @param {ol.TileRange} other Other range. + * @return {boolean} The two ranges intersect. + */ +ol.TileRange.prototype.intersects = function(other) { + return this.minX <= other.maxX && + this.maxX >= other.minX && + this.minY <= other.maxY && + this.maxY >= other.minY; +}; + diff --git a/test/spec/ol/tilerange.test.js b/test/spec/ol/tilerange.test.js index fd59068325..6906c57e78 100644 --- a/test/spec/ol/tilerange.test.js +++ b/test/spec/ol/tilerange.test.js @@ -2,6 +2,20 @@ goog.provide('ol.test.TileRange'); describe('ol.TileRange', function() { + describe('constructor', function() { + it('creates a range', function() { + var range = new ol.TileRange(1, 2, 3, 4); + expect(range).toBeA(ol.TileRange); + }); + + it('can represent a range of one tile', function() { + var range = new ol.TileRange(2, 3, 2, 3); + expect(range).toBeA(ol.TileRange); + expect(range.getHeight()).toBe(1); + expect(range.getWidth()).toBe(1); + }); + }); + describe('contains', function() { it('returns the expected value', function() { var tileRange = new ol.TileRange(1, 1, 3, 3); @@ -53,6 +67,36 @@ describe('ol.TileRange', function() { }); }); + describe('equals', function() { + it('determines equivalence of two ranges', function() { + var one = new ol.TileRange(0, 1, 2, 4); + var same = new ol.TileRange(0, 1, 2, 4); + var diff1 = new ol.TileRange(0, 1, 2, 5); + var diff2 = new ol.TileRange(0, 1, 3, 4); + var diff3 = new ol.TileRange(0, 2, 2, 4); + var diff4 = new ol.TileRange(1, 1, 2, 4); + expect(one.equals(same)).toBe(true); + expect(one.equals(diff1)).toBe(false); + expect(one.equals(diff2)).toBe(false); + expect(one.equals(diff3)).toBe(false); + expect(one.equals(diff4)).toBe(false); + }); + }); + + describe('extent', function() { + it('modifies range so it includes another', function() { + var one = new ol.TileRange(0, 1, 2, 4); + var other = new ol.TileRange(-1, 10, -3, 12); + one.extend(other); + + expect(one.minX).toBe(-1); + expect(one.minY).toBe(1); + expect(one.maxX).toBe(2); + expect(one.maxY).toBe(12); + + }); + }); + describe('getSize', function() { it('returns the expected size', function() { var tileRange = new ol.TileRange(0, 1, 2, 4); @@ -62,6 +106,29 @@ describe('ol.TileRange', function() { }); }); + describe('intersects', function() { + it('determines if two ranges overlap', function() { + var one = new ol.TileRange(0, 1, 2, 4); + var overlapsRight = new ol.TileRange(2, 1, 4, 4); + var overlapsLeft = new ol.TileRange(-3, 1, 0, 4); + var overlapsTop = new ol.TileRange(0, 4, 2, 5); + var overlapsBottom = new ol.TileRange(0, -3, 2, 1); + expect(one.intersects(overlapsLeft)).toBe(true); + expect(one.intersects(overlapsRight)).toBe(true); + expect(one.intersects(overlapsTop)).toBe(true); + expect(one.intersects(overlapsBottom)).toBe(true); + + var right = new ol.TileRange(3, 1, 5, 4); + var left = new ol.TileRange(-3, 1, -1, 4); + var above = new ol.TileRange(0, 5, 2, 6); + var below = new ol.TileRange(0, -3, 2, 0); + expect(one.intersects(right)).toBe(false); + expect(one.intersects(left)).toBe(false); + expect(one.intersects(above)).toBe(false); + expect(one.intersects(below)).toBe(false); + }); + }); + }); goog.require('ol.TileRange'); From 0a5dae9ed0bc96b71fbeacaa668f378f25462f12 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 18:37:27 -0700 Subject: [PATCH 3/9] Avoid left shift in resolution calculations --- src/ol/tilegrid/tilegrid.js | 3 ++- src/ol/tilegrid/xyztilegrid.js | 4 ++-- src/ol/view2d.js | 2 +- test/spec/ol/tilegrid.test.js | 31 +++++++++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js index 58dfaacfda..7a0fecf81e 100644 --- a/src/ol/tilegrid/tilegrid.js +++ b/src/ol/tilegrid/tilegrid.js @@ -333,8 +333,9 @@ ol.tilegrid.createForProjection = opt_tileSize : new ol.Size(ol.DEFAULT_TILE_SIZE, ol.DEFAULT_TILE_SIZE); var resolutions = new Array(maxZoom + 1); goog.asserts.assert(tileSize.width == tileSize.height); + size = size / tileSize.width; for (var z = 0, zz = resolutions.length; z < zz; ++z) { - resolutions[z] = size / (tileSize.width << z); + resolutions[z] = size / Math.pow(2, z); } return new ol.tilegrid.TileGrid({ origin: projectionExtent.getTopLeft(), diff --git a/src/ol/tilegrid/xyztilegrid.js b/src/ol/tilegrid/xyztilegrid.js index c948010ba7..d57909413e 100644 --- a/src/ol/tilegrid/xyztilegrid.js +++ b/src/ol/tilegrid/xyztilegrid.js @@ -17,9 +17,9 @@ ol.tilegrid.XYZ = function(xyzOptions) { var resolutions = new Array(xyzOptions.maxZoom + 1); var z; + var size = 2 * ol.Projection.EPSG_3857_HALF_SIZE / ol.DEFAULT_TILE_SIZE; for (z = 0; z <= xyzOptions.maxZoom; ++z) { - resolutions[z] = - 2 * ol.Projection.EPSG_3857_HALF_SIZE / (ol.DEFAULT_TILE_SIZE << z); + resolutions[z] = size / Math.pow(2, z); } goog.base(this, { diff --git a/src/ol/view2d.js b/src/ol/view2d.js index ed707f2e74..be3e30564b 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -56,7 +56,7 @@ ol.View2D = function(opt_view2DOptions) { projectionExtent.maxX - projectionExtent.minX, projectionExtent.maxY - projectionExtent.minY); values[ol.View2DProperty.RESOLUTION] = - size / (ol.DEFAULT_TILE_SIZE << view2DOptions.zoom); + size / (ol.DEFAULT_TILE_SIZE * Math.pow(2, view2DOptions.zoom)); } values[ol.View2DProperty.ROTATION] = view2DOptions.rotation; this.setValues(values); diff --git a/test/spec/ol/tilegrid.test.js b/test/spec/ol/tilegrid.test.js index bcaba344f6..3831522c4b 100644 --- a/test/spec/ol/tilegrid.test.js +++ b/test/spec/ol/tilegrid.test.js @@ -94,6 +94,37 @@ describe('ol.tilegrid.TileGrid', function() { }); }); + describe('createForProjection', function() { + + it('allows easier creation of a tile grid', function() { + var projection = ol.Projection.getFromCode('EPSG:3857'); + var grid = ol.tilegrid.createForProjection(projection); + expect(grid).toBeA(ol.tilegrid.TileGrid); + + var resolutions = grid.getResolutions(); + expect(resolutions.length).toBe(19); + }); + + it('accepts a number of zoom levels', function() { + var projection = ol.Projection.getFromCode('EPSG:3857'); + var grid = ol.tilegrid.createForProjection(projection, 22); + expect(grid).toBeA(ol.tilegrid.TileGrid); + + var resolutions = grid.getResolutions(); + expect(resolutions.length).toBe(23); + }); + + it('accepts a big number of zoom levels', function() { + var projection = ol.Projection.getFromCode('EPSG:3857'); + var grid = ol.tilegrid.createForProjection(projection, 23); + expect(grid).toBeA(ol.tilegrid.TileGrid); + + var resolutions = grid.getResolutions(); + expect(resolutions.length).toBe(24); + }); + + }); + describe('getTileCoordFromCoordAndZ', function() { describe('Y North, X East', function() { From f27c749e49cb4e2a22a36fa2aabcdb8b78c63213 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 19 Feb 2013 09:12:40 +0100 Subject: [PATCH 4/9] Allow Proj4js to be non-global and cache test result --- src/ol/projection.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/ol/projection.js b/src/ol/projection.js index 3afd93e977..df23c5fb68 100644 --- a/src/ol/projection.js +++ b/src/ol/projection.js @@ -15,6 +15,12 @@ goog.require('ol.TransformFunction'); ol.ENABLE_PROJ4JS = true; +/** + * @const {boolean} Have Proj4js. + */ +ol.HAVE_PROJ4JS = ol.ENABLE_PROJ4JS && typeof Proj4js == 'object'; + + /** * @enum {string} */ @@ -253,7 +259,7 @@ ol.Projection.addTransform = function(source, destination, transformFn) { */ ol.Projection.getFromCode = function(code) { var projection = ol.Projection.projections_[code]; - if (ol.Projection.isProj4jsSupported() && !goog.isDef(projection)) { + if (ol.HAVE_PROJ4JS && !goog.isDef(projection)) { projection = ol.Projection.getProj4jsProjectionFromCode_(code); } if (!goog.isDef(projection)) { @@ -319,7 +325,7 @@ ol.Projection.getTransform = function(source, destination) { goog.object.containsKey(transforms[sourceCode], destinationCode)) { transform = transforms[sourceCode][destinationCode]; } - if (ol.Projection.isProj4jsSupported() && !goog.isDef(transform)) { + if (ol.HAVE_PROJ4JS && !goog.isDef(transform)) { var proj4jsSource; if (source instanceof ol.Proj4jsProjection) { proj4jsSource = source; @@ -373,14 +379,6 @@ ol.Projection.getTransformFromCodes = function(sourceCode, destinationCode) { }; -/** - * @return {boolean} True if Proj4js is available and enabled. - */ -ol.Projection.isProj4jsSupported = function() { - return ol.ENABLE_PROJ4JS && 'Proj4js' in goog.global; -}; - - /** * @param {ol.Coordinate} point Point. * @return {ol.Coordinate} Unaltered point (same reference). From f540dd86893de2365f0e4528925ff5b05869df7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 31 Jan 2013 13:37:18 +0100 Subject: [PATCH 5/9] Image layer renderers add attributions to frame state --- src/ol/framestate.js | 2 ++ src/ol/image.js | 18 +++++++++++++++++- src/ol/map.js | 1 + .../canvas/canvasimagelayerrenderer.js | 3 +++ src/ol/renderer/dom/domimagelayerrenderer.js | 2 ++ src/ol/renderer/layerrenderer.js | 18 ++++++++++++++++++ .../renderer/webgl/webglimagelayerrenderer.js | 2 ++ src/ol/source/imagesource.js | 3 ++- 8 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/ol/framestate.js b/src/ol/framestate.js index 9e45ce99b6..a4c97039a0 100644 --- a/src/ol/framestate.js +++ b/src/ol/framestate.js @@ -6,6 +6,7 @@ goog.provide('ol.PostRenderFunction'); goog.provide('ol.PreRenderFunction'); goog.require('goog.vec.Mat4'); +goog.require('ol.Attribution'); goog.require('ol.Color'); goog.require('ol.Extent'); goog.require('ol.Size'); @@ -18,6 +19,7 @@ goog.require('ol.layer.LayerState'); /** * @typedef {{animate: boolean, + * attributions: Object., * backgroundColor: ol.Color, * coordinateToPixelMatrix: goog.vec.Mat4.Number, * extent: (null|ol.Extent), diff --git a/src/ol/image.js b/src/ol/image.js index 9f7cbe0c47..41d5e6fc88 100644 --- a/src/ol/image.js +++ b/src/ol/image.js @@ -5,6 +5,7 @@ goog.require('goog.array'); goog.require('goog.events'); goog.require('goog.events.EventTarget'); goog.require('goog.events.EventType'); +goog.require('ol.Attribution'); goog.require('ol.Extent'); @@ -27,8 +28,15 @@ ol.ImageState = { * @param {number} resolution Resolution. * @param {string} src Image source URI. * @param {?string} crossOrigin Cross origin. + * @param {Array.} attributions Attributions. */ -ol.Image = function(extent, resolution, src, crossOrigin) { +ol.Image = function(extent, resolution, src, crossOrigin, attributions) { + + /** + * @private + * @type {Array.} + */ + this.attributions_ = attributions; /** * @private @@ -86,6 +94,14 @@ ol.Image.prototype.dispatchChangeEvent = function() { }; +/** + * @return {Array.} Attributions. + */ +ol.Image.prototype.getAttributions = function() { + return this.attributions_; +}; + + /** * @return {ol.Extent} Extent. */ diff --git a/src/ol/map.js b/src/ol/map.js index 9d391c90d5..4034fc9e5a 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -664,6 +664,7 @@ ol.Map.prototype.renderFrame_ = function(time) { var view2DState = view2D.getView2DState(); frameState = { animate: false, + attributions: {}, backgroundColor: goog.isDef(backgroundColor) ? backgroundColor : new ol.Color(255, 255, 255, 1), coordinateToPixelMatrix: this.coordinateToPixelMatrix_, diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js index 273f22d49f..61eb2b898d 100644 --- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -94,6 +94,7 @@ ol.renderer.canvas.ImageLayer.prototype.renderFrame = if (!goog.isNull(this.image_)) { image = this.image_; + var imageExtent = image.getExtent(); var imageResolution = image.getResolution(); var transform = this.transform_; @@ -111,5 +112,7 @@ ol.renderer.canvas.ImageLayer.prototype.renderFrame = (imageExtent.minX - viewCenter.x) / imageResolution, (viewCenter.y - imageExtent.maxY) / imageResolution, 0); + + this.updateAttributions(frameState.attributions, image.getAttributions()); } }; diff --git a/src/ol/renderer/dom/domimagelayerrenderer.js b/src/ol/renderer/dom/domimagelayerrenderer.js index b3f5f9f224..7190d8c268 100644 --- a/src/ol/renderer/dom/domimagelayerrenderer.js +++ b/src/ol/renderer/dom/domimagelayerrenderer.js @@ -106,6 +106,8 @@ ol.renderer.dom.ImageLayer.prototype.renderFrame = this.image_ = image; } this.setTransform(transform); + + this.updateAttributions(frameState.attributions, image.getAttributions()); } }; diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index bfe98600c4..3379e0da5e 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -2,6 +2,7 @@ goog.provide('ol.renderer.Layer'); goog.require('goog.events'); goog.require('goog.events.EventType'); +goog.require('ol.Attribution'); goog.require('ol.FrameState'); goog.require('ol.Image'); goog.require('ol.ImageState'); @@ -201,6 +202,23 @@ ol.renderer.Layer.prototype.scheduleExpireCache = }; +/** + * @protected + * @param {Object.} attributionsSet Attributions + * set (target). + * @param {Array.} attributions Attributions (source). + */ +ol.renderer.Layer.prototype.updateAttributions = + function(attributionsSet, attributions) { + var i; + var attribution; + for (i = 0; i < attributions.length; ++i) { + attribution = attributions[i]; + attributionsSet[goog.getUid(attribution).toString()] = attribution; + } +}; + + /** * @protected * @param {Object.>} usedTiles Used tiles. diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js index 6449a7c178..5d0341f3c1 100644 --- a/src/ol/renderer/webgl/webglimagelayerrenderer.js +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -198,6 +198,8 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame = this.image_ = image; this.texture_ = texture; + + this.updateAttributions(frameState.attributions, image.getAttributions()); } }; diff --git a/src/ol/source/imagesource.js b/src/ol/source/imagesource.js index caad04b5e9..879a007d5c 100644 --- a/src/ol/source/imagesource.js +++ b/src/ol/source/imagesource.js @@ -84,7 +84,8 @@ ol.source.ImageSource.prototype.createImage = var imageUrl = this.imageUrlFunction(extent, size); if (goog.isDef(imageUrl)) { image = new ol.Image( - extent, resolution, imageUrl, this.crossOrigin_); + extent, resolution, imageUrl, this.crossOrigin_, + this.getAttributions()); } return image; }; From 82aa44fdd1403774de16e68fb5c46441bdec35aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 31 Jan 2013 13:38:36 +0100 Subject: [PATCH 6/9] Attribution control reads attribs from frame state --- src/ol/control/attributioncontrol.js | 89 +++++++++++++++------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/src/ol/control/attributioncontrol.js b/src/ol/control/attributioncontrol.js index 731e2b9435..c6f79717aa 100644 --- a/src/ol/control/attributioncontrol.js +++ b/src/ol/control/attributioncontrol.js @@ -1,5 +1,4 @@ // FIXME handle date line wrap -// FIXME does not handle image sources goog.provide('ol.control.Attribution'); @@ -10,11 +9,12 @@ goog.require('goog.events'); goog.require('goog.object'); goog.require('goog.style'); goog.require('ol.Attribution'); +goog.require('ol.FrameState'); goog.require('ol.MapEvent'); goog.require('ol.MapEventType'); goog.require('ol.TileRange'); goog.require('ol.control.Control'); -goog.require('ol.source.TileSource'); +goog.require('ol.source.Source'); @@ -66,16 +66,46 @@ ol.control.Attribution = function(attributionOptions) { goog.inherits(ol.control.Attribution, ol.control.Control); +/** + * @param {?Object.>} usedTiles Used + * tiles. + * @param {Object.} sources Sources. + * @return {Object.} Attributions. + */ +ol.control.Attribution.prototype.getTileSourceAttributions = + function(usedTiles, sources) { + /** @type {Object.} */ + var attributions = {}; + var i, tileRanges, tileSource, tileSourceAttribution, + tileSourceAttributionKey, tileSourceAttributions, tileSourceKey, z; + for (tileSourceKey in usedTiles) { + goog.asserts.assert(tileSourceKey in sources); + tileSource = sources[tileSourceKey]; + tileSourceAttributions = tileSource.getAttributions(); + if (goog.isNull(tileSourceAttributions)) { + continue; + } + tileRanges = usedTiles[tileSourceKey]; + for (i = 0; i < tileSourceAttributions.length; ++i) { + tileSourceAttribution = tileSourceAttributions[i]; + tileSourceAttributionKey = goog.getUid(tileSourceAttribution).toString(); + if (tileSourceAttributionKey in attributions) { + continue; + } + if (tileSourceAttribution.intersectsAnyTileRange(tileRanges)) { + attributions[tileSourceAttributionKey] = tileSourceAttribution; + } + } + } + return attributions; +}; + + /** * @param {ol.MapEvent} mapEvent Map event. */ ol.control.Attribution.prototype.handleMapPostrender = function(mapEvent) { - var frameState = mapEvent.frameState; - if (goog.isNull(frameState)) { - this.updateElement_(null); - } else { - this.updateElement_(frameState.usedTiles); - } + this.updateElement_(mapEvent.frameState); }; @@ -99,12 +129,11 @@ ol.control.Attribution.prototype.setMap = function(map) { /** * @private - * @param {?Object.>} usedTiles Used - * tiles. + * @param {?ol.FrameState} frameState Frame state. */ -ol.control.Attribution.prototype.updateElement_ = function(usedTiles) { +ol.control.Attribution.prototype.updateElement_ = function(frameState) { - if (goog.isNull(usedTiles)) { + if (goog.isNull(frameState)) { if (this.renderedVisible_) { goog.style.showElement(this.element, false); this.renderedVisible_ = false; @@ -116,15 +145,13 @@ ol.control.Attribution.prototype.updateElement_ = function(usedTiles) { /** @type {Object.} */ var attributionsToRemove = {}; - /** @type {Object.} */ - var tileSources = {}; + /** @type {Object.} */ + var sources = {}; var layers = map.getLayers(); if (goog.isDef(layers)) { layers.forEach(function(layer) { var source = layer.getSource(); - if (source instanceof ol.source.TileSource) { - tileSources[goog.getUid(source).toString()] = source; - } + sources[goog.getUid(source).toString()] = source; var attributions = source.getAttributions(); if (!goog.isNull(attributions)) { var attribution, i; @@ -138,34 +165,16 @@ ol.control.Attribution.prototype.updateElement_ = function(usedTiles) { } /** @type {Object.} */ - var attributions = {}; - var i, tileRanges, tileSource, tileSourceAttribution, - tileSourceAttributionKey, tileSourceAttributions, tileSourceKey, z; - for (tileSourceKey in usedTiles) { - goog.asserts.assert(tileSourceKey in tileSources); - tileSource = tileSources[tileSourceKey]; - tileSourceAttributions = tileSource.getAttributions(); - if (goog.isNull(tileSourceAttributions)) { - continue; - } - tileRanges = usedTiles[tileSourceKey]; - for (i = 0; i < tileSourceAttributions.length; ++i) { - tileSourceAttribution = tileSourceAttributions[i]; - tileSourceAttributionKey = goog.getUid(tileSourceAttribution).toString(); - if (tileSourceAttributionKey in attributions) { - continue; - } - if (tileSourceAttribution.intersectsAnyTileRange(tileRanges)) { - attributions[tileSourceAttributionKey] = tileSourceAttribution; - } - } - } + var attributions = goog.object.clone(frameState.attributions); + var tileSourceAttributions = this.getTileSourceAttributions( + frameState.usedTiles, sources); + goog.object.extend(attributions, tileSourceAttributions); /** @type {Array.} */ var attributionKeys = goog.array.map(goog.object.getKeys(attributions), Number); goog.array.sort(attributionKeys); - var attributionElement, attributionKey; + var i, attributionElement, attributionKey; for (i = 0; i < attributionKeys.length; ++i) { attributionKey = attributionKeys[i].toString(); if (attributionKey in this.attributionElements_) { From 797dba2cdbabd8fa0a78c32a7b3bfcdbff29eea8 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 17:09:33 -0700 Subject: [PATCH 7/9] Correct range for extent and resolution Tile ranges are inclusive. When getting the tile range for an extent, the top-right corner of the extent should be considered with a different intersection policy than the bottom-left corner. --- src/ol/tilegrid/tilegrid.js | 46 ++++++++++----- test/spec/ol/tilegrid.test.js | 102 ++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 13 deletions(-) diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js index ba9e6ff441..7786581932 100644 --- a/src/ol/tilegrid/tilegrid.js +++ b/src/ol/tilegrid/tilegrid.js @@ -183,10 +183,10 @@ ol.tilegrid.TileGrid.prototype.getTileRangeExtent = function(z, tileRange) { */ ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndResolution = function( extent, resolution) { - var min = this.getTileCoordForCoordAndResolution( + var min = this.getTileCoordForCoordAndResolution_( new ol.Coordinate(extent.minX, extent.minY), resolution); - var max = this.getTileCoordForCoordAndResolution( - new ol.Coordinate(extent.maxX, extent.maxY), resolution); + var max = this.getTileCoordForCoordAndResolution_( + new ol.Coordinate(extent.maxX, extent.maxY), resolution, true); return new ol.TileRange(min.x, min.y, max.x, max.y); }; @@ -233,26 +233,46 @@ ol.tilegrid.TileGrid.prototype.getTileCoordExtent = function(tileCoord) { /** + * Get the tile coordinate for the given map coordinate and resolution. This + * method considers that coordinates that intersect tile boundaries should be + * assigned the higher tile coordinate. + * * @param {ol.Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. * @return {ol.TileCoord} Tile coordinate. */ ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function( coordinate, resolution) { + return this.getTileCoordForCoordAndResolution_(coordinate, resolution); +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {boolean=} opt_reverseIntersectionPolicy Instead of letting edge + * intersections go to the higher tile coordinate, let edge intersections + * go to the lower tile coordinate. + * @return {ol.TileCoord} Tile coordinate. + * @private + */ +ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution_ = function( + coordinate, resolution, opt_reverseIntersectionPolicy) { var z = this.getZForResolution(resolution); var scale = resolution / this.getResolution(z); var origin = this.getOrigin(z); - - var offsetFromOrigin = new ol.Coordinate( - Math.floor((coordinate.x - origin.x) / resolution), - Math.floor((coordinate.y - origin.y) / resolution)); - var tileSize = this.getTileSize(); - tileSize = new ol.Size(tileSize.width / scale, - tileSize.height / scale); - var x = Math.floor(offsetFromOrigin.x / tileSize.width); - var y = Math.floor(offsetFromOrigin.y / tileSize.height); + var x = scale * (coordinate.x - origin.x) / (resolution * tileSize.width); + var y = scale * (coordinate.y - origin.y) / (resolution * tileSize.height); + + if (!opt_reverseIntersectionPolicy) { + x = Math.floor(x); + y = Math.floor(y); + } else { + x = Math.ceil(x) - 1; + y = Math.ceil(y) - 1; + } return new ol.TileCoord(z, x, y); }; @@ -266,7 +286,7 @@ ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function( ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ = function(coordinate, z) { var resolution = this.getResolution(z); - return this.getTileCoordForCoordAndResolution(coordinate, resolution); + return this.getTileCoordForCoordAndResolution_(coordinate, resolution); }; diff --git a/test/spec/ol/tilegrid.test.js b/test/spec/ol/tilegrid.test.js index 63451a1668..abcb2c358a 100644 --- a/test/spec/ol/tilegrid.test.js +++ b/test/spec/ol/tilegrid.test.js @@ -258,6 +258,68 @@ describe('ol.tilegrid.TileGrid', function() { }); }); + + describe('getTileCoordForCoordAndResolution_', function() { + it('returns higher tile coord for intersections by default', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + resolutions: resolutions, + extent: extent, + origin: origin, + tileSize: tileSize + }); + + var coordinate; + var tileCoord; + + // gets higher tile for edge intersection + coordinate = new ol.Coordinate(0, 0); + tileCoord = tileGrid.getTileCoordForCoordAndResolution_( + coordinate, 100); + expect(tileCoord.z).toEqual(3); + expect(tileCoord.x).toEqual(0); + expect(tileCoord.y).toEqual(0); + + // gets higher tile for edge intersection + coordinate = new ol.Coordinate(100000, 100000); + tileCoord = tileGrid.getTileCoordForCoordAndResolution_( + coordinate, 100); + expect(tileCoord.z).toEqual(3); + expect(tileCoord.x).toEqual(10); + expect(tileCoord.y).toEqual(10); + + }); + + it('handles alt intersection policy', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + resolutions: resolutions, + extent: extent, + origin: origin, + tileSize: tileSize + }); + + var coordinate; + var tileCoord; + + // can get lower tile for edge intersection + coordinate = new ol.Coordinate(0, 0); + tileCoord = tileGrid.getTileCoordForCoordAndResolution_( + coordinate, 100, true); + expect(tileCoord.z).toEqual(3); + expect(tileCoord.x).toEqual(-1); + expect(tileCoord.y).toEqual(-1); + + // gets higher tile for edge intersection + coordinate = new ol.Coordinate(100000, 100000); + tileCoord = tileGrid.getTileCoordForCoordAndResolution_( + coordinate, 100, true); + expect(tileCoord.z).toEqual(3); + expect(tileCoord.x).toEqual(9); + expect(tileCoord.y).toEqual(9); + + }); + + }); + describe('getTileCoordCenter', function() { it('returns the expected center', function() { var tileGrid = new ol.tilegrid.TileGrid({ @@ -312,6 +374,46 @@ describe('ol.tilegrid.TileGrid', function() { }); }); + describe('getTileRangeForExtentAndResolution', function() { + it('returns the expected TileRange', function() { + var tileGrid = new ol.tilegrid.TileGrid({ + resolutions: resolutions, + extent: extent, + origin: origin, + tileSize: tileSize + }); + var tileRange; + + tileRange = tileGrid.getTileRangeForExtentAndResolution(extent, + resolutions[0]); + expect(tileRange.minY).toEqual(0); + expect(tileRange.minX).toEqual(0); + expect(tileRange.maxX).toEqual(0); + expect(tileRange.maxY).toEqual(0); + + tileRange = tileGrid.getTileRangeForExtentAndResolution(extent, + resolutions[1]); + expect(tileRange.minX).toEqual(0); + expect(tileRange.minY).toEqual(0); + expect(tileRange.maxX).toEqual(1); + expect(tileRange.maxY).toEqual(1); + + tileRange = tileGrid.getTileRangeForExtentAndResolution(extent, + resolutions[2]); + expect(tileRange.minX).toEqual(0); + expect(tileRange.minY).toEqual(0); + expect(tileRange.maxX).toEqual(3); + expect(tileRange.maxY).toEqual(3); + + tileRange = tileGrid.getTileRangeForExtentAndResolution(extent, + resolutions[3]); + expect(tileRange.minX).toEqual(0); + expect(tileRange.minY).toEqual(0); + expect(tileRange.maxX).toEqual(9); + expect(tileRange.maxY).toEqual(9); + }); + }); + describe('getTileRangeForExtentAndZ', function() { it('returns the expected TileRange', function() { var tileGrid = new ol.tilegrid.TileGrid({ From 5148ad038c58482d4eec83b8f925ad26bf7ffb5a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 19 Feb 2013 07:30:54 -0700 Subject: [PATCH 8/9] Sharing code with rectangle We cannot use the rectangle constructor here because a rectangle makes an assertion about min being less than max and a range may have equal min/max. --- src/ol/tilerange.js | 50 +++++---------------------------------------- 1 file changed, 5 insertions(+), 45 deletions(-) diff --git a/src/ol/tilerange.js b/src/ol/tilerange.js index 752716a4dc..76d3d7437c 100644 --- a/src/ol/tilerange.js +++ b/src/ol/tilerange.js @@ -1,7 +1,7 @@ goog.provide('ol.TileRange'); goog.require('goog.asserts'); -goog.require('ol.Size'); +goog.require('ol.Rectangle'); goog.require('ol.TileCoord'); @@ -11,6 +11,7 @@ goog.require('ol.TileCoord'); * by its min/max tile coordinates and is inclusive of coordinates. * * @constructor + * @extends {ol.Rectangle} * @param {number} minX Minimum X. * @param {number} minY Minimum Y. * @param {number} maxX Maximum X. @@ -39,6 +40,7 @@ ol.TileRange = function(minX, minY, maxX, maxY) { this.maxY = maxY; }; +goog.inherits(ol.TileRange, ol.Rectangle); /** @@ -83,28 +85,7 @@ ol.TileRange.prototype.containsTileRange = function(tileRange) { /** - * @param {ol.TileRange} range Other range. - * @return {boolean} The two ranges are equivalent. - */ -ol.TileRange.prototype.equals = function(range) { - return this.minX == range.minX && this.minY == range.minY && - this.maxX == range.maxX && this.maxY == range.maxY; -}; - - -/** - * Extend this range so it includes the other. - * @param {ol.TileRange} other Other range. - */ -ol.TileRange.prototype.extend = function(other) { - this.minX = Math.min(this.minX, other.minX); - this.minY = Math.min(this.minY, other.minY); - this.maxX = Math.max(this.maxX, other.maxX); - this.maxY = Math.max(this.maxY, other.maxY); -}; - - -/** + * @inheritDoc * @return {number} Height. */ ol.TileRange.prototype.getHeight = function() { @@ -113,30 +94,9 @@ ol.TileRange.prototype.getHeight = function() { /** - * @return {ol.Size} Size. - */ -ol.TileRange.prototype.getSize = function() { - return new ol.Size(this.getWidth(), this.getHeight()); -}; - - -/** + * @inheritDoc * @return {number} Width. */ ol.TileRange.prototype.getWidth = function() { return this.maxX - this.minX + 1; }; - - -/** - * Test for range intersection. - * @param {ol.TileRange} other Other range. - * @return {boolean} The two ranges intersect. - */ -ol.TileRange.prototype.intersects = function(other) { - return this.minX <= other.maxX && - this.maxX >= other.minX && - this.minY <= other.maxY && - this.maxY >= other.minY; -}; - From 5e298304625d0af24d3fff518583f08b49c4bb9e Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Tue, 19 Feb 2013 16:32:56 +0100 Subject: [PATCH 9/9] Add WMTS GetCapabilities parser Implements WMTS GetCapabilities parser, which depends partly on an OWSCommon parser. Integration with the layer source will be done at a later stage. --- src/ol/coordinate.js | 16 ++ src/ol/parser/ogc/owscommon_v1.js | 214 ++++++++++++++++++ src/ol/parser/ogc/owscommon_v1_1_0.js | 45 ++++ src/ol/parser/ogc/wmtscapabilities.exports | 2 + src/ol/parser/ogc/wmtscapabilities.js | 19 ++ src/ol/parser/ogc/wmtscapabilities_v1_0_0.js | 161 +++++++++++++ src/ol/parser/xml.js | 6 + src/ol/projection.js | 22 +- .../ogc/wmtscapabilities_v1_0_0.test.js | 183 +++++++++++++++ .../xml/wmtscapabilities_v1_0_0/arcgis.xml | 70 ++++++ .../multi-getile-1.xml | 92 ++++++++ .../multi-getile-2.xml | 78 +++++++ .../xml/wmtscapabilities_v1_0_0/ogcsample.xml | 158 +++++++++++++ .../restsample-alternate-proj1.xml | 72 ++++++ .../restsample-alternate-proj2.xml | 72 ++++++ .../wmtscapabilities_v1_0_0/restsample.xml | 126 +++++++++++ 16 files changed, 1334 insertions(+), 2 deletions(-) create mode 100644 src/ol/parser/ogc/owscommon_v1.js create mode 100644 src/ol/parser/ogc/owscommon_v1_1_0.js create mode 100644 src/ol/parser/ogc/wmtscapabilities.exports create mode 100644 src/ol/parser/ogc/wmtscapabilities.js create mode 100644 src/ol/parser/ogc/wmtscapabilities_v1_0_0.js create mode 100644 test/spec/ol/parser/ogc/wmtscapabilities_v1_0_0.test.js create mode 100644 test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/arcgis.xml create mode 100644 test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/multi-getile-1.xml create mode 100644 test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/multi-getile-2.xml create mode 100644 test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/ogcsample.xml create mode 100644 test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample-alternate-proj1.xml create mode 100644 test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample-alternate-proj2.xml create mode 100644 test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample.xml diff --git a/src/ol/coordinate.js b/src/ol/coordinate.js index b3bd66b10c..dcf4832038 100644 --- a/src/ol/coordinate.js +++ b/src/ol/coordinate.js @@ -88,3 +88,19 @@ ol.Coordinate.toStringXY = function(coordinate, opt_precision) { return ''; } }; + + +/** + * Create an ol.Coordinate from an Array and take into account axis order. + * @param {Array} array The array with coordinates. + * @param {string} axis the axis info. + * @return {ol.Coordinate} The coordinate created. + */ +ol.Coordinate.fromProjectedArray = function(array, axis) { + var firstAxis = axis.charAt(0); + if (firstAxis === 'n' || firstAxis === 's') { + return new ol.Coordinate(array[1], array[0]); + } else { + return new ol.Coordinate(array[0], array[1]); + } +}; diff --git a/src/ol/parser/ogc/owscommon_v1.js b/src/ol/parser/ogc/owscommon_v1.js new file mode 100644 index 0000000000..ae01b4ec93 --- /dev/null +++ b/src/ol/parser/ogc/owscommon_v1.js @@ -0,0 +1,214 @@ +goog.provide('ol.parser.ogc.OWSCommon_v1'); +goog.require('ol.Extent'); +goog.require('ol.parser.XML'); + + + +/** + * @constructor + * @extends {ol.parser.XML} + */ +ol.parser.ogc.OWSCommon_v1 = function() { + this.readers = { + 'http://www.opengis.net/ows': { + 'ServiceIdentification': function(node, obj) { + obj['serviceIdentification'] = {}; + this.readChildNodes(node, obj['serviceIdentification']); + }, + 'Title': function(node, obj) { + obj['title'] = this.getChildValue(node); + }, + 'Abstract': function(node, serviceIdentification) { + serviceIdentification['abstract'] = this.getChildValue(node); + }, + 'Keywords': function(node, serviceIdentification) { + serviceIdentification['keywords'] = {}; + this.readChildNodes(node, serviceIdentification['keywords']); + }, + 'Keyword': function(node, keywords) { + keywords[this.getChildValue(node)] = true; + }, + 'ServiceType': function(node, serviceIdentification) { + serviceIdentification['serviceType'] = { + 'codeSpace': node.getAttribute('codeSpace'), + 'value': this.getChildValue(node)}; + }, + 'ServiceTypeVersion': function(node, serviceIdentification) { + serviceIdentification['serviceTypeVersion'] = this.getChildValue(node); + }, + 'Fees': function(node, serviceIdentification) { + serviceIdentification['fees'] = this.getChildValue(node); + }, + 'AccessConstraints': function(node, serviceIdentification) { + serviceIdentification['accessConstraints'] = + this.getChildValue(node); + }, + 'ServiceProvider': function(node, obj) { + obj['serviceProvider'] = {}; + this.readChildNodes(node, obj['serviceProvider']); + }, + 'ProviderName': function(node, serviceProvider) { + serviceProvider['providerName'] = this.getChildValue(node); + }, + 'ProviderSite': function(node, serviceProvider) { + serviceProvider['providerSite'] = this.getAttributeNS(node, + 'http://www.w3.org/1999/xlink', 'href'); + }, + 'ServiceContact': function(node, serviceProvider) { + serviceProvider['serviceContact'] = {}; + this.readChildNodes(node, serviceProvider['serviceContact']); + }, + 'IndividualName': function(node, serviceContact) { + serviceContact['individualName'] = this.getChildValue(node); + }, + 'PositionName': function(node, serviceContact) { + serviceContact['positionName'] = this.getChildValue(node); + }, + 'ContactInfo': function(node, serviceContact) { + serviceContact['contactInfo'] = {}; + this.readChildNodes(node, serviceContact['contactInfo']); + }, + 'Phone': function(node, contactInfo) { + contactInfo['phone'] = {}; + this.readChildNodes(node, contactInfo['phone']); + }, + 'Voice': function(node, phone) { + phone['voice'] = this.getChildValue(node); + }, + 'Address': function(node, contactInfo) { + contactInfo['address'] = {}; + this.readChildNodes(node, contactInfo['address']); + }, + 'DeliveryPoint': function(node, address) { + address['deliveryPoint'] = this.getChildValue(node); + }, + 'City': function(node, address) { + address['city'] = this.getChildValue(node); + }, + 'AdministrativeArea': function(node, address) { + address['administrativeArea'] = this.getChildValue(node); + }, + 'PostalCode': function(node, address) { + address['postalCode'] = this.getChildValue(node); + }, + 'Country': function(node, address) { + address['country'] = this.getChildValue(node); + }, + 'ElectronicMailAddress': function(node, address) { + address['electronicMailAddress'] = this.getChildValue(node); + }, + 'Role': function(node, serviceContact) { + serviceContact['role'] = this.getChildValue(node); + }, + 'OperationsMetadata': function(node, obj) { + obj['operationsMetadata'] = {}; + this.readChildNodes(node, obj['operationsMetadata']); + }, + 'Operation': function(node, operationsMetadata) { + var name = node.getAttribute('name'); + operationsMetadata[name] = {}; + this.readChildNodes(node, operationsMetadata[name]); + }, + 'DCP': function(node, operation) { + operation['dcp'] = {}; + this.readChildNodes(node, operation['dcp']); + }, + 'HTTP': function(node, dcp) { + dcp['http'] = {}; + this.readChildNodes(node, dcp['http']); + }, + 'Get': function(node, http) { + if (!http['get']) { + http['get'] = []; + } + var obj = { + 'url': this.getAttributeNS(node, 'http://www.w3.org/1999/xlink', + 'href') + }; + this.readChildNodes(node, obj); + http['get'].push(obj); + }, + 'Post': function(node, http) { + if (!http['post']) { + http['post'] = []; + } + var obj = { + 'url': this.getAttributeNS(node, 'http://www.w3.org/1999/xlink', + 'href') + }; + this.readChildNodes(node, obj); + http['post'].push(obj); + }, + 'Parameter': function(node, operation) { + if (!operation['parameters']) { + operation['parameters'] = {}; + } + var name = node.getAttribute('name'); + operation['parameters'][name] = {}; + this.readChildNodes(node, operation['parameters'][name]); + }, + 'Constraint': function(node, obj) { + if (!obj['constraints']) { + obj['constraints'] = {}; + } + var name = node.getAttribute('name'); + obj['constraints'][name] = {}; + this.readChildNodes(node, obj['constraints'][name]); + }, + 'Value': function(node, allowedValues) { + allowedValues[this.getChildValue(node)] = true; + }, + 'OutputFormat': function(node, obj) { + obj['formats'].push({'value': this.getChildValue(node)}); + this.readChildNodes(node, obj); + }, + 'WGS84BoundingBox': function(node, obj) { + var boundingBox = {}; + boundingBox['crs'] = node.getAttribute('crs'); + if (obj['BoundingBox']) { + obj['BoundingBox'].push(boundingBox); + } else { + obj['projection'] = boundingBox['crs']; + boundingBox = obj; + } + this.readChildNodes(node, boundingBox); + }, + 'BoundingBox': function(node, obj) { + // FIXME: We consider that BoundingBox is the same as WGS84BoundingBox + // LowerCorner = "min_x min_y" + // UpperCorner = "max_x max_y" + // It should normally depend on the projection + var readers = this.readers['http://www.opengis.net/ows']; + readers['WGS84BoundingBox'].apply(this, [node, obj]); + }, + 'LowerCorner': function(node, obj) { + var str = this.getChildValue(node).replace( + this.regExes.trimSpace, ''); + str = str.replace(this.regExes.trimComma, ','); + var pointList = str.split(this.regExes.splitSpace); + obj['left'] = pointList[0]; + obj['bottom'] = pointList[1]; + }, + 'UpperCorner': function(node, obj) { + var str = this.getChildValue(node).replace( + this.regExes.trimSpace, ''); + str = str.replace(this.regExes.trimComma, ','); + var pointList = str.split(this.regExes.splitSpace); + obj['right'] = pointList[0]; + obj['top'] = pointList[1]; + obj['bounds'] = new ol.Extent(parseFloat(obj['left']), + parseFloat(obj['bottom']), parseFloat(obj['right']), + parseFloat(obj['top'])); + delete obj['left']; + delete obj['bottom']; + delete obj['right']; + delete obj['top']; + }, + 'Language': function(node, obj) { + obj['language'] = this.getChildValue(node); + } + } + }; + goog.base(this); +}; +goog.inherits(ol.parser.ogc.OWSCommon_v1, ol.parser.XML); diff --git a/src/ol/parser/ogc/owscommon_v1_1_0.js b/src/ol/parser/ogc/owscommon_v1_1_0.js new file mode 100644 index 0000000000..98c7bfc016 --- /dev/null +++ b/src/ol/parser/ogc/owscommon_v1_1_0.js @@ -0,0 +1,45 @@ +goog.provide('ol.parser.ogc.OWSCommon_v1_1_0'); +goog.require('goog.object'); +goog.require('ol.parser.ogc.OWSCommon_v1'); + + + +/** + * @constructor + * @extends {ol.parser.ogc.OWSCommon_v1} + */ +ol.parser.ogc.OWSCommon_v1_1_0 = function() { + goog.base(this); + this.readers['http://www.opengis.net/ows/1.1'] = + this.readers['http://www.opengis.net/ows']; + goog.object.extend(this.readers['http://www.opengis.net/ows/1.1'], { + 'AllowedValues': function(node, parameter) { + parameter['allowedValues'] = {}; + this.readChildNodes(node, parameter['allowedValues']); + }, + 'AnyValue': function(node, parameter) { + parameter['anyValue'] = true; + }, + 'DataType': function(node, parameter) { + parameter['dataType'] = this.getChildValue(node); + }, + 'Range': function(node, allowedValues) { + allowedValues['range'] = {}; + this.readChildNodes(node, allowedValues['range']); + }, + 'MinimumValue': function(node, range) { + range['minValue'] = this.getChildValue(node); + }, + 'MaximumValue': function(node, range) { + range['maxValue'] = this.getChildValue(node); + }, + 'Identifier': function(node, obj) { + obj['identifier'] = this.getChildValue(node); + }, + 'SupportedCRS': function(node, obj) { + obj['supportedCRS'] = this.getChildValue(node); + } + }); +}; +goog.inherits(ol.parser.ogc.OWSCommon_v1_1_0, + ol.parser.ogc.OWSCommon_v1); diff --git a/src/ol/parser/ogc/wmtscapabilities.exports b/src/ol/parser/ogc/wmtscapabilities.exports new file mode 100644 index 0000000000..dec7a65f4e --- /dev/null +++ b/src/ol/parser/ogc/wmtscapabilities.exports @@ -0,0 +1,2 @@ +@exportSymbol ol.parser.ogc.WMTSCapabilities +@exportProperty ol.parser.ogc.WMTSCapabilities.prototype.read diff --git a/src/ol/parser/ogc/wmtscapabilities.js b/src/ol/parser/ogc/wmtscapabilities.js new file mode 100644 index 0000000000..fee0293153 --- /dev/null +++ b/src/ol/parser/ogc/wmtscapabilities.js @@ -0,0 +1,19 @@ +goog.provide('ol.parser.ogc.WMTSCapabilities'); +goog.require('ol.parser.ogc.Versioned'); +goog.require('ol.parser.ogc.WMTSCapabilities_v1_0_0'); + + + +/** + * @constructor + * @param {Object=} opt_options Options which will be set on this object. + * @extends {ol.parser.ogc.Versioned} + */ +ol.parser.ogc.WMTSCapabilities = function(opt_options) { + opt_options = opt_options || {}; + opt_options['defaultVersion'] = '1.0.0'; + this.parsers = {}; + this.parsers['v1_0_0'] = ol.parser.ogc.WMTSCapabilities_v1_0_0; + goog.base(this, opt_options); +}; +goog.inherits(ol.parser.ogc.WMTSCapabilities, ol.parser.ogc.Versioned); diff --git a/src/ol/parser/ogc/wmtscapabilities_v1_0_0.js b/src/ol/parser/ogc/wmtscapabilities_v1_0_0.js new file mode 100644 index 0000000000..cade9332f8 --- /dev/null +++ b/src/ol/parser/ogc/wmtscapabilities_v1_0_0.js @@ -0,0 +1,161 @@ +goog.provide('ol.parser.ogc.WMTSCapabilities_v1_0_0'); +goog.require('goog.dom.xml'); +goog.require('ol.Coordinate'); +goog.require('ol.Projection'); +goog.require('ol.parser.XML'); +goog.require('ol.parser.ogc.OWSCommon_v1_1_0'); + + + +/** + * @constructor + * @extends {ol.parser.XML} + */ +ol.parser.ogc.WMTSCapabilities_v1_0_0 = function() { + this.readers = { + 'http://www.opengis.net/wmts/1.0': { + 'Capabilities': function(node, obj) { + this.readChildNodes(node, obj); + }, + 'Contents': function(node, obj) { + obj['contents'] = {}; + obj['contents']['layers'] = []; + obj['contents']['tileMatrixSets'] = {}; + this.readChildNodes(node, obj['contents']); + }, + 'Layer': function(node, obj) { + var layer = { + 'styles': [], + 'formats': [], + 'dimensions': [], + 'tileMatrixSetLinks': [] + }; + layer['layers'] = []; + this.readChildNodes(node, layer); + obj['layers'].push(layer); + }, + 'Style': function(node, obj) { + var style = {}; + style['isDefault'] = (node.getAttribute('isDefault') === 'true'); + this.readChildNodes(node, style); + obj['styles'].push(style); + }, + 'Format': function(node, obj) { + obj['formats'].push(this.getChildValue(node)); + }, + 'TileMatrixSetLink': function(node, obj) { + var tileMatrixSetLink = {}; + this.readChildNodes(node, tileMatrixSetLink); + obj['tileMatrixSetLinks'].push(tileMatrixSetLink); + }, + 'TileMatrixSet': function(node, obj) { + // node could be child of wmts:Contents or wmts:TileMatrixSetLink + // duck type wmts:Contents by looking for layers + if (obj['layers']) { + // TileMatrixSet as object type in schema + var tileMatrixSet = { + 'matrixIds': [] + }; + this.readChildNodes(node, tileMatrixSet); + obj['tileMatrixSets'][tileMatrixSet['identifier']] = tileMatrixSet; + } else { + // TileMatrixSet as string type in schema + obj['tileMatrixSet'] = this.getChildValue(node); + } + }, + 'TileMatrix': function(node, obj) { + var tileMatrix = { + 'supportedCRS': obj.supportedCRS + }; + this.readChildNodes(node, tileMatrix); + obj['matrixIds'].push(tileMatrix); + }, + 'ScaleDenominator': function(node, obj) { + obj['scaleDenominator'] = parseFloat(this.getChildValue(node)); + }, + 'TopLeftCorner': function(node, obj) { + var topLeftCorner = this.getChildValue(node); + var coords = topLeftCorner.split(' '); + var axis = ol.Projection.getFromCode(obj['supportedCRS']).getAxis(); + obj['topLeftCorner'] = ol.Coordinate.fromProjectedArray( + [parseFloat(coords[0]), parseFloat(coords[1])], axis); + }, + 'TileWidth': function(node, obj) { + obj['tileWidth'] = parseInt(this.getChildValue(node), 10); + }, + 'TileHeight': function(node, obj) { + obj['tileHeight'] = parseInt(this.getChildValue(node), 10); + }, + 'MatrixWidth': function(node, obj) { + obj['matrixWidth'] = parseInt(this.getChildValue(node), 10); + }, + 'MatrixHeight': function(node, obj) { + obj['matrixHeight'] = parseInt(this.getChildValue(node), 10); + }, + 'ResourceURL': function(node, obj) { + obj['resourceUrl'] = obj['resourceUrl'] || {}; + var resourceType = node.getAttribute('resourceType'); + if (!obj['resourceUrls']) { + obj['resourceUrls'] = []; + } + var resourceUrl = obj['resourceUrl'][resourceType] = { + 'format': node.getAttribute('format'), + 'template': node.getAttribute('template'), + 'resourceType': resourceType + }; + obj['resourceUrls'].push(resourceUrl); + }, + 'WSDL': function(node, obj) { + obj['wsdl'] = {}; + obj['wsdl']['href'] = this.getAttributeNS(node, + 'http://www.w3.org/1999/xlink', 'href'); + // TODO: other attributes of element + }, + 'ServiceMetadataURL': function(node, obj) { + obj['serviceMetadataUrl'] = {}; + obj['serviceMetadataUrl']['href'] = + this.getAttributeNS(node, 'http://www.w3.org/1999/xlink', 'href'); + // TODO: other attributes of element + }, + 'LegendURL': function(node, obj) { + obj['legend'] = {}; + obj['legend']['href'] = this.getAttributeNS(node, + 'http://www.w3.org/1999/xlink', 'href'); + obj['legend']['format'] = node.getAttribute('format'); + }, + 'Dimension': function(node, obj) { + var dimension = {'values': []}; + this.readChildNodes(node, dimension); + obj['dimensions'].push(dimension); + }, + 'Default': function(node, obj) { + obj['default'] = this.getChildValue(node); + }, + 'Value': function(node, obj) { + obj['values'].push(this.getChildValue(node)); + } + } + }; + var ows = new ol.parser.ogc.OWSCommon_v1_1_0(); + this.readers['http://www.opengis.net/ows/1.1'] = + ows.readers['http://www.opengis.net/ows/1.1']; + goog.base(this); +}; +goog.inherits(ol.parser.ogc.WMTSCapabilities_v1_0_0, ol.parser.XML); + + +/** + * @param {string|Document|Element} data Data to read. + * @return {Object} An object representing the document. + */ +ol.parser.ogc.WMTSCapabilities_v1_0_0.prototype.read = function(data) { + if (typeof data == 'string') { + data = goog.dom.xml.loadXml(data); + } + if (data && data.nodeType == 9) { + data = data.documentElement; + } + var obj = {}; + this.readNode(data, obj); + return obj; +}; diff --git a/src/ol/parser/xml.js b/src/ol/parser/xml.js index d0df3ab763..6e07cf645c 100644 --- a/src/ol/parser/xml.js +++ b/src/ol/parser/xml.js @@ -6,6 +6,12 @@ goog.provide('ol.parser.XML'); * @constructor */ ol.parser.XML = function() { + this.regExes = { + trimSpace: (/^\s*|\s*$/g), + removeSpace: (/\s*/g), + splitSpace: (/\s+/), + trimComma: (/\s*,\s*/g) + }; }; diff --git a/src/ol/projection.js b/src/ol/projection.js index df23c5fb68..d6c57812a0 100644 --- a/src/ol/projection.js +++ b/src/ol/projection.js @@ -36,8 +36,9 @@ ol.ProjectionUnits = { * @param {string} code Code. * @param {ol.ProjectionUnits} units Units. * @param {ol.Extent} extent Extent. + * @param {string=} opt_axis Axis order. */ -ol.Projection = function(code, units, extent) { +ol.Projection = function(code, units, extent, opt_axis) { /** * @private @@ -57,6 +58,12 @@ ol.Projection = function(code, units, extent) { */ this.extent_ = extent; + /** + * @private + * @type {string} + */ + this.axis_ = opt_axis || 'enu'; + }; @@ -84,6 +91,14 @@ ol.Projection.prototype.getUnits = function() { }; +/** + * @return {string} Axis. + */ +ol.Projection.prototype.getAxis = function() { + return this.axis_; +}; + + /** * @constructor @@ -526,6 +541,7 @@ ol.Projection.EPSG_4326_EXTENT_ = new ol.Extent(-180, -90, 180, 90); */ ol.Projection.EPSG_4326_LIKE_CODES_ = [ 'CRS:84', + 'urn:ogc:def:crs:OGC:1.3:CRS84', 'EPSG:4326', 'urn:ogc:def:crs:EPSG:6.6:4326' ]; @@ -543,7 +559,9 @@ ol.Projection.EPSG_4326_LIKE_PROJECTIONS = goog.array.map( return new ol.Projection( code, ol.ProjectionUnits.DEGREES, - ol.Projection.EPSG_4326_EXTENT_); + ol.Projection.EPSG_4326_EXTENT_, + code === 'CRS:84' || code === 'urn:ogc:def:crs:OGC:1.3:CRS84' ? + 'enu' : 'neu'); }); diff --git a/test/spec/ol/parser/ogc/wmtscapabilities_v1_0_0.test.js b/test/spec/ol/parser/ogc/wmtscapabilities_v1_0_0.test.js new file mode 100644 index 0000000000..af7b43cd8d --- /dev/null +++ b/test/spec/ol/parser/ogc/wmtscapabilities_v1_0_0.test.js @@ -0,0 +1,183 @@ +goog.provide('ol.test.parser.ogc.WMTSCapabilities_v1_0_0'); + +describe('ol.parser.ogc.wmtscapabilities_v1_0_0', function() { + + var parser = new ol.parser.ogc.WMTSCapabilities(); + + describe('test ows', function() { + it('Test ows', function() { + var obj, serviceIdentification, serviceProvider, operationsMetadata, + contactInfo; + runs(function() { + var url = 'spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/' + + 'ogcsample.xml'; + goog.net.XhrIo.send(url, function(e) { + var xhr = e.target; + obj = parser.read(xhr.getResponseXml()); + serviceIdentification = obj.serviceIdentification; + serviceProvider = obj.serviceProvider; + operationsMetadata = obj.operationsMetadata; + contactInfo = serviceProvider.serviceContact.contactInfo; + }); + }); + waitsFor(function() { + return (obj !== undefined); + }, 'XHR timeout', 1000); + runs(function() { + expect(serviceIdentification.title).toEqual('Web Map Tile Service'); + expect(serviceIdentification.serviceTypeVersion).toEqual('1.0.0'); + expect(serviceIdentification.serviceType.value).toEqual('OGC WMTS'); + expect(serviceProvider.providerName).toEqual('MiraMon'); + var url = 'http://www.creaf.uab.es/miramon'; + expect(serviceProvider.providerSite).toEqual(url); + var name = 'Joan Maso Pau'; + expect(serviceProvider.serviceContact.individualName).toEqual(name); + var position = 'Senior Software Engineer'; + expect(serviceProvider.serviceContact.positionName).toEqual(position); + expect(contactInfo.address.administrativeArea).toEqual('Barcelona'); + expect(contactInfo.address.city).toEqual('Bellaterra'); + expect(contactInfo.address.country).toEqual('Spain'); + expect(contactInfo.address.deliveryPoint).toEqual('Fac Ciencies UAB'); + var email = 'joan.maso@uab.es'; + expect(contactInfo.address.electronicMailAddress).toEqual(email); + expect(contactInfo.address.postalCode).toEqual('08193'); + expect(contactInfo.phone.voice).toEqual('+34 93 581 1312'); + var dcp = operationsMetadata.GetCapabilities.dcp; + url = 'http://www.miramon.uab.es/cgi-bin/MiraMon5_0.cgi?'; + expect(dcp.http.get[0].url).toEqual(url); + dcp = operationsMetadata.GetCapabilities.dcp; + expect(dcp.http.get[0].constraints.GetEncoding.allowedValues).toEqual( + {'KVP': true}); + url = 'http://www.miramon.uab.es/cgi-bin/MiraMon5_0.cgi?'; + dcp = operationsMetadata.GetFeatureInfo.dcp; + expect(dcp.http.get[0].url).toEqual(url); + dcp = operationsMetadata.GetFeatureInfo.dcp; + expect(dcp.http.get[0].constraints).toBeUndefined(); + url = 'http://www.miramon.uab.es/cgi-bin/MiraMon5_0.cgi?'; + expect(operationsMetadata.GetTile.dcp.http.get[0].url).toEqual(url); + dcp = operationsMetadata.GetTile.dcp; + expect(dcp.http.get[0].constraints).toBeUndefined(); + }); + }); + }); + describe('test layers', function() { + it('Test layers', function() { + var obj, contents, layer, wgs84Bbox, dimensions; + runs(function() { + var url = 'spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/' + + 'ogcsample.xml'; + goog.net.XhrIo.send(url, function(e) { + var xhr = e.target; + obj = parser.read(xhr.getResponseXml()); + contents = obj.contents; + layer = contents.layers[0]; + wgs84Bbox = layer.bounds; + dimensions = layer.dimensions; + }); + }); + waitsFor(function() { + return (obj !== undefined); + }, 'XHR timeout', 1000); + runs(function() { + expect(contents.layers.length).toEqual(1); + expect(layer['abstract']).toEqual('Coastline/shorelines (BA010)'); + expect(layer.identifier).toEqual('coastlines'); + expect(layer.title).toEqual('Coastlines'); + expect(layer.formats.length).toEqual(2); + expect(layer.formats[0]).toEqual('image/png'); + expect(layer.formats[1]).toEqual('image/gif'); + expect(layer.styles.length).toEqual(2); + expect(layer.styles[0].identifier).toEqual('DarkBlue'); + expect(layer.styles[0].isDefault).toBeTruthy(); + expect(layer.styles[0].title).toEqual('Dark Blue'); + var url = 'http://www.miramon.uab.es/wmts/Coastlines/' + + 'coastlines_darkBlue.png'; + expect(layer.styles[0].legend.href).toEqual(url); + expect(layer.styles[0].legend.format).toEqual('image/png'); + expect(layer.styles[1].identifier).toEqual('thickAndRed'); + expect(!layer.styles[1].isDefault).toBeTruthy(); + expect(layer.styles[1].title).toEqual('Thick And Red'); + expect(layer.styles[1].legend).toBeUndefined(); + expect(layer.tileMatrixSetLinks.length).toEqual(1); + expect(layer.tileMatrixSetLinks[0].tileMatrixSet).toEqual('BigWorld'); + expect(wgs84Bbox instanceof ol.Extent).toBeTruthy(); + expect(wgs84Bbox.minX).toEqual(-180.0); + expect(wgs84Bbox.maxX).toEqual(180.0); + expect(wgs84Bbox.minY).toEqual(-90.0); + expect(wgs84Bbox.maxY).toEqual(90.0); + expect(layer.resourceUrl.tile.format).toEqual('image/png'); + var tpl = 'http://www.example.com/wmts/coastlines/{TileMatrix}/' + + '{TileRow}/{TileCol}.png'; + expect(layer.resourceUrl.tile.template).toEqual(tpl); + var format = 'application/gml+xml; version=3.1'; + expect(layer.resourceUrl.FeatureInfo.format).toEqual(format); + tpl = 'http://www.example.com/wmts/coastlines/{TileMatrixSet}/' + + '{TileMatrix}/{TileRow}/{TileCol}/{J}/{I}.xml'; + expect(layer.resourceUrl.FeatureInfo.template).toEqual(tpl); + expect(layer.resourceUrls[0].format).toEqual('image/png'); + expect(layer.resourceUrls[0].resourceType).toEqual('tile'); + tpl = 'http://www.example.com/wmts/coastlines/{TileMatrix}/' + + '{TileRow}/{TileCol}.png'; + expect(layer.resourceUrls[0].template).toEqual(tpl); + format = 'application/gml+xml; version=3.1'; + expect(layer.resourceUrls[1].format).toEqual(format); + expect(layer.resourceUrls[1].resourceType).toEqual('FeatureInfo'); + tpl = 'http://www.example.com/wmts/coastlines/{TileMatrixSet}/' + + '{TileMatrix}/{TileRow}/{TileCol}/{J}/{I}.xml'; + expect(layer.resourceUrls[1].template).toEqual(tpl); + expect(dimensions.length).toEqual(1); + expect(dimensions[0].title).toEqual('Time'); + expect(dimensions[0]['abstract']).toEqual('Monthly datasets'); + expect(dimensions[0].identifier).toEqual('TIME'); + expect(dimensions[0]['default']).toEqual('default'); + expect(dimensions[0].values.length).toEqual(3); + expect(dimensions[0].values[0]).toEqual('2007-05'); + expect(dimensions[0].values[1]).toEqual('2007-06'); + expect(dimensions[0].values[1]).toEqual('2007-06'); + expect(dimensions[0].values[2]).toEqual('2007-07'); + }); + }); + }); + describe('test tileMatrixSets', function() { + it('Test tileMatrixSets', function() { + var obj, tileMatrixSets, bigWorld; + runs(function() { + var url = 'spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/' + + 'ogcsample.xml'; + goog.net.XhrIo.send(url, function(e) { + var xhr = e.target; + obj = parser.read(xhr.getResponseXml()); + tileMatrixSets = obj.contents.tileMatrixSets; + bigWorld = tileMatrixSets['BigWorld']; + }); + }); + waitsFor(function() { + return (obj !== undefined); + }, 'XHR timeout', 1000); + runs(function() { + expect(bigWorld).toBeDefined(); + expect(bigWorld.identifier).toEqual('BigWorld'); + expect(bigWorld.matrixIds.length).toEqual(2); + expect(bigWorld.matrixIds[0].identifier).toEqual('1e6'); + expect(bigWorld.matrixIds[0].matrixHeight).toEqual(50000); + expect(bigWorld.matrixIds[0].matrixWidth).toEqual(60000); + expect(bigWorld.matrixIds[0].scaleDenominator).toEqual(1000000); + expect(bigWorld.matrixIds[0].tileWidth).toEqual(256); + expect(bigWorld.matrixIds[0].tileHeight).toEqual(256); + expect(bigWorld.matrixIds[0].topLeftCorner.x).toEqual(-180); + expect(bigWorld.matrixIds[0].topLeftCorner.y).toEqual(84); + expect(bigWorld.matrixIds[1].identifier).toEqual('2.5e6'); + expect(bigWorld.matrixIds[1].matrixHeight).toEqual(7000); + expect(bigWorld.matrixIds[1].matrixWidth).toEqual(9000); + expect(bigWorld.matrixIds[1].scaleDenominator).toEqual(2500000); + expect(bigWorld.matrixIds[1].tileWidth).toEqual(256); + expect(bigWorld.matrixIds[1].tileHeight).toEqual(256); + expect(bigWorld.matrixIds[1].topLeftCorner.x).toEqual(-180); + expect(bigWorld.matrixIds[1].topLeftCorner.y).toEqual(84); + }); + }); + }); +}); + +goog.require('goog.net.XhrIo'); +goog.require('ol.parser.ogc.WMTSCapabilities'); diff --git a/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/arcgis.xml b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/arcgis.xml new file mode 100644 index 0000000000..50279f5303 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/arcgis.xml @@ -0,0 +1,70 @@ + + + + WorldTimeZones + OGC WMTS + 1.0.0 + + + + + + + + + RESTful + + + + + + + KVP + + + + + + + + + + WorldTimeZones + WorldTimeZones + + -2.0037507067161843E7 -3.024097195838617E7 + 2.0037507067161843E7 3.0240971458386205E7 + + + -179.99999550841463 -88.99999992161119 + 179.99999550841463 88.99999992161118 + + + image/png + + GoogleMapsCompatible + + + + + GoogleMapsCompatible + the wellknown 'GoogleMapsCompatible' tile matrix set defined by OGC WMTS specification + GoogleMapsCompatible + urn:ogc:def:crs:EPSG:6.18:3:3857 + urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible + + 5 + 17471320.75089743 + -20037508.34278925 20037508.34278925 + 256 + 256 + 32 + 32 + + + + + diff --git a/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/multi-getile-1.xml b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/multi-getile-1.xml new file mode 100644 index 0000000000..f4c36b969a --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/multi-getile-1.xml @@ -0,0 +1,92 @@ + + + + + + + + + + REST + + + + + + + + + + + + + REST + + + + + + + REST + + + + + + + KVP + + + + + + + KVP + + + + + + + + + + Agglomérations et villes isolées + Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale. + + 5.140242 45.398181 + 11.47757 48.230651 + + ch.are.agglomerationen_isolierte_staedte-2000 + + + image/png + + Time + 20090101 + 20090101 + + + 21781 + + + + 21781 + urn:ogc:def:crs:EPSG:1.0:21781 + + 0 + 14285750.5715 + 420000.0 350000.0 + 256 + 256 + 1 + 1 + + + + + diff --git a/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/multi-getile-2.xml b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/multi-getile-2.xml new file mode 100644 index 0000000000..58f0f61155 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/multi-getile-2.xml @@ -0,0 +1,78 @@ + + + + + + + + + + REST + + + + + + + + + + + + + KVP + + + + + + + REST + + + + + + + + + + Agglomérations et villes isolées + Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale. + + 5.140242 45.398181 + 11.47757 48.230651 + + ch.are.agglomerationen_isolierte_staedte-2000 + + + image/png + + Time + 20090101 + 20090101 + + + 21781 + + + + 21781 + urn:ogc:def:crs:EPSG:1.0:21781 + + 0 + 14285750.5715 + 420000.0 350000.0 + 256 + 256 + 1 + 1 + + + + + diff --git a/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/ogcsample.xml b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/ogcsample.xml new file mode 100644 index 0000000000..9f0f1bc690 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/ogcsample.xml @@ -0,0 +1,158 @@ + + + + Web Map Tile Service + Service that contrains the map access interface to some TileMatrixSets + + tile + tile matrix set + map + + OGC WMTS + 1.0.0 + none + none + + + MiraMon + + + Joan Maso Pau + Senior Software Engineer + + + +34 93 581 1312 + +34 93 581 4151 + + + Fac Ciencies UAB + Bellaterra + Barcelona + 08193 + Spain + joan.maso@uab.es + + + + + + + + + + + + KVP + + + + + + + + + + + + + + + + + + + + + + + + Coastlines + Coastline/shorelines (BA010) + + -180 -90 + 180 90 + + coastlines + + + + + image/png + image/gif + + Time + Monthly datasets + TIME + 2007-05 + 2007-06 + 2007-07 + default + + + BigWorld + + + + BigWorld + urn:ogc:def:crs:OGC:1.3:CRS84 + + 1e6 + 1e6 + -180 84 + 256 + 256 + 60000 + 50000 + + + 2.5e6 + 2.5e6 + -180 84 + 256 + 256 + 9000 + 7000 + + + + + + Foundation + "Digital Chart Of The World" data + Foundation + + Boundaries + Boundaries + coastlines + politicalBoundaries + depthContours + + + Transportation + Transportation + roads + railroads + airports + + + + World Geology + World Geology + worldAgeRockType + worldFaultLines + felsicMagmatic + maficMagmatic + + + diff --git a/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample-alternate-proj1.xml b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample-alternate-proj1.xml new file mode 100644 index 0000000000..5b57b8b2f9 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample-alternate-proj1.xml @@ -0,0 +1,72 @@ + + + + + + + + + + REST + + + + + + + + + + + + + REST + + + + + + + + + + Agglomérations et villes isolées + Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale. + + 5.140242 45.398181 + 11.47757 48.230651 + + ch.are.agglomerationen_isolierte_staedte-2000 + + + image/png + + Time + 20090101 + 20090101 + + + 21781 + + + + + 21781 + urn:ogc:def:crs:EPSG:21781 + + 0 + 14285750.5715 + 420000.0 350000.0 + 256 + 256 + 1 + 1 + + + + + diff --git a/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample-alternate-proj2.xml b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample-alternate-proj2.xml new file mode 100644 index 0000000000..6d73ae4dc5 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample-alternate-proj2.xml @@ -0,0 +1,72 @@ + + + + + + + + + + REST + + + + + + + + + + + + + REST + + + + + + + + + + Agglomérations et villes isolées + Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale. + + 5.140242 45.398181 + 11.47757 48.230651 + + ch.are.agglomerationen_isolierte_staedte-2000 + + + image/png + + Time + 20090101 + 20090101 + + + 21781 + + + + + 21781 + urn:ogc:def:crs:EPSG:1.0:21781 + + 0 + 14285750.5715 + 420000.0 350000.0 + 256 + 256 + 1 + 1 + + + + + diff --git a/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample.xml b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample.xml new file mode 100644 index 0000000000..b7434e4724 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/wmtscapabilities_v1_0_0/restsample.xml @@ -0,0 +1,126 @@ + + + + Federal Geodata Infrastructure of Switzerland + Some Geodata are subject to license and fees + + FGDI + Pixelkarte + Switzerland + + OGC WMTS + 1.0.0 + yes + license + + + swisstopo + + + David Oesch + + + + +41 (0)31 / 963 21 11 + +41 (0)31 / 963 24 59 + + + swisstopo + Bern + BE + 3084 + Switzerland + + + + + + + + + + + + + REST + + + + + + + + + + + + + REST + + + + + + + + + + Agglomérations et villes isolées + Les agglomérations et villes isolées (communes non rattachées à une agglomération et comptant au moins 10`000 habitants) font partie des régions d’analyse de la statistique suisse. Ce niveau géographique est défini depuis plus de 100 ans, afin de mesurer l’urbanisation, phénomène fondamental structurant l’organisation du territoire. Sa fonction principale est de permettre une comparaison spatiale entre des espaces urbains inégalement délimités sur le plan institutionnel. Une version ancienne est appliquée pour la première fois en 1930, puis révisée en 1984 et 1990, toujours sur la base des recensements de la population. La version actuelle classe les 2896 communes de Suisse (état 2000) selon leur appartenance ou pas à une agglomération ou ville isolée en fonction de critères statistiques (Etat et évolution de la population, lien de continuité de la zone bâtie, rapport entre population active occupée et population résidante, structure économique et flux de pendulaires). Les agglomérations et les villes isolées forment l`espace urbain, les territoires restant l`espace rural. La définition des agglomérations de l’OFS n’a pas valeur d’obligation légale. + + 5.140242 45.398181 + 11.47757 48.230651 + + ch.are.agglomerationen_isolierte_staedte-2000 + + + image/png + + Time + 20090101 + 20090101 + + + 21781 + + + + + + 21781 + urn:ogc:def:crs:EPSG::21781 + + 0 + 14285750.5715 + 420000.0 350000.0 + 256 + 256 + 1 + 1 + + + 8 + 7142875.28575 + 420000.0 350000.0 + 256 + 256 + 1 + 1 + + + 12 + 3571437.64288 + 420000.0 350000.0 + 256 + 256 + 2 + 2 + + + + +