Skip to content

Commit fe64685

Browse files
committed
Merge pull request #30 from juliangut/master
Geolocation
2 parents f91e9ca + 61533c9 commit fe64685

File tree

6 files changed

+179
-13
lines changed

6 files changed

+179
-13
lines changed

lib/PHPExif/Adapter/Exiftool.php

+39-2
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,15 @@ public function getToolPath()
9696
*/
9797
public function getExifFromFile($file)
9898
{
99+
$gpsFormat = '%d deg %d\' %.4f\"';
100+
99101
$result = $this->getCliOutput(
100102
sprintf(
101-
'%1$s%3$s -j %2$s',
103+
'%1$s%3$s -j -c "%4$s" %2$s',
102104
$this->getToolPath(),
103105
$file,
104-
$this->numeric ? ' -n' : ''
106+
$this->numeric ? ' -n' : '',
107+
$gpsFormat
105108
)
106109
);
107110

@@ -172,6 +175,20 @@ public function mapData(array $source)
172175
$caption = $source['Caption-Abstract'];
173176
}
174177

178+
$gpsLocation = false;
179+
if (isset($source['GPSLatitudeRef']) && isset($source['GPSLongitudeRef'])) {
180+
$latitude = $this->extractGPSCoordinates($source['GPSLatitude']);
181+
$longitude = $this->extractGPSCoordinates($source['GPSLongitude']);
182+
183+
if ($latitude !== false && $longitude !== false) {
184+
$gpsLocation = sprintf(
185+
'%s,%s',
186+
(strtoupper($source['GPSLatitudeRef'][0]) === 'S' ? -1 : 1) * $latitude,
187+
(strtoupper($source['GPSLongitudeRef'][0]) === 'W' ? -1 : 1) * $longitude
188+
);
189+
}
190+
}
191+
175192
return array(
176193
Exif::APERTURE => (!isset($source['Aperture'])) ?
177194
false : sprintf('f/%01.1f', $source['Aperture']),
@@ -201,6 +218,26 @@ public function mapData(array $source)
201218
Exif::TITLE => (!isset($source['Title'])) ? false : $source['Title'],
202219
Exif::VERTICAL_RESOLUTION => (!isset($source['YResolution'])) ? false : $source['YResolution'],
203220
Exif::WIDTH => (!isset($source['ImageWidth'])) ? false : $source['ImageWidth'],
221+
Exif::GPS => $gpsLocation,
204222
);
205223
}
224+
225+
/**
226+
* Extract GPS coordinates from formatted string
227+
*
228+
* @param string $coordinates
229+
* @return array
230+
*/
231+
protected function extractGPSCoordinates($coordinates)
232+
{
233+
if ($this->numeric === true) {
234+
return abs((float) $coordinates);
235+
} else {
236+
if (!preg_match('!^([0-9.]+) deg ([0-9.]+)\' ([0-9.]+)"!', $coordinates, $matches)) {
237+
return false;
238+
}
239+
240+
return intval($matches[1]) + (intval($matches[2]) / 60) + (floatval($matches[3]) / 3600);
241+
}
242+
}
206243
}

lib/PHPExif/Adapter/Native.php

+40-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ public function getExifFromFile($file)
204204
*/
205205
public function getIptcData($file)
206206
{
207-
$size = getimagesize($file, $info);
207+
getimagesize($file, $info);
208208
$arrData = array();
209209
if (isset($info['APP13'])) {
210210
$iptc = iptcparse($info['APP13']);
@@ -262,6 +262,18 @@ public function mapData(array $source)
262262
$exposureTime = '1/' . round($denominator);
263263
}
264264

265+
$gpsLocation = false;
266+
if (isset($source['GPSLatitudeRef']) && isset($source['GPSLongitudeRef'])) {
267+
$latitude = $this->extractGPSCoordinate($source['GPSLatitude']);
268+
$longitude = $this->extractGPSCoordinate($source['GPSLongitude']);
269+
270+
$gpsLocation = sprintf(
271+
'%s,%s',
272+
(strtoupper($source['GPSLatitudeRef'][0]) === 'S' ? -1 : 1) * $latitude,
273+
(strtoupper($source['GPSLongitudeRef'][0]) === 'W' ? -1 : 1) * $longitude
274+
);
275+
}
276+
265277
return array(
266278
Exif::APERTURE => (!isset($source[self::SECTION_COMPUTED]['ApertureFNumber'])) ?
267279
false : $source[self::SECTION_COMPUTED]['ApertureFNumber'],
@@ -301,6 +313,7 @@ public function mapData(array $source)
301313
Exif::VERTICAL_RESOLUTION => $vertResolution,
302314
Exif::WIDTH => (!isset($source[self::SECTION_COMPUTED]['Width'])) ?
303315
false : $source[self::SECTION_COMPUTED]['Width'],
316+
Exif::GPS => $gpsLocation,
304317
);
305318

306319
$arrMapping = array(
@@ -345,4 +358,30 @@ public function mapData(array $source)
345358

346359
return $mappedData;
347360
}
361+
362+
/**
363+
* Extract GPS coordinates from components array
364+
*
365+
* @param array $components
366+
* @return float
367+
*/
368+
protected function extractGPSCoordinate(array $components)
369+
{
370+
$components = array_map(array($this, 'normalizeGPSComponent'), $components);
371+
372+
return intval($components[0]) + (intval($components[1]) / 60) + (floatval($components[2]) / 3600);
373+
}
374+
375+
/**
376+
* Normalize GPS coordinates components
377+
*
378+
* @param mixed $component
379+
* @return int|float
380+
*/
381+
protected function normalizeGPSComponent($component)
382+
{
383+
$parts = explode('/', $component);
384+
385+
return count($parts) === 1 ? $parts[0] : (int) reset($parts) / (int) end($parts);
386+
}
348387
}

lib/PHPExif/Exif.php

+22-7
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class Exif
4747
const TITLE = 'title';
4848
const VERTICAL_RESOLUTION = 'verticalResolution';
4949
const WIDTH = 'width';
50+
const GPS = 'gps';
5051

5152
/**
5253
* The mapped EXIF data
@@ -427,7 +428,7 @@ public function getCreationDate()
427428

428429
return $this->data[self::CREATION_DATE];
429430
}
430-
431+
431432
/**
432433
* Returns the colorspace, if it exists
433434
*
@@ -438,10 +439,10 @@ public function getColorSpace()
438439
if (!isset($this->data[self::COLORSPACE])) {
439440
return false;
440441
}
441-
442+
442443
return $this->data[self::COLORSPACE];
443444
}
444-
445+
445446
/**
446447
* Returns the mimetype, if it exists
447448
*
@@ -452,21 +453,21 @@ public function getMimeType()
452453
if (!isset($this->data[self::MIMETYPE])) {
453454
return false;
454455
}
455-
456+
456457
return $this->data[self::MIMETYPE];
457458
}
458-
459+
459460
/**
460461
* Returns the filesize, if it exists
461-
*
462+
*
462463
* @return integer
463464
*/
464465
public function getFileSize()
465466
{
466467
if (!isset($this->data[self::FILESIZE])) {
467468
return false;
468469
}
469-
470+
470471
return $this->data[self::FILESIZE];
471472
}
472473

@@ -483,4 +484,18 @@ public function getOrientation()
483484

484485
return $this->data[self::ORIENTATION];
485486
}
487+
488+
/**
489+
* Returns GPS coordinates, if it exists
490+
*
491+
* @return array|boolean
492+
*/
493+
public function getGPS()
494+
{
495+
if (!isset($this->data[self::GPS])) {
496+
return false;
497+
}
498+
499+
return $this->data[self::GPS];
500+
}
486501
}

tests/PHPExif/Adapter/ExiftoolTest.php

+42
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,48 @@ public function testMapDataFocalLengthIsCalculated()
128128
$this->assertEquals(18, $result[\PHPExif\Exif::FOCAL_LENGTH]);
129129
}
130130

131+
/**
132+
* @group exiftool
133+
* @covers \PHPExif\Adapter\Exiftool::setNumeric
134+
* @covers \PHPExif\Adapter\Exiftool::mapData
135+
* @covers \PHPExif\Adapter\Exiftool::extractGPSCoordinates
136+
*/
137+
public function testMapDataCreationDegGPSIsCalculated()
138+
{
139+
$this->adapter->setNumeric(false);
140+
$result = $this->adapter->mapData(
141+
array(
142+
'GPSLatitude' => '40 deg 20\' 0.42857" N',
143+
'GPSLatitudeRef' => 'North',
144+
'GPSLongitude' => '20 deg 10\' 2.33333" W',
145+
'GPSLongitudeRef' => 'West',
146+
)
147+
);
148+
149+
$expected = '40.333452380556,-20.167314813889';
150+
$this->assertEquals($expected, $result[\PHPExif\Exif::GPS]);
151+
}
152+
153+
/**
154+
* @group exiftool
155+
* @covers \PHPExif\Adapter\Exiftool::mapData
156+
* @covers \PHPExif\Adapter\Exiftool::extractGPSCoordinates
157+
*/
158+
public function testMapDataCreationNumericGPSIsCalculated()
159+
{
160+
$result = $this->adapter->mapData(
161+
array(
162+
'GPSLatitude' => '40.333452381',
163+
'GPSLatitudeRef' => 'North',
164+
'GPSLongitude' => '20.167314814',
165+
'GPSLongitudeRef' => 'West',
166+
)
167+
);
168+
169+
$expected = '40.333452381,-20.167314814';
170+
$this->assertEquals($expected, $result[\PHPExif\Exif::GPS]);
171+
}
172+
131173
/**
132174
* @group exiftool
133175
* @covers \PHPExif\Adapter\Exiftool::getCliOutput

tests/PHPExif/Adapter/NativeTest.php

+21
Original file line numberDiff line numberDiff line change
@@ -319,4 +319,25 @@ public function testMapDataCreationDateIsConvertedToDatetime()
319319

320320
$this->assertInstanceOf('DateTime', $result[\PHPExif\Exif::CREATION_DATE]);
321321
}
322+
323+
/**
324+
* @group native
325+
* @covers \PHPExif\Adapter\Native::mapData
326+
* @covers \PHPExif\Adapter\Native::extractGPSCoordinate
327+
* @covers \PHPExif\Adapter\Native::normalizeGPSComponent
328+
*/
329+
public function testMapDataCreationGPSIsCalculated()
330+
{
331+
$result = $this->adapter->mapData(
332+
array(
333+
'GPSLatitude' => array('40/1', '20/1', '15/35'),
334+
'GPSLatitudeRef' => 'N',
335+
'GPSLongitude' => array('20/1', '10/1', '35/15'),
336+
'GPSLongitudeRef' => 'W',
337+
)
338+
);
339+
340+
$expected = '40.333452380952,-20.167314814815';
341+
$this->assertEquals($expected, $result[\PHPExif\Exif::GPS]);
342+
}
322343
}

tests/PHPExif/ExifTest.php

+15-3
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ public function testGetJobtitle()
401401
$this->exif->setData($data);
402402
$this->assertEquals($expected, $this->exif->getJobtitle());
403403
}
404-
404+
405405
/**
406406
* @group exif
407407
* @covers \PHPExif\Exif::getColorSpace
@@ -413,7 +413,7 @@ public function testGetColorSpace()
413413
$this->exif->setData($data);
414414
$this->assertEquals($expected, $this->exif->getColorSpace());
415415
}
416-
416+
417417
/**
418418
* @group exif
419419
* @covers \PHPExif\Exif::getMimeType
@@ -425,7 +425,7 @@ public function testGetMimeType()
425425
$this->exif->setData($data);
426426
$this->assertEquals($expected, $this->exif->getMimeType());
427427
}
428-
428+
429429
/**
430430
* @group exif
431431
* @covers \PHPExif\Exif::getFileSize
@@ -446,6 +446,18 @@ public function testGetOrientation()
446446
$this->assertEquals($expected, $this->exif->getOrientation());
447447
}
448448

449+
/**
450+
* @group exif
451+
* @covers \PHPExif\Exif::getGPS
452+
*/
453+
public function testGetGPS()
454+
{
455+
$expected = '40.333452380556,-20.167314813889';
456+
$data[\PHPExif\Exif::GPS] = $expected;
457+
$this->exif->setData($data);
458+
$this->assertEquals($expected, $this->exif->getGPS());
459+
}
460+
449461
/**
450462
* Test that the values returned by both adapters are equal
451463
*/

0 commit comments

Comments
 (0)