/* * This file is part of INDI for Java. * * INDI for Java is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * INDI for Java is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with INDI for Java. If not, see * <http://www.gnu.org/licenses/>. */ package laazotea.indi; import java.util.Formatter; import java.util.Locale; import java.util.NoSuchElementException; import java.util.StringTokenizer; /** * A class to format and parse numbers in sexagesimal format. * * @author S. Alonso (Zerjillo) [zerjioi at ugr.es] * @version 1.10, March 19, 2012 */ public class INDISexagesimalFormatter { private String format; private int length; private int fractionLength; /** * Constructs an instance of * <code>INDISexagesimalFormatter</code> with a particular format. * * @param format The desired format * @throws IllegalArgumentException if the format is not correct: begins with * %, ends with m and specifies a length and fractionLength in the form * length.fractionLength. Valid fractionLengths are 3, 5, 6, 8 and 9. For * example %5.3m. */ public INDISexagesimalFormatter(String format) throws IllegalArgumentException { this.format = format; checkFormat(); } /** * Gets the format of this formatter. * * @return the format of this formatter. */ public String getFormat() { return format; } /** * Checks the specified format string. * * @throws IllegalArgumentException if the format string is not valid: begins * with %, ends with m and specifies a length and fractionLength in the form * length.fractionLength. Valid fractionLengths are 3, 5, 6, 8 and 9. For * example %5.3m. */ private void checkFormat() throws IllegalArgumentException { if (!format.startsWith("%")) { throw new IllegalArgumentException("Number format not starting with %"); } if (!format.endsWith("m")) { throw new IllegalArgumentException("Sexagesimal format not recognized (not ending m)"); } String remaining = format.substring(1, format.length() - 1); int dotPos = remaining.indexOf("."); if (dotPos == -1) { throw new IllegalArgumentException("Sexagesimal format not correct (no dot)"); } String l = remaining.substring(0, dotPos); String frLength = remaining.substring(dotPos + 1); try { length = Integer.parseInt(l); fractionLength = Integer.parseInt(frLength); } catch (NumberFormatException e) { throw new IllegalArgumentException("Illegal sexagesimal length or fraction length"); } if ((fractionLength != 3) && (fractionLength != 5) && (fractionLength != 6) && (fractionLength != 8) && (fractionLength != 9)) { throw new IllegalArgumentException("Illegal sexagesimal fraction length"); } } /** * Parses a sexagesimal number. DO NOT USE IT. THIS IS A PRELIMINARY VERSION * AND DOES NOT WORK AS EXPECTED. THIS METHOD WILL DISAPEAR IN FUTURE VERSIONS * OF THE CLASS. * * @param number NOT USED * @return NOT USED * @throws IllegalArgumentException * @deprecated */ @Deprecated public double parseSexagesimal2(String number) throws IllegalArgumentException { number = number.replace(' ', ':'); number = number.replace(';', ':'); // System.out.println(" ->" + number + " ; " + format); // System.out.flush(); if (number.indexOf(":") == -1) { // If there are no separators maybe they have sent just a single double try { double n = Double.parseDouble(number); return n; } catch (NumberFormatException e) { throw new IllegalArgumentException("Sexagesimal number format not correct (not even single number)"); } } StringTokenizer st = new StringTokenizer(number, ":", false); double degrees = 0; double minutes = 0; double seconds = 0; try { String aux = st.nextToken().trim(); if (aux.length() > 0) { degrees = Double.parseDouble(aux); } aux = st.nextToken().trim(); if (aux.length() > 0) { minutes = Double.parseDouble(aux); } if (fractionLength > 5) { aux = st.nextToken().trim(); if (aux.length() > 0) { seconds = Double.parseDouble(aux); } } } catch (NoSuchElementException e) { throw new IllegalArgumentException("Sexagesimal number format not correct"); } catch (NumberFormatException e) { throw new IllegalArgumentException("Sexagesimal number component not correct"); } double res = degrees; if (degrees > 0) { res += (minutes / 60.0) + (seconds / 3600.0); } else { res -= (minutes / 60.0) + (seconds / 3600.0); } return res; } /** * Parses a sexagesimal number. The input * <code>String</code> is formatted as a maximum of three doubles separated by * : ; or a blank space. The first number represents the number of degrees, * the second is the number of minutes and the third is the number of seconds. * * @param number The number to be parsed. * @return The parsed double. * @throws IllegalArgumentException if the number format is not correct. */ public double parseSexagesimal(String number) throws IllegalArgumentException { number = number.trim(); if (number.length() == 0) { throw new IllegalArgumentException("Empty number"); } number = number.replace(' ', ':'); number = number.replace(';', ':'); int charCount = number.length() - number.replaceAll(":", "").length(); if (charCount > 2) { throw new IllegalArgumentException("Too many components for the sexagesimal formatter"); } double degrees = 0; double minutes = 0; double seconds = 0; StringTokenizer st = new StringTokenizer(number, ":", false); String d = st.nextToken().trim(); try { degrees = Double.parseDouble(d); } catch (NumberFormatException e) { throw new IllegalArgumentException("Number format incorrect"); } if (st.hasMoreTokens()) { String m = st.nextToken().trim(); try { minutes = Double.parseDouble(m); } catch (NumberFormatException e) { throw new IllegalArgumentException("Minutes format incorrect"); } if (minutes < 0) { throw new IllegalArgumentException("Minutes cannot be negative"); } if (st.hasMoreTokens()) { String s = st.nextToken().trim(); try { seconds = Double.parseDouble(s); } catch (NumberFormatException e) { throw new IllegalArgumentException("Seconds format incorrect"); } if (seconds < 0) { throw new IllegalArgumentException("Seconds cannot be negative"); } } } double res = degrees; if (Double.valueOf(degrees).compareTo(-0.) > 0) { res += (minutes / 60.0) + (seconds / 3600.0); } else { res -= (minutes / 60.0) + (seconds / 3600.0); } return res; } /** * Fomats a number according to the number format os this formatter. * * @param number the number to be formatted. * @return The formatted number as a <code>String</code>. */ public String format(Double number) { int sign = 1; if (number < 0) { sign = -1; } number = Math.abs(number); String fractionalPart = ":"; int integerPart; integerPart = ((int)Math.floor(number)); double fractional = Math.abs(number - integerPart); if (fractionLength < 6) { double minutes = fractional * 60; String form = "%02.0f"; if (fractionLength == 5) { form = "%04.1f"; } Formatter formatter = new Formatter(Locale.US); String newMinutes = formatter.format(form, minutes).toString(); if (Double.parseDouble(newMinutes) >= 60.0) { minutes = 0.0; integerPart++; } formatter = new Formatter(Locale.US); fractionalPart += formatter.format(form, minutes); } else { double minutes = Math.floor(fractional * 60); double rest = fractional - ((double)minutes / 60.0); double seconds = rest * 3600; String form = "%02.0f"; if (fractionLength == 8) { form = "%04.1f"; } else if (fractionLength == 9) { form = "%05.2f"; } Formatter formatter = new Formatter(Locale.US); String newSeconds = formatter.format(form, seconds).toString(); if (Double.parseDouble(newSeconds) >= 60.0) { seconds = 0.0; minutes++; } formatter = new Formatter(Locale.US); String newMinutes = formatter.format("%02.0f", minutes).toString(); if (Double.parseDouble(newMinutes) >= 60.0) { minutes = 0.0; integerPart++; } formatter = new Formatter(Locale.US); fractionalPart += formatter.format("%02.0f:" + form, minutes, seconds); } String res = integerPart + fractionalPart; if (sign < 0) { res = "-" + res; } res = padLeft(res, length); return res; } /** * Pads a String to the left with spaces. * * @param s The <code>String</code> to be padded. * @param n The maximum size of the padded <code>String</code>. * @return The padded <code>String</code> */ private String padLeft(String s, int n) { if (s.length() >= n) { return s; } String spaces = ""; for (int i = 0 ; i < n - s.length() ; i++) { spaces += " "; } return spaces + s; } }