itowns 2.44.3-next.2 → 2.44.3-next.21
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.map +1 -1
- package/dist/itowns_widgets.js +1 -1
- package/dist/itowns_widgets.js.map +1 -1
- package/examples/3dtiles_loader.html +105 -44
- package/examples/config.json +2 -10
- package/examples/images/itowns_logo.svg +123 -0
- package/examples/js/plugins/COGParser.js +1 -1
- package/lib/Controls/GlobeControls.js +38 -21
- package/lib/Controls/StateControl.js +5 -2
- package/lib/Converter/Feature2Mesh.js +9 -2
- package/lib/Converter/textureConverter.js +3 -3
- package/lib/Core/Geographic/Extent.js +74 -266
- package/lib/Core/Prefab/Globe/GlobeLayer.js +1 -1
- package/lib/Core/Prefab/GlobeView.js +0 -4
- package/lib/Core/Prefab/Planar/PlanarLayer.js +1 -1
- package/lib/Core/Tile/Tile.js +219 -0
- package/lib/Core/Tile/TileGrid.js +46 -0
- package/lib/Core/TileMesh.js +2 -1
- package/lib/Core/View.js +13 -6
- package/lib/Layer/C3DTilesLayer.js +15 -14
- package/lib/Layer/LabelLayer.js +8 -4
- package/lib/Layer/OGC3DTilesLayer.js +62 -31
- package/lib/Parser/GeoJsonParser.js +1 -1
- package/lib/Parser/VectorTileParser.js +1 -1
- package/lib/Parser/XbilParser.js +14 -2
- package/lib/Provider/URLBuilder.js +22 -11
- package/lib/Renderer/PointsMaterial.js +1 -1
- package/lib/Source/TMSSource.js +9 -7
- package/lib/Source/VectorTilesSource.js +2 -2
- package/lib/Source/WFSSource.js +14 -8
- package/lib/Source/WMSSource.js +20 -10
- package/lib/Source/WMTSSource.js +13 -7
- package/lib/Utils/gui/C3DTilesStyle.js +2 -3
- package/package.json +9 -3
- 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
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import Coordinates from "../Geographic/Coordinates.js";
|
|
3
|
+
import CRS from "../Geographic/Crs.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 == CRS.tms_3857) {
|
|
173
|
+
const WMTS_PM = [];
|
|
174
|
+
const extent = _extent.copy(e).as(CRS.formatToEPSG(tms), _extent2);
|
|
175
|
+
const {
|
|
176
|
+
globalExtent,
|
|
177
|
+
globalDimension,
|
|
178
|
+
sTs
|
|
179
|
+
} = getInfoTms(CRS.formatToEPSG(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,46 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import CRS from "../Geographic/Crs.js";
|
|
3
|
+
import Extent from "../Geographic/Extent.js";
|
|
4
|
+
const _countTiles = new THREE.Vector2();
|
|
5
|
+
const _dim = new THREE.Vector2();
|
|
6
|
+
export const globalExtentTMS = new Map();
|
|
7
|
+
export const schemeTiles = new Map();
|
|
8
|
+
const extent4326 = new Extent('EPSG:4326', -180, 180, -90, 90);
|
|
9
|
+
globalExtentTMS.set('EPSG:4326', extent4326);
|
|
10
|
+
|
|
11
|
+
// Compute global extent of TMS in EPSG:3857
|
|
12
|
+
// It's square whose a side is between -180° to 180°.
|
|
13
|
+
// So, west extent, it's 180 convert in EPSG:3857
|
|
14
|
+
const extent3857 = extent4326.as('EPSG:3857');
|
|
15
|
+
extent3857.clampSouthNorth(extent3857.west, extent3857.east);
|
|
16
|
+
globalExtentTMS.set('EPSG:3857', extent3857);
|
|
17
|
+
schemeTiles.set('default', new THREE.Vector2(1, 1));
|
|
18
|
+
schemeTiles.set(CRS.tms_3857, schemeTiles.get('default'));
|
|
19
|
+
schemeTiles.set(CRS.tms_4326, new THREE.Vector2(2, 1));
|
|
20
|
+
export function getInfoTms(/** @type {string} */crs) {
|
|
21
|
+
const epsg = CRS.formatToEPSG(crs);
|
|
22
|
+
const globalExtent = globalExtentTMS.get(epsg);
|
|
23
|
+
const globalDimension = globalExtent.planarDimensions(_dim);
|
|
24
|
+
const tms = CRS.formatToTms(crs);
|
|
25
|
+
const sTs = schemeTiles.get(tms) || schemeTiles.get('default');
|
|
26
|
+
// The isInverted parameter is to be set to the correct value, true or false
|
|
27
|
+
// (default being false) if the computation of the coordinates needs to be
|
|
28
|
+
// inverted to match the same scheme as OSM, Google Maps or other system.
|
|
29
|
+
// See link below for more information
|
|
30
|
+
// https://alastaira.wordpress.com/2011/07/06/converting-tms-tile-coordinates-to-googlebingosm-tile-coordinates/
|
|
31
|
+
// in crs includes ':NI' => tms isn't inverted (NOT INVERTED)
|
|
32
|
+
const isInverted = !tms.includes(':NI');
|
|
33
|
+
return {
|
|
34
|
+
epsg,
|
|
35
|
+
globalExtent,
|
|
36
|
+
globalDimension,
|
|
37
|
+
sTs,
|
|
38
|
+
isInverted
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function getCountTiles(/** @type {string} */crs, /** @type {number} */zoom) {
|
|
42
|
+
const sTs = schemeTiles.get(CRS.formatToTms(crs)) || schemeTiles.get('default');
|
|
43
|
+
const count = 2 ** zoom;
|
|
44
|
+
_countTiles.set(count, count).multiply(sTs);
|
|
45
|
+
return _countTiles;
|
|
46
|
+
}
|
package/lib/Core/TileMesh.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import CRS from "./Geographic/Crs.js";
|
|
3
3
|
import { geoidLayerIsVisible } from "../Layer/GeoidLayer.js";
|
|
4
|
+
import { tiledCovering } from "./Tile/Tile.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* A TileMesh is a THREE.Mesh with a geometricError and an OBB
|
|
@@ -29,7 +30,7 @@ class TileMesh extends THREE.Mesh {
|
|
|
29
30
|
this.boundingSphere = new THREE.Sphere();
|
|
30
31
|
this.obb.box3D.getBoundingSphere(this.boundingSphere);
|
|
31
32
|
for (const tms of layer.tileMatrixSets) {
|
|
32
|
-
this.#_tms.set(tms, this.extent
|
|
33
|
+
this.#_tms.set(tms, tiledCovering(this.extent, tms));
|
|
33
34
|
}
|
|
34
35
|
this.frustumCulled = false;
|
|
35
36
|
this.matrixAutoUpdate = false;
|
package/lib/Core/View.js
CHANGED
|
@@ -30,7 +30,8 @@ export const VIEW_EVENTS = {
|
|
|
30
30
|
LAYER_ADDED: 'layer-added',
|
|
31
31
|
INITIALIZED: 'initialized',
|
|
32
32
|
COLOR_LAYERS_ORDER_CHANGED,
|
|
33
|
-
CAMERA_MOVED: 'camera-moved'
|
|
33
|
+
CAMERA_MOVED: 'camera-moved',
|
|
34
|
+
DISPOSED: 'disposed'
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
/**
|
|
@@ -109,9 +110,11 @@ const coordinates = new Coordinates('EPSG:4326');
|
|
|
109
110
|
const viewers = [];
|
|
110
111
|
// Size of the camera frustrum, in meters
|
|
111
112
|
let screenMeters;
|
|
113
|
+
let id = 0;
|
|
112
114
|
|
|
113
115
|
/**
|
|
114
|
-
* @property {
|
|
116
|
+
* @property {number} id - The id of the view. It's incremented at each new view instance, starting at 0.
|
|
117
|
+
* @property {HTMLElement} domElement - The domElement holding the canvas where the view is displayed
|
|
115
118
|
* @property {String} referenceCrs - The coordinate reference system of the view
|
|
116
119
|
* @property {MainLoop} mainLoop - itowns mainloop scheduling the operations
|
|
117
120
|
* @property {THREE.Scene} scene - threejs scene of the view
|
|
@@ -136,10 +139,10 @@ class View extends THREE.EventDispatcher {
|
|
|
136
139
|
* var view = itowns.View('EPSG:4326', viewerDiv, { camera: { type: itowns.CAMERA_TYPE.ORTHOGRAPHIC } });
|
|
137
140
|
* var customControls = itowns.THREE.OrbitControls(view.camera3D, viewerDiv);
|
|
138
141
|
*
|
|
139
|
-
* @param {
|
|
142
|
+
* @param {String} crs - The default CRS of Three.js coordinates. Should be a cartesian CRS.
|
|
140
143
|
* @param {HTMLElement} viewerDiv - Where to instanciate the Three.js scene in the DOM
|
|
141
144
|
* @param {Object} [options] - Optional properties.
|
|
142
|
-
* @param {
|
|
145
|
+
* @param {Object} [options.camera] - Options for the camera associated to the view. See {@link Camera} options.
|
|
143
146
|
* @param {MainLoop} [options.mainLoop] - {@link MainLoop} instance to use, otherwise a default one will be constructed
|
|
144
147
|
* @param {WebGLRenderer|Object} [options.renderer] - {@link WebGLRenderer} instance to use, otherwise
|
|
145
148
|
* a default one will be constructed. In this case, if options.renderer is an object, it will be used to
|
|
@@ -159,6 +162,7 @@ class View extends THREE.EventDispatcher {
|
|
|
159
162
|
}
|
|
160
163
|
super();
|
|
161
164
|
this.domElement = viewerDiv;
|
|
165
|
+
this.id = id++;
|
|
162
166
|
this.referenceCrs = crs;
|
|
163
167
|
let engine;
|
|
164
168
|
// options.renderer can be 2 separate things:
|
|
@@ -272,8 +276,6 @@ class View extends THREE.EventDispatcher {
|
|
|
272
276
|
}
|
|
273
277
|
// remove alls frameRequester
|
|
274
278
|
this.removeAllFrameRequesters();
|
|
275
|
-
// remove alls events
|
|
276
|
-
this.removeAllEvents();
|
|
277
279
|
// remove all layers
|
|
278
280
|
const layers = this.getLayers(l => !l.isTiledGeometryLayer && !l.isAtmosphere);
|
|
279
281
|
for (const layer of layers) {
|
|
@@ -290,6 +292,11 @@ class View extends THREE.EventDispatcher {
|
|
|
290
292
|
viewers.splice(id, 1);
|
|
291
293
|
// Remove remaining objects in the scene (e.g. helpers, debug, etc.)
|
|
292
294
|
this.scene.traverse(ObjectRemovalHelper.cleanup);
|
|
295
|
+
this.dispatchEvent({
|
|
296
|
+
type: VIEW_EVENTS.DISPOSED
|
|
297
|
+
});
|
|
298
|
+
// remove alls events
|
|
299
|
+
this.removeAllEvents();
|
|
293
300
|
}
|
|
294
301
|
|
|
295
302
|
/**
|
|
@@ -102,6 +102,7 @@ class C3DTilesLayer extends GeometryLayer {
|
|
|
102
102
|
* @param {View} view The view
|
|
103
103
|
*/
|
|
104
104
|
constructor(id, config, view) {
|
|
105
|
+
console.warn('C3DTilesLayer is deprecated and will be removed in iTowns 3.0 version. Use OGC3DTilesLayer instead.');
|
|
105
106
|
super(id, new THREE.Group(), {
|
|
106
107
|
source: config.source
|
|
107
108
|
});
|
|
@@ -143,7 +144,7 @@ class C3DTilesLayer extends GeometryLayer {
|
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
/** @type {Style} */
|
|
146
|
-
this.
|
|
147
|
+
this.style = config.style || null;
|
|
147
148
|
|
|
148
149
|
/** @type {Map<string, THREE.MeshStandardMaterial>} */
|
|
149
150
|
this.#fillColorMaterialsBuffer = new Map();
|
|
@@ -368,21 +369,15 @@ class C3DTilesLayer extends GeometryLayer {
|
|
|
368
369
|
if (c3DTileFeature.object3d != object3d) {
|
|
369
370
|
continue; // this feature do not belong to object3d
|
|
370
371
|
}
|
|
372
|
+
this._style.context.setGeometry({
|
|
373
|
+
properties: c3DTileFeature
|
|
374
|
+
});
|
|
375
|
+
|
|
371
376
|
/** @type {THREE.Color} */
|
|
372
|
-
|
|
373
|
-
if (typeof this._style.fill.color === 'function') {
|
|
374
|
-
color = new THREE.Color(this._style.fill.color(c3DTileFeature));
|
|
375
|
-
} else {
|
|
376
|
-
color = new THREE.Color(this._style.fill.color);
|
|
377
|
-
}
|
|
377
|
+
const color = new THREE.Color(this._style.fill.color);
|
|
378
378
|
|
|
379
379
|
/** @type {number} */
|
|
380
|
-
|
|
381
|
-
if (typeof this._style.fill.opacity === 'function') {
|
|
382
|
-
opacity = this._style.fill.opacity(c3DTileFeature);
|
|
383
|
-
} else {
|
|
384
|
-
opacity = this._style.fill.opacity;
|
|
385
|
-
}
|
|
380
|
+
const opacity = this._style.fill.opacity;
|
|
386
381
|
const materialId = color.getHexString() + opacity;
|
|
387
382
|
let material = null;
|
|
388
383
|
if (this.#fillColorMaterialsBuffer.has(materialId)) {
|
|
@@ -445,7 +440,13 @@ class C3DTilesLayer extends GeometryLayer {
|
|
|
445
440
|
return this.#fillColorMaterialsBuffer.size;
|
|
446
441
|
}
|
|
447
442
|
set style(value) {
|
|
448
|
-
|
|
443
|
+
if (value instanceof Style) {
|
|
444
|
+
this._style = value;
|
|
445
|
+
} else if (!value) {
|
|
446
|
+
this._style = null;
|
|
447
|
+
} else {
|
|
448
|
+
this._style = new Style(value);
|
|
449
|
+
}
|
|
449
450
|
this.updateStyle();
|
|
450
451
|
}
|
|
451
452
|
get style() {
|
package/lib/Layer/LabelLayer.js
CHANGED
|
@@ -216,17 +216,21 @@ class LabelLayer extends GeometryLayer {
|
|
|
216
216
|
*
|
|
217
217
|
* @param {FeatureCollection} data - The FeatureCollection to read the
|
|
218
218
|
* labels from.
|
|
219
|
-
* @param {Extent}
|
|
219
|
+
* @param {Extent|Tile} extentOrTile
|
|
220
220
|
*
|
|
221
221
|
* @return {Label[]} An array containing all the created labels.
|
|
222
222
|
*/
|
|
223
|
-
convert(data,
|
|
223
|
+
convert(data, extentOrTile) {
|
|
224
224
|
const labels = [];
|
|
225
225
|
|
|
226
226
|
// Converting the extent now is faster for further operation
|
|
227
|
-
|
|
227
|
+
if (extentOrTile.isExtent) {
|
|
228
|
+
extentOrTile.as(data.crs, _extent);
|
|
229
|
+
} else {
|
|
230
|
+
extentOrTile.toExtent(data.crs, _extent);
|
|
231
|
+
}
|
|
228
232
|
coord.crs = data.crs;
|
|
229
|
-
context.setZoom(
|
|
233
|
+
context.setZoom(extentOrTile.zoom);
|
|
230
234
|
data.features.forEach(f => {
|
|
231
235
|
// TODO: add support for LINE and POLYGON
|
|
232
236
|
if (f.type !== FEATURE_TYPES.POINT) {
|
|
@@ -6,19 +6,20 @@ import { DRACOLoader } from "../ThreeExtended/loaders/DRACOLoader.js";
|
|
|
6
6
|
import { KTX2Loader } from "../ThreeExtended/loaders/KTX2Loader.js";
|
|
7
7
|
import ReferLayerProperties from "./ReferencingLayerProperties.js";
|
|
8
8
|
import PointsMaterial, { PNTS_MODE, PNTS_SHAPE, PNTS_SIZE_MODE, ClassificationScheme } from "../Renderer/PointsMaterial.js";
|
|
9
|
+
import { VIEW_EVENTS } from "../Core/View.js";
|
|
9
10
|
const _raycaster = new THREE.Raycaster();
|
|
10
11
|
|
|
12
|
+
// Stores lruCache, downloadQueue and parseQueue for each id of view {@link View}
|
|
13
|
+
// every time a tileset has been added
|
|
14
|
+
// https://github.com/iTowns/itowns/issues/2426
|
|
15
|
+
const viewers = {};
|
|
16
|
+
|
|
11
17
|
// Internal instance of GLTFLoader, passed to 3d-tiles-renderer-js to support GLTF 1.0 and 2.0
|
|
12
18
|
// Temporary exported to be used in deprecated B3dmParser
|
|
13
19
|
export const itownsGLTFLoader = new iGLTFLoader();
|
|
14
20
|
itownsGLTFLoader.register(() => new GLTFMeshFeaturesExtension());
|
|
15
21
|
itownsGLTFLoader.register(() => new GLTFStructuralMetadataExtension());
|
|
16
22
|
itownsGLTFLoader.register(() => new GLTFCesiumRTCExtension());
|
|
17
|
-
|
|
18
|
-
// Instantiated by the first tileset. Used to share cache and download and parse queues between tilesets
|
|
19
|
-
let lruCache = null;
|
|
20
|
-
let downloadQueue = null;
|
|
21
|
-
let parseQueue = null;
|
|
22
23
|
export const OGC3DTILES_LAYER_EVENTS = {
|
|
23
24
|
/**
|
|
24
25
|
* Fired when a new root or child tile set is loaded
|
|
@@ -52,7 +53,19 @@ export const OGC3DTILES_LAYER_EVENTS = {
|
|
|
52
53
|
* @property {Object} tile - the tile metadata from the tileset
|
|
53
54
|
* @property {boolean} visible - the tile visible state
|
|
54
55
|
*/
|
|
55
|
-
TILE_VISIBILITY_CHANGE: 'tile-visibility-change'
|
|
56
|
+
TILE_VISIBILITY_CHANGE: 'tile-visibility-change',
|
|
57
|
+
/**
|
|
58
|
+
* Fired when a new batch of tiles start loading (can be fired multiple times, e.g. when the camera moves and new tiles
|
|
59
|
+
* start loading)
|
|
60
|
+
* @event OGC3DTilesLayer#tiles-load-start
|
|
61
|
+
*/
|
|
62
|
+
TILES_LOAD_START: 'tiles-load-start',
|
|
63
|
+
/**
|
|
64
|
+
* Fired when all visible tiles are loaded (can be fired multiple times, e.g. when the camera moves and new tiles
|
|
65
|
+
* are loaded)
|
|
66
|
+
* @event OGC3DTilesLayer#tiles-load-end
|
|
67
|
+
*/
|
|
68
|
+
TILES_LOAD_END: 'tiles-load-end'
|
|
56
69
|
};
|
|
57
70
|
|
|
58
71
|
/**
|
|
@@ -95,6 +108,13 @@ export function enableKtx2Loader(path, renderer) {
|
|
|
95
108
|
class OGC3DTilesLayer extends GeometryLayer {
|
|
96
109
|
/**
|
|
97
110
|
* Layer for [3D Tiles](https://www.ogc.org/standard/3dtiles/) datasets.
|
|
111
|
+
*
|
|
112
|
+
* Advanced configuration note: 3D Tiles rendering is delegated to 3DTilesRendererJS that exposes several
|
|
113
|
+
* configuration options accessible through the tilesRenderer property of this class. see the
|
|
114
|
+
* [3DTilesRendererJS doc](https://github.com/NASA-AMMOS/3DTilesRendererJS/blob/master/README.md). Also note that
|
|
115
|
+
* the cache is shared amongst 3D tiles layers and can be configured through tilesRenderer.lruCache (see the
|
|
116
|
+
* [following documentation](https://github.com/NASA-AMMOS/3DTilesRendererJS/blob/master/README.md#lrucache-1).
|
|
117
|
+
*
|
|
98
118
|
* @extends Layer
|
|
99
119
|
*
|
|
100
120
|
* @param {String} id - unique layer id.
|
|
@@ -109,8 +129,8 @@ class OGC3DTilesLayer extends GeometryLayer {
|
|
|
109
129
|
* @param {String} [config.pntsSizeMode= PNTS_SIZE_MODE.VALUE] {@link PointsMaterial} Point cloud size mode (passed to {@link PointsMaterial}).
|
|
110
130
|
* Only 'VALUE' or 'ATTENUATED' are possible. VALUE use constant size, ATTENUATED compute size depending on distance
|
|
111
131
|
* from point to camera.
|
|
112
|
-
* @param {Number} [config.pntsMinAttenuatedSize=
|
|
113
|
-
* @param {Number} [config.pntsMaxAttenuatedSize=
|
|
132
|
+
* @param {Number} [config.pntsMinAttenuatedSize=3] Minimum scale used by 'ATTENUATED' size mode.
|
|
133
|
+
* @param {Number} [config.pntsMaxAttenuatedSize=10] Maximum scale used by 'ATTENUATED' size mode.
|
|
114
134
|
*/
|
|
115
135
|
constructor(id, config) {
|
|
116
136
|
super(id, new THREE.Group(), {
|
|
@@ -122,17 +142,17 @@ class OGC3DTilesLayer extends GeometryLayer {
|
|
|
122
142
|
if (config.source.isOGC3DTilesIonSource) {
|
|
123
143
|
this.tilesRenderer.registerPlugin(new CesiumIonAuthPlugin({
|
|
124
144
|
apiToken: config.source.accessToken,
|
|
125
|
-
assetId: config.source.assetId
|
|
145
|
+
assetId: config.source.assetId,
|
|
146
|
+
autoRefreshToken: true
|
|
126
147
|
}));
|
|
127
148
|
} else if (config.source.isOGC3DTilesGoogleSource) {
|
|
128
149
|
this.tilesRenderer.registerPlugin(new GoogleCloudAuthPlugin({
|
|
129
|
-
apiToken: config.source.key
|
|
150
|
+
apiToken: config.source.key,
|
|
151
|
+
autoRefreshToken: true
|
|
130
152
|
}));
|
|
131
153
|
}
|
|
132
154
|
this.tilesRenderer.registerPlugin(new ImplicitTilingPlugin());
|
|
133
155
|
this.tilesRenderer.manager.addHandler(/\.gltf$/, itownsGLTFLoader);
|
|
134
|
-
this._setupCacheAndQueues();
|
|
135
|
-
this._setupEvents();
|
|
136
156
|
this.object3d.add(this.tilesRenderer.group);
|
|
137
157
|
|
|
138
158
|
// Add an initialization step that is resolved when the root tileset is loaded (see this._setup below), meaning
|
|
@@ -167,29 +187,31 @@ class OGC3DTilesLayer extends GeometryLayer {
|
|
|
167
187
|
this.pntsShape = config.pntsShape ?? PNTS_SHAPE.CIRCLE;
|
|
168
188
|
this.classification = config.classification ?? ClassificationScheme.DEFAULT;
|
|
169
189
|
this.pntsSizeMode = config.pntsSizeMode ?? PNTS_SIZE_MODE.VALUE;
|
|
170
|
-
this.pntsMinAttenuatedSize = config.pntsMinAttenuatedSize ||
|
|
171
|
-
this.pntsMaxAttenuatedSize = config.pntsMaxAttenuatedSize ||
|
|
190
|
+
this.pntsMinAttenuatedSize = config.pntsMinAttenuatedSize || 3;
|
|
191
|
+
this.pntsMaxAttenuatedSize = config.pntsMaxAttenuatedSize || 10;
|
|
172
192
|
}
|
|
173
193
|
|
|
174
194
|
/**
|
|
175
|
-
* Sets the lruCache and download and parse queues so they are shared amongst
|
|
195
|
+
* Sets the lruCache and download and parse queues so they are shared amongst
|
|
196
|
+
* all tilesets from a same {@link View} view.
|
|
197
|
+
* @param {View} view - view associated to this layer.
|
|
176
198
|
* @private
|
|
177
199
|
*/
|
|
178
|
-
_setupCacheAndQueues() {
|
|
179
|
-
|
|
180
|
-
|
|
200
|
+
_setupCacheAndQueues(view) {
|
|
201
|
+
const id = view.id;
|
|
202
|
+
if (viewers[id]) {
|
|
203
|
+
this.tilesRenderer.lruCache = viewers[id].lruCache;
|
|
204
|
+
this.tilesRenderer.downloadQueue = viewers[id].downloadQueue;
|
|
205
|
+
this.tilesRenderer.parseQueue = viewers[id].parseQueue;
|
|
181
206
|
} else {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
parseQueue = this.tilesRenderer.parseQueue;
|
|
191
|
-
} else {
|
|
192
|
-
this.tilesRenderer.parseQueue = parseQueue;
|
|
207
|
+
viewers[id] = {
|
|
208
|
+
lruCache: this.tilesRenderer.lruCache,
|
|
209
|
+
downloadQueue: this.tilesRenderer.downloadQueue,
|
|
210
|
+
parseQueue: this.tilesRenderer.parseQueue
|
|
211
|
+
};
|
|
212
|
+
view.addEventListener(VIEW_EVENTS.DISPOSED, evt => {
|
|
213
|
+
delete viewers[evt.target.id];
|
|
214
|
+
});
|
|
193
215
|
}
|
|
194
216
|
}
|
|
195
217
|
|
|
@@ -233,6 +255,9 @@ class OGC3DTilesLayer extends GeometryLayer {
|
|
|
233
255
|
});
|
|
234
256
|
view.notifyChange(this);
|
|
235
257
|
});
|
|
258
|
+
this._setupCacheAndQueues(view);
|
|
259
|
+
this._setupEvents();
|
|
260
|
+
|
|
236
261
|
// Start loading tileset and tiles
|
|
237
262
|
this.tilesRenderer.update();
|
|
238
263
|
}
|
|
@@ -274,7 +299,7 @@ class OGC3DTilesLayer extends GeometryLayer {
|
|
|
274
299
|
|
|
275
300
|
// Setup classification bufferAttribute
|
|
276
301
|
if (model.isPoints) {
|
|
277
|
-
const classificationData = batchTable?.
|
|
302
|
+
const classificationData = batchTable?.getPropertyArray('Classification');
|
|
278
303
|
if (classificationData) {
|
|
279
304
|
geometry.setAttribute('classification', new THREE.BufferAttribute(classificationData, 1));
|
|
280
305
|
}
|
|
@@ -348,7 +373,13 @@ class OGC3DTilesLayer extends GeometryLayer {
|
|
|
348
373
|
_raycaster.near = camera.near;
|
|
349
374
|
_raycaster.far = camera.far;
|
|
350
375
|
_raycaster.firstHitOnly = true;
|
|
351
|
-
_raycaster.intersectObject(this.tilesRenderer.group, true
|
|
376
|
+
const picked = _raycaster.intersectObject(this.tilesRenderer.group, true);
|
|
377
|
+
// Store the layer of the picked object to conform to the interface of what's returned by Picking.js (used for
|
|
378
|
+
// other GeometryLayers
|
|
379
|
+
picked.forEach(p => {
|
|
380
|
+
p.layer = this;
|
|
381
|
+
});
|
|
382
|
+
target.push(...picked);
|
|
352
383
|
return target;
|
|
353
384
|
}
|
|
354
385
|
|
|
@@ -195,7 +195,7 @@ export default {
|
|
|
195
195
|
_in.crs = _in.crs || readCRS(json);
|
|
196
196
|
if (out.filteringExtent) {
|
|
197
197
|
if (typeof out.filteringExtent == 'boolean') {
|
|
198
|
-
out.filterExtent = options.extent.as(_in.crs);
|
|
198
|
+
out.filterExtent = options.extent.isExtent ? options.extent.as(_in.crs) : options.extent.toExtent(_in.crs);
|
|
199
199
|
} else if (out.filteringExtent.isExtent) {
|
|
200
200
|
out.filterExtent = out.filteringExtent;
|
|
201
201
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Vector2, Vector3 } from 'three';
|
|
2
2
|
import Protobuf from 'pbf';
|
|
3
3
|
import { VectorTile } from '@mapbox/vector-tile';
|
|
4
|
-
import { globalExtentTMS } from "../Core/
|
|
4
|
+
import { globalExtentTMS } from "../Core/Tile/TileGrid.js";
|
|
5
5
|
import { FeatureCollection, FEATURE_TYPES } from "../Core/Feature.js";
|
|
6
6
|
import { deprecatedParsingOptionsToNewOne } from "../Core/Deprecated/Undeprecator.js";
|
|
7
7
|
import Coordinates from "../Core/Geographic/Coordinates.js";
|
package/lib/Parser/XbilParser.js
CHANGED
|
@@ -67,10 +67,22 @@ export function computeMinMaxElevation(texture, pitch, options) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Clamp values to zmin and zmax values configured in ElevationLayer
|
|
73
|
+
if (options.zmin != null) {
|
|
74
|
+
if (min < options.zmin) {
|
|
71
75
|
min = options.zmin;
|
|
72
76
|
}
|
|
73
|
-
if (
|
|
77
|
+
if (max < options.zmin) {
|
|
78
|
+
max = options.zmin;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (options.zmax != null) {
|
|
82
|
+
if (min > options.zmax) {
|
|
83
|
+
min = options.zmax;
|
|
84
|
+
}
|
|
85
|
+
if (max > options.zmax) {
|
|
74
86
|
max = options.zmax;
|
|
75
87
|
}
|
|
76
88
|
}
|