可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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 :
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 ?
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 !
回答1:
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.
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).
//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]); }
回答2:
This is my modified version. The other ones didn't work for me. It will give you the decimal versions of the GPS coordinates.
The code to process the EXIF data:
$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]); }
回答3:
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.
$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); }
回答4:
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); }
回答5:
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; } }
回答6:
To get the altitude value, you can use the following 3 lines:
$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];
回答7:
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.
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]); }
回答8:
I'm using the modified version from Gerald Kaszuba but it's not accurate. so i change the formula a bit.
from:
return $flip * ($degrees + $minutes / 60);
changed to:
return floatval($flip * ($degrees +($minutes/60)+($seconds/3600)));
It works for me.
回答9:
This is a javascript port of the PHP-code posted @Gerald above. This way you can figure out the location of an image without ever uploading the image, in conjunction with libraries like dropzone.js and 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 }; });
回答10:
short story. First part N Leave the grade multiply the minutes with 60 devide the seconds with 100. count the grades,minuts and seconds with eachother.
回答11:
i have seen nobody mentioned this: 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:
(like 46°56′48〃N 7°26′39〃E from wikipedia)
print Coordonates.to_string('d%°%m%′%S%〃%H')
You than have to convert from ascii, and you are done: