php latitude longitude format conversions

Pass any longitude/latitude coordinate to function arrDecimalDegrees() and it will return an array with the coordinate in decimal degrees.   Pass the longitude/latitude in decima degrees to function arrLatLonFmtsFromDecDeg() and it will return an array with all of the various latitude/longitude formats as both strings, and float numbers.


// The main function arrDecimalDegrees() returns
// decimal degrees from a variety of lat/long string
// formats. 
// Alternative formats are returned by passing Decimal Degrees
// to the function arrLatLonFmtsFromDecDeg().

// Demonstration of function arrDecimalDegrees() and arrLatLonFmtsFromDecDeg().
$arrStrLatLon = arrStrLatLonFmts();
foreach ($arrStrLatLon as $sLatLonFmt) {
	$arrDecDeg = arrDecimalDegrees($sLatLonFmt,false);
	if ($arrDecDeg == false) {
		echo '

"'.$sLatLonFmt.'" ERROR - arrDecimalDegrees() invalid format or characters

'; } else { echo '

'.$arrDecDeg['src'].'   '.$sLatLonFmt.' -> '.number_format($arrDecDeg['lat'],4).','.number_format($arrDecDeg['lon'],4).'

'; $arrLatLonFormats = arrLatLonFmtsFromDecDeg($arrDecDeg['lat'],$arrDecDeg['lon'], false); if ($arrLatLonFormats === false) { echo '

ERROR - unable to extract formats via arrLatLonFmtsFromDecDeg() from '.number_format($arrDecDeg['lat'],4).','.number_format($arrDecDeg['lon'],4).'   '.$arrDecDeg['src'].'

'; } else { foreach ($arrLatLonFormats as $key => $dLatLon) { echo '

  '.$key.'   '.$dLatLon.'

'; } // foreach } } //die(); } // foreach // Sample of output: //DegMinSec 37 31 56.4, -80 45 30.6 -> 37.5323,-80.7585 // DecimalDegrees 37.5323, -80.7585 // DegMinSec 37 31 56, -80 45 31 // Garmin N37 31.9400, W, -80 45.5100 // DegMinDecSec 37 31 56.400, -80 45 30.600 // DegDecMin 37 31.9400, -80 45.5100 // NMEA 3731.9400, -8045.5100 // dDeg 37, -80 // dMin 31, 45 // dSec 56, 31 // dDecMin 31.94, 45.51 // dDecSec 56.4, 30.6 function arrLatLonFmtsFromDecDeg($dLat, $dLon, $bVerbose) { // Return an array with all of the various latitude and // longitude formats derived from $dLat and $dLon in // decimal degrees. // Returns FALSE on error. if ((is_float($dLat) === false) || (is_float($dLon) === false)) return false; if (($dLat > 90.0) || ($dLat < -90.0) || ($dLon > 180.0) || ($dLon < -180.0)) { if ($bVerbose == true) echo '

ERROR - lat &/or long out of range '.$sLatLon.' -> '.number_format($arrLatLon['lat'],1).','.number_format($arrLatLon['lon'],1).'

'; return false; } // DecimalDegrees 37.5324,-80.7585 // Garmin N37 31 56.4,W80 45 30.6 // DegMinDecSec 37 31 56.4,-80 45 30.6 // DegDecMin 37 31.9404,-80 45.5100 // DegMinSec 37° 31' 56",-80° 45' 31" // NMEA 3731.9404,-8045.51 unset($dSign,$dDeg,$dMin,$dSec,$dDeg,$arrReturn); // latitude $dSign = 1.0; if ($dLat < 0) $dSign = -1.0; $dDeg = intval(abs($dLat)); $dMin = intval(60.0*(abs($dLat)-$dDeg)); $dSec = 3600.0*(abs($dLat)-$dDeg-$dMin/60); $dDeg = $dDeg * $dSign; $arrReturn['DecimalDegrees'] = number_format($dLat,4); $arrReturn['DegMinSec'] = number_format($dDeg,0).' '.number_format($dMin,0).' '.number_format(round($dSec,0,PHP_ROUND_HALF_UP),0); if ($dSign > 0.0) { $arrReturn['Garmin'] = 'N'; } else { $arrReturn['Garmin'] = 'S'; } $arrReturn['Garmin'] .= number_format(abs($dDeg),0).' '.number_format($dMin,0).' '.number_format($dSec,3); $arrReturn['DegMinDecSec'] = number_format($dDeg,0).' '.number_format($dMin,0).' '.number_format($dSec,3); $arrReturn['DegDecMin'] = number_format($dDeg,0).' '.number_format($dMin+$dSec/60.0,4); $arrReturn['NMEA'] = number_format($dDeg,0).number_format($dMin+$dSec/60.0,4); $arrReturn['dDeg'] = round($dDeg,0,PHP_ROUND_HALF_UP); $arrReturn['dMin'] = round($dMin,0,PHP_ROUND_HALF_UP); $arrReturn['dSec'] = round($dSec,0,PHP_ROUND_HALF_UP); $arrReturn['dDecMin'] = round($dMin+$dSec/60,4,PHP_ROUND_HALF_UP); $arrReturn['dDecSec'] = round($dSec,4,PHP_ROUND_HALF_UP); // longitude unset($dSign,$dDeg,$dMin,$dSec,$dDeg); $dSign = 1.0; if ($dLon < 0) $dSign = -1.0; $dDeg = intval(abs($dLon)); $dMin = intval(60.0*(abs($dLon)-$dDeg)); $dSec = 3600.0*(abs($dLon)-$dDeg-$dMin/60); $dDeg = $dDeg * $dSign; $arrReturn['DecimalDegrees'] .= ', '.number_format($dLon,4); $arrReturn['DegMinSec'] .= ', '.number_format($dDeg,0).' '.number_format($dMin,0).' '.number_format(round($dSec,0,PHP_ROUND_HALF_UP),0); if ($dSign > 0.0) { $arrReturn['Garmin'] .= ', E'; } else { $arrReturn['Garmin'] .= ', W'; } $arrReturn['Garmin'] .= number_format(abs($dDeg),0).' '.number_format($dMin,0).' '.number_format($dSec,3); $arrReturn['DegMinDecSec'] .= ', '.number_format($dDeg,0).' '.number_format($dMin,0).' '.number_format($dSec,3); $arrReturn['DegDecMin'] .= ', '.number_format($dDeg,0).' '.number_format($dMin+$dSec/60.0,4); $arrReturn['NMEA'] .= ', '.number_format($dDeg,0).number_format($dMin+$dSec/60.0,4); $arrReturn['dDeg'] .= ', '.round($dDeg,0,PHP_ROUND_HALF_UP); $arrReturn['dMin'] .= ', '.round($dMin,0,PHP_ROUND_HALF_UP); $arrReturn['dSec'] .= ', '.round($dSec,0,PHP_ROUND_HALF_UP); $arrReturn['dDecMin'] .= ', '.round($dMin+$dSec/60,4,PHP_ROUND_HALF_UP); $arrReturn['dDecSec'] .= ', '.round($dSec,4,PHP_ROUND_HALF_UP); return $arrReturn; } // arrLatLonFmtsFromDecDeg() /* // Demonstration of function arrDecimalDegrees(). $arrStrLatLon = arrStrLatLonFmts(); foreach ($arrStrLatLon as $sLatLonFmt) { $arrDecDeg = arrDecimalDegrees($sLatLonFmt,false); if ($arrDecDeg == false) { echo '

"'.$sLatLonFmt.'" ERROR - arrDecimalDegrees() invalid format or characters

'; //if ($arrDecDeg == false) die('ERROR - arrDecimalDegrees() "'.$sLatLonFmt.'" invalid format or characters'); } else { echo '

'.$sLatLonFmt.' -> '.number_format($arrDecDeg['lat'],4).','.number_format($arrDecDeg['lon'],4).'   '.$arrDecDeg['src'].'

'; } } */ function arrDecimalDegrees($sLatLon, $bVerbose) { // Convert $sLatLon consisting of any of the following // latitude/longitude formats into Decimal Degrees and // return them in an array: // 37.5324,-80.7585 Decimal Degrees (google) // N37 31 56.4,W80 45 30.6 Garmin // 37 31 56.4,-80 45 30.6 Degrees Minutes Decimal Seconds // 37 31.9404,-80 45.5100 Degrees Decimal Minutes // 37° 31' 56",-80° 45' 31" Degrees Minutes Seconds // 3731.9404,-8045.51 GPS NMEA sentence // // Strip any bad characters out of $sLatLon.. if (strlen($sLatLon) != strlen(filter_var($sLatLon, FILTER_SANITIZE_STRING))) return false; $sLatLon = utf8_decode($sLatLon); $sLatLon = preg_replace("/[^A-Za-z0-9\+\.\,\-[:space:]]/", " ", $sLatLon); $sLatLon = strtoupper($sLatLon); // Must have a comma to delimit lat/lon.. if (strpos($sLatLon,',') === false) return false; // Divide string into lat and lon $arrStrLatLon = Explode(',',$sLatLon); if (count($arrStrLatLon) < 2) return false; // Get lat degrees (only).. $arrStrLat = Explode('.',$arrStrLatLon[0]); $arrStrLat[0] = trim($arrStrLat[0]); if ((strpos($sLatLon,'N') !== false) || (strpos($sLatLon,'S') !== false) || (strpos($sLatLon,'E') !== false) || (strpos($sLatLon,'W') !== false)) { // Garmin N37 31 56.4, W80 45 30.6 // Latitude .. $dLatSign = 1.0; if (strpos($sLatLon,'S') !== false) $dLatSign = -1.0; $arrStrLatLon[0] = str_replace('N','',$arrStrLatLon[0]); $arrStrLatLon[0] = str_replace('S','',$arrStrLatLon[0]); // Split lat string by space $arrStr = Explode(' ',trim($arrStrLatLon[0])); if (count($arrStrLatLon) < 2) return false; $dDeg = floatval(trim($arrStr[0])) * $dLatSign; $dMin = floatval(trim($arrStr[1])); $dSec = floatval(trim($arrStr[2])); $arrLatLon['lat'] = floatval(sign($dDeg)*(abs($dDeg)+$dMin/60.0+$dSec/3600.0)); // Longitude .. $dLonSign = 1.0; if (strpos($sLatLon,'W') !== false) $dLonSign = -1.0; $arrStrLatLon[1] = str_replace('E','',$arrStrLatLon[1]); $arrStrLatLon[1] = str_replace('W','',$arrStrLatLon[1]); // Split lat string by space $arrStr = Explode(' ',trim($arrStrLatLon[1])); if (count($arrStrLatLon) < 2) return false; $dDeg = floatval(trim($arrStr[0])) * $dLonSign; $dMin = floatval(trim($arrStr[1])); $dSec = floatval(trim($arrStr[2])); $arrLatLon['lon'] = floatval(sign($dDeg)*(abs($dDeg)+$dMin/60.0+$dSec/3600.0)); $arrLatLon['src'] = 'Garmin'; } elseif ((stripos($sLatLon,'.') > 3) && (StrLen($arrStrLat[0])>3) && (substr_count($arrStrLat[0],' ') == 0)) { // NMEA // ddmm.mmmm format for latitude and in dddmm.mmmm for longitude // Latitude // Split lat string by decimal point $arrStr = Explode('.',$arrStrLatLon[0]); $arrStr[0] = trim($arrStr[0]); // as amall as 0105.02 and as large as +8959.9834 if ((strlen($arrStr[0]) < 4) || (strlen($arrStr[1]) != 4)) return false; $dDeg = floatval(substr($arrStr[0],0,2)); // dd $dMin = floatval(substr($arrStr[0],2,2)); // mm $sDecMin = substr($arrStr[0],2,2).'.'.$arrStr[1]; // mm.mmmm $dDecMin = floatval($sDecMin); $dSec = fmod($dDecMin,1.0) * 60.0; $arrLatLon['lat'] = floatval(sign($dDeg)*(abs($dDeg)+$dMin/60.0+$dSec/3600.0)); // longitude // Split lat string by decimal point $arrStr = Explode('.',$arrStrLatLon[1]); $arrStr[0] = trim($arrStr[0]); // as amall as 00105.02 and as large as +17959.9834 $dDeg = floatval(substr($arrStr[0],0,3)); // ddd $dMin = floatval(substr($arrStr[0],3,2)); // mm $sDecMin = substr($arrStr[0],3,2).'.'.$arrStr[1]; // mm.mmmm $dDecMin = floatval($sDecMin); $dSec = fmod($dDecMin,1.0) * 60.0; $arrLatLon['lon'] = floatval(sign($dDeg)*(abs($dDeg)+$dMin/60.0+$dSec/3600.0)); $arrLatLon['src'] = 'NMEA'; } elseif ((substr_count($arrStrLat[0],' ') == 1)) { // DegDecMin 37 31.9404,-80 45.5100 // Split lat string by space $arrStr = Explode(' ',trim($arrStrLatLon[0])); if (count($arrStrLatLon) < 2) return false; $dDeg = floatval(trim($arrStr[0])); $sDecMin = $arrStr[1]; $dDecMin = floatval($sDecMin); $dSec = fmod($dDecMin,1.0) * 60.0; // Split lat string by decimal point $arrStr = Explode('.',$arrStr[1]); $arrStr[0] = trim($arrStr[0]); $dMin = floatval($arrStr[0]); $arrLatLon['lat'] = floatval(sign($dDeg)*(abs($dDeg)+$dMin/60.0+$dSec/3600.0)); // Longitude .. // Split lat string by space $arrStr = Explode(' ',trim($arrStrLatLon[1])); if (count($arrStrLatLon) < 2) return false; $dDeg = floatval(trim($arrStr[0])); $sDecMin = $arrStr[1]; $dDecMin = floatval($sDecMin); $dSec = fmod($dDecMin,1.0) * 60.0; // Split lat string by decimal point $arrStr = Explode('.',$arrStr[1]); $arrStr[0] = trim($arrStr[0]); $dMin = floatval($arrStr[0]); $arrLatLon['lon'] = floatval(sign($dDeg)*(abs($dDeg)+$dMin/60.0+$dSec/3600.0)); $arrLatLon['src'] = 'DegDecMin'; } elseif (substr_count($arrStrLat[0],' ') == 2) { // DegMinSec 37 31 56.4, -80 45 30.6 // Latitude .. // Split lat string by space $arrStr = Explode(' ',trim($arrStrLatLon[0])); if (count($arrStrLatLon) < 2) return false; $dDeg = floatval(trim($arrStr[0])); $dMin = floatval(trim($arrStr[1])); $dSec = floatval(trim($arrStr[2])); $arrLatLon['lat'] = floatval(sign($dDeg)*(abs($dDeg)+$dMin/60.0+$dSec/3600.0)); // Longitude .. // Split lat string by space $arrStr = Explode(' ',trim($arrStrLatLon[1])); if (count($arrStrLatLon) < 2) return false; $dDeg = floatval(trim($arrStr[0])); $dMin = floatval(trim($arrStr[1])); $dSec = floatval(trim($arrStr[2])); $arrLatLon['lon'] = floatval(sign($dDeg)*(abs($dDeg)+$dMin/60.0+$dSec/3600.0)); $arrLatLon['src'] = 'DegMinSec'; } elseif ((substr_count($arrStrLat[0],' ') === 0) && (substr_count($arrStrLatLon[0],'.') === 1)) { // DecimalDegrees if ((floatval($arrStrLatLon[0]) === false) || (floatval($arrStrLatLon[1]) === false)) return false; $arrLatLon['lat'] = floatval($arrStrLatLon[0]); $arrLatLon['lon'] = floatval($arrStrLatLon[1]); $arrLatLon['src'] = 'DecimalDegrees'; } else { echo '

"'.$sLatLon.'"   "unrecognized lat/long format

'; return false; } unset($arrStrLatLon,$dSec,$dMin,$dDeg,$arrStr); // validate lat and long values.. if (($arrLatLon['lat'] > 90.0) || ($arrLatLon['lat'] < -90.0) || ($arrLatLon['lon'] > 180.0) || ($arrLatLon['lat'] < -180.0)) { if ($bVerbose == true) echo '

ERROR - lat &/or long out of range '.$sLatLon.' -> '.number_format($arrLatLon['lat'],1).','.number_format($arrLatLon['lon'],1).'

'; return false; } return $arrLatLon; } // arrDecimalDegrees() function arrStrLatLonFmts() { // Returns an associative array of strings with all of the // latitude/longitude formats. // // Latitude +/- 90 deg, Longitude +/- 180 deg $arrStrLatLon['Garmin'] = 'N37 31 56.4, W80 45 30.6'; $arrStrLatLon['DegDecMin'] = '37 31.9404,-80 45.5100'; $arrStrLatLon['DegMinDecSec'] = '37 31 56.4, -80 45 30.6'; $arrStrLatLon['DecimalDegrees'] = '37.5324,-80.7585'; $arrStrLatLon['NMEA'] = '3731.9404,-8045.51'; // Greenland N76 58 10.6 W66 04 00.5 -> 76.969611, -66.066806 // New Zealand S43 03 53.6 E174 56 57.7 -> -43.064889, 174.949361 //$arrStrLatLon['validB'] = '37 31 56.4, W80 45 30.6'; //$arrStrLatLon['validC'] = '37 31 56.4, E80 45 30.6'; //$arrStrLatLon['validD'] = 'S37 31 56.4, -80 45 30.6'; //$arrStrLatLon['validE'] = '-3731.9404,-8045.51'; //$arrStrLatLon['validF'] = '37 31.9404,-80 45.5100'; // has unknown character in it for the space //$arrStrLatLon['validG'] = '+38.1389, -76.632'; //$arrStrLatLon['validH'] = '76.969611, -66.066806'; //$arrStrLatLon['validI'] = '-43.064889, 174.949361'; //$arrStrLatLon['invalidA'] = '3731.9404 -8045.51'; //$arrStrLatLon['invalidB'] = '37° 31\' 56", -80° 45\' 31"'; //$arrStrLatLon['invalidF'] = '37° 31\' 56", -80° 45\' 31"'; //$arrStrLatLon['invalidG'] = '-91.064889, -181.949361'; //$arrStrLatLon['invalidH'] = '+91.064889, +181.949361'; return $arrStrLatLon; } // arrStrLatLonFmts() function sign($n){ return ($n > 0) - ($n < 0); } // // =========================================================================== // // MIT License // // Copyright (c) 2018 Mechatronic Solutions LLC (www.http://mechatronicsolutionsllc.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // ===========================================================================