Changeset 11045 in josm


Ignore:
Timestamp:
2016-09-23T21:13:02+02:00 (12 months ago)
Author:
Don-vip
Message:

fix #13674 - allow case insensitive lat/lon input + reject more invalid cases

Location:
trunk
Files:
1 deleted
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/JumpToAction.java

    r10601 r11045  
    2020import org.openstreetmap.josm.data.coor.LatLon;
    2121import org.openstreetmap.josm.gui.MapView;
    22 import org.openstreetmap.josm.gui.dialogs.LatLonDialog;
    2322import org.openstreetmap.josm.gui.widgets.JosmTextField;
    2423import org.openstreetmap.josm.tools.GBC;
     
    148147            } catch (NumberFormatException ex) {
    149148                try {
    150                     ll = LatLonDialog.parseLatLon(lat.getText() + "; " + lon.getText());
     149                    ll = LatLon.parse(lat.getText() + "; " + lon.getText());
    151150                } catch (IllegalArgumentException ex2) {
    152151                    JOptionPane.showMessageDialog(Main.parent,
  • trunk/src/org/openstreetmap/josm/data/coor/LatLon.java

    r10915 r11045  
    1515import java.text.DecimalFormat;
    1616import java.text.NumberFormat;
     17import java.util.ArrayList;
    1718import java.util.Arrays;
     19import java.util.List;
    1820import java.util.Locale;
    1921import java.util.Objects;
     22import java.util.regex.Matcher;
     23import java.util.regex.Pattern;
    2024
    2125import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
     
    8286    private static final String cDm00 = cDmMinuteFormatter.format(0.0);
    8387
     88    /** Character denoting South, as string */
     89    public static final String SOUTH = trc("compass", "S");
     90    /** Character denoting North, as string */
     91    public static final String NORTH = trc("compass", "N");
     92    /** Character denoting West, as string */
     93    public static final String WEST = trc("compass", "W");
     94    /** Character denoting East, as string */
     95    public static final String EAST = trc("compass", "E");
     96
     97    private static final char N_TR = NORTH.charAt(0);
     98    private static final char S_TR = SOUTH.charAt(0);
     99    private static final char E_TR = EAST.charAt(0);
     100    private static final char W_TR = WEST.charAt(0);
     101
     102    private static final String DEG = "\u00B0";
     103    private static final String MIN = "\u2032";
     104    private static final String SEC = "\u2033";
     105
     106    private static final Pattern P = Pattern.compile(
     107            "([+|-]?\\d+[.,]\\d+)|"             // (1)
     108            + "([+|-]?\\d+)|"                   // (2)
     109            + "("+DEG+"|o|deg)|"                // (3)
     110            + "('|"+MIN+"|min)|"                // (4)
     111            + "(\"|"+SEC+"|sec)|"               // (5)
     112            + "(,|;)|"                          // (6)
     113            + "([NSEW"+N_TR+S_TR+E_TR+W_TR+"])|"// (7)
     114            + "\\s+|"
     115            + "(.+)", Pattern.CASE_INSENSITIVE);
     116
     117    private static final Pattern P_XML = Pattern.compile(
     118            "lat=[\"']([+|-]?\\d+[.,]\\d+)[\"']\\s+lon=[\"']([+|-]?\\d+[.,]\\d+)[\"']");
     119
    84120    /**
    85121     * Replies true if lat is in the range [-90,90]
     
    235271    }
    236272
    237     public static final String SOUTH = trc("compass", "S");
    238     public static final String NORTH = trc("compass", "N");
    239 
    240273    /**
    241274     * Formats the latitude part according to the given format
     
    260293        return x;
    261294    }
    262 
    263     public static final String WEST = trc("compass", "W");
    264     public static final String EAST = trc("compass", "E");
    265295
    266296    /**
     
    512542        return new org.openstreetmap.gui.jmapviewer.Coordinate(lat(), lon());
    513543    }
     544
     545    private static class LatLonHolder {
     546        private double lat = Double.NaN;
     547        private double lon = Double.NaN;
     548    }
     549
     550    private static void setLatLonObj(final LatLonHolder latLon,
     551            final Object coord1deg, final Object coord1min, final Object coord1sec, final Object card1,
     552            final Object coord2deg, final Object coord2min, final Object coord2sec, final Object card2) {
     553
     554        setLatLon(latLon,
     555                (Double) coord1deg, (Double) coord1min, (Double) coord1sec, (String) card1,
     556                (Double) coord2deg, (Double) coord2min, (Double) coord2sec, (String) card2);
     557    }
     558
     559    private static void setLatLon(final LatLonHolder latLon,
     560            final double coord1deg, final double coord1min, final double coord1sec, final String card1,
     561            final double coord2deg, final double coord2min, final double coord2sec, final String card2) {
     562
     563        setLatLon(latLon, coord1deg, coord1min, coord1sec, card1);
     564        setLatLon(latLon, coord2deg, coord2min, coord2sec, card2);
     565        if (Double.isNaN(latLon.lat) || Double.isNaN(latLon.lon)) {
     566            throw new IllegalArgumentException("invalid lat/lon parameters");
     567        }
     568    }
     569
     570    private static void setLatLon(final LatLonHolder latLon, final double coordDeg, final double coordMin, final double coordSec,
     571            final String card) {
     572        if (coordDeg < -180 || coordDeg > 180 || coordMin < 0 || coordMin >= 60 || coordSec < 0 || coordSec > 60) {
     573            throw new IllegalArgumentException("out of range");
     574        }
     575
     576        double coord = (coordDeg < 0 ? -1 : 1) * (Math.abs(coordDeg) + coordMin / 60 + coordSec / 3600);
     577        coord = "N".equals(card) || "E".equals(card) ? coord : -coord;
     578        if ("N".equals(card) || "S".equals(card)) {
     579            latLon.lat = coord;
     580        } else {
     581            latLon.lon = coord;
     582        }
     583    }
     584
     585    /**
     586     * Parses the given string as lat/lon.
     587     * @param coord String to parse
     588     * @return parsed lat/lon
     589     * @since 11045
     590     */
     591    public static LatLon parse(String coord) {
     592        final LatLonHolder latLon = new LatLonHolder();
     593        final Matcher mXml = P_XML.matcher(coord);
     594        if (mXml.matches()) {
     595            setLatLonObj(latLon,
     596                    Double.valueOf(mXml.group(1).replace(',', '.')), 0.0, 0.0, "N",
     597                    Double.valueOf(mXml.group(2).replace(',', '.')), 0.0, 0.0, "E");
     598        } else {
     599            final Matcher m = P.matcher(coord);
     600
     601            final StringBuilder sb = new StringBuilder();
     602            final List<Object> list = new ArrayList<>();
     603
     604            while (m.find()) {
     605                if (m.group(1) != null) {
     606                    sb.append('R');     // floating point number
     607                    list.add(Double.valueOf(m.group(1).replace(',', '.')));
     608                } else if (m.group(2) != null) {
     609                    sb.append('Z');     // integer number
     610                    list.add(Double.valueOf(m.group(2)));
     611                } else if (m.group(3) != null) {
     612                    sb.append('o');     // degree sign
     613                } else if (m.group(4) != null) {
     614                    sb.append('\'');    // seconds sign
     615                } else if (m.group(5) != null) {
     616                    sb.append('"');     // minutes sign
     617                } else if (m.group(6) != null) {
     618                    sb.append(',');     // separator
     619                } else if (m.group(7) != null) {
     620                    sb.append('x');     // cardinal direction
     621                    String c = m.group(7).toUpperCase(Locale.ENGLISH);
     622                    if ("N".equalsIgnoreCase(c) || "S".equalsIgnoreCase(c) || "E".equalsIgnoreCase(c) || "W".equalsIgnoreCase(c)) {
     623                        list.add(c);
     624                    } else {
     625                        list.add(c.replace(N_TR, 'N').replace(S_TR, 'S')
     626                                  .replace(E_TR, 'E').replace(W_TR, 'W'));
     627                    }
     628                } else if (m.group(8) != null) {
     629                    throw new IllegalArgumentException("invalid token: " + m.group(8));
     630                }
     631            }
     632
     633            final String pattern = sb.toString();
     634
     635            final Object[] params = list.toArray();
     636
     637            if (pattern.matches("Ro?,?Ro?")) {
     638                setLatLonObj(latLon,
     639                        params[0], 0.0, 0.0, "N",
     640                        params[1], 0.0, 0.0, "E");
     641            } else if (pattern.matches("xRo?,?xRo?")) {
     642                setLatLonObj(latLon,
     643                        params[1], 0.0, 0.0, params[0],
     644                        params[3], 0.0, 0.0, params[2]);
     645            } else if (pattern.matches("Ro?x,?Ro?x")) {
     646                setLatLonObj(latLon,
     647                        params[0], 0.0, 0.0, params[1],
     648                        params[2], 0.0, 0.0, params[3]);
     649            } else if (pattern.matches("Zo[RZ]'?,?Zo[RZ]'?|Z[RZ],?Z[RZ]")) {
     650                setLatLonObj(latLon,
     651                        params[0], params[1], 0.0, "N",
     652                        params[2], params[3], 0.0, "E");
     653            } else if (pattern.matches("xZo[RZ]'?,?xZo[RZ]'?|xZo?[RZ],?xZo?[RZ]")) {
     654                setLatLonObj(latLon,
     655                        params[1], params[2], 0.0, params[0],
     656                        params[4], params[5], 0.0, params[3]);
     657            } else if (pattern.matches("Zo[RZ]'?x,?Zo[RZ]'?x|Zo?[RZ]x,?Zo?[RZ]x")) {
     658                setLatLonObj(latLon,
     659                        params[0], params[1], 0.0, params[2],
     660                        params[3], params[4], 0.0, params[5]);
     661            } else if (pattern.matches("ZoZ'[RZ]\"?x,?ZoZ'[RZ]\"?x|ZZ[RZ]x,?ZZ[RZ]x")) {
     662                setLatLonObj(latLon,
     663                        params[0], params[1], params[2], params[3],
     664                        params[4], params[5], params[6], params[7]);
     665            } else if (pattern.matches("xZoZ'[RZ]\"?,?xZoZ'[RZ]\"?|xZZ[RZ],?xZZ[RZ]")) {
     666                setLatLonObj(latLon,
     667                        params[1], params[2], params[3], params[0],
     668                        params[5], params[6], params[7], params[4]);
     669            } else if (pattern.matches("ZZ[RZ],?ZZ[RZ]")) {
     670                setLatLonObj(latLon,
     671                        params[0], params[1], params[2], "N",
     672                        params[3], params[4], params[5], "E");
     673            } else {
     674                throw new IllegalArgumentException("invalid format: " + pattern);
     675            }
     676        }
     677
     678        return new LatLon(latLon.lat, latLon.lon);
     679    }
    514680}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/LatLonDialog.java

    r10627 r11045  
    99import java.awt.event.FocusEvent;
    1010import java.awt.event.FocusListener;
    11 import java.util.ArrayList;
    1211import java.util.Arrays;
    13 import java.util.List;
    14 import java.util.Locale;
    15 import java.util.regex.Matcher;
    16 import java.util.regex.Pattern;
    1712
    1813import javax.swing.BorderFactory;
     
    4338    private LatLon latLonCoordinates;
    4439    private EastNorth eastNorthCoordinates;
    45 
    46     private static final Double ZERO = 0.0;
    47     private static final String DEG = "\u00B0";
    48     private static final String MIN = "\u2032";
    49     private static final String SEC = "\u2033";
    50 
    51     private static final char N_TR = LatLon.NORTH.charAt(0);
    52     private static final char S_TR = LatLon.SOUTH.charAt(0);
    53     private static final char E_TR = LatLon.EAST.charAt(0);
    54     private static final char W_TR = LatLon.WEST.charAt(0);
    55 
    56     private static final Pattern P = Pattern.compile(
    57             "([+|-]?\\d+[.,]\\d+)|"             // (1)
    58             + "([+|-]?\\d+)|"                   // (2)
    59             + "("+DEG+"|o|deg)|"                // (3)
    60             + "('|"+MIN+"|min)|"                // (4)
    61             + "(\"|"+SEC+"|sec)|"               // (5)
    62             + "(,|;)|"                          // (6)
    63             + "([NSEW"+N_TR+S_TR+E_TR+W_TR+"])|"// (7)
    64             + "\\s+|"
    65             + "(.+)");
    66 
    67     private static final Pattern P_XML = Pattern.compile(
    68             "lat=[\"']([+|-]?\\d+[.,]\\d+)[\"']\\s+lon=[\"']([+|-]?\\d+[.,]\\d+)[\"']");
    6940
    7041    protected JPanel buildLatLon() {
     
    231202        LatLon latLon;
    232203        try {
    233             latLon = parseLatLon(tfLatLon.getText());
     204            latLon = LatLon.parse(tfLatLon.getText());
    234205            if (!LatLon.isValidLat(latLon.lat()) || !LatLon.isValidLon(latLon.lon())) {
    235206                latLon = null;
     
    339310    }
    340311
     312    /**
     313     * Parses the given string as lat/lon.
     314     * @param coord String to parse
     315     * @return parsed lat/lon
     316     * @deprecated use {@link LatLon#parse(String)} instead
     317     */
     318    @Deprecated
    341319    public static LatLon parseLatLon(final String coord) {
    342         final LatLonHolder latLon = new LatLonHolder();
    343         final Matcher mXml = P_XML.matcher(coord);
    344         if (mXml.matches()) {
    345             setLatLonObj(latLon,
    346                     Double.valueOf(mXml.group(1).replace(',', '.')), ZERO, ZERO, "N",
    347                     Double.valueOf(mXml.group(2).replace(',', '.')), ZERO, ZERO, "E");
    348         } else {
    349             final Matcher m = P.matcher(coord);
    350 
    351             final StringBuilder sb = new StringBuilder();
    352             final List<Object> list = new ArrayList<>();
    353 
    354             while (m.find()) {
    355                 if (m.group(1) != null) {
    356                     sb.append('R');     // floating point number
    357                     list.add(Double.valueOf(m.group(1).replace(',', '.')));
    358                 } else if (m.group(2) != null) {
    359                     sb.append('Z');     // integer number
    360                     list.add(Double.valueOf(m.group(2)));
    361                 } else if (m.group(3) != null) {
    362                     sb.append('o');     // degree sign
    363                 } else if (m.group(4) != null) {
    364                     sb.append('\'');    // seconds sign
    365                 } else if (m.group(5) != null) {
    366                     sb.append('"');     // minutes sign
    367                 } else if (m.group(6) != null) {
    368                     sb.append(',');     // separator
    369                 } else if (m.group(7) != null) {
    370                     sb.append('x');     // cardinal direction
    371                     String c = m.group(7).toUpperCase(Locale.ENGLISH);
    372                     if ("N".equals(c) || "S".equals(c) || "E".equals(c) || "W".equals(c)) {
    373                         list.add(c);
    374                     } else {
    375                         list.add(c.replace(N_TR, 'N').replace(S_TR, 'S')
    376                                 .replace(E_TR, 'E').replace(W_TR, 'W'));
    377                     }
    378                 } else if (m.group(8) != null) {
    379                     throw new IllegalArgumentException("invalid token: " + m.group(8));
    380                 }
    381             }
    382 
    383             final String pattern = sb.toString();
    384 
    385             final Object[] params = list.toArray();
    386 
    387             if (pattern.matches("Ro?,?Ro?")) {
    388                 setLatLonObj(latLon,
    389                         params[0], ZERO, ZERO, "N",
    390                         params[1], ZERO, ZERO, "E");
    391             } else if (pattern.matches("xRo?,?xRo?")) {
    392                 setLatLonObj(latLon,
    393                         params[1], ZERO, ZERO, params[0],
    394                         params[3], ZERO, ZERO, params[2]);
    395             } else if (pattern.matches("Ro?x,?Ro?x")) {
    396                 setLatLonObj(latLon,
    397                         params[0], ZERO, ZERO, params[1],
    398                         params[2], ZERO, ZERO, params[3]);
    399             } else if (pattern.matches("Zo[RZ]'?,?Zo[RZ]'?|Z[RZ],?Z[RZ]")) {
    400                 setLatLonObj(latLon,
    401                         params[0], params[1], ZERO, "N",
    402                         params[2], params[3], ZERO, "E");
    403             } else if (pattern.matches("xZo[RZ]'?,?xZo[RZ]'?|xZo?[RZ],?xZo?[RZ]")) {
    404                 setLatLonObj(latLon,
    405                         params[1], params[2], ZERO, params[0],
    406                         params[4], params[5], ZERO, params[3]);
    407             } else if (pattern.matches("Zo[RZ]'?x,?Zo[RZ]'?x|Zo?[RZ]x,?Zo?[RZ]x")) {
    408                 setLatLonObj(latLon,
    409                         params[0], params[1], ZERO, params[2],
    410                         params[3], params[4], ZERO, params[5]);
    411             } else if (pattern.matches("ZoZ'[RZ]\"?x,?ZoZ'[RZ]\"?x|ZZ[RZ]x,?ZZ[RZ]x")) {
    412                 setLatLonObj(latLon,
    413                         params[0], params[1], params[2], params[3],
    414                         params[4], params[5], params[6], params[7]);
    415             } else if (pattern.matches("xZoZ'[RZ]\"?,?xZoZ'[RZ]\"?|xZZ[RZ],?xZZ[RZ]")) {
    416                 setLatLonObj(latLon,
    417                         params[1], params[2], params[3], params[0],
    418                         params[5], params[6], params[7], params[4]);
    419             } else if (pattern.matches("ZZ[RZ],?ZZ[RZ]")) {
    420                 setLatLonObj(latLon,
    421                         params[0], params[1], params[2], "N",
    422                         params[3], params[4], params[5], "E");
    423             } else {
    424                 throw new IllegalArgumentException("invalid format: " + pattern);
    425             }
    426         }
    427 
    428         return new LatLon(latLon.lat, latLon.lon);
     320        return LatLon.parse(coord);
    429321    }
    430322
     
    441333    }
    442334
    443     private static class LatLonHolder {
    444         private double lat;
    445         private double lon;
    446     }
    447 
    448     private static void setLatLonObj(final LatLonHolder latLon,
    449             final Object coord1deg, final Object coord1min, final Object coord1sec, final Object card1,
    450             final Object coord2deg, final Object coord2min, final Object coord2sec, final Object card2) {
    451 
    452         setLatLon(latLon,
    453                 (Double) coord1deg, (Double) coord1min, (Double) coord1sec, (String) card1,
    454                 (Double) coord2deg, (Double) coord2min, (Double) coord2sec, (String) card2);
    455     }
    456 
    457     private static void setLatLon(final LatLonHolder latLon,
    458             final double coord1deg, final double coord1min, final double coord1sec, final String card1,
    459             final double coord2deg, final double coord2min, final double coord2sec, final String card2) {
    460 
    461         setLatLon(latLon, coord1deg, coord1min, coord1sec, card1);
    462         setLatLon(latLon, coord2deg, coord2min, coord2sec, card2);
    463     }
    464 
    465     private static void setLatLon(final LatLonHolder latLon, final double coordDeg, final double coordMin, final double coordSec,
    466             final String card) {
    467         if (coordDeg < -180 || coordDeg > 180 || coordMin < 0 || coordMin >= 60 || coordSec < 0 || coordSec > 60) {
    468             throw new IllegalArgumentException("out of range");
    469         }
    470 
    471         double coord = (coordDeg < 0 ? -1 : 1) * (Math.abs(coordDeg) + coordMin / 60 + coordSec / 3600);
    472         coord = "N".equals(card) || "E".equals(card) ? coord : -coord;
    473         if ("N".equals(card) || "S".equals(card)) {
    474             latLon.lat = coord;
    475         } else {
    476             latLon.lon = coord;
    477         }
    478     }
    479 
    480335    public String getLatLonText() {
    481336        return tfLatLon.getText();
  • trunk/test/unit/org/openstreetmap/josm/data/coor/LatLonTest.java

    r10945 r11045  
    204204        assertEquals(10, ll3.getCenter(ll2).lon(), 1e-10);
    205205    }
     206
     207    /**
     208     * Unit test of {@link LatLon#parse} method.
     209     */
     210    @Test
     211    public void testParse() {
     212        assertEquals(new LatLon(49.29918, 19.24788), LatLon.parse("49.29918° 19.24788°"));
     213        assertEquals(new LatLon(49.29918, 19.24788), LatLon.parse("N 49.29918 E 19.24788°"));
     214        assertEquals(new LatLon(49.29918, 19.24788), LatLon.parse("49.29918° 19.24788°"));
     215        assertEquals(new LatLon(49.29918, 19.24788), LatLon.parse("N 49.29918 E 19.24788"));
     216        assertEquals(new LatLon(49.29918, 19.24788), LatLon.parse("n 49.29918 e 19.24788"));
     217        assertEquals(new LatLon(-19 - 24.788 / 60, -49 - 29.918 / 60), LatLon.parse("W 49°29.918' S 19°24.788'"));
     218        assertEquals(new LatLon(-19 - 24.788 / 60, -49 - 29.918 / 60), LatLon.parse("w 49°29.918' s 19°24.788'"));
     219        assertEquals(new LatLon(49 + 29. / 60 + 04. / 3600, 19 + 24. / 60 + 43. / 3600), LatLon.parse("N 49°29'04\" E 19°24'43\""));
     220        assertEquals(new LatLon(49.29918, 19.24788), LatLon.parse("49.29918 N, 19.24788 E"));
     221        assertEquals(new LatLon(49.29918, 19.24788), LatLon.parse("49.29918 n, 19.24788 e"));
     222        assertEquals(new LatLon(49 + 29. / 60 + 21. / 3600, 19 + 24. / 60 + 38. / 3600), LatLon.parse("49°29'21\" N 19°24'38\" E"));
     223        assertEquals(new LatLon(49 + 29. / 60 + 51. / 3600, 19 + 24. / 60 + 18. / 3600), LatLon.parse("49 29 51, 19 24 18"));
     224        assertEquals(new LatLon(49 + 29. / 60, 19 + 24. / 60), LatLon.parse("49 29, 19 24"));
     225        assertEquals(new LatLon(19 + 24. / 60, 49 + 29. / 60), LatLon.parse("E 49 29, N 19 24"));
     226        assertEquals(new LatLon(49 + 29. / 60, 19 + 24. / 60), LatLon.parse("49° 29; 19° 24"));
     227        assertEquals(new LatLon(49 + 29. / 60, 19 + 24. / 60), LatLon.parse("49° 29; 19° 24"));
     228        assertEquals(new LatLon(49 + 29. / 60, -19 - 24. / 60), LatLon.parse("N 49° 29, W 19° 24"));
     229        assertEquals(new LatLon(-49 - 29.5 / 60, 19 + 24.6 / 60), LatLon.parse("49° 29.5 S, 19° 24.6 E"));
     230        assertEquals(new LatLon(49 + 29.918 / 60, 19 + 15.88 / 60), LatLon.parse("N 49 29.918 E 19 15.88"));
     231        assertEquals(new LatLon(49 + 29.4 / 60, 19 + 24.5 / 60), LatLon.parse("49 29.4 19 24.5"));
     232        assertEquals(new LatLon(-49 - 29.4 / 60, 19 + 24.5 / 60), LatLon.parse("-49 29.4 N -19 24.5 W"));
     233    }
     234
     235    /**
     236     * Unit test of {@link LatLon#parse} method - invalid case 1.
     237     */
     238    @Test(expected = IllegalArgumentException.class)
     239    public void testParseInvalid1() {
     240        LatLon.parse("48°45'S 23°30'S");
     241    }
     242
     243    /**
     244     * Unit test of {@link LatLon#parse} method - invalid case 2.
     245     */
     246    @Test(expected = IllegalArgumentException.class)
     247    public void testParseInvalid2() {
     248        LatLon.parse("47°45'N 24°00'S");
     249    }
    206250}
Note: See TracChangeset for help on using the changeset viewer.