Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Render part of MKMapView to image

I am interested in capturing a portion of the map to create the image you see on the top of the popover. I guess I'll capture a UIImage, you can do so starting from the coordinates of the pin?

Thanks

Apple maps popover

like image 616
Mattia Lancieri Avatar asked Feb 04 '13 09:02

Mattia Lancieri


2 Answers

You can try something like this:

- (UIImage*) imageFromView:(UIView*)view rect:(CGRect)rect {
    // capture the full view in an image
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);

    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage* viewImg = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    // crop down to just our desired rectangle, accounting for Retina scale 
    CGFloat scale = [[UIScreen mainScreen] scale];
    CGRect scaledRect = CGRectMake(scale * rect.origin.x, scale * rect.origin.y,
                                   scale * rect.size.width, scale * rect.size.height);
    CGImageRef resultImgRef = CGImageCreateWithImageInRect([viewImg CGImage], scaledRect);
    UIImage* result = [UIImage imageWithCGImage: resultImgRef
                                          scale: 1.0f / scale
                                    orientation: UIImageOrientationUp];

    CGImageRelease(resultImgRef);
    return result;
}

- (IBAction)onButtonTapped:(id)sender {
    // just pick the map's center as the location to capture.  could be anything.
    CLLocationCoordinate2D center = self.map.centerCoordinate;
    // convert geo coordinates to screen (view) coordinates
    CGPoint point = [self.map convertCoordinate:center toPointToView: self.map];

    // make this span whatever you want - I use 120x80 points
    float width = 120.0;
    float height = 80.0;
    // here's the frame in which we'll capture the underlying map
    CGRect frame = CGRectMake(point.x - width / 2.0,
                              point.y - height / 2.0,
                              width, height);

    // just show the captured image in a UIImageView overlay 
    //  in the top, left corner, with 1:1 scale
    UIImageView* overlay = [[UIImageView alloc] initWithImage: [self imageFromView: self.map rect: frame]];
    frame.origin = CGPointZero;
    overlay.frame = frame;
    [self.view addSubview: overlay];
}

The imageFromView:rect: method just captures a given rectangular area, within a given view, and produces a UIImage.

The onButtonTapped method uses the first method to capture an area around the center of my map, and display the captured image at the upper left corner of the screen. Of course, you can replace the map center with the coordinate of your pin, make the area width/height whatever you like, and put the resulting image into your popup view.

This is just a demonstration. I used a button in my sample app, just to trigger the capture, after panning the map where I wanted it to be.

Results

enter image description here

Limitations

My code just displays the captured rectangle at a 1:1 size ratio. Of course, if you want, you can put the captured UIImage into a UIImageView that scales it however you like. You could make the image bigger. But, you will lose image sharpness if you do that. This process just does a screen capture of a UIView. It doesn't work on map data directly, so when you blow it up (display the image at a larger size), the image doesn't sharpen as it does when you actually zoom in on a MKMapView.

like image 102
Nate Avatar answered Sep 28 '22 10:09

Nate


On iOS7 you can use MKMapSnapshotter

like image 22
dwery Avatar answered Sep 28 '22 10:09

dwery