-
Notifications
You must be signed in to change notification settings - Fork 1
Adding New Units
Go back Home
There are 2 primary ways for adding new units to the library:
- Adding new units using the code generator
- Adding units manually
We highly recommend adding new units only by using the units generator. If you still wish to add a new unit manually, you should read the contributors guide first. Here we'll only explain how to use the generator.
The units generator reads a json file and generate all units from it. This json file is in unit_generator/src/main/resources/data/units.json. Edit this file in order to add new unit types and unit scales.
This is the abstract type of units (such as length, duration, force, frequency, etc.). You can add new unit types by adding a new json object to the "unit_types" list. This object must have a "type_name" slot with a valid name (only letters and spaces). There are 2 kinds of unit types:
This is a type that's not dependent on any other unit type. For example, length, duration, mass, angle, etc. A new unit type is a basic type by default.
This is a type that depends on other units. A complex type is always a ratio and/or multiplication of other units. For example:
speed = length/duration
area = length * length
force = mass * length / duration * duration = mass * acceleration
energy = mass * length * length / duration * duration = mass * speed * speed = force * length
In order to add new complex type, add to your new unit type json object a "ratio" slot with a json object that has the following slots in it:
- numerators - a json list of strings with the names of the units which are in the numerator of the declaration fraction. If a unit is multiplied few times in the numerator, write it the same amount of times.
- denominators - a json list of strings with the names of the units which are in the denominator of the declaration fraction. If a unit is multiplied few times in the denominator, write it the same amount of times.
Basic type:
{
"type_name" : "length",
}
Multiplication type:
{
"type_name" : "area",
"ratio" : {
"numerators" : ["length", "length"],
"denominators" : []
}
}
Ratio type:
{
"type_name" : "speed",
"ratio" : {
"numerators" : ["length"],
"denominators" : ["duration"]
}
Complex type:
{
"type_name" : "force",
"ratio" : {
"numerators" : ["mass", "length"],
"denominators" : ["duration", "duration"]
}
This is the actual unit (such as meters, yards, degrees, newtons, hertz, etc.). Every unit scale has a type. You can add new scales to an existing type or create a new type and your scale to it. You should add the new scale to the "unit_scales" list of this type as a json object. This object must have a "singular_name" and "plural_name" slots with a string value which are used for prints. If the "plural_name" value doesn't have letters and spaces only you should add a "name" slot that does.
Every new scale must also have exactly one of the following slots:
- "is_basic" - this is a boolean slot that says that this is the first scale of a type. There can be at most one scale with this slot per type, and basic types must have one.
- "multiplier_number" and "relative_to" - those slots imply the relation between this new scale to an existing scale. for example, yards is 0.9144 meters.
- "multiplier_string" and "relative_to" - Same is the above, but with string such as kilo, mega, centi, etc. as a multiplier.
- "ratio" - same as types ratio, but for scales. for example:
newtons = kilograms * meters / seconds * seconds
Basic type:
{
"type_name" : "length",
"unit_scales" : [
{
"singular_name" : "meter",
"plural_name" : "meters",
"is_basic" : "true"
},
{
"singular_name" : "yard",
"plural_name" : "yards",
"multiplier_number" : 0.9144,
"relative_to" : "meters"
},
{
"singular_name" : "kilometer",
"plural_name" : "kilometers",
"multiplier_string" : "kilo",
"relative_to" : "meters"
}
]
}
Multiplication type:
{
"type_name" : "volume",
"ratio" : {
"numerators" : ["length", "length", "length"],
"denominators" : []
},
"unit_scales" : [
{
"name" : "cubic meters",
"singular_name" : "meter^3",
"plural_name" : "meters^3",
"ratio" : {
"numerators" : ["meters", "meters", "meters"],
"denominators" : []
}
},
{
"singular_name" : "liter",
"plural_name" : "liters",
"multiplier_string" : "milli",
"relative_to" : "cubic meters"
}
]
}
Go to unit_generator/src/main/resources/data/units_tests.json. This file contains all the conversions possible between different units.
when adding new unit scales (and especially unit types) one should write in this file all of the conversion tests from the new scale and to the new scale.
Tests are very easy to add:
- if you add a new unit type, you should add a new test suite for this type. This test suite should have a unit_type property specifying which unit type it tests.
- when adding new scale, you should add test case to the suitable test suite. this test case has a "from" and "to" properties specifying the conversion tested and "value" specifying the conversion value.
for example:
{
"unit_type" : "volume",
"test_cases" : [
{
"from" : "cubic meters",
"to": "cubic yards",
"value" : 1.3079506193143928
},
...
{
"from" : "cubic yards",
"to": "cubic meters",
"value" : 0.764554857984
},
...
]
}
After you finished on editing the units.json file with your new content, go to the base project (units) and run the following gradle command:
./gradlew generate
Pay attention:
Make sure you compile the generator before generating. You can do it by running:
./gradlew units_generator:build
This will run the generator and copy the generated files into their projects
Before generating, the units generator does some validations on the units.json file automatically. These validations are:
- Syntax validations - these validations are received using a Json Schema. the units.json must fit the schema in order for the generator to run.
- Logical validations - the data in the units.json is checked before generating.
Pay Attention:
Not all validations are running automatically. We are improving our validations all the time, but sometimes we can miss things. Add new units carefully.
- You should compile the projects to see that the generation passed well
- You should add unit tests when necessary (mostly in c++, for now)
- If you think that this new unit is helpful for others, contribute it to the library with a pull request! but don't forget to test it first.