@mapcatch/util 2.0.4 → 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.
- package/.eslintrc.js +54 -0
- package/.husky/pre-commit +1 -0
- package/.prettierrc +4 -0
- package/.vscode/settings.json +2 -0
- package/README.md +44 -0
- package/debug/app.js +26 -0
- package/debug/index.html +55 -0
- package/debug/libs/vue.global.js +16159 -0
- package/debug/my_icon.png +0 -0
- package/docs/Catolog.md +24 -0
- package/docs/Constant.md +92 -0
- package/docs/Event.md +90 -0
- package/docs/Util.md +345 -0
- package/package.json +21 -1
- package/src/constants/crs.js +42098 -42098
- package/src/constants/default_layers.js +42 -94
- package/src/gl-operations/constants.js +9 -9
- package/src/gl-operations/default_options.js +97 -97
- package/src/gl-operations/index.js +532 -532
- package/src/gl-operations/reglCommands/contours.js +27 -27
- package/src/gl-operations/reglCommands/default.js +45 -45
- package/src/gl-operations/reglCommands/hillshading.js +340 -340
- package/src/gl-operations/reglCommands/index.js +6 -6
- package/src/gl-operations/reglCommands/multiLayers.js +303 -303
- package/src/gl-operations/reglCommands/transitions.js +111 -111
- package/src/gl-operations/reglCommands/util.js +71 -71
- package/src/gl-operations/renderer.js +209 -209
- package/src/gl-operations/shaders/fragment/convertDem.js +25 -25
- package/src/gl-operations/shaders/fragment/convolutionSmooth.js +54 -54
- package/src/gl-operations/shaders/fragment/diffCalc.js +33 -33
- package/src/gl-operations/shaders/fragment/drawResult.js +46 -46
- package/src/gl-operations/shaders/fragment/hillshading/hsAdvAmbientShadows.js +78 -78
- package/src/gl-operations/shaders/fragment/hillshading/hsAdvDirect.js +59 -59
- package/src/gl-operations/shaders/fragment/hillshading/hsAdvFinalBaselayer.js +30 -30
- package/src/gl-operations/shaders/fragment/hillshading/hsAdvFinalColorscale.js +60 -60
- package/src/gl-operations/shaders/fragment/hillshading/hsAdvMergeAndScaleTiles.js +26 -26
- package/src/gl-operations/shaders/fragment/hillshading/hsAdvNormals.js +25 -25
- package/src/gl-operations/shaders/fragment/hillshading/hsAdvSmooth.js +53 -53
- package/src/gl-operations/shaders/fragment/hillshading/hsAdvSoftShadows.js +80 -80
- package/src/gl-operations/shaders/fragment/hillshading/hsPregen.js +54 -54
- package/src/gl-operations/shaders/fragment/interpolateColor.js +65 -65
- package/src/gl-operations/shaders/fragment/interpolateColorOnly.js +49 -49
- package/src/gl-operations/shaders/fragment/interpolateValue.js +136 -136
- package/src/gl-operations/shaders/fragment/multiAnalyze1Calc.js +35 -35
- package/src/gl-operations/shaders/fragment/multiAnalyze2Calc.js +45 -45
- package/src/gl-operations/shaders/fragment/multiAnalyze3Calc.js +53 -53
- package/src/gl-operations/shaders/fragment/multiAnalyze4Calc.js +61 -61
- package/src/gl-operations/shaders/fragment/multiAnalyze5Calc.js +69 -69
- package/src/gl-operations/shaders/fragment/multiAnalyze6Calc.js +77 -77
- package/src/gl-operations/shaders/fragment/single.js +93 -93
- package/src/gl-operations/shaders/transform.js +21 -21
- package/src/gl-operations/shaders/util/computeColor.glsl +85 -85
- package/src/gl-operations/shaders/util/getTexelValue.glsl +10 -10
- package/src/gl-operations/shaders/util/isCloseEnough.glsl +9 -9
- package/src/gl-operations/shaders/util/rgbaToFloat.glsl +17 -17
- package/src/gl-operations/shaders/vertex/double.js +16 -16
- package/src/gl-operations/shaders/vertex/multi3.js +19 -19
- package/src/gl-operations/shaders/vertex/multi4.js +22 -22
- package/src/gl-operations/shaders/vertex/multi5.js +25 -25
- package/src/gl-operations/shaders/vertex/multi6.js +28 -28
- package/src/gl-operations/shaders/vertex/single.js +13 -13
- package/src/gl-operations/shaders/vertex/singleNotTransformed.js +11 -11
- package/src/gl-operations/texture_manager.js +141 -141
- package/src/gl-operations/util.js +336 -336
- package/src/util.js +14 -6
- package/vite.config.js +58 -0
- package/dist/catchUtil.min.esm.js +0 -113044
- package/dist/catchUtil.min.js +0 -2928
|
@@ -1,336 +1,336 @@
|
|
|
1
|
-
import { memoize } from 'lodash'
|
|
2
|
-
import { decode, toRGBA8 } from 'upng-js'
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
RGB_REGEX,
|
|
6
|
-
HEX_REGEX
|
|
7
|
-
} from './constants'
|
|
8
|
-
|
|
9
|
-
export function machineIsLittleEndian () {
|
|
10
|
-
const uint8Array = new Uint8Array([0xAA, 0xBB])
|
|
11
|
-
const uint16array = new Uint16Array(uint8Array.buffer)
|
|
12
|
-
return uint16array[0] === 0xBBAA
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Cribbed from Python's built-in `range` function.
|
|
17
|
-
*/
|
|
18
|
-
export function range (...args) {
|
|
19
|
-
if (args.length === 1) {
|
|
20
|
-
const [until] = args
|
|
21
|
-
return new Array(until).fill(undefined).map((_, i) => i)
|
|
22
|
-
} else {
|
|
23
|
-
const [from, until, step = 1] = args
|
|
24
|
-
if (step === 0) {
|
|
25
|
-
throw new Error('Argument step must be nonzero.')
|
|
26
|
-
}
|
|
27
|
-
const output = []
|
|
28
|
-
for (let val = from; (step > 0) ? val < until : val > until; val += step) {
|
|
29
|
-
output.push(val)
|
|
30
|
-
}
|
|
31
|
-
return output
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Fetch a png and decode data. If png does not exist return an array with nodataValue.
|
|
37
|
-
*/
|
|
38
|
-
export async function fetchPNGData (url, nodataValue, tileDimension) {
|
|
39
|
-
return new Promise((resolve, reject) => {
|
|
40
|
-
const xhr = new XMLHttpRequest()
|
|
41
|
-
xhr.open('GET', url, true)
|
|
42
|
-
xhr.responseType = 'arraybuffer'
|
|
43
|
-
xhr.addEventListener('load', () => {
|
|
44
|
-
resolve(xhr.response)
|
|
45
|
-
})
|
|
46
|
-
xhr.addEventListener('error', reject)
|
|
47
|
-
xhr.send(null)
|
|
48
|
-
}).then((data) => {
|
|
49
|
-
const img = decode(data)
|
|
50
|
-
const rgba = toRGBA8(img)[0]
|
|
51
|
-
return new Uint8Array(rgba)
|
|
52
|
-
}).catch(() => createNoDataTile(nodataValue, tileDimension))
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check if two TypedArrays are equal
|
|
57
|
-
*/
|
|
58
|
-
export function typedArraysAreEqual (a, b) {
|
|
59
|
-
if (a.byteLength !== b.byteLength) return false
|
|
60
|
-
return a.every((val, i) => val === b[i])
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* The matrix output by this function transforms coordinates in pixel space within the drawing
|
|
65
|
-
* buffer (with upper left corner (0, 0) and lower right corner (buffer width, buffer height))
|
|
66
|
-
* to WebGL "clipspace", with upper left corner (-1, 1) and lower right corner (1, -1).
|
|
67
|
-
*/
|
|
68
|
-
export function getTransformMatrix (drawingBufferWidth, drawingBufferHeight) {
|
|
69
|
-
// To scale horizontally, divide by width (in pixels) and multiply by 2, because width is 2 in clipspace.
|
|
70
|
-
const sx = 2 / drawingBufferWidth
|
|
71
|
-
// To scale vertically, divide by height (in pixels) and multiply by -2, because height is 2 in clipspace,
|
|
72
|
-
// and the direction is flipped (positive is up, negative is down).
|
|
73
|
-
const sy = -2 / drawingBufferHeight
|
|
74
|
-
// We translate by -1 horizontally (so the range 0 to 2 maps to the range -1 to 1).
|
|
75
|
-
const tx = -1
|
|
76
|
-
// We translate by 1 horizontally (so the range -2 to 0 maps to the range -1 to 1).
|
|
77
|
-
const ty = 1
|
|
78
|
-
// Matrix must be in column-major order for WebGL.
|
|
79
|
-
return [
|
|
80
|
-
sx, 0, 0, 0,
|
|
81
|
-
0, sy, 0, 0,
|
|
82
|
-
0, 0, 1, 0,
|
|
83
|
-
tx, ty, 0, 1
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* From a TextureBounds object, this function generates the four vertices WebGL needs to draw the
|
|
89
|
-
* corresponding rectangle (as two conjoined triangles generated with the triangle strip primitive).
|
|
90
|
-
*/
|
|
91
|
-
export function getTexCoordVerticesTriangleStripQuad (textureBounds) {
|
|
92
|
-
const [{ x: left, y: top }, { x: right, y: bottom }] = textureBounds
|
|
93
|
-
return [
|
|
94
|
-
[left, top ],
|
|
95
|
-
[right, top ],
|
|
96
|
-
[left, bottom],
|
|
97
|
-
[right, bottom]
|
|
98
|
-
]
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* From a TextureBounds object, this function generates the six vertices WebGL needs to draw the
|
|
103
|
-
* corresponding rectangle (as two triangles).
|
|
104
|
-
*/
|
|
105
|
-
export function getTexCoordVerticesTriangleQuad (textureBounds) {
|
|
106
|
-
const [{ x: left, y: top }, { x: right, y: bottom }] = textureBounds
|
|
107
|
-
return [
|
|
108
|
-
[left, top ],
|
|
109
|
-
[right, top ],
|
|
110
|
-
[left, bottom],
|
|
111
|
-
[right, bottom],
|
|
112
|
-
[right, top ],
|
|
113
|
-
[left, bottom]
|
|
114
|
-
]
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Produces a Promise that resolves when the desired `duration` has expired.
|
|
119
|
-
*/
|
|
120
|
-
export function Timer (duration) {
|
|
121
|
-
return new Promise((resolve) => setTimeout(resolve, duration))
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Useful for sorting TileCoordinates objects.
|
|
126
|
-
*/
|
|
127
|
-
export function compareTileCoordinates (a, b) {
|
|
128
|
-
const z = a.z - b.z
|
|
129
|
-
const x = a.x - b.x
|
|
130
|
-
const y = a.y - b.y
|
|
131
|
-
if (z !== 0) {
|
|
132
|
-
// First compare z values.
|
|
133
|
-
return z
|
|
134
|
-
} else if (x !== 0) {
|
|
135
|
-
// If z values are the same, compare x values.
|
|
136
|
-
return x
|
|
137
|
-
} else {
|
|
138
|
-
// If x values are the same, compare y values.
|
|
139
|
-
return y
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Determines whether two arrays of TileCoordinates are the same.
|
|
145
|
-
*/
|
|
146
|
-
export function sameTiles (a, b) {
|
|
147
|
-
return (
|
|
148
|
-
// arrays are of the same length
|
|
149
|
-
a.length === b.length
|
|
150
|
-
// and corresponding elements have the same tile coordinates
|
|
151
|
-
&& a.every((tileA, index) => compareTileCoordinates(tileA, b[index]) === 0)
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export const createNoDataTile = memoize((nodataValue, tileDimension = 256) => {
|
|
156
|
-
// Create a float 32 array.
|
|
157
|
-
const float32Tile = new Float32Array(tileDimension * tileDimension)
|
|
158
|
-
// Fill the tile array with the no data value
|
|
159
|
-
float32Tile.fill(nodataValue)
|
|
160
|
-
// return the no data tile.
|
|
161
|
-
return new Uint8Array(float32Tile.buffer)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Force TypeScript to interpret value `val` as type `T`.
|
|
166
|
-
*/
|
|
167
|
-
export function staticCast (val) {
|
|
168
|
-
return val
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Add one or more macro definitions to a GLSL source string.
|
|
173
|
-
*/
|
|
174
|
-
export function defineMacros (src, macros) {
|
|
175
|
-
const defs = Object.keys(macros).map((key) => `#define ${key} ${macros[key]}\n`).join('')
|
|
176
|
-
return `${defs}\n${src}`
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Ping-pong technique. Render to a destination framebuffer,
|
|
181
|
-
* then use it as a source texture in our next iteration.
|
|
182
|
-
* Then swap them and continue. Used for advanced hillshading.
|
|
183
|
-
*/
|
|
184
|
-
export function PingPong (regl, opts) {
|
|
185
|
-
const fbos = [regl.framebuffer(opts), regl.framebuffer(opts)]
|
|
186
|
-
|
|
187
|
-
let index = 0
|
|
188
|
-
|
|
189
|
-
function ping () {
|
|
190
|
-
return fbos[index]
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function pong () {
|
|
194
|
-
return fbos[1 - index]
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function swap () {
|
|
198
|
-
index = 1 - index
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function destroy () {
|
|
202
|
-
fbos[0].destroy()
|
|
203
|
-
fbos[1].destroy()
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
ping,
|
|
208
|
-
pong,
|
|
209
|
-
swap,
|
|
210
|
-
destroy
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* hexToRGB converts a color from hex format to rgba.
|
|
216
|
-
* const [r, g, b, a] = hexToRGB("#ffeeaaff")
|
|
217
|
-
*/
|
|
218
|
-
export const hexToRGB = (hex) => {
|
|
219
|
-
const hasAlpha = hex.length === 9
|
|
220
|
-
const start = hasAlpha ? 24 : 16
|
|
221
|
-
const bigint = parseInt(hex.slice(1), 16)
|
|
222
|
-
const r = (bigint >> start) & 255
|
|
223
|
-
const g = (bigint >> (start - 8)) & 255
|
|
224
|
-
const b = (bigint >> (start - 16)) & 255
|
|
225
|
-
const a = hasAlpha ? (bigint >> (start - 24)) & 255 : 255
|
|
226
|
-
return [r, g, b, a]
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Parses a color string of the form 'rgb({rVal}, {gVal}, {bVal})' and converts the resulting values
|
|
231
|
-
* to an array with ints 0 - 255.
|
|
232
|
-
*/
|
|
233
|
-
export function colorStringToInts (colorstring) {
|
|
234
|
-
if (colorstring === 'transparent') {
|
|
235
|
-
return [0, 0, 0, 0]
|
|
236
|
-
}
|
|
237
|
-
const rgbmatch = colorstring.match(RGB_REGEX)
|
|
238
|
-
const hexmatch = colorstring.match(HEX_REGEX)
|
|
239
|
-
if (rgbmatch !== null) {
|
|
240
|
-
const [, r, g, b] = rgbmatch
|
|
241
|
-
return [+r, +g, +b, 255]
|
|
242
|
-
} else if (hexmatch !== null) {
|
|
243
|
-
return hexToRGB(colorstring)
|
|
244
|
-
} else {
|
|
245
|
-
throw new Error(`'${colorstring}' is not a valid RGB or hex color expression.`)
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* colormapToFlatArray takes the input colormap and returns a flat array to be
|
|
251
|
-
* used as input to a texture. The first row in the array contains the colors.
|
|
252
|
-
* The second row contains the encoded offset values.
|
|
253
|
-
*/
|
|
254
|
-
export const colormapToFlatArray = (colormap) => {
|
|
255
|
-
const offsets = []
|
|
256
|
-
let colors = []
|
|
257
|
-
for (let i = 0; i < colormap.length; i++) {
|
|
258
|
-
offsets.push(colormap[i].offset)
|
|
259
|
-
const colorsnew = colorStringToInts(colormap[i].color)
|
|
260
|
-
colors = colors.concat(colorsnew)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const floatOffsets = new Float32Array(offsets)
|
|
264
|
-
const uintOffsets = new Uint8Array(floatOffsets.buffer)
|
|
265
|
-
const normalOffsets = Array.from(uintOffsets)
|
|
266
|
-
const colormapArray = colors.concat(normalOffsets)
|
|
267
|
-
|
|
268
|
-
return colormapArray
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Creates a texture with colors on first row and offsets on second row
|
|
273
|
-
*/
|
|
274
|
-
export function createColormapTexture (colormapInput, regl) {
|
|
275
|
-
const colormapFlatArray = colormapToFlatArray(colormapInput)
|
|
276
|
-
let colormapTexture
|
|
277
|
-
if (colormapInput.length === 0) {
|
|
278
|
-
// empty texture
|
|
279
|
-
colormapTexture = regl.texture({
|
|
280
|
-
shape: [2, 2]
|
|
281
|
-
})
|
|
282
|
-
} else {
|
|
283
|
-
colormapTexture = regl.texture({
|
|
284
|
-
width: colormapInput.length,
|
|
285
|
-
height: 2,
|
|
286
|
-
data: colormapFlatArray
|
|
287
|
-
})
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return colormapTexture
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Fetch 8 adjacent tiles, if not already existing in tileManager.
|
|
295
|
-
* Return array with texture coord vertices for all tiles.
|
|
296
|
-
*/
|
|
297
|
-
export async function getAdjacentTilesTexCoords (gloperations, textureManager, coords, url) {
|
|
298
|
-
// Get existing tiles in TextureManager
|
|
299
|
-
const textureContents = textureManager.contents
|
|
300
|
-
|
|
301
|
-
// use 3x3 tiles for adv. hillshading
|
|
302
|
-
// TODO: add as plugin option?
|
|
303
|
-
const adjacentTiles = 3
|
|
304
|
-
let textureCoords = []
|
|
305
|
-
|
|
306
|
-
for (let i = 0; i < adjacentTiles; i++) {
|
|
307
|
-
const _x = coords['x'] + (i - 1)
|
|
308
|
-
for (let j = 0; j < adjacentTiles; j++) {
|
|
309
|
-
const _y = coords['y'] + (j - 1)
|
|
310
|
-
const coordsAdjacent = {
|
|
311
|
-
x: _x,
|
|
312
|
-
y: _y,
|
|
313
|
-
z: coords['z']
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Fetch data for adjacent tile if not already existing in TextureManager
|
|
317
|
-
const hashKey = textureManager.hashTileCoordinates(coordsAdjacent)
|
|
318
|
-
if (!textureContents.has(hashKey)) {
|
|
319
|
-
// Retrieve and add data to TextureManager
|
|
320
|
-
const pixelDataAdjacent = await gloperations._fetchTileData(coordsAdjacent, url)
|
|
321
|
-
const textureBounds = gloperations._renderer.textureManager.addTile(coordsAdjacent, pixelDataAdjacent)
|
|
322
|
-
textureCoords = textureCoords.concat(getTexCoordVerticesTriangleQuad(textureBounds))
|
|
323
|
-
} else {
|
|
324
|
-
const textureBounds = gloperations._renderer.textureManager.getTextureCoordinates(coordsAdjacent)
|
|
325
|
-
textureCoords = textureCoords.concat(getTexCoordVerticesTriangleQuad(textureBounds))
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
return textureCoords
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
export function delay (ms) {
|
|
333
|
-
return new Promise(function (resolve) {
|
|
334
|
-
setTimeout(resolve, ms)
|
|
335
|
-
})
|
|
336
|
-
}
|
|
1
|
+
import { memoize } from 'lodash'
|
|
2
|
+
import { decode, toRGBA8 } from 'upng-js'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
RGB_REGEX,
|
|
6
|
+
HEX_REGEX
|
|
7
|
+
} from './constants'
|
|
8
|
+
|
|
9
|
+
export function machineIsLittleEndian () {
|
|
10
|
+
const uint8Array = new Uint8Array([0xAA, 0xBB])
|
|
11
|
+
const uint16array = new Uint16Array(uint8Array.buffer)
|
|
12
|
+
return uint16array[0] === 0xBBAA
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Cribbed from Python's built-in `range` function.
|
|
17
|
+
*/
|
|
18
|
+
export function range (...args) {
|
|
19
|
+
if (args.length === 1) {
|
|
20
|
+
const [until] = args
|
|
21
|
+
return new Array(until).fill(undefined).map((_, i) => i)
|
|
22
|
+
} else {
|
|
23
|
+
const [from, until, step = 1] = args
|
|
24
|
+
if (step === 0) {
|
|
25
|
+
throw new Error('Argument step must be nonzero.')
|
|
26
|
+
}
|
|
27
|
+
const output = []
|
|
28
|
+
for (let val = from; (step > 0) ? val < until : val > until; val += step) {
|
|
29
|
+
output.push(val)
|
|
30
|
+
}
|
|
31
|
+
return output
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Fetch a png and decode data. If png does not exist return an array with nodataValue.
|
|
37
|
+
*/
|
|
38
|
+
export async function fetchPNGData (url, nodataValue, tileDimension) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const xhr = new XMLHttpRequest()
|
|
41
|
+
xhr.open('GET', url, true)
|
|
42
|
+
xhr.responseType = 'arraybuffer'
|
|
43
|
+
xhr.addEventListener('load', () => {
|
|
44
|
+
resolve(xhr.response)
|
|
45
|
+
})
|
|
46
|
+
xhr.addEventListener('error', reject)
|
|
47
|
+
xhr.send(null)
|
|
48
|
+
}).then((data) => {
|
|
49
|
+
const img = decode(data)
|
|
50
|
+
const rgba = toRGBA8(img)[0]
|
|
51
|
+
return new Uint8Array(rgba)
|
|
52
|
+
}).catch(() => createNoDataTile(nodataValue, tileDimension))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if two TypedArrays are equal
|
|
57
|
+
*/
|
|
58
|
+
export function typedArraysAreEqual (a, b) {
|
|
59
|
+
if (a.byteLength !== b.byteLength) return false
|
|
60
|
+
return a.every((val, i) => val === b[i])
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The matrix output by this function transforms coordinates in pixel space within the drawing
|
|
65
|
+
* buffer (with upper left corner (0, 0) and lower right corner (buffer width, buffer height))
|
|
66
|
+
* to WebGL "clipspace", with upper left corner (-1, 1) and lower right corner (1, -1).
|
|
67
|
+
*/
|
|
68
|
+
export function getTransformMatrix (drawingBufferWidth, drawingBufferHeight) {
|
|
69
|
+
// To scale horizontally, divide by width (in pixels) and multiply by 2, because width is 2 in clipspace.
|
|
70
|
+
const sx = 2 / drawingBufferWidth
|
|
71
|
+
// To scale vertically, divide by height (in pixels) and multiply by -2, because height is 2 in clipspace,
|
|
72
|
+
// and the direction is flipped (positive is up, negative is down).
|
|
73
|
+
const sy = -2 / drawingBufferHeight
|
|
74
|
+
// We translate by -1 horizontally (so the range 0 to 2 maps to the range -1 to 1).
|
|
75
|
+
const tx = -1
|
|
76
|
+
// We translate by 1 horizontally (so the range -2 to 0 maps to the range -1 to 1).
|
|
77
|
+
const ty = 1
|
|
78
|
+
// Matrix must be in column-major order for WebGL.
|
|
79
|
+
return [
|
|
80
|
+
sx, 0, 0, 0,
|
|
81
|
+
0, sy, 0, 0,
|
|
82
|
+
0, 0, 1, 0,
|
|
83
|
+
tx, ty, 0, 1
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* From a TextureBounds object, this function generates the four vertices WebGL needs to draw the
|
|
89
|
+
* corresponding rectangle (as two conjoined triangles generated with the triangle strip primitive).
|
|
90
|
+
*/
|
|
91
|
+
export function getTexCoordVerticesTriangleStripQuad (textureBounds) {
|
|
92
|
+
const [{ x: left, y: top }, { x: right, y: bottom }] = textureBounds
|
|
93
|
+
return [
|
|
94
|
+
[left, top ],
|
|
95
|
+
[right, top ],
|
|
96
|
+
[left, bottom],
|
|
97
|
+
[right, bottom]
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* From a TextureBounds object, this function generates the six vertices WebGL needs to draw the
|
|
103
|
+
* corresponding rectangle (as two triangles).
|
|
104
|
+
*/
|
|
105
|
+
export function getTexCoordVerticesTriangleQuad (textureBounds) {
|
|
106
|
+
const [{ x: left, y: top }, { x: right, y: bottom }] = textureBounds
|
|
107
|
+
return [
|
|
108
|
+
[left, top ],
|
|
109
|
+
[right, top ],
|
|
110
|
+
[left, bottom],
|
|
111
|
+
[right, bottom],
|
|
112
|
+
[right, top ],
|
|
113
|
+
[left, bottom]
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Produces a Promise that resolves when the desired `duration` has expired.
|
|
119
|
+
*/
|
|
120
|
+
export function Timer (duration) {
|
|
121
|
+
return new Promise((resolve) => setTimeout(resolve, duration))
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Useful for sorting TileCoordinates objects.
|
|
126
|
+
*/
|
|
127
|
+
export function compareTileCoordinates (a, b) {
|
|
128
|
+
const z = a.z - b.z
|
|
129
|
+
const x = a.x - b.x
|
|
130
|
+
const y = a.y - b.y
|
|
131
|
+
if (z !== 0) {
|
|
132
|
+
// First compare z values.
|
|
133
|
+
return z
|
|
134
|
+
} else if (x !== 0) {
|
|
135
|
+
// If z values are the same, compare x values.
|
|
136
|
+
return x
|
|
137
|
+
} else {
|
|
138
|
+
// If x values are the same, compare y values.
|
|
139
|
+
return y
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Determines whether two arrays of TileCoordinates are the same.
|
|
145
|
+
*/
|
|
146
|
+
export function sameTiles (a, b) {
|
|
147
|
+
return (
|
|
148
|
+
// arrays are of the same length
|
|
149
|
+
a.length === b.length
|
|
150
|
+
// and corresponding elements have the same tile coordinates
|
|
151
|
+
&& a.every((tileA, index) => compareTileCoordinates(tileA, b[index]) === 0)
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const createNoDataTile = memoize((nodataValue, tileDimension = 256) => {
|
|
156
|
+
// Create a float 32 array.
|
|
157
|
+
const float32Tile = new Float32Array(tileDimension * tileDimension)
|
|
158
|
+
// Fill the tile array with the no data value
|
|
159
|
+
float32Tile.fill(nodataValue)
|
|
160
|
+
// return the no data tile.
|
|
161
|
+
return new Uint8Array(float32Tile.buffer)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Force TypeScript to interpret value `val` as type `T`.
|
|
166
|
+
*/
|
|
167
|
+
export function staticCast (val) {
|
|
168
|
+
return val
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Add one or more macro definitions to a GLSL source string.
|
|
173
|
+
*/
|
|
174
|
+
export function defineMacros (src, macros) {
|
|
175
|
+
const defs = Object.keys(macros).map((key) => `#define ${key} ${macros[key]}\n`).join('')
|
|
176
|
+
return `${defs}\n${src}`
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Ping-pong technique. Render to a destination framebuffer,
|
|
181
|
+
* then use it as a source texture in our next iteration.
|
|
182
|
+
* Then swap them and continue. Used for advanced hillshading.
|
|
183
|
+
*/
|
|
184
|
+
export function PingPong (regl, opts) {
|
|
185
|
+
const fbos = [regl.framebuffer(opts), regl.framebuffer(opts)]
|
|
186
|
+
|
|
187
|
+
let index = 0
|
|
188
|
+
|
|
189
|
+
function ping () {
|
|
190
|
+
return fbos[index]
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function pong () {
|
|
194
|
+
return fbos[1 - index]
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function swap () {
|
|
198
|
+
index = 1 - index
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function destroy () {
|
|
202
|
+
fbos[0].destroy()
|
|
203
|
+
fbos[1].destroy()
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
ping,
|
|
208
|
+
pong,
|
|
209
|
+
swap,
|
|
210
|
+
destroy
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* hexToRGB converts a color from hex format to rgba.
|
|
216
|
+
* const [r, g, b, a] = hexToRGB("#ffeeaaff")
|
|
217
|
+
*/
|
|
218
|
+
export const hexToRGB = (hex) => {
|
|
219
|
+
const hasAlpha = hex.length === 9
|
|
220
|
+
const start = hasAlpha ? 24 : 16
|
|
221
|
+
const bigint = parseInt(hex.slice(1), 16)
|
|
222
|
+
const r = (bigint >> start) & 255
|
|
223
|
+
const g = (bigint >> (start - 8)) & 255
|
|
224
|
+
const b = (bigint >> (start - 16)) & 255
|
|
225
|
+
const a = hasAlpha ? (bigint >> (start - 24)) & 255 : 255
|
|
226
|
+
return [r, g, b, a]
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Parses a color string of the form 'rgb({rVal}, {gVal}, {bVal})' and converts the resulting values
|
|
231
|
+
* to an array with ints 0 - 255.
|
|
232
|
+
*/
|
|
233
|
+
export function colorStringToInts (colorstring) {
|
|
234
|
+
if (colorstring === 'transparent') {
|
|
235
|
+
return [0, 0, 0, 0]
|
|
236
|
+
}
|
|
237
|
+
const rgbmatch = colorstring.match(RGB_REGEX)
|
|
238
|
+
const hexmatch = colorstring.match(HEX_REGEX)
|
|
239
|
+
if (rgbmatch !== null) {
|
|
240
|
+
const [, r, g, b] = rgbmatch
|
|
241
|
+
return [+r, +g, +b, 255]
|
|
242
|
+
} else if (hexmatch !== null) {
|
|
243
|
+
return hexToRGB(colorstring)
|
|
244
|
+
} else {
|
|
245
|
+
throw new Error(`'${colorstring}' is not a valid RGB or hex color expression.`)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* colormapToFlatArray takes the input colormap and returns a flat array to be
|
|
251
|
+
* used as input to a texture. The first row in the array contains the colors.
|
|
252
|
+
* The second row contains the encoded offset values.
|
|
253
|
+
*/
|
|
254
|
+
export const colormapToFlatArray = (colormap) => {
|
|
255
|
+
const offsets = []
|
|
256
|
+
let colors = []
|
|
257
|
+
for (let i = 0; i < colormap.length; i++) {
|
|
258
|
+
offsets.push(colormap[i].offset)
|
|
259
|
+
const colorsnew = colorStringToInts(colormap[i].color)
|
|
260
|
+
colors = colors.concat(colorsnew)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const floatOffsets = new Float32Array(offsets)
|
|
264
|
+
const uintOffsets = new Uint8Array(floatOffsets.buffer)
|
|
265
|
+
const normalOffsets = Array.from(uintOffsets)
|
|
266
|
+
const colormapArray = colors.concat(normalOffsets)
|
|
267
|
+
|
|
268
|
+
return colormapArray
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Creates a texture with colors on first row and offsets on second row
|
|
273
|
+
*/
|
|
274
|
+
export function createColormapTexture (colormapInput, regl) {
|
|
275
|
+
const colormapFlatArray = colormapToFlatArray(colormapInput)
|
|
276
|
+
let colormapTexture
|
|
277
|
+
if (colormapInput.length === 0) {
|
|
278
|
+
// empty texture
|
|
279
|
+
colormapTexture = regl.texture({
|
|
280
|
+
shape: [2, 2]
|
|
281
|
+
})
|
|
282
|
+
} else {
|
|
283
|
+
colormapTexture = regl.texture({
|
|
284
|
+
width: colormapInput.length,
|
|
285
|
+
height: 2,
|
|
286
|
+
data: colormapFlatArray
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return colormapTexture
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Fetch 8 adjacent tiles, if not already existing in tileManager.
|
|
295
|
+
* Return array with texture coord vertices for all tiles.
|
|
296
|
+
*/
|
|
297
|
+
export async function getAdjacentTilesTexCoords (gloperations, textureManager, coords, url) {
|
|
298
|
+
// Get existing tiles in TextureManager
|
|
299
|
+
const textureContents = textureManager.contents
|
|
300
|
+
|
|
301
|
+
// use 3x3 tiles for adv. hillshading
|
|
302
|
+
// TODO: add as plugin option?
|
|
303
|
+
const adjacentTiles = 3
|
|
304
|
+
let textureCoords = []
|
|
305
|
+
|
|
306
|
+
for (let i = 0; i < adjacentTiles; i++) {
|
|
307
|
+
const _x = coords['x'] + (i - 1)
|
|
308
|
+
for (let j = 0; j < adjacentTiles; j++) {
|
|
309
|
+
const _y = coords['y'] + (j - 1)
|
|
310
|
+
const coordsAdjacent = {
|
|
311
|
+
x: _x,
|
|
312
|
+
y: _y,
|
|
313
|
+
z: coords['z']
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Fetch data for adjacent tile if not already existing in TextureManager
|
|
317
|
+
const hashKey = textureManager.hashTileCoordinates(coordsAdjacent)
|
|
318
|
+
if (!textureContents.has(hashKey)) {
|
|
319
|
+
// Retrieve and add data to TextureManager
|
|
320
|
+
const pixelDataAdjacent = await gloperations._fetchTileData(coordsAdjacent, url)
|
|
321
|
+
const textureBounds = gloperations._renderer.textureManager.addTile(coordsAdjacent, pixelDataAdjacent)
|
|
322
|
+
textureCoords = textureCoords.concat(getTexCoordVerticesTriangleQuad(textureBounds))
|
|
323
|
+
} else {
|
|
324
|
+
const textureBounds = gloperations._renderer.textureManager.getTextureCoordinates(coordsAdjacent)
|
|
325
|
+
textureCoords = textureCoords.concat(getTexCoordVerticesTriangleQuad(textureBounds))
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return textureCoords
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function delay (ms) {
|
|
333
|
+
return new Promise(function (resolve) {
|
|
334
|
+
setTimeout(resolve, ms)
|
|
335
|
+
})
|
|
336
|
+
}
|