From 458a373cb380d75888c73faa11407f0f38d59ccb Mon Sep 17 00:00:00 2001 From: Louis Chemineau Date: Thu, 11 Aug 2022 19:26:03 +0200 Subject: [PATCH] Extract GPS data from EXIF Signed-off-by: Louis Chemineau --- .../Metadata/Provider/ExifProvider.php | 75 +++++++++++++++---- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/lib/private/Metadata/Provider/ExifProvider.php b/lib/private/Metadata/Provider/ExifProvider.php index 892671556b368..bb252a6ec4104 100644 --- a/lib/private/Metadata/Provider/ExifProvider.php +++ b/lib/private/Metadata/Provider/ExifProvider.php @@ -16,8 +16,23 @@ public static function isAvailable(): bool { } public function execute(File $file): array { + $exifData = []; $fileDescriptor = $file->fopen('rb'); - $data = exif_read_data($fileDescriptor, 'COMPUTED', true); + + // Copy image data into tmp stream as exif_read_data don't work properly with our streams wrappers. + $nativeFileDescriptorStream = fopen('php://temp', 'rw'); + $result = stream_copy_to_stream( + $fileDescriptor, + $nativeFileDescriptorStream, + ); + + if ($result === false) { + fclose($nativeFileDescriptorStream); + throw new \Exception("Failed to copy into tmp stream from fileid: " . $file->getId()); + } + + $data = exif_read_data($nativeFileDescriptorStream, 'ANY_TAG', true); + fclose($nativeFileDescriptorStream); $size = new FileMetadata(); $size->setGroupName('size'); @@ -31,29 +46,63 @@ public function execute(File $file): array { 'width' => $sizeResult[0], 'height' => $sizeResult[1], ]); + + $exifData['size'] = $size; } - return [ - 'size' => $size, - ]; + } elseif (array_key_exists('COMPUTED', $data)) { + if (array_key_exists('Width', $data['COMPUTED']) && array_key_exists('Height', $data['COMPUTED'])) { + $size->setMetadata([ + 'width' => $data['COMPUTED']['Width'], + 'height' => $data['COMPUTED']['Height'], + ]); + + $exifData['size'] = $size; + } } - if (array_key_exists('COMPUTED', $data) - && array_key_exists('Width', $data['COMPUTED']) - && array_key_exists('Height', $data['COMPUTED']) + if ($data && array_key_exists('GPS', $data) + && array_key_exists('GPSLatitude', $data['GPS']) && array_key_exists('GPSLatitudeRef', $data['GPS']) + && array_key_exists('GPSLongitude', $data['GPS']) && array_key_exists('GPSLongitudeRef', $data['GPS']) ) { - $size->setMetadata([ - 'width' => $data['COMPUTED']['Width'], - 'height' => $data['COMPUTED']['Height'], + $gps = new FileMetadata(); + $gps->setGroupName('gps'); + $gps->setId($file->getId()); + $gps->setMetadata([ + 'coordinate' => [ + 'latitude' => $this->gpsDegreesToDecimal($data['GPS']['GPSLatitude'], $data['GPS']['GPSLatitudeRef']), + 'longitude' => $this->gpsDegreesToDecimal($data['GPS']['GPSLongitude'], $data['GPS']['GPSLongitudeRef']), + ], ]); + + $exifData['gps'] = $gps; } - return [ - 'size' => $size, - ]; + return $exifData; } public static function getMimetypesSupported(): string { return '/image\/.*/'; } + + /** + * @param array|string $coordinate + */ + private static function gpsDegreesToDecimal(array $coordinates, string $hemisphere): float { + if (is_string($coordinates)) { + $coordinates = array_map("trim", explode(",", $coordinates)); + } + + if (count($coordinates) !== 3) { + throw new \Exception('Invalid coordinate format: ' . $coordinates); + } + + [$degrees, $minutes, $seconds] = array_map(function (string $rawDegree) { + $parts = explode('/', $rawDegree); + return floatval($parts[0])/floatval($parts[1] ?? 1); + }, $coordinates); + + $sign = ($hemisphere === 'W' || $hemisphere === 'S') ? -1 : 1; + return $sign * ($degrees + $minutes/60 + $seconds/3600); + } }