Saturday, June 21, 2008

Geo Tagging images on the iPhone using EXIF (PART II)

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.

GPS Tags
The GPS tags in the EXIF spec are represented as follows:
EXIF_GPSLatitudeRef     
EXIF_GPSLatitude        
EXIF_GPSLongitudeRef      
EXIF_GPSLongitude         
EXIF_GPSAltitudeRef      
EXIF_GPSAltitude           
EXIF_GPSTimeStamp      
EXIF_GPSSatellites    
EXIF_GPSStatus        
EXIF_GPSMeasureMode          
EXIF_GPSDOP              
EXIF_GPSSpeedRef      
EXIF_GPSSpeed            
EXIF_GPSTrackRef         
EXIF_GPSTrack                       
EXIF_GPSImgDirectionRef     
EXIF_GPSImgDirection       
EXIF_GPSMapDatum           
EXIF_GPSDestLatitudeRef       
EXIF_GPSDestLatitude            
EXIF_GPSDestLongitudeRef     
EXIF_GPSDestLongitude     
EXIF_GPSDestBearingRef    
EXIF_GPSDestBearing          
EXIF_GPSDestDistanceRef       
EXIF_GPSDestDistance     



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.

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.

The library models this exif format as a GPS location class, which contains three member variables of a Fraction type.
@interface EXFGPSLoc : NSObject {
 EXFraction* degrees;
 EXFraction* minutes;
 EXFfraction* seconds
}
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.

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.

// Helper methods for location conversion
-(NSMutableArray*) createLocArray:(double) val{
 val = fabs(val);
 NSMutableArray* array = [[NSMutableArray alloc] init];
 double deg = (int)val;
 [array addObject:[NSNumber numberWithDouble:deg]];
 val = val - deg;
 val = val*60;
 double minutes = (int) val;
 [array addObject:[NSNumber numberWithDouble:minutes]];
 val = val - minutes;
 val = val *60;
 double seconds = val;
 [array addObject:[NSNumber numberWithDouble:seconds]];
 return array;

-(void) populateGPS: (EXFGPSLoc*)gpsLoc :(NSArray*) locArray{
 long numDenumArray[2];
 long* arrPtr = numDenumArray;
 [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:0]];
 EXFraction* fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]];
 gpsLoc.degrees = fract;
 [fract release];
 [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:1]];
 fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]];
 gpsLoc.minutes = fract;
 [fract release];
 [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:2]];
 fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]];
 gpsLoc.seconds = fract;
 [fract release]   
}
// end of helper methods

// adding GPS data to the Exif object
 NSMutableArray* locArray = [self createLocArray:location.coordinate.latitude];
 EXFGPSLoc* gpsLoc = [[EXFGPSLoc alloc] init];
 [self populateGPS: gpsLoc :locArray];
 [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLatitude] ];
 [gpsLoc release];
 [locArray release];

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. 

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.


The ref part of the tag pair is dealt with as a String e.g 

 location.coordinate.latitude;
 if (location.coordinate.latitude <0.0){
 ref = @"S";
 }else{
 ref =@"N";
 }
[exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLatitudeRef] ];

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.
Custom Tag Handlers
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.

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

@interface EXFTag : NSObject {     
    EXFTagId tagId;
    EXFDataType dataType;
    int parentTagId;
    NSString* name;
    BOOL editable;
    int components;  
}

The data type will be one of:

enum EXFDataType {
    FMT_BYTE =       1,
    FMT_STRING  =    2,
    FMT_USHORT  =    3,
    FMT_ULONG   =    4,
    FMT_URATIONAL  = 5,
    FMT_SBYTE      = 6,
    FMT_UNDEFINED  = 7,
    FMT_SSHORT     = 8,
    FMT_SLONG      = 9,
    FMT_SRATIONAL  =10,
    FMT_SINGLE     =11,
    FMT_DOUBLE     =12
};

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.

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.

The handler protocol is defined as:

@protocol EXFTagHandler
-(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder;

-(int) getSizeOfValue:(id)value;

-(BOOL)supportsValueType:(id) value;

-(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder;

@optional
-(int) tagFormat;
-(int) parentTagId;
-(BOOL) isEditable;

@end

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.

For instance, the internal custom handler that deals with ASCII tags looks like:

 
@implementation EXFASCIIHandler

-(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder{
    
    UInt8* ptr = (UInt8*) CFDataGetBytePtr(*tagData);
    CFIndex byteLength = CFDataGetLength(*tagData);
    NSString* value = [EXFUtils createStringFromBuffer:&ptr: byteLength: NSASCIIStringEncoding];
    // Debug(@"Assigned string %@",value);
    
    [keyedValues setObject: value forKey: tagId];
    
    [value release];
    
}

-(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder{
    // tag data is an array of NSNumber
    int length = [((NSString*)tagData) lengthOfBytesUsingEncoding:NSASCIIStringEncoding];
    const char* cString = [((NSString*)tagData) cStringUsingEncoding:NSASCIIStringEncoding];
    [targetBuffer appendBytes: cString length:length];
    
    
}

-(BOOL)supportsValueType:(id) value{
    
    if ([value isMemberOfClass:[NSString class]]){
        return TRUE;
    }else{
        return FALSE;
    }
}

-(int) getSizeOfValue:(id)value{
    // value should be a GPS Loc
    if ([value isKindOfClass:[NSString class]]){
        
        return[((NSString*)value) lengthOfBytesUsingEncoding:NSASCIIStringEncoding];
    }else{
        return -1;
    }
}

-(NSString*) description{
    return @"EXF ASCII Handler";
}
@end

The tag handler is registered using the following call prior to any tag values being set.

 
 [exifMetadata addHandler:asciiHandler :EXIF_ExifVersion];
 
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:

 
-(void) populateImageData: (NSMutableData*) newImageData;

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.







33 comments:

Anonymous said...

What does the actual call look like for this statement?

EXFTag* tagDefinition = [exifData tagDefinition: aTagId];

What do you put for "aTagId" (let's say we want latitude, which is defined to be 0x0002)?

steve woodcock said...

Hi Vince,
I am not sure what you are asking. You want the TagDefinition for Latitude - or do you need to know ehat the actual call would be?

In this case the call would be
[exifData tagDefinition:[NSNumber numberWithInt:EXIF_GPSLatitude] ]


Be aware that this is not the value for the tag but the tag definition

which will give you:

[[EXFTag alloc] initWith:0x0002 : FMT_URATIONAL :@"GPSLatitude":0x8825: TRUE:3];

Anonymous said...

Right, thanks for clarifying. I don't need to use tagDefinition. I need to know how to retrieve the value for the tag. I couldn't follow it from your documentation, though I appreciate that you wrote it in the first place (I hate writing docs!).

Anonymous said...

Well, maybe I'm not clear on what your API does. You wrote in a post a couple of months ago (I'll quote it below) that it is not possible to get the EXIF data from the UIImage returned from the iPhone SDK ImagePicker. Is this still the case? If so, then I assume your API can only be used if you've first retrieved the entire image some other way (e.g., from the file system). Is that correct?

I ask because I turned on debugging and it looks like your code is parsing some tags, but maybe it would behave that way even if it was not actually parsing EXIF data.


2008-09-10 02:41:20.434 WhereAmI[5419:20b] DEBUG: Starting scan headers
2008-09-10 02:41:20.435 WhereAmI[5419:20b] DEBUG: Length of image 1692782
2008-09-10 02:41:20.435 WhereAmI[5419:20b] DEBUG: Got next marker e0 at byte count 4
2008-09-10 02:41:20.436 WhereAmI[5419:20b] DEBUG: Found app e0 at 4
2008-09-10 02:41:20.436 WhereAmI[5419:20b] DEBUG: Got length of comment of 16
2008-09-10 02:41:20.436 WhereAmI[5419:20b] DEBUG: comment data without length 2 bytes 14
2008-09-10 02:41:20.438 WhereAmI[5419:20b] DEBUG: Parsing JFIF APP_0 at 20
2008-09-10 02:41:20.439 WhereAmI[5419:20b] DEBUG: Got next marker e1 at byte count 22
2008-09-10 02:41:20.439 WhereAmI[5419:20b] DEBUG: Found app e1 at 22
2008-09-10 02:41:20.439 WhereAmI[5419:20b] DEBUG: Got length of comment of 76
2008-09-10 02:41:20.440 WhereAmI[5419:20b] DEBUG: comment data without length 2 bytes 74
2008-09-10 02:41:20.440 WhereAmI[5419:20b] DEBUG: Length of exif 74
2008-09-10 02:41:20.441 WhereAmI[5419:20b] DEBUG: Big endian type found for data
2008-09-10 02:41:20.441 WhereAmI[5419:20b] DEBUG: ********** Entering exif processing at offset 14 ********
2008-09-10 02:41:20.441 WhereAmI[5419:20b] DEBUG: Number of entries in block 2
2008-09-10 02:41:20.441 WhereAmI[5419:20b] DEBUG: Parsing tag 274 at location 16
2008-09-10 02:41:20.442 WhereAmI[5419:20b] DEBUG: Parsing tag 34665 at location 28
2008-09-10 02:41:20.442 WhereAmI[5419:20b] DEBUG: Nested pointer to 44 found for tag 34665 with bytecount 4
2008-09-10 02:41:20.444 WhereAmI[5419:20b] DEBUG: ********** Entering exif processing at offset 44 ********
2008-09-10 02:41:20.445 WhereAmI[5419:20b] DEBUG: Number of entries in block 2
2008-09-10 02:41:20.445 WhereAmI[5419:20b] DEBUG: Parsing tag 40962 at location 46
2008-09-10 02:41:20.446 WhereAmI[5419:20b] DEBUG: Parsing tag 40963 at location 58
2008-09-10 02:41:20.446 WhereAmI[5419:20b] DEBUG: Next Offset 0 at 70
2008-09-10 02:41:20.446 WhereAmI[5419:20b] DEBUG: ********** Leaving exif processing at 44 with nextOffset 0***********
2008-09-10 02:41:20.447 WhereAmI[5419:20b] DEBUG: Next Offset 0 at 40
2008-09-10 02:41:20.447 WhereAmI[5419:20b] DEBUG: ********** Leaving exif processing at 14 with nextOffset 0***********
2008-09-10 02:41:20.449 WhereAmI[5419:20b] DEBUG: Returning from parsing at 0
2008-09-10 02:41:20.449 WhereAmI[5419:20b] DEBUG: Finished App1 at 98
2008-09-10 02:41:20.450 WhereAmI[5419:20b] DEBUG: Got next marker db at byte count 100
2008-09-10 02:41:20.450 WhereAmI[5419:20b] DEBUG: NOt handled db skipping at 100
2008-09-10 02:41:20.450 WhereAmI[5419:20b] Skipping length 65
2008-09-10 02:41:20.451 WhereAmI[5419:20b] DEBUG: Got next marker db at byte count 169
2008-09-10 02:41:20.451 WhereAmI[5419:20b] DEBUG: NOt handled db skipping at 169
2008-09-10 02:41:20.451 WhereAmI[5419:20b] Skipping length 65
2008-09-10 02:41:20.452 WhereAmI[5419:20b] DEBUG: Got next marker c0 at byte count 238
2008-09-10 02:41:20.452 WhereAmI[5419:20b] Length in image info 15
2008-09-10 02:41:20.453 WhereAmI[5419:20b] Skipping length 9
2008-09-10 02:41:20.453 WhereAmI[5419:20b] DEBUG: Got next marker c4 at byte count 257
2008-09-10 02:41:20.453 WhereAmI[5419:20b] DEBUG: NOt handled c4 skipping at 257
2008-09-10 02:41:20.453 WhereAmI[5419:20b] Skipping length 29
2008-09-10 02:41:20.454 WhereAmI[5419:20b] DEBUG: Got next marker c4 at byte count 290
2008-09-10 02:41:20.454 WhereAmI[5419:20b] DEBUG: NOt handled c4 skipping at 290
2008-09-10 02:41:20.454 WhereAmI[5419:20b] Skipping length 179
2008-09-10 02:41:20.455 WhereAmI[5419:20b] DEBUG: Got next marker c4 at byte count 473
2008-09-10 02:41:20.455 WhereAmI[5419:20b] DEBUG: NOt handled c4 skipping at 473
2008-09-10 02:41:20.455 WhereAmI[5419:20b] Skipping length 29
2008-09-10 02:41:20.456 WhereAmI[5419:20b] DEBUG: Got next marker c4 at byte count 506
2008-09-10 02:41:20.456 WhereAmI[5419:20b] DEBUG: NOt handled c4 skipping at 506
2008-09-10 02:41:20.456 WhereAmI[5419:20b] Skipping length 179
2008-09-10 02:41:20.457 WhereAmI[5419:20b] DEBUG: Got next marker da at byte count 689
2008-09-10 02:41:20.457 WhereAmI[5419:20b] DEBUG: Found SOS at 689


Quoting:


Hi,
The actual problem is in the UIImagePicker API in the iPhone OS that all third party apps use to get at the photos you have stored.

So in a third party app (as well as the mail app it seems) - the app uses this API to delegate to Apple's standard screens to select a photo you have taken or saved with the geo tags correctly in place.

However, once you have chosen the image the application is handed back a handle to that image by the API, but this handle is a UIImage with a lot the data stripped out and the image scaled. There is no way to get the original Data for the image from this handle.

There is no work around for this if the app on the phone uses the standard SDK calls, the photo must be directly read from the file system in order to not be affected. This is strongly discouraged by Apple. Alternatively, you could download the image to your Mac/Pc and upload from there.

I suggest you file a bug report to allow the original file data to be retrieved by the iPhone native apps in this call.

steve woodcock said...

Hi Vince,
Maybe I should have been a bit more specific that it is not possible to get the Geo EXIF data (as well as a lot of other tags) from the API.

The debug shows the few tags that do come through (The lines that say Parsing Tag...):
Orientation of image: 274
Exif IFD Pointer 34665
PixelXDimension 40962
PixelYDimension 40963

(And in this set the EXIF IFD is a container for the X/Y dimensions).

So all you get is really orientation and Dimensions.

The rest of the output is expected and is the rest of the JPEG Meta data blocks that the library ignores.

The aim of the API is then that you can inject your own location and other tags into the image, and if Apple finally addresses the defect - you can edit the existing ones.

If you pull in the image directly then most of the tags are available.

If you have any other questions let me know

Anonymous said...

Thanks very much! I get it now. It's too bad that they strip that stuff out. I will do as you suggested and file a bug report. In the mean time, as an experiment (I'm very new to the iPhone SDK, as you might have guessed :>), I'll roll my own picker.

Anonymous said...

I`m make iphone-exif work in my project but only with files from filesystem, when I`m try to use it with images from UIImagePickermethod:

- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo

it get allways null values, don`t work.
Do you know why?
I`m trying to get location from an picture taken from my app and from images of the album.

Thank you

Unknown said...

Thanks a lot for the code on EXIF. I would really appreciate if there is a project that can help us see the code in action.

Andy said...

Thanks a lot for this library, I'm trying it in a small project and it's working like a charm!
I've been able to retreive everything, even the location data (although this is a little cumbersome, as you pointed out... maybe would be helpful to add a helper method to retrieve directly in decimal numbers: currently I do calculations directly in my code classes, but I will move the code in a separate function, I will share this if you like. I know we lose precision, but for me is not a problem as i read only data, I do not write back.

I have a question on a warning I get: I've tried many different images, and in every single image I get always the same NSLog: "***Warning 'EXIF' string not present at start". No image I have does not generate this. I had a look at the code, but could not really get what's the problem with this... everything seems to work flawlessly despite this warning... What's wrong with this?

Thanks again for sharing

steve woodcock said...

Hi Andy,
If I get time I will add the conversion routines - but at the moment I am full time on another project.

As to the warning is just a logging statement as most devices do not seem to correctly deal with the spec - you can ignore this and I should really turn the level down to debug on this.

Unknown said...

hey Andy,
Good that it works for you! So you say "I will share this if you like." ...I would really like to see your implementation if you don't mind. I am not able to maneuver the gps code yet. Thanks! Erastus

andy said...

Yes, I try to post it here. Consider that this is a very simple code: I followed a logical flow, but it is a first try, maybe it should be reworked a little bit to optimize and get a fancy code, I don't know and since this is actually working fine, for now I just keep this. I will simply move this in a class method, having a EXFscanner as input and returning a CLLocationCoordinate2D.
This comment page has a curios page format, anyway if you copy/paste this in xcode the indentation should be preserved I hope.
In the end of the procedure, my GPSLocation variable which is a CLLocationCoordinate2D is correctly filled.

EXFGPSLoc *latitude = [[scanner exifMetaData] tagValue:[NSNumber numberWithInt:EXIF_GPSLatitude]];
if (latitude) {
NSString *latitudeRef = [[scanner exifMetaData] tagValue:[NSNumber numberWithInt:EXIF_GPSLatitudeRef]];
EXFGPSLoc *longitude = [[scanner exifMetaData] tagValue:[NSNumber numberWithInt:EXIF_GPSLongitude]];
NSString *longitudeRef = [[scanner exifMetaData] tagValue:[NSNumber numberWithInt:EXIF_GPSLongitudeRef]];

if (latitude != nil && longitude != nil) {
EXFraction *fract = latitude.degrees;
double degrees = fract.numerator/fract.denominator;
fract = latitude.minutes;
double minutes = fract.numerator/fract.denominator;
fract = latitude.seconds;
double seconds = (double)fract.numerator/(double)fract.denominator;

gpsLocation.latitude = degrees + (minutes / 60) + (seconds / 3600);
if ([latitudeRef characterAtIndex:0] == 'S')
gpsLocation.latitude = 0 - gpsLocation.latitude;

fract = longitude.degrees;
degrees = fract.numerator/fract.denominator;
fract = longitude.minutes;
minutes = fract.numerator/fract.denominator;
fract = longitude.seconds;
seconds = (double)fract.numerator/(double)fract.denominator;

gpsLocation.longitude = degrees + (minutes / 60) + (seconds / 3600);
if ([longitudeRef characterAtIndex:0] == 'W')
gpsLocation.longitude = 0 - gpsLocation.longitude;
}

Unknown said...

Thanks Andy,
Lemme see how it goes then I will get back to you :)

Jason said...

I've gone through your code (Part I and II) and figured I'd write some comments.

First off, thank you so much for uploading this! In case other readers might come across what i did, here ya go:

- the zip contains release-sim and release-iphoneos folders. I just used the sim files and lib.
- put the BOOL gLogging = FALSE; after the @syntesize statements and before your appDidFinishLaunching method.
- you're missing a semicolon after [fract release] at the end of the populateGPS method.
- By the end of the above, you're left with a NSMutableArray, but almost all image uploaders need an NSArray*. Any idea how to convert these?

Overall here's the code that actually matters (make sure to copy in the helper functions above):

CLLocationCoordinate2D location;
location.latitude = 0;
location.longitude = 0;

NSString *ref;
NSData *imageData = [NSData dataWithContentsOfFile:[R3DUtils userFile:self.white.fileName]];
EXFJpeg* jpegScanner = [[EXFJpeg alloc] init];
[jpegScanner scanImageData: imageData];
EXFMetaData* exifData = jpegScanner.exifMetaData;

// adding GPS latitude to the Exif object
NSMutableArray* locArray = [self createLocArray:location.latitude];
EXFGPSLoc* gpsLoc = [[EXFGPSLoc alloc] init];
[self populateGPS: gpsLoc :locArray];
[exifData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLatitude] ];
[gpsLoc release];
[locArray release];

if (location.latitude<0.0){
ref = [[NSString alloc] initWithString:@"S"];
}else{
ref = [[NSString alloc] initWithString:@"N"];
}
[exifData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLatitudeRef] ];

// adding GPS longitude to the Exif object
NSMutableArray* locArray = [self createLocArray:location.longitude];
EXFGPSLoc* gpsLoc = [[EXFGPSLoc alloc] init];
[self populateGPS: gpsLoc :locArray];
[exifData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLongitude] ];
[gpsLoc release];
[locArray release];

/* Now get final image data! */
NSMutableArray *newImageData;
[jpegScanner populateImageData:newImageData];

Jason said...

Biggest question I have...how do i get an NSData from my final MutableArray?

Jason said...

I guess I should also comment on the final code is giving me a warning: "Passing argument 1 of 'populateImageData' from distict obj-c type'. Any idea there?

Unknown said...

How do I get rid of this warning:

warning: (armv6) /Users/stevewoodcock/dev/iphoneGeo-2.0/iphone-exif/build/iphoneExif.build/Release-iphoneos/exifLib.build/Objects-normal/armv6/EXFHandlers.o unable to open object file

Unknown said...

Must be the way I link the static library. I followed the 'dependency static library linking' and the error is

".objc_class_name_EXFJpeg", referenced from:
literal-pointer@__OBJC@__cls_refs@EXFJpeg in testPickerViewController.o
symbol(s) not found
collect2: ld returned 1 exit status

Unknown said...

@jason: which of the lib files did you use?

The one on the "Release-iphoneos" folder or "Release-simulator-iphonesimulator" folder?

Jason said...

well i'm just doing some testing for the simulator, so the Release-simulator-iphonesimulator

Unknown said...

There are no include .h files in the "Release-iphoneos" pack so I wonder whether they use the same files ...
and I am curious. How are you able to get GPS data through the simulator, is it possible to take pictures, then use UIImagejpegRepresentation for your NSData? Or are you using the album pictures ... which I guess should have their EXIF tags stripped?

Jason said...

here's two screenshots of how the libes appear. I tried using the iphoneos .lib files too but not luck.

http://skitch.com/jasonpurdy/nj8km/newimageviewcontroller.m-iphone-white

http://skitch.com/jasonpurdy/nj8ks/newimageviewcontroller.m-iphone-white

Jason said...

thanks for your help @erastusnjuki

ya, i'm just using a UIImage i have saved.

Unknown said...

@jason ... If you could do me the favor of sending me a link that explains how you linked libiphone-exif.a, I would be a happy guy :)

Jason said...

@erastusnjuki - i'm simply dragging the files into xcode. right click -> create new group and then i drop from finder into the new group. the .a file and the .h files.

Unknown said...

Ref: Earlier Discussion btn Steve and Vince

Thanks Jason! I see you use a file path. I have advanced to the point where I need to read the original files(Because all I get are null values).

Anybody who knows how to get the original file path of Camera Roll images in the iphone?

cyril godefroy said...

Hi Steve, I'd like to get in contact with you to discuss license options. I don't want to use a GPL. Please contact me on big google mail domain. My identifier is cgodefroy.

futureman said...

Why do I get this error?

".objc_class_name_EXFJpeg", referenced from:

Anonymous said...

Hi, I must be missing something. I have taken snips from your code and have added a tag to the EXIF, now when I populate the image from the EXIFMetaData and save it, it is a 4K file rather than the original Jpeg.

How can I create and save the JPEG file? using the library? Is there some kind of a sample project that illustrates the usage of the library?

kind regards,

jeyanthan said...

hai Vince.can u tell me how to add our own custom tag.is there any code to create our own addreess for custom tag..

jeyanthan said...
This comment has been removed by the author.
Hardy said...

Anyone have a full sample project for this yet? I can't get it to work. I set the tags, but when I look at file in other apps to see geotags they aren't there.

Plus after saving a file with the tags added, the file is twice as big as the file when I don't save the tags.

Hardy said...

I put up a sample project to with adding long/lat tags, but it's not working. Can use some help on what I'm doing wrong, then I'll make the project available to everyone so there's a sample project out there.

http://www.catamount.com/tmp/GeoTagImage.zip