PHP 提取 GPS EXIF 数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2526304/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP extract GPS EXIF data
提问by Kami
I would like to extract the GPS EXIF tag from pictures using php.
I'm using the exif_read_data()that returns a array of all tags + data :
我想使用 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
I don't know how to interpret 46/1 5403/100 and 0/1 ? 46 might be 46° but what about the rest especially 0/1 ?
我不知道如何解释 46/1 5403/100 和 0/1 ?46 可能是 46° 但其余的特别是 0/1 呢?
angle/1 5403/100 0/1
What is this structure about ?
这个结构是关于什么的?
How to convert them to "standard" ones (like 46°56′48″N 7°26′39″E from wikipedia) ? I would like to pass thoses coordinates to the google maps api to display the pictures positions on a map !
如何将它们转换为“标准”(如维基百科中的 46°56′48″N 7°26′39″E)?我想将这些坐标传递给谷歌地图 api 以在地图上显示图片位置!
采纳答案by Kip
According to http://en.wikipedia.org/wiki/Geotagging, ( [0] => 46/1 [1] => 5403/100 [2] => 0/1 )should mean 46/1 degrees, 5403/100 minutes, 0/1 seconds, i.e. 46°54.03′0″N. Normalizing the seconds gives 46°54′1.8″N.
根据http://en.wikipedia.org/wiki/Geotagging,( [0] => 46/1 [1] => 5403/100 [2] => 0/1 )应该表示 46/1 度,5403/100 分钟,0/1 秒,即 46°54.03′0″N。标准化秒数给出 46°54′1.8″N。
This code below should work, as long as you don't get negative coordinates (given that you get N/S and E/W as a separate coordinate, you shouldn't ever have negative coordinates). Let me know if there is a bug (I don't have a PHP environment handy at the moment).
只要您没有得到负坐标,下面的代码就应该可以工作(假设您将 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]);
}
回答by gak
This is my modified version. The other ones didn't work for me. It will give you the decimal versions of the GPS coordinates.
这是我修改后的版本。其他的对我不起作用。它将为您提供 GPS 坐标的十进制版本。
The code to process the EXIF data:
处理EXIF数据的代码:
$exif = exif_read_data($filename);
$lon = getGps($exif["GPSLongitude"], $exif['GPSLongitudeRef']);
$lat = getGps($exif["GPSLatitude"], $exif['GPSLatitudeRef']);
var_dump($lat, $lon);
Prints out in this format:
以这种格式打印出来:
float(-33.8751666667)
float(151.207166667)
Here are the functions:
以下是功能:
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]);
}
回答by David Jones
This is a refactored version of Gerald Kaszuba's code (currently the most widely accepted answer). The result should be identical, but I've made several micro-optimizations and combined the two separate functions into one. In my benchmark testing, this version shaved about 5 microseconds off the runtime, which is probably negligible for most applications, but might be useful for applications which involve a large number of repeated calculations.
这是 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);
}
回答by Hassan Al-Jeshi
I know this question has been asked a long time ago, but I came across it while searching in google and the solutions proposed here did not worked for me. So, after further searching, here is what worked for me.
我知道很久以前就有人问过这个问题,但是我在谷歌搜索时遇到了它,这里提出的解决方案对我不起作用。因此,经过进一步搜索,这对我有用。
I'm putting it here so that anybody who comes here through some googling, can find different approaches to solve the same problem:
我把它放在这里,以便任何通过谷歌搜索来到这里的人都可以找到解决同一问题的不同方法:
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);
}
回答by Trent Renshaw
This is an old question but felt it could use a more eloquent solution (OOP approach and lambda to process the fractional parts)
这是一个老问题,但觉得它可以使用更雄辩的解决方案(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;
}
回答by Rowland Shaw
The code I've used in the past is something like (in reality, it also checks that the data is vaguely valid):
我过去使用的代码类似于(实际上,它还检查数据是否有效):
// 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 );
Where you also have:
您还拥有:
function defraction( $fraction )
{
list( $nominator, $denominator ) = explode( "/", $fraction );
if( $denominator )
{
return ( $nominator / $denominator );
}
else
{
return $fraction;
}
}
回答by Ustym Ukhman
To get the altitude value, you can use the following 3 lines:
要获取高度值,您可以使用以下 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];
回答by Jerome
In case you need a function to read Coordinates from Imagick Exif here we go, I hope it saves you time. Tested under PHP 7.
如果您需要一个函数来从 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]);
}
回答by Thanatos11th
i have seen nobody mentioned this: https://pypi.python.org/pypi/LatLon/1.0.2
我见过没有人提到这个: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 )
now using the Coordonates objecct you can do what you want: Example:
现在使用 Coordonates 对象你可以做你想做的事: 示例:
(like 46°56′48″N 7°26′39″E from wikipedia)
(如维基百科中的 46°56′48″N 7°26′39″E)
print Coordonates.to_string('d%°%m%′%S%″%H')
You than have to convert from ascii, and you are done:
你必须从 ascii 转换,你就完成了:
('5\xc2\xb052\xe2\x80\xb259.88\xe2\x80\xb3N', '162\xc2\xb04\xe2\x80\xb259.88\xe2\x80\xb3W')
and than printing example:
而不是打印示例:
print "Latitude:" + Latitude.to_string('d%°%m%′%S%″%H')[0].decode('utf8')
>> Latitude: 5°52′59.88″N
回答by sapewady
I'm using the modified version from Gerald Kaszuba but it's not accurate. so i change the formula a bit.
我使用的是 Gerald Kaszuba 的修改版,但它不准确。所以我稍微改变一下公式。
from:
从:
return $flip * ($degrees + $minutes / 60);
changed to:
变成:
return floatval($flip * ($degrees +($minutes/60)+($seconds/3600)));
It works for me.
这个对我有用。

