Fixes for the WMTS layer to properly update matrix related properties. Determining the best matrix in the set based on current map resolution. Properly parsing TopLeftCorner coordinates and providing a way to specify identifiers for backwards CRS. r=ahocevar (closes #2677)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@10570 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
@@ -4,7 +4,19 @@ var map, format;
|
||||
|
||||
function init() {
|
||||
|
||||
format = new OpenLayers.Format.WMTSCapabilities();
|
||||
format = new OpenLayers.Format.WMTSCapabilities({
|
||||
/**
|
||||
* This particular service is not in compliance with the WMTS spec and
|
||||
* is providing coordinates in y, x order regardless of the CRS. To
|
||||
* work around this, we can provide the format a table of CRS URN that
|
||||
* should be considered y, x order. These will extend the defaults on
|
||||
* the format.
|
||||
*/
|
||||
yx: {
|
||||
"urn:ogc:def:crs:EPSG::900913": true
|
||||
}
|
||||
});
|
||||
|
||||
OpenLayers.Request.GET({
|
||||
url: "http://v2.suite.opengeo.org/geoserver/gwc/service/wmts",
|
||||
params: {
|
||||
|
||||
@@ -51,6 +51,9 @@ OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.OWSCommo
|
||||
},
|
||||
"Identifier": function(node, obj) {
|
||||
obj.identifier = this.getChildValue(node);
|
||||
},
|
||||
"SupportedCRS": function(node, obj) {
|
||||
obj.supportedCRS = this.getChildValue(node);
|
||||
}
|
||||
}, OpenLayers.Format.OWSCommon.v1.prototype.readers["ows"])
|
||||
},
|
||||
|
||||
@@ -31,7 +31,20 @@ OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML, {
|
||||
* Property: parser
|
||||
* {<OpenLayers.Format>} A cached versioned format used for reading.
|
||||
*/
|
||||
parser: null,
|
||||
parser: null,
|
||||
|
||||
/**
|
||||
* APIProperty: yx
|
||||
* {Object} Members in the yx object are used to determine if a CRS URN
|
||||
* corresponds to a CRS with y,x axis order. Member names are CRS URNs
|
||||
* and values are boolean. By default, the following CRS URN are
|
||||
* assumed to correspond to a CRS with y,x axis order:
|
||||
*
|
||||
* * urn:ogc:def:crs:EPSG::4326
|
||||
*/
|
||||
yx: {
|
||||
"urn:ogc:def:crs:EPSG::4326": true
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructor: OpenLayers.Format.WMTSCapabilities
|
||||
@@ -70,9 +83,9 @@ OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML, {
|
||||
if (!constr) {
|
||||
throw new Error("Can't find a WMTS capabilities parser for version " + version);
|
||||
}
|
||||
var parser = new constr(this.options);
|
||||
this.parser = new constr(this.options);
|
||||
}
|
||||
return parser.read(data);
|
||||
return this.parser.read(data);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,15 @@ OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(
|
||||
xlink: "http://www.w3.org/1999/xlink"
|
||||
},
|
||||
|
||||
/**
|
||||
* Property: yx
|
||||
* {Object} Members in the yx object are used to determine if a CRS URN
|
||||
* corresponds to a CRS with y,x axis order. Member names are CRS URNs
|
||||
* and values are boolean. Defaults come from the
|
||||
* <OpenLayers.Format.WMTSCapabilities> prototype.
|
||||
*/
|
||||
yx: null,
|
||||
|
||||
/**
|
||||
* Property: defaultPrefix
|
||||
* {String} The default namespace alias for creating element nodes.
|
||||
@@ -50,6 +59,10 @@ OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(
|
||||
initialize: function(options) {
|
||||
OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
|
||||
this.options = options;
|
||||
var yx = OpenLayers.Util.extend(
|
||||
{}, OpenLayers.Format.WMTSCapabilities.prototype.yx
|
||||
);
|
||||
this.yx = OpenLayers.Util.extend(yx, this.yx);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -123,8 +136,9 @@ OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(
|
||||
// duck type wmts:Contents by looking for layers
|
||||
if (obj.layers) {
|
||||
// TileMatrixSet as object type in schema
|
||||
var tileMatrixSet = {};
|
||||
tileMatrixSet.matrixIds = [];
|
||||
var tileMatrixSet = {
|
||||
matrixIds: []
|
||||
};
|
||||
this.readChildNodes(node, tileMatrixSet);
|
||||
obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet;
|
||||
} else {
|
||||
@@ -133,7 +147,9 @@ OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(
|
||||
}
|
||||
},
|
||||
"TileMatrix": function(node, obj) {
|
||||
var tileMatrix = {};
|
||||
var tileMatrix = {
|
||||
supportedCRS: obj.supportedCRS
|
||||
};
|
||||
this.readChildNodes(node, tileMatrix);
|
||||
obj.matrixIds.push(tileMatrix);
|
||||
},
|
||||
@@ -143,7 +159,25 @@ OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(
|
||||
"TopLeftCorner": function(node, obj) {
|
||||
var topLeftCorner = this.getChildValue(node);
|
||||
var coords = topLeftCorner.split(" ");
|
||||
obj.topLeftCorner = new OpenLayers.LonLat(parseFloat(coords[1]), parseFloat(coords[0]));
|
||||
// decide on axis order for the given CRS
|
||||
var yx;
|
||||
if (obj.supportedCRS) {
|
||||
// extract out version from URN
|
||||
var crs = obj.supportedCRS.replace(
|
||||
/urn:ogc:def:crs:(\w+):.+:(\w+)$/,
|
||||
"urn:ogc:def:crs:$1::$2"
|
||||
);
|
||||
yx = !!this.yx[crs];
|
||||
}
|
||||
if (yx) {
|
||||
obj.topLeftCorner = new OpenLayers.LonLat(
|
||||
coords[1], coords[0]
|
||||
);
|
||||
} else {
|
||||
obj.topLeftCorner = new OpenLayers.LonLat(
|
||||
coords[0], coords[1]
|
||||
);
|
||||
}
|
||||
},
|
||||
"TileWidth": function(node, obj) {
|
||||
obj.tileWidth = parseInt(this.getChildValue(node));
|
||||
|
||||
@@ -129,7 +129,26 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
* become part of the request path, with order determined by the
|
||||
* <dimensions> list.
|
||||
*/
|
||||
params: null,
|
||||
params: null,
|
||||
|
||||
/**
|
||||
* APIProperty: zoomOffset
|
||||
* {Number} If your cache has more levels than you want to provide
|
||||
* access to with this layer, supply a zoomOffset. This zoom offset
|
||||
* is added to the current map zoom level to determine the level
|
||||
* for a requested tile. For example, if you supply a zoomOffset
|
||||
* of 3, when the map is at the zoom 0, tiles will be requested from
|
||||
* level 3 of your cache. Default is 0 (assumes cache level and map
|
||||
* zoom are equivalent). Additionally, if this layer is to be used
|
||||
* as an overlay and the cache has fewer zoom levels than the base
|
||||
* layer, you can supply a negative zoomOffset. For example, if a
|
||||
* map zoom level of 1 corresponds to your cache level zero, you would
|
||||
* supply a -1 zoomOffset (and set the maxResolution of the layer
|
||||
* appropriately). The zoomOffset value has no effect if complete
|
||||
* matrix definitions (including scaleDenominator) are supplied in
|
||||
* the <matrixIds> property. Defaults to 0 (no zoom offset).
|
||||
*/
|
||||
zoomOffset: 0,
|
||||
|
||||
/**
|
||||
* Property: formatSuffixMap
|
||||
@@ -147,6 +166,13 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
"jpg": "jpg"
|
||||
},
|
||||
|
||||
/**
|
||||
* Property: matrix
|
||||
* {Object} Matrix definition for the current map resolution. Updated by
|
||||
* the <updateMatrixProperties> method.
|
||||
*/
|
||||
matrix: null,
|
||||
|
||||
/**
|
||||
* Constructor: OpenLayers.Layer.WMTS
|
||||
* Create a new WMTS layer.
|
||||
@@ -175,10 +201,7 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
* Any other documented layer properties can be provided in the config object.
|
||||
*/
|
||||
initialize: function(config) {
|
||||
config.params = OpenLayers.Util.upperCaseObject(config.params);
|
||||
var args = [config.name, config.url, config.params, config];
|
||||
OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);
|
||||
|
||||
|
||||
// confirm required properties are supplied
|
||||
var required = {
|
||||
url: true,
|
||||
@@ -187,11 +210,16 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
matrixSet: true
|
||||
};
|
||||
for (var prop in required) {
|
||||
if (!(prop in this)) {
|
||||
if (!(prop in config)) {
|
||||
throw new Error("Missing property '" + prop + "' in layer configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
config.params = OpenLayers.Util.upperCaseObject(config.params);
|
||||
var args = [config.name, config.url, config.params, config];
|
||||
OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);
|
||||
|
||||
|
||||
// determine format suffix (for REST)
|
||||
if (!this.formatSuffix) {
|
||||
this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop();
|
||||
@@ -211,60 +239,54 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: setMap
|
||||
*/
|
||||
setMap: function() {
|
||||
OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
|
||||
this.updateMatrixProperties();
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: updateMatrixProperties
|
||||
* Set as a listener for zoom end to update tile matrix related properties.
|
||||
* Called when map resolution changes to update matrix related properties.
|
||||
*/
|
||||
updateMatrixProperties: function() {
|
||||
if (this.matrixIds && this.matrixIds.length) {
|
||||
var zoom = this.map.getZoom();
|
||||
var matrix = this.matrixIds[zoom];
|
||||
if (matrix) {
|
||||
if (matrix.topLeftCorner) {
|
||||
this.tileOrigin = matrix.topLeftCorner;
|
||||
}
|
||||
if (matrix.tileWidth && matrix.tileHeight) {
|
||||
this.tileSize = new OpenLayers.Size(
|
||||
matrix.tileWidth, matrix.tileHeight
|
||||
);
|
||||
}
|
||||
this.matrix = this.getMatrix();
|
||||
if (this.matrix) {
|
||||
if (this.matrix.topLeftCorner) {
|
||||
this.tileOrigin = this.matrix.topLeftCorner;
|
||||
}
|
||||
if (this.matrix.tileWidth && this.matrix.tileHeight) {
|
||||
this.tileSize = new OpenLayers.Size(
|
||||
this.matrix.tileWidth, this.matrix.tileHeight
|
||||
);
|
||||
}
|
||||
if (!this.tileOrigin) {
|
||||
this.tileOrigin = new OpenLayers.LonLat(
|
||||
this.maxExtent.left, this.maxExtent.top
|
||||
);
|
||||
}
|
||||
if (!this.tileFullExtent) {
|
||||
this.tileFullExtent = this.maxExtent;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: setMap
|
||||
* Overwrite default <setMap> from Layer
|
||||
*
|
||||
* Method: moveTo
|
||||
*
|
||||
* Parameters:
|
||||
* map - {<OpenLayers.Map>}
|
||||
* bound - {<OpenLayers.Bounds>}
|
||||
* zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
|
||||
* do some init work in that case.
|
||||
* dragging - {Boolean}
|
||||
*/
|
||||
setMap: function(map) {
|
||||
OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
|
||||
this.map.events.on({
|
||||
zoomend: this.updateMatrixProperties,
|
||||
scope: this
|
||||
});
|
||||
this.updateMatrixProperties();
|
||||
if (!this.tileOrigin) {
|
||||
this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.top);
|
||||
}
|
||||
if (!this.tileFullExtent) {
|
||||
this.tileFullExtent = this.maxExtent;
|
||||
moveTo:function(bounds, zoomChanged, dragging) {
|
||||
if (zoomChanged || !this.matrix) {
|
||||
this.updateMatrixProperties();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: removeMap
|
||||
*/
|
||||
removeMap: function() {
|
||||
if (this.map) {
|
||||
this.map.events.un({
|
||||
zoomend: this.updateMatrixProperties,
|
||||
scope: this
|
||||
});
|
||||
}
|
||||
OpenLayers.Layer.Grid.prototype.removeMap.apply(this, arguments);
|
||||
return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -287,19 +309,66 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: getMatrixId
|
||||
* Determine the appropriate matrix id for the given map resolution.
|
||||
* Method: getMatrix
|
||||
* Get the appropriate matrix definition for the current map resolution.
|
||||
*/
|
||||
getMatrixId: function() {
|
||||
var id;
|
||||
var zoom = this.map.getZoom();
|
||||
getMatrix: function() {
|
||||
var matrix;
|
||||
if (!this.matrixIds || this.matrixIds.length === 0) {
|
||||
id = zoom;
|
||||
matrix = {identifier: this.map.getZoom() + this.zoomOffset};
|
||||
} else {
|
||||
// TODO: get appropriate matrix id given the map resolution
|
||||
id = this.matrixIds[zoom].identifier;
|
||||
// get appropriate matrix given the map scale if possible
|
||||
if ("scaleDenominator" in this.matrixIds[0]) {
|
||||
// scale denominator calculation based on WMTS spec
|
||||
var denom =
|
||||
OpenLayers.METERS_PER_INCH *
|
||||
OpenLayers.INCHES_PER_UNIT[this.units] *
|
||||
this.map.getResolution() / 0.28E-3;
|
||||
var diff = Number.POSITIVE_INFINITY;
|
||||
var delta;
|
||||
for (var i=0, ii=this.matrixIds.length; i<ii; ++i) {
|
||||
delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom));
|
||||
if (delta < diff) {
|
||||
diff = delta;
|
||||
matrix = this.matrixIds[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// fall back on zoom as index
|
||||
matrix = this.matrixIds[this.map.getZoom() + this.zoomOffset];
|
||||
}
|
||||
}
|
||||
return id;
|
||||
return matrix;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: getTileInfo
|
||||
* Get tile information for a given location at the current map resolution.
|
||||
*
|
||||
* Parameters:
|
||||
* loc - {<OpenLayers.LonLat} A location in map coordinates.
|
||||
*
|
||||
* Returns:
|
||||
* {Object} An object with "col", "row", "i", and "j" properties. The col
|
||||
* and row values are zero based tile indexes from the top left. The
|
||||
* i and j values are the number of pixels to the left and top
|
||||
* (respectively) of the given location within the target tile.
|
||||
*/
|
||||
getTileInfo: function(loc) {
|
||||
var res = this.map.getResolution();
|
||||
|
||||
var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);
|
||||
var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);
|
||||
|
||||
var col = Math.floor(fx);
|
||||
var row = Math.floor(fy);
|
||||
|
||||
return {
|
||||
col: col,
|
||||
row: row,
|
||||
i: Math.floor((fx - col) * this.tileSize.w),
|
||||
j: Math.floor((fy - row) * this.tileSize.h)
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -316,14 +385,9 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
var url = "";
|
||||
if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {
|
||||
|
||||
var res = this.map.getResolution();
|
||||
var zoom = this.map.getZoom();
|
||||
var center = bounds.getCenterLonLat();
|
||||
|
||||
var col = Math.floor((center.lon - this.tileOrigin.lon) / (res * this.tileSize.w));
|
||||
var row = Math.floor((this.tileOrigin.lat - center.lat) / (res * this.tileSize.h));
|
||||
|
||||
var matrixId = this.getMatrixId();
|
||||
var center = bounds.getCenterLonLat();
|
||||
var info = this.getTileInfo(center);
|
||||
var matrixId = this.matrix.identifier;
|
||||
|
||||
if (this.requestEncoding.toUpperCase() === "REST") {
|
||||
|
||||
@@ -340,7 +404,8 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
}
|
||||
|
||||
// append other required path elements
|
||||
path = path + this.matrixSet + "/" + matrixId + "/" + row + "/" + col + "." + this.formatSuffix;
|
||||
path = path + this.matrixSet + "/" + this.matrix.identifier +
|
||||
"/" + info.row + "/" + info.col + "." + this.formatSuffix;
|
||||
|
||||
if (this.url instanceof Array) {
|
||||
url = this.selectUrl(path, this.url);
|
||||
@@ -362,9 +427,9 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
||||
LAYER: this.layer,
|
||||
STYLE: this.style,
|
||||
TILEMATRIXSET: this.matrixSet,
|
||||
TILEMATRIX: matrixId,
|
||||
TILEROW: row,
|
||||
TILECOL: col,
|
||||
TILEMATRIX: this.matrix.identifier,
|
||||
TILEROW: info.row,
|
||||
TILECOL: info.col,
|
||||
FORMAT: this.format
|
||||
};
|
||||
url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]);
|
||||
|
||||
@@ -271,7 +271,7 @@ http://schemas.opengis.net/wmts/1.0/examples/wmtsGetCapabilities_response.xml
|
||||
<TileMatrix>
|
||||
<ows:Identifier>1e6</ows:Identifier>
|
||||
<ScaleDenominator>1e6</ScaleDenominator>
|
||||
<TopLeftCorner>84 -180</TopLeftCorner>
|
||||
<TopLeftCorner>-180 84</TopLeftCorner>
|
||||
<TileWidth>256</TileWidth>
|
||||
<TileHeight>256</TileHeight>
|
||||
<MatrixWidth>60000</MatrixWidth>
|
||||
@@ -280,7 +280,7 @@ http://schemas.opengis.net/wmts/1.0/examples/wmtsGetCapabilities_response.xml
|
||||
<TileMatrix>
|
||||
<ows:Identifier>2.5e6</ows:Identifier>
|
||||
<ScaleDenominator>2.5e6</ScaleDenominator>
|
||||
<TopLeftCorner>84 -180</TopLeftCorner>
|
||||
<TopLeftCorner>-180 84</TopLeftCorner>
|
||||
<TileWidth>256</TileWidth>
|
||||
<TileHeight>256</TileHeight>
|
||||
<MatrixWidth>9000</MatrixWidth>
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
format: "image/png",
|
||||
isBaseLayer: false,
|
||||
requestEncoding: "KVP",
|
||||
maxResolution: 0.3521969032857032,
|
||||
numZoomLevels: 7,
|
||||
matrixIds: obj.contents.tileMatrixSets["arcgis-online-wgs84"].matrixIds
|
||||
});
|
||||
|
||||
@@ -40,7 +42,7 @@
|
||||
t.eq(layer1.tileSize.h, 512.0, "tileSize h is set correctly");
|
||||
}
|
||||
|
||||
function test_setMap(t) {
|
||||
function test_moveTo(t) {
|
||||
t.plan(9);
|
||||
var xml = document.getElementById("capabilities").firstChild.nodeValue;
|
||||
var doc = new OpenLayers.Format.XML().read(xml);
|
||||
@@ -54,12 +56,16 @@
|
||||
matrixSet: "arcgis-online-wgs84",
|
||||
format: "image/png",
|
||||
requestEncoding: "KVP",
|
||||
maxResolution: 0.3521969032857032,
|
||||
numZoomLevels: 7,
|
||||
matrixIds: obj.contents.tileMatrixSets["arcgis-online-wgs84"].matrixIds
|
||||
});
|
||||
|
||||
var map = new OpenLayers.Map('map');
|
||||
map.addLayer(layer0);
|
||||
|
||||
map.setCenter(new OpenLayers.LonLat(-97, 38), 1);
|
||||
|
||||
t.ok((layer0.tileOrigin instanceof OpenLayers.LonLat), "tileOrigin is an instance of OpenLayers.LonLat");
|
||||
t.ok((layer0.tileOrigin.lon == -180 && layer0.tileOrigin.lat == 90), "tileOrigin is set correctly");
|
||||
t.ok((layer0.tileSize instanceof OpenLayers.Size), "tileSize is an instance of OpenLayers.Size");
|
||||
@@ -177,6 +183,8 @@
|
||||
matrixSet: "arcgis-online-wgs84",
|
||||
format: "image/png",
|
||||
requestEncoding: "KVP",
|
||||
maxResolution: 0.3521969032857032,
|
||||
numZoomLevels: 7,
|
||||
matrixIds: obj.contents.tileMatrixSets["arcgis-online-wgs84"].matrixIds
|
||||
});
|
||||
|
||||
@@ -188,7 +196,8 @@
|
||||
matrixSet: "arcgis_online",
|
||||
format: "image/jpeg",
|
||||
tileSize: new OpenLayers.Size(512, 512),
|
||||
requestEncoding: "REST"
|
||||
requestEncoding: "REST",
|
||||
isBaseLayer: false
|
||||
});
|
||||
|
||||
var options = {
|
||||
|
||||
Reference in New Issue
Block a user