diff --git a/mapbox-gl-js b/mapbox-gl-js index b01859294fe..6b96d69ab54 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit b01859294fec88111601028a539e45be1f088666 +Subproject commit 6b96d69ab54b149db1f6ef06daed37379ac07447 diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java index 05c67018a4e..fa4a802e135 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java @@ -81,7 +81,6 @@ * ) * } * - * */ public class Expression { @@ -1061,6 +1060,7 @@ public static Expression coalesce(@NonNull Expression... input) { * ); * } * + * * @return expression * @see Style specification */ @@ -1081,6 +1081,7 @@ public static Expression properties() { * ); * } * + * * @return expression * @see Style specification */ @@ -2294,6 +2295,185 @@ public static Expression max(@Size(min = 1) Number... numbers) { return max(numberExpression); } + /** + * Rounds the input to the nearest integer. + * Halfway values are rounded away from zero. + * For example `[\"round\", -1.5]` evaluates to -2. + *

+ * Example usage: + *

+ *
+   * {@code
+   * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+   * circleLayer.setProperties(
+   *     circleRadius(round(pi()))
+   * );
+   * }
+   * 
+ * + * @param expression number expression to round + * @return expression + * @see Style specification + */ + public static Expression round(Expression expression) { + return new Expression("round", expression); + } + + /** + * Rounds the input to the nearest integer. + * Halfway values are rounded away from zero. + * For example `[\"round\", -1.5]` evaluates to -2. + *

+ * Example usage: + *

+ *
+   * {@code
+   * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+   * circleLayer.setProperties(
+   *     circleRadius(round(3.14159265359f))
+   * );
+   * }
+   * 
+ * + * @param number number to round + * @return expression + * @see Style specification + */ + public static Expression round(Number number) { + return round(literal(number)); + } + + /** + * Returns the absolute value of the input. + *

+ * Example usage: + *

+ *
+   * {@code
+   * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+   * circleLayer.setProperties(
+   *     circleRadius(abs(subtract(pi())))
+   * );
+   * }
+   * 
+ * + * @param expression number expression to get absolute value from + * @return expression + * @see Style specification + */ + public static Expression abs(Expression expression) { + return new Expression("abs", expression); + } + + /** + * Returns the absolute value of the input. + *

+ * Example usage: + *

+ *
+   * {@code
+   * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+   * circleLayer.setProperties(
+   *     circleRadius(abs(-3.14159265359f))
+   * );
+   * }
+   * 
+ * + * @param number number to get absolute value from + * @return expression + * @see Style specification + */ + public static Expression abs(Number number) { + return abs(literal(number)); + } + + /** + * Returns the smallest integer that is greater than or equal to the input. + *

+ * Example usage: + *

+ *
+   * {@code
+   * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+   * circleLayer.setProperties(
+   *     circleRadius(ceil(pi()))
+   * );
+   * }
+   * 
+ * + * @param expression number expression to get value from + * @return expression + * @see Style specification + */ + public static Expression ceil(Expression expression) { + return new Expression("ceil", expression); + } + + /** + * Returns the smallest integer that is greater than or equal to the input. + *

+ * Example usage: + *

+ *
+   * {@code
+   * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+   * circleLayer.setProperties(
+   *     circleRadius(ceil(3.14159265359))
+   * );
+   * }
+   * 
+ * @param number number to get value from + * @return expression + * @see Style specification + */ + public static Expression ceil(Number number) { + return ceil(literal(number)); + } + + /** + * Returns the largest integer that is less than or equal to the input. + *

+ * Example usage: + *

+ *
+   * {@code
+   * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+   * circleLayer.setProperties(
+   *     circleRadius(floor(pi()))
+   * );
+   * }
+   * 
+ * + * @param expression number expression to get value from + * @return expression + * @see Style specification + */ + public static Expression floor(Expression expression) { + return new Expression("floor", expression); + } + + /** + * Returns the largest integer that is less than or equal to the input. + *

+ * Example usage: + *

+ *
+   * {@code
+   * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+   * circleLayer.setProperties(
+   *     circleRadius(floor(pi()))
+   * );
+   * }
+   * 
+ * + * @param number number to get value from + * @return expression + * @see Style specification + */ + public static Expression floor(Number number) { + return floor(literal(number)); + } + /** * Returns the input string converted to uppercase. *

@@ -3128,6 +3308,7 @@ public static Interpolator exponential(@NonNull Number base) { * ); * } * + * * @param expression base number expression * @return expression * @see Style specification diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java index 66d78a40e59..4289deeda3b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java @@ -1856,7 +1856,7 @@ public static PropertyValue iconTextFitPadding(Expression value) { } /** - * Name of image in sprite to use for drawing an image background. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators. + * Name of image in sprite to use for drawing an image background. * * @param value a String value * @return property wrapper around String @@ -1866,7 +1866,7 @@ public static PropertyValue iconImage(String value) { } /** - * Name of image in sprite to use for drawing an image background. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators. + * Name of image in sprite to use for drawing an image background. * * @param value a String value * @return property wrapper around String @@ -2036,7 +2036,7 @@ public static PropertyValue textRotationAlignment(Expression value) } /** - * Value to use for a text label. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators. + * Value to use for a text label. * * @param value a String value * @return property wrapper around String @@ -2046,7 +2046,7 @@ public static PropertyValue textField(String value) { } /** - * Value to use for a text label. Within literal values and zoom functions, property names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named property. Expressions and property functions do not support this syntax; for equivalent functionality in expressions, use the [`concat`](#expressions-concat) and [`get`](#expressions-get) operators. + * Value to use for a text label. * * @param value a String value * @return property wrapper around String diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java index 5694242a0a2..45833e8556b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java @@ -6,6 +6,7 @@ import java.util.Arrays; +import static com.mapbox.mapboxsdk.style.expressions.Expression.abs; import static com.mapbox.mapboxsdk.style.expressions.Expression.acos; import static com.mapbox.mapboxsdk.style.expressions.Expression.all; import static com.mapbox.mapboxsdk.style.expressions.Expression.any; @@ -14,6 +15,7 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.at; import static com.mapbox.mapboxsdk.style.expressions.Expression.atan; import static com.mapbox.mapboxsdk.style.expressions.Expression.bool; +import static com.mapbox.mapboxsdk.style.expressions.Expression.ceil; import static com.mapbox.mapboxsdk.style.expressions.Expression.coalesce; import static com.mapbox.mapboxsdk.style.expressions.Expression.color; import static com.mapbox.mapboxsdk.style.expressions.Expression.concat; @@ -24,6 +26,7 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.e; import static com.mapbox.mapboxsdk.style.expressions.Expression.eq; import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential; +import static com.mapbox.mapboxsdk.style.expressions.Expression.floor; import static com.mapbox.mapboxsdk.style.expressions.Expression.geometryType; import static com.mapbox.mapboxsdk.style.expressions.Expression.get; import static com.mapbox.mapboxsdk.style.expressions.Expression.gt; @@ -56,6 +59,7 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.properties; import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb; import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba; +import static com.mapbox.mapboxsdk.style.expressions.Expression.round; import static com.mapbox.mapboxsdk.style.expressions.Expression.sin; import static com.mapbox.mapboxsdk.style.expressions.Expression.sqrt; import static com.mapbox.mapboxsdk.style.expressions.Expression.step; @@ -1105,4 +1109,60 @@ public void testThrowIllegalArgumentExceptionForPropertyValueLiteral() { ); expression.toArray(); } + + @Test + public void testRound() { + Object[] expected = new Object[] {"round", 2.2f}; + Object[] actual = round(2.2f).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testRoundLiteral() { + Object[] expected = new Object[] {"round", 2.2f}; + Object[] actual = round(literal(2.2f)).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testAbs() { + Object[] expected = new Object[] {"abs", -2.2f}; + Object[] actual = abs(-2.2f).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testAbsLiteral() { + Object[] expected = new Object[] {"abs", -2.2f}; + Object[] actual = abs(literal(-2.2f)).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testCeil() { + Object[] expected = new Object[] {"ceil", 2.2f}; + Object[] actual = ceil(2.2f).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testCeilLiteral() { + Object[] expected = new Object[] {"ceil", 2.2f}; + Object[] actual = ceil(literal(2.2f)).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testFloor() { + Object[] expected = new Object[] {"floor", 2.2f}; + Object[] actual = floor(2.2f).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testFloorLiteral() { + Object[] expected = new Object[] {"floor", 2.2f}; + Object[] actual = floor(literal(2.2f)).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } } \ No newline at end of file diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index d61b2921487..857d03d6e7c 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -379,11 +379,14 @@ In style specification | Method, function, or predicate type | Format string syn `%` | `modulus:by:` | `^` | `raise:toPower:` | `2 ** 2` `+` | `add:to:` | `1 + 2` +`abs` | `abs:` | `abs(-1)` `acos` | | `asin` | | `atan` | | +`ceil` | `ceiling:` | `ceiling(0.99999)` `cos` | | `e` | | `%@` representing `NSNumber` containing `M_E` +`floor` | `floor:` | `floor(-0.99999)` `ln` | `ln:` | `ln(2)` `ln2` | | `%@` representing `NSNumber` containing `M_LN2` `log10` | `log:` | `log(1)` @@ -391,6 +394,7 @@ In style specification | Method, function, or predicate type | Format string syn `max` | `max:` | `max({1, 2, 2, 3, 4, 7, 9})` `min` | `min:` | `min({1, 2, 2, 3, 4, 7, 9})` `pi` | | `%@` representing `NSNumber` containing `M_PI` +`round` | `mgl_round:` | `mgl_round(1.5)` `sin` | | `sqrt` | `sqrt:` | `sqrt(2)` `tan` | | diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md index 13204b09a6b..1b8714811db 100644 --- a/platform/darwin/docs/guides/Predicates and Expressions.md +++ b/platform/darwin/docs/guides/Predicates and Expressions.md @@ -196,6 +196,7 @@ Initializer parameter | Format string syntax | Description `mgl_interpolate:withCurveType:parameters:stops:` | `mgl_interpolate:withCurveType:parameters:stops:(x, 'linear', nil, %@)` | Produces continuous, smooth results by interpolating between pairs of input and output values (“stops”). Compared to the `mgl_interpolateWithCurveType:parameters:stops:` custom function, the input expression (that function’s target) is instead passed in as the first argument to this function. `mgl_step:from:stops:` | `mgl_step:from:stops:(x, 11, %@)` | Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of input and output values ("stops"). Compared to the `mgl_stepWithMinimum:stops:` custom function, the input expression (that function’s target) is instead passed in as the first argument to this function. `mgl_join:` | `mgl_join({'Old', 'MacDonald'})` | Returns the result of concatenating together all the elements of an array in order. Compared to the `stringByAppendingString:` custom function, this function takes only one argument, which is an aggregate expression containing the strings to concatenate. +`mgl_round:` | `mgl_round(1.5)` | Returns the number rounded to the nearest integer. If the number is halfway between two integers, this function rounds it away from zero. `mgl_coalesce:` | `mgl_coalesce({x, y, z})` | Returns the first non-`nil` value from an array of expressions. `MGL_LET` | `MGL_LET('age', uppercase('old'), 'name', uppercase('MacDonald'), mgl_join({$age, $name}))` | Any number of variable names interspersed with their assigned `NSExpression` values, followed by an `NSExpression` that may contain references to those variables. Compared to the `mgl_expressionWithContext:` custom function, this function takes the variable names and values inline before the expression that contains references to those variables. `MGL_MATCH` | `MGL_MATCH(x, 0, 'zero match', 1, 'one match', 'two match', 'default')` | Evaluates the first expression and returns the value that matches the initial condition. After the first expression condition a pair of matching/return value should be added and a default value. diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index 8c82c84c7d6..6a61dd9abb4 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -58,6 +58,7 @@ + (void)installFunctions { // Install method-like functions, taking the number of arguments implied by // the selector name. INSTALL_METHOD(mgl_join:); + INSTALL_METHOD(mgl_round:); INSTALL_METHOD(mgl_interpolate:withCurveType:parameters:stops:); INSTALL_METHOD(mgl_step:from:stops:); INSTALL_METHOD(mgl_coalesce:); @@ -83,6 +84,14 @@ - (NSString *)mgl_join:(NSArray *)components { return [components componentsJoinedByString:@""]; } +/** + Rounds the given number to the nearest integer. If the number is halfway + between two integers, this method rounds it away from zero. + */ +- (NSNumber *)mgl_round:(NSNumber *)number { + return @(round(number.doubleValue)); +} + /** A placeholder for a method that evaluates an interpolation expression. */ @@ -487,6 +496,10 @@ + (instancetype)mgl_expressionWithJSONObject:(id)object { @"sqrt": @"sqrt:", @"log10": @"log:", @"ln": @"ln:", + @"abs": @"abs:", + @"round": @"mgl_round:", + @"floor": @"floor:", + @"ceil": @"ceiling:", @"^": @"raise:toPower:", @"upcase": @"uppercase:", @"downcase": @"lowercase:", @@ -720,9 +733,13 @@ - (id)mgl_jsonExpressionObject { @"log:": @"log10", @"ln:": @"ln", @"raise:toPower:": @"^", + @"ceiling:": @"ceil", + @"abs:": @"abs", + @"floor:": @"floor", @"uppercase:": @"upcase", @"lowercase:": @"downcase", @"length:": @"length", + @"mgl_round:": @"round", // Vararg aftermarket expressions need to be declared with an explicit and implicit first argument. @"MGL_LET": @"let", @"MGL_LET:": @"let", @@ -826,18 +843,9 @@ - (id)mgl_jsonExpressionObject { return [@[@"max"] arrayByAddingObjectsFromArray:arguments]; } else if ([function isEqualToString:@"exp:"]) { return [NSExpression expressionForFunction:@"raise:toPower:" arguments:@[@(M_E), self.arguments.firstObject]].mgl_jsonExpressionObject; - } else if ([function isEqualToString:@"ceiling:"]) { - return [NSExpression expressionWithFormat:@"trunc:(%@) + TERNARY(modulus:by:(%@, 1) > 0, 1, 0)", - self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject; } else if ([function isEqualToString:@"trunc:"]) { return [NSExpression expressionWithFormat:@"%@ - modulus:by:(%@, 1)", self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject; - } else if ([function isEqualToString:@"abs:"]) { - return [NSExpression expressionWithFormat:@"%@ * TERNARY(%@ > 0, 1, -1)", - self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject; - } else if ([function isEqualToString:@"floor:"]) { - return [NSExpression expressionWithFormat:@"trunc:(%@) - TERNARY(modulus:by:(%@, 1) < 0, 1, 0)", - self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject; } else if ([function isEqualToString:@"mgl_join:"]) { NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"]; return [@[@"concat"] arrayByAddingObjectsFromArray:arguments]; diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index 579d2505f2b..236c8d1ad9b 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -393,29 +393,25 @@ - (void)testArithmeticExpressionObject { } { NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@1.5)]]; - NSArray *jsonTruncation = @[@"-", @1.5, @[@"%", @1.5, @1]]; - NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @1.5, @1], @0], @1, @0]]; + NSArray *jsonExpression = @[@"ceil", @1.5]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); } { NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@-1.5)]]; - NSArray *jsonTruncation = @[@"-", @-1.5, @[@"%", @-1.5, @1]]; - NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @-1.5, @1], @0], @1, @0]]; + NSArray *jsonExpression = @[@"ceil", @-1.5]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-1); } { NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@2)]]; - NSArray *jsonTruncation = @[@"-", @2, @[@"%", @2, @1]]; - NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @2, @1], @0], @1, @0]]; + NSArray *jsonExpression = @[@"ceil", @2]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); } { NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@-2)]]; - NSArray *jsonTruncation = @[@"-", @-2, @[@"%", @-2, @1]]; - NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @-2, @1], @0], @1, @0]]; + NSArray *jsonExpression = @[@"ceil", @-2]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2); } @@ -433,44 +429,64 @@ - (void)testArithmeticExpressionObject { } { NSExpression *expression = [NSExpression expressionForFunction:@"abs:" arguments:@[MGLConstantExpression(@2)]]; - NSArray *jsonExpression = @[@"*", @2, @[@"case", @[@">", @2, @0], @1, @-1]]; + NSArray *jsonExpression = @[@"abs", @2]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); } { NSExpression *expression = [NSExpression expressionForFunction:@"abs:" arguments:@[MGLConstantExpression(@-2)]]; - NSArray *jsonExpression = @[@"*", @-2, @[@"case", @[@">", @-2, @0], @1, @-1]]; + NSArray *jsonExpression = @[@"abs", @-2]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); } { NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@1.5)]]; - NSArray *jsonTruncation = @[@"-", @1.5, @[@"%", @1.5, @1]]; - NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @1.5, @1], @0], @1, @0]]; + NSArray *jsonExpression = @[@"floor", @1.5]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1); } { NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@-1.5)]]; - NSArray *jsonTruncation = @[@"-", @-1.5, @[@"%", @-1.5, @1]]; - NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @-1.5, @1], @0], @1, @0]]; + NSArray *jsonExpression = @[@"floor", @-1.5]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2); } { NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@2)]]; - NSArray *jsonTruncation = @[@"-", @2, @[@"%", @2, @1]]; - NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @2, @1], @0], @1, @0]]; + NSArray *jsonExpression = @[@"floor", @2]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); } { NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@-2)]]; - NSArray *jsonTruncation = @[@"-", @-2, @[@"%", @-2, @1]]; - NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @-2, @1], @0], @1, @0]]; + NSArray *jsonExpression = @[@"floor", @-2]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2); } + { + NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@1.5)]]; + NSArray *jsonExpression = @[@"round", @1.5]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@-1.5)]]; + NSArray *jsonExpression = @[@"round", @-1.5]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@2.5)]]; + NSArray *jsonExpression = @[@"round", @2.5]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @3); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@-2.5)]]; + NSArray *jsonExpression = @[@"round", @-2.5]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-3); + } } - (void)testTrigonometricExpressionObject { diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 6b8839ecbe8..87da59162e5 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -364,11 +364,14 @@ In style specification | Method, function, or predicate type | Format string syn `%` | `modulus:by:` | `^` | `raise:toPower:` | `2 ** 2` `+` | `add:to:` | `1 + 2` +`abs` | `abs:` | `abs(-1)` `acos` | | `asin` | | `atan` | | +`ceil` | `ceiling:` | `ceiling(0.99999)` `cos` | | `e` | | `%@` representing `NSNumber` containing `M_E` +`floor` | `floor:` | `floor(-0.99999)` `ln` | `ln:` | `ln(2)` `ln2` | | `%@` representing `NSNumber` containing `M_LN2` `log10` | `log:` | `log(1)` @@ -376,6 +379,7 @@ In style specification | Method, function, or predicate type | Format string syn `max` | `max:` | `max({1, 2, 2, 3, 4, 7, 9})` `min` | `min:` | `min({1, 2, 2, 3, 4, 7, 9})` `pi` | | `%@` representing `NSNumber` containing `M_PI` +`round` | `mgl_round:` | `mgl_round(1.5)` `sin` | | `sqrt` | `sqrt:` | `sqrt(2)` `tan` | | diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 8bd2e04d2b5..e90858c6649 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -357,11 +357,14 @@ In style specification | Method, function, or predicate type | Format string syn `%` | `modulus:by:` | `^` | `raise:toPower:` | `2 ** 2` `+` | `add:to:` | `1 + 2` +`abs` | `abs:` | `abs(-1)` `acos` | | `asin` | | `atan` | | +`ceil` | `ceiling:` | `ceiling(0.99999)` `cos` | | `e` | | `%@` representing `NSNumber` containing `M_E` +`floor` | `floor:` | `floor(-0.99999)` `ln` | `ln:` | `ln(2)` `ln2` | | `%@` representing `NSNumber` containing `M_LN2` `log10` | `log:` | `log(1)` @@ -369,6 +372,7 @@ In style specification | Method, function, or predicate type | Format string syn `max` | `max:` | `max({1, 2, 2, 3, 4, 7, 9})` `min` | `min:` | `min({1, 2, 2, 3, 4, 7, 9})` `pi` | | `%@` representing `NSNumber` containing `M_PI` +`round` | `mgl_round:` | `mgl_round(1.5)` `sin` | | `sqrt` | `sqrt:` | `sqrt(2)` `tan` | | diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index 77b294b3807..c36ffa33e35 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace mbgl { namespace style { @@ -362,6 +363,11 @@ std::unordered_map initiali return result; }); + define("round", [](double x) -> Result { return std::round(x); }); + define("floor", [](double x) -> Result { return std::floor(x); }); + define("ceil", [](double x) -> Result { return std::ceil(x); }); + define("abs", [](double x) -> Result { return std::abs(x); }); + define(">", [](double lhs, double rhs) -> Result { return lhs > rhs; }); define(">", [](const std::string& lhs, const std::string& rhs) -> Result { return lhs > rhs; }); define(">=", [](double lhs, double rhs) -> Result { return lhs >= rhs; });