/
TileJSON.js
211 lines (193 loc) · 6.94 KB
/
TileJSON.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/**
* @module ol/source/TileJSON
*/
// FIXME check order of async callbacks
/**
* See https://mapbox.com/developers/api/.
*/
import TileImage from './TileImage.js';
import {applyTransform, intersects} from '../extent.js';
import {createFromTemplates} from '../tileurlfunction.js';
import {createXYZ, extentFromProjection} from '../tilegrid.js';
import {get as getProjection, getTransformFromProjections} from '../proj.js';
import {jsonp as requestJSONP} from '../net.js';
/**
* @typedef {Object} Config
* @property {string} [name] The name.
* @property {string} [description] The description.
* @property {string} [version] The version.
* @property {string} [attribution] The attribution.
* @property {string} [template] The template.
* @property {string} [legend] The legend.
* @property {string} [scheme] The scheme.
* @property {Array<string>} tiles The tile URL templates.
* @property {Array<string>} [grids] Optional grids.
* @property {number} [minzoom] Minimum zoom level.
* @property {number} [maxzoom] Maximum zoom level.
* @property {Array<number>} [bounds] Optional bounds.
* @property {Array<number>} [center] Optional center.
*/
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
* @property {boolean} [interpolate=true] Use interpolated values when resampling. By default,
* linear interpolation is used when resampling. Set to false to use the nearest neighbor instead.
* @property {boolean} [jsonp=false] Use JSONP with callback to load the TileJSON.
* Useful when the server does not support CORS..
* @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
* Higher values can increase reprojection performance, but decrease precision.
* @property {Config} [tileJSON] TileJSON configuration for this source.
* If not provided, `url` must be configured.
* @property {import("../Tile.js").LoadFunction} [tileLoadFunction] Optional function to load a tile given a URL. The default is
* ```js
* function(imageTile, src) {
* imageTile.getImage().src = src;
* };
* ```
* @property {number|import("../size.js").Size} [tileSize=[256, 256]] The tile size used by the tile service.
* Note: `tileSize` and other non-standard TileJSON properties are currently ignored.
* @property {string} [url] URL to the TileJSON file. If not provided, `tileJSON` must be configured.
* @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
* @property {number} [transition] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`.
* @property {number|import("../array.js").NearestDirectionFunction} [zDirection=0]
* Choose whether to use tiles with a higher or lower zoom level when between integer
* zoom levels. See {@link module:ol/tilegrid/TileGrid~TileGrid#getZForResolution}.
*/
/**
* @classdesc
* Layer source for tile data in TileJSON format.
* @api
*/
class TileJSON extends TileImage {
/**
* @param {Options} options TileJSON options.
*/
constructor(options) {
super({
attributions: options.attributions,
cacheSize: options.cacheSize,
crossOrigin: options.crossOrigin,
interpolate: options.interpolate,
projection: getProjection('EPSG:3857'),
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
state: 'loading',
tileLoadFunction: options.tileLoadFunction,
wrapX: options.wrapX !== undefined ? options.wrapX : true,
transition: options.transition,
zDirection: options.zDirection,
});
/**
* @type {Config}
* @private
*/
this.tileJSON_ = null;
/**
* @type {number|import("../size.js").Size}
* @private
*/
this.tileSize_ = options.tileSize;
if (options.url) {
if (options.jsonp) {
requestJSONP(
options.url,
this.handleTileJSONResponse.bind(this),
this.handleTileJSONError.bind(this),
);
} else {
const client = new XMLHttpRequest();
client.addEventListener('load', this.onXHRLoad_.bind(this));
client.addEventListener('error', this.onXHRError_.bind(this));
client.open('GET', options.url);
client.send();
}
} else if (options.tileJSON) {
this.handleTileJSONResponse(options.tileJSON);
} else {
throw new Error('Either `url` or `tileJSON` options must be provided');
}
}
/**
* @private
* @param {Event} event The load event.
*/
onXHRLoad_(event) {
const client = /** @type {XMLHttpRequest} */ (event.target);
// status will be 0 for file:// urls
if (!client.status || (client.status >= 200 && client.status < 300)) {
let response;
try {
response = /** @type {Config} */ (JSON.parse(client.responseText));
} catch (err) {
this.handleTileJSONError();
return;
}
this.handleTileJSONResponse(response);
} else {
this.handleTileJSONError();
}
}
/**
* @private
* @param {Event} event The error event.
*/
onXHRError_(event) {
this.handleTileJSONError();
}
/**
* @return {Config} The tilejson object.
* @api
*/
getTileJSON() {
return this.tileJSON_;
}
/**
* @protected
* @param {Config} tileJSON Tile JSON.
*/
handleTileJSONResponse(tileJSON) {
const epsg4326Projection = getProjection('EPSG:4326');
const sourceProjection = this.getProjection();
let extent;
if (tileJSON['bounds'] !== undefined) {
const transform = getTransformFromProjections(
epsg4326Projection,
sourceProjection,
);
extent = applyTransform(tileJSON['bounds'], transform);
}
const gridExtent = extentFromProjection(sourceProjection);
const minZoom = tileJSON['minzoom'] || 0;
const maxZoom = tileJSON['maxzoom'] || 22;
const tileGrid = createXYZ({
extent: gridExtent,
maxZoom: maxZoom,
minZoom: minZoom,
tileSize: this.tileSize_,
});
this.tileGrid = tileGrid;
this.tileUrlFunction = createFromTemplates(tileJSON['tiles'], tileGrid);
if (tileJSON['attribution'] && !this.getAttributions()) {
const attributionExtent = extent !== undefined ? extent : gridExtent;
this.setAttributions(function (frameState) {
if (intersects(attributionExtent, frameState.extent)) {
return [tileJSON['attribution']];
}
return null;
});
}
this.tileJSON_ = tileJSON;
this.setState('ready');
}
/**
* @protected
*/
handleTileJSONError() {
this.setState('error');
}
}
export default TileJSON;