Regular expression for matching latitude/longitude coordinates?
RegexRegex Problem Overview
I'm trying to create a regular expression for matching latitude/longitude coordinates. For matching a double-precision number I've used (\-?\d+(\.\d+)?)
, and tried to combine that into a single expression:
^(\-?\d+(\.\d+)?),\w*(\-?\d+(\.\d+)?)$
I expected this to match a double, a comma, perhaps some space, and another double, but it doesn't seem to work. Specifically it only works if there's NO space, not one or more. What have I done wrong?
Regex Solutions
Solution 1 - Regex
This one will strictly match latitude and longitude values that fall within the correct range:
^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$
Matches
- +90.0, -127.554334
- 45, 180
- -90, -180
- -90.000, -180.0000
- +90, +180
- 47.1231231, 179.99999999
Doesn't Match
- -90., -180.
- +90.1, -100.111
- -91, 123.456
- 045, 180
Solution 2 - Regex
I am using these ones (decimal format, with 6 decimal digits):
Latitude
^(\+|-)?(?:90(?:(?:\.0{1,6})?)|(?:[0-9]|[1-8][0-9])(?:(?:\.[0-9]{1,6})?))$
Longitude
^(\+|-)?(?:180(?:(?:\.0{1,6})?)|(?:[0-9]|[1-9][0-9]|1[0-7][0-9])(?:(?:\.[0-9]{1,6})?))$
Here is a gist that tests both, reported here also, for ease of access. It's a Java TestNG test. You need Slf4j, Hamcrest and Lombok to run it:
import static org.hamcrest.Matchers.*;
import static org.hamcrest.MatcherAssert.*;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;
@Slf4j
public class LatLongValidationTest {
protected static final String LATITUDE_PATTERN="^(\\+|-)?(?:90(?:(?:\\.0{1,6})?)|(?:[0-9]|[1-8][0-9])(?:(?:\\.[0-9]{1,6})?))$";
protected static final String LONGITUDE_PATTERN="^(\\+|-)?(?:180(?:(?:\\.0{1,6})?)|(?:[0-9]|[1-9][0-9]|1[0-7][0-9])(?:(?:\\.[0-9]{1,6})?))$";
@Test
public void latitudeTest(){
DecimalFormat df = new DecimalFormat("#.######");
df.setRoundingMode(RoundingMode.UP);
double step = 0.01;
Double latitudeToTest = -90.0;
while(latitudeToTest <= 90.0){
boolean result = df.format(latitudeToTest).matches(LATITUDE_PATTERN);
log.info("Latitude: tested {}. Result (matches regex): {}", df.format(latitudeToTest), result);
assertThat(result, is(true));
latitudeToTest += step;
}
latitudeToTest = -90.1;
while(latitudeToTest >= -200.0){
boolean result = df.format(latitudeToTest).matches(LATITUDE_PATTERN);
log.info("Latitude: tested {}. Result (matches regex): {}", df.format(latitudeToTest), result);
assertThat(result, is(false));
latitudeToTest -= step;
}
latitudeToTest = 90.01;
while(latitudeToTest <= 200.0){
boolean result = df.format(latitudeToTest).matches(LATITUDE_PATTERN);
log.info("Latitude: tested {}. Result (matches regex): {}", df.format(latitudeToTest), result);
assertThat(result, is(false));
latitudeToTest += step;
}
}
@Test
public void longitudeTest(){
DecimalFormat df = new DecimalFormat("#.######");
df.setRoundingMode(RoundingMode.UP);
double step = 0.01;
Double longitudeToTest = -180.0;
while(longitudeToTest <= 180.0){
boolean result = df.format(longitudeToTest).matches(LONGITUDE_PATTERN);
log.info("Longitude: tested {}. Result (matches regex): {}", df.format(longitudeToTest), result);
assertThat(result, is(true));
longitudeToTest += step;
}
longitudeToTest = -180.01;
while(longitudeToTest >= -300.0){
boolean result = df.format(longitudeToTest).matches(LONGITUDE_PATTERN);
log.info("Longitude: tested {}. Result (matches regex): {}", df.format(longitudeToTest), result);
assertThat(result, is(false));
longitudeToTest -= step;
}
longitudeToTest = 180.01;
while(longitudeToTest <= 300.0){
boolean result = df.format(longitudeToTest).matches(LONGITUDE_PATTERN);
log.info("Longitude: tested {}. Result (matches regex): {}", df.format(longitudeToTest), result);
assertThat(result, is(false));
longitudeToTest += step;
}
}
}
Solution 3 - Regex
Whitespace is \s, not \w
^(-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)$
See if this works
Solution 4 - Regex
Actually Alix Axel, above regex is wrong in latitude, longitude ranges point of view.
Latitude measurements range from –90° to +90° Longitude measurements range from –180° to +180°
So the regex given below validates more accurately.
Also, as per my thought no one should restrict decimal point in latitude/longitude.
^([-+]?\d{1,2}([.]\d+)?),\s*([-+]?\d{1,3}([.]\d+)?)$
OR for Objective C
^([-+]?\\d{1,2}([.]\\d+)?),\\s*([-+]?\\d{1,3}([.]\\d+)?)$
Solution 5 - Regex
^-?[0-9]{1,3}(?:\.[0-9]{1,10})?$
Regex breakdown:
^-?[0-9]{1,3}(?:\.[0-9]{1,10})?$
-?
# accept negative values
^
# Start of string
[0-9]{1,3}
# Match 1-3 digits (i. e. 0-999)
(?:
# Try to match...
\.
# a decimal point
[0-9]{1,10}
# followed by one to 10 digits (i. e. 0-9999999999)
)?
# ...optionally
$
# End of string
Solution 6 - Regex
@macro-ferrari I did find a way to shorten it, and without look aheads in the light of all recent talks about regex engines
const LAT_RE = /^[+-]?(([1-8]?[0-9])(\.[0-9]{1,6})?|90(\.0{1,6})?)$/;
const LONG_RE = /^[+-]?((([1-9]?[0-9]|1[0-7][0-9])(\.[0-9]{1,6})?)|180(\.0{1,6})?)$/;
Solution 7 - Regex
Try this:
^(\()([-+]?)([\d]{1,2})(((\.)(\d+)(,)))(\s*)(([-+]?)([\d]{1,3})((\.)(\d+))?(\)))$
Check it out at:
Paste the expression in the top box, then put things like this in the bottom box:
(80.0123, -34.034)
(80.0123)
(80.a)
(980.13, 40)
(99.000, 122.000)
Regex breakdown:
^ # The string must start this way (there can't be anything before).
(\() # An opening parentheses (escaped with a backslash).
([-+]?) # An optional minus, or an optional plus.
([\d]{1,2}) # 1 or 2 digits (0-9).
( # Start of a sub-pattern.
( # Start of a sub-pattern.
(\.) # A dot (escaped with a backslash).
(\d+) # One or more digits (0-9).
(,) # A comma.
) # End of a sub-pattern.
) # End of a sub-pattern.
(\s*) # Zero or more spaces.
( # Start of a sub-pattern.
([-+]?) # An optional minus, or an optional plus.
([\d]{1,3}) # 1 to 3 digits (0-9).
( # Start of a pattern.
(\.) # A dot (escaped with a backslash).
(\d+) # One or more digits (0-9).
)? # End of an optional pattern.
(\)) # A closing parenthesis (escaped with a backkslash).
) # End of a pattern
$ # The string must end this way (there can't be anything after).
Now, what this does NOT do is restrict itself to this range:
(-90 to +90, and -180 to +180)
Instead, it simple restricts itself to this range:
(-99 to +99, -199 to +199)
But the point is mainly just to break down each piece of the expression.
Solution 8 - Regex
Here is a more strict version:
^([-+]?\d{1,2}[.]\d+),\s*([-+]?\d{1,3}[.]\d+)$
- Latitude =
-90
--+90
- Longitude =
-180
--+180
Solution 9 - Regex
Python:
Latitude: result = re.match("^[+-]?((90\.?0*$)|(([0-8]?[0-9])\.?[0-9]*$))", '-90.00001')
Longitude: result = re.match("^[+-]?((180\.?0*$)|(((1[0-7][0-9])|([0-9]{0,2}))\.?[0-9]*$))", '-0.0000')
Latitude should fail in the example.
Solution 10 - Regex
Regex shorten @marco-ferrari solution by replacing the multiple use of [0-9] with subset of [0-9]. Also removed unnecessary quantifiers such as ?: from various places
lat "^([+-])?(?:90(?:\\.0{1,6})?|((?:|[1-8])[0-9])(?:\\.[0-9]{1,6})?)$";
long "^([+-])?(?:180(?:\\.0{1,6})?|((?:|[1-9]|1[0-7])[0-9])(?:\\.[0-9]{1,6})?)$";
**Matches for Lat**
Valid between -90 to +90 with up to 6 decimals.
**Matches for Long**
Valid between -180 to +180 with up to 6 decimals.
Solution 11 - Regex
I believe you're using \w (word character) where you ought to be using \s (whitespace). Word characters typically consist of [A-Za-z0-9_], so that excludes your space, which then further fails to match on the optional minus sign or a digit.
Solution 12 - Regex
This would work for format like this: 31 ͦ 37.4' E
^[-]?\d{1,2}[ ]*ͦ[ ]*\d{1,2}\.?\d{1,2}[ ]*\x27[ ]*\w$
Solution 13 - Regex
Ruby
Longitude -179.99999999..180
/^(-?(?:1[0-7]|[1-9])?\d(?:\.\d{1,8})?|180(?:\.0{1,8})?)$/ === longitude.to_s
Latitude -89.99999999..90
/^(-?[1-8]?\d(?:\.\d{1,8})?|90(?:\.0{1,8})?)$/ === latitude.to_s
Solution 14 - Regex
A complete and simple method in objective C for checking correct pattern for latitude and longitude is:
-( BOOL )textIsValidValue:(NSString*) searchedString
{
NSRange searchedRange = NSMakeRange(0, [searchedString length]);
NSError *error = nil;
NSString *pattern = @"^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?),\\s*[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$";
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern: pattern options:0 error:&error];
NSTextCheckingResult *match = [regex firstMatchInString:searchedString options:0 range: searchedRange];
return match ? YES : NO;
}
where searchedString is the input that user would enter in the respective textfield.
Solution 15 - Regex
PHP
Here is the PHP's version (input values are: $latitude
and $longitude
):
$latitude_pattern = '/\A[+-]?(?:90(?:\.0{1,18})?|\d(?(?<=9)|\d?)\.\d{1,18})\z/x';
$longitude_pattern = '/\A[+-]?(?:180(?:\.0{1,18})?|(?:1[0-7]\d|\d{1,2})\.\d{1,18})\z/x';
if (preg_match($latitude_pattern, $latitude) && preg_match($longitude_pattern, $longitude)) {
// Valid coordinates.
}
Solution 16 - Regex
this one enforces 3 numbers after the comma to avoid false matches:
(?<latitude>-?\d+\.\d{3,10}),(?<longitude>-?\d+\.\d{3,10})
Solution 17 - Regex
You can try this:
var latExp = /^(?=.)-?((8[0-5]?)|([0-7]?[0-9]))?(?:\.[0-9]{1,20})?$/;
var lngExp = /^(?=.)-?((0?[8-9][0-9])|180|([0-1]?[0-7]?[0-9]))?(?:\.[0-9]{1,20})?$/;
Solution 18 - Regex
Try this:
^[-+]?(([0-8]\\d|\\d)(\\.\\d+)?|90(\\.0+)?)$,\s*^[-+]?((1[0-7]\\d(\\.\\d+)?)|(180(\\.0+)?)|(\\d\\d(\\.\\d+)?)|(\\d(\\.\\d+)?))$
Solution 19 - Regex
Try this:
(?<!\d)([-+]?(?:[1-8]?\d(?:\.\d+)?|90(?:\.0+)?)),\s*([-+]?(?:180(?:\.0+)?|(?:(?:1[0-7]\d)|(?:[1-9]?\d))(?:\.\d+)?))(?!\d)`