//-------------------------------------------------------------------------- // Parsing of GPS info from exif header. // // Matthias Wandel, Dec 1999 - Dec 2002 //-------------------------------------------------------------------------- #include "jhead.h" #include <string.h> #include <utils/Log.h> #define TAG_GPS_LAT_REF 1 #define TAG_GPS_LAT 2 #define TAG_GPS_LONG_REF 3 #define TAG_GPS_LONG 4 #define TAG_GPS_ALT_REF 5 #define TAG_GPS_ALT 6 #define TAG_GPS_TIMESTAMP 7 #define TAG_GPS_PROCESSING_METHOD 27 #define TAG_GPS_DATESTAMP 29 static TagTable_t GpsTags[]= { { 0x00, "GPSVersionID", FMT_BYTE, 4}, { 0x01, "GPSLatitudeRef", FMT_STRING, 2}, { 0x02, "GPSLatitude", FMT_URATIONAL, 3}, { 0x03, "GPSLongitudeRef", FMT_STRING, 2}, { 0x04, "GPSLongitude", FMT_URATIONAL, 3}, { 0x05, "GPSAltitudeRef", FMT_BYTE, 1}, { 0x06, "GPSAltitude", FMT_URATIONAL, 1}, { 0x07, "GPSTimeStamp", FMT_SRATIONAL, 3}, { 0x08, "GPSSatellites", FMT_STRING, -1}, { 0x09, "GPSStatus", FMT_STRING, 2}, { 0x0A, "GPSMeasureMode", FMT_STRING, 2}, { 0x0B, "GPSDOP", FMT_SRATIONAL, 1}, { 0x0C, "GPSSpeedRef", FMT_STRING, 2}, { 0x0D, "GPSSpeed", FMT_SRATIONAL, 1}, { 0x0E, "GPSTrackRef", FMT_STRING, 2}, { 0x0F, "GPSTrack", FMT_SRATIONAL, 1}, { 0x10, "GPSImgDirectionRef", FMT_STRING, -1}, { 0x11, "GPSImgDirection", FMT_SRATIONAL, 1}, { 0x12, "GPSMapDatum", FMT_STRING, -1}, { 0x13, "GPSDestLatitudeRef", FMT_STRING, 2}, { 0x14, "GPSDestLatitude", FMT_SRATIONAL, 3}, { 0x15, "GPSDestLongitudeRef", FMT_STRING, 2}, { 0x16, "GPSDestLongitude", FMT_SRATIONAL, 3}, { 0x17, "GPSDestBearingRef", FMT_STRING, 1}, { 0x18, "GPSDestBearing", FMT_SRATIONAL, 1}, { 0x19, "GPSDestDistanceRef", FMT_STRING, 2}, { 0x1A, "GPSDestDistance", FMT_SRATIONAL, 1}, { 0x1B, "GPSProcessingMethod", FMT_STRING, -1}, { 0x1C, "GPSAreaInformation", FMT_STRING, -1}, { 0x1D, "GPSDateStamp", FMT_STRING, 11}, { 0x1E, "GPSDifferential", FMT_SSHORT, 1}, }; static const char ExifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 }; #define MAX_GPS_TAG (sizeof(GpsTags) / sizeof(TagTable_t)) #define EXIF_ASCII_PREFIX_LEN (sizeof(ExifAsciiPrefix)) // Define the line below to turn on poor man's debugging output #undef SUPERDEBUG #ifdef SUPERDEBUG #define printf LOGE #endif int IsGpsTag(const char* tag) { return strstr(tag, "GPS") == tag; } TagTable_t* GpsTagToTagTableEntry(unsigned short tag) { unsigned int i; for (i = 0; i < MAX_GPS_TAG; i++) { if (GpsTags[i].Tag == tag) { printf("found tag %d", tag); int format = GpsTags[i].Format; if (format == 0) { printf("tag %s format not defined", GpsTags[i].Desc); return NULL; } return &GpsTags[i]; } } printf("tag %d NOT FOUND", tag); return NULL; } int GpsTagToFormatType(unsigned short tag) { unsigned int i; for (i = 0; i < MAX_GPS_TAG; i++) { if (GpsTags[i].Tag == tag) { printf("found tag %d", tag); int format = GpsTags[i].Format; if (format == 0) { printf("tag %s format not defined", GpsTags[i].Desc); return -1; } return format; } } printf("tag %d NOT FOUND", tag); return -1; } int GpsTagNameToValue(const char* tagName) { unsigned int i; for (i = 0; i < MAX_GPS_TAG; i++) { if (strcmp(GpsTags[i].Desc, tagName) == 0) { printf("found GPS tag %s val %d", GpsTags[i].Desc, GpsTags[i].Tag); return GpsTags[i].Tag; } } printf("GPS tag %s NOT FOUND", tagName); return -1; } //-------------------------------------------------------------------------- // Process GPS info directory //-------------------------------------------------------------------------- void ProcessGpsInfo(unsigned char * DirStart, int ByteCountUnused, unsigned char * OffsetBase, unsigned ExifLength) { int de; unsigned a; int NumDirEntries; NumDirEntries = Get16u(DirStart); #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) if (ShowTags){ printf("(dir has %d entries)\n",NumDirEntries); } ImageInfo.GpsInfoPresent = TRUE; strcpy(ImageInfo.GpsLat, "? ?"); strcpy(ImageInfo.GpsLong, "? ?"); ImageInfo.GpsAlt[0] = 0; for (de=0;de<NumDirEntries;de++){ unsigned Tag, Format, Components; unsigned char * ValuePtr; int ComponentSize; unsigned ByteCount; unsigned char * DirEntry; DirEntry = DIR_ENTRY_ADDR(DirStart, de); if (DirEntry+12 > OffsetBase+ExifLength){ ErrNonfatal("GPS info directory goes past end of exif",0,0); return; } Tag = Get16u(DirEntry); Format = Get16u(DirEntry+2); Components = Get32u(DirEntry+4); if ((Format-1) >= NUM_FORMATS) { // (-1) catches illegal zero case as unsigned underflows to positive large. ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag); continue; } ComponentSize = BytesPerFormat[Format]; ByteCount = Components * ComponentSize; #ifdef SUPERDEBUG printf("GPS tag %x format %s #components %d componentsize %d bytecount %d", Tag, formatStr(Format), Components, ComponentSize, ByteCount); #endif if (ByteCount > 4){ unsigned OffsetVal; OffsetVal = Get32u(DirEntry+8); // If its bigger than 4 bytes, the dir entry contains an offset. if (OffsetVal+ByteCount > ExifLength){ // Bogus pointer offset and / or bytecount value ErrNonfatal("Illegal value pointer for tag %04x", Tag,0); continue; } ValuePtr = OffsetBase+OffsetVal; }else{ // 4 bytes or less and value is in the dir entry itself ValuePtr = DirEntry+8; } switch(Tag){ char FmtString[21]; char TempString[50]; double Values[3]; case TAG_GPS_LAT_REF: ImageInfo.GpsLat[0] = ValuePtr[0]; ImageInfo.GpsLatRef[0] = ValuePtr[0]; ImageInfo.GpsLatRef[1] = '\0'; break; case TAG_GPS_LONG_REF: ImageInfo.GpsLong[0] = ValuePtr[0]; ImageInfo.GpsLongRef[0] = ValuePtr[0]; ImageInfo.GpsLongRef[1] = '\0'; break; case TAG_GPS_LAT: case TAG_GPS_LONG: if (Format != FMT_URATIONAL){ ErrNonfatal("Inappropriate format (%d) for GPS coordinates!", Format, 0); } strcpy(FmtString, "%0.0fd %0.0fm %0.0fs"); for (a=0;a<3;a++){ int den, digits; den = Get32s(ValuePtr+4+a*ComponentSize); digits = 0; while (den > 1 && digits <= 6){ den = den / 10; digits += 1; } if (digits > 6) digits = 6; FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0)); FmtString[3+a*7] = (char)('0'+digits); Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format); } sprintf(TempString, FmtString, Values[0], Values[1], Values[2]); if (Tag == TAG_GPS_LAT){ strncpy(ImageInfo.GpsLat+2, TempString, 29); }else{ strncpy(ImageInfo.GpsLong+2, TempString, 29); } sprintf(TempString, "%d/%d,%d/%d,%d/%d", Get32s(ValuePtr), Get32s(4+(char*)ValuePtr), Get32s(8+(char*)ValuePtr), Get32s(12+(char*)ValuePtr), Get32s(16+(char*)ValuePtr), Get32s(20+(char*)ValuePtr)); if (Tag == TAG_GPS_LAT){ strncpy(ImageInfo.GpsLatRaw, TempString, 31); }else{ strncpy(ImageInfo.GpsLongRaw, TempString, 31); } break; case TAG_GPS_ALT_REF: ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' '); ImageInfo.GpsAltRef = (char)ValuePtr[0]; break; case TAG_GPS_ALT: sprintf(ImageInfo.GpsAlt + 1, "%.2fm", ConvertAnyFormat(ValuePtr, Format)); ImageInfo.GpsAltRaw.num = Get32u(ValuePtr); ImageInfo.GpsAltRaw.denom = Get32u(4+(char *)ValuePtr); break; case TAG_GPS_TIMESTAMP: snprintf(ImageInfo.GpsTimeStamp, sizeof(ImageInfo.GpsTimeStamp), "%d:%d:%d", (int) ConvertAnyFormat(ValuePtr, Format), (int) ConvertAnyFormat(ValuePtr + 8, Format), (int) ConvertAnyFormat(ValuePtr + 16, Format) ); break; case TAG_GPS_DATESTAMP: strncpy(ImageInfo.GpsDateStamp, (char*)ValuePtr, sizeof(ImageInfo.GpsDateStamp)); break; case TAG_GPS_PROCESSING_METHOD: if (ByteCount > EXIF_ASCII_PREFIX_LEN && memcmp(ValuePtr, ExifAsciiPrefix, EXIF_ASCII_PREFIX_LEN) == 0) { int length = ByteCount < GPS_PROCESSING_METHOD_LEN + EXIF_ASCII_PREFIX_LEN ? ByteCount - EXIF_ASCII_PREFIX_LEN : GPS_PROCESSING_METHOD_LEN; memcpy(ImageInfo.GpsProcessingMethod, (char*)(ValuePtr + EXIF_ASCII_PREFIX_LEN), length); ImageInfo.GpsProcessingMethod[length] = 0; } else { LOGW("Unsupported encoding for GPSProcessingMethod"); } break; } if (ShowTags){ // Show tag value. if (Tag < MAX_GPS_TAG){ printf(" %s =", GpsTags[Tag].Desc); }else{ // Show unknown tag printf(" Illegal GPS tag %04x=", Tag); } switch(Format){ case FMT_UNDEFINED: // Undefined is typically an ascii string. case FMT_STRING: // String arrays printed without function call (different from int arrays) { printf("\""); for (a=0;a<ByteCount;a++){ int ZeroSkipped = 0; if (ValuePtr[a] >= 32){ if (ZeroSkipped){ printf("?"); ZeroSkipped = 0; } putchar(ValuePtr[a]); }else{ if (ValuePtr[a] == 0){ ZeroSkipped = 1; } } } printf("\"\n"); } break; default: // Handle arrays of numbers later (will there ever be?) for (a=0;;){ PrintFormatNumber(ValuePtr+a*ComponentSize, Format, ByteCount); if (++a >= Components) break; printf(", "); } printf("\n"); } } } }