开发者

Saving Geotag info with photo on iOS4.1

I am having major issues trying to save a photo to camera roll with geotag info on iOS4.1. I am using following ALAssetsLibrary API:

- (void)writeImageDataToSavedPhotosAlbum:(NSData *)imageData 
                                metadata:(NSDictionary *)metadata 
                         completionBlock:(ALAssetsLibraryWriteImageCompletionBlock)completionBlock

I have the GPS coordinates that i wish to save with the photo as an input. Unfortunately, there is no documentation or sample code that describes how to form the metadata NSDictionary that encapsulates the GPS coordinates. Can somebody post a sample code that is known to work ?

I have also tried using iPhone Exif library to save geo info in imageData rather than using metadata, but unfortunately iPhone Exif l开发者_如何学编程ibrary is crashing. Any help is greatly appreciated.


Here is code to copy all available information from a CLLocation object into the proper format for a GPS metadata dictionary:

- (NSDictionary *)getGPSDictionaryForLocation:(CLLocation *)location {
    NSMutableDictionary *gps = [NSMutableDictionary dictionary];

    // GPS tag version
    [gps setObject:@"2.2.0.0" forKey:(NSString *)kCGImagePropertyGPSVersion];

    // Time and date must be provided as strings, not as an NSDate object
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"HH:mm:ss.SSSSSS"];
    [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    [gps setObject:[formatter stringFromDate:location.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp];
    [formatter setDateFormat:@"yyyy:MM:dd"];
    [gps setObject:[formatter stringFromDate:location.timestamp] forKey:(NSString *)kCGImagePropertyGPSDateStamp];
    [formatter release];

    // Latitude
    CGFloat latitude = location.coordinate.latitude;
    if (latitude < 0) {
        latitude = -latitude;
        [gps setObject:@"S" forKey:(NSString *)kCGImagePropertyGPSLatitudeRef];
    } else {
        [gps setObject:@"N" forKey:(NSString *)kCGImagePropertyGPSLatitudeRef];
    }
    [gps setObject:[NSNumber numberWithFloat:latitude] forKey:(NSString *)kCGImagePropertyGPSLatitude];

    // Longitude
    CGFloat longitude = location.coordinate.longitude;
    if (longitude < 0) {
        longitude = -longitude;
        [gps setObject:@"W" forKey:(NSString *)kCGImagePropertyGPSLongitudeRef];
    } else {
        [gps setObject:@"E" forKey:(NSString *)kCGImagePropertyGPSLongitudeRef];
    }
    [gps setObject:[NSNumber numberWithFloat:longitude] forKey:(NSString *)kCGImagePropertyGPSLongitude];

    // Altitude
    CGFloat altitude = location.altitude;
    if (!isnan(altitude)){
        if (altitude < 0) {
            altitude = -altitude;
            [gps setObject:@"1" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
        } else {
            [gps setObject:@"0" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
        }
        [gps setObject:[NSNumber numberWithFloat:altitude] forKey:(NSString *)kCGImagePropertyGPSAltitude];
    }

    // Speed, must be converted from m/s to km/h
    if (location.speed >= 0){
        [gps setObject:@"K" forKey:(NSString *)kCGImagePropertyGPSSpeedRef];
        [gps setObject:[NSNumber numberWithFloat:location.speed*3.6] forKey:(NSString *)kCGImagePropertyGPSSpeed];
    }

    // Heading
    if (location.course >= 0){
        [gps setObject:@"T" forKey:(NSString *)kCGImagePropertyGPSTrackRef];
        [gps setObject:[NSNumber numberWithFloat:location.course] forKey:(NSString *)kCGImagePropertyGPSTrack];
    }

    return gps;
}

Assign the dictionary returned by this method as the value for the kCGImagePropertyGPSDictionary key in the metadata dictionary you pass to writeImageDataToSavedPhotosAlbum:metadata:completionBlock: or CGImageDestinationAddImage().


I used this code and created a NSMutableDictionary to help save geotag and other metadata to an image. Check out my blog post here:

http://blog.codecropper.com/2011/05/adding-metadata-to-ios-images-the-easy-way/


After much searching I found and adapted this

This turns cclocation data into a suitable NSDictionary

 #import <ImageIO/ImageIO.h>

+(NSMutableDictionary *)updateExif:(CLLocation *)currentLocation{


    NSMutableDictionary* locDict = [[NSMutableDictionary alloc] init];


    CLLocationDegrees exifLatitude = currentLocation.coordinate.latitude;
    CLLocationDegrees exifLongitude = currentLocation.coordinate.longitude;

    [locDict setObject:currentLocation.timestamp forKey:(NSString*)kCGImagePropertyGPSTimeStamp];

    if (exifLatitude <0.0){
        exifLatitude = exifLatitude*(-1);
        [locDict setObject:@"S" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
    }else{
        [locDict setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
    }
    [locDict setObject:[NSNumber numberWithFloat:exifLatitude] forKey:(NSString*)kCGImagePropertyGPSLatitude];

    if (exifLongitude <0.0){
        exifLongitude=exifLongitude*(-1);
        [locDict setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
    }else{
        [locDict setObject:@"E" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
    }
    [locDict setObject:[NSNumber numberWithFloat:exifLongitude] forKey:(NSString*) kCGImagePropertyGPSLongitude];


    return [locDict autorelease];

}

Then I add it to the existing metadata that you get through the camera (which doesn't by default have the gps data)

I get the original metadata like this

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{  
    [imageMetaData setDictionary:[[info objectForKey:UIImagePickerControllerMediaMetadata] copy]];
}

then I add the gps dictionary the previous method produces.

[imageMetaData setObject:currentLocation forKey:(NSString*)kCGImagePropertyGPSDictionary];          

    [library writeImageToSavedPhotosAlbum:[viewImage CGImage] metadata:imageMetaData completionBlock:photoCompblock];   


Here's a handy CLLocation category on gist to do all this for you:

https://gist.github.com/phildow/6043486


Anomie's response in Swift 4.0:

func getGPSDictionaryForLocation(location:CLLocation) -> [String:AnyObject] {

    var gps = [String:AnyObject]()

    var latitude = location.coordinate.latitude

    if(latitude < 0){
        latitude = -latitude
        gps[kCGImagePropertyGPSLatitudeRef as String] = "S" as AnyObject

    }else{
        gps[kCGImagePropertyGPSLatitudeRef as String] = "N" as AnyObject
    }

    gps[kCGImagePropertyGPSLatitude as String] = latitude as AnyObject


    var longitude = location.coordinate.longitude

    if(longitude < 0){
        longitude = -longitude
        gps[kCGImagePropertyGPSLongitudeRef as String] = "W" as AnyObject
    }else{
        gps[kCGImagePropertyGPSLongitudeRef as String] = "E" as AnyObject
    }

    gps[kCGImagePropertyGPSLongitude as String] = longitude as AnyObject

    gps[kCGImagePropertyGPSAltitude as String] = location.altitude as AnyObject

    return gps
}


Class to Write GPS Data to Meta-Data.

class GeoTagImage {

  /// Writes GPS data into the meta data.
  /// - Parameters:
  ///   - data: Coordinate meta data will be written to the copy of this data.
  ///   - coordinate: Cooordinates to write to meta data.
  static func mark(_ data: Data, with coordinate: Coordinate) -> Data {
    var source: CGImageSource? = nil
    source = CGImageSourceCreateWithData((data as CFData?)!, nil)
    // Get all the metadata in the image
    let metadata = CGImageSourceCopyPropertiesAtIndex(source!, 0, nil) as? [AnyHashable: Any]
    // Make the metadata dictionary mutable so we can add properties to it
    var metadataAsMutable = metadata
    var EXIFDictionary = (metadataAsMutable?[(kCGImagePropertyExifDictionary as String)]) as? [AnyHashable: Any]
    var GPSDictionary = (metadataAsMutable?[(kCGImagePropertyGPSDictionary as String)]) as? [AnyHashable: Any]

    if !(EXIFDictionary != nil) {
      // If the image does not have an EXIF dictionary (not all images do), then create one.
      EXIFDictionary = [:]
    }
    if !(GPSDictionary != nil) {
      GPSDictionary = [:]
    }

    // add coordinates in the GPS Dictionary
    GPSDictionary![(kCGImagePropertyGPSLatitude as String)] = coordinate.latitude
    GPSDictionary![(kCGImagePropertyGPSLongitude as String)] = coordinate.longitude
    EXIFDictionary![(kCGImagePropertyExifUserComment as String)] = "Raw Image"

    // Add our modified EXIF data back into the image’s metadata
    metadataAsMutable!.updateValue(GPSDictionary!, forKey: kCGImagePropertyGPSDictionary)
    metadataAsMutable!.updateValue(EXIFDictionary!, forKey: kCGImagePropertyExifDictionary)

    // This is the type of image (e.g., public.jpeg)
    let UTI: CFString = CGImageSourceGetType(source!)!

    // This will be the data CGImageDestinationRef will write into
    let dest_data = NSMutableData()
    let destination: CGImageDestination = CGImageDestinationCreateWithData(dest_data as CFMutableData, UTI, 1, nil)!
    // Add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
    CGImageDestinationAddImageFromSource(destination, source!, 0, (metadataAsMutable as CFDictionary?))

    // Tells the destination to write the image data and metadata into our data object.
    // It will return false if something goes wrong
    _ = CGImageDestinationFinalize(destination)

    return (dest_data as Data)
  }

  /// Prints the Meta Data from the Data.
  /// - Parameter data: Meta data will be printed of this object.
  static func logMetaData(from data: Data) {
    if let imageSource = CGImageSourceCreateWithData(data as CFData, nil) {
      let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil)
      if let dict = imageProperties as? [String: Any] {
        print(dict)
      }
    }
  }
}


- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *picture = [info objectForKey:UIImagePickerControllerOriginalImage];
    NSData *imageData = UIImagePNGRepresentation(picture);
    CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
    CFStringRef UTI = CGImageSourceGetType(sourceRef);
    NSMutableData *destinationData = [NSMutableData data];
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)destinationData, UTI, 1, NULL);
    NSMutableDictionary *metadata = [[[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex(sourceRef, 0, NULL)] autorelease];
    
    CGFloat latitude = 54.7;
    CGFloat longitude = 25.3;
    CGFloat altitude = 100.5;
    
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    [dictionary setObject:@(latitude) forKey:(NSString *)kCGImagePropertyGPSLatitude];
    [dictionary setObject:@(longitude) forKey:(NSString *)kCGImagePropertyGPSLongitude];
    [dictionary setObject:@(altitude) forKey:(NSString *)kCGImagePropertyGPSAltitude];
    
    [metadata setObject:dictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];
    
    CGImageDestinationAddImageFromSource(destination, sourceRef, 0, (__bridge CFDictionaryRef)metadata);
    
    BOOL success = CGImageDestinationFinalize(destination);
    
    if (success)
    {
        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
            PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset];
            [request addResourceWithType:PHAssetResourceTypePhoto data:imageData options:nil];
        } completionHandler:^(BOOL success, NSError *error)
         {
            if (error)
            {
                NSLog(@"Error : %@",error);
            }
        }];
    }
    
    CFRelease(destination);
    CFRelease(sourceRef);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜