1 //-------------------------------------------------------------------------- 2 // Parsing of GPS info from exif header. 3 // 4 // Matthias Wandel, Dec 1999 - Dec 2002 5 //-------------------------------------------------------------------------- 6 #include "jhead.h" 7 8 #include <string.h> 9 #include <utils/Log.h> 10 11 12 #define TAG_GPS_LAT_REF 1 13 #define TAG_GPS_LAT 2 14 #define TAG_GPS_LONG_REF 3 15 #define TAG_GPS_LONG 4 16 #define TAG_GPS_ALT_REF 5 17 #define TAG_GPS_ALT 6 18 #define TAG_GPS_TIMESTAMP 7 19 #define TAG_GPS_PROCESSING_METHOD 27 20 #define TAG_GPS_DATESTAMP 29 21 22 static TagTable_t GpsTags[]= { 23 { 0x00, "GPSVersionID", FMT_BYTE, 4}, 24 { 0x01, "GPSLatitudeRef", FMT_STRING, 2}, 25 { 0x02, "GPSLatitude", FMT_URATIONAL, 3}, 26 { 0x03, "GPSLongitudeRef", FMT_STRING, 2}, 27 { 0x04, "GPSLongitude", FMT_URATIONAL, 3}, 28 { 0x05, "GPSAltitudeRef", FMT_BYTE, 1}, 29 { 0x06, "GPSAltitude", FMT_URATIONAL, 1}, 30 { 0x07, "GPSTimeStamp", FMT_SRATIONAL, 3}, 31 { 0x08, "GPSSatellites", FMT_STRING, -1}, 32 { 0x09, "GPSStatus", FMT_STRING, 2}, 33 { 0x0A, "GPSMeasureMode", FMT_STRING, 2}, 34 { 0x0B, "GPSDOP", FMT_SRATIONAL, 1}, 35 { 0x0C, "GPSSpeedRef", FMT_STRING, 2}, 36 { 0x0D, "GPSSpeed", FMT_SRATIONAL, 1}, 37 { 0x0E, "GPSTrackRef", FMT_STRING, 2}, 38 { 0x0F, "GPSTrack", FMT_SRATIONAL, 1}, 39 { 0x10, "GPSImgDirectionRef", FMT_STRING, -1}, 40 { 0x11, "GPSImgDirection", FMT_SRATIONAL, 1}, 41 { 0x12, "GPSMapDatum", FMT_STRING, -1}, 42 { 0x13, "GPSDestLatitudeRef", FMT_STRING, 2}, 43 { 0x14, "GPSDestLatitude", FMT_SRATIONAL, 3}, 44 { 0x15, "GPSDestLongitudeRef", FMT_STRING, 2}, 45 { 0x16, "GPSDestLongitude", FMT_SRATIONAL, 3}, 46 { 0x17, "GPSDestBearingRef", FMT_STRING, 1}, 47 { 0x18, "GPSDestBearing", FMT_SRATIONAL, 1}, 48 { 0x19, "GPSDestDistanceRef", FMT_STRING, 2}, 49 { 0x1A, "GPSDestDistance", FMT_SRATIONAL, 1}, 50 { 0x1B, "GPSProcessingMethod", FMT_STRING, -1}, 51 { 0x1C, "GPSAreaInformation", FMT_STRING, -1}, 52 { 0x1D, "GPSDateStamp", FMT_STRING, 11}, 53 { 0x1E, "GPSDifferential", FMT_SSHORT, 1}, 54 }; 55 56 static const char ExifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 }; 57 58 #define MAX_GPS_TAG (sizeof(GpsTags) / sizeof(TagTable_t)) 59 #define EXIF_ASCII_PREFIX_LEN (sizeof(ExifAsciiPrefix)) 60 61 // Define the line below to turn on poor man's debugging output 62 #undef SUPERDEBUG 63 64 #ifdef SUPERDEBUG 65 #define printf LOGE 66 #endif 67 68 69 int IsGpsTag(const char* tag) { 70 return strstr(tag, "GPS") == tag; 71 } 72 73 TagTable_t* GpsTagToTagTableEntry(unsigned short tag) 74 { 75 unsigned int i; 76 for (i = 0; i < MAX_GPS_TAG; i++) { 77 if (GpsTags[i].Tag == tag) { 78 printf("found tag %d", tag); 79 int format = GpsTags[i].Format; 80 if (format == 0) { 81 printf("tag %s format not defined", GpsTags[i].Desc); 82 return NULL; 83 } 84 return &GpsTags[i]; 85 } 86 } 87 printf("tag %d NOT FOUND", tag); 88 return NULL; 89 } 90 91 int GpsTagToFormatType(unsigned short tag) 92 { 93 unsigned int i; 94 for (i = 0; i < MAX_GPS_TAG; i++) { 95 if (GpsTags[i].Tag == tag) { 96 printf("found tag %d", tag); 97 int format = GpsTags[i].Format; 98 if (format == 0) { 99 printf("tag %s format not defined", GpsTags[i].Desc); 100 return -1; 101 } 102 return format; 103 } 104 } 105 printf("tag %d NOT FOUND", tag); 106 return -1; 107 } 108 109 int GpsTagNameToValue(const char* tagName) 110 { 111 unsigned int i; 112 for (i = 0; i < MAX_GPS_TAG; i++) { 113 if (strcmp(GpsTags[i].Desc, tagName) == 0) { 114 printf("found GPS tag %s val %d", GpsTags[i].Desc, GpsTags[i].Tag); 115 return GpsTags[i].Tag; 116 } 117 } 118 printf("GPS tag %s NOT FOUND", tagName); 119 return -1; 120 } 121 122 123 //-------------------------------------------------------------------------- 124 // Process GPS info directory 125 //-------------------------------------------------------------------------- 126 void ProcessGpsInfo(unsigned char * DirStart, int ByteCountUnused, unsigned char * OffsetBase, unsigned ExifLength) 127 { 128 int de; 129 unsigned a; 130 int NumDirEntries; 131 132 NumDirEntries = Get16u(DirStart); 133 #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) 134 135 if (ShowTags){ 136 printf("(dir has %d entries)\n",NumDirEntries); 137 } 138 139 ImageInfo.GpsInfoPresent = TRUE; 140 strcpy(ImageInfo.GpsLat, "? ?"); 141 strcpy(ImageInfo.GpsLong, "? ?"); 142 ImageInfo.GpsAlt[0] = 0; 143 144 for (de=0;de<NumDirEntries;de++){ 145 unsigned Tag, Format, Components; 146 unsigned char * ValuePtr; 147 int ComponentSize; 148 unsigned ByteCount; 149 unsigned char * DirEntry; 150 DirEntry = DIR_ENTRY_ADDR(DirStart, de); 151 152 if (DirEntry+12 > OffsetBase+ExifLength){ 153 ErrNonfatal("GPS info directory goes past end of exif",0,0); 154 return; 155 } 156 157 Tag = Get16u(DirEntry); 158 Format = Get16u(DirEntry+2); 159 Components = Get32u(DirEntry+4); 160 161 if ((Format-1) >= NUM_FORMATS) { 162 // (-1) catches illegal zero case as unsigned underflows to positive large. 163 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag); 164 continue; 165 } 166 167 ComponentSize = BytesPerFormat[Format]; 168 ByteCount = Components * ComponentSize; 169 170 #ifdef SUPERDEBUG 171 printf("GPS tag %x format %s #components %d componentsize %d bytecount %d", Tag, formatStr(Format), Components, ComponentSize, 172 ByteCount); 173 #endif 174 175 if (ByteCount > 4){ 176 unsigned OffsetVal; 177 OffsetVal = Get32u(DirEntry+8); 178 // If its bigger than 4 bytes, the dir entry contains an offset. 179 if (OffsetVal+ByteCount > ExifLength){ 180 // Bogus pointer offset and / or bytecount value 181 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0); 182 continue; 183 } 184 ValuePtr = OffsetBase+OffsetVal; 185 }else{ 186 // 4 bytes or less and value is in the dir entry itself 187 ValuePtr = DirEntry+8; 188 } 189 190 switch(Tag){ 191 char FmtString[21]; 192 char TempString[50]; 193 double Values[3]; 194 195 case TAG_GPS_LAT_REF: 196 ImageInfo.GpsLat[0] = ValuePtr[0]; 197 ImageInfo.GpsLatRef[0] = ValuePtr[0]; 198 ImageInfo.GpsLatRef[1] = '\0'; 199 break; 200 201 case TAG_GPS_LONG_REF: 202 ImageInfo.GpsLong[0] = ValuePtr[0]; 203 ImageInfo.GpsLongRef[0] = ValuePtr[0]; 204 ImageInfo.GpsLongRef[1] = '\0'; 205 break; 206 207 case TAG_GPS_LAT: 208 case TAG_GPS_LONG: 209 if (Format != FMT_URATIONAL){ 210 ErrNonfatal("Inappropriate format (%d) for GPS coordinates!", Format, 0); 211 } 212 strcpy(FmtString, "%0.0fd %0.0fm %0.0fs"); 213 for (a=0;a<3;a++){ 214 int den, digits; 215 216 den = Get32s(ValuePtr+4+a*ComponentSize); 217 digits = 0; 218 while (den > 1 && digits <= 6){ 219 den = den / 10; 220 digits += 1; 221 } 222 if (digits > 6) digits = 6; 223 FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0)); 224 FmtString[3+a*7] = (char)('0'+digits); 225 226 Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format); 227 } 228 229 sprintf(TempString, FmtString, Values[0], Values[1], Values[2]); 230 231 if (Tag == TAG_GPS_LAT){ 232 strncpy(ImageInfo.GpsLat+2, TempString, 29); 233 }else{ 234 strncpy(ImageInfo.GpsLong+2, TempString, 29); 235 } 236 237 sprintf(TempString, "%d/%d,%d/%d,%d/%d", 238 Get32s(ValuePtr), Get32s(4+(char*)ValuePtr), 239 Get32s(8+(char*)ValuePtr), Get32s(12+(char*)ValuePtr), 240 Get32s(16+(char*)ValuePtr), Get32s(20+(char*)ValuePtr)); 241 if (Tag == TAG_GPS_LAT){ 242 strncpy(ImageInfo.GpsLatRaw, TempString, 31); 243 }else{ 244 strncpy(ImageInfo.GpsLongRaw, TempString, 31); 245 } 246 break; 247 248 case TAG_GPS_ALT_REF: 249 ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' '); 250 ImageInfo.GpsAltRef = (char)ValuePtr[0]; 251 break; 252 253 case TAG_GPS_ALT: 254 sprintf(ImageInfo.GpsAlt + 1, "%.2fm", 255 ConvertAnyFormat(ValuePtr, Format)); 256 ImageInfo.GpsAltRaw.num = Get32u(ValuePtr); 257 ImageInfo.GpsAltRaw.denom = Get32u(4+(char *)ValuePtr); 258 break; 259 260 case TAG_GPS_TIMESTAMP: 261 snprintf(ImageInfo.GpsTimeStamp, 262 sizeof(ImageInfo.GpsTimeStamp), "%d:%d:%d", 263 (int) ConvertAnyFormat(ValuePtr, Format), 264 (int) ConvertAnyFormat(ValuePtr + 8, Format), 265 (int) ConvertAnyFormat(ValuePtr + 16, Format) 266 ); 267 break; 268 269 case TAG_GPS_DATESTAMP: 270 strncpy(ImageInfo.GpsDateStamp, (char*)ValuePtr, sizeof(ImageInfo.GpsDateStamp)); 271 break; 272 273 case TAG_GPS_PROCESSING_METHOD: 274 if (ByteCount > EXIF_ASCII_PREFIX_LEN && 275 memcmp(ValuePtr, ExifAsciiPrefix, EXIF_ASCII_PREFIX_LEN) == 0) { 276 int length = 277 ByteCount < GPS_PROCESSING_METHOD_LEN + EXIF_ASCII_PREFIX_LEN ? 278 ByteCount - EXIF_ASCII_PREFIX_LEN : GPS_PROCESSING_METHOD_LEN; 279 memcpy(ImageInfo.GpsProcessingMethod, 280 (char*)(ValuePtr + EXIF_ASCII_PREFIX_LEN), length); 281 ImageInfo.GpsProcessingMethod[length] = 0; 282 } else { 283 LOGW("Unsupported encoding for GPSProcessingMethod"); 284 } 285 break; 286 } 287 288 if (ShowTags){ 289 // Show tag value. 290 if (Tag < MAX_GPS_TAG){ 291 printf(" %s =", GpsTags[Tag].Desc); 292 }else{ 293 // Show unknown tag 294 printf(" Illegal GPS tag %04x=", Tag); 295 } 296 297 switch(Format){ 298 case FMT_UNDEFINED: 299 // Undefined is typically an ascii string. 300 301 case FMT_STRING: 302 // String arrays printed without function call (different from int arrays) 303 { 304 printf("\""); 305 for (a=0;a<ByteCount;a++){ 306 int ZeroSkipped = 0; 307 if (ValuePtr[a] >= 32){ 308 if (ZeroSkipped){ 309 printf("?"); 310 ZeroSkipped = 0; 311 } 312 putchar(ValuePtr[a]); 313 }else{ 314 if (ValuePtr[a] == 0){ 315 ZeroSkipped = 1; 316 } 317 } 318 } 319 printf("\"\n"); 320 } 321 break; 322 323 default: 324 // Handle arrays of numbers later (will there ever be?) 325 for (a=0;;){ 326 PrintFormatNumber(ValuePtr+a*ComponentSize, Format, ByteCount); 327 if (++a >= Components) break; 328 printf(", "); 329 } 330 printf("\n"); 331 } 332 } 333 } 334 } 335 336 337