Says where a point is: its distance from the project origin, the level it is on, its height above that level and its map grid coordinates.
How the precision is kept
Millimeters and meters in 64-bit. Coordinates pass through 4 × 4 matrices from ifc-util.js, and the survey point is read from the site's placement. A map value is divided by the map unit's size as the file gives it and rounded only when it is written as text.
Calculations under this policy
8 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
Where the site's survey point is in the file's coordinates, with its elevation and map grid position.
survey point = site placement origin × file unit in mm; elevation in m = RefElevation × unit ÷ 1000
The survey point is the translation part of the site's world matrix times the file's unit in millimeters. Origin and survey point count as one marker when the survey point is under 1 mm from the origin (Math.hypot). Latitude and longitude are kept only within ±90° and ±180°.
Picks the level whose floor is at or just below a height.
the highest level with elevation ≤ z + 1 mm
A 1 mm absolute allowance puts a point on a level's floor on that level. Below every level the lowest one is returned.
coords.js
lines 43 to 48
BimCraft 0.8.1
export function levelFor(levels, zMm) {
if (!levels?.length || !Number.isFinite(zMm)) return null;
let best = null;
for (const l of levels) if (l.elevation <= zMm + 1 && (!best || l.elevation > best.elevation)) best = l;
return best || levels.reduce((a, b) => (b.elevation < a.elevation ? b : a));
}
Foot and US survey foot
Exact
The two sizes of foot a map's unit is compared with.
ft = 0.3048 m; US ft = 1200 ÷ 3937 m
0.3048 is the foot's exact definition and 1200 ÷ 3937 the US survey foot's exact fraction, evaluated once in 64-bit. They are used to recognize the map's unit, not to convert.
coords.js
line 50
BimCraft 0.8.1
const FT = 0.3048, USFT = 1200 / 3937;
Map grid coordinates as text
64-bit
A point's eastings and northings in the map's own unit.
value = meters ÷ map unit in meters, written with 3 decimals
The metric coordinate is divided by the map unit's size as the georeference gives it, in 64-bit, and written with exactly 3 decimals by toLocaleString. That rounding is of the text only. In the person's display unit the value goes to model-units.js instead.
coords.js
lines 51 to 61
BimCraft 0.8.1
export function mapText(georef, eM, nM, units = null) {
if (units) { // GH.3-A: the person's display unit (eM, nM in metres, as fileToMap gives them); the map's own text is kept in `own`
const own = mapText(georef, eM, nM), M = 1000; // eM, nM are metres (fileToMap)
return { e: coordNumber(eM * M, units), n: coordNumber(nM * M, units), unit: unitMark(units === 'ftin' ? 'ftdec' : units), own, note: crsUnitNote(units, own.unit) }; // note: the CRS's own unit id when it differs ('ft', 'us-ft', 'm')
}
const u = georef?.mapUnitM || 1;
const unit = Math.abs(u - FT) < 1e-9 ? 'ft' : Math.abs(u - USFT) < 1e-9 ? 'US ft' : 'm'; // the two feet differ by 2 ppm // i18n-ignore: unit symbols
const k = unit === 'm' ? 1 : u;
const f = (v) => (v / k).toLocaleString('en-US', { minimumFractionDigits: 3, maximumFractionDigits: 3 }).replace(/,/g, ' ');
return { e: f(eM), n: f(nM), unit };
}
Height above the level
64-bit
The lines of a readout: the level and the height above its floor, X and Y from the project origin, Z, and the map position.
height above the level = z − level elevation
One 64-bit subtraction in millimeters. Every value is handed to the unit formatter that is passed in; nothing is rounded here.
coords.js
lines 63 to 73
BimCraft 0.8.1
/**
* The lines of a readout. p = { x, y (mm from the project origin), z (mm, or null in the plan), level ({ name, elevation }),
* map ({ e, n, unit } | null) }. The 3D view gives z; the line under the level then says how high above its floor.
*/
export function readout(p, fmt, t) {
const lines = [];
if (p.level) lines.push(Number.isFinite(p.z) ? t('coords.level.at', { level: p.level.name, h: fmt(p.z - p.level.elevation) }) : p.level.name);
if (Number.isFinite(p.x)) lines.push(t('coords.xy', { x: fmt(p.x), y: fmt(p.y) }) + (Number.isFinite(p.z) ? ' ' + t('coords.z', { z: fmt(p.z) }) : ''));
if (p.map) lines.push(t('coords.map', { e: p.map.e, n: p.map.n, unit: p.map.unit }));
return lines;
}
Plan point to file coordinates
64-bit
A point in a storey's plan, in the file's coordinates.
q = M (x, y, 0)
One 4 × 4 matrix applied in 64-bit (ifc-util.js apply4).
A point in the file's coordinates, in a storey's plan.
p = M⁻¹ q
The storey matrix is inverted on each call (ifc-util.js inv4) and applied in 64-bit. The height is dropped.
coords.js
line 76
BimCraft 0.8.1
export function fileToPlan(M, q) { const r = apply4(inv4(M), q[0], q[1], q[2] || 0); return [r[0], r[1]]; }
File coordinates to the 3D view
64-bit
Where a point of the file sits in the 3D view, to draw the origin and survey markers.
(x, y, z) × unit → (x, z, −y) → C → placement
Scales by the file unit in meters, changes to Y up by a swap and a sign (exact), then applies two 4 × 4 matrices in 64-bit.
coords.js
lines 78 to 82
BimCraft 0.8.1
export function fileToViewer(p, place, C, fileUnitM) {
const m = [p[0] * fileUnitM, p[2] * fileUnitM, -p[1] * fileUnitM]; // Z up → Y up, metres, where the file has it
const c = apply4(C, m[0], m[1], m[2]);
return apply4(place, c[0], c[1], c[2]);
}
In detail
Method
Matrix products and inverses for plan, file and viewer coordinates; the level is the highest whose floor is at or below the point.
Number types
64-bit numbers; matrices as arrays of 16.
Tolerances
A point up to 1 mm below a level's floor is on that level. Origin and survey point are one marker when under 1 mm apart. The map unit is taken as the foot or the US survey foot when within 1e-9 m of 0.3048 or 1200 ÷ 3937.
Limits
Map text in the map's own unit has 3 decimals.
Approach: 64-bit, direct
Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.