@gisatcz/deckgl-geolib 1.12.0-dev.4 → 2.0.0

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 (48) hide show
  1. package/README.md +67 -64
  2. package/dist/cjs/index.js +631 -641
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/index.min.js +3 -3
  5. package/dist/cjs/index.min.js.map +1 -1
  6. package/dist/{esm/types/cogtiles/cogtiles.d.ts → cjs/types/core/CogTiles.d.ts} +12 -9
  7. package/dist/cjs/types/{geoimage/geoimage.d.ts → core/GeoImage.d.ts} +6 -6
  8. package/dist/cjs/types/core/index.d.ts +2 -0
  9. package/dist/cjs/types/index.d.ts +2 -11
  10. package/dist/{esm/types/cogbitmaplayer → cjs/types/layers}/CogBitmapLayer.d.ts +3 -5
  11. package/dist/cjs/types/{cogterrainlayer → layers}/CogTerrainLayer.d.ts +13 -8
  12. package/dist/cjs/types/layers/index.d.ts +2 -0
  13. package/dist/esm/index.js +628 -641
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/index.min.js +3 -3
  16. package/dist/esm/index.min.js.map +1 -1
  17. package/dist/{cjs/types/cogtiles/cogtiles.d.ts → esm/types/core/CogTiles.d.ts} +12 -9
  18. package/dist/esm/types/{geoimage/geoimage.d.ts → core/GeoImage.d.ts} +6 -6
  19. package/dist/esm/types/core/index.d.ts +2 -0
  20. package/dist/esm/types/index.d.ts +2 -11
  21. package/dist/{cjs/types/cogbitmaplayer → esm/types/layers}/CogBitmapLayer.d.ts +3 -5
  22. package/dist/esm/types/{cogterrainlayer → layers}/CogTerrainLayer.d.ts +13 -8
  23. package/dist/esm/types/layers/index.d.ts +2 -0
  24. package/package.json +40 -4
  25. package/.eslintignore +0 -2
  26. package/.eslintrc.cjs +0 -3
  27. package/CHANGELOG.md +0 -166
  28. package/rollup.config.mjs +0 -77
  29. package/src/cogbitmaplayer/CogBitmapLayer.ts +0 -337
  30. package/src/cogbitmaplayer/README.md +0 -113
  31. package/src/cogterrainlayer/CogTerrainLayer.ts +0 -445
  32. package/src/cogterrainlayer/README.md +0 -118
  33. package/src/cogtiles/README.md +0 -72
  34. package/src/cogtiles/cogtiles.ts +0 -420
  35. package/src/cogtiles/lzw.js +0 -256
  36. package/src/geoimage/README.md +0 -129
  37. package/src/geoimage/delatin/index.ts +0 -495
  38. package/src/geoimage/geoimage.ts +0 -599
  39. package/src/geoimage/helpers/skirt.ts +0 -171
  40. package/src/index.ts +0 -11
  41. package/src/utilities/tileurls.ts +0 -21
  42. package/tsconfig.json +0 -6
  43. /package/dist/cjs/types/{geoimage → core}/delatin/index.d.ts +0 -0
  44. /package/dist/cjs/types/{geoimage → core}/helpers/skirt.d.ts +0 -0
  45. /package/dist/cjs/types/{utilities → utils}/tileurls.d.ts +0 -0
  46. /package/dist/esm/types/{geoimage → core}/delatin/index.d.ts +0 -0
  47. /package/dist/esm/types/{geoimage → core}/helpers/skirt.d.ts +0 -0
  48. /package/dist/esm/types/{utilities → utils}/tileurls.d.ts +0 -0
@@ -1,599 +0,0 @@
1
- /* eslint 'max-len': [1, { code: 105, comments: 999, ignoreStrings: true, ignoreUrls: true }] */
2
-
3
- // import { ExtentsLeftBottomRightTop } from '@deck.gl/core/utils/positions';
4
- import { fromArrayBuffer, GeoTIFFImage, TypedArray } from 'geotiff';
5
- import chroma from 'chroma-js';
6
- import Martini from '@mapbox/martini';
7
- import { getMeshBoundingBox } from '@loaders.gl/schema';
8
- import { addSkirt } from './helpers/skirt.ts';
9
- import Delatin from './delatin/index.ts';
10
-
11
- export type Bounds = [minX: number, minY: number, maxX: number, maxY: number];
12
-
13
- // FIXME - tesselator as a parameter
14
- const tesselator = 'martini';
15
- // const tesselator = 'delatin';
16
- export type ClampToTerrainOptions = {
17
- terrainDrawMode?: string
18
- }
19
- export type GeoImageOptions = {
20
- type: 'image' | 'terrain',
21
- format?: 'uint8' | 'uint16' | 'uint32' |'int8' | 'int16' | 'int32' | 'float32' | 'float64'
22
- useHeatMap?: boolean,
23
- useColorsBasedOnValues? : boolean,
24
- useColorClasses? : boolean,
25
- useAutoRange?: boolean,
26
- useDataForOpacity?: boolean,
27
- useChannel?: Exclude<number, 0> | null,
28
- useChannelIndex?: number | null,
29
- useSingleColor?: boolean,
30
- blurredTexture? : boolean,
31
- clipLow?: number | null,
32
- clipHigh?: number | null,
33
- multiplier?: number,
34
- color?: Array<number> | chroma.Color,
35
- colorScale?: Array<string> | Array<chroma.Color>,
36
- colorScaleValueRange?: number[],
37
- colorsBasedOnValues? : [number|undefined, chroma.Color][],
38
- colorClasses? : [chroma.Color, [number, number], [boolean, boolean]?][],
39
- alpha?: number,
40
- noDataValue?: number
41
- numOfChannels?: number,
42
- nullColor?: Array<number> | chroma.Color
43
- unidentifiedColor?: Array<number> | chroma.Color,
44
- clippedColor?: Array<number> | chroma.Color,
45
- clampToTerrain?: ClampToTerrainOptions | boolean, // terrainDrawMode: 'drape',
46
- terrainColor?: Array<number> | chroma.Color,
47
- terrainSkirtHeight?: number,
48
- terrainMinValue?: number,
49
- planarConfig?: number,
50
- }
51
-
52
- export const DefaultGeoImageOptions: GeoImageOptions = {
53
- type: 'image',
54
- format: 'uint8',
55
- useHeatMap: true,
56
- useColorsBasedOnValues: false,
57
- useAutoRange: false,
58
- useDataForOpacity: false,
59
- useSingleColor: false,
60
- useColorClasses: false,
61
- blurredTexture: true,
62
- clipLow: null,
63
- clipHigh: null,
64
- multiplier: 1.0,
65
- color: [255, 0, 255, 255],
66
- colorScale: chroma.brewer.YlOrRd,
67
- colorScaleValueRange: [0, 255],
68
- colorsBasedOnValues: null,
69
- colorClasses: null,
70
- alpha: 100,
71
- useChannel: null,
72
- useChannelIndex: null,
73
- noDataValue: undefined,
74
- numOfChannels: undefined,
75
- nullColor: [0, 0, 0, 0],
76
- unidentifiedColor: [0, 0, 0, 0],
77
- clippedColor: [0, 0, 0, 0],
78
- terrainColor: [133, 133, 133, 255],
79
- terrainSkirtHeight: 100,
80
- terrainMinValue: 0,
81
- planarConfig: undefined,
82
- };
83
-
84
- export default class GeoImage {
85
- data: GeoTIFFImage | undefined;
86
-
87
- scale = (
88
- num: number,
89
- inMin: number,
90
- inMax: number,
91
- outMin: number,
92
- outMax: number,
93
- ) => ((num - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
94
-
95
- async setUrl(url: string) {
96
- // TODO - not tested
97
- const response = await fetch(url);
98
- const arrayBuffer = await response.arrayBuffer();
99
- const tiff = await fromArrayBuffer(arrayBuffer);
100
-
101
- const data = await tiff.getImage(0);
102
-
103
- this.data = data;
104
- }
105
-
106
- async getMap(
107
- input: string | {
108
- width: number,
109
- height: number,
110
- rasters: any[],
111
- bounds: Bounds
112
- },
113
- options: GeoImageOptions,
114
- meshMaxError,
115
- ) {
116
- const mergedOptions = { ...DefaultGeoImageOptions, ...options };
117
-
118
- switch (mergedOptions.type) {
119
- case 'image':
120
- return this.getBitmap(input, mergedOptions);
121
- case 'terrain':
122
- return this.getHeightmap(input, mergedOptions, meshMaxError);
123
- default:
124
- return null;
125
- }
126
- }
127
-
128
- // GetHeightmap uses only "useChannel" and "multiplier" options
129
- async getHeightmap(
130
- input: string | {
131
- bounds: Bounds,
132
- width: number,
133
- height: number,
134
- rasters: any[] },
135
- options: GeoImageOptions,
136
- meshMaxError,
137
- ) {
138
- let rasters = [];
139
- let width: number;
140
- let height: number;
141
-
142
- if (typeof (input) === 'string') {
143
- // TODO not tested
144
- // input is type of object
145
- await this.setUrl(input);
146
-
147
- rasters = (await this.data!.readRasters()) as TypedArray[];
148
- width = this.data!.getWidth();
149
- height = this.data!.getHeight();
150
- } else {
151
- rasters = input.rasters;
152
- width = input.width;
153
- height = input.height;
154
- }
155
- const optionsLocal = { ...options };
156
-
157
- let channel = rasters[0];
158
-
159
- optionsLocal.useChannelIndex ??= optionsLocal.useChannel == null ? null : optionsLocal.useChannel - 1;
160
- if (options.useChannelIndex != null) {
161
- if (rasters[optionsLocal.useChannelIndex]) {
162
- channel = rasters[optionsLocal.useChannelIndex];
163
- }
164
- }
165
-
166
- const terrain = new Float32Array((width + 1) * (height + 1));
167
-
168
- const numOfChannels = channel.length / (width * height);
169
-
170
- let pixel:number = options.useChannelIndex === null ? 0 : options.useChannelIndex;
171
-
172
- for (let i = 0, y = 0; y < height; y++) {
173
- for (let x = 0; x < width; x++, i++) {
174
- const elevationValue = (options.noDataValue && channel[pixel] === options.noDataValue) ? options.terrainMinValue : channel[pixel] * options.multiplier!;
175
- terrain[i + y] = elevationValue;
176
- pixel += numOfChannels;
177
- }
178
- }
179
-
180
- if (tesselator === 'martini') {
181
- // backfill bottom border
182
- for (let i = (width + 1) * width, x = 0; x < width; x++, i++) {
183
- terrain[i] = terrain[i - width - 1];
184
- }
185
- // backfill right border
186
- for (let i = height, y = 0; y < height + 1; y++, i += height + 1) {
187
- terrain[i] = terrain[i - 1];
188
- }
189
- }
190
-
191
- // getMesh
192
- const { terrainSkirtHeight } = options;
193
-
194
- let mesh;
195
- switch (tesselator) {
196
- case 'martini':
197
- mesh = getMartiniTileMesh(meshMaxError, width, terrain);
198
-
199
- break;
200
- case 'delatin':
201
- mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
202
- break;
203
-
204
- default:
205
- if (width === height && !(height && (width - 1))) {
206
- // fixme get terrain to separate method
207
- // terrain = getTerrain(data, width, height, elevationDecoder, 'martini');
208
- mesh = getMartiniTileMesh(meshMaxError, width, terrain);
209
- } else {
210
- // fixme get terrain to separate method
211
- // terrain = getTerrain(data, width, height, elevationDecoder, 'delatin');
212
- mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
213
- }
214
- break;
215
- }
216
-
217
- // Martini
218
- // Martini
219
-
220
- // Delatin
221
- // Delatin
222
-
223
- const { vertices } = mesh;
224
- let { triangles } = mesh;
225
- let attributes = getMeshAttributes(vertices, terrain, width, height, input.bounds);
226
- // Compute bounding box before adding skirt so that z values are not skewed
227
- const boundingBox = getMeshBoundingBox(attributes);
228
-
229
- if (terrainSkirtHeight) {
230
- const { attributes: newAttributes, triangles: newTriangles } = addSkirt(
231
- attributes,
232
- triangles,
233
- terrainSkirtHeight,
234
- );
235
- attributes = newAttributes;
236
- triangles = newTriangles;
237
- }
238
-
239
- return {
240
- // Data return by this loader implementation
241
- loaderData: {
242
- header: {},
243
- },
244
- header: {
245
- vertexCount: triangles.length,
246
- boundingBox,
247
- },
248
- mode: 4, // TRIANGLES
249
- indices: { value: Uint32Array.from(triangles), size: 1 },
250
- attributes,
251
- };
252
- }
253
-
254
- async getBitmap(
255
- input: string | {
256
- width: number,
257
- height: number,
258
- rasters: any[] },
259
- options: GeoImageOptions,
260
- ) {
261
- // console.time('bitmap-generated-in');
262
- // const optionsLocal = { ...options };
263
- const optionsLocal = { ...options };
264
-
265
- let rasters = [];
266
- let channels: number;
267
- let width: number;
268
- let height: number;
269
-
270
- if (typeof (input) === 'string') {
271
- // TODO not tested
272
- // input is type of object
273
- await this.setUrl(input);
274
- rasters = (await this.data!.readRasters()) as TypedArray[];
275
- channels = rasters.length;
276
- width = this.data!.getWidth();
277
- height = this.data!.getHeight();
278
- } else {
279
- rasters = input.rasters;
280
- channels = rasters.length;
281
- width = input.width;
282
- height = input.height;
283
- }
284
-
285
- const canvas = document.createElement('canvas');
286
- canvas.width = width;
287
- canvas.height = height;
288
- const c = canvas.getContext('2d');
289
- const imageData: ImageData = c!.createImageData(width, height);
290
-
291
- let r; let g; let b; let
292
- a;
293
- const size = width * height * 4;
294
- // const size = width * height;
295
-
296
- // if (!options.noDataValue) {
297
- // console.log('Missing noData value. Raster might be displayed incorrectly.');
298
- // }
299
- optionsLocal.unidentifiedColor = this.getColorFromChromaType(optionsLocal.unidentifiedColor);
300
- optionsLocal.nullColor = this.getColorFromChromaType(optionsLocal.nullColor);
301
- optionsLocal.clippedColor = this.getColorFromChromaType(optionsLocal.clippedColor);
302
- optionsLocal.color = this.getColorFromChromaType(optionsLocal.color);
303
- optionsLocal.useChannelIndex ??= options.useChannel === null ? null : options.useChannel - 1;
304
-
305
- // console.log(rasters[0])
306
- /* console.log("raster 0 length: " + rasters[0].length)
307
- console.log("image width: " + width)
308
- console.log("channels: " + channels)
309
- console.log("format: " + rasters[0].length / (width * height))
310
- */
311
-
312
- if (optionsLocal.useChannelIndex == null) {
313
- if (channels === 1) {
314
- if (rasters[0].length / (width * height) === 1) {
315
- const channel = rasters[0];
316
- // AUTO RANGE
317
- if (optionsLocal.useAutoRange) {
318
- optionsLocal.colorScaleValueRange = this.getMinMax(channel, optionsLocal);
319
- // console.log('data min: ' + optionsLocal.rangeMin + ', max: ' + optionsLocal.rangeMax);
320
- }
321
- // SINGLE CHANNEL
322
- const colorData = this.getColorValue(channel, optionsLocal, size);
323
- colorData.forEach((value, index) => {
324
- imageData.data[index] = value;
325
- });
326
- }
327
- // RGB values in one channel
328
- if (rasters[0].length / (width * height) === 3) {
329
- // console.log("geoImage: " + "RGB 1 array of length: " + rasters[0].length);
330
- let pixel = 0;
331
- for (let idx = 0; idx < size; idx += 4) {
332
- const rgbColor = [rasters[0][pixel], rasters[0][pixel + 1], rasters[0][pixel + 2]];
333
- const rgbaColor = this.hasPixelsNoData(rgbColor, optionsLocal.noDataValue)
334
- ? optionsLocal.nullColor
335
- : [...rgbColor, Math.floor(optionsLocal.alpha! * 2.55)];
336
- // eslint-disable-next-line max-len
337
- [imageData.data[idx], imageData.data[idx + 1], imageData.data[idx + 2], imageData.data[idx + 3]] = rgbaColor;
338
- pixel += 3;
339
- }
340
- }
341
- if (rasters[0].length / (width * height) === 4) {
342
- // console.log("geoImage: " + "RGBA 1 array");
343
- rasters[0].forEach((value, index) => {
344
- imageData.data[index] = value;
345
- });
346
- }
347
- }
348
- if (channels === 3) {
349
- // RGB
350
- let pixel = 0;
351
- for (let i = 0; i < size; i += 4) {
352
- r = rasters[0][pixel];
353
- g = rasters[1][pixel];
354
- b = rasters[2][pixel];
355
- a = Math.floor(optionsLocal.alpha! * 2.55);
356
-
357
- imageData.data[i] = r;
358
- imageData.data[i + 1] = g;
359
- imageData.data[i + 2] = b;
360
- imageData.data[i + 3] = a;
361
-
362
- pixel += 1;
363
- }
364
- }
365
- if (channels === 4) {
366
- // RGBA
367
- let pixel = 0;
368
- for (let i = 0; i < size; i += 4) {
369
- r = rasters[0][pixel];
370
- g = rasters[1][pixel];
371
- b = rasters[2][pixel];
372
- a = Math.floor(optionsLocal.alpha! * 2.55);
373
-
374
- imageData.data[i] = r;
375
- imageData.data[i + 1] = g;
376
- imageData.data[i + 2] = b;
377
- imageData.data[i + 3] = a;
378
-
379
- pixel += 1;
380
- }
381
- }
382
- } else if (optionsLocal.useChannelIndex < optionsLocal.numOfChannels && optionsLocal.useChannelIndex >= 0) {
383
- let channel = rasters[0];
384
- if (rasters[optionsLocal.useChannelIndex]) {
385
- channel = rasters[optionsLocal.useChannelIndex];
386
- }
387
- // AUTO RANGE
388
- if (optionsLocal.useAutoRange) {
389
- optionsLocal.colorScaleValueRange = this.getMinMax(channel, optionsLocal);
390
- // console.log('data min: ' + optionsLocal.rangeMin + ', max: ' + optionsLocal.rangeMax);
391
- }
392
- // const numOfChannels = channel.length / (width * height);
393
- const colorData = this.getColorValue(channel, optionsLocal, size, optionsLocal.numOfChannels);
394
- colorData.forEach((value, index) => {
395
- imageData.data[index] = value;
396
- });
397
- } else {
398
- // if user defined channel does not exist
399
- console.log(`Defined channel(${options.useChannel}) or channel index(${options.useChannelIndex}) does not exist, choose a different channel or set the useChannel property to null if you want to visualize RGB(A) imagery`);
400
- const defaultColorData = this.getDefaultColor(size, optionsLocal.nullColor);
401
- defaultColorData.forEach((value, index) => {
402
- imageData.data[index] = value;
403
- });
404
- }
405
- // console.timeEnd('bitmap-generated-in');
406
-
407
- c!.putImageData(imageData, 0, 0);
408
- const imageUrl = canvas.toDataURL('image/png');
409
- // console.log('Bitmap generated.');
410
- return imageUrl;
411
- }
412
-
413
- getMinMax(array, options) {
414
- let maxValue = options.maxValue ? options.maxValue : Number.MIN_VALUE;
415
- let minValue = options.minValue ? options.minValue : Number.MAX_VALUE;
416
- for (let idx = 0; idx < array.length; idx += 1) {
417
- if (options.noDataValue === undefined || array[idx] !== options.noDataValue) {
418
- if (array[idx] > maxValue) maxValue = array[idx];
419
- if (array[idx] < minValue) minValue = array[idx];
420
- }
421
- }
422
- return [minValue, maxValue];
423
- }
424
-
425
- getColorValue(dataArray:[], options:GeoImageOptions, arrayLength:number, numOfChannels = 1) {
426
- const colorScale = chroma.scale(options.colorScale).domain(options.colorScaleValueRange);
427
- // channel index is equal to channel number - 1
428
- let pixel:number = options.useChannelIndex === null ? 0 : options.useChannelIndex;
429
- const colorsArray = new Array(arrayLength);
430
-
431
- // if useColorsBasedOnValues is true
432
- const dataValues = options.colorsBasedOnValues ? options.colorsBasedOnValues.map(([first]) => first) : undefined;
433
- const colorValues = options.colorsBasedOnValues ? options.colorsBasedOnValues.map(([, second]) => [...chroma(second).rgb(), Math.floor(options.alpha * 2.55)]) : undefined;
434
-
435
- // if useClasses is true
436
- const colorClasses = options.useColorClasses ? options.colorClasses.map(([color]) => [...chroma(color).rgb(), Math.floor(options.alpha * 2.55)]) : undefined;
437
- const dataIntervals = options.useColorClasses ? options.colorClasses.map(([, interval]) => interval) : undefined;
438
- const dataIntervalBounds = options.useColorClasses ? options.colorClasses.map(([, , bounds], index) => {
439
- if (bounds !== undefined) return bounds;
440
- if (index === options.colorClasses.length - 1) return [true, true];
441
- return [true, false];
442
- }) : undefined;
443
-
444
- for (let i = 0; i < arrayLength; i += 4) {
445
- let pixelColor = options.nullColor;
446
- // FIXME
447
- // eslint-disable-next-line max-len
448
- if ((!Number.isNaN(dataArray[pixel])) && (options.noDataValue === undefined || dataArray[pixel] !== options.noDataValue)) {
449
- if (
450
- (options.clipLow != null && dataArray[pixel] <= options.clipLow)
451
- || (options.clipHigh != null && dataArray[pixel] >= options.clipHigh)
452
- ) {
453
- pixelColor = options.clippedColor;
454
- } else {
455
- if (options.useHeatMap) {
456
- // FIXME
457
- // eslint-disable-next-line
458
- pixelColor = [...colorScale(dataArray[pixel]).rgb(), Math.floor(options.alpha * 2.55)];
459
- }
460
- if (options.useColorsBasedOnValues) {
461
- const index = dataValues.indexOf(dataArray[pixel]);
462
- if (index > -1) {
463
- pixelColor = colorValues[index];
464
- } else pixelColor = options.unidentifiedColor;
465
- }
466
- if (options.useColorClasses) {
467
- const index = this.findClassIndex(dataArray[pixel], dataIntervals, dataIntervalBounds);
468
- if (index > -1) {
469
- pixelColor = colorClasses[index];
470
- } else pixelColor = options.unidentifiedColor;
471
- }
472
- if (options.useSingleColor) {
473
- // FIXME - Is this compatible with chroma.color?
474
- pixelColor = options.color;
475
- }
476
- if (options.useDataForOpacity) {
477
- // eslint-disable-next-line max-len
478
- pixelColor[3] = this.scale(dataArray[pixel], options.colorScaleValueRange[0]!, options.colorScaleValueRange.slice(-1)[0]!, 0, 255);
479
- }
480
- }
481
- }
482
- // FIXME
483
- // eslint-disable-next-line
484
- [colorsArray[i], colorsArray[i + 1], colorsArray[i + 2], colorsArray[i + 3]] = pixelColor;
485
-
486
- pixel += numOfChannels;
487
- }
488
- return colorsArray;
489
- }
490
-
491
- findClassIndex(number, intervals, bounds) {
492
- // returns index of the first class to which the number belongs
493
- for (let idx = 0; idx < intervals.length; idx += 1) {
494
- const [min, max] = intervals[idx];
495
- const [includeEqualMin, includeEqualMax] = bounds[idx];
496
- if ((includeEqualMin ? number >= min : number > min)
497
- && (includeEqualMax ? number <= max : number < max)) {
498
- return idx;
499
- }
500
- }
501
- return -1;
502
- }
503
-
504
- getDefaultColor(size, nullColor) {
505
- const colorsArray = new Array(size);
506
- for (let i = 0; i < size; i += 4) {
507
- [colorsArray[i], colorsArray[i + 1], colorsArray[i + 2], colorsArray[i + 3]] = nullColor;
508
- }
509
- return colorsArray;
510
- }
511
-
512
- getColorFromChromaType(colorDefinition) {
513
- if (!Array.isArray(colorDefinition) || colorDefinition.length !== 4) {
514
- return [...chroma(colorDefinition).rgb(), 255];
515
- }
516
- return colorDefinition;
517
- }
518
-
519
- hasPixelsNoData(pixels, noDataValue) {
520
- return noDataValue !== undefined && pixels.every((pixel) => pixel === noDataValue);
521
- }
522
- }
523
-
524
- //
525
- //
526
- //
527
-
528
- /**
529
- * Get Martini generated vertices and triangles
530
- *
531
- * @param {number} meshMaxError threshold for simplifying mesh
532
- * @param {number} width width of the input data
533
- * @param {number[] | Float32Array} terrain elevation data
534
- * @returns {{vertices: Uint16Array, triangles: Uint32Array}} vertices and triangles data
535
- */
536
- function getMartiniTileMesh(meshMaxError, width, terrain) {
537
- const gridSize = width + 1;
538
- const martini = new Martini(gridSize);
539
- const tile = martini.createTile(terrain);
540
- const { vertices, triangles } = tile.getMesh(meshMaxError);
541
-
542
- return { vertices, triangles };
543
- }
544
-
545
- function getMeshAttributes(
546
- vertices,
547
- terrain: Uint8Array,
548
- width: number,
549
- height: number,
550
- bounds: number[],
551
- ) {
552
- const gridSize = width + 1;
553
- const numOfVerticies = vertices.length / 2;
554
- // vec3. x, y in pixels, z in meters
555
- const positions = new Float32Array(numOfVerticies * 3);
556
- // vec2. 1 to 1 relationship with position. represents the uv on the texture image. 0,0 to 1,1.
557
- const texCoords = new Float32Array(numOfVerticies * 2);
558
-
559
- const [minX, minY, maxX, maxY] = bounds || [0, 0, width, height];
560
- const xScale = (maxX - minX) / width;
561
- const yScale = (maxY - minY) / height;
562
-
563
- for (let i = 0; i < numOfVerticies; i++) {
564
- const x = vertices[i * 2];
565
- const y = vertices[i * 2 + 1];
566
- const pixelIdx = y * gridSize + x;
567
-
568
- positions[3 * i + 0] = x * xScale + minX;
569
- positions[3 * i + 1] = -y * yScale + maxY;
570
- positions[3 * i + 2] = terrain[pixelIdx];
571
-
572
- texCoords[2 * i + 0] = x / width;
573
- texCoords[2 * i + 1] = y / height;
574
- }
575
-
576
- return {
577
- POSITION: { value: positions, size: 3 },
578
- TEXCOORD_0: { value: texCoords, size: 2 },
579
- // NORMAL: {}, - optional, but creates the high poly look with lighting
580
- };
581
- }
582
-
583
- /**
584
- * Get Delatin generated vertices and triangles
585
- *
586
- * @param {number} meshMaxError threshold for simplifying mesh
587
- * @param {number} width width of the input data array
588
- * @param {number} height height of the input data array
589
- * @param {number[] | Float32Array} terrain elevation data
590
- * @returns {{vertices: number[], triangles: number[]}} vertices and triangles data
591
- */
592
- function getDelatinTileMesh(meshMaxError, width, height, terrain) {
593
- const tin = new Delatin(terrain, width + 1, height + 1);
594
- tin.run(meshMaxError);
595
- // @ts-expect-error
596
- const { coords, triangles } = tin;
597
- const vertices = coords;
598
- return { vertices, triangles };
599
- }