in modules/renderer/background_source.js [112:227]
source.url = function(coord) {
var result = _template;
if (result === '') return result; // source 'none'
// Guess a type based on the tokens present in the template
// (This is for 'custom' source, where we don't know)
if (!source.type) {
if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
source.type = 'wms';
source.projection = 'EPSG:3857'; // guess
} else if (/\{(x|y)\}/.test(_template)) {
source.type = 'tms';
} else if (/\{u\}/.test(_template)) {
source.type = 'bing';
}
}
if (source.type === 'wms') {
var tileToProjectedCoords = (function(x, y, z) {
//polyfill for IE11, PhantomJS
var sinh = Math.sinh || function(x) {
var y = Math.exp(x);
return (y - 1 / y) / 2;
};
var zoomSize = Math.pow(2, z);
var lon = x / zoomSize * Math.PI * 2 - Math.PI;
var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
switch (source.projection) {
case 'EPSG:4326':
return {
x: lon * 180 / Math.PI,
y: lat * 180 / Math.PI
};
default: // EPSG:3857 and synonyms
var mercCoords = d3_geoMercatorRaw(lon, lat);
return {
x: 20037508.34 / Math.PI * mercCoords[0],
y: 20037508.34 / Math.PI * mercCoords[1]
};
}
});
var tileSize = source.tileSize;
var projection = source.projection;
var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]);
result = result.replace(/\{(\w+)\}/g, function (token, key) {
switch (key) {
case 'width':
case 'height':
return tileSize;
case 'proj':
return projection;
case 'wkid':
return projection.replace(/^EPSG:/, '');
case 'bbox':
// WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
if (projection === 'EPSG:4326' &&
// The CRS parameter implies version 1.3 (prior versions use SRS)
/VERSION=1.3|CRS={proj}/.test(source.template().toUpperCase())) {
return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
} else {
return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
}
case 'w':
return minXmaxY.x;
case 's':
return maxXminY.y;
case 'n':
return maxXminY.x;
case 'e':
return minXmaxY.y;
default:
return token;
}
});
} else if (source.type === 'tms') {
result = result
.replace('{x}', coord[0])
.replace('{y}', coord[1])
// TMS-flipped y coordinate
.replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
.replace(/\{z(oom)?\}/, coord[2])
// only fetch retina tiles for retina screens
.replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
} else if (source.type === 'bing') {
result = result
.replace('{u}', function() {
var u = '';
for (var zoom = coord[2]; zoom > 0; zoom--) {
var b = 0;
var mask = 1 << (zoom - 1);
if ((coord[0] & mask) !== 0) b++;
if ((coord[1] & mask) !== 0) b += 2;
u += b.toString();
}
return u;
});
}
// these apply to any type..
result = result.replace(/\{switch:([^}]+)\}/, function(s, r) {
var subdomains = r.split(',');
return subdomains[(coord[0] + coord[1]) % subdomains.length];
});
return result;
};