summaryrefslogtreecommitdiff
path: root/platform/macos/app/MapDocument.m
diff options
context:
space:
mode:
Diffstat (limited to 'platform/macos/app/MapDocument.m')
-rw-r--r--platform/macos/app/MapDocument.m198
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