1
|
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
2
|
* full list of contributors). Published under the 2-clause BSD license.
|
3
|
* See license.txt in the OpenLayers distribution or repository for the
|
4
|
* full text of the license. */
|
5
|
|
6
|
/**
|
7
|
* @requires OpenLayers/Layer/Grid.js
|
8
|
*/
|
9
|
|
10
|
/**
|
11
|
* Class: OpenLayers.Layer.WMTS
|
12
|
* Instances of the WMTS class allow viewing of tiles from a service that
|
13
|
* implements the OGC WMTS specification version 1.0.0.
|
14
|
*
|
15
|
* Inherits from:
|
16
|
* - <OpenLayers.Layer.Grid>
|
17
|
*/
|
18
|
OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
|
19
|
|
20
|
/**
|
21
|
* APIProperty: isBaseLayer
|
22
|
* {Boolean} The layer will be considered a base layer. Default is true.
|
23
|
*/
|
24
|
isBaseLayer: true,
|
25
|
|
26
|
/**
|
27
|
* Property: version
|
28
|
* {String} WMTS version. Default is "1.0.0".
|
29
|
*/
|
30
|
version: "1.0.0",
|
31
|
|
32
|
/**
|
33
|
* APIProperty: requestEncoding
|
34
|
* {String} Request encoding. Can be "REST" or "KVP". Default is "KVP".
|
35
|
*/
|
36
|
requestEncoding: "KVP",
|
37
|
|
38
|
/**
|
39
|
* APIProperty: url
|
40
|
* {String|Array(String)} The base URL or request URL template for the WMTS
|
41
|
* service. Must be provided. Array is only supported for base URLs, not
|
42
|
* for request URL templates. URL templates are only supported for
|
43
|
* REST <requestEncoding>.
|
44
|
*/
|
45
|
url: null,
|
46
|
|
47
|
/**
|
48
|
* APIProperty: layer
|
49
|
* {String} The layer identifier advertised by the WMTS service. Must be
|
50
|
* provided.
|
51
|
*/
|
52
|
layer: null,
|
53
|
|
54
|
/**
|
55
|
* APIProperty: matrixSet
|
56
|
* {String} One of the advertised matrix set identifiers. Must be provided.
|
57
|
*/
|
58
|
matrixSet: null,
|
59
|
|
60
|
/**
|
61
|
* APIProperty: style
|
62
|
* {String} One of the advertised layer styles. Must be provided.
|
63
|
*/
|
64
|
style: null,
|
65
|
|
66
|
/**
|
67
|
* APIProperty: format
|
68
|
* {String} The image MIME type. Default is "image/jpeg".
|
69
|
*/
|
70
|
format: "image/jpeg",
|
71
|
|
72
|
/**
|
73
|
* APIProperty: tileOrigin
|
74
|
* {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map
|
75
|
* units. If the tile origin for each matrix in a set is different,
|
76
|
* the <matrixIds> should include a topLeftCorner property. If
|
77
|
* not provided, the tile origin will default to the top left corner
|
78
|
* of the layer <maxExtent>.
|
79
|
*/
|
80
|
tileOrigin: null,
|
81
|
|
82
|
/**
|
83
|
* APIProperty: tileFullExtent
|
84
|
* {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied,
|
85
|
* the layer's <maxExtent> property will be used.
|
86
|
*/
|
87
|
tileFullExtent: null,
|
88
|
|
89
|
/**
|
90
|
* APIProperty: formatSuffix
|
91
|
* {String} For REST request encoding, an image format suffix must be
|
92
|
* included in the request. If not provided, the suffix will be derived
|
93
|
* from the <format> property.
|
94
|
*/
|
95
|
formatSuffix: null,
|
96
|
|
97
|
/**
|
98
|
* APIProperty: matrixIds
|
99
|
* {Array} A list of tile matrix identifiers. If not provided, the matrix
|
100
|
* identifiers will be assumed to be integers corresponding to the
|
101
|
* map zoom level. If a list of strings is provided, each item should
|
102
|
* be the matrix identifier that corresponds to the map zoom level.
|
103
|
* Additionally, a list of objects can be provided. Each object should
|
104
|
* describe the matrix as presented in the WMTS capabilities. These
|
105
|
* objects should have the propertes shown below.
|
106
|
*
|
107
|
* Matrix properties:
|
108
|
* identifier - {String} The matrix identifier (required).
|
109
|
* scaleDenominator - {Number} The matrix scale denominator.
|
110
|
* topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the
|
111
|
* matrix. Must be provided if different than the layer <tileOrigin>.
|
112
|
* tileWidth - {Number} The tile width for the matrix. Must be provided
|
113
|
* if different than the width given in the layer <tileSize>.
|
114
|
* tileHeight - {Number} The tile height for the matrix. Must be provided
|
115
|
* if different than the height given in the layer <tileSize>.
|
116
|
*/
|
117
|
matrixIds: null,
|
118
|
|
119
|
/**
|
120
|
* APIProperty: dimensions
|
121
|
* {Array} For RESTful request encoding, extra dimensions may be specified.
|
122
|
* Items in this list should be property names in the <params> object.
|
123
|
* Values of extra dimensions will be determined from the corresponding
|
124
|
* values in the <params> object.
|
125
|
*/
|
126
|
dimensions: null,
|
127
|
|
128
|
/**
|
129
|
* APIProperty: params
|
130
|
* {Object} Extra parameters to include in tile requests. For KVP
|
131
|
* <requestEncoding>, these properties will be encoded in the request
|
132
|
* query string. For REST <requestEncoding>, these properties will
|
133
|
* become part of the request path, with order determined by the
|
134
|
* <dimensions> list.
|
135
|
*/
|
136
|
params: null,
|
137
|
|
138
|
/**
|
139
|
* APIProperty: zoomOffset
|
140
|
* {Number} If your cache has more levels than you want to provide
|
141
|
* access to with this layer, supply a zoomOffset. This zoom offset
|
142
|
* is added to the current map zoom level to determine the level
|
143
|
* for a requested tile. For example, if you supply a zoomOffset
|
144
|
* of 3, when the map is at the zoom 0, tiles will be requested from
|
145
|
* level 3 of your cache. Default is 0 (assumes cache level and map
|
146
|
* zoom are equivalent). Additionally, if this layer is to be used
|
147
|
* as an overlay and the cache has fewer zoom levels than the base
|
148
|
* layer, you can supply a negative zoomOffset. For example, if a
|
149
|
* map zoom level of 1 corresponds to your cache level zero, you would
|
150
|
* supply a -1 zoomOffset (and set the maxResolution of the layer
|
151
|
* appropriately). The zoomOffset value has no effect if complete
|
152
|
* matrix definitions (including scaleDenominator) are supplied in
|
153
|
* the <matrixIds> property. Defaults to 0 (no zoom offset).
|
154
|
*/
|
155
|
zoomOffset: 0,
|
156
|
|
157
|
/**
|
158
|
* APIProperty: serverResolutions
|
159
|
* {Array} A list of all resolutions available on the server. Only set this
|
160
|
* property if the map resolutions differ from the server. This
|
161
|
* property serves two purposes. (a) <serverResolutions> can include
|
162
|
* resolutions that the server supports and that you don't want to
|
163
|
* provide with this layer; you can also look at <zoomOffset>, which is
|
164
|
* an alternative to <serverResolutions> for that specific purpose.
|
165
|
* (b) The map can work with resolutions that aren't supported by
|
166
|
* the server, i.e. that aren't in <serverResolutions>. When the
|
167
|
* map is displayed in such a resolution data for the closest
|
168
|
* server-supported resolution is loaded and the layer div is
|
169
|
* stretched as necessary.
|
170
|
*/
|
171
|
serverResolutions: null,
|
172
|
|
173
|
/**
|
174
|
* Property: formatSuffixMap
|
175
|
* {Object} a map between WMTS 'format' request parameter and tile image file suffix
|
176
|
*/
|
177
|
formatSuffixMap: {
|
178
|
"image/png": "png",
|
179
|
"image/png8": "png",
|
180
|
"image/png24": "png",
|
181
|
"image/png32": "png",
|
182
|
"png": "png",
|
183
|
"image/jpeg": "jpg",
|
184
|
"image/jpg": "jpg",
|
185
|
"jpeg": "jpg",
|
186
|
"jpg": "jpg"
|
187
|
},
|
188
|
|
189
|
/**
|
190
|
* Property: matrix
|
191
|
* {Object} Matrix definition for the current map resolution. Updated by
|
192
|
* the <updateMatrixProperties> method.
|
193
|
*/
|
194
|
matrix: null,
|
195
|
|
196
|
/**
|
197
|
* Constructor: OpenLayers.Layer.WMTS
|
198
|
* Create a new WMTS layer.
|
199
|
*
|
200
|
* Example:
|
201
|
* (code)
|
202
|
* var wmts = new OpenLayers.Layer.WMTS({
|
203
|
* name: "My WMTS Layer",
|
204
|
* url: "http://example.com/wmts",
|
205
|
* layer: "layer_id",
|
206
|
* style: "default",
|
207
|
* matrixSet: "matrix_id"
|
208
|
* });
|
209
|
* (end)
|
210
|
*
|
211
|
* Parameters:
|
212
|
* config - {Object} Configuration properties for the layer.
|
213
|
*
|
214
|
* Required configuration properties:
|
215
|
* url - {String} The base url for the service. See the <url> property.
|
216
|
* layer - {String} The layer identifier. See the <layer> property.
|
217
|
* style - {String} The layer style identifier. See the <style> property.
|
218
|
* matrixSet - {String} The tile matrix set identifier. See the <matrixSet>
|
219
|
* property.
|
220
|
*
|
221
|
* Any other documented layer properties can be provided in the config object.
|
222
|
*/
|
223
|
initialize: function(config) {
|
224
|
|
225
|
// confirm required properties are supplied
|
226
|
var required = {
|
227
|
url: true,
|
228
|
layer: true,
|
229
|
style: true,
|
230
|
matrixSet: true
|
231
|
};
|
232
|
for (var prop in required) {
|
233
|
if (!(prop in config)) {
|
234
|
throw new Error("Missing property '" + prop + "' in layer configuration.");
|
235
|
}
|
236
|
}
|
237
|
|
238
|
config.params = OpenLayers.Util.upperCaseObject(config.params);
|
239
|
var args = [config.name, config.url, config.params, config];
|
240
|
OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);
|
241
|
|
242
|
|
243
|
// determine format suffix (for REST)
|
244
|
if (!this.formatSuffix) {
|
245
|
this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop();
|
246
|
}
|
247
|
|
248
|
// expand matrixIds (may be array of string or array of object)
|
249
|
if (this.matrixIds) {
|
250
|
var len = this.matrixIds.length;
|
251
|
if (len && typeof this.matrixIds[0] === "string") {
|
252
|
var ids = this.matrixIds;
|
253
|
this.matrixIds = new Array(len);
|
254
|
for (var i=0; i<len; ++i) {
|
255
|
this.matrixIds[i] = {identifier: ids[i]};
|
256
|
}
|
257
|
}
|
258
|
}
|
259
|
|
260
|
},
|
261
|
|
262
|
/**
|
263
|
* Method: setMap
|
264
|
*/
|
265
|
setMap: function() {
|
266
|
OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
|
267
|
},
|
268
|
|
269
|
/**
|
270
|
* Method: updateMatrixProperties
|
271
|
* Called when map resolution changes to update matrix related properties.
|
272
|
*/
|
273
|
updateMatrixProperties: function() {
|
274
|
this.matrix = this.getMatrix();
|
275
|
if (this.matrix) {
|
276
|
if (this.matrix.topLeftCorner) {
|
277
|
this.tileOrigin = this.matrix.topLeftCorner;
|
278
|
}
|
279
|
if (this.matrix.tileWidth && this.matrix.tileHeight) {
|
280
|
this.tileSize = new OpenLayers.Size(
|
281
|
this.matrix.tileWidth, this.matrix.tileHeight
|
282
|
);
|
283
|
}
|
284
|
if (!this.tileOrigin) {
|
285
|
this.tileOrigin = new OpenLayers.LonLat(
|
286
|
this.maxExtent.left, this.maxExtent.top
|
287
|
);
|
288
|
}
|
289
|
if (!this.tileFullExtent) {
|
290
|
this.tileFullExtent = this.maxExtent;
|
291
|
}
|
292
|
}
|
293
|
},
|
294
|
|
295
|
/**
|
296
|
* Method: moveTo
|
297
|
*
|
298
|
* Parameters:
|
299
|
* bounds - {<OpenLayers.Bounds>}
|
300
|
* zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
|
301
|
* do some init work in that case.
|
302
|
* dragging - {Boolean}
|
303
|
*/
|
304
|
moveTo:function(bounds, zoomChanged, dragging) {
|
305
|
if (zoomChanged || !this.matrix) {
|
306
|
this.updateMatrixProperties();
|
307
|
}
|
308
|
return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments);
|
309
|
},
|
310
|
|
311
|
/**
|
312
|
* APIMethod: clone
|
313
|
*
|
314
|
* Parameters:
|
315
|
* obj - {Object}
|
316
|
*
|
317
|
* Returns:
|
318
|
* {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS>
|
319
|
*/
|
320
|
clone: function(obj) {
|
321
|
if (obj == null) {
|
322
|
obj = new OpenLayers.Layer.WMTS(this.options);
|
323
|
}
|
324
|
//get all additions from superclasses
|
325
|
obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
|
326
|
// copy/set any non-init, non-simple values here
|
327
|
return obj;
|
328
|
},
|
329
|
|
330
|
/**
|
331
|
* Method: getIdentifier
|
332
|
* Get the current index in the matrixIds array.
|
333
|
*/
|
334
|
getIdentifier: function() {
|
335
|
return this.getServerZoom();
|
336
|
},
|
337
|
|
338
|
/**
|
339
|
* Method: getMatrix
|
340
|
* Get the appropriate matrix definition for the current map resolution.
|
341
|
*/
|
342
|
getMatrix: function() {
|
343
|
var matrix;
|
344
|
if (!this.matrixIds || this.matrixIds.length === 0) {
|
345
|
matrix = {identifier: this.getIdentifier()};
|
346
|
} else {
|
347
|
// get appropriate matrix given the map scale if possible
|
348
|
if ("scaleDenominator" in this.matrixIds[0]) {
|
349
|
// scale denominator calculation based on WMTS spec
|
350
|
var denom =
|
351
|
OpenLayers.METERS_PER_INCH *
|
352
|
OpenLayers.INCHES_PER_UNIT[this.units] *
|
353
|
this.getServerResolution() / 0.28E-3;
|
354
|
var diff = Number.POSITIVE_INFINITY;
|
355
|
var delta;
|
356
|
for (var i=0, ii=this.matrixIds.length; i<ii; ++i) {
|
357
|
delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom));
|
358
|
if (delta < diff) {
|
359
|
diff = delta;
|
360
|
matrix = this.matrixIds[i];
|
361
|
}
|
362
|
}
|
363
|
} else {
|
364
|
// fall back on zoom as index
|
365
|
matrix = this.matrixIds[this.getIdentifier()];
|
366
|
}
|
367
|
}
|
368
|
return matrix;
|
369
|
},
|
370
|
|
371
|
/**
|
372
|
* Method: getTileInfo
|
373
|
* Get tile information for a given location at the current map resolution.
|
374
|
*
|
375
|
* Parameters:
|
376
|
* loc - {<OpenLayers.LonLat} A location in map coordinates.
|
377
|
*
|
378
|
* Returns:
|
379
|
* {Object} An object with "col", "row", "i", and "j" properties. The col
|
380
|
* and row values are zero based tile indexes from the top left. The
|
381
|
* i and j values are the number of pixels to the left and top
|
382
|
* (respectively) of the given location within the target tile.
|
383
|
*/
|
384
|
getTileInfo: function(loc) {
|
385
|
var res = this.getServerResolution();
|
386
|
|
387
|
var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);
|
388
|
var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);
|
389
|
|
390
|
var col = Math.floor(fx);
|
391
|
var row = Math.floor(fy);
|
392
|
|
393
|
return {
|
394
|
col: col,
|
395
|
row: row,
|
396
|
i: Math.floor((fx - col) * this.tileSize.w),
|
397
|
j: Math.floor((fy - row) * this.tileSize.h)
|
398
|
};
|
399
|
},
|
400
|
|
401
|
/**
|
402
|
* Method: getURL
|
403
|
*
|
404
|
* Parameters:
|
405
|
* bounds - {<OpenLayers.Bounds>}
|
406
|
*
|
407
|
* Returns:
|
408
|
* {String} A URL for the tile corresponding to the given bounds.
|
409
|
*/
|
410
|
getURL: function(bounds) {
|
411
|
bounds = this.adjustBounds(bounds);
|
412
|
var url = "";
|
413
|
if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {
|
414
|
|
415
|
var center = bounds.getCenterLonLat();
|
416
|
var info = this.getTileInfo(center);
|
417
|
var matrixId = this.matrix.identifier;
|
418
|
var dimensions = this.dimensions, params;
|
419
|
|
420
|
if (OpenLayers.Util.isArray(this.url)) {
|
421
|
url = this.selectUrl([
|
422
|
this.version, this.style, this.matrixSet,
|
423
|
this.matrix.identifier, info.row, info.col
|
424
|
].join(","), this.url);
|
425
|
} else {
|
426
|
url = this.url;
|
427
|
}
|
428
|
|
429
|
if (this.requestEncoding.toUpperCase() === "REST") {
|
430
|
params = this.params;
|
431
|
if (url.indexOf("{") !== -1) {
|
432
|
var template = url.replace(/\{/g, "${");
|
433
|
var context = {
|
434
|
// spec does not make clear if capital S or not
|
435
|
style: this.style, Style: this.style,
|
436
|
TileMatrixSet: this.matrixSet,
|
437
|
TileMatrix: this.matrix.identifier,
|
438
|
TileRow: info.row,
|
439
|
TileCol: info.col
|
440
|
};
|
441
|
if (dimensions) {
|
442
|
var dimension, i;
|
443
|
for (i=dimensions.length-1; i>=0; --i) {
|
444
|
dimension = dimensions[i];
|
445
|
context[dimension] = params[dimension.toUpperCase()];
|
446
|
}
|
447
|
}
|
448
|
url = OpenLayers.String.format(template, context);
|
449
|
} else {
|
450
|
// include 'version', 'layer' and 'style' in tile resource url
|
451
|
var path = this.version + "/" + this.layer + "/" + this.style + "/";
|
452
|
|
453
|
// append optional dimension path elements
|
454
|
if (dimensions) {
|
455
|
for (var i=0; i<dimensions.length; i++) {
|
456
|
if (params[dimensions[i]]) {
|
457
|
path = path + params[dimensions[i]] + "/";
|
458
|
}
|
459
|
}
|
460
|
}
|
461
|
|
462
|
// append other required path elements
|
463
|
path = path + this.matrixSet + "/" + this.matrix.identifier +
|
464
|
"/" + info.row + "/" + info.col + "." + this.formatSuffix;
|
465
|
|
466
|
if (!url.match(/\/$/)) {
|
467
|
url = url + "/";
|
468
|
}
|
469
|
url = url + path;
|
470
|
}
|
471
|
} else if (this.requestEncoding.toUpperCase() === "KVP") {
|
472
|
|
473
|
// assemble all required parameters
|
474
|
params = {
|
475
|
SERVICE: "WMTS",
|
476
|
REQUEST: "GetTile",
|
477
|
VERSION: this.version,
|
478
|
LAYER: this.layer,
|
479
|
STYLE: this.style,
|
480
|
TILEMATRIXSET: this.matrixSet,
|
481
|
TILEMATRIX: this.matrix.identifier,
|
482
|
TILEROW: info.row,
|
483
|
TILECOL: info.col,
|
484
|
FORMAT: this.format
|
485
|
};
|
486
|
url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]);
|
487
|
|
488
|
}
|
489
|
}
|
490
|
return url;
|
491
|
},
|
492
|
|
493
|
/**
|
494
|
* APIMethod: mergeNewParams
|
495
|
* Extend the existing layer <params> with new properties. Tiles will be
|
496
|
* reloaded with updated params in the request.
|
497
|
*
|
498
|
* Parameters:
|
499
|
* newParams - {Object} Properties to extend to existing <params>.
|
500
|
*/
|
501
|
mergeNewParams: function(newParams) {
|
502
|
if (this.requestEncoding.toUpperCase() === "KVP") {
|
503
|
return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(
|
504
|
this, [OpenLayers.Util.upperCaseObject(newParams)]
|
505
|
);
|
506
|
}
|
507
|
},
|
508
|
|
509
|
CLASS_NAME: "OpenLayers.Layer.WMTS"
|
510
|
});
|