From f71f20dd2265ad6b588a9b679a94a0a1990575ef Mon Sep 17 00:00:00 2001 From: Jesse Crocker Date: Fri, 8 Sep 2017 14:07:51 -0700 Subject: [ios,macos] Add custom vector source example to demo apps --- platform/darwin/src/MGLComputedShapeSource.mm | 46 ++++++------ platform/ios/app/MBXViewController.m | 104 +++++++++++++++++++++++++- platform/macos/app/Base.lproj/MainMenu.xib | 10 ++- platform/macos/app/MapDocument.m | 93 ++++++++++++++++++++++- 4 files changed, 227 insertions(+), 26 deletions(-) diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm index 9049f5862a..a980872a3f 100644 --- a/platform/darwin/src/MGLComputedShapeSource.mm +++ b/platform/darwin/src/MGLComputedShapeSource.mm @@ -17,7 +17,6 @@ } @property (nonatomic, readwrite) NSDictionary *options; -@property (nonnull) mbgl::style::CustomVectorSource *rawSource; @property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForTile; @property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForBounds; @@ -32,15 +31,14 @@ @property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForBounds; @property (nonatomic, weak, nullable) id dataSource; @property (nonatomic, nullable) mbgl::style::CustomVectorSource *rawSource; -@property (nonatomic) mbgl::style::FetchTileCallback callback; -- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileId callback:(mbgl::style::FetchTileCallback) callback; +- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileId; @end @implementation MGLComputedShapeSourceFetchOperation -- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileID callback:(mbgl::style::FetchTileCallback) callback { +- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileID { self = [super init]; _z = tileID.z; _x = tileID.x; @@ -50,7 +48,6 @@ _dataSource = source.dataSource; mbgl::style::CustomVectorSource *rawSource = (mbgl::style::CustomVectorSource *)source.rawSource; _rawSource = rawSource; - _callback = callback; return self; } @@ -83,7 +80,7 @@ const auto geojson = mbgl::GeoJSON{featureCollection}; dispatch_sync(dispatch_get_main_queue(), ^{ if(![self isCancelled] && self.rawSource) { - self.callback(mbgl::CanonicalTileID(self.z, self.x, self.y), geojson); + self.rawSource->setTileData(mbgl::CanonicalTileID(self.z, self.x, self.y), geojson); } }); } @@ -99,22 +96,27 @@ @implementation MGLComputedShapeSource - (instancetype)initWithIdentifier:(NSString *)identifier options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { - if (self = [super initWithIdentifier:identifier]) { - _requestQueue = [[NSOperationQueue alloc] init]; - self.requestQueue.name = [NSString stringWithFormat:@"mgl.MGLComputedShapeSource.%@", identifier]; - auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options); - auto source = std::make_unique - (self.identifier.UTF8String, geoJSONOptions, - ^void(const mbgl::CanonicalTileID& tileID, mbgl::style::FetchTileCallback callback) - { - NSOperation *operation = [[MGLComputedShapeSourceFetchOperation alloc] initForSource:self tile:tileID callback: callback]; - [self.requestQueue addOperation:operation]; - }); - - _pendingSource = std::move(source); - self.rawSource = _pendingSource.get(); - } - return self; + _requestQueue = [[NSOperationQueue alloc] init]; + self.requestQueue.name = [NSString stringWithFormat:@"mgl.MGLComputedShapeSource.%@", identifier]; + self.requestQueue.qualityOfService = NSQualityOfServiceUtility; + self.requestQueue.maxConcurrentOperationCount = 4; + auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options); + auto source = std::make_unique + (identifier.UTF8String, geoJSONOptions, + ^void(const mbgl::CanonicalTileID& tileID) + { + NSOperation *operation = [[MGLComputedShapeSourceFetchOperation alloc] initForSource:self tile:tileID]; + [self.requestQueue addOperation:operation]; + }, + ^void(const mbgl::CanonicalTileID& tileID) + { + for(MGLComputedShapeSourceFetchOperation *operation in [self.requestQueue operations]) { + if(operation.x == tileID.x && operation.y == tileID.y && operation.z == tileID.z) { + [operation cancel]; + } + } + }); + return self = [super initWithPendingSource:std::move(source)]; } - (void)dealloc { diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 07838bc6bd..2dcf745bdb 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -75,6 +75,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) { MBXSettingsRuntimeStylingImageSource, MBXSettingsRuntimeStylingRouteLine, MBXSettingsRuntimeStylingDDSPolygon, + MBXSettingsRuntimeStylingCustomLatLonGrid, }; typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @@ -111,7 +112,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @interface MBXViewController () + MGLMapViewDelegate, + MGLComputedShapeSourceDataSource> @property (nonatomic) IBOutlet MGLMapView *mapView; @@ -351,6 +353,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Style Image Source", @"Add Route Line", @"Dynamically Style Polygon", + @"Add Custom Lat/Lon Grid", ]]; break; case MBXSettingsMiscellaneous: @@ -527,6 +530,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { case MBXSettingsRuntimeStylingDDSPolygon: [self stylePolygonWithDDS]; break; + case MBXSettingsRuntimeStylingCustomLatLonGrid: + [self addLatLonGrid]; + break; default: NSAssert(NO, @"All runtime styling setting rows should be implemented"); break; @@ -1442,6 +1448,54 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addLayer:fillStyleLayer]; } +- (void)addLatLonGrid +{ + MGLComputedShapeSource *source = [[MGLComputedShapeSource alloc] initWithIdentifier:@"latlon" + options:@{MGLShapeSourceOptionMaximumZoomLevel:@14}]; + source.dataSource = self; + [self.mapView.style addSource:source]; + MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"latlonlines" + source:source]; + [self.mapView.style addLayer:lineLayer]; + MGLSymbolStyleLayer *labelLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"latlonlabels" + source:source]; + labelLayer.text = [MGLStyleValue valueWithRawValue:@"{value}"]; + [self.mapView.style addLayer:labelLayer]; +} + +- (void)styleLabelLanguageForLayersNamed:(NSArray *)layers +{ + _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels; + NSString *bestLanguageForUser = [NSString stringWithFormat:@"{name_%@}", [self bestLanguageForUser]]; + NSString *language = _usingLocaleBasedCountryLabels ? bestLanguageForUser : @"{name}"; + + for (NSString *layerName in layers) { + MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:layerName]; + + if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) { + if ([layer.text isKindOfClass:[MGLStyleConstantValue class]]) { + MGLStyleConstantValue *label = (MGLStyleConstantValue *)layer.text; + if ([label.rawValue hasPrefix:@"{name"]) { + layer.text = [MGLStyleValue valueWithRawValue:language]; + } + } + else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { + MGLCameraStyleFunction *function = (MGLCameraStyleFunction *)layer.text; + NSMutableDictionary *stops = function.stops.mutableCopy; + [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLStyleConstantValue *stop, BOOL *done) { + if ([stop.rawValue hasPrefix:@"{name"]) { + stops[zoomLevel] = [MGLStyleValue valueWithRawValue:language]; + } + }]; + function.stops = stops; + layer.text = function; + } + } else { + NSLog(@"%@ is not a symbol style layer", layerName); + } + } +} + - (NSString *)bestLanguageForUser { // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview @@ -1907,4 +1961,52 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.hudLabel setTitle:hudString forState:UIControlStateNormal]; } +#pragma mark - MGLComputedShapeSourceDataSource + +- (NSArray>*)featuresInCoordinateBounds:(MGLCoordinateBounds)bounds zoomLevel:(NSUInteger)zoom { + double gridSpacing; + if(zoom >= 13) { + gridSpacing = 0.01; + } else if(zoom >= 11) { + gridSpacing = 0.05; + } else if(zoom == 10) { + gridSpacing = .1; + } else if(zoom == 9) { + gridSpacing = 0.25; + } else if(zoom == 8) { + gridSpacing = 0.5; + } else if (zoom >= 6) { + gridSpacing = 1; + } else if(zoom == 5) { + gridSpacing = 2; + } else if(zoom >= 4) { + gridSpacing = 5; + } else if(zoom == 2) { + gridSpacing = 10; + } else { + gridSpacing = 20; + } + + NSMutableArray > * features = [NSMutableArray array]; + CLLocationCoordinate2D coords[2]; + + for (double y = ceil(bounds.ne.latitude / gridSpacing) * gridSpacing; y >= floor(bounds.sw.latitude / gridSpacing) * gridSpacing; y -= gridSpacing) { + coords[0] = CLLocationCoordinate2DMake(y, bounds.sw.longitude); + coords[1] = CLLocationCoordinate2DMake(y, bounds.ne.longitude); + MGLPolylineFeature *feature = [MGLPolylineFeature polylineWithCoordinates:coords count:2]; + feature.attributes = @{@"value": @(y)}; + [features addObject:feature]; + } + + for (double x = floor(bounds.sw.longitude / gridSpacing) * gridSpacing; x <= ceil(bounds.ne.longitude / gridSpacing) * gridSpacing; x += gridSpacing) { + coords[0] = CLLocationCoordinate2DMake(bounds.sw.latitude, x); + coords[1] = CLLocationCoordinate2DMake(bounds.ne.latitude, x); + MGLPolylineFeature *feature = [MGLPolylineFeature polylineWithCoordinates:coords count:2]; + feature.attributes = @{@"value": @(x)}; + [features addObject:feature]; + } + + return features; +} + @end diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib index 3243838848..d014676caa 100644 --- a/platform/macos/app/Base.lproj/MainMenu.xib +++ b/platform/macos/app/Base.lproj/MainMenu.xib @@ -564,6 +564,12 @@ + + + + + + @@ -574,8 +580,8 @@ -CA - + CA + diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index feef53062b..c0dde02343 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -50,7 +50,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id +@interface MapDocument () @property (weak) IBOutlet NSArrayController *styleLayersArrayController; @property (weak) IBOutlet NSTableView *styleLayersTableView; @@ -698,6 +698,47 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id 0; } @@ -1185,6 +1229,53 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id >*)featuresInCoordinateBounds:(MGLCoordinateBounds)bounds zoomLevel:(NSUInteger)zoom { + double gridSpacing; + if(zoom >= 13) { + gridSpacing = 0.01; + } else if(zoom >= 11) { + gridSpacing = 0.05; + } else if(zoom == 10) { + gridSpacing = .1; + } else if(zoom == 9) { + gridSpacing = 0.25; + } else if(zoom == 8) { + gridSpacing = 0.5; + } else if (zoom >= 6) { + gridSpacing = 1; + } else if(zoom == 5) { + gridSpacing = 2; + } else if(zoom >= 4) { + gridSpacing = 5; + } else if(zoom == 2) { + gridSpacing = 10; + } else { + gridSpacing = 20; + } + + NSMutableArray > * features = [NSMutableArray array]; + CLLocationCoordinate2D coords[2]; + + for (double y = ceil(bounds.ne.latitude / gridSpacing) * gridSpacing; y >= floor(bounds.sw.latitude / gridSpacing) * gridSpacing; y -= gridSpacing) { + coords[0] = CLLocationCoordinate2DMake(y, bounds.sw.longitude); + coords[1] = CLLocationCoordinate2DMake(y, bounds.ne.longitude); + MGLPolylineFeature *feature = [MGLPolylineFeature polylineWithCoordinates:coords count:2]; + feature.attributes = @{@"value": @(y)}; + [features addObject:feature]; + } + + for (double x = floor(bounds.sw.longitude / gridSpacing) * gridSpacing; x <= ceil(bounds.ne.longitude / gridSpacing) * gridSpacing; x += gridSpacing) { + coords[0] = CLLocationCoordinate2DMake(bounds.sw.latitude, x); + coords[1] = CLLocationCoordinate2DMake(bounds.ne.latitude, x); + MGLPolylineFeature *feature = [MGLPolylineFeature polylineWithCoordinates:coords count:2]; + feature.attributes = @{@"value": @(x)}; + [features addObject:feature]; + } + + return features; +} + @end @interface ValidatedToolbarItem : NSToolbarItem -- cgit v1.2.1