Merge pull request #1124 from tschaub/xyz

Allow tilegrid creation from projection or extent.
This commit is contained in:
Tim Schaub
2014-08-19 11:59:52 -06:00
9 changed files with 255 additions and 39 deletions

View File

@@ -5092,19 +5092,48 @@ olx.tilegrid.WMTSOptions.prototype.tileSizes;
/**
* @typedef {{maxZoom: number}}
* @typedef {{extent: (ol.Extent|undefined),
* maxZoom: (number|undefined),
* minZoom: (number|undefined),
* tileSize: (number|undefined)}}
* @api
*/
olx.tilegrid.XYZOptions;
/**
* Maximum zoom.
* @type {number}
* Extent for the tile grid. The origin for an XYZ tile grid is the top-left
* corner of the extent. The zero level of the grid is defined by the
* resolution at which one tile fits in the provided extent. If not provided,
* the extent of the EPSG:3857 projection is used.
* @type {ol.Extent|undefined}
*/
olx.tilegrid.XYZOptions.prototype.extent;
/**
* Maximum zoom. The default is `ol.DEFAULT_MAX_ZOOM`. This determines the
* number of levels in the grid set. For example, a `maxZoom` of 21 means there
* are 22 levels in the grid set.
* @type {number|undefined}
*/
olx.tilegrid.XYZOptions.prototype.maxZoom;
/**
* Minimum zoom. Default is 0.
* @type {number|undefined}
*/
olx.tilegrid.XYZOptions.prototype.minZoom;
/**
* Tile size in pixels. Default is 256. (Only square tiles are supported.)
* @type {number|undefined}
*/
olx.tilegrid.XYZOptions.prototype.tileSize;
/**
* @typedef {{resolutions: !Array.<number>}}
* @api

View File

@@ -1,5 +1,6 @@
goog.provide('ol.Extent');
goog.provide('ol.extent');
goog.provide('ol.extent.Corner');
goog.provide('ol.extent.Relationship');
goog.require('goog.asserts');
@@ -17,6 +18,18 @@ goog.require('ol.TransformFunction');
ol.Extent;
/**
* Extent corner.
* @enum {string}
*/
ol.extent.Corner = {
BOTTOM_LEFT: 'bottom-left',
BOTTOM_RIGHT: 'bottom-right',
TOP_LEFT: 'top-left',
TOP_RIGHT: 'top-right'
};
/**
* Relationship to an extent.
* @enum {number}
@@ -456,6 +469,30 @@ ol.extent.getCenter = function(extent) {
};
/**
* Get a corner coordinate of an extent.
* @param {ol.Extent} extent Extent.
* @param {ol.extent.Corner} corner Corner.
* @return {ol.Coordinate} Corner coordinate.
*/
ol.extent.getCorner = function(extent, corner) {
var coordinate;
if (corner === ol.extent.Corner.BOTTOM_LEFT) {
coordinate = ol.extent.getBottomLeft(extent);
} else if (corner === ol.extent.Corner.BOTTOM_RIGHT) {
coordinate = ol.extent.getBottomRight(extent);
} else if (corner === ol.extent.Corner.TOP_LEFT) {
coordinate = ol.extent.getTopLeft(extent);
} else if (corner === ol.extent.Corner.TOP_RIGHT) {
coordinate = ol.extent.getTopRight(extent);
} else {
goog.asserts.fail('Invalid corner: %s', corner);
}
goog.asserts.assert(goog.isDef(coordinate));
return coordinate;
};
/**
* @param {ol.Extent} extent1 Extent 1.
* @param {ol.Extent} extent2 Extent 2.

View File

@@ -89,7 +89,9 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse =
var resource = response.resourceSets[0].resources[0];
goog.asserts.assert(resource.imageWidth == resource.imageHeight);
var sourceProjection = this.getProjection();
var tileGrid = new ol.tilegrid.XYZ({
extent: ol.tilegrid.extentFromProjection(sourceProjection),
minZoom: resource.zoomMin,
maxZoom: resource.zoomMax,
tileSize: resource.imageWidth
@@ -97,7 +99,6 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse =
this.tileGrid = tileGrid;
var culture = this.culture_;
var sourceProjection = this.getProjection();
this.tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform(
tileGrid.createTileCoordTransform(),
ol.TileUrlFunction.createFromTileUrlFunctions(

View File

@@ -54,10 +54,11 @@ ol.source.TileJSON.prototype.handleTileJSONResponse = function(tileJSON) {
var epsg4326Projection = ol.proj.get('EPSG:4326');
var sourceProjection = this.getProjection();
var extent;
if (goog.isDef(tileJSON.bounds)) {
var transform = ol.proj.getTransformFromProjections(
epsg4326Projection, this.getProjection());
epsg4326Projection, sourceProjection);
extent = ol.extent.applyTransform(tileJSON.bounds, transform);
}
@@ -67,6 +68,7 @@ ol.source.TileJSON.prototype.handleTileJSONResponse = function(tileJSON) {
var minZoom = tileJSON.minzoom || 0;
var maxZoom = tileJSON.maxzoom || 22;
var tileGrid = new ol.tilegrid.XYZ({
extent: ol.tilegrid.extentFromProjection(sourceProjection),
maxZoom: maxZoom,
minZoom: minZoom
});

View File

@@ -17,14 +17,12 @@ goog.require('ol.tilegrid.XYZ');
* @api
*/
ol.source.XYZ = function(options) {
var projection = goog.isDef(options.projection) ?
options.projection : 'EPSG:3857';
var maxZoom = goog.isDef(options.maxZoom) ? options.maxZoom : 18;
var tileGrid = new ol.tilegrid.XYZ({
maxZoom: maxZoom
extent: ol.tilegrid.extentFromProjection(projection),
maxZoom: options.maxZoom
});
goog.base(this, {

View File

@@ -8,6 +8,8 @@ goog.require('ol.TileCoord');
goog.require('ol.TileRange');
goog.require('ol.array');
goog.require('ol.extent');
goog.require('ol.extent.Corner');
goog.require('ol.proj');
goog.require('ol.proj.METERS_PER_UNIT');
goog.require('ol.proj.Projection');
goog.require('ol.proj.Units');
@@ -404,31 +406,93 @@ ol.tilegrid.getForProjection = function(projection) {
/**
* @param {ol.proj.Projection} projection Projection.
* @param {number=} opt_maxZoom Maximum zoom level.
* @param {number=} opt_tileSize Tile size.
* @param {ol.Extent} extent Extent.
* @param {number=} opt_maxZoom Maximum zoom level (default is
* ol.DEFAULT_MAX_ZOOM).
* @param {number=} opt_tileSize Tile size (default uses ol.DEFAULT_TILE_SIZE).
* @param {ol.extent.Corner=} opt_corner Extent corner (default is
* ol.extent.Corner.BOTTOM_LEFT).
* @return {ol.tilegrid.TileGrid} TileGrid instance.
*/
ol.tilegrid.createForProjection =
function(projection, opt_maxZoom, opt_tileSize) {
var projectionExtent = projection.getExtent();
var size = goog.isNull(projectionExtent) ?
360 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] /
projection.getMetersPerUnit() :
Math.max(projectionExtent[2] - projectionExtent[0],
projectionExtent[3] - projectionExtent[1]);
var maxZoom = goog.isDef(opt_maxZoom) ?
opt_maxZoom : ol.DEFAULT_MAX_ZOOM;
var tileSize = goog.isDef(opt_tileSize) ? opt_tileSize : ol.DEFAULT_TILE_SIZE;
var resolutions = new Array(maxZoom + 1);
var maxResolution = size / tileSize;
for (var z = 0, zz = resolutions.length; z < zz; ++z) {
resolutions[z] = maxResolution / Math.pow(2, z);
}
ol.tilegrid.createForExtent =
function(extent, opt_maxZoom, opt_tileSize, opt_corner) {
var tileSize = goog.isDef(opt_tileSize) ?
opt_tileSize : ol.DEFAULT_TILE_SIZE;
var corner = goog.isDef(opt_corner) ?
opt_corner : ol.extent.Corner.BOTTOM_LEFT;
var resolutions = ol.tilegrid.resolutionsFromExtent(
extent, opt_maxZoom, tileSize);
return new ol.tilegrid.TileGrid({
origin: goog.isNull(projectionExtent) ? [0, 0] :
ol.extent.getBottomLeft(projectionExtent),
origin: ol.extent.getCorner(extent, corner),
resolutions: resolutions,
tileSize: tileSize
});
};
/**
* Create a resolutions array from an extent. A zoom factor of 2 is assumed.
* @param {ol.Extent} extent Extent.
* @param {number=} opt_maxZoom Maximum zoom level (default is
* ol.DEFAULT_MAX_ZOOM).
* @param {number=} opt_tileSize Tile size (default uses ol.DEFAULT_TILE_SIZE).
* @return {!Array.<number>} Resolutions array.
*/
ol.tilegrid.resolutionsFromExtent =
function(extent, opt_maxZoom, opt_tileSize) {
var maxZoom = goog.isDef(opt_maxZoom) ?
opt_maxZoom : ol.DEFAULT_MAX_ZOOM;
var height = ol.extent.getHeight(extent);
var width = ol.extent.getWidth(extent);
var tileSize = goog.isDef(opt_tileSize) ?
opt_tileSize : ol.DEFAULT_TILE_SIZE;
var maxResolution = Math.max(
width / tileSize, height / tileSize);
var length = maxZoom + 1;
var resolutions = new Array(length);
for (var z = 0; z < length; ++z) {
resolutions[z] = maxResolution / Math.pow(2, z);
}
return resolutions;
};
/**
* @param {ol.proj.ProjectionLike} projection Projection.
* @param {number=} opt_maxZoom Maximum zoom level (default is
* ol.DEFAULT_MAX_ZOOM).
* @param {number=} opt_tileSize Tile size (default uses ol.DEFAULT_TILE_SIZE).
* @param {ol.extent.Corner=} opt_corner Extent corner (default is
* ol.extent.Corner.BOTTOM_LEFT).
* @return {ol.tilegrid.TileGrid} TileGrid instance.
*/
ol.tilegrid.createForProjection =
function(projection, opt_maxZoom, opt_tileSize, opt_corner) {
var extent = ol.tilegrid.extentFromProjection(projection);
return ol.tilegrid.createForExtent(
extent, opt_maxZoom, opt_tileSize, opt_corner);
};
/**
* Generate a tile grid extent from a projection. If the projection has an
* extent, it is used. If not, a global extent is assumed.
* @param {ol.proj.ProjectionLike} projection Projection.
* @return {ol.Extent} Extent.
*/
ol.tilegrid.extentFromProjection = function(projection) {
projection = ol.proj.get(projection);
var extent = projection.getExtent();
if (goog.isNull(extent)) {
var half = 180 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] /
projection.getMetersPerUnit();
extent = ol.extent.createOrUpdate(-half, -half, half, half);
}
return extent;
};

View File

@@ -4,6 +4,8 @@ goog.require('goog.math');
goog.require('ol');
goog.require('ol.TileCoord');
goog.require('ol.TileRange');
goog.require('ol.extent');
goog.require('ol.extent.Corner');
goog.require('ol.proj');
goog.require('ol.proj.EPSG3857');
goog.require('ol.tilecoord');
@@ -22,19 +24,16 @@ goog.require('ol.tilegrid.TileGrid');
* @api
*/
ol.tilegrid.XYZ = function(options) {
var resolutions = new Array(options.maxZoom + 1);
var z;
var size = 2 * ol.proj.EPSG3857.HALF_SIZE / ol.DEFAULT_TILE_SIZE;
for (z = 0; z <= options.maxZoom; ++z) {
resolutions[z] = size / Math.pow(2, z);
}
var extent = goog.isDef(options.extent) ?
options.extent : ol.proj.EPSG3857.EXTENT;
var resolutions = ol.tilegrid.resolutionsFromExtent(
extent, options.maxZoom, options.tileSize);
goog.base(this, {
minZoom: options.minZoom,
origin: [-ol.proj.EPSG3857.HALF_SIZE, ol.proj.EPSG3857.HALF_SIZE],
origin: ol.extent.getCorner(extent, ol.extent.Corner.TOP_LEFT),
resolutions: resolutions,
tileSize: ol.DEFAULT_TILE_SIZE
tileSize: options.tileSize
});
};

View File

@@ -177,6 +177,31 @@ describe('ol.extent', function() {
});
});
describe('getCorner', function() {
var extent = [1, 2, 3, 4];
it('gets the bottom left', function() {
var corner = ol.extent.Corner.BOTTOM_LEFT;
expect(ol.extent.getCorner(extent, corner)).to.eql([1, 2]);
});
it('gets the bottom right', function() {
var corner = ol.extent.Corner.BOTTOM_RIGHT;
expect(ol.extent.getCorner(extent, corner)).to.eql([3, 2]);
});
it('gets the top left', function() {
var corner = ol.extent.Corner.TOP_LEFT;
expect(ol.extent.getCorner(extent, corner)).to.eql([1, 4]);
});
it('gets the top right', function() {
var corner = ol.extent.Corner.TOP_RIGHT;
expect(ol.extent.getCorner(extent, corner)).to.eql([3, 4]);
});
});
describe('getForViewAndSize', function() {
it('works for a unit square', function() {
@@ -538,5 +563,6 @@ describe('ol.extent', function() {
goog.require('goog.vec.Mat4');
goog.require('ol.extent');
goog.require('ol.extent.Corner');
goog.require('ol.extent.Relationship');
goog.require('ol.proj');

View File

@@ -161,6 +161,18 @@ describe('ol.tilegrid.TileGrid', function() {
});
});
describe('createForExtent', function() {
it('allows creation of tile grid from extent', function() {
var extent = ol.extent.createOrUpdate(-100, -100, 100, 100);
var grid = ol.tilegrid.createForExtent(extent);
expect(grid).to.be.a(ol.tilegrid.TileGrid);
var resolutions = grid.getResolutions();
expect(resolutions.length).to.be(ol.DEFAULT_MAX_ZOOM + 1);
expect(grid.getOrigin()).to.eql([-100, -100]);
});
});
describe('createForProjection', function() {
it('allows easier creation of a tile grid', function() {
@@ -200,6 +212,50 @@ describe('ol.tilegrid.TileGrid', function() {
ol.DEFAULT_TILE_SIZE / Math.pow(2, 5));
});
it('assumes origin is bottom-left', function() {
var projection = ol.proj.get('EPSG:3857');
var grid = ol.tilegrid.createForProjection(projection);
var origin = grid.getOrigin();
var half = ol.proj.EPSG3857.HALF_SIZE;
expect(origin).to.eql([-half, -half]);
});
it('accepts bottom-left as corner', function() {
var projection = ol.proj.get('EPSG:3857');
var grid = ol.tilegrid.createForProjection(
projection, undefined, undefined, ol.extent.Corner.BOTTOM_LEFT);
var origin = grid.getOrigin();
var half = ol.proj.EPSG3857.HALF_SIZE;
expect(origin).to.eql([-half, -half]);
});
it('accepts bottom-right as corner', function() {
var projection = ol.proj.get('EPSG:3857');
var grid = ol.tilegrid.createForProjection(
projection, undefined, undefined, ol.extent.Corner.BOTTOM_RIGHT);
var origin = grid.getOrigin();
var half = ol.proj.EPSG3857.HALF_SIZE;
expect(origin).to.eql([half, -half]);
});
it('accepts top-left as corner', function() {
var projection = ol.proj.get('EPSG:3857');
var grid = ol.tilegrid.createForProjection(
projection, undefined, undefined, ol.extent.Corner.TOP_LEFT);
var origin = grid.getOrigin();
var half = ol.proj.EPSG3857.HALF_SIZE;
expect(origin).to.eql([-half, half]);
});
it('accepts top-right as corner', function() {
var projection = ol.proj.get('EPSG:3857');
var grid = ol.tilegrid.createForProjection(
projection, undefined, undefined, ol.extent.Corner.TOP_RIGHT);
var origin = grid.getOrigin();
var half = ol.proj.EPSG3857.HALF_SIZE;
expect(origin).to.eql([half, half]);
});
});
describe('getForProjection', function() {
@@ -643,8 +699,12 @@ describe('ol.tilegrid.TileGrid', function() {
});
goog.require('ol.Coordinate');
goog.require('ol.extent');
goog.require('ol.extent.Corner');
goog.require('ol.proj');
goog.require('ol.proj.METERS_PER_UNIT');
goog.require('ol.proj');
goog.require('ol.proj.EPSG3857');
goog.require('ol.proj.Projection');
goog.require('ol.proj.Units');
goog.require('ol.tilegrid.TileGrid');