Project

General

Profile

Download (48.9 KB) Statistics
| Branch: | Tag: | Revision:
1
// $Id$
2
/**
3
* Copyright (C) 2009 EDIT
4
* European Distributed Institute of Taxonomy 
5
* http://www.e-taxonomy.eu
6
* 
7
* The contents of this file are subject to the Mozilla Public License Version 1.1
8
* See LICENSE.TXT at the top of this package for the full license terms.
9
* 
10
* This file is an Java adaption from the orginal CoordinateConverter written by Dominik Mikiewicz
11
* @see www.cartomatic.pl
12
* @see http://dev.e-taxonomy.eu/svn/trunk/geo/coordinateConverter/CoordinateConverter.cs
13
* @see http://gis.miiz.waw.pl/webapps/coordinateconverter/
14
*/
15
package eu.etaxonomy.cdm.strategy.parser.location;
16

    
17
import java.util.ArrayList;
18
import java.util.Collections;
19
import java.util.Comparator;
20
import java.util.List;
21
import java.util.regex.Pattern;
22

    
23
import org.apache.log4j.Logger;
24

    
25
/**
26
 * @author a.mueller
27
 * @date 07.06.2010
28
 *
29
 */
30
public class CoordinateConverter {
31
	@SuppressWarnings("unused")
32
	private static final Logger logger = Logger.getLogger(CoordinateConverter.class);
33

    
34
    //Patterns
35
    private List<CoordinatePattern> patterns;
36

    
37

    
38
    private class CoordinatePattern{
39
    	String description;
40
    	String pattern;
41
    }
42
    
43
    
44
    private Comparator<CustomHemisphereIndicator> lengthComparator = new Comparator<CustomHemisphereIndicator>(){
45
		public int compare(CustomHemisphereIndicator ind1, CustomHemisphereIndicator ind2) {
46
			return Integer.valueOf(ind1.getLength()).compareTo(ind2.getLength());
47
		}
48
    };
49
    
50
    //Class constructor
51
    public CoordinateConverter() {
52
        //initialise pattern array
53
        patterns = new ArrayList<CoordinatePattern>();
54

    
55
        //temp pattern variable
56
        CoordinatePattern pattern;
57

    
58

    
59
        //variations of DD.DDD with white space characters
60
        pattern = new CoordinatePattern();
61
        pattern.description = "Variation of DD.DDD";
62
        pattern.pattern =
63
            //+/-/Nn/Ss/Ww/EeDD.DDDD
64
            "(^" +
65
            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
66
            "((\\d{1,3}(\\.|\\,)?(\\s)*$)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*$))" +
67
            ")" +
68
            ////DD.DDDDNn/Ss/Ww/Ee
69
            "|(^" +
70
            "(\\s)*((\\d{1,3}(\\.|\\,)?(\\s)*)|(\\d{1,3}(\\.|\\,)\\d+(\\s)*))" +
71
            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
72
            ")";
73
        patterns.add(pattern);
74

    
75

    
76
        //Variations of DD(\u00B0|d)MM.MMM' with whitespace characters
77
        pattern = new CoordinatePattern();
78
        pattern.description = "Variation of DD(\u00B0|d)MM.MMM('|m)";
79
        pattern.pattern =
80
            "(^" +
81
            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
82
            "((\\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)*$))" +
83
            ")" +
84
            "|(^" +
85
            "(\\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)*))" +
86
            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
87
            ")";
88
        patterns.add(pattern);
89

    
90

    
91
        //Variations of DD\u00B0MM'SS.SSS" with whitespace characters
92
        pattern = new CoordinatePattern();
93
        pattern.description = "Variation of DD(\u00B0|d)MM(\u02B9|m)SS.SSS(\u02BA|s)";
94
        pattern.pattern =
95
            //+/-/Nn/Ss/Ww/EeDD\u00B0MM\u02B9SS.SSS
96
            "(^" +
97
            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
98
            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)?(\\s)*$)|(\\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)*$)|(\\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)*$))" +
99
            ")" +
100
            //DD°MM\u02B9SS.SSSNn/Ss/Ww/Ee
101
            "|(^" +
102
            "(\\s)*((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)?(\\s)*)|(\\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)*)|(\\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)*))" +
103
            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
104
            ")";
105
        patterns.add(pattern);
106

    
107

    
108
        //Variations of DD:MM:SS.SSS with whitespace characters
109
        pattern = new CoordinatePattern();
110
        pattern.description = "Variation of DD:MM:SS.SSS";
111
        pattern.pattern =
112
            // +/-/Nn/Ss/Ww/EeDD:MM:SS.SSS
113
            "(^" +
114
            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
115
            "((\\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)*$))" +
116
            ")" +
117
            //DD:MM:SS.SSSNn/Ss/Ww/Ee
118
            "|(^" +
119
            "(\\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)*))" +
120
            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
121
            ")";
122
        patterns.add(pattern);
123

    
124
    }
125

    
126

    
127
    //tests if a string matches one of the defined patterns
128
    private int matchPattern(String str){
129
        int recognised = -1;
130

    
131
        //match the string against each available patern
132
        for (int i = 0; i < patterns.size(); i++){
133

    
134
        	CoordinatePattern pattern = patterns.get(i);
135
        	Pattern regEx = Pattern.compile(pattern.pattern);
136
        	if (regEx.matcher(str).find()) { 
137
        		recognised = i; 
138
            	break; 
139
            }
140

    
141
        }
142
        return recognised;
143
    }
144

    
145

    
146
    //gets sign of the coordinate (tests for presence of negative sign)
147
    private int getSign(String str){
148

    
149
        //This regex checks for the negative hemisphere indicator
150
        Pattern regexNegative = Pattern.compile("(-|S|s|W|w)");
151

    
152
        //This regex checks if there weren't any other hemisphere indicators
153
        //it is needed for the specific case of the DDdMMmSSs S
154
        //so it needs to be ensured there where no positive indicators
155
        Pattern regexPositive = Pattern.compile("(\\+|N|n|E|e)");
156

    
157
        //if a positive indicator is found no need to search further
158
        if (regexPositive.matcher(str).find()){
159
            return 1;
160
        }else{
161
            //if not check whether there was a negative indicator. if so negate otherwise return positive
162
            if (regexNegative.matcher(str).find()){
163
                return -1;
164
            }else{
165
                return 1;
166
            }
167
        }
168
    }
169

    
170

    
171
    //this checks for the coordinate sign by evaluating user supplied data
172
    private int getCustomSign(String str){
173
        //Note:
174
        //Indicators are evaluated from the longest ones to the shortes ones
175
        //So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
176

    
177
        
178
        //search for the presence of indicators
179
        boolean hasPositive = false;
180
        boolean hasNegative = false;
181

    
182
        //keep previous negative indicators here
183
        List<String> previousNegatives = new ArrayList<String>();
184
        
185
        //compare the string with user supplied custom pattern
186
        for (int x = customPtrn.hemisphereIndicators.size() - 1; x >= 0; x--){
187

    
188
            CustomHemisphereIndicator ind = customPtrn.hemisphereIndicators.get(x);
189

    
190
            //test here if the indicator exists (has length >0)
191
            if (ind.getLength() > 0){
192

    
193
                //check if the supplied pattern was marked as case insensitive?
194
                String caseInsensitive = "";
195

    
196
                if (customPtrn.caseInsensitive){
197
                    caseInsensitive = "(?i)";
198
                }
199

    
200
                //create a regex
201
                Pattern tempRegex = Pattern.compile(caseInsensitive + ind.getIndicator());
202

    
203
                //if a pattern is found
204
                if (tempRegex.matcher(str).find()){
205
                    //check whether it's a positive or negative indicator
206
                    if (ind.getPositive()){
207
                        /* Note:
208
                         * See the note below to understand why checking for previous negatives is performed here
209
                        */
210
                        
211
                        //check the previous negatives
212
                        if (previousNegatives.size() != 0){
213
                            boolean sameNegative = false;
214

    
215
                            for (int i = previousNegatives.size() - 1; i >= 0; i--){
216
                                if (ind.getIndicator() == previousNegatives.get(i)){
217
                                    sameNegative = true;
218
                                    break;
219
                                }
220
                            }
221

    
222
                            //mark as positive only if the previously found negative is the same
223
                            if (sameNegative){
224
                                hasPositive = true;
225
                            }
226

    
227
                        }else{ //if no negatives before it already marks the sign as positive 
228
                            hasPositive = true;
229
                        }
230

    
231
                    } else {
232
                        /* Note:
233
                         * save the negative indicator here so it can be compared later if a positive wants to overwrite it!
234
                         * in a case a longer negative "Pn" has already been found a shorter positive "P" will not overwrite it
235
                         * and the hasPositive will remain false;
236
                         * In a case a "P" negative indicator has already been found, positive will mark hasPositive and therefore
237
                         * later a default positive value will be returned (if the indicators for positive & negative are the same
238
                         * positive is returned)
239
                         * testing for previous positives is not required since if a hasPositive is already true method will return
240
                         * true anyway
241
                         * 
242
                        */
243
                        previousNegatives.add(ind.getIndicator());
244
                        
245
                        hasNegative = true;
246

    
247
                    }
248
                }
249
            }
250
            
251
        }
252

    
253
        //Note:
254
        //positive indicator has priority here - if both indicators supplied by the user are the same, a positive is chosen
255
        //if there were no indicator found in the tested coordinate, a positive value is returned by default
256

    
257
        if (hasPositive){
258
            return 1;
259
        } else {
260
            if (hasNegative) {
261
                return -1;
262
            } else {
263
                return 1;
264
            }
265
        }
266
    }
267

    
268
      
269

    
270
    //returns a currently used decimal separator
271
    private String getDecimalSeparator(){
272
    	//TODO not yet transformed from C#
273
//    	return System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
274
        return ".";
275
    }
276

    
277

    
278
    //replaces comma or dot for current decimal separator
279
    private String fixDecimalSeparator(String str){
280
        //Note:
281
        //Coma is replaced as parsers often recognise dot as a decimal separator
282
        //Comma or dot is replaced with a decimal separator here (environment settings)
283
        //But decimal separator has to be used later too;
284

    
285
        String regExReplaceComma = "(\\,|\\.)";
286
        str = str.replaceAll(regExReplaceComma, getDecimalSeparator());
287
        
288
        return str;
289
    }
290

    
291

    
292
    //removes sign
293
    private String removeSign(String str){
294
        String regExRemoveSign = "(\\+|-|S|s|W|w|N|n|E|e)";
295
        str = str.replaceAll(regExRemoveSign, "");
296
        return str;
297
    }
298

    
299
    //removes custom sign indicators
300
    private String removeCustomPatternParts(String str){
301

    
302
        /* Note:
303
         * Symbols are added here so the removing tries to not affect the coordinate too much
304
         * Strings to be removed then are evaluated from the longest ones to the shortes ones
305
         * So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
306
         * */
307

    
308
        //CustomHemisphereIndicator is used here so another object does not have to be created
309
        //only for the string cleanning
310
        List<CustomHemisphereIndicator> stringsToRemove = customPtrn.hemisphereIndicators;
311

    
312
        //add degree symbol
313
        CustomHemisphereIndicator stringToRemove = new CustomHemisphereIndicator("Degree", customPtrn.degreeSymbol,customPtrn.degreeSymbol.length(), false);
314
        stringsToRemove.add(stringToRemove);
315

    
316
        //add minute symbol
317
        stringToRemove = new CustomHemisphereIndicator("Minute", customPtrn.minuteSymbol, customPtrn.minuteSymbol.length(), false);
318
        stringsToRemove.add(stringToRemove);
319

    
320
        //add second symbol
321
        stringToRemove = new CustomHemisphereIndicator("Second", customPtrn.secondSymbol, customPtrn.secondSymbol.length(), false);
322
        stringsToRemove.add(stringToRemove);
323

    
324
        //sort the list (by element's Length property)
325
        Collections.sort(stringsToRemove, lengthComparator);
326
        
327
        
328
//        ListSelectionEv.sort(lengthComparator);
329

    
330

    
331
        for (int x = stringsToRemove.size() - 1; x >= 0; x--){
332

    
333
            CustomHemisphereIndicator toBeRemoved = stringsToRemove.get(x);
334

    
335
            //check if the string exists so replacing does not yield errors
336
            if (toBeRemoved.getLength() > 0)
337
            {
338
                //check if the supplied pattern was marked as case insensitive?
339
                String CaseInsensitive = "";
340

    
341
                if (customPtrn.caseInsensitive){
342
                    CaseInsensitive = "(?i)";
343
                }
344

    
345
                //create regex for replacing
346
                String tempRegex = CaseInsensitive + toBeRemoved.getIndicator();
347

    
348

    
349
                if (toBeRemoved.getName().equals("Degree") || toBeRemoved.getName().equals("Minute")) {
350
                    //replace with a symbol used later for splitting
351
                    str =  str.replaceAll(tempRegex, ":");
352
                } else {
353
                    //remove the string
354
                    str = str.replaceAll(tempRegex, "");
355
                }
356
            }
357
        }
358
        return str;
359
    }
360

    
361

    
362

    
363
    //removes whitespace characters
364
    private String removeWhiteSpace(String str){
365
        str = str.replaceFirst("\\s*", "");
366
        return str;
367
    }
368

    
369

    
370
    //Object for the conversion results
371
    public class ConversionResults{
372
        public boolean patternRecognised;
373
        public String patternMatched;
374
        public String patternType;
375

    
376
        public boolean conversionSuccessful;
377
        public double convertedCoord;
378
        public boolean canBeLat;
379
        
380
        public String conversionComments;
381
        
382
        public Boolean isLongitude;
383

    
384
        public int dd;
385
        public int mm;
386
        public double mmm;
387
        public int ss;
388
        public double sss;
389

    
390
    }
391

    
392

    
393
    public ConversionResults tryConvert(String str){
394
        //some local variables
395
        int sign; //sign of the coordinate
396
        String[] decimalBit, ddmmss, ddmm; //arrays for splitting
397
        double dd = 0, mm = 0, ss = 0, mmm = 0, sss = 0, dec = 0; //parts of the coordinates
398
        
399
        String decSeparatorRaw = String.valueOf(getDecimalSeparator()); //gets the current decimal separator
400
        String decSeparatorRegEx = decSeparatorRaw.replace(".", "\\.");
401
        
402
        ConversionResults results = new ConversionResults();
403

    
404
        //Get the matched pattern
405
        CoordinatePattern pattern;
406
        int ptrnnum = matchPattern(str);
407
        if (ptrnnum != -1) {
408
            pattern = patterns.get(ptrnnum);
409
        } else {
410
            pattern = new CoordinatePattern();
411
            pattern.description = "Unknown";
412
            pattern.pattern = "No pattern matched";
413
        }
414

    
415

    
416

    
417
        if (pattern.description.equals("Variation of DD.DDD")){
418
            
419
       	  	//Sets pattern machted, successful, pattern type and pattern info
420
        	initializeResult(results, pattern);
421

    
422
            //get sign
423
            sign = getSign(str);
424
            results.isLongitude = getIsLongitude(str);
425
            
426
            //Replace comma or dot with a current decimal separator
427
            str = fixDecimalSeparator(str);
428

    
429
            //Remove all the unwanted stuff
430
            str = removeSign(str);
431
            str = removeWhiteSpace(str);
432

    
433
            //Since this is already a decimal degree no spliting is needed
434
            dd =  Double.valueOf(str);
435

    
436
            checkDegreeRange(dd, results);
437
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
438

    
439
        }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM.MMM('|m)")){
440

    
441
        	//Sets pattern machted, successful, pattern type and pattern info
442
        	initializeResult(results, pattern);
443

    
444
            //get sign
445
            sign = getSign(str);
446
            results.isLongitude = getIsLongitude(str);
447
            
448
            //Replace comma or dot with a current decimal separator
449
            str = fixDecimalSeparator(str);
450

    
451
            //Remove all the unwanted stuff
452
            str = removeSign(str);
453
            str = removeWhiteSpace(str);
454

    
455
            //do some further replacing
456
            //Replace degree symbol
457
            str = str.replaceAll("(\u00B0|\u00B0|D|d)", ":");
458
            
459
            //remove minute symbol
460
            str = str.replaceAll("(\u02B9|'|M|m)", "");
461
            
462
            //Extract decimal part
463
            decimalBit = str.split(decSeparatorRegEx);
464
            
465
            //split degrees and minutes
466
            ddmm = decimalBit[0].split(":");
467

    
468

    
469
            //extract values from the strings
470
            dd = Integer.valueOf(ddmm[0]); //Degrees
471

    
472
            if (ddmm.length > 1){ //Minutes
473
                //check if the string is not empty
474
                if (ddmm[1] != "") { 
475
                	mm = Integer.valueOf(ddmm[1]); 
476
                }
477
            }
478

    
479
            if (decimalBit.length > 1){//DecimalSeconds
480
                //check if the string is not empty
481
                if (decimalBit[1] != "") {
482
                    mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
483
                }
484
            }
485

    
486
            checkDegreeRange(dd, results);
487
            checkMinuteRange(mm, results);
488
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
489
            
490
        }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM(\u02B9|m)SS.SSS(\u02BA|s)")){
491
        	
492
        	/* 
493
             * Note:
494
             * This pattern allows the seconds to be specified with S, s or " or nothing at all
495
             * If the seconds are marked with "s" and there is no other indication of the hemisphere
496
             * the coordinate will be parsed as southern (negative).
497
             * 
498
             * If the N / E / W / + indicator is found the coordinate will be parsed appropriately no matter
499
             * what is the second notation
500
            */
501

    
502
        	//Sets pattern machted, successful, pattern type and pattern info
503
        	initializeResult(results, pattern);
504

    
505
            //get sign
506
            sign = getSign(str);
507
            //TODO test S
508
            results.isLongitude = getIsLongitude(str);
509
            
510
            //Replace comma or dot with a current decimal separator
511
            str = fixDecimalSeparator(str);
512

    
513
            //Remove all the unwanted stuff
514
            str = removeSign(str);
515
            str = removeWhiteSpace(str);
516

    
517
            //remove second symbol (s is removed by the get sign method)
518
            //double apostrophe is not removed here as single apostrphe may mark minutes!
519
            //it's taken care of later after extracting the decimal part
520
            str = str.replaceAll("(u\02BA|\")", "");
521
            
522
            //do some further replacing
523
            //Replace degree symbol
524
            str = str.replaceAll("(\u00B0|\u00B0|D|d|\u02B9|'|M|m)",":");
525

    
526
            //Extract decimal part
527
            decimalBit = str.split(decSeparatorRegEx);
528

    
529
            //remove : from the decimal part [1]! This is needed when a double apostrophe was used to mark seconds
530
            if (decimalBit.length > 1)
531
            {
532
                decimalBit[1].replace(":", "");
533
            }
534

    
535
            //split degrees and minutes
536
            ddmmss = decimalBit[0].split(":");
537

    
538

    
539
            //extract values from the strings
540
            dd = Integer.valueOf(ddmmss[0]); //Degrees
541
            if (ddmmss.length > 1){//Minutes
542
                //check if the string is not empty
543
                if (ddmmss[1] != "") { 
544
                	mm = Integer.valueOf(ddmmss[1]); 
545
                }
546
            }
547
            if (ddmmss.length > 2){//Seconds
548
                //check if the string is not empty
549
                if (ddmmss[2] != "") { 
550
                	ss = Integer.valueOf(ddmmss[2]); 
551
                }
552
            }
553
            if (decimalBit.length > 1) { //DecimalSeconds
554
                //check if the string is not empty
555
                if (decimalBit[1] != "") {
556
                    sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
557
                }
558
            }
559

    
560
            checkDegreeRange(dd, results);
561
            checkMinuteRange(mm, results);
562
            checkSecondRange(ss, results);
563

    
564
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
565

    
566
        }else if (pattern.description.equals("Variation of DD:MM:SS.SSS")){
567

    
568
        	//Sets pattern machted, successful, pattern type and pattern info
569
        	initializeResult(results, pattern);
570

    
571
            //get sign
572
            sign = getSign(str);
573
            results.isLongitude = getIsLongitude(str);
574
            
575
            //Replace comma or dot with a current decimal separator
576
            str = fixDecimalSeparator(str);
577

    
578
            //Remove all the unwanted stuff
579
            str = removeSign(str);
580
            str = removeWhiteSpace(str);
581

    
582
            //Do some splitting
583
            decimalBit = str.split(decSeparatorRegEx);
584
            ddmmss = decimalBit[0].split(":");
585

    
586

    
587
            //extract values from the strings
588
            dd = Integer.valueOf(ddmmss[0]); //Degrees
589
            if (ddmmss.length > 1)//Minutes
590
            {
591
                //check if the string is not empty
592
                if (ddmmss[1] != "") { mm = Integer.valueOf(ddmmss[1]); }
593
            }
594
            if (ddmmss.length > 2) {//Seconds{
595
                //check if the string is not empty
596
                if (ddmmss[2] != "") { 
597
                	ss = Integer.valueOf(ddmmss[2]); 
598
                }
599
            }
600
            if (decimalBit.length > 1) { //DecimalSeconds
601
                //check if the string is not empty
602
                if (decimalBit[1] != "") {
603
                    sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
604
                }
605
            }
606

    
607
            checkDegreeRange(dd, results);
608
            checkMinuteRange(mm, results);
609
            checkSecondRange(ss, results);
610

    
611
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
612

    
613
        }else if (pattern.description.equals("Custom variation of DD.DDD")){
614

    
615
        	//Sets pattern machted, successful, pattern type and pattern info
616
        	initializeResult(results, pattern);
617
	
618
	
619
	        //get sign
620
	        sign = getCustomSign(str);
621
	       
622
	        //TODO still needs to be adapted to custom pattern
623
	        results.isLongitude = getIsLongitude(str);
624
	           
625
	
626
	        //Remove all the unwanted stuff
627
	        //Note: This method also replaces the symbols with ":"
628
	        //Note: In certain cases it may make the coord unparsable
629
	        str = removeCustomPatternParts(str);
630
	
631
	        str = removeWhiteSpace(str);
632
	
633
	        //Replace comma or dot with a current decimal separator
634
	        str = fixDecimalSeparator(str);
635
	
636
	        //remove the ":" here as it is not needed here for decimal degrees
637
	        str = str.replace(":", "");
638
	
639
	        try {
640
	            //Since this is already a decimal degree no spliting is needed
641
	            dd = Double.valueOf(str);
642
	        } catch (Exception e)  {
643
	            results.conversionSuccessful = false;
644
	            results.convertedCoord = 99999; //this is to mark an error...
645
	            results.conversionComments =
646
	                "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
647
	                "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " + 
648
	                "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
649
	                ;
650
	
651
	            //exit method
652
	            return results;
653
	        }
654
	
655
	        //Since this is already a decimal degree no spliting is needed
656
	        dd = Double.valueOf(str);
657
	
658
	        checkDegreeRange(dd, results);
659
	        doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
660
	        
661
	
662
	    }else if (pattern.description.equals("Custom variation of DD:MM.MMM")){
663
           //-------------Customs patterns start here-------------
664

    
665
	    	//Sets pattern machted, successful, pattern type and pattern info
666
        	initializeResult(results, pattern);
667

    
668
            //get sign
669
            sign = getCustomSign(str);
670
          
671
            //TODO still needs to be adapted to custom pattern
672
	        results.isLongitude = getIsLongitude(str);
673
	        
674

    
675

    
676
            //Remove all the unwanted stuff
677
            //Note: This method also replaces the symbols with ":"
678
            //Note: In certain cases it may make the coord unparsable
679
            str = removeCustomPatternParts(str);
680

    
681
            str = removeWhiteSpace(str);
682

    
683
            //Replace comma or dot with a current decimal separator
684
            str = fixDecimalSeparator(str);
685

    
686
            
687
            //Extract decimal part
688
            decimalBit = str.split(decSeparatorRegEx);
689

    
690
            //split degrees and minutes
691
            ddmm = decimalBit[0].split(":");
692

    
693

    
694
            try {
695
                //extract values from the strings
696
                dd = Integer.valueOf(ddmm[0]); //Degrees
697

    
698
                if (ddmm.length > 1){//Minutes
699
                    //check if the string is not empty
700
                    if (ddmm[1] != "") { mm = Integer.valueOf(ddmm[1]); }
701
                }
702

    
703
                if (decimalBit.length > 1){//DecimalSeconds
704
                    //check if the string is not empty
705
                    if (decimalBit[1] != ""){
706
                        //replace the ":" if any (may be here as a result of custom symbol replacement
707
                        decimalBit[1] = decimalBit[1].replace(":", "");
708

    
709
                        mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
710
                    }
711
                }
712
            } catch (Exception e){
713
                results.conversionSuccessful = false;
714
                results.convertedCoord = 99999; //this is to mark an error...
715
                results.conversionComments =
716
                    "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
717
                    "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
718
                    "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
719
                    ;
720

    
721
                //exit method
722
                return results;
723
            }
724

    
725

    
726
            checkDegreeRange(dd, results);
727
            checkMinuteRange(mm, results);
728
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
729

    
730
	    } else if (pattern.description.equals("Custom variation of DD:MM:SS.SSS")){
731

    
732
	    	//Sets pattern machted, successful, pattern type and pattern info
733
        	initializeResult(results, pattern);
734

    
735

    
736
            //get sign
737
            sign = getCustomSign(str);
738

    
739
            //TODO still needs to be adapted to custom pattern
740
	        results.isLongitude = getIsLongitude(str);
741
	        
742

    
743
            //Remove all the unwanted stuff
744
            //Note: This method also replaces the symbols with ":"
745
            //Note: In certain cases it may make the coord unparsable
746
            str = removeCustomPatternParts(str);
747

    
748
            str = removeWhiteSpace(str);
749

    
750
            //Replace comma or dot with a current decimal separator
751
            str = fixDecimalSeparator(str);
752

    
753

    
754
            //Extract decimal part
755
            decimalBit = str.split(decSeparatorRegEx);
756

    
757
            //split degrees and minutes
758
            ddmmss = decimalBit[0].split(":");
759

    
760

    
761
            try {
762

    
763
                //extract values from the strings
764
                dd = Integer.valueOf(ddmmss[0]); //Degrees
765
                if (ddmmss.length > 1) {//Minutes
766
                    //check if the string is not empty
767
                    if (ddmmss[1] != "") { 
768
                    	mm = Integer.valueOf(ddmmss[1]); 
769
                    }
770
                }
771
                if (ddmmss.length > 2){ //Seconds
772
                    //check if the string is not empty
773
                    if (ddmmss[2] != "") { 
774
                    	ss = Integer.valueOf(ddmmss[2]); 
775
                    }
776
                }
777
                if (decimalBit.length > 1){ //DecimalSeconds
778
                    //check if the string is not empty
779
                    if (decimalBit[1] != "") {
780
                        sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
781
                    }
782
                }
783
            } catch (Exception e) {
784
                results.conversionSuccessful = false;
785
                results.convertedCoord = 99999; //this is to mark an error...
786
                results.conversionComments =
787
                    "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
788
                    "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
789
                    "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
790
                    ;
791

    
792
                //exit method
793
                return results;
794
            }
795

    
796

    
797
            checkDegreeRange(dd, results);
798
            checkMinuteRange(mm, results);
799
            checkSecondRange(ss, results);
800

    
801
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
802

    
803
	    }else {   //default  : pattern not recognized
804
            results.patternRecognised = false;
805
            results.patternType = pattern.description;
806
            results.patternMatched = pattern.pattern;
807

    
808
            results.conversionSuccessful = false;
809
            results.convertedCoord = 99999; //this is to mark an error...
810

    
811
            results.conversionComments = "Coordinate pattern not recognised!";
812

    
813
 	    }
814
        
815
        //do the self check here
816
        results = selfTest(results);
817

    
818
        //return conversion results
819
        return results;
820
    }
821

    
822

    
823
	/**
824
	 * @param sign
825
	 * @param dd
826
	 * @param mm
827
	 * @param ss
828
	 * @param sss
829
	 * @param results
830
	 */
831
	private void doConvertWithCheck(int sign, double dd, double mm, double mmm, double ss, double sss, ConversionResults results) {
832
		double dec;
833
		//Do the conversion if everything ok
834
		if (results.conversionSuccessful){
835
		    results.conversionComments = "Conversion successful.";
836

    
837
		    dec = sign * (dd + (mm + mmm) / 60 + (ss + sss) / 3600);
838

    
839
		    //one more check to ensure a coord does not exceed 180
840
		    if (dec > 180 | dec < -180){
841
		        results.conversionSuccessful = false;
842
		        results.convertedCoord = 99999; //this is to mark an error...
843
		        results.conversionComments += "Coordinate is either > 180 or < -180; ";
844
		    } else {
845
		        results.convertedCoord = dec;
846

    
847
		        results.conversionComments = "Conversion successful.";
848

    
849
		        //Check whether the coordinate exceeds +/- 90 and mark it in comments
850
		        
851
		        if (dec <= 90 && dec >= -90 && (results.isLongitude == null || results.isLongitude == false) ) { 
852
                	results.canBeLat = true; 
853
                }else{
854
                	results.isLongitude = true;
855
                }
856
		    }
857
		}
858
	}
859

    
860

    
861
	/**
862
	 * @param ss
863
	 * @param results
864
	 */
865
	private void checkSecondRange(double ss, ConversionResults results) {
866
		if (ss > 59) {//seconds
867
		    results.conversionSuccessful = false;
868
		    results.convertedCoord = 99999; //this is to mark an error...
869
		    results.conversionComments += "Seconds fall outside the range: MM >= 60; ";
870
		}
871
	}
872

    
873

    
874
	/**
875
	 * @param mm
876
	 * @param results
877
	 */
878
	private void checkMinuteRange(double mm, ConversionResults results) {
879
		if (mm > 59) {//minutes
880
		    results.conversionSuccessful = false;
881
		    results.convertedCoord = 99999; //this is to mark an error...
882
		    results.conversionComments += "Minutes fall outside the range: MM > 59; ";
883
		}
884
	}
885

    
886

    
887
	/**
888
	 * @param dd
889
	 * @param results
890
	 */
891
	private void checkDegreeRange(double dd, ConversionResults results) {
892
		//do some additional checking if the coords fall into the range
893
		if (dd < -180 | dd > 180){  //degree may require another param specifying whether it's lat or lon...
894
		    results.conversionSuccessful = false;
895
		    results.convertedCoord = 99999; //this is to mark an error...
896
		    results.conversionComments += "Degrees fall outside the range: DD < -180 | DD > 180; ";
897
		}
898
	}
899

    
900

    
901
	/**
902
	 * @param str
903
	 * @return
904
	 */
905
	private Boolean getIsLongitude(String str) {
906
	    //This regex checks for the negative hemisphere indicator
907
		Pattern regexLatitudeNonAmbigous = Pattern.compile("(N|n)");
908
		Pattern regexLatitudeAmbigous = Pattern.compile("(S|s)");
909

    
910
        //This regex checks if there weren't any other hemisphere indicators
911
        //it is needed for the specific case of the DDdMMmSSs S
912
        //so it needs to be ensured there where no positive indicators
913
        Pattern regexLongitude = Pattern.compile("(W|w|E|e)");
914

    
915
        //if a positive indicator is found no need to search further
916
        if (regexLongitude.matcher(str).find()){
917
            return true;
918
        }else if (regexLatitudeNonAmbigous.matcher(str).find()){
919
        	return false;
920
        }else if (regexLatitudeAmbigous.matcher(str).find()){
921
        	Pattern regexLiteralUnits = Pattern.compile("(D|d|M|m)");
922
        	
923
        	//if there are no other literal units we assume that S is a
924
        	//direction and not a second indicator
925
            if (! regexLiteralUnits.matcher(str).find()){
926
                return false;
927
            }else if (regexLatitudeAmbigous.matcher(str).groupCount() > 1){
928
            	return false;
929
            }else{
930
            	return null;
931
            }
932
        }else{
933
        	return null;
934
        }
935
	}
936

    
937

    
938
	/**
939
	 * Sets pattern machted, successful, pattern type and pattern info
940
	 * @param results
941
	 * @param pattern
942
	 */
943
	private void initializeResult(ConversionResults results,
944
			CoordinatePattern pattern) {
945
		//Pattern matched
946
		results.patternRecognised = true;
947

    
948
		//Matching pattern succeeded so intialy the parsing is ok
949
		results.conversionSuccessful = true;
950

    
951
		//pattern info
952
		results.patternType = pattern.description;
953
		results.patternMatched = pattern.pattern;
954
	}
955

    
956

    
957
    private ConversionResults selfTest(ConversionResults results){
958

    
959
        ConversionResults newresults = results;
960

    
961
        if (results.conversionSuccessful != false){
962
            int sign = 1;
963
            if (Math.signum(results.convertedCoord) < 0) { 
964
            	sign = -1; 
965
            }
966
            
967
            double decimalDegrees = sign * results.convertedCoord;
968
            int fullDegrees;
969

    
970
            double decimalMinutes;
971
            int fullMinutes;
972

    
973
            double decimalSeconds;
974
            int fullSeconds;
975

    
976
            //Get full degrees
977
            fullDegrees = (int)Math.floor(decimalDegrees);
978

    
979
            //get minutes
980
            decimalMinutes = (decimalDegrees - fullDegrees) * 60;
981
            fullMinutes = (int)Math.floor(decimalMinutes);
982

    
983
            decimalSeconds = (decimalMinutes - fullMinutes) * 60;
984
            fullSeconds = (int)Math.floor(decimalSeconds);
985

    
986
            //save the test results
987
            newresults.dd = fullDegrees;
988
            newresults.mm = fullMinutes;
989
            newresults.mmm = decimalSeconds;
990
            newresults.ss = fullSeconds;
991
            newresults.sss = decimalSeconds;
992

    
993
        }
994

    
995
        return newresults;
996

    
997
    }
998

    
999

    
1000

    
1001
    //------------ CUSTOM PATTERN BUILDER--------------
1002

    
1003
    public class CustomPatternIn {
1004
        public String north;
1005
        public String south;
1006
        public String east;
1007
        public String west;
1008

    
1009
        public String degreeSymbol;
1010
        public String minuteSymbol;
1011
        public String secondSymbol;
1012

    
1013
        public boolean caseInsensitive;
1014
        public boolean allowWhiteSpace;
1015
        public boolean priorityOverDefaultPatterns;
1016
        public boolean disableDefaultPatterns;
1017

    
1018
    }
1019

    
1020
    
1021
    private class CustomPattern{
1022

    
1023
        public List<CustomHemisphereIndicator> hemisphereIndicators;
1024

    
1025
        public String degreeSymbol;
1026
        public String minuteSymbol;
1027
        public String secondSymbol;
1028

    
1029
        public boolean caseInsensitive;
1030

    
1031
    }
1032

    
1033
    //global variable to be used if a custom pattern is used
1034
    private CustomPattern customPtrn;
1035

    
1036
    //escape some of the chars
1037
    private String escapeChars(String str){
1038
        // backslash - first so it is not messed when other escape chars are corrected for being used in a string
1039
        str = str.replace("\\", "\\\\");
1040

    
1041
        //dot and comma
1042
        str = str.replace(".", "\\.");
1043
        str = str.replace(",", "\\,");
1044

    
1045
        //brackets
1046
        str = str.replace("(", "\\(");
1047
        str = str.replace(")", "\\)");
1048
        str = str.replace("[", "\\[");
1049
        str = str.replace("]", "\\]");
1050
        str = str.replace("{", "\\{");
1051
        str = str.replace("}", "\\}");
1052

    
1053
        //other replacements
1054
        str = str.replace("^", "\\^");
1055
        str = str.replace("$", "\\$");
1056
        str = str.replace("+", "\\+");
1057
        str = str.replace("*", "\\*");
1058
        str = str.replace("?", "\\?");
1059
        str = str.replace("|", "\\|");
1060

    
1061
        return str;
1062
    }
1063

    
1064

    
1065
    //this implements sorting by using system.Icomparable - sorting is needed later when replacing
1066
    private class CustomHemisphereIndicator implements Comparable<CustomHemisphereIndicator> {
1067
        //private variables
1068
        private int m_length;
1069
        private String m_name;
1070
        private String m_indicator;
1071
        private boolean m_positive;
1072

    
1073
        //constructor
1074
        public CustomHemisphereIndicator(String name, String indicator, int length, boolean positive){
1075
            this.m_name = name;
1076
            this.m_indicator = indicator;
1077
            this.m_length = length;
1078
            this.m_positive = positive;
1079
        }
1080

    
1081
        //properties
1082

    
1083
        public String getName(){
1084
        	return this.m_name;
1085
        }
1086
        public void setName(String value){
1087
        	this.m_name = value;
1088
        }
1089

    
1090
        public String getIndicator(){
1091
            return this.m_indicator;
1092
        }
1093
        public void setIndicator(String value){
1094
            this.m_indicator = value;
1095
        }
1096

    
1097
        public int getLength(){
1098
            return this.m_length;
1099
        }
1100
        public void setLength(int value){
1101
            this.m_length = value; 
1102
        }
1103

    
1104

    
1105
        public boolean getPositive(){
1106
            return this.m_positive; 
1107
        }
1108
        public void setPositive(boolean value){
1109
            this.m_positive = value;
1110
        }
1111
        
1112
        /* Less than zero if this instance is less than obj.
1113
         * Zero if this instance is equal to obj. 
1114
         * Greater than zero if this instance is greater than obj. 
1115
         * 
1116
         * This method uses the predefined method Int32.CompareTo
1117
         * */
1118

    
1119
        public int compareTo(CustomHemisphereIndicator ind){
1120
	        
1121
	        //no need to rewrite the code again, we have Integer.compareTo ready to use
1122
	        return Integer.valueOf(this.getLength()).compareTo(Integer.valueOf(ind.getLength()));
1123
        }
1124
    }
1125

    
1126

    
1127

    
1128
    //This adds custom pattern to a list of already predefined patterns
1129
    //useful for batch conversions - allows for totally mixed input data (predefined & custom)
1130
    public void addCustomPattern(CustomPatternIn patternIn){
1131

    
1132
        //new custom pattern object - to pass the needed data farther
1133
        CustomPattern pattern = new CustomPattern();
1134

    
1135
        //keep indicators for parsing
1136
        List<CustomHemisphereIndicator> indicators = new ArrayList<CustomHemisphereIndicator>();
1137

    
1138
        //north
1139
        CustomHemisphereIndicator ind = new CustomHemisphereIndicator("North", patternIn.north, patternIn.north.length() ,true);
1140
        indicators.add(ind);
1141
        
1142
        //south
1143
        ind = new CustomHemisphereIndicator("South", patternIn.south, patternIn.south.length(), false);
1144
        indicators.add(ind);
1145

    
1146
        //east
1147
        ind = new CustomHemisphereIndicator("East", patternIn.east, patternIn.east.length(), true);
1148
        indicators.add(ind);
1149

    
1150
        //west
1151
        ind = new CustomHemisphereIndicator("West", patternIn.west, patternIn.west.length(), false);
1152
        indicators.add(ind);
1153

    
1154
        //sort the arraylist
1155
        Collections.sort(indicators, lengthComparator);
1156

    
1157
        
1158
        //add it to the pattern object
1159
        pattern.hemisphereIndicators = indicators; 
1160
        
1161
        //case insensitive
1162
        pattern.caseInsensitive = patternIn.caseInsensitive;
1163

    
1164
        //keep symbols for parsing
1165
        pattern.degreeSymbol = patternIn.degreeSymbol;
1166
        pattern.minuteSymbol = patternIn.minuteSymbol;
1167
        pattern.secondSymbol = patternIn.secondSymbol;
1168

    
1169

    
1170
        //save the data
1171
        customPtrn = pattern;
1172
        
1173

    
1174
        //----------------build custom patterns----------------
1175

    
1176
        //prepare hemisphere indicators
1177
        String north = escapeChars(patternIn.north);
1178
        String south = escapeChars(patternIn.south);
1179
        String east = escapeChars(patternIn.east);
1180
        String west = escapeChars(patternIn.west);
1181

    
1182
        //prepare symbols
1183
        String degreesymbol = "";
1184
        if (patternIn.degreeSymbol != ""){
1185
            degreesymbol = "(" + escapeChars(patternIn.degreeSymbol) + ")?";
1186
        }
1187

    
1188
        String minutesymbol = "";
1189
        if (patternIn.minuteSymbol != ""){
1190
            minutesymbol = "(" + escapeChars(patternIn.minuteSymbol) + ")?";
1191
        }
1192

    
1193
        String secondsymbol = "";
1194
        if (escapeChars(patternIn.secondSymbol) != ""){
1195
            secondsymbol = "(" + escapeChars(patternIn.secondSymbol) + ")?";
1196
        }
1197

    
1198

    
1199
        //is the pattern to be case insensitive?
1200
        String CaseInsensitive = "";
1201
        if (patternIn.caseInsensitive){
1202
            CaseInsensitive = "(?i)";
1203
        }
1204

    
1205
        //allow whitespace
1206
        String WhiteSpace = "";
1207
        if (patternIn.allowWhiteSpace == true){
1208
            WhiteSpace = "(\\s)*";
1209
        }
1210

    
1211
        //hemisphere indicator
1212
        String HemisphereIndicator = "";
1213

    
1214
        //add north if present
1215
        if (north == ""){
1216
            HemisphereIndicator += south;
1217
        }else{
1218
            HemisphereIndicator += north;
1219
            if (south != ""){
1220
                HemisphereIndicator += "|" + south;
1221
            }
1222
        }
1223

    
1224
        //add east
1225
        if (north == "" & south == ""){
1226
            HemisphereIndicator += east;
1227
        } else {
1228
            if (east != ""){
1229
                HemisphereIndicator += "|" + east;
1230
            }
1231
        }
1232

    
1233
        //add west
1234
        if (north == "" & south == "" & east == ""){
1235
            HemisphereIndicator += west;
1236
        } else {
1237
            if (west != "") {
1238
                HemisphereIndicator += "|" + west;
1239
            }
1240
        }
1241

    
1242
        //add remaining bits if not empty
1243
        if (HemisphereIndicator != "") {
1244
            HemisphereIndicator = "(" + HemisphereIndicator + ")?";
1245
        }
1246

    
1247
        List<CoordinatePattern> customPatterns = new ArrayList<CoordinatePattern>();
1248

    
1249
        //create custom patterns based on the specified user's input
1250
        CoordinatePattern ptrn;
1251

    
1252
        //Custom variation of DD.DDD
1253
        ptrn = new CoordinatePattern();
1254
        ptrn.description = "Custom variation of DD.DDD";
1255
        ptrn.pattern  = 
1256
            CaseInsensitive + "(^" +
1257
            WhiteSpace + HemisphereIndicator + WhiteSpace +
1258
            "(" +
1259
            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + "$)" +
1260
            ")" +
1261
            "|(^" + WhiteSpace +
1262
            "(" +
1263
            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + ")" +
1264
            ")" +
1265
            HemisphereIndicator + WhiteSpace + "$" +
1266
            "))"
1267
            ;
1268
        customPatterns.add(ptrn);
1269

    
1270
        //Custom variation of DD:MM.MMM
1271
        ptrn = new CoordinatePattern();
1272
        ptrn.description = "Custom variation of DD:MM.MMM";
1273
        ptrn.pattern = 
1274
            CaseInsensitive + "(^" +
1275
            WhiteSpace + HemisphereIndicator + WhiteSpace +
1276
            "(" +
1277
            "(\\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 + "$)" +
1278
            ")" +
1279
            "|(^" + WhiteSpace +
1280
            "(" +
1281
            "(\\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 + ")" +
1282
            ")" +
1283
            HemisphereIndicator + WhiteSpace + "$" +
1284
            "))"
1285
            ;
1286
        customPatterns.add(ptrn);
1287
        
1288
        //Custom variation of DD:MM:SS.SSS
1289
        ptrn = new CoordinatePattern();
1290
        ptrn.description = "Custom variation of DD:MM:SS.SSS";
1291
        ptrn.pattern =
1292
            CaseInsensitive + "(^" +
1293
            WhiteSpace + HemisphereIndicator + WhiteSpace +
1294
            "(" +
1295
            "(\\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 + "$)" +
1296
            ")" +
1297
            "|(^" + WhiteSpace +
1298
            "(" +
1299
            "(\\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 + ")" +
1300
            ")" +
1301
            HemisphereIndicator + WhiteSpace + "$" +
1302
            "))"
1303
            ;
1304
        customPatterns.add(ptrn);
1305
        
1306
        //check if the default patterns are to be used
1307
        if (patternIn.disableDefaultPatterns) {
1308
            patterns = customPatterns;
1309
        } else { //if all patterns are to be used check which set has the matching priority
1310
        
1311
            //check if the custom patterns are to have priority over the default ones
1312
            if (patternIn.priorityOverDefaultPatterns){
1313

    
1314
                //add default patterns to the custom patterns
1315
                for (int i = 0; i < patterns.size(); i++){
1316
                    customPatterns.add(patterns.get(i));
1317
                }
1318

    
1319
                //swap array lists
1320
                patterns = customPatterns;
1321

    
1322
            }else{
1323
                //add custom patterns to the default patterns
1324
                for (int i = 0; i < customPatterns.size(); i++){
1325
                    patterns.add(customPatterns.get(i));
1326

    
1327
                }
1328
            }
1329
        }
1330
    }
1331

    
1332
}
    (1-1/1)