diff options
Diffstat (limited to 'platform/darwin')
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. +// |