PHP提取GPS EXIF数据

Posted

技术标签:

【中文标题】PHP提取GPS EXIF数据【英文标题】:PHP extract GPS EXIF data 【发布时间】:2011-02-01 08:22:38 【问题描述】:

我想使用 php 从图片中提取 GPS EXIF 标签。 我正在使用exif_read_data(),它返回一个包含所有标签+数据的数组:

GPS.GPSLatitudeRef: N
GPS.GPSLatitude:Array ( [0] => 46/1 [1] => 5403/100 [2] => 0/1 ) 
GPS.GPSLongitudeRef: E
GPS.GPSLongitude:Array ( [0] => 7/1 [1] => 880/100 [2] => 0/1 ) 
GPS.GPSAltitudeRef: 
GPS.GPSAltitude: 634/1

我不知道如何解释 46/1 5403/100 和 0/1 ? 46 可能是 46°,但其余的,尤其是 0/1 呢?

angle/1 5403/100 0/1

这个结构是关于什么的?

如何将它们转换为“标准”(如***中的 46°56′48″N 7°26′39″E)?我想将这些坐标传递给 google maps api 以在地图上显示图片位置!

【问题讨论】:

@Kami:我用一些代码更新了我的答案 【参考方案1】:

这是我的修改版。其他的对我不起作用。它会给你十进制版本的 GPS 坐标。

处理EXIF数据的代码:

$exif = exif_read_data($filename);
$lon = getGps($exif["GPSLongitude"], $exif['GPSLongitudeRef']);
$lat = getGps($exif["GPSLatitude"], $exif['GPSLatitudeRef']);
var_dump($lat, $lon);

以这种格式打印出来:

float(-33.8751666667)
float(151.207166667)

以下是函数:

function getGps($exifCoord, $hemi) 

    $degrees = count($exifCoord) > 0 ? gps2Num($exifCoord[0]) : 0;
    $minutes = count($exifCoord) > 1 ? gps2Num($exifCoord[1]) : 0;
    $seconds = count($exifCoord) > 2 ? gps2Num($exifCoord[2]) : 0;

    $flip = ($hemi == 'W' or $hemi == 'S') ? -1 : 1;

    return $flip * ($degrees + $minutes / 60 + $seconds / 3600);



function gps2Num($coordPart) 

    $parts = explode('/', $coordPart);

    if (count($parts) <= 0)
        return 0;

    if (count($parts) == 1)
        return $parts[0];

    return floatval($parts[0]) / floatval($parts[1]);

【讨论】:

我发现最新版本的 PHP 在 exif 对象中有一个额外的数组层,要求它们像这样调用:getGps($exif['GPS']["GPSLongitude"], $exif ['GPS']['GPSLongitudeRef']) @OrbitingEden 此行为由第三个参数启用。默认禁用。【参考方案2】:

这是 Gerald Kaszuba 代码的重构版本(目前是最广泛接受的答案)。结果应该是相同的,但我做了几个微优化并将两个独立的功能合二为一。在我的基准测试中,这个版本将运行时间缩短了大约 5 微秒,这对于大多数应用程序来说可能可以忽略不计,但对于涉及大量重复计算的应用程序可能很有用。

$exif = exif_read_data($filename);
$latitude = gps($exif["GPSLatitude"], $exif['GPSLatitudeRef']);
$longitude = gps($exif["GPSLongitude"], $exif['GPSLongitudeRef']);

function gps($coordinate, $hemisphere) 
  if (is_string($coordinate)) 
    $coordinate = array_map("trim", explode(",", $coordinate));
  
  for ($i = 0; $i < 3; $i++) 
    $part = explode('/', $coordinate[$i]);
    if (count($part) == 1) 
      $coordinate[$i] = $part[0];
     else if (count($part) == 2) 
      $coordinate[$i] = floatval($part[0])/floatval($part[1]);
     else 
      $coordinate[$i] = 0;
    
  
  list($degrees, $minutes, $seconds) = $coordinate;
  $sign = ($hemisphere == 'W' || $hemisphere == 'S') ? -1 : 1;
  return $sign * ($degrees + $minutes/60 + $seconds/3600);

【讨论】:

thhiiiisss。几天来,我一直在盲目地从堆栈溢出中复制和粘贴 PHP exif 代码 sn-ps,而没有自己做任何研究。 (真的)而且他们的范围从有点错误到真的非常错误。产生不同程度的错误数据。 这个工作。我做的一件事是根据已知的良好来源验证输出。而这个是100%正确的。 10/10 会再次复制意大利面。 谢谢大卫(和杰拉德)。您的修改是迄今为止最准确的,并且可以 100% 开箱即用。我只想指出的是 exif_read_data() 现在以 $exif['GPS']['GPSLatitude'] 的形式返回结果 - 所以你可以稍微修改你的调用代码。【参考方案3】:

根据http://en.wikipedia.org/wiki/Geotagging,( [0] =&gt; 46/1 [1] =&gt; 5403/100 [2] =&gt; 0/1 ) 应该表示 46/1 度,5403/100 分钟,0/1 秒,即 46°54.03′0″N。归一化秒数得到 46°54′1.8″N。

下面的代码应该可以工作,只要您没有得到负坐标(假设您将 N/S 和 E/W 作为单独的坐标,您不应该有负坐标)。如果有错误请告诉我(我目前手头没有 PHP 环境)。

//Pass in GPS.GPSLatitude or GPS.GPSLongitude or something in that format
function getGps($exifCoord)

  $degrees = count($exifCoord) > 0 ? gps2Num($exifCoord[0]) : 0;
  $minutes = count($exifCoord) > 1 ? gps2Num($exifCoord[1]) : 0;
  $seconds = count($exifCoord) > 2 ? gps2Num($exifCoord[2]) : 0;

  //normalize
  $minutes += 60 * ($degrees - floor($degrees));
  $degrees = floor($degrees);

  $seconds += 60 * ($minutes - floor($minutes));
  $minutes = floor($minutes);

  //extra normalization, probably not necessary unless you get weird data
  if($seconds >= 60)
  
    $minutes += floor($seconds/60.0);
    $seconds -= 60*floor($seconds/60.0);
  

  if($minutes >= 60)
  
    $degrees += floor($minutes/60.0);
    $minutes -= 60*floor($minutes/60.0);
  

  return array('degrees' => $degrees, 'minutes' => $minutes, 'seconds' => $seconds);


function gps2Num($coordPart)

  $parts = explode('/', $coordPart);

  if(count($parts) <= 0)// jic
    return 0;
  if(count($parts) == 1)
    return $parts[0];

  return floatval($parts[0]) / floatval($parts[1]);

【讨论】:

【参考方案4】:

我知道很久以前就有人问过这个问题,但是我在谷歌搜索时遇到了这个问题,这里提出的解决方案对我不起作用。因此,经过进一步搜索,这对我有用。

我把它放在这里是为了让任何通过谷歌搜索来到这里的人都可以找到解决同一问题的不同方法:

function triphoto_getGPS($fileName, $assoc = false)

    //get the EXIF
    $exif = exif_read_data($fileName);

    //get the Hemisphere multiplier
    $LatM = 1; $LongM = 1;
    if($exif["GPSLatitudeRef"] == 'S')
    
    $LatM = -1;
    
    if($exif["GPSLongitudeRef"] == 'W')
    
    $LongM = -1;
    

    //get the GPS data
    $gps['LatDegree']=$exif["GPSLatitude"][0];
    $gps['LatMinute']=$exif["GPSLatitude"][1];
    $gps['LatgSeconds']=$exif["GPSLatitude"][2];
    $gps['LongDegree']=$exif["GPSLongitude"][0];
    $gps['LongMinute']=$exif["GPSLongitude"][1];
    $gps['LongSeconds']=$exif["GPSLongitude"][2];

    //convert strings to numbers
    foreach($gps as $key => $value)
    
    $pos = strpos($value, '/');
    if($pos !== false)
    
        $temp = explode('/',$value);
        $gps[$key] = $temp[0] / $temp[1];
    
    

    //calculate the decimal degree
    $result['latitude'] = $LatM * ($gps['LatDegree'] + ($gps['LatMinute'] / 60) + ($gps['LatgSeconds'] / 3600));
    $result['longitude'] = $LongM * ($gps['LongDegree'] + ($gps['LongMinute'] / 60) + ($gps['LongSeconds'] / 3600));

    if($assoc)
    
    return $result;
    

    return json_encode($result);

【讨论】:

我使用了这个答案,非常棒!请记住,新的 PHP 制作了 exif 数组,以便 GPS 拥有自己的密钥。 EG $gps['LatDegree']=$exif["GPSLatitude"][0]; 变为 $gps['LatDegree']=$exif["GPS"]["GPSLatitude"][0]; 这个解决方案对我有用,我把这个答案变成了一个作曲家包——做了一些改变。你可以在这里找到它:github.com/diversen/gps-from-exif【参考方案5】:

这是一个老问题,但感觉它可以使用更雄辩的解决方案(OOP 方法和 lambda 来处理小数部分)

/**
 * Example coordinate values
 *
 * Latitude - 49/1, 4/1, 2881/100, N
 * Longitude - 121/1, 58/1, 4768/100, W
 */
protected function _toDecimal($deg, $min, $sec, $ref) 

    $float = function($v) 
        return (count($v = explode('/', $v)) > 1) ? $v[0] / $v[1] : $v[0];
    ;

    $d = $float($deg) + (($float($min) / 60) + ($float($sec) / 3600));
    return ($ref == 'S' || $ref == 'W') ? $d *= -1 : $d;



public function getCoordinates() 

    $exif = @exif_read_data('image_with_exif_data.jpeg');

    $coord = (isset($exif['GPSLatitude'], $exif['GPSLongitude'])) ? implode(',', array(
        'latitude' => sprintf('%.6f', $this->_toDecimal($exif['GPSLatitude'][0], $exif['GPSLatitude'][1], $exif['GPSLatitude'][2], $exif['GPSLatitudeRef'])),
        'longitude' => sprintf('%.6f', $this->_toDecimal($exif['GPSLongitude'][0], $exif['GPSLongitude'][1], $exif['GPSLongitude'][2], $exif['GPSLongitudeRef']))
    )) : null;


【讨论】:

【参考方案6】:

我过去使用的代码是这样的(实际上,它还检查数据是否模糊有效):

// Latitude
$northing = -1;
if( $gpsblock['GPSLatitudeRef'] && 'N' == $gpsblock['GPSLatitudeRef'] )

    $northing = 1;


$northing *= defraction( $gpsblock['GPSLatitude'][0] ) + ( defraction($gpsblock['GPSLatitude'][1] ) / 60 ) + ( defraction( $gpsblock['GPSLatitude'][2] ) / 3600 );

// Longitude
$easting = -1;
if( $gpsblock['GPSLongitudeRef'] && 'E' == $gpsblock['GPSLongitudeRef'] )

    $easting = 1;


$easting *= defraction( $gpsblock['GPSLongitude'][0] ) + ( defraction( $gpsblock['GPSLongitude'][1] ) / 60 ) + ( defraction( $gpsblock['GPSLongitude'][2] ) / 3600 );

您还拥有的地方:

function defraction( $fraction )

    list( $nominator, $denominator ) = explode( "/", $fraction );

    if( $denominator )
    
        return ( $nominator / $denominator );
    
    else
    
        return $fraction;
    

【讨论】:

知道为什么这被否决了吗?如果需要,我希望能够修复我的代码。【参考方案7】:

要获取高度值,可以使用以下3行:

$data     = exif_read_data($path_to_your_photo, 0, TRUE);
$alt      = explode('/', $data["GPS"]["GPSAltitude"]);
$altitude = (isset($alt[1])) ? ($alt[0] / $alt[1]) : $alt[0];

【讨论】:

【参考方案8】:

如果您需要一个函数来从 Imagick Exif 读取坐标,我们开始吧,我希望它可以节省您的时间。在 PHP 7 下测试。

function create_gps_imagick($coordinate, $hemi) 

  $exifCoord = explode(', ', $coordinate);

  $degrees = count($exifCoord) > 0 ? gps2Num($exifCoord[0]) : 0;
  $minutes = count($exifCoord) > 1 ? gps2Num($exifCoord[1]) : 0;
  $seconds = count($exifCoord) > 2 ? gps2Num($exifCoord[2]) : 0;

  $flip = ($hemi == 'W' or $hemi == 'S') ? -1 : 1;

  return $flip * ($degrees + $minutes / 60 + $seconds / 3600);



function gps2Num($coordPart) 

    $parts = explode('/', $coordPart);

    if (count($parts) <= 0)
        return 0;

    if (count($parts) == 1)
        return $parts[0];

    return floatval($parts[0]) / floatval($parts[1]);

【讨论】:

【参考方案9】:

我正在使用 Gerald Kaszuba 的修改版本,但它不准确。 所以我稍微改变一下公式。

来自:

return $flip * ($degrees + $minutes / 60);

改为:

return floatval($flip * ($degrees +($minutes/60)+($seconds/3600)));

它对我有用。

【讨论】:

【参考方案10】:

这是上面@Gerald 发布的 PHP 代码的 javascript 端口。通过这种方式,您可以在不上传图像的情况下找出图像的位置,并结合 dropzone.js 和 Javascript-Load-Image 等库

define(function()

    function parseExif(map) 
        var gps = 
            lng : getGps(map.get('GPSLongitude'), data.get('GPSLongitudeRef')),
            lat : getGps(map.get('GPSLatitude'), data.get('GPSLatitudeRef'))
        
        return gps;
    

    function getGps(exifCoord, hemi) 
        var degrees = exifCoord.length > 0 ? parseFloat(gps2Num(exifCoord[0])) : 0,
            minutes = exifCoord.length > 1 ? parseFloat(gps2Num(exifCoord[1])) : 0,
            seconds = exifCoord.length > 2 ? parseFloat(gps2Num(exifCoord[2])) : 0,
            flip = (/w|s/i.test(hemi)) ? -1 : 1;
        return flip * (degrees + (minutes / 60) + (seconds / 3600));
    

    function gps2Num(coordPart) 
        var parts = (""+coordPart).split('/');
        if (parts.length <= 0) 
            return 0;
        
        if (parts.length === 1) 
            return parts[0];
        
        return parts[0] / parts[1];
           

    return 
        parseExif: parseExif
    ;

);

【讨论】:

【参考方案11】:

短篇小说。 第一部分 N 离开年级 将分钟乘以 60 用 100 来划分秒数。 互相计算成绩、分和秒。

第二部分 E 离开年级 将分钟乘以 60 用 ...1000 划分秒数 互相计算成绩、分和秒

【讨论】:

【参考方案12】:

我看到没有人提到这个:https://pypi.python.org/pypi/LatLon/1.0.2

from fractions import Fraction
from LatLon import LatLon, Longitude, Latitude

latSigned = GPS.GPSLatitudeRef == "N" ? 1 : -1
longSigned = GPS.GPSLongitudeRef == "E" ? 1 : -1

latitudeObj = Latitude(
              degree = float(Fraction(GPS.GPSLatitude[0]))*latSigned , 
              minute = float(Fraction(GPS.GPSLatitude[0]))*latSigned , 
              second = float(Fraction(GPS.GPSLatitude[0])*latSigned)
longitudeObj = Latitude(
              degree = float(Fraction(GPS.GPSLongitude[0]))*longSigned , 
              minute = float(Fraction(GPS.GPSLongitude[0]))*longSigned , 
              second = float(Fraction(GPS.GPSLongitude[0])*longSigned )
Coordonates = LatLon(latitudeObj, longitudeObj )

现在使用 Coordonates 对象你可以做你想做的事: 示例:

(如***中的 46°56′48″N 7°26′39″E)

print Coordonates.to_string('d%°%m%′%S%″%H')

你不必从 ascii 转换,你就完成了:

('5\xc2\xb052\xe2\x80\xb259.88\xe2\x80\xb3N', '162\xc2\xb04\xe2\x80\xb259.88\xe2\x80\xb3W')

比打印示例:

print "Latitude:" + Latitude.to_string('d%°%m%′%S%″%H')[0].decode('utf8')

>> Latitude: 5°52′59.88″N

【讨论】:

是的,因为这是一个 php 问题的 Python 答案

以上是关于PHP提取GPS EXIF数据的主要内容,如果未能解决你的问题,请参考以下文章

Cordova 3.6:如何从 Android 的照片库中提取 GPS Exif 数据?

使用 PHP 删除 GPS EXIF 数据

无法将 gps 位置写入 exif

web获取照片EXIF信息(例如:拍照方向相机设备型号拍摄时间ISO 感光度GPS 地理位置等数据)

web获取照片EXIF信息(例如:拍照方向相机设备型号拍摄时间ISO 感光度GPS 地理位置等数据)

php 通过exif_read_data读取GPS信息后计算出不正常的经纬度