26

Alright I have what I would call a massive list of longitude and latitude coordinates. That said I also have a handful of sources I pull these coordinates in from. Some of them come from get/post methods which can cause potential security holes in my site/service. So I am trying to figure out how to validate longitude and latitude via PHP. I was thinking something regex via preg_match. But I could be wrong, maybe there's an easier way someone would like to suggest. I've tried my own concepts, and I have tried various internet brew concepts of trying to find a regex pattern that will validate these for me via preg_match (or similar again if you got a better suggestion I am all ears).

My Last failed attempt prior to finally caving in and coming here was..

preg_match('^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$', $geoResults['latitude']))

which yields " preg_match() [function.preg-match]: No ending delimiter '^' found " as my error. Last couple attempts I have tried yielded that error so I have no idea what it is or means.

Rusty Fausak
  • 7,355
  • 1
  • 27
  • 38
chris
  • 36,115
  • 52
  • 143
  • 252

8 Answers8

38

It's a bit old question, but anyway I post my solution here:

preg_match('/^[-]?(([0-8]?[0-9])\.(\d+))|(90(\.0+)?);[-]?((((1[0-7][0-9])|([0-9]?[0-9]))\.(\d+))|180(\.0+)?)$/', $geoResults['latlng']);

I assumed here that u split lat. from lng. by semicolon. If u want to check only lat. or only lng. here are regexp's;

Rgx for lat.:

/^[-]?(([0-8]?[0-9])\.(\d+))|(90(\.0+)?)$/

Rgx for lng.:

/^[-]?((((1[0-7][0-9])|([0-9]?[0-9]))\.(\d+))|180(\.0+)?)$/

Here is an improved online demo: https://regex101.com/r/bV5fA1/1

Doro
  • 755
  • 8
  • 25
  • Why the limit at `80` and `179`? – Toto Feb 25 '14 at 08:42
  • I've edited my posted and changed the limits to 89.(9) and 179.(9). Why? Becouse of point 179.(9) and -179.(9) are pretty the same and it allows to make rgx easier. – Doro Feb 25 '14 at 10:05
  • 2
    Better but not suficient, lat 90 and long 180 are perfectly valid. – Toto Feb 25 '14 at 10:07
  • Edited once again. Now lat 90 and lng 180 will be valid ;) Thanks for suggestions. – Doro Feb 25 '14 at 10:16
  • OK, +1 but you should escape the dot in `(.0+)`. – Toto Feb 25 '14 at 10:34
  • Done. Thank you for appreciation and your help to finally create a valid answer. – Doro Feb 25 '14 at 11:16
  • `[0-8]?` should be replaced with `[1-9]?` to make values like `05.12345` invalid. Anyway, thank you for your regexp! PS: The same with `[0-9]?` for longitude. – Kirzilla Oct 01 '19 at 13:33
  • I tried to test on 0.000000;0.000000, yes I know they are still valid and point to a certain location but in my case that's never gonna happen and it's a bit of an overkill but other then that your solution is awesome! – Anupam Dec 16 '20 at 00:36
24

Add forward slashes to the beginning and end of the match sequence to make it valid regex syntax:

preg_match('/^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$/', $geoResults['latitude']);

For your question on whether to use regular expressions (regex) or not, in this case using regex (PCRE preg_match()) is the best way to secure your site. When matching variable complex string arrangements, regex is the way to go. It's common for developers to turn to regex for matching a static string such as 'abc'. This is when strpos() or str_replace() are better choices.

Chris Bornhoft
  • 4,195
  • 4
  • 37
  • 55
2

Why not use modern and unit tested Assertion library for that?

Example Value Object class:

<?php
namespace MyApp\ValueObject;

use Assert\Assertion;

class Location
{
    private $lat;
    private $lon;

    public function __construct($lat, $lon)
    {
        Assertion::greaterOrEqualThan($lat, -90.0);
        Assertion::lessOrEqualThan($lat, 90.0);
        Assertion::greaterOrEqualThan($lon, -180.0);
        Assertion::lessOrEqualThan($lon, 180.0);

        $this->lat = $lat;
        $this->lon = $lon;
    }

    public function latitude()
    {
        return $this->lat;
    }

    public function longitude()
    {
        return $this->lon;
    }

    public function __toString()
    {
        return $this->lat . ' ' . $this->lon;
    }

Usage:

$location = new \MyApp\ValueObject\Location(24.7, -20.4059);

echo $location->latitude() , ' ' , $location->longitude();

// or simply

echo $location;
Mike Doe
  • 16,349
  • 11
  • 65
  • 88
1

I want to validate latitude and longitude, too, and have this result:

-90.0000 - 90.0000

^-?([0-9]|[1-8][0-9]|90)\.{1}\d{4}$

-180.0000 - 180.0000

^-?([0-9]|[1-9][0-9]|1[0-7][0-9]|180)\.{1}\d{4}$

I have tested here with pcre.

haheute
  • 2,129
  • 3
  • 32
  • 49
  • 1
    You should post this as 1 regex to fetch both lat and long from the same line. Also, `[0-9]|[1-9][0-9]` can be simplified as `[1-9]?[0-9]`. – Mariano Nov 30 '15 at 09:01
1

regex

/([0-9.-]+).+?([0-9.-]+)/
Mahdi Bashirpour
  • 17,147
  • 12
  • 117
  • 144
1

Function to validate Latitude

function validateLatitude($lat) {


    $lat_array = explode( '.' , $lat );

    if( sizeof($lat_array) !=2 ){
        return '_n_';
    }

    if ( ! ( is_numeric($lat_array[0]) && $lat_array[0]==round($lat_array[0], 0) && is_numeric($lat_array[1]) && $lat_array[1]==round($lat_array[1], 0)  ) ){
        return '_n_';
    }

    if( $lat >= -90 && $lat <= 90 ){
        return '_s_';
    }
    else {
        return '_n_';
    }

}

Function to validate Longitude

function validateLongitude($long) {

    $long_array = explode( '.' , $long );

    if( sizeof($long_array) !=2 ){
        return '_n_';
    }

    if (!( is_numeric($long_array[0]) && $long_array[0]==round($long_array[0], 0) && is_numeric($long_array[1]) && $long_array[1]==round($long_array[1], 0)  ) ){
        return '_n_';
    }

    if( $long >= -180 && $long <= 180 ){
        return '_s_';
    }
    else {
        return '_n_';
    }

}
0

It's real work unicum solution in net " -90.0000 - 90.0000

^-?([0-9]|[1-8][0-9]|90)\.{1}\d{4}$

-180.0000 - 180.0000

^-?([0-9]|[1-9][0-9]|1[0-7][0-9]|180)\.{1}\d{4}$
"

For &lat=90.000000 &lon=180.000000 :

"/^-?([0-9]|[1-8][0-9]|90)\.{1}\d{1,6}$/"

"/^-?([1]?[1-7][1-9]|[1]?[1-8][0]|[1-9]?[0-9])\.{1}\d{1,6}/"
Den812
  • 13
  • 3
0

These didn't seem very accurate being that:

latitude is between -90 and 90

longitude is between -180 and 90

and

\d in regex matches more than [0-9]

Also a whole library just for some regex didn't seem logical...

So I built and tested:

//Latitude
if(preg_match('/^-?(90|[1-8][0-9][.][0-9]{1,20}|[0-9][.][0-9]{1,20})$/', '89.33333')) {
                            echo "<br>a match<br>"; 
} //will pass ->89.33333


//Longitude
if(preg_match('/^-?(180|1[1-7][0-9][.][0-9]{1,20}|[1-9][0-9][.][0-9]{1,20}|[0-9][.][0-9]{1,20})$/', '180.33333')) {
                            echo "<br>b match<br>"; 
} // will fail anything over 180 ->180.33333
JSG
  • 390
  • 1
  • 4
  • 13