*/
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 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_NONE = 19;
// Instance variables
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;
}
if (strLen > 1)
{
- // Check for leading character NSEW
- _cardinal = getCardinal(inString.charAt(0));
+ // Check for cardinal character either at beginning or end
+ _cardinal = getCardinal(inString.charAt(0), inString.charAt(strLen-1));
// count numeric fields - 1=d, 2=dm, 3=dm.m/dms, 4=dms.s
int numFields = 0;
boolean inNumeric = false;
char currChar;
- long[] fields = new long[4];
+ long[] fields = new long[4]; // needs to be long for lengthy decimals
long[] denoms = new long[4];
+ String secondDelim = "";
try
{
+ // Loop over characters in input string, populating fields array
for (int i=0; i<strLen; i++)
{
currChar = inString.charAt(i);
else
{
inNumeric = false;
+ // Remember second delimiter
+ if (numFields == 2) {
+ secondDelim += currChar;
+ }
}
}
_valid = (numFields > 0);
// parse fields according to number found
_degrees = (int) fields[0];
_originalFormat = FORMAT_DEG;
+ _fracDenom = 10;
if (numFields == 2)
{
// String is just decimal degrees
_seconds = (int) numSecs;
_fracs = (int) ((numSecs - _seconds) * 10);
}
- else if (numFields == 3)
+ // Differentiate between d-m.f and d-m-s using . or ,
+ else if (numFields == 3 && (secondDelim.equals(".") || secondDelim.equals(",")))
{
// String is degrees-minutes.fractions
_originalFormat = FORMAT_DEG_MIN;
_seconds = (int) numSecs;
_fracs = (int) ((numSecs - _seconds) * 10);
}
- else if (numFields == 4)
+ else if (numFields == 4 || numFields == 3)
{
- _originalFormat = FORMAT_DEG_MIN_SEC;
// 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 / 36000.0);
+ _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);
}
else _valid = false;
}
+ /**
+ * Get the cardinal from the given character
+ * @param inFirstChar first character from file
+ * @param inLastChar last character from file
+ */
+ protected 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);
+ }
+ // use default from concrete subclass
+ if (cardinal == NO_CARDINAL) {
+ cardinal = getDefaultCardinal();
+ }
+ return cardinal;
+ }
+
+
/**
* 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)
+ protected Coordinate(double inValue, int inFormat, int inCardinal)
{
_asDouble = inValue;
// Calculate degrees, minutes, seconds
- _degrees = (int) inValue;
- double numMins = (Math.abs(_asDouble)-Math.abs(_degrees)) * 60.0;
+ _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;
}
/**
* Output the Coordinate in the given format
- * @param inOriginalString the original String to use as default
* @param inFormat format to use, eg FORMAT_DEG_MIN_SEC
* @return String for output
*/
// format as specified
switch (inFormat)
{
- case FORMAT_DEG_MIN_SEC: {
+ case FORMAT_DEG_MIN_SEC:
+ {
StringBuffer buffer = new StringBuffer();
buffer.append(PRINTABLE_CARDINALS[_cardinal])
.append(threeDigitString(_degrees)).append('°')
.append(twoDigitString(_minutes)).append('\'')
.append(twoDigitString(_seconds)).append('.')
- .append(_fracs);
- answer = buffer.toString(); break;
+ .append(formatFraction(_fracs, _fracDenom));
+ answer = buffer.toString();
+ break;
+ }
+ case FORMAT_DEG_MIN:
+ {
+ answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "°"
+ + (_minutes + _seconds / 60.0 + _fracs / 60.0 / _fracDenom) + "'";
+ 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) + "'";
+ break;
}
- case FORMAT_DEG_MIN: answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "°"
- + (_minutes + _seconds / 60.0 + _fracs / 600.0); break;
case FORMAT_DEG:
- case FORMAT_DEG_WITHOUT_CARDINAL: answer = (_asDouble<0.0?"-":"")
- + (_degrees + _minutes / 60.0 + _seconds / 3600.0 + _fracs / 36000.0); break;
+ case FORMAT_DEG_WITHOUT_CARDINAL:
+ {
+ answer = (_asDouble<0.0?"-":"")
+ + (_degrees + _minutes / 60.0 + _seconds / 3600.0 + _fracs / 3600.0 / _fracDenom);
+ 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
*/
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) * (inIndex+1) / (inNumPoints + 1);
+ double newValue = startValue + (endValue - startValue) * inFraction;
Coordinate answer = inStart.makeNew(newValue, inStart._originalFormat);
return answer;
}
/**
* Create a String representation for debug
+ * @return String describing coordinate value
*/
public String toString()
{
- return "Coord: " + _cardinal + " (" + _degrees + ") (" + _minutes + ") (" + _seconds + "." + _fracs + ") = " + _asDouble;
+ return "Coord: " + _cardinal + " (" + _degrees + ") (" + _minutes + ") (" + _seconds + "."
+ + formatFraction(_fracs, _fracDenom) + ") = " + _asDouble;
}
}