ref #6190 removing svn property place holder in first line of code
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / strategy / parser / location / CoordinateConverter.java
1 /**
2 * Copyright (C) 2009 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 *
9 * This file is an Java adaption from the orginal CoordinateConverter written by Dominik Mikiewicz
10 * @see www.cartomatic.pl
11 * @see http://dev.e-taxonomy.eu/svn/trunk/geo/coordinateConverter/CoordinateConverter.cs
12 * @see http://gis.miiz.waw.pl/webapps/coordinateconverter/
13 */
14 package eu.etaxonomy.cdm.strategy.parser.location;
15
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.List;
20 import java.util.regex.Pattern;
21
22 import org.apache.log4j.Logger;
23
24 /**
25 * @author a.mueller
26 * @date 07.06.2010
27 *
28 */
29 public class CoordinateConverter {
30 @SuppressWarnings("unused")
31 private static final Logger logger = Logger.getLogger(CoordinateConverter.class);
32
33 //Patterns
34 private List<CoordinatePattern> patterns;
35
36 private static String minuteUtf8 = "\u02B9|\u00B4|\u02CA|\u0301|\u0374";
37 private static String secondUtf8 = "\u02BA|\u030B|\u2033|\u00B4\u00B4";
38
39
40 private class CoordinatePattern{
41 String description;
42 String pattern;
43 }
44
45
46 private Comparator<CustomHemisphereIndicator> lengthComparator = new Comparator<CustomHemisphereIndicator>(){
47 @Override
48 public int compare(CustomHemisphereIndicator ind1, CustomHemisphereIndicator ind2) {
49 return Integer.valueOf(ind1.getLength()).compareTo(ind2.getLength());
50 }
51 };
52
53 //Class constructor
54 public CoordinateConverter() {
55 //initialise pattern array
56 patterns = new ArrayList<CoordinatePattern>();
57
58 //temp pattern variable
59 CoordinatePattern pattern;
60
61
62 //variations of DD.DDD with white space characters
63 pattern = new CoordinatePattern();
64 pattern.description = "Variation of DD.DDD";
65 pattern.pattern =
66 //+/-/Nn/Ss/Ww/EeDD.DDDD
67 "(^" +
68 "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
69 "((\\d{1,3}(\\.|\\,)?(\\s)*$)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*$))" +
70 ")" +
71 ////DD.DDDDNn/Ss/Ww/Ee
72 "|(^" +
73 "(\\s)*((\\d{1,3}(\\.|\\,)?(\\s)*)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*))" +
74 "(W|w|E|e|N|n|S|s)?(\\s)*$" +
75 ")";
76 patterns.add(pattern);
77
78
79 //Variations of DD(\u00B0|d)MM.MMM' with whitespace characters
80 pattern = new CoordinatePattern();
81 pattern.description = "Variation of DD(\u00B0|d)MM.MMM('|m)";
82 pattern.pattern =
83 "(^" +
84 "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
85 "((\\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)*$))" +
86 ")" +
87 "|(^" +
88 "(\\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)*))" +
89 "(W|w|E|e|N|n|S|s)?(\\s)*$" +
90 ")";
91 patterns.add(pattern);
92
93
94 //Variations of DD\u00B0MM'SS.SSS" with whitespace characters
95 pattern = new CoordinatePattern();
96 pattern.description = "Variation of DD(\u00B0|d)MM("+ minuteUtf8 + "|m)SS.SSS("+secondUtf8+"|s)";
97 pattern.pattern =
98 //+/-/Nn/Ss/Ww/EeDD\u00B0MM"+ minuteUtf8 + "SS.SSS
99 "(^" +
100 "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
101 "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)" +
102 "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*$)" +
103 "|(\\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)*$)" +
104 "|(\\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)*$))" +
105 ")" +
106 //DD°MM"+ minuteUtf8 + "SS.SSSNn/Ss/Ww/Ee
107 "|(^(\\s)*" +
108 "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|" +
109 "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*)|" +
110 "(\\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)*)|" +
111 "(\\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)*))" +
112 "(W|w|E|e|N|n|S|s)?(\\s)*$" +
113 ")";
114 patterns.add(pattern);
115
116
117 //Variations of DD:MM:SS.SSS with whitespace characters
118 pattern = new CoordinatePattern();
119 pattern.description = "Variation of DD:MM:SS.SSS";
120 pattern.pattern =
121 // +/-/Nn/Ss/Ww/EeDD:MM:SS.SSS
122 "(^" +
123 "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
124 "((\\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)*$))" +
125 ")" +
126 //DD:MM:SS.SSSNn/Ss/Ww/Ee
127 "|(^" +
128 "(\\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)*))" +
129 "(W|w|E|e|N|n|S|s)?(\\s)*$" +
130 ")";
131 patterns.add(pattern);
132
133 }
134
135
136 //tests if a string matches one of the defined patterns
137 private int matchPattern(String str){
138 int recognised = -1;
139
140 //match the string against each available patern
141 for (int i = 0; i < patterns.size(); i++){
142
143 CoordinatePattern pattern = patterns.get(i);
144 Pattern regEx = Pattern.compile(pattern.pattern);
145 if (regEx.matcher(str).find()) {
146 recognised = i;
147 break;
148 }
149
150 }
151 return recognised;
152 }
153
154
155 //gets sign of the coordinate (tests for presence of negative sign)
156 private int getSign(String str){
157
158 //This regex checks for the negative hemisphere indicator
159 Pattern regexNegative = Pattern.compile("(-|S|s|W|w)");
160
161 //This regex checks if there weren't any other hemisphere indicators
162 //it is needed for the specific case of the DDdMMmSSs S
163 //so it needs to be ensured there where no positive indicators
164 Pattern regexPositive = Pattern.compile("(\\+|N|n|E|e)");
165
166 //if a positive indicator is found no need to search further
167 if (regexPositive.matcher(str).find()){
168 return 1;
169 }else{
170 //if not check whether there was a negative indicator. if so negate otherwise return positive
171 if (regexNegative.matcher(str).find()){
172 return -1;
173 }else{
174 return 1;
175 }
176 }
177 }
178
179
180 //this checks for the coordinate sign by evaluating user supplied data
181 private int getCustomSign(String str){
182 //Note:
183 //Indicators are evaluated from the longest ones to the shortes ones
184 //So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
185
186
187 //search for the presence of indicators
188 boolean hasPositive = false;
189 boolean hasNegative = false;
190
191 //keep previous negative indicators here
192 List<String> previousNegatives = new ArrayList<String>();
193
194 //compare the string with user supplied custom pattern
195 for (int x = customPtrn.hemisphereIndicators.size() - 1; x >= 0; x--){
196
197 CustomHemisphereIndicator ind = customPtrn.hemisphereIndicators.get(x);
198
199 //test here if the indicator exists (has length >0)
200 if (ind.getLength() > 0){
201
202 //check if the supplied pattern was marked as case insensitive?
203 String caseInsensitive = "";
204
205 if (customPtrn.caseInsensitive){
206 caseInsensitive = "(?i)";
207 }
208
209 //create a regex
210 Pattern tempRegex = Pattern.compile(caseInsensitive + ind.getIndicator());
211
212 //if a pattern is found
213 if (tempRegex.matcher(str).find()){
214 //check whether it's a positive or negative indicator
215 if (ind.getPositive()){
216 /* Note:
217 * See the note below to understand why checking for previous negatives is performed here
218 */
219
220 //check the previous negatives
221 if (previousNegatives.size() != 0){
222 boolean sameNegative = false;
223
224 for (int i = previousNegatives.size() - 1; i >= 0; i--){
225 if (ind.getIndicator() == previousNegatives.get(i)){
226 sameNegative = true;
227 break;
228 }
229 }
230
231 //mark as positive only if the previously found negative is the same
232 if (sameNegative){
233 hasPositive = true;
234 }
235
236 }else{ //if no negatives before it already marks the sign as positive
237 hasPositive = true;
238 }
239
240 } else {
241 /* Note:
242 * save the negative indicator here so it can be compared later if a positive wants to overwrite it!
243 * in a case a longer negative "Pn" has already been found a shorter positive "P" will not overwrite it
244 * and the hasPositive will remain false;
245 * In a case a "P" negative indicator has already been found, positive will mark hasPositive and therefore
246 * later a default positive value will be returned (if the indicators for positive & negative are the same
247 * positive is returned)
248 * testing for previous positives is not required since if a hasPositive is already true method will return
249 * true anyway
250 *
251 */
252 previousNegatives.add(ind.getIndicator());
253
254 hasNegative = true;
255
256 }
257 }
258 }
259
260 }
261
262 //Note:
263 //positive indicator has priority here - if both indicators supplied by the user are the same, a positive is chosen
264 //if there were no indicator found in the tested coordinate, a positive value is returned by default
265
266 if (hasPositive){
267 return 1;
268 } else {
269 if (hasNegative) {
270 return -1;
271 } else {
272 return 1;
273 }
274 }
275 }
276
277
278
279 //returns a currently used decimal separator
280 private String getDecimalSeparator(){
281 //TODO not yet transformed from C#
282 // return System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
283 return ".";
284 }
285
286
287 //replaces comma or dot for current decimal separator
288 private String fixDecimalSeparator(String str){
289 //Note:
290 //Coma is replaced as parsers often recognise dot as a decimal separator
291 //Comma or dot is replaced with a decimal separator here (environment settings)
292 //But decimal separator has to be used later too;
293
294 String regExReplaceComma = "(\\,|\\.)";
295 str = str.replaceAll(regExReplaceComma, getDecimalSeparator());
296
297 return str;
298 }
299
300
301 //removes sign
302 private String removeSign(String str){
303 String regExRemoveSign = "(\\+|-|S|s|W|w|N|n|E|e)";
304 str = str.replaceAll(regExRemoveSign, "");
305 return str;
306 }
307
308 //removes custom sign indicators
309 private String removeCustomPatternParts(String str){
310
311 /* Note:
312 * Symbols are added here so the removing tries to not affect the coordinate too much
313 * Strings to be removed then are evaluated from the longest ones to the shortes ones
314 * So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
315 * */
316
317 //CustomHemisphereIndicator is used here so another object does not have to be created
318 //only for the string cleanning
319 List<CustomHemisphereIndicator> stringsToRemove = customPtrn.hemisphereIndicators;
320
321 //add degree symbol
322 CustomHemisphereIndicator stringToRemove = new CustomHemisphereIndicator("Degree", customPtrn.degreeSymbol,customPtrn.degreeSymbol.length(), false);
323 stringsToRemove.add(stringToRemove);
324
325 //add minute symbol
326 stringToRemove = new CustomHemisphereIndicator("Minute", customPtrn.minuteSymbol, customPtrn.minuteSymbol.length(), false);
327 stringsToRemove.add(stringToRemove);
328
329 //add second symbol
330 stringToRemove = new CustomHemisphereIndicator("Second", customPtrn.secondSymbol, customPtrn.secondSymbol.length(), false);
331 stringsToRemove.add(stringToRemove);
332
333 //sort the list (by element's Length property)
334 Collections.sort(stringsToRemove, lengthComparator);
335
336
337 // ListSelectionEv.sort(lengthComparator);
338
339
340 for (int x = stringsToRemove.size() - 1; x >= 0; x--){
341
342 CustomHemisphereIndicator toBeRemoved = stringsToRemove.get(x);
343
344 //check if the string exists so replacing does not yield errors
345 if (toBeRemoved.getLength() > 0)
346 {
347 //check if the supplied pattern was marked as case insensitive?
348 String CaseInsensitive = "";
349
350 if (customPtrn.caseInsensitive){
351 CaseInsensitive = "(?i)";
352 }
353
354 //create regex for replacing
355 String tempRegex = CaseInsensitive + toBeRemoved.getIndicator();
356
357
358 if (toBeRemoved.getName().equals("Degree") || toBeRemoved.getName().equals("Minute")) {
359 //replace with a symbol used later for splitting
360 str = str.replaceAll(tempRegex, ":");
361 } else {
362 //remove the string
363 str = str.replaceAll(tempRegex, "");
364 }
365 }
366 }
367 return str;
368 }
369
370
371
372 //removes whitespace characters
373 private String removeWhiteSpace(String str){
374 str = str.replaceFirst("\\s+", "");
375 return str;
376 }
377
378
379 //Object for the conversion results
380 public class ConversionResults{
381 public boolean patternRecognised;
382 public String patternMatched;
383 public String patternType;
384
385 public boolean conversionSuccessful;
386 public double convertedCoord;
387 public boolean canBeLat;
388
389 public String conversionComments;
390
391 public Boolean isLongitude;
392
393 public int dd;
394 public int mm;
395 public double mmm;
396 public int ss;
397 public double sss;
398
399 }
400
401
402 public ConversionResults tryConvert(String str){
403 //some local variables
404 int sign; //sign of the coordinate
405 String[] decimalBit, ddmmss, ddmm; //arrays for splitting
406 double dd = 0, mm = 0, ss = 0, mmm = 0, sss = 0, dec = 0; //parts of the coordinates
407
408 String decSeparatorRaw = String.valueOf(getDecimalSeparator()); //gets the current decimal separator
409 String decSeparatorRegEx = decSeparatorRaw.replace(".", "\\.");
410
411 ConversionResults results = new ConversionResults();
412
413 //Get the matched pattern
414 CoordinatePattern pattern;
415 int ptrnnum = matchPattern(str);
416 if (ptrnnum != -1) {
417 pattern = patterns.get(ptrnnum);
418 } else {
419 pattern = new CoordinatePattern();
420 pattern.description = "Unknown";
421 pattern.pattern = "No pattern matched";
422 }
423
424
425
426 if (pattern.description.equals("Variation of DD.DDD")){
427
428 //Sets pattern machted, successful, pattern type and pattern info
429 initializeResult(results, pattern);
430
431 //get sign
432 sign = getSign(str);
433 results.isLongitude = getIsLongitude(str);
434
435 //Replace comma or dot with a current decimal separator
436 str = fixDecimalSeparator(str);
437
438 //Remove all the unwanted stuff
439 str = removeSign(str);
440 str = removeWhiteSpace(str);
441
442 //Since this is already a decimal degree no spliting is needed
443 dd = Double.valueOf(str);
444
445 checkDegreeRange(dd, results);
446 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
447
448 }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM.MMM('|m)")){
449
450 //Sets pattern machted, successful, pattern type and pattern info
451 initializeResult(results, pattern);
452
453 //get sign
454 sign = getSign(str);
455 results.isLongitude = getIsLongitude(str);
456
457 //Replace comma or dot with a current decimal separator
458 str = fixDecimalSeparator(str);
459
460 //Remove all the unwanted stuff
461 str = removeSign(str);
462 str = removeWhiteSpace(str);
463
464 //do some further replacing
465 //Replace degree symbol
466 str = str.replaceAll("(\u00B0|\u00BA|D|d)", ":");
467
468 //remove minute symbol
469 str = str.replaceAll("("+ minuteUtf8 + "|'|M|m)", "");
470
471 //Extract decimal part
472 decimalBit = str.split(decSeparatorRegEx);
473
474 //split degrees and minutes
475 ddmm = decimalBit[0].split(":");
476
477
478 //extract values from the strings
479 dd = Integer.valueOf(ddmm[0]); //Degrees
480
481 if (ddmm.length > 1){ //Minutes
482 //check if the string is not empty
483 if (ddmm[1] != "") {
484 mm = Integer.valueOf(ddmm[1]);
485 }
486 }
487
488 if (decimalBit.length > 1){//DecimalSeconds
489 //check if the string is not empty
490 if (decimalBit[1] != "") {
491 mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
492 }
493 }
494
495 checkDegreeRange(dd, results);
496 checkMinuteRange(mm, results);
497 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
498
499 }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM("+ minuteUtf8 + "|m)SS.SSS("+secondUtf8+"|s)")){
500
501 /*
502 * Note:
503 * This pattern allows the seconds to be specified with S, s or " or nothing at all
504 * If the seconds are marked with "s" and there is no other indication of the hemisphere
505 * the coordinate will be parsed as southern (negative).
506 *
507 * If the N / E / W / + indicator is found the coordinate will be parsed appropriately no matter
508 * what is the second notation
509 */
510
511 //Sets pattern machted, successful, pattern type and pattern info
512 initializeResult(results, pattern);
513
514 //get sign
515 sign = getSign(str);
516 //TODO test S
517 results.isLongitude = getIsLongitude(str);
518
519 //Replace comma or dot with a current decimal separator
520 str = fixDecimalSeparator(str);
521
522 //Remove all the unwanted stuff
523 str = removeSign(str);
524 str = removeWhiteSpace(str);
525
526 //remove second symbol (s is removed by the get sign method)
527 //double apostrophe is not removed here as single apostrphe may mark minutes!
528 //it's taken care of later after extracting the decimal part
529 str = str.replaceAll("("+secondUtf8+"|\")", "");
530
531 //do some further replacing
532 //Replace degree symbol
533 str = str.replaceAll("(\u00B0|\u00B0|D|d|"+ minuteUtf8 + "|'|M|m)",":");
534
535 //Extract decimal part
536 decimalBit = str.split(decSeparatorRegEx);
537
538 //remove : from the decimal part [1]! This is needed when a double apostrophe was used to mark seconds
539 if (decimalBit.length > 1)
540 {
541 decimalBit[1].replace(":", "");
542 }
543
544 //split degrees and minutes
545 ddmmss = decimalBit[0].split(":");
546
547
548 //extract values from the strings
549 dd = Integer.valueOf(ddmmss[0]); //Degrees
550 if (ddmmss.length > 1){//Minutes
551 //check if the string is not empty
552 if (ddmmss[1] != "") {
553 mm = Integer.valueOf(ddmmss[1]);
554 }
555 }
556 if (ddmmss.length > 2){//Seconds
557 //check if the string is not empty
558 if (ddmmss[2] != "") {
559 ss = Integer.valueOf(ddmmss[2]);
560 }
561 }
562 if (decimalBit.length > 1) { //DecimalSeconds
563 //check if the string is not empty
564 if (decimalBit[1] != "") {
565 sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
566 }
567 }
568
569 checkDegreeRange(dd, results);
570 checkMinuteRange(mm, results);
571 checkSecondRange(ss, results);
572
573 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
574
575 }else if (pattern.description.equals("Variation of DD:MM:SS.SSS")){
576
577 //Sets pattern machted, successful, pattern type and pattern info
578 initializeResult(results, pattern);
579
580 //get sign
581 sign = getSign(str);
582 results.isLongitude = getIsLongitude(str);
583
584 //Replace comma or dot with a current decimal separator
585 str = fixDecimalSeparator(str);
586
587 //Remove all the unwanted stuff
588 str = removeSign(str);
589 str = removeWhiteSpace(str);
590
591 //Do some splitting
592 decimalBit = str.split(decSeparatorRegEx);
593 ddmmss = decimalBit[0].split(":");
594
595
596 //extract values from the strings
597 dd = Integer.valueOf(ddmmss[0]); //Degrees
598 if (ddmmss.length > 1)//Minutes
599 {
600 //check if the string is not empty
601 if (ddmmss[1] != "") { mm = Integer.valueOf(ddmmss[1]); }
602 }
603 if (ddmmss.length > 2) {//Seconds{
604 //check if the string is not empty
605 if (ddmmss[2] != "") {
606 ss = Integer.valueOf(ddmmss[2]);
607 }
608 }
609 if (decimalBit.length > 1) { //DecimalSeconds
610 //check if the string is not empty
611 if (decimalBit[1] != "") {
612 sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
613 }
614 }
615
616 checkDegreeRange(dd, results);
617 checkMinuteRange(mm, results);
618 checkSecondRange(ss, results);
619
620 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
621
622 }else if (pattern.description.equals("Custom variation of DD.DDD")){
623
624 //Sets pattern machted, successful, pattern type and pattern info
625 initializeResult(results, pattern);
626
627
628 //get sign
629 sign = getCustomSign(str);
630
631 //TODO still needs to be adapted to custom pattern
632 results.isLongitude = getIsLongitude(str);
633
634
635 //Remove all the unwanted stuff
636 //Note: This method also replaces the symbols with ":"
637 //Note: In certain cases it may make the coord unparsable
638 str = removeCustomPatternParts(str);
639
640 str = removeWhiteSpace(str);
641
642 //Replace comma or dot with a current decimal separator
643 str = fixDecimalSeparator(str);
644
645 //remove the ":" here as it is not needed here for decimal degrees
646 str = str.replace(":", "");
647
648 try {
649 //Since this is already a decimal degree no spliting is needed
650 dd = Double.valueOf(str);
651 } catch (Exception e) {
652 results.conversionSuccessful = false;
653 results.convertedCoord = 99999; //this is to mark an error...
654 results.conversionComments =
655 "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
656 "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
657 "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
658 ;
659
660 //exit method
661 return results;
662 }
663
664 //Since this is already a decimal degree no spliting is needed
665 dd = Double.valueOf(str);
666
667 checkDegreeRange(dd, results);
668 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
669
670
671 }else if (pattern.description.equals("Custom variation of DD:MM.MMM")){
672 //-------------Customs patterns start here-------------
673
674 //Sets pattern machted, successful, pattern type and pattern info
675 initializeResult(results, pattern);
676
677 //get sign
678 sign = getCustomSign(str);
679
680 //TODO still needs to be adapted to custom pattern
681 results.isLongitude = getIsLongitude(str);
682
683
684
685 //Remove all the unwanted stuff
686 //Note: This method also replaces the symbols with ":"
687 //Note: In certain cases it may make the coord unparsable
688 str = removeCustomPatternParts(str);
689
690 str = removeWhiteSpace(str);
691
692 //Replace comma or dot with a current decimal separator
693 str = fixDecimalSeparator(str);
694
695
696 //Extract decimal part
697 decimalBit = str.split(decSeparatorRegEx);
698
699 //split degrees and minutes
700 ddmm = decimalBit[0].split(":");
701
702
703 try {
704 //extract values from the strings
705 dd = Integer.valueOf(ddmm[0]); //Degrees
706
707 if (ddmm.length > 1){//Minutes
708 //check if the string is not empty
709 if (ddmm[1] != "") { mm = Integer.valueOf(ddmm[1]); }
710 }
711
712 if (decimalBit.length > 1){//DecimalSeconds
713 //check if the string is not empty
714 if (decimalBit[1] != ""){
715 //replace the ":" if any (may be here as a result of custom symbol replacement
716 decimalBit[1] = decimalBit[1].replace(":", "");
717
718 mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
719 }
720 }
721 } catch (Exception e){
722 results.conversionSuccessful = false;
723 results.convertedCoord = 99999; //this is to mark an error...
724 results.conversionComments =
725 "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
726 "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
727 "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
728 ;
729
730 //exit method
731 return results;
732 }
733
734
735 checkDegreeRange(dd, results);
736 checkMinuteRange(mm, results);
737 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
738
739 } else if (pattern.description.equals("Custom variation of DD:MM:SS.SSS")){
740
741 //Sets pattern machted, successful, pattern type and pattern info
742 initializeResult(results, pattern);
743
744
745 //get sign
746 sign = getCustomSign(str);
747
748 //TODO still needs to be adapted to custom pattern
749 results.isLongitude = getIsLongitude(str);
750
751
752 //Remove all the unwanted stuff
753 //Note: This method also replaces the symbols with ":"
754 //Note: In certain cases it may make the coord unparsable
755 str = removeCustomPatternParts(str);
756
757 str = removeWhiteSpace(str);
758
759 //Replace comma or dot with a current decimal separator
760 str = fixDecimalSeparator(str);
761
762
763 //Extract decimal part
764 decimalBit = str.split(decSeparatorRegEx);
765
766 //split degrees and minutes
767 ddmmss = decimalBit[0].split(":");
768
769
770 try {
771
772 //extract values from the strings
773 dd = Integer.valueOf(ddmmss[0]); //Degrees
774 if (ddmmss.length > 1) {//Minutes
775 //check if the string is not empty
776 if (ddmmss[1] != "") {
777 mm = Integer.valueOf(ddmmss[1]);
778 }
779 }
780 if (ddmmss.length > 2){ //Seconds
781 //check if the string is not empty
782 if (ddmmss[2] != "") {
783 ss = Integer.valueOf(ddmmss[2]);
784 }
785 }
786 if (decimalBit.length > 1){ //DecimalSeconds
787 //check if the string is not empty
788 if (decimalBit[1] != "") {
789 sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
790 }
791 }
792 } catch (Exception e) {
793 results.conversionSuccessful = false;
794 results.convertedCoord = 99999; //this is to mark an error...
795 results.conversionComments =
796 "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
797 "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
798 "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
799 ;
800
801 //exit method
802 return results;
803 }
804
805
806 checkDegreeRange(dd, results);
807 checkMinuteRange(mm, results);
808 checkSecondRange(ss, results);
809
810 doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
811
812 }else { //default : pattern not recognized
813 results.patternRecognised = false;
814 results.patternType = pattern.description;
815 results.patternMatched = pattern.pattern;
816
817 results.conversionSuccessful = false;
818 results.convertedCoord = 99999; //this is to mark an error...
819
820 results.conversionComments = "Coordinate pattern not recognised!";
821
822 }
823
824 //do the self check here
825 results = selfTest(results);
826
827 //return conversion results
828 return results;
829 }
830
831
832 /**
833 * @param sign
834 * @param dd
835 * @param mm
836 * @param ss
837 * @param sss
838 * @param results
839 */
840 private void doConvertWithCheck(int sign, double dd, double mm, double mmm, double ss, double sss, ConversionResults results) {
841 double dec;
842 //Do the conversion if everything ok
843 if (results.conversionSuccessful){
844 results.conversionComments = "Conversion successful.";
845
846 dec = sign * (dd + (mm + mmm) / 60 + (ss + sss) / 3600);
847
848 //one more check to ensure a coord does not exceed 180
849 if (dec > 180 | dec < -180){
850 results.conversionSuccessful = false;
851 results.convertedCoord = 99999; //this is to mark an error...
852 results.conversionComments += "Coordinate is either > 180 or < -180; ";
853 } else {
854 results.convertedCoord = dec;
855
856 results.conversionComments = "Conversion successful.";
857
858 //Check whether the coordinate exceeds +/- 90 and mark it in comments
859
860 if (dec <= 90 && dec >= -90 && (results.isLongitude == null || results.isLongitude == false) ) {
861 results.canBeLat = true;
862 }else{
863 results.isLongitude = true;
864 }
865 }
866 }
867 }
868
869
870 /**
871 * @param ss
872 * @param results
873 */
874 private void checkSecondRange(double ss, ConversionResults results) {
875 if (ss > 59) {//seconds
876 results.conversionSuccessful = false;
877 results.convertedCoord = 99999; //this is to mark an error...
878 results.conversionComments += "Seconds fall outside the range: MM >= 60; ";
879 }
880 }
881
882
883 /**
884 * @param mm
885 * @param results
886 */
887 private void checkMinuteRange(double mm, ConversionResults results) {
888 if (mm > 59) {//minutes
889 results.conversionSuccessful = false;
890 results.convertedCoord = 99999; //this is to mark an error...
891 results.conversionComments += "Minutes fall outside the range: MM > 59; ";
892 }
893 }
894
895
896 /**
897 * @param dd
898 * @param results
899 */
900 private void checkDegreeRange(double dd, ConversionResults results) {
901 //do some additional checking if the coords fall into the range
902 if (dd < -180 | dd > 180){ //degree may require another param specifying whether it's lat or lon...
903 results.conversionSuccessful = false;
904 results.convertedCoord = 99999; //this is to mark an error...
905 results.conversionComments += "Degrees fall outside the range: DD < -180 | DD > 180; ";
906 }
907 }
908
909
910 /**
911 * @param str
912 * @return
913 */
914 private Boolean getIsLongitude(String str) {
915 //This regex checks for the negative hemisphere indicator
916 Pattern regexLatitudeNonAmbigous = Pattern.compile("(N|n)");
917 Pattern regexLatitudeAmbigous = Pattern.compile("(S|s)");
918
919 //This regex checks if there weren't any other hemisphere indicators
920 //it is needed for the specific case of the DDdMMmSSs S
921 //so it needs to be ensured there where no positive indicators
922 Pattern regexLongitude = Pattern.compile("(W|w|E|e)");
923
924 //if a positive indicator is found no need to search further
925 if (regexLongitude.matcher(str).find()){
926 return true;
927 }else if (regexLatitudeNonAmbigous.matcher(str).find()){
928 return false;
929 }else if (regexLatitudeAmbigous.matcher(str).find()){
930 Pattern regexLiteralUnits = Pattern.compile("(D|d|M|m)");
931
932 //if there are no other literal units we assume that S is a
933 //direction and not a second indicator
934 if (! regexLiteralUnits.matcher(str).find()){
935 return false;
936 }else if (regexLatitudeAmbigous.matcher(str).groupCount() > 1){
937 return false;
938 }else{
939 return null;
940 }
941 }else{
942 return null;
943 }
944 }
945
946
947 /**
948 * Sets pattern machted, successful, pattern type and pattern info
949 * @param results
950 * @param pattern
951 */
952 private void initializeResult(ConversionResults results,
953 CoordinatePattern pattern) {
954 //Pattern matched
955 results.patternRecognised = true;
956
957 //Matching pattern succeeded so intialy the parsing is ok
958 results.conversionSuccessful = true;
959
960 //pattern info
961 results.patternType = pattern.description;
962 results.patternMatched = pattern.pattern;
963 }
964
965
966 private ConversionResults selfTest(ConversionResults results){
967
968 ConversionResults newresults = results;
969
970 if (results.conversionSuccessful != false){
971 int sign = 1;
972 if (Math.signum(results.convertedCoord) < 0) {
973 sign = -1;
974 }
975
976 double decimalDegrees = sign * results.convertedCoord;
977 int fullDegrees;
978
979 double decimalMinutes;
980 int fullMinutes;
981
982 double decimalSeconds;
983 int fullSeconds;
984
985 //Get full degrees
986 fullDegrees = (int)Math.floor(decimalDegrees);
987
988 //get minutes
989 decimalMinutes = (decimalDegrees - fullDegrees) * 60;
990 fullMinutes = (int)Math.floor(decimalMinutes);
991
992 decimalSeconds = (decimalMinutes - fullMinutes) * 60;
993 fullSeconds = (int)Math.floor(decimalSeconds);
994
995 //save the test results
996 newresults.dd = fullDegrees;
997 newresults.mm = fullMinutes;
998 newresults.mmm = decimalSeconds;
999 newresults.ss = fullSeconds;
1000 newresults.sss = decimalSeconds;
1001
1002 }
1003
1004 return newresults;
1005
1006 }
1007
1008
1009
1010 //------------ CUSTOM PATTERN BUILDER--------------
1011
1012 public class CustomPatternIn {
1013 public String north;
1014 public String south;
1015 public String east;
1016 public String west;
1017
1018 public String degreeSymbol;
1019 public String minuteSymbol;
1020 public String secondSymbol;
1021
1022 public boolean caseInsensitive;
1023 public boolean allowWhiteSpace;
1024 public boolean priorityOverDefaultPatterns;
1025 public boolean disableDefaultPatterns;
1026
1027 }
1028
1029
1030 private class CustomPattern{
1031
1032 public List<CustomHemisphereIndicator> hemisphereIndicators;
1033
1034 public String degreeSymbol;
1035 public String minuteSymbol;
1036 public String secondSymbol;
1037
1038 public boolean caseInsensitive;
1039
1040 }
1041
1042 //global variable to be used if a custom pattern is used
1043 private CustomPattern customPtrn;
1044
1045 //escape some of the chars
1046 private String escapeChars(String str){
1047 // backslash - first so it is not messed when other escape chars are corrected for being used in a string
1048 str = str.replace("\\", "\\\\");
1049
1050 //dot and comma
1051 str = str.replace(".", "\\.");
1052 str = str.replace(",", "\\,");
1053
1054 //brackets
1055 str = str.replace("(", "\\(");
1056 str = str.replace(")", "\\)");
1057 str = str.replace("[", "\\[");
1058 str = str.replace("]", "\\]");
1059 str = str.replace("{", "\\{");
1060 str = str.replace("}", "\\}");
1061
1062 //other replacements
1063 str = str.replace("^", "\\^");
1064 str = str.replace("$", "\\$");
1065 str = str.replace("+", "\\+");
1066 str = str.replace("*", "\\*");
1067 str = str.replace("?", "\\?");
1068 str = str.replace("|", "\\|");
1069
1070 return str;
1071 }
1072
1073
1074 //this implements sorting by using system.Icomparable - sorting is needed later when replacing
1075 private class CustomHemisphereIndicator implements Comparable<CustomHemisphereIndicator> {
1076 //private variables
1077 private int m_length;
1078 private String m_name;
1079 private String m_indicator;
1080 private boolean m_positive;
1081
1082 //constructor
1083 public CustomHemisphereIndicator(String name, String indicator, int length, boolean positive){
1084 this.m_name = name;
1085 this.m_indicator = indicator;
1086 this.m_length = length;
1087 this.m_positive = positive;
1088 }
1089
1090 //properties
1091
1092 public String getName(){
1093 return this.m_name;
1094 }
1095 public void setName(String value){
1096 this.m_name = value;
1097 }
1098
1099 public String getIndicator(){
1100 return this.m_indicator;
1101 }
1102 public void setIndicator(String value){
1103 this.m_indicator = value;
1104 }
1105
1106 public int getLength(){
1107 return this.m_length;
1108 }
1109 public void setLength(int value){
1110 this.m_length = value;
1111 }
1112
1113
1114 public boolean getPositive(){
1115 return this.m_positive;
1116 }
1117 public void setPositive(boolean value){
1118 this.m_positive = value;
1119 }
1120
1121 /* Less than zero if this instance is less than obj.
1122 * Zero if this instance is equal to obj.
1123 * Greater than zero if this instance is greater than obj.
1124 *
1125 * This method uses the predefined method Int32.CompareTo
1126 * */
1127
1128 @Override
1129 public int compareTo(CustomHemisphereIndicator ind){
1130
1131 //no need to rewrite the code again, we have Integer.compareTo ready to use
1132 return Integer.valueOf(this.getLength()).compareTo(Integer.valueOf(ind.getLength()));
1133 }
1134 }
1135
1136
1137
1138 //This adds custom pattern to a list of already predefined patterns
1139 //useful for batch conversions - allows for totally mixed input data (predefined & custom)
1140 public void addCustomPattern(CustomPatternIn patternIn){
1141
1142 //new custom pattern object - to pass the needed data farther
1143 CustomPattern pattern = new CustomPattern();
1144
1145 //keep indicators for parsing
1146 List<CustomHemisphereIndicator> indicators = new ArrayList<CustomHemisphereIndicator>();
1147
1148 //north
1149 CustomHemisphereIndicator ind = new CustomHemisphereIndicator("North", patternIn.north, patternIn.north.length() ,true);
1150 indicators.add(ind);
1151
1152 //south
1153 ind = new CustomHemisphereIndicator("South", patternIn.south, patternIn.south.length(), false);
1154 indicators.add(ind);
1155
1156 //east
1157 ind = new CustomHemisphereIndicator("East", patternIn.east, patternIn.east.length(), true);
1158 indicators.add(ind);
1159
1160 //west
1161 ind = new CustomHemisphereIndicator("West", patternIn.west, patternIn.west.length(), false);
1162 indicators.add(ind);
1163
1164 //sort the arraylist
1165 Collections.sort(indicators, lengthComparator);
1166
1167
1168 //add it to the pattern object
1169 pattern.hemisphereIndicators = indicators;
1170
1171 //case insensitive
1172 pattern.caseInsensitive = patternIn.caseInsensitive;
1173
1174 //keep symbols for parsing
1175 pattern.degreeSymbol = patternIn.degreeSymbol;
1176 pattern.minuteSymbol = patternIn.minuteSymbol;
1177 pattern.secondSymbol = patternIn.secondSymbol;
1178
1179
1180 //save the data
1181 customPtrn = pattern;
1182
1183
1184 //----------------build custom patterns----------------
1185
1186 //prepare hemisphere indicators
1187 String north = escapeChars(patternIn.north);
1188 String south = escapeChars(patternIn.south);
1189 String east = escapeChars(patternIn.east);
1190 String west = escapeChars(patternIn.west);
1191
1192 //prepare symbols
1193 String degreesymbol = "";
1194 if (patternIn.degreeSymbol != ""){
1195 degreesymbol = "(" + escapeChars(patternIn.degreeSymbol) + ")?";
1196 }
1197
1198 String minutesymbol = "";
1199 if (patternIn.minuteSymbol != ""){
1200 minutesymbol = "(" + escapeChars(patternIn.minuteSymbol) + ")?";
1201 }
1202
1203 String secondsymbol = "";
1204 if (escapeChars(patternIn.secondSymbol) != ""){
1205 secondsymbol = "(" + escapeChars(patternIn.secondSymbol) + ")?";
1206 }
1207
1208
1209 //is the pattern to be case insensitive?
1210 String CaseInsensitive = "";
1211 if (patternIn.caseInsensitive){
1212 CaseInsensitive = "(?i)";
1213 }
1214
1215 //allow whitespace
1216 String WhiteSpace = "";
1217 if (patternIn.allowWhiteSpace == true){
1218 WhiteSpace = "(\\s)*";
1219 }
1220
1221 //hemisphere indicator
1222 String HemisphereIndicator = "";
1223
1224 //add north if present
1225 if (north == ""){
1226 HemisphereIndicator += south;
1227 }else{
1228 HemisphereIndicator += north;
1229 if (south != ""){
1230 HemisphereIndicator += "|" + south;
1231 }
1232 }
1233
1234 //add east
1235 if (north == "" & south == ""){
1236 HemisphereIndicator += east;
1237 } else {
1238 if (east != ""){
1239 HemisphereIndicator += "|" + east;
1240 }
1241 }
1242
1243 //add west
1244 if (north == "" & south == "" & east == ""){
1245 HemisphereIndicator += west;
1246 } else {
1247 if (west != "") {
1248 HemisphereIndicator += "|" + west;
1249 }
1250 }
1251
1252 //add remaining bits if not empty
1253 if (HemisphereIndicator != "") {
1254 HemisphereIndicator = "(" + HemisphereIndicator + ")?";
1255 }
1256
1257 List<CoordinatePattern> customPatterns = new ArrayList<CoordinatePattern>();
1258
1259 //create custom patterns based on the specified user's input
1260 CoordinatePattern ptrn;
1261
1262 //Custom variation of DD.DDD
1263 ptrn = new CoordinatePattern();
1264 ptrn.description = "Custom variation of DD.DDD";
1265 ptrn.pattern =
1266 CaseInsensitive + "(^" +
1267 WhiteSpace + HemisphereIndicator + WhiteSpace +
1268 "(" +
1269 "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + "$)" +
1270 ")" +
1271 "|(^" + WhiteSpace +
1272 "(" +
1273 "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + ")" +
1274 ")" +
1275 HemisphereIndicator + WhiteSpace + "$" +
1276 "))"
1277 ;
1278 customPatterns.add(ptrn);
1279
1280 //Custom variation of DD:MM.MMM
1281 ptrn = new CoordinatePattern();
1282 ptrn.description = "Custom variation of DD:MM.MMM";
1283 ptrn.pattern =
1284 CaseInsensitive + "(^" +
1285 WhiteSpace + HemisphereIndicator + WhiteSpace +
1286 "(" +
1287 "(\\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 + "$)" +
1288 ")" +
1289 "|(^" + WhiteSpace +
1290 "(" +
1291 "(\\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 + ")" +
1292 ")" +
1293 HemisphereIndicator + WhiteSpace + "$" +
1294 "))"
1295 ;
1296 customPatterns.add(ptrn);
1297
1298 //Custom variation of DD:MM:SS.SSS
1299 ptrn = new CoordinatePattern();
1300 ptrn.description = "Custom variation of DD:MM:SS.SSS";
1301 ptrn.pattern =
1302 CaseInsensitive + "(^" +
1303 WhiteSpace + HemisphereIndicator + WhiteSpace +
1304 "(" +
1305 "(\\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 + "$)" +
1306 ")" +
1307 "|(^" + WhiteSpace +
1308 "(" +
1309 "(\\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 + ")" +
1310 ")" +
1311 HemisphereIndicator + WhiteSpace + "$" +
1312 "))"
1313 ;
1314 customPatterns.add(ptrn);
1315
1316 //check if the default patterns are to be used
1317 if (patternIn.disableDefaultPatterns) {
1318 patterns = customPatterns;
1319 } else { //if all patterns are to be used check which set has the matching priority
1320
1321 //check if the custom patterns are to have priority over the default ones
1322 if (patternIn.priorityOverDefaultPatterns){
1323
1324 //add default patterns to the custom patterns
1325 for (int i = 0; i < patterns.size(); i++){
1326 customPatterns.add(patterns.get(i));
1327 }
1328
1329 //swap array lists
1330 patterns = customPatterns;
1331
1332 }else{
1333 //add custom patterns to the default patterns
1334 for (int i = 0; i < customPatterns.size(); i++){
1335 patterns.add(customPatterns.get(i));
1336
1337 }
1338 }
1339 }
1340 }
1341
1342 }