A nice API for handling numbers with associated units.
Only units relevant to cave surveying are built into this package, but it's possible to define your own using the API.
I've dealt with a lot of unit conversion bugs over the years. There are usually two root causes:
- storing the associated unit in a separate variable than a number
- forgetting to perform a unit conversion somewhere during a calculation
To cut down on these kinds of mistakes, now I try to always store the number and its unit together in a single object, and perform calculations on those objects instead of directly on the numbers. I only deal with raw numbers at the input or output boundaries of an API.
The TypeScript types are also designed to help you avoid accidentally mixing quantities of different unit types, for instance lengths and angles.
Here are some examples of what the API for this looks like:
Unitize.feet(1).add(Unitize.inches(6)) // 1.5 ft
Unitize.feet(1).get(Length.meters) // 0.3048 m
Unitize.feet(2).div(Unitize.inches(4)) // 6
type Point = {
northing: UnitizedNumber<Length>,
easting: UnitizedNumber<Length>,
elevation: UnitizedNumber<Length>,
}
type SurveyLeg = {
distance: UnitizedNumber<Length>,
azimuth: UnitizedNumber<Angle>,
inclination: UnitizedNumber<Angle>,
}
function calculateLeg(
from: Point,
{ distance, azimuth, inclination }: SurveyLeg
) {
const xy = distance.mul(Angle.cos(inclination))
const northing = xy.mul(Angle.cos(azimuth))
const easting = xy.mul(Angle.sin(azimuth))
const elevation = distance.mul(Angle.sin(inclination))
return {
northing: from.northing.add(northing),
easting: from.easting.add(easting),
elevation: from.elevation.add(elevation),
}
}
import { Unitize } from '@speleotica/unitized'
Contains shortcut functions for making unitized numbers.
For example, Unitize.meters(2)
is equivalent to new UnitizedNumber(2, Length.meters)
.
Unitize.meters
Unitize.centimeters
Unitize.kilometers
Unitize.feet
Unitize.inches
Unitize.yards
Unitize.miles
Unitize.radians
Unitize.degrees
Unitize.gradians
(1/400 of a unit circle)Unitize.milsNATO
(1/6400 of a unit circle)Unitize.percentGrade
(rise over run as %; 100% = 45 degrees)
import { Length } from '@speleotica/unitized'
Contains length units:
Length.meters
Length.centimeters
Length.kilometers
Length.feet
Length.inches
Length.yards
Length.miles
Each of these units is an instance of Unit<Length>
.
import { Angle } from '@speleotica/unitized'
Contains angle units:
Angle.radians
Angle.degrees
Angle.gradians
(1/400 of a unit circle)Angle.milsNATO
(1/6400 of a unit circle)Angle.percentGrade
(rise over run as %; 100% = 45 degrees)
Each of these units is an instance of Unit<Angle>
.
Computes the sine of the given angle.
Computes the cosine of the given angle.
Computes the tangent of the given angle.
Computes the arcsine of the given number.
Computes the arccosine of the given number.
Computes the arctangent of the given number.
Equivalent to Math.atan2
, but returns a UnitizedNumber<Angle>
.
Equivalent to Math.atan2
, but returns a UnitizedNumber<Angle>
.
Normalizes the given angle
to the range [0, one revolution); returns the normalized
angle in the same units.
Returns the angle facing the opposite direction, in the same units.
import { UnitizedNumber } from '@speleotica/unitized'
Creates a UnitizedNumber
with the given value
and unit
The unit this UnitizedNumber
's value is in.
The numeric value of this UnitizedNumber
. Accessing this directly is discouraged;
use get(unit)
instead.
Converts this UnitizedNumber
's value
to the given unit
.
Returns this + addend
as a new UnitizedNumber
in the same units as this.
Returns true
iff the numeric value is not NaN
or infinite.
Returns true
iff the numeric value is infinite.
Returns true
iff the numeric value is NaN
.
Returns a new UnitizedNumber
in the given unit
.
Returns a new UnitizedNumber
with the same units and negative value.
Returns this - subtrahend
as a new UnitizedNumber
in the same units as this.
Returns this * multiplicand
as a new UnitizedNumber
in the same units as this.
Returns true
iff the numeric value is negative.
Returns true
iff the numeric value is positive.
Returns true
iff the numeric value is 0.
Returns true
iff the numeric value is not 0.
Returns this % modulus
as a UnitizedNumber
in the same units as this.
Returns a new UnitizedNumber
with the same units and absolute value.
Returns this / denominator
.
Returns this / denominator
as a UnitizedNumber
in the same units as this.
Returns > 0
if this > other
, < 0
if this < other
, and 0
otherwise.
import { UnitType } from '@speleotica/unitized'
A type of unit, for example length or angle or temperature.
Converts a value from one unit to another. The default implementation just returns
to.fromBase(from.toBase(value))
If you want more precision you can use a FactorTableUnitType
to provide a table of
conversion factors from one unit to another, or override this method in a derived class.
import { Unit } from '@speleotica/unitized'
Props may have fromBaseFactor
and toBaseFactor
, which
will be used for the default fromBase
and toBase
implementations (which you may override in a derived class).
The type of this unit.
The unique id of this unit.
Converts the given number from this unit to the base unit. Only UnitType
should call this method.
You may override this method in a derived class for nonlinear units.
Converts the given number from the base unit to this unit. Only UnitType
should call this method.
You may override this method in a derived class for nonlinear units.
import { FactorTableUnitType } from '@speleotica/unitized'
A UnitType
that uses a table of factors to more accurately convert from one unit to another
(instead of converting to some base unit as an intermediary).
Factors is the conversion table, with unit id
s as keys.
value * factors[from.id][to.id]
is used to convert from one unit to another.
Not all pairs of units have to be included in the table; convert
will fall back
to converting to the base unit as an intermediary if a factor isn't found in this table.