diff options
Diffstat (limited to 'platform/macos/app/MapDocument.m')
-rw-r--r-- | platform/macos/app/MapDocument.m | 198 |
1 files changed, 180 insertions, 18 deletions
diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 0df6b10518..7d39f93347 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -6,7 +6,7 @@ #import "MGLMapsnapshotter.h" #import "MGLStyle+MBXAdditions.h" -#import "MGLVectorSource+MGLAdditions.h" +#import "MGLVectorSource_Private.h" #import <Mapbox/Mapbox.h> @@ -50,7 +50,26 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio return flattenedShapes; } -@interface MapDocument () <NSWindowDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, NSSplitViewDelegate, MGLMapViewDelegate> +@interface MGLVectorSource (MBXAdditions) + +@property (nonatomic, readonly, getter=isMapboxTerrain) BOOL mapboxTerrain; + +@end + +@implementation MGLVectorSource (MBXAdditions) + +- (BOOL)isMapboxTerrain { + NSURL *url = self.configurationURL; + if (![url.scheme isEqualToString:@"mapbox"]) { + return NO; + } + NSArray *identifiers = [url.host componentsSeparatedByString:@","]; + return [identifiers containsObject:@"mapbox.mapbox-terrain-v2"] || [identifiers containsObject:@"mapbox.mapbox-terrain-v1"]; +} + +@end + +@interface MapDocument () <NSWindowDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, NSSplitViewDelegate, MGLMapViewDelegate, MGLComputedShapeSourceDataSource> @property (weak) IBOutlet NSArrayController *styleLayersArrayController; @property (weak) IBOutlet NSTableView *styleLayersTableView; @@ -698,6 +717,96 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio [self.mapView.style removeLayer:layer]; } +- (IBAction)insertGraticuleLayer:(id)sender { + [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) { + [self removeGraticuleLayer:sender]; + }]; + + if (!self.undoManager.isUndoing) { + [self.undoManager setActionName:@"Add Graticule"]; + } + + NSDictionary *sourceOptions = @{ + MGLShapeSourceOptionMaximumZoomLevel:@14, + MGLShapeSourceOptionWrapsCoordinates: @YES, + MGLShapeSourceOptionClipsCoordinates: @YES, + }; + MGLComputedShapeSource *source = [[MGLComputedShapeSource alloc] initWithIdentifier:@"graticule" + options:sourceOptions]; + + source.dataSource = self; + [self.mapView.style addSource:source]; + MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"graticule.lines" + source:source]; + [self.mapView.style addLayer:lineLayer]; + MGLSymbolStyleLayer *labelLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"graticule.labels" + source:source]; + labelLayer.text = [NSExpression expressionWithFormat:@"value"]; + [self.mapView.style addLayer:labelLayer]; +} + +- (IBAction)removeGraticuleLayer:(id)sender { + [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) { + [self insertGraticuleLayer:sender]; + }]; + + if (!self.undoManager.isUndoing) { + [self.undoManager setActionName:@"Delete Graticule"]; + } + + MGLStyleLayer *layer = [self.mapView.style layerWithIdentifier:@"graticule.lines"]; + [self.mapView.style removeLayer:layer]; + + layer = [self.mapView.style layerWithIdentifier:@"graticule.labels"]; + [self.mapView.style removeLayer:layer]; + + MGLSource *source = [self.mapView.style sourceWithIdentifier:@"graticule"]; + [self.mapView.style removeSource:source]; +} + +- (IBAction)enhanceTerrain:(id)sender { + // Find all the identifiers of Mapbox Terrain sources used in the style. + NSMutableSet *terrainSourceIdentifiers = [NSMutableSet set]; + for (MGLVectorSource *source in self.mapView.style.sources) { + if (![source isKindOfClass:[MGLVectorSource class]]) { + continue; + } + + if (source.mapboxTerrain) { + [terrainSourceIdentifiers addObject:source.identifier]; + } + } + + // Find and remove all the style layers using those sources. + NSUInteger hillshadeIndex = NSNotFound; + NSEnumerator *layerEnumerator = self.mapView.style.layers.objectEnumerator; + MGLVectorStyleLayer *layer; + for (NSUInteger i = 0; (layer = layerEnumerator.nextObject); i++) { + if (![layer isKindOfClass:[MGLVectorStyleLayer class]]) { + continue; + } + + if ([terrainSourceIdentifiers containsObject:layer.sourceIdentifier] + && [layer.sourceLayerIdentifier isEqualToString:@"hillshade"]) { + hillshadeIndex = i; + [self.mapView.style removeLayer:layer]; + } + } + + if (hillshadeIndex == NSNotFound) { + return; + } + + // Add a Mapbox Terrain-RGB source. + NSURL *terrainRGBURL = [NSURL URLWithString:@"mapbox://mapbox.terrain-rgb"]; + MGLRasterDEMSource *terrainRGBSource = [[MGLRasterDEMSource alloc] initWithIdentifier:@"terrain" configurationURL:terrainRGBURL]; + [self.mapView.style addSource:terrainRGBSource]; + + // Insert a hillshade layer where the Mapbox Terrain–based layers were. + MGLHillshadeStyleLayer *hillshadeLayer = [[MGLHillshadeStyleLayer alloc] initWithIdentifier:@"hillshade" source:terrainRGBSource]; + [self.mapView.style insertLayer:hillshadeLayer atIndex:hillshadeIndex]; +} + #pragma mark Offline packs - (IBAction)addOfflinePack:(id)sender { @@ -746,16 +855,16 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio self.mapView.style.transition = transition; MGLStyleLayer *waterLayer = [self.mapView.style layerWithIdentifier:@"water"]; - MGLStyleValue *colorFunction = [MGLStyleValue<NSColor *> valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{ - @0.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor redColor]], - @10.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor yellowColor]], - @20.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor blackColor]], - } options:nil]; + NSExpression *colorExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{ + @0.0: [NSColor redColor], + @10.0: [NSColor yellowColor], + @20.0: [NSColor blackColor], + }]; if ([waterLayer respondsToSelector:@selector(fillColor)]) { - [waterLayer setValue:colorFunction forKey:@"fillColor"]; + [waterLayer setValue:colorExpression forKey:@"fillColor"]; } else if ([waterLayer respondsToSelector:@selector(lineColor)]) { - [waterLayer setValue:colorFunction forKey:@"lineColor"]; + [waterLayer setValue:colorExpression forKey:@"lineColor"]; } NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"]; @@ -764,8 +873,8 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio [self.mapView.style addSource:source]; MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"test" source:source]; - circleLayer.circleColor = [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor greenColor]]; - circleLayer.circleRadius = [MGLStyleValue<NSNumber *> valueWithRawValue:[NSNumber numberWithInteger:40]]; + circleLayer.circleColor = [NSExpression expressionForConstantValue:[NSColor greenColor]]; + circleLayer.circleRadius = [NSExpression expressionForConstantValue:@40]; // fillLayer.predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"type", @"park"]; [self.mapView.style addLayer:circleLayer]; @@ -777,13 +886,13 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio MGLSymbolStyleLayer *theaterLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"theaters" source:streetsSource]; theaterLayer.sourceLayerIdentifier = @"poi_label"; theaterLayer.predicate = [NSPredicate predicateWithFormat:@"maki == 'theatre'"]; - theaterLayer.iconImageName = [MGLStyleValue valueWithRawValue:NSImageNameIChatTheaterTemplate]; - theaterLayer.iconScale = [MGLStyleValue valueWithRawValue:@2]; - theaterLayer.iconColor = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{ - @16.0: [MGLStyleValue valueWithRawValue:[NSColor redColor]], - @18.0: [MGLStyleValue valueWithRawValue:[NSColor yellowColor]], - @20.0: [MGLStyleValue valueWithRawValue:[NSColor blackColor]], - } options:nil]; + theaterLayer.iconImageName = [NSExpression expressionForConstantValue:NSImageNameIChatTheaterTemplate]; + theaterLayer.iconScale = [NSExpression expressionForConstantValue:@2]; + theaterLayer.iconColor = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{ + @16.0: [NSColor redColor], + @18.0: [NSColor yellowColor], + @20.0: [NSColor blackColor], + }]; [self.mapView.style addLayer:theaterLayer]; } @@ -1011,9 +1120,15 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio if (menuItem.action == @selector(insertCustomStyleLayer:)) { return ![self.mapView.style layerWithIdentifier:@"mbx-custom"]; } + if (menuItem.action == @selector(insertGraticuleLayer:)) { + return ![self.mapView.style sourceWithIdentifier:@"graticule"]; + } if (menuItem.action == @selector(showAllAnnotations:) || menuItem.action == @selector(removeAllAnnotations:)) { return self.mapView.annotations.count > 0; } + if (menuItem.action == @selector(enhanceTerrain:)) { + return YES; + } if (menuItem.action == @selector(startWorldTour:)) { return !_isTouringWorld; } @@ -1185,6 +1300,53 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio return 0.8; } +#pragma mark - MGLComputedShapeSourceDataSource +- (NSArray<id <MGLFeature>>*)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 <id <MGLFeature>> * 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 |