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

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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}
Note: See TracChangeset for help on using the changeset viewer.