Resources Numerical accuracy Calculation policy

Where an IFC model point is on the earth

Turns a point of an IFC model into map grid coordinates when the file has a map conversion, or into an approximate longitude and latitude from the site's reference point.

How the precision is kept

With a map conversion the point goes through one 64-bit 4 × 4 matrix and one multiplication by the meters per map unit. Without one, east and north offsets from the site's reference point are divided by the ellipsoid's radii of curvature at the site latitude, which is a first-order approximation that the code describes as fine across a site, not across a city. The WGS 84 constants are the defining values.

Calculations under this policy

5 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.

ifc-geo.js BimCraft 0.8.1 64-bit 5 calculations Reviewed

Tests bin/test-bimcraft.mjs, tools/bimcraft-test/georef-test.mjs

Degrees from degrees, minutes, seconds and millionths

64-bit

Reads an IFC compound angle as decimal degrees.

deg = d + m ÷ 60 + (s + μ ÷ 10⁶) ÷ 3600

The seconds and the millionths of a second are added first and divided once by 3600, then the three terms are added in 64-bit. A part that is missing or not a number counts as zero.

  • ifc-geo.js
  • lines 38 to 43
  • BimCraft 0.8.1
/** IfcCompoundPlaneAngleMeasure [deg, min, sec, millionths of a second] (all the same sign) → degrees. */
export function compound(v) {
  const a = (Array.isArray(v) ? v : []).map((x) => Number(val(x)) || 0);
  if (!a.length) return null;
  return a[0] + (a[1] || 0) / 60 + ((a[2] || 0) + (a[3] || 0) / 1e6) / 3600;
}

File point to map grid

64-bit

Easting, northing and height in meters of a point of the file.

[E, N, H] = (matrix × p) × meters per map unit

One 4 × 4 matrix product and one multiplication per coordinate in 64-bit. A 64-bit easting of 500 000 m is held in steps of about 6e-11 m.

  • ifc-geo.js
  • lines 87 to 91
  • BimCraft 0.8.1
/** A file point (file units, Z up) → [E, N, H] metres on the map grid. */
export function fileToMap(g, p) {
  const q = apply4(g.matrix, p[0], p[1], p[2]);
  return [q[0] * g.mapUnitM, q[1] * g.mapUnitM, q[2] * g.mapUnitM];
}

Longitude and latitude from the site's reference point

64-bit

An approximate WGS 84 position of a file point from its offset east and north of the site's point.

w = √(1 − e² sin² φ); N = a ÷ w; M = a (1 − e²) ÷ w³; lon = lon₀ + east ÷ ((N + h) cos φ); lat = lat₀ + north ÷ (M + h)

64-bit throughout, with the WGS 84 defining constants. The offsets are turned by the file's true-north direction and divided by radii evaluated at the reference latitude only, a first-order approximation that the file's header calls fine across a site, not across a city. The division by cos φ is not guarded at the poles.

  • ifc-geo.js
  • lines 93 to 100
  • BimCraft 0.8.1
/** A file point → [lon, lat, h] on WGS 84 from the site's reference point (east / north along TrueNorth, up). */
export function siteLonLat(g, p) {
  const u = g.fileUnitM, d = [(p[0] - g.origin[0]) * u, (p[1] - g.origin[1]) * u, (p[2] - g.origin[2]) * u];
  const [nx, ny] = g.north, east = d[0] * ny - d[1] * nx, north = d[0] * nx + d[1] * ny;
  const phi = g.lat * Math.PI / 180, w = Math.sqrt(1 - E2 * Math.sin(phi) ** 2);
  const N = A / w, M = A * (1 - E2) / w ** 3;
  return [g.lon + (east / ((N + g.elev) * Math.cos(phi))) * 180 / Math.PI, g.lat + (north / (M + g.elev)) * 180 / Math.PI, g.elev + d[2]];
}

Degrees to degrees, minutes and seconds

64-bit

Formats a latitude or longitude with its hemisphere letter.

d = ⌊|deg|⌋; m = ⌊(|deg| − d) × 60⌋; s = (|deg| − d − m ÷ 60) × 3600, shown with 2 decimals

The seconds are what is left after subtracting the whole degrees and minutes, so they keep fewer significant digits than the angle; the result is shown to 0.01 second. Seconds of 59.995 or more roll over to the next minute so that 60.00 is never printed.

  • ifc-geo.js
  • lines 108 to 115
  • BimCraft 0.8.1
/** Degrees → degrees, minutes, seconds with the hemisphere letter. */
export function dms(deg, axis) {
  const h = axis === 'lat' ? (deg < 0 ? 'S' : 'N') : (deg < 0 ? 'W' : 'E');   // i18n-ignore: compass letters as on maps
  let a = Math.abs(deg), d = Math.floor(a), m = Math.floor((a - d) * 60), s = (a - d - m / 60) * 3600;
  if (s >= 59.995) { s = 0; m += 1; }
  if (m >= 60) { m = 0; d += 1; }
  return d + '° ' + String(m).padStart(2, '0') + '′ ' + s.toFixed(2).padStart(5, '0') + '″ ' + h;
}

Viewer point to the file's own coordinates

64-bit

Takes a point picked in the viewer back to the file's Z-up coordinates in its own length unit.

y = C⁻¹ × (place⁻¹ × p); file = (y_x, −y_z, y_y) ÷ meters per file unit

Two general 4 × 4 inverses and two matrix products in 64-bit, then one division per coordinate. The point itself comes from the viewer.

  • ifc-geo.js
  • lines 117 to 124
  • BimCraft 0.8.1
/**
 * A point in BimCraft's viewer (scene space, metres, Y up) → the file's own coordinates (file units, Z up).
 * `place` = the model's scene placement (viewer.modelMatrix), `C` = its coordination matrix (api.GetCoordinationMatrix).
 */
export function viewerToFile(p, place, C, fileUnitM) {
  const local = apply4(inv4(place), p[0], p[1], p[2]), y = apply4(inv4(C), local[0], local[1], local[2]);   // Y up, metres, where the file has it
  return [y[0] / fileUnitM, -y[2] / fileUnitM, y[1] / fileUnitM];
}

In detail

Method
Map: [E, N, H] = matrix × point × map unit. Site: N = a ÷ √(1 − e² sin² φ), M = a (1 − e²) ÷ (1 − e² sin² φ)^1.5, Δlon = east ÷ ((N + h) cos φ), Δlat = north ÷ (M + h).
Number types
64-bit numbers. a = 6378137 m, f = 1 ÷ 298.257223563, e² = f (2 − f).
Tolerances
A true-north direction shorter than 1e-9 is ignored. Seconds of 59.995 or more are carried into the next minute when an angle is formatted.
Checked by
bin/test-bimcraft.mjs (--geo) runs compound, dms, fileToMap and viewerToFile; tools/bimcraft-test/georef-test.mjs runs fileToMap.
Limits
The site method uses the radii at the reference latitude only and divides by cos φ, so it degrades with distance from the reference point and near the poles. It assumes the site's latitude and longitude are WGS 84.

Approach: 64-bit, stated limit

The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.

Every policy that takes this approach