When converting a constant NSExpression to an mbgl::Value, turn an NSColor or UIColor into an std::string containing a CSS color string. This allows developers to set an attribute of an MGLFeature to an NSColor or UIColor (rather than a CSS color string, which would be foreign), then use it in an MGLStyleFunction with identity interpolation.
157 lines
6.3 KiB
Plaintext
157 lines
6.3 KiB
Plaintext
#import "NSExpression+MGLAdditions.h"
|
||
|
||
#import "MGLTypes.h"
|
||
#if TARGET_OS_IPHONE
|
||
#import "UIColor+MGLAdditions.h"
|
||
#else
|
||
#import "NSColor+MGLAdditions.h"
|
||
#endif
|
||
|
||
@implementation NSExpression (MGLAdditions)
|
||
|
||
- (std::vector<mbgl::Value>)mgl_aggregateMBGLValue {
|
||
if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) {
|
||
std::vector<mbgl::Value> convertedValues;
|
||
for (id value in self.constantValue) {
|
||
NSExpression *expression = value;
|
||
if (![expression isKindOfClass:[NSExpression class]]) {
|
||
expression = [NSExpression expressionForConstantValue:expression];
|
||
}
|
||
convertedValues.push_back(expression.mgl_constantMBGLValue);
|
||
}
|
||
return convertedValues;
|
||
}
|
||
[NSException raise:NSInvalidArgumentException
|
||
format:@"Constant value expression must contain an array or set."];
|
||
return {};
|
||
}
|
||
|
||
- (mbgl::Value)mgl_constantMBGLValue {
|
||
id value = self.constantValue;
|
||
if ([value isKindOfClass:NSString.class]) {
|
||
return { std::string([(NSString *)value UTF8String]) };
|
||
} else if ([value isKindOfClass:NSNumber.class]) {
|
||
NSNumber *number = (NSNumber *)value;
|
||
if ((strcmp([number objCType], @encode(char)) == 0) ||
|
||
(strcmp([number objCType], @encode(BOOL)) == 0)) {
|
||
// char: 32-bit boolean
|
||
// BOOL: 64-bit boolean
|
||
return { (bool)number.boolValue };
|
||
} else if (strcmp([number objCType], @encode(double)) == 0) {
|
||
// Double values on all platforms are interpreted precisely.
|
||
return { (double)number.doubleValue };
|
||
} else if (strcmp([number objCType], @encode(float)) == 0) {
|
||
// Float values when taken as double introduce precision problems,
|
||
// so warn the user to avoid them. This would require them to
|
||
// explicitly use -[NSNumber numberWithFloat:] arguments anyway.
|
||
// We still do this conversion in order to provide a valid value.
|
||
static dispatch_once_t onceToken;
|
||
dispatch_once(&onceToken, ^{
|
||
NSLog(@"Float value in expression will be converted to a double; some imprecision may result. "
|
||
@"Use double values explicitly when specifying constant expression values and "
|
||
@"when specifying arguments to predicate and expression format strings. "
|
||
@"This will be logged only once.");
|
||
});
|
||
return { (double)number.doubleValue };
|
||
} else if ([number compare:@(0)] == NSOrderedDescending ||
|
||
[number compare:@(0)] == NSOrderedSame) {
|
||
// Positive integer or zero; use uint64_t per mbgl::Value definition.
|
||
// We use unsigned long long here to avoid any truncation.
|
||
return { (uint64_t)number.unsignedLongLongValue };
|
||
} else if ([number compare:@(0)] == NSOrderedAscending) {
|
||
// Negative integer; use int64_t per mbgl::Value definition.
|
||
// We use long long here to avoid any truncation.
|
||
return { (int64_t)number.longLongValue };
|
||
}
|
||
} else if ([value isKindOfClass:[MGLColor class]]) {
|
||
auto hexString = [(MGLColor *)value mgl_color].stringify();
|
||
return { hexString };
|
||
} else if (value && value != [NSNull null]) {
|
||
[NSException raise:NSInvalidArgumentException
|
||
format:@"Can’t convert %s:%@ to mbgl::Value", [value objCType], value];
|
||
}
|
||
return {};
|
||
}
|
||
|
||
- (std::vector<mbgl::FeatureType>)mgl_aggregateFeatureType {
|
||
if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) {
|
||
std::vector<mbgl::FeatureType> convertedValues;
|
||
for (id value in self.constantValue) {
|
||
NSExpression *expression = value;
|
||
if (![expression isKindOfClass:[NSExpression class]]) {
|
||
expression = [NSExpression expressionForConstantValue:expression];
|
||
}
|
||
convertedValues.push_back(expression.mgl_featureType);
|
||
}
|
||
return convertedValues;
|
||
}
|
||
[NSException raise:NSInvalidArgumentException
|
||
format:@"Constant value expression must contain an array or set."];
|
||
return {};
|
||
}
|
||
|
||
- (mbgl::FeatureType)mgl_featureType {
|
||
id value = self.constantValue;
|
||
if ([value isKindOfClass:NSString.class]) {
|
||
if ([value isEqualToString:@"Point"]) {
|
||
return mbgl::FeatureType::Point;
|
||
}
|
||
if ([value isEqualToString:@"LineString"]) {
|
||
return mbgl::FeatureType::LineString;
|
||
}
|
||
if ([value isEqualToString:@"Polygon"]) {
|
||
return mbgl::FeatureType::Polygon;
|
||
}
|
||
} else if ([value isKindOfClass:NSNumber.class]) {
|
||
switch ([value integerValue]) {
|
||
case 1:
|
||
return mbgl::FeatureType::Point;
|
||
case 2:
|
||
return mbgl::FeatureType::LineString;
|
||
case 3:
|
||
return mbgl::FeatureType::Polygon;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
return mbgl::FeatureType::Unknown;
|
||
}
|
||
|
||
- (std::vector<mbgl::FeatureIdentifier>)mgl_aggregateFeatureIdentifier {
|
||
if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) {
|
||
std::vector<mbgl::FeatureIdentifier> convertedValues;
|
||
for (id value in self.constantValue) {
|
||
NSExpression *expression = value;
|
||
if (![expression isKindOfClass:[NSExpression class]]) {
|
||
expression = [NSExpression expressionForConstantValue:expression];
|
||
}
|
||
convertedValues.push_back(expression.mgl_featureIdentifier);
|
||
}
|
||
return convertedValues;
|
||
}
|
||
[NSException raise:NSInvalidArgumentException
|
||
format:@"Constant value expression must contain an array or set."];
|
||
return {};
|
||
}
|
||
|
||
- (mbgl::FeatureIdentifier)mgl_featureIdentifier {
|
||
mbgl::Value mbglValue = self.mgl_constantMBGLValue;
|
||
|
||
if (mbglValue.is<std::string>()) {
|
||
return mbglValue.get<std::string>();
|
||
}
|
||
if (mbglValue.is<double>()) {
|
||
return mbglValue.get<double>();
|
||
}
|
||
if (mbglValue.is<uint64_t>()) {
|
||
return mbglValue.get<uint64_t>();
|
||
}
|
||
if (mbglValue.is<int64_t>()) {
|
||
return mbglValue.get<int64_t>();
|
||
}
|
||
|
||
return {};
|
||
}
|
||
|
||
@end
|