summaryrefslogtreecommitdiff
path: root/platform/darwin
diff options
context:
space:
mode:
Diffstat (limited to 'platform/darwin')
-rw-r--r--platform/darwin/scripts/generate-style-code.js3
-rw-r--r--platform/darwin/src/MGLFeature.h25
-rw-r--r--platform/darwin/src/MGLFeature.mm8
-rw-r--r--platform/darwin/src/MGLFeature_Private.h3
-rw-r--r--platform/darwin/src/MGLGeoJSONSource.h33
-rw-r--r--platform/darwin/src/MGLGeoJSONSource.mm134
-rw-r--r--platform/darwin/src/MGLGeoJSONSource_Private.h1
-rw-r--r--platform/darwin/src/MGLMultiPoint.h21
-rw-r--r--platform/darwin/src/MGLMultiPoint.mm28
-rw-r--r--platform/darwin/src/MGLOfflineStorage.h27
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm28
-rw-r--r--platform/darwin/src/MGLPointCollection.h51
-rw-r--r--platform/darwin/src/MGLPointCollection.mm112
-rw-r--r--platform/darwin/src/MGLPointCollection_Private.h11
-rw-r--r--platform/darwin/src/MGLPolygon.h2
-rw-r--r--platform/darwin/src/MGLPolyline.h2
-rw-r--r--platform/darwin/src/MGLRasterSource.mm23
-rw-r--r--platform/darwin/src/MGLSource.mm17
-rw-r--r--platform/darwin/src/MGLSource_Private.h23
-rw-r--r--platform/darwin/src/MGLStyle.mm12
-rw-r--r--platform/darwin/src/MGLStyleValue.h4
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h2
-rw-r--r--platform/darwin/src/MGLTypes.h8
-rw-r--r--platform/darwin/src/MGLVectorSource.mm22
-rw-r--r--platform/darwin/test/MGLFeatureTests.mm37
-rw-r--r--platform/darwin/test/MGLGeoJSONSourceTests.mm242
-rw-r--r--platform/darwin/test/MGLOfflineStorageTests.m4
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.m1
-rw-r--r--platform/darwin/test/MGLStyleValueTests.swift36
-rw-r--r--platform/darwin/test/MGLTileSetTests.mm82
-rw-r--r--platform/darwin/test/test-Bridging-Header.h3
31 files changed, 824 insertions, 181 deletions
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 4bcfe54d32..62ab3b6b98 100644
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -152,6 +152,9 @@ global.describeValue = function (value, property, layerType) {
case 'number':
return 'an `NSNumber` object containing the float `' + value + '`';
case 'string':
+ if (value === '') {
+ return 'the empty string';
+ }
return 'the string `' + value + '`';
case 'enum':
let displayValue;
diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h
index 239a338f67..384c5a073e 100644
--- a/platform/darwin/src/MGLFeature.h
+++ b/platform/darwin/src/MGLFeature.h
@@ -3,6 +3,7 @@
#import "MGLPolyline.h"
#import "MGLPolygon.h"
#import "MGLPointAnnotation.h"
+#import "MGLPointCollection.h"
#import "MGLShapeCollection.h"
NS_ASSUME_NONNULL_BEGIN
@@ -18,8 +19,8 @@ NS_ASSUME_NONNULL_BEGIN
using `-[MGLMapView visibleFeaturesAtPoint:]` and related methods. Each feature
object associates a shape with an identifier and attributes as specified by the
source. Like ordinary `MGLAnnotation` objects, some kinds of `MGLFeature`
- objects can also be added to a map view using `-[MGLMapView addAnnotations:]`
- and related methods.
+ objects can also be added to a map view using an `MGLGeoJSONSource` or
+ `-[MGLMapView addAnnotations:]` and related methods.
*/
@protocol MGLFeature <MGLAnnotation>
@@ -48,9 +49,13 @@ NS_ASSUME_NONNULL_BEGIN
For details about the identifiers used in most Mapbox-provided styles, consult
the
<a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets</a>
- layer reference.
+ layer reference. Note that while it is possible to change this value on feature
+ instances obtained from `-[MGLMapView visibleFeaturesAtPoint:]` and related
+ methods, there will be no effect on the map. Setting this value can be useful
+ when the feature instance is used to initialize an `MGLGeoJSONSource` and that
+ source is added to the map and styled.
*/
-@property (nonatomic, copy, nullable, readonly) id identifier;
+@property (nonatomic, copy, nullable) id identifier;
/**
A dictionary of attributes for this feature specified by the
@@ -79,9 +84,13 @@ NS_ASSUME_NONNULL_BEGIN
<a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets</a>
and
<a href="https://www.mapbox.com/vector-tiles/mapbox-terrain/">Mapbox Terrain</a>
- layer references.
+ layer references. Note that while it is possible to change this value on feature
+ instances obtained from `-[MGLMapView visibleFeaturesAtPoint:]` and related
+ methods, there will be no effect on the map. Setting this value can be useful
+ when the feature instance is used to initialize an `MGLGeoJSONSource` and that
+ source is added to the map and styled.
*/
-@property (nonatomic, copy, readonly) NS_DICTIONARY_OF(NSString *, id) *attributes;
+@property (nonatomic, copy) NS_DICTIONARY_OF(NSString *, id) *attributes;
/**
Returns the feature attribute for the given attribute name.
@@ -126,10 +135,10 @@ NS_ASSUME_NONNULL_BEGIN
@end
/**
- The `MGLMultiPointFeature` class represents a multipoint in a
+ The `MGLPointCollectionFeature` class represents a multipoint in a
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
*/
-@interface MGLMultiPointFeature : MGLMultiPoint <MGLFeature>
+@interface MGLPointCollectionFeature : MGLPointCollection <MGLFeature>
@end
/**
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
index 84dcc4f0bb..5483433710 100644
--- a/platform/darwin/src/MGLFeature.mm
+++ b/platform/darwin/src/MGLFeature.mm
@@ -6,7 +6,7 @@
#import "MGLValueEvaluator.h"
#import "MGLShape_Private.h"
-#import "MGLMultiPoint_Private.h"
+#import "MGLPointCollection_Private.h"
#import "MGLPolyline+MGLAdditions.h"
#import "MGLPolygon+MGLAdditions.h"
#import "NSDictionary+MGLAdditions.h"
@@ -82,10 +82,10 @@
@end
-@interface MGLMultiPointFeature () <MGLFeaturePrivate>
+@interface MGLPointCollectionFeature () <MGLFeaturePrivate>
@end
-@implementation MGLMultiPointFeature
+@implementation MGLPointCollectionFeature
@synthesize identifier;
@synthesize attributes;
@@ -202,7 +202,7 @@ public:
MGLShape <MGLFeaturePrivate> * operator()(const mbgl::MultiPoint<T> &geometry) const {
std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(geometry);
- return [[MGLMultiPointFeature alloc] initWithCoordinates:&coordinates[0] count:coordinates.size()];
+ return [[MGLPointCollectionFeature alloc] initWithCoordinates:&coordinates[0] count:coordinates.size()];
}
MGLShape <MGLFeaturePrivate> * operator()(const mbgl::MultiLineString<T> &geometry) const {
diff --git a/platform/darwin/src/MGLFeature_Private.h b/platform/darwin/src/MGLFeature_Private.h
index 5fb82bde5b..e6858c7c11 100644
--- a/platform/darwin/src/MGLFeature_Private.h
+++ b/platform/darwin/src/MGLFeature_Private.h
@@ -26,9 +26,6 @@ NS_DICTIONARY_OF(NSString *, id) *NSDictionaryFeatureForGeometry(NSDictionary *g
@protocol MGLFeaturePrivate <MGLFeature>
-@property (nonatomic, copy, nullable, readwrite) id identifier;
-@property (nonatomic, copy, readwrite) NS_DICTIONARY_OF(NSString *, id) *attributes;
-
- (mbgl::Feature)mbglFeature;
@end
diff --git a/platform/darwin/src/MGLGeoJSONSource.h b/platform/darwin/src/MGLGeoJSONSource.h
index 9eeb1e1188..30232c6211 100644
--- a/platform/darwin/src/MGLGeoJSONSource.h
+++ b/platform/darwin/src/MGLGeoJSONSource.h
@@ -7,33 +7,38 @@ NS_ASSUME_NONNULL_BEGIN
@protocol MGLFeature;
/**
+ Options for `MGLGeoJSONSource` objects.
+ */
+typedef NSString *MGLGeoJSONSourceOption NS_STRING_ENUM;
+
+/**
An `NSNumber` object containing a Boolean enabling or disabling clustering.
If the `features` property contains point features, setting this option to
`YES` clusters the points by radius into groups. The default value is `NO`.
*/
-extern NSString * const MGLGeoJSONClusterOption;
+extern const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionClustered;
/**
An `NSNumber` object containing an integer; specifies the radius of each
cluster if clustering is enabled. A value of 512 produces a radius equal to
the width of a tile. The default value is 50.
*/
-extern NSString * const MGLGeoJSONClusterRadiusOption;
+extern const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionClusterRadius;
/**
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 `MGLGeoJSONMaximumZoomLevelOption` so that, at the
+ less than the value of `MGLGeoJSONSourceOptionMaximumZoomLevel` so that, at the
maximum zoom level, the features are not clustered.
*/
-extern NSString * const MGLGeoJSONClusterMaximumZoomLevelOption;
+extern const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionMaximumZoomLevelForClustering;
/**
An `NSNumber` object containing an integer; specifies the maximum zoom level at
which to create vector tiles. A greater value produces greater detail at high
zoom levels. The default value is 18.
*/
-extern NSString * const MGLGeoJSONMaximumZoomLevelOption;
+extern const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionMaximumZoomLevel;
/**
An `NSNumber` object containing an integer; specifies the size of the tile
@@ -41,14 +46,14 @@ extern NSString * const MGLGeoJSONMaximumZoomLevelOption;
buffer as wide as the tile itself. Larger values produce fewer rendering
artifacts near tile edges and slower performance. The default value is 128.
*/
-extern NSString * const MGLGeoJSONBufferOption;
+extern const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionBuffer;
/**
An `NSNumber` object containing a double; specifies the Douglas-Peucker
simplification tolerance. A greater value produces simpler geometries and
improves performance. The default value is 0.375.
*/
-extern NSString * const MGLGeoJSONToleranceOption;
+extern const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionSimplificationTolerance;
/**
A GeoJSON source.
@@ -71,7 +76,7 @@ extern NSString * const MGLGeoJSONToleranceOption;
@param options An `NSDictionary` of options for this source.
@return An initialized GeoJSON source.
*/
-- (instancetype)initWithIdentifier:(NSString *)identifier geoJSONData:(NSData *)data options:(nullable NS_DICTIONARY_OF(NSString *, id) *)options NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithIdentifier:(NSString *)identifier geoJSONData:(NSData *)data options:(nullable NS_DICTIONARY_OF(MGLGeoJSONSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
/**
Returns a GeoJSON source with an identifier, URL, and dictionary of options for
@@ -85,7 +90,7 @@ extern NSString * const MGLGeoJSONToleranceOption;
@param options An `NSDictionary` of options for this source.
@return An initialized GeoJSON source.
*/
-- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(nullable NS_DICTIONARY_OF(NSString *, id) *)options NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(nullable NS_DICTIONARY_OF(MGLGeoJSONSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
/**
Returns a GeoJSON source with an identifier, features dictionary, and dictionary
@@ -98,7 +103,7 @@ extern NSString * const MGLGeoJSONToleranceOption;
@param options An `NSDictionary` of options for this source.
@return An initialized GeoJSON source.
*/
-- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray<id<MGLFeature>> *)features options:(nullable NS_DICTIONARY_OF(NSString *,id) *)options NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray<id<MGLFeature>> *)features options:(nullable NS_DICTIONARY_OF(MGLGeoJSONSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
#pragma mark Accessing a Source’s Content
@@ -109,13 +114,13 @@ extern NSString * const MGLGeoJSONToleranceOption;
is set to `nil`. This property is unavailable until the receiver is passed into
`-[MGLStyle addSource]`.
*/
-@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLFeature>) *features;
+@property (nonatomic, nullable) NS_ARRAY_OF(id <MGLFeature>) *features;
/**
A GeoJSON representation of the contents of the source.
Use the `features` property instead to get an object representation of the
- contents. Alternatively, use NSJSONSerialization with the value of this
+ contents. Alternatively, use `NSJSONSerialization` with the value of this
property to transform it into Foundation types.
If the receiver was initialized using `-initWithIdentifier:URL:options` or
@@ -123,7 +128,7 @@ extern NSString * const MGLGeoJSONToleranceOption;
This property is unavailable until the receiver is passed
into `-[MGLStyle addSource]`.
*/
-@property (nonatomic, readonly, nullable, copy) NSData *geoJSONData;
+@property (nonatomic, nullable, copy) NSData *geoJSONData;
/**
The URL to the GeoJSON document that specifies the contents of the source.
@@ -131,7 +136,7 @@ extern NSString * const MGLGeoJSONToleranceOption;
If the receiver was initialized using `-initWithIdentifier:geoJSONData:options`, this
property is set to `nil`.
*/
-@property (nonatomic, readonly, nullable) NSURL *URL;
+@property (nonatomic, nullable) NSURL *URL;
@end
diff --git a/platform/darwin/src/MGLGeoJSONSource.mm b/platform/darwin/src/MGLGeoJSONSource.mm
index 27f2eb8bda..0dbe1030c6 100644
--- a/platform/darwin/src/MGLGeoJSONSource.mm
+++ b/platform/darwin/src/MGLGeoJSONSource.mm
@@ -1,5 +1,6 @@
-#import "MGLGeoJSONSource.h"
+#import "MGLGeoJSONSource_Private.h"
+#import "MGLMapView_Private.h"
#import "MGLSource_Private.h"
#import "MGLFeature_Private.h"
@@ -7,20 +8,24 @@
#include <mbgl/style/sources/geojson_source.hpp>
-NSString * const MGLGeoJSONClusterOption = @"MGLGeoJSONCluster";
-NSString * const MGLGeoJSONClusterRadiusOption = @"MGLGeoJSONClusterRadius";
-NSString * const MGLGeoJSONClusterMaximumZoomLevelOption = @"MGLGeoJSONClusterMaximumZoomLevel";
-NSString * const MGLGeoJSONMaximumZoomLevelOption = @"MGLGeoJSONMaximumZoomLevel";
-NSString * const MGLGeoJSONBufferOption = @"MGLGeoJSONBuffer";
-NSString * const MGLGeoJSONToleranceOption = @"MGLGeoJSONOptionsClusterTolerance";
+const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionClustered = @"MGLGeoJSONSourceOptionClustered";
+const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionClusterRadius = @"MGLGeoJSONSourceOptionClusterRadius";
+const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionMaximumZoomLevelForClustering = @"MGLGeoJSONSourceOptionMaximumZoomLevelForClustering";
+const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionMaximumZoomLevel = @"MGLGeoJSONSourceOptionMaximumZoomLevel";
+const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionBuffer = @"MGLGeoJSONSourceOptionBuffer";
+const MGLGeoJSONSourceOption MGLGeoJSONSourceOptionSimplificationTolerance = @"MGLGeoJSONSourceOptionSimplificationTolerance";
@interface MGLGeoJSONSource ()
@property (nonatomic, readwrite) NSDictionary *options;
+@property (nonatomic) mbgl::style::GeoJSONSource *rawSource;
@end
@implementation MGLGeoJSONSource
+{
+ std::unique_ptr<mbgl::style::GeoJSONSource> _pendingSource;
+}
- (instancetype)initWithIdentifier:(NSString *)identifier geoJSONData:(NSData *)data options:(NS_DICTIONARY_OF(NSString *, id) *)options
{
@@ -28,6 +33,7 @@ NSString * const MGLGeoJSONToleranceOption = @"MGLGeoJSONOptionsClusterTolerance
{
_geoJSONData = data;
_options = options;
+ [self commonInit];
}
return self;
}
@@ -38,6 +44,7 @@ NSString * const MGLGeoJSONToleranceOption = @"MGLGeoJSONOptionsClusterTolerance
{
_URL = url;
_options = options;
+ [self commonInit];
}
return self;
}
@@ -46,47 +53,75 @@ NSString * const MGLGeoJSONToleranceOption = @"MGLGeoJSONOptionsClusterTolerance
if (self = [super initWithIdentifier:identifier]) {
_features = features;
_options = options;
+ [self commonInit];
}
return self;
}
+- (void)addToMapView:(MGLMapView *)mapView
+{
+ mapView.mbglMap->addSource(std::move(_pendingSource));
+}
+
+- (void)commonInit
+{
+ auto source = std::make_unique<mbgl::style::GeoJSONSource>(self.identifier.UTF8String, self.geoJSONOptions);
+
+ if (self.URL) {
+ NSURL *url = self.URL.mgl_URLByStandardizingScheme;
+ source->setURL(url.absoluteString.UTF8String);
+ _features = nil;
+ } else if (self.geoJSONData) {
+ NSString *string = [[NSString alloc] initWithData:self.geoJSONData encoding:NSUTF8StringEncoding];
+ const auto geojson = mapbox::geojson::parse(string.UTF8String).get<mapbox::geojson::feature_collection>();
+ source->setGeoJSON(geojson);
+ _features = MGLFeaturesFromMBGLFeatures(geojson);
+ } else {
+ mbgl::FeatureCollection featureCollection;
+ featureCollection.reserve(self.features.count);
+ for (id <MGLFeaturePrivate> feature in self.features) {
+ featureCollection.push_back([feature mbglFeature]);
+ }
+ const auto geojson = mbgl::GeoJSON{featureCollection};
+ source->setGeoJSON(geojson);
+ _features = MGLFeaturesFromMBGLFeatures(featureCollection);
+ }
+
+ _pendingSource = std::move(source);
+ self.rawSource = _pendingSource.get();
+}
+
- (mbgl::style::GeoJSONOptions)geoJSONOptions
{
auto mbglOptions = mbgl::style::GeoJSONOptions();
- if (self.options[MGLGeoJSONMaximumZoomLevelOption]) {
- id value = self.options[MGLGeoJSONMaximumZoomLevelOption];
+ if (id value = self.options[MGLGeoJSONSourceOptionMaximumZoomLevel]) {
[self validateValue:value];
mbglOptions.maxzoom = [value integerValue];
}
- if (self.options[MGLGeoJSONBufferOption]) {
- id value = self.options[MGLGeoJSONBufferOption];
+ if (id value = self.options[MGLGeoJSONSourceOptionBuffer]) {
[self validateValue:value];
mbglOptions.buffer = [value integerValue];
}
- if (self.options[MGLGeoJSONToleranceOption]) {
- id value = self.options[MGLGeoJSONToleranceOption];
+ if (id value = self.options[MGLGeoJSONSourceOptionSimplificationTolerance]) {
[self validateValue:value];
mbglOptions.tolerance = [value doubleValue];
}
- if (self.options[MGLGeoJSONClusterRadiusOption]) {
- id value = self.options[MGLGeoJSONClusterRadiusOption];
+ if (id value = self.options[MGLGeoJSONSourceOptionClusterRadius]) {
[self validateValue:value];
mbglOptions.clusterRadius = [value integerValue];
}
- if (self.options[MGLGeoJSONClusterMaximumZoomLevelOption]) {
- id value = self.options[MGLGeoJSONClusterMaximumZoomLevelOption];
+ if (id value = self.options[MGLGeoJSONSourceOptionMaximumZoomLevelForClustering]) {
[self validateValue:value];
mbglOptions.clusterMaxZoom = [value integerValue];
}
- if (self.options[MGLGeoJSONClusterOption]) {
- id value = self.options[MGLGeoJSONClusterOption];
+ if (id value = self.options[MGLGeoJSONSourceOptionClustered]) {
[self validateValue:value];
mbglOptions.cluster = [value boolValue];
}
@@ -102,30 +137,51 @@ NSString * const MGLGeoJSONToleranceOption = @"MGLGeoJSONOptionsClusterTolerance
}
}
-- (std::unique_ptr<mbgl::style::Source>)mbglSource
+- (void)setGeoJSONData:(NSData *)geoJSONData
{
- auto source = std::make_unique<mbgl::style::GeoJSONSource>(self.identifier.UTF8String, self.geoJSONOptions);
+ _geoJSONData = geoJSONData;
- if (self.URL) {
- NSURL *url = self.URL.mgl_URLByStandardizingScheme;
- source->setURL(url.absoluteString.UTF8String);
- } else if (self.geoJSONData) {
- NSString *string = [[NSString alloc] initWithData:self.geoJSONData encoding:NSUTF8StringEncoding];
- const auto geojson = mapbox::geojson::parse(string.UTF8String).get<mapbox::geojson::feature_collection>();
- source->setGeoJSON(geojson);
- _features = MGLFeaturesFromMBGLFeatures(geojson);
- } else {
- mbgl::FeatureCollection featureCollection;
- featureCollection.reserve(self.features.count);
- for (id <MGLFeaturePrivate> feature in self.features) {
- featureCollection.push_back([feature mbglFeature]);
- }
- const auto geojson = mbgl::GeoJSON{featureCollection};
- source->setGeoJSON(geojson);
- _features = MGLFeaturesFromMBGLFeatures(featureCollection);
+ if (self.rawSource == NULL)
+ {
+ [self commonInit];
+ }
+
+ NSString *string = [[NSString alloc] initWithData:_geoJSONData encoding:NSUTF8StringEncoding];
+ const auto geojson = mapbox::geojson::parse(string.UTF8String).get<mapbox::geojson::feature_collection>();
+ self.rawSource->setGeoJSON(geojson);
+
+ _features = MGLFeaturesFromMBGLFeatures(geojson);
+}
+
+- (void)setURL:(NSURL *)URL
+{
+ _URL = URL;
+
+ if (self.rawSource == NULL)
+ {
+ [self commonInit];
+ }
+
+ NSURL *url = self.URL.mgl_URLByStandardizingScheme;
+ self.rawSource->setURL(url.absoluteString.UTF8String);
+}
+
+- (void)setFeatures:(NSArray *)features
+{
+ if (self.rawSource == NULL)
+ {
+ [self commonInit];
+ }
+
+ mbgl::FeatureCollection featureCollection;
+ featureCollection.reserve(features.count);
+ for (id <MGLFeaturePrivate> feature in features) {
+ featureCollection.push_back([feature mbglFeature]);
}
+ const auto geojson = mbgl::GeoJSON{featureCollection};
+ self.rawSource->setGeoJSON(geojson);
- return std::move(source);
+ _features = MGLFeaturesFromMBGLFeatures(featureCollection);
}
@end
diff --git a/platform/darwin/src/MGLGeoJSONSource_Private.h b/platform/darwin/src/MGLGeoJSONSource_Private.h
index 3aeb07ad25..de5bb10fac 100644
--- a/platform/darwin/src/MGLGeoJSONSource_Private.h
+++ b/platform/darwin/src/MGLGeoJSONSource_Private.h
@@ -1,3 +1,4 @@
+#import "MGLGeoJSONSource.h"
#import "MGLGeoJSONSource_Private.h"
#include <mbgl/style/sources/geojson_source.hpp>
diff --git a/platform/darwin/src/MGLMultiPoint.h b/platform/darwin/src/MGLMultiPoint.h
index b936205ab2..69c7295842 100644
--- a/platform/darwin/src/MGLMultiPoint.h
+++ b/platform/darwin/src/MGLMultiPoint.h
@@ -6,25 +6,16 @@
NS_ASSUME_NONNULL_BEGIN
/**
- The `MGLMultiPoint` class is used to define shapes composed of multiple points.
- This class is also the superclass of `MGLPolyline` and `MGLPolygon`. The
- methods and properties of this class can be used to access information about
- the specific points associated with a line or polygon.
+ The `MGLMultiPoint` class is an abstract superclass used to define shapes
+ composed of multiple points. You should not create instances of this class
+ directly. Instead, you should create instances of the `MGLPolyline` or
+ `MGLPolygon` classes. However, you can use the method and properties of this
+ class to access information about the specific points associated with the line
+ or polygon.
*/
@interface MGLMultiPoint : MGLShape
/**
- Creates and returns an `MGLMultiPoint` object from the specified set of
- coordinates.
-
- @param coords The array of coordinates defining the shape. The data in this
- array is copied to the new object.
- @param count The number of items in the `coords` array.
- @return A new multipoint object.
- */
-+ (instancetype)multiPointWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count NS_SWIFT_NAME(multiPoint(coordinates:count:));
-
-/**
The array of coordinates associated with the shape.
This C array is a pointer to a structure inside the multipoint object,
diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm
index 0090c5e35f..17a61ed081 100644
--- a/platform/darwin/src/MGLMultiPoint.mm
+++ b/platform/darwin/src/MGLMultiPoint.mm
@@ -18,11 +18,6 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor)
MGLCoordinateBounds _bounds;
}
-+ (instancetype)multiPointWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count
-{
- return [[self alloc] initWithCoordinates:coords count:count];
-}
-
- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count
{
self = [super init];
@@ -146,29 +141,6 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor)
return mbgl::SymbolAnnotation({mbgl::Point<double>()});
}
-- (mbgl::Feature)featureObject
-{
- mbgl::MultiPoint<double> multiPoint;
- multiPoint.reserve(self.pointCount);
- for (NSInteger i = 0; i< self.pointCount; i++)
- {
- multiPoint.push_back(mbgl::Point<double>(self.coordinates[i].longitude, self.coordinates[i].latitude));
- }
- return mbgl::Feature {multiPoint};
-}
-
-- (NSDictionary *)geoJSONDictionary
-{
- NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
- for (NSUInteger index = 0; index < self.pointCount; index++) {
- CLLocationCoordinate2D coordinate = self.coordinates[index];
- [coordinates addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
- }
-
- return @{@"type": @"MultiPoint",
- @"coordinates": coordinates};
-}
-
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; count = %lu; bounds = %@>",
diff --git a/platform/darwin/src/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h
index 211135f84f..b3fb7a2d54 100644
--- a/platform/darwin/src/MGLOfflineStorage.h
+++ b/platform/darwin/src/MGLOfflineStorage.h
@@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
alternatively observe KVO change notifications to the pack’s `progress` key
path.
*/
-extern NSString * const MGLOfflinePackProgressChangedNotification;
+extern const NSNotificationName MGLOfflinePackProgressChangedNotification;
/**
Posted by the shared `MGLOfflineStorage` object whenever an `MGLOfflinePack`
@@ -37,7 +37,7 @@ extern NSString * const MGLOfflinePackProgressChangedNotification;
`userInfo` dictionary contains the error object in the
`MGLOfflinePackErrorUserInfoKey` key.
*/
-extern NSString * const MGLOfflinePackErrorNotification;
+extern const NSNotificationName MGLOfflinePackErrorNotification;
/**
Posted by the shared `MGLOfflineStorage` object when the maximum number of
@@ -52,7 +52,12 @@ extern NSString * const MGLOfflinePackErrorNotification;
calling the `-[MGLOfflineStorage removePack:withCompletionHandler:]` method.
Contact your Mapbox sales representative to have the limit raised.
*/
-extern NSString * const MGLOfflinePackMaximumMapboxTilesReachedNotification;
+extern const NSNotificationName MGLOfflinePackMaximumMapboxTilesReachedNotification;
+
+/**
+ A key in the `userInfo` property of a notification posted by `MGLOfflinePack`.
+ */
+typedef NSString *MGLOfflinePackUserInfoKey NS_EXTENSIBLE_STRING_ENUM;
/**
The key for an `NSNumber` object that indicates an offline pack’s current
@@ -60,7 +65,9 @@ extern NSString * const MGLOfflinePackMaximumMapboxTilesReachedNotification;
`MGLOfflinePackProgressChangedNotification` notification. Call `-integerValue`
on the object to receive the `MGLOfflinePackState`-typed state.
*/
-extern NSString * const MGLOfflinePackStateUserInfoKey;
+extern const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyState;
+
+extern NSString * const MGLOfflinePackStateUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyState")));
/**
The key for an `NSValue` object that indicates an offline pack’s current
@@ -69,7 +76,9 @@ extern NSString * const MGLOfflinePackStateUserInfoKey;
`-MGLOfflinePackProgressValue` on the object to receive the
`MGLOfflinePackProgress`-typed progress.
*/
-extern NSString * const MGLOfflinePackProgressUserInfoKey;
+extern const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress;
+
+extern NSString * const MGLOfflinePackProgressUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyProgress")));
/**
The key for an `NSError` object that is encountered in the course of
@@ -77,7 +86,9 @@ extern NSString * const MGLOfflinePackProgressUserInfoKey;
an `MGLOfflinePackErrorNotification` notification. The error’s domain is
`MGLErrorDomain`. See `MGLErrorCode` for possible error codes.
*/
-extern NSString * const MGLOfflinePackErrorUserInfoKey;
+extern const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError;
+
+extern NSString * const MGLOfflinePackErrorUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyError")));
/**
The key for an `NSNumber` object that indicates the maximum number of
@@ -87,7 +98,9 @@ extern NSString * const MGLOfflinePackErrorUserInfoKey;
`-unsignedLongLongValue` on the object to receive the `uint64_t`-typed tile
limit.
*/
-extern NSString * const MGLOfflinePackMaximumCountUserInfoKey;
+extern const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount;
+
+extern NSString * const MGLOfflinePackMaximumCountUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyMaximumCount")));
/**
A block to be called once an offline pack has been completely created and
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
index 5f284b76a1..10acc58b25 100644
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ b/platform/darwin/src/MGLOfflineStorage.mm
@@ -13,14 +13,18 @@
static NSString * const MGLOfflineStorageFileName = @"cache.db";
static NSString * const MGLOfflineStorageFileName3_2_0_beta_1 = @"offline.db";
-NSString * const MGLOfflinePackProgressChangedNotification = @"MGLOfflinePackProgressChanged";
-NSString * const MGLOfflinePackErrorNotification = @"MGLOfflinePackError";
-NSString * const MGLOfflinePackMaximumMapboxTilesReachedNotification = @"MGLOfflinePackMaximumMapboxTilesReached";
-
-NSString * const MGLOfflinePackStateUserInfoKey = @"State";
-NSString * const MGLOfflinePackProgressUserInfoKey = @"Progress";
-NSString * const MGLOfflinePackErrorUserInfoKey = @"Error";
-NSString * const MGLOfflinePackMaximumCountUserInfoKey = @"MaximumCount";
+const NSNotificationName MGLOfflinePackProgressChangedNotification = @"MGLOfflinePackProgressChanged";
+const NSNotificationName MGLOfflinePackErrorNotification = @"MGLOfflinePackError";
+const NSNotificationName MGLOfflinePackMaximumMapboxTilesReachedNotification = @"MGLOfflinePackMaximumMapboxTilesReached";
+
+const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyState = @"State";
+NSString * const MGLOfflinePackStateUserInfoKey = MGLOfflinePackUserInfoKeyState;
+const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress = @"Progress";
+NSString * const MGLOfflinePackProgressUserInfoKey = MGLOfflinePackUserInfoKeyProgress;
+const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError = @"Error";
+NSString * const MGLOfflinePackErrorUserInfoKey = MGLOfflinePackUserInfoKeyError;
+const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"MaximumCount";
+NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoKeyMaximumCount;
@interface MGLOfflineStorage () <MGLOfflinePackDelegate>
@@ -317,20 +321,20 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = @"MaximumCount";
- (void)offlinePack:(MGLOfflinePack *)pack progressDidChange:(__unused MGLOfflinePackProgress)progress {
[[NSNotificationCenter defaultCenter] postNotificationName:MGLOfflinePackProgressChangedNotification object:pack userInfo:@{
- MGLOfflinePackStateUserInfoKey: @(pack.state),
- MGLOfflinePackProgressUserInfoKey: [NSValue valueWithMGLOfflinePackProgress:progress],
+ MGLOfflinePackUserInfoKeyState: @(pack.state),
+ MGLOfflinePackUserInfoKeyProgress: [NSValue valueWithMGLOfflinePackProgress:progress],
}];
}
- (void)offlinePack:(MGLOfflinePack *)pack didReceiveError:(NSError *)error {
[[NSNotificationCenter defaultCenter] postNotificationName:MGLOfflinePackErrorNotification object:pack userInfo:@{
- MGLOfflinePackErrorUserInfoKey: error,
+ MGLOfflinePackUserInfoKeyError: error,
}];
}
- (void)offlinePack:(MGLOfflinePack *)pack didReceiveMaximumAllowedMapboxTiles:(uint64_t)maximumCount {
[[NSNotificationCenter defaultCenter] postNotificationName:MGLOfflinePackMaximumMapboxTilesReachedNotification object:pack userInfo:@{
- MGLOfflinePackMaximumCountUserInfoKey: @(maximumCount),
+ MGLOfflinePackUserInfoKeyMaximumCount: @(maximumCount),
}];
}
diff --git a/platform/darwin/src/MGLPointCollection.h b/platform/darwin/src/MGLPointCollection.h
new file mode 100644
index 0000000000..db497d0a52
--- /dev/null
+++ b/platform/darwin/src/MGLPointCollection.h
@@ -0,0 +1,51 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLOverlay.h"
+#import "MGLShape.h"
+
+/**
+ The `MGLPointCollection` class is used to define an array of disconnected
+ coordinates. The points in the collection may be related but are not
+ connected visually in any way.
+
+ @note `MGLPointCollection` objects cannot be added to a map view using
+ `-[MGLMapView addAnnotations:]` and related methods. However, when used in a
+ `MGLPointCollectionFeature` to initialize a `MGLGeoJSONSource` that is added
+ to the map view's style, the point collection represents as a group of distinct
+ annotations.
+ */
+@interface MGLPointCollection : MGLShape <MGLOverlay>
+
+/**
+ Creates and returns a `MGLPointCollection` object from the specified set of
+ coordinates.
+
+ @param coords The array of coordinates defining the shape. The data in this
+ array is copied to the new object.
+ @param count The number of items in the `coords` array.
+ @return A new point collection object.
+ */
++ (instancetype)pointCollectionWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count;
+
+/** The array of coordinates associated with the shape. */
+@property (nonatomic, readonly) CLLocationCoordinate2D *coordinates NS_RETURNS_INNER_POINTER;
+
+/** The number of coordinates associated with the shape. */
+@property (nonatomic, readonly) NSUInteger pointCount;
+
+/**
+ Retrieves one or more coordinates associated with the shape.
+
+ @param coords On input, you must provide a C array of structures large enough
+ to hold the desired number of coordinates. On output, this structure
+ contains the requested coordinate data.
+ @param range The range of points you want. The `location` field indicates the
+ first point you are requesting, with `0` being the first point, `1` being
+ the second point, and so on. The `length` field indicates the number of
+ points you want. The array in _`coords`_ must be large enough to accommodate
+ the number of requested coordinates.
+ */
+- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range;
+
+@end
diff --git a/platform/darwin/src/MGLPointCollection.mm b/platform/darwin/src/MGLPointCollection.mm
new file mode 100644
index 0000000000..5871915b5d
--- /dev/null
+++ b/platform/darwin/src/MGLPointCollection.mm
@@ -0,0 +1,112 @@
+#import "MGLPointCollection_Private.h"
+#import "MGLGeometry_Private.h"
+
+#import <mbgl/util/geometry.hpp>
+#import <mbgl/util/feature.hpp>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation MGLPointCollection
+{
+ size_t _count;
+ MGLCoordinateBounds _bounds;
+}
+
++ (instancetype)pointCollectionWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count
+{
+ return [[self alloc] initWithCoordinates:coords count:count];
+}
+
+- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count
+{
+ self = [super init];
+ if (self)
+ {
+ _count = count;
+ _coordinates = (CLLocationCoordinate2D *)malloc(_count * sizeof(CLLocationCoordinate2D));
+
+ mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
+
+ for (NSUInteger i = 0; i < count; i++)
+ {
+ _coordinates[i] = coords[i];
+ bounds.extend(mbgl::LatLng(coords[i].latitude, coords[i].longitude));
+ }
+
+ _bounds = MGLCoordinateBoundsFromLatLngBounds(bounds);
+ }
+ return self;
+}
+
+- (NSUInteger)pointCount
+{
+ return _count;
+}
+
+- (CLLocationCoordinate2D)coordinate
+{
+ assert(_count > 0);
+
+ return CLLocationCoordinate2DMake(_coordinates[0].latitude, _coordinates[0].longitude);
+}
+
+- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range
+{
+ if (range.location + range.length > _count)
+ {
+ [NSException raise:NSRangeException
+ format:@"Invalid coordinate range %@ extends beyond current coordinate count of %zu",
+ NSStringFromRange(range), _count];
+ }
+
+ NSUInteger index = 0;
+
+ for (NSUInteger i = range.location; i < range.location + range.length; i++)
+ {
+ coords[index] = _coordinates[i];
+ index++;
+ }
+}
+
+- (MGLCoordinateBounds)overlayBounds
+{
+ return _bounds;
+}
+
+- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds
+{
+ return MGLLatLngBoundsFromCoordinateBounds(_bounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds));
+}
+
+- (mbgl::Feature)featureObject
+{
+ mbgl::MultiPoint<double> multiPoint;
+ multiPoint.reserve(self.pointCount);
+ for (NSInteger i = 0; i< self.pointCount; i++)
+ {
+ multiPoint.push_back(mbgl::Point<double>(self.coordinates[i].longitude, self.coordinates[i].latitude));
+ }
+ return mbgl::Feature {multiPoint};
+}
+
+- (NSDictionary *)geoJSONDictionary
+{
+ NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
+ for (NSUInteger index = 0; index < self.pointCount; index++) {
+ CLLocationCoordinate2D coordinate = self.coordinates[index];
+ [coordinates addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
+ }
+
+ return @{@"type": @"MultiPoint",
+ @"coordinates": coordinates};
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; count = %lu>",
+ NSStringFromClass([self class]), (void *)self, (unsigned long)_count];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLPointCollection_Private.h b/platform/darwin/src/MGLPointCollection_Private.h
new file mode 100644
index 0000000000..039c1f18be
--- /dev/null
+++ b/platform/darwin/src/MGLPointCollection_Private.h
@@ -0,0 +1,11 @@
+#import "MGLPointCollection.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLPointCollection (Private)
+
+- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLPolygon.h b/platform/darwin/src/MGLPolygon.h
index 6d53356ba4..3d5b36abb6 100644
--- a/platform/darwin/src/MGLPolygon.h
+++ b/platform/darwin/src/MGLPolygon.h
@@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN
@param count The number of items in the `coords` array.
@return A new polygon object.
*/
-+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count NS_SWIFT_NAME(polygon(coordinates:count:));
++ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count;
/**
Creates and returns an `MGLPolygon` object from the specified set of
diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h
index 6642afef7e..d0274b44e3 100644
--- a/platform/darwin/src/MGLPolyline.h
+++ b/platform/darwin/src/MGLPolyline.h
@@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
@param count The number of items in the `coords` array.
@return A new polyline object.
*/
-+ (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count NS_SWIFT_NAME(polyline(coordinates:count:));
++ (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count;
@end
diff --git a/platform/darwin/src/MGLRasterSource.mm b/platform/darwin/src/MGLRasterSource.mm
index 41b9a5b043..fc47c23853 100644
--- a/platform/darwin/src/MGLRasterSource.mm
+++ b/platform/darwin/src/MGLRasterSource.mm
@@ -1,18 +1,29 @@
#import "MGLRasterSource.h"
+#import "MGLMapView_Private.h"
#import "MGLSource_Private.h"
#import "MGLTileSet_Private.h"
#import "NSURL+MGLAdditions.h"
#include <mbgl/style/sources/raster_source.hpp>
+@interface MGLRasterSource ()
+
+@property (nonatomic) mbgl::style::RasterSource *rawSource;
+
+@end
+
@implementation MGLRasterSource
+{
+ std::unique_ptr<mbgl::style::RasterSource> _pendingSource;
+}
- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url tileSize:(CGFloat)tileSize
{
if (self = [super initWithIdentifier:identifier]) {
_URL = url;
_tileSize = tileSize;
+ [self commonInit];
}
return self;
}
@@ -23,11 +34,12 @@
{
_tileSet = tileSet;
_tileSize = tileSize;
+ [self commonInit];
}
return self;
}
-- (std::unique_ptr<mbgl::style::Source>)mbglSource
+- (void)commonInit
{
std::unique_ptr<mbgl::style::RasterSource> source;
@@ -42,10 +54,15 @@
source = std::make_unique<mbgl::style::RasterSource>(self.identifier.UTF8String,
self.tileSet.mbglTileset,
uint16_t(self.tileSize));
-
}
- return std::move(source);
+ _pendingSource = std::move(source);
+ self.rawSource = _pendingSource.get();
+}
+
+- (void)addToMapView:(MGLMapView *)mapView
+{
+ mapView.mbglMap->addSource(std::move(_pendingSource));
}
@end
diff --git a/platform/darwin/src/MGLSource.mm b/platform/darwin/src/MGLSource.mm
index 85bbc06342..1b889d44d7 100644
--- a/platform/darwin/src/MGLSource.mm
+++ b/platform/darwin/src/MGLSource.mm
@@ -2,27 +2,14 @@
#include <mbgl/style/source.hpp>
-@interface MGLSource ()
-
-@property (nonatomic) mbgl::style::Source *source;
-
-@end
-
@implementation MGLSource
-- (instancetype)initWithIdentifier:(NSString *)identifier {
+- (instancetype)initWithIdentifier:(NSString *)identifier
+{
if (self = [super init]) {
_identifier = identifier;
}
return self;
}
-- (std::unique_ptr<mbgl::style::Source>)mbglSource {
- [NSException raise:@"MGLAbstractClassException" format:
- @"The source %@ cannot be added to the style. "
- @"Make sure the source was created as a member of a concrete subclass of MGLSource.",
- NSStringFromClass(self)];
- return nil;
-}
-
@end
diff --git a/platform/darwin/src/MGLSource_Private.h b/platform/darwin/src/MGLSource_Private.h
index 2b8658b4cb..dff230ede5 100644
--- a/platform/darwin/src/MGLSource_Private.h
+++ b/platform/darwin/src/MGLSource_Private.h
@@ -3,10 +3,29 @@
#include <mbgl/mbgl.hpp>
#include <mbgl/style/source.hpp>
+@class MGLMapView;
+
@interface MGLSource (Private)
-- (std::unique_ptr<mbgl::style::Source>)mbglSource;
+/**
+ A raw pointer to the mbgl object, which is always initialized, either to the
+ value returned by `mbgl::Map getSource`, or for independently created objects,
+ to the pointer value held in `pendingSource`. In the latter case, this raw
+ pointer value stays even after ownership of the object is transferred via
+ `mbgl::Map addSource`.
+ */
+@property (nonatomic) mbgl::style::Source *rawSource;
-@property (nonatomic) mbgl::style::Source *source;
+/**
+ Adds the mbgl source that this object represents to the mbgl map.
+
+ Once a mbgl source is added, ownership of the object is transferred to the
+ `mbgl::Map` and this object no longer has an active unique_ptr reference to the
+ `mbgl::Source`. If this object's mbgl source is in that state, the mbgl source
+ can still be changed but the changes will not be visible until the `MGLSource`
+ is added back to the map via `-[MGLStyle addSource:]` and styled with a
+ `MGLLayer`.
+ */
+- (void)addToMapView:(MGLMapView *)mapView;
@end
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index 3964b47ad6..a17b7d6b74 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -142,6 +142,7 @@ static NSURL *MGLStyleURL_emerald;
- (MGLSource *)sourceWithIdentifier:(NSString *)identifier
{
auto mbglSource = self.mapView.mbglMap->getSource(identifier.UTF8String);
+
if (!mbglSource) {
return nil;
}
@@ -159,8 +160,8 @@ static NSURL *MGLStyleURL_emerald;
NSAssert(NO, @"Unrecognized source type");
return nil;
}
-
- source.source = mbglSource;
+
+ source.rawSource = mbglSource;
return source;
}
@@ -205,12 +206,17 @@ static NSURL *MGLStyleURL_emerald;
- (void)addSource:(MGLSource *)source
{
- self.mapView.mbglMap->addSource(source.mbglSource);
+ [source addToMapView:self.mapView];
}
- (void)removeSource:(MGLSource *)source
{
self.mapView.mbglMap->removeSource(source.identifier.UTF8String);
+
+ // Once a mbgl source is removed from the map, ownership does not return
+ // to the MGL source. Therefore, the rawSource pointer is set to NULL
+ // so that the implementation of MGL source can avoid using it again.
+ source.rawSource = NULL;
}
- (NS_ARRAY_OF(NSString *) *)styleClasses
diff --git a/platform/darwin/src/MGLStyleValue.h b/platform/darwin/src/MGLStyleValue.h
index eb0eaf5340..ab5e76bbe3 100644
--- a/platform/darwin/src/MGLStyleValue.h
+++ b/platform/darwin/src/MGLStyleValue.h
@@ -68,7 +68,7 @@ NS_ASSUME_NONNULL_BEGIN
The `MGLStyleConstantValue` class takes a generic parameter `T` that indicates
the Foundation class being wrapped by this class.
*/
-@interface MGLStyleConstantValue<T> : MGLStyleValue
+@interface MGLStyleConstantValue<T> : MGLStyleValue<T>
#pragma mark Creating a Style Constant Value
@@ -111,7 +111,7 @@ NS_ASSUME_NONNULL_BEGIN
The `MGLStyleFunction` class takes a generic parameter `T` that indicates the
Foundation class being wrapped by this class.
*/
-@interface MGLStyleFunction<T> : MGLStyleValue
+@interface MGLStyleFunction<T> : MGLStyleValue<T>
#pragma mark Creating a Style Function
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index 3fcecedca3..9bd943a34e 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -388,7 +388,7 @@ typedef NS_ENUM(NSUInteger, MGLTextTranslateAnchor) {
/**
Value to use for a text label. Feature properties are specified using tokens like {field_name}.
- The default value of this property is an `MGLStyleValue` object containing the string ``. Set this property to `nil` to reset it to the default value.
+ The default value of this property is an `MGLStyleValue` object containing the empty string. Set this property to `nil` to reset it to the default value.
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *textField;
diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h
index a3ea587add..e9e05bc684 100644
--- a/platform/darwin/src/MGLTypes.h
+++ b/platform/darwin/src/MGLTypes.h
@@ -21,7 +21,7 @@
NS_ASSUME_NONNULL_BEGIN
/** Indicates an error occurred in the Mapbox SDK. */
-extern NSString * const MGLErrorDomain;
+extern NSErrorDomain const MGLErrorDomain;
/** Error constants for the Mapbox SDK. */
typedef NS_ENUM(NSInteger, MGLErrorCode) {
@@ -97,3 +97,9 @@ NS_ASSUME_NONNULL_END
#define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary
#endif
#endif
+
+#if !defined(FOUNDATION_SWIFT_SDK_EPOCH_LESS_THAN) || FOUNDATION_SWIFT_SDK_EPOCH_LESS_THAN(8)
+ #define NS_STRING_ENUM
+ #define NS_EXTENSIBLE_STRING_ENUM
+ #define NSNotificationName NSString *
+#endif
diff --git a/platform/darwin/src/MGLVectorSource.mm b/platform/darwin/src/MGLVectorSource.mm
index 3597812359..995565419f 100644
--- a/platform/darwin/src/MGLVectorSource.mm
+++ b/platform/darwin/src/MGLVectorSource.mm
@@ -1,12 +1,22 @@
#import "MGLVectorSource.h"
+#import "MGLMapView_Private.h"
#import "MGLSource_Private.h"
#import "MGLTileSet_Private.h"
#import "NSURL+MGLAdditions.h"
#include <mbgl/style/sources/vector_source.hpp>
+@interface MGLVectorSource ()
+
+@property (nonatomic) mbgl::style::VectorSource *rawSource;
+
+@end
+
@implementation MGLVectorSource
+{
+ std::unique_ptr<mbgl::style::VectorSource> _pendingSource;
+}
static NSString *MGLVectorSourceType = @"vector";
@@ -15,6 +25,7 @@ static NSString *MGLVectorSourceType = @"vector";
if (self = [super initWithIdentifier:identifier])
{
_URL = url;
+ [self commonInit];
}
return self;
}
@@ -24,11 +35,12 @@ static NSString *MGLVectorSourceType = @"vector";
if (self = [super initWithIdentifier:identifier])
{
_tileSet = tileSet;
+ [self commonInit];
}
return self;
}
-- (std::unique_ptr<mbgl::style::Source>)mbglSource
+- (void)commonInit
{
std::unique_ptr<mbgl::style::VectorSource> source;
@@ -43,7 +55,13 @@ static NSString *MGLVectorSourceType = @"vector";
self.tileSet.mbglTileset);
}
- return std::move(source);
+ _pendingSource = std::move(source);
+ self.rawSource = _pendingSource.get();
+}
+
+- (void)addToMapView:(MGLMapView *)mapView
+{
+ mapView.mbglMap->addSource(std::move(_pendingSource));
}
@end
diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm
index 18c3fd16c2..7f464aaab1 100644
--- a/platform/darwin/test/MGLFeatureTests.mm
+++ b/platform/darwin/test/MGLFeatureTests.mm
@@ -227,24 +227,6 @@
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
}
-- (void)testMultiPointFeatureGeoJSONDictionary {
- CLLocationCoordinate2D coord1 = { 0, 0 };
- CLLocationCoordinate2D coord2 = { 10, 10 };
- CLLocationCoordinate2D coord3 = { 0, 0 };
- CLLocationCoordinate2D coords[] = { coord1, coord2, coord3 };
- MGLMultiPointFeature *multiPointFeature = [MGLMultiPointFeature multiPointWithCoordinates:coords count:3];
-
- // A GeoJSON feature
- NSDictionary *geoJSONFeature = [multiPointFeature geoJSONDictionary];
-
- // it has the correct geometry
- NSDictionary *expectedGeometry = @{@"type": @"MultiPoint",
- @"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)],
- @[@(coord2.longitude), @(coord2.latitude)],
- @[@(coord3.longitude), @(coord3.latitude)]]};
- XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
-}
-
- (void)testMultiPolylineFeatureGeoJSONDictionary {
CLLocationCoordinate2D coord1 = { 0, 0 };
CLLocationCoordinate2D coord2 = { 10, 10 };
@@ -296,6 +278,25 @@
XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
}
+- (void)testPointCollectionFeatureGeoJSONDictionary {
+ CLLocationCoordinate2D coord1 = { 0, 0 };
+ CLLocationCoordinate2D coord2 = { 10, 10 };
+ CLLocationCoordinate2D coord3 = { 0, 0 };
+ CLLocationCoordinate2D coords[] = { coord1, coord2, coord3 };
+ MGLPointCollectionFeature *pointCollectionFeature = [MGLPointCollectionFeature pointCollectionWithCoordinates:coords count:3];
+
+ // A GeoJSON feature
+ NSDictionary *geoJSONFeature = [pointCollectionFeature geoJSONDictionary];
+
+ // it has the correct geometry
+ NSDictionary *expectedGeometry = @{@"type": @"MultiPoint",
+ @"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)],
+ @[@(coord2.longitude), @(coord2.latitude)],
+ @[@(coord3.longitude), @(coord3.latitude)]]};
+ XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry);
+
+}
+
- (void)testShapeCollectionFeatureGeoJSONDictionary {
MGLPointAnnotation *pointFeature = [[MGLPointAnnotation alloc] init];
CLLocationCoordinate2D pointCoordinate = { 10, 10 };
diff --git a/platform/darwin/test/MGLGeoJSONSourceTests.mm b/platform/darwin/test/MGLGeoJSONSourceTests.mm
new file mode 100644
index 0000000000..be8bb143ce
--- /dev/null
+++ b/platform/darwin/test/MGLGeoJSONSourceTests.mm
@@ -0,0 +1,242 @@
+#import <XCTest/XCTest.h>
+
+#import <Mapbox/Mapbox.h>
+#import "MGLFeature_Private.h"
+#import "MGLGeoJSONSource_Private.h"
+#import "MGLSource_Private.h"
+
+#include <mbgl/style/sources/geojson_source.hpp>
+
+@interface MGLGeoJSONSourceTests : XCTestCase
+@end
+
+@implementation MGLGeoJSONSourceTests
+
+- (void)testMGLGeoJSONSourceWithOptions {
+ NSURL *url = [NSURL URLWithString:@"http://www.mapbox.com/source"];
+
+ NSDictionary *options = @{MGLGeoJSONSourceOptionClustered: @YES,
+ MGLGeoJSONSourceOptionClusterRadius: @42,
+ MGLGeoJSONSourceOptionMaximumZoomLevelForClustering: @98,
+ MGLGeoJSONSourceOptionMaximumZoomLevel: @99,
+ MGLGeoJSONSourceOptionBuffer: @1976,
+ MGLGeoJSONSourceOptionSimplificationTolerance: @0.42};
+ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" URL:url options:options];
+
+ auto mbglOptions = [source geoJSONOptions];
+ XCTAssertTrue(mbglOptions.cluster);
+ XCTAssertEqual(mbglOptions.clusterRadius, 42);
+ XCTAssertEqual(mbglOptions.clusterMaxZoom, 98);
+ XCTAssertEqual(mbglOptions.maxzoom, 99);
+ XCTAssertEqual(mbglOptions.buffer, 1976);
+ XCTAssertEqual(mbglOptions.tolerance, 0.42);
+
+ options = @{MGLGeoJSONSourceOptionClustered: @"number 1"};
+ XCTAssertThrows([[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" URL:url options:options]);
+}
+
+- (void)testMGLGeoJSONSourceWithData {
+
+ NSString *geoJSON = @"{\"type\": \"FeatureCollection\",\"features\": [{\"type\": \"Feature\",\"properties\": {},\"geometry\": {\"type\": \"LineString\",\"coordinates\": [[-107.75390625,40.329795743702064],[-104.34814453125,37.64903402157866]]}}]}";
+
+ NSData *data = [geoJSON dataUsingEncoding:NSUTF8StringEncoding];
+ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" geoJSONData:data options:nil];
+
+ XCTAssertNotNil(source.features);
+ XCTAssertEqual(source.features.count, 1);
+ XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLPolylineFeature class]]);
+}
+
+- (void)testMGLGeoJSONSourceWithPolylineFeatures {
+ CLLocationCoordinate2D coordinates[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
+ MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coordinates count:2];
+
+ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[polylineFeature] options:nil];
+
+ XCTAssertNotNil(source.features);
+ XCTAssertEqual(source.features.count, 1);
+ XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLPolylineFeature class]]);
+}
+
+- (void)testMGLGeoJSONSourceWithPolygonFeatures {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(100.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 0.0)};
+
+ MGLPolygonFeature<MGLFeaturePrivate> *polygonFeature = (MGLPolygonFeature<MGLFeaturePrivate> *)[MGLPolygonFeature polygonWithCoordinates:coordinates count:5];
+ polygonFeature.identifier = @"feature-id";
+ NSString *stringAttribute = @"string";
+ NSNumber *boolAttribute = [NSNumber numberWithBool:YES];
+ NSNumber *doubleAttribute = [NSNumber numberWithDouble:1.23];
+ NSDictionary *nestedDictionaryValue = @{@"nested-key-1": @"nested-string-value"};
+ NSArray *arrayValue = @[@"string-value", @2];
+ NSDictionary *dictionaryValue = @{@"key-1": @"string-value",
+ @"key-2": @1,
+ @"key-3": nestedDictionaryValue,
+ @"key-4": arrayValue};
+ NSArray *arrayOfArrays = @[@[@1, @"string-value", @[@"jagged"]]];
+ NSArray *arrayOfDictionaries = @[@{@"key": @"value"}];
+
+ polygonFeature.attributes = @{@"name": stringAttribute,
+ @"bool": boolAttribute,
+ @"double": doubleAttribute,
+ @"dictionary-attribute": dictionaryValue,
+ @"array-attribute": arrayValue,
+ @"array-of-array-attribute": arrayOfArrays,
+ @"array-of-dictionary-attribute": arrayOfDictionaries};
+
+ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[polygonFeature] options:nil];
+
+ XCTAssertNotNil(source.features);
+ XCTAssertEqual(source.features.count, 1);
+ MGLPolygonFeature *expectedPolygonFeature = (MGLPolygonFeature *)source.features.firstObject;
+ XCTAssertTrue([expectedPolygonFeature isMemberOfClass:[MGLPolygonFeature class]]);
+ XCTAssertEqualObjects(expectedPolygonFeature.identifier, polygonFeature.identifier);
+ XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"name"], stringAttribute);
+ XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"bool"], boolAttribute);
+ XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"double"], doubleAttribute);
+ XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"dictionary-attribute"], dictionaryValue);
+ XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-attribute"], arrayValue);
+ XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-of-array-attribute"], arrayOfArrays);
+ XCTAssertEqualObjects(expectedPolygonFeature.attributes[@"array-of-dictionary-attribute"], arrayOfDictionaries);
+}
+
+- (void)testMGLGeoJSONSourceWithPolygonFeaturesInculdingInteriorPolygons {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(100.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 0.0)};
+
+ CLLocationCoordinate2D interiorCoordinates[] = {
+ CLLocationCoordinate2DMake(100.2, 0.2),
+ CLLocationCoordinate2DMake(100.8, 0.2),
+ CLLocationCoordinate2DMake(100.8, 0.8),
+ CLLocationCoordinate2DMake(100.2, 0.8),
+ CLLocationCoordinate2DMake(100.2, 0.2)};
+
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:interiorCoordinates count:5];
+
+ MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
+
+ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[polygonFeature] options:nil];
+
+
+ XCTAssertNotNil(source.features);
+ XCTAssertEqual(source.features.count, 1);
+ XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLPolygonFeature class]]);
+}
+
+- (void)testMGLGeoJSONSourceWithMultiPolylineFeatures {
+ CLLocationCoordinate2D firstCoordinates[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
+ MGLPolylineFeature *firstPolylineFeature = [MGLPolylineFeature polylineWithCoordinates:firstCoordinates count:2];
+ CLLocationCoordinate2D secondCoordinates[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
+ MGLPolylineFeature *secondPolylineFeature = [MGLPolylineFeature polylineWithCoordinates:secondCoordinates count:2];
+ MGLMultiPolylineFeature *multiPolylineFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:@[firstPolylineFeature, secondPolylineFeature]];
+
+ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[multiPolylineFeature] options:nil];
+
+ XCTAssertNotNil(source.features);
+ XCTAssertEqual(source.features.count, 1);
+ XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLMultiPolylineFeature class]]);
+}
+
+- (void)testMGLGeoJSONSourceWithMultiPolygonFeatures {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(100.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 0.0)};
+
+ CLLocationCoordinate2D interiorCoordinates[] = {
+ CLLocationCoordinate2DMake(100.2, 0.2),
+ CLLocationCoordinate2DMake(100.8, 0.2),
+ CLLocationCoordinate2DMake(100.8, 0.8),
+ CLLocationCoordinate2DMake(100.2, 0.8),
+ CLLocationCoordinate2DMake(100.2, 0.2)};
+
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:interiorCoordinates count:5];
+
+ MGLPolygonFeature *firstPolygon = [MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
+ MGLPolygonFeature *secondPolygon = [MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
+
+ MGLMultiPolygonFeature *multiPolygonFeature = [MGLMultiPolygonFeature multiPolygonWithPolygons:@[firstPolygon, secondPolygon]];
+
+ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[multiPolygonFeature] options:nil];
+
+ XCTAssertNotNil(source.features);
+ XCTAssertEqual(source.features.count, 1);
+ XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLMultiPolygonFeature class]]);
+
+}
+
+- (void)testMGLGeoJSONSourceWithPointFeature {
+ MGLPointFeature *pointFeature = [MGLPointFeature new];
+ pointFeature.coordinate = CLLocationCoordinate2DMake(100.2, 0.2);
+
+ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"souce-id" features:@[pointFeature] options:nil];
+
+ XCTAssertNotNil(source.features);
+ XCTAssertEqual(source.features.count, 1);
+ XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLPointFeature class]]);
+}
+
+- (void)testMGLGeoJSONSourceWithPointCollectionFeature {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(100.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 0.0)};
+ MGLPointCollectionFeature *pointCollectionFeature = [MGLPointCollectionFeature pointCollectionWithCoordinates:coordinates count:5];
+ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"souce-id" features:@[pointCollectionFeature] options:nil];
+
+ XCTAssertNotNil(source.features);
+ XCTAssertEqual(source.features.count, 1);
+ XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLPointCollectionFeature class]]);
+}
+
+- (void)testMGLGeoJSONSourceWithShapeCollectionFeatures {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(100.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 0.0),
+ CLLocationCoordinate2DMake(101.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 1.0),
+ CLLocationCoordinate2DMake(100.0, 0.0)};
+
+ CLLocationCoordinate2D interiorCoordinates[] = {
+ CLLocationCoordinate2DMake(100.2, 0.2),
+ CLLocationCoordinate2DMake(100.8, 0.2),
+ CLLocationCoordinate2DMake(100.8, 0.8),
+ CLLocationCoordinate2DMake(100.2, 0.8),
+ CLLocationCoordinate2DMake(100.2, 0.2)};
+
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:interiorCoordinates count:5];
+
+ MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]];
+
+ CLLocationCoordinate2D coordinates_2[] = { CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 10)};
+ MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coordinates_2 count:2];
+
+ MGLMultiPolygonFeature *multiPolygonFeature = [MGLMultiPolygonFeature multiPolygonWithPolygons:@[polygonFeature, polygonFeature]];
+
+ MGLMultiPolylineFeature *multiPolylineFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:@[polylineFeature, polylineFeature]];
+
+ MGLPointCollectionFeature *pointCollectionFeature = [MGLPointCollectionFeature pointCollectionWithCoordinates:coordinates count:5];
+
+ MGLPointFeature *pointFeature = [MGLPointFeature new];
+ pointFeature.coordinate = CLLocationCoordinate2DMake(100.2, 0.2);
+
+ MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[polygonFeature, polylineFeature, multiPolygonFeature, multiPolylineFeature, pointCollectionFeature, pointFeature]];
+
+ MGLShapeCollectionFeature *shapeCollectionFeature_1 = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[polygonFeature, polylineFeature, multiPolygonFeature, multiPolylineFeature, pointCollectionFeature, pointFeature, shapeCollectionFeature]];
+
+ XCTAssertThrowsSpecificNamed([[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[shapeCollectionFeature_1] options:nil], NSException, @"Method unavailable");
+}
+
+@end
diff --git a/platform/darwin/test/MGLOfflineStorageTests.m b/platform/darwin/test/MGLOfflineStorageTests.m
index 8400cb43cb..cd13a9262e 100644
--- a/platform/darwin/test/MGLOfflineStorageTests.m
+++ b/platform/darwin/test/MGLOfflineStorageTests.m
@@ -90,11 +90,11 @@
NSDictionary *userInfo = notification.userInfo;
XCTAssertNotNil(userInfo, @"Progress change notification should have a userInfo dictionary.");
- NSNumber *stateNumber = userInfo[MGLOfflinePackStateUserInfoKey];
+ NSNumber *stateNumber = userInfo[MGLOfflinePackUserInfoKeyState];
XCTAssert([stateNumber isKindOfClass:[NSNumber class]], @"Progress change notification’s state should be an NSNumber.");
XCTAssertEqual(stateNumber.integerValue, pack.state, @"State in a progress change notification should match the pack’s state.");
- NSValue *progressValue = userInfo[MGLOfflinePackProgressUserInfoKey];
+ NSValue *progressValue = userInfo[MGLOfflinePackUserInfoKeyProgress];
XCTAssert([progressValue isKindOfClass:[NSValue class]], @"Progress change notification’s progress should be an NSValue.");
XCTAssertEqualObjects(progressValue, [NSValue valueWithMGLOfflinePackProgress:pack.progress], @"Progress change notification’s progress should match pack’s progress.");
diff --git a/platform/darwin/test/MGLStyleLayerTests.m b/platform/darwin/test/MGLStyleLayerTests.m
index b16bcfed56..74c6b2f906 100644
--- a/platform/darwin/test/MGLStyleLayerTests.m
+++ b/platform/darwin/test/MGLStyleLayerTests.m
@@ -13,6 +13,7 @@
[vc.view addSubview:_mapView];
_mapView.delegate = self;
#else
+ [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
NSWindowController *windowController = [[NSWindowController alloc] initWithWindowNibName:@"MGLStyleLayerTests" owner:self];
[windowController showWindow:nil];
#endif
diff --git a/platform/darwin/test/MGLStyleValueTests.swift b/platform/darwin/test/MGLStyleValueTests.swift
new file mode 100644
index 0000000000..f7bf343852
--- /dev/null
+++ b/platform/darwin/test/MGLStyleValueTests.swift
@@ -0,0 +1,36 @@
+import XCTest
+import Mapbox
+
+class MGLStyleValueTests: XCTestCase {
+ func testConstantValues() {
+ let geoJSONSource = MGLGeoJSONSource(identifier: "test", features: [], options: nil)
+ let symbolStyleLayer = MGLSymbolStyleLayer(identifier: "test", source: geoJSONSource)
+
+ // Boolean
+ symbolStyleLayer.iconAllowOverlap = MGLStyleConstantValue(rawValue: true)
+ XCTAssertEqual((symbolStyleLayer.iconAllowOverlap as! MGLStyleConstantValue<NSNumber>).rawValue, true)
+
+ // Number
+ symbolStyleLayer.iconHaloWidth = MGLStyleConstantValue(rawValue: 3)
+ XCTAssertEqual((symbolStyleLayer.iconHaloWidth as! MGLStyleConstantValue<NSNumber>).rawValue, 3)
+
+ // String
+ symbolStyleLayer.textField = MGLStyleConstantValue(rawValue: "{name}")
+ XCTAssertEqual((symbolStyleLayer.textField as! MGLStyleConstantValue<NSString>).rawValue, "{name}")
+ }
+
+ func testFunctions() {
+ let geoJSONSource = MGLGeoJSONSource(identifier: "test", features: [], options: nil)
+ let symbolStyleLayer = MGLSymbolStyleLayer(identifier: "test", source: geoJSONSource)
+
+ // Boolean
+ let stops: [NSNumber: MGLStyleValue<NSNumber>] = [
+ 1: MGLStyleValue(rawValue: true),
+ 2: MGLStyleValue(rawValue: false),
+ 3: MGLStyleValue(rawValue: true),
+ 4: MGLStyleValue(rawValue: false),
+ ]
+ symbolStyleLayer.iconAllowOverlap = MGLStyleFunction<NSNumber>(base: 1, stops: stops)
+ XCTAssertEqual((symbolStyleLayer.iconAllowOverlap as! MGLStyleFunction<NSNumber>), MGLStyleFunction(base: 1, stops: stops))
+ }
+}
diff --git a/platform/darwin/test/MGLTileSetTests.mm b/platform/darwin/test/MGLTileSetTests.mm
new file mode 100644
index 0000000000..d77046928c
--- /dev/null
+++ b/platform/darwin/test/MGLTileSetTests.mm
@@ -0,0 +1,82 @@
+#import <XCTest/XCTest.h>
+
+#import <Mapbox/Mapbox.h>
+#import "MGLTileSet_Private.h"
+
+#include <mbgl/util/tileset.hpp>
+
+@interface MGLTileSetTests : XCTestCase
+
+@end
+
+@implementation MGLTileSetTests
+
+- (void)testTileSet {
+ // a tile set that provides an mbgl tile set
+ MGLTileSet *tileSet = [[MGLTileSet alloc] initWithTileURLTemplates:@[@"tile.1",
+ @"tile.2",
+ @"tile.3"]];
+ mbgl::Tileset mbglTileset = [tileSet mbglTileset];
+
+ // has the correct URL templates
+ XCTAssertEqual(mbglTileset.tiles.size(), 3);
+ XCTAssertEqual(mbglTileset.tiles[0], "tile.1");
+ XCTAssertEqual(mbglTileset.tiles[1], "tile.2");
+ XCTAssertEqual(mbglTileset.tiles[2], "tile.3");
+
+ // has the default scheme
+ XCTAssertEqual(mbglTileset.scheme, mbgl::Tileset::Scheme::XYZ);
+
+ // when the tile set has no min or max zoom level set
+ tileSet.minimumZoomLevel = nil;
+ tileSet.maximumZoomLevel = nil;
+
+ // the mbgl object has default values for min and max zoom level
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.min, 0);
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.max, 22);
+
+ // when the tile set has min and/or max zoom level set
+ tileSet.minimumZoomLevel = @(1);
+ tileSet.maximumZoomLevel = @(2);
+
+ // the mbgl object reflects the set values for min and max zoom level
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.min, 1);
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.max, 2);
+
+ // when the tile set has an attribution
+ tileSet.attribution = @"my tileset © ©️🎈";
+
+ // the attribution is reflected by the mbgl tileset
+ XCTAssertEqual([tileSet mbglTileset].attribution, tileSet.attribution.UTF8String);
+
+ // when the scheme is changed
+ tileSet.scheme = MGLTileSetSchemeTMS;
+
+ // the scheme is reflected by the mbgl tileset
+ XCTAssertEqual([tileSet mbglTileset].scheme , mbgl::Tileset::Scheme::TMS);
+
+ // a tile set that provides an mbgl tile set and minimum and maximum zoom levels
+ tileSet = [[MGLTileSet alloc] initWithTileURLTemplates:@[@"tile.1"] minimumZoomLevel:15 maximumZoomLevel:20];
+
+ // the zoom levels are reflected by the mbgl tileset
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.min, 15);
+ XCTAssertEqual([tileSet mbglTileset].zoomRange.max, 20);
+}
+
+- (void)testInvalidTileSet {
+ // a tile set that provides an mbgl tile set and invalid (crossed) minimum and maximum zoom levels throws an exception
+ XCTAssertThrowsSpecificNamed([[MGLTileSet alloc] initWithTileURLTemplates:@[@"tile.1"] minimumZoomLevel:10 maximumZoomLevel:9], NSException, @"Invalid minimumZoomLevel");
+
+ // a tile set that provides an mbgl tile set
+ MGLTileSet *tileSet = [[MGLTileSet alloc] initWithTileURLTemplates:@[@"tile.1"]];
+ tileSet.maximumZoomLevel = @(10);
+
+ // when the minimum zoom level is set higher than the maximum zoom level
+ XCTAssertThrowsSpecificNamed(tileSet.minimumZoomLevel = @(11), NSException, @"Invalid minimumZoomLevel");
+
+ // when the maximum zoom level is set lower than the minimum zoom level
+ tileSet.minimumZoomLevel = @(5);
+ XCTAssertThrowsSpecificNamed(tileSet.maximumZoomLevel = @(4), NSException, @"Invalid minimumZoomLevel");
+}
+
+@end
diff --git a/platform/darwin/test/test-Bridging-Header.h b/platform/darwin/test/test-Bridging-Header.h
new file mode 100644
index 0000000000..e11d920b12
--- /dev/null
+++ b/platform/darwin/test/test-Bridging-Header.h
@@ -0,0 +1,3 @@
+//
+// Use this file to import your target's public headers that you would like to expose to Swift.
+//