itowns 2.44.3-next.3 → 2.44.3-next.30
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/CODING.md +1 -1
- package/CONTRIBUTORS.md +1 -0
- package/dist/debug.js +1 -1
- package/dist/debug.js.map +1 -1
- package/dist/itowns.js +1 -1
- package/dist/itowns.js.LICENSE.txt +0 -2
- package/dist/itowns.js.map +1 -1
- package/dist/itowns_widgets.js +1 -1
- package/dist/itowns_widgets.js.map +1 -1
- package/examples/3dtiles_loader.html +109 -45
- package/examples/config.json +2 -10
- package/examples/entwine_3d_loader.html +3 -1
- package/examples/entwine_simple_loader.html +1 -1
- package/examples/images/itowns_logo.svg +123 -0
- package/examples/js/plugins/COGParser.js +1 -1
- package/examples/jsm/OGC3DTilesHelper.js +1 -1
- package/examples/layers/JSONLayers/GeoidMNT.json +3 -1
- package/examples/source_file_geojson_3d.html +0 -1
- package/examples/source_stream_wfs_raster.html +0 -7
- package/lib/Controls/GlobeControls.js +45 -28
- package/lib/Controls/StateControl.js +5 -2
- package/lib/Converter/Feature2Mesh.js +10 -4
- package/lib/Converter/Feature2Texture.js +3 -1
- package/lib/Converter/convertToTile.js +3 -8
- package/lib/Converter/textureConverter.js +3 -4
- package/lib/Core/Deprecated/Undeprecator.js +0 -1
- package/lib/Core/Feature.js +1 -2
- package/lib/Core/Geographic/Coordinates.js +143 -132
- package/lib/Core/Geographic/Crs.js +140 -145
- package/lib/Core/Geographic/Extent.js +72 -267
- package/lib/Core/Geographic/GeoidGrid.js +1 -1
- package/lib/Core/Math/Ellipsoid.js +62 -21
- package/lib/Core/Prefab/Globe/Atmosphere.js +4 -8
- package/lib/Core/Prefab/Globe/GlobeLayer.js +22 -15
- package/lib/Core/Prefab/Globe/GlobeTileBuilder.js +111 -0
- package/lib/Core/Prefab/GlobeView.js +2 -7
- package/lib/Core/Prefab/Planar/PlanarLayer.js +17 -11
- package/lib/Core/Prefab/Planar/PlanarTileBuilder.js +43 -43
- package/lib/Core/Prefab/TileBuilder.js +27 -32
- package/lib/Core/Prefab/computeBufferTileGeometry.js +189 -130
- package/lib/Core/Style.js +3 -3
- package/lib/Core/Tile/Tile.js +219 -0
- package/lib/Core/Tile/TileGrid.js +43 -0
- package/lib/Core/TileGeometry.js +112 -28
- package/lib/Core/TileMesh.js +3 -3
- package/lib/Core/View.js +15 -8
- package/lib/Layer/C3DTilesLayer.js +20 -16
- package/lib/Layer/ColorLayer.js +35 -9
- package/lib/Layer/CopcLayer.js +5 -0
- package/lib/Layer/ElevationLayer.js +39 -7
- package/lib/Layer/EntwinePointTileLayer.js +12 -5
- package/lib/Layer/FeatureGeometryLayer.js +20 -6
- package/lib/Layer/GeometryLayer.js +42 -11
- package/lib/Layer/LabelLayer.js +19 -9
- package/lib/Layer/Layer.js +83 -57
- package/lib/Layer/OGC3DTilesLayer.js +81 -30
- package/lib/Layer/OrientedImageLayer.js +11 -5
- package/lib/Layer/PointCloudLayer.js +74 -30
- package/lib/Layer/Potree2Layer.js +7 -2
- package/lib/Layer/PotreeLayer.js +8 -3
- package/lib/Layer/RasterLayer.js +12 -2
- package/lib/Layer/TiledGeometryLayer.js +69 -13
- package/lib/Main.js +2 -2
- package/lib/Parser/GeoJsonParser.js +1 -1
- package/lib/Parser/VectorTileParser.js +1 -1
- package/lib/Parser/XbilParser.js +14 -2
- package/lib/Provider/Fetcher.js +5 -1
- package/lib/Provider/URLBuilder.js +22 -11
- package/lib/Renderer/Camera.js +1 -1
- package/lib/Renderer/OBB.js +11 -13
- package/lib/Renderer/PointsMaterial.js +1 -1
- package/lib/Renderer/RasterTile.js +1 -2
- package/lib/Renderer/SphereHelper.js +0 -6
- package/lib/Source/CopcSource.js +13 -2
- package/lib/Source/EntwinePointTileSource.js +14 -4
- package/lib/Source/FileSource.js +1 -4
- package/lib/Source/Source.js +1 -4
- package/lib/Source/TMSSource.js +10 -9
- package/lib/Source/VectorTilesSource.js +3 -5
- package/lib/Source/WFSSource.js +15 -10
- package/lib/Source/WMSSource.js +56 -18
- package/lib/Source/WMTSSource.js +13 -7
- package/lib/Utils/CameraUtils.js +1 -1
- package/lib/Utils/gui/C3DTilesStyle.js +2 -3
- package/lib/Utils/placeObjectOnGround.js +0 -1
- package/package.json +13 -6
- package/examples/3dtiles_25d.html +0 -120
- package/examples/3dtiles_basic.html +0 -94
- package/examples/3dtiles_batch_table.html +0 -86
- package/examples/3dtiles_ion.html +0 -126
- package/examples/3dtiles_pointcloud.html +0 -95
- package/lib/Core/Prefab/Globe/BuilderEllipsoidTile.js +0 -110
|
@@ -3,14 +3,52 @@ export function getBufferIndexSize(segments, noSkirt) {
|
|
|
3
3
|
const triangles = segments * segments * 2 + (noSkirt ? 0 : 4 * segments * 2);
|
|
4
4
|
return triangles * 3;
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
function getUintArrayConstructor(highestValue) {
|
|
7
|
+
let picked = null;
|
|
8
|
+
if (highestValue < 2 ** 8) {
|
|
9
|
+
picked = Uint8Array;
|
|
10
|
+
} else if (highestValue < 2 ** 16) {
|
|
11
|
+
picked = Uint16Array;
|
|
12
|
+
} else if (highestValue < 2 ** 32) {
|
|
13
|
+
picked = Uint32Array;
|
|
14
|
+
} else {
|
|
15
|
+
throw new Error('Value is too high');
|
|
16
|
+
}
|
|
17
|
+
return picked;
|
|
18
|
+
}
|
|
19
|
+
function allocateIndexBuffer(nVertex, nSeg, params) {
|
|
20
|
+
if (!params.buildIndexAndUv_0) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const indexBufferSize = getBufferIndexSize(nSeg, params.disableSkirt);
|
|
24
|
+
const indexConstructor = getUintArrayConstructor(nVertex);
|
|
25
|
+
const tileLen = indexBufferSize;
|
|
26
|
+
const skirtLen = 4 * nSeg;
|
|
27
|
+
const indexBuffer = new ArrayBuffer((
|
|
28
|
+
// Tile
|
|
29
|
+
tileLen
|
|
30
|
+
// Skirt
|
|
31
|
+
+ (params.disableSkirt ? 0 : skirtLen)) * indexConstructor.BYTES_PER_ELEMENT);
|
|
32
|
+
const index = new indexConstructor(indexBuffer);
|
|
33
|
+
const skirt = !params.disableSkirt ? index.subarray(tileLen, tileLen + skirtLen) : undefined;
|
|
34
|
+
return {
|
|
35
|
+
index,
|
|
36
|
+
skirt
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function allocateBuffers(nVertex, nSeg, builder, params) {
|
|
40
|
+
const {
|
|
41
|
+
index,
|
|
42
|
+
skirt
|
|
43
|
+
} = allocateIndexBuffer(nVertex, nSeg, params) ?? {};
|
|
44
|
+
return {
|
|
45
|
+
index,
|
|
46
|
+
skirt,
|
|
47
|
+
position: new Float32Array(nVertex * 3),
|
|
48
|
+
normal: new Float32Array(nVertex * 3),
|
|
49
|
+
// 2 UV set per tile: wgs84 (uv[0]) and pseudo-mercator (pm, uv[1])
|
|
50
|
+
// - wgs84: 1 texture per tile because tiles are using wgs84
|
|
51
|
+
// projection
|
|
14
52
|
// - pm: use multiple textures per tile.
|
|
15
53
|
// +-------------------------+
|
|
16
54
|
// | |
|
|
@@ -24,142 +62,157 @@ export default function computeBuffers(params) {
|
|
|
24
62
|
// +-------------------------+
|
|
25
63
|
// * u = wgs84.u
|
|
26
64
|
// * v = textureid + v in builder texture
|
|
27
|
-
uvs: []
|
|
65
|
+
uvs: [params.buildIndexAndUv_0 ? new Float32Array(nVertex * 2) : undefined, builder.computeExtraOffset !== undefined ? new Float32Array(nVertex) : undefined]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function computeUv0(uv, id, u, v) {
|
|
69
|
+
uv[id * 2 + 0] = u;
|
|
70
|
+
uv[id * 2 + 1] = v;
|
|
71
|
+
}
|
|
72
|
+
function initComputeUv1(value) {
|
|
73
|
+
return (uv, id) => {
|
|
74
|
+
uv[id] = value;
|
|
28
75
|
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
76
|
+
}
|
|
77
|
+
/** Compute buffers describing a tile according to a builder and its params. */
|
|
78
|
+
// TODO: Split this even further into subfunctions
|
|
79
|
+
export function computeBuffers(builder, params) {
|
|
80
|
+
// n seg, n+1 vert + <- skirt, n verts per side
|
|
81
|
+
// <---------------> / |
|
|
82
|
+
// +---+---+---+---+ |
|
|
83
|
+
// | / | / | / | / | | Vertices:
|
|
84
|
+
// +---+---+---+---+ - + tile = (n + 1)^2
|
|
85
|
+
// | / | / | / | / | | skirt = 4n
|
|
86
|
+
// +---+---+---+---+ - +
|
|
87
|
+
// | / | / | / | / | | Segments:
|
|
88
|
+
// +---+---+---+---+ - + tile = 2 * n * (n + 1) + n^2
|
|
89
|
+
// | / | / | / | / | | skirt = 2n * 4
|
|
90
|
+
// +---+---+---+---+ |
|
|
91
|
+
const nSeg = Math.max(2, params.segments);
|
|
92
|
+
const nVertex = nSeg + 1;
|
|
93
|
+
const nTileVertex = nVertex ** 2;
|
|
94
|
+
const nSkirtVertex = params.disableSkirt ? 0 : 4 * nSeg;
|
|
95
|
+
const nTotalVertex = nTileVertex + nSkirtVertex;
|
|
96
|
+
|
|
97
|
+
// Computer should combust before this happens
|
|
98
|
+
if (nTotalVertex > 2 ** 32) {
|
|
37
99
|
throw new Error('Tile segments count is too big');
|
|
38
100
|
}
|
|
39
|
-
outBuffers
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (params.buildIndexAndUv_0) {
|
|
48
|
-
if (nVertex < 2 ** 8) {
|
|
49
|
-
outBuffers.index = new Uint8Array(bufferIndexSize);
|
|
50
|
-
} else if (nVertex < 2 ** 16) {
|
|
51
|
-
outBuffers.index = new Uint16Array(bufferIndexSize);
|
|
52
|
-
} else if (nVertex < 2 ** 32) {
|
|
53
|
-
outBuffers.index = new Uint32Array(bufferIndexSize);
|
|
101
|
+
const outBuffers = allocateBuffers(nTotalVertex, nSeg, builder, params);
|
|
102
|
+
const computeUvs = [params.buildIndexAndUv_0 ? computeUv0 : () => {}];
|
|
103
|
+
params = builder.prepare(params);
|
|
104
|
+
for (let y = 0; y <= nSeg; y++) {
|
|
105
|
+
const v = y / nSeg;
|
|
106
|
+
params.coordinates.y = builder.vProject(v, params.extent);
|
|
107
|
+
if (builder.computeExtraOffset !== undefined) {
|
|
108
|
+
computeUvs[1] = initComputeUv1(builder.computeExtraOffset(params));
|
|
54
109
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const widthSegments = Math.max(2, Math.floor(nSeg) || 2);
|
|
62
|
-
const heightSegments = Math.max(2, Math.floor(nSeg) || 2);
|
|
63
|
-
let idVertex = 0;
|
|
64
|
-
const vertices = [];
|
|
65
|
-
let skirt = [];
|
|
66
|
-
const skirtEnd = [];
|
|
67
|
-
builder.prepare(params);
|
|
68
|
-
for (let y = 0; y <= heightSegments; y++) {
|
|
69
|
-
const verticesRow = [];
|
|
70
|
-
const v = y / heightSegments;
|
|
71
|
-
builder.vProjecte(v, params);
|
|
72
|
-
if (uvCount > 1) {
|
|
73
|
-
const u = builder.computeUvs[1](params);
|
|
74
|
-
computeUvs[1] = id => {
|
|
75
|
-
outBuffers.uvs[1][id] = u;
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
for (let x = 0; x <= widthSegments; x++) {
|
|
79
|
-
const u = x / widthSegments;
|
|
80
|
-
const id_m3 = idVertex * 3;
|
|
81
|
-
builder.uProjecte(u, params);
|
|
82
|
-
const vertex = builder.vertexPosition(params, params.projected);
|
|
83
|
-
const normal = builder.vertexNormal(params);
|
|
110
|
+
for (let x = 0; x <= nSeg; x++) {
|
|
111
|
+
const u = x / nSeg;
|
|
112
|
+
const id_m3 = (y * nVertex + x) * 3;
|
|
113
|
+
params.coordinates.x = builder.uProject(u, params.extent);
|
|
114
|
+
const vertex = builder.vertexPosition(params.coordinates);
|
|
115
|
+
const normal = builder.vertexNormal();
|
|
84
116
|
|
|
85
117
|
// move geometry to center world
|
|
86
118
|
vertex.sub(params.center);
|
|
87
119
|
|
|
88
120
|
// align normal to z axis
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
121
|
+
// HACK: this check style is not great
|
|
122
|
+
if ('quatNormalToZ' in params) {
|
|
123
|
+
const quat = params.quatNormalToZ;
|
|
124
|
+
vertex.applyQuaternion(quat);
|
|
125
|
+
normal.applyQuaternion(quat);
|
|
92
126
|
}
|
|
93
127
|
vertex.toArray(outBuffers.position, id_m3);
|
|
94
128
|
normal.toArray(outBuffers.normal, id_m3);
|
|
95
|
-
for (const computeUv of computeUvs) {
|
|
96
|
-
computeUv
|
|
97
|
-
|
|
98
|
-
if (!params.disableSkirt) {
|
|
99
|
-
if (y !== 0 && y !== heightSegments) {
|
|
100
|
-
if (x === widthSegments) {
|
|
101
|
-
skirt.push(idVertex);
|
|
102
|
-
} else if (x === 0) {
|
|
103
|
-
skirtEnd.push(idVertex);
|
|
104
|
-
}
|
|
129
|
+
for (const [index, computeUv] of computeUvs.entries()) {
|
|
130
|
+
if (computeUv !== undefined) {
|
|
131
|
+
computeUv(outBuffers.uvs[index], y * nVertex + x, u, v);
|
|
105
132
|
}
|
|
106
133
|
}
|
|
107
|
-
verticesRow.push(idVertex);
|
|
108
|
-
idVertex++;
|
|
109
|
-
}
|
|
110
|
-
vertices.push(verticesRow);
|
|
111
|
-
if (y === 0) {
|
|
112
|
-
skirt = skirt.concat(verticesRow);
|
|
113
|
-
} else if (y === heightSegments) {
|
|
114
|
-
skirt = skirt.concat(verticesRow.slice().reverse());
|
|
115
134
|
}
|
|
116
135
|
}
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
|
|
137
|
+
// Fill skirt index buffer
|
|
138
|
+
if (params.buildIndexAndUv_0 && !params.disableSkirt) {
|
|
139
|
+
for (let x = 0; x < nVertex; x++) {
|
|
140
|
+
// -------->
|
|
141
|
+
// 0---1---2
|
|
142
|
+
// | / | / | [0-9] = assign order
|
|
143
|
+
// +---+---+
|
|
144
|
+
// | / | / |
|
|
145
|
+
// +---+---+
|
|
146
|
+
outBuffers.skirt[x] = x;
|
|
147
|
+
// +---+---+
|
|
148
|
+
// | / | / | [0-9] = assign order
|
|
149
|
+
// +---+---x x = skipped for now
|
|
150
|
+
// | / | / |
|
|
151
|
+
// 0---1---2
|
|
152
|
+
// <--------
|
|
153
|
+
outBuffers.skirt[2 * nVertex - 2 + x] = nVertex ** 2 - (x + 1);
|
|
154
|
+
}
|
|
155
|
+
for (let y = 1; y < nVertex - 1; y++) {
|
|
156
|
+
// +---+---s |
|
|
157
|
+
// | / | / | | o = stored vertices
|
|
158
|
+
// +---+---o | s = already stored
|
|
159
|
+
// | / | / | |
|
|
160
|
+
// +---+---s v
|
|
161
|
+
outBuffers.skirt[nVertex - 1 + y] = y * nVertex + (nVertex - 1);
|
|
162
|
+
// ^ s---+---+
|
|
163
|
+
// | | / | / | o = stored vertices
|
|
164
|
+
// | o---+---+ s = already stored
|
|
165
|
+
// | | / | / |
|
|
166
|
+
// | s---+---+
|
|
167
|
+
outBuffers.skirt[3 * nVertex - 3 + y] = nVertex * (nVertex - 1 - y);
|
|
168
|
+
}
|
|
119
169
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
outBuffers.index[
|
|
124
|
-
|
|
170
|
+
|
|
171
|
+
/** Copy passed indices at the desired index of the output index buffer. */
|
|
172
|
+
function bufferizeTri(id, va, vb, vc) {
|
|
173
|
+
outBuffers.index[id + 0] = va;
|
|
174
|
+
outBuffers.index[id + 1] = vb;
|
|
175
|
+
outBuffers.index[id + 2] = vc;
|
|
125
176
|
}
|
|
126
|
-
let idVertex2 = 0;
|
|
127
177
|
if (params.buildIndexAndUv_0) {
|
|
128
|
-
for (let y = 0; y <
|
|
129
|
-
for (let x = 0; x <
|
|
130
|
-
const v1 =
|
|
131
|
-
const v2 =
|
|
132
|
-
const v3 =
|
|
133
|
-
const v4 =
|
|
134
|
-
|
|
135
|
-
|
|
178
|
+
for (let y = 0; y < nSeg; y++) {
|
|
179
|
+
for (let x = 0; x < nSeg; x++) {
|
|
180
|
+
const v1 = y * nVertex + (x + 1);
|
|
181
|
+
const v2 = y * nVertex + x;
|
|
182
|
+
const v3 = (y + 1) * nVertex + x;
|
|
183
|
+
const v4 = (y + 1) * nVertex + (x + 1);
|
|
184
|
+
const id = (y * nSeg + x) * 6;
|
|
185
|
+
bufferizeTri(id, /**/v4, v2, v1);
|
|
186
|
+
bufferizeTri(id + 3, v4, v3, v2);
|
|
136
187
|
}
|
|
137
188
|
}
|
|
138
189
|
}
|
|
139
|
-
const iStart = idVertex;
|
|
140
190
|
|
|
141
|
-
//
|
|
142
|
-
// The size of the skirt is now a ratio of the size of the tile.
|
|
143
|
-
// To be perfect it should depend on the real elevation delta but too heavy
|
|
144
|
-
|
|
145
|
-
|
|
191
|
+
// PERF: Beware skirt's size influences performance
|
|
192
|
+
// INFO: The size of the skirt is now a ratio of the size of the tile.
|
|
193
|
+
// To be perfect it should depend on the real elevation delta but too heavy
|
|
194
|
+
// to compute
|
|
195
|
+
if (params.buildIndexAndUv_0 && !params.disableSkirt) {
|
|
196
|
+
// We compute the actual size of tile segment to use later for
|
|
197
|
+
// the skirt.
|
|
146
198
|
const segmentSize = new THREE.Vector3().fromArray(outBuffers.position).distanceTo(new THREE.Vector3().fromArray(outBuffers.position, 3));
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
id
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
199
|
+
const buildSkirt = {
|
|
200
|
+
index: (id, v1, v2, v3, v4) => {
|
|
201
|
+
bufferizeTri(id, v1, v2, v3);
|
|
202
|
+
bufferizeTri(id + 3, v1, v3, v4);
|
|
203
|
+
return id + 6;
|
|
204
|
+
},
|
|
205
|
+
uv: (buf, idTo, idFrom) => {
|
|
206
|
+
buf[idTo * 2 + 0] = buf[idFrom * 2 + 0];
|
|
207
|
+
buf[idTo * 2 + 1] = buf[idFrom * 2 + 1];
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Alias for readability
|
|
212
|
+
const start = nTileVertex;
|
|
213
|
+
for (let i = 0; i < outBuffers.skirt.length; i++) {
|
|
214
|
+
const id = outBuffers.skirt[i];
|
|
215
|
+
const id_m3 = (start + i) * 3;
|
|
163
216
|
const id2_m3 = id * 3;
|
|
164
217
|
outBuffers.position[id_m3 + 0] = outBuffers.position[id2_m3 + 0] - outBuffers.normal[id2_m3 + 0] * segmentSize;
|
|
165
218
|
outBuffers.position[id_m3 + 1] = outBuffers.position[id2_m3 + 1] - outBuffers.normal[id2_m3 + 1] * segmentSize;
|
|
@@ -167,17 +220,23 @@ export default function computeBuffers(params) {
|
|
|
167
220
|
outBuffers.normal[id_m3 + 0] = outBuffers.normal[id2_m3 + 0];
|
|
168
221
|
outBuffers.normal[id_m3 + 1] = outBuffers.normal[id2_m3 + 1];
|
|
169
222
|
outBuffers.normal[id_m3 + 2] = outBuffers.normal[id2_m3 + 2];
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
outBuffers.uvs[1][
|
|
223
|
+
buildSkirt.uv(outBuffers.uvs[0], start + i, id);
|
|
224
|
+
if (outBuffers.uvs[1] !== undefined) {
|
|
225
|
+
outBuffers.uvs[1][start + i] = outBuffers.uvs[1][id];
|
|
173
226
|
}
|
|
174
|
-
const idf = (i + 1) % skirt.length;
|
|
175
|
-
const v2 =
|
|
176
|
-
const v3 = idf === 0 ?
|
|
177
|
-
const v4 = skirt[idf];
|
|
178
|
-
|
|
179
|
-
idVertex++;
|
|
227
|
+
const idf = (i + 1) % outBuffers.skirt.length;
|
|
228
|
+
const v2 = start + i;
|
|
229
|
+
const v3 = idf === 0 ? start : start + i + 1;
|
|
230
|
+
const v4 = outBuffers.skirt[idf];
|
|
231
|
+
buildSkirt.index(6 * nSeg ** 2 + i * 6, id, v2, v3, v4);
|
|
180
232
|
}
|
|
181
233
|
}
|
|
182
|
-
|
|
234
|
+
|
|
235
|
+
// Dropping skirt view
|
|
236
|
+
return {
|
|
237
|
+
index: outBuffers.index,
|
|
238
|
+
position: outBuffers.position,
|
|
239
|
+
uvs: outBuffers.uvs,
|
|
240
|
+
normal: outBuffers.normal
|
|
241
|
+
};
|
|
183
242
|
}
|
package/lib/Core/Style.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FEATURE_TYPES } from "./Feature.js";
|
|
2
2
|
import Cache from "./Scheduler/Cache.js";
|
|
3
3
|
import Fetcher from "../Provider/Fetcher.js";
|
|
4
|
-
import * as
|
|
4
|
+
import * as maplibre from '@maplibre/maplibre-gl-style-spec';
|
|
5
5
|
import { Color } from 'three';
|
|
6
6
|
import { deltaE } from "../Renderer/Color.js";
|
|
7
7
|
import Coordinates from "./Geographic/Coordinates.js";
|
|
@@ -73,8 +73,8 @@ function rgba2rgb(orig) {
|
|
|
73
73
|
}
|
|
74
74
|
function readVectorProperty(property, options) {
|
|
75
75
|
if (property != undefined) {
|
|
76
|
-
if (
|
|
77
|
-
return
|
|
76
|
+
if (maplibre.expression.isExpression(property)) {
|
|
77
|
+
return maplibre.expression.createExpression(property, options).value;
|
|
78
78
|
} else {
|
|
79
79
|
return property;
|
|
80
80
|
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import * as CRS from "../Geographic/Crs.js";
|
|
3
|
+
import Coordinates from "../Geographic/Coordinates.js";
|
|
4
|
+
import Extent from "../Geographic/Extent.js";
|
|
5
|
+
import { getInfoTms, getCountTiles } from "./TileGrid.js";
|
|
6
|
+
const _tmsCoord = new THREE.Vector2();
|
|
7
|
+
const _dimensionTile = new THREE.Vector2();
|
|
8
|
+
const r = {
|
|
9
|
+
row: 0,
|
|
10
|
+
col: 0,
|
|
11
|
+
invDiff: 0
|
|
12
|
+
};
|
|
13
|
+
function _rowColfromParent(/** @type {Tile} */tile, /** @type {number} */zoom) {
|
|
14
|
+
const diffLevel = tile.zoom - zoom;
|
|
15
|
+
const diff = 2 ** diffLevel;
|
|
16
|
+
r.invDiff = 1 / diff;
|
|
17
|
+
r.row = (tile.row - tile.row % diff) * r.invDiff;
|
|
18
|
+
r.col = (tile.col - tile.col % diff) * r.invDiff;
|
|
19
|
+
return r;
|
|
20
|
+
}
|
|
21
|
+
const _extent = new Extent('EPSG:4326', [0, 0, 0, 0]);
|
|
22
|
+
const _extent2 = new Extent('EPSG:4326', [0, 0, 0, 0]);
|
|
23
|
+
const _c = new Coordinates('EPSG:4326', 0, 0);
|
|
24
|
+
class Tile {
|
|
25
|
+
/**
|
|
26
|
+
* Tile is a geographical bounding rectangle defined by zoom, row and column.
|
|
27
|
+
*
|
|
28
|
+
* @param {String} crs projection of limit values.
|
|
29
|
+
* @param {number} [zoom=0] zoom value
|
|
30
|
+
* @param {number} [row=0] row value
|
|
31
|
+
* @param {number} [col=0] column value
|
|
32
|
+
*/
|
|
33
|
+
constructor(crs) {
|
|
34
|
+
let zoom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
35
|
+
let row = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
36
|
+
let col = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
|
|
37
|
+
this.isTile = true;
|
|
38
|
+
this.crs = crs;
|
|
39
|
+
this.zoom = zoom;
|
|
40
|
+
this.row = row;
|
|
41
|
+
this.col = col;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Clone this tile
|
|
46
|
+
* @return {Tile} cloned tile
|
|
47
|
+
*/
|
|
48
|
+
clone() {
|
|
49
|
+
return new Tile(this.crs, this.zoom, this.row, this.col);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Convert tile to the specified extent.
|
|
54
|
+
* @param {string} crs the projection of destination.
|
|
55
|
+
* @param {Extent} target copy the destination to target.
|
|
56
|
+
* @return {Extent}
|
|
57
|
+
*/
|
|
58
|
+
toExtent(crs, target) {
|
|
59
|
+
CRS.isValid(crs);
|
|
60
|
+
target = target || new Extent('EPSG:4326', [0, 0, 0, 0]);
|
|
61
|
+
const {
|
|
62
|
+
epsg,
|
|
63
|
+
globalExtent,
|
|
64
|
+
globalDimension
|
|
65
|
+
} = getInfoTms(this.crs);
|
|
66
|
+
const countTiles = getCountTiles(this.crs, this.zoom);
|
|
67
|
+
_dimensionTile.set(1, 1).divide(countTiles).multiply(globalDimension);
|
|
68
|
+
target.west = globalExtent.west + (globalDimension.x - _dimensionTile.x * (countTiles.x - this.col));
|
|
69
|
+
target.east = target.west + _dimensionTile.x;
|
|
70
|
+
target.south = globalExtent.south + _dimensionTile.y * (countTiles.y - this.row - 1);
|
|
71
|
+
target.north = target.south + _dimensionTile.y;
|
|
72
|
+
target.crs = epsg;
|
|
73
|
+
target.zoom = this.zoom;
|
|
74
|
+
return crs == epsg ? target : target.as(crs, target);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Return true if `tile` is inside this tile.
|
|
79
|
+
*
|
|
80
|
+
* @param {Tile} tile the tile to check
|
|
81
|
+
*
|
|
82
|
+
* @return {boolean}
|
|
83
|
+
*/
|
|
84
|
+
isInside(tile) {
|
|
85
|
+
if (this.zoom == tile.zoom) {
|
|
86
|
+
return this.row == tile.row && this.col == tile.col;
|
|
87
|
+
} else if (this.zoom < tile.zoom) {
|
|
88
|
+
return false;
|
|
89
|
+
} else {
|
|
90
|
+
_rowColfromParent(this, tile.zoom);
|
|
91
|
+
return r.row == tile.row && r.col == tile.col;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Return the translation and scale to transform this tile to input tile.
|
|
97
|
+
*
|
|
98
|
+
* @param {Tile} tile input tile
|
|
99
|
+
* @param {THREE.Vector4} target copy the result to target.
|
|
100
|
+
* @return {THREE.Vector4} {x: translation on west-east, y: translation on south-north, z: scale on west-east, w: scale on south-north}
|
|
101
|
+
*/
|
|
102
|
+
offsetToParent(tile) {
|
|
103
|
+
let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Vector4();
|
|
104
|
+
if (this.crs != tile.crs) {
|
|
105
|
+
throw new Error('unsupported mix');
|
|
106
|
+
}
|
|
107
|
+
_rowColfromParent(this, tile.zoom);
|
|
108
|
+
return target.set(this.col * r.invDiff - r.col, this.row * r.invDiff - r.row, r.invDiff, r.invDiff);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Return parent tile with input level
|
|
113
|
+
*
|
|
114
|
+
* @param {number} levelParent level of parent.
|
|
115
|
+
* @return {Tile}
|
|
116
|
+
*/
|
|
117
|
+
tiledExtentParent(levelParent) {
|
|
118
|
+
if (levelParent && levelParent < this.zoom) {
|
|
119
|
+
_rowColfromParent(this, levelParent);
|
|
120
|
+
return new Tile(this.crs, levelParent, r.row, r.col);
|
|
121
|
+
} else {
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Set zoom, row and column values
|
|
128
|
+
*
|
|
129
|
+
* @param {number} [zoom=0] zoom value
|
|
130
|
+
* @param {number} [row=0] row value
|
|
131
|
+
* @param {number} [col=0] column value
|
|
132
|
+
*
|
|
133
|
+
* @return {Tile}
|
|
134
|
+
*/
|
|
135
|
+
set() {
|
|
136
|
+
let zoom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
137
|
+
let row = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
138
|
+
let col = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
139
|
+
this.zoom = zoom;
|
|
140
|
+
this.row = row;
|
|
141
|
+
this.col = col;
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Copy to this tile to input tile.
|
|
147
|
+
* @param {Tile} tile
|
|
148
|
+
* @return {Tile} copied extent
|
|
149
|
+
*/
|
|
150
|
+
copy(tile) {
|
|
151
|
+
this.crs = tile.crs;
|
|
152
|
+
return this.set(tile.zoom, tile.row, tile.col);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Return values of tile in string, separated by the separator input.
|
|
157
|
+
* @param {string} separator
|
|
158
|
+
* @return {string}
|
|
159
|
+
*/
|
|
160
|
+
toString() {
|
|
161
|
+
let separator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
162
|
+
return `${this.zoom}${separator}${this.row}${separator}${this.col}`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {Extent} e
|
|
168
|
+
* @param {string} tms
|
|
169
|
+
* @returns {Tile[]}
|
|
170
|
+
*/
|
|
171
|
+
export function tiledCovering(e, tms) {
|
|
172
|
+
if (e.crs == 'EPSG:4326' && tms == 'EPSG:3857') {
|
|
173
|
+
const WMTS_PM = [];
|
|
174
|
+
const extent = _extent.copy(e).as(tms, _extent2);
|
|
175
|
+
const {
|
|
176
|
+
globalExtent,
|
|
177
|
+
globalDimension,
|
|
178
|
+
sTs
|
|
179
|
+
} = getInfoTms(tms);
|
|
180
|
+
extent.clampByExtent(globalExtent);
|
|
181
|
+
extent.planarDimensions(_dimensionTile);
|
|
182
|
+
const zoom = e.zoom + 1 || Math.floor(Math.log2(Math.round(globalDimension.x / (_dimensionTile.x * sTs.x))));
|
|
183
|
+
const countTiles = getCountTiles(tms, zoom);
|
|
184
|
+
const center = extent.center(_c);
|
|
185
|
+
_tmsCoord.x = center.x - globalExtent.west;
|
|
186
|
+
_tmsCoord.y = globalExtent.north - extent.north;
|
|
187
|
+
_tmsCoord.divide(globalDimension).multiply(countTiles).floor();
|
|
188
|
+
|
|
189
|
+
// ]N; N+1] => N
|
|
190
|
+
const maxRow = Math.ceil((globalExtent.north - extent.south) / globalDimension.x * countTiles.y) - 1;
|
|
191
|
+
for (let r = maxRow; r >= _tmsCoord.y; r--) {
|
|
192
|
+
WMTS_PM.push(new Tile(tms, zoom, r, _tmsCoord.x));
|
|
193
|
+
}
|
|
194
|
+
return WMTS_PM;
|
|
195
|
+
} else {
|
|
196
|
+
const target = new Tile(tms, 0, 0, 0);
|
|
197
|
+
const {
|
|
198
|
+
globalExtent,
|
|
199
|
+
globalDimension,
|
|
200
|
+
sTs,
|
|
201
|
+
isInverted
|
|
202
|
+
} = getInfoTms(e.crs);
|
|
203
|
+
const center = e.center(_c);
|
|
204
|
+
e.planarDimensions(_dimensionTile);
|
|
205
|
+
// Each level has 2^n * 2^n tiles...
|
|
206
|
+
// ... so we count how many tiles of the same width as tile we can fit in the layer
|
|
207
|
+
// ... 2^zoom = tilecount => zoom = log2(tilecount)
|
|
208
|
+
const zoom = Math.floor(Math.log2(Math.round(globalDimension.x / (_dimensionTile.x * sTs.x))));
|
|
209
|
+
const countTiles = getCountTiles(tms, zoom);
|
|
210
|
+
|
|
211
|
+
// Now that we have computed zoom, we can deduce x and y (or row / column)
|
|
212
|
+
_tmsCoord.x = center.x - globalExtent.west;
|
|
213
|
+
_tmsCoord.y = isInverted ? globalExtent.north - center.y : center.y - globalExtent.south;
|
|
214
|
+
_tmsCoord.divide(globalDimension).multiply(countTiles).floor();
|
|
215
|
+
target.set(zoom, _tmsCoord.y, _tmsCoord.x);
|
|
216
|
+
return [target];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
export default Tile;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import Extent from "../Geographic/Extent.js";
|
|
3
|
+
const _countTiles = new THREE.Vector2();
|
|
4
|
+
const _dim = new THREE.Vector2();
|
|
5
|
+
export const globalExtentTMS = new Map();
|
|
6
|
+
export const schemeTiles = new Map();
|
|
7
|
+
const extent4326 = new Extent('EPSG:4326', -180, 180, -90, 90);
|
|
8
|
+
globalExtentTMS.set('EPSG:4326', extent4326);
|
|
9
|
+
|
|
10
|
+
// Compute global extent of TMS in EPSG:3857
|
|
11
|
+
// It's square whose a side is between -180° to 180°.
|
|
12
|
+
// So, west extent, it's 180 convert in EPSG:3857
|
|
13
|
+
const extent3857 = extent4326.as('EPSG:3857');
|
|
14
|
+
extent3857.clampSouthNorth(extent3857.west, extent3857.east);
|
|
15
|
+
globalExtentTMS.set('EPSG:3857', extent3857);
|
|
16
|
+
schemeTiles.set('default', new THREE.Vector2(1, 1));
|
|
17
|
+
schemeTiles.set('EPSG:3857', schemeTiles.get('default'));
|
|
18
|
+
schemeTiles.set('EPSG:4326', new THREE.Vector2(2, 1));
|
|
19
|
+
export function getInfoTms(/** @type {string} */crs) {
|
|
20
|
+
const globalExtent = globalExtentTMS.get(crs);
|
|
21
|
+
const globalDimension = globalExtent.planarDimensions(_dim);
|
|
22
|
+
const sTs = schemeTiles.get(crs) || schemeTiles.get('default');
|
|
23
|
+
// The isInverted parameter is to be set to the correct value, true or false
|
|
24
|
+
// (default being false) if the computation of the coordinates needs to be
|
|
25
|
+
// inverted to match the same scheme as OSM, Google Maps or other system.
|
|
26
|
+
// See link below for more information
|
|
27
|
+
// https://alastaira.wordpress.com/2011/07/06/converting-tms-tile-coordinates-to-googlebingosm-tile-coordinates/
|
|
28
|
+
// in crs includes ':NI' => tms isn't inverted (NOT INVERTED)
|
|
29
|
+
const isInverted = !crs.includes(':NI');
|
|
30
|
+
return {
|
|
31
|
+
epsg: crs,
|
|
32
|
+
globalExtent,
|
|
33
|
+
globalDimension,
|
|
34
|
+
sTs,
|
|
35
|
+
isInverted
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function getCountTiles(/** @type {string} */crs, /** @type {number} */zoom) {
|
|
39
|
+
const sTs = schemeTiles.get(crs) || schemeTiles.get('default');
|
|
40
|
+
const count = 2 ** zoom;
|
|
41
|
+
_countTiles.set(count, count).multiply(sTs);
|
|
42
|
+
return _countTiles;
|
|
43
|
+
}
|