-// $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));
+
+ }
+ }
+ }
+ }
+
+}