summaryrefslogtreecommitdiff
path: root/platform/darwin/src
diff options
context:
space:
mode:
Diffstat (limited to 'platform/darwin/src')
-rw-r--r--platform/darwin/src/MGLShapeSource.h32
-rw-r--r--platform/darwin/src/MGLShapeSource.mm53
-rw-r--r--platform/darwin/src/MGLStyleValue.mm13
-rw-r--r--platform/darwin/src/MGLStyleValue_Private.h5
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.h7
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm43
6 files changed, 146 insertions, 7 deletions
diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h
index a57b963c63..675c219300 100644
--- a/platform/darwin/src/MGLShapeSource.h
+++ b/platform/darwin/src/MGLShapeSource.h
@@ -42,6 +42,38 @@ FOUNDATION_EXTERN MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClus
FOUNDATION_EXTERN MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
/**
+ An `NSDictionary` object where the key is an `NSString`. The dictionary key will
+ be the feature attribute key. The resulting attribute value is
+ aggregated from the clustered points. The dictionary value is an `NSArray`
+ consisting of two `NSExpression` objects.
+
+ The first object determines how the attribute values are accumulated from the
+ cluster points. It is an `NSExpression` with an expression function that accepts
+ two or more arguments, such as `sum` or `max`. The arguments should be
+ `featureAccumulated` and the previously defined feature attribute key. The
+ resulting value is assigned to the specified attribute key.
+
+ The second `NSExpression` in the array determines which
+ attribute values are accessed from individual features within a cluster.
+
+ ```swift
+ let firstExpression = NSExpression(format: "sum:({$featureAccumulated, sumValue})")
+ let secondExpression = NSExpression(forKeyPath: "magnitude")
+ let clusterPropertiesDictionary = ["sumValue" : [firstExpression, secondExpression]]
+
+ let options : [MGLShapeSourceOption : Any] = [.clustered : true,
+ .clusterProperties: clusterPropertiesDictionary]
+ ```
+
+ This option corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-clusterProperties"><code>clusterProperties</code></a>
+ source property in the Mapbox Style Specification.
+
+ This option only affects point features within an `MGLShapeSource` object; it
+ is ignored when creating an `MGLComputedShapeSource` object.
+ */
+FOUNDATION_EXTERN MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterProperties;
+/**
An `NSNumber` object containing an integer; specifies the maximum zoom level at
which to cluster points if clustering is enabled. Defaults to one zoom level
less than the value of `MGLShapeSourceOptionMaximumZoomLevel` so that, at the
diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm
index 3628a0eb74..3820fe9d60 100644
--- a/platform/darwin/src/MGLShapeSource.mm
+++ b/platform/darwin/src/MGLShapeSource.mm
@@ -3,6 +3,7 @@
#import "MGLLoggingConfiguration_Private.h"
#import "MGLStyle_Private.h"
+#import "MGLStyleValue_Private.h"
#import "MGLMapView_Private.h"
#import "MGLSource_Private.h"
#import "MGLFeature_Private.h"
@@ -19,6 +20,7 @@
const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer";
const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius = @"MGLShapeSourceOptionClusterRadius";
const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
+const MGLShapeSourceOption MGLShapeSourceOptionClusterProperties = @"MGLShapeSourceOptionClusterProperties";
const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSourceOptionMaximumZoomLevel";
const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering";
const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel";
@@ -84,6 +86,57 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShap
geoJSONOptions.cluster = value.boolValue;
}
+ if (NSDictionary *value = options[MGLShapeSourceOptionClusterProperties]) {
+ if (![value isKindOfClass:[NSDictionary<NSString *, NSArray *> class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClusterProperties must be an NSDictionary with an NSString as a key and an array containing two NSExpression objects as a value."];
+ }
+
+ NSEnumerator *stringEnumerator = [value keyEnumerator];
+ NSString *key;
+
+ while (key = [stringEnumerator nextObject]) {
+ NSArray *expressionsArray = value[key];
+ if (![expressionsArray isKindOfClass:[NSArray class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClusterProperties dictionary member value must be an array containing two objects."];
+ }
+ // Check that the array has 2 values. One should be a the reduce expression and one should be the map expression.
+ if ([expressionsArray count] != 2) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClusterProperties member value requires array of two objects."];
+ }
+
+ // reduceExpression should be a valid NSExpression
+ NSExpression *reduceExpression = expressionsArray[0];
+ if (![reduceExpression isKindOfClass:[NSExpression class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClusterProperties array value requires two expression objects."];
+ }
+ auto reduce = MGLClusterPropertyFromNSExpression(reduceExpression);
+ if (!reduce) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Failed to convert MGLShapeSourceOptionClusterProperties reduce expression."];
+ }
+
+ // mapExpression should be a valid NSExpression
+ NSExpression *mapExpression = expressionsArray[1];
+ if (![mapExpression isKindOfClass:[NSExpression class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClusterProperties member value must contain a valid NSExpression."];
+ }
+ auto map = MGLClusterPropertyFromNSExpression(mapExpression);
+ if (!map) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Failed to convert MGLShapeSourceOptionClusterProperties map expression."];
+ }
+
+ std::string keyString = std::string([key UTF8String]);
+
+ geoJSONOptions.clusterProperties.emplace(keyString, std::make_pair(std::move(map), std::move(reduce)));
+ }
+ }
+
if (NSNumber *value = options[MGLShapeSourceOptionLineDistanceMetrics]) {
if (![value isKindOfClass:[NSNumber class]]) {
[NSException raise:NSInvalidArgumentException
diff --git a/platform/darwin/src/MGLStyleValue.mm b/platform/darwin/src/MGLStyleValue.mm
index 5103b5f5cf..01ad108d7f 100644
--- a/platform/darwin/src/MGLStyleValue.mm
+++ b/platform/darwin/src/MGLStyleValue.mm
@@ -44,3 +44,16 @@ id MGLJSONObjectFromMBGLValue(const mbgl::Value &value) {
id MGLJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mbglExpression) {
return MGLJSONObjectFromMBGLValue(mbglExpression.serialize());
}
+
+
+std::unique_ptr<mbgl::style::expression::Expression> MGLClusterPropertyFromNSExpression(NSExpression *expression) {
+ if (!expression) {
+ return nullptr;
+ }
+
+ NSArray *jsonExpression = expression.mgl_jsonExpressionObject;
+
+ auto expr = mbgl::style::expression::dsl::createExpression(mbgl::style::conversion::makeConvertible(jsonExpression));
+
+ return expr;
+}
diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h
index 376bf5e73b..82ce232c6b 100644
--- a/platform/darwin/src/MGLStyleValue_Private.h
+++ b/platform/darwin/src/MGLStyleValue_Private.h
@@ -12,12 +12,15 @@
#include <mbgl/style/conversion/color_ramp_property_value.hpp>
#include <mbgl/style/conversion/property_value.hpp>
#include <mbgl/style/conversion/position.hpp>
+#include <mbgl/style/expression/dsl.hpp>
#import <mbgl/style/transition_options.hpp>
#import <mbgl/style/types.hpp>
#import <mbgl/util/enum.hpp>
#include <mbgl/util/interpolate.hpp>
+#include <memory>
+
#if TARGET_OS_IPHONE
#import "UIColor+MGLAdditions.h"
#else
@@ -45,6 +48,8 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
return options;
}
+std::unique_ptr<mbgl::style::expression::Expression> MGLClusterPropertyFromNSExpression(NSExpression *expression);
+
id MGLJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mbglExpression);
template <typename MBGLType, typename ObjCType, typename MBGLElement = MBGLType, typename ObjCEnum = ObjCType>
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.h b/platform/darwin/src/NSExpression+MGLAdditions.h
index 2a33367e9c..2109310e69 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLAdditions.h
@@ -86,6 +86,13 @@ FOUNDATION_EXTERN MGL_EXPORT const MGLExpressionInterpolationMode MGLExpressionI
/**
`NSExpression` variable that corresponds to the
+ <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/#accumulated"><code>id</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *featureAccumulatedVariableExpression;
+
+/**
+ `NSExpression` variable that corresponds to the
<a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-properties"><code>properties</code></a>
expression operator in the Mapbox Style Specification.
*/
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index 2ca4e0ed88..f139b86a88 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -553,6 +553,10 @@ const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier =
return [NSExpression expressionForVariable:@"lineProgress"];
}
++ (NSExpression *)featureAccumulatedVariableExpression {
+ return [NSExpression expressionForVariable:@"featureAccumulated"];
+}
+
+ (NSExpression *)geometryTypeVariableExpression {
return [NSExpression expressionForVariable:@"geometryType"];
}
@@ -648,7 +652,6 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
@"let": @"MGL_LET",
};
});
-
if (!object || object == [NSNull null]) {
return [NSExpression expressionForConstantValue:nil];
}
@@ -667,11 +670,10 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
}];
return [NSExpression expressionForConstantValue:dictionary];
}
-
if ([object isKindOfClass:[NSArray class]]) {
NSArray *array = (NSArray *)object;
NSString *op = array.firstObject;
-
+
if (![op isKindOfClass:[NSString class]]) {
NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(array);
return [NSExpression expressionForFunction:@"MGL_FUNCTION" arguments:subexpressions];
@@ -839,6 +841,8 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
return NSExpression.heatmapDensityVariableExpression;
} else if ([op isEqualToString:@"line-progress"]) {
return NSExpression.lineProgressVariableExpression;
+ } else if ([op isEqualToString:@"accumulated"]) {
+ return NSExpression.featureAccumulatedVariableExpression;
} else if ([op isEqualToString:@"geometry-type"]) {
return NSExpression.geometryTypeVariableExpression;
} else if ([op isEqualToString:@"id"]) {
@@ -961,6 +965,9 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
if ([self.variable isEqualToString:@"zoomLevel"]) {
return @[@"zoom"];
}
+ if ([self.variable isEqualToString:@"featureAccumulated"]) {
+ return @[@"accumulated"];
+ }
if ([self.variable isEqualToString:@"geometryType"]) {
return @[@"geometry-type"];
}
@@ -1046,6 +1053,8 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
case NSFunctionExpressionType: {
NSString *function = self.function;
+
+ BOOL hasCollectionProperty = !( ! [self.arguments.firstObject isKindOfClass: [NSExpression class]] || self.arguments.firstObject.expressionType != NSAggregateExpressionType || self.arguments.firstObject.expressionType == NSSubqueryExpressionType);
NSString *op = MGLExpressionOperatorsByFunctionNames[function];
if (op) {
NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
@@ -1057,16 +1066,31 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
NSExpression *count = [NSExpression expressionForFunction:@"count:" arguments:self.arguments];
return [NSExpression expressionForFunction:@"divide:by:" arguments:@[sum, count]].mgl_jsonExpressionObject;
} else if ([function isEqualToString:@"sum:"]) {
- NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ NSArray *arguments;
+ if (hasCollectionProperty) {
+ arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ } else {
+ arguments = [self.arguments valueForKeyPath:@"mgl_jsonExpressionObject"];
+ }
return [@[@"+"] arrayByAddingObjectsFromArray:arguments];
} else if ([function isEqualToString:@"count:"]) {
NSArray *arguments = self.arguments.firstObject.mgl_jsonExpressionObject;
return @[@"length", arguments];
} else if ([function isEqualToString:@"min:"]) {
- NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ NSArray *arguments;
+ if (!hasCollectionProperty) {
+ arguments = [self.arguments valueForKeyPath:@"mgl_jsonExpressionObject"];
+ } else {
+ arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ }
return [@[@"min"] arrayByAddingObjectsFromArray:arguments];
} else if ([function isEqualToString:@"max:"]) {
- NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ NSArray *arguments;
+ if (!hasCollectionProperty) {
+ arguments = [self.arguments valueForKeyPath:@"mgl_jsonExpressionObject"];
+ } else {
+ arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ }
return [@[@"max"] arrayByAddingObjectsFromArray:arguments];
} else if ([function isEqualToString:@"exp:"]) {
return [NSExpression expressionForFunction:@"raise:toPower:" arguments:@[@(M_E), self.arguments.firstObject]].mgl_jsonExpressionObject;
@@ -1074,7 +1098,12 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
return [NSExpression expressionWithFormat:@"%@ - modulus:by:(%@, 1)",
self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject;
} else if ([function isEqualToString:@"mgl_join:"]) {
- NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ NSArray *arguments;
+ if (!hasCollectionProperty) {
+ arguments = [self.arguments valueForKeyPath:@"mgl_jsonExpressionObject"];
+ } else {
+ arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ }
return [@[@"concat"] arrayByAddingObjectsFromArray:arguments];
} else if ([function isEqualToString:@"stringByAppendingString:"]) {
NSArray *arguments = self.arguments.mgl_jsonExpressionObject;