@mapcatch/util 2.0.3 → 2.0.5-a

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