diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h
index 8ba56874800..2380a817e35 100644
--- a/platform/darwin/src/MGLFeature.h
+++ b/platform/darwin/src/MGLFeature.h
@@ -83,12 +83,21 @@ NS_ASSUME_NONNULL_BEGIN
`MGLVectorStyleLayer.predicate` to an `NSPredicate` with the format
`importance > 50`.
- You can also configure some attributes of an `MGLSymbolStyleLayer` object to
- include the value of an attribute in this dictionary whenever it renders this
- feature. For example, to label features in an `MGLShapeSource` object by their
- names, you can assign a `name` attribute to each of the source’s features, then
- set `MGLSymbolStyleLayer.textField` to an `MGLStyleValue` object containing the
- string `{name}`.
+ You can also configure many layout and paint attributes of an `MGLStyleLayer`
+ object to match the value of an attribute in this dictionary whenever it
+ renders this feature. For example, if you display features in an
+ `MGLShapeSource` using an `MGLCircleStyleLayer`, you can assign a `halfway`
+ attribute to each of the source’s features, then set
+ `MGLCircleStyleLayer.circleRadius` to an `MGLStyleValue` object with an
+ interpolation mode of `MGLInterpolationModeIdentity` and an attribute name of
+ `halfway`.
+
+ The `MGLSymbolStyleLayer.textField` and `MGLSymbolStyleLayer.iconImageName`
+ properties allow you to use attributes yet another way. For example, to label
+ features in an `MGLShapeSource` object by their names, you can assign a `name`
+ attribute to each of the source’s features, then set
+ `MGLSymbolStyleLayer.textField` to an `MGLStyleValue` object containing the
+ raw string value `{name}`.
In vector tiles loaded by `MGLVectorSource` objects, the keys and values of
each feature’s attribute dictionary are determined by the source. Each
@@ -115,6 +124,15 @@ NS_ASSUME_NONNULL_BEGIN
and
Mapbox Terrain
layer references.
+
+ When adding a feature to an `MGLShapeSource`, use the same Foundation types
+ listed above for each attribute value. In addition to the Foundation types, you
+ may also set an attribute to an `NSColor` (macOS) or `UIColor` (iOS), which
+ will be converted into its
+ CSS string representation
+ when the feature is added to an `MGLShapeSource`. This can be convenient when
+ using the attribute to supply a value for a color-typed layout or paint
+ attribute via the `MGLInterpolationModeIdentity` interpolation mode.
Note that while it is possible to change this value on feature
instances obtained from `-[MGLMapView visibleFeaturesAtPoint:]` and related
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index c54102b8c97..a7759cda9dd 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -1,5 +1,12 @@
#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)mgl_aggregateMBGLValue {
@@ -56,6 +63,9 @@ @implementation NSExpression (MGLAdditions)
// 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];
diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm
index 47315b97d6f..ad0833a0682 100644
--- a/platform/darwin/test/MGLExpressionTests.mm
+++ b/platform/darwin/test/MGLExpressionTests.mm
@@ -2,8 +2,29 @@
#import
+#import "MGLTypes.h"
#import "NSExpression+MGLAdditions.h"
+#define MGLAssertEqualValues(actual, expected, ...) \
+ XCTAssertTrue(actual.is<__typeof__(expected)>()); \
+ if (actual.is<__typeof__(expected)>()) { \
+ XCTAssertEqual(actual.get<__typeof__(expected)>(), expected, __VA_ARGS__); \
+ }
+
+#define MGLAssertEqualValuesWithAccuracy(actual, expected, accuracy, ...) \
+ XCTAssertTrue(actual.is<__typeof__(expected)>()); \
+ if (actual.is<__typeof__(expected)>()) { \
+ XCTAssertEqualWithAccuracy(actual.get<__typeof__(expected)>(), expected, accuracy, __VA_ARGS__); \
+ }
+
+#define MGLAssertConstantEqualsValue(constant, value, ...) \
+ MGLAssertEqualValues([NSExpression expressionForConstantValue:constant].mgl_constantMBGLValue, value, __VA_ARGS__);
+
+#define MGLAssertConstantEqualsValueWithAccuracy(constant, value, accuracy, ...) \
+ MGLAssertEqualValuesWithAccuracy([NSExpression expressionForConstantValue:constant].mgl_constantMBGLValue, value, accuracy, __VA_ARGS__);
+
+using namespace std::string_literals;
+
@interface MGLExpressionTests : XCTestCase
@end
@@ -23,212 +44,100 @@ - (NSComparisonPredicate *)equalityComparisonPredicateWithRightConstantValue:(id
return predicate;
}
-#pragma mark - String Tests
-
-- (void)testExpressionConversionString
-{
- NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@"bar"];
- mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqualObjects(@(convertedValue.get().c_str()), @"bar");
-}
+#pragma mark - Valuation tests
-- (void)testExpressionConversionStringWithUnicode
-{
- NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@"🆔🆗🇦🇶"];
- mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), "🆔🆗🇦🇶");
+- (void)testStringValuation {
+ MGLAssertConstantEqualsValue(@"bar", "bar"s, @"NSString should convert to std::string.");
+ MGLAssertConstantEqualsValue(@"🆔🆗🇦🇶", "🆔🆗🇦🇶"s, @"NSString with non-ASCII characters should convert losslessly to std::string.");
}
-#pragma mark - Boolean Tests
-
-- (void)testExpressionConversionBooleanTrue
-{
- NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@YES];
- mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), true);
+- (void)testColorValuation {
+ MGLAssertConstantEqualsValue([MGLColor redColor], "rgba(255,0,0,1)"s, @"MGLColor should convert to std::string containing CSS color string.");
}
-- (void)testExpressionConversionBooleanFalse
-{
- NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:@NO];
- mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), false);
+- (void)testBooleanValuation {
+ MGLAssertConstantEqualsValue(@NO, false, @"Boolean NSNumber should convert to bool.");
+ MGLAssertConstantEqualsValue(@YES, true, @"Boolean NSNumber should convert to bool.");
}
-#pragma mark - Floating Point Tests
-
-- (void)testExpressionConversionDouble
+- (void)testDoubleValuation
{
- NSComparisonPredicate *predicate;
- mbgl::Value convertedValue;
-
- predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithDouble:DBL_MIN]];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), DBL_MIN);
-
- predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithDouble:DBL_MAX]];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), DBL_MAX);
+ MGLAssertConstantEqualsValue(@DBL_MIN, DBL_MIN, @"Double NSNumber should convert to double.");
+ MGLAssertConstantEqualsValue(@DBL_MAX, DBL_MAX, @"Double NSNumber should convert to double.");
}
-- (void)testExpressionConversionFloat
-{
+- (void)testFloatValuation {
// Because we can't guarantee precision when using float, and because
// we warn the user to this effect in -[NSExpression mgl_constantMBGLValue],
// we just check that things are in the ballpark here with integer values
// and some lower-precision checks.
-
- NSComparisonPredicate *predicate;
- mbgl::Value convertedValue;
-
- predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:-1]];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), -1);
-
- predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:1]];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), 1);
-
- predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:-23.232342]];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqualWithAccuracy(convertedValue.get(), -23.232342, 0.000001);
-
- predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNumber numberWithFloat:23.232342]];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqualWithAccuracy(convertedValue.get(), 23.232342, 0.000001);
+
+ MGLAssertConstantEqualsValue(@-1.0f, -1.0, @"Float NSNumber should convert to double.");
+ MGLAssertConstantEqualsValue(@1.0f, 1.0, @"Float NSNumber should convert to double.");
+ MGLAssertConstantEqualsValueWithAccuracy(@-23.232342f, -23.232342, 0.000001, @"Float NSNumber should convert to double.");
+ MGLAssertConstantEqualsValueWithAccuracy(@23.232342f, 23.232342, 0.000001, @"Float NSNumber should convert to double.");
+ MGLAssertConstantEqualsValueWithAccuracy(@-FLT_MAX, static_cast(-FLT_MAX), 0.000001, @"Float NSNumber should convert to double.");
+ MGLAssertConstantEqualsValueWithAccuracy(@FLT_MAX, static_cast(FLT_MAX), 0.000001, @"Float NSNumber should convert to double.");
}
-#pragma mark - Integer Tests
-
-- (void)testExpressionNegativeIntegers
-{
- NSComparisonPredicate *predicate;
- mbgl::Value convertedValue;
-
- NSArray *minValues = @[
- [NSNumber numberWithShort: SHRT_MIN],
- [NSNumber numberWithInt: INT_MIN],
- [NSNumber numberWithLong: LONG_MIN],
- [NSNumber numberWithLongLong: LLONG_MIN],
- [NSNumber numberWithInteger: NSIntegerMin]
- ];
-
- NSArray *maxValues = @[
- [NSNumber numberWithShort: SHRT_MAX],
- [NSNumber numberWithInt: INT_MAX],
- [NSNumber numberWithLong: LONG_MAX],
- [NSNumber numberWithLongLong: LLONG_MAX],
- [NSNumber numberWithInteger: NSIntegerMax]
- ];
-
+- (void)testIntegerValuation {
// Negative integers should always come back as int64_t per mbgl::Value definition.
- // We use the long long value because it can store the highest number on both 32-
- // and 64-bit and won't overflow.
-
- for (NSNumber *min in minValues)
- {
- predicate = [self equalityComparisonPredicateWithRightConstantValue:min];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), min.longLongValue);
- }
+ MGLAssertConstantEqualsValue(@SHRT_MIN, static_cast(SHRT_MIN), @"Negative short NSNumber should convert to int64_t.");
+ MGLAssertConstantEqualsValue(@INT_MIN, static_cast(INT_MIN), @"Negative int NSNumber should convert to int64_t.");
+ MGLAssertConstantEqualsValue(@LONG_MIN, static_cast(LONG_MIN), @"Negative long NSNumber should convert to int64_t.");
+ MGLAssertConstantEqualsValue(@LLONG_MIN, static_cast(LLONG_MIN), @"Negative long long NSNumber should convert to int64_t.");
+ MGLAssertConstantEqualsValue(@NSIntegerMin, static_cast(NSIntegerMin), @"Negative NSInteger NSNumber should convert to int64_t.");
// Positive integers should always come back as uint64_t per mbgl::Value definition.
- // We use the unsigned long long value because it can store the highest number on
- // both 32- and 64-bit and won't overflow.
-
- for (NSNumber *max in maxValues)
- {
- predicate = [self equalityComparisonPredicateWithRightConstantValue:max];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), max.unsignedLongLongValue);
- }
-
+ MGLAssertConstantEqualsValue(@SHRT_MAX, static_cast(SHRT_MAX), @"Positive short NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@INT_MAX, static_cast(INT_MAX), @"Positive int NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@LONG_MAX, static_cast(LONG_MAX), @"Positive long NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@LLONG_MAX, static_cast(LLONG_MAX), @"Positive long long NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@NSIntegerMax, static_cast(NSIntegerMax), @"Positive NSInteger NSNumber should convert to uint64_t.");
}
-- (void)testExpressionPositiveAndZeroIntegers
-{
- NSComparisonPredicate *predicate;
- mbgl::Value convertedValue;
-
- NSArray *minValues = @[
- [NSNumber numberWithUnsignedShort: 0],
- [NSNumber numberWithUnsignedInt: 0],
- [NSNumber numberWithUnsignedLong: 0],
- [NSNumber numberWithUnsignedLongLong: 0],
- [NSNumber numberWithUnsignedInteger: 0]
- ];
-
- NSArray *maxValues = @[
- [NSNumber numberWithUnsignedShort: USHRT_MAX],
- [NSNumber numberWithUnsignedInt: UINT_MAX],
- [NSNumber numberWithUnsignedLong: ULONG_MAX],
- [NSNumber numberWithUnsignedLongLong: ULLONG_MAX],
- [NSNumber numberWithUnsignedInteger: NSUIntegerMax]
- ];
-
+- (void)testUnsignedIntegerValuation {
// Zero-value integers should always come back as uint64_t per mbgl::Value definition
// (using the interpretation that zero is not negative). We use the unsigned long long
// value just for parity with the positive integer test.
-
- for (NSNumber *min in minValues)
- {
- predicate = [self equalityComparisonPredicateWithRightConstantValue:min];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), min.unsignedLongLongValue);
- }
+ MGLAssertConstantEqualsValue(@(static_cast(0)), static_cast(0), @"Unsigned short NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@0u, static_cast(0), @"Unsigned int NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@0UL, static_cast(0), @"Unsigned long NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@0ULL, static_cast(0), @"Unsigned long long NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@(static_cast(0)), static_cast(0), @"Unsigned NSUInteger NSNumber should convert to uint64_t.");
// Positive integers should always come back as uint64_t per mbgl::Value definition.
// We use the unsigned long long value because it can store the highest number on
// both 32- and 64-bit and won't overflow.
-
- for (NSNumber *max in maxValues)
- {
- predicate = [self equalityComparisonPredicateWithRightConstantValue:max];
- convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
- XCTAssertEqual(convertedValue.get(), max.unsignedLongLongValue);
- }
+ MGLAssertConstantEqualsValue(@USHRT_MAX, static_cast(USHRT_MAX), @"Unsigned short NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@UINT_MAX, static_cast(UINT_MAX), @"Unsigned int NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@ULONG_MAX, static_cast(ULONG_MAX), @"Unsigned long NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@ULLONG_MAX, static_cast(ULLONG_MAX), @"Unsigned long long NSNumber should convert to uint64_t.");
+ MGLAssertConstantEqualsValue(@NSUIntegerMax, static_cast(NSUIntegerMax), @"Unsigned NSUInteger NSNumber should convert to uint64_t.");
}
-#pragma mark - Null Tests
-
-- (void)testExpressionConversionNull
-{
- NSComparisonPredicate *predicate = [self equalityComparisonPredicateWithRightConstantValue:[NSNull null]];
- mbgl::Value convertedValue = predicate.rightExpression.mgl_constantMBGLValue;
- XCTAssertTrue(convertedValue.is());
+- (void)testNullValuation {
+ mbgl::NullValue nullValue;
+ MGLAssertConstantEqualsValue([NSNull null], nullValue, @"NSNull should convert to mbgl::NullValue.");
}
#pragma mark - Feature type tests
- (void)testFeatureType {
- XCTAssertEqual([NSExpression expressionWithFormat:@"'Point'"].mgl_featureType, mbgl::FeatureType::Point);
- XCTAssertEqual([NSExpression expressionWithFormat:@"'LineString'"].mgl_featureType, mbgl::FeatureType::LineString);
- XCTAssertEqual([NSExpression expressionWithFormat:@"'Polygon'"].mgl_featureType, mbgl::FeatureType::Polygon);
- XCTAssertEqual([NSExpression expressionWithFormat:@"'Unknown'"].mgl_featureType, mbgl::FeatureType::Unknown);
- XCTAssertEqual([NSExpression expressionWithFormat:@"''"].mgl_featureType, mbgl::FeatureType::Unknown);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@"Point"].mgl_featureType, mbgl::FeatureType::Point);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@"LineString"].mgl_featureType, mbgl::FeatureType::LineString);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@"Polygon"].mgl_featureType, mbgl::FeatureType::Polygon);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@"Unknown"].mgl_featureType, mbgl::FeatureType::Unknown);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@""].mgl_featureType, mbgl::FeatureType::Unknown);
- XCTAssertEqual([NSExpression expressionWithFormat:@"1"].mgl_featureType, mbgl::FeatureType::Point);
- XCTAssertEqual([NSExpression expressionWithFormat:@"2"].mgl_featureType, mbgl::FeatureType::LineString);
- XCTAssertEqual([NSExpression expressionWithFormat:@"3"].mgl_featureType, mbgl::FeatureType::Polygon);
- XCTAssertEqual([NSExpression expressionWithFormat:@"0"].mgl_featureType, mbgl::FeatureType::Unknown);
- XCTAssertEqual([NSExpression expressionWithFormat:@"-1"].mgl_featureType, mbgl::FeatureType::Unknown);
- XCTAssertEqual([NSExpression expressionWithFormat:@"4"].mgl_featureType, mbgl::FeatureType::Unknown);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@1].mgl_featureType, mbgl::FeatureType::Point);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@2].mgl_featureType, mbgl::FeatureType::LineString);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@3].mgl_featureType, mbgl::FeatureType::Polygon);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@0].mgl_featureType, mbgl::FeatureType::Unknown);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@-1].mgl_featureType, mbgl::FeatureType::Unknown);
+ XCTAssertEqual([NSExpression expressionForConstantValue:@4].mgl_featureType, mbgl::FeatureType::Unknown);
- XCTAssertEqual([NSExpression expressionWithFormat:@"nil"].mgl_featureType, mbgl::FeatureType::Unknown);
+ XCTAssertEqual([NSExpression expressionForConstantValue:nil].mgl_featureType, mbgl::FeatureType::Unknown);
}
@end
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index a0281b3f02f..cd7b44165c4 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -26,6 +26,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Fixed artifacts when drawing particularly acute line joins. ([#7786](https://github.com/mapbox/mapbox-gl-native/pull/7786))
* Fixed an issue in which a vector style layer predicate involving the `$id` key path would exclude all features from the layer. ([#7989](https://github.com/mapbox/mapbox-gl-native/pull/7989), [#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
* Fixed an issue causing vector style layer predicates to be evaluated as if each feature had a `$type` attribute of 1, 2, or 3. The `$type` key path can now be compared to `Point`, `LineString`, or `Polygon`, as described in the documentation. ([#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
+* When setting an `MGLShapeSource`’s shape to an `MGLFeature` instance, any `UIColor` attribute value is now converted to the equivalent CSS string representation for use with `MGLInterpolationModeIdentity` in style functions. ([#8025](https://github.com/mapbox/mapbox-gl-native/pull/8025))
### User interaction
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 4738cc3b780..b00b0cee41c 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -25,6 +25,7 @@
* Fixed artifacts when drawing particularly acute line joins. ([#7786](https://github.com/mapbox/mapbox-gl-native/pull/7786))
* Fixed an issue in which a vector style layer predicate involving the `$id` key path would exclude all features from the layer. ([#7989](https://github.com/mapbox/mapbox-gl-native/pull/7989), [#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
* Fixed an issue causing vector style layer predicates to be evaluated as if each feature had a `$type` attribute of 1, 2, or 3. The `$type` key path can now be compared to `Point`, `LineString`, or `Polygon`, as described in the documentation. ([#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
+* When setting an `MGLShapeSource`’s shape to an `MGLFeature` instance, any `NSColor` attribute value is now converted to the equivalent CSS string representation for use with `MGLInterpolationModeIdentity` in style functions. ([#8025](https://github.com/mapbox/mapbox-gl-native/pull/8025))
### User interaction