@gisatcz/deckgl-geolib 1.10.1-dev.0 → 1.10.1-dev.1

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/dist/esm/index.js CHANGED
@@ -14985,7 +14985,750 @@ async function fromArrayBuffer(arrayBuffer, signal) {
14985
14985
  return GeoTIFF.fromSource(makeBufferSource(arrayBuffer), signal);
14986
14986
  }
14987
14987
 
14988
+ class Martini {
14989
+ constructor(gridSize = 257) {
14990
+ this.gridSize = gridSize;
14991
+ const tileSize = gridSize - 1;
14992
+ if (tileSize & (tileSize - 1)) throw new Error(
14993
+ `Expected grid size to be 2^n+1, got ${gridSize}.`);
14994
+
14995
+ this.numTriangles = tileSize * tileSize * 2 - 2;
14996
+ this.numParentTriangles = this.numTriangles - tileSize * tileSize;
14997
+
14998
+ this.indices = new Uint32Array(this.gridSize * this.gridSize);
14999
+
15000
+ // coordinates for all possible triangles in an RTIN tile
15001
+ this.coords = new Uint16Array(this.numTriangles * 4);
15002
+
15003
+ // get triangle coordinates from its index in an implicit binary tree
15004
+ for (let i = 0; i < this.numTriangles; i++) {
15005
+ let id = i + 2;
15006
+ let ax = 0, ay = 0, bx = 0, by = 0, cx = 0, cy = 0;
15007
+ if (id & 1) {
15008
+ bx = by = cx = tileSize; // bottom-left triangle
15009
+ } else {
15010
+ ax = ay = cy = tileSize; // top-right triangle
15011
+ }
15012
+ while ((id >>= 1) > 1) {
15013
+ const mx = (ax + bx) >> 1;
15014
+ const my = (ay + by) >> 1;
15015
+
15016
+ if (id & 1) { // left half
15017
+ bx = ax; by = ay;
15018
+ ax = cx; ay = cy;
15019
+ } else { // right half
15020
+ ax = bx; ay = by;
15021
+ bx = cx; by = cy;
15022
+ }
15023
+ cx = mx; cy = my;
15024
+ }
15025
+ const k = i * 4;
15026
+ this.coords[k + 0] = ax;
15027
+ this.coords[k + 1] = ay;
15028
+ this.coords[k + 2] = bx;
15029
+ this.coords[k + 3] = by;
15030
+ }
15031
+ }
15032
+
15033
+ createTile(terrain) {
15034
+ return new Tile(terrain, this);
15035
+ }
15036
+ }
15037
+
15038
+ class Tile {
15039
+ constructor(terrain, martini) {
15040
+ const size = martini.gridSize;
15041
+ if (terrain.length !== size * size) throw new Error(
15042
+ `Expected terrain data of length ${size * size} (${size} x ${size}), got ${terrain.length}.`);
15043
+
15044
+ this.terrain = terrain;
15045
+ this.martini = martini;
15046
+ this.errors = new Float32Array(terrain.length);
15047
+ this.update();
15048
+ }
15049
+
15050
+ update() {
15051
+ const {numTriangles, numParentTriangles, coords, gridSize: size} = this.martini;
15052
+ const {terrain, errors} = this;
15053
+
15054
+ // iterate over all possible triangles, starting from the smallest level
15055
+ for (let i = numTriangles - 1; i >= 0; i--) {
15056
+ const k = i * 4;
15057
+ const ax = coords[k + 0];
15058
+ const ay = coords[k + 1];
15059
+ const bx = coords[k + 2];
15060
+ const by = coords[k + 3];
15061
+ const mx = (ax + bx) >> 1;
15062
+ const my = (ay + by) >> 1;
15063
+ const cx = mx + my - ay;
15064
+ const cy = my + ax - mx;
15065
+
15066
+ // calculate error in the middle of the long edge of the triangle
15067
+ const interpolatedHeight = (terrain[ay * size + ax] + terrain[by * size + bx]) / 2;
15068
+ const middleIndex = my * size + mx;
15069
+ const middleError = Math.abs(interpolatedHeight - terrain[middleIndex]);
15070
+
15071
+ errors[middleIndex] = Math.max(errors[middleIndex], middleError);
15072
+
15073
+ if (i < numParentTriangles) { // bigger triangles; accumulate error with children
15074
+ const leftChildIndex = ((ay + cy) >> 1) * size + ((ax + cx) >> 1);
15075
+ const rightChildIndex = ((by + cy) >> 1) * size + ((bx + cx) >> 1);
15076
+ errors[middleIndex] = Math.max(errors[middleIndex], errors[leftChildIndex], errors[rightChildIndex]);
15077
+ }
15078
+ }
15079
+ }
15080
+
15081
+ getMesh(maxError = 0) {
15082
+ const {gridSize: size, indices} = this.martini;
15083
+ const {errors} = this;
15084
+ let numVertices = 0;
15085
+ let numTriangles = 0;
15086
+ const max = size - 1;
15087
+
15088
+ // use an index grid to keep track of vertices that were already used to avoid duplication
15089
+ indices.fill(0);
15090
+
15091
+ // retrieve mesh in two stages that both traverse the error map:
15092
+ // - countElements: find used vertices (and assign each an index), and count triangles (for minimum allocation)
15093
+ // - processTriangle: fill the allocated vertices & triangles typed arrays
15094
+
15095
+ function countElements(ax, ay, bx, by, cx, cy) {
15096
+ const mx = (ax + bx) >> 1;
15097
+ const my = (ay + by) >> 1;
15098
+
15099
+ if (Math.abs(ax - cx) + Math.abs(ay - cy) > 1 && errors[my * size + mx] > maxError) {
15100
+ countElements(cx, cy, ax, ay, mx, my);
15101
+ countElements(bx, by, cx, cy, mx, my);
15102
+ } else {
15103
+ indices[ay * size + ax] = indices[ay * size + ax] || ++numVertices;
15104
+ indices[by * size + bx] = indices[by * size + bx] || ++numVertices;
15105
+ indices[cy * size + cx] = indices[cy * size + cx] || ++numVertices;
15106
+ numTriangles++;
15107
+ }
15108
+ }
15109
+ countElements(0, 0, max, max, max, 0);
15110
+ countElements(max, max, 0, 0, 0, max);
15111
+
15112
+ const vertices = new Uint16Array(numVertices * 2);
15113
+ const triangles = new Uint32Array(numTriangles * 3);
15114
+ let triIndex = 0;
15115
+
15116
+ function processTriangle(ax, ay, bx, by, cx, cy) {
15117
+ const mx = (ax + bx) >> 1;
15118
+ const my = (ay + by) >> 1;
15119
+
15120
+ if (Math.abs(ax - cx) + Math.abs(ay - cy) > 1 && errors[my * size + mx] > maxError) {
15121
+ // triangle doesn't approximate the surface well enough; drill down further
15122
+ processTriangle(cx, cy, ax, ay, mx, my);
15123
+ processTriangle(bx, by, cx, cy, mx, my);
15124
+
15125
+ } else {
15126
+ // add a triangle
15127
+ const a = indices[ay * size + ax] - 1;
15128
+ const b = indices[by * size + bx] - 1;
15129
+ const c = indices[cy * size + cx] - 1;
15130
+
15131
+ vertices[2 * a] = ax;
15132
+ vertices[2 * a + 1] = ay;
15133
+
15134
+ vertices[2 * b] = bx;
15135
+ vertices[2 * b + 1] = by;
15136
+
15137
+ vertices[2 * c] = cx;
15138
+ vertices[2 * c + 1] = cy;
15139
+
15140
+ triangles[triIndex++] = a;
15141
+ triangles[triIndex++] = b;
15142
+ triangles[triIndex++] = c;
15143
+ }
15144
+ }
15145
+ processTriangle(0, 0, max, max, max, 0);
15146
+ processTriangle(max, max, 0, 0, 0, max);
15147
+
15148
+ return {vertices, triangles};
15149
+ }
15150
+ }
15151
+
15152
+ function getMeshBoundingBox(attributes) {
15153
+ let minX = Infinity;
15154
+ let minY = Infinity;
15155
+ let minZ = Infinity;
15156
+ let maxX = -Infinity;
15157
+ let maxY = -Infinity;
15158
+ let maxZ = -Infinity;
15159
+ const positions = attributes.POSITION ? attributes.POSITION.value : [];
15160
+ const len = positions && positions.length;
15161
+ for (let i = 0; i < len; i += 3) {
15162
+ const x = positions[i];
15163
+ const y = positions[i + 1];
15164
+ const z = positions[i + 2];
15165
+ minX = x < minX ? x : minX;
15166
+ minY = y < minY ? y : minY;
15167
+ minZ = z < minZ ? z : minZ;
15168
+ maxX = x > maxX ? x : maxX;
15169
+ maxY = y > maxY ? y : maxY;
15170
+ maxZ = z > maxZ ? z : maxZ;
15171
+ }
15172
+ return [[minX, minY, minZ], [maxX, maxY, maxZ]];
15173
+ }
15174
+
15175
+ function concatenateTypedArrays() {
15176
+ for (var _len2 = arguments.length, typedArrays = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
15177
+ typedArrays[_key2] = arguments[_key2];
15178
+ }
15179
+ const arrays = typedArrays;
15180
+ const TypedArrayConstructor = arrays && arrays.length > 1 && arrays[0].constructor || null;
15181
+ if (!TypedArrayConstructor) {
15182
+ throw new Error('"concatenateTypedArrays" - incorrect quantity of arguments or arguments have incompatible data types');
15183
+ }
15184
+ const sumLength = arrays.reduce((acc, value) => acc + value.length, 0);
15185
+ const result = new TypedArrayConstructor(sumLength);
15186
+ let offset = 0;
15187
+ for (const array of arrays) {
15188
+ result.set(array, offset);
15189
+ offset += array.length;
15190
+ }
15191
+ return result;
15192
+ }
15193
+
15194
+ // loaders.gl
15195
+ // SPDX-License-Identifier: MIT
15196
+ // Copyright (c) vis.gl contributors
15197
+ /**
15198
+ * Add skirt to existing mesh
15199
+ * @param {object} attributes - POSITION and TEXCOOD_0 attributes data
15200
+ * @param {any} triangles - indices array of the mesh geometry
15201
+ * @param skirtHeight - height of the skirt geometry
15202
+ * @param outsideIndices - edge indices from quantized mesh data
15203
+ * @returns - geometry data with added skirt
15204
+ */
15205
+ function addSkirt(attributes, triangles, skirtHeight, outsideIndices) {
15206
+ const outsideEdges = outsideIndices
15207
+ ? getOutsideEdgesFromIndices(outsideIndices, attributes.POSITION.value)
15208
+ : getOutsideEdgesFromTriangles(triangles);
15209
+ // 2 new vertices for each outside edge
15210
+ const newPosition = new attributes.POSITION.value.constructor(outsideEdges.length * 6);
15211
+ const newTexcoord0 = new attributes.TEXCOORD_0.value.constructor(outsideEdges.length * 4);
15212
+ // 2 new triangles for each outside edge
15213
+ const newTriangles = new triangles.constructor(outsideEdges.length * 6);
15214
+ for (let i = 0; i < outsideEdges.length; i++) {
15215
+ const edge = outsideEdges[i];
15216
+ updateAttributesForNewEdge({
15217
+ edge,
15218
+ edgeIndex: i,
15219
+ attributes,
15220
+ skirtHeight,
15221
+ newPosition,
15222
+ newTexcoord0,
15223
+ newTriangles,
15224
+ });
15225
+ }
15226
+ attributes.POSITION.value = concatenateTypedArrays(attributes.POSITION.value, newPosition);
15227
+ attributes.TEXCOORD_0.value = concatenateTypedArrays(attributes.TEXCOORD_0.value, newTexcoord0);
15228
+ const resultTriangles = triangles instanceof Array
15229
+ ? triangles.concat(newTriangles)
15230
+ : concatenateTypedArrays(triangles, newTriangles);
15231
+ return {
15232
+ attributes,
15233
+ triangles: resultTriangles,
15234
+ };
15235
+ }
15236
+ /**
15237
+ * Get geometry edges that located on a border of the mesh
15238
+ * @param {any} triangles - indices array of the mesh geometry
15239
+ * @returns {number[][]} - outside edges data
15240
+ */
15241
+ function getOutsideEdgesFromTriangles(triangles) {
15242
+ var _a, _b;
15243
+ const edges = [];
15244
+ for (let i = 0; i < triangles.length; i += 3) {
15245
+ edges.push([triangles[i], triangles[i + 1]]);
15246
+ edges.push([triangles[i + 1], triangles[i + 2]]);
15247
+ edges.push([triangles[i + 2], triangles[i]]);
15248
+ }
15249
+ edges.sort((a, b) => Math.min(...a) - Math.min(...b) || Math.max(...a) - Math.max(...b));
15250
+ const outsideEdges = [];
15251
+ let index = 0;
15252
+ while (index < edges.length) {
15253
+ if (edges[index][0] === ((_a = edges[index + 1]) === null || _a === void 0 ? void 0 : _a[1]) && edges[index][1] === ((_b = edges[index + 1]) === null || _b === void 0 ? void 0 : _b[0])) {
15254
+ index += 2;
15255
+ }
15256
+ else {
15257
+ outsideEdges.push(edges[index]);
15258
+ index++;
15259
+ }
15260
+ }
15261
+ return outsideEdges;
15262
+ }
15263
+ /**
15264
+ * Get geometry edges that located on a border of the mesh
15265
+ * @param {object} indices - edge indices from quantized mesh data
15266
+ * @param {TypedArray} position - position attribute geometry data
15267
+ * @returns {number[][]} - outside edges data
15268
+ */
15269
+ function getOutsideEdgesFromIndices(indices, position) {
15270
+ // Sort skirt indices to create adjacent triangles
15271
+ indices.westIndices.sort((a, b) => position[3 * a + 1] - position[3 * b + 1]);
15272
+ // Reverse (b - a) to match triangle winding
15273
+ indices.eastIndices.sort((a, b) => position[3 * b + 1] - position[3 * a + 1]);
15274
+ indices.southIndices.sort((a, b) => position[3 * b] - position[3 * a]);
15275
+ // Reverse (b - a) to match triangle winding
15276
+ indices.northIndices.sort((a, b) => position[3 * a] - position[3 * b]);
15277
+ const edges = [];
15278
+ for (const index in indices) {
15279
+ const indexGroup = indices[index];
15280
+ for (let i = 0; i < indexGroup.length - 1; i++) {
15281
+ edges.push([indexGroup[i], indexGroup[i + 1]]);
15282
+ }
15283
+ }
15284
+ return edges;
15285
+ }
15286
+ /**
15287
+ * Get geometry edges that located on a border of the mesh
15288
+ * @param {object} args
15289
+ * @param {number[]} args.edge - edge indices in geometry
15290
+ * @param {number} args.edgeIndex - edge index in outsideEdges array
15291
+ * @param {object} args.attributes - POSITION and TEXCOORD_0 attributes
15292
+ * @param {number} args.skirtHeight - height of the skirt geometry
15293
+ * @param {TypedArray} args.newPosition - POSITION array for skirt data
15294
+ * @param {TypedArray} args.newTexcoord0 - TEXCOORD_0 array for skirt data
15295
+ * @param {TypedArray | Array} args.newTriangles - trinagle indices array for skirt data
15296
+ * @returns {void}
15297
+ */
15298
+ function updateAttributesForNewEdge({ edge, edgeIndex, attributes, skirtHeight, newPosition, newTexcoord0, newTriangles, }) {
15299
+ const positionsLength = attributes.POSITION.value.length;
15300
+ const vertex1Offset = edgeIndex * 2;
15301
+ const vertex2Offset = edgeIndex * 2 + 1;
15302
+ // Define POSITION for new 1st vertex
15303
+ newPosition.set(attributes.POSITION.value.subarray(edge[0] * 3, edge[0] * 3 + 3), vertex1Offset * 3);
15304
+ newPosition[vertex1Offset * 3 + 2] = newPosition[vertex1Offset * 3 + 2] - skirtHeight; // put down elevation on the skirt height
15305
+ // Define POSITION for new 2nd vertex
15306
+ newPosition.set(attributes.POSITION.value.subarray(edge[1] * 3, edge[1] * 3 + 3), vertex2Offset * 3);
15307
+ newPosition[vertex2Offset * 3 + 2] = newPosition[vertex2Offset * 3 + 2] - skirtHeight; // put down elevation on the skirt height
15308
+ // Use same TEXCOORDS for skirt vertices
15309
+ newTexcoord0.set(attributes.TEXCOORD_0.value.subarray(edge[0] * 2, edge[0] * 2 + 2), vertex1Offset * 2);
15310
+ newTexcoord0.set(attributes.TEXCOORD_0.value.subarray(edge[1] * 2, edge[1] * 2 + 2), vertex2Offset * 2);
15311
+ // Define new triangles
15312
+ const triangle1Offset = edgeIndex * 2 * 3;
15313
+ newTriangles[triangle1Offset] = edge[0];
15314
+ newTriangles[triangle1Offset + 1] = positionsLength / 3 + vertex2Offset;
15315
+ newTriangles[triangle1Offset + 2] = edge[1];
15316
+ newTriangles[triangle1Offset + 3] = positionsLength / 3 + vertex2Offset;
15317
+ newTriangles[triangle1Offset + 4] = edge[0];
15318
+ newTriangles[triangle1Offset + 5] = positionsLength / 3 + vertex1Offset;
15319
+ }
15320
+
15321
+ // loaders.gl
15322
+ // SPDX-License-Identifier: MIT
15323
+ // Copyright (c) vis.gl contributors
15324
+ // ISC License
15325
+ // Copyright(c) 2019, Michael Fogleman, Vladimir Agafonkin
15326
+ // @ts-nocheck
15327
+ /* eslint-disable complexity, max-params, max-statements, max-depth, no-constant-condition */
15328
+ class Delatin {
15329
+ constructor(data, width, height = width) {
15330
+ this.data = data; // height data
15331
+ this.width = width;
15332
+ this.height = height;
15333
+ this.coords = []; // vertex coordinates (x, y)
15334
+ this.triangles = []; // mesh triangle indices
15335
+ // additional triangle data
15336
+ this._halfedges = [];
15337
+ this._candidates = [];
15338
+ this._queueIndices = [];
15339
+ this._queue = []; // queue of added triangles
15340
+ this._errors = [];
15341
+ this._rms = [];
15342
+ this._pending = []; // triangles pending addition to queue
15343
+ this._pendingLen = 0;
15344
+ this._rmsSum = 0;
15345
+ const x1 = width - 1;
15346
+ const y1 = height - 1;
15347
+ const p0 = this._addPoint(0, 0);
15348
+ const p1 = this._addPoint(x1, 0);
15349
+ const p2 = this._addPoint(0, y1);
15350
+ const p3 = this._addPoint(x1, y1);
15351
+ // add initial two triangles
15352
+ const t0 = this._addTriangle(p3, p0, p2, -1, -1, -1);
15353
+ this._addTriangle(p0, p3, p1, t0, -1, -1);
15354
+ this._flush();
15355
+ }
15356
+ // refine the mesh until its maximum error gets below the given one
15357
+ run(maxError = 1) {
15358
+ while (this.getMaxError() > maxError) {
15359
+ this.refine();
15360
+ }
15361
+ }
15362
+ // refine the mesh with a single point
15363
+ refine() {
15364
+ this._step();
15365
+ this._flush();
15366
+ }
15367
+ // max error of the current mesh
15368
+ getMaxError() {
15369
+ return this._errors[0];
15370
+ }
15371
+ // root-mean-square deviation of the current mesh
15372
+ getRMSD() {
15373
+ return this._rmsSum > 0 ? Math.sqrt(this._rmsSum / (this.width * this.height)) : 0;
15374
+ }
15375
+ // height value at a given position
15376
+ heightAt(x, y) {
15377
+ return this.data[this.width * y + x];
15378
+ }
15379
+ // rasterize and queue all triangles that got added or updated in _step
15380
+ _flush() {
15381
+ const { coords } = this;
15382
+ for (let i = 0; i < this._pendingLen; i++) {
15383
+ const t = this._pending[i];
15384
+ // rasterize triangle to find maximum pixel error
15385
+ const a = 2 * this.triangles[t * 3 + 0];
15386
+ const b = 2 * this.triangles[t * 3 + 1];
15387
+ const c = 2 * this.triangles[t * 3 + 2];
15388
+ this._findCandidate(coords[a], coords[a + 1], coords[b], coords[b + 1], coords[c], coords[c + 1], t);
15389
+ }
15390
+ this._pendingLen = 0;
15391
+ }
15392
+ // rasterize a triangle, find its max error, and queue it for processing
15393
+ _findCandidate(p0x, p0y, p1x, p1y, p2x, p2y, t) {
15394
+ // triangle bounding box
15395
+ const minX = Math.min(p0x, p1x, p2x);
15396
+ const minY = Math.min(p0y, p1y, p2y);
15397
+ const maxX = Math.max(p0x, p1x, p2x);
15398
+ const maxY = Math.max(p0y, p1y, p2y);
15399
+ // forward differencing variables
15400
+ let w00 = orient(p1x, p1y, p2x, p2y, minX, minY);
15401
+ let w01 = orient(p2x, p2y, p0x, p0y, minX, minY);
15402
+ let w02 = orient(p0x, p0y, p1x, p1y, minX, minY);
15403
+ const a01 = p1y - p0y;
15404
+ const b01 = p0x - p1x;
15405
+ const a12 = p2y - p1y;
15406
+ const b12 = p1x - p2x;
15407
+ const a20 = p0y - p2y;
15408
+ const b20 = p2x - p0x;
15409
+ // pre-multiplied z values at vertices
15410
+ const a = orient(p0x, p0y, p1x, p1y, p2x, p2y);
15411
+ const z0 = this.heightAt(p0x, p0y) / a;
15412
+ const z1 = this.heightAt(p1x, p1y) / a;
15413
+ const z2 = this.heightAt(p2x, p2y) / a;
15414
+ // iterate over pixels in bounding box
15415
+ let maxError = 0;
15416
+ let mx = 0;
15417
+ let my = 0;
15418
+ let rms = 0;
15419
+ for (let y = minY; y <= maxY; y++) {
15420
+ // compute starting offset
15421
+ let dx = 0;
15422
+ if (w00 < 0 && a12 !== 0) {
15423
+ dx = Math.max(dx, Math.floor(-w00 / a12));
15424
+ }
15425
+ if (w01 < 0 && a20 !== 0) {
15426
+ dx = Math.max(dx, Math.floor(-w01 / a20));
15427
+ }
15428
+ if (w02 < 0 && a01 !== 0) {
15429
+ dx = Math.max(dx, Math.floor(-w02 / a01));
15430
+ }
15431
+ let w0 = w00 + a12 * dx;
15432
+ let w1 = w01 + a20 * dx;
15433
+ let w2 = w02 + a01 * dx;
15434
+ let wasInside = false;
15435
+ for (let x = minX + dx; x <= maxX; x++) {
15436
+ // check if inside triangle
15437
+ if (w0 >= 0 && w1 >= 0 && w2 >= 0) {
15438
+ wasInside = true;
15439
+ // compute z using barycentric coordinates
15440
+ const z = z0 * w0 + z1 * w1 + z2 * w2;
15441
+ const dz = Math.abs(z - this.heightAt(x, y));
15442
+ rms += dz * dz;
15443
+ if (dz > maxError) {
15444
+ maxError = dz;
15445
+ mx = x;
15446
+ my = y;
15447
+ }
15448
+ }
15449
+ else if (wasInside) {
15450
+ break;
15451
+ }
15452
+ w0 += a12;
15453
+ w1 += a20;
15454
+ w2 += a01;
15455
+ }
15456
+ w00 += b12;
15457
+ w01 += b20;
15458
+ w02 += b01;
15459
+ }
15460
+ if ((mx === p0x && my === p0y) || (mx === p1x && my === p1y) || (mx === p2x && my === p2y)) {
15461
+ maxError = 0;
15462
+ }
15463
+ // update triangle metadata
15464
+ this._candidates[2 * t] = mx;
15465
+ this._candidates[2 * t + 1] = my;
15466
+ this._rms[t] = rms;
15467
+ // add triangle to priority queue
15468
+ this._queuePush(t, maxError, rms);
15469
+ }
15470
+ // process the next triangle in the queue, splitting it with a new point
15471
+ _step() {
15472
+ // pop triangle with highest error from priority queue
15473
+ const t = this._queuePop();
15474
+ const e0 = t * 3 + 0;
15475
+ const e1 = t * 3 + 1;
15476
+ const e2 = t * 3 + 2;
15477
+ const p0 = this.triangles[e0];
15478
+ const p1 = this.triangles[e1];
15479
+ const p2 = this.triangles[e2];
15480
+ const ax = this.coords[2 * p0];
15481
+ const ay = this.coords[2 * p0 + 1];
15482
+ const bx = this.coords[2 * p1];
15483
+ const by = this.coords[2 * p1 + 1];
15484
+ const cx = this.coords[2 * p2];
15485
+ const cy = this.coords[2 * p2 + 1];
15486
+ const px = this._candidates[2 * t];
15487
+ const py = this._candidates[2 * t + 1];
15488
+ const pn = this._addPoint(px, py);
15489
+ if (orient(ax, ay, bx, by, px, py) === 0) {
15490
+ this._handleCollinear(pn, e0);
15491
+ }
15492
+ else if (orient(bx, by, cx, cy, px, py) === 0) {
15493
+ this._handleCollinear(pn, e1);
15494
+ }
15495
+ else if (orient(cx, cy, ax, ay, px, py) === 0) {
15496
+ this._handleCollinear(pn, e2);
15497
+ }
15498
+ else {
15499
+ const h0 = this._halfedges[e0];
15500
+ const h1 = this._halfedges[e1];
15501
+ const h2 = this._halfedges[e2];
15502
+ const t0 = this._addTriangle(p0, p1, pn, h0, -1, -1, e0);
15503
+ const t1 = this._addTriangle(p1, p2, pn, h1, -1, t0 + 1);
15504
+ const t2 = this._addTriangle(p2, p0, pn, h2, t0 + 2, t1 + 1);
15505
+ this._legalize(t0);
15506
+ this._legalize(t1);
15507
+ this._legalize(t2);
15508
+ }
15509
+ }
15510
+ // add coordinates for a new vertex
15511
+ _addPoint(x, y) {
15512
+ const i = this.coords.length >> 1;
15513
+ this.coords.push(x, y);
15514
+ return i;
15515
+ }
15516
+ // add or update a triangle in the mesh
15517
+ _addTriangle(a, b, c, ab, bc, ca, e = this.triangles.length) {
15518
+ const t = e / 3; // new triangle index
15519
+ // add triangle vertices
15520
+ this.triangles[e + 0] = a;
15521
+ this.triangles[e + 1] = b;
15522
+ this.triangles[e + 2] = c;
15523
+ // add triangle halfedges
15524
+ this._halfedges[e + 0] = ab;
15525
+ this._halfedges[e + 1] = bc;
15526
+ this._halfedges[e + 2] = ca;
15527
+ // link neighboring halfedges
15528
+ if (ab >= 0) {
15529
+ this._halfedges[ab] = e + 0;
15530
+ }
15531
+ if (bc >= 0) {
15532
+ this._halfedges[bc] = e + 1;
15533
+ }
15534
+ if (ca >= 0) {
15535
+ this._halfedges[ca] = e + 2;
15536
+ }
15537
+ // init triangle metadata
15538
+ this._candidates[2 * t + 0] = 0;
15539
+ this._candidates[2 * t + 1] = 0;
15540
+ this._queueIndices[t] = -1;
15541
+ this._rms[t] = 0;
15542
+ // add triangle to pending queue for later rasterization
15543
+ this._pending[this._pendingLen++] = t;
15544
+ // return first halfedge index
15545
+ return e;
15546
+ }
15547
+ _legalize(a) {
15548
+ // if the pair of triangles doesn't satisfy the Delaunay condition
15549
+ // (p1 is inside the circumcircle of [p0, pl, pr]), flip them,
15550
+ // then do the same check/flip recursively for the new pair of triangles
15551
+ //
15552
+ // pl pl
15553
+ // /||\ / \
15554
+ // al/ || \bl al/ \a
15555
+ // / || \ / \
15556
+ // / a||b \ flip /___ar___\
15557
+ // p0\ || /p1 => p0\---bl---/p1
15558
+ // \ || / \ /
15559
+ // ar\ || /br b\ /br
15560
+ // \||/ \ /
15561
+ // pr pr
15562
+ const b = this._halfedges[a];
15563
+ if (b < 0) {
15564
+ return;
15565
+ }
15566
+ const a0 = a - (a % 3);
15567
+ const b0 = b - (b % 3);
15568
+ const al = a0 + ((a + 1) % 3);
15569
+ const ar = a0 + ((a + 2) % 3);
15570
+ const bl = b0 + ((b + 2) % 3);
15571
+ const br = b0 + ((b + 1) % 3);
15572
+ const p0 = this.triangles[ar];
15573
+ const pr = this.triangles[a];
15574
+ const pl = this.triangles[al];
15575
+ const p1 = this.triangles[bl];
15576
+ const { coords } = this;
15577
+ if (!inCircle(coords[2 * p0], coords[2 * p0 + 1], coords[2 * pr], coords[2 * pr + 1], coords[2 * pl], coords[2 * pl + 1], coords[2 * p1], coords[2 * p1 + 1])) {
15578
+ return;
15579
+ }
15580
+ const hal = this._halfedges[al];
15581
+ const har = this._halfedges[ar];
15582
+ const hbl = this._halfedges[bl];
15583
+ const hbr = this._halfedges[br];
15584
+ this._queueRemove(a0 / 3);
15585
+ this._queueRemove(b0 / 3);
15586
+ const t0 = this._addTriangle(p0, p1, pl, -1, hbl, hal, a0);
15587
+ const t1 = this._addTriangle(p1, p0, pr, t0, har, hbr, b0);
15588
+ this._legalize(t0 + 1);
15589
+ this._legalize(t1 + 2);
15590
+ }
15591
+ // handle a case where new vertex is on the edge of a triangle
15592
+ _handleCollinear(pn, a) {
15593
+ const a0 = a - (a % 3);
15594
+ const al = a0 + ((a + 1) % 3);
15595
+ const ar = a0 + ((a + 2) % 3);
15596
+ const p0 = this.triangles[ar];
15597
+ const pr = this.triangles[a];
15598
+ const pl = this.triangles[al];
15599
+ const hal = this._halfedges[al];
15600
+ const har = this._halfedges[ar];
15601
+ const b = this._halfedges[a];
15602
+ if (b < 0) {
15603
+ const t0 = this._addTriangle(pn, p0, pr, -1, har, -1, a0);
15604
+ const t1 = this._addTriangle(p0, pn, pl, t0, -1, hal);
15605
+ this._legalize(t0 + 1);
15606
+ this._legalize(t1 + 2);
15607
+ return;
15608
+ }
15609
+ const b0 = b - (b % 3);
15610
+ const bl = b0 + ((b + 2) % 3);
15611
+ const br = b0 + ((b + 1) % 3);
15612
+ const p1 = this.triangles[bl];
15613
+ const hbl = this._halfedges[bl];
15614
+ const hbr = this._halfedges[br];
15615
+ this._queueRemove(b0 / 3);
15616
+ const t0 = this._addTriangle(p0, pr, pn, har, -1, -1, a0);
15617
+ const t1 = this._addTriangle(pr, p1, pn, hbr, -1, t0 + 1, b0);
15618
+ const t2 = this._addTriangle(p1, pl, pn, hbl, -1, t1 + 1);
15619
+ const t3 = this._addTriangle(pl, p0, pn, hal, t0 + 2, t2 + 1);
15620
+ this._legalize(t0);
15621
+ this._legalize(t1);
15622
+ this._legalize(t2);
15623
+ this._legalize(t3);
15624
+ }
15625
+ // priority queue methods
15626
+ _queuePush(t, error, rms) {
15627
+ const i = this._queue.length;
15628
+ this._queueIndices[t] = i;
15629
+ this._queue.push(t);
15630
+ this._errors.push(error);
15631
+ this._rmsSum += rms;
15632
+ this._queueUp(i);
15633
+ }
15634
+ _queuePop() {
15635
+ const n = this._queue.length - 1;
15636
+ this._queueSwap(0, n);
15637
+ this._queueDown(0, n);
15638
+ return this._queuePopBack();
15639
+ }
15640
+ _queuePopBack() {
15641
+ const t = this._queue.pop();
15642
+ this._errors.pop();
15643
+ this._rmsSum -= this._rms[t];
15644
+ this._queueIndices[t] = -1;
15645
+ return t;
15646
+ }
15647
+ _queueRemove(t) {
15648
+ const i = this._queueIndices[t];
15649
+ if (i < 0) {
15650
+ const it = this._pending.indexOf(t);
15651
+ if (it !== -1) {
15652
+ this._pending[it] = this._pending[--this._pendingLen];
15653
+ }
15654
+ else {
15655
+ throw new Error('Broken triangulation (something went wrong).');
15656
+ }
15657
+ return;
15658
+ }
15659
+ const n = this._queue.length - 1;
15660
+ if (n !== i) {
15661
+ this._queueSwap(i, n);
15662
+ if (!this._queueDown(i, n)) {
15663
+ this._queueUp(i);
15664
+ }
15665
+ }
15666
+ this._queuePopBack();
15667
+ }
15668
+ _queueLess(i, j) {
15669
+ return this._errors[i] > this._errors[j];
15670
+ }
15671
+ _queueSwap(i, j) {
15672
+ const pi = this._queue[i];
15673
+ const pj = this._queue[j];
15674
+ this._queue[i] = pj;
15675
+ this._queue[j] = pi;
15676
+ this._queueIndices[pi] = j;
15677
+ this._queueIndices[pj] = i;
15678
+ const e = this._errors[i];
15679
+ this._errors[i] = this._errors[j];
15680
+ this._errors[j] = e;
15681
+ }
15682
+ _queueUp(j0) {
15683
+ let j = j0;
15684
+ while (true) {
15685
+ const i = (j - 1) >> 1;
15686
+ if (i === j || !this._queueLess(j, i)) {
15687
+ break;
15688
+ }
15689
+ this._queueSwap(i, j);
15690
+ j = i;
15691
+ }
15692
+ }
15693
+ _queueDown(i0, n) {
15694
+ let i = i0;
15695
+ while (true) {
15696
+ const j1 = 2 * i + 1;
15697
+ if (j1 >= n || j1 < 0) {
15698
+ break;
15699
+ }
15700
+ const j2 = j1 + 1;
15701
+ let j = j1;
15702
+ if (j2 < n && this._queueLess(j2, j1)) {
15703
+ j = j2;
15704
+ }
15705
+ if (!this._queueLess(j, i)) {
15706
+ break;
15707
+ }
15708
+ this._queueSwap(i, j);
15709
+ i = j;
15710
+ }
15711
+ return i > i0;
15712
+ }
15713
+ }
15714
+ function orient(ax, ay, bx, by, cx, cy) {
15715
+ return (bx - cx) * (ay - cy) - (by - cy) * (ax - cx);
15716
+ }
15717
+ function inCircle(ax, ay, bx, by, cx, cy, px, py) {
15718
+ const dx = ax - px;
15719
+ const dy = ay - py;
15720
+ const ex = bx - px;
15721
+ const ey = by - py;
15722
+ const fx = cx - px;
15723
+ const fy = cy - py;
15724
+ const ap = dx * dx + dy * dy;
15725
+ const bp = ex * ex + ey * ey;
15726
+ const cp = fx * fx + fy * fy;
15727
+ return dx * (ey * cp - bp * fy) - dy * (ex * cp - bp * fx) + ap * (ex * fy - ey * fx) < 0;
15728
+ }
15729
+
14988
15730
  /* eslint 'max-len': [1, { code: 105, comments: 999, ignoreStrings: true, ignoreUrls: true }] */
15731
+ const tesselator = 'martini';
14989
15732
  const DefaultGeoImageOptions = {
14990
15733
  type: 'image',
14991
15734
  format: 'uint8',
@@ -15032,6 +15775,7 @@ class GeoImage {
15032
15775
  getMap(input, options) {
15033
15776
  return __awaiter(this, void 0, void 0, function* () {
15034
15777
  const mergedOptions = Object.assign(Object.assign({}, DefaultGeoImageOptions), options);
15778
+ console.log('xxx_mergedOptions', mergedOptions);
15035
15779
  switch (mergedOptions.type) {
15036
15780
  case 'image':
15037
15781
  return this.getBitmap(input, mergedOptions);
@@ -15064,32 +15808,111 @@ class GeoImage {
15064
15808
  let channel = rasters[0];
15065
15809
  if (options.useChannel != null) {
15066
15810
  if (rasters[options.useChannel]) {
15067
- channel = rasters[options.useChannel];
15811
+ channel = rasters[options.useChannel]; // length = 65536
15068
15812
  }
15069
15813
  }
15070
- const canvas = document.createElement('canvas');
15071
- canvas.width = width;
15072
- canvas.height = height;
15073
- const c = canvas.getContext('2d');
15074
- const imageData = c.createImageData(width, height);
15814
+ // const canvas = document.createElement('canvas');
15815
+ // canvas.width = width;
15816
+ // canvas.height = height;
15817
+ // // const c = canvas.getContext('2d');
15818
+ // // const imageData = c!.createImageData(width, height);
15819
+ // const terrain = new Float32Array((width + 1) * (height + 1));
15820
+ const terrain = new Float32Array((width + 1) * (height + 1)); // length = 66049
15075
15821
  const numOfChannels = channel.length / (width * height);
15076
- const size = width * height * 4;
15822
+ // return mesh data
15823
+ // const size: number = width * height * 4;
15824
+ const size = width * height;
15825
+ console.log('xxx_size', size);
15077
15826
  let pixel = options.useChannel === null ? 0 : options.useChannel;
15078
- for (let i = 0; i < size; i += 4) {
15079
- // height image calculation based on:
15080
- // https://deck.gl/docs/api-reference/geo-layers/terrain-layer
15081
- const elevationValue = (options.noDataValue && channel[pixel] === options.noDataValue) ? options.terrainMinValue : channel[pixel] * options.multiplier;
15082
- const colorValue = Math.floor((elevationValue + 10000) / 0.1);
15083
- imageData.data[i] = Math.floor(colorValue / (256 * 256));
15084
- imageData.data[i + 1] = Math.floor((colorValue / 256) % 256);
15085
- imageData.data[i + 2] = colorValue % 256;
15086
- imageData.data[i + 3] = 255;
15087
- pixel += numOfChannels;
15827
+ for (let i = 0, y = 0; y < height; y++) {
15828
+ for (let x = 0; x < width; x++, i++) {
15829
+ const elevationValue = (options.noDataValue && channel[pixel] === options.noDataValue) ? options.terrainMinValue : channel[pixel] * options.multiplier;
15830
+ terrain[i + y] = elevationValue;
15831
+ pixel += numOfChannels;
15832
+ }
15088
15833
  }
15089
- c.putImageData(imageData, 0, 0);
15090
- const imageUrl = canvas.toDataURL('image/png');
15834
+ // for (let i = 0; i < size; i++) {
15835
+ // // height image calculation based on:
15836
+ // // https://deck.gl/docs/api-reference/geo-layers/terrain-layer
15837
+ // const elevationValue = (options.noDataValue && channel[pixel] === options.noDataValue) ? options.terrainMinValue : channel[pixel] * options.multiplier!;
15838
+ // terrain[i] = elevationValue;
15839
+ // // const colorValue = Math.floor((elevationValue + 10000) / 0.1);
15840
+ // // imageData.data[i] = Math.floor(colorValue / (256 * 256));
15841
+ // // imageData.data[i + 1] = Math.floor((colorValue / 256) % 256);
15842
+ // // imageData.data[i + 2] = colorValue % 256;
15843
+ // // imageData.data[i + 3] = 255;
15844
+ // pixel += numOfChannels;
15845
+ // }
15846
+ // c!.putImageData(imageData, 0, 0);
15847
+ // const imageUrl = canvas.toDataURL('image/png');
15091
15848
  // console.log('Heightmap generated.');
15092
- return imageUrl;
15849
+ console.log('xxx_terrain', terrain);
15850
+ if (terrain[0] > 0) {
15851
+ debugger;
15852
+ }
15853
+ {
15854
+ // backfill bottom border
15855
+ for (let i = (width + 1) * width, x = 0; x < width; x++, i++) {
15856
+ terrain[i] = terrain[i - width - 1];
15857
+ }
15858
+ // backfill right border
15859
+ for (let i = height, y = 0; y < height + 1; y++, i += height + 1) {
15860
+ terrain[i] = terrain[i - 1];
15861
+ }
15862
+ }
15863
+ // getMesh
15864
+ const { terrainSkirtHeight } = options;
15865
+ console.log('xxx_bounds_0', input.bounds);
15866
+ let mesh;
15867
+ switch (tesselator) {
15868
+ case 'martini':
15869
+ mesh = getMartiniTileMesh(terrainSkirtHeight, width, terrain);
15870
+ break;
15871
+ case 'delatin':
15872
+ mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
15873
+ break;
15874
+ default:
15875
+ if (width === height && !(height && (width - 1))) {
15876
+ // fixme get terrain to separate method
15877
+ // terrain = getTerrain(data, width, height, elevationDecoder, 'martini');
15878
+ mesh = getMartiniTileMesh(terrainSkirtHeight, width, terrain);
15879
+ }
15880
+ else {
15881
+ // fixme get terrain to separate method
15882
+ // terrain = getTerrain(data, width, height, elevationDecoder, 'delatin');
15883
+ mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
15884
+ }
15885
+ break;
15886
+ }
15887
+ // Martini
15888
+ // Martini
15889
+ // Delatin
15890
+ // Delatin
15891
+ const { vertices } = mesh;
15892
+ let { triangles } = mesh;
15893
+ let attributes = getMeshAttributes(vertices, terrain, width, height, input.bounds);
15894
+ // Compute bounding box before adding skirt so that z values are not skewed
15895
+ const boundingBox = getMeshBoundingBox(attributes);
15896
+ // FIXME uncomment and add skirt
15897
+ console.log('xxx_skirtHeight', terrainSkirtHeight);
15898
+ if (terrainSkirtHeight) {
15899
+ const { attributes: newAttributes, triangles: newTriangles } = addSkirt(attributes, triangles, terrainSkirtHeight);
15900
+ attributes = newAttributes;
15901
+ triangles = newTriangles;
15902
+ }
15903
+ return {
15904
+ // Data return by this loader implementation
15905
+ loaderData: {
15906
+ header: {},
15907
+ },
15908
+ header: {
15909
+ vertexCount: triangles.length,
15910
+ boundingBox,
15911
+ },
15912
+ mode: 4,
15913
+ indices: { value: Uint32Array.from(triangles), size: 1 },
15914
+ attributes,
15915
+ };
15093
15916
  });
15094
15917
  }
15095
15918
  getBitmap(input, options) {
@@ -15126,6 +15949,7 @@ class GeoImage {
15126
15949
  let b;
15127
15950
  let a;
15128
15951
  const size = width * height * 4;
15952
+ // const size = width * height;
15129
15953
  if (!options.noDataValue) {
15130
15954
  console.log('Missing noData value. Raster might be displayed incorrectly.');
15131
15955
  }
@@ -15346,6 +16170,68 @@ class GeoImage {
15346
16170
  return noDataValue !== undefined && pixels.every((pixel) => pixel === noDataValue);
15347
16171
  }
15348
16172
  }
16173
+ //
16174
+ //
16175
+ //
16176
+ /**
16177
+ * Get Martini generated vertices and triangles
16178
+ *
16179
+ * @param {number} meshMaxError threshold for simplifying mesh
16180
+ * @param {number} width width of the input data
16181
+ * @param {number[] | Float32Array} terrain elevation data
16182
+ * @returns {{vertices: Uint16Array, triangles: Uint32Array}} vertices and triangles data
16183
+ */
16184
+ function getMartiniTileMesh(meshMaxError, width, terrain) {
16185
+ const gridSize = width + 1;
16186
+ const martini = new Martini(gridSize);
16187
+ const tile = martini.createTile(terrain);
16188
+ const { vertices, triangles } = tile.getMesh(meshMaxError);
16189
+ return { vertices, triangles };
16190
+ }
16191
+ function getMeshAttributes(vertices, terrain, width, height, bounds) {
16192
+ const gridSize = width + 1;
16193
+ const numOfVerticies = vertices.length / 2;
16194
+ // vec3. x, y in pixels, z in meters
16195
+ const positions = new Float32Array(numOfVerticies * 3);
16196
+ // vec2. 1 to 1 relationship with position. represents the uv on the texture image. 0,0 to 1,1.
16197
+ const texCoords = new Float32Array(numOfVerticies * 2);
16198
+ console.log('xxx_bounds', bounds);
16199
+ const [minX, minY, maxX, maxY] = bounds || [0, 0, width, height];
16200
+ const xScale = (maxX - minX) / width;
16201
+ const yScale = (maxY - minY) / height;
16202
+ for (let i = 0; i < numOfVerticies; i++) {
16203
+ const x = vertices[i * 2];
16204
+ const y = vertices[i * 2 + 1];
16205
+ const pixelIdx = y * gridSize + x;
16206
+ positions[3 * i + 0] = x * xScale + minX;
16207
+ positions[3 * i + 1] = -y * yScale + maxY;
16208
+ positions[3 * i + 2] = terrain[pixelIdx];
16209
+ texCoords[2 * i + 0] = x / width;
16210
+ texCoords[2 * i + 1] = y / height;
16211
+ }
16212
+ return {
16213
+ POSITION: { value: positions, size: 3 },
16214
+ TEXCOORD_0: { value: texCoords, size: 2 },
16215
+ // NORMAL: {}, - optional, but creates the high poly look with lighting
16216
+ };
16217
+ }
16218
+ /**
16219
+ * Get Delatin generated vertices and triangles
16220
+ *
16221
+ * @param {number} meshMaxError threshold for simplifying mesh
16222
+ * @param {number} width width of the input data array
16223
+ * @param {number} height height of the input data array
16224
+ * @param {number[] | Float32Array} terrain elevation data
16225
+ * @returns {{vertices: number[], triangles: number[]}} vertices and triangles data
16226
+ */
16227
+ function getDelatinTileMesh(meshMaxError, width, height, terrain) {
16228
+ const tin = new Delatin(terrain, width + 1, height + 1);
16229
+ tin.run(meshMaxError);
16230
+ // @ts-expect-error
16231
+ const { coords, triangles } = tin;
16232
+ const vertices = coords;
16233
+ return { vertices, triangles };
16234
+ }
15349
16235
 
15350
16236
  const EARTH_CIRCUMFERENCE = 40075000.0;
15351
16237
  const EARTH_HALF_CIRCUMFERENCE = 20037500.0;
@@ -15439,7 +16325,7 @@ class CogTiles {
15439
16325
  const cartographicPositionAdjusted = [cartographicPosition[0], -cartographicPosition[1]];
15440
16326
  return cartographicPositionAdjusted;
15441
16327
  }
15442
- getTile(x, y, z) {
16328
+ getTile(x, y, z, bounds) {
15443
16329
  return __awaiter(this, void 0, void 0, function* () {
15444
16330
  const wantedMpp = this.getResolutionFromZoomLevel(this.tileSize, z);
15445
16331
  const img = this.cog.getImageByResolution(wantedMpp);
@@ -15485,7 +16371,7 @@ class CogTiles {
15485
16371
  // console.log("Bits per sample: " + bitsPerSample)
15486
16372
  // console.log("Single channel pixel format: " + bitsPerSample/)
15487
16373
  if (x - ox >= 0 && y - oy >= 0 && x - ox < tilesX && y - oy < tilesY) {
15488
- // console.log("getting tile: " + [x - ox, y - oy]);
16374
+ // console.log(`getting tile: ${[x - ox, y - oy]}`);
15489
16375
  const tile = yield img.getTile((x - ox), (y - oy));
15490
16376
  // console.time("Request to data time: ")
15491
16377
  switch (img.compression) {
@@ -15531,10 +16417,12 @@ class CogTiles {
15531
16417
  default: decompressedFormatted = null;
15532
16418
  }
15533
16419
  // console.log(decompressedFormatted)
16420
+ // const { meshMaxError, bounds, elevationDecoder } = this.options;
15534
16421
  decompressed = yield this.geo.getMap({
15535
16422
  rasters: [decompressedFormatted],
15536
16423
  width: this.tileSize,
15537
16424
  height: this.tileSize,
16425
+ bounds,
15538
16426
  }, this.options);
15539
16427
  // console.log(decompressed.length)
15540
16428
  return decompressed;
@@ -15776,7 +16664,7 @@ class CogTerrainLayer extends CompositeLayer {
15776
16664
  const cog = yield this.terrainCogTiles.initializeCog(terrainUrl);
15777
16665
  this.tileSize = this.terrainCogTiles.getTileSize(cog);
15778
16666
  const zoomRange = this.terrainCogTiles.getZoomRange(cog);
15779
- [this.minZoom, this.maxZoom] = zoomRange;
16667
+ [this.props.minZoom, this.props.maxZoom] = zoomRange;
15780
16668
  this.setState({ initialized: true });
15781
16669
  });
15782
16670
  }