diff options
Diffstat (limited to 'platform/macos/app')
-rw-r--r-- | platform/macos/app/Base.lproj/MapDocument.xib | 3 | ||||
-rw-r--r-- | platform/macos/app/MGLStyle+MBXAdditions.h | 7 | ||||
-rw-r--r-- | platform/macos/app/MGLStyle+MBXAdditions.m | 42 | ||||
-rw-r--r-- | platform/macos/app/MGLVectorSource+MBXAdditions.h | 15 | ||||
-rw-r--r-- | platform/macos/app/MGLVectorSource+MBXAdditions.m | 49 | ||||
-rw-r--r-- | platform/macos/app/MapDocument.m | 96 |
6 files changed, 165 insertions, 47 deletions
diff --git a/platform/macos/app/Base.lproj/MapDocument.xib b/platform/macos/app/Base.lproj/MapDocument.xib index e147ba83d0..4ba8f0a3ad 100644 --- a/platform/macos/app/Base.lproj/MapDocument.xib +++ b/platform/macos/app/Base.lproj/MapDocument.xib @@ -27,6 +27,7 @@ <declaredKeys> <string>layers</string> <string>name</string> + <string>reversedLayers</string> </declaredKeys> <connections> <binding destination="jxx-uM-ZTC" name="contentObject" keyPath="selection.style" id="60N-aU-tgJ"/> @@ -38,7 +39,7 @@ <string>visible</string> </declaredKeys> <connections> - <binding destination="Xji-k6-iQ4" name="contentArray" keyPath="selection.layers" id="X25-Nb-Brf"/> + <binding destination="Xji-k6-iQ4" name="contentArray" keyPath="selection.reversedLayers" id="wtL-d8-GNd"/> </connections> </arrayController> <customObject id="-3" userLabel="Application" customClass="NSObject"/> diff --git a/platform/macos/app/MGLStyle+MBXAdditions.h b/platform/macos/app/MGLStyle+MBXAdditions.h new file mode 100644 index 0000000000..dcaf42af28 --- /dev/null +++ b/platform/macos/app/MGLStyle+MBXAdditions.h @@ -0,0 +1,7 @@ +#import <Mapbox/Mapbox.h> + +@interface MGLStyle (MBXAdditions) + +@property (nonatomic, strong) NS_ARRAY_OF(__kindof MGLStyleLayer *) *reversedLayers; + +@end diff --git a/platform/macos/app/MGLStyle+MBXAdditions.m b/platform/macos/app/MGLStyle+MBXAdditions.m new file mode 100644 index 0000000000..be571d8b30 --- /dev/null +++ b/platform/macos/app/MGLStyle+MBXAdditions.m @@ -0,0 +1,42 @@ +#import "MGLStyle+MBXAdditions.h" + +@implementation MGLStyle (MBXAdditions) + ++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingReversedLayers { + return [NSSet setWithObject:@"layers"]; +} + +- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers { + return self.layers.reverseObjectEnumerator.allObjects; +} + +- (void)setReversedLayers:(NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers { + self.layers = reversedLayers.reverseObjectEnumerator.allObjects; +} + +- (NSUInteger)countOfReversedLayers { + return self.layers.count; +} + +- (id)objectInReversedLayersAtIndex:(NSUInteger)index { + NSArray *layers = self.layers; + return layers[layers.count - 1 - index]; +} + +- (void)getReversedLayers:(__kindof MGLStyleLayer **)buffer range:(NSRange)inRange { + NSArray *layers = self.layers; + for (NSUInteger i = inRange.location; i < NSMaxRange(inRange); i++) { + MGLStyleLayer *styleLayer = layers[layers.count - 1 - i]; + buffer[i] = styleLayer; + } +} + +- (void)insertObject:(__kindof MGLStyleLayer *)object inReversedLayersAtIndex:(NSUInteger)index { + [self insertLayer:object atIndex:self.layers.count - index]; +} + +- (void)removeObjectFromReversedLayersAtIndex:(NSUInteger)index { + [self removeLayer:[self objectInReversedLayersAtIndex:index]]; +} + +@end diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.h b/platform/macos/app/MGLVectorSource+MBXAdditions.h new file mode 100644 index 0000000000..312081ec51 --- /dev/null +++ b/platform/macos/app/MGLVectorSource+MBXAdditions.h @@ -0,0 +1,15 @@ +#import <Mapbox/Mapbox.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface MGLVectorSource (MBXAdditions) + ++ (nullable 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/macos/app/MGLVectorSource+MBXAdditions.m b/platform/macos/app/MGLVectorSource+MBXAdditions.m new file mode 100644 index 0000000000..f59b72aa9f --- /dev/null +++ b/platform/macos/app/MGLVectorSource+MBXAdditions.m @@ -0,0 +1,49 @@ +#import "MGLVectorSource+MBXAdditions.h" + +@implementation MGLVectorSource (MBXAdditions) + ++ (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, ^{ + mapboxStreetsLanguages = [NSSet setWithObjects:@"en", @"es", @"fr", @"de", @"ru", @"zh", nil]; + }); + return mapboxStreetsLanguages; +} + ++ (nullable NSString *)preferredMapboxStreetsLanguage { + for (NSString *language in [NSLocale preferredLanguages]) { + NSString *languageCode = [[NSLocale localeWithLocaleIdentifier:language] objectForKey:NSLocaleLanguageCode]; + if ([[MGLVectorSource mapboxStreetsLanguages] containsObject:languageCode]) { + return languageCode; + } + } + return nil; +} + +- (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/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 2c31610779..64833a8560 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -4,6 +4,9 @@ #import "LimeGreenStyleLayer.h" #import "DroppedPinAnnotation.h" +#import "MGLStyle+MBXAdditions.h" +#import "MGLVectorSource+MBXAdditions.h" + #import <Mapbox/Mapbox.h> static NSString * const MGLDroppedPinAnnotationImageIdentifier = @"dropped"; @@ -143,11 +146,12 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio - (NSURL *)shareURL { NSArray *components = self.mapView.styleURL.pathComponents; - CLLocationCoordinate2D centerCoordinate = self.mapView.centerCoordinate; + MGLMapCamera *camera = self.mapView.camera; return [NSURL URLWithString: - [NSString stringWithFormat:@"https://api.mapbox.com/styles/v1/%@/%@.html?access_token=%@#%.2f/%.5f/%.5f/%.f", + [NSString stringWithFormat:@"https://api.mapbox.com/styles/v1/%@/%@.html?access_token=%@#%.2f/%.5f/%.5f/%.f/%.f", components[1], components[2], [MGLAccountManager accessToken], - self.mapView.zoomLevel, centerCoordinate.latitude, centerCoordinate.longitude, self.mapView.direction]]; + self.mapView.zoomLevel, camera.centerCoordinate.latitude, camera.centerCoordinate.longitude, + camera.heading, camera.pitch]]; } #pragma mark View methods @@ -254,7 +258,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio } - (void)toggleStyleLayersAtArrangedObjectIndexes:(NSIndexSet *)indices { - NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.layers objectsAtIndexes:indices]; + NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices]; BOOL isVisible = layers.firstObject.visible; [self.undoManager registerUndoWithTarget:self handler:^(MapDocument * _Nonnull target) { [target toggleStyleLayersAtArrangedObjectIndexes:indices]; @@ -310,7 +314,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio } - (void)deleteStyleLayersAtArrangedObjectIndexes:(NSIndexSet *)indices { - NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.layers objectsAtIndexes:indices]; + NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices]; [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) { [self insertStyleLayers:layers atArrangedObjectIndexes:indices]; }]; @@ -331,56 +335,55 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio - (IBAction)setLabelLanguage:(NSMenuItem *)sender { _isLocalizingLabels = sender.tag; - [self updateLabels]; + [self reload:sender]; } - (void)updateLabels { - NSString *preferredLanguageCode = self.preferredLanguageCode; - NSString *preferredNameToken = _isLocalizingLabels ? [NSString stringWithFormat:@"{name_%@}", preferredLanguageCode] : @"{name}"; - NSRegularExpression *nameTokenExpression = [NSRegularExpression regularExpressionWithPattern:@"\\{name(?:_\\w{2})?\\}" options:0 error:NULL]; - - for (MGLSymbolStyleLayer *layer in self.mapView.style.layers) { + MGLStyle *style = self.mapView.style; + NSString *preferredLanguage = _isLocalizingLabels ? ([MGLVectorSource preferredMapboxStreetsLanguage] ?: @"en") : nil; + NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary]; + for (MGLSymbolStyleLayer *layer in style.layers) { if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) { continue; } - if ([layer.textField isKindOfClass:[MGLStyleConstantValue class]]) { - NSString *textField = [(MGLStyleConstantValue<NSString *> *)layer.textField rawValue]; - textField = [nameTokenExpression stringByReplacingMatchesInString:textField - options:0 - range:NSMakeRange(0, textField.length) - withTemplate:preferredNameToken]; - layer.textField = [MGLStyleValue<NSString *> valueWithRawValue:textField]; - } else if ([layer.textField isKindOfClass:[MGLStyleFunction class]]) { - MGLStyleFunction *function = (MGLStyleFunction<NSString *> *)layer.textField; + MGLVectorSource *source = (MGLVectorSource *)[style 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:[MGLStyleConstantValue class]]) { + NSString *textField = [(MGLStyleConstantValue<NSString *> *)layer.text rawValue]; + layer.text = [MGLStyleValue<NSString *> valueWithRawValue:stringByLocalizingString(textField)]; + } else if ([layer.text isKindOfClass:[MGLStyleFunction class]]) { + MGLStyleFunction *function = (MGLStyleFunction<NSString *> *)layer.text; NSMutableDictionary *stops = function.stops.mutableCopy; [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLStyleConstantValue<NSString *> *stop, BOOL *done) { NSString *textField = stop.rawValue; - textField = [nameTokenExpression stringByReplacingMatchesInString:textField - options:0 - range:NSMakeRange(0, textField.length) - withTemplate:preferredNameToken]; - stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:textField]; + stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:stringByLocalizingString(textField)]; }]; function.stops = stops; - layer.textField = function; - } - } -} - -- (NSString *)preferredLanguageCode { - // Languages supported by Mapbox Streets v10. - NSSet *supportedLanguages = [NSSet setWithObjects:@"en", @"es", @"fr", @"de", @"ru", @"zh", nil]; - NSArray *preferredLanguages = [NSLocale preferredLanguages]; - - for (NSString *language in preferredLanguages) { - NSString *languageCode = [[NSLocale localeWithLocaleIdentifier:language] objectForKey:NSLocaleLanguageCode]; - if ([supportedLanguages containsObject:languageCode]) { - return languageCode; + layer.text = function; } } - - return @"en"; } - (void)applyPendingState { @@ -824,7 +827,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio if (row == -1) { menuItem.title = @"Show"; } else { - BOOL isVisible = self.mapView.style.layers[row].visible; + BOOL isVisible = self.mapView.style.reversedLayers[row].visible; menuItem.title = isVisible ? @"Hide" : @"Show"; } return row != -1; @@ -836,7 +839,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio menuItem.state = menuItem.tag == _isLocalizingLabels ? NSOnState: NSOffState; if (menuItem.tag) { NSLocale *locale = [NSLocale localeWithLocaleIdentifier:[NSBundle mainBundle].developmentLocalization]; - menuItem.title = [locale displayNameForKey:NSLocaleIdentifier value:self.preferredLanguageCode]; + NSString *preferredLanguage = [MGLVectorSource preferredMapboxStreetsLanguage]; + menuItem.enabled = !!preferredLanguage; + menuItem.title = [locale displayNameForKey:NSLocaleIdentifier value:preferredLanguage ?: @"Preferred Language"]; } return YES; } @@ -1083,9 +1088,8 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio } } -- (NSColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation { - NSColor *color = [[NSColor selectedMenuItemColor] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; - return [color colorWithAlphaComponent:0.8]; +- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation { + return 0.8; } @end |