@thatopen/services 0.16.1 → 0.18.0
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/built-in/index.d.ts +1043 -0
- package/dist/cli.js +15 -5
- package/dist/core/client.d.ts +11 -4
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +27 -19
- package/dist/types/files.d.ts +2 -0
- package/docs/builtin/paths.json +16 -0
- package/package.json +2 -1
- package/src/built-in/GISManager/example.ts +326 -0
- package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +5 -2
- package/src/built-in/UIManager/src/gis-panel/example.ts +163 -0
- package/src/built-in/UIManager/src/gis-properties-panel/example.ts +168 -0
- package/src/built-in/UIManager/src/map-panel/example.ts +189 -0
- package/src/built-in/UIManager/src/models-list/example.ts +61 -2
- package/src/built-in/UIManager/src/settings-panel/example.ts +5 -3
- package/src/built-in/UIManager/src/viewer-toolbar/example.ts +143 -19
- package/src/built-in/index.ts +1051 -0
- package/src/core/examples/hidden-files.ts +7 -0
package/src/built-in/index.ts
CHANGED
|
@@ -428,6 +428,11 @@ declare class _CDEManager extends OBC.Component implements OBC.Transitionable<CD
|
|
|
428
428
|
private _permissions;
|
|
429
429
|
private _projectDataFolderId;
|
|
430
430
|
private _appFolderIds;
|
|
431
|
+
/** In-flight folder ensures. Two concurrent first calls (user config and
|
|
432
|
+
* app config load in parallel) used to each miss the cache and create a
|
|
433
|
+
* second folder of the same name; the promise is the lock. */
|
|
434
|
+
private _projectDataFolderPromise;
|
|
435
|
+
private _appFolderPromises;
|
|
431
436
|
private _excludedFolderIds;
|
|
432
437
|
private _projectMembers;
|
|
433
438
|
get projectMembers(): {
|
|
@@ -471,8 +476,20 @@ declare class _CDEManager extends OBC.Component implements OBC.Transitionable<CD
|
|
|
471
476
|
getSelectedFiles(): CDEFile[];
|
|
472
477
|
getActivity(fileId: string): CDEFileActivity[];
|
|
473
478
|
updateFilesMetadata(fileIds: string[], updates: Record<string, string>): Promise<void>;
|
|
479
|
+
/** The context this component cannot work without. Reading or writing
|
|
480
|
+
* project data with no context used to resolve with null / the empty
|
|
481
|
+
* string while persisting NOTHING - a call indistinguishable from
|
|
482
|
+
* success, which cost an integrator days (reported 2026-10-01). A
|
|
483
|
+
* missing context is a setup problem and must say so. */
|
|
484
|
+
private _requireCtx;
|
|
474
485
|
readProjectData(app: string, filename: string): Promise<any>;
|
|
475
486
|
writeProjectData(app: string, filename: string, data: unknown): Promise<string>;
|
|
487
|
+
/** Of several same-named folders, always the oldest. Duplicates exist in
|
|
488
|
+
* real projects (the backend had no unique index and this class had no
|
|
489
|
+
* lock), and `loadFolders` used to pick the LAST root match while the
|
|
490
|
+
* ensure path picked the FIRST: reads and writes could land in different
|
|
491
|
+
* folders of the same name. ObjectIds sort by creation time as strings. */
|
|
492
|
+
private static _oldest;
|
|
476
493
|
private _ensureProjectDataFolder;
|
|
477
494
|
private _ensureAppFolder;
|
|
478
495
|
private _loadPermissions;
|
|
@@ -949,6 +966,1040 @@ export type FileList = InstanceType<typeof _FileList>;
|
|
|
949
966
|
*/
|
|
950
967
|
export const FileList = { uuid: 'b0b5e2a2-0b3a-4a6b-8b1c-0b1c4a6b8b1c' } as typeof _FileList & { uuid: 'b0b5e2a2-0b3a-4a6b-8b1c-0b1c4a6b8b1c' };
|
|
951
968
|
|
|
969
|
+
/** A point-layer vertex whose Z gives the surface height at that place. */
|
|
970
|
+
export interface HeightNode {
|
|
971
|
+
lon: number;
|
|
972
|
+
lat: number;
|
|
973
|
+
z: number;
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* One orthometric height per vertex, in the same nesting as the geometry's
|
|
977
|
+
* coordinates: `number` for a Point, `number[]` for a LineString/MultiPoint/
|
|
978
|
+
* ring, `number[][]` for a MultiLineString/Polygon, and so on.
|
|
979
|
+
*/
|
|
980
|
+
export type HeightTree = number | HeightTree[];
|
|
981
|
+
/**
|
|
982
|
+
* Which rule gave a line end its height (see `resolveLineEnds`): 1 = its own
|
|
983
|
+
* property, 2 = node surface height minus depth, 3 = copied from the other end
|
|
984
|
+
* (pipe drawn level), 4 = node surface height (no depth available).
|
|
985
|
+
*/
|
|
986
|
+
export type EndRule = 1 | 2 | 3 | 4;
|
|
987
|
+
export interface FeatureHeights {
|
|
988
|
+
/**
|
|
989
|
+
* "placed": `heights` holds the per-vertex heights. "flat": draw this
|
|
990
|
+
* feature with the legacy flat behaviour. "skipped": it cannot be placed
|
|
991
|
+
* and must not be drawn.
|
|
992
|
+
*/
|
|
993
|
+
status: "placed" | "flat" | "skipped";
|
|
994
|
+
heights?: HeightTree;
|
|
995
|
+
/** "attributes" lines only — the rule that resolved each end. */
|
|
996
|
+
startRule?: EndRule;
|
|
997
|
+
endRule?: EndRule;
|
|
998
|
+
}
|
|
999
|
+
export interface LayerHeights {
|
|
1000
|
+
/** True when the whole layer must be drawn with the legacy flat behaviour. */
|
|
1001
|
+
fallback: boolean;
|
|
1002
|
+
/** One entry per input feature, same order. Empty when `fallback`. */
|
|
1003
|
+
features: FeatureHeights[];
|
|
1004
|
+
/** Number of "skipped" features. */
|
|
1005
|
+
skipped: number;
|
|
1006
|
+
/** How many line ends each rule resolved ("attributes" layers). */
|
|
1007
|
+
ruleCounts: Record<EndRule, number>;
|
|
1008
|
+
}
|
|
1009
|
+
/** A value read from a property or Z is MISSING when it is not a finite number or is exactly 0. */
|
|
1010
|
+
export declare function isMissingHeight(value: unknown): boolean;
|
|
1011
|
+
/**
|
|
1012
|
+
* Horizontal distance in metres between two lon/lat pairs — equirectangular
|
|
1013
|
+
* approximation, plenty for the sub-metre comparisons and line-length weights
|
|
1014
|
+
* it is used for here.
|
|
1015
|
+
*/
|
|
1016
|
+
export declare function horizontalDistance(lon1: number, lat1: number, lon2: number, lat2: number): number;
|
|
1017
|
+
/**
|
|
1018
|
+
* Spatial hash over nodes: cells are twice the snap distance wide, so any node
|
|
1019
|
+
* within the snap distance of a query lies in the 3×3 block of cells around
|
|
1020
|
+
* it — a lookup costs the same at 2,000 nodes as at 50,000.
|
|
1021
|
+
*/
|
|
1022
|
+
export declare class NodeIndex {
|
|
1023
|
+
private readonly _snapDistance;
|
|
1024
|
+
private readonly _cells;
|
|
1025
|
+
private readonly _cellSize;
|
|
1026
|
+
private readonly _lonScale;
|
|
1027
|
+
constructor(nodes: HeightNode[], _snapDistance: number);
|
|
1028
|
+
private _cell;
|
|
1029
|
+
private _key;
|
|
1030
|
+
/** The nearest node within the snap distance of (lon, lat), or null. */
|
|
1031
|
+
nearest(lon: number, lat: number): HeightNode | null;
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Resolves the heights of a line's two ends (rules 1–4, see `EndRule`).
|
|
1035
|
+
* Both ends get rules 1–2 first, so rule 3 never copies a value that itself
|
|
1036
|
+
* came from rule 3/4. Returns null when an end ends up with no height at all.
|
|
1037
|
+
*/
|
|
1038
|
+
export declare function resolveLineEnds(properties: GeoJSON.GeoJsonProperties, start: [number, number], end: [number, number], config: VectorLayerHeightConfig, index: NodeIndex | null): {
|
|
1039
|
+
start: number;
|
|
1040
|
+
end: number;
|
|
1041
|
+
startRule: EndRule;
|
|
1042
|
+
endRule: EndRule;
|
|
1043
|
+
} | null;
|
|
1044
|
+
/**
|
|
1045
|
+
* Resolves the orthometric height of every vertex of every feature in a
|
|
1046
|
+
* layer, following the rules documented on `VectorLayerHeightConfig` ("geometry"
|
|
1047
|
+
* and "attributes"). Emits at most ONE `console.warn` per call (the fallback,
|
|
1048
|
+
* or the skipped-feature summary), never one per vertex/feature.
|
|
1049
|
+
*/
|
|
1050
|
+
export declare function resolveLayerHeights(features: GeoJSON.Feature[], config: VectorLayerHeightConfig, nodes?: HeightNode[], layerId?: string): LayerHeights;
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* Pixels either side of a fat line's own stroke (`Line2`) that still count as
|
|
1054
|
+
* a hit — the strokes are only a couple of pixels wide, so without it a click
|
|
1055
|
+
* has to land on the exact pixel row of the line. three adds
|
|
1056
|
+
* `raycaster.params.Line2.threshold` to the material's full `linewidth` and
|
|
1057
|
+
* then tests against HALF of the sum, hence the doubling in
|
|
1058
|
+
* `pickVectorFeature()`.
|
|
1059
|
+
*/
|
|
1060
|
+
export declare const VECTOR_PICK_TOLERANCE_PX = 6;
|
|
1061
|
+
/** What a pick found: which feature of which layer, and where along the ray. */
|
|
1062
|
+
export interface VectorPickHit {
|
|
1063
|
+
layerId: string;
|
|
1064
|
+
/** Index in the layer's `geojson.features`. */
|
|
1065
|
+
featureIndex: number;
|
|
1066
|
+
/** Distance from the ray origin (the camera, for a perspective camera). */
|
|
1067
|
+
distance: number;
|
|
1068
|
+
point: THREE.Vector3;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Nearest vector-layer feature under a pointer, or null.
|
|
1072
|
+
*
|
|
1073
|
+
* `objects` are the roots `VectorLayers` built (each, or one of its
|
|
1074
|
+
* descendants, carries `userData.{layerId, featureIndex}`) — raycast directly
|
|
1075
|
+
* rather than through a scene, since they may live in the world's scene or in
|
|
1076
|
+
* the private deferred-mode one. `ndc` is the pointer in normalised device
|
|
1077
|
+
* coordinates; `size` is the renderer's pixel size, which fat lines need both
|
|
1078
|
+
* to set their `LineMaterial.resolution` and to turn
|
|
1079
|
+
* {@link VECTOR_PICK_TOLERANCE_PX} into world units.
|
|
1080
|
+
*/
|
|
1081
|
+
export declare function pickVectorFeature(objects: THREE.Object3D[], camera: THREE.Camera, ndc: THREE.Vector2, size: THREE.Vector2): VectorPickHit | null;
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* Per-layer style — `simplestyle-spec` property names, see
|
|
1085
|
+
* docs/adrs/vector-layer-style-property-naming.md for why. Layer-level only
|
|
1086
|
+
* (applies to every feature in the layer), not per-feature.
|
|
1087
|
+
*/
|
|
1088
|
+
export interface VectorLayerStyle {
|
|
1089
|
+
stroke?: string;
|
|
1090
|
+
"stroke-width"?: number;
|
|
1091
|
+
fill?: string;
|
|
1092
|
+
"fill-opacity"?: number;
|
|
1093
|
+
}
|
|
1094
|
+
/** Per-layer placement options (not styling). */
|
|
1095
|
+
export interface VectorLayerOptions {
|
|
1096
|
+
/**
|
|
1097
|
+
* Height of every feature relative to the origin, in metres (negative =
|
|
1098
|
+
* below ground, e.g. buried utilities). Default 0.
|
|
1099
|
+
*/
|
|
1100
|
+
elevation?: number;
|
|
1101
|
+
/**
|
|
1102
|
+
* Opt-in per-feature heights (see `VectorLayerHeightConfig`). When set and
|
|
1103
|
+
* resolvable, each vertex is placed at its own height + the project's geoid
|
|
1104
|
+
* undulation instead of flat at `elevation`. Absent = legacy flat drawing.
|
|
1105
|
+
*/
|
|
1106
|
+
height?: VectorLayerHeightConfig;
|
|
1107
|
+
/** "attributes" heights only — surface nodes (a line end snaps to the nearest one for its surface height). */
|
|
1108
|
+
nodes?: HeightNode[];
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* One feature's geometry in scene coordinates, for 2D consumers (the
|
|
1112
|
+
* minimap). `rings` holds a single vertex for points, the vertices for
|
|
1113
|
+
* lines, and outer ring + holes for polygons (multi-geometries are split
|
|
1114
|
+
* into one feature per part).
|
|
1115
|
+
*/
|
|
1116
|
+
export interface VectorMapFeature {
|
|
1117
|
+
type: "point" | "line" | "polygon";
|
|
1118
|
+
rings: THREE.Vector3[][];
|
|
1119
|
+
/** Index in the layer's `geojson.features` — the same identity a 3D pick yields, shared by every part of a multi-geometry. */
|
|
1120
|
+
featureIndex: number;
|
|
1121
|
+
}
|
|
1122
|
+
/** A loaded layer as seen by 2D consumers. */
|
|
1123
|
+
export interface VectorMapLayer {
|
|
1124
|
+
id: string;
|
|
1125
|
+
style: {
|
|
1126
|
+
stroke: string;
|
|
1127
|
+
strokeWidth: number;
|
|
1128
|
+
fill?: string;
|
|
1129
|
+
fillOpacity: number;
|
|
1130
|
+
};
|
|
1131
|
+
features: VectorMapFeature[];
|
|
1132
|
+
}
|
|
1133
|
+
export declare const VECTOR_HOVER_COLOR = "#FFC107";
|
|
1134
|
+
export declare const VECTOR_SELECTION_COLOR = "#00E5FF";
|
|
1135
|
+
/**
|
|
1136
|
+
* Payload of `VectorLayers.onFeatureSelected`: the selected feature's identity
|
|
1137
|
+
* plus its attribute-table row. `properties` is the feature's own
|
|
1138
|
+
* `properties` object, untouched (keys keep whatever the source file has).
|
|
1139
|
+
*/
|
|
1140
|
+
export interface VectorFeatureSelection {
|
|
1141
|
+
layerId: string;
|
|
1142
|
+
layerName: string;
|
|
1143
|
+
/** Index in the layer's `geojson.features`. */
|
|
1144
|
+
featureIndex: number;
|
|
1145
|
+
geometryType: string;
|
|
1146
|
+
properties: GeoJSON.GeoJsonProperties;
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Internal submodule of `GISManager` for vector GIS layers (shapefile/GeoJSON —
|
|
1150
|
+
* shapefiles are converted to GeoJSON at ingestion time, see docs/adrs). Kept as
|
|
1151
|
+
* a plain internal class rather than a separate `OBC.Component`: `GISManager`
|
|
1152
|
+
* already covers "everything GIS", not just the basemap, so this stays behind
|
|
1153
|
+
* `gisManager.vectorLayers` instead of its own public component with its own
|
|
1154
|
+
* `components.get(...)` entry point.
|
|
1155
|
+
*
|
|
1156
|
+
* Positions features using the same georeferencing math `ReorientationPlugin`
|
|
1157
|
+
* uses to align the 3D Tiles (`Ellipsoid.getObjectFrame()`, WGS84 lat/lon/
|
|
1158
|
+
* height/azimuth -> a rigid ECEF<->scene-local transform), computed directly
|
|
1159
|
+
* from `GISManager`'s own origin params rather than read off the tileset's
|
|
1160
|
+
* `matrixWorld` — see `geoToWorld()` for why: that transform is a pure
|
|
1161
|
+
* function of the 4 origin parameters (confirmed by reading
|
|
1162
|
+
* `ReorientationPlugin.transformLatLonHeightToOrigin`, which touches nothing
|
|
1163
|
+
* tileset-specific besides which `group`/`ellipsoid` object to write into),
|
|
1164
|
+
* so vector layers don't need to wait for a tileset to exist or load, and
|
|
1165
|
+
* stay correct even when `GISManager.snapToGround()` changes the origin's
|
|
1166
|
+
* height independently of any tileset event.
|
|
1167
|
+
*/
|
|
1168
|
+
export declare class VectorLayers {
|
|
1169
|
+
world?: OBC.World;
|
|
1170
|
+
getOrigin?: () => {
|
|
1171
|
+
latitude: number;
|
|
1172
|
+
longitude: number;
|
|
1173
|
+
height: number;
|
|
1174
|
+
rotation: number;
|
|
1175
|
+
};
|
|
1176
|
+
getGeoidUndulation?: () => number;
|
|
1177
|
+
private static readonly _POINT_RADIUS;
|
|
1178
|
+
private _occlusionScene;
|
|
1179
|
+
/** Called once the deferred scene exists, so the owner registers its pass. */
|
|
1180
|
+
ensurePass?: () => void;
|
|
1181
|
+
/** Fires whenever layers are added, removed or rebuilt (e.g. origin change). */
|
|
1182
|
+
readonly onLayersChanged: OBC.Event<void>;
|
|
1183
|
+
/**
|
|
1184
|
+
* Fires with the newly selected feature, or null when the selection is
|
|
1185
|
+
* cleared. Not fired when the already-selected feature is selected again.
|
|
1186
|
+
*/
|
|
1187
|
+
readonly onFeatureSelected: OBC.Event<VectorFeatureSelection | null>;
|
|
1188
|
+
getLayerName?: (id: string) => string | undefined;
|
|
1189
|
+
private _selected;
|
|
1190
|
+
private _hovered;
|
|
1191
|
+
private readonly _highlighted;
|
|
1192
|
+
private readonly _layers;
|
|
1193
|
+
private readonly _mapLayers;
|
|
1194
|
+
/** Loaded layers' geometry for 2D consumers (see `VectorMapFeature`). */
|
|
1195
|
+
get mapLayers(): VectorMapLayer[];
|
|
1196
|
+
private readonly _layerSources;
|
|
1197
|
+
get hasLayers(): boolean;
|
|
1198
|
+
/**
|
|
1199
|
+
* Draws every loaded layer into the renderer's current target, with fat
|
|
1200
|
+
* lines sized for a `width`×`height` pixel target (their width is in
|
|
1201
|
+
* pixels of it). Used by `GISManager`'s deferred pass; also works in
|
|
1202
|
+
* composer mode (objects drawn one by one). The minimap doesn't use this —
|
|
1203
|
+
* it draws `mapLayers` as live 2D vectors instead.
|
|
1204
|
+
*/
|
|
1205
|
+
render(renderer: THREE.WebGLRenderer, camera: THREE.Camera, width: number, height: number): void;
|
|
1206
|
+
/**
|
|
1207
|
+
* @param id When given, tracks everything this call adds under that id so
|
|
1208
|
+
* `removeLayer(id)` can clean it up later — calling `addLayer` again with an
|
|
1209
|
+
* id already in use replaces it (removes the old one first), rather than
|
|
1210
|
+
* duplicating it. Without an id, nothing is tracked and there is nothing to
|
|
1211
|
+
* remove later (matches the previous, untracked behaviour).
|
|
1212
|
+
*/
|
|
1213
|
+
addLayer(geojson: GeoJSON.FeatureCollection, style?: VectorLayerStyle, id?: string, options?: VectorLayerOptions): void;
|
|
1214
|
+
/** Removes and disposes everything the given `id`'s `addLayer()` call added. No-op if unknown/already removed. */
|
|
1215
|
+
removeLayer(id: string): void;
|
|
1216
|
+
private _dropLayer;
|
|
1217
|
+
/**
|
|
1218
|
+
* Nearest feature under a pointer (NDC), or null — see
|
|
1219
|
+
* `pickVectorFeature()`. Costs nothing when no layer is loaded.
|
|
1220
|
+
*/
|
|
1221
|
+
pickFeature(ndc: THREE.Vector2): VectorPickHit | null;
|
|
1222
|
+
/** Selects (and highlights) one feature, replacing any previous selection. No event if it is already the selected one. */
|
|
1223
|
+
selectFeature(layerId: string, featureIndex: number): void;
|
|
1224
|
+
/** The current selection as the `onFeatureSelected` payload (null when nothing is selected) — for consumers created after the event fired. */
|
|
1225
|
+
get selection(): VectorFeatureSelection | null;
|
|
1226
|
+
/** Drops the selection and its highlight. No-op (no event) when nothing is selected. */
|
|
1227
|
+
clearSelection(): void;
|
|
1228
|
+
/** The currently selected / hovered feature (null = none) — read by the 2D map to draw the same states as 3D. */
|
|
1229
|
+
get selectedFeature(): {
|
|
1230
|
+
layerId: string;
|
|
1231
|
+
featureIndex: number;
|
|
1232
|
+
} | null;
|
|
1233
|
+
get hoveredFeature(): {
|
|
1234
|
+
layerId: string;
|
|
1235
|
+
featureIndex: number;
|
|
1236
|
+
} | null;
|
|
1237
|
+
/** Scene-space box of everything built for one feature (null if it isn't loaded), at its real resolved heights. */
|
|
1238
|
+
getFeatureBounds(layerId: string, featureIndex: number): THREE.Box3 | null;
|
|
1239
|
+
/** Hover-highlights one feature (null = none). Does not touch the selection and fires no event. */
|
|
1240
|
+
setHoveredFeature(hit: {
|
|
1241
|
+
layerId: string;
|
|
1242
|
+
featureIndex: number;
|
|
1243
|
+
} | null): void;
|
|
1244
|
+
/** Top-level objects built for one feature (empty if the layer/feature isn't loaded). */
|
|
1245
|
+
private _featureObjects;
|
|
1246
|
+
private _paint;
|
|
1247
|
+
isLayerLoaded(id: string): boolean;
|
|
1248
|
+
/**
|
|
1249
|
+
* Rebuilds every currently loaded layer's geometry from its cached
|
|
1250
|
+
* geojson/style, using the current `getOrigin()` — the fix for
|
|
1251
|
+
* loaded-layer-geometry-freezing-at-load-time: `addLayer()` only reads
|
|
1252
|
+
* `getOrigin()` once, at build time, so an origin change (gizmo drag,
|
|
1253
|
+
* lat/lon/height/rotation inputs, `snapToGround()`) otherwise never moves
|
|
1254
|
+
* already-loaded layers. Callers should debounce this — see
|
|
1255
|
+
* `GISManager.setPosition()` — since it rebuilds real geometry, not just
|
|
1256
|
+
* repositions existing objects.
|
|
1257
|
+
*/
|
|
1258
|
+
reprojectAll(): void;
|
|
1259
|
+
/** Scene-space bounding box of everything `id`'s `addLayer()` call added — `null` if the id is unknown or nothing was actually added. */
|
|
1260
|
+
getLayerBounds(id: string): THREE.Box3 | null;
|
|
1261
|
+
private _disposeObject;
|
|
1262
|
+
private _resolveTargetScene;
|
|
1263
|
+
/**
|
|
1264
|
+
* WGS84 lat/lon/height (degrees, metres above the ellipsoid) -> scene-local
|
|
1265
|
+
* position, computed straight from `GISManager`'s origin params — the exact
|
|
1266
|
+
* same math `ReorientationPlugin.transformLatLonHeightToOrigin` runs against
|
|
1267
|
+
* `tiles.group.matrix` (`Ellipsoid.getObjectFrame()` for the origin's own
|
|
1268
|
+
* lat/lon/height/azimuth -> ECEF<->local transform, inverted), just without
|
|
1269
|
+
* needing a `TilesRenderer` to hold the intermediate result. No tileset
|
|
1270
|
+
* required, and always current — including immediately after
|
|
1271
|
+
* `GISManager.snapToGround()` changes the origin height, with nothing to
|
|
1272
|
+
* resync.
|
|
1273
|
+
*/
|
|
1274
|
+
geoToWorld(lat: number, lon: number, height?: number): THREE.Vector3;
|
|
1275
|
+
/**
|
|
1276
|
+
* lon/lat (GeoJSON coordinate order) -> scene position, X/Z only — Y is the
|
|
1277
|
+
* layer's `elevation` (relative to the origin). This is the legacy flat
|
|
1278
|
+
* path, used by every layer that doesn't opt in to per-feature heights:
|
|
1279
|
+
* `geoToWorld`'s real Y depends on the gap between the origin's height and
|
|
1280
|
+
* each vertex's own height, which produced large, confusing vertical
|
|
1281
|
+
* offsets for raw file Z values — so for these layers per-vertex heights
|
|
1282
|
+
* are ignored and the whole layer sits flat at its `elevation`. Layers with
|
|
1283
|
+
* a `height` config go through `_toScenePosition()` instead.
|
|
1284
|
+
*/
|
|
1285
|
+
private _toSceneVector;
|
|
1286
|
+
/**
|
|
1287
|
+
* One vertex's scene position: with a resolved orthometric `height`, the
|
|
1288
|
+
* full `geoToWorld()` result for `height + undulation` (all three
|
|
1289
|
+
* components, Y included); without one, the legacy flat `_toSceneVector()`.
|
|
1290
|
+
*/
|
|
1291
|
+
private _toScenePosition;
|
|
1292
|
+
/** Renderer's current pixel size — `LineMaterial` needs it to size `linewidth` correctly. */
|
|
1293
|
+
private _resolution;
|
|
1294
|
+
private _buildFeatureObject;
|
|
1295
|
+
private _buildMapFeatures;
|
|
1296
|
+
private _buildPoint;
|
|
1297
|
+
private _buildLine;
|
|
1298
|
+
/**
|
|
1299
|
+
* Surface (`THREE.ShapeGeometry`, triangulated — handles non-convex rings)
|
|
1300
|
+
* when `fill` is set, plus the outline for every ring either way. Built in
|
|
1301
|
+
* local 2D (x, z) then rotated flat: `ShapeGeometry` lives in the XY plane,
|
|
1302
|
+
* and rotating +90° around X maps local (x, y, 0) -> scene (x, 0, y), which
|
|
1303
|
+
* lines up with `_toSceneVector`'s (x, 0, z) exactly; the layer's
|
|
1304
|
+
* `elevation` is re-applied afterwards as a plain vertical translation.
|
|
1305
|
+
* With per-vertex `heights` the outlines follow them, while the (planar)
|
|
1306
|
+
* surface sits at the average resolved height of the outer ring.
|
|
1307
|
+
*/
|
|
1308
|
+
private _buildPolygonSurface;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
export interface GISManagerState {
|
|
1312
|
+
apiToken: string;
|
|
1313
|
+
assetId: string;
|
|
1314
|
+
latitude: number;
|
|
1315
|
+
longitude: number;
|
|
1316
|
+
height: number;
|
|
1317
|
+
rotation: number;
|
|
1318
|
+
tilesVisible: boolean;
|
|
1319
|
+
gizmoVisible: boolean;
|
|
1320
|
+
tilesOffsetEast: number;
|
|
1321
|
+
tilesOffsetNorth: number;
|
|
1322
|
+
tilesOffsetUp: number;
|
|
1323
|
+
/** See-through factor for the tiles (1 = opaque) — see `GISManager.setTilesOpacity`. */
|
|
1324
|
+
tilesOpacity: number;
|
|
1325
|
+
}
|
|
1326
|
+
/** One entry in the persisted vector-layer manifest — metadata only, not the geometry itself. */
|
|
1327
|
+
export interface GISVectorLayerConfig {
|
|
1328
|
+
id: string;
|
|
1329
|
+
name: string;
|
|
1330
|
+
fileId: string;
|
|
1331
|
+
/**
|
|
1332
|
+
* File format: `"shapefile"` (a zip with .shp/.dbf/.prj…) or `"geojson"`. When
|
|
1333
|
+
* missing or unrecognised the downloaded bytes decide (zip signature →
|
|
1334
|
+
* shapefile, leading `{` → GeoJSON).
|
|
1335
|
+
*/
|
|
1336
|
+
type: string;
|
|
1337
|
+
style: VectorLayerStyle;
|
|
1338
|
+
/** Opt-in per-feature heights (see `VectorLayerHeightConfig`); absent = flat. */
|
|
1339
|
+
height?: VectorLayerHeightConfig;
|
|
1340
|
+
}
|
|
1341
|
+
/** How a layer's features get their height. Absent = legacy behaviour: the whole layer is drawn flat. */
|
|
1342
|
+
export interface VectorLayerHeightConfig {
|
|
1343
|
+
/**
|
|
1344
|
+
* "geometry": each vertex uses its own Z (third GeoJSON coordinate).
|
|
1345
|
+
* "attributes": lines get their start/end heights from feature properties.
|
|
1346
|
+
*/
|
|
1347
|
+
source: "geometry" | "attributes";
|
|
1348
|
+
/** "attributes" only — property holding the height of a line's first vertex. */
|
|
1349
|
+
startProperty?: string;
|
|
1350
|
+
/** "attributes" only — property holding the height of a line's last vertex. */
|
|
1351
|
+
endProperty?: string;
|
|
1352
|
+
/** "attributes" only — property holding the depth below the surface at the first vertex. */
|
|
1353
|
+
startDepthProperty?: string;
|
|
1354
|
+
/** "attributes" only — property holding the depth below the surface at the last vertex. */
|
|
1355
|
+
endDepthProperty?: string;
|
|
1356
|
+
/** "attributes" only — ids of point layers (in this same manifest) whose Z gives the surface height at a line end. */
|
|
1357
|
+
nodeLayers?: string[];
|
|
1358
|
+
/** "attributes" only — max horizontal distance (m) between a line end and a node for them to count as the same place. Default 0.5. */
|
|
1359
|
+
nodeSnapDistance?: number;
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Shape of `{GISManager.uuid}.json`, the project-storage file
|
|
1363
|
+
* `GISManager.init()` reads. Origin lives in `ProjectManager`'s own record
|
|
1364
|
+
* instead (the platform's canonical project data) — this file is only the
|
|
1365
|
+
* vector-layer manifest.
|
|
1366
|
+
*/
|
|
1367
|
+
export interface GISPersistedConfig {
|
|
1368
|
+
layers: GISVectorLayerConfig[];
|
|
1369
|
+
/** Small local East/North/Up fine-tune offset (metres) for Google 3D
|
|
1370
|
+
* Tiles — see `GISManager.setTilesOffset`'s own doc comment. Optional:
|
|
1371
|
+
* absent means "no offset saved yet", same as `{0,0,0}`. */
|
|
1372
|
+
tilesOffset?: {
|
|
1373
|
+
east: number;
|
|
1374
|
+
north: number;
|
|
1375
|
+
up: number;
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/** Calls `visit` with every [lon, lat, …] vertex of a GeoJSON geometry (all supported types, collections included). */
|
|
1380
|
+
export declare function forEachVertex(geometry: GeoJSON.Geometry | null, visit: (coord: number[]) => void): void;
|
|
1381
|
+
/**
|
|
1382
|
+
* Horizontal (scene X/Z) bounding box of every vertex of every given feature
|
|
1383
|
+
* collection, each vertex taken through `project(lat, lon)` (the real
|
|
1384
|
+
* `geoToWorld`). Heights are irrelevant here and left at 0. Vertices with
|
|
1385
|
+
* non-finite coordinates are ignored; null when nothing usable is left.
|
|
1386
|
+
*/
|
|
1387
|
+
export declare function layersBoundsXZ(collections: GeoJSON.FeatureCollection[], project: (lat: number, lon: number) => THREE.Vector3): THREE.Box3 | null;
|
|
1388
|
+
/** The square, in scene X/Z, that a minimap capture covers. */
|
|
1389
|
+
export interface MinimapExtent {
|
|
1390
|
+
centerX: number;
|
|
1391
|
+
centerZ: number;
|
|
1392
|
+
/** Half the side of the square, in metres. */
|
|
1393
|
+
half: number;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Extra room around the content's extent, as a FRACTION of its half-size (not
|
|
1397
|
+
* metres), so the outermost features don't sit exactly on the image edge.
|
|
1398
|
+
*/
|
|
1399
|
+
export declare const MINIMAP_EXTENT_MARGIN = 0.05;
|
|
1400
|
+
/**
|
|
1401
|
+
* The capture square for the vector layers' box (world space): centred on its
|
|
1402
|
+
* X/Z centre, with half the larger X/Z span as its half-size (the image stays
|
|
1403
|
+
* square), plus `margin`. With nothing usable — no box, an empty or non-finite
|
|
1404
|
+
* one, or content with no horizontal size at all — falls back to
|
|
1405
|
+
* `fallbackHalf` metres around the origin.
|
|
1406
|
+
*/
|
|
1407
|
+
export declare function computeMinimapExtent(box: THREE.Box3 | null | undefined, fallbackHalf: number, margin?: number): MinimapExtent;
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* What to do when the Highlighter's "select" style highlights something:
|
|
1411
|
+
* - "undo-bim": the highlight belongs to a click that a vector feature just
|
|
1412
|
+
* won (it fires inside that click's short window) — take the BIM highlight
|
|
1413
|
+
* back and keep the vector selection;
|
|
1414
|
+
* - "clear-vector": a genuine BIM selection (viewer, model tree, objects
|
|
1415
|
+
* panel, …) — the vector selection must go;
|
|
1416
|
+
* - "none": nothing to do (an empty highlight outside the window).
|
|
1417
|
+
*/
|
|
1418
|
+
export type BimHighlightAction = "undo-bim" | "clear-vector" | "none";
|
|
1419
|
+
export declare function bimHighlightAction(insideVectorWinWindow: boolean, bimSelectionEmpty: boolean): BimHighlightAction;
|
|
1420
|
+
/** Whether a Highlighter selection map (`modelId → Set<localId>`) selects nothing. */
|
|
1421
|
+
export declare function isModelIdMapEmpty(map: Record<string, Set<number>> | null | undefined): boolean;
|
|
1422
|
+
|
|
1423
|
+
/**
|
|
1424
|
+
* What the minimap needs from `GISManager`, provided as a private hook object
|
|
1425
|
+
* (the minimap never reaches into `GISManager` itself).
|
|
1426
|
+
*/
|
|
1427
|
+
export interface MinimapSource {
|
|
1428
|
+
/**
|
|
1429
|
+
* Gets the Google GIS tiles ready to be drawn from `camera` (registers it
|
|
1430
|
+
* with the streaming renderer and waits until the content it sees has
|
|
1431
|
+
* loaded).
|
|
1432
|
+
*/
|
|
1433
|
+
prepare(camera: THREE.OrthographicCamera, resolution: number): Promise<void>;
|
|
1434
|
+
/** Draws the tiles into the renderer's CURRENT render target (a fresh, transparent one). */
|
|
1435
|
+
draw(renderer: THREE.WebGLRenderer, camera: THREE.OrthographicCamera): void;
|
|
1436
|
+
/** Undoes whatever `prepare` set up. Always called, even if `prepare` threw. */
|
|
1437
|
+
cleanup(camera: THREE.OrthographicCamera): void;
|
|
1438
|
+
/**
|
|
1439
|
+
* Live opacity (0..1) the map applies when drawing the captured image — read
|
|
1440
|
+
* on every redraw, so it can change without recapturing.
|
|
1441
|
+
*/
|
|
1442
|
+
opacity(): number;
|
|
1443
|
+
/** Horizontal world box of ALL the project's vector layers (null when there is none). */
|
|
1444
|
+
getVectorLayersBox(): Promise<THREE.Box3 | null>;
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Result of a capture: the image plus what's needed to map world ↔ image.
|
|
1448
|
+
*/
|
|
1449
|
+
export interface MinimapCapture {
|
|
1450
|
+
/** Top-down, north-up image of the GIS tiles (canvas pixels, sRGB, transparent elsewhere). */
|
|
1451
|
+
image: HTMLCanvasElement;
|
|
1452
|
+
/** Live opacity (0..1) to draw `image` with. */
|
|
1453
|
+
opacity: () => number;
|
|
1454
|
+
/** Side of the image, in pixels. */
|
|
1455
|
+
size: number;
|
|
1456
|
+
/** Copy of the camera the image was taken with, for world ↔ map conversion. */
|
|
1457
|
+
camera: THREE.OrthographicCamera;
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* A marker drawn over the minimap (an HTML overlay on the panel's canvas,
|
|
1461
|
+
* following zoom and pan; hidden while there is no capture).
|
|
1462
|
+
*/
|
|
1463
|
+
export interface MinimapMarker {
|
|
1464
|
+
id: string;
|
|
1465
|
+
/** Scene coordinates. Read on every redraw, so a marker follows if this changes. */
|
|
1466
|
+
position: THREE.Vector3;
|
|
1467
|
+
label?: string;
|
|
1468
|
+
/**
|
|
1469
|
+
* Builds the marker's element. A FACTORY, not an element: a panel can be
|
|
1470
|
+
* destroyed and recreated (the user switching layouts), and each one needs
|
|
1471
|
+
* its own DOM node. A marker with one owns its click behaviour; without it
|
|
1472
|
+
* the panel draws a default badge with the label and reports clicks through
|
|
1473
|
+
* `onMarkerClicked`.
|
|
1474
|
+
*/
|
|
1475
|
+
createElement?: () => HTMLElement;
|
|
1476
|
+
}
|
|
1477
|
+
/** What changed in the marker list — the panel just reconciles against `markers`. */
|
|
1478
|
+
export interface MinimapMarkersChange {
|
|
1479
|
+
kind: "added" | "updated" | "removed" | "cleared";
|
|
1480
|
+
/** The marker's id (absent for "cleared"). */
|
|
1481
|
+
id?: string;
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* Internal submodule of `GISManager` for the 2D minimap, same pattern as
|
|
1485
|
+
* `VectorLayers` (a plain internal class behind `gisManager.minimap`, not its
|
|
1486
|
+
* own component). Baked top-down image: renders the Google GIS tiles once,
|
|
1487
|
+
* from an orthographic camera looking straight down at the middle of the
|
|
1488
|
+
* project's vector layers, into an in-memory image (never persisted). The UI
|
|
1489
|
+
* (`<top-map-panel>`) draws live overlays (vector layers, camera arrow, view
|
|
1490
|
+
* cone) on top of that static image, game-minimap style.
|
|
1491
|
+
*
|
|
1492
|
+
* North-up: scene axes follow the codebase's +X=West, +Y=Up, +Z=North
|
|
1493
|
+
* convention (origin rotation fixed at 0), so the camera's "up" is +Z. The
|
|
1494
|
+
* world ↔ map conversions go through the capture camera's own projection, so
|
|
1495
|
+
* they stay correct without hardcoding that convention anywhere else.
|
|
1496
|
+
*/
|
|
1497
|
+
export declare class Minimap {
|
|
1498
|
+
private readonly _source;
|
|
1499
|
+
world?: OBC.World;
|
|
1500
|
+
/**
|
|
1501
|
+
* Fallback only: half the side of the captured square, in metres, centred on
|
|
1502
|
+
* the origin — used when the project has no usable vector layers. Otherwise
|
|
1503
|
+
* the area is the box of all of them.
|
|
1504
|
+
*/
|
|
1505
|
+
halfExtent: number;
|
|
1506
|
+
/**
|
|
1507
|
+
* Side of the captured image, in pixels (clamped to the GPU's max texture
|
|
1508
|
+
* size). Fixed, so the map gets coarser as the site gets larger: the image
|
|
1509
|
+
* covers the whole data-driven extent. Scaling the image with the extent was
|
|
1510
|
+
* tried and was too slow to capture. The intended improvement is to recapture
|
|
1511
|
+
* only the visible area when the user zooms in on the map, so sharpness no
|
|
1512
|
+
* longer depends on the size of the site.
|
|
1513
|
+
*/
|
|
1514
|
+
resolution: number;
|
|
1515
|
+
readonly onCaptured: OBC.Event<MinimapCapture>;
|
|
1516
|
+
/** Fires once per `addMarker` / `updateMarker` / `removeMarker` / `clearMarkers` that changed something. */
|
|
1517
|
+
readonly onMarkersChanged: OBC.Event<MinimapMarkersChange>;
|
|
1518
|
+
/** Fires with the id when the panel's DEFAULT marker (one without `createElement`) is clicked. */
|
|
1519
|
+
readonly onMarkerClicked: OBC.Event<string>;
|
|
1520
|
+
private readonly _markers;
|
|
1521
|
+
private _capture;
|
|
1522
|
+
private _capturing;
|
|
1523
|
+
constructor(_source: MinimapSource);
|
|
1524
|
+
get capture(): MinimapCapture | null;
|
|
1525
|
+
get capturing(): boolean;
|
|
1526
|
+
/** Every marker, by id (read-only). */
|
|
1527
|
+
get markers(): ReadonlyMap<string, MinimapMarker>;
|
|
1528
|
+
getMarker(id: string): MinimapMarker | undefined;
|
|
1529
|
+
/** Adds a marker, or replaces the one with the same id. */
|
|
1530
|
+
addMarker(marker: MinimapMarker): void;
|
|
1531
|
+
/** Changes some fields of a marker. Returns false (and fires nothing) if there is no such marker. */
|
|
1532
|
+
updateMarker(id: string, partial: Partial<Omit<MinimapMarker, "id">>): boolean;
|
|
1533
|
+
/** Returns false (and fires nothing) if there is no such marker. */
|
|
1534
|
+
removeMarker(id: string): boolean;
|
|
1535
|
+
clearMarkers(): void;
|
|
1536
|
+
/**
|
|
1537
|
+
* The area the next capture covers: the box of all the project's vector
|
|
1538
|
+
* layers (waits for them to be read as data), or `halfExtent` around the
|
|
1539
|
+
* origin without any.
|
|
1540
|
+
*/
|
|
1541
|
+
getExtent(): Promise<MinimapExtent>;
|
|
1542
|
+
/** Renders the GIS tiles into a fresh image. */
|
|
1543
|
+
captureMap(): Promise<MinimapCapture>;
|
|
1544
|
+
/** World point → normalized image coords (0..1, origin top-left), or null without a capture. */
|
|
1545
|
+
worldToMap(point: THREE.Vector3): {
|
|
1546
|
+
x: number;
|
|
1547
|
+
y: number;
|
|
1548
|
+
} | null;
|
|
1549
|
+
/** Normalized image coords → world point on the horizontal plane at `height`. */
|
|
1550
|
+
mapToWorld(x: number, y: number, height?: number): THREE.Vector3 | null;
|
|
1551
|
+
private _render;
|
|
1552
|
+
dispose(): void;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/** The file formats a vector layer can come in (the manifest entry's `type`). */
|
|
1556
|
+
export type VectorLayerFormat = "shapefile" | "geojson";
|
|
1557
|
+
/**
|
|
1558
|
+
* Which parser a downloaded layer file needs. The manifest's `type` decides
|
|
1559
|
+
* when it is recognised; otherwise the bytes do: a zip signature (`PK\x03\x04`)
|
|
1560
|
+
* means a shapefile, a first non-whitespace `{` means GeoJSON. Anything else
|
|
1561
|
+
* throws, with a message meant for the user.
|
|
1562
|
+
*/
|
|
1563
|
+
export declare function detectLayerFormat(type: string | undefined, bytes: Uint8Array): VectorLayerFormat;
|
|
1564
|
+
type CrsKind = {
|
|
1565
|
+
kind: "lonlat";
|
|
1566
|
+
} | {
|
|
1567
|
+
kind: "reproject";
|
|
1568
|
+
from: string;
|
|
1569
|
+
} | {
|
|
1570
|
+
kind: "unsupported";
|
|
1571
|
+
name: string;
|
|
1572
|
+
};
|
|
1573
|
+
/**
|
|
1574
|
+
* Reads the legacy GeoJSON `crs` member's name. Understands the CRS84 URN,
|
|
1575
|
+
* EPSG URNs (with or without a version), `EPSG:<code>` and the OpenGIS http
|
|
1576
|
+
* form. EPSG:4326 is taken as lon/lat as-is: GDAL/QGIS write lon/lat even when
|
|
1577
|
+
* they label it 4326. Reprojected: EPSG:3857 and the UTM families (326xx/327xx
|
|
1578
|
+
* WGS84, 258xx ETRS89, 269xx NAD83), their definition built from the code.
|
|
1579
|
+
*/
|
|
1580
|
+
export declare function parseCrsName(name: string): CrsKind;
|
|
1581
|
+
/**
|
|
1582
|
+
* Parses GeoJSON text into a `FeatureCollection` in WGS84 lon/lat (`z` kept).
|
|
1583
|
+
* The coordinate system comes from the legacy `crs` member; without one the
|
|
1584
|
+
* coordinates must already be lon/lat, which is checked. Throws a clear error
|
|
1585
|
+
* for an unsupported CRS, or projected coordinates that don't say which CRS
|
|
1586
|
+
* they are in. The legacy `crs` member is dropped from the result.
|
|
1587
|
+
*/
|
|
1588
|
+
export declare function parseGeoJSONLayer(text: string): Promise<GeoJSON.FeatureCollection>;
|
|
1589
|
+
|
|
1590
|
+
/**
|
|
1591
|
+
* Streams Cesium Ion 3D Tiles (Google Photorealistic 3D Tiles by default) co-located
|
|
1592
|
+
* with the BIM model.
|
|
1593
|
+
*
|
|
1594
|
+
* Rendering path depends on `world.renderer.postproduction.mode`:
|
|
1595
|
+
* - DEFERRED: the deferred G-buffer capture pass only draws objects whose material
|
|
1596
|
+
* carries the engine's "capture-emission" marker (set on Fragments/BIM surface
|
|
1597
|
+
* materials only) — every other object, however opaque, is hidden for the
|
|
1598
|
+
* duration of the capture (confirmed directly from the engine's own
|
|
1599
|
+
* `SurfaceCapturePass` docs). So tiles are added to a private off-scene
|
|
1600
|
+
* `THREE.Scene` and drawn via `addOcclusionPass` (direct mode — opaque,
|
|
1601
|
+
* self-occluding, same as `PointCloudLoader`), which runs AFTER the deferred
|
|
1602
|
+
* composite and bypasses the G-buffer requirement entirely.
|
|
1603
|
+
* - COMPOSER: the EffectComposer path renders the real scene graph directly, no
|
|
1604
|
+
* special material marker needed, so tiles are added straight to
|
|
1605
|
+
* `world.scene.three` like any other mesh.
|
|
1606
|
+
*
|
|
1607
|
+
* The Cesium Ion API token is kept in memory only (no persistence yet) — see
|
|
1608
|
+
* docs/adrs for the pending decision on where project geo-location should live.
|
|
1609
|
+
*/
|
|
1610
|
+
declare class _GISManager extends OBC.Component {
|
|
1611
|
+
static readonly uuid: "d8f3a1c2-6b4e-4f9a-8c1d-2e7b5a9f3c60";
|
|
1612
|
+
enabled: boolean;
|
|
1613
|
+
readonly onStateChanged: OBC.Event<GISManagerState>;
|
|
1614
|
+
readonly onLoadError: OBC.Event<string>;
|
|
1615
|
+
readonly onLayersLoaded: OBC.Event<GISVectorLayerConfig[]>;
|
|
1616
|
+
readonly onVectorLayerError: OBC.Event<{
|
|
1617
|
+
id: string;
|
|
1618
|
+
message: string;
|
|
1619
|
+
}>;
|
|
1620
|
+
readonly onOffsetDirtyChange: OBC.Event<boolean>;
|
|
1621
|
+
readonly onOffsetSaveComplete: OBC.Event<boolean>;
|
|
1622
|
+
world?: OBC.World;
|
|
1623
|
+
vectorLayersConfig: GISVectorLayerConfig[];
|
|
1624
|
+
private _client?;
|
|
1625
|
+
private _projectId?;
|
|
1626
|
+
private _appDataStore?;
|
|
1627
|
+
private _offsetDirty;
|
|
1628
|
+
get offsetDirty(): boolean;
|
|
1629
|
+
readonly vectorLayers: VectorLayers;
|
|
1630
|
+
/**
|
|
1631
|
+
* Fires when a click selects a vector feature (payload: its layer, index and
|
|
1632
|
+
* attribute row) and with null when the selection is cleared — re-exposed
|
|
1633
|
+
* from `vectorLayers` for consumers (the future properties panel).
|
|
1634
|
+
*/
|
|
1635
|
+
readonly onFeatureSelected: OBC.Event<VectorFeatureSelection | null>;
|
|
1636
|
+
/**
|
|
1637
|
+
* Fires on every `selectVectorFeature(..., { focus: true })`, including a
|
|
1638
|
+
* re-select of the already-selected feature (which fires no
|
|
1639
|
+
* `onFeatureSelected`) — carries the scene-space sphere the 3D camera frames,
|
|
1640
|
+
* so other views (the 2D map) can focus the same neighbourhood.
|
|
1641
|
+
*/
|
|
1642
|
+
readonly onFocusRequested: OBC.Event<{
|
|
1643
|
+
layerId: string;
|
|
1644
|
+
featureIndex: number;
|
|
1645
|
+
sphere: THREE.Sphere;
|
|
1646
|
+
}>;
|
|
1647
|
+
private _apiToken;
|
|
1648
|
+
private _assetId;
|
|
1649
|
+
private _latitude;
|
|
1650
|
+
private _longitude;
|
|
1651
|
+
private _height;
|
|
1652
|
+
private _rotation;
|
|
1653
|
+
private _tilesVisible;
|
|
1654
|
+
private _gizmoVisible;
|
|
1655
|
+
private _tilesOffsetEast;
|
|
1656
|
+
private _tilesOffsetNorth;
|
|
1657
|
+
private _tilesOffsetUp;
|
|
1658
|
+
private _tiles;
|
|
1659
|
+
private _reorientation;
|
|
1660
|
+
private _draco;
|
|
1661
|
+
private _occlusionScene;
|
|
1662
|
+
private _usingOcclusionPass;
|
|
1663
|
+
private _locationControls;
|
|
1664
|
+
private _controlPoint;
|
|
1665
|
+
private _dragStartMatrix;
|
|
1666
|
+
private _tilesOpacity;
|
|
1667
|
+
private _fadeTarget;
|
|
1668
|
+
private _fadeQuad;
|
|
1669
|
+
private _passRegistered;
|
|
1670
|
+
private _ensureOcclusionPass;
|
|
1671
|
+
private readonly _occlusion;
|
|
1672
|
+
private _ensureFadeTarget;
|
|
1673
|
+
private _disposeFade;
|
|
1674
|
+
/** Freezes the reference frame for the upcoming gesture — see `_onControlPointMoved`. */
|
|
1675
|
+
private readonly _onDragStart;
|
|
1676
|
+
/**
|
|
1677
|
+
* Fires on every pointer move during the drag (live relocation). Always
|
|
1678
|
+
* converts the TOTAL offset since the drag began (that's what
|
|
1679
|
+
* `_controlPoint.position` holds throughout) through the FROZEN
|
|
1680
|
+
* start-of-gesture matrix, never the live `tiles.group.matrixWorld` —
|
|
1681
|
+
* otherwise every event would re-apply the same growing total against a frame
|
|
1682
|
+
* already shifted by all earlier events, compounding into runaway motion.
|
|
1683
|
+
*
|
|
1684
|
+
* Immediately resets `_controlPoint.position` back to (0,0,0) afterwards, so
|
|
1685
|
+
* the gizmo reads as a joystick fixed at the origin — only the terrain visibly
|
|
1686
|
+
* moves. This is safe precisely because of the point above: TransformControls
|
|
1687
|
+
* computes each event's position from its own frozen drag-start reference plus
|
|
1688
|
+
* the raw mouse offset, never incrementally from the object's last value, so
|
|
1689
|
+
* it doesn't notice or care that we reset it out from under it.
|
|
1690
|
+
*/
|
|
1691
|
+
private readonly _onControlPointMoved;
|
|
1692
|
+
/** Gesture is over — nothing left to commit (every event already did), just clear the frozen reference. */
|
|
1693
|
+
private readonly _onDragEnd;
|
|
1694
|
+
private readonly _minimapSource;
|
|
1695
|
+
/** The 2D minimap (a baked top-down image of the GIS tiles over the project's vector layers) — see `Minimap`. */
|
|
1696
|
+
readonly minimap: Minimap;
|
|
1697
|
+
private readonly _onBeforeUpdate;
|
|
1698
|
+
private readonly _onOriginChange;
|
|
1699
|
+
private readonly _onCrsChange;
|
|
1700
|
+
private static readonly _CLICK_MAX_MOVE_PX;
|
|
1701
|
+
private static readonly _BIM_CLEAR_WINDOW_MS;
|
|
1702
|
+
private _pickDown?;
|
|
1703
|
+
private _clearBimUntil;
|
|
1704
|
+
private _bimClearHandler?;
|
|
1705
|
+
/**
|
|
1706
|
+
* Wires the click and hover handling on the viewer canvas. Window-level
|
|
1707
|
+
* CAPTURE listeners filtered to the canvas, so they run before the
|
|
1708
|
+
* Highlighter's own canvas listeners for the same event (it selects BIM
|
|
1709
|
+
* elements on mouse up). The canvas lives inside `top-viewer`'s shadow root,
|
|
1710
|
+
* where an event observed at `window` is retargeted to the shadow host — so
|
|
1711
|
+
* the filter compares `composedPath()[0]` (the real origin), never
|
|
1712
|
+
* `event.target`. The canvas itself is looked up per event rather than
|
|
1713
|
+
* cached: the world can resolve before its renderer exists.
|
|
1714
|
+
*
|
|
1715
|
+
* "Nearest wins" between a vector feature and a BIM element: on mouse DOWN,
|
|
1716
|
+
* only if the raycast hits a vector feature, the BIM element under the same
|
|
1717
|
+
* pixel is picked with `FastModelPickers` (`getItemAt` + `getPointAt`, the
|
|
1718
|
+
* same GPU pick the Highlighter selects with) and the two camera distances
|
|
1719
|
+
* are compared; the camera doesn't move during a click, so the answer is
|
|
1720
|
+
* ready by mouse up. On mouse up, if the vector feature won it is selected
|
|
1721
|
+
* and the BIM selection that same click makes is undone (the Highlighter
|
|
1722
|
+
* can't be told to skip a click — disabling it for one event leaves its
|
|
1723
|
+
* internal pointer state stuck — so its highlight is cleared the moment it
|
|
1724
|
+
* fires). Otherwise the vector selection is cleared and the Highlighter
|
|
1725
|
+
* proceeds as always. Gaussian-splat and Google 3D tiles are not considered.
|
|
1726
|
+
*/
|
|
1727
|
+
private _setupFeaturePicking;
|
|
1728
|
+
private readonly _onHighlighterSetup;
|
|
1729
|
+
private get _pickCanvas();
|
|
1730
|
+
/** True when the event really originated on the viewer canvas (see `_setupFeaturePicking`). */
|
|
1731
|
+
private _isOnCanvas;
|
|
1732
|
+
private _hoverPoint?;
|
|
1733
|
+
private _hoverFrame;
|
|
1734
|
+
private readonly _onHoverMove;
|
|
1735
|
+
private readonly _runHover;
|
|
1736
|
+
private readonly _clearHover;
|
|
1737
|
+
/** Vector layers loaded and the viewer in plain select mode (no modal tool). */
|
|
1738
|
+
private _canPick;
|
|
1739
|
+
private _ensureBimClearHandler;
|
|
1740
|
+
private _ndc;
|
|
1741
|
+
private _isPickable;
|
|
1742
|
+
private readonly _onPickDown;
|
|
1743
|
+
private readonly _onPickUp;
|
|
1744
|
+
/**
|
|
1745
|
+
* Horizontal scene-space box of ALL the project's vector layers (every
|
|
1746
|
+
* manifest entry, displayed or not), for the minimap extent — the only thing
|
|
1747
|
+
* that defines it. Layers are read as data through the shared per-layer
|
|
1748
|
+
* cache (so nothing downloads twice and nothing is added to the scene), then
|
|
1749
|
+
* projected with the CURRENT origin on every call, so an origin change never
|
|
1750
|
+
* leaves a stale box. A layer that fails to load is skipped with one warning.
|
|
1751
|
+
*/
|
|
1752
|
+
private _vectorLayersBox;
|
|
1753
|
+
constructor(components: OBC.Components);
|
|
1754
|
+
/**
|
|
1755
|
+
* Reads `{GISManager.uuid}.json` from project storage (same pattern as
|
|
1756
|
+
* `ClashesManager`/`TopicsManager`'s own `init(client)` — a plain method
|
|
1757
|
+
* call, not `@lit/context`).
|
|
1758
|
+
* Restores the origin (lat/lon/height/rotation) by composition from
|
|
1759
|
+
* `ProjectManager` — the platform's canonical project record — instead of
|
|
1760
|
+
* duplicating it into GIS's own persisted file, plus the vector-layer
|
|
1761
|
+
* manifest (metadata only — id/name/fileId/type/style, NOT the geometry
|
|
1762
|
+
* itself) from GIS's own `{GISManager.uuid}.json`. If that layers file
|
|
1763
|
+
* doesn't exist yet in the project, leaves the layer list empty and does
|
|
1764
|
+
* NOT create one — that only happens later, on an explicit save.
|
|
1765
|
+
*
|
|
1766
|
+
* Assumes `ProjectManager.init(client)` has already run by the time this is
|
|
1767
|
+
* called — the caller must chain `GISManager.init()` after
|
|
1768
|
+
* the `ProjectManager.init()` promise resolves. If `GISManager` can ever be
|
|
1769
|
+
* initialized in a different order/context, `ProjectManager.origin.get()`
|
|
1770
|
+
* below would just read stale/undefined data silently — revisit this if
|
|
1771
|
+
* that assumption stops holding (e.g. by awaiting `ProjectManager.init`
|
|
1772
|
+
* here directly, if `GISManager` can no longer rely on caller ordering).
|
|
1773
|
+
*
|
|
1774
|
+
* No-op (never throws) if there's no project context, no matching file, or
|
|
1775
|
+
* the read fails for any reason — the app should always be usable with
|
|
1776
|
+
* defaults, storage is a progressive enhancement here, not a hard
|
|
1777
|
+
* dependency.
|
|
1778
|
+
*/
|
|
1779
|
+
init(client: PlatformClient): Promise<void>;
|
|
1780
|
+
/** Whether loading a vector layer also frames the camera on it. Off for now. */
|
|
1781
|
+
fitCameraOnLayerLoad: boolean;
|
|
1782
|
+
/**
|
|
1783
|
+
* Downloads the given layer's shapefile `.zip` from project storage
|
|
1784
|
+
* (`vectorLayersConfig` entry's `fileId`), parses it with `shpjs.parseZip`
|
|
1785
|
+
* (dynamic import — same heavy-dependency convention as the rest of this
|
|
1786
|
+
* component), and renders it via `vectorLayers.addLayer(geojson, style,
|
|
1787
|
+
* id)`. The single entry point for "the user checked this layer's box" —
|
|
1788
|
+
* callers don't need to touch storage/shpjs/`VectorLayers` directly, and
|
|
1789
|
+
* don't need their own try/catch: failures are reported via
|
|
1790
|
+
* `onVectorLayerError`, mirroring `enableTiles()`/`onLoadError`.
|
|
1791
|
+
*/
|
|
1792
|
+
loadVectorLayer(id: string): Promise<void>;
|
|
1793
|
+
private readonly _layerGeoJSON;
|
|
1794
|
+
private _getLayerGeoJSON;
|
|
1795
|
+
/**
|
|
1796
|
+
* Surface nodes for a layer whose heights come from attributes: every
|
|
1797
|
+
* Point/MultiPoint coordinate with a finite Z in the layers named by
|
|
1798
|
+
* `height.nodeLayers`. Those layers are only read as data here — nothing is
|
|
1799
|
+
* added to the scene and the panel's checkboxes are untouched. An unknown id
|
|
1800
|
+
* is warned about and skipped.
|
|
1801
|
+
*/
|
|
1802
|
+
private _loadHeightNodes;
|
|
1803
|
+
private _downloadAndParseLayer;
|
|
1804
|
+
/**
|
|
1805
|
+
* Real-world vector data can easily sit tens of km from the origin (a whole
|
|
1806
|
+
* shapefile's extent, not just small hand-built test shapes) —
|
|
1807
|
+
* far outside the default camera framing (~87 units from origin). Frames
|
|
1808
|
+
* the camera on the layer's actual bounding box right after it loads, same
|
|
1809
|
+
* `CameraControls.fitToBox()` the point-cloud loader already uses for the
|
|
1810
|
+
* same reason, so newly loaded data doesn't just silently look like nothing
|
|
1811
|
+
* rendered.
|
|
1812
|
+
*/
|
|
1813
|
+
private _fitCameraToLayer;
|
|
1814
|
+
private static readonly _FOCUS_MIN_RADIUS;
|
|
1815
|
+
/**
|
|
1816
|
+
* The single entry point for selecting a vector feature — the 3D click and
|
|
1817
|
+
* the 2D map's click both come through here, so they share one selection
|
|
1818
|
+
* state, one `onFeatureSelected` event and one camera travel. With `focus`,
|
|
1819
|
+
* the camera flies to the feature: `CameraControls.fitToSphere()` (animated)
|
|
1820
|
+
* on the bounding sphere of what `VectorLayers` built for it, at its real
|
|
1821
|
+
* resolved heights; the viewing direction is kept, only the target and the
|
|
1822
|
+
* distance change. Selecting the already-selected feature fires no event but
|
|
1823
|
+
* still focuses. Clearing stays `vectorLayers.clearSelection()`.
|
|
1824
|
+
*/
|
|
1825
|
+
selectVectorFeature(layerId: string, featureIndex: number, options?: {
|
|
1826
|
+
focus?: boolean;
|
|
1827
|
+
}): void;
|
|
1828
|
+
private _focusVectorFeature;
|
|
1829
|
+
/** Removes a layer loaded via `loadVectorLayer` — the "user unchecked the box" counterpart. */
|
|
1830
|
+
unloadVectorLayer(id: string): void;
|
|
1831
|
+
private _firstWorld;
|
|
1832
|
+
get apiToken(): string;
|
|
1833
|
+
set apiToken(value: string);
|
|
1834
|
+
get assetId(): string;
|
|
1835
|
+
set assetId(value: string);
|
|
1836
|
+
get latitude(): number;
|
|
1837
|
+
get longitude(): number;
|
|
1838
|
+
get height(): number;
|
|
1839
|
+
get rotation(): number;
|
|
1840
|
+
get tilesVisible(): boolean;
|
|
1841
|
+
get gizmoVisible(): boolean;
|
|
1842
|
+
get tilesOffsetEast(): number;
|
|
1843
|
+
get tilesOffsetNorth(): number;
|
|
1844
|
+
get tilesOffsetUp(): number;
|
|
1845
|
+
get tilesOpacity(): number;
|
|
1846
|
+
/**
|
|
1847
|
+
* See-through factor for the tiles (0..1, 1 = opaque), mirrored by the
|
|
1848
|
+
* minimap's terrain image. In-memory only. Applies in DEFERRED mode (the
|
|
1849
|
+
* viewer's default); the composer path keeps the tiles opaque.
|
|
1850
|
+
*/
|
|
1851
|
+
setTilesOpacity(opacity: number): void;
|
|
1852
|
+
toggleGizmo(): void;
|
|
1853
|
+
/** Manual geo-location input (sliders) — no IFC georeferencing read yet, see docs/adrs. */
|
|
1854
|
+
setPosition(latitude: number, longitude: number, height?: number, rotation?: number): void;
|
|
1855
|
+
/**
|
|
1856
|
+
* Small local fine-tune offset (metres, East/North/Up) layered ON TOP of
|
|
1857
|
+
* the origin-derived tiles position — a manual calibration knob for Google
|
|
1858
|
+
* Photorealistic 3D Tiles' own (independently known) absolute-positioning
|
|
1859
|
+
* inaccuracy, NOT a change to `ProjectManager.origin`/real georeferencing.
|
|
1860
|
+
* Re-derives the tiles position from the CURRENT origin fields + this
|
|
1861
|
+
* offset on every call (via `_reorientation`), so repeated calls replace
|
|
1862
|
+
* rather than accumulate.
|
|
1863
|
+
*
|
|
1864
|
+
* NOT auto-saved — marks `offsetDirty`/fires `onOffsetDirtyChange` instead,
|
|
1865
|
+
* same pattern `ProjectManager.origin` uses, so a caller driving this
|
|
1866
|
+
* continuously (the fine-tune sliders) doesn't write to the network on
|
|
1867
|
+
* every tick. `saveTilesOffset()` persists explicitly.
|
|
1868
|
+
*/
|
|
1869
|
+
setTilesOffset(east: number, north: number, up: number): void;
|
|
1870
|
+
/**
|
|
1871
|
+
* Persists the current tiles fine-tune offset to `{GISManager.uuid}.json`
|
|
1872
|
+
* (loose in `__project_data`, alongside `ProjectManager`'s own file — see
|
|
1873
|
+
* `_appDataStore`), preserving whatever `layers` are already in there.
|
|
1874
|
+
* `save()` itself is fire-and-forget/debounced — `offsetDirty` only clears
|
|
1875
|
+
* (and `onOffsetSaveComplete` fires) once `_appDataStore.onSaveComplete`
|
|
1876
|
+
* confirms the write actually succeeded (wired in `init()`), not
|
|
1877
|
+
* optimistically here.
|
|
1878
|
+
*/
|
|
1879
|
+
saveTilesOffset(): void;
|
|
1880
|
+
private _applyTilesOffset;
|
|
1881
|
+
private _reprojectDebounce?;
|
|
1882
|
+
private _scheduleReprojectLayers;
|
|
1883
|
+
/**
|
|
1884
|
+
* Casts a ray straight down from high above the recentered origin against the
|
|
1885
|
+
* currently loaded tile geometry to find the real ground elevation at
|
|
1886
|
+
* (latitude, longitude), then re-applies the reorientation using that as
|
|
1887
|
+
* `height` so the visible terrain/rooftop lands at world (0,0,0) instead of
|
|
1888
|
+
* wherever `height=0` (the bare WGS84 ellipsoid, which can be hundreds of
|
|
1889
|
+
* meters away from the real surface at elevated locations) happened to put it.
|
|
1890
|
+
*
|
|
1891
|
+
* Relies on the ENU tangent-plane guarantee `transformLatLonHeightToOrigin`
|
|
1892
|
+
* gives at the origin: after recentering at some height, "up" from that lat/lon
|
|
1893
|
+
* is exactly world +Y, and world Y directly corresponds to metres of height — so
|
|
1894
|
+
* a hit at world Y = `dy` means the real surface is `dy` metres higher than the
|
|
1895
|
+
* height currently used, and re-running the transform with
|
|
1896
|
+
* `height + dy` should bring it to Y ≈ 0. Only accurate near the origin (a few
|
|
1897
|
+
* km), which is exactly the regime this is used in.
|
|
1898
|
+
*
|
|
1899
|
+
* Tries straight down first, then straight up (content can end up above OR below
|
|
1900
|
+
* the origin depending on how far off `height=0` was from the real surface).
|
|
1901
|
+
*
|
|
1902
|
+
* Uses `TilesRenderer.raycast()` — 3d-tiles-renderer's own documented raycasting
|
|
1903
|
+
* entry point — instead of a generic `THREE.Raycaster.intersectObject(tiles.group)`.
|
|
1904
|
+
* The library keeps its own tile-activity bookkeeping (`activeTiles`,
|
|
1905
|
+
* `tile.traversal.used`, per-tile bounding volumes) separate from the plain
|
|
1906
|
+
* Object3D tree, and only that entry point knows how to walk it; a generic
|
|
1907
|
+
* scene-graph raycast against `tiles.group` silently finds nothing.
|
|
1908
|
+
*
|
|
1909
|
+
* Tile traversal (`markUsedTiles` in the engine's own source) bails out
|
|
1910
|
+
* immediately — without even descending into children — for any tile whose
|
|
1911
|
+
* bounding volume isn't in the frustum of AT LEAST ONE registered camera:
|
|
1912
|
+
* `if (!tile.traversal.inFrustum) return;`. That branch is never visited, let
|
|
1913
|
+
* alone loaded or marked active, regardless of `displayActiveTiles` (which only
|
|
1914
|
+
* affects the final visibility toggle of tiles that already got marked active —
|
|
1915
|
+
* a narrower case). So if the real viewport camera isn't looking anywhere near
|
|
1916
|
+
* the target point, nothing here ever loads and the raycast finds nothing no
|
|
1917
|
+
* matter what.
|
|
1918
|
+
*
|
|
1919
|
+
* `TilesRenderer` supports multiple simultaneously-registered cameras
|
|
1920
|
+
* (`cameras: Camera[]`), and a tile counts as "in frustum" if ANY of them sees
|
|
1921
|
+
* it. So we register a throwaway camera positioned high above the origin,
|
|
1922
|
+
* looking straight down — wide FOV and far plane so its frustum (a cone
|
|
1923
|
+
* widening downward, centred on the target column) covers a generous height
|
|
1924
|
+
* range around the origin regardless of whether the real surface turns out to
|
|
1925
|
+
* be above or below it — force one `update()` traversal with it registered,
|
|
1926
|
+
* then remove it again. The real viewport camera's own registration and
|
|
1927
|
+
* behaviour are untouched throughout.
|
|
1928
|
+
*
|
|
1929
|
+
* Returns false if no tile geometry was found (nothing to snap to) — caller
|
|
1930
|
+
* should retry once more of the tileset streams in. The first call after
|
|
1931
|
+
* moving to a new area may land approximately rather than exactly, since the
|
|
1932
|
+
* newly-forced-into-frustum content is still streaming in asynchronously in
|
|
1933
|
+
* the background — calling it again shortly after tends to refine it further.
|
|
1934
|
+
*/
|
|
1935
|
+
snapToGround(): boolean;
|
|
1936
|
+
/**
|
|
1937
|
+
* `CesiumIonAuth.fetch()`/`refreshToken()` (see `3d-tiles-renderer/core/plugins/auth/CesiumIonAuth.js`)
|
|
1938
|
+
* only ever throws `"CesiumIonAuthPlugin: Failed to load data with error code {status}"`
|
|
1939
|
+
* on a non-OK HTTP response — no further detail. Map the common statuses to
|
|
1940
|
+
* something a user can actually act on; fall back to the raw message otherwise.
|
|
1941
|
+
*/
|
|
1942
|
+
private _toFriendlyLoadError;
|
|
1943
|
+
/**
|
|
1944
|
+
* Sets everything up and resolves only once the FIRST real outcome is known —
|
|
1945
|
+
* either actual content loaded ("load-tileset") or the initial fetch (auth
|
|
1946
|
+
* against the token/asset) failed ("load-error") — instead of resolving the
|
|
1947
|
+
* instant the TilesRenderer is constructed, before any network activity has
|
|
1948
|
+
* even started. `tilesVisible`/state only flips to enabled on success; on
|
|
1949
|
+
* failure everything set up here is torn down again and the promise rejects,
|
|
1950
|
+
* so the caller (the panel) never has to un-render a half-enabled state.
|
|
1951
|
+
*/
|
|
1952
|
+
enableTiles(): Promise<void>;
|
|
1953
|
+
disableTiles(): void;
|
|
1954
|
+
/** Tears down everything `enableTiles()` set up, without touching `_tilesVisible`. */
|
|
1955
|
+
private _teardownTiles;
|
|
1956
|
+
dispose(): void;
|
|
1957
|
+
private _trigger;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
/**
|
|
1961
|
+
* Streams Cesium Ion 3D Tiles (Google Photorealistic 3D Tiles by default) co-located
|
|
1962
|
+
* with the BIM model.
|
|
1963
|
+
*
|
|
1964
|
+
* Rendering path depends on `world.renderer.postproduction.mode`:
|
|
1965
|
+
* - DEFERRED: the deferred G-buffer capture pass only draws objects whose material
|
|
1966
|
+
* carries the engine's "capture-emission" marker (set on Fragments/BIM surface
|
|
1967
|
+
* materials only) — every other object, however opaque, is hidden for the
|
|
1968
|
+
* duration of the capture (confirmed directly from the engine's own
|
|
1969
|
+
* `SurfaceCapturePass` docs). So tiles are added to a private off-scene
|
|
1970
|
+
* `THREE.Scene` and drawn via `addOcclusionPass` (direct mode — opaque,
|
|
1971
|
+
* self-occluding, same as `PointCloudLoader`), which runs AFTER the deferred
|
|
1972
|
+
* composite and bypasses the G-buffer requirement entirely.
|
|
1973
|
+
* - COMPOSER: the EffectComposer path renders the real scene graph directly, no
|
|
1974
|
+
* special material marker needed, so tiles are added straight to
|
|
1975
|
+
* `world.scene.three` like any other mesh.
|
|
1976
|
+
*
|
|
1977
|
+
* The Cesium Ion API token is kept in memory only (no persistence yet) — see
|
|
1978
|
+
* docs/adrs for the pending decision on where project geo-location should live.
|
|
1979
|
+
*/
|
|
1980
|
+
export type GISManager = InstanceType<typeof _GISManager>;
|
|
1981
|
+
/**
|
|
1982
|
+
* Streams Cesium Ion 3D Tiles (Google Photorealistic 3D Tiles by default) co-located
|
|
1983
|
+
* with the BIM model.
|
|
1984
|
+
*
|
|
1985
|
+
* Rendering path depends on `world.renderer.postproduction.mode`:
|
|
1986
|
+
* - DEFERRED: the deferred G-buffer capture pass only draws objects whose material
|
|
1987
|
+
* carries the engine's "capture-emission" marker (set on Fragments/BIM surface
|
|
1988
|
+
* materials only) — every other object, however opaque, is hidden for the
|
|
1989
|
+
* duration of the capture (confirmed directly from the engine's own
|
|
1990
|
+
* `SurfaceCapturePass` docs). So tiles are added to a private off-scene
|
|
1991
|
+
* `THREE.Scene` and drawn via `addOcclusionPass` (direct mode — opaque,
|
|
1992
|
+
* self-occluding, same as `PointCloudLoader`), which runs AFTER the deferred
|
|
1993
|
+
* composite and bypasses the G-buffer requirement entirely.
|
|
1994
|
+
* - COMPOSER: the EffectComposer path renders the real scene graph directly, no
|
|
1995
|
+
* special material marker needed, so tiles are added straight to
|
|
1996
|
+
* `world.scene.three` like any other mesh.
|
|
1997
|
+
*
|
|
1998
|
+
* The Cesium Ion API token is kept in memory only (no persistence yet) — see
|
|
1999
|
+
* docs/adrs for the pending decision on where project geo-location should live.
|
|
2000
|
+
*/
|
|
2001
|
+
export const GISManager = { uuid: 'd8f3a1c2-6b4e-4f9a-8c1d-2e7b5a9f3c60' } as typeof _GISManager & { uuid: 'd8f3a1c2-6b4e-4f9a-8c1d-2e7b5a9f3c60' };
|
|
2002
|
+
|
|
952
2003
|
/** One element mutation inside a revit-flow commit. */
|
|
953
2004
|
export interface RevitFlowChange {
|
|
954
2005
|
/** create → element added, update → geometry/data changed, delete → removed. */
|