diff --git a/src/gov/nasa/worldwind/geom/LatLon.java b/src/gov/nasa/worldwind/geom/LatLon.java index 723e3aa90d..cbcf64ca7d 100644 --- a/src/gov/nasa/worldwind/geom/LatLon.java +++ b/src/gov/nasa/worldwind/geom/LatLon.java @@ -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, @@ -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 @@ -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 LatLon instance with the parsed angles. * * @throws IllegalArgumentException if latLonString 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) { @@ -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 diff --git a/src/gov/nasa/worldwindx/examples/GoToCoordinatePanel.java b/src/gov/nasa/worldwindx/examples/GoToCoordinatePanel.java index ce15a11596..5fdd7b0f5b 100644 --- a/src/gov/nasa/worldwindx/examples/GoToCoordinatePanel.java +++ b/src/gov/nasa/worldwindx/examples/GoToCoordinatePanel.java @@ -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); } }); @@ -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) { @@ -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 Globe. - * @return the corresponding LatLon or null. - */ - 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 Angle 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; - } }