Skip to content

Commit 476367b

Browse files
author
Tom Van Herreweghe
committed
Add GPS code
Signed-off-by: Tom Van Herreweghe <[email protected]>
1 parent 795fab5 commit 476367b

File tree

7 files changed

+152
-2
lines changed

7 files changed

+152
-2
lines changed

Diff for: lib/PHPExif/Adapter/Exiftool.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,23 @@ public function getToolPath()
100100
*/
101101
public function getExifFromFile($file)
102102
{
103+
$gpsFormat = '%d deg %d\' %.4f\"';
104+
103105
$result = $this->getCliOutput(
104106
sprintf(
105-
'%1$s%3$s -j %2$s',
107+
'%1$s%3$s -j -c "%4$s" %2$s',
106108
$this->getToolPath(),
107109
$file,
108-
$this->numeric ? ' -n' : ''
110+
$this->numeric ? ' -n' : '',
111+
$gpsFormat
109112
)
110113
);
111114

112115
$data = json_decode($result, true);
113116

114117
// map the data:
115118
$mapper = $this->getMapper();
119+
$mapper->setNumeric($this->numeric);
116120
$mappedData = $mapper->mapRawData(reset($data));
117121

118122
// hydrate a new Exif object

Diff for: lib/PHPExif/Exif.php

+28
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
@@ -808,4 +809,31 @@ public function setOrientation($value)
808809

809810
return $this;
810811
}
812+
813+
/**
814+
* Returns GPS coordinates, if it exists
815+
*
816+
* @return array|boolean
817+
*/
818+
public function getGPS()
819+
{
820+
if (!isset($this->data[self::GPS])) {
821+
return false;
822+
}
823+
824+
return $this->data[self::GPS];
825+
}
826+
827+
/**
828+
* Sets the GPS coordinates
829+
*
830+
* @param array $value
831+
* @return \PHPExif\Exif
832+
*/
833+
public function setGPS(array $value)
834+
{
835+
$this->data[self::GPS] = $value;
836+
837+
return $this;
838+
}
811839
}

Diff for: lib/PHPExif/Mapper/Exiftool.php

+66
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class Exiftool implements MapperInterface
5050
const TITLE = 'Title';
5151
const XRESOLUTION = 'XResolution';
5252
const YRESOLUTION = 'YResolution';
53+
const GPSLATITUDEREF = 'GPSLatitudeRef';
54+
const GPSLONGITUDEREF = 'GPSLongitudeRef';
5355

5456
/**
5557
* Maps the ExifTool fields to the fields of
@@ -84,8 +86,28 @@ class Exiftool implements MapperInterface
8486
self::YRESOLUTION => Exif::VERTICAL_RESOLUTION,
8587
self::IMAGEWIDTH => Exif::WIDTH,
8688
self::CAPTIONABSTRACT => Exif::CAPTION,
89+
self::GPSLATITUDEREF => Exif::GPS,
90+
self::GPSLONGITUDEREF => Exif::GPS,
8791
);
8892

93+
/**
94+
* @var bool
95+
*/
96+
protected $numeric = true;
97+
98+
/**
99+
* Mutator method for the numeric property
100+
*
101+
* @param bool $numeric
102+
* @return \PHPExif\Mapper\Exiftool
103+
*/
104+
public function setNumeric($numeric)
105+
{
106+
$this->numeric = (bool)$numeric;
107+
108+
return $this;
109+
}
110+
89111
/**
90112
* Maps the array of raw source data to the correct
91113
* fields for the \PHPExif\Exif class
@@ -96,6 +118,7 @@ class Exiftool implements MapperInterface
96118
public function mapRawData(array $data)
97119
{
98120
$mappedData = array();
121+
$gpsData = array();
99122
foreach ($data as $field => $value) {
100123
if (!array_key_exists($field, $this->map)) {
101124
// silently ignore unknown fields
@@ -122,12 +145,55 @@ public function mapRawData(array $data)
122145
$focalLengthParts = explode(' ', $value);
123146
$value = (int) reset($focalLengthParts);
124147
break;
148+
case self::GPSLATITUDEREF:
149+
$gpsData['lat'] = $this->extractGPSCoordinates($value);
150+
break;
151+
case self::GPSLONGITUDEREF:
152+
$gpsData['lon'] = $this->extractGPSCoordinates($value);
153+
break;
125154
}
126155

127156
// set end result
128157
$mappedData[$key] = $value;
129158
}
130159

160+
// add GPS coordinates, if available
161+
if (count($gpsData) === 2) {
162+
$latitude = $gpsData['lat'];
163+
$longitude = $gpsData['lon'];
164+
165+
if ($latitude !== false && $longitude !== false) {
166+
$gpsLocation = sprintf(
167+
'%s,%s',
168+
(strtoupper($data[self::GPSLATITUDEREF][0]) === 'S' ? -1 : 1) * $latitude,
169+
(strtoupper($data[self::GPSLONGITUDEREF][0]) === 'W' ? -1 : 1) * $longitude
170+
);
171+
172+
$key = $this->map[self::GPSLATITUDEREF];
173+
174+
$mappedData[$key] = $gpsLocation;
175+
}
176+
}
177+
131178
return $mappedData;
132179
}
180+
181+
/**
182+
* Extract GPS coordinates from formatted string
183+
*
184+
* @param string $coordinates
185+
* @return array
186+
*/
187+
protected function extractGPSCoordinates($coordinates)
188+
{
189+
if ($this->numeric === true) {
190+
return abs((float) $coordinates);
191+
} else {
192+
if (!preg_match('!^([0-9.]+) deg ([0-9.]+)\' ([0-9.]+)"!', $coordinates, $matches)) {
193+
return false;
194+
}
195+
196+
return intval($matches[1]) + (intval($matches[2]) / 60) + (floatval($matches[3]) / 3600);
197+
}
198+
}
133199
}

Diff for: lib/PHPExif/Mapper/Native.php

+46
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class Native implements MapperInterface
4949
const WIDTH = 'Width';
5050
const XRESOLUTION = 'XResolution';
5151
const YRESOLUTION = 'YResolution';
52+
const GPSLATITUDEREF = 'GPSLatitudeRef';
53+
const GPSLONGITUDEREF = 'GPSLongitudeRef';
5254

5355
const SECTION_FILE = 'FILE';
5456
const SECTION_COMPUTED = 'COMPUTED';
@@ -107,6 +109,8 @@ class Native implements MapperInterface
107109
self::SOFTWARE => Exif::SOFTWARE,
108110
self::XRESOLUTION => Exif::HORIZONTAL_RESOLUTION,
109111
self::YRESOLUTION => Exif::VERTICAL_RESOLUTION,
112+
self::GPSLATITUDEREF => Exif::GPS,
113+
self::GPSLONGITUDEREF => Exif::GPS,
110114
);
111115

112116
/**
@@ -119,6 +123,7 @@ class Native implements MapperInterface
119123
public function mapRawData(array $data)
120124
{
121125
$mappedData = array();
126+
$gpsData = array();
122127
foreach ($data as $field => $value) {
123128
if ($this->isSection($field) && is_array($value)) {
124129
$subData = $this->mapRawData($value);
@@ -157,12 +162,27 @@ public function mapRawData(array $data)
157162
$resolutionParts = explode('/', $value);
158163
$value = (int)reset($resolutionParts);
159164
break;
165+
case self::GPSLATITUDEREF:
166+
$gpsData['lat'] = $this->extractGPSCoordinate($value);
167+
break;
168+
case self::GPSLONGITUDEREF:
169+
$gpsData['lon'] = $this->extractGPSCoordinate($value);
170+
break;
160171
}
161172

162173
// set end result
163174
$mappedData[$key] = $value;
164175
}
165176

177+
if (count($gpsData) === 2) {
178+
$gpsLocation = sprintf(
179+
'%s,%s',
180+
(strtoupper($data[self::GPSLATITUDEREF][0]) === 'S' ? -1 : 1) * $gpsData['lat'],
181+
(strtoupper($data[self::GPSLONGITUDEREF][0]) === 'W' ? -1 : 1) * $gpsData['lon']
182+
);
183+
$mappedData[Exif::GPS] = $gpsLocation;
184+
}
185+
166186
return $mappedData;
167187
}
168188

@@ -176,4 +196,30 @@ protected function isSection($field)
176196
{
177197
return (in_array($field, $this->sections));
178198
}
199+
200+
/**
201+
* Extract GPS coordinates from components array
202+
*
203+
* @param array $components
204+
* @return float
205+
*/
206+
protected function extractGPSCoordinate(array $components)
207+
{
208+
$components = array_map(array($this, 'normalizeGPSComponent'), $components);
209+
210+
return intval($components[0]) + (intval($components[1]) / 60) + (floatval($components[2]) / 3600);
211+
}
212+
213+
/**
214+
* Normalize GPS coordinates components
215+
*
216+
* @param mixed $component
217+
* @return int|float
218+
*/
219+
protected function normalizeGPSComponent($component)
220+
{
221+
$parts = explode('/', $component);
222+
223+
return count($parts) === 1 ? $parts[0] : (int) reset($parts) / (int) end($parts);
224+
}
179225
}

Diff for: tests/PHPExif/ExifTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ public function testMutatorMethodsSetInProperty()
549549
break;
550550
case 'focalDistance':
551551
$setter = 'setFocusDistance';
552+
case 'gps':
553+
break;
552554
default:
553555
$this->exif->$setter($expected);
554556
$propertyValue = $reflProp->getValue($this->exif);

Diff for: tests/PHPExif/Mapper/ExiftoolMapperTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public function testMapRawDataMapsFieldsCorrectly()
4747
unset($map[\PHPExif\Mapper\Exiftool::CREATEDATE]);
4848
unset($map[\PHPExif\Mapper\Exiftool::EXPOSURETIME]);
4949
unset($map[\PHPExif\Mapper\Exiftool::FOCALLENGTH]);
50+
unset($map[\PHPExif\Mapper\Exiftool::GPSLATITUDEREF]);
51+
unset($map[\PHPExif\Mapper\Exiftool::GPSLONGITUDEREF]);
5052

5153
// create raw data
5254
$keys = array_keys($map);

Diff for: tests/PHPExif/Mapper/NativeMapperTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public function testMapRawDataMapsFieldsCorrectly()
4747
unset($map[\PHPExif\Mapper\Native::FOCALLENGTH]);
4848
unset($map[\PHPExif\Mapper\Native::XRESOLUTION]);
4949
unset($map[\PHPExif\Mapper\Native::YRESOLUTION]);
50+
unset($map[\PHPExif\Mapper\Native::GPSLATITUDEREF]);
51+
unset($map[\PHPExif\Mapper\Native::GPSLONGITUDEREF]);
5052

5153
// create raw data
5254
$keys = array_keys($map);

0 commit comments

Comments
 (0)