summaryrefslogtreecommitdiff
path: root/platform/darwin
diff options
context:
space:
mode:
Diffstat (limited to 'platform/darwin')
-rwxr-xr-xplatform/darwin/scripts/generate-style-code.js9
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.mm4
-rw-r--r--platform/darwin/src/MGLFeature.mm18
-rw-r--r--platform/darwin/src/MGLFeature_Private.h15
-rw-r--r--platform/darwin/src/MGLImageSource.mm7
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.h25
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.mm66
-rw-r--r--platform/darwin/src/MGLMapSnapshotter_Private.h14
-rw-r--r--platform/darwin/src/MGLRasterTileSource.mm7
-rw-r--r--platform/darwin/src/MGLSDKMetricsManager.m28
-rw-r--r--platform/darwin/src/MGLShapeSource.h32
-rw-r--r--platform/darwin/src/MGLShapeSource.mm61
-rw-r--r--platform/darwin/src/MGLStyleValue.mm13
-rw-r--r--platform/darwin/src/MGLStyleValue_Private.h5
-rw-r--r--platform/darwin/src/MGLVectorTileSource.mm7
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.h7
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm43
-rw-r--r--platform/darwin/src/collator.mm9
-rw-r--r--platform/darwin/src/number_format.mm36
-rw-r--r--platform/darwin/src/string_nsstring.mm31
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift34
-rw-r--r--platform/darwin/test/MGLExpressionTests.mm55
-rw-r--r--platform/darwin/test/MGLShapeSourceTests.mm5
23 files changed, 459 insertions, 72 deletions
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 38066c9f43..75dbdf367c 100755
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -159,6 +159,7 @@ global.objCTestValue = function (property, layerType, arraysAsStructs, indent) {
`@"'${_.startCase(propertyName)}'"` :
`@"${_.startCase(propertyName)}"`;
case 'string':
+ case 'image': // TODO: replace once we implement image expressions
return `@"'${_.startCase(propertyName)}'"`;
case 'enum':
return `@"'${_.last(_.keys(property.values))}'"`;
@@ -208,6 +209,7 @@ global.mbglTestValue = function (property, layerType) {
return '1.0';
case 'formatted':
case 'string':
+ case 'image': // TODO: replace once we implement image expressions
return `"${_.startCase(propertyName)}"`;
case 'enum': {
let type = camelize(originalPropertyName(property));
@@ -294,6 +296,7 @@ global.testHelperMessage = function (property, layerType, isFunction) {
return 'testNumber' + fnSuffix;
case 'formatted':
case 'string':
+ case 'image': // TODO: replace once we implement image expressions
return 'testString' + fnSuffix;
case 'enum':
let objCType = global.objCType(layerType, property.name);
@@ -474,6 +477,7 @@ global.describeType = function (property) {
return 'numeric';
case 'formatted':
case 'string':
+ case 'image': // TODO: replace once we implement image expressions
return 'string';
case 'enum':
return '`MGL' + camelize(property.name) + '`';
@@ -522,6 +526,7 @@ global.describeValue = function (value, property, layerType) {
return 'the float ' + '`' + formatNumber(value) + '`';
case 'formatted':
case 'string':
+ case 'image': // TODO: replace once we implement image expressions
if (value === '') {
return 'the empty string';
}
@@ -608,6 +613,7 @@ global.propertyType = function (property) {
return 'NSNumber *';
case 'formatted':
case 'string':
+ case 'image': // TODO: replace once we implement image expressions
return 'NSString *';
case 'enum':
return 'NSValue *';
@@ -640,6 +646,7 @@ global.isInterpolatable = function (property) {
return type !== 'boolean' &&
type !== 'enum' &&
type !== 'string' &&
+ type !== 'image' &&
type !== 'formatted';
};
@@ -653,6 +660,7 @@ global.valueTransformerArguments = function (property) {
case 'formatted':
return ['mbgl::style::expression::Formatted', objCType];
case 'string':
+ case 'image': // TODO: replace once we implement image expressions
return ['std::string', objCType];
case 'enum':
return [mbglType(property), 'NSValue *', mbglType(property), `MGL${camelize(property.name)}`];
@@ -692,6 +700,7 @@ global.mbglType = function(property) {
case 'formatted':
return 'mbgl::style::expression::Formatted';
case 'string':
+ case 'image': // TODO: replace once we implement image expressions
return 'std::string';
case 'enum': {
let type = camelize(originalPropertyName(property));
diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm
index a04181af2f..ceb83b3740 100644
--- a/platform/darwin/src/MGLComputedShapeSource.mm
+++ b/platform/darwin/src/MGLComputedShapeSource.mm
@@ -140,7 +140,7 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
@"This will be logged only once.");
});
}
- mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>();
+ mbgl::GeoJSONFeature geoJsonObject = [feature geoJSONObject].get<mbgl::GeoJSONFeature>();
featureCollection.push_back(geoJsonObject);
}
const auto geojson = mbgl::GeoJSON{featureCollection};
@@ -204,7 +204,7 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
mbgl::FeatureCollection featureCollection;
featureCollection.reserve(features.count);
for (MGLShape <MGLFeature> * feature in features) {
- mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>();
+ mbgl::GeoJSONFeature geoJsonObject = [feature geoJSONObject].get<mbgl::GeoJSONFeature>();
featureCollection.push_back(geoJsonObject);
if ([feature isMemberOfClass:[MGLShapeCollection class]]) {
static dispatch_once_t onceToken;
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
index fbf262af29..df6b1bffea 100644
--- a/platform/darwin/src/MGLFeature.mm
+++ b/platform/darwin/src/MGLFeature.mm
@@ -336,8 +336,8 @@ MGL_DEFINE_FEATURE_ATTRIBUTES_GETTER();
featureCollection.reserve(self.shapes.count);
for (MGLShape <MGLFeature> *feature in self.shapes) {
auto geoJSONObject = feature.geoJSONObject;
- MGLAssert(geoJSONObject.is<mbgl::Feature>(), @"Feature collection must only contain features.");
- featureCollection.push_back(geoJSONObject.get<mbgl::Feature>());
+ MGLAssert(geoJSONObject.is<mbgl::GeoJSONFeature>(), @"Feature collection must only contain features.");
+ featureCollection.push_back(geoJSONObject.get<mbgl::GeoJSONFeature>());
}
return featureCollection;
}
@@ -470,7 +470,7 @@ public:
return shape;
}
- MGLShape <MGLFeature> * operator()(const mbgl::Feature &feature) const {
+ MGLShape <MGLFeature> * operator()(const mbgl::GeoJSONFeature &feature) const {
MGLShape <MGLFeature> *shape = (MGLShape <MGLFeature> *)MGLFeatureFromMBGLFeature(feature);
return shape;
}
@@ -487,12 +487,20 @@ public:
NSArray<MGLShape <MGLFeature> *> *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features) {
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()];
for (const auto &feature : features) {
+ [shapes addObject:MGLFeatureFromMBGLFeature(static_cast<mbgl::GeoJSONFeature>(feature))];
+ }
+ return shapes;
+}
+
+NSArray<MGLShape <MGLFeature> *> *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::GeoJSONFeature> &features) {
+ NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()];
+ for (const auto &feature : features) {
[shapes addObject:MGLFeatureFromMBGLFeature(feature)];
}
return shapes;
}
-id <MGLFeature> MGLFeatureFromMBGLFeature(const mbgl::Feature &feature) {
+id <MGLFeature> MGLFeatureFromMBGLFeature(const mbgl::GeoJSONFeature &feature) {
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:feature.properties.size()];
for (auto &pair : feature.properties) {
auto &value = pair.second;
@@ -515,7 +523,7 @@ MGLShape* MGLShapeFromGeoJSON(const mapbox::geojson::geojson &geojson) {
return shape;
}
-mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary *attributes)
+mbgl::GeoJSONFeature mbglFeature(mbgl::GeoJSONFeature feature, id identifier, NSDictionary *attributes)
{
if (identifier) {
NSExpression *identifierExpression = [NSExpression expressionForConstantValue:identifier];
diff --git a/platform/darwin/src/MGLFeature_Private.h b/platform/darwin/src/MGLFeature_Private.h
index 9b0e16f4b9..599633dd31 100644
--- a/platform/darwin/src/MGLFeature_Private.h
+++ b/platform/darwin/src/MGLFeature_Private.h
@@ -16,10 +16,17 @@ MGL_EXPORT
NSArray<MGLShape <MGLFeature> *> *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features);
/**
- Returns an `MGLFeature` object converted from the given mbgl::Feature
+ Returns an array of `MGLFeature` objects converted from the given vector of
+ vector tile features.
+ */
+MGL_EXPORT
+NSArray<MGLShape <MGLFeature> *> *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::GeoJSONFeature> &features);
+
+/**
+ Returns an `MGLFeature` object converted from the given mbgl::GeoJSONFeature
*/
MGL_EXPORT
-id <MGLFeature> MGLFeatureFromMBGLFeature(const mbgl::Feature &feature);
+id <MGLFeature> MGLFeatureFromMBGLFeature(const mbgl::GeoJSONFeature &feature);
/**
Returns an `MGLShape` representing the given geojson. The shape can be
@@ -28,11 +35,11 @@ id <MGLFeature> MGLFeatureFromMBGLFeature(const mbgl::Feature &feature);
MGLShape* MGLShapeFromGeoJSON(const mapbox::geojson::geojson &geojson);
/**
- Takes an `mbgl::Feature` object, an identifer, and attributes dictionary and
+ Takes an `mbgl::GeoJSONFeature` object, an identifer, and attributes dictionary and
returns the feature object with converted `mbgl::FeatureIdentifier` and
`mbgl::PropertyMap` properties.
*/
-mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary * attributes);
+mbgl::GeoJSONFeature mbglFeature(mbgl::GeoJSONFeature feature, id identifier, NSDictionary * attributes);
/**
Returns an `NSDictionary` representation of an `MGLFeature`.
diff --git a/platform/darwin/src/MGLImageSource.mm b/platform/darwin/src/MGLImageSource.mm
index 421cc7a155..2d2f090079 100644
--- a/platform/darwin/src/MGLImageSource.mm
+++ b/platform/darwin/src/MGLImageSource.mm
@@ -1,6 +1,7 @@
#import "MGLImageSource.h"
#import "MGLGeometry_Private.h"
+#import "MGLLoggingConfiguration_Private.h"
#import "MGLSource_Private.h"
#import "MGLTileSource_Private.h"
#import "NSURL+MGLAdditions.h"
@@ -99,7 +100,11 @@
}
- (NSString *)attributionHTMLString {
- MGLAssertStyleSourceIsValid();
+ if (!self.rawSource) {
+ MGLAssert(0, @"Source with identifier `%@` was invalidated after a style change", self.identifier);
+ return nil;
+ }
+
auto attribution = self.rawSource->getAttribution();
return attribution ? @(attribution->c_str()) : nil;
}
diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h
index 33febe0d0c..98c8e8a375 100644
--- a/platform/darwin/src/MGLMapSnapshotter.h
+++ b/platform/darwin/src/MGLMapSnapshotter.h
@@ -19,6 +19,31 @@ MGL_EXPORT
*/
@property (nonatomic, readonly) CGContextRef context;
+#if TARGET_OS_IPHONE
+/**
+ Converts the specified map coordinate to a point in the coordinate space of the
+ context.
+ */
+- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate;
+
+/**
+ Converts the specified context point to a map coordinate.
+ */
+- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point;
+
+#else
+/**
+ Converts the specified map coordinate to a point in the coordinate space of the
+ context.
+ */
+- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate;
+
+/**
+ Converts the specified context point to a map coordinate.
+ */
+- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point;
+#endif
+
@end
/**
diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm
index 85619a780b..0dde94292c 100644
--- a/platform/darwin/src/MGLMapSnapshotter.mm
+++ b/platform/darwin/src/MGLMapSnapshotter.mm
@@ -17,6 +17,8 @@
#import "MGLAttributionInfo_Private.h"
#import "MGLLoggingConfiguration_Private.h"
#import "MGLRendererConfiguration.h"
+#import "MGLMapSnapshotter_Private.h"
+
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
#import "MGLMapboxEvents.h"
#endif
@@ -33,23 +35,61 @@ const CGPoint MGLLogoImagePosition = CGPointMake(8, 8);
const CGFloat MGLSnapshotterMinimumPixelSize = 64;
-@interface MGLMapSnapshotOverlay()
-
-- (instancetype)initWithContext:(CGContextRef)context;
+@interface MGLMapSnapshotOverlay() <MGLMapSnapshotProtocol>
+@property (nonatomic, assign) CGFloat scale;
+- (instancetype)initWithContext:(CGContextRef)context scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn;
@end
-@implementation MGLMapSnapshotOverlay
+@implementation MGLMapSnapshotOverlay {
+ mbgl::MapSnapshotter::PointForFn _pointForFn;
+ mbgl::MapSnapshotter::LatLngForFn _latLngForFn;
+}
-- (instancetype) initWithContext:(CGContextRef)context {
+- (instancetype) initWithContext:(CGContextRef)context scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn {
self = [super init];
if (self) {
_context = context;
+ _scale = scale;
+ _pointForFn = pointForFn;
+ _latLngForFn = latLngForFn;
}
return self;
}
+#if TARGET_OS_IPHONE
+
+- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate
+{
+ mbgl::ScreenCoordinate sc = _pointForFn(MGLLatLngFromLocationCoordinate2D(coordinate));
+ return CGPointMake(sc.x, sc.y);
+}
+
+- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point
+{
+ mbgl::LatLng latLng = _latLngForFn(mbgl::ScreenCoordinate(point.x, point.y));
+ return MGLLocationCoordinate2DFromLatLng(latLng);
+}
+
+#else
+
+- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate
+{
+ mbgl::ScreenCoordinate sc = _pointForFn(MGLLatLngFromLocationCoordinate2D(coordinate));
+ CGFloat height = ((CGFloat)CGBitmapContextGetHeight(self.context))/self.scale;
+ return NSMakePoint(sc.x, height - sc.y);
+}
+
+- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point
+{
+ CGFloat height = ((CGFloat)CGBitmapContextGetHeight(self.context))/self.scale;
+ auto screenCoord = mbgl::ScreenCoordinate(point.x, height - point.y);
+ mbgl::LatLng latLng = _latLngForFn(screenCoord);
+ return MGLLocationCoordinate2DFromLatLng(latLng);
+}
+
+#endif
@end
@implementation MGLMapSnapshotOptions
@@ -78,7 +118,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
@end
-@interface MGLMapSnapshot()
+@interface MGLMapSnapshot() <MGLMapSnapshotProtocol>
- (instancetype)initWithImage:(nullable MGLImage *)image scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn;
@property (nonatomic) CGFloat scale;
@@ -93,8 +133,8 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
{
self = [super init];
if (self) {
- _pointForFn = std::move(pointForFn);
- _latLngForFn = std::move(latLngForFn);
+ _pointForFn = pointForFn;
+ _latLngForFn = latLngForFn;
_scale = scale;
_image = image;
}
@@ -328,7 +368,10 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
CGContextRef currentContext = UIGraphicsGetCurrentContext();
if (currentContext && overlayHandler) {
- MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext];
+ MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext
+ scale:scale
+ pointForFn:pointForFn
+ latLngForFn:latLngForFn];
CGContextSaveGState(snapshotOverlay.context);
overlayHandler(snapshotOverlay);
CGContextRestoreGState(snapshotOverlay.context);
@@ -409,7 +452,10 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
if (currentContext && overlayHandler) {
- MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext.CGContext];
+ MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext.CGContext
+ scale:scale
+ pointForFn:pointForFn
+ latLngForFn:latLngForFn];
[currentContext saveGraphicsState];
overlayHandler(snapshotOverlay);
[currentContext restoreGraphicsState];
diff --git a/platform/darwin/src/MGLMapSnapshotter_Private.h b/platform/darwin/src/MGLMapSnapshotter_Private.h
new file mode 100644
index 0000000000..205e51a6de
--- /dev/null
+++ b/platform/darwin/src/MGLMapSnapshotter_Private.h
@@ -0,0 +1,14 @@
+#import "MGLMapSnapshotter.h"
+
+@protocol MGLMapSnapshotProtocol <NSObject>
+
+#if TARGET_OS_IPHONE
+- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate;
+- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point;
+
+#else
+- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate;
+- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point;
+#endif
+
+@end
diff --git a/platform/darwin/src/MGLRasterTileSource.mm b/platform/darwin/src/MGLRasterTileSource.mm
index b31cee296f..80b21cc7dd 100644
--- a/platform/darwin/src/MGLRasterTileSource.mm
+++ b/platform/darwin/src/MGLRasterTileSource.mm
@@ -1,5 +1,6 @@
#import "MGLRasterTileSource_Private.h"
+#import "MGLLoggingConfiguration_Private.h"
#import "MGLMapView_Private.h"
#import "MGLSource_Private.h"
#import "MGLTileSource_Private.h"
@@ -72,7 +73,11 @@ static const CGFloat MGLRasterTileSourceRetinaTileSize = 512;
}
- (NSString *)attributionHTMLString {
- MGLAssertStyleSourceIsValid();
+ if (!self.rawSource) {
+ MGLAssert(0, @"Source with identifier `%@` was invalidated after a style change", self.identifier);
+ return nil;
+ }
+
auto attribution = self.rawSource->getAttribution();
return attribution ? @(attribution->c_str()) : nil;
}
diff --git a/platform/darwin/src/MGLSDKMetricsManager.m b/platform/darwin/src/MGLSDKMetricsManager.m
index 828fbcd505..0ef9ecda10 100644
--- a/platform/darwin/src/MGLSDKMetricsManager.m
+++ b/platform/darwin/src/MGLSDKMetricsManager.m
@@ -18,6 +18,23 @@ NSString* MGLStringFromMetricType(MGLMetricType metricType) {
return eventName;
}
+// Taken verbatim from NXFreeArchInfo header documentation
+#if TARGET_OS_IOS
+static void MGLFreeArchInfo(const NXArchInfo *x)
+{
+ const NXArchInfo *p;
+
+ p = NXGetAllArchInfos();
+ while(p->name != NULL){
+ if(x == p)
+ return;
+ p++;
+ }
+ free((char *)x->description);
+ free((NXArchInfo *)x);
+}
+#endif
+
@interface MGLMetricsManager() <MGLNetworkConfigurationMetricsDelegate>
@property (strong, nonatomic) NSDictionary *metadata;
@@ -54,7 +71,16 @@ NSString* MGLStringFromMetricType(MGLMetricType metricType) {
if (localArchInfo) {
abi = @(localArchInfo->description);
- NXFreeArchInfo(localArchInfo);
+
+ NSProcessInfo *processInfo = [NSProcessInfo processInfo];
+
+ // Although NXFreeArchInfo appears to be weakly linked, it does
+ // not have the weak_import attribute, so check the OS version.
+ if (&NXFreeArchInfo && [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 0, 0}]) {
+ NXFreeArchInfo(localArchInfo);
+ } else {
+ MGLFreeArchInfo(localArchInfo);
+ }
}
}
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..a4a100aaa2 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
@@ -209,12 +262,12 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShap
auto geoJSON = [cluster geoJSONObject];
- if (!geoJSON.is<mbgl::Feature>()) {
+ if (!geoJSON.is<mbgl::GeoJSONFeature>()) {
MGLAssert(0, @"cluster geoJSON object is not a feature.");
return extensionValue;
}
- auto clusterFeature = geoJSON.get<mbgl::Feature>();
+ auto clusterFeature = geoJSON.get<mbgl::GeoJSONFeature>();
extensionValue = self.mapView.renderer->queryFeatureExtensions(self.rawSource->getID(),
clusterFeature,
@@ -240,7 +293,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShap
return @[];
}
- std::vector<mbgl::Feature> leaves = featureExtension->get<mbgl::FeatureCollection>();
+ std::vector<mbgl::GeoJSONFeature> leaves = featureExtension->get<mbgl::FeatureCollection>();
return MGLFeaturesFromMBGLFeatures(leaves);
}
@@ -255,7 +308,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShap
return @[];
}
- std::vector<mbgl::Feature> leaves = featureExtension->get<mbgl::FeatureCollection>();
+ std::vector<mbgl::GeoJSONFeature> leaves = featureExtension->get<mbgl::FeatureCollection>();
return MGLFeaturesFromMBGLFeatures(leaves);
}
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/MGLVectorTileSource.mm b/platform/darwin/src/MGLVectorTileSource.mm
index 85270c4a49..9ab11e2e56 100644
--- a/platform/darwin/src/MGLVectorTileSource.mm
+++ b/platform/darwin/src/MGLVectorTileSource.mm
@@ -1,6 +1,7 @@
#import "MGLVectorTileSource_Private.h"
#import "MGLFeature_Private.h"
+#import "MGLLoggingConfiguration_Private.h"
#import "MGLSource_Private.h"
#import "MGLTileSource_Private.h"
#import "MGLStyle_Private.h"
@@ -44,7 +45,11 @@
}
- (NSString *)attributionHTMLString {
- MGLAssertStyleSourceIsValid();
+ if (!self.rawSource) {
+ MGLAssert(0, @"Source with identifier `%@` was invalidated after a style change", self.identifier);
+ return nil;
+ }
+
auto attribution = self.rawSource->getAttribution();
return attribution ? @(attribution->c_str()) : nil;
}
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;
diff --git a/platform/darwin/src/collator.mm b/platform/darwin/src/collator.mm
index fe2b46ffa7..0f010c1df8 100644
--- a/platform/darwin/src/collator.mm
+++ b/platform/darwin/src/collator.mm
@@ -1,12 +1,11 @@
-#include <mbgl/style/expression/collator.hpp>
+#include <mbgl/i18n/collator.hpp>
#include <sstream>
#import <Foundation/Foundation.h>
namespace mbgl {
-namespace style {
-namespace expression {
+namespace platform {
class Collator::Impl {
public:
@@ -48,7 +47,6 @@ private:
NSLocale* locale;
};
-
Collator::Collator(bool caseSensitive, bool diacriticSensitive, optional<std::string> locale_)
: impl(std::make_shared<Impl>(caseSensitive, diacriticSensitive, std::move(locale_)))
{}
@@ -65,6 +63,5 @@ std::string Collator::resolvedLocale() const {
return impl->resolvedLocale();
}
-} // namespace expression
-} // namespace style
+} // namespace platform
} // namespace mbgl
diff --git a/platform/darwin/src/number_format.mm b/platform/darwin/src/number_format.mm
new file mode 100644
index 0000000000..86f16eb6dd
--- /dev/null
+++ b/platform/darwin/src/number_format.mm
@@ -0,0 +1,36 @@
+#import <Foundation/Foundation.h>
+
+#include <mbgl/i18n/number_format.hpp>
+
+namespace mbgl {
+namespace platform {
+
+std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
+ uint8_t minFractionDigits, uint8_t maxFractionDigits) {
+
+ static NSNumberFormatter *numberFormatter;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ numberFormatter = [[NSNumberFormatter alloc] init];
+ });
+
+ numberFormatter.locale = !localeId.empty() ? [NSLocale localeWithLocaleIdentifier:@(localeId.c_str())] : nil;
+ numberFormatter.currencyCode = !currency.empty() ? @(currency.c_str()) : nil;
+ if (currency.empty()) {
+ numberFormatter.minimumFractionDigits = minFractionDigits;
+ numberFormatter.maximumFractionDigits = maxFractionDigits;
+ numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
+ } else {
+ // Reset fraction digits to NSNumberFormatter's default values, so that the
+ // system will choose formatting based on number formatter locale.
+ numberFormatter.minimumFractionDigits = 0;
+ numberFormatter.maximumFractionDigits = 0;
+ numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
+ }
+ NSString *formatted = [numberFormatter stringFromNumber:@(number)];
+ std::string result = std::string([formatted UTF8String]);
+ return result;
+}
+
+} // namespace platform
+} // namespace mbgl
diff --git a/platform/darwin/src/string_nsstring.mm b/platform/darwin/src/string_nsstring.mm
index 382a2acf33..d7c81236ef 100644
--- a/platform/darwin/src/string_nsstring.mm
+++ b/platform/darwin/src/string_nsstring.mm
@@ -27,32 +27,5 @@ std::string lowercase(const std::string &string) {
return result;
}
-std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
- uint8_t minFractionDigits, uint8_t maxFractionDigits) {
-
- static NSNumberFormatter *numberFormatter;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- numberFormatter = [[NSNumberFormatter alloc] init];
- });
-
- numberFormatter.locale = !localeId.empty() ? [NSLocale localeWithLocaleIdentifier:@(localeId.c_str())] : nil;
- numberFormatter.currencyCode = !currency.empty() ? @(currency.c_str()) : nil;
- if (currency.empty()) {
- numberFormatter.minimumFractionDigits = minFractionDigits;
- numberFormatter.maximumFractionDigits = maxFractionDigits;
- numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
- } else {
- // Reset fraction digits to NSNumberFormatter's default values, so that the
- // system will choose formatting based on number formatter locale.
- numberFormatter.minimumFractionDigits = 0;
- numberFormatter.maximumFractionDigits = 0;
- numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
- }
- NSString *formatted = [numberFormatter stringFromNumber:@(number)];
- std::string result = std::string([formatted UTF8String]);
- return result;
-}
-
-}
-}
+} // namespace platform
+} // namespace mbgl
diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift
index 9fbb0cc329..7d6bdbed54 100644
--- a/platform/darwin/test/MGLDocumentationExampleTests.swift
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -554,7 +554,39 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(attributedExpression)
}
-
+
+ func testMGLShapeSourceOptionClusterProperties() {
+ //#-example-code
+ 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]
+ //#-end-example-code
+ let geoJSON: [String: Any] = [
+ "type" : "Feature",
+ "geometry" : [
+ "coordinates" : [
+ -77.00896639534831,
+ 38.87031006108791,
+ 0.0
+ ],
+ "type" : "Point"
+ ],
+ "properties" : [
+ "cluster" : true,
+ "cluster_id" : 123,
+ "point_count" : 4567,
+ ]
+ ]
+
+ let clusterShapeData = try! JSONSerialization.data(withJSONObject: geoJSON, options: [])
+ let shape = try! MGLShape(data: clusterShapeData, encoding: String.Encoding.utf8.rawValue)
+ let source = MGLShapeSource(identifier: "source", shape: shape, options: options)
+ mapView.style?.addSource(source)
+
+ }
// For testMGLMapView().
func myCustomFunction() {}
}
diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm
index f1fe3ea878..4ccd7adb6e 100644
--- a/platform/darwin/test/MGLExpressionTests.mm
+++ b/platform/darwin/test/MGLExpressionTests.mm
@@ -180,6 +180,13 @@ using namespace std::string_literals;
XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @1);
}
{
+ NSExpression *expression = [NSExpression expressionForVariable:@"featureAccumulated"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"accumulated"]);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$featureAccumulated"].mgl_jsonExpressionObject, @[@"accumulated"]);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:@[@"accumulated"]], expression);
+ }
+
+ {
NSExpression *expression = [NSExpression expressionForVariable:@"geometryType"];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"geometry-type"]);
XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$geometryType"].mgl_jsonExpressionObject, @[@"geometry-type"]);
@@ -381,6 +388,26 @@ using namespace std::string_literals;
XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
{
+ NSExpression *testExpression = [NSExpression expressionWithFormat:@"sum:({1, 1, 2})"];
+ NSExpression *expression = [NSExpression expressionForFunction:@"sum:" arguments:@[[NSExpression expressionForAggregate:@[MGLConstantExpression(@1), MGLConstantExpression(@1), MGLConstantExpression(@2)]]]];
+
+ NSArray *jsonExpression = @[@"+", @1, @1, @2];
+
+ XCTAssertEqualObjects(testExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(expression, testExpression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"sum:" arguments:@[MGLConstantExpression(@1), MGLConstantExpression(@1), MGLConstantExpression(@2)]];
+ NSArray *jsonExpression = @[@"+", @1, @1, @2];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+
+ // - [NSExpression expressionWithMGLJSONObject:] creates an expression with an aggregate expression as an argument. This is not equal to an expression with an array of expressions as an argument. For testing purposes, we will compare their operands and arrays of expressions.
+ NSExpression *aggregateExpression = [NSExpression expressionWithMGLJSONObject:jsonExpression];
+ XCTAssertEqualObjects(aggregateExpression.operand, expression.operand);
+ XCTAssertEqualObjects(aggregateExpression.arguments.firstObject.collection, expression.arguments);
+ }
+ {
NSArray *threeArguments = @[MGLConstantExpression(@1), MGLConstantExpression(@1), MGLConstantExpression(@1)];
NSExpression *expression = [NSExpression expressionForFunction:@"add:to:" arguments:threeArguments];
NSArray *jsonExpression = @[@"+", @1, @1, @1];
@@ -418,6 +445,24 @@ using namespace std::string_literals;
XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
{
+ NSExpression *expression = [NSExpression expressionForFunction:@"max:" arguments:arguments];
+ NSArray *jsonExpression = @[@"max", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+
+ NSExpression *aggregateExpression = [NSExpression expressionWithMGLJSONObject:jsonExpression];
+ XCTAssertEqualObjects(aggregateExpression.operand, expression.operand);
+ XCTAssertEqualObjects(aggregateExpression.arguments.firstObject.collection, expression.arguments);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"min:" arguments:arguments];
+ NSArray *jsonExpression = @[@"min", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+
+ NSExpression *aggregateExpression = [NSExpression expressionWithMGLJSONObject:jsonExpression];
+ XCTAssertEqualObjects(aggregateExpression.operand, expression.operand);
+ XCTAssertEqualObjects(aggregateExpression.arguments.firstObject.collection, expression.arguments);
+ }
+ {
NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@1.5)]];
NSArray *jsonExpression = @[@"ceil", @1.5];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
@@ -622,6 +667,16 @@ using namespace std::string_literals;
XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], aftermarketExpression);
}
{
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_join:" arguments:@[@"Old", @"MacDonald"]];
+ NSExpression *aftermarketExpression = [NSExpression expressionWithFormat:@"mgl_join({'Old', 'MacDonald'})"];
+ NSArray *jsonExpression = @[@"concat", @"Old", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+
+ XCTAssertEqualObjects(aftermarketExpression.mgl_jsonExpressionObject, expression.mgl_jsonExpressionObject);
+ NSExpression *aggregateExpression = [NSExpression expressionWithMGLJSONObject:jsonExpression];
+ XCTAssertEqualObjects(aggregateExpression.operand, expression.operand);
+ }
+ {
NSExpression *expression = [NSExpression expressionForFunction:@"uppercase:" arguments:arguments];
NSArray *jsonExpression = @[@"upcase", @"MacDonald"];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
diff --git a/platform/darwin/test/MGLShapeSourceTests.mm b/platform/darwin/test/MGLShapeSourceTests.mm
index 3459fb1733..3bf3ef04bd 100644
--- a/platform/darwin/test/MGLShapeSourceTests.mm
+++ b/platform/darwin/test/MGLShapeSourceTests.mm
@@ -13,8 +13,12 @@
@implementation MGLShapeSourceTests
- (void)testGeoJSONOptionsFromDictionary {
+ NSExpression *reduceExpression = [NSExpression expressionForFunction:@"sum:" arguments:@[[NSExpression expressionForKeyPath:@"featureAccumulated"], [NSExpression expressionForKeyPath:@"sumValue"]]];
+ NSExpression *mapExpression = [NSExpression expressionForKeyPath:@"mag"];
+ NSArray *clusterPropertyArray = @[reduceExpression, mapExpression];
NSDictionary *options = @{MGLShapeSourceOptionClustered: @YES,
MGLShapeSourceOptionClusterRadius: @42,
+ MGLShapeSourceOptionClusterProperties: @{@"sumValue": clusterPropertyArray},
MGLShapeSourceOptionMaximumZoomLevelForClustering: @98,
MGLShapeSourceOptionMaximumZoomLevel: @99,
MGLShapeSourceOptionBuffer: @1976,
@@ -29,6 +33,7 @@
XCTAssertEqual(mbglOptions.buffer, 1976);
XCTAssertEqual(mbglOptions.tolerance, 0.42);
XCTAssertTrue(mbglOptions.lineMetrics);
+ XCTAssertTrue(!mbglOptions.clusterProperties.empty());
options = @{MGLShapeSourceOptionClustered: @"number 1"};
XCTAssertThrows(MGLGeoJSONOptionsFromDictionary(options));