Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Add map snapshot API #1701

Closed
bleege opened this issue Jun 10, 2015 · 19 comments · Fixed by mapbox/MapboxStatic.swift#54
Closed

Add map snapshot API #1701

bleege opened this issue Jun 10, 2015 · 19 comments · Fixed by mapbox/MapboxStatic.swift#54
Labels
feature iOS Mapbox Maps SDK for iOS MapKit parity For feature parity with MapKit on iOS or macOS

Comments

@bleege
Copy link
Contributor

bleege commented Jun 10, 2015

To achieve closer parity with Apple's MapKit, Mapbox GL should have a corresponsding MKMapSnapshotter API for asynchronously loading map imagery. This will likely just mean providing a cleaner API around the Mapbox Static Images API.

@bleege bleege added feature iOS Mapbox Maps SDK for iOS labels Jun 10, 2015
@1ec5
Copy link
Contributor

1ec5 commented Jun 10, 2015

mbgl is already capable of static rendering. Instead of running that same code on the server, we should just expose static rendering mode in the Cocoa later.

@bleege
Copy link
Contributor Author

bleege commented Jun 10, 2015

^^ Cool. Yeah, if we can accomplish this on device let's build the MapKit mirroring API that way. 👍

@incanus
Copy link
Contributor

incanus commented Jul 21, 2015

AFAICT this will be using one or both of -[UIView snapshotViewAfterScreenUpdates:] and -[GLKView snapshot] since capturing only the GLKView isn't enough to also get the user dot, logo, attribution button, compass, and any annotation views.

Performance might also be an issue here, but I guess it depends on the use cases for the snapshot (e.g. a simple dummy overlay view for transitions vs. something needing to be more performant like a blur).

@incanus
Copy link
Contributor

incanus commented Jul 21, 2015

The other interesting bit will be determining if all tiles are loaded to the best of our ability before capturing, so #1804 becomes relevant.

@friedbunny
Copy link
Contributor

An interim solution is to do basic snapshotting yourself. This will get you the base map, annotations, and the map view UI.

Objective-C:

- (UIImage *)snapshot:(UIView *)view {
     UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0);
    [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
     UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();

     return snapshot;
}

Swift:

func snapshot(view: UIView) -> UIImage {
     UIGraphicsBeginImageContextWithOptions(view.bounds.size, true, 0)
     view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
     let snapshot = UIGraphicsGetImageFromCurrentImageContext()
     UIGraphicsEndImageContext()

     return snapshot
}

simulator-screen-shot-mar-1 -2016 -12 11 20-pm

(Rehashing my Stack Overflow answer.)

@jfirebaugh jfirebaugh removed the P2 label Mar 24, 2016
@TWDdpigram
Copy link

@friedbunny
I've noticed something when trying to take a snapshot of the map view. I'm having to actually add the map view to the a subview before taking snapshot of it. If I were to take a snap shot immediately after adding it, the map won't be loaded and the snapshot's base map would be completely black. I'm still able to see Mapbox logo at the bottom of the view but there is no map. Below is the example I've used to make it work. Is there a way to have the map load faster in order for the snapshot to not appear as a black screen or will I always have to wait for the map to load before taking a snap shot?

-(void)viewDidLoad {
    MGLMapView *mapView = [[MGLMapView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:mapView];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        [mapView removeFromSuperview];

        UIImageView *snapShotImgView = [[UIImageView alloc]initWithImage:[self snapshot:mapView]];
        snapShotImgView.frame = self.view.frame;

        [self.view addSubview:snapShotImgView];
    });
}

@friedbunny
Copy link
Contributor

@TWDdpigram Initiating the manual snapshot in -[MGLMapViewDelegate mapViewDidFinishLoadingMap:] would probably be the fastest way to do this right now. It won’t load the map more quickly, but it should be the first instant the map is visually ready.

@jcarlos-p
Copy link

@friedbunny does visually ready means all the elements in the map are already rendered on top of it? I've tried taking a snapshot of an MGLMapView using the code suggested inside of its delegate's mapViewDidFinishLoadingMap: and most of the time the map is drawn without many elements such as city labels and there's even the case where the map is completely blank.

Here are some examples of what I'm getting:

atlanta

cupertino lbs

@friedbunny
Copy link
Contributor

friedbunny commented Jun 18, 2016

@jcarlos-p It appears that -[MGLMapViewDelegate mapViewDidFinishLoadingMap:] can be sent a tick too early. Snapshotting using the above method is working for me, after inserting a zero second delay (which causes it to fire on the next run loop cycle):

[self performSelector:@selector(snapshot:) withObject:mapView afterDelay:0];

@friedbunny friedbunny changed the title MKMapSnapshotter API Add map snapshot API Jul 29, 2016
@1ec5 1ec5 added the MapKit parity For feature parity with MapKit on iOS or macOS label Aug 22, 2016
@jcarlos-p
Copy link

@friedbunny Can the sample code be safely executed while the app is in the background ?

We found a problem where we execute this code but if the App is in the background, the map snapshot would come back completely black.

@friedbunny
Copy link
Contributor

friedbunny commented Oct 11, 2016

@jcarlos-p I wouldn’t expect MGLMapView to render anything if the app is not in the foreground. We’ve had crashes related to instantiating a GL context in the background and currently guard against this happening.

As others have touched on on this thread, how to accomplish off-screen rendering is the main hurdle that needs to be addressed for this feature to move forward.

@jcarlos-p
Copy link

That's what I thought. Thanks for the clarification.

We'll just put a workaround in place then.

@quicklywilliam
Copy link

@jcarlos-p just wondering if you could share your workaround for this? We also need to render our map in the background so that it can be included in a UNNotificationAttachment (this, in turn, is a workaround for #6230 – Mapbox doesn't work in a Notification Extension). We're currently using MapboxStatic to accomplish this, but the results are not nearly as pretty and are tied to the old Classic API's/styles.

@jcarlos-p
Copy link

@quicklywilliam We had to go the static API way also :(

@friedbunny
Copy link
Contributor

Accidentally closed by errant verbiage in mapbox/MapboxStatic.swift#54.

@friedbunny friedbunny reopened this Mar 27, 2017
@1ec5
Copy link
Contributor

1ec5 commented Mar 28, 2017

lol, if only it were so simple!

@kkaefer
Copy link
Contributor

kkaefer commented May 12, 2017

We found a problem where we execute this code but if the App is in the background, the map snapshot would come back completely black.

iOS prohibits OpenGL operations when the application is in the background: https://developer.apple.com/library/content/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/ImplementingaMultitasking-awareOpenGLESApplication/ImplementingaMultitasking-awareOpenGLESApplication.html

@tobrun
Copy link
Member

tobrun commented Sep 12, 2017

This happened in #9891, tail work is ticketed out in #9916, #9914 and #9920

@tobrun tobrun closed this as completed Sep 12, 2017
@friedbunny friedbunny added this to the ios-v3.7.0 milestone Feb 3, 2018
@haxpor
Copy link

haxpor commented May 16, 2018

Just for a note for someone else trying to achieve the same thing. I have to directly use MGLMapView and not its snapshotView(afterScreenUpdates:) as UIView parameter. If not, I will get black screen. Use solution as posted by friedbunny above. Thanks.
PS. Xcode 9.3, iOS 11, both release version and Mapbox 3.7.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature iOS Mapbox Maps SDK for iOS MapKit parity For feature parity with MapKit on iOS or macOS
Projects
None yet
Development

Successfully merging a pull request may close this issue.