PHP извлечение данных GPS EXIF
Я хотел бы извлечь тег GPS EXIF из изображений с помощью php.
Я использую exif_read_data()
который возвращает массив всех тегов + 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 из Википедии) ? Я хотел бы передать координаты thoses в google maps api для отображения позиций изображений на карте !
13 ответов
согласно 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.
этот код ниже должен работать, пока вы не получите отрицательные координаты (учитывая, что вы получаете 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]);
}
Это моя измененная версия. Другие на меня не работали. Он даст вам десятичные версии координат 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]);
}
это переделанный вариант кода Джеральд Кашуба (в настоящее время наиболее широко принято отвечать). Результат должен быть идентичным, но я сделал несколько микро-оптимизаций и совместил две отдельные функции в одну. В моем тестировании эта версия сбрила около 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);
}
Я знаю, что этот вопрос был задан давно, но я наткнулся на него во время поиска в google, и предлагаемые здесь решения не сработали для меня. Итак, после дальнейших поисков, вот что сработало для меня.
я помещаю его здесь, чтобы любой, кто приходит сюда через какой-то гуглинг, мог найти разные подходы для решения одной и той же проблемы:
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);
}
Это старый вопрос, но чувствовал, что он может использовать более красноречивое решение (подход ООП и лямбда для обработки дробных частей)
/**
* 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;
}
код, который я использовал в прошлом, что-то вроде (на самом деле он также проверяет, что данные неопределенно действительны):
// 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;
}
}
чтобы получить значение высоты, вы можете использовать следующие 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];
в случае, если вам нужна функция для чтения координат из 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]);
}
Я использую модифицированную версию от Gerald Kaszuba, но это не точно. поэтому я немного меняю формулу.
from:
return $flip * ($degrees + $minutes / 60);
изменен на:
return floatval($flip * ($degrees +($minutes/60)+($seconds/3600)));
это работает для меня.
это порт javascript PHP-кода, размещенного @Gerald выше. Таким образом, вы можете определить местоположение изображения без загрузки изображения в сочетании с такими библиотеками, как 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
};
});
короткий рассказ. Первая часть N Оставить оценку умножьте минуты на 60 разделите секунды на 100. считайте оценки, минуты и секунды друг с другом.
вторая часть E Оставить оценку умножьте минуты на 60 разделите секунды ...Одна тысяча cöunt оценки, минуты и секунды друг с другом
Я видел, что никто не упомянул это: 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 objecct, вы можете делать то, что хотите: Пример:
(например, 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
С этими данными GPS:
["GPSLatitudeRef"]=>
string(1) "N"
["GPSLatitude"]=>
array(3) {
[0]=>
string(7) "65539/0"
[1]=>
string(17) "-1542717440/65539"
[2]=>
string(8) "196608/0"
}
["GPSLongitudeRef"]=>
string(1) "E"
["GPSLongitude"]=>
array(3) {
[0]=>
string(20) "39321600/-1166016512"
[1]=>
string(21) "1111490956/1811939343"
[2]=>
string(22) "1111491292/-1725956081"
}
используя приведенный выше код (спасибо Джеральд), я получаю следующие значения широты и долготы:
-392.31537456069,-0.023678137550796
Это неправильно. Это делает мою голову, поскольку код работает, но ответ в этом случае неправильный! Многие другие изображения работают нормально, просто кажется, что какая-то логика отсутствует, чтобы удовлетворить что-то в этом dat. Например, когда я загружаю изображение в iPhoto (извините за пример Apple для тех, кто без Macs), он получает правильный ответ; эти данные EXIF предназначены для изображения вблизи Красного моря.