# Formulas

## Variables

Formulas can work with variables, which use the field values of a location, referring to them by their key.

Warning: Fields’ keys might have a dash (`-`), but a formula would interpret that as a subtraction. To use a field that has a dash in its name either encapsulate it in backwards ticks (``my-variable` > 10`) or refer to it in camel casing (`myVariable > 10`).

Check if a variable exists:

• `has(Variable): Bool`, e.g., `has(myVariable)`
• `Variable == nil: Bool`, e.g., `myVariable == nil`
• `Variable != nil: Bool`, e.g., `myVariable != nil`
• `??`: Nil coalescing, e.g., `unknownVariable ?? 'not found' == 'not found'`

## Boolean

Indicates true/false states.

You can use the built-in constants `true` and `false` to reference them, compare them using `==` or `!=` and negate an expression by prefixing it with `!`.

## String

A text, which can be short or long.

Create it by enclosing text in double quotes (`"`) or single quotes (`'`), and you can concatenate it using a ‘+’, e.g., `"Hello" + " world"`. You can also compare two strings using `==` or `!=`.

You can also extract characters of a string using `[Number]`, e.g., `"Hello" == "H"`.

Note that Maparoni also handles text that is formatted as Markdown or as HTML.

### Constants

`id: String`

The ID of the location in its collection.

### Global functions

`find(needle: String, haystack: String) -> String`

Finds the first String in the second String, returning what was matched.

The first String is typically a regular expression, and the second one a (longer) field from which you want to extract something. Wrap what you want to extract in brackets.

Note: Before doing the look-up the second String will get whitespace and new-line characters removed at the beginning and end of each line.

Examples:

• `Number(find('=(.*)', 'Value=42')) == 42`: Extracts the numeric value from the String, which is then also a String, and then turns it into a Number.
• `find('<td>Status</td><td>(.*?)</td>', htmlField)`: Finds the content within a `td` HTML tag that’s following one with has “Status” as its content. Note the `?` in the grouping to do a non-greedy search.

`matches(haystack: String, needle: String) -> Boolean`

Checks if the first String matches the second expression. E.g., `matches('Hello', 'H?llo') == true`

Note: Before doing the match the first String will get whitespace and new-line characters removed at the beginning and end of each line.

### Functions

`String.characters: Number`

Counts the total number of characters.

`String.contains(String) -> Boolean`

Returns if the String contains the provided String.

`String.hasPrefix(String) -> Boolean`

Returns if the String starts with the provided String.

`String.hasSuffix(String) -> Boolean`

Returns if the String end with the provided String.

`String.in(strings: List) -> Boolean`

Returns if the String is contained in the provided list.

`String.prefix(maxLength: Number) -> String`

Returns the prefix of String with a maximum length.

`String.suffix(maxLength: Number) -> String`

Returns the suffix of String with a maximum length.

`String.words: Number`

Counts the total number of words.

## Number

A number, which can have decimals.

### Constants

`index: Number`

The index of the location within the collection.

Warning: This does not account for the current filtering or sorting of the collection.

`pi: Number`

π, the ratio of a circle’s circumference to its diameter.

### Global functions

`avg(numbers: List) -> Number`

Returns the average value of all the numbers in the list.

`count(List) -> Number`

Returns the number of elements in the provided list.

`format(Number, mode: String) -> String`

Formats a Number according to the specified formatting mode.

• `format(Number, 'distance')`: Formats the number, which is interpreted as metres, as a distance according to the current locale, e.g., `format(160000, 'distance') == '160km'`
• `format(Number, 'fiveStars')`: Turns the number into little stars, e.g., `format(3, 'fiveStars') == '★★★☆☆'`
• `format(Number, 'percent')`: Turns the number into a percentage, e.g., `format(0.5, 'percent') == '50%'`
• `format(Number, 'significant')`: Turns the number into a formatted rounded number with a limited number of non-zero digits, e.g., `format(0.51241, 'percent') == '0.51'`

`max(numbers: List) -> Number`

Returns the maximum value in the provided list.

`min(numbers: List) -> Number`

Returns the minimum value in the provided list.

`sum(numbers: List) -> Number`

Returns the sum of all the numbers in the list.

### Constructors

`Number(String) -> Number`

Turns text into a number.

### Functions

`Number.abs: Number`

Gets the absolute value.

## Rating

Can be constructed using `Rating/2`, `Number of Number` or using a mixture of ★ and ☆ such as ★★★☆☆.

Note that ratings don’t have to be out of five stars, but can be out of any positive maximum value.

Ratings aren’t regular number as they have special arithmetic rules to combine them:

• `Rating + Rating`: Adds two ratings by averaging them, e.g., `★★★☆☆ + ★★★★★ == ★★★★☆`
• `Number * Rating`: Multiplies a rating to give it more weight, e.g., `(2 * ★★★★☆) + ★☆☆☆☆ == ★★★☆☆`

You can compare numbers to number, too, such as `★★★★★ > 3 == true`, which is short-hand for `★★★★★.score > 3 == true`

### Constructors

`Rating(score: Number, max: Number) -> Rating`

The first value is the score and the second is the maximum score, e.g., `Rating(3, 5) == ★★★☆☆`.

There’s also syntactic sugar to write `Number of Number` to do the same, or mix ★ and ☆ as in the example above.

### Functions

`Rating.maxScore: Number`

Gets the maximum score, e.g., `★★★☆☆.maxScore == 5`.

`Rating.score: Number`

Gets the score of a rating, e.g., `★★★☆☆.score == 3`.

## Date

A date as on the calendar, e.g., 15th of April 2015.

These are interpreted by Maparoni according to the device’s timezone.

### Global functions

`daysBetween(start: Date, end: Date) -> Number`

### Constructors

`Date(String) -> Date`

Turns a `yyyy-MM-dd` String into a Date.

`Date(Instant) -> Date`

Extracts the Date part of a Instant.

`Date(String, format: String) -> Date`

Turns a String into a Date using the provided date `format`.

`Date(year: Number, month: Number, day: Number) -> Date`

Turns three numbers representing year, month and day into a date.

### Functions

`Date.day: Number` `Date.month: Number` `Date.monthName: String`

Full name of the Date’s month in the language your device is set to.

`Date.weekday: String`

Full name of the Date’s day of the week in the language your device is set to.

`Date.weekdayEn: String`

Full name of the Date’s day of the week in English.

`Date.year: Number`

## Time

A time as on the clock, e.g., 22:30.

These are interpreted by Maparoni according to the device’s timezone.

### Constructors

`Time(String) -> Time`

Turns an `HH:mm` String into a Time.

`Time(hour: Number, minute: Number) -> Time`

Turns two numbers representing `hour` and `minute` into a relative Time.

`Time(String, format: String) -> Time`

Turns a String into a Time using the provided date `format`.

### Functions

`Time.hour: Number` `Time.minute: Number`

## Instant

An instant reflects a single moment in time, e.g., the exact moment you were both or a combination of date, time and a place, such as 15th of April 2015 at 22:30 in Sydney, Australia.

While an instant itself is unambiguous, it can be represented as many different dates - depending on the timezone. When Maparoni displays an instant, it used the time zone of your device.

### Constants

`now: Instant`

The current instant.

### Global functions

`format(Instant) -> String`

Turns the provided Instant into a nicely formatted text.

`format(Instant, format: String) -> String`

Formats a Instant according to the specified `format` String.

Special case for setting the second parameter to “ago”.

Examples:

• `format(Instant, 'HH:mm')`: Hours in 24h format, plus the minutes.
• `format(Instant, 'ago')`: Nicely formatted duration between now and the instant.

### Constructors

`Instant(isoText: String) -> Instant`

Turns a ISO8601 String into a Instant.

`Instant(String, format: String) -> Instant`

Turns a String into a Instant using the provided date `format`.

### Functions

`Instant.day: Number` `Instant.hour: Number` `Instant.minute: Number` `Instant.month: Number` `Instant.monthName: String`

Full name of the Instant’s month in the language your device is set to.

`Instant.weekday: String`

Full name of the Instant’s day of the week in the language your device is set to.

`Instant.weekdayEn: String`

Full name of the Instant’s day of the week in English.

`Instant.year: Number`

## Position

A geo-coordinate that represents a latitude/longitude pair anywhere on the planet.

### Constants

`area: Number`

The area for locations with associated polygons, in square metres. Returns zero for others.

`coordinate: Position`

The centre coordinates of the location.

`distance: Number`

Returns the distance in metres of the location to your current location. For polygons, polylines and multi-points is the minimum distance.

`latitude: Number`

The latitude of the centre coordinate of the location.

`length: Number`

The length for locations with associated polylines, in metres. Returns zero for others.

`longitude: Number`

The longitude of the centre coordinate of the location.

### Global functions

`distance(to: Position) -> Number`

Returns the distance in metres of the location to the specified coordinates. For polygons, polylines and multi-points it is the minimum distance.

### Constructors

`Position(String) -> Position`

Creates a geographical coordinate extracted from the provided String.

`Position(latitude: Number, longitude: Number) -> Position`

Creates a geographical coordinate with the provided `latitude` and `longitude`.

### Functions

`Position.country: Style`

Returns a geometry map style of the country that includes this coordinate, typically this is the country’s polygon(s)

`Position.distance(to: Position) -> Number`

Returns the distance in metres to the provided geo coordinate.

`Position.flag: String`

Returns the flag emoji of the country that includes this coordinate.

`Position.latitude: Number`

Returns the latitude component of the geo coordinates.

`Position.longitude: Number`

Returns the longitude component of the geo coordinates.

`Position.lookup(database: String) -> Style`

Returns a geometry map style by looking up the coordinate in the provided database

`Position.lookup(database: String, field: String) -> String`

Returns the value for the provided field by looking up the coordinate in the provided database

`Position.country(Color) -> Style`

Use `Coordinate.country.fill(Color)` instead

## Geometry

A GeoJSON-compatible geometry, which can be one of: Feature, FeatureCollection).

### Constants

`geometry: Geometry`

The GeoJSON geometry of the location.

`LineString: Geometry`

Creates a line string of the provided coordinates.

`Polygon: Geometry`

Creates a polygon of the provided coordinates.

### Global functions

`geometryValue(json: String) -> Geometry`

Parses the provided data field, which has to be a JSON object, as a GeoJSON geometry

### Functions

`Geometry.area: Number`

Calculates the area for a geometry in square metres.

`Geometry.center: Position`

Returns a coordinate that’s on the geometry.

Note: This can be slow for complex line string and polygons.

`Geometry.length: Number`

Calculates the length of a geometry in metres.

For line strings, that’s its length and for polygons, it’s its circumference.

`Geometry.simplify: Geometry`

Simplifies lines and polygons

`Geometry.simplify(tolerance: Number) -> Style`

Simplifies lines and polygons using the provided tolerance value. Default is 0.01.

`Geometry.transform(targetGeometry: String) -> Style`

Transforms the geometry into the provided target GeoJSON geometry.

Allowed values are: Feature, FeatureCollection, Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection, BoundingBox. Not all of these will make sense or work, depending on your data.

Note: “BoundingBox” is a special type that results in a polygon representing the bounding box.

## Color

Colours can be constructed using a CSS-like hex formats like `#fff`, `#ff80ee` or the `RGB/3` and `RGBA/4` functions. You can also reference a small number of built-in colours just by their names: `white`, `grey` (or `gray`), `black`, and `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `brown` and `indigo`.

### Global functions

`gradient(value: Number, mode: String, min: Number, max: Number) -> Color`

Creates a Color for the first number for a gradient mode between the provided start (third parameter) and end (forth parameter) values.

• `'alpha'`: Start is transparent, end is last color (i.e., requires a 5th parameter).
• `'rainbow'`: Start is red, then goes via orange, yellow, green, blue, and indigo to purple.
• `'traffic'`: Start is green, mid value is yellow, end is red.

You can provide a start value that is greater than the end to invert the gradient.

Examples:

``````gradient(-5, 'traffic', 0, 50) == green
gradient( 0, 'traffic', 0, 50) == green
gradient(25, 'traffic', 0, 50) == yellow
gradient(50, 'traffic', 0, 50) == red
gradient(99, 'traffic', 0, 50) == red
``````

`gradient(value: Number, mode: String, min: Number, max: Number, color: Color) -> Color`

Creates a Color for the first number for a gradient mode between the provided start (third parameter) and end (forth parameter) values, using the provided end Color.

• `'alpha'`: Start is transparent, end is last color.
• `'rainbow'`: Start is red, then goes via orange, yellow, green, blue, and indigo to purple. (Ignores last parameter.)
• `'traffic'`: Start is green, mid value is yellow, end is red. (Ignores last parameter.)

You can provide a start value that is greater than the end to invert the gradient.

Examples:

``````gradient(-5, 'alpha', 0, 50, red) == RGB(255, 0, 0, 0.0)
gradient( 0, 'alpha', 0, 50, red) == RGB(255, 0, 0, 0.0)
gradient(25, 'alpha', 0, 50, red) == RGB(255, 0, 0, 0.5)
gradient(50, 'alpha', 0, 50, red) == RGB(255, 0, 0, 1.0)
gradient(99, 'alpha', 0, 50, red) == RGB(255, 0, 0, 1.0)
``````

### Constructors

`Color(value: Any) -> Color`

Creates a Color for the provided value.

For a String starting with ‘#’, it’ll try to interpret that first as a hex-encoded color. Otherwise it’ll use a random colour which will be the same for same values, but not necessarily the same across restarts.

`RGB(red: Number, green: Number, blue: Number) -> Color`

Creates an red-green-blue colour where each number should be in a range of 0 to 255.

`RGBA(red: Number, green: Number, blue: Number, alpha: Number) -> Color`

Creates an colour like `RGB` with an additional alpha component.

### Functions

`Color.alpha(percentage: Number) -> Color`

Creates a colour updated with the provided alpha value (between 0 and 1)

## Style

A style defines how a location is displayed on the map, defining both a geometric shape and a colour.

A location might be saved to the collection as a single coordinate (i.e., with a specific latitude longitude) but you can create a formula that instead draws it as a line from some other point or as a circle. Or it might be saved on the collection as a polygon, but a formula can instead draw it as a pin (using a point centred in the polygon).

### Constants

`linePoints: Style`

Turns shapes into pins for every corners (with numbers representing their order in them)

`simplify: Style`

Use `geometry.simplified` instead

### Global functions

`circle(radius: Number) -> Style`

Show location as a circle annotation of the provided radius, which maintains the same radius as you zoom in and out.

`colored(Color) -> Style`

Applies the provided colour to each location in the collection

`colored(fill: Color, stroke: Color) -> Style`

Applies the provided Color to each item in the collection. The first Color will be used for the fill, the second Color will be applied as the stroke.

`fixedCircle(radius: Number) -> Style`

Show location as a circle of the provided radius, where the radius is a fixed actual distance in metres from the centre, i.e., it’ll appear smaller or larger as you zoom in and out.

`pin(letter: Any) -> Style`

Show location as a pin with the provided letter

`circle(radius: Number, Color) -> Style`

Use `circle(radius: Number).fill(Color)` instead

`line(origin: Position) -> Style`

Use `LineString(coordinate, origin)` instead

`line(origin: Position, Color) -> Style`

Use `line(Coordinate).fill(Color)` instead

`pin(letter: Any, cluster: Any) -> Style`

Use `pin(letter: Any).cluster(Any)` instead

`pin(letter: Any, Color, cluster: Any) -> Style`

Use `pin(letter: Any, cluster: Any).fill(Color)` instead

`simplify(tolerance: Number) -> Style`

Use `geometry.simplified(tolerance)` instead

`transform(geometry: String) -> Style`

Use `geometry.transform(targetGeometry)` instead

### Constructors

`Style(Geometry) -> Style`

Turns a GeoJSON geometry into a style, so that it can get colours applied.

By itself, this doesn’t do much, but it can then be combined with other functions on the Style, to colour it. For example, show the bounding box as green outlines by using:

``````Style(geometry.transform("BoundingBox"))
.stroke(green)
.fill(clear)
``````

### Functions

`Style.cluster(Any) -> Style`

Specifies a cluster identifier to control whether nearby locations can be merged into a single bubble when displaying it on the map.

`Style.fill(Color) -> Style`

Applies the provided Color to each location in the collection as the fill colour. For lines use `.stroke` instead.

`Style.stroke(Color) -> Style`

Applies the Color to each location in the collection as the stroke colour. Only has an impact on polygons, circles and lines.

## List

A list of any of the other types. Typically each element in the list should be of the same type as the others, but this is not enforced and lists of elements of mixed types are allowed.

### Functions

`List.contains(needle: Any) -> Boolean`

Returns whether this list contains the provided value.

## Switcher

A helper type for building `switch` statements.

### Global functions

`switch(value: Any, …) -> Switcher`

The `switch` function checks specified expressions and returns a value based on the first condition that matches. Note: If no condition matches and the `default/1` value is set, then that’s returned, otherwise `nil` is returned.

There are two ways to use this:

1. `switch/1` with a single parameter (the value), followed by different `case/2` conditions, and, optionally, a `default/1` value.
2. `switch/3+` with pairs of conditions and, optionally, a final default value.

Example (for more see `case/2`):

``````switch(distance)
.case(..<2000, "🚶‍♂️")
.case(..<10000, "🚴‍♀️")
.default("🚌")
``````

### Functions

`Switcher.case(pattern: Any, result: Any) -> Switcher`

A condition for a `switch/1` function, where the first argument is the pattern to match against the switch’s value and the second argument is the result to return, if the pattern matches.

What patterns you can use depends on the type of the switch’s value:

• Boolean: `true` or `false`
• Number: A Number, or ranges, e.g., `0...5`, `..<500`, `10...`, `..<(150km)`
• String: A String, or a list of them.

`Switcher.default(result: Any) -> Any`

The value to return from a `switch/1` function, when no `case/2` condition matches.

## Any

A metatype that says that any type can be used.

### Global functions

`has(Any) -> Boolean`

Returns whether the location has the provided value. Same as checking `key == nil`.

`value(key: String) -> Any`

Returns the value for the provided key.

`value(key: String, default: Any) -> Any`

Returns the value for the provided key or, if it that key does not exist, the provided default.

## Meta functions

When viewing multiple collections at once, you can also use these ‘Meta functions’ when organising the view. You can use this to provide information about the collection that each item is in, or you can use it to do cross-collection lookup.

### Constants

`collectionColor: Color`

Color of the collection that contains the location.

`collectionLetter: String`

Letter/emoji of the collection that contains the location.

`collectionName: String`

Name of the collection that contains the location.

### Global functions

`container(collection: String) -> Any`

Finds the first item in location named with the first parameter.

`container(collection: String, key: String) -> Any`

Finds the first item in location named with the first parameter, and returns the field with the same key as the second parameter.