Resources Numerical accuracy Calculation policy

Survey levels from text and the survey check

Reads spot levels written as text or block attributes into points with a height, and checks a surveyor's drawing for unit, origin, flat-contour, outlier and duplicate problems.

How the precision is kept

A level is taken from its written digits with one conversion, then x, y and z are multiplied once by the unit scale. The check is a set of rules with fixed thresholds: sizes in meters, outliers by the median and the median absolute deviation, duplicates by 1 mm cells. Its results are counts and warnings, not measurements.

Calculations under this policy

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

survey.js CadCraft 0.8.2 64-bit 6 calculations Reviewed

Tests bin/test-survey.mjs

A level written as text

64-bit

Reads a level such as 104.37, +104,37 or EL 104.37 from a text.

value = Number(whole digits . decimal digits) × sign

The digits are joined as text and converted once, so the level is the nearest 64-bit number to what is written; a comma is read as the decimal mark. Up to 7 digits before and 6 after the mark are accepted, and without a prefix word the number needs a decimal part.

  • survey.js
  • lines 46 to 57
  • CadCraft 0.8.2
/** A level written as text, or null. */
export function levelFromText(raw, { minDecimals = 1 } = {}) {
  const s = plainText(raw);
  if (!s || s.length > 40) return null;
  const re = new RegExp('^(?:(' + PREFIX + ')\\s*[.:=]?\\s*)?([+\\-−]?)\\s*(\\d{1,7})(?:[.,](\\d{1,6}))?\\s*(?:m|ft|\'|’)?$', 'i');
  const m = re.exec(s);
  if (!m) return null;
  const dec = m[4] || '';
  if (!m[1] && dec.length < minDecimals) return null;
  const v = Number(m[3] + (dec ? '.' + dec : '')) * (m[2] === '-' || m[2] === '−' ? -1 : 1);
  return fin(v) ? v : null;
}

Levels into project units

64-bit

A level point's x, y and z in project units.

x·k, y·k, z·k

Each coordinate is multiplied once by the unit scale k; a scale that is not a positive finite number is replaced by 1. The level takes the text's insertion or alignment point as its position.

  • survey.js
  • lines 74 to 76
  • CadCraft 0.8.2
const p = e.type === 'TEXT' && (e.hAlign || e.vAlign) && e.align ? e.align : e.insert;
if (z === null || !p || !fin(p.x) || !fin(p.y)) continue;
found.push({ x: p.x * k, y: p.y * k, z: z * k, src: 'text', layer: String(e.layer ?? '0'), text: plainText(e.value).slice(0, 40), handle: String(e.handle ?? '').slice(0, 32) });

Extent and distance from the origin

64-bit

How far the data spans and how far it lies from zero, in meters.

span = |(maxX − minX, maxY − minY)| × m per unit; far = max |coordinate| × m per unit

A project in feet is converted with 1200 ÷ 3937 m, the US survey foot, whichever foot it uses; against the international foot that is 2 parts per million in the reported extent. Minimum and maximum are plain comparisons.

  • survey.js
  • lines 136 to 146
  • CadCraft 0.8.2
const mPer = METRES[projectUnit === 'm' ? 'm' : 'ft'];
const code = header?.INSUNITS | 0;
const pts = points.filter((p) => p && fin(p.x) && fin(p.y)).slice(0, MAX_POINTS);

// extent (project units) from the linework and the points
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity, maxAbs = 0, nv = 0;
const grow = (x, y) => { if (!fin(x) || !fin(y)) return; nv++; if (x < minX) minX = x; if (x > maxX) maxX = x; if (y < minY) minY = y; if (y > maxY) maxY = y; maxAbs = Math.max(maxAbs, Math.abs(x), Math.abs(y)); };
eachVertex(layers, grow);
for (const p of pts) grow(p.x, p.y);
const spanM = nv > 1 ? Math.hypot(maxX - minX, maxY - minY) * mPer : 0;
const farM = maxAbs * mPer;

Median

64-bit

The middle value of a list of levels.

odd count: the middle value; even: mean of the two middle values

The list is sorted numerically; an even count takes one addition and one halving.

  • survey.js
  • line 115
  • CadCraft 0.8.2
const median = (a) => { const s = [...a].sort((x, y) => x - y), n = s.length; return n % 2 ? s[(n - 1) / 2] : (s[n / 2 - 1] + s[n / 2]) / 2; };

Level outliers

64-bit

Counts levels that are far from the rest, and levels of exactly 0 that look like missing heights.

MAD = median(|z − median|) × 1.4826; limit = max(6 × MAD, 5 m); outlier when |z − median| > limit

1.4826 is the usual scale factor for the median absolute deviation, written to five digits. The rule needs at least 8 levels; with fewer, only levels above 9000 m or below −11 000 m are counted. The median and limits in the message are rounded to 2 decimals.

  • survey.js
  • lines 181 to 185
  • CadCraft 0.8.2
if (zs.length >= 8) {
  const med = median(zs), mad = median(zs.map((z) => Math.abs(z - med))) * 1.4826;
  const lim = Math.max(6 * mad, 5 / mPer);
  const zero = Math.abs(med) * mPer > 5 ? zs.filter((z) => z === 0).length : 0;
  const out = zs.filter((z) => z !== 0 && (absurd(z) || Math.abs(z - med) > lim)).length;

Duplicate points

64-bit

Counts points that sit on top of another point, with the same or a different level.

cell = 1 mm; key = (round(x ÷ cell), round(y ÷ cell)); same level when |Δz| ≤ cell

Points are binned into 1 mm cells by rounding, so two points closer than 1 mm on either side of a cell edge get different keys and are not counted. Each later point is compared with the first point of its cell only.

  • survey.js
  • lines 194 to 200
  • CadCraft 0.8.2
const cell = 1e-3 / mPer, seen = new Map();
let dup = 0, conflict = 0;
for (const p of pts) {
  const key = Math.round(p.x / cell) + ',' + Math.round(p.y / cell);
  const prev = seen.get(key);
  if (prev === undefined) { seen.set(key, fin(p.z) ? p.z : null); continue; }
  if (prev === null || !fin(p.z) || Math.abs(prev - p.z) <= cell) dup++; else conflict++;

In detail

Method
Median and scaled median absolute deviation for outliers; grid binning for duplicates; bounding box for extent.
Number types
64-bit numbers.
Tolerances
Outlier when |z − median| > max(6 × 1.4826 × MAD, 5 m), or outside −11 000 m to 9000 m. Duplicate cells 1 mm; same level within one cell size. Span warnings above 200 km and below 0.5 m; far from the origin above 100 km. Unit scale counts as 1 within 1e-9.
Limits
Feet are taken as 1200 ÷ 3937 m for every project in feet. Two points under 1 mm apart across a cell edge are not counted as duplicates. 1.4826 is a rounded constant.

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