Mathjs: Adding custom units into the library

Created on 21 Jan 2016  路  28Comments  路  Source: josdejong/mathjs

Is there any API for devs to add their own units into the system? I have a few custom units (in a new unit base) that I want to convert via the math.js library but have no idea where to start. Basically I need to add a new base, then a couple of units with that new base, then a couple of aliases for that. An API like this would be cool:

// here "30" is the base ID, and "kram" is the name of the base unit (like gram)
math.units.registerBase(30, "kram")

// register 1 krg = 1000 krams
math.units.registerAlias("kram", 1000, "krg")

// register 1 "kound" is = 453 krams
math.units.registerUnit("kram", 453.592, ["kound", "kounds"])

I could help build such an API if you tell me which arrays/objects the unit / base must be registered in in order to work.

feature

Most helpful comment

@PfxMobile have you checked out the docs?

http://mathjs.org/docs/datatypes/units.html#userdefined-units

All 28 comments

Thanks for the suggestion, this is currently not supported but has been on my wishlist for a long time. I've marked this issue as a feature request.

I could help build such an API if you tell me which arrays/objects the unit / base must be registered in in order to work. Just point me to the files in question and I'll build something that you guys can review. I've done PR's before.

Check out /lib/type/unit/Unit.js. The base definitions are here and the definition for the built-in units starts here. There are a few bits of initializing that happen afterward that would also need to be done for custom units.

If you're really ambitious, I don't see any reason the built-in units need to be declared in Unit.js; they could be defined in another file (kind of like how the physical constants are declared). Then your API could be used to load the built-in units. Users could extend Unit with their own file, or add units one at a time using a function call. Lots of possibilities here.

Thanks, I'll look it up and see what I can do. I might be busy the next few days but I'll try to make time.

Thanks for your efforts! -- I'd love to have this feature too.

I have tried to add new unit, cubic mile.
In units declaration of units.js i have added new entry :

    cumil: {
        name: 'cumil',
        base: BASE_UNITS.VOLUME,
        prefixes: PREFIX_NONE, 
        value: 2.39913e-10,
        offset: 0
    }, //  2.39913e-10 m3

but i'm getting error "SyntaxError: Unit "cumil" not found." somewhere in units.js file.
Can someone point me how to add new unit in units.js file?

Try PREFIXES.NONE instead of PREFIX_NONE.

See #570 and #571.

@josdejong @ericman314 @ellis

Okay, an update. I've just added the acre and hectare units (my first!) into the library, and it was super easy. Obviously you can add aliases with math.import so no improvements needed here. So, if I build a basic API say, the following, can someone help me close it up? I'll add the API tomorrow, and perhaps we can take it from there?

// API
math.importUnit(nameOfUnit, value, baseUnit, prefixes, [aliases]);

// usage 1
math.importUnit("acre", "4046.86 meters", "surface", "none"); // 1 acre = 4046 m

// usage 2
math.importUnit("acre", "4046.86 meters", BASE_UNITS.SURFACE, PREFIXES.NONE);

Which usage do you guys prefer?

The function will work as follows:

  • check for conflicts (units already named that) and exit with error or overwrite
  • convert the value to a Unit object with the base units (such as meters), compatible with UNITS format
  • add the unit definition into UNITS
  • call some function responsible for re-initialization of the units (this is the tricky part)

Awesome that you are working on this! I like both of those options. I am leaning more towards using strings so the user wouldn't have to use Unit.BASE_UNITS and Unit.PREFIXES directly.

As far as initialization, the only work that is done (as far as I know) is in this snippet near the end of Unit.js:

 // Add dimensions to each built-in unit
  for (var key in UNITS) {
    var unit = UNITS[key];
    unit.dimensions = unit.base.dimensions;
  }    

It should be straightforward to copy the dimensions from the specified base into the new unit.

Here's another idea. What if you could also just pass an object, like this:

math.importUnit({name: "acre", value: 4046.86, base: "surface"});

Where you could omit any properties, like prefix, that don't apply. Then our big list of units which is currently hard-coded in Unit.js could be moved to a separate file and loaded directly using your API. Users may be able to load their own files too.

Looks good @hgupta9 . Like Eric I like the string based usage better.

How would we define a new base unit? (do we want to support that?)

As for the name of the function, how about createUnit? You create something new here so "create" may make more sense then "import".

I'm very interested in this. I would like to use mathjs for my project, which has quite a few different unit types that need converting:

  • Mass
  • Velocity
  • Force
  • Length

Mass and length are already catered for, but I don't believe velocity (m/s, m/min, ft/s, ft/min etc) and force (kN, kps) are supported.

@olimortimer I'm not sure what you mean: you can actually use m/s, m/min, ft/s, ft/min, kN, kb/s.

Here some examples: https://github.com/josdejong/mathjs/blob/master/examples/units.js

Thanks @josdejong I must have missed those.

Syntax would be:

  • m/s
  • m/minute
  • ft/s
  • ft/minute

However, I can't seem to find a match for forces (kips is an imperial unit of force - 1 kip = 4.4482216 kN):

1 kN to kps
Error: Undefined symbol kps

1 kN to kips
Error: Undefined symbol kips

1 kN to kip
Error: Undefined symbol kip

The unit kip is indeed missing. @hgupta9 added this unit, it's now in the develop branch. Besides that, we still need functionality to add custom units yourself of course.

Excellent, thanks @josdejong

The ability to pass custom units would definitely be a big plus for mathjs.

Keep up the great work!

Has there been any progress on this? I am very interested in using math.js for a project, but really need to add some custom units.

@BMcFerrin there hasn't been progress here. What sort of units do you need? Most units can be described in terms of the existing units, like:

var scope = {}
math.eval('knots = 0.514444444 meters / second', scope)
math.eval('5 knots in km/h', scope)   // 9.259999992 km / h   

@josdejong: If you use math.eval to define a unit, you still cannot convert to your unit:

var scope = {}
math.eval('knots = 0.514444444 meters / second', scope)
math.eval('9.26 km/h in knots', scope)   // Error: Cannot convert to a unit with a value

@hgupta9, are you still working on this? I can pick it up if you want.

@josdejong: For now, I have an immediate need for additional liquid volume units (totes and drums); however, I may need more in the future. Currently I just added my units to the javascript file myself, but I would much rather be able to use an API as described earlier in this thread.

Thanks for your feedback @BMcFerrin.

@ericman314 creating units as a regular variable is indeed limited. Would be great if you could pick this up!

See PR #683.

Hi folks - going back to the Custom units
We are working for the application that need to work with Labs Results and we requires special units like
Milliequivalents or Units(International units) U/IU
As i understand right now i couldn't register any unit that not derived from existing available Base ?

You can create a custom base as well. You need to add some values in some
places.

>

@robinrodricks Could you provide the sample?

@PfxMobile have you checked out the docs?

http://mathjs.org/docs/datatypes/units.html#userdefined-units

@josdejong Glad you could add support for adding custom units directly into the library API! No more fiddling around with the JS statics!

@josdejong Right sorry i missed the line 'If you cannot express the new unit in terms of any existing unit'
That's cool
So then i guess this item could be closed

:+1:

Was this page helpful?
0 / 5 - 0 ratings