diff options
author | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-08-23 18:39:14 -0400 |
---|---|---|
committer | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-08-23 18:39:14 -0400 |
commit | 2b8affc3c76c19ea3f44cabf672c06e158e67b58 (patch) | |
tree | 83e7437c12d1d5b3df4b7e8107aeb033a0534fcb /platform/darwin | |
parent | f2c9f2bf3695fc30579a40978ebda9d87e4b4bcd (diff) | |
parent | acb8199d326eda02102b2d409ebec510053fec1b (diff) | |
download | qtlocation-mapboxgl-2b8affc3c76c19ea3f44cabf672c06e158e67b58.tar.gz |
Merge branch 'release-ios-v3.6.0-android-v5.1.0' into releaseupstream/fabian-merge-v3.6.0-branch
# Conflicts:
# cmake/core-files.cmake
# include/mbgl/renderer/renderer_backend.hpp
# platform/android/CHANGELOG.md
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
# platform/darwin/src/MGLStyle.mm
# platform/ios/CHANGELOG.md
# platform/ios/app/Info.plist
# platform/ios/app/MBXViewController.m
# platform/ios/ios.xcodeproj/project.pbxproj
# platform/macos/CHANGELOG.md
# src/mbgl/gl/attribute.cpp
# src/mbgl/gl/attribute.hpp
# src/mbgl/gl/context.hpp
# src/mbgl/gl/types.hpp
# src/mbgl/gl/value.cpp
# src/mbgl/gl/value.hpp
# src/mbgl/map/backend_scope.cpp
# src/mbgl/programs/attributes.hpp
# src/mbgl/programs/program.hpp
# src/mbgl/programs/segment.cpp
# src/mbgl/programs/segment.hpp
# src/mbgl/programs/symbol_program.hpp
# src/mbgl/programs/uniforms.hpp
# src/mbgl/renderer/buckets/symbol_bucket.hpp
# src/mbgl/renderer/painter.cpp
# src/mbgl/renderer/painter.hpp
# src/mbgl/renderer/painter_background.cpp
# src/mbgl/renderer/painter_circle.cpp
# src/mbgl/renderer/painter_clipping.cpp
# src/mbgl/renderer/painter_debug.cpp
# src/mbgl/renderer/painter_fill.cpp
# src/mbgl/renderer/painter_fill_extrusion.cpp
# src/mbgl/renderer/painter_line.cpp
# src/mbgl/renderer/painter_raster.cpp
# src/mbgl/renderer/painter_symbol.cpp
# src/mbgl/shaders/line.cpp
# src/mbgl/shaders/line_pattern.cpp
# test/renderer/backend_scope.test.cpp
Diffstat (limited to 'platform/darwin')
-rw-r--r-- | platform/darwin/docs/guides/Working with GeoJSON Data.md | 6 | ||||
-rw-r--r-- | platform/darwin/resources/fr.lproj/Foundation.strings | 291 | ||||
-rw-r--r-- | platform/darwin/src/MGLFeature.h | 14 | ||||
-rw-r--r-- | platform/darwin/src/MGLFeature.mm | 49 | ||||
-rw-r--r-- | platform/darwin/src/MGLPolygon.mm | 10 | ||||
-rw-r--r-- | platform/darwin/src/MGLPolyline.h | 14 | ||||
-rw-r--r-- | platform/darwin/src/MGLPolyline.mm | 10 | ||||
-rw-r--r-- | platform/darwin/src/MGLSource.mm | 5 | ||||
-rw-r--r-- | platform/darwin/src/MGLSource_Private.h | 2 | ||||
-rw-r--r-- | platform/darwin/src/MGLStyle.h | 15 | ||||
-rw-r--r-- | platform/darwin/src/MGLStyle.mm | 265 | ||||
-rw-r--r-- | platform/darwin/src/MGLVectorSource+MGLAdditions.h | 15 | ||||
-rw-r--r-- | platform/darwin/src/MGLVectorSource+MGLAdditions.m | 53 | ||||
-rw-r--r-- | platform/darwin/test/MGLDocumentationExampleTests.swift | 12 |
14 files changed, 699 insertions, 62 deletions
diff --git a/platform/darwin/docs/guides/Working with GeoJSON Data.md b/platform/darwin/docs/guides/Working with GeoJSON Data.md index 57aaa3855d..f3b3dc0918 100644 --- a/platform/darwin/docs/guides/Working with GeoJSON Data.md +++ b/platform/darwin/docs/guides/Working with GeoJSON Data.md @@ -81,8 +81,10 @@ Linear ring | `MGLPolygon.coordinates`, `MGLPolygon.interiorPolygons` A `Feature` object in GeoJSON corresponds to an instance of an `MGLShape` subclass conforming to the `MGLFeature` protocol. There is a distinct `MGLFeature`-conforming class for each type of geometry that a GeoJSON feature -can contain. This allows features to be used as shapes where convenient. For -example, some features can be added to a map view as annotations. +can contain. This allows features to be used as raw shapes where convenient. For +example, some features can be added to a map view as annotations. Note that +identifiers and attributes will not be available for feature querying when a +feature is used as an annotation. In contrast to the GeoJSON standard, it is possible for `MGLShape` subclasses other than `MGLPointAnnotation` to straddle the antimeridian. diff --git a/platform/darwin/resources/fr.lproj/Foundation.strings b/platform/darwin/resources/fr.lproj/Foundation.strings new file mode 100644 index 0000000000..d2f6c1f6df --- /dev/null +++ b/platform/darwin/resources/fr.lproj/Foundation.strings @@ -0,0 +1,291 @@ +/* Clock position format, long: {hours} o’clock */ +"CLOCK_FMT_LONG" = "%@ heures"; + +/* Clock position format, medium: {hours} o’clock */ +"CLOCK_FMT_MEDIUM" = "%@ heures"; + +/* Clock position format, short: {hours}:00 */ +"CLOCK_FMT_SHORT" = "%@h 00"; + +/* East, long */ +"COMPASS_E_LONG" = "est"; + +/* East, short */ +"COMPASS_E_SHORT" = "E"; + +/* East by north, long */ +"COMPASS_EbN_LONG" = "est par nord"; + +/* East by north, short */ +"COMPASS_EbN_SHORT" = "EpN"; + +/* East by south, long */ +"COMPASS_EbS_LONG" = "est par sud"; + +/* East by south, short */ +"COMPASS_EbS_SHORT" = "EpS"; + +/* East-northeast, long */ +"COMPASS_ENE_LONG" = "est-nord-est"; + +/* East-northeast, short */ +"COMPASS_ENE_SHORT" = "ENE"; + +/* East-southeast, long */ +"COMPASS_ESE_LONG" = "est-sud-est"; + +/* East-southeast, short */ +"COMPASS_ESE_SHORT" = "ESE"; + +/* North, long */ +"COMPASS_N_LONG" = "nord"; + +/* North, short */ +"COMPASS_N_SHORT" = "N"; + +/* North by east, long */ +"COMPASS_NbE_LONG" = "nord par est"; + +/* North by east, short */ +"COMPASS_NbE_SHORT" = "NpE"; + +/* North by west, long */ +"COMPASS_NbW_LONG" = "nord par ouest"; + +/* North by west, short */ +"COMPASS_NbW_SHORT" = "NpO"; + +/* Northeast, long */ +"COMPASS_NE_LONG" = "nord-est"; + +/* Northeast, short */ +"COMPASS_NE_SHORT" = "NE"; + +/* Northeast by east, long */ +"COMPASS_NEbE_LONG" = "nord-est par est"; + +/* Northeast by east, short */ +"COMPASS_NEbE_SHORT" = "NEpE"; + +/* Northeast by north, long */ +"COMPASS_NEbN_LONG" = "nord-est par nord"; + +/* Northeast by north, short */ +"COMPASS_NEbN_SHORT" = "NEpN"; + +/* North-northeast, long */ +"COMPASS_NNE_LONG" = "nord-nord-est"; + +/* North-northeast, short */ +"COMPASS_NNE_SHORT" = "NNE"; + +/* North-northwest, long */ +"COMPASS_NNW_LONG" = "nord-nord-ouest"; + +/* North-northwest, short */ +"COMPASS_NNW_SHORT" = "NNO"; + +/* Northwest, long */ +"COMPASS_NW_LONG" = "nord-ouest"; + +/* Northwest, short */ +"COMPASS_NW_SHORT" = "NO"; + +/* Northwest by north, long */ +"COMPASS_NWbN_LONG" = "nord-ouest par nord"; + +/* Northwest by north, short */ +"COMPASS_NWbN_SHORT" = "NOpN"; + +/* Northwest by west, long */ +"COMPASS_NWbW_LONG" = "nord-ouest par ouest"; + +/* Northwest by west, short */ +"COMPASS_NWbW_SHORT" = "NOpO"; + +/* South, long */ +"COMPASS_S_LONG" = "sud"; + +/* South, short */ +"COMPASS_S_SHORT" = "S"; + +/* South by east, long */ +"COMPASS_SbE_LONG" = "sud par est"; + +/* South by east, short */ +"COMPASS_SbE_SHORT" = "SpE"; + +/* South by west, long */ +"COMPASS_SbW_LONG" = "sud par ouest"; + +/* South by west, short */ +"COMPASS_SbW_SHORT" = "SpO"; + +/* Southeast, long */ +"COMPASS_SE_LONG" = "sud-est"; + +/* Southeast, short */ +"COMPASS_SE_SHORT" = "SE"; + +/* Southeast by east, long */ +"COMPASS_SEbE_LONG" = "sud-est par est"; + +/* Southeast by east, short */ +"COMPASS_SEbE_SHORT" = "SEpE"; + +/* Southeast by south, long */ +"COMPASS_SEbS_LONG" = "sud-est par sud"; + +/* Southeast by south, short */ +"COMPASS_SEbS_SHORT" = "SEpS"; + +/* South-southeast, long */ +"COMPASS_SSE_LONG" = "sud-sud-est"; + +/* South-southeast, short */ +"COMPASS_SSE_SHORT" = "SSE"; + +/* South-southwest, long */ +"COMPASS_SSW_LONG" = "sud-sud-ouest"; + +/* South-southwest, short */ +"COMPASS_SSW_SHORT" = "SSO"; + +/* Southwest, long */ +"COMPASS_SW_LONG" = "sud-ouest"; + +/* Southwest, short */ +"COMPASS_SW_SHORT" = "SO"; + +/* Southwest by south, long */ +"COMPASS_SWbS_LONG" = "sud-ouest par sud"; + +/* Southwest by south, short */ +"COMPASS_SWbS_SHORT" = "SOpS"; + +/* Southwest by west, long */ +"COMPASS_SWbW_LONG" = "sud-ouest par ouest"; + +/* Southwest by west, short */ +"COMPASS_SWbW_SHORT" = "SOpO"; + +/* West, long */ +"COMPASS_W_LONG" = "ouest"; + +/* West, short */ +"COMPASS_W_SHORT" = "O"; + +/* West by north, long */ +"COMPASS_WbN_LONG" = "ouest par nord"; + +/* West by north, short */ +"COMPASS_WbN_SHORT" = "OpN"; + +/* West by south, long */ +"COMPASS_WbS_LONG" = "ouest par sud"; + +/* West by south, short */ +"COMPASS_WbS_SHORT" = "OpS"; + +/* West-northwest, long */ +"COMPASS_WNW_LONG" = "ouest-nord-ouest"; + +/* West-northwest, short */ +"COMPASS_WNW_SHORT" = "ONO"; + +/* West-southwest, long */ +"COMPASS_WSW_LONG" = "ouest-sud-ouest"; + +/* West-southwest, short */ +"COMPASS_WSW_SHORT" = "OSO"; + +/* Degrees format, long */ +"COORD_DEG_LONG" = "%d degré(s)"; + +/* Degrees format, medium: {degrees} */ +"COORD_DEG_MEDIUM" = "%d°"; + +/* Degrees format, short: {degrees} */ +"COORD_DEG_SHORT" = "%d°"; + +/* Coordinate format, long: {degrees}{minutes} */ +"COORD_DM_LONG" = "%1$@ et %2$@"; + +/* Coordinate format, medium: {degrees}{minutes} */ +"COORD_DM_MEDIUM" = "%1$@%2$@"; + +/* Coordinate format, short: {degrees}{minutes} */ +"COORD_DM_SHORT" = "%1$@%2$@"; + +/* Coordinate format, long: {degrees}{minutes}{seconds} */ +"COORD_DMS_LONG" = "%1$@, %2$@ et %3$@"; + +/* Coordinate format, medium: {degrees}{minutes}{seconds} */ +"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@"; + +/* Coordinate format, short: {degrees}{minutes}{seconds} */ +"COORD_DMS_SHORT" = "%1$@%2$@%3$@"; + +/* East longitude format, long: {longitude} */ +"COORD_E_LONG" = "%@ est"; + +/* East longitude format, medium: {longitude} */ +"COORD_E_MEDIUM" = "%@ est"; + +/* East longitude format, short: {longitude} */ +"COORD_E_SHORT" = "%@E"; + +/* Coordinate pair format, long: {latitude}, {longitude} */ +"COORD_FMT_LONG" = "%1$@, %2$@"; + +/* Coordinate pair format, medium: {latitude}, {longitude} */ +"COORD_FMT_MEDIUM" = "%1$@, %2$@"; + +/* Coordinate pair format, short: {latitude}, {longitude} */ +"COORD_FMT_SHORT" = "%1$@, %2$@"; + +/* Minutes format, long */ +"COORD_MIN_LONG" = "%d minute(s)"; + +/* Minutes format, medium: {minutes} */ +"COORD_MIN_MEDIUM" = "%d′"; + +/* Minutes format, short: {minutes} */ +"COORD_MIN_SHORT" = "%d′"; + +/* North latitude format, long: {latitude} */ +"COORD_N_LONG" = "%@ nord"; + +/* North latitude format, medium: {latitude} */ +"COORD_N_MEDIUM" = "%@ nord"; + +/* North latitude format, short: {latitude} */ +"COORD_N_SHORT" = "%@N"; + +/* South latitude format, long: {latitude} */ +"COORD_S_LONG" = "%@ sud"; + +/* South latitude format, medium: {latitude} */ +"COORD_S_MEDIUM" = "%@ sud"; + +/* South latitude format, short: {latitude} */ +"COORD_S_SHORT" = "%@S"; + +/* Seconds format, long */ +"COORD_SEC_LONG" = "%d seconde(s)"; + +/* Seconds format, medium: {seconds} */ +"COORD_SEC_MEDIUM" = "%d″"; + +/* Seconds format, short: {seconds} */ +"COORD_SEC_SHORT" = "%d″"; + +/* West longitude format, long: {longitude} */ +"COORD_W_LONG" = "%@ ouest"; + +/* West longitude format, medium: {longitude} */ +"COORD_W_MEDIUM" = "%@ ouest"; + +/* West longitude format, short: {longitude} */ +"COORD_W_SHORT" = "%@O"; + diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h index 491c89b608..a13821cf96 100644 --- a/platform/darwin/src/MGLFeature.h +++ b/platform/darwin/src/MGLFeature.h @@ -18,15 +18,23 @@ NS_ASSUME_NONNULL_BEGIN You can add custom data to display on the map by creating feature objects and adding them to an `MGLShapeSource` using the `-[MGLShapeSource initWithIdentifier:shape:options:]` method or - `MGLShapeSource.shape` property. Similarly, you can add `MGLPointFeature`, - `MGLPolylineFeature`, and `MGLPolygonFeature` objects to the map as annotations - using `-[MGLMapView addAnnotations:]` and related methods. + `MGLShapeSource.shape` property. In addition to adding data to the map, you can also extract data from the map: `-[MGLMapView visibleFeaturesAtPoint:]` and related methods return feature objects that correspond to features in the source. This enables you to inspect the properties of features in vector tiles loaded by `MGLVectorSource` objects. You also reuse these feature objects as overlay annotations. + + While it is possible to add `MGLFeature`-conforming objects to the map as + annotations using `-[MGLMapView addAnnotations:]` and related methods, doing so + has trade-offs: + + - Features added as annotations will not have `identifier` or `attributes` + properties when used with feature querying. + + - Features added as annotations become interactive. Taps and selection can be + handled in `-[MGLMapViewDelegate mapView:didSelectAnnotation:]`. */ @protocol MGLFeature <MGLAnnotation> diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm index e169ee19bb..84f1a1ff25 100644 --- a/platform/darwin/src/MGLFeature.mm +++ b/platform/darwin/src/MGLFeature.mm @@ -42,6 +42,15 @@ MGL_DEFINE_FEATURE_IS_EQUAL(); return mbglFeature({[self geometryObject]}, identifier, self.attributes); } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p; identifier = %@, coordinate = %f, %f, attributes = %@>", + NSStringFromClass([self class]), (void *)self, + self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier, + self.coordinate.latitude, self.coordinate.longitude, + self.attributes.count ? self.attributes : @"none"]; +} + @end @interface MGLPolylineFeature () @@ -68,6 +77,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL(); return mbglFeature({[self geometryObject]}, identifier, self.attributes); } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>", + NSStringFromClass([self class]), (void *)self, + self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier, + (unsigned long)[self pointCount], + MGLStringFromCoordinateBounds(self.overlayBounds), + self.attributes.count ? self.attributes : @"none"]; +} + @end @interface MGLPolygonFeature () @@ -94,6 +113,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL(); return mbglFeature({[self geometryObject]}, identifier, self.attributes); } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>", + NSStringFromClass([self class]), (void *)self, + self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier, + (unsigned long)[self pointCount], + MGLStringFromCoordinateBounds(self.overlayBounds), + self.attributes.count ? self.attributes : @"none"]; +} + @end @interface MGLPointCollectionFeature () @@ -146,6 +175,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL(); return mbglFeature({[self geometryObject]}, identifier, self.attributes); } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>", + NSStringFromClass([self class]), (void *)self, + self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier, + (unsigned long)self.polylines.count, + MGLStringFromCoordinateBounds(self.overlayBounds), + self.attributes.count ? self.attributes : @"none"]; +} + @end @interface MGLMultiPolygonFeature () @@ -172,6 +211,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL(); return mbglFeature({[self geometryObject]}, identifier, self.attributes); } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>", + NSStringFromClass([self class]), (void *)self, + self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier, + (unsigned long)self.polygons.count, + MGLStringFromCoordinateBounds(self.overlayBounds), + self.attributes.count ? self.attributes : @"none"]; +} + @end @interface MGLShapeCollectionFeature () diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm index d966ff13ce..e7843224e9 100644 --- a/platform/darwin/src/MGLPolygon.mm +++ b/platform/darwin/src/MGLPolygon.mm @@ -200,4 +200,14 @@ @"coordinates": coordinates}; } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>", + NSStringFromClass([self class]), (void *)self, + self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title, + self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle] : self.subtitle, + (unsigned long)self.polygons.count, + MGLStringFromCoordinateBounds(self.overlayBounds)]; +} + @end diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h index b3db0fd39f..e46baa91cc 100644 --- a/platform/darwin/src/MGLPolyline.h +++ b/platform/darwin/src/MGLPolyline.h @@ -33,8 +33,18 @@ NS_ASSUME_NONNULL_BEGIN `MGLPolygon` object. To group multiple polylines together in one shape, use an `MGLMultiPolyline` or `MGLShapeCollection` object. - To make the polyline straddle the antimeridian, specify some longitudes less - than −180 degrees or greater than 180 degrees. + To make the polyline go across the antimeridian or international date line, + specify some longitudes less than −180 degrees or greater than 180 degrees. + For example, a polyline that stretches from Tokyo to San Francisco would have + coordinates of (35.68476, -220.24257) and (37.78428, -122.41310). + + ```swift + let coordinates = [ + CLLocationCoordinate2D(latitude: 35.68476, longitude: -220.24257), + CLLocationCoordinate2D(latitude: 37.78428, longitude: -122.41310) + ] + let polyline = MGLPolyline(coordinates: coordinates, count: UInt(coordinates.count)) + ``` A polyline is known as a <a href="https://tools.ietf.org/html/rfc7946#section-3.1.4">LineString</a> diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm index fd75dc2795..0e371a4dda 100644 --- a/platform/darwin/src/MGLPolyline.mm +++ b/platform/darwin/src/MGLPolyline.mm @@ -201,4 +201,14 @@ @"coordinates": coordinates}; } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>", + NSStringFromClass([self class]), (void *)self, + self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title, + self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle] : self.subtitle, + (unsigned long)self.polylines.count, + MGLStringFromCoordinateBounds(self.overlayBounds)]; +} + @end diff --git a/platform/darwin/src/MGLSource.mm b/platform/darwin/src/MGLSource.mm index ee012f4d66..dde55967d7 100644 --- a/platform/darwin/src/MGLSource.mm +++ b/platform/darwin/src/MGLSource.mm @@ -27,17 +27,18 @@ return self; } -- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource { +- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource mapView:(MGLMapView *)mapView { NSString *identifier = @(rawSource->getID().c_str()); if (self = [self initWithIdentifier:identifier]) { _rawSource = rawSource; _rawSource->peer = SourceWrapper { self }; + _mapView = mapView; } return self; } - (instancetype)initWithPendingSource:(std::unique_ptr<mbgl::style::Source>)pendingSource { - if (self = [self initWithRawSource:pendingSource.get()]) { + if (self = [self initWithRawSource:pendingSource.get() mapView:nil]) { _pendingSource = std::move(pendingSource); } return self; diff --git a/platform/darwin/src/MGLSource_Private.h b/platform/darwin/src/MGLSource_Private.h index ba78973279..d83ca3335a 100644 --- a/platform/darwin/src/MGLSource_Private.h +++ b/platform/darwin/src/MGLSource_Private.h @@ -26,7 +26,7 @@ struct SourceWrapper { Initializes and returns a source with a raw pointer to the backing store, associated with a style. */ -- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource; +- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource mapView:(nullable MGLMapView *)mapView; /** Initializes and returns a source with an owning pointer to the backing store, diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index 6fb4a6cc6b..98be70be9c 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -557,6 +557,21 @@ MGL_EXPORT */ @property (nonatomic, strong) MGLLight *light; +#pragma mark Localizing Map Content + +/** + A Boolean value that determines whether the style attempts to localize labels in + the style into the system’s preferred language. + + When this property is enabled, the style automatically modifies the text property + of any symbol style layer whose source is the + <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox + Streets source</a>. On iOS, the user can set the system’s preferred language in + Settings, General Settings, Language & Region. On macOS, the user can set the + system’s preferred language in the Language & Region pane of System Preferences. + */ +@property (nonatomic) BOOL localizesLabels; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 2365641f02..d8aa014341 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -2,7 +2,6 @@ #import "MGLMapView_Private.h" #import "MGLStyleLayer.h" -#import "MGLStyleLayer_Private.h" #import "MGLFillStyleLayer.h" #import "MGLFillExtrusionStyleLayer.h" #import "MGLLineStyleLayer.h" @@ -12,20 +11,24 @@ #import "MGLBackgroundStyleLayer.h" #import "MGLOpenGLStyleLayer.h" -#import "MGLSource.h" +#import "MGLStyle_Private.h" +#import "MGLStyleLayer_Private.h" #import "MGLSource_Private.h" #import "MGLLight_Private.h" + +#import "NSDate+MGLAdditions.h" + +#import "MGLSource.h" #import "MGLTileSource_Private.h" #import "MGLVectorSource.h" +#import "MGLVectorSource+MGLAdditions.h" #import "MGLRasterSource.h" #import "MGLShapeSource.h" -#import "MGLImageSource.h" #import "MGLAttributionInfo_Private.h" #include <mbgl/map/map.hpp> #include <mbgl/util/default_styles.hpp> -#include <mbgl/style/style.hpp> #include <mbgl/style/image.hpp> #include <mbgl/style/light.hpp> #include <mbgl/style/layers/fill_layer.hpp> @@ -39,9 +42,6 @@ #include <mbgl/style/sources/geojson_source.hpp> #include <mbgl/style/sources/vector_source.hpp> #include <mbgl/style/sources/raster_source.hpp> -#include <mbgl/style/sources/image_source.hpp> - -#import "NSDate+MGLAdditions.h" #if TARGET_OS_IPHONE #import "UIImage+MGLAdditions.h" @@ -49,12 +49,34 @@ #import "NSImage+MGLAdditions.h" #endif +/** + Model class for localization changes. + */ +@interface MGLTextLanguage: NSObject +@property (strong, nonatomic) NSString *originalTextField; +@property (strong, nonatomic) NSString *updatedTextField; + +- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField; + +@end + +@implementation MGLTextLanguage +- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField +{ + if (self = [super init]) { + _originalTextField = originalTextField; + _updatedTextField = updatedTextField; + } + return self; +} +@end + @interface MGLStyle() -@property (nonatomic, readonly, weak) MGLMapView *mapView; -@property (nonatomic, readonly) mbgl::style::Style *rawStyle; +@property (nonatomic, readwrite, weak) MGLMapView *mapView; @property (readonly, copy, nullable) NSURL *URL; @property (nonatomic, readwrite, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers; +@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier; @end @@ -114,28 +136,28 @@ static NSURL *MGLStyleURL_emerald; #pragma mark - -- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle mapView:(MGLMapView *)mapView { +- (instancetype)initWithMapView:(MGLMapView *)mapView { if (self = [super init]) { _mapView = mapView; - _rawStyle = rawStyle; _openGLLayers = [NSMutableDictionary dictionary]; + _localizedLayersByIdentifier = [NSMutableDictionary dictionary]; } return self; } - (NSURL *)URL { - return [NSURL URLWithString:@(self.rawStyle->getURL().c_str())]; + return [NSURL URLWithString:@(self.mapView.mbglMap->getStyleURL().c_str())]; } - (NSString *)name { - std::string name = self.rawStyle->getName(); + std::string name = self.mapView.mbglMap->getStyleName(); return name.empty() ? nil : @(name.c_str()); } #pragma mark Sources - (NS_SET_OF(__kindof MGLSource *) *)sources { - auto rawSources = self.rawStyle->getSources(); + auto rawSources = self.mapView.mbglMap->getSources(); NS_MUTABLE_SET_OF(__kindof MGLSource *) *sources = [NSMutableSet setWithCapacity:rawSources.size()]; for (auto rawSource = rawSources.begin(); rawSource != rawSources.end(); ++rawSource) { MGLSource *source = [self sourceFromMBGLSource:*rawSource]; @@ -154,7 +176,8 @@ static NSURL *MGLStyleURL_emerald; } - (NSUInteger)countOfSources { - return self.rawStyle->getSources().size(); + auto rawSources = self.mapView.mbglMap->getSources(); + return rawSources.size(); } - (MGLSource *)memberOfSources:(MGLSource *)object { @@ -163,7 +186,8 @@ static NSURL *MGLStyleURL_emerald; - (MGLSource *)sourceWithIdentifier:(NSString *)identifier { - auto rawSource = self.rawStyle->getSource(identifier.UTF8String); + auto rawSource = self.mapView.mbglMap->getSource(identifier.UTF8String); + return rawSource ? [self sourceFromMBGLSource:rawSource] : nil; } @@ -175,15 +199,13 @@ static NSURL *MGLStyleURL_emerald; // TODO: Fill in options specific to the respective source classes // https://github.com/mapbox/mapbox-gl-native/issues/6584 if (auto vectorSource = rawSource->as<mbgl::style::VectorSource>()) { - return [[MGLVectorSource alloc] initWithRawSource:vectorSource]; + return [[MGLVectorSource alloc] initWithRawSource:vectorSource mapView:self.mapView]; } else if (auto geoJSONSource = rawSource->as<mbgl::style::GeoJSONSource>()) { - return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource]; + return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource mapView:self.mapView]; } else if (auto rasterSource = rawSource->as<mbgl::style::RasterSource>()) { - return [[MGLRasterSource alloc] initWithRawSource:rasterSource]; - } else if (auto imageSource = rawSource->as<mbgl::style::ImageSource>()) { - return [[MGLImageSource alloc] initWithRawSource:imageSource]; + return [[MGLRasterSource alloc] initWithRawSource:rasterSource mapView:self.mapView]; } else { - return [[MGLSource alloc] initWithRawSource:rawSource]; + return [[MGLSource alloc] initWithRawSource:rawSource mapView:self.mapView]; } } @@ -197,7 +219,7 @@ static NSURL *MGLStyleURL_emerald; } try { - [source addToStyle:self]; + [source addToMapView:self.mapView]; } catch (std::runtime_error & err) { [NSException raise:@"MGLRedundantSourceIdentifierException" format:@"%s", err.what()]; } @@ -211,14 +233,14 @@ static NSURL *MGLStyleURL_emerald; @"Make sure the source was created as a member of a concrete subclass of MGLSource.", source]; } - [source removeFromStyle:self]; + [source removeFromMapView:self.mapView]; } - (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { // It’d be incredibly convenient to use -sources here, but this operation // depends on the sources being sorted in ascending order by creation, as // with the std::vector used in mbgl. - auto rawSources = self.rawStyle->getSources(); + auto rawSources = self.mapView.mbglMap->getSources(); NSMutableArray *infos = [NSMutableArray arrayWithCapacity:rawSources.size()]; for (auto rawSource = rawSources.begin(); rawSource != rawSources.end(); ++rawSource) { MGLTileSource *source = (MGLTileSource *)[self sourceFromMBGLSource:*rawSource]; @@ -236,7 +258,7 @@ static NSURL *MGLStyleURL_emerald; - (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers { - auto layers = self.rawStyle->getLayers(); + auto layers = self.mapView.mbglMap->getLayers(); NS_MUTABLE_ARRAY_OF(__kindof MGLStyleLayer *) *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()]; for (auto layer : layers) { MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:layer]; @@ -256,12 +278,12 @@ static NSURL *MGLStyleURL_emerald; - (NSUInteger)countOfLayers { - return self.rawStyle->getLayers().size(); + return self.mapView.mbglMap->getLayers().size(); } - (MGLStyleLayer *)objectInLayersAtIndex:(NSUInteger)index { - auto layers = self.rawStyle->getLayers(); + auto layers = self.mapView.mbglMap->getLayers(); if (index >= layers.size()) { [NSException raise:NSRangeException format:@"No style layer at index %lu.", (unsigned long)index]; @@ -273,7 +295,7 @@ static NSURL *MGLStyleURL_emerald; - (void)getLayers:(MGLStyleLayer **)buffer range:(NSRange)inRange { - auto layers = self.rawStyle->getLayers(); + auto layers = self.mapView.mbglMap->getLayers(); if (NSMaxRange(inRange) > layers.size()) { [NSException raise:NSRangeException format:@"Style layer range %@ is out of bounds.", NSStringFromRange(inRange)]; @@ -293,21 +315,21 @@ static NSURL *MGLStyleURL_emerald; @"Make sure the style layer was created as a member of a concrete subclass of MGLStyleLayer.", styleLayer]; } - auto layers = self.rawStyle->getLayers(); + auto layers = self.mapView.mbglMap->getLayers(); if (index > layers.size()) { [NSException raise:NSRangeException format:@"Cannot insert style layer at out-of-bounds index %lu.", (unsigned long)index]; } else if (index == 0) { try { MGLStyleLayer *sibling = layers.size() ? [self layerFromMBGLLayer:layers.at(0)] : nil; - [styleLayer addToStyle:self belowLayer:sibling]; + [styleLayer addToMapView:self.mapView belowLayer:sibling]; } catch (const std::runtime_error & err) { [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; } } else { try { MGLStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(index)]; - [styleLayer addToStyle:self belowLayer:sibling]; + [styleLayer addToMapView:self.mapView belowLayer:sibling]; } catch (std::runtime_error & err) { [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; } @@ -316,14 +338,14 @@ static NSURL *MGLStyleURL_emerald; - (void)removeObjectFromLayersAtIndex:(NSUInteger)index { - auto layers = self.rawStyle->getLayers(); + auto layers = self.mapView.mbglMap->getLayers(); if (index >= layers.size()) { [NSException raise:NSRangeException format:@"Cannot remove style layer at out-of-bounds index %lu.", (unsigned long)index]; } auto layer = layers.at(index); MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:layer]; - [styleLayer removeFromStyle:self]; + [styleLayer removeFromMapView:self.mapView]; } - (MGLStyleLayer *)layerFromMBGLLayer:(mbgl::style::Layer *)rawLayer @@ -358,7 +380,7 @@ static NSURL *MGLStyleURL_emerald; - (MGLStyleLayer *)layerWithIdentifier:(NSString *)identifier { - auto mbglLayer = self.rawStyle->getLayer(identifier.UTF8String); + auto mbglLayer = self.mapView.mbglMap->getLayer(identifier.UTF8String); return mbglLayer ? [self layerFromMBGLLayer:mbglLayer] : nil; } @@ -371,7 +393,7 @@ static NSURL *MGLStyleURL_emerald; layer]; } [self willChangeValueForKey:@"layers"]; - [layer removeFromStyle:self]; + [layer removeFromMapView:self.mapView]; [self didChangeValueForKey:@"layers"]; } @@ -385,7 +407,7 @@ static NSURL *MGLStyleURL_emerald; } [self willChangeValueForKey:@"layers"]; try { - [layer addToStyle:self belowLayer:nil]; + [layer addToMapView:self.mapView belowLayer:nil]; } catch (std::runtime_error & err) { [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; } @@ -414,7 +436,7 @@ static NSURL *MGLStyleURL_emerald; } [self willChangeValueForKey:@"layers"]; try { - [layer addToStyle:self belowLayer:sibling]; + [layer addToMapView:self.mapView belowLayer:sibling]; } catch (std::runtime_error & err) { [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; } @@ -437,7 +459,7 @@ static NSURL *MGLStyleURL_emerald; sibling]; } - auto layers = self.rawStyle->getLayers(); + auto layers = self.mapView.mbglMap->getLayers(); std::string siblingIdentifier = sibling.identifier.UTF8String; NSUInteger index = 0; for (auto layer : layers) { @@ -456,14 +478,14 @@ static NSURL *MGLStyleURL_emerald; sibling]; } else if (index + 1 == layers.size()) { try { - [layer addToStyle:self belowLayer:nil]; + [layer addToMapView:self.mapView belowLayer:nil]; } catch (std::runtime_error & err) { [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; } } else { MGLStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(index + 1)]; try { - [layer addToStyle:self belowLayer:sibling]; + [layer addToMapView:self.mapView belowLayer:sibling]; } catch (std::runtime_error & err) { [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; } @@ -475,32 +497,60 @@ static NSURL *MGLStyleURL_emerald; - (NS_ARRAY_OF(NSString *) *)styleClasses { - return @[]; + const std::vector<std::string> &appliedClasses = self.mapView.mbglMap->getClasses(); + + NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:appliedClasses.size()]; + + for (auto appliedClass : appliedClasses) { + [returnArray addObject:@(appliedClass.c_str())]; + } + + return returnArray; } - (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses { + [self setStyleClasses:appliedClasses transitionDuration:0]; } - (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration { + std::vector<std::string> newAppliedClasses; + + for (NSString *appliedClass in appliedClasses) + { + newAppliedClasses.push_back([appliedClass UTF8String]); + } + + mbgl::style::TransitionOptions transition { { MGLDurationFromTimeInterval(transitionDuration) } }; + self.mapView.mbglMap->setTransitionOptions(transition); + self.mapView.mbglMap->setClasses(newAppliedClasses); } - (NSUInteger)countOfStyleClasses { - return 0; + const auto &classes = self.mapView.mbglMap->getClasses(); + return classes.size(); } - (BOOL)hasStyleClass:(NSString *)styleClass { - return NO; + return styleClass && self.mapView.mbglMap->hasClass([styleClass UTF8String]); } - (void)addStyleClass:(NSString *)styleClass { + if (styleClass) + { + self.mapView.mbglMap->addClass([styleClass UTF8String]); + } } - (void)removeStyleClass:(NSString *)styleClass { + if (styleClass) + { + self.mapView.mbglMap->removeClass([styleClass UTF8String]); + } } #pragma mark Style images @@ -516,7 +566,7 @@ static NSURL *MGLStyleURL_emerald; format:@"Cannot assign image %@ to a nil name.", image]; } - self.rawStyle->addImage([image mgl_styleImageWithIdentifier:name]); + self.mapView.mbglMap->addImage([name UTF8String], image.mgl_styleImage); } - (void)removeImageForName:(NSString *)name @@ -526,7 +576,7 @@ static NSURL *MGLStyleURL_emerald; format:@"Cannot remove image with nil name."]; } - self.rawStyle->removeImage([name UTF8String]); + self.mapView.mbglMap->removeImage([name UTF8String]); } - (MGLImage *)imageForName:(NSString *)name @@ -536,7 +586,7 @@ static NSURL *MGLStyleURL_emerald; format:@"Cannot get image with nil name."]; } - auto styleImage = self.rawStyle->getImage([name UTF8String]); + auto styleImage = self.mapView.mbglMap->getImage([name UTF8String]); return styleImage ? [[MGLImage alloc] initWithMGLStyleImage:styleImage] : nil; } @@ -544,17 +594,17 @@ static NSURL *MGLStyleURL_emerald; - (void)setTransition:(MGLTransition)transition { - auto transitionOptions = self.rawStyle->getTransitionOptions(); + auto transitionOptions = self.mapView.mbglMap->getTransitionOptions(); transitionOptions.duration = MGLDurationFromTimeInterval(transition.duration); transitionOptions.delay = MGLDurationFromTimeInterval(transition.delay); - self.rawStyle->setTransitionOptions(transitionOptions); + self.mapView.mbglMap->setTransitionOptions(transitionOptions); } - (MGLTransition)transition { MGLTransition transition; - const mbgl::style::TransitionOptions transitionOptions = self.rawStyle->getTransitionOptions(); + const mbgl::style::TransitionOptions transitionOptions = self.mapView.mbglMap->getTransitionOptions(); transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); @@ -567,12 +617,12 @@ static NSURL *MGLStyleURL_emerald; - (void)setLight:(MGLLight *)light { std::unique_ptr<mbgl::style::Light> mbglLight = std::make_unique<mbgl::style::Light>([light mbglLight]); - self.rawStyle->setLight(std::move(mbglLight)); + self.mapView.mbglMap->setLight(std::move(mbglLight)); } - (MGLLight *)light { - auto mbglLight = self.rawStyle->getLight(); + auto mbglLight = self.mapView.mbglMap->getLight(); MGLLight *light = [[MGLLight alloc] initWithMBGLLight:mbglLight]; return light; } @@ -585,4 +635,115 @@ static NSURL *MGLStyleURL_emerald; self.URL ? [NSString stringWithFormat:@"\"%@\"", self.URL] : self.URL]; } +#pragma mark Style language preferences + +- (void)setLocalizesLabels:(BOOL)localizesLabels +{ + if (_localizesLabels != localizesLabels) { + _localizesLabels = localizesLabels; + } else { + return; + } + + if (_localizesLabels) { + NSString *preferredLanguage = [MGLVectorSource preferredMapboxStreetsLanguage]; + NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary]; + for (MGLSymbolStyleLayer *layer in self.layers) { + if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) { + continue; + } + + MGLVectorSource *source = (MGLVectorSource *)[self sourceWithIdentifier:layer.sourceIdentifier]; + if (![source isKindOfClass:[MGLVectorSource class]] || !source.mapboxStreets) { + continue; + } + + NSDictionary *localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier]; + if (!localizedKeysByKey) { + localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier] = [source localizedKeysByKeyForPreferredLanguage:preferredLanguage]; + } + + NSString *(^stringByLocalizingString)(NSString *) = ^ NSString * (NSString *string) { + NSMutableString *localizedString = string.mutableCopy; + [localizedKeysByKey enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull localizedKey, BOOL * _Nonnull stop) { + NSAssert([key isKindOfClass:[NSString class]], @"key is not a string"); + NSAssert([localizedKey isKindOfClass:[NSString class]], @"localizedKey is not a string"); + [localizedString replaceOccurrencesOfString:[NSString stringWithFormat:@"{%@}", key] + withString:[NSString stringWithFormat:@"{%@}", localizedKey] + options:0 + range:NSMakeRange(0, localizedString.length)]; + }]; + return localizedString; + }; + + if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) { + NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue]; + NSString *localizingString = stringByLocalizingString(textField); + if (![textField isEqualToString:localizingString]) { + MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField + updatedTextField:localizingString]; + [self.localizedLayersByIdentifier setObject:@{ textField : textLanguage } forKey:layer.identifier]; + layer.text = [MGLStyleValue<NSString *> valueWithRawValue:localizingString]; + } + } + else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { + MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text; + NSMutableDictionary *stops = function.stops.mutableCopy; + NSMutableDictionary *cameraStops = [NSMutableDictionary dictionary]; + [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *stop, BOOL *done) { + NSString *textField = stop.rawValue; + NSString *localizingString = stringByLocalizingString(textField); + if (![textField isEqualToString:localizingString]) { + MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField + updatedTextField:localizingString]; + [cameraStops setObject:textLanguage forKey:zoomLevel]; + stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:localizingString]; + } + + }]; + if (cameraStops.count > 0) { + [self.localizedLayersByIdentifier setObject:cameraStops forKey:layer.identifier]; + } + function.stops = stops; + layer.text = function; + } + } + } else { + + [self.localizedLayersByIdentifier enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSDictionary<NSObject *, MGLTextLanguage *> *textFields, BOOL *done) { + MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:identifier]; + + if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) { + NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue]; + [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *originalLanguage, MGLTextLanguage *textLanguage, BOOL *done) { + if ([textLanguage.updatedTextField isEqualToString:textField]) { + layer.text = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField]; + } + }]; + + } + else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { + MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text; + NSMutableDictionary *stops = function.stops.mutableCopy; + [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *zoomKey, MGLTextLanguage *textLanguage, BOOL *done) { + if ([zoomKey isKindOfClass:[NSNumber class]]) { + NSNumber *zoomLevel = (NSNumber*)zoomKey; + MGLConstantStyleValue<NSString *> *stop = [stops objectForKey:zoomLevel]; + NSString *textField = stop.rawValue; + if ([textLanguage.updatedTextField isEqualToString:textField]) { + stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField]; + } + } + }]; + + function.stops = stops; + layer.text = function; + } + + }]; + + self.localizedLayersByIdentifier = [NSMutableDictionary dictionary]; + } +} + @end diff --git a/platform/darwin/src/MGLVectorSource+MGLAdditions.h b/platform/darwin/src/MGLVectorSource+MGLAdditions.h new file mode 100644 index 0000000000..43b0aba747 --- /dev/null +++ b/platform/darwin/src/MGLVectorSource+MGLAdditions.h @@ -0,0 +1,15 @@ +#import <Mapbox/Mapbox.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface MGLVectorSource (MGLAdditions) + ++ (NSString *)preferredMapboxStreetsLanguage; + +- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage; + +@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLVectorSource+MGLAdditions.m b/platform/darwin/src/MGLVectorSource+MGLAdditions.m new file mode 100644 index 0000000000..a305388117 --- /dev/null +++ b/platform/darwin/src/MGLVectorSource+MGLAdditions.m @@ -0,0 +1,53 @@ +#import "MGLVectorSource+MGLAdditions.h" + +@implementation MGLVectorSource (MGLAdditions) + ++ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages { + // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview + static dispatch_once_t onceToken; + static NS_SET_OF(NSString *) *mapboxStreetsLanguages; + dispatch_once(&onceToken, ^{ + // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview + mapboxStreetsLanguages = [NSSet setWithObjects:@"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", nil]; + }); + return mapboxStreetsLanguages; +} + ++ (NSString *)preferredMapboxStreetsLanguage { + NSArray<NSString *> *supportedLanguages = [MGLVectorSource mapboxStreetsLanguages].allObjects; + NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages + forPreferences:[NSLocale preferredLanguages]]; + NSString *mostSpecificLanguage; + for (NSString *language in preferredLanguages) { + if (language.length > mostSpecificLanguage.length) { + mostSpecificLanguage = language; + } + } + return mostSpecificLanguage ?: @"en"; +} + +- (BOOL)isMapboxStreets { + NSURL *url = self.configurationURL; + if (![url.scheme isEqualToString:@"mapbox"]) { + return NO; + } + NSArray *identifiers = [url.host componentsSeparatedByString:@","]; + return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"]; +} + +- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage { + if (!self.mapboxStreets) { + return @{}; + } + + // Replace {name} and {name_*} with the matching localized name tag. + NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name"; + NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"]; + for (NSString *languageCode in [MGLVectorSource mapboxStreetsLanguages]) { + NSString *key = [NSString stringWithFormat:@"name_%@", languageCode]; + localizedKeysByKey[key] = localizedKey; + } + return localizedKeysByKey; +} + +@end diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index ae72b35d82..42c656f203 100644 --- a/platform/darwin/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -103,6 +103,18 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { XCTAssertNotNil(mapView.style?.source(withIdentifier: "pois")) } + + func testMGLPolyline() { + //#-example-code + let coordinates = [ + CLLocationCoordinate2D(latitude: 35.68476, longitude: -220.24257), + CLLocationCoordinate2D(latitude: 37.78428, longitude: -122.41310) + ] + let polyline = MGLPolyline(coordinates: coordinates, count: UInt(coordinates.count)) + //#-end-example-code + + XCTAssertNotNil(polyline) + } func testMGLImageSource() { //#-example-code |