Skip to content

LatLon parsing #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 81 additions & 4 deletions src/gov/nasa/worldwind/geom/LatLon.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import gov.nasa.worldwind.util.*;

import java.util.*;
import java.util.regex.*;

/**
* Represents a point on the two-dimensional surface of a globe. Latitude is the degrees North and ranges between [-90,
Expand All @@ -23,6 +24,20 @@
public class LatLon
{
public static final LatLon ZERO = new LatLon(Angle.ZERO, Angle.ZERO);

private static final String SEPARATORS = "(\\s*|,|,\\s*)";

public static final Pattern DECIMAL_PATTERN = Pattern.compile(
"([-|\\+]?\\d+?(\\.\\d+?)??\\s*[N|n|S|s]??)" +
SEPARATORS +
"([-|\\+]?\\d+?(\\.\\d+?)??\\s*[E|e|W|w]??)");

public static final Pattern DMS_PATTERN = Pattern.compile(
"([-|\\+]?\\d{1,3}[d|D|\u00B0|\\s](\\s*\\d{1,2}['|\u2019|\\s])?(\\s*\\d{1,2}(\\.\\d+?)??[\"|\u201d])?\\s*[N|n|S|s]?)" +
SEPARATORS +
"([-|\\+]?\\d{1,3}[d|D|\u00B0|\\s](\\s*\\d{1,2}['|\u2019|\\s])?(\\s*\\d{1,2}(\\.\\d+?)??[\"|\u201d])?\\s*[E|e|W|w]?)");

public static final Pattern PATTERN = Pattern.compile(DECIMAL_PATTERN.pattern() + "|" + DMS_PATTERN.pattern());

/**
* A near zero threshold used in some of the rhumb line calculations where floating point calculations cause
Expand Down Expand Up @@ -1640,15 +1655,14 @@ public static LatLon intersectionWithMeridian(LatLon p1, LatLon p2, Angle meridi
* Parses a string containing latitude and longitude coordinates in either Degrees-minutes-seconds or decimal
* degrees. The latitude must precede the longitude and the angles must be separated by a comma.
*
* @param latLonString a string containing the comma separated latitude and longitude in either DMS or decimal
* degrees.
* @param latLonString a string containing the comma separated latitude and longitude in either DMS or decimal degrees.
*
* @return a <code>LatLon</code> instance with the parsed angles.
*
* @throws IllegalArgumentException if <code>latLonString</code> is null.
* @throws NumberFormatException if the string does not form a latitude, longitude pair.
*/
public LatLon parseLatLon(String latLonString) // TODO
public static LatLon parseLatLon(String latLonString)
{
if (latLonString == null)
{
Expand All @@ -1657,7 +1671,70 @@ public LatLon parseLatLon(String latLonString) // TODO
throw new IllegalArgumentException(msg);
}

throw new UnsupportedOperationException(); // TODO: remove when implemented
Angle lat = null;
Angle lon = null;
latLonString = latLonString.trim();

// Try to extract a pair of signed decimal values separated by a space, ',' or ', '
// Allow E, W, S, N sufixes
Matcher matcher = DECIMAL_PATTERN.matcher(latLonString);
if (matcher.matches())
{
String sLat = matcher.group(1).trim(); // Latitude
int signLat = 1;
char suffix = sLat.toUpperCase().charAt(sLat.length() - 1);
if (!Character.isDigit(suffix))
{
signLat = suffix == 'N' ? 1 : -1;
sLat = sLat.substring(0, sLat.length() - 1);
sLat = sLat.trim();
}

String sLon = matcher.group(4).trim(); // Longitude
int signLon = 1;
suffix = sLon.toUpperCase().charAt(sLon.length() - 1);
if (!Character.isDigit(suffix))
{
signLon = suffix == 'E' ? 1 : -1;
sLon = sLon.substring(0, sLon.length() - 1);
sLon = sLon.trim();
}

lat = Angle.fromDegrees(Double.parseDouble(sLat) * signLat);
lon = Angle.fromDegrees(Double.parseDouble(sLon) * signLon);
}

// Try to extract two degrees minute seconds blocks separated by a space, ',' or ', '
// Allow S, N, W, E suffixes and signs.
// eg: -123° 34' 42" +45° 12' 30"
// eg: 123° 34' 42"S 45° 12' 30"W
if (lat == null || lon == null)
{
matcher = DMS_PATTERN.matcher(latLonString);
if (matcher.matches())
{
lat = Angle.fromDMS(matcher.group(1));
lon = Angle.fromDMS(matcher.group(6));
}
}

if (lat == null || lon == null)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", latLonString);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}

if(lat.degrees >= -90 && lat.degrees <= 90 && lon.degrees >= -180 && lon.degrees <= 180)
{
return new LatLon(lat, lon);
}
else
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", latLonString);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
}

@Override
Expand Down
174 changes: 20 additions & 154 deletions src/gov/nasa/worldwindx/examples/GoToCoordinatePanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,16 @@ private JPanel makePanel()
{
public void actionPerformed(ActionEvent event)
{
LatLon latLon = computeLatLonFromString(coordInput.getText(), wwd.getModel().getGlobe());
LatLon latLon = null;
try
{
MGRSCoord coordinate = MGRSCoord.fromString(coordInput.getText(), wwd.getModel().getGlobe());
latLon = new LatLon(coordinate.getLatitude(), coordinate.getLongitude());
}
catch (Exception e)
{
latLon = LatLon.parseLatLon(coordInput.getText());
}
updateResult(latLon);
}
});
Expand All @@ -92,7 +101,16 @@ public void actionPerformed(ActionEvent event)
{
public void actionPerformed(ActionEvent event)
{
LatLon latLon = computeLatLonFromString(coordInput.getText(), wwd.getModel().getGlobe());
LatLon latLon = null;
try
{
MGRSCoord coordinate = MGRSCoord.fromString(coordInput.getText(), wwd.getModel().getGlobe());
latLon = new LatLon(coordinate.getLatitude(), coordinate.getLongitude());
}
catch (Exception e)
{
latLon = LatLon.parseLatLon(coordInput.getText());
}
updateResult(latLon);
if (latLon != null)
{
Expand Down Expand Up @@ -125,156 +143,4 @@ private void updateResult(LatLon latLon)
resultLabel.setText("Invalid coordinates");

}

/**
* Tries to extract a latitude and a longitude from the given text string.
*
* @param coordString the input string.
* @param globe the current <code>Globe</code>.
* @return the corresponding <code>LatLon</code> or <code>null</code>.
*/
private static LatLon computeLatLonFromString(String coordString, Globe globe)
{
if (coordString == null)
{
String msg = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}

Angle lat = null;
Angle lon = null;
coordString = coordString.trim();
String regex;
String separators = "(\\s*|,|,\\s*)";
Pattern pattern;
Matcher matcher;

// Try MGRS - allow spaces
regex = "\\d{1,2}[A-Za-z]\\s*[A-Za-z]{2}\\s*\\d{1,5}\\s*\\d{1,5}";
if (coordString.matches(regex))
{
try
{
MGRSCoord MGRS = MGRSCoord.fromString(coordString, globe);
// NOTE: the MGRSCoord does not always report errors with invalide strings,
// but will have lat and lon set to zero
if (MGRS.getLatitude().degrees != 0 || MGRS.getLatitude().degrees != 0)
{
lat = MGRS.getLatitude();
lon = MGRS.getLongitude();
}
else
return null;
}
catch (IllegalArgumentException e)
{
return null;
}
}

// Try to extract a pair of signed decimal values separated by a space, ',' or ', '
// Allow E, W, S, N sufixes
if (lat == null || lon == null)
{
regex = "([-|\\+]?\\d+?(\\.\\d+?)??\\s*[N|n|S|s]??)";
regex += separators;
regex += "([-|\\+]?\\d+?(\\.\\d+?)??\\s*[E|e|W|w]??)";
pattern = Pattern.compile(regex);
matcher = pattern.matcher(coordString);
if (matcher.matches())
{
String sLat = matcher.group(1).trim(); // Latitude
int signLat = 1;
char suffix = sLat.toUpperCase().charAt(sLat.length() - 1);
if (!Character.isDigit(suffix))
{
signLat = suffix == 'N' ? 1 : -1;
sLat = sLat.substring(0, sLat.length() - 1);
sLat = sLat.trim();
}

String sLon = matcher.group(4).trim(); // Longitude
int signLon = 1;
suffix = sLon.toUpperCase().charAt(sLon.length() - 1);
if (!Character.isDigit(suffix))
{
signLon = suffix == 'E' ? 1 : -1;
sLon = sLon.substring(0, sLon.length() - 1);
sLon = sLon.trim();
}

lat = Angle.fromDegrees(Double.parseDouble(sLat) * signLat);
lon = Angle.fromDegrees(Double.parseDouble(sLon) * signLon);
}
}

// Try to extract two degrees minute seconds blocks separated by a space, ',' or ', '
// Allow S, N, W, E suffixes and signs.
// eg: -123� 34' 42" +45� 12' 30"
// eg: 123� 34' 42"S 45� 12' 30"W
if (lat == null || lon == null)
{
regex = "([-|\\+]?\\d{1,3}[d|D|\u00B0|\\s](\\s*\\d{1,2}['|\u2019|\\s])?(\\s*\\d{1,2}[\"|\u201d])?\\s*[N|n|S|s]?)";
regex += separators;
regex += "([-|\\+]?\\d{1,3}[d|D|\u00B0|\\s](\\s*\\d{1,2}['|\u2019|\\s])?(\\s*\\d{1,2}[\"|\u201d])?\\s*[E|e|W|w]?)";
pattern = Pattern.compile(regex);
matcher = pattern.matcher(coordString);
if (matcher.matches())
{
lat = parseDMSString(matcher.group(1));
lon = parseDMSString(matcher.group(5));
}
}

if (lat == null || lon == null)
return null;

if(lat.degrees >= -90 && lat.degrees <= 90 && lon.degrees >= -180 && lon.degrees <= 180)
return new LatLon(lat, lon);

return null;
}

/**
* Parse a Degrees, Minute, Second coordinate string.
*
* @param dmsString the string to parse.
* @return the corresponding <code>Angle</code> or null.
*/
private static Angle parseDMSString(String dmsString)
{
// Replace degree, min and sec signs with space
dmsString = dmsString.replaceAll("[D|d|\u00B0|'|\u2019|\"|\u201d]", " ");
// Replace multiple spaces with single ones
dmsString = dmsString.replaceAll("\\s+", " ");
dmsString = dmsString.trim();

// Check for sign prefix and suffix
int sign = 1;
char suffix = dmsString.toUpperCase().charAt(dmsString.length() - 1);
if (!Character.isDigit(suffix))
{
sign = (suffix == 'N' || suffix == 'E') ? 1 : -1;
dmsString = dmsString.substring(0, dmsString.length() - 1);
dmsString = dmsString.trim();
}
char prefix = dmsString.charAt(0);
if (!Character.isDigit(prefix))
{
sign *= (prefix == '-') ? -1 : 1;
dmsString = dmsString.substring(1, dmsString.length());
}

// Process degrees, minutes and seconds
String[] DMS = dmsString.split(" ");
double d = Integer.parseInt(DMS[0]);
double m = DMS.length > 1 ? Integer.parseInt(DMS[1]) : 0;
double s = DMS.length > 2 ? Integer.parseInt(DMS[2]) : 0;

if (m >= 0 && m <= 60 && s >= 0 && s <= 60)
return Angle.fromDegrees(d * sign + m / 60 * sign + s / 3600 * sign);

return null;
}
}