X-Git-Url: https://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2Fdata%2FCoordinate.java;h=e2b67a8554c996c57e93e40b9e72f6b0ceb6069e;hb=a6197ddcaac11c0b943183da7d46169742d024af;hp=19a1688ed45cc0d0f99385c0b02ba22d50041259;hpb=54b9d8bc8f0025ccf97a67d9dd217ef1f9cf082f;p=GpsPrune.git diff --git a/tim/prune/data/Coordinate.java b/tim/prune/data/Coordinate.java index 19a1688..e2b67a8 100644 --- a/tim/prune/data/Coordinate.java +++ b/tim/prune/data/Coordinate.java @@ -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: @@ -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