Сохранение информации Geotag с фотографией на iOS4.1

У меня возникли серьезные проблемы при попытке сохранить фотографию в фотопленку с информацией о геотегах на iOS4.1. Я использую следующий API ALAssetsLibrary:

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

У меня есть GPS-координаты, которые я хочу сохранить с фотографией в качестве входа. К сожалению, нет документации или примера кода, описывающего, как формировать метаданные NSDictionary, которые инкапсулируют координаты GPS. Может кто-нибудь опубликовать пример кода, который, как известно, работает?

Я также пытался использовать библиотеку iPhone Exif для сохранения географической информации в imageData вместо использования метаданных, но, к сожалению, библиотека iPhone Exif дает сбой. Любая помощь с благодарностью.

7 ответов

Вот код для копирования всей доступной информации из объекта CLLocation в соответствующий формат для словаря метаданных GPS:

- (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;
}

Назначьте словарь, возвращаемый этим методом, в качестве значения для kCGImagePropertyGPSDictionary введите словарь метаданных, который вы передаете writeImageDataToSavedPhotosAlbum:metadata:completionBlock: или же CGImageDestinationAddImage(),

Я использовал этот код и создал NSMutableDictionary, чтобы помочь сохранить геотег и другие метаданные в изображение. Проверьте мой блог здесь:

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

Вот удобная категория CLLocation для GIST, чтобы сделать все это для вас:

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

После долгих поисков я нашел и адаптировал это

Это превращает данные cclocation в подходящий 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];

}

Затем я добавляю его к существующим метаданным, которые вы получаете через камеру (которая по умолчанию не имеет данных GPS)

Я получаю оригинальные метаданные, как это

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

Затем я добавляю словарь GPS, который производит предыдущий метод.

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

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

Ответ Аноми в 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
}

Класс для записи данных GPS в метаданные.

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);
}
Другие вопросы по тегам