Resources Numerical accuracy Calculation policy

Drawing location written to DWG and DXF

Writes where the drawing sits on its map grid (design point, reference point, north direction) into the file's GEODATA object, and reads another program's back.

How the precision is kept

The design point and the grid reference point are copied as 64-bit numbers without change. The rotation is the part that is limited: the north vector is rounded to 9 decimals when written, and the rotation read back from a file is rounded to 9 decimals of a degree.

Open item. Write the north vector's sine and cosine unrounded, and keep the rotation read from a file unrounded.

Calculations under this policy

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

geodwg.js CadCraft 0.8.2 64-bit 3 calculations Reviewed

Tests bin/test-cadcraft.mjs, bin/test-crs-share.mjs

GEODATA fields from the drawing's location

64-bit

The design point, reference point, units and north vector written to the file.

rot = degrees × π ÷ 180; north = (round₉(sin rot), round₉(cos rot))

The design point and reference point are copied unchanged. Both components of the north vector are rounded to 9 decimals (Math.round(v × 1e9) ÷ 1e9) before they are stored, so the written direction is held to about 1 × 10⁻⁹ and not to 15 digits. The projection radius is the constant 6378137.

  • geodwg.js
  • lines 178 to 193
  • CadCraft 0.8.2
/** The GEODATA a drawing's geo record writes (lib/dwg model fields, host left to the caller), or null (nothing to place). */
export function geodataFields(geo, units) {
  const g = cleanGeo(geo);
  if (!g) return null;
  if (g.src && sameCrs(g.crs, g.src.crs)) return { ...g.src.fields };   // as read, while unchanged (another program's own definition)
  const c = g.crs;
  if (!c || c.place.e === undefined) return null;
  const rot = (c.place.rot * Math.PI) / 180;
  const u = GEO_UNIT[units] || GEO_UNIT[c.units] || CS_UNIT[c.unit] || 6;
  return {
    coordType: 2, designPoint: { x: c.place.x, y: c.place.y, z: 0 }, referencePoint: { x: c.place.e, y: c.place.n, z: 0 },
    unitScaleH: 1, unitsH: u, unitScaleV: 1, unitsV: u, up: { x: 0, y: 0, z: 1 },
    north: { x: r9(Math.sin(rot)), y: r9(Math.cos(rot)) }, scaleMethod: 1, userScale: 1, seaLevel: false, seaElevation: 0, projRadius: 6378137,
    csDef: csXml(c.code, c.unit || 'm'), geoRss: '', obsFrom: '', obsTo: '', obsCoverage: '', meshPoints: [], meshFaces: [],
  };
}

Location from another program's GEODATA

64-bit

Turns a file's GEODATA into the drawing's coordinate system, placement point and rotation.

rot = round₉(atan2(north.x, north.y) × 180 ÷ π)

The rotation in degrees is rounded to 9 decimals before it is kept. The scale and up-vector tests use an absolute tolerance of 1e-9. Design and reference point coordinates are copied unchanged.

  • geodwg.js
  • lines 195 to 212
  • CadCraft 0.8.2
/**
 * Another program's GEODATA → { crs, src, why }. CadCraft places the drawing when the definition names an EPSG code and the
 * location is a projected grid (or latitude / longitude) at scale 1 with up = +Z; otherwise crs is null and `why` says
 * which ('code' | 'kind' | 'scale'), and the fields are still kept to write back.
 */
export function fromGeodata(fields, { units = '' } = {}) {
  const f = cleanFields(fields);
  if (!f) return null;
  const why = !epsgOf(f.csDef) ? 'code' : (f.coordType !== 2 && f.coordType !== 3) || Math.abs(f.up.z - 1) > 1e-9 ? 'kind' : Math.abs(f.unitScaleH - 1) > 1e-9 ? 'scale' : '';
  let crs = null;
  if (!why) {
    const rot = r9((Math.atan2(f.north.x, f.north.y) * 180) / Math.PI);
    const place = f.coordType === 2 ? { x: f.designPoint.x, y: f.designPoint.y, e: f.referencePoint.x, n: f.referencePoint.y, rot }
      : { x: f.designPoint.x, y: f.designPoint.y, lon: f.referencePoint.x, lat: f.referencePoint.y, rot };
    crs = cleanCrs({ code: epsgOf(f.csDef), name: csLabel(f.csDef), place, ...(!units && UNIT_NAME[f.unitsH] ? { units: UNIT_NAME[f.unitsH] } : {}) });
  }
  return { crs, src: { fields: f, crs }, why: crs ? '' : why || 'kind', label: csLabel(f.csDef) };
}

Fingerprint of the written location

64-bit

Decides whether the GEODATA in a file is still the one CadCraft wrote or another program moved the location since.

Every point and vector component is rounded to 9 decimals before the comparison, so a change of the design or reference point smaller than about 5 × 10⁻¹⁰ drawing units is not seen as a change. The definition text is compared by its length only.

  • geodwg.js
  • lines 174 to 176
  • CadCraft 0.8.2
const r9 = (v) => Math.round(v * 1e9) / 1e9;
/** A fingerprint of the GEODATA fields that place the drawing (to tell whether another program changed them). */
export const geoKey = (f) => JSON.stringify([f.coordType, f.designPoint, f.referencePoint, f.north, f.unitsH, f.unitScaleH].map((v) => (v && typeof v === 'object' ? Object.values(v).map(r9) : v)).concat(String(f.csDef).length));

In detail

Method
North vector = (sin rot, cos rot) with rot in radians from degrees; on reading, rot = atan2(north.x, north.y) in degrees.
Number types
64-bit numbers. DXF group values are read with parseFloat.
Tolerances
1e-9 absolute on the unit scale (|scale − 1|) and on the up vector's z (|z − 1|) to decide whether CadCraft can place the drawing. The fingerprint that detects a location changed by another program compares values rounded to 9 decimals.
Limits
Place values (x, y, rotation, easting, northing) of 1e10 or more in size are refused. A GEODATA with a unit scale other than 1 or a coordinate type other than projected grid or latitude and longitude is kept to write back but not used to place the drawing.

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