Project

General

Profile

Download (49 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)*$)" +
99
            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)?(\\s)*$)" +
100
            "|(\\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)*$)" +
101
            "|(\\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)*$))" +
102
            ")" +
103
            //DD°MM\u02B9SS.SSSNn/Ss/Ww/Ee
104
            "|(^(\\s)*" +
105
            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|" +
106
            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*(\u02B9|'|M|m)?(\\s)*)|" +
107
            "(\\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)*)|" +
108
            "(\\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)*))" +
109
            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
110
            ")";
111
        patterns.add(pattern);
112

    
113

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

    
130
    }
131

    
132

    
133
    //tests if a string matches one of the defined patterns
134
    private int matchPattern(String str){
135
        int recognised = -1;
136

    
137
        //match the string against each available patern
138
        for (int i = 0; i < patterns.size(); i++){
139

    
140
        	CoordinatePattern pattern = patterns.get(i);
141
        	Pattern regEx = Pattern.compile(pattern.pattern);
142
        	if (regEx.matcher(str).find()) { 
143
        		recognised = i; 
144
            	break; 
145
            }
146

    
147
        }
148
        return recognised;
149
    }
150

    
151

    
152
    //gets sign of the coordinate (tests for presence of negative sign)
153
    private int getSign(String str){
154

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

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

    
163
        //if a positive indicator is found no need to search further
164
        if (regexPositive.matcher(str).find()){
165
            return 1;
166
        }else{
167
            //if not check whether there was a negative indicator. if so negate otherwise return positive
168
            if (regexNegative.matcher(str).find()){
169
                return -1;
170
            }else{
171
                return 1;
172
            }
173
        }
174
    }
175

    
176

    
177
    //this checks for the coordinate sign by evaluating user supplied data
178
    private int getCustomSign(String str){
179
        //Note:
180
        //Indicators are evaluated from the longest ones to the shortes ones
181
        //So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
182

    
183
        
184
        //search for the presence of indicators
185
        boolean hasPositive = false;
186
        boolean hasNegative = false;
187

    
188
        //keep previous negative indicators here
189
        List<String> previousNegatives = new ArrayList<String>();
190
        
191
        //compare the string with user supplied custom pattern
192
        for (int x = customPtrn.hemisphereIndicators.size() - 1; x >= 0; x--){
193

    
194
            CustomHemisphereIndicator ind = customPtrn.hemisphereIndicators.get(x);
195

    
196
            //test here if the indicator exists (has length >0)
197
            if (ind.getLength() > 0){
198

    
199
                //check if the supplied pattern was marked as case insensitive?
200
                String caseInsensitive = "";
201

    
202
                if (customPtrn.caseInsensitive){
203
                    caseInsensitive = "(?i)";
204
                }
205

    
206
                //create a regex
207
                Pattern tempRegex = Pattern.compile(caseInsensitive + ind.getIndicator());
208

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

    
221
                            for (int i = previousNegatives.size() - 1; i >= 0; i--){
222
                                if (ind.getIndicator() == previousNegatives.get(i)){
223
                                    sameNegative = true;
224
                                    break;
225
                                }
226
                            }
227

    
228
                            //mark as positive only if the previously found negative is the same
229
                            if (sameNegative){
230
                                hasPositive = true;
231
                            }
232

    
233
                        }else{ //if no negatives before it already marks the sign as positive 
234
                            hasPositive = true;
235
                        }
236

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

    
253
                    }
254
                }
255
            }
256
            
257
        }
258

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

    
263
        if (hasPositive){
264
            return 1;
265
        } else {
266
            if (hasNegative) {
267
                return -1;
268
            } else {
269
                return 1;
270
            }
271
        }
272
    }
273

    
274
      
275

    
276
    //returns a currently used decimal separator
277
    private String getDecimalSeparator(){
278
    	//TODO not yet transformed from C#
279
//    	return System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
280
        return ".";
281
    }
282

    
283

    
284
    //replaces comma or dot for current decimal separator
285
    private String fixDecimalSeparator(String str){
286
        //Note:
287
        //Coma is replaced as parsers often recognise dot as a decimal separator
288
        //Comma or dot is replaced with a decimal separator here (environment settings)
289
        //But decimal separator has to be used later too;
290

    
291
        String regExReplaceComma = "(\\,|\\.)";
292
        str = str.replaceAll(regExReplaceComma, getDecimalSeparator());
293
        
294
        return str;
295
    }
296

    
297

    
298
    //removes sign
299
    private String removeSign(String str){
300
        String regExRemoveSign = "(\\+|-|S|s|W|w|N|n|E|e)";
301
        str = str.replaceAll(regExRemoveSign, "");
302
        return str;
303
    }
304

    
305
    //removes custom sign indicators
306
    private String removeCustomPatternParts(String str){
307

    
308
        /* Note:
309
         * Symbols are added here so the removing tries to not affect the coordinate too much
310
         * Strings to be removed then are evaluated from the longest ones to the shortes ones
311
         * So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
312
         * */
313

    
314
        //CustomHemisphereIndicator is used here so another object does not have to be created
315
        //only for the string cleanning
316
        List<CustomHemisphereIndicator> stringsToRemove = customPtrn.hemisphereIndicators;
317

    
318
        //add degree symbol
319
        CustomHemisphereIndicator stringToRemove = new CustomHemisphereIndicator("Degree", customPtrn.degreeSymbol,customPtrn.degreeSymbol.length(), false);
320
        stringsToRemove.add(stringToRemove);
321

    
322
        //add minute symbol
323
        stringToRemove = new CustomHemisphereIndicator("Minute", customPtrn.minuteSymbol, customPtrn.minuteSymbol.length(), false);
324
        stringsToRemove.add(stringToRemove);
325

    
326
        //add second symbol
327
        stringToRemove = new CustomHemisphereIndicator("Second", customPtrn.secondSymbol, customPtrn.secondSymbol.length(), false);
328
        stringsToRemove.add(stringToRemove);
329

    
330
        //sort the list (by element's Length property)
331
        Collections.sort(stringsToRemove, lengthComparator);
332
        
333
        
334
//        ListSelectionEv.sort(lengthComparator);
335

    
336

    
337
        for (int x = stringsToRemove.size() - 1; x >= 0; x--){
338

    
339
            CustomHemisphereIndicator toBeRemoved = stringsToRemove.get(x);
340

    
341
            //check if the string exists so replacing does not yield errors
342
            if (toBeRemoved.getLength() > 0)
343
            {
344
                //check if the supplied pattern was marked as case insensitive?
345
                String CaseInsensitive = "";
346

    
347
                if (customPtrn.caseInsensitive){
348
                    CaseInsensitive = "(?i)";
349
                }
350

    
351
                //create regex for replacing
352
                String tempRegex = CaseInsensitive + toBeRemoved.getIndicator();
353

    
354

    
355
                if (toBeRemoved.getName().equals("Degree") || toBeRemoved.getName().equals("Minute")) {
356
                    //replace with a symbol used later for splitting
357
                    str =  str.replaceAll(tempRegex, ":");
358
                } else {
359
                    //remove the string
360
                    str = str.replaceAll(tempRegex, "");
361
                }
362
            }
363
        }
364
        return str;
365
    }
366

    
367

    
368

    
369
    //removes whitespace characters
370
    private String removeWhiteSpace(String str){
371
        str = str.replaceFirst("\\s*", "");
372
        return str;
373
    }
374

    
375

    
376
    //Object for the conversion results
377
    public class ConversionResults{
378
        public boolean patternRecognised;
379
        public String patternMatched;
380
        public String patternType;
381

    
382
        public boolean conversionSuccessful;
383
        public double convertedCoord;
384
        public boolean canBeLat;
385
        
386
        public String conversionComments;
387
        
388
        public Boolean isLongitude;
389

    
390
        public int dd;
391
        public int mm;
392
        public double mmm;
393
        public int ss;
394
        public double sss;
395

    
396
    }
397

    
398

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

    
410
        //Get the matched pattern
411
        CoordinatePattern pattern;
412
        int ptrnnum = matchPattern(str);
413
        if (ptrnnum != -1) {
414
            pattern = patterns.get(ptrnnum);
415
        } else {
416
            pattern = new CoordinatePattern();
417
            pattern.description = "Unknown";
418
            pattern.pattern = "No pattern matched";
419
        }
420

    
421

    
422

    
423
        if (pattern.description.equals("Variation of DD.DDD")){
424
            
425
       	  	//Sets pattern machted, successful, pattern type and pattern info
426
        	initializeResult(results, pattern);
427

    
428
            //get sign
429
            sign = getSign(str);
430
            results.isLongitude = getIsLongitude(str);
431
            
432
            //Replace comma or dot with a current decimal separator
433
            str = fixDecimalSeparator(str);
434

    
435
            //Remove all the unwanted stuff
436
            str = removeSign(str);
437
            str = removeWhiteSpace(str);
438

    
439
            //Since this is already a decimal degree no spliting is needed
440
            dd =  Double.valueOf(str);
441

    
442
            checkDegreeRange(dd, results);
443
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
444

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

    
447
        	//Sets pattern machted, successful, pattern type and pattern info
448
        	initializeResult(results, pattern);
449

    
450
            //get sign
451
            sign = getSign(str);
452
            results.isLongitude = getIsLongitude(str);
453
            
454
            //Replace comma or dot with a current decimal separator
455
            str = fixDecimalSeparator(str);
456

    
457
            //Remove all the unwanted stuff
458
            str = removeSign(str);
459
            str = removeWhiteSpace(str);
460

    
461
            //do some further replacing
462
            //Replace degree symbol
463
            str = str.replaceAll("(\u00B0|\u00BA|D|d)", ":");
464
            
465
            //remove minute symbol
466
            str = str.replaceAll("(\u02B9|'|M|m)", "");
467
            
468
            //Extract decimal part
469
            decimalBit = str.split(decSeparatorRegEx);
470
            
471
            //split degrees and minutes
472
            ddmm = decimalBit[0].split(":");
473

    
474

    
475
            //extract values from the strings
476
            dd = Integer.valueOf(ddmm[0]); //Degrees
477

    
478
            if (ddmm.length > 1){ //Minutes
479
                //check if the string is not empty
480
                if (ddmm[1] != "") { 
481
                	mm = Integer.valueOf(ddmm[1]); 
482
                }
483
            }
484

    
485
            if (decimalBit.length > 1){//DecimalSeconds
486
                //check if the string is not empty
487
                if (decimalBit[1] != "") {
488
                    mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
489
                }
490
            }
491

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

    
508
        	//Sets pattern machted, successful, pattern type and pattern info
509
        	initializeResult(results, pattern);
510

    
511
            //get sign
512
            sign = getSign(str);
513
            //TODO test S
514
            results.isLongitude = getIsLongitude(str);
515
            
516
            //Replace comma or dot with a current decimal separator
517
            str = fixDecimalSeparator(str);
518

    
519
            //Remove all the unwanted stuff
520
            str = removeSign(str);
521
            str = removeWhiteSpace(str);
522

    
523
            //remove second symbol (s is removed by the get sign method)
524
            //double apostrophe is not removed here as single apostrphe may mark minutes!
525
            //it's taken care of later after extracting the decimal part
526
            str = str.replaceAll("(\u02BA|\")", "");
527
            
528
            //do some further replacing
529
            //Replace degree symbol
530
            str = str.replaceAll("(\u00B0|\u00B0|D|d|\u02B9|'|M|m)",":");
531

    
532
            //Extract decimal part
533
            decimalBit = str.split(decSeparatorRegEx);
534

    
535
            //remove : from the decimal part [1]! This is needed when a double apostrophe was used to mark seconds
536
            if (decimalBit.length > 1)
537
            {
538
                decimalBit[1].replace(":", "");
539
            }
540

    
541
            //split degrees and minutes
542
            ddmmss = decimalBit[0].split(":");
543

    
544

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

    
566
            checkDegreeRange(dd, results);
567
            checkMinuteRange(mm, results);
568
            checkSecondRange(ss, results);
569

    
570
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
571

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

    
574
        	//Sets pattern machted, successful, pattern type and pattern info
575
        	initializeResult(results, pattern);
576

    
577
            //get sign
578
            sign = getSign(str);
579
            results.isLongitude = getIsLongitude(str);
580
            
581
            //Replace comma or dot with a current decimal separator
582
            str = fixDecimalSeparator(str);
583

    
584
            //Remove all the unwanted stuff
585
            str = removeSign(str);
586
            str = removeWhiteSpace(str);
587

    
588
            //Do some splitting
589
            decimalBit = str.split(decSeparatorRegEx);
590
            ddmmss = decimalBit[0].split(":");
591

    
592

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

    
613
            checkDegreeRange(dd, results);
614
            checkMinuteRange(mm, results);
615
            checkSecondRange(ss, results);
616

    
617
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
618

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

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

    
671
	    	//Sets pattern machted, successful, pattern type and pattern info
672
        	initializeResult(results, pattern);
673

    
674
            //get sign
675
            sign = getCustomSign(str);
676
          
677
            //TODO still needs to be adapted to custom pattern
678
	        results.isLongitude = getIsLongitude(str);
679
	        
680

    
681

    
682
            //Remove all the unwanted stuff
683
            //Note: This method also replaces the symbols with ":"
684
            //Note: In certain cases it may make the coord unparsable
685
            str = removeCustomPatternParts(str);
686

    
687
            str = removeWhiteSpace(str);
688

    
689
            //Replace comma or dot with a current decimal separator
690
            str = fixDecimalSeparator(str);
691

    
692
            
693
            //Extract decimal part
694
            decimalBit = str.split(decSeparatorRegEx);
695

    
696
            //split degrees and minutes
697
            ddmm = decimalBit[0].split(":");
698

    
699

    
700
            try {
701
                //extract values from the strings
702
                dd = Integer.valueOf(ddmm[0]); //Degrees
703

    
704
                if (ddmm.length > 1){//Minutes
705
                    //check if the string is not empty
706
                    if (ddmm[1] != "") { mm = Integer.valueOf(ddmm[1]); }
707
                }
708

    
709
                if (decimalBit.length > 1){//DecimalSeconds
710
                    //check if the string is not empty
711
                    if (decimalBit[1] != ""){
712
                        //replace the ":" if any (may be here as a result of custom symbol replacement
713
                        decimalBit[1] = decimalBit[1].replace(":", "");
714

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

    
727
                //exit method
728
                return results;
729
            }
730

    
731

    
732
            checkDegreeRange(dd, results);
733
            checkMinuteRange(mm, results);
734
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
735

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

    
738
	    	//Sets pattern machted, successful, pattern type and pattern info
739
        	initializeResult(results, pattern);
740

    
741

    
742
            //get sign
743
            sign = getCustomSign(str);
744

    
745
            //TODO still needs to be adapted to custom pattern
746
	        results.isLongitude = getIsLongitude(str);
747
	        
748

    
749
            //Remove all the unwanted stuff
750
            //Note: This method also replaces the symbols with ":"
751
            //Note: In certain cases it may make the coord unparsable
752
            str = removeCustomPatternParts(str);
753

    
754
            str = removeWhiteSpace(str);
755

    
756
            //Replace comma or dot with a current decimal separator
757
            str = fixDecimalSeparator(str);
758

    
759

    
760
            //Extract decimal part
761
            decimalBit = str.split(decSeparatorRegEx);
762

    
763
            //split degrees and minutes
764
            ddmmss = decimalBit[0].split(":");
765

    
766

    
767
            try {
768

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

    
798
                //exit method
799
                return results;
800
            }
801

    
802

    
803
            checkDegreeRange(dd, results);
804
            checkMinuteRange(mm, results);
805
            checkSecondRange(ss, results);
806

    
807
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
808

    
809
	    }else {   //default  : pattern not recognized
810
            results.patternRecognised = false;
811
            results.patternType = pattern.description;
812
            results.patternMatched = pattern.pattern;
813

    
814
            results.conversionSuccessful = false;
815
            results.convertedCoord = 99999; //this is to mark an error...
816

    
817
            results.conversionComments = "Coordinate pattern not recognised!";
818

    
819
 	    }
820
        
821
        //do the self check here
822
        results = selfTest(results);
823

    
824
        //return conversion results
825
        return results;
826
    }
827

    
828

    
829
	/**
830
	 * @param sign
831
	 * @param dd
832
	 * @param mm
833
	 * @param ss
834
	 * @param sss
835
	 * @param results
836
	 */
837
	private void doConvertWithCheck(int sign, double dd, double mm, double mmm, double ss, double sss, ConversionResults results) {
838
		double dec;
839
		//Do the conversion if everything ok
840
		if (results.conversionSuccessful){
841
		    results.conversionComments = "Conversion successful.";
842

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

    
845
		    //one more check to ensure a coord does not exceed 180
846
		    if (dec > 180 | dec < -180){
847
		        results.conversionSuccessful = false;
848
		        results.convertedCoord = 99999; //this is to mark an error...
849
		        results.conversionComments += "Coordinate is either > 180 or < -180; ";
850
		    } else {
851
		        results.convertedCoord = dec;
852

    
853
		        results.conversionComments = "Conversion successful.";
854

    
855
		        //Check whether the coordinate exceeds +/- 90 and mark it in comments
856
		        
857
		        if (dec <= 90 && dec >= -90 && (results.isLongitude == null || results.isLongitude == false) ) { 
858
                	results.canBeLat = true; 
859
                }else{
860
                	results.isLongitude = true;
861
                }
862
		    }
863
		}
864
	}
865

    
866

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

    
879

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

    
892

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

    
906

    
907
	/**
908
	 * @param str
909
	 * @return
910
	 */
911
	private Boolean getIsLongitude(String str) {
912
	    //This regex checks for the negative hemisphere indicator
913
		Pattern regexLatitudeNonAmbigous = Pattern.compile("(N|n)");
914
		Pattern regexLatitudeAmbigous = Pattern.compile("(S|s)");
915

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

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

    
943

    
944
	/**
945
	 * Sets pattern machted, successful, pattern type and pattern info
946
	 * @param results
947
	 * @param pattern
948
	 */
949
	private void initializeResult(ConversionResults results,
950
			CoordinatePattern pattern) {
951
		//Pattern matched
952
		results.patternRecognised = true;
953

    
954
		//Matching pattern succeeded so intialy the parsing is ok
955
		results.conversionSuccessful = true;
956

    
957
		//pattern info
958
		results.patternType = pattern.description;
959
		results.patternMatched = pattern.pattern;
960
	}
961

    
962

    
963
    private ConversionResults selfTest(ConversionResults results){
964

    
965
        ConversionResults newresults = results;
966

    
967
        if (results.conversionSuccessful != false){
968
            int sign = 1;
969
            if (Math.signum(results.convertedCoord) < 0) { 
970
            	sign = -1; 
971
            }
972
            
973
            double decimalDegrees = sign * results.convertedCoord;
974
            int fullDegrees;
975

    
976
            double decimalMinutes;
977
            int fullMinutes;
978

    
979
            double decimalSeconds;
980
            int fullSeconds;
981

    
982
            //Get full degrees
983
            fullDegrees = (int)Math.floor(decimalDegrees);
984

    
985
            //get minutes
986
            decimalMinutes = (decimalDegrees - fullDegrees) * 60;
987
            fullMinutes = (int)Math.floor(decimalMinutes);
988

    
989
            decimalSeconds = (decimalMinutes - fullMinutes) * 60;
990
            fullSeconds = (int)Math.floor(decimalSeconds);
991

    
992
            //save the test results
993
            newresults.dd = fullDegrees;
994
            newresults.mm = fullMinutes;
995
            newresults.mmm = decimalSeconds;
996
            newresults.ss = fullSeconds;
997
            newresults.sss = decimalSeconds;
998

    
999
        }
1000

    
1001
        return newresults;
1002

    
1003
    }
1004

    
1005

    
1006

    
1007
    //------------ CUSTOM PATTERN BUILDER--------------
1008

    
1009
    public class CustomPatternIn {
1010
        public String north;
1011
        public String south;
1012
        public String east;
1013
        public String west;
1014

    
1015
        public String degreeSymbol;
1016
        public String minuteSymbol;
1017
        public String secondSymbol;
1018

    
1019
        public boolean caseInsensitive;
1020
        public boolean allowWhiteSpace;
1021
        public boolean priorityOverDefaultPatterns;
1022
        public boolean disableDefaultPatterns;
1023

    
1024
    }
1025

    
1026
    
1027
    private class CustomPattern{
1028

    
1029
        public List<CustomHemisphereIndicator> hemisphereIndicators;
1030

    
1031
        public String degreeSymbol;
1032
        public String minuteSymbol;
1033
        public String secondSymbol;
1034

    
1035
        public boolean caseInsensitive;
1036

    
1037
    }
1038

    
1039
    //global variable to be used if a custom pattern is used
1040
    private CustomPattern customPtrn;
1041

    
1042
    //escape some of the chars
1043
    private String escapeChars(String str){
1044
        // backslash - first so it is not messed when other escape chars are corrected for being used in a string
1045
        str = str.replace("\\", "\\\\");
1046

    
1047
        //dot and comma
1048
        str = str.replace(".", "\\.");
1049
        str = str.replace(",", "\\,");
1050

    
1051
        //brackets
1052
        str = str.replace("(", "\\(");
1053
        str = str.replace(")", "\\)");
1054
        str = str.replace("[", "\\[");
1055
        str = str.replace("]", "\\]");
1056
        str = str.replace("{", "\\{");
1057
        str = str.replace("}", "\\}");
1058

    
1059
        //other replacements
1060
        str = str.replace("^", "\\^");
1061
        str = str.replace("$", "\\$");
1062
        str = str.replace("+", "\\+");
1063
        str = str.replace("*", "\\*");
1064
        str = str.replace("?", "\\?");
1065
        str = str.replace("|", "\\|");
1066

    
1067
        return str;
1068
    }
1069

    
1070

    
1071
    //this implements sorting by using system.Icomparable - sorting is needed later when replacing
1072
    private class CustomHemisphereIndicator implements Comparable<CustomHemisphereIndicator> {
1073
        //private variables
1074
        private int m_length;
1075
        private String m_name;
1076
        private String m_indicator;
1077
        private boolean m_positive;
1078

    
1079
        //constructor
1080
        public CustomHemisphereIndicator(String name, String indicator, int length, boolean positive){
1081
            this.m_name = name;
1082
            this.m_indicator = indicator;
1083
            this.m_length = length;
1084
            this.m_positive = positive;
1085
        }
1086

    
1087
        //properties
1088

    
1089
        public String getName(){
1090
        	return this.m_name;
1091
        }
1092
        public void setName(String value){
1093
        	this.m_name = value;
1094
        }
1095

    
1096
        public String getIndicator(){
1097
            return this.m_indicator;
1098
        }
1099
        public void setIndicator(String value){
1100
            this.m_indicator = value;
1101
        }
1102

    
1103
        public int getLength(){
1104
            return this.m_length;
1105
        }
1106
        public void setLength(int value){
1107
            this.m_length = value; 
1108
        }
1109

    
1110

    
1111
        public boolean getPositive(){
1112
            return this.m_positive; 
1113
        }
1114
        public void setPositive(boolean value){
1115
            this.m_positive = value;
1116
        }
1117
        
1118
        /* Less than zero if this instance is less than obj.
1119
         * Zero if this instance is equal to obj. 
1120
         * Greater than zero if this instance is greater than obj. 
1121
         * 
1122
         * This method uses the predefined method Int32.CompareTo
1123
         * */
1124

    
1125
        public int compareTo(CustomHemisphereIndicator ind){
1126
	        
1127
	        //no need to rewrite the code again, we have Integer.compareTo ready to use
1128
	        return Integer.valueOf(this.getLength()).compareTo(Integer.valueOf(ind.getLength()));
1129
        }
1130
    }
1131

    
1132

    
1133

    
1134
    //This adds custom pattern to a list of already predefined patterns
1135
    //useful for batch conversions - allows for totally mixed input data (predefined & custom)
1136
    public void addCustomPattern(CustomPatternIn patternIn){
1137

    
1138
        //new custom pattern object - to pass the needed data farther
1139
        CustomPattern pattern = new CustomPattern();
1140

    
1141
        //keep indicators for parsing
1142
        List<CustomHemisphereIndicator> indicators = new ArrayList<CustomHemisphereIndicator>();
1143

    
1144
        //north
1145
        CustomHemisphereIndicator ind = new CustomHemisphereIndicator("North", patternIn.north, patternIn.north.length() ,true);
1146
        indicators.add(ind);
1147
        
1148
        //south
1149
        ind = new CustomHemisphereIndicator("South", patternIn.south, patternIn.south.length(), false);
1150
        indicators.add(ind);
1151

    
1152
        //east
1153
        ind = new CustomHemisphereIndicator("East", patternIn.east, patternIn.east.length(), true);
1154
        indicators.add(ind);
1155

    
1156
        //west
1157
        ind = new CustomHemisphereIndicator("West", patternIn.west, patternIn.west.length(), false);
1158
        indicators.add(ind);
1159

    
1160
        //sort the arraylist
1161
        Collections.sort(indicators, lengthComparator);
1162

    
1163
        
1164
        //add it to the pattern object
1165
        pattern.hemisphereIndicators = indicators; 
1166
        
1167
        //case insensitive
1168
        pattern.caseInsensitive = patternIn.caseInsensitive;
1169

    
1170
        //keep symbols for parsing
1171
        pattern.degreeSymbol = patternIn.degreeSymbol;
1172
        pattern.minuteSymbol = patternIn.minuteSymbol;
1173
        pattern.secondSymbol = patternIn.secondSymbol;
1174

    
1175

    
1176
        //save the data
1177
        customPtrn = pattern;
1178
        
1179

    
1180
        //----------------build custom patterns----------------
1181

    
1182
        //prepare hemisphere indicators
1183
        String north = escapeChars(patternIn.north);
1184
        String south = escapeChars(patternIn.south);
1185
        String east = escapeChars(patternIn.east);
1186
        String west = escapeChars(patternIn.west);
1187

    
1188
        //prepare symbols
1189
        String degreesymbol = "";
1190
        if (patternIn.degreeSymbol != ""){
1191
            degreesymbol = "(" + escapeChars(patternIn.degreeSymbol) + ")?";
1192
        }
1193

    
1194
        String minutesymbol = "";
1195
        if (patternIn.minuteSymbol != ""){
1196
            minutesymbol = "(" + escapeChars(patternIn.minuteSymbol) + ")?";
1197
        }
1198

    
1199
        String secondsymbol = "";
1200
        if (escapeChars(patternIn.secondSymbol) != ""){
1201
            secondsymbol = "(" + escapeChars(patternIn.secondSymbol) + ")?";
1202
        }
1203

    
1204

    
1205
        //is the pattern to be case insensitive?
1206
        String CaseInsensitive = "";
1207
        if (patternIn.caseInsensitive){
1208
            CaseInsensitive = "(?i)";
1209
        }
1210

    
1211
        //allow whitespace
1212
        String WhiteSpace = "";
1213
        if (patternIn.allowWhiteSpace == true){
1214
            WhiteSpace = "(\\s)*";
1215
        }
1216

    
1217
        //hemisphere indicator
1218
        String HemisphereIndicator = "";
1219

    
1220
        //add north if present
1221
        if (north == ""){
1222
            HemisphereIndicator += south;
1223
        }else{
1224
            HemisphereIndicator += north;
1225
            if (south != ""){
1226
                HemisphereIndicator += "|" + south;
1227
            }
1228
        }
1229

    
1230
        //add east
1231
        if (north == "" & south == ""){
1232
            HemisphereIndicator += east;
1233
        } else {
1234
            if (east != ""){
1235
                HemisphereIndicator += "|" + east;
1236
            }
1237
        }
1238

    
1239
        //add west
1240
        if (north == "" & south == "" & east == ""){
1241
            HemisphereIndicator += west;
1242
        } else {
1243
            if (west != "") {
1244
                HemisphereIndicator += "|" + west;
1245
            }
1246
        }
1247

    
1248
        //add remaining bits if not empty
1249
        if (HemisphereIndicator != "") {
1250
            HemisphereIndicator = "(" + HemisphereIndicator + ")?";
1251
        }
1252

    
1253
        List<CoordinatePattern> customPatterns = new ArrayList<CoordinatePattern>();
1254

    
1255
        //create custom patterns based on the specified user's input
1256
        CoordinatePattern ptrn;
1257

    
1258
        //Custom variation of DD.DDD
1259
        ptrn = new CoordinatePattern();
1260
        ptrn.description = "Custom variation of DD.DDD";
1261
        ptrn.pattern  = 
1262
            CaseInsensitive + "(^" +
1263
            WhiteSpace + HemisphereIndicator + WhiteSpace +
1264
            "(" +
1265
            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + "$)" +
1266
            ")" +
1267
            "|(^" + WhiteSpace +
1268
            "(" +
1269
            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + ")" +
1270
            ")" +
1271
            HemisphereIndicator + WhiteSpace + "$" +
1272
            "))"
1273
            ;
1274
        customPatterns.add(ptrn);
1275

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

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

    
1325
                //swap array lists
1326
                patterns = customPatterns;
1327

    
1328
            }else{
1329
                //add custom patterns to the default patterns
1330
                for (int i = 0; i < customPatterns.size(); i++){
1331
                    patterns.add(customPatterns.get(i));
1332

    
1333
                }
1334
            }
1335
        }
1336
    }
1337

    
1338
}
    (1-1/1)