<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" gd:etag="W/&quot;A0EEQXo6eSp7ImA9WhRQFk8.&quot;"><id>tag:blogger.com,1999:blog-5055301227416552647</id><updated>2011-12-11T10:53:20.411-08:00</updated><category term="picasa" /><category term="objective c" /><category term="iphone" /><category term="geotagging" /><category term="flickr" /><category term="geotag" /><category term="exif" /><title>iphoneland</title><subtitle type="html">A blog about iPhone development and useful tips and techniques</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://iphone-land.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://iphone-land.blogspot.com/" /><author><name>steve woodcock</name><uri>http://www.blogger.com/profile/10359417248202531146</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>2</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/blogspot/SYrj" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="blogspot/syrj" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DU4DR3k7eCp7ImA9WxdXEU4.&quot;"><id>tag:blogger.com,1999:blog-5055301227416552647.post-7964290528774289032</id><published>2008-06-21T16:07:00.000-07:00</published><updated>2008-06-22T05:32:56.700-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-06-22T05:32:56.700-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="geotagging" /><category scheme="http://www.blogger.com/atom/ns#" term="exif" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><category scheme="http://www.blogger.com/atom/ns#" term="objective c" /><title>Geo Tagging images on the iPhone using EXIF (PART II)</title><content type="html">&lt;div&gt;&lt;blockquote&gt;&lt;/blockquote&gt;In the first part we briefly examined the iphone-exif library API. In this part we will address the wider API, covering the GPS tags and the use of custom handlers to read/write nonstandard data or deal with specific bespoke formats.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;GPS Tags&lt;/div&gt;&lt;div&gt;The GPS tags in the EXIF spec are represented as follows:&lt;/div&gt;&lt;div&gt;&lt;div style="text-align: left;"&gt;&lt;blockquote&gt;EXIF_GPSLatitudeRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;    &lt;br /&gt;EXIF_GPSLatitude&lt;span class="Apple-tab-span" style="white-space:pre"&gt;    &lt;/span&gt;       &lt;br /&gt;EXIF_GPSLongitudeRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;     &lt;br /&gt;EXIF_GPSLongitude&lt;span class="Apple-tab-span" style="white-space:pre"&gt;    &lt;/span&gt;        &lt;br /&gt;EXIF_GPSAltitudeRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;     &lt;br /&gt;EXIF_GPSAltitude&lt;span class="Apple-tab-span" style="white-space:pre"&gt;    &lt;/span&gt;          &lt;br /&gt;EXIF_GPSTimeStamp&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;     &lt;br /&gt;EXIF_GPSSatellites&lt;span class="Apple-tab-span" style="white-space:pre"&gt;    &lt;/span&gt;   &lt;br /&gt;EXIF_GPSStatus&lt;span class="Apple-tab-span" style="white-space:pre"&gt;     &lt;/span&gt;       &lt;br /&gt;EXIF_GPSMeasureMode&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;         &lt;br /&gt;EXIF_GPSDOP&lt;span class="Apple-tab-span" style="white-space:pre"&gt;     &lt;/span&gt;             &lt;br /&gt;EXIF_GPSSpeedRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;    &lt;/span&gt;     &lt;br /&gt;EXIF_GPSSpeed&lt;span class="Apple-tab-span" style="white-space:pre"&gt;     &lt;/span&gt;           &lt;br /&gt;EXIF_GPSTrackRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;    &lt;/span&gt;        &lt;br /&gt;EXIF_GPSTrack&lt;span class="Apple-tab-span" style="white-space:pre"&gt;     &lt;/span&gt;                      &lt;br /&gt;EXIF_GPSImgDirectionRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;  &lt;/span&gt;    &lt;br /&gt;EXIF_GPSImgDirection&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;      &lt;br /&gt;EXIF_GPSMapDatum&lt;span class="Apple-tab-span" style="white-space:pre"&gt;    &lt;/span&gt;          &lt;br /&gt;EXIF_GPSDestLatitudeRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;  &lt;/span&gt;      &lt;br /&gt;EXIF_GPSDestLatitude&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;           &lt;br /&gt;EXIF_GPSDestLongitudeRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;  &lt;/span&gt;    &lt;br /&gt;EXIF_GPSDestLongitude&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;    &lt;br /&gt;EXIF_GPSDestBearingRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;   &lt;br /&gt;EXIF_GPSDestBearing&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;         &lt;br /&gt;EXIF_GPSDestDistanceRef&lt;span class="Apple-tab-span" style="white-space:pre"&gt;  &lt;/span&gt;      &lt;br /&gt;EXIF_GPSDestDistance&lt;span class="Apple-tab-span" style="white-space:pre"&gt;   &lt;/span&gt;    &lt;/blockquote&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;The actual location data is generally represented as a pair of tags with the xxxRef tag representing a modifier for the corresponding value tag. For instance the GPSLongitudeRef and GPSLongitude tag together represent the longitude value.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The longitude and latitude tag data (and indeed all rational number types) in the Exif spec is defined as a block of rational numbers. In this case, one for degrees, minutes and seconds. While this sounds reasonable the spec goes on to say that each rational number is actually stored as a fraction comprising two longs rather than a floating point number. So, the iPhone representation as a single decimal must be converted to and from this block of longs.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The library models this exif format as a GPS location class, which contains three member variables of a Fraction type.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;@interface EXFGPSLoc : NSObject {&lt;/div&gt;&lt;div&gt; EXFraction* degrees;&lt;/div&gt;&lt;div&gt; EXFraction* minutes;&lt;/div&gt;&lt;div&gt; EXFfraction* seconds&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;The class provides a method to output the normal string representation in our expected form (122° 1' 50.6316") to the GUI but the fraction structure was adopted internally, rather than say doubles, so that we can round trip the values in any existing EXIF GPS Tags without losing any precision.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While this is fine in principle it can be a little awkward in practice. In order to set the GPS data the main the programming task is to construct an instance of a GPSLoc class and use this as the data type to pass into the exif Library e.g.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;// Helper methods for location conversion&lt;/div&gt;&lt;div&gt;-(NSMutableArray*) createLocArray:(double) val{&lt;/div&gt;&lt;div&gt; val = fabs(val);&lt;/div&gt;&lt;div&gt; NSMutableArray* array = [[NSMutableArray alloc] init];&lt;/div&gt;&lt;div&gt; double deg = (int)val;&lt;/div&gt;&lt;div&gt; [array addObject:[NSNumber numberWithDouble:deg]];&lt;/div&gt;&lt;div&gt; val = val - deg;&lt;/div&gt;&lt;div&gt; val = val*60;&lt;/div&gt;&lt;div&gt; double minutes = (int) val;&lt;/div&gt;&lt;div&gt; [array addObject:[NSNumber numberWithDouble:minutes]];&lt;/div&gt;&lt;div&gt; val = val - minutes;&lt;/div&gt;&lt;div&gt; val = val *60;&lt;/div&gt;&lt;div&gt; double seconds = val;&lt;/div&gt;&lt;div&gt; [array addObject:[NSNumber numberWithDouble:seconds]];&lt;/div&gt;&lt;div&gt; return array;&lt;/div&gt;&lt;div&gt;} &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-(void) populateGPS: (EXFGPSLoc*)gpsLoc :(NSArray*) locArray{&lt;/div&gt;&lt;div&gt; long numDenumArray[2];&lt;/div&gt;&lt;div&gt; long* arrPtr = numDenumArray;&lt;/div&gt;&lt;div&gt; [EXFUtils convertRationalToFraction:&amp;amp;arrPtr :[locArray objectAtIndex:0]];&lt;/div&gt;&lt;div&gt; EXFraction* fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]];&lt;/div&gt;&lt;div&gt; gpsLoc.degrees = fract;&lt;/div&gt;&lt;div&gt; [fract release];&lt;/div&gt;&lt;div&gt; [EXFUtils convertRationalToFraction:&amp;amp;arrPtr :[locArray objectAtIndex:1]];&lt;/div&gt;&lt;div&gt; fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]];&lt;/div&gt;&lt;div&gt; gpsLoc.minutes = fract;&lt;/div&gt;&lt;div&gt; [fract release];&lt;/div&gt;&lt;div&gt; [EXFUtils convertRationalToFraction:&amp;amp;arrPtr :[locArray objectAtIndex:2]];&lt;/div&gt;&lt;div&gt; fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]];&lt;/div&gt;&lt;div&gt; gpsLoc.seconds = fract;&lt;/div&gt;&lt;div&gt; [fract release]   &lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;// end of helper methods&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;// adding GPS data to the Exif object&lt;/div&gt;&lt;div&gt; NSMutableArray* locArray = [self createLocArray:location.coordinate.latitude];&lt;/div&gt;&lt;div&gt; EXFGPSLoc* gpsLoc = [[EXFGPSLoc alloc] init];&lt;/div&gt;&lt;div&gt; [self populateGPS: gpsLoc :locArray];&lt;/div&gt;&lt;div&gt; [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLatitude] ];&lt;/div&gt;&lt;div&gt; [gpsLoc release];&lt;/div&gt;&lt;div&gt; [locArray release];&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the above code we are converting the  iPhone representation of the location.coordinate into an array representation of degrees, minutes and seconds, then populating the GPSLoc object and setting this into the exifMetaData object. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The createLocArray method first converts the decimal representation of the latitude from the iphone, which looks something like -122.030731, to an array of NSNumbers. These are then passed to the populateGPS method which converts each NSNumber to a fraction and sets this into the GPSLoc instance. This is a simple process but a little tedious. Note: the conversion of each of these numbers to a fraction in the EXFUtils class uses a standard Euclidean GCD method.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The ref part of the tag pair is dealt with as a String e.g &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt; location.coordinate.latitude;&lt;/div&gt;&lt;div&gt; if (location.coordinate.latitude &lt;0.0){&lt;/div&gt;&lt;div&gt; ref = @"S";&lt;/div&gt;&lt;div&gt; }else{&lt;/div&gt;&lt;div&gt; ref =@"N";&lt;/div&gt;&lt;div&gt; }&lt;/div&gt;&lt;div&gt;[exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLatitudeRef] ];&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;Of course, this means that you have to have some knowledge of the Exif GPS tags, but the library shields you from the nitty-gritty of the underlying byte formats. Generally this is not too much of a problem, once you get the conversion routines written.&lt;/div&gt;&lt;div&gt;Custom Tag Handlers&lt;/div&gt;&lt;div&gt;Mostly, You do not have to deal with custom handlers, as the tags are either Numeric (Single Byte, Short, Long), a String of ASCII or a Rational number (as a fraction). Some tags are specified as arrays of each of these types and the library takes this into account.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The type to pass to the Library can easily be determined by examining the Tag definition for the TagId we are interested in e.g&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;@interface EXFTag : NSObject {     &lt;/div&gt;&lt;div&gt;    EXFTagId tagId;&lt;/div&gt;&lt;div&gt;    EXFDataType dataType;&lt;/div&gt;&lt;div&gt;    int parentTagId;&lt;/div&gt;&lt;div&gt;    NSString* name;&lt;/div&gt;&lt;div&gt;    BOOL editable;&lt;/div&gt;&lt;div&gt;    int components;  &lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The data type will be one of:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;enum EXFDataType {&lt;/div&gt;&lt;div&gt;    FMT_BYTE =       1,&lt;/div&gt;&lt;div&gt;    FMT_STRING  =    2,&lt;/div&gt;&lt;div&gt;    FMT_USHORT  =    3,&lt;/div&gt;&lt;div&gt;    FMT_ULONG   =    4,&lt;/div&gt;&lt;div&gt;    FMT_URATIONAL  = 5,&lt;/div&gt;&lt;div&gt;    FMT_SBYTE      = 6,&lt;/div&gt;&lt;div&gt;    FMT_UNDEFINED  = 7,&lt;/div&gt;&lt;div&gt;    FMT_SSHORT     = 8,&lt;/div&gt;&lt;div&gt;    FMT_SLONG      = 9,&lt;/div&gt;&lt;div&gt;    FMT_SRATIONAL  =10,&lt;/div&gt;&lt;div&gt;    FMT_SINGLE     =11,&lt;/div&gt;&lt;div&gt;    FMT_DOUBLE     =12&lt;/div&gt;&lt;div&gt;};&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For the numeric types you must pass a numeric instance that is coercible to that type, the S prefix is for signed values and the single and double are not really present in the spec, but are representable as rational numbers. In order to work out if the data type is an array we examine the components entry in the tag definition, if the value is greater than one then that is how many slots in the array we must pass.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The only real problem is the undefined type. This basically means that the data can be of any type nad is usually used for data that is related to manufacturer data or where the numeric types don't provide the correct formats. In this case we can use an NSData instance and process it ourselves or we can register a handler with the library to do it for us.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The handler protocol is defined as:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;@protocol EXFTagHandler&lt;/div&gt;&lt;div&gt;-(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-(int) getSizeOfValue:(id)value;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-(BOOL)supportsValueType:(id) value;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@optional&lt;/div&gt;&lt;div&gt;-(int) tagFormat;&lt;/div&gt;&lt;div&gt;-(int) parentTagId;&lt;/div&gt;&lt;div&gt;-(BOOL) isEditable;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;@end&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The decode and encode methods are called by the library when it is constructing an instance from the JPEG bytes or adding the byte representation of the data into the JPEG byte format. The sizeof and supports are used to enable the library to test if an actual value can be supported by the handler and what the encoded byte size of the data would be.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For instance, the internal custom handler that deals with ASCII tags looks like:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;@implementation EXFASCIIHandler&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder{&lt;/div&gt;&lt;div&gt;    &lt;/div&gt;&lt;div&gt;    UInt8* ptr = (UInt8*) CFDataGetBytePtr(*tagData);&lt;/div&gt;&lt;div&gt;    CFIndex byteLength = CFDataGetLength(*tagData);&lt;/div&gt;&lt;div&gt;    NSString* value = [EXFUtils createStringFromBuffer:&amp;amp;ptr: byteLength: NSASCIIStringEncoding];&lt;/div&gt;&lt;div&gt;    // Debug(@"Assigned string %@",value);&lt;/div&gt;&lt;div&gt;    &lt;/div&gt;&lt;div&gt;    [keyedValues setObject: value forKey: tagId];&lt;/div&gt;&lt;div&gt;    &lt;/div&gt;&lt;div&gt;    [value release];&lt;/div&gt;&lt;div&gt;    &lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder{&lt;/div&gt;&lt;div&gt;    // tag data is an array of NSNumber&lt;/div&gt;&lt;div&gt;    int length = [((NSString*)tagData) lengthOfBytesUsingEncoding:NSASCIIStringEncoding];&lt;/div&gt;&lt;div&gt;    const char* cString = [((NSString*)tagData) cStringUsingEncoding:NSASCIIStringEncoding];&lt;/div&gt;&lt;div&gt;    [targetBuffer appendBytes: cString length:length];&lt;/div&gt;&lt;div&gt;    &lt;/div&gt;&lt;div&gt;    &lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-(BOOL)supportsValueType:(id) value{&lt;/div&gt;&lt;div&gt;    &lt;/div&gt;&lt;div&gt;    if ([value isMemberOfClass:[NSString class]]){&lt;/div&gt;&lt;div&gt;        return TRUE;&lt;/div&gt;&lt;div&gt;    }else{&lt;/div&gt;&lt;div&gt;        return FALSE;&lt;/div&gt;&lt;div&gt;    }&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-(int) getSizeOfValue:(id)value{&lt;/div&gt;&lt;div&gt;    // value should be a GPS Loc&lt;/div&gt;&lt;div&gt;    if ([value isKindOfClass:[NSString class]]){&lt;/div&gt;&lt;div&gt;        &lt;/div&gt;&lt;div&gt;        return[((NSString*)value) lengthOfBytesUsingEncoding:NSASCIIStringEncoding];&lt;/div&gt;&lt;div&gt;    }else{&lt;/div&gt;&lt;div&gt;        return -1;&lt;/div&gt;&lt;div&gt;    }&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-(NSString*) description{&lt;/div&gt;&lt;div&gt;    return @"EXF ASCII Handler";&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;@end&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The tag handler is registered using the following call prior to any tag values being set.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt; &lt;blockquote&gt; [exifMetadata addHandler:asciiHandler :EXIF_ExifVersion];&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;Once we have set the data into the exif Object we must obviously be able to retrieve the JPEG data including the tags we have altered. This is achieved in a single call using the following method:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;-(void) populateImageData: (NSMutableData*) newImageData;&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The programmer must provide an NSMutableData instance to the library which will be filled with the bytes of the resulting JPEG with the amended EXIF data. This can then be saved or uploaded to the desired site.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5055301227416552647-7964290528774289032?l=iphone-land.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://iphone-land.blogspot.com/feeds/7964290528774289032/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5055301227416552647&amp;postID=7964290528774289032" title="33 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5055301227416552647/posts/default/7964290528774289032?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5055301227416552647/posts/default/7964290528774289032?v=2" /><link rel="alternate" type="text/html" href="http://iphone-land.blogspot.com/2008/06/in-first-part-we-briefly-examined.html" title="Geo Tagging images on the iPhone using EXIF (PART II)" /><author><name>steve woodcock</name><uri>http://www.blogger.com/profile/10359417248202531146</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>33</thr:total></entry><entry gd:etag="W/&quot;DUYCSXs-eyp7ImA9WxdXFEw.&quot;"><id>tag:blogger.com,1999:blog-5055301227416552647.post-3395999670839524616</id><published>2008-06-19T08:05:00.000-07:00</published><updated>2008-06-25T11:06:08.553-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-06-25T11:06:08.553-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="picasa" /><category scheme="http://www.blogger.com/atom/ns#" term="flickr" /><category scheme="http://www.blogger.com/atom/ns#" term="geotag" /><category scheme="http://www.blogger.com/atom/ns#" term="iphone" /><title>Geo Tagging images using iphone-exif</title><content type="html">&lt;blockquote&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;The GeoTagging of photos can be really useful. Flickr and Picasa can both use Exif data embedded into an image to generate the Map locations displayed on the site. The only downside is that it is quite tricky to do this directly on the phone.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There are a number of solutions to do this on the PC or by uploading to the site and then tag each image or image folder with  a location, but it is another step when you really want to do it all at once.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I ended up creating a static C/Objective-C library that allows reading/editing and deleting standard Exif tags directly on images on your iPhone. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is a quick guide to getting it set up in your project. The Exif spec is a bit tedious and can be found at &lt;a href="http://www.exif.org/Exif2-2.PDF"&gt;www.exif.org/Exif2-2.PDF&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Add Into Xcode&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Download the library from &lt;a href="http://code.google.com/p/iphone-exif/"&gt;code.google.com/p/iphone-exif&lt;/a&gt;  and unzip into a directory. The zip file contains a static library which you must add into the project as a dependency. then place the header files into your source directory and you are good to go. This is not a dynamic library as the application must ship with the code embedded, it will not be on the iPhone to link with like the framework files you use.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Getting Started&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;NOTE: The 0.8 version of the library expects a logging variable to be declared - just add it into the AppDelegate .m class like so:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco"&gt;&lt;span style="color: #aa0d91"&gt;BOOL&lt;/span&gt; gLogging = &lt;span style="color: #aa0d91"&gt;FALSE&lt;/span&gt;;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;&lt;span class="Apple-style-span" style="font-family: 'times new roman';"&gt;this controls whether internal log statements are echoed to standard out.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Monaco"&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;The initial task is to parse the Jpeg file data to extrac the Exif data.&lt;/div&gt;&lt;div&gt;This we achieve in the following lines&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;NSData* theImageData = UIImageRepresentation(anImage,1.0);&lt;/div&gt;&lt;div&gt;EXFJpeg* jpegScanner = [[EXFJpeg alloc] init];&lt;/div&gt;&lt;div&gt;[jpegScanner scanImageData: theImageData];&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This will scan the image and extract any Exif data. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The ExifJpeg class gives us an interface that provides access to the Exif data and the JFIF tags using the properties:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;EXFMetaData* exifData = jpegScanner.exifMetaData;&lt;/div&gt;&lt;div&gt;EXFJFIF* jfif = jpegScanner.jfif;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The EXFMetaData class is where most of the work is done. It allows us to &lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;retrieve a tag definition&lt;/li&gt;&lt;li&gt;get a list of child tags&lt;/li&gt;&lt;li&gt;get a value for a tag&lt;/li&gt;&lt;li&gt;remove a tag value&lt;/li&gt;&lt;li&gt;add a remove a custom handler&lt;/li&gt;&lt;li&gt;get a list of all the tag values.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Retrieving a tag definition&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The tag definition details the id, data type, short string name, parent tag, and the number of components (see the Exif Spec for details) it can occupy.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;EXFTag* tagDefinition = [exifData tagDefinition: aTagId];&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Getting a list of child tags for a parent&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;NSMutableArray* tagDefinitions = [exifData tagDefinitionsForParent:aTagId];&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Getting a value for a tag&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The value is an id as it could be a Numeric value, a String, a binary type, or a type provided by a custom tag handler.&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;id tagValue = [exifData tagValue: aTagId];&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Add a tag value&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;[exifData addTagValue: aValue forKey: aTagId];&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Remove a tag value&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;[exifData removeTagValue:aTagId];&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;There is obviously a requirement to know what the tagIds are, and these are available in the spec. The EXFConstants.h file also has a defined set of constants to make it easy to see what these are.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;Part II&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The second post will deal with adding custom handlers and a discussion of the data types, especially around the GPS tag set.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5055301227416552647-3395999670839524616?l=iphone-land.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://iphone-land.blogspot.com/feeds/3395999670839524616/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=5055301227416552647&amp;postID=3395999670839524616" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5055301227416552647/posts/default/3395999670839524616?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5055301227416552647/posts/default/3395999670839524616?v=2" /><link rel="alternate" type="text/html" href="http://iphone-land.blogspot.com/2008/06/geo-tagging-images-using-iphone-exif.html" title="Geo Tagging images using iphone-exif" /><author><name>steve woodcock</name><uri>http://www.blogger.com/profile/10359417248202531146</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>8</thr:total></entry></feed>

