1 package tim.prune.data;
4 * Class to represent a lat/long coordinate
5 * and provide conversion functions
7 public abstract class Coordinate
9 public static final int NORTH = 0;
10 public static final int EAST = 1;
11 public static final int SOUTH = 2;
12 public static final int WEST = 3;
13 public static final char[] PRINTABLE_CARDINALS = {'N', 'E', 'S', 'W'};
14 public static final int FORMAT_DEG_MIN_SEC = 10;
15 public static final int FORMAT_DEG_MIN = 11;
16 public static final int FORMAT_DEG = 12;
17 public static final int FORMAT_DEG_WITHOUT_CARDINAL = 13;
18 public static final int FORMAT_DEG_WHOLE_MIN = 14;
19 public static final int FORMAT_DEG_MIN_SEC_WITH_SPACES = 15;
20 public static final int FORMAT_CARDINAL = 16;
21 public static final int FORMAT_NONE = 19;
24 private boolean _valid = false;
25 protected int _cardinal = NORTH;
26 private int _degrees = 0;
27 private int _minutes = 0;
28 private int _seconds = 0;
29 private int _fracs = 0;
30 private String _originalString = null;
31 private int _originalFormat = FORMAT_NONE;
32 private double _asDouble = 0.0;
36 * Constructor given String
37 * @param inString string to parse
39 public Coordinate(String inString)
41 _originalString = inString;
45 inString = inString.trim();
46 strLen = inString.length();
50 // Check for leading character NSEW
51 _cardinal = getCardinal(inString.charAt(0));
52 // count numeric fields - 1=d, 2=dm, 3=dm.m/dms, 4=dms.s
54 boolean inNumeric = false;
56 long[] fields = new long[4];
57 long[] denoms = new long[4];
60 for (int i=0; i<strLen; i++)
62 currChar = inString.charAt(i);
63 if (currChar >= '0' && currChar <= '9')
69 denoms[numFields-1] = 1;
71 fields[numFields-1] = fields[numFields-1] * 10 + (currChar - '0');
72 denoms[numFields-1] *= 10;
79 _valid = (numFields > 0);
81 catch (ArrayIndexOutOfBoundsException obe)
83 // more than four fields found - unable to parse
86 // parse fields according to number found
87 _degrees = (int) fields[0];
88 _originalFormat = FORMAT_DEG;
91 // String is just decimal degrees
92 double numMins = fields[1] * 60.0 / denoms[1];
93 _minutes = (int) numMins;
94 double numSecs = (numMins - _minutes) * 60.0;
95 _seconds = (int) numSecs;
96 _fracs = (int) ((numSecs - _seconds) * 10);
98 else if (numFields == 3)
100 // String is degrees-minutes.fractions
101 _originalFormat = FORMAT_DEG_MIN;
102 _minutes = (int) fields[1];
103 double numSecs = fields[2] * 60.0 / denoms[2];
104 _seconds = (int) numSecs;
105 _fracs = (int) ((numSecs - _seconds) * 10);
107 else if (numFields == 4)
109 _originalFormat = FORMAT_DEG_MIN_SEC;
110 // String is degrees-minutes-seconds.fractions
111 _minutes = (int) fields[1];
112 _seconds = (int) fields[2];
113 _fracs = (int) fields[3];
115 _asDouble = 1.0 * _degrees + (_minutes / 60.0) + (_seconds / 3600.0) + (_fracs / 36000.0);
116 if (_cardinal == WEST || _cardinal == SOUTH || inString.charAt(0) == '-')
117 _asDouble = -_asDouble;
124 * Get the cardinal from the given character
125 * @param inChar character from file
127 protected abstract int getCardinal(char inChar);
132 * @param inValue value of coordinate
133 * @param inFormat format to use
134 * @param inCardinal cardinal
136 protected Coordinate(double inValue, int inFormat, int inCardinal)
139 // Calculate degrees, minutes, seconds
140 _degrees = (int) Math.abs(inValue);
141 double numMins = (Math.abs(_asDouble)-_degrees) * 60.0;
142 _minutes = (int) numMins;
143 double numSecs = (numMins - _minutes) * 60.0;
144 _seconds = (int) numSecs;
145 _fracs = (int) ((numSecs - _seconds) * 10);
146 // Make a string to display on screen
147 _cardinal = inCardinal;
148 _originalFormat = FORMAT_NONE;
149 if (inFormat == FORMAT_NONE) inFormat = FORMAT_DEG_WITHOUT_CARDINAL;
150 _originalString = output(inFormat);
151 _originalFormat = inFormat;
157 * @return coordinate as a double
159 public double getDouble()
165 * @return true if Coordinate is valid
167 public boolean isValid()
173 * Compares two Coordinates for equality
174 * @param inOther other Coordinate object with which to compare
175 * @return true if the two objects are equal
177 public boolean equals(Coordinate inOther)
179 return (inOther != null && _cardinal == inOther._cardinal
180 && _degrees == inOther._degrees
181 && _minutes == inOther._minutes
182 && _seconds == inOther._seconds
183 && _fracs == inOther._fracs);
188 * Output the Coordinate in the given format
189 * @param inOriginalString the original String to use as default
190 * @param inFormat format to use, eg FORMAT_DEG_MIN_SEC
191 * @return String for output
193 public String output(int inFormat)
195 String answer = _originalString;
196 if (inFormat != FORMAT_NONE && inFormat != _originalFormat)
198 // TODO: allow specification of precision for output of d-m and d
199 // format as specified
202 case FORMAT_DEG_MIN_SEC:
204 StringBuffer buffer = new StringBuffer();
205 buffer.append(PRINTABLE_CARDINALS[_cardinal])
206 .append(threeDigitString(_degrees)).append('°')
207 .append(twoDigitString(_minutes)).append('\'')
208 .append(twoDigitString(_seconds)).append('.')
210 answer = buffer.toString();
215 answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "°"
216 + (_minutes + _seconds / 60.0 + _fracs / 600.0) + "'";
219 case FORMAT_DEG_WHOLE_MIN:
221 answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "°"
222 + (int) Math.floor(_minutes + _seconds / 60.0 + _fracs / 600.0 + 0.5) + "'";
226 case FORMAT_DEG_WITHOUT_CARDINAL:
228 answer = (_asDouble<0.0?"-":"")
229 + (_degrees + _minutes / 60.0 + _seconds / 3600.0 + _fracs / 36000.0);
232 case FORMAT_DEG_MIN_SEC_WITH_SPACES:
234 answer = "" + _degrees + " " + _minutes + " " + _seconds + "." + _fracs;
237 case FORMAT_CARDINAL:
239 answer = "" + PRINTABLE_CARDINALS[_cardinal];
249 * Format an integer to a two-digit String
250 * @param inNumber number to format
251 * @return two-character String
253 private static String twoDigitString(int inNumber)
255 if (inNumber <= 0) return "00";
256 if (inNumber < 10) return "0" + inNumber;
257 if (inNumber < 100) return "" + inNumber;
258 return "" + (inNumber % 100);
263 * Format an integer to a three-digit String for degrees
264 * @param inNumber number to format
265 * @return three-character String
267 private static String threeDigitString(int inNumber)
269 if (inNumber <= 0) return "000";
270 if (inNumber < 10) return "00" + inNumber;
271 if (inNumber < 100) return "0" + inNumber;
272 return "" + (inNumber % 1000);
277 * Create a new Coordinate between two others
278 * @param inStart start coordinate
279 * @param inEnd end coordinate
280 * @param inIndex index of point
281 * @param inNumPoints number of points to interpolate
282 * @return new Coordinate object
284 public static Coordinate interpolate(Coordinate inStart, Coordinate inEnd,
285 int inIndex, int inNumPoints)
287 double startValue = inStart.getDouble();
288 double endValue = inEnd.getDouble();
289 double newValue = startValue + (endValue - startValue) * (inIndex+1) / (inNumPoints + 1);
290 Coordinate answer = inStart.makeNew(newValue, inStart._originalFormat);
296 * Make a new Coordinate according to subclass
297 * @param inValue double value
298 * @param inFormat format to use
299 * @return object of Coordinate subclass
301 protected abstract Coordinate makeNew(double inValue, int inFormat);
305 * Create a String representation for debug
307 public String toString()
309 return "Coord: " + _cardinal + " (" + _degrees + ") (" + _minutes + ") (" + _seconds + "." + _fracs + ") = " + _asDouble;