@@ -69,6 +69,9 @@ var EXIF = {
69
69
return { } ;
70
70
}
71
71
const tags = this . readIFD ( data , tiffStart + ifdOffset , tiffStart , littleEndian ) ;
72
+
73
+ this . processGPSData ( exifData , tags , data , tiffStart , littleEndian ) ;
74
+
72
75
const tagMap = {
73
76
0x010F : 'Make' ,
74
77
0x0110 : 'Model' ,
@@ -98,10 +101,9 @@ var EXIF = {
98
101
0xA434 : 'LensModel' ,
99
102
0x9205 : 'MaxApertureValue' ,
100
103
0x0002 : 'GPSLatitude' ,
101
- 0x0004 : 'GPSLongitude' ,
102
- 0x0006 : 'GPSAltitude' ,
103
104
0x001F : 'GPSDateStamp' ,
104
- 0x0020 : 'GPSTimeStamp'
105
+ 0x0020 : 'GPSTimeStamp' ,
106
+ 0x0004 : 'GPSLongitude' ,
105
107
} ;
106
108
const resolutionUnits = {
107
109
1 : 'None' ,
@@ -147,9 +149,8 @@ var EXIF = {
147
149
exifData [ key ] = `${ ( value [ 0 ] / value [ 1 ] ) . toFixed ( 1 ) } EV` ;
148
150
} else if ( key === 'FocalLength' ) {
149
151
exifData [ key ] = `${ ( value [ 0 ] / value [ 1 ] ) . toFixed ( 1 ) } mm` ;
150
- } else if ( key . startsWith ( 'GPS' ) ) {
151
- exifData [ key ] = `${ value [ 0 ] / value [ 1 ] } °` ;
152
- } else {
152
+ }
153
+ else {
153
154
exifData [ key ] = `${ value [ 0 ] } /${ value [ 1 ] } ` ;
154
155
}
155
156
} else {
@@ -178,10 +179,87 @@ var EXIF = {
178
179
return { } ;
179
180
}
180
181
} ,
182
+
183
+ convertDMSToDD : function ( degrees , minutes , seconds , direction ) {
184
+ let dd = degrees + ( minutes / 60 ) + ( seconds / 3600 ) ;
185
+ if ( direction === 'S' || direction === 'W' ) {
186
+ dd *= - 1 ;
187
+ }
188
+ return dd ;
189
+ } ,
190
+
191
+ processGPSData : function ( exifData , tags , data , tiffStart , littleEndian ) {
192
+ if ( ! tags [ 0x8825 ] ) {
193
+ console . log ( '[EXIF Viewer] No GPS IFD found' ) ;
194
+ return ;
195
+ }
196
+
197
+ console . log ( '[EXIF Viewer] Processing GPS IFD' ) ;
198
+
199
+ const gpsIFD = this . readIFD ( data , tiffStart + tags [ 0x8825 ] , tiffStart , littleEndian ) ;
200
+ console . log ( '[EXIF Viewer] GPS IFD:' , gpsIFD ) ;
201
+
202
+ let latitude , longitude , latRef , lonRef ;
203
+
204
+ if ( gpsIFD [ 0x0002 ] && gpsIFD [ 0x0001 ] ) {
205
+ const latValues = gpsIFD [ 0x0002 ] ;
206
+ latRef = gpsIFD [ 0x0001 ] ;
207
+
208
+ console . log ( '[EXIF Viewer] GPS Latitude values:' , latValues ) ;
209
+ console . log ( '[EXIF Viewer] GPS Latitude ref:' , latRef ) ;
210
+
211
+ if ( Array . isArray ( latValues ) && latValues . length === 3 ) {
212
+ const degrees = latValues [ 0 ] [ 0 ] / latValues [ 0 ] [ 1 ] ;
213
+ const minutes = latValues [ 1 ] [ 0 ] / latValues [ 1 ] [ 1 ] ;
214
+ const seconds = latValues [ 2 ] [ 0 ] / latValues [ 2 ] [ 1 ] ;
215
+
216
+ latitude = this . convertDMSToDD ( degrees , minutes , seconds , latRef ) ;
217
+ console . log ( '[EXIF Viewer] Calculated GPS Latitude:' , latitude . toFixed ( 6 ) + '° ' + latRef ) ;
218
+ }
219
+ }
220
+
221
+ if ( gpsIFD [ 0x0004 ] && gpsIFD [ 0x0003 ] ) {
222
+ const lonValues = gpsIFD [ 0x0004 ] ;
223
+ lonRef = gpsIFD [ 0x0003 ] ;
224
+
225
+ console . log ( '[EXIF Viewer] GPS Longitude values:' , lonValues ) ;
226
+ console . log ( '[EXIF Viewer] GPS Longitude ref:' , lonRef ) ;
227
+
228
+ if ( Array . isArray ( lonValues ) && lonValues . length === 3 ) {
229
+ const degrees = lonValues [ 0 ] [ 0 ] / lonValues [ 0 ] [ 1 ] ;
230
+ const minutes = lonValues [ 1 ] [ 0 ] / lonValues [ 1 ] [ 1 ] ;
231
+ const seconds = lonValues [ 2 ] [ 0 ] / lonValues [ 2 ] [ 1 ] ;
232
+
233
+ longitude = this . convertDMSToDD ( degrees , minutes , seconds , lonRef ) ;
234
+ console . log ( '[EXIF Viewer] Calculated GPS Longitude:' , longitude . toFixed ( 6 ) + '° ' + lonRef ) ;
235
+ }
236
+ }
237
+
238
+ if ( latitude !== undefined && longitude !== undefined ) {
239
+ exifData . GPSLocation = `${ latitude . toFixed ( 6 ) } , ${ longitude . toFixed ( 6 ) } ` ;
240
+ console . log ( '[EXIF Viewer] Combined GPS Location:' , exifData . GPSLocation ) ;
241
+
242
+ delete exifData . GPSInfo ;
243
+ }
244
+
245
+ if ( gpsIFD [ 0x0007 ] ) {
246
+ const timeValues = gpsIFD [ 0x0007 ] ;
247
+ if ( Array . isArray ( timeValues ) && timeValues . length === 3 ) {
248
+ const hour = timeValues [ 0 ] [ 0 ] / timeValues [ 0 ] [ 1 ] ;
249
+ const minute = timeValues [ 1 ] [ 0 ] / timeValues [ 1 ] [ 1 ] ;
250
+ const second = timeValues [ 2 ] [ 0 ] / timeValues [ 2 ] [ 1 ] ;
251
+
252
+ console . log ( '[EXIF Viewer] GPS TimeStamp:' ,
253
+ `${ Math . floor ( hour ) } :${ Math . floor ( minute ) } :${ second . toFixed ( 1 ) } ` ) ;
254
+ }
255
+ }
256
+ } ,
257
+
181
258
readIFD : function ( data , offset , tiffStart , littleEndian ) {
182
259
const tags = { } ;
183
260
try {
184
261
const entries = this . getUint16 ( data , offset , littleEndian ) ;
262
+ console . log ( `[EXIF Viewer] Reading IFD with ${ entries } entries at offset ${ offset - tiffStart } ` ) ;
185
263
offset += 2 ;
186
264
for ( let i = 0 ; i < entries ; i ++ ) {
187
265
const tagId = this . getUint16 ( data , offset , littleEndian ) ;
@@ -192,26 +270,70 @@ var EXIF = {
192
270
valueOffset = tiffStart + this . getUint32 ( data , offset + 8 , littleEndian ) ;
193
271
}
194
272
let value ;
273
+
274
+ const isGPSTag = ( offset > tiffStart + 0x8825 ) && tagId < 0x100 ;
275
+
195
276
switch ( type ) {
196
- case 2 :
277
+ case 1 :
278
+ if ( count === 1 ) {
279
+ value = data [ valueOffset ] ;
280
+ } else {
281
+ value = Array . from ( data . slice ( valueOffset , valueOffset + count ) ) ;
282
+ }
283
+ break ;
284
+ case 2 :
197
285
value = new TextDecoder ( ) . decode ( data . slice ( valueOffset , valueOffset + count - 1 ) ) ;
198
286
break ;
199
- case 3 :
200
- value = this . getUint16 ( data , valueOffset , littleEndian ) ;
287
+ case 3 :
288
+ if ( count === 1 ) {
289
+ value = this . getUint16 ( data , valueOffset , littleEndian ) ;
290
+ } else {
291
+ value = [ ] ;
292
+ for ( let k = 0 ; k < count ; k ++ ) {
293
+ value . push ( this . getUint16 ( data , valueOffset + k * 2 , littleEndian ) ) ;
294
+ }
295
+ }
201
296
break ;
202
- case 4 :
203
- value = this . getUint32 ( data , valueOffset , littleEndian ) ;
297
+ case 4 :
298
+ if ( count === 1 ) {
299
+ value = this . getUint32 ( data , valueOffset , littleEndian ) ;
300
+ } else {
301
+ value = [ ] ;
302
+ for ( let k = 0 ; k < count ; k ++ ) {
303
+ value . push ( this . getUint32 ( data , valueOffset + k * 4 , littleEndian ) ) ;
304
+ }
305
+ }
204
306
break ;
205
- case 5 :
206
- value = [
207
- this . getUint32 ( data , valueOffset , littleEndian ) ,
208
- this . getUint32 ( data , valueOffset + 4 , littleEndian )
209
- ] ;
307
+ case 5 :
308
+ if ( count === 1 ) {
309
+ value = [
310
+ this . getUint32 ( data , valueOffset , littleEndian ) ,
311
+ this . getUint32 ( data , valueOffset + 4 , littleEndian )
312
+ ] ;
313
+ } else {
314
+ value = [ ] ;
315
+ for ( let k = 0 ; k < count ; k ++ ) {
316
+ value . push ( [
317
+ this . getUint32 ( data , valueOffset + k * 8 , littleEndian ) ,
318
+ this . getUint32 ( data , valueOffset + k * 8 + 4 , littleEndian )
319
+ ] ) ;
320
+ }
321
+ }
210
322
break ;
211
323
}
324
+
212
325
if ( value !== undefined ) {
213
326
tags [ tagId ] = value ;
214
- console . log ( `[EXIF Viewer] Tag ${ tagId . toString ( 16 ) } : ${ value } ` ) ;
327
+ if ( isGPSTag ) {
328
+ const gpsTagNames = {
329
+ 1 : 'GPSLatitudeRef' , 2 : 'GPSLatitude' , 3 : 'GPSLongitudeRef' , 4 : 'GPSLongitude' ,
330
+ 5 : 'GPSAltitudeRef' , 6 : 'GPSAltitude' , 7 : 'GPSTimeStamp'
331
+ } ;
332
+ const tagName = gpsTagNames [ tagId ] || `GPS Tag ${ tagId } ` ;
333
+ console . log ( `[EXIF Viewer] ${ tagName } (${ tagId . toString ( 16 ) } ): ${ JSON . stringify ( value ) } ` ) ;
334
+ } else {
335
+ console . log ( `[EXIF Viewer] Tag ${ tagId . toString ( 16 ) } : ${ value } ` ) ;
336
+ }
215
337
}
216
338
offset += 12 ;
217
339
}
0 commit comments