iOS 拍照中加入GPS和具体地理位置
最近有一个需求,要求用手机拍个照片,并切需要拍摄时间,拍摄gps,拍摄具体街道信息。
首先要感谢PhotoGPSdemo的作者,你可以到这里下载demo http://www.cocoachina.com/bbs/read.php?tid=130501。
以前,总认为jpg就是包含了像素信息的2进制文件,其实,jpg中还可以包含许多起它的信息,只不过我们平常用查看jpg属性时,系统没有给我们把信息全部显示出来而已!
在iOS中,提供了一组函数来操作jpg的这些额外的信息,你需要#import <ImageIO/ImageIO.h>才能使用他们。
首先,需要注意的是,UIImage对象中是没有这些信息的,它仅仅包含图像本身,jpg才包含这些信息,请不要弄混。
先看看怎么从jpg图片中取得各种额外信息,代码如下
- (IBAction)test
{
NSString * home = NSHomeDirectory();
NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@/Documents/xx.jpg",home]]; CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); CFDictionaryRef metaDataDicRef = CGImageSourceCopyPropertiesAtIndex(imgSource,,nil);
NSLog(@"cfdictionary %@",(__bridge NSDictionary *)metaDataDicRef); NSDictionary *dic = (__bridge NSDictionary *)metaDataDicRef; NSDictionary *exifDic = [dic objectForKey:(NSString *)kCGImagePropertyExifDictionary]; NSString *location = [exifDic objectForKey:(NSString *)kCGImagePropertyExifCameraOwnerName];
NSLog(@"location is %@",location); NSString *DateTimeOriginal = [exifDic objectForKey:(NSString *)kCGImagePropertyExifDateTimeOriginal];
NSLog(@"time is %@",DateTimeOriginal); CFRelease(metaDataDicRef);
CFRelease(imgSource);
}
首先,需要用到CGImageSourceRef类,不能用UIImage类型(需要验证。。。),这种CGImageSourceRef类型可以通过函数取出jpg的额外数据,这些数据叫做metaData,翻译过来就是元数据。得到的metaData是以字典形式返回的,其中的key是固定的,具体声明见下方
CFStringRef kCGImagePropertyTIFFDictionary;
CFStringRef kCGImagePropertyGIFDictionary;
CFStringRef kCGImagePropertyJFIFDictionary;
CFStringRef kCGImagePropertyExifDictionary;
CFStringRef kCGImagePropertyPNGDictionary;
CFStringRef kCGImagePropertyIPTCDictionary;
CFStringRef kCGImagePropertyGPSDictionary;
CFStringRef kCGImagePropertyRawDictionary;
CFStringRef kCGImagePropertyCIFFDictionary;
CFStringRef kCGImageProperty8BIMDictionary;
CFStringRef kCGImagePropertyDNGDictionary;
CFStringRef kCGImagePropertyExifAuxDictionary;
这些key中,有许多key对应的对象还是一个字典,比如kCGImagePropertyExifDictionary所对应的对象包含一下key值
const CFStringRef kCGImagePropertyExifExposureTime;
const CFStringRef kCGImagePropertyExifFNumber;
const CFStringRef kCGImagePropertyExifExposureProgram;
const CFStringRef kCGImagePropertyExifSpectralSensitivity;
const CFStringRef kCGImagePropertyExifISOSpeedRatings;
const CFStringRef kCGImagePropertyExifOECF;
const CFStringRef kCGImagePropertyExifVersion;
const CFStringRef kCGImagePropertyExifDateTimeOriginal;
const CFStringRef kCGImagePropertyExifDateTimeDigitized;
const CFStringRef kCGImagePropertyExifComponentsConfiguration;
const CFStringRef kCGImagePropertyExifCompressedBitsPerPixel;
const CFStringRef kCGImagePropertyExifShutterSpeedValue;
const CFStringRef kCGImagePropertyExifApertureValue;
const CFStringRef kCGImagePropertyExifBrightnessValue;
const CFStringRef kCGImagePropertyExifExposureBiasValue;
const CFStringRef kCGImagePropertyExifMaxApertureValue;
const CFStringRef kCGImagePropertyExifSubjectDistance;
const CFStringRef kCGImagePropertyExifMeteringMode;
const CFStringRef kCGImagePropertyExifLightSource;
const CFStringRef kCGImagePropertyExifFlash;
const CFStringRef kCGImagePropertyExifFocalLength;
const CFStringRef kCGImagePropertyExifSubjectArea;
const CFStringRef kCGImagePropertyExifMakerNote;
const CFStringRef kCGImagePropertyExifUserComment;
const CFStringRef kCGImagePropertyExifSubsecTime;
const CFStringRef kCGImagePropertyExifSubsecTimeOrginal;
const CFStringRef kCGImagePropertyExifSubsecTimeDigitized;
const CFStringRef kCGImagePropertyExifFlashPixVersion;
const CFStringRef kCGImagePropertyExifColorSpace;
const CFStringRef kCGImagePropertyExifPixelXDimension;
const CFStringRef kCGImagePropertyExifPixelYDimension;
const CFStringRef kCGImagePropertyExifRelatedSoundFile;
const CFStringRef kCGImagePropertyExifFlashEnergy;
const CFStringRef kCGImagePropertyExifSpatialFrequencyResponse;
const CFStringRef kCGImagePropertyExifFocalPlaneXResolution;
const CFStringRef kCGImagePropertyExifFocalPlaneYResolution;
const CFStringRef kCGImagePropertyExifFocalPlaneResolutionUnit;
const CFStringRef kCGImagePropertyExifSubjectLocation;
const CFStringRef kCGImagePropertyExifExposureIndex;
const CFStringRef kCGImagePropertyExifSensingMethod;
const CFStringRef kCGImagePropertyExifFileSource;
const CFStringRef kCGImagePropertyExifSceneType;
const CFStringRef kCGImagePropertyExifCFAPattern;
const CFStringRef kCGImagePropertyExifCustomRendered;
const CFStringRef kCGImagePropertyExifExposureMode;
const CFStringRef kCGImagePropertyExifWhiteBalance;
const CFStringRef kCGImagePropertyExifDigitalZoomRatio;
const CFStringRef kCGImagePropertyExifFocalLenIn35mmFilm;
const CFStringRef kCGImagePropertyExifSceneCaptureType;
const CFStringRef kCGImagePropertyExifGainControl;
const CFStringRef kCGImagePropertyExifContrast;
const CFStringRef kCGImagePropertyExifSaturation;
const CFStringRef kCGImagePropertyExifSharpness;
const CFStringRef kCGImagePropertyExifDeviceSettingDescription;
const CFStringRef kCGImagePropertyExifSubjectDistRange;
const CFStringRef kCGImagePropertyExifImageUniqueID;
const CFStringRef kCGImagePropertyExifGamma;
const CFStringRef kCGImagePropertyExifCameraOwnerName;
const CFStringRef kCGImagePropertyExifBodySerialNumber;
const CFStringRef kCGImagePropertyExifLensSpecification;
const CFStringRef kCGImagePropertyExifLensMake;
const CFStringRef kCGImagePropertyExifLensModel;
const CFStringRef kCGImagePropertyExifLensSerialNumber;
这里的每一个key都有具体的含义,代表了jpg的一些信息。这些key和key的值时有严格的格式限定的,你用了自己定义的key或者不符合规则的值,是不能正确地写入jpg文件的(待会要提到如何写入metaData信息)。
如何用UIImagePickerController拍摄照片后加入信息呢?用这个控件拍照后,默认生成的metaData信息较少,只有拍摄时间等几个信息,没有gps信息,更没有街道信息,我们需要手动加入。
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
_mediaInfo =[NSMutableDictionarydictionaryWithDictionary:info];
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc]init];
[_locationManagersetDelegate:self];
[_locationManagersetDistanceFilter:kCLDistanceFilterNone];
[_locationManagersetDesiredAccuracy:kCLLocationAccuracyBest];
}
[_locationManagerstartUpdatingLocation];
[picker dismissViewControllerAnimated:YEScompletion:^
{
}];
}
#pragma mark CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
[manager stopUpdatingLocation];
UIImage *originalImage= (UIImage *) [_mediaInfoobjectForKey:UIImagePickerControllerOriginalImage];
NSData *imageNSData = UIImageJPEGRepresentation(originalImage,1);
CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageNSData, NULL);
//this is the type of image (e.g., public.jpeg)
CFStringRef UTI = CGImageSourceGetType(imgSource);
//this will be the data CGImageDestinationRef will write into
NSMutableData *newImageData = [NSMutableDatadata];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, UTI, 1, NULL);
if(!destination)
{
NSLog(@"***Could not create image destination ***");
return;
}
//get original metadata
NSDictionary *dict = [_mediaInfoobjectForKey:UIImagePickerControllerMediaMetadata];
NSMutableDictionary *metadata = [NSMutableDictionarydictionaryWithDictionary:dict];
//set gps info into metadata,we need a dictionary
NSDictionary * gpsDict=[newLocation GPSDictionary];
if (metadata && gpsDict) {
[metadata setValue:gpsDict forKey:(NSString*)kCGImagePropertyGPSDictionary];
}
//get location detail by CLGeocoder, this needs wifi
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error){
if(error != nil)
{
NSLog(@"CLGeocoder error :%@ ",error);
}
else//if we can get place info ,we set it into meteData dic with kCGImagePropertyExifCameraOwnerName key
{
if(placemarks.count > 0)
{
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSMutableDictionary *eifDic = (NSMutableDictionary *)[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary];
[eifDic setObject:placemark.nameforKey:(NSString *)kCGImagePropertyExifCameraOwnerName];
}
}
//add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge CFDictionaryRef) metadata);
BOOL success = NO;
success = CGImageDestinationFinalize(destination);
if(!success) {
NSLog(@"***Could not create data from image destination ***");
return ;
}
CFRelease(imgSource);
CFRelease(destination);
[self sendData:newImageData];
[self writeTest:newImageData];
}];
}
@implementation CLLocation (GPSDictionary)
-(NSDictionary*)GPSDictionary{
NSDateFormatter *formatter = [[NSDateFormatteralloc] init];
[formatter setDateFormat:@"hh:mm:ss.SS"];
CLLocation *location=self;
NSDictionary *gpsDict = [NSDictionarydictionaryWithObjectsAndKeys:
[NSNumbernumberWithFloat:fabs(location.coordinate.latitude)], kCGImagePropertyGPSLatitude,
((location.coordinate.latitude >= 0) ? @"N" : @"S"), kCGImagePropertyGPSLatitudeRef,
[NSNumbernumberWithFloat:fabs(location.coordinate.longitude)], kCGImagePropertyGPSLongitude,
((location.coordinate.longitude >= 0) ? @"E" : @"W"), kCGImagePropertyGPSLongitudeRef,
[formatter stringFromDate:[location timestamp]], kCGImagePropertyGPSTimeStamp,
nil];
NSLog(@"gpsDict i %@",gpsDict);
return gpsDict;
}
@end
上面代码需要手动改改,它的最终目的就是用 CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge CFDictionaryRef) metadata);函数用新的metaData信息生成新的jpg数据。
其中gps数据用了系统自定义的key值写入,街道信息没有找到合适的key,就冒用了kCGImagePropertyExifCameraOwnerName这个key。
iOS 拍照中加入GPS和具体地理位置的更多相关文章
- [转]iOS开发中的火星坐标系及各种坐标系转换算法
iOS开发中的火星坐标系及各种坐标系转换算法 源:https://my.oschina.net/u/2607703/blog/619183 其原理是这样的:保密局开发了一个系统,能将实际的坐标转 ...
- iOS UIWebView中javascript与Objective-C交互、获取摄像头
UIWebView是iOS开发中常用的一个视图控件,多数情况下,它被用来显示HTML格式的内容. 支持的文档格式 除了HTML以外,UIWebView还支持iWork, Office等文档格式: Ex ...
- iOS开发中图片方向的获取与更改
iOS开发中 再用到照片的时候 或多或少遇到过这样的问题 就是我想用的照片有横着拍的有竖着排的 所以导致我选取图片后的效果也横七竖八的 显示效果不好 比如: 图中红圈选中的图片选取的是横着拍 ...
- iOS开发中的零碎知识点笔记 韩俊强的博客
每日更新关注:http://weibo.com/hanjunqiang 新浪微博 1.关联 objc_setAssociatedObject关联是指把两个对象相互关联起来,使得其中的一个对象作为另外 ...
- IOS工作中的问题(转)
1.UITableView的scrollDelegate问题 下午遇到一个奇怪的问题,之前都没有注意过,由于A VC中要实现tableView和其他View位置的联动,所以实现了tableView的d ...
- iOS拍照定制之AVCaptureVideoDataOutput
问题 领导看了前面做的拍照,问了句"哪来的声音", "系统的,自带的,你看系统的拍照也有声音" "有办法能去掉吗?挺糟心的" "我 ...
- 总结iOS开发中的断点续传那些事儿
前言 断点续传概述 断点续传就是从文件赏赐中断的地方重新开始下载或者上传数据,而不是从头文件开始.当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会从头下载,这样很 ...
- iOS开发中静态库之".framework静态库"的制作及使用篇
iOS开发中静态库之".framework静态库"的制作及使用篇 .framework静态库支持OC和swift .a静态库如何制作可参照上一篇: iOS开发中静态库之" ...
- iOS开发中静态库制作 之.a静态库制作及使用篇
iOS开发中静态库之".a静态库"的制作及使用篇 一.库的简介 1.什么是库? 库是程序代码的集合,是共享程序代码的一种方式 2.库的类型? 根据源代码的公开情况,库可以分为2种类 ...
随机推荐
- Mysql-提示java.sql.SQLException: Cannot convert value '0000-00-00 00:00:00' from column 7 to TIMESTAMP.
在Mysql数据库中使用DATETIME类型来存储时间,使用JDBC中读取这个字段的时候,应该使用 ResultSet.getTimestamp(),这样会得到一个java.sql.Timestamp ...
- Java-TreeSet
如下: package 集合类.Set类; /** * Set不允许重复数据 */ /** * TreeSet 是用来进行集合排序的,请注意他和LinkedHashSet的区别. TreeSet是按照 ...
- .map文件的作用以及在chorme下会报错找不到jquery-1.10.2.min.map文件,404 的原因
source map文件是js文件压缩后,文件的变量名替换对应.变量所在位置等元信息数据文件,一般这种文件和min.js主文件放在同一个目录下. 比如压缩后原变量是map,压缩后通过变量替换规则可能会 ...
- 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))
函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...
- 【bzoj4010】 HNOI2015—菜肴制作
http://www.lydsy.com/JudgeOnline/problem.php?id=4010 (题目链接) 题意 给出一张无向图要求出一个拓扑序列满足1的位置最靠前 ,在保证上面的条件下使 ...
- 解决:Angular-cli:执行ng-build --prod后,dist文件里无js文件、文件未压缩等问题
Angular2.0于2016年9月上线,我于9月入坑. 入坑以来,一直让我很困惑的问题 1.angular-cli是个什么鬼东西? 2.为什么我们自己的资源文件还没写什么,就有起码50多个js文件加 ...
- RedGate .NET Reflector注册问题(反注册)
Reflector分为桌面版和VS集成版本,当我们使用注册机注册的时候如果注册了Standvard版本,那么我们的VS就不能集成查看,也不能Debug,那么这 显然不是我们想要的,我们会选择重新注册, ...
- ARP协议格式、ARP运行机制入门学习
相关学习资料 http://baike.baidu.com/view/149421.htm?fromtitle=ARP%E5%8D%8F%E8%AE%AE&fromid=1742212& ...
- 编译java文件,出错:Failed to establish a connection with the target VM
helloword程序,所有java学习人员人生第一个程序,哎妈,基础太差,出错 public class Helloword{ public Helloword() { public static ...
- Linux mount/unmount命令(转)
格式:mount [-参数] [设备名称] [挂载点] 其中常用的参数有:-a 安装在/etc/fstab文件中类出的所有文件系统.-f 伪装mount,作出检查设备和目录的样子,但并不真正挂载文件系 ...