Project

General

Profile

Download (18 KB) Statistics
| Branch: | Tag: | Revision:
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
});
(28-28/31)