X-Git-Url: https://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2Fdata%2FCoordinate.java;h=556bd892797cac3834f89d316dadbb6a726d0fab;hb=140e9d165f85c3d4f0435a311e091209313faa2a;hp=7e52d839cf1dfae49a649df188eac87d902b7928;hpb=52bf9e8686c916be37a26a0b75340393d4478b05;p=GpsPrune.git diff --git a/tim/prune/data/Coordinate.java b/tim/prune/data/Coordinate.java index 7e52d83..556bd89 100644 --- a/tim/prune/data/Coordinate.java +++ b/tim/prune/data/Coordinate.java @@ -1,5 +1,9 @@ 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 @@ -11,7 +15,7 @@ public abstract class Coordinate public static final int EAST = 1; public static final int SOUTH = 2; public static final int WEST = 3; - public static final char[] PRINTABLE_CARDINALS = {'N', 'E', 'S', 'W'}; + 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; @@ -19,10 +23,19 @@ public abstract class Coordinate 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"); + } + // Instance variables private boolean _valid = false; + private boolean _cardinalGuessed = false; protected int _cardinal = NORTH; private int _degrees = 0; private int _minutes = 0; @@ -56,6 +69,12 @@ 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 @@ -64,7 +83,7 @@ public abstract class Coordinate 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,10 +104,8 @@ public abstract class Coordinate else { inNumeric = false; - // Remember second delimiter - if (numFields == 2) { - secondDelim += currChar; - } + // Remember delimiters + if (currChar != ',' && currChar != '.') {otherDelims[numFields] = true;} } } _valid = (numFields > 0); @@ -100,19 +117,33 @@ public abstract class Coordinate } // 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) { - // 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); + 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); + } } // 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; @@ -120,6 +151,7 @@ public abstract class Coordinate 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) { @@ -130,8 +162,8 @@ public abstract class Coordinate _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); } - _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 @@ -157,6 +189,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 @@ -225,11 +263,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); } @@ -251,7 +285,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)); @@ -260,14 +294,18 @@ public abstract class Coordinate } case FORMAT_DEG_MIN: { - answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "°" + answer = "" + PRINTABLE_CARDINALS[_cardinal] + threeDigitString(_degrees) + "\u00B0" + (_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) + "'"; + 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: @@ -277,6 +315,14 @@ public abstract class Coordinate + (_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 @@ -379,6 +425,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