summaryrefslogtreecommitdiff
path: root/platform/ios/demo/Examples/ObjectiveC/RuntimeMultipleAnnotationsExample.m
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/demo/Examples/ObjectiveC/RuntimeMultipleAnnotationsExample.m')
-rw-r--r--platform/ios/demo/Examples/ObjectiveC/RuntimeMultipleAnnotationsExample.m216
1 files changed, 216 insertions, 0 deletions
diff --git a/platform/ios/demo/Examples/ObjectiveC/RuntimeMultipleAnnotationsExample.m b/platform/ios/demo/Examples/ObjectiveC/RuntimeMultipleAnnotationsExample.m
new file mode 100644
index 0000000000..4c4308cd3c
--- /dev/null
+++ b/platform/ios/demo/Examples/ObjectiveC/RuntimeMultipleAnnotationsExample.m
@@ -0,0 +1,216 @@
+#import "RuntimeMultipleAnnotationsExample.h"
+@import Mapbox;
+
+NSString *const MBXExampleRuntimeMultipleAnnotations = @"RuntimeMultipleAnnotationsExample";
+
+@interface RuntimeMultipleAnnotationsExample ()<MGLMapViewDelegate>
+@property (nonatomic) MGLMapView *mapView;
+@end
+
+@implementation RuntimeMultipleAnnotationsExample
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ MGLMapView *mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds];
+
+ mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+
+ [mapView setCenterCoordinate:CLLocationCoordinate2DMake(37.090240, -95.712891) zoomLevel:2 animated:NO];
+
+ mapView.delegate = self;
+
+ // Add our own gesture recognizer to handle taps on our custom map features.
+ [mapView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleMapTap:)]];
+
+ [self.view addSubview:mapView];
+
+ self.mapView = mapView;
+}
+
+- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style {
+ [self fetchPoints:^(NSArray *features) {
+ [self addItemsToMap:features];
+ }];
+}
+
+- (void)addItemsToMap:(NSArray *)features {
+ // You can add custom UIImages to the map style.
+ // These can be referenced by an MGLSymbolStyleLayer’s iconImage property.
+ [self.mapView.style setImage:[UIImage imageNamed:@"lighthouse"] forName:@"lighthouse"];
+
+ // Add the features to the map as a MGLShapeSource.
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"lighthouses" features:features options:nil];
+ [self.mapView.style addSource:source];
+
+ UIColor *lighthouseColor = [UIColor colorWithRed:0.08 green:0.44 blue:0.96 alpha:1.0];
+
+ // Use MGLCircleStyleLayer to represent the points with simple circles.
+ // In this case, we can use style functions to gradually change properties between zoom level 2 and 7: the circle opacity from 50% to 100% and the circle radius from 2pt to 3pt.
+ MGLCircleStyleLayer *circles = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"lighthouse-circles" source:source];
+ circles.circleColor = [MGLStyleValue valueWithRawValue:lighthouseColor];
+ circles.circleOpacity = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential
+ cameraStops:@{
+ @2: [MGLStyleValue valueWithRawValue:@0.5],
+ @7: [MGLStyleValue valueWithRawValue:@1.0]
+ }
+ options:@{MGLStyleFunctionOptionDefaultValue:[MGLStyleValue valueWithRawValue:@0.75]}];
+
+ circles.circleRadius = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeInterval
+ cameraStops:@{
+ @2: [MGLStyleValue valueWithRawValue:@2],
+ @7: [MGLStyleValue valueWithRawValue:@3]
+ }
+ options:@{MGLStyleFunctionOptionDefaultValue:@1}];
+
+ // Use MGLSymbolStyleLayer for more complex styling of points including custom icons and text rendering.
+ MGLSymbolStyleLayer *symbols = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"lighthouse-symbols" source:source];
+ symbols.iconImageName = [MGLStyleValue valueWithRawValue:@"lighthouse"];
+ symbols.iconScale = [MGLStyleValue valueWithRawValue:@0.5];
+ symbols.iconOpacity = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential
+ cameraStops:@{
+ @5.9: [MGLStyleValue valueWithRawValue:@0],
+ @6: [MGLStyleValue valueWithRawValue:@1],
+ }
+ options:nil];
+ symbols.iconHaloColor = [MGLStyleValue valueWithRawValue:[[UIColor whiteColor] colorWithAlphaComponent:0.5]];
+ symbols.iconHaloWidth = [MGLStyleValue valueWithRawValue:@1];
+ // {name} references the "name" key in an MGLPointFeature’s attributes dictionary.
+ symbols.text = [MGLStyleValue valueWithRawValue:@"{name}"];
+ symbols.textColor = symbols.iconColor;
+ symbols.textFontSize = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential
+ cameraStops:@{
+ @10: [MGLStyleValue valueWithRawValue:@10],
+ @16: [MGLStyleValue valueWithRawValue:@16],
+ }
+ options:nil];
+ symbols.textTranslation = [MGLStyleValue valueWithRawValue:[NSValue valueWithCGVector:CGVectorMake(10, 0)]];
+ symbols.textOpacity = symbols.iconOpacity;
+ symbols.textHaloColor = symbols.iconHaloColor;
+ symbols.textHaloWidth = symbols.iconHaloWidth;
+ symbols.textJustification = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLTextJustification:MGLTextJustificationLeft]];
+ symbols.textAnchor = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLTextAnchor:MGLTextAnchorLeft]];
+
+ [self.mapView.style addLayer:circles];
+ [self.mapView.style addLayer:symbols];
+}
+
+#pragma mark - Feature interaction
+
+- (void)handleMapTap:(UITapGestureRecognizer *)sender {
+ if (sender.state == UIGestureRecognizerStateEnded) {
+ // Limit feature selection to just the following layer identifiers.
+ NSArray *layerIdentifiers = @[@"lighthouse-symbols", @"lighthouse-circles"];
+
+ CGPoint point = [sender locationInView:sender.view];
+
+ // Try matching the exact point first
+ for (id f in [self.mapView visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:[NSSet setWithArray:layerIdentifiers]]) {
+ if ([f isKindOfClass:[MGLPointFeature class]]) {
+ [self showCallout:f];
+ return;
+ }
+ }
+
+ // Otherwise, get first features within a rect the size of a touch (44x44).
+ CGRect pointRect = {point, CGSizeZero};
+ CGRect touchRect = CGRectInset(pointRect, -22.0, -22.0);
+ for (id f in [self.mapView visibleFeaturesInRect:touchRect inStyleLayersWithIdentifiers:[NSSet setWithArray:layerIdentifiers]]) {
+ if ([f isKindOfClass:[MGLPointFeature class]]) {
+ [self showCallout:f];
+ return;
+ }
+ }
+
+ // If no features were found, deselect the selected annotation, if any.
+ [self.mapView deselectAnnotation:[[self.mapView selectedAnnotations] firstObject] animated:YES];
+ }
+}
+
+- (void)showCallout:(MGLPointFeature *)feature {
+ MGLPointFeature *point = [[MGLPointFeature alloc] init];
+ point.title = feature.attributes[@"name"];
+ point.coordinate = feature.coordinate;
+
+ // Selecting an feature that doesn’t already exist on the map will add a new annotation view.
+ // We’ll need to use the map’s delegate methods to add an empty annotation view and remove it when we’re done selecting it.
+ [self.mapView selectAnnotation:point animated:YES];
+}
+
+#pragma mark - MGLMapViewDelegate
+
+- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation {
+ return YES;
+}
+
+- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation {
+ [mapView removeAnnotation:annotation];
+}
+
+- (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id <MGLAnnotation>)annotation {
+ // Create an empty view annotation. Set a frame to offset the callout.
+ return [[MGLAnnotationView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
+}
+
+#pragma mark - Data fetching and parsing
+
+- (void)fetchPoints:(void (^)(NSArray *))completion {
+ // Wikidata query for all lighthouses in the United States: http://tinyurl.com/zrl2jc4
+ NSString *query = @"SELECT DISTINCT ?item "
+ "?itemLabel ?coor ?image "
+ "WHERE "
+ "{ "
+ "?item wdt:P31 wd:Q39715 . "
+ "?item wdt:P17 wd:Q30 . "
+ "?item wdt:P625 ?coor . "
+ "OPTIONAL { ?item wdt:P18 ?image } . "
+ "SERVICE wikibase:label { bd:serviceParam wikibase:language \"en\" } "
+ "} "
+ "ORDER BY ?itemLabel";
+
+ NSMutableCharacterSet *characterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
+ [characterSet removeCharactersInString:@"?"];
+ [characterSet removeCharactersInString:@"&"];
+ [characterSet removeCharactersInString:@":"];
+
+ NSString *encodedQuery = [query stringByAddingPercentEncodingWithAllowedCharacters:characterSet];
+
+ NSString *urlString = [NSString stringWithFormat:@"https://query.wikidata.org/sparql?query=%@&format=json", encodedQuery];
+
+ [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:urlString] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
+ if (!data) return;
+
+ NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ NSArray *items = json[@"results"][@"bindings"];
+
+ if (!items) return;
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion([self parseJSONItems:items]);
+ });
+ }] resume];
+}
+
+- (NSArray *)parseJSONItems:(NSArray *)items {
+ NSMutableArray *features = [NSMutableArray array];
+ for (NSDictionary *item in items) {
+ NSString *title = item[@"itemLabel"][@"value"];
+ NSString *point = item[@"coor"][@"value"];
+ if (!item || !point) continue;
+
+ NSString *parsedPoint = [[point stringByReplacingOccurrencesOfString:@"Point(" withString:@""] stringByReplacingOccurrencesOfString:@")" withString:@""];
+ NSArray *pointComponents = [parsedPoint componentsSeparatedByString:@" "];
+
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ feature.coordinate = CLLocationCoordinate2DMake([pointComponents[1] doubleValue], [pointComponents[0] doubleValue]);
+ feature.title = title;
+ // A feature’s attributes can used by runtime styling for things like text labels.
+ feature.attributes = @{
+ @"name": title,
+ };
+ [features addObject:feature];
+ }
+ return features;
+}
+
+@end