]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/data/Coordinate.java
Version 17.2, February 2015
[GpsPrune.git] / tim / prune / data / Coordinate.java
index 19a1688ed45cc0d0f99385c0b02ba22d50041259..e30fa4ac357d89a68041fa07eee2f76bf5e8ee22 100644 (file)
@@ -32,9 +32,15 @@ public abstract class Coordinate
        static {
                if (EIGHT_DP instanceof DecimalFormat) ((DecimalFormat) EIGHT_DP).applyPattern("0.00000000");
        }
+       /** Number formatter for fixed decimals with forced decimal point */
+       private static final NumberFormat FIVE_DP = NumberFormat.getNumberInstance(Locale.UK);
+       static {
+               if (FIVE_DP instanceof DecimalFormat) ((DecimalFormat) FIVE_DP).applyPattern("0.00000");
+       }
 
        // Instance variables
        private boolean _valid = false;
+       private boolean _cardinalGuessed = false;
        protected int _cardinal = NORTH;
        private int _degrees = 0;
        private int _minutes = 0;
@@ -59,7 +65,7 @@ public abstract class Coordinate
                        inString = inString.trim();
                        strLen = inString.length();
                }
-               if (strLen > 1)
+               if (strLen > 0)
                {
                        // Check for cardinal character either at beginning or end
                        boolean hasCardinal = true;
@@ -68,15 +74,21 @@ public abstract class Coordinate
                                hasCardinal = false;
                                // use default from concrete subclass
                                _cardinal = getDefaultCardinal();
+                               _cardinalGuessed = true;
+                       }
+                       else if (isJustNumber(inString)) {
+                               // it's just a number
+                               hasCardinal = false;
+                               _cardinalGuessed = true;
                        }
 
                        // count numeric fields - 1=d, 2=dm, 3=dm.m/dms, 4=dms.s
                        int numFields = 0;
-                       boolean inNumeric = false;
+                       boolean isNumeric = false;
                        char currChar;
                        long[] fields = new long[4]; // needs to be long for lengthy decimals
                        long[] denoms = new long[4];
-                       String secondDelim = "";
+                       boolean[] otherDelims = new boolean[5]; // remember whether delimiters have non-decimal chars
                        try
                        {
                                // Loop over characters in input string, populating fields array
@@ -85,22 +97,23 @@ public abstract class Coordinate
                                        currChar = inString.charAt(i);
                                        if (currChar >= '0' && currChar <= '9')
                                        {
-                                               if (!inNumeric)
+                                               if (!isNumeric)
                                                {
-                                                       inNumeric = true;
+                                                       isNumeric = true;
                                                        numFields++;
                                                        denoms[numFields-1] = 1;
                                                }
-                                               fields[numFields-1] = fields[numFields-1] * 10 + (currChar - '0');
-                                               denoms[numFields-1] *= 10;
+                                               if (denoms[numFields-1] < 1E18) // ignore trailing characters if too big for long
+                                               {
+                                                       fields[numFields-1] = fields[numFields-1] * 10 + (currChar - '0');
+                                                       denoms[numFields-1] *= 10;
+                                               }
                                        }
                                        else
                                        {
-                                               inNumeric = false;
-                                               // Remember second delimiter
-                                               if (numFields == 2) {
-                                                       secondDelim += currChar;
-                                               }
+                                               isNumeric = false;
+                                               // Remember delimiters
+                                               if (currChar != ',' && currChar != '.') {otherDelims[numFields] = true;}
                                        }
                                }
                                _valid = (numFields > 0);
@@ -112,20 +125,46 @@ public abstract class Coordinate
                        }
                        // parse fields according to number found
                        _degrees = (int) fields[0];
-                       _originalFormat = hasCardinal?FORMAT_DEG:FORMAT_DEG_WITHOUT_CARDINAL;
+                       _asDouble = _degrees;
+                       _originalFormat = hasCardinal ? FORMAT_DEG : FORMAT_DEG_WITHOUT_CARDINAL;
                        _fracDenom = 10;
                        if (numFields == 2)
                        {
-                               // String is just decimal degrees
-                               double numMins = fields[1] * 60.0 / denoms[1];
+                               if (!otherDelims[1])
+                               {
+                                       // String is just decimal degrees
+                                       double numMins = fields[1] * 60.0 / denoms[1];
+                                       _minutes = (int) numMins;
+                                       double numSecs = (numMins - _minutes) * 60.0;
+                                       _seconds = (int) numSecs;
+                                       _fracs = (int) ((numSecs - _seconds) * 10);
+                                       _asDouble = _degrees + 1.0 * fields[1] / denoms[1];
+                               }
+                               else
+                               {
+                                       // String is degrees and minutes (due to non-decimal separator)
+                                       _originalFormat = FORMAT_DEG_MIN;
+                                       _minutes = (int) fields[1];
+                                       _seconds = 0;
+                                       _fracs = 0;
+                                       _asDouble = 1.0 * _degrees + (_minutes / 60.0);
+                               }
+                       }
+                       // Check for exponential degrees like 1.3E-6
+                       else if (numFields == 3 && !otherDelims[1] && otherDelims[2] && isJustNumber(inString))
+                       {
+                               _originalFormat = FORMAT_DEG;
+                               _asDouble = Math.abs(Double.parseDouble(inString)); // must succeed if isJustNumber has given true
+                               // now we can ignore the fields and just use this double
+                               _degrees = (int) _asDouble;
+                               double numMins = (_asDouble - _degrees) * 60.0;
                                _minutes = (int) numMins;
                                double numSecs = (numMins - _minutes) * 60.0;
                                _seconds = (int) numSecs;
                                _fracs = (int) ((numSecs - _seconds) * 10);
-                               _asDouble = _degrees + 1.0 * fields[1] / denoms[1];
                        }
                        // Differentiate between d-m.f and d-m-s using . or ,
-                       else if (numFields == 3 && (secondDelim.equals(".") || secondDelim.equals(",")))
+                       else if (numFields == 3 && !otherDelims[2])
                        {
                                // String is degrees-minutes.fractions
                                _originalFormat = FORMAT_DEG_MIN;
@@ -149,7 +188,8 @@ public abstract class Coordinate
                        if (_cardinal == WEST || _cardinal == SOUTH || inString.charAt(0) == '-')
                                _asDouble = -_asDouble;
                        // validate fields
-                       _valid = _valid && (_degrees <= getMaxDegrees() && _minutes < 60 && _seconds < 60 && _fracs < _fracDenom);
+                       _valid = _valid && (_degrees <= getMaxDegrees() && _minutes < 60 && _seconds < 60 && _fracs < _fracDenom)
+                               && Math.abs(_asDouble) <= getMaxDegrees();
                }
                else _valid = false;
        }
@@ -157,10 +197,10 @@ public abstract class Coordinate
 
        /**
         * Get the cardinal from the given character
-        * @param inFirstChar first character from file
-        * @param inLastChar last character from file
+        * @param inFirstChar first character from string
+        * @param inLastChar last character from string
         */
-       protected int getCardinal(char inFirstChar, char inLastChar)
+       private int getCardinal(char inFirstChar, char inLastChar)
        {
                // Try leading character first
                int cardinal = getCardinal(inFirstChar);
@@ -171,6 +211,12 @@ public abstract class Coordinate
                return cardinal;
        }
 
+       /**
+        * @return true if cardinal was guessed, false if parsed
+        */
+       public boolean getCardinalGuessed() {
+               return _cardinalGuessed;
+       }
 
        /**
         * Get the cardinal from the given character
@@ -239,11 +285,7 @@ public abstract class Coordinate
         */
        public boolean equals(Coordinate inOther)
        {
-               return (inOther != null && _cardinal == inOther._cardinal
-                       && _degrees == inOther._degrees
-                       && _minutes == inOther._minutes
-                       && _seconds == inOther._seconds
-                       && _fracs == inOther._fracs);
+               return (_asDouble == inOther._asDouble);
        }
 
 
@@ -265,7 +307,7 @@ public abstract class Coordinate
                                {
                                        StringBuffer buffer = new StringBuffer();
                                        buffer.append(PRINTABLE_CARDINALS[_cardinal])
-                                               .append(threeDigitString(_degrees)).append('°')
+                                               .append(threeDigitString(_degrees)).append('\u00B0')
                                                .append(twoDigitString(_minutes)).append('\'')
                                                .append(twoDigitString(_seconds)).append('.')
                                                .append(formatFraction(_fracs, _fracDenom));
@@ -274,27 +316,36 @@ public abstract class Coordinate
                                }
                                case FORMAT_DEG_MIN:
                                {
-                                       answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "°"
-                                               + (_minutes + _seconds / 60.0 + _fracs / 60.0 / _fracDenom) + "'";
+                                       answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "\u00B0"
+                                               + FIVE_DP.format((Math.abs(_asDouble) - _degrees) * 60.0) + "'";
                                        break;
                                }
                                case FORMAT_DEG_WHOLE_MIN:
                                {
-                                       answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "°"
-                                               + (int) Math.floor(_minutes + _seconds / 60.0 + _fracs / 60.0 / _fracDenom + 0.5) + "'";
+                                       int deg = _degrees;
+                                       int min = (int) Math.floor(_minutes + _seconds / 60.0 + _fracs / 60.0 / _fracDenom + 0.5);
+                                       if (min == 60) {
+                                               min = 0; deg++;
+                                       }
+                                       answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(deg) + "\u00B0" + min + "'";
                                        break;
                                }
                                case FORMAT_DEG:
                                case FORMAT_DEG_WITHOUT_CARDINAL:
                                {
-                                       answer = (_asDouble<0.0?"-":"")
-                                               + (_degrees + _minutes / 60.0 + _seconds / 3600.0 + _fracs / 3600.0 / _fracDenom);
+                                       if (_originalFormat != FORMAT_DEG_WITHOUT_CARDINAL)
+                                       {
+                                               answer = (_asDouble<0.0?"-":"")
+                                                       + (_degrees + _minutes / 60.0 + _seconds / 3600.0 + _fracs / 3600.0 / _fracDenom);
+                                       }
                                        break;
                                }
                                case FORMAT_DECIMAL_FORCE_POINT:
                                {
                                        // Forcing a decimal point instead of system-dependent commas etc
-                                       answer = EIGHT_DP.format(_asDouble);
+                                       if (_originalFormat != FORMAT_DEG_WITHOUT_CARDINAL || answer.indexOf('.') < 0) {
+                                               answer = EIGHT_DP.format(_asDouble);
+                                       }
                                        break;
                                }
                                case FORMAT_DEG_MIN_SEC_WITH_SPACES:
@@ -386,7 +437,7 @@ public abstract class Coordinate
                double startValue = inStart.getDouble();
                double endValue = inEnd.getDouble();
                double newValue = startValue + (endValue - startValue) * inFraction;
-               Coordinate answer = inStart.makeNew(newValue, inStart._originalFormat);
+               Coordinate answer = inStart.makeNew(newValue, Coordinate.FORMAT_DECIMAL_FORCE_POINT);
                return answer;
        }
 
@@ -399,6 +450,21 @@ public abstract class Coordinate
         */
        protected abstract Coordinate makeNew(double inValue, int inFormat);
 
+       /**
+        * Try to parse the given string
+        * @param inString string to check
+        * @return true if it can be parsed as a number
+        */
+       private static boolean isJustNumber(String inString)
+       {
+               boolean justNum = false;
+               try {
+                       double x = Double.parseDouble(inString);
+                       justNum = (x >= -180.0 && x <= 360.0);
+               }
+               catch (NumberFormatException nfe) {} // flag remains false
+               return justNum;
+       }
 
        /**
         * Create a String representation for debug