diff --git a/src/api/bounds.js b/src/api/bounds.js index 89bf64de01..378b3d2914 100644 --- a/src/api/bounds.js +++ b/src/api/bounds.js @@ -38,6 +38,7 @@ ol.bounds = function(opt_arg){ maxX = opt_arg[2]; maxY = opt_arg[3]; } else if (goog.isObject(opt_arg)) { + ol.base.checkKeys(opt_arg, ['minX', 'minY', 'maxX', 'maxY', 'projection']); minX = opt_arg['minX']; minY = opt_arg['minY']; maxX = opt_arg['maxX']; diff --git a/src/api/feature.js b/src/api/feature.js index 9465a148d5..cc71cfe2e7 100644 --- a/src/api/feature.js +++ b/src/api/feature.js @@ -1,5 +1,6 @@ goog.provide('ol.feature'); +goog.require('ol.base'); goog.require('ol.Feature'); goog.require('ol.geom.Geometry'); @@ -29,6 +30,7 @@ ol.feature = function(opt_arg){ return opt_arg; } else if (goog.isObject(opt_arg)) { + ol.base.checkKeys(opt_arg, ['geometry', 'properties', 'type']); properties = opt_arg['properties']; geometry = opt_arg['geometry']; type = opt_arg['type']; diff --git a/src/api/loc.js b/src/api/loc.js index 69d2d87f4f..5fceac35f6 100644 --- a/src/api/loc.js +++ b/src/api/loc.js @@ -43,6 +43,7 @@ ol.loc = function(opt_arg){ z = opt_arg[2]; projection = opt_arg[3]; } else if (goog.isObject(opt_arg)) { + ol.base.checkKeys(opt_arg, ['projection', 'x', 'y', 'z']); x = opt_arg['x']; y = opt_arg['y']; z = opt_arg['z']; diff --git a/src/api/map.js b/src/api/map.js index 1adea7d233..67ca68569b 100644 --- a/src/api/map.js +++ b/src/api/map.js @@ -49,33 +49,18 @@ ol.map = function(opt_arg) { return opt_arg; } else if (goog.isObject(opt_arg)) { + ol.base.checkKeys(opt_arg, ['center', 'zoom', 'numZoomLevels', 'projection', 'userProjection', 'maxExtent', 'maxRes', 'resolutions', 'renderTo', 'layers', 'controls']); center = opt_arg['center']; - delete opt_arg['center']; zoom = opt_arg['zoom']; - delete opt_arg['zoom']; numZoomLevels = opt_arg['numZoomLevels']; - delete opt_arg['numZoomLevels']; projection = opt_arg['projection']; - delete opt_arg['projection']; userProjection = opt_arg['userProjection']; - delete opt_arg['userProjection']; maxExtent = opt_arg['maxExtent']; - delete opt_arg['maxExtent']; maxRes = opt_arg['maxRes']; - delete opt_arg['maxRes']; resolutions = opt_arg['resolutions']; - delete opt_arg['resolutions']; renderTo = opt_arg['renderTo']; - delete opt_arg['renderTo']; layers = opt_arg['layers']; - delete opt_arg['layers']; controls = opt_arg['controls']; - delete opt_arg['controls']; - var k = goog.object.getAnyKey(opt_arg); - if (goog.isDef(k)) { - ol.error(k + ' is not a map option'); - } - } else { throw new Error('ol.map'); @@ -236,7 +221,7 @@ ol.Map.prototype.controls = function(opt_arg) { /** * @export * @param {Array=} opt_arg - * @returns {ol.Map|ol.UnreferencedBounds|undefined} Map max extent. + * @returns {ol.Map|ol.Bounds|undefined} Map max extent. */ ol.Map.prototype.maxExtent = function(opt_arg) { if (arguments.length == 1 && goog.isDef(opt_arg)) { @@ -247,28 +232,6 @@ ol.Map.prototype.maxExtent = function(opt_arg) { } }; -/** - * @export - * @param {number=} opt_arg - * @returns {ol.Map|number|undefined} Map maximum resolution - */ -ol.Map.prototype.maxRes = function(opt_arg) { - if (arguments.length == 1 && goog.isDef(opt_arg)) { - this.setMaxRes(opt_arg); - return this; - } else { - return this.getMaxRes(); - } -}; - -/** - * @param {number} arg - * @returns {number} resolution for a given zoom level - */ -ol.Map.prototype.getResForZoom = function(arg) { - return this.getResolutionForZoom(arg); -}; - /** * @param {string|Element} arg Render the map to a container * @returns {ol.Map} diff --git a/src/api/projection.js b/src/api/projection.js index d8ff8c9821..cd302aafe1 100644 --- a/src/api/projection.js +++ b/src/api/projection.js @@ -1,10 +1,11 @@ goog.provide('ol.projection'); +goog.require('ol.base'); goog.require('ol.Projection'); /** - * @typedef {ol.Projection|string} + * @typedef {ol.Projection|Object|string} */ ol.ProjectionLike; @@ -32,6 +33,7 @@ ol.projection = function(opt_arg){ code = opt_arg; } else if (goog.isObject(opt_arg)) { + ol.base.checkKeys(opt_arg, ['code', 'maxExtent', 'units']); if (goog.isString(opt_arg['code'])) { code = opt_arg['code']; } else { diff --git a/src/ol.js b/src/ol.js index 4489f2b092..b8f91ab859 100644 --- a/src/ol.js +++ b/src/ol.js @@ -1,4 +1,6 @@ goog.provide("ol"); + +goog.require('ol.base'); goog.require('ol.bounds'); goog.require('ol.control.Control'); goog.require('ol.control.Navigation'); diff --git a/src/ol/Map.js b/src/ol/Map.js index 2f58ca8138..125b12e776 100644 --- a/src/ol/Map.js +++ b/src/ol/Map.js @@ -1,6 +1,7 @@ goog.provide('ol.Map'); goog.require('ol.Loc'); +goog.require('ol.Bounds'); goog.require('ol.Projection'); goog.require('ol.event'); goog.require('ol.event.Events'); @@ -65,7 +66,7 @@ ol.Map = function() { /** * @private - * @type {ol.UnreferencedBounds} + * @type {ol.Bounds} */ this.maxExtent_ = null; @@ -209,14 +210,19 @@ ol.Map.prototype.getControls = function() { /** - * @return {ol.UnreferencedBounds} the maxExtent for the map + * @return {ol.Bounds} the maxExtent for the map */ ol.Map.prototype.getMaxExtent = function() { if (goog.isDefAndNotNull(this.maxExtent_)) { return this.maxExtent_; } else { - var extent = this.getProjection().getExtent(); + var projection = this.getProjection(); + var extent = projection.getExtent(); if (goog.isDefAndNotNull(extent)) { + extent = new ol.Bounds( + extent.getMinX(), extent.getMinY(), + extent.getMaxX(), extent.getMaxY()); + extent.setProjection(projection); return extent; } else { throw('maxExtent must be defined either in the map or the projection'); @@ -332,7 +338,7 @@ ol.Map.prototype.setControls = function(opt_controls) { }; /** - * @param {ol.UnreferencedBounds} extent the maxExtent for the map + * @param {ol.Bounds} extent the maxExtent for the map */ ol.Map.prototype.setMaxExtent = function(extent) { this.maxExtent_ = extent; diff --git a/src/ol/base.js b/src/ol/base.js new file mode 100644 index 0000000000..1c2a6bd1ca --- /dev/null +++ b/src/ol/base.js @@ -0,0 +1,39 @@ +goog.provide('ol.base'); +goog.provide('ol.error'); + +/** + * @param {string} message Message. + */ +ol.error = function(message) { + if (ol.error.VERBOSE_ERRORS) { + throw new Error(message); + } else { + throw null; + } +}; + +/** + * @define {boolean} + */ +ol.error.VERBOSE_ERRORS = true; + +/** + * @define {boolean} + */ +ol.CHECK_KEYS = true; + +/** + * @param {Object} obj Object. + * @param {!Array.} allowedKeys Allowed keys. + */ +ol.base.checkKeys = function(obj, allowedKeys) { + if (ol.CHECK_KEYS) { + var keys = goog.object.getKeys(obj); + goog.array.forEach(allowedKeys, function(allowedKey) { + goog.array.remove(keys, allowedKey); + }); + if (!goog.array.isEmpty(keys)) { + ol.error('object contains invalid keys: ' + keys.join(', ')); + } + } +}; diff --git a/src/ol/error.js b/src/ol/error.js deleted file mode 100644 index 8f900915b9..0000000000 --- a/src/ol/error.js +++ /dev/null @@ -1,17 +0,0 @@ -goog.provide('ol.error'); - -/** - * @param {string} message Message. - */ -ol.error = function(message) { - if (ol.error.VERBOSE_ERRORS) { - throw new Error(message); - } else { - throw null; - } -}; - -/** - * @define {boolean} - */ -ol.error.VERBOSE_ERRORS = true; diff --git a/src/ol/geom/Collection.js b/src/ol/geom/Collection.js index becab21427..91ee3f0415 100644 --- a/src/ol/geom/Collection.js +++ b/src/ol/geom/Collection.js @@ -3,6 +3,7 @@ goog.provide('ol.geom.Collection'); goog.require('goog.array'); goog.require('ol.geom.Geometry'); goog.require('ol.Projection'); +goog.require('ol.base'); /** * Creates ol.geom.Collection objects. @@ -96,8 +97,9 @@ ol.geom.Collection.prototype.setComponents = function(components) { if (allValidTypes) { this.components_ = components; } else { - throw new Error('ol.geom.Collection: at least one component passed to ' - + 'setComponents is not allowed.'); + var msg = 'ol.geom.Collection: at least one component passed to ' + + 'setComponents is not allowed.'; + ol.error(msg); } }; @@ -111,8 +113,8 @@ ol.geom.Collection.prototype.addComponent = function(component, index) { if (this.isAllowedComponent(component)) { goog.array.insertAt(this.components_, component, index); } else { - throw new Error("ol.geom.Collection: The component is not allowed " - + "to be added."); + var msg = 'ol.geom.Collection: component is not allowed to be added.'; + ol.error(msg); } }; diff --git a/src/ol/geom/Point.js b/src/ol/geom/Point.js index b47a9cd5d8..bcc4d07b78 100644 --- a/src/ol/geom/Point.js +++ b/src/ol/geom/Point.js @@ -4,6 +4,7 @@ goog.require('ol.geom.Geometry'); goog.require('ol.Projection'); goog.require('ol.coord.AccessorInterface'); +goog.require('ol.base'); /** * Creates ol.geom.Point objects. @@ -135,7 +136,8 @@ ol.geom.Point.prototype._transform = function(proj) { var point = {'x': this.x_, 'y': this.y_}; var sourceProj = this.projection_; if (!goog.isDefAndNotNull(sourceProj)) { - throw new Error("Cannot transform a point without a source projection."); + var msg = 'Cannot transform a point without a source projection.'; + ol.error(msg); } ol.Projection.transform(point, sourceProj, proj); diff --git a/src/ol/layer/TileLayer.js b/src/ol/layer/TileLayer.js index 320c586d1a..12cd2242ff 100644 --- a/src/ol/layer/TileLayer.js +++ b/src/ol/layer/TileLayer.js @@ -153,15 +153,44 @@ ol.layer.TileLayer.prototype.getTileOrigin = function() { return null; }; +/** + * Get max resolution. Return undefined if the layer has no maxResolution, + * and no extent from which maxResolution could be derived. + * @return {number|undefined} + */ +ol.layer.TileLayer.prototype.getMaxResolution = function() { + if (!goog.isDef(this.maxResolution_)) { + var extent = this.getExtent(); + if (!goog.isNull(extent)) { + this.maxResolution_ = Math.max( + (extent.getMaxX() - extent.getMinX()) / this.tileWidth_, + (extent.getMaxY() - extent.getMaxX()) / this.tileHeight_); + } + } + return this.maxResolution_; +}; + +/** + * Get the number of the zoom levels. + * @return {number|undefined} + */ +ol.layer.TileLayer.prototype.getNumZoomLevels = function() { + return this.numZoomLevels_; +}; + /** * Get layer resolutions. Return null if the layer has no resolutions. * @return {Array.} */ ol.layer.TileLayer.prototype.getResolutions = function() { - if (goog.isNull(this.resolutions_) && goog.isDef(this.maxResolution_)) { - this.resolutions_ = []; - for (var i = 0; i < this.numZoomLevels_; i++) { - this.resolutions_[i] = this.maxResolution_ / Math.pow(2, i); + if (goog.isNull(this.resolutions_)) { + var maxResolution = this.getMaxResolution(), + numZoomLevels = this.getNumZoomLevels(); + if (goog.isDef(maxResolution) && goog.isDef(numZoomLevels)) { + this.resolutions_ = []; + for (var i = 0; i < numZoomLevels; i++) { + this.resolutions_[i] = maxResolution / Math.pow(2, i); + } } } return this.resolutions_; diff --git a/src/ol/layer/XYZ.js b/src/ol/layer/XYZ.js index c32ea74a50..6d6ea12485 100644 --- a/src/ol/layer/XYZ.js +++ b/src/ol/layer/XYZ.js @@ -18,7 +18,8 @@ ol.layer.XYZ = function(url) { goog.base(this); this.setUrl(url); - this.setMaxResolution(156543.03390625); + this.setProjection(new ol.Projection("EPSG:3857")); + this.setNumZoomLevels(22); }; goog.inherits(ol.layer.XYZ, ol.layer.TileLayer); diff --git a/test/index.html b/test/api.html similarity index 74% rename from test/index.html rename to test/api.html index c29ee04f34..7a70332a77 100644 --- a/test/index.html +++ b/test/api.html @@ -58,21 +58,6 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/api/bounds.test.js b/test/spec/api/bounds.test.js index 2a48944b96..2b56372aa2 100644 --- a/test/spec/api/bounds.test.js +++ b/test/spec/api/bounds.test.js @@ -27,5 +27,16 @@ describe("ol.bounds", function() { }); + it("raises an error on invalid keys", function() { + expect(function() { + ol.bounds({ + minX: 9, + minY: 10, + maxX: 11, + maxWhy: 12 + }); + }).toThrow(); + }); + }); diff --git a/test/spec/api/map.test.js b/test/spec/api/map.test.js index 6af17294d3..e6a2eddfa1 100644 --- a/test/spec/api/map.test.js +++ b/test/spec/api/map.test.js @@ -193,11 +193,11 @@ describe("ol.map", function() { var map = ol.map(); var extent = map.maxExtent(); - expect(extent).toBeA(ol.UnreferencedBounds); - expect(extent.getMinX()).toBe(-20037508.34); - expect(extent.getMaxX()).toBe(20037508.34); - expect(extent.getMinY()).toBe(-20037508.34); - expect(extent.getMaxY()).toBe(20037508.34); + expect(extent).toBeA(ol.Bounds); + expect(extent.minX()).toBe(-20037508.34); + expect(extent.maxX()).toBe(20037508.34); + expect(extent.minY()).toBe(-20037508.34); + expect(extent.maxY()).toBe(20037508.34); }); @@ -206,11 +206,11 @@ describe("ol.map", function() { map.maxExtent([-5,-4,7,9]); var extent = map.maxExtent(); - expect(extent).toBeA(ol.UnreferencedBounds); - expect(extent.getMinX()).toBe(-5); - expect(extent.getMaxX()).toBe(7); - expect(extent.getMinY()).toBe(-4); - expect(extent.getMaxY()).toBe(9); + expect(extent).toBeA(ol.Bounds); + expect(extent.minX()).toBe(-5); + expect(extent.maxX()).toBe(7); + expect(extent.minY()).toBe(-4); + expect(extent.maxY()).toBe(9); }); @@ -219,11 +219,11 @@ describe("ol.map", function() { map.projection("CRS:84"); var extent = map.maxExtent(); - expect(extent).toBeA(ol.UnreferencedBounds); - expect(extent.getMinX()).toBe(-180); - expect(extent.getMaxX()).toBe(180); - expect(extent.getMinY()).toBe(-90); - expect(extent.getMaxY()).toBe(90); + expect(extent).toBeA(ol.Bounds); + expect(extent.minX()).toBe(-180); + expect(extent.maxX()).toBe(180); + expect(extent.minY()).toBe(-90); + expect(extent.maxY()).toBe(90); }); @@ -233,60 +233,7 @@ describe("ol.map", function() { extent = map.maxExtent(); }).toThrow(); }); - - it("getMaxRes returns correct defaults", function() { - var map = ol.map(); - - var res = map.maxRes(); - expect(res.toFixed(5)).toBe("156543.03391"); - - }); - - it("allows setting of maxRes", function() { - var map = ol.map({ - maxRes: 67 - }); - - var res = map.maxRes(); - expect(res).toBe(67); - - }); - - it("getMaxRes returns correct for custom maxExtent", function() { - var map = ol.map({ - projection: ol.projection({ - code: 'foo', - maxExtent: [0,0,90,90] - }) - }); - - var res = map.maxRes(); - expect(res.toFixed(7)).toBe("0.3515625"); - - }); - - it("returns correct getResForZoom for default map", function() { - var map = ol.map(); - - var res = map.getResForZoom(0); - expect(res.toFixed(5)).toBe("156543.03391"); - res = map.getResForZoom(5); - expect(res.toFixed(5)).toBe("4891.96981"); - - }); - - it("returns correct getResForZoom when resolution array is set",function() { - var map = ol.map({ - resolutions: [1,4,7,9,12] - }); - - var res = map.getResForZoom(0); - expect(res).toBe(1); - res = map.getResForZoom(3); - expect(res).toBe(9); - - }); - + it("has no layers by default", function() { var map = ol.map(); diff --git a/test/spec/ol/layer/TileLayer.test.js b/test/spec/ol/layer/TileLayer.test.js index cd7706e06e..8cce9a34e9 100644 --- a/test/spec/ol/layer/TileLayer.test.js +++ b/test/spec/ol/layer/TileLayer.test.js @@ -134,6 +134,38 @@ describe('ol.layer.TileLayer', function() { }); }); + describe('get max resolution', function() { + var layer; + + beforeEach(function() { + layer = new ol.layer.TileLayer(); + }); + + describe('with max resolution', function() { + + beforeEach(function() { + layer.setMaxResolution(156543.03390625); + }); + + it('returns the expected maxResolution', function() { + var maxResolution = layer.getMaxResolution(); + expect(maxResolution).toEqual(156543.03390625); + }); + }); + + describe('with projection', function() { + + beforeEach(function() { + layer.setProjection(new ol.Projection("EPSG:3857")); + }); + + it('returns the expected maxResolution', function() { + var maxResolution = layer.getMaxResolution(); + expect(maxResolution).toEqual(156543.03390625); + }); + }); + }); + describe('get data from the layer', function() { var layer;