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