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