/*
Copyright (C) 2001, 2008 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.examples;
import gov.nasa.worldwind.WorldWindow;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.LatLon;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.util.Logging;
import gov.nasa.worldwind.geom.coords.MGRSCoord;
import gov.nasa.worldwind.view.FlyToOrbitViewStateIterator;
import gov.nasa.worldwind.view.OrbitView;
import javax.swing.*;
import javax.swing.border.CompoundBorder;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This panel let the user input different coordinates and displays the corresponding
* latitude and longitude in decimal degrees.
* <p>
* Supported format are:
* <ul>
* <li>MGRS strings with or without separting spaces.</li>
* <li>Decimal degrees with sign prefix or N, S, E, W suffix.</li>
* <li>Degrees, minutes and seconds with sign prefix or N, S, E, W suffix.</li>
* </ul>
* The separator between lat/lon pairs can be ',', ', ' or any number of spaces.
* </p>
* <p>
* Examples:<pre>
* 11sku528111
* 11S KU 528 111
*
* 45N 123W
* +45.1234, -123.12
* 45.1234N 123.12W
*
* 45� 30' 00"N, 50� 30'W
* 45�30' -50�30'
* 45 30 N 50 30 W
* </pre>
* </p>
*
* @author Patrick Murris
* @version $Id: GoToCoordinatePanel.java 5175 2008-04-25 21:12:21Z patrickmurris $
*/
public class GoToCoordinatePanel extends JPanel
{
private WorldWindow wwd;
private JTextField coordInput;
private JLabel resultLabel;
public GoToCoordinatePanel(WorldWindow wwd)
{
super(new GridLayout(0, 1, 0, 0));
this.wwd = wwd;
this.makePanel();
}
private JPanel makePanel()
{
JPanel controlPanel = this;
// Coord input
JPanel coordPanel = new JPanel(new GridLayout(0, 1, 0, 0));
coordPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
this.coordInput = new JTextField(10);
this.coordInput.setToolTipText("Type coordinates and press Enter");
this.coordInput.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
LatLon latLon = computeLatLonFromString(coordInput.getText(), wwd.getModel().getGlobe());
updateResult(latLon);
}
});
coordPanel.add(this.coordInput);
// result panel
JPanel resultPanel = new JPanel(new GridLayout(0, 1, 0, 0));
resultPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
this.resultLabel = new JLabel();
resultPanel.add(this.resultLabel);
// goto button
JPanel gotoPanel = new JPanel(new GridLayout(0, 1, 0, 0));
gotoPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
JButton gotoButton = new JButton("Go to location");
gotoButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
LatLon latLon = computeLatLonFromString(coordInput.getText(), wwd.getModel().getGlobe());
updateResult(latLon);
if (latLon != null)
{
OrbitView view = (OrbitView)wwd.getView();
Globe globe = wwd.getModel().getGlobe();
view.applyStateIterator(FlyToOrbitViewStateIterator.createPanToIterator(
view, globe, new Position(latLon, 0), view.getHeading(), view.getPitch(), view.getZoom()));
}
}
});
gotoPanel.add(gotoButton);
controlPanel.add(coordPanel);
controlPanel.add(resultPanel);
controlPanel.add(gotoPanel);
controlPanel.setBorder(
new CompoundBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9), new TitledBorder("Go to")));
return controlPanel;
}
private void updateResult(LatLon latLon)
{
if (latLon != null)
{
coordInput.setText(coordInput.getText().toUpperCase());
resultLabel.setText(String.format("Lat %7.4f\u00B0 Lon %7.4f\u00B0",
latLon.getLatitude().degrees, latLon.getLongitude().degrees));
}
else
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;
}
}