php - exif_read_data
PHP extraer datos GPS EXIF (13)
Me gustaría extraer la etiqueta GPS EXIF de las imágenes usando php. Estoy usando exif_read_data()
que devuelve una matriz de todas las etiquetas + datos:
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
No sé cómo interpretar 46/1 5403/100 y 0/1? 46 podría ser 46 ° pero ¿qué pasa con el resto especialmente 0/1?
angle/1 5403/100 0/1
¿De qué se trata esta estructura?
¿Cómo convertirlos a "estándar" (como 46 ° 56′48 ″ N 7 ° 26′39 ″ E de wikipedia)? ¡Me gustaría pasar estas coordenadas a la API de google maps para mostrar las posiciones de las imágenes en un mapa!
Con estos datos 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"
}
Usando el código anterior (gracias Gerald) obtengo estos valores de latitud y longitud:
-392.31537456069,-0.023678137550796
Esto no es correcto. Está haciendo mi cabeza como funciona el código, ¡pero la respuesta es incorrecta en este caso! Muchas otras imágenes funcionan bien, parece que faltan algunas lógicas para atender algo en este dato. Por ejemplo, cuando cargo la imagen en iPhoto (perdón por el ejemplo de Apple para los que no tienen Mac), la respuesta es correcta; estos datos EXIF son para una imagen cerca del Mar Rojo.
El código que he usado en el pasado es algo como (en realidad, también verifica que los datos son vagamente válidos):
// 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 );
Donde también tienes:
function defraction( $fraction )
{
list( $nominator, $denominator ) = explode( "/", $fraction );
if( $denominator )
{
return ( $nominator / $denominator );
}
else
{
return $fraction;
}
}
En caso de que necesite una función para leer Coordenadas de Imagick Exif, aquí vamos, espero que le ahorre tiempo. Probado bajo 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]);
}
Esta es mi versión modificada. Los otros no funcionaron para mí. Te dará las versiones decimales de las coordenadas GPS.
El código para procesar los datos EXIF:
$exif = exif_read_data($filename);
$lon = getGps($exif["GPSLongitude"], $exif[''GPSLongitudeRef'']);
$lat = getGps($exif["GPSLatitude"], $exif[''GPSLatitudeRef'']);
var_dump($lat, $lon);
Imprime en este formato:
float(-33.8751666667)
float(151.207166667)
Aquí están las funciones:
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]);
}
Esta es una pregunta antigua pero sentí que podría usar una solución más elocuente (enfoque OOP y lambda para procesar las partes fraccionarias)
/**
* 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;
}
Esta es una versión refactorizada del código de Gerald Kaszuba (actualmente la respuesta más aceptada). El resultado debería ser idéntico, pero he realizado varias microoptimizaciones y he combinado las dos funciones separadas en una sola. En mis pruebas comparativas, esta versión eliminó aproximadamente 5 microsegundos del tiempo de ejecución, lo que probablemente sea insignificante para la mayoría de las aplicaciones, pero podría ser útil para aplicaciones que involucren una gran cantidad de cálculos repetidos.
$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);
}
Este es un puerto javascript del código PHP publicado en @Gerald arriba. De esta manera, puede averiguar la ubicación de una imagen sin tener que dropzone.js nunca, junto con bibliotecas como dropzone.js y 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
};
});
Estoy usando la versión modificada de Gerald Kaszuba pero no es precisa. Así que cambio la fórmula un poco.
desde:
return $flip * ($degrees + $minutes / 60);
cambiado a:
return floatval($flip * ($degrees +($minutes/60)+($seconds/3600)));
Esto funciona para mi.
No he visto a nadie que haya mencionado esto: 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 )
Ahora usando el objeto Coordonates puede hacer lo que quiera: Ejemplo:
(como 46 ° 56′48 "N 7 ° 26′39" E de wikipedia)
print Coordonates.to_string(''d%°%m%′%S%″%H'')
Usted que tiene que convertir de ascii, y ha terminado:
(''5/xc2/xb052/xe2/x80/xb259.88/xe2/x80/xb3N'', ''162/xc2/xb04/xe2/x80/xb259.88/xe2/x80/xb3W'')
y que imprimiendo ejemplo:
print "Latitude:" + Latitude.to_string(''d%°%m%′%S%″%H'')[0].decode(''utf8'')
>> Latitude: 5°52′59.88″N
Para obtener el valor de altitud, puede utilizar las siguientes 3 líneas:
$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];
Sé que esta pregunta se hizo hace mucho tiempo, pero la encontré mientras buscaba en Google y las soluciones propuestas aquí no funcionaron para mí. Entonces, después de buscar más, aquí está lo que me funcionó.
Lo estoy poniendo aquí para que cualquiera que venga a través de Google, pueda encontrar diferentes enfoques para resolver el mismo problema:
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);
}
Según http://en.wikipedia.org/wiki/Geotagging , ( [0] => 46/1 [1] => 5403/100 [2] => 0/1 )
debe significar 46/1 grados, 5403 / 100 minutos, 0/1 segundos, es decir, 46 ° 54.03′0 ″ N. La normalización de los segundos da 46 ° 54′1.8 ″ N.
Este código a continuación debería funcionar, siempre que no obtengas las coordenadas negativas (dado que obtienes N / S y E / W como una coordenada separada, nunca deberías tener coordenadas negativas). Avíseme si hay un error (no tengo un entorno PHP a la mano en este momento).
//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]);
}
cuento. Primera parte N Deja la nota, multiplica los minutos con 60, divide los segundos con 100. cuenta las calificaciones, minutos y segundos entre sí.
Segunda parte E Deje la calificación multiplique los minutos con 60 divida los segundos con ... 1000 cöunt las calificaciones, los minutos y los segundos entre sí