diff options
author | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-08-28 12:43:29 -0400 |
---|---|---|
committer | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-08-28 12:43:29 -0400 |
commit | acfe485302d4356ace93b1ca4a0dcbb9554e08a5 (patch) | |
tree | 6a8f18343cd1856be59d4de06a00034b27e95816 /platform/darwin | |
parent | c6ab20e5c69c3705422e49c3511faf3e5ab79b05 (diff) | |
parent | 001988fba1e6eb2f6db2acdb68f26d583c48f53a (diff) | |
download | qtlocation-mapboxgl-acfe485302d4356ace93b1ca4a0dcbb9554e08a5.tar.gz |
Merge tag 'ios-v3.6.2' into master
# 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/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
# platform/android/dependencies.gradle
# 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/MGLShapeSource.mm | 4 | ||||
-rw-r--r-- | platform/darwin/src/MGLSource.mm | 27 | ||||
-rw-r--r-- | platform/darwin/src/MGLSource_Private.h | 25 | ||||
-rw-r--r-- | platform/darwin/src/MGLStyle.h | 15 | ||||
-rw-r--r-- | platform/darwin/src/MGLStyle.mm | 151 | ||||
-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/src/MGLVectorSource.mm | 4 | ||||
-rw-r--r-- | platform/darwin/test/MGLDocumentationExampleTests.swift | 12 |
16 files changed, 656 insertions, 44 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/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm index f02fc98ded..571cbdcc62 100644 --- a/platform/darwin/src/MGLShapeSource.mm +++ b/platform/darwin/src/MGLShapeSource.mm @@ -98,8 +98,8 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh } std::vector<mbgl::Feature> features; - if (self.style) { - features = self.style.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter }); + if (self.mapView) { + features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter }); } return MGLFeaturesFromMBGLFeatures(features); } diff --git a/platform/darwin/src/MGLSource.mm b/platform/darwin/src/MGLSource.mm index ee012f4d66..6d57e14e8c 100644 --- a/platform/darwin/src/MGLSource.mm +++ b/platform/darwin/src/MGLSource.mm @@ -1,7 +1,9 @@ #import "MGLSource_Private.h" #import "MGLStyle_Private.h" +#import "MGLMapView_Private.h" #include <mbgl/style/style.hpp> +#include <mbgl/map/map.hpp> #include <mbgl/style/source.hpp> @interface MGLSource () @@ -10,7 +12,7 @@ // special internal source types like mbgl::AnnotationSource. @property (nonatomic, readonly) mbgl::style::Source *rawSource; -@property (nonatomic, readonly, weak) MGLStyle *style; +@property (nonatomic, readonly, weak) MGLMapView *mapView; @end @@ -27,37 +29,38 @@ 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; } -- (void)addToStyle:(MGLStyle *)style { +- (void)addToMapView:(MGLMapView *)mapView { if (_pendingSource == nullptr) { [NSException raise:@"MGLRedundantSourceException" format:@"This instance %@ was already added to %@. Adding the same source instance " \ - "to the style more than once is invalid.", self, style]; + "to the style more than once is invalid.", self, mapView.style]; } - - _style = style; - style.rawStyle->addSource(std::move(_pendingSource)); + + _mapView = mapView; + _mapView.style.rawStyle->addSource(std::move(_pendingSource)); } -- (void)removeFromStyle:(MGLStyle *)style { - if (self.rawSource == style.rawStyle->getSource(self.identifier.UTF8String)) { - _pendingSource = style.rawStyle->removeSource(self.identifier.UTF8String); - _style = nil; +- (void)removeFromMapView:(MGLMapView *)mapView { + if (self.rawSource == mapView.style.rawStyle->getSource(self.identifier.UTF8String)) { + _pendingSource = mapView.style.rawStyle->removeSource(self.identifier.UTF8String); + _mapView = nil; } } diff --git a/platform/darwin/src/MGLSource_Private.h b/platform/darwin/src/MGLSource_Private.h index ba78973279..d7d1f66641 100644 --- a/platform/darwin/src/MGLSource_Private.h +++ b/platform/darwin/src/MGLSource_Private.h @@ -18,7 +18,7 @@ struct SourceWrapper { __weak MGLSource *source; }; -@class MGLStyle; +@class MGLMapView; @interface MGLSource (Private) @@ -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, @@ -44,33 +44,30 @@ struct SourceWrapper { @property (nonatomic, readonly) mbgl::style::Source *rawSource; /** - The style which currently contains the source. - - If the source is not currently part of a style, this property is + The map view whose style currently contains the source. + If the source is not currently part of any map view’s style, this property is set to `nil`. */ -@property (nonatomic, readonly, weak) MGLStyle *style; +@property (nonatomic, readonly, weak) MGLMapView *mapView; /** - Adds the mbgl source that this object represents to the style. - + 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::Style` and this object no longer has an active unique_ptr reference 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 style via `-[MGLStyle addSource:]` and styled with a + is added back to the map via `-[MGLStyle addSource:]` and styled with a `MGLLayer`. */ -- (void)addToStyle:(MGLStyle *)style; +- (void)addToMapView:(MGLMapView *)mapView; /** - Removes the mbgl source that this object represents from the style. - + Removes the mbgl source that this object represents from the mbgl map. When a mbgl source is removed, ownership of the object is transferred back to the `MGLSource` instance and the unique_ptr reference is valid again. It is safe to add the source back to the style after it is removed. */ -- (void)removeFromStyle:(MGLStyle *)style; +- (void)removeFromMapView:(MGLMapView *)mapView; @end 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..94f199fd21 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -17,6 +17,7 @@ #import "MGLLight_Private.h" #import "MGLTileSource_Private.h" #import "MGLVectorSource.h" +#import "MGLVectorSource+MGLAdditions.h" #import "MGLRasterSource.h" #import "MGLShapeSource.h" #import "MGLImageSource.h" @@ -49,12 +50,35 @@ #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 (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 @@ -119,6 +143,7 @@ static NSURL *MGLStyleURL_emerald; _mapView = mapView; _rawStyle = rawStyle; _openGLLayers = [NSMutableDictionary dictionary]; + _localizedLayersByIdentifier = [NSMutableDictionary dictionary]; } return self; } @@ -164,6 +189,7 @@ static NSURL *MGLStyleURL_emerald; - (MGLSource *)sourceWithIdentifier:(NSString *)identifier { auto rawSource = self.rawStyle->getSource(identifier.UTF8String); + return rawSource ? [self sourceFromMBGLSource:rawSource] : nil; } @@ -175,15 +201,15 @@ 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]; + return [[MGLRasterSource alloc] initWithRawSource:rasterSource mapView:self.mapView]; } else if (auto imageSource = rawSource->as<mbgl::style::ImageSource>()) { - return [[MGLImageSource alloc] initWithRawSource:imageSource]; + return [[MGLImageSource alloc] initWithRawSource:imageSource mapView:self.mapView]; } else { - return [[MGLSource alloc] initWithRawSource:rawSource]; + return [[MGLSource alloc] initWithRawSource:rawSource mapView:self.mapView]; } } @@ -197,7 +223,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,7 +237,7 @@ 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 { @@ -585,4 +611,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/src/MGLVectorSource.mm b/platform/darwin/src/MGLVectorSource.mm index 431e0c250c..b1bda56f2d 100644 --- a/platform/darwin/src/MGLVectorSource.mm +++ b/platform/darwin/src/MGLVectorSource.mm @@ -64,8 +64,8 @@ } std::vector<mbgl::Feature> features; - if (self.style) { - features = self.style.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter }); + if (self.mapView) { + features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter }); } return MGLFeaturesFromMBGLFeatures(features); } 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 |