Ios 如何使用AVCapturePhoto包含位置数据?

Ios 如何使用AVCapturePhoto包含位置数据?,ios,swift,xcode,avfoundation,cllocationmanager,Ios,Swift,Xcode,Avfoundation,Cllocationmanager,我正在尝试用Swift编写一个iOS应用程序,让用户拍照,然后我将在图像上覆盖一些额外的数据。我希望图像包括位置数据。我使用的是AVCapturePhoto,我可以在文档中看到它有一些元数据变量,但是我找不到关于如何使用它们的任何信息。当我现在用我的应用程序拍照时,EXIF信息中没有位置数据 我如何设置捕获会话以嵌入位置数据?我还没有处理新的IOS 11 AVCapturePhoto对象,因此这个答案必须对如何访问数据做出一些假设,但理论上所有这些都应该可行 在添加位置数据之前,您需要询问用户是

我正在尝试用Swift编写一个iOS应用程序,让用户拍照,然后我将在图像上覆盖一些额外的数据。我希望图像包括位置数据。我使用的是AVCapturePhoto,我可以在文档中看到它有一些元数据变量,但是我找不到关于如何使用它们的任何信息。当我现在用我的应用程序拍照时,EXIF信息中没有位置数据


我如何设置捕获会话以嵌入位置数据?

我还没有处理新的IOS 11 AVCapturePhoto对象,因此这个答案必须对如何访问数据做出一些假设,但理论上所有这些都应该可行

在添加位置数据之前,您需要询问用户是否可以使用他们的位置。将“隐私-使用时的位置”标签添加到您的Info.plist。然后在初始化过程中的某个地方添加以下代码

// Request authorization for location manager 
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
    break

case .notDetermined:
    locationManager.requestWhenInUseAuthorization()
    locationManagerStatus = CLLocationManager.authorizationStatus()

default:
    locationManagerStatus = .denied
}
现在,下面将创建一个包含位置信息的字典

// create GPS metadata properties
func createLocationMetadata() -> NSMutableDictionary? {

    guard CLLocationManager.authorizationStatus() == .authorizedWhenInUse else {return nil}

    if let location = locationManager.location {
        let gpsDictionary = NSMutableDictionary()
        var latitude = location.coordinate.latitude
        var longitude = location.coordinate.longitude
        var altitude = location.altitude
        var latitudeRef = "N"
        var longitudeRef = "E"
        var altitudeRef = 0

        if latitude < 0.0 {
            latitude = -latitude
            latitudeRef = "S"
        }

        if longitude < 0.0 {
            longitude = -longitude
            longitudeRef = "W"
        }

        if altitude < 0.0 {
            altitude = -altitude
            altitudeRef = 1
        }

        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy:MM:dd"
        gpsDictionary[kCGImagePropertyGPSDateStamp] = formatter.string(from:location.timestamp)
        formatter.dateFormat = "HH:mm:ss"
        gpsDictionary[kCGImagePropertyGPSTimeStamp] = formatter.string(from:location.timestamp)
        gpsDictionary[kCGImagePropertyGPSLatitudeRef] = latitudeRef
        gpsDictionary[kCGImagePropertyGPSLatitude] = latitude
        gpsDictionary[kCGImagePropertyGPSLongitudeRef] = longitudeRef
        gpsDictionary[kCGImagePropertyGPSLongitude] = longitude
        gpsDictionary[kCGImagePropertyGPSDOP] = location.horizontalAccuracy
        gpsDictionary[kCGImagePropertyGPSAltitudeRef] = altitudeRef
        gpsDictionary[kCGImagePropertyGPSAltitude] = altitude

        if let heading = locationManager.heading {
            gpsDictionary[kCGImagePropertyGPSImgDirectionRef] = "T"
            gpsDictionary[kCGImagePropertyGPSImgDirection] = heading.trueHeading
        }

        return gpsDictionary;
    }
    return nil
}
退回这个。此外,您还需要原始文件元数据。我猜是这样的

AVCapturePhoto.metadata
包含这个。根据这些假设,下面的函数将为您提供带有附加位置数据的文件数据表示。可能会有更新的IOS 11方法以更干净的方式实现这一点

func getFileRepresentationWithLocationData(photo : AVCapturePhoto) -> Data {
    // get image metadata
    var properties = photo.metadata

    // add gps data to metadata
    if let gpsDictionary = createLocationMetadata() {
        properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
    }

    // create new file representation with edited metadata
    return photo.fileDataRepresentation(withReplacementMetadata:properties,
        replacementEmbeddedThumbnailPhotoFormat:photo.embeddedThumbnailPhotoFormat,
        replacementEmbeddedThumbnailPixelBuffer:photo.previewPixelBuffer,
        replacementDepthData:photo.depthData)
}

@adamfowlerphoto给出了迄今为止我能找到的关于这个问题的最佳答案

AVCapturePhoto.fileDataRepresentation(带ReplacementMetadata:replacementEmbeddedThumbnailPhotoFormat:replacementEmbeddedThumbnailPixelBuffer:replacementDepthData:)
在iOS 12中被弃用。对于新API,必须使用方法
replacementmatadata()
实现
AVCapturePhotoFileDataRepresentationCustomizer
协议。以下是更新的代码:

extension CameraViewController: AVCapturePhotoFileDataRepresentationCustomizer {

    // create GPS metadata properties
    func createLocationMetadata() -> NSMutableDictionary? {
        if let location = locationManager.location {
            let gpsDictionary = NSMutableDictionary()
            var latitude = location.coordinate.latitude
            var longitude = location.coordinate.longitude
            var altitude = location.altitude
            var latitudeRef = "N"
            var longitudeRef = "E"
            var altitudeRef = 0

            if latitude < 0.0 {
                latitude = -latitude
                latitudeRef = "S"
            }

            if longitude < 0.0 {
                longitude = -longitude
                longitudeRef = "W"
            }

            if altitude < 0.0 {
                altitude = -altitude
                altitudeRef = 1
            }

            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy:MM:dd"
            gpsDictionary[kCGImagePropertyGPSDateStamp] = formatter.string(from:location.timestamp)
            formatter.dateFormat = "HH:mm:ss"
            gpsDictionary[kCGImagePropertyGPSTimeStamp] = formatter.string(from:location.timestamp)
            gpsDictionary[kCGImagePropertyGPSLatitudeRef] = latitudeRef
            gpsDictionary[kCGImagePropertyGPSLatitude] = latitude
            gpsDictionary[kCGImagePropertyGPSLongitudeRef] = longitudeRef
            gpsDictionary[kCGImagePropertyGPSLongitude] = longitude
            gpsDictionary[kCGImagePropertyGPSDOP] = location.horizontalAccuracy
            gpsDictionary[kCGImagePropertyGPSAltitudeRef] = altitudeRef
            gpsDictionary[kCGImagePropertyGPSAltitude] = altitude

            if let heading = locationManager.heading {
                gpsDictionary[kCGImagePropertyGPSImgDirectionRef] = "T"
                gpsDictionary[kCGImagePropertyGPSImgDirection] = heading.trueHeading
            }

            return gpsDictionary;
        }
        return nil
    }
    
    func getFileRepresentationWithLocationData(photo : AVCapturePhoto) -> Data {
        // get image metadata
        var properties = photo.metadata

        // add gps data to metadata
        if let gpsDictionary = createLocationMetadata() {
            properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
        }

        // create new file representation with edited metadata
        
        return photo.fileDataRepresentation(with: self) ?? Data()
    }
    
    func replacementMetadata(for photo: AVCapturePhoto) -> [String : Any]? {
        var properties = photo.metadata

        // add gps data to metadata
        if let gpsDictionary = createLocationMetadata() {
            properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
        }
        return properties
    }
}
不要尝试将图像数据转换为JPEG格式,因为它将丢弃GPS元数据。(来源:)


你能添加你如何保存图像的代码吗?快速浏览一下AVCapturePhoto文档,看起来有一种更简单的方法来替换元数据。而不是使用您可以使用的cgImage函数。AVCapturePhoto.fileDataRepresentation(带ReplacementMetadata:replacementEmbeddedThumbnailPhotoFormat:replacementEmbeddedThumbnailPixelBuffer:replacementDepthData:)我将编辑我的答案
extension CameraViewController: AVCapturePhotoFileDataRepresentationCustomizer {

    // create GPS metadata properties
    func createLocationMetadata() -> NSMutableDictionary? {
        if let location = locationManager.location {
            let gpsDictionary = NSMutableDictionary()
            var latitude = location.coordinate.latitude
            var longitude = location.coordinate.longitude
            var altitude = location.altitude
            var latitudeRef = "N"
            var longitudeRef = "E"
            var altitudeRef = 0

            if latitude < 0.0 {
                latitude = -latitude
                latitudeRef = "S"
            }

            if longitude < 0.0 {
                longitude = -longitude
                longitudeRef = "W"
            }

            if altitude < 0.0 {
                altitude = -altitude
                altitudeRef = 1
            }

            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy:MM:dd"
            gpsDictionary[kCGImagePropertyGPSDateStamp] = formatter.string(from:location.timestamp)
            formatter.dateFormat = "HH:mm:ss"
            gpsDictionary[kCGImagePropertyGPSTimeStamp] = formatter.string(from:location.timestamp)
            gpsDictionary[kCGImagePropertyGPSLatitudeRef] = latitudeRef
            gpsDictionary[kCGImagePropertyGPSLatitude] = latitude
            gpsDictionary[kCGImagePropertyGPSLongitudeRef] = longitudeRef
            gpsDictionary[kCGImagePropertyGPSLongitude] = longitude
            gpsDictionary[kCGImagePropertyGPSDOP] = location.horizontalAccuracy
            gpsDictionary[kCGImagePropertyGPSAltitudeRef] = altitudeRef
            gpsDictionary[kCGImagePropertyGPSAltitude] = altitude

            if let heading = locationManager.heading {
                gpsDictionary[kCGImagePropertyGPSImgDirectionRef] = "T"
                gpsDictionary[kCGImagePropertyGPSImgDirection] = heading.trueHeading
            }

            return gpsDictionary;
        }
        return nil
    }
    
    func getFileRepresentationWithLocationData(photo : AVCapturePhoto) -> Data {
        // get image metadata
        var properties = photo.metadata

        // add gps data to metadata
        if let gpsDictionary = createLocationMetadata() {
            properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
        }

        // create new file representation with edited metadata
        
        return photo.fileDataRepresentation(with: self) ?? Data()
    }
    
    func replacementMetadata(for photo: AVCapturePhoto) -> [String : Any]? {
        var properties = photo.metadata

        // add gps data to metadata
        if let gpsDictionary = createLocationMetadata() {
            properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
        }
        return properties
    }
}
    static func printEXIFData(imageData: Data) {
        var exifData: CFDictionary? = nil
        imageData.withUnsafeBytes {
            let bytes = $0.baseAddress?.assumingMemoryBound(to: UInt8.self)
            if let cfData = CFDataCreate(kCFAllocatorDefault, bytes, imageData.count),
               let source = CGImageSourceCreateWithData(cfData, nil) {
                exifData = CGImageSourceCopyPropertiesAtIndex(source, 0, nil)
                print(exifData)
            }
        }
    }
let data = jpegData(compressionQuality: 1.0) // NOOOOO