summaryrefslogtreecommitdiff
path: root/platform/darwin
diff options
context:
space:
mode:
authorFredrik Karlsson <bjorn.fredrik.karlsson@gmail.com>2016-10-03 11:33:38 +0200
committerFredrik Karlsson <bjorn.fredrik.karlsson@gmail.com>2016-12-22 13:58:45 +0100
commit2c7569b879eb2bc9438a17226b333392052dd2db (patch)
tree1e62ab55e23c7a7be057b437f8b6aecfd21d764a /platform/darwin
parent916cd6c310ee4b7978efa9010123673f067ca6a9 (diff)
downloadqtlocation-mapboxgl-2c7569b879eb2bc9438a17226b333392052dd2db.tar.gz
[ios, macos] features and annotations now conforms to NSSecureCoding
Diffstat (limited to 'platform/darwin')
-rw-r--r--platform/darwin/src/MGLFeature.mm29
-rw-r--r--platform/darwin/src/MGLFeature_Private.h28
-rw-r--r--platform/darwin/src/MGLMultiPoint.mm38
-rw-r--r--platform/darwin/src/MGLPointAnnotation.mm36
-rw-r--r--platform/darwin/src/MGLPointCollection.mm42
-rw-r--r--platform/darwin/src/MGLPolygon.mm55
-rw-r--r--platform/darwin/src/MGLPolyline.mm30
-rw-r--r--platform/darwin/src/MGLShape.h2
-rw-r--r--platform/darwin/src/MGLShape.mm68
-rw-r--r--platform/darwin/src/MGLShapeCollection.mm29
-rw-r--r--platform/darwin/src/MGLShape_Private.h3
-rw-r--r--platform/darwin/src/NSArray+MGLAdditions.h15
-rw-r--r--platform/darwin/src/NSArray+MGLAdditions.mm24
-rw-r--r--platform/darwin/src/NSCoder+MGLAdditions.h16
-rw-r--r--platform/darwin/src/NSCoder+MGLAdditions.mm26
-rw-r--r--platform/darwin/test/MGLCodingTests.m469
16 files changed, 895 insertions, 15 deletions
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
index c1e0c312a0..5b63beae87 100644
--- a/platform/darwin/src/MGLFeature.mm
+++ b/platform/darwin/src/MGLFeature.mm
@@ -10,6 +10,7 @@
#import "MGLPolyline+MGLAdditions.h"
#import "MGLPolygon+MGLAdditions.h"
#import "NSDictionary+MGLAdditions.h"
+#import "NSArray+MGLAdditions.h"
#import "NSExpression+MGLAdditions.h"
@@ -25,6 +26,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -47,6 +52,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -69,6 +78,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -91,6 +104,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -113,6 +130,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -135,6 +156,10 @@
@synthesize identifier;
@synthesize attributes;
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
@@ -163,6 +188,10 @@
return [super shapeCollectionWithShapes:shapes];
}
+MGL_DEFINE_FEATURE_INIT_WITH_CODER();
+MGL_DEFINE_FEATURE_ENCODE();
+MGL_DEFINE_FEATURE_IS_EQUAL();
+
- (id)attributeForKey:(NSString *)key {
return self.attributes[key];
}
diff --git a/platform/darwin/src/MGLFeature_Private.h b/platform/darwin/src/MGLFeature_Private.h
index 97af509893..3277a94d5b 100644
--- a/platform/darwin/src/MGLFeature_Private.h
+++ b/platform/darwin/src/MGLFeature_Private.h
@@ -37,3 +37,31 @@ mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary *at
NS_DICTIONARY_OF(NSString *, id) *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier);
NS_ASSUME_NONNULL_END
+
+#define MGL_DEFINE_FEATURE_INIT_WITH_CODER() \
+ - (instancetype)initWithCoder:(NSCoder *)decoder { \
+ if (self = [super initWithCoder:decoder]) { \
+ NSSet<Class> *identifierClasses = [NSSet setWithArray:@[[NSString class], [NSNumber class]]]; \
+ identifier = [decoder decodeObjectOfClasses:identifierClasses forKey:@"identifier"]; \
+ attributes = [decoder decodeObjectOfClass:[NSDictionary class] forKey:@"attributes"]; \
+ } \
+ return self; \
+ }
+
+#define MGL_DEFINE_FEATURE_ENCODE() \
+ - (void)encodeWithCoder:(NSCoder *)coder { \
+ [super encodeWithCoder:coder]; \
+ [coder encodeObject:identifier forKey:@"identifier"]; \
+ [coder encodeObject:attributes forKey:@"attributes"]; \
+ }
+
+#define MGL_DEFINE_FEATURE_IS_EQUAL() \
+ - (BOOL)isEqual:(id)other { \
+ if (other == self) return YES; \
+ if (![other isKindOfClass:[self class]]) return NO; \
+ __typeof(self) otherFeature = other; \
+ return [super isEqual:other] && [self geoJSONObject] == [otherFeature geoJSONObject]; \
+ } \
+ - (NSUInteger)hash { \
+ return [super hash] + [[self geoJSONDictionary] hash]; \
+ }
diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm
index c49e970c6b..3b03b78ca6 100644
--- a/platform/darwin/src/MGLMultiPoint.mm
+++ b/platform/darwin/src/MGLMultiPoint.mm
@@ -1,10 +1,9 @@
#import "MGLMultiPoint_Private.h"
#import "MGLGeometry_Private.h"
+#import "MGLShape_Private.h"
+#import "NSCoder+MGLAdditions.h"
#import "MGLTypes.h"
-#include <mbgl/util/geo.hpp>
-#include <mbgl/util/optional.hpp>
-
@implementation MGLMultiPoint
{
mbgl::optional<mbgl::LatLngBounds> _bounds;
@@ -27,6 +26,39 @@
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder
+{
+ if (self = [super initWithCoder:decoder]) {
+ _coordinates = [decoder mgl_decodeLocationCoordinates2DForKey:@"coordinates"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder
+{
+ [super encodeWithCoder:coder];
+ [coder mgl_encodeLocationCoordinates2D:_coordinates forKey:@"coordinates"];
+}
+
+- (BOOL)isEqual:(id)other
+{
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLMultiPoint class]]) return NO;
+
+ MGLMultiPoint *otherMultipoint = other;
+ return ([super isEqual:otherMultipoint]
+ && _coordinates == otherMultipoint->_coordinates);
+}
+
+- (NSUInteger)hash
+{
+ NSUInteger hash = [super hash];
+ for (auto coord : _coordinates) {
+ hash += @(coord.latitude+coord.longitude).hash;
+ }
+ return hash;
+}
+
- (CLLocationCoordinate2D)coordinate
{
NSAssert([self pointCount] > 0, @"A multipoint must have coordinates");
diff --git a/platform/darwin/src/MGLPointAnnotation.mm b/platform/darwin/src/MGLPointAnnotation.mm
index d2e87f07d1..a2108a9e3b 100644
--- a/platform/darwin/src/MGLPointAnnotation.mm
+++ b/platform/darwin/src/MGLPointAnnotation.mm
@@ -1,6 +1,7 @@
#import "MGLPointAnnotation.h"
#import "MGLShape_Private.h"
+#import "NSCoder+MGLAdditions.h"
#import <mbgl/util/geometry.hpp>
@@ -9,6 +10,41 @@
@synthesize coordinate;
++ (BOOL)supportsSecureCoding
+{
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ if (self = [super initWithCoder:coder]) {
+ self.coordinate = [coder decodeMGLCoordinateForKey:@"coordinate"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder
+{
+ [super encodeWithCoder:coder];
+ [coder encodeMGLCoordinate:coordinate forKey:@"coordinate"];
+}
+
+- (BOOL)isEqual:(id)other
+{
+ if (other == self) return YES;
+ if (![other isKindOfClass:[MGLPointAnnotation class]]) return NO;
+
+ MGLPointAnnotation *otherAnnotation = other;
+ return ([super isEqual:other]
+ && self.coordinate.latitude == otherAnnotation.coordinate.latitude
+ && self.coordinate.longitude == otherAnnotation.coordinate.longitude);
+}
+
+- (NSUInteger)hash
+{
+ return [super hash] + @(self.coordinate.latitude).hash + @(self.coordinate.longitude).hash;
+}
+
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; title = %@; subtitle = %@; coordinate = %f, %f>",
diff --git a/platform/darwin/src/MGLPointCollection.mm b/platform/darwin/src/MGLPointCollection.mm
index 387a575b2d..acd78b8b33 100644
--- a/platform/darwin/src/MGLPointCollection.mm
+++ b/platform/darwin/src/MGLPointCollection.mm
@@ -1,5 +1,6 @@
#import "MGLPointCollection_Private.h"
#import "MGLGeometry_Private.h"
+#import "NSArray+MGLAdditions.h"
#import <mbgl/util/geojson.hpp>
#import <mbgl/util/geometry.hpp>
@@ -8,12 +9,10 @@ NS_ASSUME_NONNULL_BEGIN
@implementation MGLPointCollection
{
- MGLCoordinateBounds _overlayBounds;
+ mbgl::optional<mbgl::LatLngBounds> _bounds;
std::vector<CLLocationCoordinate2D> _coordinates;
}
-@synthesize overlayBounds = _overlayBounds;
-
+ (instancetype)pointCollectionWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count
{
return [[self alloc] initWithCoordinates:coords count:count];
@@ -25,14 +24,41 @@ NS_ASSUME_NONNULL_BEGIN
if (self)
{
_coordinates = { coords, coords + count };
+ }
+ return self;
+}
+
+- (nullable instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super initWithCoder:decoder]) {
+ NSArray *coordinates = [decoder decodeObjectOfClass:[NSArray class] forKey:@"coordinates"];
+ _coordinates = [coordinates mgl_coordinates];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:[NSArray mgl_coordinatesFromCoordinates:_coordinates] forKey:@"coordinates"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLPointCollection class]]) return NO;
+
+ MGLPointCollection *otherCollection = (MGLPointCollection *)other;
+ return ([super isEqual:other]
+ && ((![self geoJSONDictionary] && ![otherCollection geoJSONDictionary]) || [[self geoJSONDictionary] isEqualToDictionary:[otherCollection geoJSONDictionary]]));
+}
+
+- (MGLCoordinateBounds)overlayBounds {
+ if (!_bounds) {
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
- for (auto coordinate : _coordinates)
- {
+ for (auto coordinate : _coordinates) {
bounds.extend(mbgl::LatLng(coordinate.latitude, coordinate.longitude));
}
- _overlayBounds = MGLCoordinateBoundsFromLatLngBounds(bounds);
+ _bounds = bounds;
}
- return self;
+ return MGLCoordinateBoundsFromLatLngBounds(*_bounds);
}
- (NSUInteger)pointCount
@@ -65,7 +91,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds
{
- return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
+ return MGLCoordinateBoundsIntersectsCoordinateBounds(self.overlayBounds, overlayBounds);
}
- (mbgl::Geometry<double>)geometryObject
diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm
index 393bd31d0d..565de017cc 100644
--- a/platform/darwin/src/MGLPolygon.mm
+++ b/platform/darwin/src/MGLPolygon.mm
@@ -28,6 +28,32 @@
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (self) {
+ _interiorPolygons = [decoder decodeObjectOfClass:[NSArray class] forKey:@"interiorPolygons"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:self.interiorPolygons forKey:@"interiorPolygons"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLPolygon class]]) return NO;
+
+ MGLPolygon *otherPolygon = (MGLPolygon *)other;
+ return ([super isEqual:otherPolygon] &&
+ [[self geoJSONDictionary] isEqualToDictionary:[otherPolygon geoJSONDictionary]]);
+}
+
+- (NSUInteger)hash {
+ return [super hash] + [[self geoJSONDictionary] hash];
+}
+
- (mbgl::LinearRing<double>)ring {
NSUInteger count = self.pointCount;
CLLocationCoordinate2D *coordinates = self.coordinates;
@@ -100,6 +126,35 @@
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super initWithCoder:decoder]) {
+ _polygons = [decoder decodeObjectOfClass:[NSArray class] forKey:@"polygons"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:_polygons forKey:@"polygons"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLMultiPolygon class]]) return NO;
+
+ MGLMultiPolygon *otherMultiPolygon = other;
+ return [super isEqual:other]
+ && [self.polygons isEqualToArray:otherMultiPolygon.polygons];
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = [super hash];
+ for (MGLPolygon *polygon in self.polygons) {
+ hash += [polygon hash];
+ }
+ return hash;
+}
+
- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
}
diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm
index 0baeb68e1a..e6b1cdebf6 100644
--- a/platform/darwin/src/MGLPolyline.mm
+++ b/platform/darwin/src/MGLPolyline.mm
@@ -80,6 +80,36 @@
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super initWithCoder:decoder]) {
+ _polylines = [decoder decodeObjectOfClass:[NSArray class] forKey:@"polylines"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:_polylines forKey:@"polylines"];
+}
+
+- (BOOL)isEqual:(id)other
+{
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLMultiPolyline class]]) return NO;
+
+ MGLMultiPolyline *otherMultipoline = other;
+ return ([super isEqual:otherMultipoline]
+ && [self.polylines isEqualToArray:otherMultipoline.polylines]);
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = [super hash];
+ for (MGLPolyline *polyline in self.polylines) {
+ hash += [polyline hash];
+ }
+ return hash;
+}
+
- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
}
diff --git a/platform/darwin/src/MGLShape.h b/platform/darwin/src/MGLShape.h
index ecb66118a1..4063ab39ea 100644
--- a/platform/darwin/src/MGLShape.h
+++ b/platform/darwin/src/MGLShape.h
@@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
you can add some kinds of shapes directly to a map view as annotations or
overlays.
*/
-@interface MGLShape : NSObject <MGLAnnotation>
+@interface MGLShape : NSObject <MGLAnnotation, NSSecureCoding>
#pragma mark Creating a Shape
diff --git a/platform/darwin/src/MGLShape.mm b/platform/darwin/src/MGLShape.mm
index 889ef8b3d7..4ddf7c9df7 100644
--- a/platform/darwin/src/MGLShape.mm
+++ b/platform/darwin/src/MGLShape.mm
@@ -2,6 +2,15 @@
#import "MGLFeature_Private.h"
+#import "NSString+MGLAdditions.h"
+#import "MGLTypes.h"
+
+#import <mbgl/util/geo.hpp>
+
+bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D rhs) {
+ return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude;
+}
+
@implementation MGLShape
+ (nullable MGLShape *)shapeWithData:(NSData *)data encoding:(NSStringEncoding)encoding error:(NSError * _Nullable *)outError {
@@ -42,10 +51,63 @@
return [string dataUsingEncoding:NSUTF8StringEncoding];
}
-- (CLLocationCoordinate2D)coordinate {
- [NSException raise:@"MGLAbstractClassException"
- format:@"MGLShape is an abstract class"];
++ (BOOL)supportsSecureCoding
+{
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ if (self = [super init]) {
+ _title = [coder decodeObjectOfClass:[NSString class] forKey:@"title"];
+ _subtitle = [coder decodeObjectOfClass:[NSString class] forKey:@"subtitle"];
+#if !TARGET_OS_IPHONE
+ _toolTip = [coder decodeObjectOfClass:[NSString class] forKey:@"toolTip"];
+#endif
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder
+{
+ [coder encodeObject:_title forKey:@"title"];
+ [coder encodeObject:_subtitle forKey:@"subtitle"];
+#if !TARGET_OS_IPHONE
+ [coder encodeObject:_toolTip forKey:@"toolTip"];
+#endif
+}
+
+- (BOOL)isEqual:(id)other
+{
+ if (other == self) { return YES; }
+ id <MGLAnnotation> annotation = other;
+
+#if TARGET_OS_IPHONE
+ return ((!_title && ![annotation title]) || [_title isEqualToString:[annotation title]])
+ && ((!_subtitle && ![annotation subtitle]) || [_subtitle isEqualToString:[annotation subtitle]]);
+#else
+ return ((!_title && ![annotation title]) || [_title isEqualToString:[annotation title]])
+ && ((!_subtitle && ![annotation subtitle]) || [_subtitle isEqualToString:[annotation subtitle]])
+ && ((!_toolTip && ![annotation toolTip]) || [_toolTip isEqualToString:[annotation toolTip]]);
+#endif
+}
+
+- (NSUInteger)hash
+{
+ NSUInteger hash;
+ hash += _title.hash;
+ hash += _subtitle.hash;
+#if !TARGET_OS_IPHONE
+ hash += _toolTip.hash;
+#endif
+ return hash;
+}
+- (CLLocationCoordinate2D)coordinate
+{
+ [[NSException exceptionWithName:@"MGLAbstractClassException"
+ reason:@"MGLShape is an abstract class"
+ userInfo:nil] raise];
return kCLLocationCoordinate2DInvalid;
}
diff --git a/platform/darwin/src/MGLShapeCollection.mm b/platform/darwin/src/MGLShapeCollection.mm
index e317a443fe..c8f0d09f9a 100644
--- a/platform/darwin/src/MGLShapeCollection.mm
+++ b/platform/darwin/src/MGLShapeCollection.mm
@@ -18,6 +18,35 @@
return self;
}
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ if (self = [super initWithCoder:decoder]) {
+ _shapes = [decoder decodeObjectOfClass:[NSArray class] forKey:@"shapes"];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:_shapes forKey:@"shapes"];
+}
+
+- (BOOL)isEqual:(id)other {
+ if (self == other) return YES;
+ if (![other isKindOfClass:[MGLShapeCollection class]]) return NO;
+
+ MGLShapeCollection *otherShapeCollection = other;
+ return [super isEqual:otherShapeCollection]
+ && [_shapes isEqualToArray:otherShapeCollection.shapes];
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = [super hash];
+ for (MGLShape *shape in _shapes) {
+ hash += [shape hash];
+ }
+ return hash;
+}
+
- (CLLocationCoordinate2D)coordinate {
return _shapes.firstObject.coordinate;
}
diff --git a/platform/darwin/src/MGLShape_Private.h b/platform/darwin/src/MGLShape_Private.h
index 15d1c1eb97..9821d49176 100644
--- a/platform/darwin/src/MGLShape_Private.h
+++ b/platform/darwin/src/MGLShape_Private.h
@@ -2,6 +2,9 @@
#import <mbgl/util/geojson.hpp>
#import <mbgl/util/geometry.hpp>
+#import <mbgl/util/geo.hpp>
+
+bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D rhs);
@interface MGLShape (Private)
diff --git a/platform/darwin/src/NSArray+MGLAdditions.h b/platform/darwin/src/NSArray+MGLAdditions.h
index eb1cfb7c47..c4dfd8207b 100644
--- a/platform/darwin/src/NSArray+MGLAdditions.h
+++ b/platform/darwin/src/NSArray+MGLAdditions.h
@@ -1,4 +1,5 @@
#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
#import <mbgl/util/feature.hpp>
@@ -9,4 +10,18 @@
/** Returns a string resulting from inserting a separator between each attributed string in the array */
- (NSAttributedString *)mgl_attributedComponentsJoinedByString:(NSString *)separator;
+/**
+ Converts std::vector<CLLocationCoordinate> into an NSArray containing dictionary
+ representations of coordinates with the following structure:
+ [{"latitude": lat, "longitude": lng}]
+ */
++ (NSArray *)mgl_coordinatesFromCoordinates:(std::vector<CLLocationCoordinate2D>)coords;
+
+/**
+ Converts the receiver into a std::vector<CLLocationCoordinate>.
+ Receiver must conform to the following structure:
+ [{"latitude": lat, "longitude": lng}]
+ */
+- (std::vector<CLLocationCoordinate2D>)mgl_coordinates;
+
@end
diff --git a/platform/darwin/src/NSArray+MGLAdditions.mm b/platform/darwin/src/NSArray+MGLAdditions.mm
index b2799c46e1..f2c5a096cc 100644
--- a/platform/darwin/src/NSArray+MGLAdditions.mm
+++ b/platform/darwin/src/NSArray+MGLAdditions.mm
@@ -38,4 +38,28 @@
return attributedString;
}
++ (NSArray *)mgl_coordinatesFromCoordinates:(std::vector<CLLocationCoordinate2D>)coords {
+ NSMutableArray *coordinates = [NSMutableArray array];
+ for (auto coord : coords) {
+ [coordinates addObject:@{@"latitude": @(coord.latitude),
+ @"longitude": @(coord.longitude)}];
+ }
+ return coordinates;
+}
+
+- (std::vector<CLLocationCoordinate2D>)mgl_coordinates {
+ NSUInteger numberOfCoordinates = [self count];
+ CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(numberOfCoordinates * sizeof(CLLocationCoordinate2D));
+
+ for (NSUInteger i = 0; i < numberOfCoordinates; i++) {
+ coords[i] = CLLocationCoordinate2DMake([self[i][@"latitude"] doubleValue],
+ [self[i][@"longitude"] doubleValue]);
+ }
+
+ std::vector<CLLocationCoordinate2D> coordinates = { coords, coords + numberOfCoordinates };
+ free(coords);
+
+ return coordinates;
+}
+
@end
diff --git a/platform/darwin/src/NSCoder+MGLAdditions.h b/platform/darwin/src/NSCoder+MGLAdditions.h
new file mode 100644
index 0000000000..036a99c5af
--- /dev/null
+++ b/platform/darwin/src/NSCoder+MGLAdditions.h
@@ -0,0 +1,16 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import <mbgl/util/feature.hpp>
+
+@interface NSCoder (MGLAdditions)
+
+- (void)encodeMGLCoordinate:(CLLocationCoordinate2D)coordinate forKey:(NSString *)key;
+
+- (CLLocationCoordinate2D)decodeMGLCoordinateForKey:(NSString *)key;
+
+- (void)mgl_encodeLocationCoordinates2D:(std::vector<CLLocationCoordinate2D>)coordinates forKey:(NSString *)key;
+
+- (std::vector<CLLocationCoordinate2D>)mgl_decodeLocationCoordinates2DForKey:(NSString *)key;
+
+@end
diff --git a/platform/darwin/src/NSCoder+MGLAdditions.mm b/platform/darwin/src/NSCoder+MGLAdditions.mm
new file mode 100644
index 0000000000..4af6c7588b
--- /dev/null
+++ b/platform/darwin/src/NSCoder+MGLAdditions.mm
@@ -0,0 +1,26 @@
+#import "NSCoder+MGLAdditions.h"
+
+#import "NSArray+MGLAdditions.h"
+#import "NSValue+MGLAdditions.h"
+
+@implementation NSCoder (MGLAdditions)
+
+- (void)mgl_encodeLocationCoordinates2D:(std::vector<CLLocationCoordinate2D>)coordinates forKey:(NSString *)key {
+ [self encodeObject:[NSArray mgl_coordinatesFromCoordinates:coordinates] forKey:key];
+}
+
+- (std::vector<CLLocationCoordinate2D>)mgl_decodeLocationCoordinates2DForKey:(NSString *)key {
+ NSArray *coordinates = [self decodeObjectOfClass:[NSArray class] forKey:key];
+ return [coordinates mgl_coordinates];
+}
+
+- (void)encodeMGLCoordinate:(CLLocationCoordinate2D)coordinate forKey:(NSString *)key {
+ [self encodeObject:@{@"latitude": @(coordinate.latitude), @"longitude": @(coordinate.longitude)} forKey:key];
+}
+
+- (CLLocationCoordinate2D)decodeMGLCoordinateForKey:(NSString *)key {
+ NSDictionary *coordinate = [self decodeObjectForKey:key];
+ return CLLocationCoordinate2DMake([coordinate[@"latitude"] doubleValue], [coordinate[@"longitude"] doubleValue]);
+}
+
+@end
diff --git a/platform/darwin/test/MGLCodingTests.m b/platform/darwin/test/MGLCodingTests.m
new file mode 100644
index 0000000000..b9b299d50f
--- /dev/null
+++ b/platform/darwin/test/MGLCodingTests.m
@@ -0,0 +1,469 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+#if TARGET_OS_IPHONE
+#import "MGLUserLocation_Private.h"
+#endif
+
+@interface MGLCodingTests : XCTestCase
+@end
+
+@implementation MGLCodingTests
+
+- (NSString *)temporaryFilePathForClass:(Class)clazz {
+ return [NSTemporaryDirectory() stringByAppendingPathComponent:NSStringFromClass(clazz)];
+}
+
+- (void)testPointAnnotation {
+ MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init];
+ annotation.coordinate = CLLocationCoordinate2DMake(0.5, 0.5);
+ annotation.title = @"title";
+ annotation.subtitle = @"subtitle";
+
+ NSString *filePath = [self temporaryFilePathForClass:MGLPointAnnotation.class];
+ [NSKeyedArchiver archiveRootObject:annotation toFile:filePath];
+ MGLPointAnnotation *unarchivedAnnotation = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(annotation, unarchivedAnnotation);
+}
+
+- (void)testPointFeature {
+ MGLPointFeature *pointFeature = [[MGLPointFeature alloc] init];
+ pointFeature.title = @"title";
+ pointFeature.subtitle = @"subtitle";
+ pointFeature.identifier = @(123);
+ pointFeature.attributes = @{@"bbox": @[@1, @2, @3, @4]};
+
+ NSString *filePath = [self temporaryFilePathForClass:MGLPointFeature.class];
+ [NSKeyedArchiver archiveRootObject:pointFeature toFile:filePath];
+ MGLPointFeature *unarchivedPointFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(pointFeature, unarchivedPointFeature);
+}
+
+- (void)testPolyline {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0.129631234123, 1.7812739312551),
+ CLLocationCoordinate2DMake(2.532083092342, 3.5216418292392)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ polyline.title = @"title";
+ polyline.subtitle = @"subtitle";
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolyline class]];
+ [NSKeyedArchiver archiveRootObject:polyline toFile:filePath];
+ MGLPolyline *unarchivedPolyline = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polyline, unarchivedPolyline);
+
+ CLLocationCoordinate2D otherCoordinates[] = {
+ CLLocationCoordinate2DMake(-1, -2)
+ };
+
+ [unarchivedPolyline replaceCoordinatesInRange:NSMakeRange(0, 1) withCoordinates:otherCoordinates];
+
+ XCTAssertNotEqualObjects(polyline, unarchivedPolyline);
+}
+
+- (void)testPolygon {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0.664482398, 1.8865675),
+ CLLocationCoordinate2DMake(2.13224687, 3.9984632)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates];
+ polygon.title = nil;
+ polygon.subtitle = @"subtitle";
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolygon class]];
+ [NSKeyedArchiver archiveRootObject:polygon toFile:filePath];
+
+ MGLPolygon *unarchivedPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polygon, unarchivedPolygon);
+}
+
+- (void)testPolygonWithInteriorPolygons {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 20)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ CLLocationCoordinate2D interiorCoordinates[] = {
+ CLLocationCoordinate2DMake(4, 4),
+ CLLocationCoordinate2DMake(6, 6)
+ };
+
+ NSUInteger numberOfInteriorCoordinates = sizeof(interiorCoordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolygon *interiorPolygon = [MGLPolygon polygonWithCoordinates:interiorCoordinates count:numberOfInteriorCoordinates];
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates interiorPolygons:@[interiorPolygon]];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolygon class]];
+ [NSKeyedArchiver archiveRootObject:polygon toFile:filePath];
+
+ MGLPolygon *unarchivedPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polygon, unarchivedPolygon);
+}
+
+- (void)testPolylineFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 20)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+ MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ polylineFeature.attributes = @{@"bbox": @[@0, @1, @2, @3]};
+ polylineFeature.identifier = @"identifier";
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolylineFeature class]];
+ [NSKeyedArchiver archiveRootObject:polylineFeature toFile:filePath];
+
+ MGLPolylineFeature *unarchivedPolylineFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polylineFeature, unarchivedPolylineFeature);
+
+ unarchivedPolylineFeature.attributes = @{@"bbox": @[@4, @3, @2, @1]};
+
+ XCTAssertNotEqualObjects(polylineFeature, unarchivedPolylineFeature);
+}
+
+- (void)testPolygonFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 20)
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+ MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPolygonFeature class]];
+ [NSKeyedArchiver archiveRootObject:polygonFeature toFile:filePath];
+
+ MGLPolygonFeature *unarchivedPolygonFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(polygonFeature, unarchivedPolygonFeature);
+
+ unarchivedPolygonFeature.identifier = @"test";
+
+ XCTAssertNotEqualObjects(polygonFeature, unarchivedPolygonFeature);
+}
+
+- (void)testPointCollection {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 11),
+ CLLocationCoordinate2DMake(20, 21),
+ CLLocationCoordinate2DMake(30, 31),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPointCollection *pointCollection = [MGLPointCollection pointCollectionWithCoordinates:coordinates count:numberOfCoordinates];
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPointCollection class]];
+ [NSKeyedArchiver archiveRootObject:pointCollection toFile:filePath];
+
+ MGLPointCollection *unarchivedPointCollection = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(pointCollection, unarchivedPointCollection);
+}
+
+- (void)testPointCollectionFeature {
+ NSMutableArray *features = [NSMutableArray array];
+ for (NSUInteger i = 0; i < 100; i++) {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ feature.coordinate = CLLocationCoordinate2DMake(arc4random() % 90, arc4random() % 180);
+ [features addObject:feature];
+ }
+
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 11),
+ CLLocationCoordinate2DMake(20, 21),
+ CLLocationCoordinate2DMake(30, 31),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPointCollectionFeature *collection = [MGLPointCollectionFeature pointCollectionWithCoordinates:coordinates count:numberOfCoordinates];
+ collection.identifier = @"identifier";
+ collection.attributes = @{@"bbox": @[@1, @2, @3, @4]};
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLPointCollectionFeature class]];
+ [NSKeyedArchiver archiveRootObject:collection toFile:filePath];
+
+ MGLPointCollectionFeature *unarchivedCollection = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(collection, unarchivedCollection);
+
+ unarchivedCollection.identifier = @"newIdentifier";
+
+ XCTAssertNotEqualObjects(collection, unarchivedCollection);
+}
+
+- (void)testMultiPolyline {
+
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 11),
+ CLLocationCoordinate2DMake(20, 21),
+ CLLocationCoordinate2DMake(30, 31),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ NSMutableArray *polylines = [NSMutableArray array];
+
+ for (NSUInteger i = 0; i < 100; i++) {
+ MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ [polylines addObject:polyline];
+ }
+
+ MGLMultiPolyline *multiPolyline = [MGLMultiPolyline multiPolylineWithPolylines:polylines];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLMultiPolyline class]];
+ [NSKeyedArchiver archiveRootObject:multiPolyline toFile:filePath];
+
+ MGLMultiPolyline *unarchivedMultiPolyline = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLMultiPolyline *anotherMultipolyline = [MGLMultiPolyline multiPolylineWithPolylines:[polylines subarrayWithRange:NSMakeRange(0, polylines.count/2)]];
+
+ XCTAssertEqualObjects(multiPolyline, unarchivedMultiPolyline);
+ XCTAssertNotEqualObjects(unarchivedMultiPolyline, anotherMultipolyline);
+}
+
+- (void)testMultiPolygon {
+
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(10, 11),
+ CLLocationCoordinate2DMake(20, 21),
+ CLLocationCoordinate2DMake(30, 31),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ NSMutableArray *polygons = [NSMutableArray array];
+
+ for (NSUInteger i = 0; i < 100; i++) {
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates];
+ [polygons addObject:polygon];
+ }
+
+ MGLMultiPolygon *multiPolygon = [MGLMultiPolygon multiPolygonWithPolygons:polygons];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLMultiPolygon class]];
+ [NSKeyedArchiver archiveRootObject:multiPolygon toFile:filePath];
+
+ MGLMultiPolygon *unarchivedMultiPolygon = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLMultiPolygon *anotherMultiPolygon = [MGLMultiPolygon multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count/2)]];
+
+ XCTAssertEqualObjects(multiPolygon, unarchivedMultiPolygon);
+ XCTAssertNotEqualObjects(anotherMultiPolygon, unarchivedMultiPolygon);
+}
+
+- (void)testShapeCollection {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(10.12315786, 11.23451186),
+ CLLocationCoordinate2DMake(20.91836515, 21.93689215),
+ CLLocationCoordinate2DMake(30.55697246, 31.33988123),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:numberOfCoordinates];
+
+ MGLShapeCollection *shapeCollection = [MGLShapeCollection shapeCollectionWithShapes:@[polyline, polygon]];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLShapeCollection class]];
+ [NSKeyedArchiver archiveRootObject:shapeCollection toFile:filePath];
+
+ MGLShapeCollection *unarchivedShapeCollection = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLShapeCollection *anotherShapeCollection = [MGLShapeCollection shapeCollectionWithShapes:@[polygon]];
+
+ XCTAssertEqualObjects(shapeCollection, unarchivedShapeCollection);
+ XCTAssertNotEqualObjects(shapeCollection, anotherShapeCollection);
+}
+
+- (void)testMultiPolylineFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(10.12315786, 11.23451186),
+ CLLocationCoordinate2DMake(20.91836515, 21.93689215),
+ CLLocationCoordinate2DMake(30.55697246, 31.33988123),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ NSMutableArray *polylines = [NSMutableArray array];
+ for (NSUInteger i = 0; i < 100; i++) {
+ MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ polylineFeature.identifier = @(arc4random() % 100).stringValue;
+ [polylines addObject:polylineFeature];
+ }
+
+ MGLMultiPolylineFeature *multiPolylineFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:polylines];
+ multiPolylineFeature.attributes = @{@"bbox": @[@4, @3, @2, @1]};
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLMultiPolylineFeature class]];
+ [NSKeyedArchiver archiveRootObject:multiPolylineFeature toFile:filePath];
+
+ MGLMultiPolylineFeature *unarchivedMultiPolylineFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLMultiPolylineFeature *anotherMultiPolylineFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:[polylines subarrayWithRange:NSMakeRange(0, polylines.count/2)]];
+
+ XCTAssertEqualObjects(multiPolylineFeature, unarchivedMultiPolylineFeature);
+ XCTAssertNotEqualObjects(unarchivedMultiPolylineFeature, anotherMultiPolylineFeature);
+}
+
+- (void)testMultiPolygonFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(10.12315786, 11.23451185),
+ CLLocationCoordinate2DMake(20.88471238, 21.93684215),
+ CLLocationCoordinate2DMake(30.15697236, 31.32988123),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ NSMutableArray *polygons = [NSMutableArray array];
+ for (NSUInteger i = 0; i < 100; i++ ) {
+ MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
+ polygonFeature.identifier = @(arc4random_uniform(100)).stringValue;
+ [polygons addObject:polygonFeature];
+ }
+
+ MGLMultiPolygonFeature *multiPolygonFeature = [MGLMultiPolygonFeature multiPolygonWithPolygons:polygons];
+ multiPolygonFeature.attributes = @{@"bbox": @[@(arc4random_uniform(100)),
+ @(arc4random_uniform(100)),
+ @(arc4random_uniform(100)),
+ @(arc4random_uniform(100))]};
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLMultiPolylineFeature class]];
+ [NSKeyedArchiver archiveRootObject:multiPolygonFeature toFile:filePath];
+
+ MGLMultiPolygonFeature *unarchivedMultiPolygonFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+ MGLMultiPolygonFeature *anotherMultiPolygonFeature = [MGLMultiPolygonFeature multiPolygonWithPolygons:[polygons subarrayWithRange:NSMakeRange(0, polygons.count/2)]];
+
+ XCTAssertEqualObjects(multiPolygonFeature, unarchivedMultiPolygonFeature);
+ XCTAssertNotEqualObjects(anotherMultiPolygonFeature, unarchivedMultiPolygonFeature);
+}
+
+- (void)testShapeCollectionFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(10.12315786, 11.23451186),
+ CLLocationCoordinate2DMake(20.91836515, 21.93689215),
+ CLLocationCoordinate2DMake(30.55697246, 31.33988123),
+ };
+
+ NSUInteger numberOfCoordinates = sizeof(coordinates) / sizeof(CLLocationCoordinate2D);
+
+ MGLPolylineFeature *polyline = [MGLPolylineFeature polylineWithCoordinates:coordinates count:numberOfCoordinates];
+ MGLPolygonFeature *polygon = [MGLPolygonFeature polygonWithCoordinates:coordinates count:numberOfCoordinates];
+
+ MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[polyline, polygon]];
+ shapeCollectionFeature.identifier = @(arc4random_uniform(100)).stringValue;
+ shapeCollectionFeature.attributes = @{@"bbox":@[@(arc4random_uniform(100)),
+ @(arc4random_uniform(100)),
+ @(arc4random_uniform(100)),
+ @(arc4random_uniform(100))]};
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLShapeCollectionFeature class]];
+ [NSKeyedArchiver archiveRootObject:shapeCollectionFeature toFile:filePath];
+
+ MGLShapeCollectionFeature *unarchivedShapeCollectionFeature = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(shapeCollectionFeature, unarchivedShapeCollectionFeature);
+}
+
+- (void)testAnnotationImage {
+#if TARGET_OS_IPHONE
+ UIGraphicsBeginImageContext(CGSizeMake(10, 10));
+ [[UIColor redColor] setFill];
+ UIRectFill(CGRectMake(0, 0, 10, 10));
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+#else
+ NSImage *image = [[NSImage alloc] initWithSize:CGSizeMake(10, 10)];
+ [image lockFocus];
+ [[NSColor redColor] drawSwatchInRect:CGRectMake(0, 0, 10, 10)];
+ [image unlockFocus];
+#endif
+
+ MGLAnnotationImage *annotationImage = [MGLAnnotationImage annotationImageWithImage:image reuseIdentifier:@(arc4random_uniform(100)).stringValue];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLAnnotationImage class]];
+ [NSKeyedArchiver archiveRootObject:annotationImage toFile:filePath];
+
+ MGLAnnotationImage *unarchivedAnnotationImage = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(annotationImage, unarchivedAnnotationImage);
+}
+
+#if TARGET_OS_IPHONE
+- (void)testAnnotationView {
+ MGLAnnotationView *annotationView = [[MGLAnnotationView alloc] initWithReuseIdentifier:@"id"];
+ annotationView.enabled = NO;
+ annotationView.selected = YES;
+ annotationView.draggable = YES;
+ annotationView.centerOffset = CGVectorMake(10, 10);
+ annotationView.scalesWithViewingDistance = NO;
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLAnnotationView class]];
+ [NSKeyedArchiver archiveRootObject:annotationView toFile:filePath];
+
+ MGLAnnotationView *unarchivedAnnotationView = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqual(annotationView.enabled, unarchivedAnnotationView.enabled);
+ XCTAssertEqual(annotationView.selected, unarchivedAnnotationView.selected);
+ XCTAssertEqual(annotationView.draggable, unarchivedAnnotationView.draggable);
+ XCTAssertEqualObjects(NSStringFromCGVector(annotationView.centerOffset), NSStringFromCGVector(unarchivedAnnotationView.centerOffset));
+ XCTAssertEqual(annotationView.scalesWithViewingDistance, unarchivedAnnotationView.scalesWithViewingDistance);
+}
+#endif
+
+#if TARGET_OS_IPHONE
+- (void)testUserLocation {
+ MGLUserLocation *userLocation = [[MGLUserLocation alloc] init];
+ userLocation.location = [[CLLocation alloc] initWithLatitude:1 longitude:1];
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLUserLocation class]];
+ [NSKeyedArchiver archiveRootObject:userLocation toFile:filePath];
+
+ MGLUserLocation *unarchivedUserLocation = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqualObjects(userLocation, unarchivedUserLocation);
+ unarchivedUserLocation.location = [[CLLocation alloc] initWithLatitude:10 longitude:10];
+ XCTAssertNotEqualObjects(userLocation, unarchivedUserLocation);
+}
+#endif
+
+#if TARGET_OS_IPHONE
+- (void)testUserLocationAnnotationView {
+ MGLUserLocationAnnotationView *annotationView = [[MGLUserLocationAnnotationView alloc] init];
+ annotationView.enabled = NO;
+ annotationView.selected = YES;
+ annotationView.draggable = YES;
+ annotationView.centerOffset = CGVectorMake(10, 10);
+ annotationView.scalesWithViewingDistance = NO;
+
+ NSString *filePath = [self temporaryFilePathForClass:[MGLUserLocationAnnotationView class]];
+ [NSKeyedArchiver archiveRootObject:annotationView toFile:filePath];
+
+ MGLUserLocationAnnotationView *unarchivedAnnotationView = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ XCTAssertEqual(annotationView.enabled, unarchivedAnnotationView.enabled);
+ XCTAssertEqual(annotationView.selected, unarchivedAnnotationView.selected);
+ XCTAssertEqual(annotationView.draggable, unarchivedAnnotationView.draggable);
+ XCTAssertEqualObjects(NSStringFromCGVector(annotationView.centerOffset), NSStringFromCGVector(unarchivedAnnotationView.centerOffset));
+ XCTAssertEqual(annotationView.scalesWithViewingDistance, unarchivedAnnotationView.scalesWithViewingDistance);
+}
+#endif
+
+@end