gpsinfo.c revision 434623a0e87c8e145dbb46917b4ab9777475d9eb
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 22static 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_SRATIONAL, 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 56static 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 69int IsGpsTag(const char* tag) { 70 return strstr(tag, "GPS") == tag; 71} 72 73TagTable_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 91int 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 109int 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//-------------------------------------------------------------------------- 126void 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 bzero(ImageInfo.GpsTimeStamp, sizeof(ImageInfo.GpsTimeStamp)); 144 bzero(ImageInfo.GpsDateStamp, sizeof(ImageInfo.GpsDateStamp)); 145 bzero(ImageInfo.GpsProcessingMethod, sizeof(ImageInfo.GpsProcessingMethod)); 146 147 for (de=0;de<NumDirEntries;de++){ 148 unsigned Tag, Format, Components; 149 unsigned char * ValuePtr; 150 int ComponentSize; 151 unsigned ByteCount; 152 unsigned char * DirEntry; 153 DirEntry = DIR_ENTRY_ADDR(DirStart, de); 154 155 if (DirEntry+12 > OffsetBase+ExifLength){ 156 ErrNonfatal("GPS info directory goes past end of exif",0,0); 157 return; 158 } 159 160 Tag = Get16u(DirEntry); 161 Format = Get16u(DirEntry+2); 162 Components = Get32u(DirEntry+4); 163 164 if ((Format-1) >= NUM_FORMATS) { 165 // (-1) catches illegal zero case as unsigned underflows to positive large. 166 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag); 167 continue; 168 } 169 170 ComponentSize = BytesPerFormat[Format]; 171 ByteCount = Components * ComponentSize; 172 173#ifdef SUPERDEBUG 174 printf("GPS tag %x format %s #components %d componentsize %d bytecount %d", Tag, formatStr(Format), Components, ComponentSize, 175 ByteCount); 176#endif 177 178 if (ByteCount > 4){ 179 unsigned OffsetVal; 180 OffsetVal = Get32u(DirEntry+8); 181 // If its bigger than 4 bytes, the dir entry contains an offset. 182 if (OffsetVal+ByteCount > ExifLength){ 183 // Bogus pointer offset and / or bytecount value 184 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0); 185 continue; 186 } 187 ValuePtr = OffsetBase+OffsetVal; 188 }else{ 189 // 4 bytes or less and value is in the dir entry itself 190 ValuePtr = DirEntry+8; 191 } 192 193 switch(Tag){ 194 char FmtString[21]; 195 char TempString[50]; 196 double Values[3]; 197 198 case TAG_GPS_LAT_REF: 199 ImageInfo.GpsLat[0] = ValuePtr[0]; 200 ImageInfo.GpsLatRef[0] = ValuePtr[0]; 201 ImageInfo.GpsLatRef[1] = '\0'; 202 break; 203 204 case TAG_GPS_LONG_REF: 205 ImageInfo.GpsLong[0] = ValuePtr[0]; 206 ImageInfo.GpsLongRef[0] = ValuePtr[0]; 207 ImageInfo.GpsLongRef[1] = '\0'; 208 break; 209 210 case TAG_GPS_LAT: 211 case TAG_GPS_LONG: 212 if (Format != FMT_URATIONAL){ 213 ErrNonfatal("Inappropriate format (%d) for GPS coordinates!", Format, 0); 214 } 215 strcpy(FmtString, "%0.0fd %0.0fm %0.0fs"); 216 for (a=0;a<3;a++){ 217 int den, digits; 218 219 den = Get32s(ValuePtr+4+a*ComponentSize); 220 digits = 0; 221 while (den > 1 && digits <= 6){ 222 den = den / 10; 223 digits += 1; 224 } 225 if (digits > 6) digits = 6; 226 FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0)); 227 FmtString[3+a*7] = (char)('0'+digits); 228 229 Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format); 230 } 231 232 sprintf(TempString, FmtString, Values[0], Values[1], Values[2]); 233 234 if (Tag == TAG_GPS_LAT){ 235 strncpy(ImageInfo.GpsLat+2, TempString, 29); 236 }else{ 237 strncpy(ImageInfo.GpsLong+2, TempString, 29); 238 } 239 240 sprintf(TempString, "%d/%d,%d/%d,%d/%d", 241 Get32s(ValuePtr), Get32s(4+(char*)ValuePtr), 242 Get32s(8+(char*)ValuePtr), Get32s(12+(char*)ValuePtr), 243 Get32s(16+(char*)ValuePtr), Get32s(20+(char*)ValuePtr)); 244 if (Tag == TAG_GPS_LAT){ 245 strncpy(ImageInfo.GpsLatRaw, TempString, 31); 246 }else{ 247 strncpy(ImageInfo.GpsLongRaw, TempString, 31); 248 } 249 break; 250 251 case TAG_GPS_ALT_REF: 252 ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' '); 253 break; 254 255 case TAG_GPS_ALT: 256 sprintf(ImageInfo.GpsAlt + 1, "%.2fm", 257 ConvertAnyFormat(ValuePtr, Format)); 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