@nika-js/onlymap 0.5.12 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/LICENSE.md +2 -0
- package/README.md +19 -7
- package/THIRD-PARTY-LICENSES.md +27 -0
- package/dist/{LercDecode.es-C97NsRLr.js → LercDecode.es-CJnypw8j.js} +1 -1
- package/dist/{basemap-kc0aQNqr.js → basemap-DrQ0-eyR.js} +1 -1
- package/dist/cityjson-D_V5GY8b.js +332 -0
- package/dist/crs-Ciu7Xs7a.js +108 -0
- package/dist/crs.d.ts +50 -0
- package/dist/ctx.d.ts +8 -1
- package/dist/elements/om-map.d.ts +26 -2
- package/dist/elements/om-overlay.d.ts +1 -1
- package/dist/elements/om-widget.d.ts +4 -0
- package/dist/feature-colors.d.ts +41 -0
- package/dist/{geoparquet-Dn1BypCS.js → geoparquet-Dix4lTNy.js} +1 -1
- package/dist/html-data.d.ts +1 -1
- package/dist/ifc-Dc-TdSJr.js +877 -0
- package/dist/ifc.d.ts +406 -0
- package/dist/{index-aBiHCzy-.js → index-1UgNlfGR.js} +20944 -18922
- package/dist/{index-BoUIm-jd.js → index-BQMjW5w0.js} +1 -1
- package/dist/{index-BcG3m2rF.js → index-B_1PPJgC.js} +1 -1
- package/dist/{index-DA_aop8u.js → index-CAuT5j9Y.js} +2 -2
- package/dist/{index-Dc-ZLLo8.js → index-CXPaeisL.js} +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/layer-registry.d.ts +14 -0
- package/dist/layers/bim-layer.d.ts +104 -0
- package/dist/layers/feature-mesh-layer.d.ts +288 -0
- package/dist/layers/gltf-scene-walk.d.ts +55 -0
- package/dist/{lerc-74-SNPeU.js → lerc-CbTjQ7uI.js} +2 -2
- package/dist/license.d.ts +29 -5
- package/dist/meshopt_simplifier-Co6uRDDA.js +370 -0
- package/dist/onlymap.standalone.js +58057 -54751
- package/dist/onlymapjs.js +69 -67
- package/dist/programmatic.d.ts +22 -2
- package/dist/{raster-VyHCbPXy.js → raster-0b0nSHUh.js} +2 -2
- package/dist/{raster-pipeline-CGQTOUjw.js → raster-pipeline-D8siq7-4.js} +1 -1
- package/dist/react/om-overlay.d.ts +9 -0
- package/dist/react.js +144 -144
- package/dist/runtime-core.d.ts +110 -3
- package/dist/selection.d.ts +33 -0
- package/dist/site-placement.d.ts +85 -0
- package/dist/testing.d.ts +6 -2
- package/dist/tile3d-metadata.d.ts +185 -0
- package/dist/version.d.ts +1 -1
- package/dist/widget-registry.d.ts +11 -0
- package/dist/widgets/ifc-clash.d.ts +18 -0
- package/dist/widgets/ifc.d.ts +41 -0
- package/dist/{zarr-DddHNVim.js → zarr-DCYro_Vs.js} +79 -66
- package/docs/3d-assets.md +3 -3
- package/docs/live-data.md +2 -2
- package/docs/stories.md +1 -1
- package/docs/testing.md +2 -2
- package/llms.txt +3 -3
- package/onlymapjs.html-data.json +152 -0
- package/package.json +7 -2
- package/skills/onlymapjs/SKILL.md +4 -2
- package/skills/onlymapjs/references/react.md +1 -1
- package/skills/onlymapjs/references/syntax.md +299 -12
- package/dist/cityjson-urQeujQv.js +0 -407
package/dist/ifc.d.ts
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-browser IFC loader — parses an `.ifc` with web-ifc (WASM) and produces,
|
|
3
|
+
* IN MEMORY, exactly the artifacts the offline converter writes to disk: a
|
|
4
|
+
* glTF binary carrying per-element feature IDs, a 3D Tiles tileset pointing at
|
|
5
|
+
* it, a property table, and a crease/boundary outline overlay.
|
|
6
|
+
*
|
|
7
|
+
* A LAZY chunk, on the same terms as `cityjson.ts`: nothing here is reachable
|
|
8
|
+
* from the main bundle except through `loadIfc`'s dynamic import in
|
|
9
|
+
* `index.ts`, and web-ifc's ~2 MB WASM is fetched only when a page actually
|
|
10
|
+
* loads an IFC.
|
|
11
|
+
*
|
|
12
|
+
* This is a LOADER, not a converter — nothing is written out. Everything comes
|
|
13
|
+
* back as blob URLs, so `pick-features`, `feature-styles`, the declarative
|
|
14
|
+
* isolate/hide/ghost attributes and a PathLayer outline overlay all work
|
|
15
|
+
* unchanged: they cannot tell an uploaded model from a prepared tileset.
|
|
16
|
+
*
|
|
17
|
+
* Why blob URLs rather than feeding geometry to a layer directly: the whole
|
|
18
|
+
* 3D-Tiles path — tile traversal, `FeatureMeshLayer`'s per-primitive fan-out,
|
|
19
|
+
* picking, the style textures — already consumes a tileset URL. Handing it one
|
|
20
|
+
* reuses every bit of that instead of growing a second rendering path.
|
|
21
|
+
*
|
|
22
|
+
* web-ifc is MPL-2.0 and is NOT a package dependency. It is fetched from a CDN
|
|
23
|
+
* by default; `configureIfc({wasmPath})` points it at a self-hosted copy.
|
|
24
|
+
*/
|
|
25
|
+
/** Point the loader at a self-hosted web-ifc build instead of the default CDN. */
|
|
26
|
+
export declare function configureIfc(options: {
|
|
27
|
+
wasmPath?: string;
|
|
28
|
+
}): void;
|
|
29
|
+
/** One row of the property table — one IFC element. */
|
|
30
|
+
export interface IfcFeature {
|
|
31
|
+
ifcClass: string;
|
|
32
|
+
name: string;
|
|
33
|
+
material: string;
|
|
34
|
+
container: string;
|
|
35
|
+
/**
|
|
36
|
+
* The element's full spatial ancestry, outermost first, joined by
|
|
37
|
+
* `SPATIAL_SEPARATOR` — e.g. `Default site / Building / 00 groundfloor`.
|
|
38
|
+
*
|
|
39
|
+
* `container` is only the element's IMMEDIATE spatial parent, which is a
|
|
40
|
+
* flat list, not a hierarchy. This is what a model tree navigates. Empty
|
|
41
|
+
* when the model has no spatial decomposition to walk.
|
|
42
|
+
*/
|
|
43
|
+
spatialPath: string;
|
|
44
|
+
/** `IfcDoorType > Single-Flush 0915x2134` — the element's type object. */
|
|
45
|
+
typePath: string;
|
|
46
|
+
/** The system, zone or group the element is assigned to, including parent systems. */
|
|
47
|
+
systemPath: string;
|
|
48
|
+
/** `CCS > Ss > Ss_25_10_30` — the classification reference chain, walked via `ReferencedSource`. */
|
|
49
|
+
classificationPath: string;
|
|
50
|
+
netVolume: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Separator inside `spatialPath`. U+001F (UNIT SEPARATOR) exists for exactly
|
|
54
|
+
* this and cannot occur in an IFC label, so splitting is unambiguous — a
|
|
55
|
+
* readable delimiter like " / " would break on any storey actually named
|
|
56
|
+
* "Level 1 / Mezzanine".
|
|
57
|
+
*/
|
|
58
|
+
export declare const SPATIAL_SEPARATOR = "\u001F";
|
|
59
|
+
export interface IfcModel {
|
|
60
|
+
/** Blob URL of a 3D Tiles tileset — assign to a `Tile3DLayer`'s `tileset`. */
|
|
61
|
+
tilesetUrl: string;
|
|
62
|
+
/** Blob URL of PathLayer rows tracing crease/boundary edges, or null when disabled. */
|
|
63
|
+
edgesUrl: string | null;
|
|
64
|
+
/** The property table, indexed BY feature ID — the same rows picking resolves. */
|
|
65
|
+
features: IfcFeature[];
|
|
66
|
+
/**
|
|
67
|
+
* The `loadOptions` a `Tile3DLayer` needs for this model — assign alongside
|
|
68
|
+
* `tilesetUrl`.
|
|
69
|
+
*
|
|
70
|
+
* `isTileset: true` is REQUIRED, not a tuning knob. Tiles3DLoader decides
|
|
71
|
+
* tileset-vs-tile with `context.url.indexOf('.json') !== -1`, and
|
|
72
|
+
* `Response.url` strips the fragment, so a blob URL never satisfies it and
|
|
73
|
+
* the tileset JSON gets parsed as a binary tile
|
|
74
|
+
* ("3DTileLoader: unknown type {\"as"). Forcing it is safe: loaders.gl
|
|
75
|
+
* overrides the flag per tile from the tile's own type
|
|
76
|
+
* (`isTileset: this.type === 'json'`), so tile CONTENT still parses normally.
|
|
77
|
+
*/
|
|
78
|
+
loadOptions: Record<string, unknown>;
|
|
79
|
+
/**
|
|
80
|
+
* Where the model says it is: its own IfcSite position when it declares one.
|
|
81
|
+
*
|
|
82
|
+
* Feed this to the layer's `site-origin` rather than baking it in — see
|
|
83
|
+
* `readGeoreference` for why a declared position is so often an authoring
|
|
84
|
+
* tool's default rather than a survey.
|
|
85
|
+
*/
|
|
86
|
+
lonLat: [number, number];
|
|
87
|
+
/** True when `lonLat` came from the model rather than the fallback. */
|
|
88
|
+
georeferenced: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* The file's declared `IfcMapConversion.OrthogonalHeight`, RAW — undefined
|
|
91
|
+
* when the file never states one (the common case; distinct from a
|
|
92
|
+
* declared 0). No geoid/vertical-datum conversion is applied: absolute
|
|
93
|
+
* elevation only means something once the layer sits on a basemap that
|
|
94
|
+
* also has real elevation (`terrain`), and no correction turns a flat
|
|
95
|
+
* Z=0 basemap into one — feed this to `site-origin`'s elevation component
|
|
96
|
+
* only when terrain is active.
|
|
97
|
+
*/
|
|
98
|
+
orthogonalHeight?: number;
|
|
99
|
+
/** Bearing for the layer's `site-heading`, in degrees clockwise from true north. */
|
|
100
|
+
heading: number;
|
|
101
|
+
/** Multiplier for the layer's `site-scale`; 1 unless IfcMapConversion says otherwise. */
|
|
102
|
+
scale: number;
|
|
103
|
+
/** Whether `heading` was read from the file or merely assumed — never conflate the two. */
|
|
104
|
+
headingSource: HeadingSource;
|
|
105
|
+
/** Which of the file's georeferencing routes supplied `lonLat` — a survey-grade transform, a declared site, or neither. */
|
|
106
|
+
originSource: OriginSource;
|
|
107
|
+
stats: {
|
|
108
|
+
elements: number;
|
|
109
|
+
primitives: number;
|
|
110
|
+
polylines: number;
|
|
111
|
+
bytes: number;
|
|
112
|
+
};
|
|
113
|
+
/** Milliseconds per phase — for finding where a slow load actually goes. */
|
|
114
|
+
timings: Record<string, number>;
|
|
115
|
+
/** Tile-space bounds (Z-up metres) and the offset applied — for diagnosing placement. */
|
|
116
|
+
bounds: {
|
|
117
|
+
lo: [number, number, number];
|
|
118
|
+
hi: [number, number, number];
|
|
119
|
+
centre: [number, number, number];
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* The model-space origin subtracted from every vertex, in the FILE's own
|
|
123
|
+
* coordinates. Feed it back as `LoadIfcOptions.origin` when loading another
|
|
124
|
+
* discipline model of the same building so the two share a frame.
|
|
125
|
+
*/
|
|
126
|
+
origin: [number, number, number];
|
|
127
|
+
/** Frees the blob URLs. Call when swapping models — they are not garbage collected. */
|
|
128
|
+
revoke(): void;
|
|
129
|
+
}
|
|
130
|
+
export interface LoadIfcOptions {
|
|
131
|
+
onProgress?(message: string): void;
|
|
132
|
+
/** Used when the model declares no position of its own. */
|
|
133
|
+
fallbackLonLat?: [number, number];
|
|
134
|
+
/** Trace crease/boundary outlines (default true). Expensive on very large models. */
|
|
135
|
+
edges?: boolean;
|
|
136
|
+
/** Minimum angle between adjacent faces for a crease edge, in degrees. */
|
|
137
|
+
creaseDegrees?: number;
|
|
138
|
+
/**
|
|
139
|
+
* Shared model-space origin, in the file's own coordinates (tile axes:
|
|
140
|
+
* east, north, up). Pass the `origin` a previous `loadIfc` returned to keep
|
|
141
|
+
* two discipline models CO-REGISTERED — without it each model is centred on
|
|
142
|
+
* its own bounding box and they drift apart by the difference, which is
|
|
143
|
+
* precisely what makes a clash pass return nothing.
|
|
144
|
+
*/
|
|
145
|
+
origin?: [number, number, number];
|
|
146
|
+
}
|
|
147
|
+
/** Minimal shape of the bits of web-ifc's API this module touches. */
|
|
148
|
+
interface IfcApiLike {
|
|
149
|
+
SetWasmPath(path: string, absolute?: boolean): void;
|
|
150
|
+
Init(): Promise<void>;
|
|
151
|
+
OpenModel(data: Uint8Array, settings?: Record<string, unknown>): number;
|
|
152
|
+
CloseModel(modelID: number): void;
|
|
153
|
+
GetLine(modelID: number, expressID: number, flatten?: boolean): Record<string, unknown>;
|
|
154
|
+
GetLineType(modelID: number, expressID: number): number;
|
|
155
|
+
GetNameFromTypeCode(type: number): string;
|
|
156
|
+
GetLineIDsWithType(modelID: number, type: number): {
|
|
157
|
+
size(): number;
|
|
158
|
+
get(i: number): number;
|
|
159
|
+
};
|
|
160
|
+
LoadAllGeometry(modelID: number): {
|
|
161
|
+
size(): number;
|
|
162
|
+
get(i: number): StreamedMesh;
|
|
163
|
+
};
|
|
164
|
+
GetGeometry(modelID: number, id: number): IfcGeometryLike;
|
|
165
|
+
GetVertexArray(ptr: number, size: number): Float32Array;
|
|
166
|
+
GetIndexArray(ptr: number, size: number): Uint32Array;
|
|
167
|
+
}
|
|
168
|
+
interface StreamedMesh {
|
|
169
|
+
expressID: number;
|
|
170
|
+
geometries: {
|
|
171
|
+
size(): number;
|
|
172
|
+
get(i: number): {
|
|
173
|
+
geometryExpressID: number;
|
|
174
|
+
color: {
|
|
175
|
+
x: number;
|
|
176
|
+
y: number;
|
|
177
|
+
z: number;
|
|
178
|
+
w: number;
|
|
179
|
+
};
|
|
180
|
+
flatTransformation: ArrayLike<number>;
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
interface IfcGeometryLike {
|
|
185
|
+
GetVertexData(): number;
|
|
186
|
+
GetVertexDataSize(): number;
|
|
187
|
+
GetIndexData(): number;
|
|
188
|
+
GetIndexDataSize(): number;
|
|
189
|
+
delete(): void;
|
|
190
|
+
}
|
|
191
|
+
/** The pinned hash a URL must satisfy, or null when it is not the default CDN's (self-hosted → deliberately unverified, see `PINNED_SHA256`). */
|
|
192
|
+
export declare function pinnedWebIfcSha256(url: string): string | null;
|
|
193
|
+
/**
|
|
194
|
+
* `IfcCompoundPlaneAngleMeasure` -> decimal degrees.
|
|
195
|
+
*
|
|
196
|
+
* IFC2x3 predates `IfcMapConversion`, so a site's position lives on
|
|
197
|
+
* `IfcSite.RefLatitude`/`RefLongitude` as (degrees, minutes, seconds,
|
|
198
|
+
* millionths-of-a-second). The sign rides on the leading non-zero component
|
|
199
|
+
* and applies to the whole measure — reading only the degrees puts a western
|
|
200
|
+
* longitude in the wrong place by several arc-minutes.
|
|
201
|
+
*/
|
|
202
|
+
export declare function compoundAngleToDegrees(parts: unknown): number | null;
|
|
203
|
+
export type HeadingSource = "map-conversion" | "true-north" | "assumed";
|
|
204
|
+
/**
|
|
205
|
+
* The affine an `IfcMapConversion` (or its IFC2x3 / 4.3 equivalents) defines,
|
|
206
|
+
* kept so it can be applied to a REAL MODEL POINT rather than collapsed into a
|
|
207
|
+
* single coordinate.
|
|
208
|
+
*
|
|
209
|
+
* This is the difference between georeferencing a model and merely putting it
|
|
210
|
+
* near the right place. The conversion anchors the model's ORIGIN — (0,0,0) in
|
|
211
|
+
* its own coordinates — but a tileset is recentred on the geometry, and those
|
|
212
|
+
* are not the same point. MiniBIM-3.1's origin sits 32.7 m from its own
|
|
213
|
+
* centre, so un-projecting the eastings alone and dropping the model there
|
|
214
|
+
* lands it 32.7 m out with a perfectly plausible-looking result.
|
|
215
|
+
*/
|
|
216
|
+
export interface MapConversionAffine {
|
|
217
|
+
/** EPSG code of the target projected CRS, or null when it could not be read. */
|
|
218
|
+
code: number | null;
|
|
219
|
+
eastings: number;
|
|
220
|
+
northings: number;
|
|
221
|
+
orthogonalHeight: number;
|
|
222
|
+
/** Model +X in the map grid, as (cos, sin). */
|
|
223
|
+
xAxisAbscissa: number;
|
|
224
|
+
xAxisOrdinate: number;
|
|
225
|
+
scale: number;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Model coordinates -> projected map coordinates.
|
|
229
|
+
*
|
|
230
|
+
* The spec's affine, applied in full: rotate by the grid axis, scale, then
|
|
231
|
+
* offset by the eastings/northings. Straight out of IfcMapConversion's own
|
|
232
|
+
* definition rather than an approximation of it.
|
|
233
|
+
*/
|
|
234
|
+
export declare function mapConversionApply(a: MapConversionAffine, x: number, y: number): [number, number];
|
|
235
|
+
/**
|
|
236
|
+
* Assembles the affine a parsed `IfcMapConversion` line defines, from its
|
|
237
|
+
* already-unwrapped numeric attributes. Pulled out from `readGeoreference` so
|
|
238
|
+
* the one rule that matters is independently testable without booting web-ifc.
|
|
239
|
+
*
|
|
240
|
+
* `scale` is fixed at 1 — deliberately NOT the file's own `Scale` attribute.
|
|
241
|
+
* Per buildingSMART's definition, `IfcMapConversion.Scale` exists to convert
|
|
242
|
+
* a file's OWN declared length unit into the target CRS's when they differ
|
|
243
|
+
* ("used when the length unit... [is] not identical with the length unit
|
|
244
|
+
* established for this project... e.g. to convert feet into metres") — but
|
|
245
|
+
* web-ifc already returns every vertex normalised to metres regardless of
|
|
246
|
+
* what the file declares, so that conversion has already happened before any
|
|
247
|
+
* coordinate reaches this function. Taking the file's Scale at face value
|
|
248
|
+
* here — or worse, as a render-time `site-scale` — would apply it a second
|
|
249
|
+
* time: `geographic-referencing-utm.ifc` declares millimetres with
|
|
250
|
+
* Scale=1000 (mm -> m), and doing so rendered the whole model 1000x too
|
|
251
|
+
* large, a building-sized object turned into a kilometre-wide monument.
|
|
252
|
+
*/
|
|
253
|
+
export declare function buildMapConversionAffine(params: {
|
|
254
|
+
epsg: number | null;
|
|
255
|
+
eastings: number;
|
|
256
|
+
northings: number;
|
|
257
|
+
orthogonalHeight: number;
|
|
258
|
+
xAxisAbscissa: number;
|
|
259
|
+
xAxisOrdinate: number;
|
|
260
|
+
}): MapConversionAffine;
|
|
261
|
+
/**
|
|
262
|
+
* `OrthogonalHeight` corrected for the tileset's own recentring — the
|
|
263
|
+
* vertical counterpart of un-projecting `mapConversionApply` AT `centre`
|
|
264
|
+
* rather than at the file's raw origin (see that comment at the call site
|
|
265
|
+
* below). The tileset's baked origin sits at the model's LOWEST vertex (or,
|
|
266
|
+
* federated, whatever point the first model shared), not at the file's own
|
|
267
|
+
* local Z=0 — which is what `OrthogonalHeight` is declared relative to.
|
|
268
|
+
* `recentredLocalHeight` is `centre[2]`: the model's ORIGINAL, pre-recentre
|
|
269
|
+
* local height at the point that becomes the tile origin. The vertex now
|
|
270
|
+
* AT that origin was at that local height before recentring, so its
|
|
271
|
+
* absolute elevation is `orthogonalHeight + recentredLocalHeight`, not
|
|
272
|
+
* `orthogonalHeight` alone — exactly as the 32.7 m horizontal precedent
|
|
273
|
+
* this mirrors (see `applying an IfcMapConversion` tests below) needed
|
|
274
|
+
* `centre[0]`/`centre[1]` folded into the un-projection, not the model's
|
|
275
|
+
* raw origin.
|
|
276
|
+
*/
|
|
277
|
+
export declare function correctOrthogonalHeightForRecentring(orthogonalHeight: number, recentredLocalHeight: number): number;
|
|
278
|
+
/** Where `lonLat` came from — a survey-grade transform, a declared site, or nothing at all. */
|
|
279
|
+
export type OriginSource = "map-conversion" | "ifc-site" | "fallback";
|
|
280
|
+
/**
|
|
281
|
+
* UTM easting/northing -> lon/lat on WGS84 (the standard inverse series).
|
|
282
|
+
*
|
|
283
|
+
* Needed because `IfcMapConversion` states position in a PROJECTED CRS, so a
|
|
284
|
+
* properly georeferenced export — the case that actually matters on real
|
|
285
|
+
* projects — cannot be placed without un-projecting it. UTM covers the
|
|
286
|
+
* overwhelming majority of what `IfcProjectedCRS` names in practice
|
|
287
|
+
* (EPSG:326xx north / 327xx south); anything else is declined rather than
|
|
288
|
+
* approximated, because a wrong projection lands the model in a different
|
|
289
|
+
* country while looking perfectly plausible.
|
|
290
|
+
*
|
|
291
|
+
* Verified against a known control point (Sydney Opera House, zone 56S) to
|
|
292
|
+
* within 0.1 m.
|
|
293
|
+
*/
|
|
294
|
+
export declare function utmToLonLat(easting: number, northing: number, zone: number, south: boolean): [number, number];
|
|
295
|
+
/** `EPSG:32633` -> `{zone: 33, south: false}`. Anything that is not a WGS84 UTM zone returns null. */
|
|
296
|
+
export declare function parseUtmCrs(name: string): {
|
|
297
|
+
zone: number;
|
|
298
|
+
south: boolean;
|
|
299
|
+
} | null;
|
|
300
|
+
/**
|
|
301
|
+
* Grid convergence at a point in a UTM zone: the bearing of GRID north
|
|
302
|
+
* clockwise from TRUE north, in degrees — γ = atan(tan(λ−λ₀)·sinφ), the
|
|
303
|
+
* standard spherical approximation (well under the ~1e-4° heading rounding in
|
|
304
|
+
* `readGeoreference` anywhere inside a zone's extent).
|
|
305
|
+
*
|
|
306
|
+
* The analytic counterpart of crs.ts's numerically-derived `gridConvergence`,
|
|
307
|
+
* with the SAME sign convention (that function unprojects a step of grid
|
|
308
|
+
* north and takes its true bearing; verified against it in ifc.test.ts). It
|
|
309
|
+
* exists for the same reason too: a model aligned to a UTM GRID is not
|
|
310
|
+
* aligned to true north, and `site-heading` is a true bearing. UTM zones are
|
|
311
|
+
* resolved analytically precisely so ANY zone works without proj4 or a
|
|
312
|
+
* bundled def — so the correction has to be analytic as well, or an
|
|
313
|
+
* EPSG:326xx/327xx file sits skewed (up to ~3° near a zone edge at high
|
|
314
|
+
* latitude) while an EPSG:25832 file, whose convergence rides the proj4
|
|
315
|
+
* branch, does not.
|
|
316
|
+
*/
|
|
317
|
+
export declare function utmGridConvergence(lonDeg: number, latDeg: number, zone: number): number;
|
|
318
|
+
/**
|
|
319
|
+
* Un-projects an `IfcMapConversion` affine AT a real model point — the
|
|
320
|
+
* tileset's own anchor, `centre` — rather than at the raw origin the
|
|
321
|
+
* conversion names. Pulled out of `loadIfc` so the routing rule is
|
|
322
|
+
* independently testable without booting web-ifc.
|
|
323
|
+
*
|
|
324
|
+
* The routing mirrors `readGeoreference` exactly, and that symmetry IS the
|
|
325
|
+
* fix: WGS84 UTM codes go through the analytic `utmToLonLat` (any of the 120
|
|
326
|
+
* zones, either hemisphere, no proj4), everything else through the bundled
|
|
327
|
+
* CRS table. An earlier version sent every code to `projectedToLonLat`,
|
|
328
|
+
* which returns null for the UTM zones outside the bundled defs (e.g.
|
|
329
|
+
* EPSG:32656) — so exactly the files whose position had been resolved
|
|
330
|
+
* analytically had their re-anchor silently no-op, keeping the raw-origin
|
|
331
|
+
* position and reintroducing the ~32.7 m offset class this correction exists
|
|
332
|
+
* to remove.
|
|
333
|
+
*
|
|
334
|
+
* Returns null when neither route can un-project the code; the caller keeps
|
|
335
|
+
* its current position but must stop claiming survey-grade placement for it.
|
|
336
|
+
*/
|
|
337
|
+
export declare function reanchorMapConversion(affine: MapConversionAffine, centreEast: number, centreNorth: number): Promise<[number, number] | null>;
|
|
338
|
+
/**
|
|
339
|
+
* Where the model says it is, which way it says north is, and at what scale.
|
|
340
|
+
*
|
|
341
|
+
* Three independent facts from three different places, every one optional:
|
|
342
|
+
*
|
|
343
|
+
* - POSITION — `IfcSite.RefLatitude`/`RefLongitude`. Read it, but do not trust
|
|
344
|
+
* it: an authoring tool's DEFAULT project location is indistinguishable from
|
|
345
|
+
* a surveyed one. Both prepared samples in this repo carry defaults (the
|
|
346
|
+
* clinic sits on Revit's Boston, the duplex on a Chicago city centre point),
|
|
347
|
+
* so the models land on occupied downtown blocks at an arbitrary rotation.
|
|
348
|
+
* That is the whole reason `site-origin` exists as an override.
|
|
349
|
+
* - HEADING — `IfcMapConversion.XAxisAbscissa`/`XAxisOrdinate` on a properly
|
|
350
|
+
* georeferenced export, otherwise `IfcGeometricRepresentationContext.
|
|
351
|
+
* TrueNorth`. Most IFC2x3 files carry neither, and then project north is
|
|
352
|
+
* ASSUMED to be true north. `headingSource` says which of the three
|
|
353
|
+
* happened, because "read from the file" and "assumed because the file was
|
|
354
|
+
* silent" should never look the same to a caller.
|
|
355
|
+
* - SCALE — `IfcMapConversion.Scale`.
|
|
356
|
+
*
|
|
357
|
+
* `IfcMapConversion.Eastings`/`Northings` are deliberately NOT used for
|
|
358
|
+
* position. They are in a projected CRS identified only by name on
|
|
359
|
+
* `IfcProjectedCRS`, and guessing a projection for it would put the model
|
|
360
|
+
* somewhere confidently wrong instead of admittedly approximate.
|
|
361
|
+
*
|
|
362
|
+
* Exported for tests: `api` is structurally typed, so a hand-rolled fake
|
|
363
|
+
* exercises the reading rules (convergence, unit handling, precedence)
|
|
364
|
+
* without booting web-ifc's WASM.
|
|
365
|
+
*/
|
|
366
|
+
export declare function readGeoreference(api: IfcApiLike, modelID: number, fallbackLonLat: [number, number]): Promise<{
|
|
367
|
+
lonLat: [number, number];
|
|
368
|
+
georeferenced: boolean;
|
|
369
|
+
heading: number;
|
|
370
|
+
scale: number;
|
|
371
|
+
headingSource: HeadingSource;
|
|
372
|
+
originSource: OriginSource;
|
|
373
|
+
affine: MapConversionAffine | null;
|
|
374
|
+
/** True only when the file itself states `OrthogonalHeight` — absent defaults to 0 the same as a declared 0 would, so this is the one signal that tells the two apart. */
|
|
375
|
+
hasOrthogonalHeight: boolean;
|
|
376
|
+
}>;
|
|
377
|
+
/**
|
|
378
|
+
* Below this element count, tiling overhead isn't worth it: the whole model
|
|
379
|
+
* keeps today's plain single-tile treatment, verbatim (see `useGrid` in
|
|
380
|
+
* `loadIfc`). Existing small fixtures (the clinic, duplex, bridge) stay on
|
|
381
|
+
* this path and produce byte-identical output to before spatial tiling
|
|
382
|
+
* existed at all — the clinic alone has 2,626 elements, so this has to clear
|
|
383
|
+
* that with real headroom, not just be "a small number". See
|
|
384
|
+
* `ifc.test.ts`'s "spatial tiling threshold" describe block, which asserts
|
|
385
|
+
* this against the clinic's real committed element count directly — change
|
|
386
|
+
* this constant without re-checking that and it is exactly the mistake that
|
|
387
|
+
* test exists to catch.
|
|
388
|
+
*/
|
|
389
|
+
export declare const GRID_TILING_THRESHOLD = 5000;
|
|
390
|
+
/**
|
|
391
|
+
* A feature's cell, keyed off its FIRST placed geometry's own placement
|
|
392
|
+
* origin (the matrix translation column) rather than a true centroid — cheap
|
|
393
|
+
* (no vertex scan needed before bucketing starts) and adequate: it is only a
|
|
394
|
+
* bucketing heuristic, not a visual clip, since each cell's own 3D-Tiles
|
|
395
|
+
* bounding box is always computed from the vertices actually inside it.
|
|
396
|
+
*/
|
|
397
|
+
export declare function gridCellKey(east: number, north: number): string;
|
|
398
|
+
/**
|
|
399
|
+
* Parses IFC bytes into a renderable 3D Tiles model held entirely in memory.
|
|
400
|
+
*
|
|
401
|
+
* Assign `tilesetUrl` to a `Tile3DLayer`'s `tileset` and `edgesUrl` to a
|
|
402
|
+
* companion `PathLayer`'s `data`; `features` is the same property table
|
|
403
|
+
* picking resolves, for building legends and category lists.
|
|
404
|
+
*/
|
|
405
|
+
export declare function loadIfc(source: ArrayBuffer | Uint8Array | Blob, options?: LoadIfcOptions): Promise<IfcModel>;
|
|
406
|
+
export {};
|