]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/data/Coordinate.java
Moved source into separate src directory due to popular request
[GpsPrune.git] / tim / prune / data / Coordinate.java
diff --git a/tim/prune/data/Coordinate.java b/tim/prune/data/Coordinate.java
deleted file mode 100644 (file)
index e30fa4a..0000000
+++ /dev/null
@@ -1,478 +0,0 @@
-package tim.prune.data;
-
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.Locale;
-
-/**
- * Class to represent a lat/long coordinate
- * and provide conversion functions
- */
-public abstract class Coordinate
-{
-       public static final int NO_CARDINAL = -1;
-       public static final int NORTH = 0;
-       public static final int EAST = 1;
-       public static final int SOUTH = 2;
-       public static final int WEST = 3;
-       private static final char[] PRINTABLE_CARDINALS = {'N', 'E', 'S', 'W'};
-       public static final int FORMAT_DEG_MIN_SEC = 10;
-       public static final int FORMAT_DEG_MIN = 11;
-       public static final int FORMAT_DEG = 12;
-       public static final int FORMAT_DEG_WITHOUT_CARDINAL = 13;
-       public static final int FORMAT_DEG_WHOLE_MIN = 14;
-       public static final int FORMAT_DEG_MIN_SEC_WITH_SPACES = 15;
-       public static final int FORMAT_CARDINAL = 16;
-       public static final int FORMAT_DECIMAL_FORCE_POINT = 17;
-       public static final int FORMAT_NONE = 19;
-
-       /** Number formatter for fixed decimals with forced decimal point */
-       private static final NumberFormat EIGHT_DP = NumberFormat.getNumberInstance(Locale.UK);
-       // Select the UK locale for this formatter so that decimal point is always used (not comma)
-       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;
-       private int _seconds = 0;
-       private int _fracs = 0;
-       private int _fracDenom = 0;
-       private String _originalString = null;
-       private int _originalFormat = FORMAT_NONE;
-       private double _asDouble = 0.0;
-
-
-       /**
-        * Constructor given String
-        * @param inString string to parse
-        */
-       public Coordinate(String inString)
-       {
-               _originalString = inString;
-               int strLen = 0;
-               if (inString != null)
-               {
-                       inString = inString.trim();
-                       strLen = inString.length();
-               }
-               if (strLen > 0)
-               {
-                       // Check for cardinal character either at beginning or end
-                       boolean hasCardinal = true;
-                       _cardinal = getCardinal(inString.charAt(0), inString.charAt(strLen-1));
-                       if (_cardinal == NO_CARDINAL) {
-                               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 isNumeric = false;
-                       char currChar;
-                       long[] fields = new long[4]; // needs to be long for lengthy decimals
-                       long[] denoms = new long[4];
-                       boolean[] otherDelims = new boolean[5]; // remember whether delimiters have non-decimal chars
-                       try
-                       {
-                               // Loop over characters in input string, populating fields array
-                               for (int i=0; i<strLen; i++)
-                               {
-                                       currChar = inString.charAt(i);
-                                       if (currChar >= '0' && currChar <= '9')
-                                       {
-                                               if (!isNumeric)
-                                               {
-                                                       isNumeric = true;
-                                                       numFields++;
-                                                       denoms[numFields-1] = 1;
-                                               }
-                                               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
-                                       {
-                                               isNumeric = false;
-                                               // Remember delimiters
-                                               if (currChar != ',' && currChar != '.') {otherDelims[numFields] = true;}
-                                       }
-                               }
-                               _valid = (numFields > 0);
-                       }
-                       catch (ArrayIndexOutOfBoundsException obe)
-                       {
-                               // more than four fields found - unable to parse
-                               _valid = false;
-                       }
-                       // parse fields according to number found
-                       _degrees = (int) fields[0];
-                       _asDouble = _degrees;
-                       _originalFormat = hasCardinal ? FORMAT_DEG : FORMAT_DEG_WITHOUT_CARDINAL;
-                       _fracDenom = 10;
-                       if (numFields == 2)
-                       {
-                               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);
-                       }
-                       // Differentiate between d-m.f and d-m-s using . or ,
-                       else if (numFields == 3 && !otherDelims[2])
-                       {
-                               // String is degrees-minutes.fractions
-                               _originalFormat = FORMAT_DEG_MIN;
-                               _minutes = (int) fields[1];
-                               double numSecs = fields[2] * 60.0 / denoms[2];
-                               _seconds = (int) numSecs;
-                               _fracs = (int) ((numSecs - _seconds) * 10);
-                               _asDouble = 1.0 * _degrees + (_minutes / 60.0) + (numSecs / 3600.0);
-                       }
-                       else if (numFields == 4 || numFields == 3)
-                       {
-                               // String is degrees-minutes-seconds.fractions
-                               _originalFormat = FORMAT_DEG_MIN_SEC;
-                               _minutes = (int) fields[1];
-                               _seconds = (int) fields[2];
-                               _fracs = (int) fields[3];
-                               _fracDenom = (int) denoms[3];
-                               if (_fracDenom < 1) {_fracDenom = 1;}
-                               _asDouble = 1.0 * _degrees + (_minutes / 60.0) + (_seconds / 3600.0) + (_fracs / 3600.0 / _fracDenom);
-                       }
-                       if (_cardinal == WEST || _cardinal == SOUTH || inString.charAt(0) == '-')
-                               _asDouble = -_asDouble;
-                       // validate fields
-                       _valid = _valid && (_degrees <= getMaxDegrees() && _minutes < 60 && _seconds < 60 && _fracs < _fracDenom)
-                               && Math.abs(_asDouble) <= getMaxDegrees();
-               }
-               else _valid = false;
-       }
-
-
-       /**
-        * Get the cardinal from the given character
-        * @param inFirstChar first character from string
-        * @param inLastChar last character from string
-        */
-       private int getCardinal(char inFirstChar, char inLastChar)
-       {
-               // Try leading character first
-               int cardinal = getCardinal(inFirstChar);
-               // if not there, try trailing character
-               if (cardinal == NO_CARDINAL) {
-                       cardinal = getCardinal(inLastChar);
-               }
-               return cardinal;
-       }
-
-       /**
-        * @return true if cardinal was guessed, false if parsed
-        */
-       public boolean getCardinalGuessed() {
-               return _cardinalGuessed;
-       }
-
-       /**
-        * Get the cardinal from the given character
-        * @param inChar character from file
-        */
-       protected abstract int getCardinal(char inChar);
-
-       /**
-        * @return the default cardinal for the subclass
-        */
-       protected abstract int getDefaultCardinal();
-
-       /**
-        * @return the maximum degree range for this coordinate
-        */
-       protected abstract int getMaxDegrees();
-
-
-       /**
-        * Constructor
-        * @param inValue value of coordinate
-        * @param inFormat format to use
-        * @param inCardinal cardinal
-        */
-       protected Coordinate(double inValue, int inFormat, int inCardinal)
-       {
-               _asDouble = inValue;
-               // Calculate degrees, minutes, seconds
-               _degrees = (int) Math.abs(inValue);
-               double numMins = (Math.abs(_asDouble)-_degrees) * 60.0;
-               _minutes = (int) numMins;
-               double numSecs = (numMins - _minutes) * 60.0;
-               _seconds = (int) numSecs;
-               _fracs = (int) ((numSecs - _seconds) * 10);
-               _fracDenom = 10; // fixed for now
-               // Make a string to display on screen
-               _cardinal = inCardinal;
-               _originalFormat = FORMAT_NONE;
-               if (inFormat == FORMAT_NONE) inFormat = FORMAT_DEG_WITHOUT_CARDINAL;
-               _originalString = output(inFormat);
-               _originalFormat = inFormat;
-               _valid = true;
-       }
-
-
-       /**
-        * @return coordinate as a double
-        */
-       public double getDouble()
-       {
-               return _asDouble;
-       }
-
-       /**
-        * @return true if Coordinate is valid
-        */
-       public boolean isValid()
-       {
-               return _valid;
-       }
-
-       /**
-        * Compares two Coordinates for equality
-        * @param inOther other Coordinate object with which to compare
-        * @return true if the two objects are equal
-        */
-       public boolean equals(Coordinate inOther)
-       {
-               return (_asDouble == inOther._asDouble);
-       }
-
-
-       /**
-        * Output the Coordinate in the given format
-        * @param inFormat format to use, eg FORMAT_DEG_MIN_SEC
-        * @return String for output
-        */
-       public String output(int inFormat)
-       {
-               String answer = _originalString;
-               if (inFormat != FORMAT_NONE && inFormat != _originalFormat)
-               {
-                       // TODO: allow specification of precision for output of d-m and d
-                       // format as specified
-                       switch (inFormat)
-                       {
-                               case FORMAT_DEG_MIN_SEC:
-                               {
-                                       StringBuffer buffer = new StringBuffer();
-                                       buffer.append(PRINTABLE_CARDINALS[_cardinal])
-                                               .append(threeDigitString(_degrees)).append('\u00B0')
-                                               .append(twoDigitString(_minutes)).append('\'')
-                                               .append(twoDigitString(_seconds)).append('.')
-                                               .append(formatFraction(_fracs, _fracDenom));
-                                       answer = buffer.toString();
-                                       break;
-                               }
-                               case FORMAT_DEG_MIN:
-                               {
-                                       answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "\u00B0"
-                                               + FIVE_DP.format((Math.abs(_asDouble) - _degrees) * 60.0) + "'";
-                                       break;
-                               }
-                               case FORMAT_DEG_WHOLE_MIN:
-                               {
-                                       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:
-                               {
-                                       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
-                                       if (_originalFormat != FORMAT_DEG_WITHOUT_CARDINAL || answer.indexOf('.') < 0) {
-                                               answer = EIGHT_DP.format(_asDouble);
-                                       }
-                                       break;
-                               }
-                               case FORMAT_DEG_MIN_SEC_WITH_SPACES:
-                               {
-                                       // Note: cardinal not needed as this format is only for exif, which has cardinal separately
-                                       answer = "" + _degrees + " " + _minutes + " " + _seconds + "." + formatFraction(_fracs, _fracDenom);
-                                       break;
-                               }
-                               case FORMAT_CARDINAL:
-                               {
-                                       answer = "" + PRINTABLE_CARDINALS[_cardinal];
-                                       break;
-                               }
-                       }
-               }
-               return answer;
-       }
-
-       /**
-        * Format the fraction part of seconds value
-        * @param inFrac fractional part eg 123
-        * @param inDenom denominator of fraction eg 10000
-        * @return String describing fraction, in this case 0123
-        */
-       private static final String formatFraction(int inFrac, int inDenom)
-       {
-               if (inDenom <= 1 || inFrac == 0) {return "" + inFrac;}
-               String denomString = "" + inDenom;
-               int reqdLen = denomString.length() - 1;
-               String result = denomString + inFrac;
-               int resultLen = result.length();
-               return result.substring(resultLen - reqdLen);
-       }
-
-
-       /**
-        * Format an integer to a two-digit String
-        * @param inNumber number to format
-        * @return two-character String
-        */
-       private static String twoDigitString(int inNumber)
-       {
-               if (inNumber <= 0) return "00";
-               if (inNumber < 10) return "0" + inNumber;
-               if (inNumber < 100) return "" + inNumber;
-               return "" + (inNumber % 100);
-       }
-
-
-       /**
-        * Format an integer to a three-digit String for degrees
-        * @param inNumber number to format
-        * @return three-character String
-        */
-       private static String threeDigitString(int inNumber)
-       {
-               if (inNumber <= 0) return "000";
-               if (inNumber < 10) return "00" + inNumber;
-               if (inNumber < 100) return "0" + inNumber;
-               return "" + (inNumber % 1000);
-       }
-
-
-       /**
-        * Create a new Coordinate between two others
-        * @param inStart start coordinate
-        * @param inEnd end coordinate
-        * @param inIndex index of point
-        * @param inNumPoints number of points to interpolate
-        * @return new Coordinate object
-        */
-       public static Coordinate interpolate(Coordinate inStart, Coordinate inEnd,
-               int inIndex, int inNumPoints)
-       {
-               return interpolate(inStart, inEnd, 1.0 * (inIndex+1) / (inNumPoints + 1));
-       }
-
-
-       /**
-        * Create a new Coordinate between two others
-        * @param inStart start coordinate
-        * @param inEnd end coordinate
-        * @param inFraction fraction from start to end
-        * @return new Coordinate object
-        */
-       public static Coordinate interpolate(Coordinate inStart, Coordinate inEnd,
-               double inFraction)
-       {
-               double startValue = inStart.getDouble();
-               double endValue = inEnd.getDouble();
-               double newValue = startValue + (endValue - startValue) * inFraction;
-               Coordinate answer = inStart.makeNew(newValue, Coordinate.FORMAT_DECIMAL_FORCE_POINT);
-               return answer;
-       }
-
-
-       /**
-        * Make a new Coordinate according to subclass
-        * @param inValue double value
-        * @param inFormat format to use
-        * @return object of Coordinate subclass
-        */
-       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
-        * @return String describing coordinate value
-        */
-       public String toString()
-       {
-               return "Coord: " + _cardinal + " (" + _degrees + ") (" + _minutes + ") (" + _seconds + "."
-                       + formatFraction(_fracs, _fracDenom) + ") = " + _asDouble;
-       }
-}