3 #include <QImageReader>
4 #include "common/tifffile.h"
5 #include "exifparser.h"
8 #define SOI_MARKER 0xFFD8
9 #define APP1_MARKER 0xFFE1
11 #define GPSIFDTag 34853
12 #define ImageDescription 270
14 #define GPSLatitudeRef 1
16 #define GPSLongitudeRef 3
17 #define GPSLongitude 4
18 #define GPSAltitudeRef 5
20 #define GPSTimeStamp 7
21 #define GPSDateStamp 29
24 QString
EXIFParser::text(TIFFFile
&file
, const IFDEntry
&e
) const
26 if (e
.type
!= TIFF_ASCII
|| !e
.count
)
29 if (e
.count
<= sizeof(e
.offset
))
30 return QString(QByteArray((const char *)&e
.offset
, sizeof(e
.offset
)));
32 if (!file
.seek(e
.offset
))
35 QByteArray
str(file
.read(e
.count
));
36 if (str
.size() < (int)e
.count
)
42 QTime
EXIFParser::time(TIFFFile
&file
, const IFDEntry
&ts
) const
44 if (!(ts
.type
== TIFF_RATIONAL
&& ts
.count
== 3))
47 if (!file
.seek(ts
.offset
))
51 for (int i
= 0; i
< 3; i
++) {
53 if (!file
.readValue(num
))
55 if (!file
.readValue(den
))
58 hms
[i
] = num
/(double)den
;
61 return QTime((int)hms
[0], (int)hms
[1], (int)hms
[2]);
64 double EXIFParser::altitude(TIFFFile
&file
, const IFDEntry
&alt
,
65 const IFDEntry
&altRef
) const
67 if (!(alt
.type
== TIFF_RATIONAL
&& alt
.count
== 1))
70 if (!file
.seek(alt
.offset
))
74 if (!file
.readValue(num
))
76 if (!file
.readValue(den
))
79 return (altRef
.type
== TIFF_BYTE
&& altRef
.count
== 1 && altRef
.offset
)
80 ? -(num
/(double)den
) : num
/(double)den
;
83 double EXIFParser::coordinate(TIFFFile
&file
, const IFDEntry
&ll
) const
85 if (!(ll
.type
== TIFF_RATIONAL
&& ll
.count
== 3))
88 if (!file
.seek(ll
.offset
))
92 for (int i
= 0; i
< 3; i
++) {
94 if (!file
.readValue(num
))
96 if (!file
.readValue(den
))
99 dms
[i
] = num
/(double)den
;
102 return dms
[0] + dms
[1]/60 + dms
[2]/3600;
105 Coordinates
EXIFParser::coordinates(TIFFFile
&file
, const IFDEntry
&lon
,
106 const IFDEntry
&lonRef
, const IFDEntry
&lat
, const IFDEntry
&latRef
) const
108 if (!(latRef
.type
== TIFF_ASCII
&& latRef
.count
== 2
109 && lonRef
.type
== TIFF_ASCII
&& lonRef
.count
== 2))
110 return Coordinates();
112 Coordinates
c(coordinate(file
, lon
), coordinate(file
, lat
));
114 return Coordinates();
116 char ew
= file
.isBE() ? lonRef
.offset
>> 24 : lonRef
.offset
;
117 char ns
= file
.isBE() ? latRef
.offset
>> 24 : latRef
.offset
;
127 bool EXIFParser::readEntry(TIFFFile
&file
, const QSet
<quint16
> &tags
,
128 QMap
<quint16
, IFDEntry
> &entries
) const
133 if (!file
.readValue(tag
))
135 if (!file
.readValue(entry
.type
))
137 if (!file
.readValue(entry
.count
))
139 if (!file
.readValue(entry
.offset
))
142 if (tags
.contains(tag
))
143 entries
.insert(tag
, entry
);
148 bool EXIFParser::readIFD(TIFFFile
&file
, quint32 offset
,
149 const QSet
<quint16
> &tags
, QMap
<quint16
, IFDEntry
> &entries
) const
153 if (!file
.seek(offset
))
155 if (!file
.readValue(count
))
158 for (quint16 i
= 0; i
< count
; i
++)
159 if (!readEntry(file
, tags
, entries
))
165 bool EXIFParser::parseTIFF(QFile
*file
, QVector
<Waypoint
> &waypoints
)
168 if (!tiff
.isValid()) {
169 _errorString
= "Invalid EXIF data";
173 QSet
<quint16
> IFD0Tags
;
174 IFD0Tags
<< GPSIFDTag
<< ImageDescription
;
175 QMap
<quint16
, IFDEntry
> IFD0
;
176 for (quint32 ifd
= tiff
.ifd(); ifd
; ) {
177 if (!readIFD(tiff
, ifd
, IFD0Tags
, IFD0
) || !tiff
.readValue(ifd
)) {
178 _errorString
= "Invalid IFD0";
182 if (!IFD0
.contains(GPSIFDTag
)) {
183 _errorString
= "GPS IFD not found";
187 QSet
<quint16
> GPSIFDTags
;
188 GPSIFDTags
<< GPSLatitude
<< GPSLongitude
<< GPSLatitudeRef
189 << GPSLongitudeRef
<< GPSAltitude
<< GPSAltitudeRef
<< GPSDateStamp
191 QMap
<quint16
, IFDEntry
> GPSIFD
;
192 for (quint32 ifd
= IFD0
.value(GPSIFDTag
).offset
; ifd
; ) {
193 if (!readIFD(tiff
, ifd
, GPSIFDTags
, GPSIFD
) || !tiff
.readValue(ifd
)) {
194 _errorString
= "Invalid GPS IFD";
199 Coordinates
c(coordinates(tiff
, GPSIFD
.value(GPSLongitude
),
200 GPSIFD
.value(GPSLongitudeRef
), GPSIFD
.value(GPSLatitude
),
201 GPSIFD
.value(GPSLatitudeRef
)));
203 _errorString
= "Invalid/missing GPS coordinates";
208 ImageInfo
img(file
->fileName(), QImageReader(file
).size());
211 wp
.setName(QFileInfo(file
->fileName()).baseName());
213 wp
.setElevation(altitude(tiff
, GPSIFD
.value(GPSAltitude
),
214 GPSIFD
.value(GPSAltitudeRef
)));
215 wp
.setTimestamp(QDateTime(QDate::fromString(text(tiff
,
216 GPSIFD
.value(GPSDateStamp
)), "yyyy:MM:dd"), time(tiff
,
217 GPSIFD
.value(GPSTimeStamp
)), Qt::UTC
));
218 wp
.setDescription(text(tiff
, IFD0
.value(ImageDescription
)).trimmed());
220 waypoints
.append(wp
);
225 bool EXIFParser::parse(QFile
*file
, QList
<TrackData
> &tracks
,
226 QList
<RouteData
> &routes
, QList
<Area
> &polygons
,
227 QVector
<Waypoint
> &waypoints
)
234 QDataStream
stream(file
);
235 stream
.setByteOrder(QDataStream::BigEndian
);
237 if (marker
!= SOI_MARKER
) {
238 _errorString
= "Not a JPEG file";
242 while (!stream
.atEnd()) {
244 if (marker
== APP1_MARKER
) {
248 if (stream
.readRawData(magic
, sizeof(magic
)) == sizeof(magic
) &&
249 !memcmp(magic
, "Exif\0\0", sizeof(magic
)))
250 return parseTIFF(file
, waypoints
);
256 _errorString
= "No EXIF data found";