Merge branch 'release/5.45.0'
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / strategy / parser / location / CoordinateConverter.java
index 632658c1d1b3bc385d1fafd0d27bf3390a4cdd11..c1d368a4144a2aa482ebb810aa132c9a465a297c 100644 (file)
-// $Id$\r
-/**\r
-* Copyright (C) 2009 EDIT\r
-* European Distributed Institute of Taxonomy \r
-* http://www.e-taxonomy.eu\r
-* \r
-* The contents of this file are subject to the Mozilla Public License Version 1.1\r
-* See LICENSE.TXT at the top of this package for the full license terms.\r
-* \r
-* This file is an Java adaption from the orginal CoordinateConverter written by Dominik Mikiewicz\r
-* @see www.cartomatic.pl\r
-* @see http://dev.e-taxonomy.eu/svn/trunk/geo/coordinateConverter/CoordinateConverter.cs\r
-* @see http://gis.miiz.waw.pl/webapps/coordinateconverter/\r
-*/\r
-package eu.etaxonomy.cdm.strategy.parser.location;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.List;\r
-import java.util.regex.Pattern;\r
-\r
-import org.apache.log4j.Logger;\r
-\r
-/**\r
- * @author a.mueller\r
- * @date 07.06.2010\r
- *\r
- */\r
-public class CoordinateConverter {\r
-       @SuppressWarnings("unused")\r
-       private static final Logger logger = Logger.getLogger(CoordinateConverter.class);\r
-\r
-    //Patterns\r
-    private List<CoordinatePattern> patterns;\r
-\r
-\r
-    private class CoordinatePattern{\r
-       String description;\r
-       String pattern;\r
-    }\r
-    \r
-    \r
-    private Comparator<CustomHemisphereIndicator> lengthComparator = new Comparator<CustomHemisphereIndicator>(){\r
-               public int compare(CustomHemisphereIndicator ind1, CustomHemisphereIndicator ind2) {\r
-                       return Integer.valueOf(ind1.getLength()).compareTo(ind2.getLength());\r
-               }\r
-    };\r
-    \r
-    //Class constructor\r
-    public CoordinateConverter() {\r
-        //initialise pattern array\r
-        patterns = new ArrayList<CoordinatePattern>();\r
-\r
-        //temp pattern variable\r
-        CoordinatePattern pattern;\r
-\r
-\r
-        //variations of DD.DDD with white space characters\r
-        pattern = new CoordinatePattern();\r
-        pattern.description = "Variation of DD.DDD";\r
-        pattern.pattern =\r
-            //+/-/Nn/Ss/Ww/EeDD.DDDD\r
-            "(^" +\r
-            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +\r
-            "((\\d{1,3}(\\.|\\,)?(\\s)*$)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*$))" +\r
-            ")" +\r
-            ////DD.DDDDNn/Ss/Ww/Ee\r
-            "|(^" +\r
-            "(\\s)*((\\d{1,3}(\\.|\\,)?(\\s)*)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*))" +\r
-            "(W|w|E|e|N|n|S|s)?(\\s)*$" +\r
-            ")";\r
-        patterns.add(pattern);\r
-\r
-\r
-        //Variations of DD(\u00B0|d)MM.MMM' with whitespace characters\r
-        pattern = new CoordinatePattern();\r
-        pattern.description = "Variation of DD(\u00B0|d)MM.MMM('|m)";\r
-        pattern.pattern =\r
-            "(^" +\r
-            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +\r
-            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)?(\u02B9|'|M|m)?$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*(\u02B9|'|M|m)?(\\s)*$))" +\r
-            ")" +\r
-            "|(^" +\r
-            "(\\s)*((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)?(\u02B9|'|M|m)?)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*(\u02B9|'|M|m)?(\\s)*))" +\r
-            "(W|w|E|e|N|n|S|s)?(\\s)*$" +\r
-            ")";\r
-        patterns.add(pattern);\r
-\r
-\r
-        //Variations of DD\u00B0MM'SS.SSS" with whitespace characters\r
-        pattern = new CoordinatePattern();\r
-        pattern.description = "Variation of DD(\u00B0|d)MM(\u02B9|m)SS.SSS(\u02BA|s)";\r
-        pattern.pattern =\r
-            //+/-/Nn/Ss/Ww/EeDD\u00B0MM\u02B9SS.SSS\r
-            "(^" +\r
-            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +\r
-            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)" +\r
-            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)?(\\s)*$)" +\r
-            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*(\u02BA|\"|''|S|s)?(\\s)*$)" +\r
-            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*(\u02BA|\"|''|S|s)?(\\s)*$))" +\r
-            ")" +\r
-            //DD°MM\u02B9SS.SSSNn/Ss/Ww/Ee\r
-            "|(^(\\s)*" +\r
-            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|" +\r
-            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)?(\\s)*)|" +\r
-            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*(\u02BA|\"|''|S|s)?(\\s)*)|" +\r
-            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*(\u02BA|\"|''|S|s)?(\\s)*))" +\r
-            "(W|w|E|e|N|n|S|s)?(\\s)*$" +\r
-            ")";\r
-        patterns.add(pattern);\r
-\r
-\r
-        //Variations of DD:MM:SS.SSS with whitespace characters\r
-        pattern = new CoordinatePattern();\r
-        pattern.description = "Variation of DD:MM:SS.SSS";\r
-        pattern.pattern =\r
-            // +/-/Nn/Ss/Ww/EeDD:MM:SS.SSS\r
-            "(^" +\r
-            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +\r
-            "((\\d{1,3}(\\s)*\\:?(\\s)*$)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:?(\\s)*$)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*$)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*$))" +\r
-            ")" +\r
-            //DD:MM:SS.SSSNn/Ss/Ww/Ee\r
-            "|(^" +\r
-            "(\\s)*((\\d{1,3}(\\s)*\\:?(\\s)*)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:?(\\s)*)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*))" +\r
-            "(W|w|E|e|N|n|S|s)?(\\s)*$" +\r
-            ")";\r
-        patterns.add(pattern);\r
-\r
-    }\r
-\r
-\r
-    //tests if a string matches one of the defined patterns\r
-    private int matchPattern(String str){\r
-        int recognised = -1;\r
-\r
-        //match the string against each available patern\r
-        for (int i = 0; i < patterns.size(); i++){\r
-\r
-               CoordinatePattern pattern = patterns.get(i);\r
-               Pattern regEx = Pattern.compile(pattern.pattern);\r
-               if (regEx.matcher(str).find()) { \r
-                       recognised = i; \r
-               break; \r
-            }\r
-\r
-        }\r
-        return recognised;\r
-    }\r
-\r
-\r
-    //gets sign of the coordinate (tests for presence of negative sign)\r
-    private int getSign(String str){\r
-\r
-        //This regex checks for the negative hemisphere indicator\r
-        Pattern regexNegative = Pattern.compile("(-|S|s|W|w)");\r
-\r
-        //This regex checks if there weren't any other hemisphere indicators\r
-        //it is needed for the specific case of the DDdMMmSSs S\r
-        //so it needs to be ensured there where no positive indicators\r
-        Pattern regexPositive = Pattern.compile("(\\+|N|n|E|e)");\r
-\r
-        //if a positive indicator is found no need to search further\r
-        if (regexPositive.matcher(str).find()){\r
-            return 1;\r
-        }else{\r
-            //if not check whether there was a negative indicator. if so negate otherwise return positive\r
-            if (regexNegative.matcher(str).find()){\r
-                return -1;\r
-            }else{\r
-                return 1;\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    //this checks for the coordinate sign by evaluating user supplied data\r
-    private int getCustomSign(String str){\r
-        //Note:\r
-        //Indicators are evaluated from the longest ones to the shortes ones\r
-        //So when searching for "P" does not affect "PN" as "PN" is evaluated earlier\r
-\r
-        \r
-        //search for the presence of indicators\r
-        boolean hasPositive = false;\r
-        boolean hasNegative = false;\r
-\r
-        //keep previous negative indicators here\r
-        List<String> previousNegatives = new ArrayList<String>();\r
-        \r
-        //compare the string with user supplied custom pattern\r
-        for (int x = customPtrn.hemisphereIndicators.size() - 1; x >= 0; x--){\r
-\r
-            CustomHemisphereIndicator ind = customPtrn.hemisphereIndicators.get(x);\r
-\r
-            //test here if the indicator exists (has length >0)\r
-            if (ind.getLength() > 0){\r
-\r
-                //check if the supplied pattern was marked as case insensitive?\r
-                String caseInsensitive = "";\r
-\r
-                if (customPtrn.caseInsensitive){\r
-                    caseInsensitive = "(?i)";\r
-                }\r
-\r
-                //create a regex\r
-                Pattern tempRegex = Pattern.compile(caseInsensitive + ind.getIndicator());\r
-\r
-                //if a pattern is found\r
-                if (tempRegex.matcher(str).find()){\r
-                    //check whether it's a positive or negative indicator\r
-                    if (ind.getPositive()){\r
-                        /* Note:\r
-                         * See the note below to understand why checking for previous negatives is performed here\r
-                        */\r
-                        \r
-                        //check the previous negatives\r
-                        if (previousNegatives.size() != 0){\r
-                            boolean sameNegative = false;\r
-\r
-                            for (int i = previousNegatives.size() - 1; i >= 0; i--){\r
-                                if (ind.getIndicator() == previousNegatives.get(i)){\r
-                                    sameNegative = true;\r
-                                    break;\r
-                                }\r
-                            }\r
-\r
-                            //mark as positive only if the previously found negative is the same\r
-                            if (sameNegative){\r
-                                hasPositive = true;\r
-                            }\r
-\r
-                        }else{ //if no negatives before it already marks the sign as positive \r
-                            hasPositive = true;\r
-                        }\r
-\r
-                    } else {\r
-                        /* Note:\r
-                         * save the negative indicator here so it can be compared later if a positive wants to overwrite it!\r
-                         * in a case a longer negative "Pn" has already been found a shorter positive "P" will not overwrite it\r
-                         * and the hasPositive will remain false;\r
-                         * In a case a "P" negative indicator has already been found, positive will mark hasPositive and therefore\r
-                         * later a default positive value will be returned (if the indicators for positive & negative are the same\r
-                         * positive is returned)\r
-                         * testing for previous positives is not required since if a hasPositive is already true method will return\r
-                         * true anyway\r
-                         * \r
-                        */\r
-                        previousNegatives.add(ind.getIndicator());\r
-                        \r
-                        hasNegative = true;\r
-\r
-                    }\r
-                }\r
-            }\r
-            \r
-        }\r
-\r
-        //Note:\r
-        //positive indicator has priority here - if both indicators supplied by the user are the same, a positive is chosen\r
-        //if there were no indicator found in the tested coordinate, a positive value is returned by default\r
-\r
-        if (hasPositive){\r
-            return 1;\r
-        } else {\r
-            if (hasNegative) {\r
-                return -1;\r
-            } else {\r
-                return 1;\r
-            }\r
-        }\r
-    }\r
-\r
-      \r
-\r
-    //returns a currently used decimal separator\r
-    private String getDecimalSeparator(){\r
-       //TODO not yet transformed from C#\r
-//     return System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;\r
-        return ".";\r
-    }\r
-\r
-\r
-    //replaces comma or dot for current decimal separator\r
-    private String fixDecimalSeparator(String str){\r
-        //Note:\r
-        //Coma is replaced as parsers often recognise dot as a decimal separator\r
-        //Comma or dot is replaced with a decimal separator here (environment settings)\r
-        //But decimal separator has to be used later too;\r
-\r
-        String regExReplaceComma = "(\\,|\\.)";\r
-        str = str.replaceAll(regExReplaceComma, getDecimalSeparator());\r
-        \r
-        return str;\r
-    }\r
-\r
-\r
-    //removes sign\r
-    private String removeSign(String str){\r
-        String regExRemoveSign = "(\\+|-|S|s|W|w|N|n|E|e)";\r
-        str = str.replaceAll(regExRemoveSign, "");\r
-        return str;\r
-    }\r
-\r
-    //removes custom sign indicators\r
-    private String removeCustomPatternParts(String str){\r
-\r
-        /* Note:\r
-         * Symbols are added here so the removing tries to not affect the coordinate too much\r
-         * Strings to be removed then are evaluated from the longest ones to the shortes ones\r
-         * So when searching for "P" does not affect "PN" as "PN" is evaluated earlier\r
-         * */\r
-\r
-        //CustomHemisphereIndicator is used here so another object does not have to be created\r
-        //only for the string cleanning\r
-        List<CustomHemisphereIndicator> stringsToRemove = customPtrn.hemisphereIndicators;\r
-\r
-        //add degree symbol\r
-        CustomHemisphereIndicator stringToRemove = new CustomHemisphereIndicator("Degree", customPtrn.degreeSymbol,customPtrn.degreeSymbol.length(), false);\r
-        stringsToRemove.add(stringToRemove);\r
-\r
-        //add minute symbol\r
-        stringToRemove = new CustomHemisphereIndicator("Minute", customPtrn.minuteSymbol, customPtrn.minuteSymbol.length(), false);\r
-        stringsToRemove.add(stringToRemove);\r
-\r
-        //add second symbol\r
-        stringToRemove = new CustomHemisphereIndicator("Second", customPtrn.secondSymbol, customPtrn.secondSymbol.length(), false);\r
-        stringsToRemove.add(stringToRemove);\r
-\r
-        //sort the list (by element's Length property)\r
-        Collections.sort(stringsToRemove, lengthComparator);\r
-        \r
-        \r
-//        ListSelectionEv.sort(lengthComparator);\r
-\r
-\r
-        for (int x = stringsToRemove.size() - 1; x >= 0; x--){\r
-\r
-            CustomHemisphereIndicator toBeRemoved = stringsToRemove.get(x);\r
-\r
-            //check if the string exists so replacing does not yield errors\r
-            if (toBeRemoved.getLength() > 0)\r
-            {\r
-                //check if the supplied pattern was marked as case insensitive?\r
-                String CaseInsensitive = "";\r
-\r
-                if (customPtrn.caseInsensitive){\r
-                    CaseInsensitive = "(?i)";\r
-                }\r
-\r
-                //create regex for replacing\r
-                String tempRegex = CaseInsensitive + toBeRemoved.getIndicator();\r
-\r
-\r
-                if (toBeRemoved.getName().equals("Degree") || toBeRemoved.getName().equals("Minute")) {\r
-                    //replace with a symbol used later for splitting\r
-                    str =  str.replaceAll(tempRegex, ":");\r
-                } else {\r
-                    //remove the string\r
-                    str = str.replaceAll(tempRegex, "");\r
-                }\r
-            }\r
-        }\r
-        return str;\r
-    }\r
-\r
-\r
-\r
-    //removes whitespace characters\r
-    private String removeWhiteSpace(String str){\r
-        str = str.replaceFirst("\\s+", "");\r
-        return str;\r
-    }\r
-\r
-\r
-    //Object for the conversion results\r
-    public class ConversionResults{\r
-        public boolean patternRecognised;\r
-        public String patternMatched;\r
-        public String patternType;\r
-\r
-        public boolean conversionSuccessful;\r
-        public double convertedCoord;\r
-        public boolean canBeLat;\r
-        \r
-        public String conversionComments;\r
-        \r
-        public Boolean isLongitude;\r
-\r
-        public int dd;\r
-        public int mm;\r
-        public double mmm;\r
-        public int ss;\r
-        public double sss;\r
-\r
-    }\r
-\r
-\r
-    public ConversionResults tryConvert(String str){\r
-        //some local variables \r
-        int sign; //sign of the coordinate\r
-        String[] decimalBit, ddmmss, ddmm; //arrays for splitting\r
-        double dd = 0, mm = 0, ss = 0, mmm = 0, sss = 0, dec = 0; //parts of the coordinates\r
-        \r
-        String decSeparatorRaw = String.valueOf(getDecimalSeparator()); //gets the current decimal separator\r
-        String decSeparatorRegEx = decSeparatorRaw.replace(".", "\\.");\r
-        \r
-        ConversionResults results = new ConversionResults();\r
-\r
-        //Get the matched pattern\r
-        CoordinatePattern pattern;\r
-        int ptrnnum = matchPattern(str);\r
-        if (ptrnnum != -1) {\r
-            pattern = patterns.get(ptrnnum);\r
-        } else {\r
-            pattern = new CoordinatePattern();\r
-            pattern.description = "Unknown";\r
-            pattern.pattern = "No pattern matched";\r
-        }\r
-\r
-\r
-\r
-        if (pattern.description.equals("Variation of DD.DDD")){\r
-            \r
-                       //Sets pattern machted, successful, pattern type and pattern info\r
-               initializeResult(results, pattern);\r
-\r
-            //get sign\r
-            sign = getSign(str);\r
-            results.isLongitude = getIsLongitude(str);\r
-            \r
-            //Replace comma or dot with a current decimal separator\r
-            str = fixDecimalSeparator(str);\r
-\r
-            //Remove all the unwanted stuff\r
-            str = removeSign(str);\r
-            str = removeWhiteSpace(str);\r
-\r
-            //Since this is already a decimal degree no spliting is needed\r
-            dd =  Double.valueOf(str);\r
-\r
-            checkDegreeRange(dd, results);\r
-            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);\r
-\r
-        }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM.MMM('|m)")){\r
-\r
-               //Sets pattern machted, successful, pattern type and pattern info\r
-               initializeResult(results, pattern);\r
-\r
-            //get sign\r
-            sign = getSign(str);\r
-            results.isLongitude = getIsLongitude(str);\r
-            \r
-            //Replace comma or dot with a current decimal separator\r
-            str = fixDecimalSeparator(str);\r
-\r
-            //Remove all the unwanted stuff\r
-            str = removeSign(str);\r
-            str = removeWhiteSpace(str);\r
-\r
-            //do some further replacing\r
-            //Replace degree symbol\r
-            str = str.replaceAll("(\u00B0|\u00BA|D|d)", ":");\r
-            \r
-            //remove minute symbol\r
-            str = str.replaceAll("(\u02B9|'|M|m)", "");\r
-            \r
-            //Extract decimal part\r
-            decimalBit = str.split(decSeparatorRegEx);\r
-            \r
-            //split degrees and minutes\r
-            ddmm = decimalBit[0].split(":");\r
-\r
-\r
-            //extract values from the strings\r
-            dd = Integer.valueOf(ddmm[0]); //Degrees\r
-\r
-            if (ddmm.length > 1){ //Minutes\r
-                //check if the string is not empty\r
-                if (ddmm[1] != "") { \r
-                       mm = Integer.valueOf(ddmm[1]); \r
-                }\r
-            }\r
-\r
-            if (decimalBit.length > 1){//DecimalSeconds\r
-                //check if the string is not empty\r
-                if (decimalBit[1] != "") {\r
-                    mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));\r
-                }\r
-            }\r
-\r
-            checkDegreeRange(dd, results);\r
-            checkMinuteRange(mm, results);\r
-            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);\r
-            \r
-        }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM(\u02B9|m)SS.SSS(\u02BA|s)")){\r
-               \r
-               /* \r
-             * Note:\r
-             * This pattern allows the seconds to be specified with S, s or " or nothing at all\r
-             * If the seconds are marked with "s" and there is no other indication of the hemisphere\r
-             * the coordinate will be parsed as southern (negative).\r
-             * \r
-             * If the N / E / W / + indicator is found the coordinate will be parsed appropriately no matter\r
-             * what is the second notation\r
-            */\r
-\r
-               //Sets pattern machted, successful, pattern type and pattern info\r
-               initializeResult(results, pattern);\r
-\r
-            //get sign\r
-            sign = getSign(str);\r
-            //TODO test S\r
-            results.isLongitude = getIsLongitude(str);\r
-            \r
-            //Replace comma or dot with a current decimal separator\r
-            str = fixDecimalSeparator(str);\r
-\r
-            //Remove all the unwanted stuff\r
-            str = removeSign(str);\r
-            str = removeWhiteSpace(str);\r
-\r
-            //remove second symbol (s is removed by the get sign method)\r
-            //double apostrophe is not removed here as single apostrphe may mark minutes!\r
-            //it's taken care of later after extracting the decimal part\r
-            str = str.replaceAll("(\u02BA|\")", "");\r
-            \r
-            //do some further replacing\r
-            //Replace degree symbol\r
-            str = str.replaceAll("(\u00B0|\u00B0|D|d|\u02B9|'|M|m)",":");\r
-\r
-            //Extract decimal part\r
-            decimalBit = str.split(decSeparatorRegEx);\r
-\r
-            //remove : from the decimal part [1]! This is needed when a double apostrophe was used to mark seconds\r
-            if (decimalBit.length > 1)\r
-            {\r
-                decimalBit[1].replace(":", "");\r
-            }\r
-\r
-            //split degrees and minutes\r
-            ddmmss = decimalBit[0].split(":");\r
-\r
-\r
-            //extract values from the strings\r
-            dd = Integer.valueOf(ddmmss[0]); //Degrees\r
-            if (ddmmss.length > 1){//Minutes\r
-                //check if the string is not empty\r
-                if (ddmmss[1] != "") { \r
-                       mm = Integer.valueOf(ddmmss[1]); \r
-                }\r
-            }\r
-            if (ddmmss.length > 2){//Seconds\r
-                //check if the string is not empty\r
-                if (ddmmss[2] != "") { \r
-                       ss = Integer.valueOf(ddmmss[2]); \r
-                }\r
-            }\r
-            if (decimalBit.length > 1) { //DecimalSeconds\r
-                //check if the string is not empty\r
-                if (decimalBit[1] != "") {\r
-                    sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));\r
-                }\r
-            }\r
-\r
-            checkDegreeRange(dd, results);\r
-            checkMinuteRange(mm, results);\r
-            checkSecondRange(ss, results);\r
-\r
-            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);\r
-\r
-        }else if (pattern.description.equals("Variation of DD:MM:SS.SSS")){\r
-\r
-               //Sets pattern machted, successful, pattern type and pattern info\r
-               initializeResult(results, pattern);\r
-\r
-            //get sign\r
-            sign = getSign(str);\r
-            results.isLongitude = getIsLongitude(str);\r
-            \r
-            //Replace comma or dot with a current decimal separator\r
-            str = fixDecimalSeparator(str);\r
-\r
-            //Remove all the unwanted stuff\r
-            str = removeSign(str);\r
-            str = removeWhiteSpace(str);\r
-\r
-            //Do some splitting\r
-            decimalBit = str.split(decSeparatorRegEx);\r
-            ddmmss = decimalBit[0].split(":");\r
-\r
-\r
-            //extract values from the strings\r
-            dd = Integer.valueOf(ddmmss[0]); //Degrees\r
-            if (ddmmss.length > 1)//Minutes\r
-            {\r
-                //check if the string is not empty\r
-                if (ddmmss[1] != "") { mm = Integer.valueOf(ddmmss[1]); }\r
-            }\r
-            if (ddmmss.length > 2) {//Seconds{\r
-                //check if the string is not empty\r
-                if (ddmmss[2] != "") { \r
-                       ss = Integer.valueOf(ddmmss[2]); \r
-                }\r
-            }\r
-            if (decimalBit.length > 1) { //DecimalSeconds\r
-                //check if the string is not empty\r
-                if (decimalBit[1] != "") {\r
-                    sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));\r
-                }\r
-            }\r
-\r
-            checkDegreeRange(dd, results);\r
-            checkMinuteRange(mm, results);\r
-            checkSecondRange(ss, results);\r
-\r
-            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);\r
-\r
-        }else if (pattern.description.equals("Custom variation of DD.DDD")){\r
-\r
-               //Sets pattern machted, successful, pattern type and pattern info\r
-               initializeResult(results, pattern);\r
-       \r
-       \r
-               //get sign\r
-               sign = getCustomSign(str);\r
-              \r
-               //TODO still needs to be adapted to custom pattern\r
-               results.isLongitude = getIsLongitude(str);\r
-                  \r
-       \r
-               //Remove all the unwanted stuff\r
-               //Note: This method also replaces the symbols with ":"\r
-               //Note: In certain cases it may make the coord unparsable\r
-               str = removeCustomPatternParts(str);\r
-       \r
-               str = removeWhiteSpace(str);\r
-       \r
-               //Replace comma or dot with a current decimal separator\r
-               str = fixDecimalSeparator(str);\r
-       \r
-               //remove the ":" here as it is not needed here for decimal degrees\r
-               str = str.replace(":", "");\r
-       \r
-               try {\r
-                   //Since this is already a decimal degree no spliting is needed\r
-                   dd = Double.valueOf(str);\r
-               } catch (Exception e)  {\r
-                   results.conversionSuccessful = false;\r
-                   results.convertedCoord = 99999; //this is to mark an error...\r
-                   results.conversionComments =\r
-                       "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +\r
-                       "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " + \r
-                       "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."\r
-                       ;\r
-       \r
-                   //exit method\r
-                   return results;\r
-               }\r
-       \r
-               //Since this is already a decimal degree no spliting is needed\r
-               dd = Double.valueOf(str);\r
-       \r
-               checkDegreeRange(dd, results);\r
-               doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);\r
-               \r
-       \r
-           }else if (pattern.description.equals("Custom variation of DD:MM.MMM")){\r
-           //-------------Customs patterns start here-------------\r
-\r
-               //Sets pattern machted, successful, pattern type and pattern info\r
-               initializeResult(results, pattern);\r
-\r
-            //get sign\r
-            sign = getCustomSign(str);\r
-          \r
-            //TODO still needs to be adapted to custom pattern\r
-               results.isLongitude = getIsLongitude(str);\r
-               \r
-\r
-\r
-            //Remove all the unwanted stuff\r
-            //Note: This method also replaces the symbols with ":"\r
-            //Note: In certain cases it may make the coord unparsable\r
-            str = removeCustomPatternParts(str);\r
-\r
-            str = removeWhiteSpace(str);\r
-\r
-            //Replace comma or dot with a current decimal separator\r
-            str = fixDecimalSeparator(str);\r
-\r
-            \r
-            //Extract decimal part\r
-            decimalBit = str.split(decSeparatorRegEx);\r
-\r
-            //split degrees and minutes\r
-            ddmm = decimalBit[0].split(":");\r
-\r
-\r
-            try {\r
-                //extract values from the strings\r
-                dd = Integer.valueOf(ddmm[0]); //Degrees\r
-\r
-                if (ddmm.length > 1){//Minutes\r
-                    //check if the string is not empty\r
-                    if (ddmm[1] != "") { mm = Integer.valueOf(ddmm[1]); }\r
-                }\r
-\r
-                if (decimalBit.length > 1){//DecimalSeconds\r
-                    //check if the string is not empty\r
-                    if (decimalBit[1] != ""){\r
-                        //replace the ":" if any (may be here as a result of custom symbol replacement\r
-                        decimalBit[1] = decimalBit[1].replace(":", "");\r
-\r
-                        mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));\r
-                    }\r
-                }\r
-            } catch (Exception e){\r
-                results.conversionSuccessful = false;\r
-                results.convertedCoord = 99999; //this is to mark an error...\r
-                results.conversionComments =\r
-                    "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +\r
-                    "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +\r
-                    "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."\r
-                    ;\r
-\r
-                //exit method\r
-                return results;\r
-            }\r
-\r
-\r
-            checkDegreeRange(dd, results);\r
-            checkMinuteRange(mm, results);\r
-            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);\r
-\r
-           } else if (pattern.description.equals("Custom variation of DD:MM:SS.SSS")){\r
-\r
-               //Sets pattern machted, successful, pattern type and pattern info\r
-               initializeResult(results, pattern);\r
-\r
-\r
-            //get sign\r
-            sign = getCustomSign(str);\r
-\r
-            //TODO still needs to be adapted to custom pattern\r
-               results.isLongitude = getIsLongitude(str);\r
-               \r
-\r
-            //Remove all the unwanted stuff\r
-            //Note: This method also replaces the symbols with ":"\r
-            //Note: In certain cases it may make the coord unparsable\r
-            str = removeCustomPatternParts(str);\r
-\r
-            str = removeWhiteSpace(str);\r
-\r
-            //Replace comma or dot with a current decimal separator\r
-            str = fixDecimalSeparator(str);\r
-\r
-\r
-            //Extract decimal part\r
-            decimalBit = str.split(decSeparatorRegEx);\r
-\r
-            //split degrees and minutes\r
-            ddmmss = decimalBit[0].split(":");\r
-\r
-\r
-            try {\r
-\r
-                //extract values from the strings\r
-                dd = Integer.valueOf(ddmmss[0]); //Degrees\r
-                if (ddmmss.length > 1) {//Minutes\r
-                    //check if the string is not empty\r
-                    if (ddmmss[1] != "") { \r
-                       mm = Integer.valueOf(ddmmss[1]); \r
-                    }\r
-                }\r
-                if (ddmmss.length > 2){ //Seconds\r
-                    //check if the string is not empty\r
-                    if (ddmmss[2] != "") { \r
-                       ss = Integer.valueOf(ddmmss[2]); \r
-                    }\r
-                }\r
-                if (decimalBit.length > 1){ //DecimalSeconds\r
-                    //check if the string is not empty\r
-                    if (decimalBit[1] != "") {\r
-                        sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));\r
-                    }\r
-                }\r
-            } catch (Exception e) {\r
-                results.conversionSuccessful = false;\r
-                results.convertedCoord = 99999; //this is to mark an error...\r
-                results.conversionComments =\r
-                    "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +\r
-                    "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +\r
-                    "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."\r
-                    ;\r
-\r
-                //exit method\r
-                return results;\r
-            }\r
-\r
-\r
-            checkDegreeRange(dd, results);\r
-            checkMinuteRange(mm, results);\r
-            checkSecondRange(ss, results);\r
-\r
-            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);\r
-\r
-           }else {   //default  : pattern not recognized\r
-            results.patternRecognised = false;\r
-            results.patternType = pattern.description;\r
-            results.patternMatched = pattern.pattern;\r
-\r
-            results.conversionSuccessful = false;\r
-            results.convertedCoord = 99999; //this is to mark an error...\r
-\r
-            results.conversionComments = "Coordinate pattern not recognised!";\r
-\r
-           }\r
-        \r
-        //do the self check here\r
-        results = selfTest(results);\r
-\r
-        //return conversion results\r
-        return results;\r
-    }\r
-\r
-\r
-       /**\r
-        * @param sign\r
-        * @param dd\r
-        * @param mm\r
-        * @param ss\r
-        * @param sss\r
-        * @param results\r
-        */\r
-       private void doConvertWithCheck(int sign, double dd, double mm, double mmm, double ss, double sss, ConversionResults results) {\r
-               double dec;\r
-               //Do the conversion if everything ok\r
-               if (results.conversionSuccessful){\r
-                   results.conversionComments = "Conversion successful.";\r
-\r
-                   dec = sign * (dd + (mm + mmm) / 60 + (ss + sss) / 3600);\r
-\r
-                   //one more check to ensure a coord does not exceed 180\r
-                   if (dec > 180 | dec < -180){\r
-                       results.conversionSuccessful = false;\r
-                       results.convertedCoord = 99999; //this is to mark an error...\r
-                       results.conversionComments += "Coordinate is either > 180 or < -180; ";\r
-                   } else {\r
-                       results.convertedCoord = dec;\r
-\r
-                       results.conversionComments = "Conversion successful.";\r
-\r
-                       //Check whether the coordinate exceeds +/- 90 and mark it in comments\r
-                       \r
-                       if (dec <= 90 && dec >= -90 && (results.isLongitude == null || results.isLongitude == false) ) { \r
-                       results.canBeLat = true; \r
-                }else{\r
-                       results.isLongitude = true;\r
-                }\r
-                   }\r
-               }\r
-       }\r
-\r
-\r
-       /**\r
-        * @param ss\r
-        * @param results\r
-        */\r
-       private void checkSecondRange(double ss, ConversionResults results) {\r
-               if (ss > 59) {//seconds\r
-                   results.conversionSuccessful = false;\r
-                   results.convertedCoord = 99999; //this is to mark an error...\r
-                   results.conversionComments += "Seconds fall outside the range: MM >= 60; ";\r
-               }\r
-       }\r
-\r
-\r
-       /**\r
-        * @param mm\r
-        * @param results\r
-        */\r
-       private void checkMinuteRange(double mm, ConversionResults results) {\r
-               if (mm > 59) {//minutes\r
-                   results.conversionSuccessful = false;\r
-                   results.convertedCoord = 99999; //this is to mark an error...\r
-                   results.conversionComments += "Minutes fall outside the range: MM > 59; ";\r
-               }\r
-       }\r
-\r
-\r
-       /**\r
-        * @param dd\r
-        * @param results\r
-        */\r
-       private void checkDegreeRange(double dd, ConversionResults results) {\r
-               //do some additional checking if the coords fall into the range\r
-               if (dd < -180 | dd > 180){  //degree may require another param specifying whether it's lat or lon...\r
-                   results.conversionSuccessful = false;\r
-                   results.convertedCoord = 99999; //this is to mark an error...\r
-                   results.conversionComments += "Degrees fall outside the range: DD < -180 | DD > 180; ";\r
-               }\r
-       }\r
-\r
-\r
-       /**\r
-        * @param str\r
-        * @return\r
-        */\r
-       private Boolean getIsLongitude(String str) {\r
-           //This regex checks for the negative hemisphere indicator\r
-               Pattern regexLatitudeNonAmbigous = Pattern.compile("(N|n)");\r
-               Pattern regexLatitudeAmbigous = Pattern.compile("(S|s)");\r
-\r
-        //This regex checks if there weren't any other hemisphere indicators\r
-        //it is needed for the specific case of the DDdMMmSSs S\r
-        //so it needs to be ensured there where no positive indicators\r
-        Pattern regexLongitude = Pattern.compile("(W|w|E|e)");\r
-\r
-        //if a positive indicator is found no need to search further\r
-        if (regexLongitude.matcher(str).find()){\r
-            return true;\r
-        }else if (regexLatitudeNonAmbigous.matcher(str).find()){\r
-               return false;\r
-        }else if (regexLatitudeAmbigous.matcher(str).find()){\r
-               Pattern regexLiteralUnits = Pattern.compile("(D|d|M|m)");\r
-               \r
-               //if there are no other literal units we assume that S is a\r
-               //direction and not a second indicator\r
-            if (! regexLiteralUnits.matcher(str).find()){\r
-                return false;\r
-            }else if (regexLatitudeAmbigous.matcher(str).groupCount() > 1){\r
-               return false;\r
-            }else{\r
-               return null;\r
-            }\r
-        }else{\r
-               return null;\r
-        }\r
-       }\r
-\r
-\r
-       /**\r
-        * Sets pattern machted, successful, pattern type and pattern info\r
-        * @param results\r
-        * @param pattern\r
-        */\r
-       private void initializeResult(ConversionResults results,\r
-                       CoordinatePattern pattern) {\r
-               //Pattern matched\r
-               results.patternRecognised = true;\r
-\r
-               //Matching pattern succeeded so intialy the parsing is ok\r
-               results.conversionSuccessful = true;\r
-\r
-               //pattern info\r
-               results.patternType = pattern.description;\r
-               results.patternMatched = pattern.pattern;\r
-       }\r
-\r
-\r
-    private ConversionResults selfTest(ConversionResults results){\r
-\r
-        ConversionResults newresults = results;\r
-\r
-        if (results.conversionSuccessful != false){\r
-            int sign = 1;\r
-            if (Math.signum(results.convertedCoord) < 0) { \r
-               sign = -1; \r
-            }\r
-            \r
-            double decimalDegrees = sign * results.convertedCoord;\r
-            int fullDegrees;\r
-\r
-            double decimalMinutes;\r
-            int fullMinutes;\r
-\r
-            double decimalSeconds;\r
-            int fullSeconds;\r
-\r
-            //Get full degrees\r
-            fullDegrees = (int)Math.floor(decimalDegrees);\r
-\r
-            //get minutes\r
-            decimalMinutes = (decimalDegrees - fullDegrees) * 60;\r
-            fullMinutes = (int)Math.floor(decimalMinutes);\r
-\r
-            decimalSeconds = (decimalMinutes - fullMinutes) * 60;\r
-            fullSeconds = (int)Math.floor(decimalSeconds);\r
-\r
-            //save the test results\r
-            newresults.dd = fullDegrees;\r
-            newresults.mm = fullMinutes;\r
-            newresults.mmm = decimalSeconds;\r
-            newresults.ss = fullSeconds;\r
-            newresults.sss = decimalSeconds;\r
-\r
-        }\r
-\r
-        return newresults;\r
-\r
-    }\r
-\r
-\r
-\r
-    //------------ CUSTOM PATTERN BUILDER--------------\r
-\r
-    public class CustomPatternIn {\r
-        public String north;\r
-        public String south;\r
-        public String east;\r
-        public String west;\r
-\r
-        public String degreeSymbol;\r
-        public String minuteSymbol;\r
-        public String secondSymbol;\r
-\r
-        public boolean caseInsensitive;\r
-        public boolean allowWhiteSpace;\r
-        public boolean priorityOverDefaultPatterns;\r
-        public boolean disableDefaultPatterns;\r
-\r
-    }\r
-\r
-    \r
-    private class CustomPattern{\r
-\r
-        public List<CustomHemisphereIndicator> hemisphereIndicators;\r
-\r
-        public String degreeSymbol;\r
-        public String minuteSymbol;\r
-        public String secondSymbol;\r
-\r
-        public boolean caseInsensitive;\r
-\r
-    }\r
-\r
-    //global variable to be used if a custom pattern is used\r
-    private CustomPattern customPtrn;\r
-\r
-    //escape some of the chars\r
-    private String escapeChars(String str){\r
-        // backslash - first so it is not messed when other escape chars are corrected for being used in a string\r
-        str = str.replace("\\", "\\\\");\r
-\r
-        //dot and comma\r
-        str = str.replace(".", "\\.");\r
-        str = str.replace(",", "\\,");\r
-\r
-        //brackets\r
-        str = str.replace("(", "\\(");\r
-        str = str.replace(")", "\\)");\r
-        str = str.replace("[", "\\[");\r
-        str = str.replace("]", "\\]");\r
-        str = str.replace("{", "\\{");\r
-        str = str.replace("}", "\\}");\r
-\r
-        //other replacements\r
-        str = str.replace("^", "\\^");\r
-        str = str.replace("$", "\\$");\r
-        str = str.replace("+", "\\+");\r
-        str = str.replace("*", "\\*");\r
-        str = str.replace("?", "\\?");\r
-        str = str.replace("|", "\\|");\r
-\r
-        return str;\r
-    }\r
-\r
-\r
-    //this implements sorting by using system.Icomparable - sorting is needed later when replacing\r
-    private class CustomHemisphereIndicator implements Comparable<CustomHemisphereIndicator> {\r
-        //private variables\r
-        private int m_length;\r
-        private String m_name;\r
-        private String m_indicator;\r
-        private boolean m_positive;\r
-\r
-        //constructor\r
-        public CustomHemisphereIndicator(String name, String indicator, int length, boolean positive){\r
-            this.m_name = name;\r
-            this.m_indicator = indicator;\r
-            this.m_length = length;\r
-            this.m_positive = positive;\r
-        }\r
-\r
-        //properties\r
-\r
-        public String getName(){\r
-               return this.m_name;\r
-        }\r
-        public void setName(String value){\r
-               this.m_name = value;\r
-        }\r
-\r
-        public String getIndicator(){\r
-            return this.m_indicator;\r
-        }\r
-        public void setIndicator(String value){\r
-            this.m_indicator = value;\r
-        }\r
-\r
-        public int getLength(){\r
-            return this.m_length;\r
-        }\r
-        public void setLength(int value){\r
-            this.m_length = value; \r
-        }\r
-\r
-\r
-        public boolean getPositive(){\r
-            return this.m_positive; \r
-        }\r
-        public void setPositive(boolean value){\r
-            this.m_positive = value;\r
-        }\r
-        \r
-        /* Less than zero if this instance is less than obj.\r
-         * Zero if this instance is equal to obj. \r
-         * Greater than zero if this instance is greater than obj. \r
-         * \r
-         * This method uses the predefined method Int32.CompareTo\r
-         * */\r
-\r
-        public int compareTo(CustomHemisphereIndicator ind){\r
-               \r
-               //no need to rewrite the code again, we have Integer.compareTo ready to use\r
-               return Integer.valueOf(this.getLength()).compareTo(Integer.valueOf(ind.getLength()));\r
-        }\r
-    }\r
-\r
-\r
-\r
-    //This adds custom pattern to a list of already predefined patterns\r
-    //useful for batch conversions - allows for totally mixed input data (predefined & custom)\r
-    public void addCustomPattern(CustomPatternIn patternIn){\r
-\r
-        //new custom pattern object - to pass the needed data farther\r
-        CustomPattern pattern = new CustomPattern();\r
-\r
-        //keep indicators for parsing\r
-        List<CustomHemisphereIndicator> indicators = new ArrayList<CustomHemisphereIndicator>();\r
-\r
-        //north\r
-        CustomHemisphereIndicator ind = new CustomHemisphereIndicator("North", patternIn.north, patternIn.north.length() ,true);\r
-        indicators.add(ind);\r
-        \r
-        //south\r
-        ind = new CustomHemisphereIndicator("South", patternIn.south, patternIn.south.length(), false);\r
-        indicators.add(ind);\r
-\r
-        //east\r
-        ind = new CustomHemisphereIndicator("East", patternIn.east, patternIn.east.length(), true);\r
-        indicators.add(ind);\r
-\r
-        //west\r
-        ind = new CustomHemisphereIndicator("West", patternIn.west, patternIn.west.length(), false);\r
-        indicators.add(ind);\r
-\r
-        //sort the arraylist\r
-        Collections.sort(indicators, lengthComparator);\r
-\r
-        \r
-        //add it to the pattern object\r
-        pattern.hemisphereIndicators = indicators; \r
-        \r
-        //case insensitive\r
-        pattern.caseInsensitive = patternIn.caseInsensitive;\r
-\r
-        //keep symbols for parsing\r
-        pattern.degreeSymbol = patternIn.degreeSymbol;\r
-        pattern.minuteSymbol = patternIn.minuteSymbol;\r
-        pattern.secondSymbol = patternIn.secondSymbol;\r
-\r
-\r
-        //save the data\r
-        customPtrn = pattern;\r
-        \r
-\r
-        //----------------build custom patterns----------------\r
-\r
-        //prepare hemisphere indicators\r
-        String north = escapeChars(patternIn.north);\r
-        String south = escapeChars(patternIn.south);\r
-        String east = escapeChars(patternIn.east);\r
-        String west = escapeChars(patternIn.west);\r
-\r
-        //prepare symbols\r
-        String degreesymbol = "";\r
-        if (patternIn.degreeSymbol != ""){\r
-            degreesymbol = "(" + escapeChars(patternIn.degreeSymbol) + ")?";\r
-        }\r
-\r
-        String minutesymbol = "";\r
-        if (patternIn.minuteSymbol != ""){\r
-            minutesymbol = "(" + escapeChars(patternIn.minuteSymbol) + ")?";\r
-        }\r
-\r
-        String secondsymbol = "";\r
-        if (escapeChars(patternIn.secondSymbol) != ""){\r
-            secondsymbol = "(" + escapeChars(patternIn.secondSymbol) + ")?";\r
-        }\r
-\r
-\r
-        //is the pattern to be case insensitive?\r
-        String CaseInsensitive = "";\r
-        if (patternIn.caseInsensitive){\r
-            CaseInsensitive = "(?i)";\r
-        }\r
-\r
-        //allow whitespace\r
-        String WhiteSpace = "";\r
-        if (patternIn.allowWhiteSpace == true){\r
-            WhiteSpace = "(\\s)*";\r
-        }\r
-\r
-        //hemisphere indicator\r
-        String HemisphereIndicator = "";\r
-\r
-        //add north if present\r
-        if (north == ""){\r
-            HemisphereIndicator += south;\r
-        }else{\r
-            HemisphereIndicator += north;\r
-            if (south != ""){\r
-                HemisphereIndicator += "|" + south;\r
-            }\r
-        }\r
-\r
-        //add east\r
-        if (north == "" & south == ""){\r
-            HemisphereIndicator += east;\r
-        } else {\r
-            if (east != ""){\r
-                HemisphereIndicator += "|" + east;\r
-            }\r
-        }\r
-\r
-        //add west\r
-        if (north == "" & south == "" & east == ""){\r
-            HemisphereIndicator += west;\r
-        } else {\r
-            if (west != "") {\r
-                HemisphereIndicator += "|" + west;\r
-            }\r
-        }\r
-\r
-        //add remaining bits if not empty\r
-        if (HemisphereIndicator != "") {\r
-            HemisphereIndicator = "(" + HemisphereIndicator + ")?";\r
-        }\r
-\r
-        List<CoordinatePattern> customPatterns = new ArrayList<CoordinatePattern>();\r
-\r
-        //create custom patterns based on the specified user's input\r
-        CoordinatePattern ptrn;\r
-\r
-        //Custom variation of DD.DDD\r
-        ptrn = new CoordinatePattern();\r
-        ptrn.description = "Custom variation of DD.DDD";\r
-        ptrn.pattern  = \r
-            CaseInsensitive + "(^" +\r
-            WhiteSpace + HemisphereIndicator + WhiteSpace +\r
-            "(" +\r
-            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + "$)" +\r
-            ")" +\r
-            "|(^" + WhiteSpace +\r
-            "(" +\r
-            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + ")" +\r
-            ")" +\r
-            HemisphereIndicator + WhiteSpace + "$" +\r
-            "))"\r
-            ;\r
-        customPatterns.add(ptrn);\r
-\r
-        //Custom variation of DD:MM.MMM\r
-        ptrn = new CoordinatePattern();\r
-        ptrn.description = "Custom variation of DD:MM.MMM";\r
-        ptrn.pattern = \r
-            CaseInsensitive + "(^" +\r
-            WhiteSpace + HemisphereIndicator + WhiteSpace +\r
-            "(" +\r
-            "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + minutesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + minutesymbol + WhiteSpace + "$)" +\r
-            ")" +\r
-            "|(^" + WhiteSpace +\r
-            "(" +\r
-            "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + minutesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + minutesymbol + WhiteSpace + ")" +\r
-            ")" +\r
-            HemisphereIndicator + WhiteSpace + "$" +\r
-            "))"\r
-            ;\r
-        customPatterns.add(ptrn);\r
-        \r
-        //Custom variation of DD:MM:SS.SSS\r
-        ptrn = new CoordinatePattern();\r
-        ptrn.description = "Custom variation of DD:MM:SS.SSS";\r
-        ptrn.pattern =\r
-            CaseInsensitive + "(^" +\r
-            WhiteSpace + HemisphereIndicator + WhiteSpace +\r
-            "(" +\r
-            "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + secondsymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + secondsymbol + WhiteSpace + "$)" +\r
-            ")" +\r
-            "|(^" + WhiteSpace +\r
-            "(" +\r
-            "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + secondsymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + secondsymbol + WhiteSpace + ")" +\r
-            ")" +\r
-            HemisphereIndicator + WhiteSpace + "$" +\r
-            "))"\r
-            ;\r
-        customPatterns.add(ptrn);\r
-        \r
-        //check if the default patterns are to be used\r
-        if (patternIn.disableDefaultPatterns) {\r
-            patterns = customPatterns;\r
-        } else { //if all patterns are to be used check which set has the matching priority\r
-        \r
-            //check if the custom patterns are to have priority over the default ones\r
-            if (patternIn.priorityOverDefaultPatterns){\r
-\r
-                //add default patterns to the custom patterns\r
-                for (int i = 0; i < patterns.size(); i++){\r
-                    customPatterns.add(patterns.get(i));\r
-                }\r
-\r
-                //swap array lists\r
-                patterns = customPatterns;\r
-\r
-            }else{\r
-                //add custom patterns to the default patterns\r
-                for (int i = 0; i < customPatterns.size(); i++){\r
-                    patterns.add(customPatterns.get(i));\r
-\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-}\r
+/**
+* Copyright (C) 2009 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*
+* This file is an Java adaption from the orginal CoordinateConverter written by Dominik Mikiewicz
+* @see www.cartomatic.pl
+* @see http://dev.e-taxonomy.eu/svn/trunk/geo/coordinateConverter/CoordinateConverter.cs
+* @see http://gis.miiz.waw.pl/webapps/coordinateconverter/
+*/
+package eu.etaxonomy.cdm.strategy.parser.location;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import eu.etaxonomy.cdm.common.CdmUtils;
+
+/**
+ * @author a.mueller
+ * @since 07.06.2010
+ */
+public class CoordinateConverter {
+
+       @SuppressWarnings("unused")
+    private static final Logger logger = LogManager.getLogger();
+
+    //Patterns
+    private List<CoordinatePattern> patterns;
+
+    private static String minuteUtf8 = "\u02B9|\u00B4|\u02CA|\u0301|\u0374|\u2019";
+    private static String secondUtf8 = "\u02BA|\u030B|\u2033|\u00B4\u00B4|\u201D|''";
+
+
+    private class CoordinatePattern{
+       String description;
+       String pattern;
+    }
+
+
+    private Comparator<CustomHemisphereIndicator> lengthComparator = new Comparator<CustomHemisphereIndicator>(){
+               @Override
+        public int compare(CustomHemisphereIndicator ind1, CustomHemisphereIndicator ind2) {
+                       return Integer.valueOf(ind1.getLength()).compareTo(ind2.getLength());
+               }
+    };
+
+    //Class constructor
+    public CoordinateConverter() {
+        //initialise pattern array
+        patterns = new ArrayList<CoordinatePattern>();
+
+        //temp pattern variable
+        CoordinatePattern pattern;
+
+
+        //variations of DD.DDD with white space characters
+        pattern = new CoordinatePattern();
+        pattern.description = "Variation of DD.DDD";
+        pattern.pattern =
+            //+/-/Nn/Ss/Ww/EeDD.DDDD
+            "(^" +
+            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
+            "((\\d{1,3}(\\.|\\,)?(\\s)*$)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*$))" +
+            ")" +
+            ////DD.DDDDNn/Ss/Ww/Ee
+            "|(^" +
+            "(\\s)*((\\d{1,3}(\\.|\\,)?(\\s)*)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*))" +
+            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
+            ")";
+        patterns.add(pattern);
+
+
+        //Variations of DD(\u00B0|d)MM.MMM' with whitespace characters
+        pattern = new CoordinatePattern();
+        pattern.description = "Variation of DD(\u00B0|d)MM.MMM('|m)";
+        pattern.pattern =
+            "(^" +
+            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
+            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)?("+ minuteUtf8 + "|'|M|m)?$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*$))" +
+            ")" +
+            "|(^" +
+            "(\\s)*((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)?("+ minuteUtf8 + "|'|M|m)?)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*))" +
+            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
+            ")";
+        patterns.add(pattern);
+
+
+        //Variations of DD\u00B0MM'SS.SSS" with whitespace characters
+        pattern = new CoordinatePattern();
+        pattern.description = "Variation of DD(\u00B0|d)MM("+ minuteUtf8 + "|m)SS.SSS("+secondUtf8+"|s)";
+        pattern.pattern =
+            //+/-/Nn/Ss/Ww/EeDD\u00B0MM"+ minuteUtf8 + "SS.SSS
+            "(^" +
+            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
+            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)" +
+            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*$)" +
+            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*("+secondUtf8+"|\"|''|S|s)?(\\s)*$)" +
+            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*("+secondUtf8+"|\"|''|S|s)?(\\s)*$))" +
+            ")" +
+            //DD°MM"+ minuteUtf8 + "SS.SSSNn/Ss/Ww/Ee
+            "|(^(\\s)*" +
+            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|" +
+            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*)|" +
+            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*("+secondUtf8+"|\"|''|S|s)?(\\s)*)|" +
+            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*("+secondUtf8+"|\"|''|S|s)?(\\s)*))" +
+            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
+            ")";
+        patterns.add(pattern);
+
+
+        //Variations of DD:MM:SS.SSS with whitespace characters
+        pattern = new CoordinatePattern();
+        pattern.description = "Variation of DD:MM:SS.SSS";
+        pattern.pattern =
+            // +/-/Nn/Ss/Ww/EeDD:MM:SS.SSS
+            "(^" +
+            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
+            "((\\d{1,3}(\\s)*\\:?(\\s)*$)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:?(\\s)*$)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*$)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*$))" +
+            ")" +
+            //DD:MM:SS.SSSNn/Ss/Ww/Ee
+            "|(^" +
+            "(\\s)*((\\d{1,3}(\\s)*\\:?(\\s)*)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:?(\\s)*)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*)|(\\d{1,3}(\\s)*\\:(\\s)*\\d{1,2}(\\s)*\\:(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*))" +
+            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
+            ")";
+        patterns.add(pattern);
+
+    }
+
+
+    /**
+     * tests if a string matches one of the defined patterns, returns -1 if no pattern matches
+     * @param str
+     * @return
+     */
+    private int matchPattern(String str){
+        int recognised = -1;
+
+        //match the string against each available pattern
+        for (int i = 0; i < patterns.size(); i++){
+
+               CoordinatePattern pattern = patterns.get(i);
+               Pattern regEx = Pattern.compile(pattern.pattern);
+               if (regEx.matcher(str).find()) {
+                       recognised = i;
+               break;
+            }
+
+        }
+        return recognised;
+    }
+
+
+    //gets sign of the coordinate (tests for presence of negative sign)
+    private int getSign(String str){
+
+        //This regex checks for the negative hemisphere indicator
+        Pattern regexNegative = Pattern.compile("(-|S|s|W|w)");
+
+        //This regex checks if there weren't any other hemisphere indicators
+        //it is needed for the specific case of the DDdMMmSSs S
+        //so it needs to be ensured there where no positive indicators
+        Pattern regexPositive = Pattern.compile("(\\+|N|n|E|e)");
+
+        //if a positive indicator is found no need to search further
+        if (regexPositive.matcher(str).find()){
+            return 1;
+        }else{
+            //if not check whether there was a negative indicator. if so negate otherwise return positive
+            if (regexNegative.matcher(str).find()){
+                return -1;
+            }else{
+                return 1;
+            }
+        }
+    }
+
+
+    //this checks for the coordinate sign by evaluating user supplied data
+    private int getCustomSign(String str){
+        //Note:
+        //Indicators are evaluated from the longest ones to the shortes ones
+        //So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
+
+
+        //search for the presence of indicators
+        boolean hasPositive = false;
+        boolean hasNegative = false;
+
+        //keep previous negative indicators here
+        List<String> previousNegatives = new ArrayList<String>();
+
+        //compare the string with user supplied custom pattern
+        for (int x = customPtrn.hemisphereIndicators.size() - 1; x >= 0; x--){
+
+            CustomHemisphereIndicator ind = customPtrn.hemisphereIndicators.get(x);
+
+            //test here if the indicator exists (has length >0)
+            if (ind.getLength() > 0){
+
+                //check if the supplied pattern was marked as case insensitive?
+                String caseInsensitive = "";
+
+                if (customPtrn.caseInsensitive){
+                    caseInsensitive = "(?i)";
+                }
+
+                //create a regex
+                Pattern tempRegex = Pattern.compile(caseInsensitive + ind.getIndicator());
+
+                //if a pattern is found
+                if (tempRegex.matcher(str).find()){
+                    //check whether it's a positive or negative indicator
+                    if (ind.getPositive()){
+                        /* Note:
+                         * See the note below to understand why checking for previous negatives is performed here
+                        */
+
+                        //check the previous negatives
+                        if (previousNegatives.size() != 0){
+                            boolean sameNegative = false;
+
+                            for (int i = previousNegatives.size() - 1; i >= 0; i--){
+                                if (ind.getIndicator() == previousNegatives.get(i)){
+                                    sameNegative = true;
+                                    break;
+                                }
+                            }
+
+                            //mark as positive only if the previously found negative is the same
+                            if (sameNegative){
+                                hasPositive = true;
+                            }
+
+                        }else{ //if no negatives before it already marks the sign as positive
+                            hasPositive = true;
+                        }
+
+                    } else {
+                        /* Note:
+                         * save the negative indicator here so it can be compared later if a positive wants to overwrite it!
+                         * in a case a longer negative "Pn" has already been found a shorter positive "P" will not overwrite it
+                         * and the hasPositive will remain false;
+                         * In a case a "P" negative indicator has already been found, positive will mark hasPositive and therefore
+                         * later a default positive value will be returned (if the indicators for positive & negative are the same
+                         * positive is returned)
+                         * testing for previous positives is not required since if a hasPositive is already true method will return
+                         * true anyway
+                         *
+                        */
+                        previousNegatives.add(ind.getIndicator());
+
+                        hasNegative = true;
+
+                    }
+                }
+            }
+
+        }
+
+        //Note:
+        //positive indicator has priority here - if both indicators supplied by the user are the same, a positive is chosen
+        //if there were no indicator found in the tested coordinate, a positive value is returned by default
+
+        if (hasPositive){
+            return 1;
+        } else {
+            if (hasNegative) {
+                return -1;
+            } else {
+                return 1;
+            }
+        }
+    }
+
+
+
+    //returns a currently used decimal separator
+    private String getDecimalSeparator(){
+       //TODO not yet transformed from C#
+//     return System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
+        return ".";
+    }
+
+
+    //replaces comma or dot for current decimal separator
+    private String fixDecimalSeparator(String str){
+        //Note:
+        //Coma is replaced as parsers often recognise dot as a decimal separator
+        //Comma or dot is replaced with a decimal separator here (environment settings)
+        //But decimal separator has to be used later too;
+
+        String regExReplaceComma = "(\\,|\\.)";
+        str = str.replaceAll(regExReplaceComma, getDecimalSeparator());
+
+        return str;
+    }
+
+
+    //removes sign
+    private String removeSign(String str){
+        String regExRemoveSign = "(\\+|-|S|s|W|w|N|n|E|e)";
+        str = str.replaceAll(regExRemoveSign, "");
+        return str;
+    }
+
+    //removes custom sign indicators
+    private String removeCustomPatternParts(String str){
+
+        /* Note:
+         * Symbols are added here so the removing tries to not affect the coordinate too much
+         * Strings to be removed then are evaluated from the longest ones to the shortes ones
+         * So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
+         * */
+
+        //CustomHemisphereIndicator is used here so another object does not have to be created
+        //only for the string cleanning
+        List<CustomHemisphereIndicator> stringsToRemove = customPtrn.hemisphereIndicators;
+
+        //add degree symbol
+        CustomHemisphereIndicator stringToRemove = new CustomHemisphereIndicator("Degree", customPtrn.degreeSymbol,customPtrn.degreeSymbol.length(), false);
+        stringsToRemove.add(stringToRemove);
+
+        //add minute symbol
+        stringToRemove = new CustomHemisphereIndicator("Minute", customPtrn.minuteSymbol, customPtrn.minuteSymbol.length(), false);
+        stringsToRemove.add(stringToRemove);
+
+        //add second symbol
+        stringToRemove = new CustomHemisphereIndicator("Second", customPtrn.secondSymbol, customPtrn.secondSymbol.length(), false);
+        stringsToRemove.add(stringToRemove);
+
+        //sort the list (by element's Length property)
+        Collections.sort(stringsToRemove, lengthComparator);
+
+
+//        ListSelectionEv.sort(lengthComparator);
+
+
+        for (int x = stringsToRemove.size() - 1; x >= 0; x--){
+
+            CustomHemisphereIndicator toBeRemoved = stringsToRemove.get(x);
+
+            //check if the string exists so replacing does not yield errors
+            if (toBeRemoved.getLength() > 0)
+            {
+                //check if the supplied pattern was marked as case insensitive?
+                String CaseInsensitive = "";
+
+                if (customPtrn.caseInsensitive){
+                    CaseInsensitive = "(?i)";
+                }
+
+                //create regex for replacing
+                String tempRegex = CaseInsensitive + toBeRemoved.getIndicator();
+
+
+                if (toBeRemoved.getName().equals("Degree") || toBeRemoved.getName().equals("Minute")) {
+                    //replace with a symbol used later for splitting
+                    str =  str.replaceAll(tempRegex, ":");
+                } else {
+                    //remove the string
+                    str = str.replaceAll(tempRegex, "");
+                }
+            }
+        }
+        return str;
+    }
+
+
+
+    //removes whitespace characters
+    private String removeWhiteSpace(String str){
+        str = str.replaceFirst("\\s+", "");
+        return str;
+    }
+
+
+    //Object for the conversion results
+    public class ConversionResults{
+        public boolean patternRecognised;
+        public String patternMatched;
+        public String patternType;
+
+        public boolean conversionSuccessful;
+        public double convertedCoord;
+        public boolean canBeLat;
+
+        public String conversionComments;
+
+        public Boolean isLongitude;
+
+        public int dd;
+        public int mm;
+        public double mmm;
+        public int ss;
+        public double sss;
+
+    }
+
+
+    public ConversionResults tryConvert(String str){
+        //some local variables
+        int sign; //sign of the coordinate
+        String[] decimalBit, ddmmss, ddmm; //arrays for splitting
+        double dd = 0, mm = 0, ss = 0, mmm = 0, sss = 0, dec = 0; //parts of the coordinates
+
+        String decSeparatorRaw = String.valueOf(getDecimalSeparator()); //gets the current decimal separator
+        String decSeparatorRegEx = decSeparatorRaw.replace(".", "\\.");
+
+        ConversionResults results = new ConversionResults();
+
+        //Get the matched pattern
+        CoordinatePattern pattern;
+        int ptrnnum = matchPattern(str);
+        if (ptrnnum != -1) {
+            pattern = patterns.get(ptrnnum);
+        } else {
+            pattern = new CoordinatePattern();
+            pattern.description = "Unknown";
+            pattern.pattern = "No pattern matched";
+        }
+
+
+
+        if (pattern.description.equals("Variation of DD.DDD")){
+
+                       //Sets pattern machted, successful, pattern type and pattern info
+               initializeResult(results, pattern);
+
+            //get sign
+            sign = getSign(str);
+            results.isLongitude = getIsLongitude(str);
+
+            //Replace comma or dot with a current decimal separator
+            str = fixDecimalSeparator(str);
+
+            //Remove all the unwanted stuff
+            str = removeSign(str);
+            str = removeWhiteSpace(str);
+
+            //Since this is already a decimal degree no spliting is needed
+            dd =  Double.valueOf(str);
+
+            checkDegreeRange(dd, results);
+            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
+
+        }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM.MMM('|m)")){
+
+               //Sets pattern machted, successful, pattern type and pattern info
+               initializeResult(results, pattern);
+
+            //get sign
+            sign = getSign(str);
+            results.isLongitude = getIsLongitude(str);
+
+            //Replace comma or dot with a current decimal separator
+            str = fixDecimalSeparator(str);
+
+            //Remove all the unwanted stuff
+            str = removeSign(str);
+            str = removeWhiteSpace(str);
+
+            //do some further replacing
+            //Replace degree symbol
+            str = str.replaceAll("(\u00B0|\u00BA|D|d)", ":");
+
+            //remove minute symbol
+            str = str.replaceAll("("+ minuteUtf8 + "|'|M|m)", "");
+
+            //Extract decimal part
+            decimalBit = str.split(decSeparatorRegEx);
+
+            //split degrees and minutes
+            ddmm = decimalBit[0].split(":");
+
+
+            //extract values from the strings
+            dd = Integer.valueOf(ddmm[0]); //Degrees
+
+            if (ddmm.length > 1){ //Minutes
+                //check if the string is not empty
+                if (ddmm[1] != "") {
+                       mm = Integer.valueOf(ddmm[1]);
+                }
+            }
+
+            if (decimalBit.length > 1){//DecimalSeconds
+                //check if the string is not empty
+                if (decimalBit[1] != "") {
+                    mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
+                }
+            }
+
+            checkDegreeRange(dd, results);
+            checkMinuteRange(mm, results);
+            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
+
+        }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM("+ minuteUtf8 + "|m)SS.SSS("+secondUtf8+"|s)")){
+
+               /*
+             * Note:
+             * This pattern allows the seconds to be specified with S, s or " or nothing at all
+             * If the seconds are marked with "s" and there is no other indication of the hemisphere
+             * the coordinate will be parsed as southern (negative).
+             *
+             * If the N / E / W / + indicator is found the coordinate will be parsed appropriately no matter
+             * what is the second notation
+            */
+
+               //Sets pattern matched, successful, pattern type and pattern info
+               initializeResult(results, pattern);
+
+            //get sign
+            sign = getSign(str);
+            //TODO test S
+            results.isLongitude = getIsLongitude(str);
+
+            //Replace comma or dot with a current decimal separator
+            str = fixDecimalSeparator(str);
+
+            //Remove all the unwanted stuff
+            str = removeSign(str);
+            str = removeWhiteSpace(str);
+
+            //remove second symbol (s is removed by the get sign method)
+            //double apostrophe is not removed here as single apostrophe may mark minutes!
+            //it's taken care of later after extracting the decimal part
+            str = str.replaceAll("("+secondUtf8+"|\")", "");
+
+            //do some further replacing
+            //Replace degree symbol
+            str = str.replaceAll("(\u00B0|\u00B0|D|d|"+ minuteUtf8 + "|'|M|m)",":");
+
+            //Extract decimal part
+            decimalBit = str.split(decSeparatorRegEx);
+
+            //remove : from the decimal part [1]! This is needed when a double apostrophe was used to mark seconds
+            if (decimalBit.length > 1)
+            {
+                decimalBit[1].replace(":", "");
+            }
+
+            //split degrees and minutes
+            ddmmss = decimalBit[0].split(":");
+
+
+            //extract values from the strings
+            dd = Integer.valueOf(ddmmss[0]); //Degrees
+            if (ddmmss.length > 1){//Minutes
+                //check if the string is not empty
+                if (ddmmss[1] != "") {
+                       mm = Integer.valueOf(ddmmss[1]);
+                }
+            }
+            if (ddmmss.length > 2){//Seconds
+                //check if the string is not empty
+                if (ddmmss[2] != "") {
+                       ss = Integer.valueOf(Nz(ddmmss[2]).trim());
+                }
+            }
+            if (decimalBit.length > 1) { //DecimalSeconds
+                //check if the string is not empty
+                if (decimalBit[1] != "") {
+                    sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
+                }
+            }
+
+            checkDegreeRange(dd, results);
+            checkMinuteRange(mm, results);
+            checkSecondRange(ss, results);
+
+            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
+
+        }else if (pattern.description.equals("Variation of DD:MM:SS.SSS")){
+
+               //Sets pattern machted, successful, pattern type and pattern info
+               initializeResult(results, pattern);
+
+            //get sign
+            sign = getSign(str);
+            results.isLongitude = getIsLongitude(str);
+
+            //Replace comma or dot with a current decimal separator
+            str = fixDecimalSeparator(str);
+
+            //Remove all the unwanted stuff
+            str = removeSign(str);
+            str = removeWhiteSpace(str);
+
+            //Do some splitting
+            decimalBit = str.split(decSeparatorRegEx);
+            ddmmss = decimalBit[0].split(":");
+
+
+            //extract values from the strings
+            dd = Integer.valueOf(ddmmss[0]); //Degrees
+            if (ddmmss.length > 1)//Minutes
+            {
+                //check if the string is not empty
+                if (ddmmss[1] != "") { mm = Integer.valueOf(ddmmss[1]); }
+            }
+            if (ddmmss.length > 2) {//Seconds{
+                //check if the string is not empty
+                if (ddmmss[2] != "") {
+                       ss = Integer.valueOf(ddmmss[2]);
+                }
+            }
+            if (decimalBit.length > 1) { //DecimalSeconds
+                //check if the string is not empty
+                if (decimalBit[1] != "") {
+                    sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
+                }
+            }
+
+            checkDegreeRange(dd, results);
+            checkMinuteRange(mm, results);
+            checkSecondRange(ss, results);
+
+            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
+
+        }else if (pattern.description.equals("Custom variation of DD.DDD")){
+
+               //Sets pattern machted, successful, pattern type and pattern info
+               initializeResult(results, pattern);
+
+
+               //get sign
+               sign = getCustomSign(str);
+
+               //TODO still needs to be adapted to custom pattern
+               results.isLongitude = getIsLongitude(str);
+
+
+               //Remove all the unwanted stuff
+               //Note: This method also replaces the symbols with ":"
+               //Note: In certain cases it may make the coord unparsable
+               str = removeCustomPatternParts(str);
+
+               str = removeWhiteSpace(str);
+
+               //Replace comma or dot with a current decimal separator
+               str = fixDecimalSeparator(str);
+
+               //remove the ":" here as it is not needed here for decimal degrees
+               str = str.replace(":", "");
+
+               try {
+                   //Since this is already a decimal degree no spliting is needed
+                   dd = Double.valueOf(str);
+               } catch (Exception e)  {
+                   results.conversionSuccessful = false;
+                   results.convertedCoord = 99999; //this is to mark an error...
+                   results.conversionComments =
+                       "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
+                       "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
+                       "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
+                       ;
+
+                   //exit method
+                   return results;
+               }
+
+               //Since this is already a decimal degree no spliting is needed
+               dd = Double.valueOf(str);
+
+               checkDegreeRange(dd, results);
+               doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
+
+
+           }else if (pattern.description.equals("Custom variation of DD:MM.MMM")){
+           //-------------Customs patterns start here-------------
+
+               //Sets pattern machted, successful, pattern type and pattern info
+               initializeResult(results, pattern);
+
+            //get sign
+            sign = getCustomSign(str);
+
+            //TODO still needs to be adapted to custom pattern
+               results.isLongitude = getIsLongitude(str);
+
+
+
+            //Remove all the unwanted stuff
+            //Note: This method also replaces the symbols with ":"
+            //Note: In certain cases it may make the coord unparsable
+            str = removeCustomPatternParts(str);
+
+            str = removeWhiteSpace(str);
+
+            //Replace comma or dot with a current decimal separator
+            str = fixDecimalSeparator(str);
+
+
+            //Extract decimal part
+            decimalBit = str.split(decSeparatorRegEx);
+
+            //split degrees and minutes
+            ddmm = decimalBit[0].split(":");
+
+
+            try {
+                //extract values from the strings
+                dd = Integer.valueOf(ddmm[0]); //Degrees
+
+                if (ddmm.length > 1){//Minutes
+                    //check if the string is not empty
+                    if (ddmm[1] != "") { mm = Integer.valueOf(ddmm[1]); }
+                }
+
+                if (decimalBit.length > 1){//DecimalSeconds
+                    //check if the string is not empty
+                    if (decimalBit[1] != ""){
+                        //replace the ":" if any (may be here as a result of custom symbol replacement
+                        decimalBit[1] = decimalBit[1].replace(":", "");
+
+                        mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
+                    }
+                }
+            } catch (Exception e){
+                results.conversionSuccessful = false;
+                results.convertedCoord = 99999; //this is to mark an error...
+                results.conversionComments =
+                    "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
+                    "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
+                    "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
+                    ;
+
+                //exit method
+                return results;
+            }
+
+
+            checkDegreeRange(dd, results);
+            checkMinuteRange(mm, results);
+            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
+
+           } else if (pattern.description.equals("Custom variation of DD:MM:SS.SSS")){
+
+               //Sets pattern machted, successful, pattern type and pattern info
+               initializeResult(results, pattern);
+
+
+            //get sign
+            sign = getCustomSign(str);
+
+            //TODO still needs to be adapted to custom pattern
+               results.isLongitude = getIsLongitude(str);
+
+
+            //Remove all the unwanted stuff
+            //Note: This method also replaces the symbols with ":"
+            //Note: In certain cases it may make the coord unparsable
+            str = removeCustomPatternParts(str);
+
+            str = removeWhiteSpace(str);
+
+            //Replace comma or dot with a current decimal separator
+            str = fixDecimalSeparator(str);
+
+
+            //Extract decimal part
+            decimalBit = str.split(decSeparatorRegEx);
+
+            //split degrees and minutes
+            ddmmss = decimalBit[0].split(":");
+
+
+            try {
+
+                //extract values from the strings
+                dd = Integer.valueOf(ddmmss[0]); //Degrees
+                if (ddmmss.length > 1) {//Minutes
+                    //check if the string is not empty
+                    if (ddmmss[1] != "") {
+                       mm = Integer.valueOf(ddmmss[1]);
+                    }
+                }
+                if (ddmmss.length > 2){ //Seconds
+                    //check if the string is not empty
+                    if (ddmmss[2] != "") {
+                       ss = Integer.valueOf(ddmmss[2]);
+                    }
+                }
+                if (decimalBit.length > 1){ //DecimalSeconds
+                    //check if the string is not empty
+                    if (decimalBit[1] != "") {
+                        sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
+                    }
+                }
+            } catch (Exception e) {
+                results.conversionSuccessful = false;
+                results.convertedCoord = 99999; //this is to mark an error...
+                results.conversionComments =
+                    "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
+                    "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
+                    "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
+                    ;
+
+                //exit method
+                return results;
+            }
+
+
+            checkDegreeRange(dd, results);
+            checkMinuteRange(mm, results);
+            checkSecondRange(ss, results);
+
+            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
+
+           }else {   //default  : pattern not recognized
+            results.patternRecognised = false;
+            results.patternType = pattern.description;
+            results.patternMatched = pattern.pattern;
+
+            results.conversionSuccessful = false;
+            results.convertedCoord = 99999; //this is to mark an error...
+
+            results.conversionComments = "Coordinate pattern not recognised!";
+
+           }
+
+        //do the self check here
+        results = selfTest(results);
+
+        //return conversion results
+        return results;
+    }
+
+
+       /**
+     * @param string
+     * @return
+     */
+    private String Nz(String string) {
+        return CdmUtils.Nz(string);
+    }
+
+
+    /**
+        * @param sign
+        * @param dd
+        * @param mm
+        * @param ss
+        * @param sss
+        * @param results
+        */
+       private void doConvertWithCheck(int sign, double dd, double mm, double mmm, double ss, double sss, ConversionResults results) {
+               double dec;
+               //Do the conversion if everything ok
+               if (results.conversionSuccessful){
+                   results.conversionComments = "Conversion successful.";
+
+                   dec = sign * (dd + (mm + mmm) / 60 + (ss + sss) / 3600);
+
+                   //one more check to ensure a coord does not exceed 180
+                   if (dec > 180 | dec < -180){
+                       results.conversionSuccessful = false;
+                       results.convertedCoord = 99999; //this is to mark an error...
+                       results.conversionComments += "Coordinate is either > 180 or < -180; ";
+                   } else {
+                       results.convertedCoord = dec;
+
+                       results.conversionComments = "Conversion successful.";
+
+                       //Check whether the coordinate exceeds +/- 90 and mark it in comments
+
+                       if (dec <= 90 && dec >= -90 && (results.isLongitude == null || results.isLongitude == false) ) {
+                       results.canBeLat = true;
+                }else{
+                       results.isLongitude = true;
+                }
+                   }
+               }
+       }
+
+
+       /**
+        * @param ss
+        * @param results
+        */
+       private void checkSecondRange(double ss, ConversionResults results) {
+               if (ss > 59) {//seconds
+                   results.conversionSuccessful = false;
+                   results.convertedCoord = 99999; //this is to mark an error...
+                   results.conversionComments += "Seconds fall outside the range: MM >= 60; ";
+               }
+       }
+
+
+       /**
+        * @param mm
+        * @param results
+        */
+       private void checkMinuteRange(double mm, ConversionResults results) {
+               if (mm > 59) {//minutes
+                   results.conversionSuccessful = false;
+                   results.convertedCoord = 99999; //this is to mark an error...
+                   results.conversionComments += "Minutes fall outside the range: MM > 59; ";
+               }
+       }
+
+
+       /**
+        * @param dd
+        * @param results
+        */
+       private void checkDegreeRange(double dd, ConversionResults results) {
+               //do some additional checking if the coords fall into the range
+               if (dd < -180 | dd > 180){  //degree may require another param specifying whether it's lat or lon...
+                   results.conversionSuccessful = false;
+                   results.convertedCoord = 99999; //this is to mark an error...
+                   results.conversionComments += "Degrees fall outside the range: DD < -180 | DD > 180; ";
+               }
+       }
+
+
+       /**
+        * @param str
+        * @return
+        */
+       private Boolean getIsLongitude(String str) {
+           //This regex checks for the negative hemisphere indicator
+               Pattern regexLatitudeNonAmbigous = Pattern.compile("(N|n)");
+               Pattern regexLatitudeAmbigous = Pattern.compile("(S|s)");
+
+        //This regex checks if there weren't any other hemisphere indicators
+        //it is needed for the specific case of the DDdMMmSSs S
+        //so it needs to be ensured there where no positive indicators
+        Pattern regexLongitude = Pattern.compile("(W|w|E|e)");
+
+        //if a positive indicator is found no need to search further
+        if (regexLongitude.matcher(str).find()){
+            return true;
+        }else if (regexLatitudeNonAmbigous.matcher(str).find()){
+               return false;
+        }else if (regexLatitudeAmbigous.matcher(str).find()){
+               Pattern regexLiteralUnits = Pattern.compile("(D|d|M|m)");
+
+               //if there are no other literal units we assume that S is a
+               //direction and not a second indicator
+            if (! regexLiteralUnits.matcher(str).find()){
+                return false;
+            }else if (regexLatitudeAmbigous.matcher(str).groupCount() > 1){
+               return false;
+            }else{
+               return null;
+            }
+        }else{
+               return null;
+        }
+       }
+
+
+       /**
+        * Sets pattern machted, successful, pattern type and pattern info
+        * @param results
+        * @param pattern
+        */
+       private void initializeResult(ConversionResults results,
+                       CoordinatePattern pattern) {
+               //Pattern matched
+               results.patternRecognised = true;
+
+               //Matching pattern succeeded so intialy the parsing is ok
+               results.conversionSuccessful = true;
+
+               //pattern info
+               results.patternType = pattern.description;
+               results.patternMatched = pattern.pattern;
+       }
+
+
+    private ConversionResults selfTest(ConversionResults results){
+
+        ConversionResults newresults = results;
+
+        if (results.conversionSuccessful != false){
+            int sign = 1;
+            if (Math.signum(results.convertedCoord) < 0) {
+               sign = -1;
+            }
+
+            double decimalDegrees = sign * results.convertedCoord;
+            int fullDegrees;
+
+            double decimalMinutes;
+            int fullMinutes;
+
+            double decimalSeconds;
+            int fullSeconds;
+
+            //Get full degrees
+            fullDegrees = (int)Math.floor(decimalDegrees);
+
+            //get minutes
+            decimalMinutes = (decimalDegrees - fullDegrees) * 60;
+            fullMinutes = (int)Math.floor(decimalMinutes);
+
+            decimalSeconds = (decimalMinutes - fullMinutes) * 60;
+            fullSeconds = (int)Math.floor(decimalSeconds);
+
+            //save the test results
+            newresults.dd = fullDegrees;
+            newresults.mm = fullMinutes;
+            newresults.mmm = decimalSeconds;
+            newresults.ss = fullSeconds;
+            newresults.sss = decimalSeconds;
+
+        }
+
+        return newresults;
+
+    }
+
+
+
+    //------------ CUSTOM PATTERN BUILDER--------------
+
+    public class CustomPatternIn {
+        public String north;
+        public String south;
+        public String east;
+        public String west;
+
+        public String degreeSymbol;
+        public String minuteSymbol;
+        public String secondSymbol;
+
+        public boolean caseInsensitive;
+        public boolean allowWhiteSpace;
+        public boolean priorityOverDefaultPatterns;
+        public boolean disableDefaultPatterns;
+
+    }
+
+
+    private class CustomPattern{
+
+        public List<CustomHemisphereIndicator> hemisphereIndicators;
+
+        public String degreeSymbol;
+        public String minuteSymbol;
+        public String secondSymbol;
+
+        public boolean caseInsensitive;
+
+    }
+
+    //global variable to be used if a custom pattern is used
+    private CustomPattern customPtrn;
+
+    //escape some of the chars
+    private String escapeChars(String str){
+        // backslash - first so it is not messed when other escape chars are corrected for being used in a string
+        str = str.replace("\\", "\\\\");
+
+        //dot and comma
+        str = str.replace(".", "\\.");
+        str = str.replace(",", "\\,");
+
+        //brackets
+        str = str.replace("(", "\\(");
+        str = str.replace(")", "\\)");
+        str = str.replace("[", "\\[");
+        str = str.replace("]", "\\]");
+        str = str.replace("{", "\\{");
+        str = str.replace("}", "\\}");
+
+        //other replacements
+        str = str.replace("^", "\\^");
+        str = str.replace("$", "\\$");
+        str = str.replace("+", "\\+");
+        str = str.replace("*", "\\*");
+        str = str.replace("?", "\\?");
+        str = str.replace("|", "\\|");
+
+        return str;
+    }
+
+
+    //this implements sorting by using system.Icomparable - sorting is needed later when replacing
+    private class CustomHemisphereIndicator implements Comparable<CustomHemisphereIndicator> {
+        //private variables
+        private int m_length;
+        private String m_name;
+        private String m_indicator;
+        private boolean m_positive;
+
+        //constructor
+        public CustomHemisphereIndicator(String name, String indicator, int length, boolean positive){
+            this.m_name = name;
+            this.m_indicator = indicator;
+            this.m_length = length;
+            this.m_positive = positive;
+        }
+
+        //properties
+
+        public String getName(){
+               return this.m_name;
+        }
+        public void setName(String value){
+               this.m_name = value;
+        }
+
+        public String getIndicator(){
+            return this.m_indicator;
+        }
+        public void setIndicator(String value){
+            this.m_indicator = value;
+        }
+
+        public int getLength(){
+            return this.m_length;
+        }
+        public void setLength(int value){
+            this.m_length = value;
+        }
+
+
+        public boolean getPositive(){
+            return this.m_positive;
+        }
+        public void setPositive(boolean value){
+            this.m_positive = value;
+        }
+
+        /* Less than zero if this instance is less than obj.
+         * Zero if this instance is equal to obj.
+         * Greater than zero if this instance is greater than obj.
+         *
+         * This method uses the predefined method Int32.CompareTo
+         * */
+
+        @Override
+        public int compareTo(CustomHemisphereIndicator ind){
+
+               //no need to rewrite the code again, we have Integer.compareTo ready to use
+               return Integer.valueOf(this.getLength()).compareTo(Integer.valueOf(ind.getLength()));
+        }
+    }
+
+
+
+    //This adds custom pattern to a list of already predefined patterns
+    //useful for batch conversions - allows for totally mixed input data (predefined & custom)
+    public void addCustomPattern(CustomPatternIn patternIn){
+
+        //new custom pattern object - to pass the needed data farther
+        CustomPattern pattern = new CustomPattern();
+
+        //keep indicators for parsing
+        List<CustomHemisphereIndicator> indicators = new ArrayList<CustomHemisphereIndicator>();
+
+        //north
+        CustomHemisphereIndicator ind = new CustomHemisphereIndicator("North", patternIn.north, patternIn.north.length() ,true);
+        indicators.add(ind);
+
+        //south
+        ind = new CustomHemisphereIndicator("South", patternIn.south, patternIn.south.length(), false);
+        indicators.add(ind);
+
+        //east
+        ind = new CustomHemisphereIndicator("East", patternIn.east, patternIn.east.length(), true);
+        indicators.add(ind);
+
+        //west
+        ind = new CustomHemisphereIndicator("West", patternIn.west, patternIn.west.length(), false);
+        indicators.add(ind);
+
+        //sort the arraylist
+        Collections.sort(indicators, lengthComparator);
+
+
+        //add it to the pattern object
+        pattern.hemisphereIndicators = indicators;
+
+        //case insensitive
+        pattern.caseInsensitive = patternIn.caseInsensitive;
+
+        //keep symbols for parsing
+        pattern.degreeSymbol = patternIn.degreeSymbol;
+        pattern.minuteSymbol = patternIn.minuteSymbol;
+        pattern.secondSymbol = patternIn.secondSymbol;
+
+
+        //save the data
+        customPtrn = pattern;
+
+
+        //----------------build custom patterns----------------
+
+        //prepare hemisphere indicators
+        String north = escapeChars(patternIn.north);
+        String south = escapeChars(patternIn.south);
+        String east = escapeChars(patternIn.east);
+        String west = escapeChars(patternIn.west);
+
+        //prepare symbols
+        String degreesymbol = "";
+        if (patternIn.degreeSymbol != ""){
+            degreesymbol = "(" + escapeChars(patternIn.degreeSymbol) + ")?";
+        }
+
+        String minutesymbol = "";
+        if (patternIn.minuteSymbol != ""){
+            minutesymbol = "(" + escapeChars(patternIn.minuteSymbol) + ")?";
+        }
+
+        String secondsymbol = "";
+        if (escapeChars(patternIn.secondSymbol) != ""){
+            secondsymbol = "(" + escapeChars(patternIn.secondSymbol) + ")?";
+        }
+
+
+        //is the pattern to be case insensitive?
+        String CaseInsensitive = "";
+        if (patternIn.caseInsensitive){
+            CaseInsensitive = "(?i)";
+        }
+
+        //allow whitespace
+        String WhiteSpace = "";
+        if (patternIn.allowWhiteSpace == true){
+            WhiteSpace = "(\\s)*";
+        }
+
+        //hemisphere indicator
+        String HemisphereIndicator = "";
+
+        //add north if present
+        if (north == ""){
+            HemisphereIndicator += south;
+        }else{
+            HemisphereIndicator += north;
+            if (south != ""){
+                HemisphereIndicator += "|" + south;
+            }
+        }
+
+        //add east
+        if (north == "" & south == ""){
+            HemisphereIndicator += east;
+        } else {
+            if (east != ""){
+                HemisphereIndicator += "|" + east;
+            }
+        }
+
+        //add west
+        if (north == "" & south == "" & east == ""){
+            HemisphereIndicator += west;
+        } else {
+            if (west != "") {
+                HemisphereIndicator += "|" + west;
+            }
+        }
+
+        //add remaining bits if not empty
+        if (HemisphereIndicator != "") {
+            HemisphereIndicator = "(" + HemisphereIndicator + ")?";
+        }
+
+        List<CoordinatePattern> customPatterns = new ArrayList<CoordinatePattern>();
+
+        //create custom patterns based on the specified user's input
+        CoordinatePattern ptrn;
+
+        //Custom variation of DD.DDD
+        ptrn = new CoordinatePattern();
+        ptrn.description = "Custom variation of DD.DDD";
+        ptrn.pattern  =
+            CaseInsensitive + "(^" +
+            WhiteSpace + HemisphereIndicator + WhiteSpace +
+            "(" +
+            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + "$)" +
+            ")" +
+            "|(^" + WhiteSpace +
+            "(" +
+            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + ")" +
+            ")" +
+            HemisphereIndicator + WhiteSpace + "$" +
+            "))"
+            ;
+        customPatterns.add(ptrn);
+
+        //Custom variation of DD:MM.MMM
+        ptrn = new CoordinatePattern();
+        ptrn.description = "Custom variation of DD:MM.MMM";
+        ptrn.pattern =
+            CaseInsensitive + "(^" +
+            WhiteSpace + HemisphereIndicator + WhiteSpace +
+            "(" +
+            "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + minutesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + minutesymbol + WhiteSpace + "$)" +
+            ")" +
+            "|(^" + WhiteSpace +
+            "(" +
+            "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + minutesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + minutesymbol + WhiteSpace + ")" +
+            ")" +
+            HemisphereIndicator + WhiteSpace + "$" +
+            "))"
+            ;
+        customPatterns.add(ptrn);
+
+        //Custom variation of DD:MM:SS.SSS
+        ptrn = new CoordinatePattern();
+        ptrn.description = "Custom variation of DD:MM:SS.SSS";
+        ptrn.pattern =
+            CaseInsensitive + "(^" +
+            WhiteSpace + HemisphereIndicator + WhiteSpace +
+            "(" +
+            "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + secondsymbol + WhiteSpace + "$)|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + secondsymbol + WhiteSpace + "$)" +
+            ")" +
+            "|(^" + WhiteSpace +
+            "(" +
+            "(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)?" + WhiteSpace + secondsymbol + WhiteSpace + ")|(\\d{1,3}" + WhiteSpace + degreesymbol + WhiteSpace + "\\d{1,2}" + WhiteSpace + minutesymbol + WhiteSpace + "\\d{1,2}(\\.|\\,)\\d+" + WhiteSpace + secondsymbol + WhiteSpace + ")" +
+            ")" +
+            HemisphereIndicator + WhiteSpace + "$" +
+            "))"
+            ;
+        customPatterns.add(ptrn);
+
+        //check if the default patterns are to be used
+        if (patternIn.disableDefaultPatterns) {
+            patterns = customPatterns;
+        } else { //if all patterns are to be used check which set has the matching priority
+
+            //check if the custom patterns are to have priority over the default ones
+            if (patternIn.priorityOverDefaultPatterns){
+
+                //add default patterns to the custom patterns
+                for (int i = 0; i < patterns.size(); i++){
+                    customPatterns.add(patterns.get(i));
+                }
+
+                //swap array lists
+                patterns = customPatterns;
+
+            }else{
+                //add custom patterns to the default patterns
+                for (int i = 0; i < customPatterns.size(); i++){
+                    patterns.add(customPatterns.get(i));
+
+                }
+            }
+        }
+    }
+
+}