@gisatcz/deckgl-geolib 1.12.0-dev.13 → 1.12.0-dev.15
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/cjs/index.js +584 -650
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.min.js +3 -3
- package/dist/cjs/index.min.js.map +1 -1
- package/dist/cjs/types/core/CogTiles.d.ts +7 -23
- package/dist/cjs/types/layers/CogBitmapLayer.d.ts +3 -5
- package/dist/cjs/types/layers/CogTerrainLayer.d.ts +11 -6
- package/dist/esm/index.js +584 -650
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +3 -3
- package/dist/esm/index.min.js.map +1 -1
- package/dist/esm/types/core/CogTiles.d.ts +7 -23
- package/dist/esm/types/layers/CogBitmapLayer.d.ts +3 -5
- package/dist/esm/types/layers/CogTerrainLayer.d.ts +11 -6
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -9,38 +9,6 @@ var schema = require('@loaders.gl/schema');
|
|
|
9
9
|
var loaderUtils = require('@loaders.gl/loader-utils');
|
|
10
10
|
var meshLayers = require('@deck.gl/mesh-layers');
|
|
11
11
|
|
|
12
|
-
/******************************************************************************
|
|
13
|
-
Copyright (c) Microsoft Corporation.
|
|
14
|
-
|
|
15
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
16
|
-
purpose with or without fee is hereby granted.
|
|
17
|
-
|
|
18
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
19
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
20
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
21
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
22
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
23
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
24
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
25
|
-
***************************************************************************** */
|
|
26
|
-
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
30
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
31
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
32
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
33
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
34
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
35
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
40
|
-
var e = new Error(message);
|
|
41
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
12
|
/* eslint-disable no-restricted-globals, no-restricted-syntax */
|
|
45
13
|
/* global SharedArrayBuffer */
|
|
46
14
|
|
|
@@ -4443,6 +4411,29 @@ class Tile {
|
|
|
4443
4411
|
// loaders.gl
|
|
4444
4412
|
// SPDX-License-Identifier: MIT
|
|
4445
4413
|
// Copyright (c) vis.gl contributors
|
|
4414
|
+
/**
|
|
4415
|
+
* Get geometry edges that located on a border of the mesh
|
|
4416
|
+
* @param {object} indices - edge indices from quantized mesh data
|
|
4417
|
+
* @param {TypedArray} position - position attribute geometry data
|
|
4418
|
+
* @returns {number[][]} - outside edges data
|
|
4419
|
+
*/
|
|
4420
|
+
function getOutsideEdgesFromIndices(indices, position) {
|
|
4421
|
+
// Sort skirt indices to create adjacent triangles
|
|
4422
|
+
indices.westIndices.sort((a, b) => position[3 * a + 1] - position[3 * b + 1]);
|
|
4423
|
+
// Reverse (b - a) to match triangle winding
|
|
4424
|
+
indices.eastIndices.sort((a, b) => position[3 * b + 1] - position[3 * a + 1]);
|
|
4425
|
+
indices.southIndices.sort((a, b) => position[3 * b] - position[3 * a]);
|
|
4426
|
+
// Reverse (b - a) to match triangle winding
|
|
4427
|
+
indices.northIndices.sort((a, b) => position[3 * a] - position[3 * b]);
|
|
4428
|
+
const edges = [];
|
|
4429
|
+
for (const index in indices) {
|
|
4430
|
+
const indexGroup = indices[index];
|
|
4431
|
+
for (let i = 0; i < indexGroup.length - 1; i++) {
|
|
4432
|
+
edges.push([indexGroup[i], indexGroup[i + 1]]);
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
return edges;
|
|
4436
|
+
}
|
|
4446
4437
|
/**
|
|
4447
4438
|
* Add skirt to existing mesh
|
|
4448
4439
|
* @param {object} attributes - POSITION and TEXCOOD_0 attributes data
|
|
@@ -4488,7 +4479,6 @@ function addSkirt(attributes, triangles, skirtHeight, outsideIndices) {
|
|
|
4488
4479
|
* @returns {number[][]} - outside edges data
|
|
4489
4480
|
*/
|
|
4490
4481
|
function getOutsideEdgesFromTriangles(triangles) {
|
|
4491
|
-
var _a, _b;
|
|
4492
4482
|
const edges = [];
|
|
4493
4483
|
for (let i = 0; i < triangles.length; i += 3) {
|
|
4494
4484
|
edges.push([triangles[i], triangles[i + 1]]);
|
|
@@ -4499,7 +4489,7 @@ function getOutsideEdgesFromTriangles(triangles) {
|
|
|
4499
4489
|
const outsideEdges = [];
|
|
4500
4490
|
let index = 0;
|
|
4501
4491
|
while (index < edges.length) {
|
|
4502
|
-
if (edges[index][0] ===
|
|
4492
|
+
if (edges[index][0] === edges[index + 1]?.[1] && edges[index][1] === edges[index + 1]?.[0]) {
|
|
4503
4493
|
index += 2;
|
|
4504
4494
|
}
|
|
4505
4495
|
else {
|
|
@@ -4509,29 +4499,6 @@ function getOutsideEdgesFromTriangles(triangles) {
|
|
|
4509
4499
|
}
|
|
4510
4500
|
return outsideEdges;
|
|
4511
4501
|
}
|
|
4512
|
-
/**
|
|
4513
|
-
* Get geometry edges that located on a border of the mesh
|
|
4514
|
-
* @param {object} indices - edge indices from quantized mesh data
|
|
4515
|
-
* @param {TypedArray} position - position attribute geometry data
|
|
4516
|
-
* @returns {number[][]} - outside edges data
|
|
4517
|
-
*/
|
|
4518
|
-
function getOutsideEdgesFromIndices(indices, position) {
|
|
4519
|
-
// Sort skirt indices to create adjacent triangles
|
|
4520
|
-
indices.westIndices.sort((a, b) => position[3 * a + 1] - position[3 * b + 1]);
|
|
4521
|
-
// Reverse (b - a) to match triangle winding
|
|
4522
|
-
indices.eastIndices.sort((a, b) => position[3 * b + 1] - position[3 * a + 1]);
|
|
4523
|
-
indices.southIndices.sort((a, b) => position[3 * b] - position[3 * a]);
|
|
4524
|
-
// Reverse (b - a) to match triangle winding
|
|
4525
|
-
indices.northIndices.sort((a, b) => position[3 * a] - position[3 * b]);
|
|
4526
|
-
const edges = [];
|
|
4527
|
-
for (const index in indices) {
|
|
4528
|
-
const indexGroup = indices[index];
|
|
4529
|
-
for (let i = 0; i < indexGroup.length - 1; i++) {
|
|
4530
|
-
edges.push([indexGroup[i], indexGroup[i + 1]]);
|
|
4531
|
-
}
|
|
4532
|
-
}
|
|
4533
|
-
return edges;
|
|
4534
|
-
}
|
|
4535
4502
|
/**
|
|
4536
4503
|
* Get geometry edges that located on a border of the mesh
|
|
4537
4504
|
* @param {object} args
|
|
@@ -4572,7 +4539,12 @@ function updateAttributesForNewEdge({ edge, edgeIndex, attributes, skirtHeight,
|
|
|
4572
4539
|
// Copyright (c) vis.gl contributors
|
|
4573
4540
|
// ISC License
|
|
4574
4541
|
// Copyright(c) 2019, Michael Fogleman, Vladimir Agafonkin
|
|
4542
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
4575
4543
|
// @ts-nocheck
|
|
4544
|
+
/* eslint-enable @typescript-eslint/ban-ts-comment */
|
|
4545
|
+
function orient(ax, ay, bx, by, cx, cy) {
|
|
4546
|
+
return (bx - cx) * (ay - cy) - (by - cy) * (ax - cx);
|
|
4547
|
+
}
|
|
4576
4548
|
/* eslint-disable complexity, max-params, max-statements, max-depth, no-constant-condition */
|
|
4577
4549
|
class Delatin {
|
|
4578
4550
|
constructor(data, width, height = width) {
|
|
@@ -4960,9 +4932,6 @@ class Delatin {
|
|
|
4960
4932
|
return i > i0;
|
|
4961
4933
|
}
|
|
4962
4934
|
}
|
|
4963
|
-
function orient(ax, ay, bx, by, cx, cy) {
|
|
4964
|
-
return (bx - cx) * (ay - cy) - (by - cy) * (ax - cx);
|
|
4965
|
-
}
|
|
4966
4935
|
function inCircle(ax, ay, bx, by, cx, cy, px, py) {
|
|
4967
4936
|
const dx = ax - px;
|
|
4968
4937
|
const dy = ay - py;
|
|
@@ -4976,7 +4945,7 @@ function inCircle(ax, ay, bx, by, cx, cy, px, py) {
|
|
|
4976
4945
|
return dx * (ey * cp - bp * fy) - dy * (ex * cp - bp * fx) + ap * (ex * fy - ey * fx) < 0;
|
|
4977
4946
|
}
|
|
4978
4947
|
|
|
4979
|
-
|
|
4948
|
+
// import { ExtentsLeftBottomRightTop } from '@deck.gl/core/utils/positions';
|
|
4980
4949
|
// FIXME - tesselator as a parameter
|
|
4981
4950
|
const tesselator = 'martini';
|
|
4982
4951
|
const DefaultGeoImageOptions = {
|
|
@@ -5011,279 +4980,264 @@ const DefaultGeoImageOptions = {
|
|
|
5011
4980
|
planarConfig: undefined,
|
|
5012
4981
|
};
|
|
5013
4982
|
class GeoImage {
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
}
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
return this.getHeightmap(input, mergedOptions, meshMaxError);
|
|
5035
|
-
default:
|
|
5036
|
-
return null;
|
|
5037
|
-
}
|
|
5038
|
-
});
|
|
4983
|
+
data;
|
|
4984
|
+
scale = (num, inMin, inMax, outMin, outMax) => ((num - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
|
|
4985
|
+
async setUrl(url) {
|
|
4986
|
+
// TODO - not tested
|
|
4987
|
+
const response = await fetch(url);
|
|
4988
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
4989
|
+
const tiff = await fromArrayBuffer(arrayBuffer);
|
|
4990
|
+
const data = await tiff.getImage(0);
|
|
4991
|
+
this.data = data;
|
|
4992
|
+
}
|
|
4993
|
+
async getMap(input, options, meshMaxError) {
|
|
4994
|
+
const mergedOptions = { ...DefaultGeoImageOptions, ...options };
|
|
4995
|
+
switch (mergedOptions.type) {
|
|
4996
|
+
case 'image':
|
|
4997
|
+
return this.getBitmap(input, mergedOptions);
|
|
4998
|
+
case 'terrain':
|
|
4999
|
+
return this.getHeightmap(input, mergedOptions, meshMaxError);
|
|
5000
|
+
default:
|
|
5001
|
+
return null;
|
|
5002
|
+
}
|
|
5039
5003
|
}
|
|
5040
5004
|
// GetHeightmap uses only "useChannel" and "multiplier" options
|
|
5041
|
-
getHeightmap(input, options, meshMaxError) {
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5005
|
+
async getHeightmap(input, options, meshMaxError) {
|
|
5006
|
+
let rasters = [];
|
|
5007
|
+
let width;
|
|
5008
|
+
let height;
|
|
5009
|
+
if (typeof (input) === 'string') {
|
|
5010
|
+
// TODO not tested
|
|
5011
|
+
// input is type of object
|
|
5012
|
+
await this.setUrl(input);
|
|
5013
|
+
rasters = (await this.data.readRasters());
|
|
5014
|
+
width = this.data.getWidth();
|
|
5015
|
+
height = this.data.getHeight();
|
|
5016
|
+
}
|
|
5017
|
+
else {
|
|
5018
|
+
rasters = input.rasters;
|
|
5019
|
+
width = input.width;
|
|
5020
|
+
height = input.height;
|
|
5021
|
+
}
|
|
5022
|
+
const optionsLocal = { ...options };
|
|
5023
|
+
let channel = rasters[0];
|
|
5024
|
+
optionsLocal.useChannelIndex ??= optionsLocal.useChannel == null ? null : optionsLocal.useChannel - 1;
|
|
5025
|
+
if (options.useChannelIndex != null) {
|
|
5026
|
+
if (rasters[optionsLocal.useChannelIndex]) {
|
|
5027
|
+
channel = rasters[optionsLocal.useChannelIndex];
|
|
5059
5028
|
}
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5029
|
+
}
|
|
5030
|
+
const terrain = new Float32Array((width + 1) * (height + 1));
|
|
5031
|
+
const numOfChannels = channel.length / (width * height);
|
|
5032
|
+
let pixel = options.useChannelIndex === null ? 0 : options.useChannelIndex;
|
|
5033
|
+
for (let i = 0, y = 0; y < height; y++) {
|
|
5034
|
+
for (let x = 0; x < width; x++, i++) {
|
|
5035
|
+
const elevationValue = (options.noDataValue && channel[pixel] === options.noDataValue) ? options.terrainMinValue : channel[pixel] * options.multiplier;
|
|
5036
|
+
terrain[i + y] = elevationValue;
|
|
5037
|
+
pixel += numOfChannels;
|
|
5067
5038
|
}
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
for (let i =
|
|
5072
|
-
|
|
5073
|
-
const elevationValue = (options.noDataValue && channel[pixel] === options.noDataValue) ? options.terrainMinValue : channel[pixel] * options.multiplier;
|
|
5074
|
-
terrain[i + y] = elevationValue;
|
|
5075
|
-
pixel += numOfChannels;
|
|
5076
|
-
}
|
|
5039
|
+
}
|
|
5040
|
+
{
|
|
5041
|
+
// backfill bottom border
|
|
5042
|
+
for (let i = (width + 1) * width, x = 0; x < width; x++, i++) {
|
|
5043
|
+
terrain[i] = terrain[i - width - 1];
|
|
5077
5044
|
}
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
terrain[i] = terrain[i - width - 1];
|
|
5082
|
-
}
|
|
5083
|
-
// backfill right border
|
|
5084
|
-
for (let i = height, y = 0; y < height + 1; y++, i += height + 1) {
|
|
5085
|
-
terrain[i] = terrain[i - 1];
|
|
5086
|
-
}
|
|
5045
|
+
// backfill right border
|
|
5046
|
+
for (let i = height, y = 0; y < height + 1; y++, i += height + 1) {
|
|
5047
|
+
terrain[i] = terrain[i - 1];
|
|
5087
5048
|
}
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5049
|
+
}
|
|
5050
|
+
// getMesh
|
|
5051
|
+
const { terrainSkirtHeight } = options;
|
|
5052
|
+
let mesh;
|
|
5053
|
+
switch (tesselator) {
|
|
5054
|
+
case 'martini':
|
|
5055
|
+
mesh = getMartiniTileMesh(meshMaxError, width, terrain);
|
|
5056
|
+
break;
|
|
5057
|
+
case 'delatin':
|
|
5058
|
+
mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
|
|
5059
|
+
break;
|
|
5060
|
+
default:
|
|
5061
|
+
if (width === height && !(height && (width - 1))) {
|
|
5062
|
+
// fixme get terrain to separate method
|
|
5063
|
+
// terrain = getTerrain(data, width, height, elevationDecoder, 'martini');
|
|
5093
5064
|
mesh = getMartiniTileMesh(meshMaxError, width, terrain);
|
|
5094
|
-
|
|
5095
|
-
|
|
5065
|
+
}
|
|
5066
|
+
else {
|
|
5067
|
+
// fixme get terrain to separate method
|
|
5068
|
+
// terrain = getTerrain(data, width, height, elevationDecoder, 'delatin');
|
|
5096
5069
|
mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
|
|
5097
|
-
break;
|
|
5098
|
-
default:
|
|
5099
|
-
if (width === height && !(height && (width - 1))) {
|
|
5100
|
-
// fixme get terrain to separate method
|
|
5101
|
-
// terrain = getTerrain(data, width, height, elevationDecoder, 'martini');
|
|
5102
|
-
mesh = getMartiniTileMesh(meshMaxError, width, terrain);
|
|
5103
|
-
}
|
|
5104
|
-
else {
|
|
5105
|
-
// fixme get terrain to separate method
|
|
5106
|
-
// terrain = getTerrain(data, width, height, elevationDecoder, 'delatin');
|
|
5107
|
-
mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
|
|
5108
|
-
}
|
|
5109
|
-
break;
|
|
5110
|
-
}
|
|
5111
|
-
// Martini
|
|
5112
|
-
// Martini
|
|
5113
|
-
// Delatin
|
|
5114
|
-
// Delatin
|
|
5115
|
-
const { vertices } = mesh;
|
|
5116
|
-
let { triangles } = mesh;
|
|
5117
|
-
let attributes = getMeshAttributes(vertices, terrain, width, height, input.bounds);
|
|
5118
|
-
// Compute bounding box before adding skirt so that z values are not skewed
|
|
5119
|
-
const boundingBox = schema.getMeshBoundingBox(attributes);
|
|
5120
|
-
if (terrainSkirtHeight) {
|
|
5121
|
-
const { attributes: newAttributes, triangles: newTriangles } = addSkirt(attributes, triangles, terrainSkirtHeight);
|
|
5122
|
-
attributes = newAttributes;
|
|
5123
|
-
triangles = newTriangles;
|
|
5124
|
-
}
|
|
5125
|
-
return {
|
|
5126
|
-
// Data return by this loader implementation
|
|
5127
|
-
loaderData: {
|
|
5128
|
-
header: {},
|
|
5129
|
-
},
|
|
5130
|
-
header: {
|
|
5131
|
-
vertexCount: triangles.length,
|
|
5132
|
-
boundingBox,
|
|
5133
|
-
},
|
|
5134
|
-
mode: 4, // TRIANGLES
|
|
5135
|
-
indices: { value: Uint32Array.from(triangles), size: 1 },
|
|
5136
|
-
attributes,
|
|
5137
|
-
};
|
|
5138
|
-
});
|
|
5139
|
-
}
|
|
5140
|
-
getBitmap(input, options) {
|
|
5141
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
5142
|
-
var _a;
|
|
5143
|
-
// console.time('bitmap-generated-in');
|
|
5144
|
-
// const optionsLocal = { ...options };
|
|
5145
|
-
const optionsLocal = Object.assign({}, options);
|
|
5146
|
-
let rasters = [];
|
|
5147
|
-
let channels;
|
|
5148
|
-
let width;
|
|
5149
|
-
let height;
|
|
5150
|
-
if (typeof (input) === 'string') {
|
|
5151
|
-
// TODO not tested
|
|
5152
|
-
// input is type of object
|
|
5153
|
-
yield this.setUrl(input);
|
|
5154
|
-
rasters = (yield this.data.readRasters());
|
|
5155
|
-
channels = rasters.length;
|
|
5156
|
-
width = this.data.getWidth();
|
|
5157
|
-
height = this.data.getHeight();
|
|
5158
|
-
}
|
|
5159
|
-
else {
|
|
5160
|
-
rasters = input.rasters;
|
|
5161
|
-
channels = rasters.length;
|
|
5162
|
-
width = input.width;
|
|
5163
|
-
height = input.height;
|
|
5164
|
-
}
|
|
5165
|
-
const canvas = document.createElement('canvas');
|
|
5166
|
-
canvas.width = width;
|
|
5167
|
-
canvas.height = height;
|
|
5168
|
-
const c = canvas.getContext('2d');
|
|
5169
|
-
const imageData = c.createImageData(width, height);
|
|
5170
|
-
let r;
|
|
5171
|
-
let g;
|
|
5172
|
-
let b;
|
|
5173
|
-
let a;
|
|
5174
|
-
const size = width * height * 4;
|
|
5175
|
-
// const size = width * height;
|
|
5176
|
-
// if (!options.noDataValue) {
|
|
5177
|
-
// console.log('Missing noData value. Raster might be displayed incorrectly.');
|
|
5178
|
-
// }
|
|
5179
|
-
optionsLocal.unidentifiedColor = this.getColorFromChromaType(optionsLocal.unidentifiedColor);
|
|
5180
|
-
optionsLocal.nullColor = this.getColorFromChromaType(optionsLocal.nullColor);
|
|
5181
|
-
optionsLocal.clippedColor = this.getColorFromChromaType(optionsLocal.clippedColor);
|
|
5182
|
-
optionsLocal.color = this.getColorFromChromaType(optionsLocal.color);
|
|
5183
|
-
(_a = optionsLocal.useChannelIndex) !== null && _a !== void 0 ? _a : (optionsLocal.useChannelIndex = options.useChannel === null ? null : options.useChannel - 1);
|
|
5184
|
-
// console.log(rasters[0])
|
|
5185
|
-
/* console.log("raster 0 length: " + rasters[0].length)
|
|
5186
|
-
console.log("image width: " + width)
|
|
5187
|
-
console.log("channels: " + channels)
|
|
5188
|
-
console.log("format: " + rasters[0].length / (width * height))
|
|
5189
|
-
*/
|
|
5190
|
-
if (optionsLocal.useChannelIndex == null) {
|
|
5191
|
-
if (channels === 1) {
|
|
5192
|
-
if (rasters[0].length / (width * height) === 1) {
|
|
5193
|
-
const channel = rasters[0];
|
|
5194
|
-
// AUTO RANGE
|
|
5195
|
-
if (optionsLocal.useAutoRange) {
|
|
5196
|
-
optionsLocal.colorScaleValueRange = this.getMinMax(channel, optionsLocal);
|
|
5197
|
-
// console.log('data min: ' + optionsLocal.rangeMin + ', max: ' + optionsLocal.rangeMax);
|
|
5198
|
-
}
|
|
5199
|
-
// SINGLE CHANNEL
|
|
5200
|
-
const colorData = this.getColorValue(channel, optionsLocal, size);
|
|
5201
|
-
colorData.forEach((value, index) => {
|
|
5202
|
-
imageData.data[index] = value;
|
|
5203
|
-
});
|
|
5204
|
-
}
|
|
5205
|
-
// RGB values in one channel
|
|
5206
|
-
if (rasters[0].length / (width * height) === 3) {
|
|
5207
|
-
// console.log("geoImage: " + "RGB 1 array of length: " + rasters[0].length);
|
|
5208
|
-
let pixel = 0;
|
|
5209
|
-
for (let idx = 0; idx < size; idx += 4) {
|
|
5210
|
-
const rgbColor = [rasters[0][pixel], rasters[0][pixel + 1], rasters[0][pixel + 2]];
|
|
5211
|
-
const rgbaColor = this.hasPixelsNoData(rgbColor, optionsLocal.noDataValue)
|
|
5212
|
-
? optionsLocal.nullColor
|
|
5213
|
-
: [...rgbColor, Math.floor(optionsLocal.alpha * 2.55)];
|
|
5214
|
-
// eslint-disable-next-line max-len
|
|
5215
|
-
[imageData.data[idx], imageData.data[idx + 1], imageData.data[idx + 2], imageData.data[idx + 3]] = rgbaColor;
|
|
5216
|
-
pixel += 3;
|
|
5217
|
-
}
|
|
5218
|
-
}
|
|
5219
|
-
if (rasters[0].length / (width * height) === 4) {
|
|
5220
|
-
// console.log("geoImage: " + "RGBA 1 array");
|
|
5221
|
-
rasters[0].forEach((value, index) => {
|
|
5222
|
-
imageData.data[index] = value;
|
|
5223
|
-
});
|
|
5224
|
-
}
|
|
5225
5070
|
}
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5071
|
+
break;
|
|
5072
|
+
}
|
|
5073
|
+
const { vertices } = mesh;
|
|
5074
|
+
let { triangles } = mesh;
|
|
5075
|
+
let attributes = getMeshAttributes(vertices, terrain, width, height, input.bounds);
|
|
5076
|
+
// Compute bounding box before adding skirt so that z values are not skewed
|
|
5077
|
+
const boundingBox = schema.getMeshBoundingBox(attributes);
|
|
5078
|
+
if (terrainSkirtHeight) {
|
|
5079
|
+
const { attributes: newAttributes, triangles: newTriangles } = addSkirt(attributes, triangles, terrainSkirtHeight);
|
|
5080
|
+
attributes = newAttributes;
|
|
5081
|
+
triangles = newTriangles;
|
|
5082
|
+
}
|
|
5083
|
+
return {
|
|
5084
|
+
// Data return by this loader implementation
|
|
5085
|
+
loaderData: {
|
|
5086
|
+
header: {},
|
|
5087
|
+
},
|
|
5088
|
+
header: {
|
|
5089
|
+
vertexCount: triangles.length,
|
|
5090
|
+
boundingBox,
|
|
5091
|
+
},
|
|
5092
|
+
mode: 4, // TRIANGLES
|
|
5093
|
+
indices: { value: Uint32Array.from(triangles), size: 1 },
|
|
5094
|
+
attributes,
|
|
5095
|
+
};
|
|
5096
|
+
}
|
|
5097
|
+
async getBitmap(input, options) {
|
|
5098
|
+
// console.time('bitmap-generated-in');
|
|
5099
|
+
// const optionsLocal = { ...options };
|
|
5100
|
+
const optionsLocal = { ...options };
|
|
5101
|
+
let rasters = [];
|
|
5102
|
+
let channels;
|
|
5103
|
+
let width;
|
|
5104
|
+
let height;
|
|
5105
|
+
if (typeof (input) === 'string') {
|
|
5106
|
+
// TODO not tested
|
|
5107
|
+
// input is type of object
|
|
5108
|
+
await this.setUrl(input);
|
|
5109
|
+
rasters = (await this.data.readRasters());
|
|
5110
|
+
channels = rasters.length;
|
|
5111
|
+
width = this.data.getWidth();
|
|
5112
|
+
height = this.data.getHeight();
|
|
5113
|
+
}
|
|
5114
|
+
else {
|
|
5115
|
+
rasters = input.rasters;
|
|
5116
|
+
channels = rasters.length;
|
|
5117
|
+
width = input.width;
|
|
5118
|
+
height = input.height;
|
|
5119
|
+
}
|
|
5120
|
+
const canvas = document.createElement('canvas');
|
|
5121
|
+
canvas.width = width;
|
|
5122
|
+
canvas.height = height;
|
|
5123
|
+
const c = canvas.getContext('2d');
|
|
5124
|
+
const imageData = c.createImageData(width, height);
|
|
5125
|
+
let r;
|
|
5126
|
+
let g;
|
|
5127
|
+
let b;
|
|
5128
|
+
let a;
|
|
5129
|
+
const size = width * height * 4;
|
|
5130
|
+
// const size = width * height;
|
|
5131
|
+
// if (!options.noDataValue) {
|
|
5132
|
+
// console.log('Missing noData value. Raster might be displayed incorrectly.');
|
|
5133
|
+
// }
|
|
5134
|
+
optionsLocal.unidentifiedColor = this.getColorFromChromaType(optionsLocal.unidentifiedColor);
|
|
5135
|
+
optionsLocal.nullColor = this.getColorFromChromaType(optionsLocal.nullColor);
|
|
5136
|
+
optionsLocal.clippedColor = this.getColorFromChromaType(optionsLocal.clippedColor);
|
|
5137
|
+
optionsLocal.color = this.getColorFromChromaType(optionsLocal.color);
|
|
5138
|
+
optionsLocal.useChannelIndex ??= options.useChannel === null ? null : options.useChannel - 1;
|
|
5139
|
+
// console.log(rasters[0])
|
|
5140
|
+
/* console.log("raster 0 length: " + rasters[0].length)
|
|
5141
|
+
console.log("image width: " + width)
|
|
5142
|
+
console.log("channels: " + channels)
|
|
5143
|
+
console.log("format: " + rasters[0].length / (width * height))
|
|
5144
|
+
*/
|
|
5145
|
+
if (optionsLocal.useChannelIndex == null) {
|
|
5146
|
+
if (channels === 1) {
|
|
5147
|
+
if (rasters[0].length / (width * height) === 1) {
|
|
5148
|
+
const channel = rasters[0];
|
|
5149
|
+
// AUTO RANGE
|
|
5150
|
+
if (optionsLocal.useAutoRange) {
|
|
5151
|
+
optionsLocal.colorScaleValueRange = this.getMinMax(channel, optionsLocal);
|
|
5152
|
+
// console.log('data min: ' + optionsLocal.rangeMin + ', max: ' + optionsLocal.rangeMax);
|
|
5239
5153
|
}
|
|
5154
|
+
// SINGLE CHANNEL
|
|
5155
|
+
const colorData = this.getColorValue(channel, optionsLocal, size);
|
|
5156
|
+
colorData.forEach((value, index) => {
|
|
5157
|
+
imageData.data[index] = value;
|
|
5158
|
+
});
|
|
5240
5159
|
}
|
|
5241
|
-
|
|
5242
|
-
|
|
5160
|
+
// RGB values in one channel
|
|
5161
|
+
if (rasters[0].length / (width * height) === 3) {
|
|
5162
|
+
// console.log("geoImage: " + "RGB 1 array of length: " + rasters[0].length);
|
|
5243
5163
|
let pixel = 0;
|
|
5244
|
-
for (let
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
imageData.data[
|
|
5251
|
-
|
|
5252
|
-
imageData.data[i + 3] = a;
|
|
5253
|
-
pixel += 1;
|
|
5164
|
+
for (let idx = 0; idx < size; idx += 4) {
|
|
5165
|
+
const rgbColor = [rasters[0][pixel], rasters[0][pixel + 1], rasters[0][pixel + 2]];
|
|
5166
|
+
const rgbaColor = this.hasPixelsNoData(rgbColor, optionsLocal.noDataValue)
|
|
5167
|
+
? optionsLocal.nullColor
|
|
5168
|
+
: [...rgbColor, Math.floor(optionsLocal.alpha * 2.55)];
|
|
5169
|
+
// eslint-disable-next-line max-len
|
|
5170
|
+
[imageData.data[idx], imageData.data[idx + 1], imageData.data[idx + 2], imageData.data[idx + 3]] = rgbaColor;
|
|
5171
|
+
pixel += 3;
|
|
5254
5172
|
}
|
|
5255
5173
|
}
|
|
5174
|
+
if (rasters[0].length / (width * height) === 4) {
|
|
5175
|
+
// console.log("geoImage: " + "RGBA 1 array");
|
|
5176
|
+
rasters[0].forEach((value, index) => {
|
|
5177
|
+
imageData.data[index] = value;
|
|
5178
|
+
});
|
|
5179
|
+
}
|
|
5256
5180
|
}
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5181
|
+
if (channels === 3) {
|
|
5182
|
+
// RGB
|
|
5183
|
+
let pixel = 0;
|
|
5184
|
+
for (let i = 0; i < size; i += 4) {
|
|
5185
|
+
r = rasters[0][pixel];
|
|
5186
|
+
g = rasters[1][pixel];
|
|
5187
|
+
b = rasters[2][pixel];
|
|
5188
|
+
a = Math.floor(optionsLocal.alpha * 2.55);
|
|
5189
|
+
imageData.data[i] = r;
|
|
5190
|
+
imageData.data[i + 1] = g;
|
|
5191
|
+
imageData.data[i + 2] = b;
|
|
5192
|
+
imageData.data[i + 3] = a;
|
|
5193
|
+
pixel += 1;
|
|
5261
5194
|
}
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5195
|
+
}
|
|
5196
|
+
if (channels === 4) {
|
|
5197
|
+
// RGBA
|
|
5198
|
+
let pixel = 0;
|
|
5199
|
+
for (let i = 0; i < size; i += 4) {
|
|
5200
|
+
r = rasters[0][pixel];
|
|
5201
|
+
g = rasters[1][pixel];
|
|
5202
|
+
b = rasters[2][pixel];
|
|
5203
|
+
a = Math.floor(optionsLocal.alpha * 2.55);
|
|
5204
|
+
imageData.data[i] = r;
|
|
5205
|
+
imageData.data[i + 1] = g;
|
|
5206
|
+
imageData.data[i + 2] = b;
|
|
5207
|
+
imageData.data[i + 3] = a;
|
|
5208
|
+
pixel += 1;
|
|
5266
5209
|
}
|
|
5267
|
-
// const numOfChannels = channel.length / (width * height);
|
|
5268
|
-
const colorData = this.getColorValue(channel, optionsLocal, size, optionsLocal.numOfChannels);
|
|
5269
|
-
colorData.forEach((value, index) => {
|
|
5270
|
-
imageData.data[index] = value;
|
|
5271
|
-
});
|
|
5272
5210
|
}
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
imageData.data[index] = value;
|
|
5279
|
-
});
|
|
5211
|
+
}
|
|
5212
|
+
else if (optionsLocal.useChannelIndex < optionsLocal.numOfChannels && optionsLocal.useChannelIndex >= 0) {
|
|
5213
|
+
let channel = rasters[0];
|
|
5214
|
+
if (rasters[optionsLocal.useChannelIndex]) {
|
|
5215
|
+
channel = rasters[optionsLocal.useChannelIndex];
|
|
5280
5216
|
}
|
|
5281
|
-
//
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5217
|
+
// AUTO RANGE
|
|
5218
|
+
if (optionsLocal.useAutoRange) {
|
|
5219
|
+
optionsLocal.colorScaleValueRange = this.getMinMax(channel, optionsLocal);
|
|
5220
|
+
// console.log('data min: ' + optionsLocal.rangeMin + ', max: ' + optionsLocal.rangeMax);
|
|
5221
|
+
}
|
|
5222
|
+
// const numOfChannels = channel.length / (width * height);
|
|
5223
|
+
const colorData = this.getColorValue(channel, optionsLocal, size, optionsLocal.numOfChannels);
|
|
5224
|
+
colorData.forEach((value, index) => {
|
|
5225
|
+
imageData.data[index] = value;
|
|
5226
|
+
});
|
|
5227
|
+
}
|
|
5228
|
+
else {
|
|
5229
|
+
// if user defined channel does not exist
|
|
5230
|
+
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`);
|
|
5231
|
+
const defaultColorData = this.getDefaultColor(size, optionsLocal.nullColor);
|
|
5232
|
+
defaultColorData.forEach((value, index) => {
|
|
5233
|
+
imageData.data[index] = value;
|
|
5234
|
+
});
|
|
5235
|
+
}
|
|
5236
|
+
// console.timeEnd('bitmap-generated-in');
|
|
5237
|
+
c.putImageData(imageData, 0, 0);
|
|
5238
|
+
const imageUrl = canvas.toDataURL('image/png');
|
|
5239
|
+
// console.log('Bitmap generated.');
|
|
5240
|
+
return imageUrl;
|
|
5287
5241
|
}
|
|
5288
5242
|
getMinMax(array, options) {
|
|
5289
5243
|
let maxValue = options.maxValue ? options.maxValue : Number.MIN_VALUE;
|
|
@@ -5362,7 +5316,7 @@ class GeoImage {
|
|
|
5362
5316
|
}
|
|
5363
5317
|
// FIXME
|
|
5364
5318
|
// eslint-disable-next-line
|
|
5365
|
-
[colorsArray[i], colorsArray[i + 1], colorsArray[i + 2], colorsArray[i + 3]] = pixelColor;
|
|
5319
|
+
([colorsArray[i], colorsArray[i + 1], colorsArray[i + 2], colorsArray[i + 3]] = pixelColor);
|
|
5366
5320
|
pixel += numOfChannels;
|
|
5367
5321
|
}
|
|
5368
5322
|
return colorsArray;
|
|
@@ -5452,7 +5406,7 @@ function getMeshAttributes(vertices, terrain, width, height, bounds) {
|
|
|
5452
5406
|
function getDelatinTileMesh(meshMaxError, width, height, terrain) {
|
|
5453
5407
|
const tin = new Delatin(terrain, width + 1, height + 1);
|
|
5454
5408
|
tin.run(meshMaxError);
|
|
5455
|
-
// @ts-expect-error
|
|
5409
|
+
// @ts-expect-error: Delatin instance properties 'coords' and 'triangles' are not explicitly typed in the library port
|
|
5456
5410
|
const { coords, triangles } = tin;
|
|
5457
5411
|
const vertices = coords;
|
|
5458
5412
|
return { vertices, triangles };
|
|
@@ -5466,31 +5420,30 @@ const CogTilesGeoImageOptionsDefaults = {
|
|
|
5466
5420
|
blurredTexture: true,
|
|
5467
5421
|
};
|
|
5468
5422
|
class CogTiles {
|
|
5423
|
+
cog;
|
|
5424
|
+
cogZoomLookup = [0];
|
|
5425
|
+
cogResolutionLookup = [0];
|
|
5426
|
+
cogOrigin = [0, 0];
|
|
5427
|
+
zoomRange = [0, 0];
|
|
5428
|
+
tileSize;
|
|
5429
|
+
bounds;
|
|
5430
|
+
geo = new GeoImage();
|
|
5431
|
+
options;
|
|
5469
5432
|
constructor(options) {
|
|
5470
|
-
this.
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
this.
|
|
5474
|
-
this.
|
|
5475
|
-
this.
|
|
5476
|
-
this.options
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
this.cogOrigin = image.getOrigin();
|
|
5485
|
-
(_a = (_c = this.options).noDataValue) !== null && _a !== void 0 ? _a : (_c.noDataValue = this.getNoDataValue(image));
|
|
5486
|
-
(_b = (_d = this.options).format) !== null && _b !== void 0 ? _b : (_d.format = this.getDataTypeFromTags(image));
|
|
5487
|
-
this.options.numOfChannels = this.getNumberOfChannels(image);
|
|
5488
|
-
this.options.planarConfig = this.getPlanarConfiguration(image);
|
|
5489
|
-
[this.cogZoomLookup, this.cogResolutionLookup] = yield this.buildCogZoomResolutionLookup(this.cog);
|
|
5490
|
-
this.tileSize = image.getTileWidth();
|
|
5491
|
-
this.zoomRange = this.calculateZoomRange(image, yield this.cog.getImageCount());
|
|
5492
|
-
this.bounds = this.calculateBoundsAsLatLon(image);
|
|
5493
|
-
});
|
|
5433
|
+
this.options = { ...CogTilesGeoImageOptionsDefaults, ...options };
|
|
5434
|
+
}
|
|
5435
|
+
async initializeCog(url) {
|
|
5436
|
+
this.cog = await fromUrl(url);
|
|
5437
|
+
const image = await this.cog.getImage(); // by default, the first image is read.
|
|
5438
|
+
this.cogOrigin = image.getOrigin();
|
|
5439
|
+
this.options.noDataValue ??= this.getNoDataValue(image);
|
|
5440
|
+
this.options.format ??= this.getDataTypeFromTags(image);
|
|
5441
|
+
this.options.numOfChannels = this.getNumberOfChannels(image);
|
|
5442
|
+
this.options.planarConfig = this.getPlanarConfiguration(image);
|
|
5443
|
+
[this.cogZoomLookup, this.cogResolutionLookup] = await this.buildCogZoomResolutionLookup(this.cog);
|
|
5444
|
+
this.tileSize = image.getTileWidth();
|
|
5445
|
+
this.zoomRange = this.calculateZoomRange(image, await this.cog.getImageCount());
|
|
5446
|
+
this.bounds = this.calculateBoundsAsLatLon(image);
|
|
5494
5447
|
}
|
|
5495
5448
|
getZoomRange() {
|
|
5496
5449
|
return this.zoomRange;
|
|
@@ -5542,33 +5495,31 @@ class CogTiles {
|
|
|
5542
5495
|
* - The first array (`zoomLookup`) maps each image index to its computed zoom level.
|
|
5543
5496
|
* - The second array (`resolutionLookup`) maps each image index to its estimated resolution (m/pixel).
|
|
5544
5497
|
*/
|
|
5545
|
-
buildCogZoomResolutionLookup(cog) {
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
return [zoomLookup, resolutionLookup];
|
|
5571
|
-
});
|
|
5498
|
+
async buildCogZoomResolutionLookup(cog) {
|
|
5499
|
+
// Retrieve the total number of images (overviews) in the COG.
|
|
5500
|
+
const imageCount = await cog.getImageCount();
|
|
5501
|
+
// Use the first image as the base reference.
|
|
5502
|
+
const baseImage = await cog.getImage(0);
|
|
5503
|
+
const baseResolution = baseImage.getResolution()[0]; // Resolution (m/pixel) of the base image.
|
|
5504
|
+
const baseWidth = baseImage.getWidth();
|
|
5505
|
+
// Initialize arrays to store the zoom level and resolution for each image.
|
|
5506
|
+
const zoomLookup = [];
|
|
5507
|
+
const resolutionLookup = [];
|
|
5508
|
+
// Iterate over each image (overview) in the COG.
|
|
5509
|
+
for (let idx = 0; idx < imageCount; idx++) {
|
|
5510
|
+
const image = await cog.getImage(idx);
|
|
5511
|
+
const width = image.getWidth();
|
|
5512
|
+
// Calculate the scale factor relative to the base image.
|
|
5513
|
+
const scaleFactor = baseWidth / width;
|
|
5514
|
+
const estimatedResolution = baseResolution * scaleFactor;
|
|
5515
|
+
// Calculate the zoom level using the Web Mercator resolution standard:
|
|
5516
|
+
// webMercatorRes0 is the resolution at zoom level 0; each zoom level halves the resolution.
|
|
5517
|
+
const zoomLevel = Math.round(Math.log2(webMercatorRes0 / estimatedResolution));
|
|
5518
|
+
// console.log(`buildCogZoomResolutionLookup: Image index ${idx}: Estimated Resolution = ${estimatedResolution} m/pixel, Zoom Level = ${zoomLevel}`);
|
|
5519
|
+
zoomLookup[idx] = zoomLevel;
|
|
5520
|
+
resolutionLookup[idx] = estimatedResolution;
|
|
5521
|
+
}
|
|
5522
|
+
return [zoomLookup, resolutionLookup];
|
|
5572
5523
|
}
|
|
5573
5524
|
/**
|
|
5574
5525
|
* Determines the appropriate image index from the Cloud Optimized GeoTIFF (COG)
|
|
@@ -5597,93 +5548,138 @@ class CogTiles {
|
|
|
5597
5548
|
}
|
|
5598
5549
|
return exactMatchIndex;
|
|
5599
5550
|
}
|
|
5600
|
-
getTileFromImage(tileX, tileY, zoom) {
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5551
|
+
async getTileFromImage(tileX, tileY, zoom) {
|
|
5552
|
+
const imageIndex = this.getImageIndexForZoomLevel(zoom);
|
|
5553
|
+
const targetImage = await this.cog.getImage(imageIndex);
|
|
5554
|
+
// 1. Validation: Ensure the image is tiled
|
|
5555
|
+
const tileWidth = targetImage.getTileWidth();
|
|
5556
|
+
const tileHeight = targetImage.getTileHeight();
|
|
5557
|
+
if (!tileWidth || !tileHeight) {
|
|
5558
|
+
throw new Error('GeoTIFF Error: The provided image is not tiled. '
|
|
5559
|
+
+ 'Please use "rio cogeo create --web-optimized" to fix this.');
|
|
5560
|
+
}
|
|
5561
|
+
// --- STEP 1: CALCULATE BOUNDS IN METERS ---
|
|
5562
|
+
// 2. Get COG Metadata (image = COG)
|
|
5563
|
+
const imageResolution = this.cogResolutionLookup[imageIndex];
|
|
5564
|
+
const imageHeight = targetImage.getHeight();
|
|
5565
|
+
const imageWidth = targetImage.getWidth();
|
|
5566
|
+
const [imgOriginX, imgOriginY] = this.cogOrigin;
|
|
5567
|
+
// 3. Define Web Mercator Constants
|
|
5568
|
+
// We use the class property tileSize (usually 256) as the ground truth for grid calculations
|
|
5569
|
+
const TILE_SIZE = this.tileSize;
|
|
5570
|
+
const ORIGIN_X = webMercatorOrigin[0];
|
|
5571
|
+
const ORIGIN_Y = webMercatorOrigin[1];
|
|
5572
|
+
// 4. Calculate Tile BBox in World Meters
|
|
5573
|
+
// This defines where the map expects the tile to be physically located
|
|
5574
|
+
const tileGridResolution = (EARTH_CIRCUMFERENCE / TILE_SIZE) / (2 ** zoom);
|
|
5575
|
+
const tileMinXMeters = ORIGIN_X + (tileX * TILE_SIZE * tileGridResolution);
|
|
5576
|
+
const tileMaxYMeters = ORIGIN_Y - (tileY * TILE_SIZE * tileGridResolution);
|
|
5577
|
+
// Note: We don't strictly need MaxX/MinY meters for the start calculation,
|
|
5578
|
+
// but they are useful if debugging the full meter footprint.
|
|
5579
|
+
// --- STEP 2: CONVERT TO PIXEL COORDINATES ---
|
|
5580
|
+
// 5. Calculate precise floating-point start position relative to the image
|
|
5581
|
+
const windowMinX = (tileMinXMeters - imgOriginX) / imageResolution;
|
|
5582
|
+
const windowMinY = (imgOriginY - tileMaxYMeters) / imageResolution;
|
|
5583
|
+
// 6. Snap to Integer Grid (The "Force 256" Fix)
|
|
5584
|
+
// We round the start position to align with the nearest pixel.
|
|
5585
|
+
// Crucially, we calculate endX/endY by adding tileSize to startX/startY.
|
|
5586
|
+
// This guarantees the window is exactly 256x256, preventing "off-by-one" (257px) errors.
|
|
5587
|
+
const startX = Math.round(windowMinX);
|
|
5588
|
+
const startY = Math.round(windowMinY);
|
|
5589
|
+
const endX = startX + TILE_SIZE;
|
|
5590
|
+
const endY = startY + TILE_SIZE;
|
|
5591
|
+
// --- STEP 3: CALCULATE INTERSECTION ---
|
|
5592
|
+
// 7. Clamp the read window to the actual image dimensions
|
|
5593
|
+
// This defines the "Safe" area we can actually read from the file.
|
|
5594
|
+
const validReadX = Math.max(0, startX);
|
|
5595
|
+
const validReadY = Math.max(0, startY);
|
|
5596
|
+
const validReadMaxX = Math.min(imageWidth, endX);
|
|
5597
|
+
const validReadMaxY = Math.min(imageHeight, endY);
|
|
5598
|
+
const readWidth = validReadMaxX - validReadX;
|
|
5599
|
+
const readHeight = validReadMaxY - validReadY;
|
|
5600
|
+
// CHECK: If no overlap, return empty
|
|
5601
|
+
if (readWidth <= 0 || readHeight <= 0) {
|
|
5602
|
+
return [this.createEmptyTile()];
|
|
5603
|
+
}
|
|
5604
|
+
// 8. Calculate Offsets (Padding)
|
|
5605
|
+
// "missingLeft" is how many blank pixels we need to insert before the image data starts.
|
|
5606
|
+
// Logic: If we wanted to read from -50 (startX), but clamped to 0 (validReadX),
|
|
5607
|
+
// we are missing the first 50 pixels.
|
|
5608
|
+
const missingLeft = validReadX - startX;
|
|
5609
|
+
const missingTop = validReadY - startY;
|
|
5610
|
+
const window = [validReadX, validReadY, validReadMaxX, validReadMaxY];
|
|
5611
|
+
// --- STEP 4: READ AND COMPOSITE ---
|
|
5612
|
+
// Case A: Partial Overlap (Padding or Cropping required)
|
|
5613
|
+
// If the tile is hanging off the edge, we need to manually reconstruct it.
|
|
5614
|
+
if (missingLeft > 0 || missingTop > 0 || readWidth < tileWidth || readHeight < tileHeight) {
|
|
5615
|
+
/// Initialize a temporary buffer for a single band (filled with NoData)
|
|
5616
|
+
// We will reuse this buffer for each band to save memory allocations.
|
|
5617
|
+
const tileBuffer = this.createTileBuffer(this.options.format, tileWidth);
|
|
5618
|
+
tileBuffer.fill(this.options.noDataValue);
|
|
5619
|
+
// if the valid window is smaller than the tile size, it gets the image size width and height, thus validRasterData.width must be used as below
|
|
5620
|
+
const validRasterData = await targetImage.readRasters({ window });
|
|
5621
|
+
// FOR MULTI-BAND - the result is one array with sequentially typed bands, firstly all data for the band 0, then for band 1
|
|
5622
|
+
// I think this is less practical then the commented solution above, but I do it so it works with the code in GeoImage.ts in deck.gl-geoimage in function getColorValue.
|
|
5623
|
+
const validImageData = Array(validRasterData.length * validRasterData[0].length);
|
|
5624
|
+
validImageData.fill(this.options.noDataValue);
|
|
5625
|
+
// Place the valid pixel data into the tile buffer.
|
|
5626
|
+
for (let band = 0; band < validRasterData.length; band += 1) {
|
|
5627
|
+
for (let row = 0; row < readHeight; row += 1) {
|
|
5628
|
+
const destRow = missingTop + row;
|
|
5629
|
+
const destRowOffset = destRow * TILE_SIZE;
|
|
5630
|
+
const srcRowOffset = row * validRasterData.width;
|
|
5631
|
+
for (let col = 0; col < readWidth; col += 1) {
|
|
5632
|
+
// Compute the destination position in the tile buffer.
|
|
5633
|
+
// We shift by the number of missing pixels (if any) at the top/left.
|
|
5634
|
+
const destCol = missingLeft + col;
|
|
5635
|
+
// Bounds Check: Ensure we don't write outside the 256x256 buffer
|
|
5636
|
+
if (destRow < tileWidth && destCol < tileHeight) {
|
|
5637
|
+
tileBuffer[destRowOffset + destCol] = validRasterData[band][srcRowOffset + col];
|
|
5638
|
+
}
|
|
5639
|
+
else {
|
|
5640
|
+
console.log('error in assigning data to tile buffer');
|
|
5663
5641
|
}
|
|
5664
5642
|
}
|
|
5665
|
-
tileBuffer.forEach((rasterValue, index) => {
|
|
5666
|
-
validImageData[index * this.options.numOfChannels + band] = rasterValue;
|
|
5667
|
-
});
|
|
5668
5643
|
}
|
|
5669
|
-
|
|
5644
|
+
for (let i = 0; i < tileBuffer.length; i += 1) {
|
|
5645
|
+
validImageData[i * this.options.numOfChannels + band] = tileBuffer[i];
|
|
5646
|
+
}
|
|
5670
5647
|
}
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5648
|
+
return [validImageData];
|
|
5649
|
+
}
|
|
5650
|
+
// Case B: Perfect Match (Optimization)
|
|
5651
|
+
// If the read window is exactly 256x256 and aligned, we can read directly interleaved.
|
|
5652
|
+
// console.log("Perfect aligned read");
|
|
5653
|
+
const tileData = await targetImage.readRasters({ window, interleave: true });
|
|
5654
|
+
// console.log(`data that starts at the left top corner of the tile ${tileX}, ${tileY}`);
|
|
5655
|
+
return [tileData];
|
|
5676
5656
|
}
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5657
|
+
/**
|
|
5658
|
+
* Creates a blank tile buffer filled with the "No Data" value.
|
|
5659
|
+
*/
|
|
5660
|
+
createEmptyTile() {
|
|
5661
|
+
// 1. Determine the size
|
|
5662
|
+
// Default to 1 channel (grayscale) if not specified
|
|
5663
|
+
const channels = this.options.numOfChannels || 1;
|
|
5664
|
+
const size = this.tileSize * this.tileSize * channels;
|
|
5665
|
+
// 2. Create the array
|
|
5666
|
+
// Float32 is standard for GeoTIFF data handling in browsers
|
|
5667
|
+
const tileData = new Float32Array(size);
|
|
5668
|
+
// 3. Fill with "No Data" value
|
|
5669
|
+
// If noDataValue is undefined, it defaults to 0
|
|
5670
|
+
if (this.options.noDataValue !== undefined) {
|
|
5671
|
+
tileData.fill(this.options.noDataValue);
|
|
5672
|
+
}
|
|
5673
|
+
return tileData;
|
|
5674
|
+
}
|
|
5675
|
+
async getTile(x, y, z, bounds, meshMaxError) {
|
|
5676
|
+
const tileData = await this.getTileFromImage(x, y, z);
|
|
5677
|
+
return this.geo.getMap({
|
|
5678
|
+
rasters: [tileData[0]],
|
|
5679
|
+
width: this.tileSize,
|
|
5680
|
+
height: this.tileSize,
|
|
5681
|
+
bounds,
|
|
5682
|
+
}, this.options, meshMaxError);
|
|
5687
5683
|
}
|
|
5688
5684
|
/**
|
|
5689
5685
|
* Determines the data type (e.g., "Int32", "Float64") of a GeoTIFF image
|
|
@@ -5766,53 +5762,6 @@ class CogTiles {
|
|
|
5766
5762
|
getNumberOfChannels(image) {
|
|
5767
5763
|
return image.getSamplesPerPixel();
|
|
5768
5764
|
}
|
|
5769
|
-
/**
|
|
5770
|
-
* Calculates the intersection between a tile bounding box and a COG bounding box,
|
|
5771
|
-
* returning the intersection window in image pixel space (relative to COG offsets),
|
|
5772
|
-
* along with how much blank space (nodata) appears on the left and top of the tile.
|
|
5773
|
-
*
|
|
5774
|
-
* @param {number[]} tileBbox - Tile bounding box: [minX, minY, maxX, maxY]
|
|
5775
|
-
* @param {number[]} cogBbox - COG bounding box: [minX, minY, maxX, maxY]
|
|
5776
|
-
* @param {number} offsetXPixel - X offset of the COG origin in pixel space
|
|
5777
|
-
* @param {number} offsetYPixel - Y offset of the COG origin in pixel space
|
|
5778
|
-
* @param {number} tileSize - Size of the tile in pixels (default: 256)
|
|
5779
|
-
* @returns {[number, number, number[] | null, number, number]}
|
|
5780
|
-
* An array containing:
|
|
5781
|
-
* - width of the intersection
|
|
5782
|
-
* - height of the intersection
|
|
5783
|
-
* - pixel-space window: [startX, startY, endX, endY] or null if no overlap
|
|
5784
|
-
* - missingLeft: padding pixels on the left
|
|
5785
|
-
* - missingTop: padding pixels on the top
|
|
5786
|
-
*/
|
|
5787
|
-
getIntersectionBBox(tileBbox, cogBbox, offsetXPixel = 0, offsetYPixel = 0, tileSize = 256) {
|
|
5788
|
-
const interLeft = Math.max(tileBbox[0], cogBbox[0]);
|
|
5789
|
-
const interTop = Math.max(tileBbox[1], cogBbox[1]);
|
|
5790
|
-
const interRight = Math.min(tileBbox[2], cogBbox[2]);
|
|
5791
|
-
const interBottom = Math.min(tileBbox[3], cogBbox[3]);
|
|
5792
|
-
const width = Math.max(0, interRight - interLeft);
|
|
5793
|
-
const height = Math.max(0, interBottom - interTop);
|
|
5794
|
-
let window = null;
|
|
5795
|
-
let missingLeft = 0;
|
|
5796
|
-
let missingTop = 0;
|
|
5797
|
-
if (width > 0 && height > 0) {
|
|
5798
|
-
window = [
|
|
5799
|
-
interLeft - offsetXPixel,
|
|
5800
|
-
interTop - offsetYPixel,
|
|
5801
|
-
interRight - offsetXPixel,
|
|
5802
|
-
interBottom - offsetYPixel,
|
|
5803
|
-
];
|
|
5804
|
-
// Padding from the tile origin to valid data start
|
|
5805
|
-
missingLeft = interLeft - tileBbox[0];
|
|
5806
|
-
missingTop = interTop - tileBbox[1];
|
|
5807
|
-
}
|
|
5808
|
-
return [
|
|
5809
|
-
width,
|
|
5810
|
-
height,
|
|
5811
|
-
window,
|
|
5812
|
-
missingLeft,
|
|
5813
|
-
missingTop,
|
|
5814
|
-
];
|
|
5815
|
-
}
|
|
5816
5765
|
/**
|
|
5817
5766
|
* Retrieves the PlanarConfiguration value from a GeoTIFF image.
|
|
5818
5767
|
*
|
|
@@ -5885,7 +5834,8 @@ const urlType$1 = {
|
|
|
5885
5834
|
return true;
|
|
5886
5835
|
},
|
|
5887
5836
|
};
|
|
5888
|
-
const defaultProps$1 =
|
|
5837
|
+
const defaultProps$1 = {
|
|
5838
|
+
...geoLayers.TileLayer.defaultProps,
|
|
5889
5839
|
// Image url that encodes height data
|
|
5890
5840
|
// elevationData: urlType,
|
|
5891
5841
|
// Image url to use as texture
|
|
@@ -5895,10 +5845,9 @@ const defaultProps$1 = Object.assign(Object.assign({}, geoLayers.TileLayer.defau
|
|
|
5895
5845
|
// Bounding box of the terrain image, [minX, minY, maxX, maxY] in world coordinates
|
|
5896
5846
|
bounds: {
|
|
5897
5847
|
type: 'array', value: null, optional: true, compare: true,
|
|
5898
|
-
},
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
blurredTexture: true, opacity: 1, clampToTerrain: false,
|
|
5848
|
+
},
|
|
5849
|
+
rasterData: urlType$1,
|
|
5850
|
+
clampToTerrain: false,
|
|
5902
5851
|
// Object to decode height data, from (r, g, b) to height in meters
|
|
5903
5852
|
// elevationDecoder: {
|
|
5904
5853
|
// type: 'object',
|
|
@@ -5910,40 +5859,31 @@ const defaultProps$1 = Object.assign(Object.assign({}, geoLayers.TileLayer.defau
|
|
|
5910
5859
|
// },
|
|
5911
5860
|
// },
|
|
5912
5861
|
// Supply url to local terrain worker bundle. Only required if running offline and cannot access CDN.
|
|
5913
|
-
workerUrl: ''
|
|
5862
|
+
workerUrl: '',
|
|
5863
|
+
// Same as SimpleMeshLayer wireframe
|
|
5864
|
+
// wireframe: false,
|
|
5865
|
+
// material: true,
|
|
5866
|
+
// loaders: [TerrainLoader],
|
|
5867
|
+
};
|
|
5914
5868
|
/** Render bitmap texture from cog raster images. */
|
|
5915
5869
|
class CogBitmapLayer extends core.CompositeLayer {
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
//
|
|
5924
|
-
initializeState(context) {
|
|
5925
|
-
const _super = Object.create(null, {
|
|
5926
|
-
initializeState: { get: () => super.initializeState }
|
|
5927
|
-
});
|
|
5928
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
5929
|
-
_super.initializeState.call(this, context);
|
|
5930
|
-
this.setState({
|
|
5931
|
-
bitmapCogTiles: new CogTiles(this.props.cogBitmapOptions),
|
|
5932
|
-
initialized: false,
|
|
5933
|
-
});
|
|
5934
|
-
yield this.init();
|
|
5870
|
+
static defaultProps = defaultProps$1;
|
|
5871
|
+
static layerName = 'CogBitmapLayer';
|
|
5872
|
+
async initializeState(context) {
|
|
5873
|
+
super.initializeState(context);
|
|
5874
|
+
this.setState({
|
|
5875
|
+
bitmapCogTiles: new CogTiles(this.props.cogBitmapOptions),
|
|
5876
|
+
initialized: false,
|
|
5935
5877
|
});
|
|
5878
|
+
await this.init();
|
|
5936
5879
|
}
|
|
5937
|
-
init() {
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
this.setState({ initialized: true, minZoom, maxZoom });
|
|
5943
|
-
});
|
|
5880
|
+
async init() {
|
|
5881
|
+
await this.state.bitmapCogTiles.initializeCog(this.props.rasterData);
|
|
5882
|
+
const zoomRange = this.state.bitmapCogTiles.getZoomRange();
|
|
5883
|
+
const [minZoom, maxZoom] = zoomRange;
|
|
5884
|
+
this.setState({ initialized: true, minZoom, maxZoom });
|
|
5944
5885
|
}
|
|
5945
5886
|
updateState({ props, oldProps }) {
|
|
5946
|
-
var _a, _b, _c;
|
|
5947
5887
|
const rasterDataChanged = props.rasterData !== oldProps.rasterData;
|
|
5948
5888
|
if (rasterDataChanged) {
|
|
5949
5889
|
const { rasterData } = props;
|
|
@@ -5963,45 +5903,47 @@ class CogBitmapLayer extends core.CompositeLayer {
|
|
|
5963
5903
|
// object in this way is not ideal and may need refactoring in the future to follow a more
|
|
5964
5904
|
// declarative state management approach. Consider revisiting this if additional properties
|
|
5965
5905
|
// need to be synchronized or if the state structure changes.
|
|
5966
|
-
if (
|
|
5906
|
+
if (props?.cogBitmapOptions?.useChannel && (props.cogBitmapOptions?.useChannel !== oldProps.cogBitmapOptions?.useChannel)) {
|
|
5967
5907
|
this.state.bitmapCogTiles.options.useChannel = props.cogBitmapOptions.useChannel;
|
|
5968
5908
|
}
|
|
5969
|
-
// TODO - remove in v9
|
|
5970
|
-
// @ts-ignore
|
|
5971
5909
|
if (props.workerUrl) {
|
|
5972
5910
|
core.log.removed('workerUrl', 'loadOptions.terrain.workerUrl')();
|
|
5973
5911
|
}
|
|
5974
5912
|
}
|
|
5975
|
-
getTiledBitmapData(tile) {
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
return tileData;
|
|
5981
|
-
});
|
|
5913
|
+
async getTiledBitmapData(tile) {
|
|
5914
|
+
// TODO - pass signal to getTile
|
|
5915
|
+
// abort request if signal is aborted
|
|
5916
|
+
const tileData = await this.state.bitmapCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z);
|
|
5917
|
+
return tileData;
|
|
5982
5918
|
}
|
|
5983
5919
|
renderSubLayers(props) {
|
|
5984
5920
|
const SubLayerClass = this.getSubLayerClass('image', layers.BitmapLayer);
|
|
5985
5921
|
const { blurredTexture } = this.state.bitmapCogTiles.options;
|
|
5986
|
-
const {
|
|
5922
|
+
const { clampToTerrain } = this.props;
|
|
5923
|
+
const hasDrawMode = typeof clampToTerrain === 'object' && clampToTerrain !== null && 'terrainDrawMode' in clampToTerrain;
|
|
5987
5924
|
const { data } = props;
|
|
5988
5925
|
if (!data) {
|
|
5989
5926
|
return null;
|
|
5990
5927
|
}
|
|
5991
5928
|
const { bbox: { west, south, east, north, }, } = props.tile;
|
|
5992
|
-
return new SubLayerClass(
|
|
5929
|
+
return new SubLayerClass({ ...props, tileSize: this.state.bitmapCogTiles.tileSize }, {
|
|
5930
|
+
data: null,
|
|
5931
|
+
image: data,
|
|
5932
|
+
_instanced: false,
|
|
5933
|
+
bounds: [west, south, east, north],
|
|
5934
|
+
textureParameters: {
|
|
5993
5935
|
minFilter: blurredTexture ? 'linear' : 'nearest',
|
|
5994
5936
|
magFilter: blurredTexture ? 'linear' : 'nearest',
|
|
5995
|
-
},
|
|
5937
|
+
},
|
|
5996
5938
|
// TODO check if works!!!
|
|
5997
|
-
extensions: clampToTerrain ? [new extensions._TerrainExtension()] : []
|
|
5998
|
-
|
|
5999
|
-
|
|
5939
|
+
extensions: clampToTerrain ? [new extensions._TerrainExtension()] : [],
|
|
5940
|
+
...(hasDrawMode
|
|
5941
|
+
? { terrainDrawMode: clampToTerrain.terrainDrawMode }
|
|
5942
|
+
: {}),
|
|
5943
|
+
});
|
|
6000
5944
|
}
|
|
6001
5945
|
renderLayers() {
|
|
6002
|
-
const {
|
|
6003
|
-
// tileSize,
|
|
6004
|
-
maxRequests, onTileLoad, onTileUnload, onTileError, maxCacheSize, maxCacheByteSize, refinementStrategy, cogBitmapOptions, } = this.props;
|
|
5946
|
+
const { clampToTerrain, maxRequests, onTileLoad, onTileUnload, onTileError, maxCacheSize, maxCacheByteSize, refinementStrategy, cogBitmapOptions, } = this.props;
|
|
6005
5947
|
if (this.state.isTiled && this.state.initialized) {
|
|
6006
5948
|
const { tileSize } = this.state.bitmapCogTiles;
|
|
6007
5949
|
return new geoLayers.TileLayer(this.getSubLayerProps({
|
|
@@ -6036,28 +5978,7 @@ class CogBitmapLayer extends core.CompositeLayer {
|
|
|
6036
5978
|
return null;
|
|
6037
5979
|
}
|
|
6038
5980
|
}
|
|
6039
|
-
CogBitmapLayer.defaultProps = defaultProps$1;
|
|
6040
|
-
CogBitmapLayer.layerName = 'CogBitmapLayer';
|
|
6041
5981
|
|
|
6042
|
-
// Copyright (c) 2015 - 2017 Uber Technologies, Inc.
|
|
6043
|
-
//
|
|
6044
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6045
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
6046
|
-
// in the Software without restriction, including without limitation the rights
|
|
6047
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
6048
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
6049
|
-
// furnished to do so, subject to the following conditions:
|
|
6050
|
-
//
|
|
6051
|
-
// The above copyright notice and this permission notice shall be included in
|
|
6052
|
-
// all copies or substantial portions of the Software.
|
|
6053
|
-
//
|
|
6054
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
6055
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
6056
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
6057
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
6058
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
6059
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
6060
|
-
// THE SOFTWARE.
|
|
6061
5982
|
const urlType = {
|
|
6062
5983
|
type: 'object',
|
|
6063
5984
|
value: null,
|
|
@@ -6084,19 +6005,20 @@ const urlType = {
|
|
|
6084
6005
|
},
|
|
6085
6006
|
};
|
|
6086
6007
|
const DUMMY_DATA = [1];
|
|
6087
|
-
const defaultProps =
|
|
6008
|
+
const defaultProps = {
|
|
6009
|
+
...geoLayers.TileLayer.defaultProps,
|
|
6088
6010
|
// Image url that encodes height data
|
|
6089
|
-
elevationData: urlType,
|
|
6011
|
+
elevationData: urlType,
|
|
6090
6012
|
// Image url to use as texture
|
|
6091
|
-
texture:
|
|
6013
|
+
texture: { ...urlType, optional: true },
|
|
6092
6014
|
// Martini error tolerance in meters, smaller number -> more detailed mesh
|
|
6093
|
-
meshMaxError: { type: 'number', value: 4.0 },
|
|
6015
|
+
meshMaxError: { type: 'number', value: 4.0 },
|
|
6094
6016
|
// Bounding box of the terrain image, [minX, minY, maxX, maxY] in world coordinates
|
|
6095
6017
|
bounds: {
|
|
6096
6018
|
type: 'array', value: null, optional: true, compare: true,
|
|
6097
|
-
},
|
|
6019
|
+
},
|
|
6098
6020
|
// Color to use if texture is unavailable
|
|
6099
|
-
color: { type: 'color', value: [255, 255, 255] },
|
|
6021
|
+
color: { type: 'color', value: [255, 255, 255] },
|
|
6100
6022
|
// Object to decode height data, from (r, g, b) to height in meters
|
|
6101
6023
|
elevationDecoder: {
|
|
6102
6024
|
type: 'object',
|
|
@@ -6106,11 +6028,14 @@ const defaultProps = Object.assign(Object.assign({}, geoLayers.TileLayer.default
|
|
|
6106
6028
|
bScaler: 0,
|
|
6107
6029
|
offset: 0,
|
|
6108
6030
|
},
|
|
6109
|
-
},
|
|
6031
|
+
},
|
|
6110
6032
|
// Supply url to local terrain worker bundle. Only required if running offline and cannot access CDN.
|
|
6111
|
-
workerUrl: '',
|
|
6033
|
+
workerUrl: '',
|
|
6112
6034
|
// Same as SimpleMeshLayer wireframe
|
|
6113
|
-
wireframe: false,
|
|
6035
|
+
wireframe: false,
|
|
6036
|
+
material: true,
|
|
6037
|
+
// loaders: [TerrainLoader],
|
|
6038
|
+
};
|
|
6114
6039
|
// Turns array of templates into a single string to work around shallow change
|
|
6115
6040
|
function urlTemplateToUpdateTrigger(template) {
|
|
6116
6041
|
if (Array.isArray(template)) {
|
|
@@ -6122,28 +6047,25 @@ function urlTemplateToUpdateTrigger(template) {
|
|
|
6122
6047
|
// TODO use meshMaxError
|
|
6123
6048
|
// TODO - pass signal to getTile
|
|
6124
6049
|
/** Render mesh surfaces from height map images. */
|
|
6125
|
-
class
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
});
|
|
6136
|
-
yield this.init(this.terrainUrl);
|
|
6050
|
+
class CogTerrainLayer extends core.CompositeLayer {
|
|
6051
|
+
static defaultProps = defaultProps;
|
|
6052
|
+
static layerName = 'CogTerrainLayer';
|
|
6053
|
+
// terrainCogTiles: CogTiles;
|
|
6054
|
+
terrainUrl;
|
|
6055
|
+
async initializeState(context) {
|
|
6056
|
+
super.initializeState(context);
|
|
6057
|
+
this.setState({
|
|
6058
|
+
terrainCogTiles: new CogTiles(this.props.terrainOptions),
|
|
6059
|
+
initialized: false,
|
|
6137
6060
|
});
|
|
6061
|
+
await this.init(this.terrainUrl);
|
|
6138
6062
|
}
|
|
6139
|
-
init(
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
this.setState({ initialized: true, minZoom, maxZoom });
|
|
6146
|
-
});
|
|
6063
|
+
async init() {
|
|
6064
|
+
await this.state.terrainCogTiles.initializeCog(this.props.elevationData);
|
|
6065
|
+
// this.tileSize = this.terrainCogTiles.getTileSize(cog);
|
|
6066
|
+
const zoomRange = this.state.terrainCogTiles.getZoomRange();
|
|
6067
|
+
const [minZoom, maxZoom] = zoomRange;
|
|
6068
|
+
this.setState({ initialized: true, minZoom, maxZoom });
|
|
6147
6069
|
}
|
|
6148
6070
|
updateState({ props, oldProps }) {
|
|
6149
6071
|
const elevationDataChanged = props.elevationData !== oldProps.elevationData;
|
|
@@ -6160,8 +6082,6 @@ class TerrainLayer extends core.CompositeLayer {
|
|
|
6160
6082
|
|| props.elevationDecoder !== oldProps.elevationDecoder
|
|
6161
6083
|
|| props.bounds !== oldProps.bounds;
|
|
6162
6084
|
if (!this.state.isTiled && shouldReload) ;
|
|
6163
|
-
// TODO - remove in v9
|
|
6164
|
-
// @ts-ignore
|
|
6165
6085
|
if (props.workerUrl) {
|
|
6166
6086
|
core.log.removed('workerUrl', 'loadOptions.terrain.workerUrl')();
|
|
6167
6087
|
}
|
|
@@ -6171,36 +6091,47 @@ class TerrainLayer extends core.CompositeLayer {
|
|
|
6171
6091
|
return null;
|
|
6172
6092
|
}
|
|
6173
6093
|
let loadOptions = this.getLoadOptions();
|
|
6174
|
-
loadOptions =
|
|
6094
|
+
loadOptions = {
|
|
6095
|
+
...loadOptions,
|
|
6096
|
+
_workerType: 'test',
|
|
6097
|
+
terrain: {
|
|
6098
|
+
skirtHeight: this.state.isTiled ? meshMaxError * 2 : 0,
|
|
6099
|
+
...loadOptions?.terrain,
|
|
6100
|
+
bounds,
|
|
6175
6101
|
meshMaxError,
|
|
6176
|
-
elevationDecoder
|
|
6102
|
+
elevationDecoder,
|
|
6103
|
+
},
|
|
6104
|
+
};
|
|
6177
6105
|
const { fetch } = this.props;
|
|
6178
6106
|
return fetch(elevationData, {
|
|
6179
6107
|
propName: 'elevationData', layer: this, loadOptions, signal, loaders: [],
|
|
6180
6108
|
});
|
|
6181
6109
|
}
|
|
6182
|
-
getTiledTerrainData(tile) {
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
const
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6110
|
+
async getTiledTerrainData(tile) {
|
|
6111
|
+
// const {
|
|
6112
|
+
// elevationData, fetch, texture, elevationDecoder, meshMaxError,
|
|
6113
|
+
// } = this.props;
|
|
6114
|
+
const { viewport } = this.context;
|
|
6115
|
+
// const dataUrl = getURLFromTemplate(elevationData, tile);
|
|
6116
|
+
// const textureUrl = texture && getURLFromTemplate(texture, tile);
|
|
6117
|
+
// const { signal } = tile;
|
|
6118
|
+
let bottomLeft = [0, 0];
|
|
6119
|
+
let topRight = [0, 0];
|
|
6120
|
+
if (viewport.isGeospatial) {
|
|
6121
|
+
const bbox = tile.bbox;
|
|
6122
|
+
bottomLeft = viewport.projectFlat([bbox.west, bbox.south]);
|
|
6123
|
+
topRight = viewport.projectFlat([bbox.east, bbox.north]);
|
|
6124
|
+
}
|
|
6125
|
+
else {
|
|
6126
|
+
const bbox = tile.bbox;
|
|
6127
|
+
bottomLeft = [bbox.left, bbox.bottom];
|
|
6128
|
+
topRight = [bbox.right, bbox.top];
|
|
6129
|
+
}
|
|
6130
|
+
const bounds = [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]];
|
|
6131
|
+
// TODO - pass signal to getTile
|
|
6132
|
+
// abort request if signal is aborted
|
|
6133
|
+
const terrain = await this.state.terrainCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z, bounds, this.props.meshMaxError);
|
|
6134
|
+
return Promise.all([terrain, null]);
|
|
6204
6135
|
}
|
|
6205
6136
|
renderSubLayers(props) {
|
|
6206
6137
|
const SubLayerClass = this.getSubLayerClass('mesh', meshLayers.SimpleMeshLayer);
|
|
@@ -6211,13 +6142,13 @@ class TerrainLayer extends core.CompositeLayer {
|
|
|
6211
6142
|
}
|
|
6212
6143
|
// const [mesh, texture] = data;
|
|
6213
6144
|
const [mesh] = data;
|
|
6214
|
-
return new SubLayerClass(
|
|
6145
|
+
return new SubLayerClass({ ...props, tileSize: 256 }, {
|
|
6215
6146
|
data: DUMMY_DATA,
|
|
6216
6147
|
mesh,
|
|
6217
6148
|
// texture,
|
|
6218
6149
|
_instanced: false,
|
|
6219
6150
|
coordinateSystem: core.COORDINATE_SYSTEM.CARTESIAN,
|
|
6220
|
-
getPosition: (d) => [0, 0, 0],
|
|
6151
|
+
// getPosition: (d) => [0, 0, 0],
|
|
6221
6152
|
getColor: color,
|
|
6222
6153
|
wireframe,
|
|
6223
6154
|
material,
|
|
@@ -6233,10 +6164,9 @@ class TerrainLayer extends core.CompositeLayer {
|
|
|
6233
6164
|
.map((tile) => tile.content)
|
|
6234
6165
|
.filter((x) => x && x[0])
|
|
6235
6166
|
.map((arr) => {
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
return bounds === null || bounds === void 0 ? void 0 : bounds.map((bound) => bound[2]);
|
|
6167
|
+
// @ts-expect-error: Tile content structure is not fully typed in the source layer
|
|
6168
|
+
const bounds = arr[0]?.header?.boundingBox;
|
|
6169
|
+
return bounds?.map((bound) => bound[2]);
|
|
6240
6170
|
});
|
|
6241
6171
|
if (ranges.length === 0) {
|
|
6242
6172
|
return;
|
|
@@ -6248,7 +6178,13 @@ class TerrainLayer extends core.CompositeLayer {
|
|
|
6248
6178
|
}
|
|
6249
6179
|
}
|
|
6250
6180
|
renderLayers() {
|
|
6251
|
-
const {
|
|
6181
|
+
const {
|
|
6182
|
+
// color,
|
|
6183
|
+
// material,
|
|
6184
|
+
elevationData,
|
|
6185
|
+
// texture,
|
|
6186
|
+
// wireframe,
|
|
6187
|
+
meshMaxError, elevationDecoder, tileSize, extent, maxRequests, onTileLoad, onTileUnload, onTileError, maxCacheSize, maxCacheByteSize, refinementStrategy, } = this.props;
|
|
6252
6188
|
if (this.state.isTiled && this.state.initialized) {
|
|
6253
6189
|
return new geoLayers.TileLayer(this.getSubLayerProps({
|
|
6254
6190
|
id: 'tiles',
|
|
@@ -6299,8 +6235,6 @@ class TerrainLayer extends core.CompositeLayer {
|
|
|
6299
6235
|
// );
|
|
6300
6236
|
}
|
|
6301
6237
|
}
|
|
6302
|
-
TerrainLayer.defaultProps = defaultProps;
|
|
6303
|
-
TerrainLayer.layerName = 'TerrainLayer';
|
|
6304
6238
|
|
|
6305
6239
|
class RawDecoder extends BaseDecoder {
|
|
6306
6240
|
decodeBlock(buffer) {
|
|
@@ -6309,8 +6243,8 @@ class RawDecoder extends BaseDecoder {
|
|
|
6309
6243
|
}
|
|
6310
6244
|
|
|
6311
6245
|
var raw = /*#__PURE__*/Object.freeze({
|
|
6312
|
-
|
|
6313
|
-
|
|
6246
|
+
__proto__: null,
|
|
6247
|
+
default: RawDecoder
|
|
6314
6248
|
});
|
|
6315
6249
|
|
|
6316
6250
|
const MIN_BITS = 9;
|
|
@@ -6444,8 +6378,8 @@ class LZWDecoder extends BaseDecoder {
|
|
|
6444
6378
|
}
|
|
6445
6379
|
|
|
6446
6380
|
var lzw = /*#__PURE__*/Object.freeze({
|
|
6447
|
-
|
|
6448
|
-
|
|
6381
|
+
__proto__: null,
|
|
6382
|
+
default: LZWDecoder
|
|
6449
6383
|
});
|
|
6450
6384
|
|
|
6451
6385
|
/* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
|
@@ -7343,8 +7277,8 @@ class JpegDecoder extends BaseDecoder {
|
|
|
7343
7277
|
}
|
|
7344
7278
|
|
|
7345
7279
|
var jpeg = /*#__PURE__*/Object.freeze({
|
|
7346
|
-
|
|
7347
|
-
|
|
7280
|
+
__proto__: null,
|
|
7281
|
+
default: JpegDecoder
|
|
7348
7282
|
});
|
|
7349
7283
|
|
|
7350
7284
|
/*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */
|
|
@@ -14115,8 +14049,8 @@ class DeflateDecoder extends BaseDecoder {
|
|
|
14115
14049
|
}
|
|
14116
14050
|
|
|
14117
14051
|
var deflate = /*#__PURE__*/Object.freeze({
|
|
14118
|
-
|
|
14119
|
-
|
|
14052
|
+
__proto__: null,
|
|
14053
|
+
default: DeflateDecoder
|
|
14120
14054
|
});
|
|
14121
14055
|
|
|
14122
14056
|
class PackbitsDecoder extends BaseDecoder {
|
|
@@ -14145,8 +14079,8 @@ class PackbitsDecoder extends BaseDecoder {
|
|
|
14145
14079
|
}
|
|
14146
14080
|
|
|
14147
14081
|
var packbits = /*#__PURE__*/Object.freeze({
|
|
14148
|
-
|
|
14149
|
-
|
|
14082
|
+
__proto__: null,
|
|
14083
|
+
default: PackbitsDecoder
|
|
14150
14084
|
});
|
|
14151
14085
|
|
|
14152
14086
|
var LercDecode = {exports: {}};
|
|
@@ -16589,9 +16523,9 @@ class LercDecoder extends BaseDecoder {
|
|
|
16589
16523
|
}
|
|
16590
16524
|
|
|
16591
16525
|
var lerc = /*#__PURE__*/Object.freeze({
|
|
16592
|
-
|
|
16593
|
-
|
|
16594
|
-
|
|
16526
|
+
__proto__: null,
|
|
16527
|
+
default: LercDecoder,
|
|
16528
|
+
zstd: zstd
|
|
16595
16529
|
});
|
|
16596
16530
|
|
|
16597
16531
|
/**
|
|
@@ -16634,12 +16568,12 @@ class WebImageDecoder extends BaseDecoder {
|
|
|
16634
16568
|
}
|
|
16635
16569
|
|
|
16636
16570
|
var webimage = /*#__PURE__*/Object.freeze({
|
|
16637
|
-
|
|
16638
|
-
|
|
16571
|
+
__proto__: null,
|
|
16572
|
+
default: WebImageDecoder
|
|
16639
16573
|
});
|
|
16640
16574
|
|
|
16641
16575
|
exports.CogBitmapLayer = CogBitmapLayer;
|
|
16642
|
-
exports.CogTerrainLayer =
|
|
16576
|
+
exports.CogTerrainLayer = CogTerrainLayer;
|
|
16643
16577
|
exports.CogTiles = CogTiles;
|
|
16644
16578
|
exports.GeoImage = GeoImage;
|
|
16645
16579
|
//# sourceMappingURL=index.js.map
|