diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 4bcfe54d3..62ab3b6b9 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 239a338f6..384c5a073 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 @@ -48,9 +49,13 @@ NS_ASSUME_NONNULL_BEGIN For details about the identifiers used in most Mapbox-provided styles, consult the Mapbox Streets - 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 Mapbox Streets and Mapbox Terrain - 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 tile source. */ -@interface MGLMultiPointFeature : MGLMultiPoint +@interface MGLPointCollectionFeature : MGLPointCollection @end /** diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm index 84dcc4f0b..548343371 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 () +@interface MGLPointCollectionFeature () @end -@implementation MGLMultiPointFeature +@implementation MGLPointCollectionFeature @synthesize identifier; @synthesize attributes; @@ -202,7 +202,7 @@ public: MGLShape * operator()(const mbgl::MultiPoint &geometry) const { std::vector coordinates = toLocationCoordinates2D(geometry); - return [[MGLMultiPointFeature alloc] initWithCoordinates:&coordinates[0] count:coordinates.size()]; + return [[MGLPointCollectionFeature alloc] initWithCoordinates:&coordinates[0] count:coordinates.size()]; } MGLShape * operator()(const mbgl::MultiLineString &geometry) const { diff --git a/platform/darwin/src/MGLFeature_Private.h b/platform/darwin/src/MGLFeature_Private.h index 5fb82bde5..e6858c7c1 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 -@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 9eeb1e118..30232c621 100644 --- a/platform/darwin/src/MGLGeoJSONSource.h +++ b/platform/darwin/src/MGLGeoJSONSource.h @@ -6,34 +6,39 @@ 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> *)features options:(nullable NS_DICTIONARY_OF(NSString *,id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray> *)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 ) *features; +@property (nonatomic, nullable) NS_ARRAY_OF(id ) *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 27f2eb8bd..0dbe1030c 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 -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 _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(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(); + source->setGeoJSON(geojson); + _features = MGLFeaturesFromMBGLFeatures(geojson); + } else { + mbgl::FeatureCollection featureCollection; + featureCollection.reserve(self.features.count); + for (id 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)mbglSource +- (void)setGeoJSONData:(NSData *)geoJSONData { - auto source = std::make_unique(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(); - source->setGeoJSON(geojson); - _features = MGLFeaturesFromMBGLFeatures(geojson); - } else { - mbgl::FeatureCollection featureCollection; - featureCollection.reserve(self.features.count); - for (id 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]; } - return std::move(source); + NSString *string = [[NSString alloc] initWithData:_geoJSONData encoding:NSUTF8StringEncoding]; + const auto geojson = mapbox::geojson::parse(string.UTF8String).get(); + 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 feature in features) { + featureCollection.push_back([feature mbglFeature]); + } + const auto geojson = mbgl::GeoJSON{featureCollection}; + self.rawSource->setGeoJSON(geojson); + + _features = MGLFeaturesFromMBGLFeatures(featureCollection); } @end diff --git a/platform/darwin/src/MGLGeoJSONSource_Private.h b/platform/darwin/src/MGLGeoJSONSource_Private.h index 3aeb07ad2..de5bb10fa 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 diff --git a/platform/darwin/src/MGLMultiPoint.h b/platform/darwin/src/MGLMultiPoint.h index b936205ab..69c729584 100644 --- a/platform/darwin/src/MGLMultiPoint.h +++ b/platform/darwin/src/MGLMultiPoint.h @@ -6,24 +6,15 @@ 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. diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm index 0090c5e35..17a61ed08 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()}); } -- (mbgl::Feature)featureObject -{ - mbgl::MultiPoint multiPoint; - multiPoint.reserve(self.pointCount); - for (NSInteger i = 0; i< self.pointCount; i++) - { - multiPoint.push_back(mbgl::Point(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 211135f84..b3fb7a2d5 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 5f284b76a..10acc58b2 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"; +const NSNotificationName MGLOfflinePackProgressChangedNotification = @"MGLOfflinePackProgressChanged"; +const NSNotificationName MGLOfflinePackErrorNotification = @"MGLOfflinePackError"; +const NSNotificationName MGLOfflinePackMaximumMapboxTilesReachedNotification = @"MGLOfflinePackMaximumMapboxTilesReached"; -NSString * const MGLOfflinePackStateUserInfoKey = @"State"; -NSString * const MGLOfflinePackProgressUserInfoKey = @"Progress"; -NSString * const MGLOfflinePackErrorUserInfoKey = @"Error"; -NSString * const MGLOfflinePackMaximumCountUserInfoKey = @"MaximumCount"; +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 () @@ -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 000000000..db497d0a5 --- /dev/null +++ b/platform/darwin/src/MGLPointCollection.h @@ -0,0 +1,51 @@ +#import +#import + +#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 + +/** + 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 000000000..5871915b5 --- /dev/null +++ b/platform/darwin/src/MGLPointCollection.mm @@ -0,0 +1,112 @@ +#import "MGLPointCollection_Private.h" +#import "MGLGeometry_Private.h" + +#import +#import + +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 multiPoint; + multiPoint.reserve(self.pointCount); + for (NSInteger i = 0; i< self.pointCount; i++) + { + multiPoint.push_back(mbgl::Point(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 000000000..039c1f18b --- /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 6d53356ba..3d5b36abb 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 6642afef7..d0274b44e 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 41b9a5b04..fc47c2385 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 +@interface MGLRasterSource () + +@property (nonatomic) mbgl::style::RasterSource *rawSource; + +@end + @implementation MGLRasterSource +{ + std::unique_ptr _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)mbglSource +- (void)commonInit { std::unique_ptr source; @@ -42,10 +54,15 @@ source = std::make_unique(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 85bbc0634..1b889d44d 100644 --- a/platform/darwin/src/MGLSource.mm +++ b/platform/darwin/src/MGLSource.mm @@ -2,27 +2,14 @@ #include -@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)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 2b8658b4c..dff230ede 100644 --- a/platform/darwin/src/MGLSource_Private.h +++ b/platform/darwin/src/MGLSource_Private.h @@ -3,10 +3,29 @@ #include #include +@class MGLMapView; + @interface MGLSource (Private) -- (std::unique_ptr)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 3964b47ad..a17b7d6b7 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 eb0eaf534..ab5e76bbe 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 : MGLStyleValue +@interface MGLStyleConstantValue : MGLStyleValue #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 : MGLStyleValue +@interface MGLStyleFunction : MGLStyleValue #pragma mark Creating a Style Function diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index 3fcecedca..9bd943a34 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 *textField; diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h index a3ea587ad..e9e05bc68 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 359781235..995565419 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 +@interface MGLVectorSource () + +@property (nonatomic) mbgl::style::VectorSource *rawSource; + +@end + @implementation MGLVectorSource +{ + std::unique_ptr _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)mbglSource +- (void)commonInit { std::unique_ptr 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 18c3fd16c..7f464aaab 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/ios/test/MGLGeoJSONSourceTests.mm b/platform/darwin/test/MGLGeoJSONSourceTests.mm similarity index 76% rename from platform/ios/test/MGLGeoJSONSourceTests.mm rename to platform/darwin/test/MGLGeoJSONSourceTests.mm index 40d806798..be8bb143c 100644 --- a/platform/ios/test/MGLGeoJSONSourceTests.mm +++ b/platform/darwin/test/MGLGeoJSONSourceTests.mm @@ -10,23 +10,17 @@ @interface MGLGeoJSONSourceTests : XCTestCase @end -@interface MGLPolygonFeature (Test) - -@property (nonatomic, copy, readwrite) NS_DICTIONARY_OF(NSString *, id) *attributes; - -@end - @implementation MGLGeoJSONSourceTests - (void)testMGLGeoJSONSourceWithOptions { NSURL *url = [NSURL URLWithString:@"http://www.mapbox.com/source"]; - NSDictionary *options = @{MGLGeoJSONClusterOption: @(YES), - MGLGeoJSONClusterRadiusOption: @42, - MGLGeoJSONClusterMaximumZoomLevelOption: @98, - MGLGeoJSONMaximumZoomLevelOption: @99, - MGLGeoJSONBufferOption: @1976, - MGLGeoJSONToleranceOption: @0.42}; + 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]; @@ -37,9 +31,8 @@ XCTAssertEqual(mbglOptions.buffer, 1976); XCTAssertEqual(mbglOptions.tolerance, 0.42); - options = @{MGLGeoJSONClusterOption: @"number 1"}; - source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" URL:url options:options]; - XCTAssertThrows([source geoJSONOptions]); + options = @{MGLGeoJSONSourceOptionClustered: @"number 1"}; + XCTAssertThrows([[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" URL:url options:options]); } - (void)testMGLGeoJSONSourceWithData { @@ -49,8 +42,6 @@ NSData *data = [geoJSON dataUsingEncoding:NSUTF8StringEncoding]; MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" geoJSONData:data options:nil]; - [source mbglSource]; - XCTAssertNotNil(source.features); XCTAssertEqual(source.features.count, 1); XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLPolylineFeature class]]); @@ -62,8 +53,6 @@ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[polylineFeature] options:nil]; - std::unique_ptr mbglSource = [source mbglSource]; - XCTAssertNotNil(source.features); XCTAssertEqual(source.features.count, 1); XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLPolylineFeature class]]); @@ -101,8 +90,6 @@ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[polygonFeature] options:nil]; - std::unique_ptr mbglSource = [source mbglSource]; - XCTAssertNotNil(source.features); XCTAssertEqual(source.features.count, 1); MGLPolygonFeature *expectedPolygonFeature = (MGLPolygonFeature *)source.features.firstObject; @@ -137,56 +124,7 @@ MGLPolygonFeature *polygonFeature = [MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]]; MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[polygonFeature] options:nil]; - - std::unique_ptr mbglSource = [source mbglSource]; - - XCTAssertNotNil(source.features); - XCTAssertEqual(source.features.count, 1); - XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLPolygonFeature class]]); -} - - -- (void)testMGLGeoJSONSourceWithMultiPointFeaturesUsingPolylines { - 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)}; - - MGLMultiPointFeature *multiPointFeature = [MGLMultiPointFeature multiPointWithCoordinates:coordinates count:5]; - - MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[multiPointFeature] options:nil]; - - std::unique_ptr mbglSource = [source mbglSource]; - - XCTAssertNotNil(source.features); - XCTAssertEqual(source.features.count, 1); - XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLMultiPointFeature class]]); -} - -- (void)testMGLGeoJSONSourceWithMultiPointFeaturesUsingPolygons { - 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]; - - MGLMultiPointFeature *multiPointFeature = (MGLMultiPointFeature *)[MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]]; - - MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[multiPointFeature] options:nil]; - - std::unique_ptr mbglSource = [source mbglSource]; + XCTAssertNotNil(source.features); XCTAssertEqual(source.features.count, 1); @@ -202,8 +140,6 @@ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[multiPolylineFeature] options:nil]; - std::unique_ptr mbglSource = [source mbglSource]; - XCTAssertNotNil(source.features); XCTAssertEqual(source.features.count, 1); XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLMultiPolylineFeature class]]); @@ -233,8 +169,6 @@ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"source-id" features:@[multiPolygonFeature] options:nil]; - std::unique_ptr mbglSource = [source mbglSource]; - XCTAssertNotNil(source.features); XCTAssertEqual(source.features.count, 1); XCTAssertTrue([source.features.firstObject isMemberOfClass:[MGLMultiPolygonFeature class]]); @@ -247,13 +181,26 @@ MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"souce-id" features:@[pointFeature] options:nil]; - std::unique_ptr mbglSource = [source mbglSource]; - 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), @@ -280,18 +227,16 @@ MGLMultiPolylineFeature *multiPolylineFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:@[polylineFeature, polylineFeature]]; - MGLMultiPointFeature *multiPointFeature = (MGLMultiPointFeature *)[MGLPolygonFeature polygonWithCoordinates:coordinates count:5 interiorPolygons:@[polygon]]; + 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, multiPointFeature, pointFeature]]; + MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[polygonFeature, polylineFeature, multiPolygonFeature, multiPolylineFeature, pointCollectionFeature, pointFeature]]; - MGLShapeCollectionFeature *shapeCollectionFeature_1 = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[polygonFeature, polylineFeature, multiPolygonFeature, multiPolylineFeature, multiPointFeature, pointFeature, shapeCollectionFeature]]; + MGLShapeCollectionFeature *shapeCollectionFeature_1 = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[polygonFeature, polylineFeature, multiPolygonFeature, multiPolylineFeature, pointCollectionFeature, pointFeature, shapeCollectionFeature]]; - MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"" features:@[shapeCollectionFeature_1] options:nil]; - - XCTAssertThrowsSpecificNamed([source mbglSource], NSException, @"Method unavailable"); + 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 8400cb43c..cd13a9262 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 b16bcfed5..74c6b2f90 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 000000000..f7bf34385 --- /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).rawValue, true) + + // Number + symbolStyleLayer.iconHaloWidth = MGLStyleConstantValue(rawValue: 3) + XCTAssertEqual((symbolStyleLayer.iconHaloWidth as! MGLStyleConstantValue).rawValue, 3) + + // String + symbolStyleLayer.textField = MGLStyleConstantValue(rawValue: "{name}") + XCTAssertEqual((symbolStyleLayer.textField as! MGLStyleConstantValue).rawValue, "{name}") + } + + func testFunctions() { + let geoJSONSource = MGLGeoJSONSource(identifier: "test", features: [], options: nil) + let symbolStyleLayer = MGLSymbolStyleLayer(identifier: "test", source: geoJSONSource) + + // Boolean + let stops: [NSNumber: MGLStyleValue] = [ + 1: MGLStyleValue(rawValue: true), + 2: MGLStyleValue(rawValue: false), + 3: MGLStyleValue(rawValue: true), + 4: MGLStyleValue(rawValue: false), + ] + symbolStyleLayer.iconAllowOverlap = MGLStyleFunction(base: 1, stops: stops) + XCTAssertEqual((symbolStyleLayer.iconAllowOverlap as! MGLStyleFunction), MGLStyleFunction(base: 1, stops: stops)) + } +} diff --git a/platform/ios/test/MGLTileSetTests.mm b/platform/darwin/test/MGLTileSetTests.mm similarity index 100% rename from platform/ios/test/MGLTileSetTests.mm rename to platform/darwin/test/MGLTileSetTests.mm diff --git a/platform/darwin/test/test-Bridging-Header.h b/platform/darwin/test/test-Bridging-Header.h new file mode 100644 index 000000000..e11d920b1 --- /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. +// diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index b064c7181..494adbda6 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,9 +7,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Packaging * Xcode 7.3 or above is required for using this SDK. ([#6059](https://github.com/mapbox/mapbox-gl-native/issues/6059)) -* Fixed an issue with symbols not being properly stripped from the dynamic framework. The dSYM file included with the standard dynamic framework in previous releases (e.g., `mapbox-ios-sdk-3.3.4-dynamic.zip` or the `Mapbox-iOS-SDK` pod) could not be used to symbolicate crashes. ([#6531](https://github.com/mapbox/mapbox-gl-native/pull/6531)) +* Fixed an issue with symbols not being properly stripped from the dynamic framework. The dSYM file included with the standard dynamic framework in previous releases (e.g., `mapbox-ios-sdk-3.3.5-dynamic.zip` or the `Mapbox-iOS-SDK` pod) could not be used to symbolicate crashes. ([#6531](https://github.com/mapbox/mapbox-gl-native/pull/6531)) * Simulator architecture slices are included in the included dSYM file, allowing you to symbolicate crashes that occur in the Simulator. ([#5740](https://github.com/mapbox/mapbox-gl-native/pull/5740)) * Clarified that the `-ObjC` linker flag is required for linking against the static framework distribution of this SDK. ([#6213](https://github.com/mapbox/mapbox-gl-native/pull/6213)) +* Added documentation for the Info.plist keys used by this SDK. ([#6833](https://github.com/mapbox/mapbox-gl-native/pull/6833)) ### Styles and data @@ -18,8 +19,11 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * GeoJSON sources specified by the stylesheet at design time now support `cluster`, `clusterMaxZoom`, and `clusterRadius` attributes for clustering point features on the base map. ([#5724](https://github.com/mapbox/mapbox-gl-native/pull/5724)) * Added [quadkey](https://msdn.microsoft.com/en-us/library/bb259689.aspx) support and limited WMS support in raster tile URL templates. ([#5628](https://github.com/mapbox/mapbox-gl-native/pull/5628)) * TileJSON manifests can now specify `"scheme": "tms"` to indicate the use of [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) coordinates. ([#2270](https://github.com/mapbox/mapbox-gl-native/pull/2270)) +* Fixed an issue causing abstract MGLMultiPointFeature objects to be returned in feature query results. Now concrete MGLPointCollectionFeature objects are returned. ([#6742](https://github.com/mapbox/mapbox-gl-native/pull/6742)) * Fixed rendering artifacts and missing glyphs that occurred after viewing a large number of CJK characters on the map. ([#5908](https://github.com/mapbox/mapbox-gl-native/pull/5908)) * `-[MGLMapView resetPosition]` now resets to the current style’s default center coordinates, zoom level, direction, and pitch, if specified. ([#6127](https://github.com/mapbox/mapbox-gl-native/pull/6127)) +* Fixed an issue where feature querying sometimes failed to return the expected features when the map was tilted. ([#6773](https://github.com/mapbox/mapbox-gl-native/pull/6773)) +* MGLFeature’s `attributes` and `identifier` properties are now writable. ([#6728](https://github.com/mapbox/mapbox-gl-native/pull/6728)) * The `text-pitch-alignment` property is now supported in stylesheets for improved street label legibility on a tilted map. ([#5288](https://github.com/mapbox/mapbox-gl-native/pull/5288)) * The `icon-text-fit` and `icon-text-fit-padding` properties are now supported in stylesheets, allowing the background of a shield to automatically resize to fit the shield’s text. ([#5334](https://github.com/mapbox/mapbox-gl-native/pull/5334)) * The `circle-pitch-scale` property is now supported in stylesheets, allowing circle features in a tilted base map to scale or remain the same size as the viewing distance changes. ([#5576](https://github.com/mapbox/mapbox-gl-native/pull/5576)) @@ -31,6 +35,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * A source’s tiles are no longer rendered when the map is outside the source’s supported zoom levels. ([#6345](https://github.com/mapbox/mapbox-gl-native/pull/6345)) * Improved style parsing performance. ([#6170](https://github.com/mapbox/mapbox-gl-native/pull/6170)) * Improved feature querying performance. ([#6514](https://github.com/mapbox/mapbox-gl-native/pull/6514)) +* Fixed an issue where shapes that cannot currently be visually represented as annotations were still shown on the map as point annotations. ([#6764](https://github.com/mapbox/mapbox-gl-native/issues/6764)) ### User location @@ -43,16 +48,18 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Annotations * MGLPolyline annotations and the exterior coordinates of MGLPolygon annotations are now able to be mutated, part or all, and changes are displayed immediately. ([#6565](https://github.com/mapbox/mapbox-gl-native/pull/6565)) +* Added new APIs to MGLMapView to query for visible annotations. Combined with `-[MGLMapView viewForAnnotation:]`, these APIs can be used to access all visible annotation views. ([6061](https://github.com/mapbox/mapbox-gl-native/pull/6061)) +* Fixed an issue causing offscreen annotation views to be updated even when they were in the reuse queue. ([#5987](https://github.com/mapbox/mapbox-gl-native/pull/5987)) * Fixed an issue preventing MGLAnnotationView from animating when its coordinate changes. ([#6215](https://github.com/mapbox/mapbox-gl-native/pull/6215)) * Fixed an issue causing the wrong annotation view to be selected when tapping an annotation view with a center offset applied. ([#5931](https://github.com/mapbox/mapbox-gl-native/pull/5931)) * Fixed an issue that assigned annotation views to polyline and polygon annotations. ([#5770](https://github.com/mapbox/mapbox-gl-native/pull/5770)) * Per documentation, the first and last coordinates in an MGLPolygon must be identical in order for the polygon to draw correctly. The same is true for an MGLPolygon’s interior polygon. ([#5514](https://github.com/mapbox/mapbox-gl-native/pull/5514)) * To make an MGLPolyline or MGLPolygon span the antimeridian, specify coordinates with longitudes greater than 180° or less than −180°. ([#6088](https://github.com/mapbox/mapbox-gl-native/pull/6088)) +* Deprecated `-[MGLMapViewDelegate mapView:alphaForShapeAnnotation:]` in favor of specifying an alpha component via `-[MGLMapViewDelegate mapView:strokeColorForShapeAnnotation:]` or `-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:]`. ([#6706](https://github.com/mapbox/mapbox-gl-native/pull/6706)) +* Fixed an issue that caused an annotation view to disappear if it isn’t created using the annotation view reuse queue. ([#6485](https://github.com/mapbox/mapbox-gl-native/pull/6485)) +* Fixed an issue that could reset user-added transformations on annotation views. ([#6166](https://github.com/mapbox/mapbox-gl-native/pull/6166)) * Improved the performance of relocating a non-view-backed point annotation by changing its `coordinate` property. ([#5385](https://github.com/mapbox/mapbox-gl-native/pull/5385)) * Improved the precision of annotations at zoom levels greater than 18. ([#5517](https://github.com/mapbox/mapbox-gl-native/pull/5517)) -* Fixed an issue that could reset user-added transformations on annotation views. ([#6166](https://github.com/mapbox/mapbox-gl-native/pull/6166)) -* Fixed an issue that caused an annotation view to disappear if it isn’t created using the annotation view reuse queue. ([#6485](https://github.com/mapbox/mapbox-gl-native/pull/6485)) -* Deprecated `-[MGLMapViewDelegate mapView:alphaForShapeAnnotation:]` in favor of specifying an alpha component via `-[MGLMapViewDelegate mapView:strokeColorForShapeAnnotation:]` or `-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:]`. ([#6706](https://github.com/mapbox/mapbox-gl-native/pull/6706)) ### Networking and offline maps @@ -69,6 +76,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Other changes * As the user zooms in, tiles from lower zoom levels are scaled up until tiles for higher zoom levels are loaded. ([#5143](https://github.com/mapbox/mapbox-gl-native/pull/5143)) +* Notification names and user info keys are now string enumeration values for ease of use in Swift. ([#6794](https://github.com/mapbox/mapbox-gl-native/pull/6794)) * MGLMapDebugOverdrawVisualizationMask no longer has any effect in Release builds of the SDK. This debug mask has been disabled for performance reasons. ([#5555](https://github.com/mapbox/mapbox-gl-native/pull/5555)) * Fixed a typo in the documentation for the MGLCompassDirectionFormatter class. ([#5879](https://github.com/mapbox/mapbox-gl-native/pull/5879)) diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index bbc042508..e26de7915 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |m| m.name = 'Mapbox-iOS-SDK' - m.version = '3.4.0-beta.1-symbols' + m.version = '3.4.0-beta.2-symbols' m.summary = 'Open source vector map solution for iOS with full styling capabilities.' m.description = 'Open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs.' diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 77ada60fa..6fd6d4b46 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |m| m.name = 'Mapbox-iOS-SDK' - m.version = '3.4.0-beta.1' + m.version = '3.4.0-beta.2' m.summary = 'Open source vector map solution for iOS with full styling capabilities.' m.description = 'Open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs.' diff --git a/platform/ios/app/Info.plist b/platform/ios/app/Info.plist index 3602ab796..043bebc92 100644 --- a/platform/ios/app/Info.plist +++ b/platform/ios/app/Info.plist @@ -51,5 +51,18 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSAppTransportSecurity + + NSExceptionDomains + + stamen.com + + NSIncludesSubdomains + + NSTemporaryExceptionAllowsInsecureHTTPLoads + + + + diff --git a/platform/ios/app/MBXOfflinePacksTableViewController.m b/platform/ios/app/MBXOfflinePacksTableViewController.m index 008d843b1..7bceec7ef 100644 --- a/platform/ios/app/MBXOfflinePacksTableViewController.m +++ b/platform/ios/app/MBXOfflinePacksTableViewController.m @@ -244,7 +244,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac MGLOfflinePack *pack = notification.object; NSAssert([pack isKindOfClass:[MGLOfflinePack class]], @"MGLOfflineStorage notification has a non-pack object."); - NSError *error = notification.userInfo[MGLOfflinePackErrorUserInfoKey]; + NSError *error = notification.userInfo[MGLOfflinePackUserInfoKeyError]; NSAssert([error isKindOfClass:[NSError class]], @"MGLOfflineStorage notification has a non-error error."); NSString *message = [NSString stringWithFormat:@"Mapbox GL encountered an error while downloading the offline pack “%@”: %@", pack.name, error.localizedFailureReason]; @@ -261,7 +261,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac MGLOfflinePack *pack = notification.object; NSAssert([pack isKindOfClass:[MGLOfflinePack class]], @"MGLOfflineStorage notification has a non-pack object."); - uint64_t maximumCount = [notification.userInfo[MGLOfflinePackMaximumCountUserInfoKey] unsignedLongLongValue]; + uint64_t maximumCount = [notification.userInfo[MGLOfflinePackUserInfoKeyMaximumCount] unsignedLongLongValue]; NSLog(@"Offline pack “%@” reached limit of %llu tiles.", pack.name, maximumCount); } diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 046f60238..a7ea42930 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -46,6 +46,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsAnnotationsRows) { MBXSettingsAnnotations10000Sprites, MBXSettingsAnnotationsTestShapes, MBXSettingsAnnotationsCustomCallout, + MBXSettingsAnnotationsQueryAnnotations, MBXSettingsAnnotationsRemoveAnnotations, }; @@ -62,10 +63,19 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) { MBXSettingsRuntimeStylingFilteredLines, MBXSettingsRuntimeStylingNumericFilteredFill, MBXSettingsRuntimeStylingStyleQuery, + MBXSettingsRuntimeStylingFeatureSource, + MBXSettingsRuntimeStylingPointCollection, + MBXSettingsRuntimeStylingUpdateGeoJSONSourceData, + MBXSettingsRuntimeStylingUpdateGeoJSONSourceURL, + MBXSettingsRuntimeStylingUpdateGeoJSONSourceFeatures, + MBXSettingsRuntimeStylingVectorSource, + MBXSettingsRuntimeStylingRasterSource, + MBXSettingsRuntimeStylingCountryLabels, }; typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { - MBXSettingsMiscellaneousWorldTour = 0, + MBXSettingsMiscellaneousShowReuseQueueStats = 0, + MBXSettingsMiscellaneousWorldTour, MBXSettingsMiscellaneousCustomUserDot, MBXSettingsMiscellaneousPrintLogFile, MBXSettingsMiscellaneousDeleteLogFile, @@ -93,10 +103,21 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { UITableViewDataSource, MGLMapViewDelegate> + @property (nonatomic) IBOutlet MGLMapView *mapView; +@property (weak, nonatomic) IBOutlet UILabel *hudLabel; @property (nonatomic) NSInteger styleIndex; @property (nonatomic) BOOL debugLoggingEnabled; @property (nonatomic) BOOL customUserLocationAnnnotationEnabled; +@property (nonatomic) BOOL usingLocaleBasedCountryLabels; +@property (nonatomic) BOOL reuseQueueStatsEnabled; + +@end + +@interface MGLMapView (MBXViewController) + +@property (nonatomic) BOOL usingLocaleBasedCountryLabels; +@property (nonatomic) NSDictionary *annotationViewReuseQueueByIdentifier; @end @@ -130,12 +151,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self restoreState:nil]; self.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]; -} + self.hudLabel.hidden = YES; -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; - if ([MGLAccountManager accessToken].length) { self.styleIndex = -1; @@ -289,6 +306,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Add 10,000 Sprites", @"Add Test Shapes", @"Add Point With Custom Callout", + @"Query Annotations", @"Remove Annotations", ]]; break; @@ -306,9 +324,19 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Style Lines With Filter", @"Style Fill With Numeric Filter", @"Style Query For GeoJSON", + @"Style Feature", + @"Style Dynamic Point Collection", + @"Update GeoJSON Source: Data", + @"Update GeoJSON Source: URL", + @"Update GeoJSON Source: Features", + @"Style Vector Source", + @"Style Raster Source", + [NSString stringWithFormat:@"Label Countries in %@", (_usingLocaleBasedCountryLabels ? @"Local Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])], ]]; break; case MBXSettingsMiscellaneous: + [settingsTitles addObject:@"Show Reuse Queue Stats"]; + [settingsTitles addObjectsFromArray:@[ @"Start World Tour", [NSString stringWithFormat:@"%@ Custom User Dot", (_customUserLocationAnnnotationEnabled ? @"Disable" : @"Enable")], @@ -321,6 +349,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Delete Telemetry Logfile", ]]; }; + break; default: NSAssert(NO, @"All settings sections should be implemented"); @@ -387,6 +416,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { case MBXSettingsAnnotationsCustomCallout: [self addAnnotationWithCustomCallout]; break; + case MBXSettingsAnnotationsQueryAnnotations: + [self testQueryPointAnnotations]; + break; case MBXSettingsAnnotationsRemoveAnnotations: [self.mapView removeAnnotations:self.mapView.annotations]; break; @@ -434,6 +466,30 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { case MBXSettingsRuntimeStylingStyleQuery: [self styleQuery]; break; + case MBXSettingsRuntimeStylingFeatureSource: + [self styleFeature]; + break; + case MBXSettingsRuntimeStylingPointCollection: + [self styleDynamicPointCollection]; + break; + case MBXSettingsRuntimeStylingUpdateGeoJSONSourceURL: + [self updateGeoJSONSourceURL]; + break; + case MBXSettingsRuntimeStylingUpdateGeoJSONSourceData: + [self updateGeoJSONSourceData]; + break; + case MBXSettingsRuntimeStylingUpdateGeoJSONSourceFeatures: + [self updateGeoJSONSourceFeatures]; + break; + case MBXSettingsRuntimeStylingVectorSource: + [self styleVectorSource]; + break; + case MBXSettingsRuntimeStylingRasterSource: + [self styleRasterSource]; + break; + case MBXSettingsRuntimeStylingCountryLabels: + [self styleCountryLabelsLanguage]; + break; default: NSAssert(NO, @"All runtime styling setting rows should be implemented"); break; @@ -454,6 +510,12 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { case MBXSettingsMiscellaneousDeleteLogFile: [self deleteTelemetryLogFile]; break; + case MBXSettingsMiscellaneousShowReuseQueueStats: + { + self.reuseQueueStatsEnabled = YES; + self.hudLabel.hidden = NO; + break; + } default: NSAssert(NO, @"All miscellaneous setting rows should be implemented"); break; @@ -794,7 +856,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { }); } - - (void)styleQuery { CGRect queryRect = CGRectInset(self.mapView.bounds, 100, 200); @@ -828,6 +889,247 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { }); } +- (void)styleFeature +{ + self.mapView.zoomLevel = 10; + self.mapView.centerCoordinate = CLLocationCoordinate2DMake(51.068585180672635, -114.06074523925781); + + CLLocationCoordinate2D leafCoords[] = { + CLLocationCoordinate2DMake(50.9683733218221,-114.07035827636719), + CLLocationCoordinate2DMake(51.02325750523972,-114.06967163085938), + CLLocationCoordinate2DMake(51.009434536947786,-114.14245605468749), + CLLocationCoordinate2DMake(51.030599281184124,-114.12597656249999), + CLLocationCoordinate2DMake(51.060386316691016,-114.21043395996094), + CLLocationCoordinate2DMake(51.063838646941576,-114.17816162109375), + CLLocationCoordinate2DMake(51.08152779888779,-114.19876098632812), + CLLocationCoordinate2DMake(51.08066507029602,-114.16854858398438), + CLLocationCoordinate2DMake(51.09662294502995,-114.17472839355469), + CLLocationCoordinate2DMake(51.07764539352731,-114.114990234375), + CLLocationCoordinate2DMake(51.13670896949613,-114.12391662597656), + CLLocationCoordinate2DMake(51.13369295212583,-114.09576416015624), + CLLocationCoordinate2DMake(51.17546878815025,-114.07585144042969), + CLLocationCoordinate2DMake(51.140155605265896,-114.04632568359375), + CLLocationCoordinate2DMake(51.15049396880196,-114.01542663574219), + CLLocationCoordinate2DMake(51.088860342359965,-114.00924682617186), + CLLocationCoordinate2DMake(51.12205789681453,-113.94813537597656), + CLLocationCoordinate2DMake(51.106539930027225,-113.94882202148438), + CLLocationCoordinate2DMake(51.117747873223344,-113.92616271972656), + CLLocationCoordinate2DMake(51.10093493903458,-113.92616271972656), + CLLocationCoordinate2DMake(51.10697105503078,-113.90625), + CLLocationCoordinate2DMake(51.09144802136697,-113.9117431640625), + CLLocationCoordinate2DMake(51.04916446529361,-113.97010803222655), + CLLocationCoordinate2DMake(51.045279344649146,-113.9398956298828), + CLLocationCoordinate2DMake(51.022825599852496,-114.06211853027344), + CLLocationCoordinate2DMake(51.045279344649146,-113.9398956298828), + CLLocationCoordinate2DMake(51.022825599852496,-114.06211853027344), + CLLocationCoordinate2DMake(51.022825599852496,-114.06280517578125), + CLLocationCoordinate2DMake(50.968805734317804,-114.06280517578125), + CLLocationCoordinate2DMake(50.9683733218221,-114.07035827636719), + }; + NSUInteger coordsCount = sizeof(leafCoords) / sizeof(leafCoords[0]); + + MGLPolygonFeature *feature = [MGLPolygonFeature polygonWithCoordinates:leafCoords count:coordsCount]; + feature.identifier = @"leaf-feature"; + feature.attributes = @{@"color": @"red"}; + + MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"leaf-source" features:@[feature] options:nil]; + [self.mapView.style addSource:source]; + + MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"leaf-fill-layer" source:source]; + layer.predicate = [NSPredicate predicateWithFormat:@"color = %@", @"red"]; + MGLStyleValue *fillColor = [MGLStyleValue valueWithRawValue:[UIColor redColor]]; + layer.fillColor = fillColor; + [self.mapView.style addLayer:layer]; +} + +- (void)updateGeoJSONSourceData +{ + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(40.329795743702064, -107.75390625) zoomLevel:11 animated:NO]; + + 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:@"mutable-data-source-id" geoJSONData:data options:nil]; + [self.mapView.style addSource:source]; + + MGLLineStyleLayer *layer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"mutable-data-layer-id" source:source]; + [self.mapView.style addLayer:layer]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + NSString *geoJSON = @"{\"type\": \"FeatureCollection\",\"features\": [{\"type\": \"Feature\",\"properties\": {},\"geometry\": {\"type\": \"LineString\",\"coordinates\": [[-107.75390625,40.329795743702064],[-109.34814453125,37.64903402157866]]}}]}"; + NSData *data = [geoJSON dataUsingEncoding:NSUTF8StringEncoding]; + + source.geoJSONData = data; + }); +} + +- (void)updateGeoJSONSourceURL +{ + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(48.668731, -122.857151) zoomLevel:11 animated:NO]; + + NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"polyline" ofType:@"geojson"]; + NSURL *geoJSONURL = [NSURL fileURLWithPath:filePath]; + MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"mutable-data-source-url-id" URL:geoJSONURL options:nil]; + [self.mapView.style addSource:source]; + + MGLLineStyleLayer *layer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"mutable-data-layer-url-id" source:source]; + [self.mapView.style addLayer:layer]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(41.563986787078704, -75.04843935793578) zoomLevel:8 animated:NO]; + + NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"threestates" ofType:@"geojson"]; + NSURL *geoJSONURL = [NSURL fileURLWithPath:filePath]; + + source.URL = geoJSONURL; + }); +} + +- (void)updateGeoJSONSourceFeatures +{ + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(-41.1520, 288.6592) zoomLevel:10 animated:NO]; + + CLLocationCoordinate2D smallBox[] = { + {-41.14763798539186, 288.68019104003906}, + {-41.140915920129665, 288.68019104003906}, + {-41.140915920129665, 288.6887741088867}, + {-41.14763798539186, 288.6887741088867}, + {-41.14763798539186, 288.68019104003906} + }; + + CLLocationCoordinate2D largeBox[] = { + {-41.17710352162799, 288.67298126220703}, + {-41.13962313627545, 288.67298126220703}, + {-41.13962313627545, 288.7261962890625}, + {-41.17710352162799, 288.7261962890625}, + {-41.17710352162799, 288.67298126220703} + }; + + MGLPolygonFeature *smallBoxFeature = [MGLPolygonFeature polygonWithCoordinates:smallBox count:sizeof(smallBox)/sizeof(smallBox[0])]; + MGLPolygonFeature *largeBoxFeature = [MGLPolygonFeature polygonWithCoordinates:largeBox count:sizeof(largeBox)/sizeof(largeBox[0])]; + + MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"mutable-data-source-features-id" features:@[smallBoxFeature] options:nil]; + [self.mapView.style addSource:source]; + + MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"mutable-data-layer-features-id" source:source]; + MGLStyleValue *fillColor = [MGLStyleValue valueWithRawValue:[UIColor redColor]]; + layer.fillColor = fillColor; + [self.mapView.style addLayer:layer]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + source.features = @[largeBoxFeature]; + }); +} + +- (void)styleDynamicPointCollection +{ + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(36.9979, -109.0441) zoomLevel:14 animated:NO]; + CLLocationCoordinate2D coordinates[] = { + {37.00145594210082, -109.04960632324219}, + {37.00173012609867, -109.0404224395752}, + {36.99453246847359, -109.04960632324219}, + {36.99508088541243, -109.04007911682129}, + }; + MGLPointCollectionFeature *feature = [MGLPointCollectionFeature pointCollectionWithCoordinates:coordinates count:4]; + MGLGeoJSONSource *source = [[MGLGeoJSONSource alloc] initWithIdentifier:@"wiggle-source" features:@[feature] options:nil]; + [self.mapView.style addSource:source]; + + MGLCircleStyleLayer *layer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"wiggle-layer" source:source]; + [self.mapView.style addLayer:layer]; +} + +- (void)styleVectorSource +{ + NSURL *url = [[NSURL alloc] initWithString:@"mapbox://mapbox.mapbox-terrain-v2"]; + MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"style-vector-source-id" URL:url]; + [self.mapView.style addSource:vectorSource]; + + MGLBackgroundStyleLayer *backgroundLayer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"style-vector-background-layer-id"]; + backgroundLayer.backgroundColor = [MGLStyleValue valueWithRawValue:[UIColor blackColor]]; + [self.mapView.style addLayer:backgroundLayer]; + + MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"style-vector-line-layer-id" source:vectorSource]; + lineLayer.sourceLayerIdentifier = @"contour"; + NSUInteger lineJoinValue = MGLLineJoinRound; + lineLayer.lineJoin = [MGLStyleValue valueWithRawValue:[NSValue value:&lineJoinValue withObjCType:@encode(MGLLineJoin)]]; + NSUInteger lineCapValue = MGLLineCapRound; + lineLayer.lineCap = [MGLStyleValue valueWithRawValue:[NSValue value:&lineCapValue withObjCType:@encode(MGLLineCap)]]; + lineLayer.lineColor = [MGLStyleValue valueWithRawValue:[UIColor greenColor]]; + + [self.mapView.style addLayer:lineLayer]; +} + +- (void)styleRasterSource +{ + // 3rd party raster source requires NSAppTransportSecurity exception for stamen.com + MGLTileSet *rasterTileSet = [[MGLTileSet alloc] initWithTileURLTemplates:@[@"http://a.tile.stamen.com/terrain-background/{z}/{x}/{y}.jpg"]]; + MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"style-raster-source-id" tileSet:rasterTileSet tileSize:256]; + [self.mapView.style addSource:rasterSource]; + + MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"style-raster-layer-id" source:rasterSource]; + [self.mapView.style addLayer:rasterLayer]; +} + +-(void)styleCountryLabelsLanguage +{ + NSArray *labelLayers = @[ + @"country-label-lg", + @"country-label-md", + @"country-label-sm", + ]; + [self styleLabelLanguageForLayersNamed:labelLayers]; +} + +- (void)styleLabelLanguageForLayersNamed:(NSArray *)layers +{ + _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels; + NSString *bestLanguageForUser = [NSString stringWithFormat:@"{name_%@}", [self bestLanguageForUser]]; + NSString *language = _usingLocaleBasedCountryLabels ? bestLanguageForUser : @"{name}"; + + for (NSString *layerName in layers) { + MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:layerName]; + + if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) { + if ([layer.textField isKindOfClass:[MGLStyleConstantValue class]]) { + MGLStyleConstantValue *label = (MGLStyleConstantValue *)layer.textField; + if ([label.rawValue hasPrefix:@"{name"]) { + layer.textField = [MGLStyleValue valueWithRawValue:language]; + } + } else if ([layer.textField isKindOfClass:[MGLStyleFunction class]]) { + MGLStyleFunction *function = (MGLStyleFunction *)layer.textField; + [function.stops enumerateKeysAndObjectsUsingBlock:^(id zoomLevel, id stop, BOOL *done) { + if ([stop isKindOfClass:[MGLStyleConstantValue class]]) { + MGLStyleConstantValue *label = (MGLStyleConstantValue *)stop; + if ([label.rawValue hasPrefix:@"{name"]) { + [function.stops setValue:[MGLStyleValue valueWithRawValue:language] forKey:zoomLevel]; + } + } + }]; + layer.textField = function; + } + } else { + NSLog(@"%@ is not a symbol style layer", layerName); + } + } +} + +- (NSString *)bestLanguageForUser +{ + NSArray *supportedLanguages = @[ @"en", @"es", @"fr", @"de", @"ru", @"zh" ]; + NSArray *preferredLanguages = [NSLocale preferredLanguages]; + NSString *bestLanguage; + + for (NSString *language in preferredLanguages) { + NSString *thisLanguage = [NSLocale localeWithLocaleIdentifier:language].languageCode; + if ([supportedLanguages containsObject:thisLanguage]) { + bestLanguage = thisLanguage; + break; + } + } + + return bestLanguage ?: @"en"; +} + - (IBAction)startWorldTour { _isTouringWorld = YES; @@ -875,6 +1177,20 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { self.mapView.userTrackingMode = MGLUserTrackingModeFollow; } +- (void)testQueryPointAnnotations { + NSNumber *visibleAnnotationCount = @(self.mapView.visibleAnnotations.count); + NSString *message; + if ([visibleAnnotationCount integerValue] == 1) { + message = [NSString stringWithFormat:@"There is %@ visible annotation.", visibleAnnotationCount]; + } else { + message = [NSString stringWithFormat:@"There are %@ visible annotations.", visibleAnnotationCount]; + } + + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Visible Annotations" message:message preferredStyle:UIAlertControllerStyleAlert]; + [alertController addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleCancel handler:nil]]; + [self presentViewController:alertController animated:YES completion:nil]; +} + - (void)printTelemetryLogFile { NSString *fileContents = [NSString stringWithContentsOfFile:[self telemetryDebugLogFilePath] encoding:NSUTF8StringEncoding error:nil]; @@ -1208,4 +1524,24 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { point.coordinate = [self.mapView convertPoint:self.mapView.center toCoordinateFromView:self.mapView]; } +- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style +{ + // Default Mapbox styles use {name_en} as their label language, which means + // that a device with an English-language locale is already effectively + // using locale-based country labels. + _usingLocaleBasedCountryLabels = [[self bestLanguageForUser] isEqualToString:@"en"]; +} + +- (void)mapViewRegionIsChanging:(MGLMapView *)mapView +{ + if (self.reuseQueueStatsEnabled) { + NSUInteger queuedAnnotations = 0; + for (NSArray *queue in self.mapView.annotationViewReuseQueueByIdentifier.allValues) + { + queuedAnnotations += queue.count; + } + self.hudLabel.text = [NSString stringWithFormat:@"Visible: %ld Queued: %ld", (long)mapView.visibleAnnotations.count, (long)queuedAnnotations]; + } +} + @end diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard index 7d8b2c304..5819f17ed 100644 --- a/platform/ios/app/Main.storyboard +++ b/platform/ios/app/Main.storyboard @@ -1,9 +1,11 @@ - - + + - + + + @@ -15,24 +17,35 @@ - + - - + + - + + + @@ -47,7 +60,7 @@