diff --git a/examples/xyz.html b/examples/xyz.html
new file mode 100644
index 0000000000..b1f135e303
--- /dev/null
+++ b/examples/xyz.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+ XYZ example
+
+
+
+
+
+
+
+
+
+
+
+
+
XYZ example
+
Example of a XYZ source.
+
+
xyz
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/xyz.js b/examples/xyz.js
new file mode 100644
index 0000000000..11b0f484f2
--- /dev/null
+++ b/examples/xyz.js
@@ -0,0 +1,40 @@
+goog.require('ol.Attribution');
+goog.require('ol.Map');
+goog.require('ol.View2D');
+goog.require('ol.layer.Tile');
+goog.require('ol.source.OSM');
+goog.require('ol.source.XYZ');
+
+
+var attribution = new ol.Attribution({
+ html: 'Tiles © ' +
+ 'National Library of Scotland'
+});
+
+var map = new ol.Map({
+ target: 'map',
+ layers: [
+ new ol.layer.Tile({
+ source: new ol.source.OSM({
+ attributions: [
+ new ol.Attribution({
+ html: 'Tiles © ' +
+ 'OpenCycleMap'
+ }),
+ ol.source.OSM.DATA_ATTRIBUTION
+ ],
+ url: 'http://{a-c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png'
+ })
+ }),
+ new ol.layer.Tile({
+ source: new ol.source.XYZ({
+ attributions: [attribution],
+ url: 'http://geo.nls.uk/maps/towns/glasgow1857/{z}/{x}/{-y}.png'
+ })
+ })
+ ],
+ view: new ol.View2D({
+ center: [-472202, 7530279],
+ zoom: 12
+ })
+});
diff --git a/externs/olx.js b/externs/olx.js
index 87b9d20619..af2312a837 100644
--- a/externs/olx.js
+++ b/externs/olx.js
@@ -2774,7 +2774,7 @@ olx.source.TileVectorOptions.prototype.tileUrlFunction;
/**
- * URL template. Must include `{x}`, `{y}`, and `{z}` placeholders.
+ * URL template. Must include `{x}`, `{y}` or `{-y}`, and `{z}` placeholders.
* @type {string|undefined}
*/
olx.source.TileVectorOptions.prototype.url;
@@ -3173,8 +3173,8 @@ olx.source.OSMOptions.prototype.tileLoadFunction;
/**
- * URL template. Must include `{x}`, `{y}`, and `{z}` placeholders. Default is
- * `//{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png`.
+ * URL template. Must include `{x}`, `{y}` or `{-y}`, and `{z}` placeholders.
+ * Default is `//{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png`.
* @type {string|undefined}
*/
olx.source.OSMOptions.prototype.url;
@@ -3573,7 +3573,7 @@ olx.source.StamenOptions.prototype.tileLoadFunction;
/**
- * URL template. Must include `{x}`, `{y}`, and `{z}` placeholders.
+ * URL template. Must include `{x}`, `{y}` or `{-y}`, and `{z}` placeholders.
* @type {string|undefined}
*/
olx.source.StamenOptions.prototype.url;
@@ -4252,7 +4252,7 @@ olx.source.XYZOptions.prototype.tileUrlFunction;
/**
- * URL template. Must include `{x}`, `{y}`, and `{z}` placeholders.
+ * URL template. Must include `{x}`, `{y}` or `{-y}`, and `{z}` placeholders.
* @type {string|undefined}
*/
olx.source.XYZOptions.prototype.url;
diff --git a/src/ol/tileurlfunction.js b/src/ol/tileurlfunction.js
index f99bfbcee2..9cd028d1ef 100644
--- a/src/ol/tileurlfunction.js
+++ b/src/ol/tileurlfunction.js
@@ -44,7 +44,11 @@ ol.TileUrlFunction.createFromTemplate = function(template) {
} else {
return template.replace('{z}', tileCoord.z.toString())
.replace('{x}', tileCoord.x.toString())
- .replace('{y}', tileCoord.y.toString());
+ .replace('{y}', tileCoord.y.toString())
+ .replace('{-y}', function() {
+ var y = (1 << tileCoord.z) - tileCoord.y - 1;
+ return y.toString();
+ });
}
});
};
diff --git a/test/spec/ol/tileurlfunction.test.js b/test/spec/ol/tileurlfunction.test.js
index 23b32e9628..fe97b66dd2 100644
--- a/test/spec/ol/tileurlfunction.test.js
+++ b/test/spec/ol/tileurlfunction.test.js
@@ -33,6 +33,10 @@ describe('ol.TileUrlFunction', function() {
expect(tileUrl(new ol.TileCoord(3, 2, 1))).to.eql('3/2/1');
expect(tileUrl(null)).to.be(undefined);
});
+ it('accepts {-y} placeholder', function() {
+ var tileUrl = ol.TileUrlFunction.createFromTemplate('{z}/{x}/{-y}');
+ expect(tileUrl(new ol.TileCoord(3, 2, 2))).to.eql('3/2/5');
+ });
});
describe('createFromTemplates', function() {