在 MacOS 上,我可以通过查询 Photos.sqlite 数据库从所有 90,000 张照片中获取地名。
我现在正在尝试在 iOS 中做类似的事情,但只找到了
coordinate.latitude
和 coordinate.longitude
。
我看到有一个
reverseGeocodeLocation
接受坐标并返回 CLPlacemark,但每个应用程序的地理编码请求都有速率限制。
如果我查看 iPhone 上的“照片”应用程序,我可以看到我的照片已经有地名元数据,它是否在某个 API 中公开?
private func fetchPhotoLocations() {
let fetchOptions = PHFetchOptions()
fetchOptions.includeHiddenAssets = false
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let assets = PHAsset.fetchAssets(with: fetchOptions)
assets.enumerateObjects { (asset, index, stop) in
if let location = asset.location {
let resources = PHAssetResource.assetResources(for: asset)
let filename = resources.first?.originalFilename ?? "Unknown"
let options = PHAssetResourceRequestOptions()
options.isNetworkAccessAllowed = false
if let resource = resources.first {
PHAssetResourceManager.default().requestData(for: resource, options: options) { (data) in
} completionHandler: { (error) in
if let error = error {
print("Error loading resource: \(error)")
}
}
DispatchQueue.main.async {
self.photoLocations.append(PhotoLocation(
id: asset.localIdentifier,
coordinate: location.coordinate,
creationDate: asset.creationDate ?? Date(),
filename: filename
))
}
}
}
}
}
您可以批量处理位置并通过缓存地理编码结果来避免重复,而不是查询每张照片。
通过避免过多的同时请求来有效地使用
CLGeocoder
。
不幸的是,详细的地名元数据(如照片应用程序中显示的元数据)无法通过照片框架公开。
这是代码的更正版本:
import Photos
import CoreLocation
struct PhotoLocation {
let id: String
let coordinate: CLLocationCoordinate2D
let creationDate: Date
let filename: String
let placeName: String? // Optional for reverse geocoded place names
}
class PhotoManager {
private let geocoder = CLGeocoder()
private var geocodeCache = [CLLocationCoordinate2D: String]() // Cache for geocoded locations
private(set) var photoLocations: [PhotoLocation] = []
func fetchPhotoLocations() {
let fetchOptions = PHFetchOptions()
fetchOptions.includeHiddenAssets = false
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let assets = PHAsset.fetchAssets(with: fetchOptions)
assets.enumerateObjects { [weak self] (asset, index, stop) in
guard let self = self else { return }
if let location = asset.location {
let resources = PHAssetResource.assetResources(for: asset)
let filename = resources.first?.originalFilename ?? "Unknown"
let coordinate = location.coordinate
// Check cache before making a geocoding request
if let cachedPlaceName = self.geocodeCache[coordinate] {
self.addPhotoLocation(asset: asset, coordinate: coordinate, filename: filename, placeName: cachedPlaceName)
} else {
// Perform reverse geocoding
self.geocoder.reverseGeocodeLocation(location) { [weak self] (placemarks, error) in
guard let self = self else { return }
let placeName = placemarks?.first?.locality ?? placemarks?.first?.name
if let placeName = placeName {
self.geocodeCache[coordinate] = placeName // Cache the result
}
self.addPhotoLocation(asset: asset, coordinate: coordinate, filename: filename, placeName: placeName)
}
}
}
}
}
private func addPhotoLocation(asset: PHAsset, coordinate: CLLocationCoordinate2D, filename: String, placeName: String?) {
DispatchQueue.main.async {
let photoLocation = PhotoLocation(
id: asset.localIdentifier,
coordinate: coordinate,
creationDate: asset.creationDate ?? Date(),
filename: filename,
placeName: placeName
)
self.photoLocations.append(photoLocation)
}
}
对于大型照片库,您可以: