diff --git a/examples/spherical-mercator.html b/examples/spherical-mercator.html
index 221c238297..ebddb1b892 100644
--- a/examples/spherical-mercator.html
+++ b/examples/spherical-mercator.html
@@ -78,34 +78,21 @@
{'type': YAHOO_MAP_HYB, 'sphericalMercator': true}
);
+ // create OSM layer
+ var mapnik = new OpenLayers.Layer.OSM();
// create OAM layer
- var oam = new OpenLayers.Layer.TMS(
+ var oam = new OpenLayers.Layer.XYZ(
"OpenAerialMap",
- "http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap-900913/",
+ "http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap-900913/${z}/${x}/${y}.png",
{
- type: 'png', getURL: osm_getTileURL
+ sphericalMercator: true
}
);
// create OSM layer
- var mapnik = new OpenLayers.Layer.TMS(
- "OpenStreetMap (Mapnik)",
- "http://a.tile.openstreetmap.org/",
- {
- type: 'png', getURL: osm_getTileURL,
- displayOutsideMaxExtent: true,
- attribution: 'OpenStreetMap'
- }
- );
- // create OSM layer
- var osmarender = new OpenLayers.Layer.TMS(
+ var osmarender = new OpenLayers.Layer.OSM(
"OpenStreetMap (Tiles@Home)",
- "http://tah.openstreetmap.org/Tiles/tile/",
- {
- type: 'png', getURL: osm_getTileURL,
- displayOutsideMaxExtent: true,
- attribution: 'OpenStreetMap'
- }
+ "http://tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png"
);
@@ -133,21 +120,6 @@
if (!map.getCenter()) {map.zoomToMaxExtent()}
}
- function osm_getTileURL(bounds) {
- var res = this.map.getResolution();
- var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
- var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
- var z = this.map.getZoom();
- var limit = Math.pow(2, z);
-
- if (y < 0 || y >= limit) {
- return OpenLayers.Util.getImagesLocation() + "404.png";
- } else {
- x = ((x % limit) + limit) % limit;
- return this.url + z + "/" + x + "/" + y + "." + this.type;
- }
- }
-
diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js
index 0c28416ea7..aee35408c2 100644
--- a/lib/OpenLayers.js
+++ b/lib/OpenLayers.js
@@ -115,7 +115,9 @@
"OpenLayers/Layer/WMS/Untiled.js",
"OpenLayers/Layer/GeoRSS.js",
"OpenLayers/Layer/Boxes.js",
+ "OpenLayers/Layer/XYZ.js",
"OpenLayers/Layer/TMS.js",
+ "OpenLayers/Layer/ArcGISCache.js",
"OpenLayers/Layer/TileCache.js",
"OpenLayers/Popup/Anchored.js",
"OpenLayers/Popup/AnchoredBubble.js",
diff --git a/lib/OpenLayers/Layer/XYZ.js b/lib/OpenLayers/Layer/XYZ.js
new file mode 100644
index 0000000000..252607b14f
--- /dev/null
+++ b/lib/OpenLayers/Layer/XYZ.js
@@ -0,0 +1,356 @@
+/* Copyright (c) 2006-2009 MetaCarta, Inc., published under the Clear BSD
+ * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.XYZ
+ * The XYZ class is designed to make it easier for people who have tiles
+ * arranged by a standard XYZ grid.
+ */
+OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * Default is true, as this is designed to be a base tile source.
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: sphericalMecator
+ * Whether the tile extents should be set to the defaults for
+ * spherical mercator. Useful for things like OpenStreetMap.
+ * Default is false, except for the OSM subclass.
+ */
+ sphericalMercator: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.XYZ
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, url, options) {
+ if (options && options.sphericalMercator || this.sphericalMercator) {
+ options = OpenLayers.Util.extend({
+ maxExtent: new OpenLayers.Bounds(
+ -128 * 156543.0339,
+ -128 * 156543.0339,
+ 128 * 156543.0339,
+ 128 * 156543.0339
+ ),
+ maxResolution: 156543.0339,
+ numZoomLevels: 19,
+ units: "m",
+ projection: "EPSG:900913"
+ }, options);
+ }
+ url = url || this.url;
+ name = name || this.name;
+ var newArguments = [name, url, {}, options];
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+ },
+
+ /**
+ * APIMethod: clone
+ * Create a clone of this layer
+ *
+ * Parameters:
+ * obj - {Object} Is this ever used?
+ *
+ * Returns:
+ * {} An exact clone of this OpenLayers.Layer.Grid
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.XYZ(this.name,
+ this.url,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+ if (this.tileSize != null) {
+ obj.tileSize = this.tileSize.clone();
+ }
+
+ // we do not want to copy reference to grid, so we make a new array
+ obj.grid = [];
+
+ return obj;
+ },
+
+ /**
+ * Method: getUrl
+ *
+ * Parameters:
+ * bounds - {}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as
+ * parameters
+ */
+ getURL: function (bounds) {
+ var res = this.map.getResolution();
+ var x = Math.round((bounds.left - this.maxExtent.left)
+ / (res * this.tileSize.w));
+ var y = Math.round((this.maxExtent.top - bounds.top)
+ / (res * this.tileSize.h));
+ var z = this.map.getZoom();
+ var limit = Math.pow(2, z);
+
+ var url = this.url;
+ var s = '' + x + y + z;
+ if (url instanceof Array)
+ {
+ url = this.selectUrl(s, url);
+ }
+
+ var path = OpenLayers.String.format(url, {'x': x, 'y': y, 'z': z});
+
+ return path;
+ },
+
+ /**
+ * Method: addTile
+ * addTile creates a tile, initializes it, and adds it to the layer div.
+ *
+ * Parameters:
+ * bounds - {}
+ * position - {}
+ *
+ * Returns:
+ * {} The added OpenLayers.Tile.Image
+ */
+ addTile:function(bounds,position) {
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ null, this.tileSize);
+ },
+
+ /* APIMethod: setMap
+ * When the layer is added to a map, then we can fetch our origin
+ * (if we don't have one.)
+ *
+ * Parameters:
+ * map - {}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
+ if (!this.tileOrigin) {
+ this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,
+ this.maxExtent.bottom);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.XYZ"
+});
+
+
+/**
+ * Class: OpenLayers.Layer.OSM
+ * A class to access OpenStreetMap tiles. By default, uses the OpenStreetMap
+ * hosted tile.openstreetmap.org 'Mapnik' tileset. If you wish to use
+ * tiles@home / osmarender layer instead, you can pass a layer like:
+ *
+ * (code)
+ * new OpenLayers.Layer.OSM("t@h",
+ * "http://tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png");
+ * (end)
+ *
+ * This layer defaults to Spherical Mercator.
+ */
+
+OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+ name: "OpenStreetMap",
+ attribution: "Data CC-By-SA by OpenStreetMap",
+ sphericalMercator: true,
+ url: 'http://tile.openstreetmap.org/${z}/${x}/${y}.png',
+ CLASS_NAME: "OpenLayers.Layer.OSM"
+});
+/* Copyright (c) 2006-2009 MetaCarta, Inc., published under the Clear BSD
+ * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.XYZ
+ * The XYZ class is designed to make it easier for people who have tiles
+ * arranged by a standard XYZ grid.
+ */
+OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+ /**
+ * APIProperty: isBaseLayer
+ * Default is true, as this is designed to be a base tile source.
+ */
+ isBaseLayer: true,
+
+ /**
+ * APIProperty: sphericalMecator
+ * Whether the tile extents should be set to the defaults for
+ * spherical mercator. Useful for things like OpenStreetMap.
+ * Default is false, except for the OSM subclass.
+ */
+ sphericalMercator: false,
+
+ /**
+ * Constructor: OpenLayers.Layer.OSM
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, url, options) {
+ if (options && options.sphericalMercator || this.sphericalMercator) {
+ options = OpenLayers.Util.extend({
+ maxExtent: new OpenLayers.Bounds(
+ -128 * 156543.0339,
+ -128 * 156543.0339,
+ 128 * 156543.0339,
+ 128 * 156543.0339
+ ),
+ maxResolution: 156543.0339,
+ numZoomLevels: 19,
+ units: "m",
+ projection: "EPSG:900913"
+ }, options);
+ }
+ url = url || this.url;
+ name = name || this.name;
+ var newArguments = [name, url, {}, options];
+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+ },
+
+ /**
+ * APIMethod: clone
+ * Create a clone of this layer
+ *
+ * Parameters:
+ * obj - {Object} Is this ever used?
+ *
+ * Returns:
+ * {} An exact clone of this OpenLayers.Layer.Grid
+ */
+ clone: function (obj) {
+
+ if (obj == null) {
+ obj = new OpenLayers.Layer.XYZ(this.name,
+ this.url,
+ this.options);
+ }
+
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
+
+ // copy/set any non-init, non-simple values here
+ if (this.tileSize != null) {
+ obj.tileSize = this.tileSize.clone();
+ }
+
+ // we do not want to copy reference to grid, so we make a new array
+ obj.grid = [];
+
+ return obj;
+ },
+
+ /**
+ * Method: getUrl
+ *
+ * Parameters:
+ * bounds - {}
+ *
+ * Returns:
+ * {String} A string with the layer's url and parameters and also the
+ * passed-in bounds and appropriate tile size specified as
+ * parameters
+ */
+ getURL: function (bounds) {
+ var res = this.map.getResolution();
+ var x = Math.round((bounds.left - this.maxExtent.left)
+ / (res * this.tileSize.w));
+ var y = Math.round((this.maxExtent.top - bounds.top)
+ / (res * this.tileSize.h));
+ var z = this.map.getZoom();
+ var limit = Math.pow(2, z);
+
+ var url = this.url;
+ var s = '' + x + y + z;
+ if (url instanceof Array)
+ {
+ url = this.selectUrl(s, url);
+ }
+
+ var path = OpenLayers.String.format(url, {'x': x, 'y': y, 'z': z});
+
+ return path;
+ },
+
+ /**
+ * Method: addTile
+ * addTile creates a tile, initializes it, and adds it to the layer div.
+ *
+ * Parameters:
+ * bounds - {}
+ * position - {}
+ *
+ * Returns:
+ * {} The added OpenLayers.Tile.Image
+ */
+ addTile:function(bounds,position) {
+ return new OpenLayers.Tile.Image(this, position, bounds,
+ null, this.tileSize);
+ },
+
+ /* APIMethod: setMap
+ * When the layer is added to a map, then we can fetch our origin
+ * (if we don't have one.)
+ *
+ * Parameters:
+ * map - {}
+ */
+ setMap: function(map) {
+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
+ if (!this.tileOrigin) {
+ this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,
+ this.maxExtent.bottom);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.XYZ"
+});
+
+
+/**
+ * Class: OpenLayers.Layer.OSM
+ * A class to access OpenStreetMap tiles. By default, uses the OpenStreetMap
+ * hosted tile.openstreetmap.org 'Mapnik' tileset. If you wish to use
+ * tiles@home / osmarender layer instead, you can pass a layer like:
+ *
+ * (code)
+ * new OpenLayers.Layer.OSM("t@h",
+ * "http://tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png");
+ * (end)
+ *
+ * This layer defaults to Spherical Mercator.
+ */
+
+OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+ name: "OpenStreetMap",
+ attribution: "Data CC-By-SA by OpenStreetMap",
+ sphericalMercator: true,
+ url: 'http://tile.openstreetmap.org/${z}/${x}/${y}.png',
+ CLASS_NAME: "OpenLayers.Layer.OSM"
+});
diff --git a/tests/Layer/XYZ.html b/tests/Layer/XYZ.html
new file mode 100644
index 0000000000..a6cf1ef5b4
--- /dev/null
+++ b/tests/Layer/XYZ.html
@@ -0,0 +1,366 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/list-tests.html b/tests/list-tests.html
index 88b3ed79b0..70279837f1 100644
--- a/tests/list-tests.html
+++ b/tests/list-tests.html
@@ -126,6 +126,7 @@
Layer/WFS.html
Layer/WMS.html
Layer/WrapDateLine.html
+ Layer/XYZ.html
Layer/Yahoo.html
Map.html
Marker.html