edu.ku.brc.specify.config.LatLonConverter.java Source code

Java tutorial

Introduction

Here is the source code for edu.ku.brc.specify.config.LatLonConverter.java

Source

/* Copyright (C) 2013, University of Kansas Center for Research
 *
 * Specify Software Project, specify@ku.edu, Biodiversity Institute,
 * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA
 *
 * This program 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 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package edu.ku.brc.specify.config;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.validator.routines.BigDecimalValidator;
import org.apache.commons.validator.routines.DoubleValidator;

/**
 * Helper for methods for convert to and from various different formats to Decimal Degrees.
 *
 * @author rods
 *
 * @code_status Beta
 *
 * Created Date: Jan 10, 2007
 *
 */
public class LatLonConverter {
    protected static DecimalFormatSymbols formatSymbols = new DecimalFormatSymbols(Locale.getDefault());
    protected static String decimalSep;

    public final static char UNICODE_DEGREE = 0x00b0;

    public final static String DEGREES_SYMBOL = "\u00b0";
    public final static String SEPS = DEGREES_SYMBOL + ":'\" ";

    protected final static int DDDDDD_LEN = 7;
    protected final static int DDMMMM_LEN = 5;
    protected final static int DDMMSS_LEN = 3;

    //public static int[] DECIMAL_SIZES = {7, 5, 3, 0};
    public static int[] DECIMAL_SIZES = { DDDDDD_LEN, DDMMSS_LEN, DDMMMM_LEN, 0 };

    public enum LATLON {
        Latitude, Longitude
    }

    public enum FORMAT {
        DDDDDD, DDMMSS, DDMMMM, None
    } // None must be at the end so the values match Specify 5

    public enum DEGREES_FORMAT {
        None, Symbol, String
    }

    public enum DIRECTION {
        None, NorthSouth, EastWest
    }

    private static boolean useDB = false;

    public static DecimalFormat decFormatter = new DecimalFormat("#0.0000000#");
    protected static StringBuffer zeroes = new StringBuffer(64);

    //patched

    protected static BigDecimal minusOne = new BigDecimal("-1.0");
    protected static DecimalFormat decFormatter2 = new DecimalFormat("#0");
    protected static BigDecimal one = new BigDecimal("1.0");
    protected static BigDecimal sixty = new BigDecimal("60.0");

    public static String[] NORTH_SOUTH;
    public static String[] EAST_WEST;

    public static String[] northSouth = null;
    public static String[] eastWest = null;

    protected static DoubleValidator doubleValidator = new DoubleValidator();
    protected static BigDecimalValidator bigDecValidator = new BigDecimalValidator();

    static {
        NORTH_SOUTH = new String[] { "N", "S" };
        EAST_WEST = new String[] { "E", "W" };
        northSouth = new String[] { "N", "S" };
        eastWest = new String[] { "E", "W" };

        for (int i = 0; i < 8; i++) {
            zeroes.append("00000000");
        }
        decimalSep = Character.toString(formatSymbols.getDecimalSeparator());
    }

    /**
     *
     */
    private LatLonConverter() {
        super();
    }

    /**
     * @param formatInt
     * @return
     */
    public static FORMAT convertIntToFORMAT(final int formatInt) {
        switch (formatInt) {
        case 0:
            return FORMAT.DDDDDD;
        case 1:
            return FORMAT.DDMMSS;
        case 2:
            return FORMAT.DDMMMM;
        default:
            return FORMAT.DDDDDD;
        }
    }

    /**
     * @param bd
     * @param latLonStr
     * @param type
     * @return
     */
    public static String ensureFormattedString(final BigDecimal bd, final String latLonStr, final FORMAT type,
            final LATLON latOrLon) {
        return ensureFormattedString(bd, latLonStr, type, latOrLon, DECIMAL_SIZES[type.ordinal()]);
    }

    /**
     * @param bd
     * @param latLonStr
     * @param type
     * @return
     */
    public static String ensureFormattedString(final BigDecimal bd, final String latLonStr, final FORMAT type,
            final LATLON latOrLon, final int decimalSize) {
        if (StringUtils.isEmpty(latLonStr)) {
            if (bd == null) {
                return null;
            }

            String outStr = format(bd, latOrLon, type, DEGREES_FORMAT.Symbol, decimalSize);
            if (latOrLon == LATLON.Latitude) {
                outStr += " " + northSouth[bd.doubleValue() < 0.0 ? 1 : 0];
            } else {
                outStr += " " + eastWest[bd.doubleValue() < 0.0 ? 1 : 0];
            }
            return outStr;
        }
        return latLonStr;
    }

    /**
     * @param str
     * @param actualFmt
     * @param addSymbols
     * @param inclZeroes
     * @return
     */
    public static LatLonValueInfo adjustLatLonStr(final String strArg, final FORMAT actualFmt,
            final boolean addSymbols, final boolean inclZeroes, final LATLON latOrLon) {
        if (StringUtils.isNotEmpty(strArg)) {
            String str = strArg;

            boolean redoSplit = false;
            String[] tokens = breakStringAPart(str);
            boolean startsWithMinus = str.startsWith("-");
            if (startsWithMinus) {
                str = str.substring(1);
                redoSplit = true;
            }

            if (tokens.length == 1) {
                if (latOrLon == LATLON.Latitude) {
                    str += " " + northSouth[startsWithMinus ? 1 : 0];
                } else {
                    str += " " + eastWest[startsWithMinus ? 1 : 0];
                }
                redoSplit = true;
            }

            if (redoSplit) {
                tokens = breakStringAPart(str);
            }

            String zero = inclZeroes ? "0" : "";
            //System.err.println("tokens[1] ["+tokens[1]+"]["+str+"]");
            boolean hasDegreesTxt = tokens[1].length() == 3 && tokens[1].equals("deg");

            LatLonValueInfo latLonInfo = new LatLonValueInfo(hasDegreesTxt);
            latLonInfo.addPart(tokens[0]);

            int toksLen = tokens.length;
            String dirStr = null;
            FORMAT fmt = null;

            if (actualFmt != null && actualFmt != FORMAT.None) {
                switch (actualFmt) {
                case DDDDDD:
                    dirStr = tokens[toksLen - 1];
                    break;

                case DDMMMM:

                    if (toksLen == 3) {
                        latLonInfo.addPart(tokens[1]);
                        dirStr = tokens[2];
                    } else {
                        latLonInfo.addPart(zero);
                        dirStr = tokens[1];
                    }

                    break;

                case DDMMSS:
                    if (toksLen == 4) {
                        latLonInfo.addPart(tokens[1]);
                        latLonInfo.addPart(tokens[2]);
                        dirStr = tokens[3];

                    } else if (toksLen == 3) {
                        String tok1 = tokens[1];
                        if (StringUtils.contains(tok1, formatSymbols.getDecimalSeparator())) {
                            String[] tks = StringUtils.split(tok1, formatSymbols.getDecimalSeparator());
                            latLonInfo.addPart(tks[0]);
                            latLonInfo.addPart(tks[1]);

                        } else {
                            latLonInfo.addPart(tok1);
                            latLonInfo.addPart(zero);
                        }
                        dirStr = tokens[2];

                    } else if (toksLen == 2) {
                        latLonInfo.addPart(zero);
                        latLonInfo.addPart(zero);
                        dirStr = tokens[1];
                    }
                    break;

                default:
                    break;
                } // switch
                fmt = actualFmt;

            } else {
                switch (toksLen) {
                case 2: {
                    dirStr = tokens[1];
                    fmt = FORMAT.DDDDDD;
                    break;
                }

                case 3: {
                    if (hasDegreesTxt) {
                        fmt = FORMAT.DDDDDD;
                    } else {
                        latLonInfo.addPart(tokens[1]);
                        fmt = FORMAT.DDMMMM;
                    }
                    dirStr = tokens[2];
                    break;
                }

                case 4: {
                    if (hasDegreesTxt) {
                        fmt = FORMAT.DDMMMM;
                    } else {
                        latLonInfo.addPart(tokens[1]);
                        fmt = FORMAT.DDMMSS;
                    }
                    latLonInfo.addPart(tokens[2]);
                    dirStr = tokens[3];

                    break;
                }

                case 5:
                    latLonInfo.addPart(tokens[2]);
                    latLonInfo.addPart(tokens[3]);
                    dirStr = tokens[4];
                    fmt = FORMAT.DDMMSS;
                    break;

                default:
                    break;

                } // switch
            } // if
            latLonInfo.addPart(dirStr);
            latLonInfo.setFormat(fmt);
            latLonInfo.setDirStr(dirStr);
            return latLonInfo;

        }
        return null;
    }

    /**
     * @param strArg
     * @param fromFmt
     * @param toFmt
     * @param latOrLon
     * @return
     */
    public static String convert(final String strArg, final FORMAT fromFmt, final FORMAT toFmt,
            final LATLON latOrLon) {
        if (fromFmt == toFmt) {
            return strArg;
        }

        LatLonValueInfo latLonVal = adjustLatLonStr(strArg, fromFmt, false, true, latOrLon);
        if (latLonVal == null) {
            System.err.println("Unable to convert [" + strArg + "]  from: " + fromFmt + " with: " + latOrLon);
            return null;
        }

        String str = latLonVal.getStrVal(false);

        if (StringUtils.isNotEmpty(str)) {
            BigDecimal bd = null;
            switch (fromFmt) {
            case DDDDDD:
                bd = convertDDDDStrToDDDDBD(str);
                break;

            case DDMMMM:
                bd = convertDDMMMMStrToDDDDBD(str);
                break;

            case DDMMSS:
                bd = convertDDMMSSStrToDDDDBD(str);
                break;

            case None:
                break;
            }

            String outStr = "";
            if (bd != null) {
                outStr = format(bd, latOrLon, toFmt, DEGREES_FORMAT.Symbol, DECIMAL_SIZES[toFmt.ordinal()]);
                if (StringUtils.isNotEmpty(latLonVal.getDirStr())) {
                    outStr += " " + latLonVal.getDirStr();
                }
            }
            return outStr;
        }

        return null;
    }

    /**
     * Converts BigDecimal to Degrees, Minutes and Decimal Seconds.
     * @param bd the DigDecimal to be converted.
     * @return a 3 piece string
     */
    public static String convertToDDMMSS(final BigDecimal bd, final int decimalLen) {
        return convertToDDMMSS(bd, DEGREES_FORMAT.None, DIRECTION.None, decimalLen);
    }

    /**
     * @param bd
     * @param decimalLen
     * @return
     */
    public static String convertToSignedDDMMSS(final BigDecimal bd, final int decimalLen) {
        return convertToSignedDDMMSS(bd, decimalLen, DEGREES_FORMAT.None);
    }

    /**
     * @param bd
     * @param decimalLen
     * @param degFmt
     * @return
     */
    public static String convertToSignedDDMMSS(final BigDecimal bd, final int decimalLen,
            final DEGREES_FORMAT degFmt) {
        String sign = "";
        if (bd.compareTo(bd.abs()) < 0) {
            sign = "-";
        }

        String convertedAbs = convertToDDMMSS(bd, degFmt, DIRECTION.None, decimalLen);
        return sign + convertedAbs;
    }

    /**
     * Calculates how many decimal places there is in the string.
     * @param latLonStr the string to be checked
     * @return the number of decimals places
     */
    public static int getDecimalLength(final String latLonStr) {
        int decimalFmtLen = 0;
        if (StringUtils.isNotEmpty(latLonStr)) {
            int decIndex = latLonStr.lastIndexOf('.');
            if (decIndex > -1 && latLonStr.length() > decIndex) {
                decimalFmtLen = latLonStr.length() - decIndex;
            }
        }
        return decimalFmtLen;
    }

    /**
     * Converts BigDecimal to Degrees, Minutes and Decimal Seconds.
     * @param bd the DigDecimal to be converted.
     * @return a 3 piece string
     */
    public static String convertToDDMMSS(final BigDecimal bd, final DEGREES_FORMAT degreesFMT,
            final DIRECTION direction, final int decimalLen) {
        return convertToDDMMSS(bd, degreesFMT, direction, decimalLen, false);
    }

    /**
     * Converts BigDecimal to Degrees, Minutes and Decimal Seconds.
     * @param bd the DigDecimal to be converted.
     * @return a 3 piece string
     */
    public static String convertToDDMMSS(final BigDecimal bd, final DEGREES_FORMAT degreesFMT,
            final DIRECTION direction, final int decimalLen, final boolean alwaysIncludeDir) {

        if (bd.doubleValue() == 0.0) {
            return "0." + zeroes.substring(0, decimalLen);
        }

        if (useDB) {
            BigDecimal remainder = bd.remainder(one);

            BigDecimal num = bd.subtract(remainder);

            BigDecimal minutes = new BigDecimal(remainder.multiply(sixty).abs().intValue());
            BigDecimal secondsFraction = remainder.abs().multiply(sixty).subtract(minutes);
            BigDecimal seconds = secondsFraction.multiply(sixty);

            //System.out.println("["+decFormatter2.format(num)+"]["+minutes+"]["+seconds+"]");

            return decFormatter2.format(num) + " " + decFormatter2.format(minutes) + " "
                    + decFormatter.format(seconds);

        }
        //else

        double num = Math.abs(bd.doubleValue());
        int whole = (int) Math.floor(num);
        double remainder = num - whole;

        double minutes = remainder * 60.0;
        int minutesWhole = (int) Math.floor(minutes);
        double secondsFraction = minutes - minutesWhole;
        double seconds = secondsFraction * 60.0;

        boolean addMinSecsSyms = degreesFMT != DEGREES_FORMAT.None;

        if (minutesWhole == 60) {
            whole += 1;
            minutesWhole = 0;
        }

        // round to 2 decimal places precision
        seconds = Math.round(seconds * 1000) / 1000.0;

        int secondsWhole = (int) Math.floor(seconds);
        if (secondsWhole == 60) {
            minutesWhole += 1;
            seconds = 0.0;
        }

        StringBuilder sb = new StringBuilder();
        sb.append(whole);
        if (degreesFMT == DEGREES_FORMAT.Symbol) {
            sb.append(DEGREES_SYMBOL);
        }
        sb.append(' ');
        sb.append(minutesWhole);
        if (addMinSecsSyms)
            sb.append("'");
        sb.append(' ');

        sb.append(String.format("%2." + decimalLen + "f", seconds));
        if (addMinSecsSyms)
            sb.append("\"");

        if (degreesFMT == DEGREES_FORMAT.String || alwaysIncludeDir) {
            int inx = bd.doubleValue() < 0.0 ? 1 : 0;
            if (direction != DIRECTION.None) {
                sb.append(' ');
                sb.append(direction == DIRECTION.NorthSouth ? northSouth[inx] : eastWest[inx]);
            }
        }
        //System.err.println("["+sb.toString()+"]");
        //return whole + (DEGREES_FORMAT.None ? "\u00B0" : "") + " " + minutesWhole + " " + StringUtils.strip(String.format("%12.10f", new Object[] {seconds}), "0");
        return sb.toString();
    }

    /**
     * Converts BigDecimal to Degrees and Decimal Minutes.
     * @param bd the DigDecimal to be converted.
     * @return a 2 piece string
     */
    public static String convertToDDMMMM(final BigDecimal bd, final int decimalLen) {
        return convertToDDMMMM(bd, DEGREES_FORMAT.None, DIRECTION.None, decimalLen);
    }

    /**
     * @param dd
     * @param decimalLen
     * @return
     */
    public static String convertToSignedDDMMMM(final BigDecimal dd, final int decimalLen) {
        return convertToSignedDDMMMM(dd, decimalLen, DEGREES_FORMAT.None);
    }

    /**
     * @param dd
     * @param decimalLen
     * @param degFmt
     * @return
     */
    public static String convertToSignedDDMMMM(final BigDecimal dd, final int decimalLen,
            final DEGREES_FORMAT degFmt) {
        String sign = "";
        if (dd.compareTo(dd.abs()) < 0) {
            sign = "-";
        }

        String convertedAbs = convertToDDMMMM(dd, degFmt, DIRECTION.None, decimalLen);
        return sign + convertedAbs;
    }

    /**
     * Converts BigDecimal to Degrees and Decimal Minutes.
     * @param bd the DigDecimal to be converted.
     * @return a 2 piece string
     */
    public static String convertToDDMMMM(final BigDecimal bd, final DEGREES_FORMAT degreesFMT,
            final DIRECTION direction, final int decimalLen) {
        return convertToDDMMMM(bd, degreesFMT, direction, decimalLen, false);
    }

    /**
     * Converts BigDecimal to Degrees and Decimal Minutes.
     * @param bd the DigDecimal to be converted.
     * @return a 2 piece string
     */
    public static String convertToDDMMMM(final BigDecimal bd, final DEGREES_FORMAT degreesFMT,
            final DIRECTION direction, final int decimalLen, final boolean alwaysIncludeDir) {
        if (bd.doubleValue() == 0.0) {
            return "0.0";
        }

        if (useDB) {
            BigDecimal remainder = bd.remainder(one);

            BigDecimal num = bd.subtract(remainder);

            BigDecimal minutes = remainder.multiply(sixty).abs();

            //System.out.println("["+decFormatter2.format(num)+"]["+minutes+"]");
            return decFormatter2.format(num) + " " + decFormatter2.format(minutes);

        }
        //else

        boolean addMinSecsSyms = degreesFMT != DEGREES_FORMAT.None;

        double num = Math.abs(bd.doubleValue());
        int whole = (int) Math.floor(num);
        double remainder = num - whole;

        double minutes = remainder * 60.0;
        //System.out.println("["+whole+"]["+String.format("%10.10f", new Object[] {minutes})+"]");

        StringBuilder sb = new StringBuilder();
        sb.append(whole);
        if (degreesFMT == DEGREES_FORMAT.Symbol) {
            sb.append("\u00B0");
        }
        sb.append(' ');

        // round to four decimal places of precision
        //minutes = Math.round(minutes*10000) / 10000;

        sb.append(String.format("%" + 2 + "." + decimalLen + "f", minutes));
        if (addMinSecsSyms)
            sb.append("'");

        if (degreesFMT == DEGREES_FORMAT.String || alwaysIncludeDir) {
            int inx = bd.doubleValue() < 0.0 ? 1 : 0;
            if (direction != DIRECTION.None) {
                sb.append(' ');
                sb.append(direction == DIRECTION.NorthSouth ? northSouth[inx] : eastWest[inx]);
            }
        }
        //return whole + (degreesFMT == DEGREES_FORMAT.Symbol ? "\u00B0" : "") + " " + StringUtils.strip(String.format("%10.10f", new Object[] {minutes}), "0");
        return sb.toString();

    }

    /**
     * Converts BigDecimal to Decimal Degrees.
     * @param bd the DigDecimal to be converted.
     * @return a 1 piece string
     */
    public static String convertToDDDDDD(final BigDecimal bd, final int decimalLen) {
        return convertToDDDDDD(bd, DEGREES_FORMAT.String, DIRECTION.None, decimalLen);
    }

    /**
     * @param dd
     * @param decimalLen
     * @return
     */
    public static String convertToSignedDDDDDD(final BigDecimal dd, final int decimalLen) {
        return convertToSignedDDDDDD(dd, decimalLen, DEGREES_FORMAT.None);
    }

    public static String convertToSignedDDDDDD(final BigDecimal dd, final int decimalLen,
            final DEGREES_FORMAT degFmt) {
        String sign = "";
        if (dd.compareTo(dd.abs()) < 0) {
            sign = "-";
        }

        String convertedAbs = convertToDDDDDD(dd, degFmt, DIRECTION.None, decimalLen);
        return sign + convertedAbs;
    }

    /**
     * Converts BigDecimal to Decimal Degrees.
     * @param bd the DigDecimal to be converted.
     * @param degreesFMT indicates whether to include the degrees symbol
     * @return a 1 piece string
     */
    public static String convertToDDDDDD(final BigDecimal bd, final DEGREES_FORMAT degreesFMT,
            final DIRECTION direction, final int decimalLen) {
        return convertToDDDDDD(bd, degreesFMT, direction, decimalLen, false);
    }

    /**
     * Converts BigDecimal to Decimal Degrees.
     * @param bd the DigDecimal to be converted.
     * @param degreesFMT indicates whether to include the degrees symbol
     * @return a 1 piece string
     */
    public static String convertToDDDDDD(final BigDecimal bd, final DEGREES_FORMAT degreesFMT,
            final DIRECTION direction, final int decimalLen, final boolean alwaysIncludeDir) {
        if (bd == null || bd.doubleValue() == 0.0) {
            return "0.0";
        }

        StringBuilder sb = new StringBuilder();

        sb.append(String.format("%" + decimalLen + "." + decimalLen + "f", bd.abs()));

        if (degreesFMT == DEGREES_FORMAT.Symbol) {
            sb.append("\u00B0");

        }
        if (degreesFMT == DEGREES_FORMAT.String || alwaysIncludeDir) {
            int inx = bd.doubleValue() < 0.0 ? 1 : 0;
            if (direction != DIRECTION.None) {
                sb.append(' ');
                sb.append(direction == DIRECTION.NorthSouth ? northSouth[inx] : eastWest[inx]);
            }
        }
        //return format(bd.abs()) + (degreesFMT == DEGREES_FORMAT.Symbol ? "\u00B0" : "");
        return sb.toString();
    }

    /**
     * Given a single character string  should the direction be negative.
     * @param direction the string
     * @return true negative, false positive
     */
    protected static boolean isNegative(final String direction) {
        return direction.equals("S") || direction.equals("W");
    }

    public static BigDecimal convertDDDDStrToDDDDBD(final String str) {
        String withoutDegSign = StringUtils.chomp(str, "");
        //above doesn't always work for Windows/Mac so try again...
        withoutDegSign = StringUtils.chomp(withoutDegSign, "");
        //apparently need to do this on mac
        withoutDegSign = StringUtils.remove(withoutDegSign, UNICODE_DEGREE);

        String val = StringUtils.replace(StringUtils.replace(withoutDegSign, decimalSep, ""), "-", "");
        return StringUtils.isNumeric(val) ? parseDoubleToBigDecimal(withoutDegSign) : null;
    }

    public static BigDecimal convertDDDDStrToDDDDBD(final String str, final String direction) {
        String val = StringUtils.replace(StringUtils.replace(str, decimalSep, ""), "-", "");
        if (!StringUtils.isNumeric(val)) {
            return null;
        }

        BigDecimal bd = parseDoubleToBigDecimal(str);
        if (isNegative(direction)) {
            return bd.multiply(minusOne);
        }
        return bd;
    }

    public static String[] breakStringAPart(final String str) {
        return StringUtils.split(str, SEPS);
    }

    public static BigDecimal convertDDMMSSStrToDDDDBD(final String str) {
        String[] parts = StringUtils.split(str, " d'\"" + DEGREES_SYMBOL);
        if (parts.length != 3) {
            return null;
        }

        double p0 = parseDouble(parts[0]);
        boolean neg = false;
        if (p0 < 0) {
            p0 = p0 * -1;
            neg = true;
        }
        double p1 = parseDouble(parts[1]);
        double p2 = parseDouble(parts[2]);

        BigDecimal val = new BigDecimal(p0 + ((p1 + (p2 / 60.0)) / 60.0));
        if (neg) {
            val = val.multiply(minusOne);
        }
        return val;
    }

    public static BigDecimal convertDDMMSSStrToDDDDBD(final String str, final String direction) {
        BigDecimal bd = convertDDMMSSStrToDDDDBD(str);

        if (isNegative(direction)) {
            return bd.multiply(minusOne);
        }
        return bd;
    }

    public static BigDecimal convertDDMMMMStrToDDDDBD(final String str) {
        String[] parts = StringUtils.split(str, " dm'\"" + DEGREES_SYMBOL);

        double p0 = parseDouble(parts[0]);
        boolean neg = false;
        if (p0 < 0) {
            p0 = p0 * -1;
            neg = true;
        }
        Double p1 = parseDouble(parts[1]);
        if (p1 != null) {
            BigDecimal val = new BigDecimal(p0 + (p1 / 60.0));

            if (neg) {
                val = val.multiply(minusOne);
            }
            return val;
        }
        return null;
    }

    public static BigDecimal convertDDMMMMStrToDDDDBD(final String str, final String direction) {
        BigDecimal bd = convertDDMMMMStrToDDDDBD(str);
        if (isNegative(direction)) {
            return bd.multiply(minusOne);
        }
        return bd;
    }

    /**
     * Strinps any zeros at end of string, but will append a zero if string would end in a decimal.
     * @param str the string to be converted
     * @return the new string
     */
    public static String stripZeroes(final String str) {
        if (str.indexOf('.') == -1) {
            return str;

        }
        // else
        String newStr = StringUtils.stripEnd(str, "0");
        if (newStr.endsWith(decimalSep)) {
            return newStr + "0";
        }
        return newStr;
    }

    /**
     * Returns a formatted string using the class level formatter and then strips any extra zeroes.
     * @param bd the BigDecimal to be formatted.
     * @return the formatted string
     */
    public static String format(final BigDecimal bd) {
        String formatted = decFormatter.format(bd);
        return stripZeroes(formatted);
    }

    /**
     * Converts a Lat/Lon BigDecimal to a String
     * @param value the value
     * @param latOrLon whether it is a latitude or longitude
     * @param format the format to use
     * @param degreesFMT indicates whether to use a symbol or append single character text representation of the direction ('N', 'S', 'E', 'W")
     * @return string of the value
     */
    public static String format(final BigDecimal value, final LATLON latOrLon, final FORMAT format,
            final DEGREES_FORMAT degreesFMT, final int decimalLen) {
        DIRECTION dir = latOrLon == LATLON.Latitude ? DIRECTION.NorthSouth : DIRECTION.EastWest;
        switch (format) {
        case DDDDDD:
            return convertToDDDDDD(value, degreesFMT, dir, decimalLen);

        case DDMMMM:
            return convertToDDMMMM(value, degreesFMT, dir, decimalLen);

        case DDMMSS:
            return convertToDDMMSS(value, degreesFMT, dir, decimalLen);

        case None:
            break;
        }
        return "";
    }

    /**
     * @param str
     * @return
     */
    public static BigDecimal convertDirectionalDDMMSSToDDDD(final String str) {
        String[] parts = StringUtils.split(str, " d'\"" + DEGREES_SYMBOL);

        String dir = null;
        if (parts.length < 4) {
            parts[2] = parts[2].replaceAll("[NSEW]", "");

            int beginIndex = str.indexOf(parts[2]) + parts[2].length();
            dir = str.substring(beginIndex, beginIndex + 1);
        } else {
            dir = parts[3].substring(0, 1);
        }

        double p0 = parseDouble(parts[0]);
        double p1 = parseDouble(parts[1]);
        double p2 = parseDouble(parts[2]);

        BigDecimal val = new BigDecimal(p0 + ((p1 + (p2 / 60.0)) / 60.0));

        if (isNegative(dir)) {
            val = val.multiply(minusOne);
        }

        return val;
    }

    /**
     * @param dm
     * @return
     */
    public static BigDecimal convertDirectionalDDMMMMToDDDD(final String dm) {
        String[] parts = StringUtils.split(dm, " d'\"" + DEGREES_SYMBOL);

        String dir = null;
        if (parts.length < 3) {
            parts[1] = parts[1].replaceAll("[NSEW]", "");

            int beginIndex = dm.indexOf(parts[1]) + parts[1].length();
            dir = dm.substring(beginIndex, beginIndex + 1);
        } else {
            dir = parts[2].substring(0, 1);
        }

        double p0 = parseDouble(parts[0]);
        double p1 = parseDouble(parts[1]);

        BigDecimal val = new BigDecimal(p0 + (p1 / 60.0));

        if (isNegative(dir)) {
            val = val.multiply(minusOne);
        }

        return val;
    }

    /**
     * @param str
     * @return
     */
    public static BigDecimal convertDirectionalDDDDToDDDD(final String str) {
        String[] parts = StringUtils.split(str, " d'\"" + DEGREES_SYMBOL);

        String dir = null;
        if (parts.length < 2) {
            parts[0] = parts[0].replaceAll("[NSEW]", "");

            int beginIndex = str.indexOf(parts[0]) + parts[0].length();
            dir = str.substring(beginIndex, beginIndex + 1);
        } else {
            dir = parts[1].substring(0, 1);
        }

        double p0 = parseDouble(parts[0]);

        BigDecimal val = new BigDecimal(p0);

        if (isNegative(dir)) {
            val = val.multiply(minusOne);
        }

        return val;
    }

    public static Double parseDouble(final String value) {
        try {
            return doubleValidator.validate(value, Locale.getDefault());
        } catch (NullPointerException e) {
        }
        return null;
    }

    /**
     * @param value: a decimal format number (exponential or other formats not supported)
     * @return
     */
    public static BigDecimal parseDoubleToBigDecimal(final String value) {
        return bigDecValidator.validate(value, Locale.getDefault());
    }
}