Related
I was working on image manipulation with php gd library. Just now God appeared and told me that we can retrieve exif data from jpeg, tiff images.But, he did not tell me how!
I tried surfing about it and found some posts about retrieving the data. All was good here on earth until I tried getting geo-location data. I couldn't find any solution to get that data.
I mentioned in the comments exif_read_data. Now that I'm at my desk I can elaborate a bit more. I created a function a while back to do exactly this:
// get geo-data from image
function get_image_location($file) {
if (is_file($file)) {
$info = exif_read_data($file);
if ($info !== false) {
$direction = array('N', 'S', 'E', 'W');
if (isset($info['GPSLatitude'], $info['GPSLongitude'], $info['GPSLatitudeRef'], $info['GPSLongitudeRef']) &&
in_array($info['GPSLatitudeRef'], $direction) && in_array($info['GPSLongitudeRef'], $direction)) {
$lat_degrees_a = explode('/',$info['GPSLatitude'][0]);
$lat_minutes_a = explode('/',$info['GPSLatitude'][1]);
$lat_seconds_a = explode('/',$info['GPSLatitude'][2]);
$lng_degrees_a = explode('/',$info['GPSLongitude'][0]);
$lng_minutes_a = explode('/',$info['GPSLongitude'][1]);
$lng_seconds_a = explode('/',$info['GPSLongitude'][2]);
$lat_degrees = $lat_degrees_a[0] / $lat_degrees_a[1];
$lat_minutes = $lat_minutes_a[0] / $lat_minutes_a[1];
$lat_seconds = $lat_seconds_a[0] / $lat_seconds_a[1];
$lng_degrees = $lng_degrees_a[0] / $lng_degrees_a[1];
$lng_minutes = $lng_minutes_a[0] / $lng_minutes_a[1];
$lng_seconds = $lng_seconds_a[0] / $lng_seconds_a[1];
$lat = (float) $lat_degrees + ((($lat_minutes * 60) + ($lat_seconds)) / 3600);
$lng = (float) $lng_degrees + ((($lng_minutes * 60) + ($lng_seconds)) / 3600);
$lat = number_format($lat, 7);
$lng = number_format($lng, 7);
//If the latitude is South, make it negative.
//If the longitude is west, make it negative
$lat = $info['GPSLatitudeRef'] == 'S' ? $lat * -1 : $lat;
$lng = $info['GPSLongitudeRef'] == 'W' ? $lng * -1 : $lng;
return array(
'lat' => $lat,
'lng' => $lng
);
}
}
}
return false;
}
This function is intended to work with file uploads, for example:
if (($geo = get_image_location($_FILES['file']['tmp_name'])) && !empty($geo)) {
// upload file
} else {
// file does not appear to contain any location information
}
That should give you a good start.
Currently, I'm learning to use Google Maps API. From what I read, the API require the latitude and longitude in Decimal Degree (DD).
In my database, the data is stored as DMS.
Example, 110° 29' 01.1"
I would to ask if you guys have any DMS to DD in php. And, the converter must accept from a single string like the example above.
Regards
You can try if this is working for you.
<?php
function DMStoDD($deg,$min,$sec)
{
// Converting DMS ( Degrees / minutes / seconds ) to decimal format
return $deg+((($min*60)+($sec))/3600);
}
function DDtoDMS($dec)
{
// Converts decimal format to DMS ( Degrees / minutes / seconds )
$vars = explode(".",$dec);
$deg = $vars[0];
$tempma = "0.".$vars[1];
$tempma = $tempma * 3600;
$min = floor($tempma / 60);
$sec = $tempma - ($min*60);
return array("deg"=>$deg,"min"=>$min,"sec"=>$sec);
}
?>
Here's one where you pass in the latitude,longitude in DMS values and returns the converted DMS string. Easy and simnple
function DECtoDMS($latitude, $longitude)
{
$latitudeDirection = $latitude < 0 ? 'S': 'N';
$longitudeDirection = $longitude < 0 ? 'W': 'E';
$latitudeNotation = $latitude < 0 ? '-': '';
$longitudeNotation = $longitude < 0 ? '-': '';
$latitudeInDegrees = floor(abs($latitude));
$longitudeInDegrees = floor(abs($longitude));
$latitudeDecimal = abs($latitude)-$latitudeInDegrees;
$longitudeDecimal = abs($longitude)-$longitudeInDegrees;
$_precision = 3;
$latitudeMinutes = round($latitudeDecimal*60,$_precision);
$longitudeMinutes = round($longitudeDecimal*60,$_precision);
return sprintf('%s%s° %s %s %s%s° %s %s',
$latitudeNotation,
$latitudeInDegrees,
$latitudeMinutes,
$latitudeDirection,
$longitudeNotation,
$longitudeInDegrees,
$longitudeMinutes,
$longitudeDirection
);
}
I wrote a PHP function that does what the question asks: converts a string in degrees/minutes/seconds into decimal degrees. It accepts a number of different formats for the string, and honors direction (NSEW).
Here is the code:
<?php
function convertDMSToDecimal($latlng) {
$valid = false;
$decimal_degrees = 0;
$degrees = 0; $minutes = 0; $seconds = 0; $direction = 1;
// Determine if there are extra periods in the input string
$num_periods = substr_count($latlng, '.');
if ($num_periods > 1) {
$temp = preg_replace('/\./', ' ', $latlng, $num_periods - 1); // replace all but last period with delimiter
$temp = trim(preg_replace('/[a-zA-Z]/','',$temp)); // when counting chunks we only want numbers
$chunk_count = count(explode(" ",$temp));
if ($chunk_count > 2) {
$latlng = $temp; // remove last period
} else {
$latlng = str_replace("."," ",$latlng); // remove all periods, not enough chunks left by keeping last one
}
}
// Remove unneeded characters
$latlng = trim($latlng);
$latlng = str_replace("º","",$latlng);
$latlng = str_replace("'","",$latlng);
$latlng = str_replace("\"","",$latlng);
$latlng = substr($latlng,0,1) . str_replace('-', ' ', substr($latlng,1)); // remove all but first dash
if ($latlng != "") {
// DMS with the direction at the start of the string
if (preg_match("/^([nsewNSEW]?)\s*(\d{1,3})\s+(\d{1,3})\s+(\d+\.?\d*)$/",$latlng,$matches)) {
$valid = true;
$degrees = intval($matches[2]);
$minutes = intval($matches[3]);
$seconds = floatval($matches[4]);
if (strtoupper($matches[1]) == "S" || strtoupper($matches[1]) == "W")
$direction = -1;
}
// DMS with the direction at the end of the string
if (preg_match("/^(-?\d{1,3})\s+(\d{1,3})\s+(\d+(?:\.\d+)?)\s*([nsewNSEW]?)$/",$latlng,$matches)) {
$valid = true;
$degrees = intval($matches[1]);
$minutes = intval($matches[2]);
$seconds = floatval($matches[3]);
if (strtoupper($matches[4]) == "S" || strtoupper($matches[4]) == "W" || $degrees < 0) {
$direction = -1;
$degrees = abs($degrees);
}
}
if ($valid) {
// A match was found, do the calculation
$decimal_degrees = ($degrees + ($minutes / 60) + ($seconds / 3600)) * $direction;
} else {
// Decimal degrees with a direction at the start of the string
if (preg_match("/^(-?\d+(?:\.\d+)?)\s*([nsewNSEW]?)$/",$latlng,$matches)) {
$valid = true;
if (strtoupper($matches[2]) == "S" || strtoupper($matches[2]) == "W" || $degrees < 0) {
$direction = -1;
$degrees = abs($degrees);
}
$decimal_degrees = $matches[1] * $direction;
}
// Decimal degrees with a direction at the end of the string
if (preg_match("/^([nsewNSEW]?)\s*(\d+(?:\.\d+)?)$/",$latlng,$matches)) {
$valid = true;
if (strtoupper($matches[1]) == "S" || strtoupper($matches[1]) == "W")
$direction = -1;
$decimal_degrees = $matches[2] * $direction;
}
}
}
if ($valid) {
return $decimal_degrees;
} else {
return false;
}
}
?>
Here it is on Github with test cases: https://github.com/prairiewest/PHPconvertDMSToDecimal
Solved.
<?php
function DMStoDD($input)
{
$deg = " " ;
$min = " " ;
$sec = " " ;
$inputM = " " ;
print "<br> Input is ".$input." <br>";
for ($i=0; $i < strlen($input); $i++)
{
$tempD = $input[$i];
//print "<br> TempD [$i] is : $tempD";
if ($tempD == iconv("UTF-8", "ISO-8859-1//TRANSLIT", '°') )
{
$newI = $i + 1 ;
//print "<br> newI is : $newI";
$inputM = substr($input, $newI, -1) ;
break;
}//close if degree
$deg .= $tempD ;
}//close for degree
//print "InputM is ".$inputM." <br>";
for ($j=0; $j < strlen($inputM); $j++)
{
$tempM = $inputM[$j];
//print "<br> TempM [$j] is : $tempM";
if ($tempM == "'")
{
$newI = $j + 1 ;
//print "<br> newI is : $newI";
$sec = substr($inputM, $newI, -1) ;
break;
}//close if minute
$min .= $tempM ;
}//close for min
$result = $deg+( (( $min*60)+($sec) ) /3600 );
print "<br> Degree is ". $deg*1 ;
print "<br> Minutes is ". $min ;
print "<br> Seconds is ". $sec ;
print "<br> Result is ". $result ;
return $deg + ($min / 60) + ($sec / 3600);
}
?>
If you also want to include the reference, you might want to use this function:
function DMStoDD($ref, $deg, $min, $sec)
{
$n = $deg + (($min * 60 + $sec) / 3600);
if (($ref == "S") or ($ref == "W")) {
return -$n;
} else {
return $n;
}
}
This works very well:
<?php echo "<td> $deg° $min' $sec″ </td>"; ?>
where deg, min and sec are the angular co-ordinates.
How can I convert this:
26.72773551940918
Into something like this:
22°12'42"N
The trick here is that the coordinates are, actually Latitude and Longitude, I just need to format them correctly.
You can find functions to do that here
<?php
function DMStoDEC($deg,$min,$sec)
{
// Converts DMS ( Degrees / minutes / seconds )
// to decimal format longitude / latitude
return $deg+((($min*60)+($sec))/3600);
}
function DECtoDMS($dec)
{
// Converts decimal longitude / latitude to DMS
// ( Degrees / minutes / seconds )
// This is the piece of code which may appear to
// be inefficient, but to avoid issues with floating
// point math we extract the integer part and the float
// part by using a string function.
$vars = explode(".",$dec);
$deg = $vars[0];
$tempma = "0.".$vars[1];
$tempma = $tempma * 3600;
$min = floor($tempma / 60);
$sec = $tempma - ($min*60);
return array("deg"=>$deg,"min"=>$min,"sec"=>$sec);
}
?>
The lat/lon coords are written in (roughly speaking) a base-60 numeral system. Here's how you convert them:
function fraction_to_min_sec($coord)
{
$isnorth = $coord>=0;
$coord = abs($coord);
$deg = floor($coord);
$coord = ($coord-$deg)*60;
$min = floor($coord);
$sec = floor(($coord-$min)*60);
return array($deg, $min, $sec, $isnorth ? 'N' : 'S');
// or if you want the string representation
return sprintf("%d°%d'%d\"%s", $deg, $min, $sec, $isnorth ? 'N' : 'S');
}
I say my function has better numerical stability than #SeRPRo's one.
Here's one where you pass in the latitude,longitude in DMS values and returns the converted DMS string. Easy and simple
function DECtoDMS($latitude, $longitude)
{
$latitudeDirection = $latitude < 0 ? 'S': 'N';
$longitudeDirection = $longitude < 0 ? 'W': 'E';
$latitudeNotation = $latitude < 0 ? '-': '';
$longitudeNotation = $longitude < 0 ? '-': '';
$latitudeInDegrees = floor(abs($latitude));
$longitudeInDegrees = floor(abs($longitude));
$latitudeDecimal = abs($latitude)-$latitudeInDegrees;
$longitudeDecimal = abs($longitude)-$longitudeInDegrees;
$_precision = 3;
$latitudeMinutes = round($latitudeDecimal*60,$_precision);
$longitudeMinutes = round($longitudeDecimal*60,$_precision);
return sprintf('%s%s° %s %s %s%s° %s %s',
$latitudeNotation,
$latitudeInDegrees,
$latitudeMinutes,
$latitudeDirection,
$longitudeNotation,
$longitudeInDegrees,
$longitudeMinutes,
$longitudeDirection
);
}
Here is the opposite when you have DMS string and need it as float number (contains unicode characters):
//e.g.
$dec = dms_to_dec("-18° 51' 30.5697\"");
/**
* Convert a coordinate in dms to dec
*
* #param string $dms coordinate
* #return float
*/
function dms_to_dec($dms)
{
$dms = stripslashes($dms);
$neg = (preg_match('/[SWO]/i', $dms) == 0) ? 1 : -1;
$dms = preg_replace('/(^\s?-)|(\s?[NSEWO]\s?)/i', '', $dms);
$pattern = "/(\\d*\\.?\\d+)(?:[°ºd: ]+)(\\d*\\.?\\d+)*(?:['m′: ])*(\\d*\\.?\\d+)*[\"s″ ]?/i";
$parts = preg_split($pattern, $dms, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
if (!$parts) {
return;
}
// parts: 0 = degree, 1 = minutes, 2 = seconds
$d = isset($parts[0]) ? (float)$parts[0] : 0;
$m = isset($parts[1]) ? (float)$parts[1] : 0;
if (strpos($dms, ".") > 1 && isset($parts[2])) {
$m = (float)($parts[1] . '.' . $parts[2]);
unset($parts[2]);
}
$s = isset($parts[2]) ? (float)$parts[2] : 0;
$dec = ($d + ($m/60) + ($s/3600))*$neg;
return $dec;
}
I am looking for something like this: How to generate a regular expression at runtime to match a numeric range but written in php.
Answering your question here, since comments are horrible for code blocks. I wouldn't translate a statement like that directly, as it's nearly unreadable. It's far easier to pick apart like this:
if ($n == $m) { // max/min ranges are the same, so just look for that number of characters
$format = "\{$n\}"; // {n}
} elseif ($n == 1) { // min range is 1, so use the max
$format = "\{1,$m\}"; // {1,m}
} else { // arbitary n->m range
$format = "\{$n,$m\}"; // {n,m}
}
It CAN be done in PHP as a ternary, it's just as illegible/impossible to debug, though:
$format = ($n == $m) ? "\{$n\}" : (($n == 1) ? "\{1,$m\}" : "\{$n,$m\}");
I think this should work:
class NumericRangeRegexGenerator {
private function baseRange($num,$up, $leading1) {
$c = $num[0];
$low = $up ? $c : ($leading1 ? '1' : '0');
$high = $up ? '9': $c;
if (strlen($num) == 1)
return $this->charClass($low, $high);
$re = $c . "(" . $this->baseRange(substr($num,1), $up, false) . ")";
if ($up) $low++; else $high--;
if ($low <= $high)
$re .= "|" . $this->charClass($low, $high) . $this->nDigits(strlen($num) - 1);
return $re;
}
private function charClass($b, $e) {
//String.format(b==e ? "%c" : e-b>1 ? "[%c-%c]" : "[%c%c]", b, e); (in java)
if ($b == $e) {
$format = $b;
} elseif ($e-$b>1) {
$format = '['.$b.'-'.$e.']';
} else {
$format = '['.$b.$e.']';
}
return $format;
}
private function nDigits($n, $m=null) {
//String.format(n==m ? n==1 ? "":"{%d}":"{%d,%d}", n, m) (in java)
if($m===null){
nDigits($n, $n);
}
if ($n == $m) { // max/min ranges are the same, so just look for that number of characters
$format = "\{$n\}"; // {n}
} elseif ($n == 1) { // min range is 1, so use the max
$format = "\{1,$m\}"; // {1,m}
} else { // arbitary n->m range
$format = "\{$n,$m\}"; // {n,m}
}
return "[0-9]" . $format;
}
private function eqLengths($from, $to) {
$fc = $from[0];
$tc = $to[0];
if (strlen($from) == 1 && strlen($to) == 1)
return $this->charClass($fc, $tc);
if ($fc == $tc)
return $fc . "(".$this->rangeRegex(substr($from,1), substr($to,1)).")";
$re = $fc . "(" . $this->baseRange(substr($from,1), true, false) . ")|"
. $tc . "(" . $this->baseRange(substr($to,1), false, false) . ")";
if (++$fc <= --$tc)
$re .= "|" . $this->charClass($fc, $tc) . $this->nDigits(strlen($from) - 1);
return $re;
}
private function nonEqLengths($from, $to) {
$re = $this->baseRange($from,true,false) . "|" . $this->baseRange($to,false,true);
if (strlen($to) - strlen($from) > 1)
$re .= "|[1-9]" . $this->nDigits(strlen($from), strlen($to) - 2);
return $re;
}
public function rangeRegex($n, $m) {
return strlen($n) == strlen($m) ? $this->eqLengths($n, $m) : $this->nonEqLengths($n, $m);
}
}
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 !
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]);
}
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);
}
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]);
}
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);
}
This is an old question but felt it could use a more eloquent solution (OOP approach and lambda to process the fractional parts)
/**
* 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;
}
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;
}
}
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];
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]);
}
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.
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
};
});
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.
Second part E
Leave the grade
multiply the minutes with 60
devide the seconds with ...1000
cöunt the grades, minutes and seconds with each other
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:
('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