@mapcatch/util 2.0.5-b → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/catchUtil.min.esm.js +4 -4
  2. package/dist/catchUtil.min.js +1 -1
  3. package/package.json +1 -21
  4. package/src/constants/crs.js +42098 -42098
  5. package/src/event/event.js +4 -5
  6. package/src/gl-operations/constants.js +9 -9
  7. package/src/gl-operations/default_options.js +97 -97
  8. package/src/gl-operations/index.js +532 -532
  9. package/src/gl-operations/reglCommands/contours.js +27 -27
  10. package/src/gl-operations/reglCommands/default.js +45 -45
  11. package/src/gl-operations/reglCommands/hillshading.js +340 -340
  12. package/src/gl-operations/reglCommands/index.js +6 -6
  13. package/src/gl-operations/reglCommands/multiLayers.js +303 -303
  14. package/src/gl-operations/reglCommands/transitions.js +111 -111
  15. package/src/gl-operations/reglCommands/util.js +71 -71
  16. package/src/gl-operations/renderer.js +209 -209
  17. package/src/gl-operations/shaders/fragment/convertDem.js +25 -25
  18. package/src/gl-operations/shaders/fragment/convolutionSmooth.js +54 -54
  19. package/src/gl-operations/shaders/fragment/diffCalc.js +33 -33
  20. package/src/gl-operations/shaders/fragment/drawResult.js +46 -46
  21. package/src/gl-operations/shaders/fragment/hillshading/hsAdvAmbientShadows.js +78 -78
  22. package/src/gl-operations/shaders/fragment/hillshading/hsAdvDirect.js +59 -59
  23. package/src/gl-operations/shaders/fragment/hillshading/hsAdvFinalBaselayer.js +30 -30
  24. package/src/gl-operations/shaders/fragment/hillshading/hsAdvFinalColorscale.js +60 -60
  25. package/src/gl-operations/shaders/fragment/hillshading/hsAdvMergeAndScaleTiles.js +26 -26
  26. package/src/gl-operations/shaders/fragment/hillshading/hsAdvNormals.js +25 -25
  27. package/src/gl-operations/shaders/fragment/hillshading/hsAdvSmooth.js +53 -53
  28. package/src/gl-operations/shaders/fragment/hillshading/hsAdvSoftShadows.js +80 -80
  29. package/src/gl-operations/shaders/fragment/hillshading/hsPregen.js +54 -54
  30. package/src/gl-operations/shaders/fragment/interpolateColor.js +65 -65
  31. package/src/gl-operations/shaders/fragment/interpolateColorOnly.js +49 -49
  32. package/src/gl-operations/shaders/fragment/interpolateValue.js +136 -136
  33. package/src/gl-operations/shaders/fragment/multiAnalyze1Calc.js +35 -35
  34. package/src/gl-operations/shaders/fragment/multiAnalyze2Calc.js +45 -45
  35. package/src/gl-operations/shaders/fragment/multiAnalyze3Calc.js +53 -53
  36. package/src/gl-operations/shaders/fragment/multiAnalyze4Calc.js +61 -61
  37. package/src/gl-operations/shaders/fragment/multiAnalyze5Calc.js +69 -69
  38. package/src/gl-operations/shaders/fragment/multiAnalyze6Calc.js +77 -77
  39. package/src/gl-operations/shaders/fragment/single.js +93 -93
  40. package/src/gl-operations/shaders/transform.js +21 -21
  41. package/src/gl-operations/shaders/util/computeColor.glsl +85 -85
  42. package/src/gl-operations/shaders/util/getTexelValue.glsl +10 -10
  43. package/src/gl-operations/shaders/util/isCloseEnough.glsl +9 -9
  44. package/src/gl-operations/shaders/util/rgbaToFloat.glsl +17 -17
  45. package/src/gl-operations/shaders/vertex/double.js +16 -16
  46. package/src/gl-operations/shaders/vertex/multi3.js +19 -19
  47. package/src/gl-operations/shaders/vertex/multi4.js +22 -22
  48. package/src/gl-operations/shaders/vertex/multi5.js +25 -25
  49. package/src/gl-operations/shaders/vertex/multi6.js +28 -28
  50. package/src/gl-operations/shaders/vertex/single.js +13 -13
  51. package/src/gl-operations/shaders/vertex/singleNotTransformed.js +11 -11
  52. package/src/gl-operations/texture_manager.js +141 -141
  53. package/src/gl-operations/util.js +336 -336
  54. package/README.md +0 -44
@@ -1,533 +1,533 @@
1
- import defaultOptions from './default_options'
2
- import * as util from './util'
3
- import Renderer from './renderer'
4
- import { EARTH_CIRCUMFERENCE } from './constants'
5
-
6
- export default class GLOperations {
7
- constructor (options) {
8
- this.options = Object.assign({}, defaultOptions, options)
9
- this._checkColorScaleAndSentinels()
10
- this.setHillshadeOptions()
11
- const {
12
- nodataValue,
13
- tileSize
14
- } = this.options
15
-
16
- this._renderer = new Renderer(
17
- this,
18
- tileSize,
19
- nodataValue,
20
- this.options.colorScale,
21
- this.options.sentinelValues,
22
- this.options.colorscaleMaxLength,
23
- this.options.sentinelMaxLength,
24
- this.options.aboveColor,
25
- this.options.belowColor
26
- )
27
-
28
- this._renderer.generateAmbientDirections(this.options.hsAdvAmbientIterations)
29
- this._renderer.generateSunDirections(
30
- this.options.hsAdvSoftIterations,
31
- this.options.hsAdvSunRadiusMultiplier
32
- )
33
- }
34
-
35
- destroy () {
36
- if (this._renderer) this._renderer.destroy()
37
- this._renderer = null
38
- }
39
-
40
- updateOptions (options) {
41
- const {
42
- url: prevUrl,
43
- operationUrlA: prevUrlA,
44
- operationUrlB: prevUrlB,
45
- operationUrlC: prevUrlC,
46
- operationUrlD: prevUrlD,
47
- colorScale: prevColorScale,
48
- sentinelValues: prevSentinelValues,
49
- hillshadeType: prevHillshadeType,
50
- hsAdvSoftIterations: prevHsAdvSoftIterations,
51
- hsAdvAmbientIterations: prevHsAdvAmbientIterations,
52
- hsAdvSunRadiusMultiplier: prevHsAdvSunRadiusMultiplier,
53
- hsAdvBaselayerUrl: prevHsAdvBaselayerUrl,
54
- colorscaleMaxLength: prevScaleMaxLength,
55
- sentinelMaxLength: prevSentinelMaxLength,
56
- aboveColor: prevAboveColor,
57
- belowColor: prevBelowColor
58
- } = this.options
59
- Object.assign(this.options, options)
60
- // create new renderer if max length of sentinels or colorscale changes
61
- if (this.options.colorscaleMaxLength !== prevScaleMaxLength || this.options.sentinelMaxLength !== prevSentinelMaxLength) {
62
- const tileSize = this._tileSizeAsNumber()
63
- const renderer = new Renderer(
64
- this,
65
- tileSize,
66
- this.options.nodataValue,
67
- this.options.colorScale,
68
- this.options.sentinelValues,
69
- this.options.colorscaleMaxLength,
70
- this.options.sentinelMaxLength
71
- )
72
-
73
- this._renderer.regl.destroy()
74
- // @ts-ignore
75
- delete this._renderer
76
-
77
- Object.assign(this, {
78
- _renderer: renderer
79
- })
80
- }
81
- this._checkColorScaleAndSentinels()
82
- if (this.options.colorScale !== prevColorScale) {
83
- this._renderer.updateColorscale(this.options.colorScale)
84
- }
85
- if (this.options.aboveColor !== prevAboveColor || this.options.belowColor !== prevBelowColor) {
86
- this._renderer.updateOuteRangeColor(this.options.aboveColor, this.options.belowColor)
87
- }
88
- if (this.options.sentinelValues !== prevSentinelValues) {
89
- this._renderer.updateSentinels(this.options.sentinelValues)
90
- }
91
- if (this.options.hsAdvAmbientIterations !== prevHsAdvAmbientIterations) {
92
- this._renderer.generateAmbientDirections(this.options.hsAdvAmbientIterations)
93
- }
94
- if (
95
- this.options.hsAdvSoftIterations !== prevHsAdvSoftIterations ||
96
- this.options.hsAdvSunRadiusMultiplier !== prevHsAdvSunRadiusMultiplier
97
- ) {
98
- this._renderer.generateSunDirections(this.options.hsAdvSoftIterations, this.options.hsAdvSunRadiusMultiplier)
99
- }
100
- this.setHillshadeOptions()
101
-
102
- if (this.options.extraPixelLayers > 0 && this.options.glOperation === 'none') {
103
- this._maybeLoadExtraLayers(prevUrlA, prevUrlB, prevUrlC, prevUrlD)
104
- }
105
- // TODO: Fix shader so simple hillshading works ok with larger texture than tileSize
106
- if (this.options.hillshadeType !== prevHillshadeType) {
107
- // reduce textureManager size as simple hillshading type currently gets "edges" around the tiles with larger texture.
108
- if (this.options.hillshadeType === 'simple') {
109
- this._renderer.setMaxTextureDimension(this._tileSizeAsNumber())
110
- } else if (prevHillshadeType === 'simple') {
111
- this._renderer.setMaxTextureDimension(this._renderer.findMaxTextureDimension())
112
- }
113
- }
114
-
115
- if (this.options.url !== prevUrl) {
116
- // need to clear tiles so they are not reused with adv.hs.
117
- this._renderer.textureManager.clearTiles()
118
- }
119
-
120
- if (this.options.hsAdvBaselayerUrl !== prevHsAdvBaselayerUrl) {
121
- // need to clear tiles so they are not reused
122
- // TODO: Check why necessary
123
- this._renderer.textureManagerHillshade.clearTiles()
124
- }
125
- }
126
-
127
- createTile (coords) {
128
- const {
129
- extraPixelLayers,
130
- tileSize,
131
- url,
132
- operationUrlA,
133
- operationUrlB,
134
- operationUrlC,
135
- operationUrlD,
136
- operationUrlE,
137
- operationUrlF,
138
- filterLowA,
139
- filterHighA,
140
- filterLowB,
141
- filterHighB,
142
- filterLowC,
143
- filterHighC,
144
- filterLowD,
145
- filterHighD,
146
- filterLowE,
147
- filterHighE,
148
- filterLowF,
149
- filterHighF,
150
- multiplierA,
151
- multiplierB,
152
- multiplierC,
153
- multiplierD,
154
- multiplierE,
155
- multiplierF
156
- } = this.options
157
-
158
- // Create a <canvas> element to contain the rendered image.
159
- const tileCanvas = document.createElement('canvas')
160
- // Configure the element.
161
- Object.assign(tileCanvas, {
162
- className: 'gl-tile',
163
- width: tileSize,
164
- height: tileSize
165
- })
166
-
167
- return new Promise((resolve) => {
168
- if (this.options.glOperation === 'none') {
169
- // Download an extra layer if required
170
- if (extraPixelLayers === 1) {
171
- this._fetchTileData(coords, operationUrlA).then((pixelDataA) => {
172
- tileCanvas.pixelDataA = pixelDataA
173
- })
174
- }
175
-
176
- this._fetchTileData(coords, url).then((pixelData) => {
177
- let [sourceX, sourceY] = this._renderer.renderTile(
178
- { coords, pixelData },
179
- this.options._hillshadeOptions,
180
- coords.z
181
- )
182
-
183
- // Copy pixel data to a property on tile canvas element (for later retrieval).
184
- tileCanvas.pixelData = pixelData
185
-
186
- this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
187
-
188
- resolve(tileCanvas)
189
- })
190
- } else if (this.options.glOperation === 'diff') {
191
- Promise.all([
192
- this._fetchTileData(coords, operationUrlA),
193
- this._fetchTileData(coords, operationUrlB)
194
- ]).then((pixelDataArray) => {
195
- const pixelDataA = pixelDataArray[0]
196
- const pixelDataB = pixelDataArray[1]
197
- const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileDiff(
198
- { coords: coords, pixelData: pixelDataA },
199
- { coords: coords, pixelData: pixelDataB }
200
- )
201
-
202
- tileCanvas.pixelData = resultEncodedPixels
203
- tileCanvas.pixelDataA = pixelDataA
204
- tileCanvas.pixelDataB = pixelDataB
205
-
206
- // Copy contents to tileCanvas.
207
- this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
208
- })
209
- } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 1) {
210
- Promise.all([
211
- this._fetchTileData(coords, operationUrlA)
212
- ]).then((pixelDataArray) => {
213
- const pixelDataA = pixelDataArray[0]
214
- const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti1(
215
- { coords: coords, pixelData: pixelDataA },
216
- filterLowA,
217
- filterHighA,
218
- multiplierA
219
- )
220
-
221
- // Copy pixel data to a property on tile canvas element (for later retrieval).
222
- tileCanvas.pixelData = resultEncodedPixels
223
- tileCanvas.pixelDataA = pixelDataA
224
-
225
- // Copy contents to tileCanvas.
226
- this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
227
- })
228
- } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 2) {
229
- Promise.all([
230
- this._fetchTileData(coords, operationUrlA),
231
- this._fetchTileData(coords, operationUrlB)
232
- ]).then((pixelDataArray) => {
233
- const pixelDataA = pixelDataArray[0]
234
- const pixelDataB = pixelDataArray[1]
235
- const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti2(
236
- { coords: coords, pixelData: pixelDataA },
237
- { coords: coords, pixelData: pixelDataB },
238
- filterLowA,
239
- filterHighA,
240
- filterLowB,
241
- filterHighB,
242
- multiplierA,
243
- multiplierB
244
- )
245
-
246
- // Copy pixel data to a property on tile canvas element (for later retrieval).
247
- tileCanvas.pixelData = resultEncodedPixels
248
- tileCanvas.pixelDataA = pixelDataA
249
- tileCanvas.pixelDataB = pixelDataB
250
-
251
- // Copy contents to tileCanvas.
252
- this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
253
- })
254
- } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 3) {
255
- Promise.all([
256
- this._fetchTileData(coords, operationUrlA),
257
- this._fetchTileData(coords, operationUrlB),
258
- this._fetchTileData(coords, operationUrlC)
259
- ]).then((pixelDataArray) => {
260
- const pixelDataA = pixelDataArray[0]
261
- const pixelDataB = pixelDataArray[1]
262
- const pixelDataC = pixelDataArray[2]
263
- const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti3(
264
- { coords: coords, pixelData: pixelDataA },
265
- { coords: coords, pixelData: pixelDataB },
266
- { coords: coords, pixelData: pixelDataC },
267
- filterLowA,
268
- filterHighA,
269
- filterLowB,
270
- filterHighB,
271
- filterLowC,
272
- filterHighC,
273
- multiplierA,
274
- multiplierB,
275
- multiplierC
276
- )
277
-
278
- // Copy pixel data to a property on tile canvas element (for later retrieval).
279
- tileCanvas.pixelData = resultEncodedPixels
280
- tileCanvas.pixelDataA = pixelDataA
281
- tileCanvas.pixelDataB = pixelDataB
282
- tileCanvas.pixelDataC = pixelDataC
283
-
284
- // Copy contents to tileCanvas.
285
- this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
286
- })
287
- } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 4) {
288
- Promise.all([
289
- this._fetchTileData(coords, operationUrlA),
290
- this._fetchTileData(coords, operationUrlB),
291
- this._fetchTileData(coords, operationUrlC),
292
- this._fetchTileData(coords, operationUrlD)
293
- ]).then((pixelDataArray) => {
294
- const pixelDataA = pixelDataArray[0]
295
- const pixelDataB = pixelDataArray[1]
296
- const pixelDataC = pixelDataArray[2]
297
- const pixelDataD = pixelDataArray[3]
298
- const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti4(
299
- { coords: coords, pixelData: pixelDataA },
300
- { coords: coords, pixelData: pixelDataB },
301
- { coords: coords, pixelData: pixelDataC },
302
- { coords: coords, pixelData: pixelDataD },
303
- filterLowA,
304
- filterHighA,
305
- filterLowB,
306
- filterHighB,
307
- filterLowC,
308
- filterHighC,
309
- filterLowD,
310
- filterHighD,
311
- multiplierA,
312
- multiplierB,
313
- multiplierC,
314
- multiplierD
315
- )
316
-
317
- // Copy pixel data to a property on tile canvas element (for later retrieval).
318
- tileCanvas.pixelData = resultEncodedPixels
319
- tileCanvas.pixelDataA = pixelDataA
320
- tileCanvas.pixelDataB = pixelDataB
321
- tileCanvas.pixelDataC = pixelDataC
322
- tileCanvas.pixelDataD = pixelDataD
323
-
324
- // Copy contents to tileCanvas.
325
- this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
326
- })
327
- } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 5) {
328
- Promise.all([
329
- this._fetchTileData(coords, operationUrlA),
330
- this._fetchTileData(coords, operationUrlB),
331
- this._fetchTileData(coords, operationUrlC),
332
- this._fetchTileData(coords, operationUrlD),
333
- this._fetchTileData(coords, operationUrlE)
334
- ]).then((pixelDataArray) => {
335
- const pixelDataA = pixelDataArray[0]
336
- const pixelDataB = pixelDataArray[1]
337
- const pixelDataC = pixelDataArray[2]
338
- const pixelDataD = pixelDataArray[3]
339
- const pixelDataE = pixelDataArray[4]
340
- const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti5(
341
- { coords: coords, pixelData: pixelDataA },
342
- { coords: coords, pixelData: pixelDataB },
343
- { coords: coords, pixelData: pixelDataC },
344
- { coords: coords, pixelData: pixelDataD },
345
- { coords: coords, pixelData: pixelDataE },
346
- filterLowA,
347
- filterHighA,
348
- filterLowB,
349
- filterHighB,
350
- filterLowC,
351
- filterHighC,
352
- filterLowD,
353
- filterHighD,
354
- filterLowE,
355
- filterHighE,
356
- multiplierA,
357
- multiplierB,
358
- multiplierC,
359
- multiplierD,
360
- multiplierE
361
- )
362
-
363
- // Copy pixel data to a property on tile canvas element (for later retrieval).
364
- tileCanvas.pixelData = resultEncodedPixels
365
- tileCanvas.pixelDataA = pixelDataA
366
- tileCanvas.pixelDataB = pixelDataB
367
- tileCanvas.pixelDataC = pixelDataC
368
- tileCanvas.pixelDataD = pixelDataD
369
- tileCanvas.pixelDataE = pixelDataE
370
-
371
- // Copy contents to tileCanvas.
372
- this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
373
- })
374
- } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 6) {
375
- Promise.all([
376
- this._fetchTileData(coords, operationUrlA),
377
- this._fetchTileData(coords, operationUrlB),
378
- this._fetchTileData(coords, operationUrlC),
379
- this._fetchTileData(coords, operationUrlD),
380
- this._fetchTileData(coords, operationUrlE),
381
- this._fetchTileData(coords, operationUrlF)
382
- ]).then((pixelDataArray) => {
383
- const pixelDataA = pixelDataArray[0]
384
- const pixelDataB = pixelDataArray[1]
385
- const pixelDataC = pixelDataArray[2]
386
- const pixelDataD = pixelDataArray[3]
387
- const pixelDataE = pixelDataArray[4]
388
- const pixelDataF = pixelDataArray[5]
389
- const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti6(
390
- { coords: coords, pixelData: pixelDataA },
391
- { coords: coords, pixelData: pixelDataB },
392
- { coords: coords, pixelData: pixelDataC },
393
- { coords: coords, pixelData: pixelDataD },
394
- { coords: coords, pixelData: pixelDataE },
395
- { coords: coords, pixelData: pixelDataF },
396
- filterLowA,
397
- filterHighA,
398
- filterLowB,
399
- filterHighB,
400
- filterLowC,
401
- filterHighC,
402
- filterLowD,
403
- filterHighD,
404
- filterLowE,
405
- filterHighE,
406
- filterLowF,
407
- filterHighF,
408
- multiplierA,
409
- multiplierB,
410
- multiplierC,
411
- multiplierD,
412
- multiplierE,
413
- multiplierF
414
- )
415
-
416
- // Copy pixel data to a property on tile canvas element (for later retrieval).
417
- tileCanvas.pixelData = resultEncodedPixels
418
- tileCanvas.pixelDataA = pixelDataA
419
- tileCanvas.pixelDataB = pixelDataB
420
- tileCanvas.pixelDataC = pixelDataC
421
- tileCanvas.pixelDataD = pixelDataD
422
- tileCanvas.pixelDataE = pixelDataE
423
- tileCanvas.pixelDataF = pixelDataF
424
-
425
- // Copy contents to tileCanvas.
426
- this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
427
- })
428
- }
429
- })
430
-
431
- }
432
-
433
- _checkColorScaleAndSentinels () {
434
- const {
435
- colorScale,
436
- sentinelValues,
437
- colorscaleMaxLength,
438
- sentinelMaxLength
439
- } = this.options
440
- if (colorScale.length === 0 && sentinelValues.length === 0) {
441
- throw new Error('Either `colorScale` or `sentinelValues` must be of non-zero length.')
442
- }
443
- if (colorScale.length > colorscaleMaxLength) {
444
- throw new Error(
445
- `Color scale length ${colorScale.length} exceeds the maximum, ${colorscaleMaxLength}.`
446
- )
447
- }
448
- if (sentinelValues.length > sentinelMaxLength) {
449
- throw new Error(
450
- `Sentinel values length ${sentinelValues.length} exceeds the maximum, ${sentinelMaxLength}.`
451
- )
452
- }
453
- }
454
-
455
- setHillshadeOptions () {
456
- this.options._hillshadeOptions = {
457
- hillshadeType: this.options.hillshadeType,
458
- hsAdvValueScale: this.options.hsAdvValueScale,
459
- hsAdvPixelScale: this.options.hsAdvPixelScale,
460
- hsSimpleSlopescale: this.options.hsSimpleSlopescale,
461
- hsSimpleAzimuth: this.options.hsSimpleAzimuth,
462
- hsSimpleAltitude: this.options.hsSimpleAltitude,
463
- hsSimpleZoomdelta: this.options.hsSimpleZoomdelta,
464
- hsAdvSoftIterations: this.options.hsAdvSoftIterations,
465
- hsAdvAmbientIterations: this.options.hsAdvAmbientIterations,
466
- hsAdvSunRadiusMultiplier: this.options.hsAdvSunRadiusMultiplier,
467
- hsAdvFinalSoftMultiplier: this.options.hsAdvFinalSoftMultiplier,
468
- hsAdvFinalAmbientMultiplier: this.options.hsAdvFinalAmbientMultiplier,
469
- hsAdvBaselayerUrl: this.options.hsAdvBaselayerUrl,
470
- hsAdvSmoothInput: this.options.hsAdvSmoothInput,
471
- hsAdvSmoothInputKernel: this.options.hsAdvSmoothInputKernel,
472
- hsPregenUrl: this.options.hsPregenUrl
473
- }
474
- }
475
-
476
- _tileSizeAsNumber () {
477
- return this.options.tileSize
478
- }
479
-
480
- async _fetchTileData (coords, url, tileFormat = this.options.tileFormat) {
481
- if (tileFormat === 'float32' || tileFormat === 'image') {
482
- return util.fetchPNGData(this.getTileUrl(coords, url), this.options.nodataValue, this._tileSizeAsNumber())
483
- } else if (tileFormat === 'dem') {
484
- const nodataTile = util.createNoDataTile(this.options.nodataValue, this._tileSizeAsNumber())
485
- const imageData = await util.fetchPNGData(this.getTileUrl(coords, url), this.options.nodataValue, this._tileSizeAsNumber())
486
- if (util.typedArraysAreEqual(imageData, nodataTile)) {
487
- return imageData
488
- } else {
489
- const rgbaData = this._renderer.renderConvertDem(imageData)
490
- return rgbaData
491
- }
492
- }
493
- return util.createNoDataTile(this.options.nodataValue, this._tileSizeAsNumber())
494
- }
495
-
496
- _copyToTileCanvas (tile, sourceX, sourceY) {
497
- const tileSize = this._tileSizeAsNumber()
498
- const tileCanvas2DContext = tile.getContext('2d')
499
- if (tileCanvas2DContext === null) {
500
- throw new Error('Tile canvas 2D context is null.')
501
- }
502
- // Clear the current contents of the canvas. Otherwise, the new image will be composited with
503
- // the existing image.
504
- tileCanvas2DContext.clearRect(0, 0, tileSize, tileSize)
505
- // Copy the image data from the Renderer's canvas to the tile's canvas.
506
- tileCanvas2DContext.drawImage(
507
- this._renderer.canvas,
508
- sourceX, sourceY, tileSize, tileSize, // source canvas offset (x, y) and size (x, y)
509
- 0, 0, tileSize, tileSize // destination canvas offset (x, y) and size (x, y)
510
- )
511
- }
512
-
513
- getTileUrl (coords, url) {
514
- return url
515
- .replace(/{z}/g, String(coords.z))
516
- .replace(/{x}/g, String(coords.x))
517
- .replace(/{y}/g, String(coords.y))
518
- }
519
-
520
- _getPixelScale (coords) {
521
- let {bbox: {south, north}, z: zoom} = coords
522
- let pixelScale = 1
523
- let lat = (south + north) / 2
524
- if (this.options.hsAdvPixelScale === 'auto') {
525
- pixelScale = EARTH_CIRCUMFERENCE * Math.abs(
526
- Math.cos(lat / 180 * Math.PI
527
- )) / Math.pow(2, zoom + 8)
528
- } else if (typeof this.options.hsAdvPixelScale === 'number') {
529
- pixelScale = this.options.hsAdvPixelScale / (this._tileSizeAsNumber() * (2 ** zoom))
530
- }
531
- return pixelScale
532
- }
1
+ import defaultOptions from './default_options'
2
+ import * as util from './util'
3
+ import Renderer from './renderer'
4
+ import { EARTH_CIRCUMFERENCE } from './constants'
5
+
6
+ export default class GLOperations {
7
+ constructor (options) {
8
+ this.options = Object.assign({}, defaultOptions, options)
9
+ this._checkColorScaleAndSentinels()
10
+ this.setHillshadeOptions()
11
+ const {
12
+ nodataValue,
13
+ tileSize
14
+ } = this.options
15
+
16
+ this._renderer = new Renderer(
17
+ this,
18
+ tileSize,
19
+ nodataValue,
20
+ this.options.colorScale,
21
+ this.options.sentinelValues,
22
+ this.options.colorscaleMaxLength,
23
+ this.options.sentinelMaxLength,
24
+ this.options.aboveColor,
25
+ this.options.belowColor
26
+ )
27
+
28
+ this._renderer.generateAmbientDirections(this.options.hsAdvAmbientIterations)
29
+ this._renderer.generateSunDirections(
30
+ this.options.hsAdvSoftIterations,
31
+ this.options.hsAdvSunRadiusMultiplier
32
+ )
33
+ }
34
+
35
+ destroy () {
36
+ if (this._renderer) this._renderer.destroy()
37
+ this._renderer = null
38
+ }
39
+
40
+ updateOptions (options) {
41
+ const {
42
+ url: prevUrl,
43
+ operationUrlA: prevUrlA,
44
+ operationUrlB: prevUrlB,
45
+ operationUrlC: prevUrlC,
46
+ operationUrlD: prevUrlD,
47
+ colorScale: prevColorScale,
48
+ sentinelValues: prevSentinelValues,
49
+ hillshadeType: prevHillshadeType,
50
+ hsAdvSoftIterations: prevHsAdvSoftIterations,
51
+ hsAdvAmbientIterations: prevHsAdvAmbientIterations,
52
+ hsAdvSunRadiusMultiplier: prevHsAdvSunRadiusMultiplier,
53
+ hsAdvBaselayerUrl: prevHsAdvBaselayerUrl,
54
+ colorscaleMaxLength: prevScaleMaxLength,
55
+ sentinelMaxLength: prevSentinelMaxLength,
56
+ aboveColor: prevAboveColor,
57
+ belowColor: prevBelowColor
58
+ } = this.options
59
+ Object.assign(this.options, options)
60
+ // create new renderer if max length of sentinels or colorscale changes
61
+ if (this.options.colorscaleMaxLength !== prevScaleMaxLength || this.options.sentinelMaxLength !== prevSentinelMaxLength) {
62
+ const tileSize = this._tileSizeAsNumber()
63
+ const renderer = new Renderer(
64
+ this,
65
+ tileSize,
66
+ this.options.nodataValue,
67
+ this.options.colorScale,
68
+ this.options.sentinelValues,
69
+ this.options.colorscaleMaxLength,
70
+ this.options.sentinelMaxLength
71
+ )
72
+
73
+ this._renderer.regl.destroy()
74
+ // @ts-ignore
75
+ delete this._renderer
76
+
77
+ Object.assign(this, {
78
+ _renderer: renderer
79
+ })
80
+ }
81
+ this._checkColorScaleAndSentinels()
82
+ if (this.options.colorScale !== prevColorScale) {
83
+ this._renderer.updateColorscale(this.options.colorScale)
84
+ }
85
+ if (this.options.aboveColor !== prevAboveColor || this.options.belowColor !== prevBelowColor) {
86
+ this._renderer.updateOuteRangeColor(this.options.aboveColor, this.options.belowColor)
87
+ }
88
+ if (this.options.sentinelValues !== prevSentinelValues) {
89
+ this._renderer.updateSentinels(this.options.sentinelValues)
90
+ }
91
+ if (this.options.hsAdvAmbientIterations !== prevHsAdvAmbientIterations) {
92
+ this._renderer.generateAmbientDirections(this.options.hsAdvAmbientIterations)
93
+ }
94
+ if (
95
+ this.options.hsAdvSoftIterations !== prevHsAdvSoftIterations ||
96
+ this.options.hsAdvSunRadiusMultiplier !== prevHsAdvSunRadiusMultiplier
97
+ ) {
98
+ this._renderer.generateSunDirections(this.options.hsAdvSoftIterations, this.options.hsAdvSunRadiusMultiplier)
99
+ }
100
+ this.setHillshadeOptions()
101
+
102
+ if (this.options.extraPixelLayers > 0 && this.options.glOperation === 'none') {
103
+ this._maybeLoadExtraLayers(prevUrlA, prevUrlB, prevUrlC, prevUrlD)
104
+ }
105
+ // TODO: Fix shader so simple hillshading works ok with larger texture than tileSize
106
+ if (this.options.hillshadeType !== prevHillshadeType) {
107
+ // reduce textureManager size as simple hillshading type currently gets "edges" around the tiles with larger texture.
108
+ if (this.options.hillshadeType === 'simple') {
109
+ this._renderer.setMaxTextureDimension(this._tileSizeAsNumber())
110
+ } else if (prevHillshadeType === 'simple') {
111
+ this._renderer.setMaxTextureDimension(this._renderer.findMaxTextureDimension())
112
+ }
113
+ }
114
+
115
+ if (this.options.url !== prevUrl) {
116
+ // need to clear tiles so they are not reused with adv.hs.
117
+ this._renderer.textureManager.clearTiles()
118
+ }
119
+
120
+ if (this.options.hsAdvBaselayerUrl !== prevHsAdvBaselayerUrl) {
121
+ // need to clear tiles so they are not reused
122
+ // TODO: Check why necessary
123
+ this._renderer.textureManagerHillshade.clearTiles()
124
+ }
125
+ }
126
+
127
+ createTile (coords) {
128
+ const {
129
+ extraPixelLayers,
130
+ tileSize,
131
+ url,
132
+ operationUrlA,
133
+ operationUrlB,
134
+ operationUrlC,
135
+ operationUrlD,
136
+ operationUrlE,
137
+ operationUrlF,
138
+ filterLowA,
139
+ filterHighA,
140
+ filterLowB,
141
+ filterHighB,
142
+ filterLowC,
143
+ filterHighC,
144
+ filterLowD,
145
+ filterHighD,
146
+ filterLowE,
147
+ filterHighE,
148
+ filterLowF,
149
+ filterHighF,
150
+ multiplierA,
151
+ multiplierB,
152
+ multiplierC,
153
+ multiplierD,
154
+ multiplierE,
155
+ multiplierF
156
+ } = this.options
157
+
158
+ // Create a <canvas> element to contain the rendered image.
159
+ const tileCanvas = document.createElement('canvas')
160
+ // Configure the element.
161
+ Object.assign(tileCanvas, {
162
+ className: 'gl-tile',
163
+ width: tileSize,
164
+ height: tileSize
165
+ })
166
+
167
+ return new Promise((resolve) => {
168
+ if (this.options.glOperation === 'none') {
169
+ // Download an extra layer if required
170
+ if (extraPixelLayers === 1) {
171
+ this._fetchTileData(coords, operationUrlA).then((pixelDataA) => {
172
+ tileCanvas.pixelDataA = pixelDataA
173
+ })
174
+ }
175
+
176
+ this._fetchTileData(coords, url).then((pixelData) => {
177
+ let [sourceX, sourceY] = this._renderer.renderTile(
178
+ { coords, pixelData },
179
+ this.options._hillshadeOptions,
180
+ coords.z
181
+ )
182
+
183
+ // Copy pixel data to a property on tile canvas element (for later retrieval).
184
+ tileCanvas.pixelData = pixelData
185
+
186
+ this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
187
+
188
+ resolve(tileCanvas)
189
+ })
190
+ } else if (this.options.glOperation === 'diff') {
191
+ Promise.all([
192
+ this._fetchTileData(coords, operationUrlA),
193
+ this._fetchTileData(coords, operationUrlB)
194
+ ]).then((pixelDataArray) => {
195
+ const pixelDataA = pixelDataArray[0]
196
+ const pixelDataB = pixelDataArray[1]
197
+ const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileDiff(
198
+ { coords: coords, pixelData: pixelDataA },
199
+ { coords: coords, pixelData: pixelDataB }
200
+ )
201
+
202
+ tileCanvas.pixelData = resultEncodedPixels
203
+ tileCanvas.pixelDataA = pixelDataA
204
+ tileCanvas.pixelDataB = pixelDataB
205
+
206
+ // Copy contents to tileCanvas.
207
+ this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
208
+ })
209
+ } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 1) {
210
+ Promise.all([
211
+ this._fetchTileData(coords, operationUrlA)
212
+ ]).then((pixelDataArray) => {
213
+ const pixelDataA = pixelDataArray[0]
214
+ const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti1(
215
+ { coords: coords, pixelData: pixelDataA },
216
+ filterLowA,
217
+ filterHighA,
218
+ multiplierA
219
+ )
220
+
221
+ // Copy pixel data to a property on tile canvas element (for later retrieval).
222
+ tileCanvas.pixelData = resultEncodedPixels
223
+ tileCanvas.pixelDataA = pixelDataA
224
+
225
+ // Copy contents to tileCanvas.
226
+ this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
227
+ })
228
+ } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 2) {
229
+ Promise.all([
230
+ this._fetchTileData(coords, operationUrlA),
231
+ this._fetchTileData(coords, operationUrlB)
232
+ ]).then((pixelDataArray) => {
233
+ const pixelDataA = pixelDataArray[0]
234
+ const pixelDataB = pixelDataArray[1]
235
+ const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti2(
236
+ { coords: coords, pixelData: pixelDataA },
237
+ { coords: coords, pixelData: pixelDataB },
238
+ filterLowA,
239
+ filterHighA,
240
+ filterLowB,
241
+ filterHighB,
242
+ multiplierA,
243
+ multiplierB
244
+ )
245
+
246
+ // Copy pixel data to a property on tile canvas element (for later retrieval).
247
+ tileCanvas.pixelData = resultEncodedPixels
248
+ tileCanvas.pixelDataA = pixelDataA
249
+ tileCanvas.pixelDataB = pixelDataB
250
+
251
+ // Copy contents to tileCanvas.
252
+ this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
253
+ })
254
+ } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 3) {
255
+ Promise.all([
256
+ this._fetchTileData(coords, operationUrlA),
257
+ this._fetchTileData(coords, operationUrlB),
258
+ this._fetchTileData(coords, operationUrlC)
259
+ ]).then((pixelDataArray) => {
260
+ const pixelDataA = pixelDataArray[0]
261
+ const pixelDataB = pixelDataArray[1]
262
+ const pixelDataC = pixelDataArray[2]
263
+ const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti3(
264
+ { coords: coords, pixelData: pixelDataA },
265
+ { coords: coords, pixelData: pixelDataB },
266
+ { coords: coords, pixelData: pixelDataC },
267
+ filterLowA,
268
+ filterHighA,
269
+ filterLowB,
270
+ filterHighB,
271
+ filterLowC,
272
+ filterHighC,
273
+ multiplierA,
274
+ multiplierB,
275
+ multiplierC
276
+ )
277
+
278
+ // Copy pixel data to a property on tile canvas element (for later retrieval).
279
+ tileCanvas.pixelData = resultEncodedPixels
280
+ tileCanvas.pixelDataA = pixelDataA
281
+ tileCanvas.pixelDataB = pixelDataB
282
+ tileCanvas.pixelDataC = pixelDataC
283
+
284
+ // Copy contents to tileCanvas.
285
+ this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
286
+ })
287
+ } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 4) {
288
+ Promise.all([
289
+ this._fetchTileData(coords, operationUrlA),
290
+ this._fetchTileData(coords, operationUrlB),
291
+ this._fetchTileData(coords, operationUrlC),
292
+ this._fetchTileData(coords, operationUrlD)
293
+ ]).then((pixelDataArray) => {
294
+ const pixelDataA = pixelDataArray[0]
295
+ const pixelDataB = pixelDataArray[1]
296
+ const pixelDataC = pixelDataArray[2]
297
+ const pixelDataD = pixelDataArray[3]
298
+ const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti4(
299
+ { coords: coords, pixelData: pixelDataA },
300
+ { coords: coords, pixelData: pixelDataB },
301
+ { coords: coords, pixelData: pixelDataC },
302
+ { coords: coords, pixelData: pixelDataD },
303
+ filterLowA,
304
+ filterHighA,
305
+ filterLowB,
306
+ filterHighB,
307
+ filterLowC,
308
+ filterHighC,
309
+ filterLowD,
310
+ filterHighD,
311
+ multiplierA,
312
+ multiplierB,
313
+ multiplierC,
314
+ multiplierD
315
+ )
316
+
317
+ // Copy pixel data to a property on tile canvas element (for later retrieval).
318
+ tileCanvas.pixelData = resultEncodedPixels
319
+ tileCanvas.pixelDataA = pixelDataA
320
+ tileCanvas.pixelDataB = pixelDataB
321
+ tileCanvas.pixelDataC = pixelDataC
322
+ tileCanvas.pixelDataD = pixelDataD
323
+
324
+ // Copy contents to tileCanvas.
325
+ this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
326
+ })
327
+ } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 5) {
328
+ Promise.all([
329
+ this._fetchTileData(coords, operationUrlA),
330
+ this._fetchTileData(coords, operationUrlB),
331
+ this._fetchTileData(coords, operationUrlC),
332
+ this._fetchTileData(coords, operationUrlD),
333
+ this._fetchTileData(coords, operationUrlE)
334
+ ]).then((pixelDataArray) => {
335
+ const pixelDataA = pixelDataArray[0]
336
+ const pixelDataB = pixelDataArray[1]
337
+ const pixelDataC = pixelDataArray[2]
338
+ const pixelDataD = pixelDataArray[3]
339
+ const pixelDataE = pixelDataArray[4]
340
+ const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti5(
341
+ { coords: coords, pixelData: pixelDataA },
342
+ { coords: coords, pixelData: pixelDataB },
343
+ { coords: coords, pixelData: pixelDataC },
344
+ { coords: coords, pixelData: pixelDataD },
345
+ { coords: coords, pixelData: pixelDataE },
346
+ filterLowA,
347
+ filterHighA,
348
+ filterLowB,
349
+ filterHighB,
350
+ filterLowC,
351
+ filterHighC,
352
+ filterLowD,
353
+ filterHighD,
354
+ filterLowE,
355
+ filterHighE,
356
+ multiplierA,
357
+ multiplierB,
358
+ multiplierC,
359
+ multiplierD,
360
+ multiplierE
361
+ )
362
+
363
+ // Copy pixel data to a property on tile canvas element (for later retrieval).
364
+ tileCanvas.pixelData = resultEncodedPixels
365
+ tileCanvas.pixelDataA = pixelDataA
366
+ tileCanvas.pixelDataB = pixelDataB
367
+ tileCanvas.pixelDataC = pixelDataC
368
+ tileCanvas.pixelDataD = pixelDataD
369
+ tileCanvas.pixelDataE = pixelDataE
370
+
371
+ // Copy contents to tileCanvas.
372
+ this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
373
+ })
374
+ } else if (this.options.glOperation === 'multi' && this.options.multiLayers === 6) {
375
+ Promise.all([
376
+ this._fetchTileData(coords, operationUrlA),
377
+ this._fetchTileData(coords, operationUrlB),
378
+ this._fetchTileData(coords, operationUrlC),
379
+ this._fetchTileData(coords, operationUrlD),
380
+ this._fetchTileData(coords, operationUrlE),
381
+ this._fetchTileData(coords, operationUrlF)
382
+ ]).then((pixelDataArray) => {
383
+ const pixelDataA = pixelDataArray[0]
384
+ const pixelDataB = pixelDataArray[1]
385
+ const pixelDataC = pixelDataArray[2]
386
+ const pixelDataD = pixelDataArray[3]
387
+ const pixelDataE = pixelDataArray[4]
388
+ const pixelDataF = pixelDataArray[5]
389
+ const [sourceX, sourceY, resultEncodedPixels] = this._renderer.renderTileMulti6(
390
+ { coords: coords, pixelData: pixelDataA },
391
+ { coords: coords, pixelData: pixelDataB },
392
+ { coords: coords, pixelData: pixelDataC },
393
+ { coords: coords, pixelData: pixelDataD },
394
+ { coords: coords, pixelData: pixelDataE },
395
+ { coords: coords, pixelData: pixelDataF },
396
+ filterLowA,
397
+ filterHighA,
398
+ filterLowB,
399
+ filterHighB,
400
+ filterLowC,
401
+ filterHighC,
402
+ filterLowD,
403
+ filterHighD,
404
+ filterLowE,
405
+ filterHighE,
406
+ filterLowF,
407
+ filterHighF,
408
+ multiplierA,
409
+ multiplierB,
410
+ multiplierC,
411
+ multiplierD,
412
+ multiplierE,
413
+ multiplierF
414
+ )
415
+
416
+ // Copy pixel data to a property on tile canvas element (for later retrieval).
417
+ tileCanvas.pixelData = resultEncodedPixels
418
+ tileCanvas.pixelDataA = pixelDataA
419
+ tileCanvas.pixelDataB = pixelDataB
420
+ tileCanvas.pixelDataC = pixelDataC
421
+ tileCanvas.pixelDataD = pixelDataD
422
+ tileCanvas.pixelDataE = pixelDataE
423
+ tileCanvas.pixelDataF = pixelDataF
424
+
425
+ // Copy contents to tileCanvas.
426
+ this._copyToTileCanvas(tileCanvas, sourceX, sourceY)
427
+ })
428
+ }
429
+ })
430
+
431
+ }
432
+
433
+ _checkColorScaleAndSentinels () {
434
+ const {
435
+ colorScale,
436
+ sentinelValues,
437
+ colorscaleMaxLength,
438
+ sentinelMaxLength
439
+ } = this.options
440
+ if (colorScale.length === 0 && sentinelValues.length === 0) {
441
+ throw new Error('Either `colorScale` or `sentinelValues` must be of non-zero length.')
442
+ }
443
+ if (colorScale.length > colorscaleMaxLength) {
444
+ throw new Error(
445
+ `Color scale length ${colorScale.length} exceeds the maximum, ${colorscaleMaxLength}.`
446
+ )
447
+ }
448
+ if (sentinelValues.length > sentinelMaxLength) {
449
+ throw new Error(
450
+ `Sentinel values length ${sentinelValues.length} exceeds the maximum, ${sentinelMaxLength}.`
451
+ )
452
+ }
453
+ }
454
+
455
+ setHillshadeOptions () {
456
+ this.options._hillshadeOptions = {
457
+ hillshadeType: this.options.hillshadeType,
458
+ hsAdvValueScale: this.options.hsAdvValueScale,
459
+ hsAdvPixelScale: this.options.hsAdvPixelScale,
460
+ hsSimpleSlopescale: this.options.hsSimpleSlopescale,
461
+ hsSimpleAzimuth: this.options.hsSimpleAzimuth,
462
+ hsSimpleAltitude: this.options.hsSimpleAltitude,
463
+ hsSimpleZoomdelta: this.options.hsSimpleZoomdelta,
464
+ hsAdvSoftIterations: this.options.hsAdvSoftIterations,
465
+ hsAdvAmbientIterations: this.options.hsAdvAmbientIterations,
466
+ hsAdvSunRadiusMultiplier: this.options.hsAdvSunRadiusMultiplier,
467
+ hsAdvFinalSoftMultiplier: this.options.hsAdvFinalSoftMultiplier,
468
+ hsAdvFinalAmbientMultiplier: this.options.hsAdvFinalAmbientMultiplier,
469
+ hsAdvBaselayerUrl: this.options.hsAdvBaselayerUrl,
470
+ hsAdvSmoothInput: this.options.hsAdvSmoothInput,
471
+ hsAdvSmoothInputKernel: this.options.hsAdvSmoothInputKernel,
472
+ hsPregenUrl: this.options.hsPregenUrl
473
+ }
474
+ }
475
+
476
+ _tileSizeAsNumber () {
477
+ return this.options.tileSize
478
+ }
479
+
480
+ async _fetchTileData (coords, url, tileFormat = this.options.tileFormat) {
481
+ if (tileFormat === 'float32' || tileFormat === 'image') {
482
+ return util.fetchPNGData(this.getTileUrl(coords, url), this.options.nodataValue, this._tileSizeAsNumber())
483
+ } else if (tileFormat === 'dem') {
484
+ const nodataTile = util.createNoDataTile(this.options.nodataValue, this._tileSizeAsNumber())
485
+ const imageData = await util.fetchPNGData(this.getTileUrl(coords, url), this.options.nodataValue, this._tileSizeAsNumber())
486
+ if (util.typedArraysAreEqual(imageData, nodataTile)) {
487
+ return imageData
488
+ } else {
489
+ const rgbaData = this._renderer.renderConvertDem(imageData)
490
+ return rgbaData
491
+ }
492
+ }
493
+ return util.createNoDataTile(this.options.nodataValue, this._tileSizeAsNumber())
494
+ }
495
+
496
+ _copyToTileCanvas (tile, sourceX, sourceY) {
497
+ const tileSize = this._tileSizeAsNumber()
498
+ const tileCanvas2DContext = tile.getContext('2d')
499
+ if (tileCanvas2DContext === null) {
500
+ throw new Error('Tile canvas 2D context is null.')
501
+ }
502
+ // Clear the current contents of the canvas. Otherwise, the new image will be composited with
503
+ // the existing image.
504
+ tileCanvas2DContext.clearRect(0, 0, tileSize, tileSize)
505
+ // Copy the image data from the Renderer's canvas to the tile's canvas.
506
+ tileCanvas2DContext.drawImage(
507
+ this._renderer.canvas,
508
+ sourceX, sourceY, tileSize, tileSize, // source canvas offset (x, y) and size (x, y)
509
+ 0, 0, tileSize, tileSize // destination canvas offset (x, y) and size (x, y)
510
+ )
511
+ }
512
+
513
+ getTileUrl (coords, url) {
514
+ return url
515
+ .replace(/{z}/g, String(coords.z))
516
+ .replace(/{x}/g, String(coords.x))
517
+ .replace(/{y}/g, String(coords.y))
518
+ }
519
+
520
+ _getPixelScale (coords) {
521
+ let {bbox: {south, north}, z: zoom} = coords
522
+ let pixelScale = 1
523
+ let lat = (south + north) / 2
524
+ if (this.options.hsAdvPixelScale === 'auto') {
525
+ pixelScale = EARTH_CIRCUMFERENCE * Math.abs(
526
+ Math.cos(lat / 180 * Math.PI
527
+ )) / Math.pow(2, zoom + 8)
528
+ } else if (typeof this.options.hsAdvPixelScale === 'number') {
529
+ pixelScale = this.options.hsAdvPixelScale / (this._tileSizeAsNumber() * (2 ** zoom))
530
+ }
531
+ return pixelScale
532
+ }
533
533
  }