diff --git a/lib/PHPExif/Adapter/Exiftool.php b/lib/PHPExif/Adapter/Exiftool.php index 156de43..8e6bbe6 100644 --- a/lib/PHPExif/Adapter/Exiftool.php +++ b/lib/PHPExif/Adapter/Exiftool.php @@ -96,12 +96,15 @@ public function getToolPath() */ public function getExifFromFile($file) { + $gpsFormat = '%d deg %d\' %.4f\"'; + $result = $this->getCliOutput( sprintf( - '%1$s%3$s -j %2$s', + '%1$s%3$s -j -c "%4$s" %2$s', $this->getToolPath(), $file, - $this->numeric ? ' -n' : '' + $this->numeric ? ' -n' : '', + $gpsFormat ) ); @@ -172,6 +175,20 @@ public function mapData(array $source) $caption = $source['Caption-Abstract']; } + $gpsLocation = false; + if (isset($source['GPSLatitudeRef']) && isset($source['GPSLongitudeRef'])) { + $latitude = $this->extractGPSCoordinates($source['GPSLatitude']); + $longitude = $this->extractGPSCoordinates($source['GPSLongitude']); + + if ($latitude !== false && $longitude !== false) { + $gpsLocation = sprintf( + '%s,%s', + (strtoupper($source['GPSLatitudeRef'][0]) === 'S' ? -1 : 1) * $latitude, + (strtoupper($source['GPSLongitudeRef'][0]) === 'W' ? -1 : 1) * $longitude + ); + } + } + return array( Exif::APERTURE => (!isset($source['Aperture'])) ? false : sprintf('f/%01.1f', $source['Aperture']), @@ -201,6 +218,26 @@ public function mapData(array $source) Exif::TITLE => (!isset($source['Title'])) ? false : $source['Title'], Exif::VERTICAL_RESOLUTION => (!isset($source['YResolution'])) ? false : $source['YResolution'], Exif::WIDTH => (!isset($source['ImageWidth'])) ? false : $source['ImageWidth'], + Exif::GPS => $gpsLocation, ); } + + /** + * Extract GPS coordinates from formatted string + * + * @param string $coordinates + * @return array + */ + protected function extractGPSCoordinates($coordinates) + { + if ($this->numeric === true) { + return abs((float) $coordinates); + } else { + if (!preg_match('!^([0-9.]+) deg ([0-9.]+)\' ([0-9.]+)"!', $coordinates, $matches)) { + return false; + } + + return intval($matches[1]) + (intval($matches[2]) / 60) + (floatval($matches[3]) / 3600); + } + } } diff --git a/lib/PHPExif/Adapter/Native.php b/lib/PHPExif/Adapter/Native.php index 7b3822f..a7d6756 100644 --- a/lib/PHPExif/Adapter/Native.php +++ b/lib/PHPExif/Adapter/Native.php @@ -204,7 +204,7 @@ public function getExifFromFile($file) */ public function getIptcData($file) { - $size = getimagesize($file, $info); + getimagesize($file, $info); $arrData = array(); if (isset($info['APP13'])) { $iptc = iptcparse($info['APP13']); @@ -262,6 +262,18 @@ public function mapData(array $source) $exposureTime = '1/' . round($denominator); } + $gpsLocation = false; + if (isset($source['GPSLatitudeRef']) && isset($source['GPSLongitudeRef'])) { + $latitude = $this->extractGPSCoordinate($source['GPSLatitude']); + $longitude = $this->extractGPSCoordinate($source['GPSLongitude']); + + $gpsLocation = sprintf( + '%s,%s', + (strtoupper($source['GPSLatitudeRef'][0]) === 'S' ? -1 : 1) * $latitude, + (strtoupper($source['GPSLongitudeRef'][0]) === 'W' ? -1 : 1) * $longitude + ); + } + return array( Exif::APERTURE => (!isset($source[self::SECTION_COMPUTED]['ApertureFNumber'])) ? false : $source[self::SECTION_COMPUTED]['ApertureFNumber'], @@ -301,6 +313,7 @@ public function mapData(array $source) Exif::VERTICAL_RESOLUTION => $vertResolution, Exif::WIDTH => (!isset($source[self::SECTION_COMPUTED]['Width'])) ? false : $source[self::SECTION_COMPUTED]['Width'], + Exif::GPS => $gpsLocation, ); $arrMapping = array( @@ -345,4 +358,30 @@ public function mapData(array $source) return $mappedData; } + + /** + * Extract GPS coordinates from components array + * + * @param array $components + * @return float + */ + protected function extractGPSCoordinate(array $components) + { + $components = array_map(array($this, 'normalizeGPSComponent'), $components); + + return intval($components[0]) + (intval($components[1]) / 60) + (floatval($components[2]) / 3600); + } + + /** + * Normalize GPS coordinates components + * + * @param mixed $component + * @return int|float + */ + protected function normalizeGPSComponent($component) + { + $parts = explode('/', $component); + + return count($parts) === 1 ? $parts[0] : (int) reset($parts) / (int) end($parts); + } } diff --git a/lib/PHPExif/Exif.php b/lib/PHPExif/Exif.php index fb58985..4f39c6d 100755 --- a/lib/PHPExif/Exif.php +++ b/lib/PHPExif/Exif.php @@ -47,6 +47,7 @@ class Exif const TITLE = 'title'; const VERTICAL_RESOLUTION = 'verticalResolution'; const WIDTH = 'width'; + const GPS = 'gps'; /** * The mapped EXIF data @@ -427,7 +428,7 @@ public function getCreationDate() return $this->data[self::CREATION_DATE]; } - + /** * Returns the colorspace, if it exists * @@ -438,10 +439,10 @@ public function getColorSpace() if (!isset($this->data[self::COLORSPACE])) { return false; } - + return $this->data[self::COLORSPACE]; } - + /** * Returns the mimetype, if it exists * @@ -452,13 +453,13 @@ public function getMimeType() if (!isset($this->data[self::MIMETYPE])) { return false; } - + return $this->data[self::MIMETYPE]; } - + /** * Returns the filesize, if it exists - * + * * @return integer */ public function getFileSize() @@ -466,7 +467,7 @@ public function getFileSize() if (!isset($this->data[self::FILESIZE])) { return false; } - + return $this->data[self::FILESIZE]; } @@ -483,4 +484,18 @@ public function getOrientation() return $this->data[self::ORIENTATION]; } + + /** + * Returns GPS coordinates, if it exists + * + * @return array|boolean + */ + public function getGPS() + { + if (!isset($this->data[self::GPS])) { + return false; + } + + return $this->data[self::GPS]; + } } diff --git a/tests/PHPExif/Adapter/ExiftoolTest.php b/tests/PHPExif/Adapter/ExiftoolTest.php index 37c0f0d..5e8783b 100644 --- a/tests/PHPExif/Adapter/ExiftoolTest.php +++ b/tests/PHPExif/Adapter/ExiftoolTest.php @@ -128,6 +128,48 @@ public function testMapDataFocalLengthIsCalculated() $this->assertEquals(18, $result[\PHPExif\Exif::FOCAL_LENGTH]); } + /** + * @group exiftool + * @covers \PHPExif\Adapter\Exiftool::setNumeric + * @covers \PHPExif\Adapter\Exiftool::mapData + * @covers \PHPExif\Adapter\Exiftool::extractGPSCoordinates + */ + public function testMapDataCreationDegGPSIsCalculated() + { + $this->adapter->setNumeric(false); + $result = $this->adapter->mapData( + array( + 'GPSLatitude' => '40 deg 20\' 0.42857" N', + 'GPSLatitudeRef' => 'North', + 'GPSLongitude' => '20 deg 10\' 2.33333" W', + 'GPSLongitudeRef' => 'West', + ) + ); + + $expected = '40.333452380556,-20.167314813889'; + $this->assertEquals($expected, $result[\PHPExif\Exif::GPS]); + } + + /** + * @group exiftool + * @covers \PHPExif\Adapter\Exiftool::mapData + * @covers \PHPExif\Adapter\Exiftool::extractGPSCoordinates + */ + public function testMapDataCreationNumericGPSIsCalculated() + { + $result = $this->adapter->mapData( + array( + 'GPSLatitude' => '40.333452381', + 'GPSLatitudeRef' => 'North', + 'GPSLongitude' => '20.167314814', + 'GPSLongitudeRef' => 'West', + ) + ); + + $expected = '40.333452381,-20.167314814'; + $this->assertEquals($expected, $result[\PHPExif\Exif::GPS]); + } + /** * @group exiftool * @covers \PHPExif\Adapter\Exiftool::getCliOutput diff --git a/tests/PHPExif/Adapter/NativeTest.php b/tests/PHPExif/Adapter/NativeTest.php index 35c427e..381f506 100755 --- a/tests/PHPExif/Adapter/NativeTest.php +++ b/tests/PHPExif/Adapter/NativeTest.php @@ -319,4 +319,25 @@ public function testMapDataCreationDateIsConvertedToDatetime() $this->assertInstanceOf('DateTime', $result[\PHPExif\Exif::CREATION_DATE]); } + + /** + * @group native + * @covers \PHPExif\Adapter\Native::mapData + * @covers \PHPExif\Adapter\Native::extractGPSCoordinate + * @covers \PHPExif\Adapter\Native::normalizeGPSComponent + */ + public function testMapDataCreationGPSIsCalculated() + { + $result = $this->adapter->mapData( + array( + 'GPSLatitude' => array('40/1', '20/1', '15/35'), + 'GPSLatitudeRef' => 'N', + 'GPSLongitude' => array('20/1', '10/1', '35/15'), + 'GPSLongitudeRef' => 'W', + ) + ); + + $expected = '40.333452380952,-20.167314814815'; + $this->assertEquals($expected, $result[\PHPExif\Exif::GPS]); + } } diff --git a/tests/PHPExif/ExifTest.php b/tests/PHPExif/ExifTest.php index d9f5885..8f51f21 100755 --- a/tests/PHPExif/ExifTest.php +++ b/tests/PHPExif/ExifTest.php @@ -401,7 +401,7 @@ public function testGetJobtitle() $this->exif->setData($data); $this->assertEquals($expected, $this->exif->getJobtitle()); } - + /** * @group exif * @covers \PHPExif\Exif::getColorSpace @@ -413,7 +413,7 @@ public function testGetColorSpace() $this->exif->setData($data); $this->assertEquals($expected, $this->exif->getColorSpace()); } - + /** * @group exif * @covers \PHPExif\Exif::getMimeType @@ -425,7 +425,7 @@ public function testGetMimeType() $this->exif->setData($data); $this->assertEquals($expected, $this->exif->getMimeType()); } - + /** * @group exif * @covers \PHPExif\Exif::getFileSize @@ -446,6 +446,18 @@ public function testGetOrientation() $this->assertEquals($expected, $this->exif->getOrientation()); } + /** + * @group exif + * @covers \PHPExif\Exif::getGPS + */ + public function testGetGPS() + { + $expected = '40.333452380556,-20.167314813889'; + $data[\PHPExif\Exif::GPS] = $expected; + $this->exif->setData($data); + $this->assertEquals($expected, $this->exif->getGPS()); + } + /** * Test that the values returned by both adapters are equal */