web-dev-qa-db-fra.com

PHP extraire des données GPS EXIF

Je voudrais extraire le tag GPS EXIF ​​à partir d'images en utilisant php . J'utilise le exif_read_data() qui renvoie un tableau contenant tous les tags + données

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

Je ne sais pas comment interpréter 46/1 5403/100 et 0/1? 46 pourrait être 46 ° mais qu'en est-il du reste surtout 0/1?

angle/1 5403/100 0/1

De quoi parle cette structure? 

Comment les convertir en "standards" (46 ° 56′48 ″ N 7 ° 26′39 ″ E de wikipedia, par exemple)? J'aimerais transmettre ces coordonnées à l'API Google Maps pour afficher les positions des images sur une carte!

48
Kami

Selon http://en.wikipedia.org/wiki/Geotagging , ( [0] => 46/1 [1] => 5403/100 [2] => 0/1 ) devrait signifier 46/1 degrés, 5403/100 minutes, 0/1 secondes, soit 46 ° 54.03′0 ″ N. La normalisation des secondes donne 46 ° 54′1.8 ″ N.

Ce code ci-dessous devrait fonctionner tant que vous n'obtenez pas de coordonnées négatives (étant donné que vous obtenez N/S et E/W en tant que coordonnées séparées, vous ne devriez jamais avoir de coordonnées négatives). Faites-moi savoir s'il y a un bogue (je n'ai pas d'environnement PHP à portée de main pour le moment).

//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]);
}
21
Kip

Ceci est ma version modifiée. Les autres n'ont pas fonctionné pour moi. Il vous donnera les versions décimales des coordonnées GPS.

Le code pour traiter les données EXIF:

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

Imprime dans ce format:

float(-33.8751666667)
float(151.207166667)

Voici les fonctions:

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]);
}
85
Gerald Kaszuba

Ceci est une version refactorisée du code de Gerald Kaszuba (actuellement la réponse la plus largement acceptée). Le résultat devrait être identique, mais j'ai effectué plusieurs micro-optimisations et combiné les deux fonctions distinctes en une. Lors de mes tests d'évaluation, cette version a réduit d'environ 5 microsecondes le temps d'exécution, ce qui est probablement négligeable pour la plupart des applications, mais peut être utile pour les applications qui impliquent un grand nombre de calculs répétés.

$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);
}
19
David Jones

Je sais que cette question a été posée il y a longtemps, mais je l'ai trouvée lors d'une recherche dans google et les solutions proposées ici n'ont pas fonctionné pour moi. Donc, après plus de recherches, voici ce qui a fonctionné pour moi.

Je le mets ici pour que quiconque vient ici par google, puisse trouver différentes approches pour résoudre le même problème:

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);
}
5
Hassan Al-Jeshi

C'est une vieille question, mais elle a estimé qu'une solution plus éloquente pourrait être utilisée (approche OOP et lambda pour traiter les fractions).

/**
 * 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;

}
2
Trent Renshaw

Le code que j'ai utilisé par le passé ressemble à quelque chose du genre (en réalité, il vérifie également que les données sont vaguement valides):

// 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 );

Où vous avez aussi:

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

    if( $denominator )
    {
        return ( $nominator / $denominator );
    }
    else
    {
        return $fraction;
    }
}
1
Rowland Shaw

Pour obtenir la valeur d'altitude, vous pouvez utiliser les 3 lignes suivantes:

$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];
1
Ustym Ukhman

Au cas où vous auriez besoin d’une fonction pour lire les coordonnées d’Imagick Exif, nous espérons que cela vous fera gagner du temps. Testé sous 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]);
}
1
Jerome

j'ai vu personne ne l'a mentionné: 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 )

maintenant, en utilisant l’objet Coordonates, vous pouvez faire ce que vous voulez: Exemple:

(comme 46 ° 56′48 ″ N 7 ° 26′39 ″ E de wikipedia) 

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

Vous devez ensuite convertir d'asci et vous avez terminé:

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

et que l'impression exemple:

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

>> Latitude: 5°52′59.88″N
0
Thanatos11th

J'utilise la version modifiée de Gerald Kaszuba mais elle n'est pas précise .. donc je change un peu la formule.

de:

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

changé en:

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

Ça marche pour moi.

0
sapewady

histoire courte . Première partie N Quitter la note multiplier les minutes par 60 diviser les secondes par 100 .. compter les notes, minutes et secondes les unes avec les autres.

Deuxième partie E Laisser la note Multiplier les minutes par 60 Diviser les secondes avec ... 1000 Appeler les notes, les minutes et les secondes les unes avec les autres

0
e ander

Ceci est un port javascript du code PHP affiché par @Gerald ci-dessus. De cette façon, vous pouvez déterminer l'emplacement d'une image sans la télécharger, en association avec des bibliothèques comme dropzone.js et 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
    };

});
0
Aksel N.