Skip to content

Commit

Permalink
Merge pull request kartena#40 from perliedman/tms-upper-bounds
Browse files Browse the repository at this point in the history
Allow upper bounds for TMS tile layers to not align with tile grid.
  • Loading branch information
pthorin committed Sep 3, 2013
2 parents 211e7aa + 70b9891 commit 351ebd1
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 13 deletions.
45 changes: 36 additions & 9 deletions src/proj4leaflet.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ L.Proj._isProj4Proj = function(a) {
return typeof a['projName'] !== 'undefined';
};

L.Proj.ScaleDependantTransformation = function(scaleTransforms) {
this.scaleTransforms = scaleTransforms;
}

L.Proj.ScaleDependantTransformation.prototype.transform = function(point, scale) {
return this.scaleTransforms[scale].transform(point, scale);
};

L.Proj.ScaleDependantTransformation.prototype.untransform = function(point, scale) {
return this.scaleTransforms[scale].untransform(point, scale);
};

L.Proj.Projection = L.Class.extend({
initialize: function(a, def) {
if (L.Proj._isProj4Proj(a)) {
Expand Down Expand Up @@ -102,32 +114,47 @@ L.Proj.TileLayer.TMS = L.TileLayer.extend({
},

initialize: function(urlTemplate, crs, options) {
var boundsMatchesGrid = true,
scaleTransforms,
upperY,
crsBounds;

if (!(crs instanceof L.Proj.CRS.TMS)) {
throw new Error("CRS is not L.Proj.CRS.TMS.");
}

L.TileLayer.prototype.initialize.call(this, urlTemplate, options);
this.crs = crs;
crsBounds = this.crs.projectedBounds;

// Verify grid alignment
for (var i = this.options.minZoom; i < this.options.maxZoom; i++) {
var gridHeight = (this.crs.projectedBounds[3] - this.crs.projectedBounds[1]) /
for (var i = this.options.minZoom; i < this.options.maxZoom && boundsMatchesGrid; i++) {
var gridHeight = (crsBounds[3] - crsBounds[1]) /
this._projectedTileSize(i);
if (Math.abs(gridHeight - Math.round(gridHeight)) > 1e-3) {
throw new Error("Projected bounds does not match grid at zoom " + i);
boundsMatchesGrid = Math.abs(gridHeight - Math.round(gridHeight)) > 1e-3;
}

if (!boundsMatchesGrid) {
scaleTransforms = {};
for (var i = this.options.minZoom; i < this.options.maxZoom; i++) {
upperY = crsBounds[1] + Math.ceil((crsBounds[3] - crsBounds[1]) /
this._projectedTileSize(i)) * this._projectedTileSize(i);
scaleTransforms[this.crs.scale(i)] = new L.Transformation(1, -crsBounds[0], -1, upperY);
}

this.crs = new L.Proj.CRS.TMS(this.crs.projection._proj, crsBounds, this.crs.options);
this.crs.transformation = new L.Proj.ScaleDependantTransformation(scaleTransforms);
}
},

getTileUrl: function(tilePoint) {
var gridHeight =
Math.round((this.crs.projectedBounds[3] - this.crs.projectedBounds[1]) /
this._projectedTileSize(this._map.getZoom()));

// TODO: relies on some of TileLayer's internals
var z = this._getZoomForUrl(),
gridHeight = Math.ceil((this.crs.projectedBounds[3] - this.crs.projectedBounds[1]) / this._projectedTileSize(z));

return L.Util.template(this._url, L.Util.extend({
s: this._getSubdomain(tilePoint),
z: this._getZoomForUrl(),
z: z,
x: tilePoint.x,
y: gridHeight - tilePoint.y - 1
}, this.options));
Expand Down
41 changes: 37 additions & 4 deletions test/specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ describe('L.Proj.CRS', function() {
pp = crs.latLngToPoint(ll, 0),
up = crs.pointToLatLng(pp, 0);

expect(pp.x).toBe(ll.lng - 10);
expect(pp.y).toBe(-ll.lat + 10);
expect(up.lat).toBe(ll.lng);
expect(up.lng).toBe(ll.lat);
expect(pp.x).toBeCloseTo(ll.lng - 10, 6);
expect(pp.y).toBeCloseTo(-ll.lat + 10, 6);
expect(up.lat).toBeCloseTo(ll.lng, 6);
expect(up.lng).toBeCloseTo(ll.lat, 6);
});

it('accepts custom transformation', function() {
Expand Down Expand Up @@ -140,6 +140,39 @@ describe('L.Proj.CRS.TMS', function() {
expect(t._c).toBe(-1);
expect(t._d).toBe(100)
});

it('can adjust bounds to align with tilegrid', function() {
var resolutions = [6386.233628906251, 3193.1168144531257, 1596.5584072265628, 798.2792036132814, 399.1396018066407, 199.56980090332036, 99.78490045166018, 49.89245022583009],
crs = new L.Proj.CRS.TMS('EPSG:900913',
'+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs',
[-851225.043, 6422198.546, 196550.197, 9691000.164],
{
resolutions: resolutions
}
),
tileLayer = new L.Proj.TileLayer.TMS('http://test/{z}/{x}/{y}.png', crs),
crs = tileLayer.crs,
t = crs.transformation,
upperLeft = new L.Point(-851225.043, 9691950.164),
lowerLeft = new L.Point(-851225.043, 6422198.546),
tp;

for (i = 0; i < resolutions.length; i++) {
// Mock a very stupid map
tileLayer._map = {getZoom: function() { return i; }};

tp = t.transform(upperLeft, crs.scale(i));
expect(tp.x).toBeCloseTo(0, 6);
expect(tp.y).toBeCloseTo(0, 6);

tp = t.transform(lowerLeft, crs.scale(i));
// Convert to a tile point
tp.x = Math.round(tp.x / 256);
// -1 since Leaflet uses tile's upper edge as reference
tp.y = Math.round(tp.y / 256) - 1;
expect(tileLayer.getTileUrl(tp)).toBe('http://test/' + i + '/0/0.png');
}
})
});

describe('legacy API', function() {
Expand Down

0 comments on commit 351ebd1

Please sign in to comment.