Project

General

Profile

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

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

    
22
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
23

    
24
import eu.etaxonomy.cdm.common.CdmUtils;
25

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

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

    
38
    private static String minuteUtf8 = "\u02B9|\u00B4|\u02CA|\u0301|\u0374|\u2019";
39
    private static String secondUtf8 = "\u02BA|\u030B|\u2033|\u00B4\u00B4|\u201D|''";
40

    
41

    
42
    private class CoordinatePattern{
43
    	String description;
44
    	String pattern;
45
    }
46

    
47

    
48
    private Comparator<CustomHemisphereIndicator> lengthComparator = new Comparator<CustomHemisphereIndicator>(){
49
		@Override
50
        public int compare(CustomHemisphereIndicator ind1, CustomHemisphereIndicator ind2) {
51
			return Integer.valueOf(ind1.getLength()).compareTo(ind2.getLength());
52
		}
53
    };
54

    
55
    //Class constructor
56
    public CoordinateConverter() {
57
        //initialise pattern array
58
        patterns = new ArrayList<CoordinatePattern>();
59

    
60
        //temp pattern variable
61
        CoordinatePattern pattern;
62

    
63

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

    
80

    
81
        //Variations of DD(\u00B0|d)MM.MMM' with whitespace characters
82
        pattern = new CoordinatePattern();
83
        pattern.description = "Variation of DD(\u00B0|d)MM.MMM('|m)";
84
        pattern.pattern =
85
            "(^" +
86
            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
87
            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)?("+ minuteUtf8 + "|'|M|m)?$)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*$))" +
88
            ")" +
89
            "|(^" +
90
            "(\\s)*((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)?("+ minuteUtf8 + "|'|M|m)?)|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*))" +
91
            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
92
            ")";
93
        patterns.add(pattern);
94

    
95

    
96
        //Variations of DD\u00B0MM'SS.SSS" with whitespace characters
97
        pattern = new CoordinatePattern();
98
        pattern.description = "Variation of DD(\u00B0|d)MM("+ minuteUtf8 + "|m)SS.SSS("+secondUtf8+"|s)";
99
        pattern.pattern =
100
            //+/-/Nn/Ss/Ww/EeDD\u00B0MM"+ minuteUtf8 + "SS.SSS
101
            "(^" +
102
            "(\\s)*(\\+|-|W|w|E|e|N|n|S|s)?(\\s)*" +
103
            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*$)" +
104
            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*$)" +
105
            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*("+secondUtf8+"|\"|''|S|s)?(\\s)*$)" +
106
            "|(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*("+secondUtf8+"|\"|''|S|s)?(\\s)*$))" +
107
            ")" +
108
            //DD°MM"+ minuteUtf8 + "SS.SSSNn/Ss/Ww/Ee
109
            "|(^(\\s)*" +
110
            "((\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)?(\\s)*)|" +
111
            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)?(\\s)*)|" +
112
            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)?(\\s)*("+secondUtf8+"|\"|''|S|s)?(\\s)*)|" +
113
            "(\\d{1,3}(\\s)*(\u00B0|\u00BA|D|d)(\\s)*\\d{1,2}(\\s)*("+ minuteUtf8 + "|'|M|m)(\\s)*\\d{1,2}(\\.|\\,)\\d+(\\s)*("+secondUtf8+"|\"|''|S|s)?(\\s)*))" +
114
            "(W|w|E|e|N|n|S|s)?(\\s)*$" +
115
            ")";
116
        patterns.add(pattern);
117

    
118

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

    
135
    }
136

    
137

    
138
    /**
139
     * tests if a string matches one of the defined patterns, returns -1 if no pattern matches
140
     * @param str
141
     * @return
142
     */
143
    private int matchPattern(String str){
144
        int recognised = -1;
145

    
146
        //match the string against each available pattern
147
        for (int i = 0; i < patterns.size(); i++){
148

    
149
        	CoordinatePattern pattern = patterns.get(i);
150
        	Pattern regEx = Pattern.compile(pattern.pattern);
151
        	if (regEx.matcher(str).find()) {
152
        		recognised = i;
153
            	break;
154
            }
155

    
156
        }
157
        return recognised;
158
    }
159

    
160

    
161
    //gets sign of the coordinate (tests for presence of negative sign)
162
    private int getSign(String str){
163

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

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

    
172
        //if a positive indicator is found no need to search further
173
        if (regexPositive.matcher(str).find()){
174
            return 1;
175
        }else{
176
            //if not check whether there was a negative indicator. if so negate otherwise return positive
177
            if (regexNegative.matcher(str).find()){
178
                return -1;
179
            }else{
180
                return 1;
181
            }
182
        }
183
    }
184

    
185

    
186
    //this checks for the coordinate sign by evaluating user supplied data
187
    private int getCustomSign(String str){
188
        //Note:
189
        //Indicators are evaluated from the longest ones to the shortes ones
190
        //So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
191

    
192

    
193
        //search for the presence of indicators
194
        boolean hasPositive = false;
195
        boolean hasNegative = false;
196

    
197
        //keep previous negative indicators here
198
        List<String> previousNegatives = new ArrayList<String>();
199

    
200
        //compare the string with user supplied custom pattern
201
        for (int x = customPtrn.hemisphereIndicators.size() - 1; x >= 0; x--){
202

    
203
            CustomHemisphereIndicator ind = customPtrn.hemisphereIndicators.get(x);
204

    
205
            //test here if the indicator exists (has length >0)
206
            if (ind.getLength() > 0){
207

    
208
                //check if the supplied pattern was marked as case insensitive?
209
                String caseInsensitive = "";
210

    
211
                if (customPtrn.caseInsensitive){
212
                    caseInsensitive = "(?i)";
213
                }
214

    
215
                //create a regex
216
                Pattern tempRegex = Pattern.compile(caseInsensitive + ind.getIndicator());
217

    
218
                //if a pattern is found
219
                if (tempRegex.matcher(str).find()){
220
                    //check whether it's a positive or negative indicator
221
                    if (ind.getPositive()){
222
                        /* Note:
223
                         * See the note below to understand why checking for previous negatives is performed here
224
                        */
225

    
226
                        //check the previous negatives
227
                        if (previousNegatives.size() != 0){
228
                            boolean sameNegative = false;
229

    
230
                            for (int i = previousNegatives.size() - 1; i >= 0; i--){
231
                                if (ind.getIndicator() == previousNegatives.get(i)){
232
                                    sameNegative = true;
233
                                    break;
234
                                }
235
                            }
236

    
237
                            //mark as positive only if the previously found negative is the same
238
                            if (sameNegative){
239
                                hasPositive = true;
240
                            }
241

    
242
                        }else{ //if no negatives before it already marks the sign as positive
243
                            hasPositive = true;
244
                        }
245

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

    
260
                        hasNegative = true;
261

    
262
                    }
263
                }
264
            }
265

    
266
        }
267

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

    
272
        if (hasPositive){
273
            return 1;
274
        } else {
275
            if (hasNegative) {
276
                return -1;
277
            } else {
278
                return 1;
279
            }
280
        }
281
    }
282

    
283

    
284

    
285
    //returns a currently used decimal separator
286
    private String getDecimalSeparator(){
287
    	//TODO not yet transformed from C#
288
//    	return System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
289
        return ".";
290
    }
291

    
292

    
293
    //replaces comma or dot for current decimal separator
294
    private String fixDecimalSeparator(String str){
295
        //Note:
296
        //Coma is replaced as parsers often recognise dot as a decimal separator
297
        //Comma or dot is replaced with a decimal separator here (environment settings)
298
        //But decimal separator has to be used later too;
299

    
300
        String regExReplaceComma = "(\\,|\\.)";
301
        str = str.replaceAll(regExReplaceComma, getDecimalSeparator());
302

    
303
        return str;
304
    }
305

    
306

    
307
    //removes sign
308
    private String removeSign(String str){
309
        String regExRemoveSign = "(\\+|-|S|s|W|w|N|n|E|e)";
310
        str = str.replaceAll(regExRemoveSign, "");
311
        return str;
312
    }
313

    
314
    //removes custom sign indicators
315
    private String removeCustomPatternParts(String str){
316

    
317
        /* Note:
318
         * Symbols are added here so the removing tries to not affect the coordinate too much
319
         * Strings to be removed then are evaluated from the longest ones to the shortes ones
320
         * So when searching for "P" does not affect "PN" as "PN" is evaluated earlier
321
         * */
322

    
323
        //CustomHemisphereIndicator is used here so another object does not have to be created
324
        //only for the string cleanning
325
        List<CustomHemisphereIndicator> stringsToRemove = customPtrn.hemisphereIndicators;
326

    
327
        //add degree symbol
328
        CustomHemisphereIndicator stringToRemove = new CustomHemisphereIndicator("Degree", customPtrn.degreeSymbol,customPtrn.degreeSymbol.length(), false);
329
        stringsToRemove.add(stringToRemove);
330

    
331
        //add minute symbol
332
        stringToRemove = new CustomHemisphereIndicator("Minute", customPtrn.minuteSymbol, customPtrn.minuteSymbol.length(), false);
333
        stringsToRemove.add(stringToRemove);
334

    
335
        //add second symbol
336
        stringToRemove = new CustomHemisphereIndicator("Second", customPtrn.secondSymbol, customPtrn.secondSymbol.length(), false);
337
        stringsToRemove.add(stringToRemove);
338

    
339
        //sort the list (by element's Length property)
340
        Collections.sort(stringsToRemove, lengthComparator);
341

    
342

    
343
//        ListSelectionEv.sort(lengthComparator);
344

    
345

    
346
        for (int x = stringsToRemove.size() - 1; x >= 0; x--){
347

    
348
            CustomHemisphereIndicator toBeRemoved = stringsToRemove.get(x);
349

    
350
            //check if the string exists so replacing does not yield errors
351
            if (toBeRemoved.getLength() > 0)
352
            {
353
                //check if the supplied pattern was marked as case insensitive?
354
                String CaseInsensitive = "";
355

    
356
                if (customPtrn.caseInsensitive){
357
                    CaseInsensitive = "(?i)";
358
                }
359

    
360
                //create regex for replacing
361
                String tempRegex = CaseInsensitive + toBeRemoved.getIndicator();
362

    
363

    
364
                if (toBeRemoved.getName().equals("Degree") || toBeRemoved.getName().equals("Minute")) {
365
                    //replace with a symbol used later for splitting
366
                    str =  str.replaceAll(tempRegex, ":");
367
                } else {
368
                    //remove the string
369
                    str = str.replaceAll(tempRegex, "");
370
                }
371
            }
372
        }
373
        return str;
374
    }
375

    
376

    
377

    
378
    //removes whitespace characters
379
    private String removeWhiteSpace(String str){
380
        str = str.replaceFirst("\\s+", "");
381
        return str;
382
    }
383

    
384

    
385
    //Object for the conversion results
386
    public class ConversionResults{
387
        public boolean patternRecognised;
388
        public String patternMatched;
389
        public String patternType;
390

    
391
        public boolean conversionSuccessful;
392
        public double convertedCoord;
393
        public boolean canBeLat;
394

    
395
        public String conversionComments;
396

    
397
        public Boolean isLongitude;
398

    
399
        public int dd;
400
        public int mm;
401
        public double mmm;
402
        public int ss;
403
        public double sss;
404

    
405
    }
406

    
407

    
408
    public ConversionResults tryConvert(String str){
409
        //some local variables
410
        int sign; //sign of the coordinate
411
        String[] decimalBit, ddmmss, ddmm; //arrays for splitting
412
        double dd = 0, mm = 0, ss = 0, mmm = 0, sss = 0, dec = 0; //parts of the coordinates
413

    
414
        String decSeparatorRaw = String.valueOf(getDecimalSeparator()); //gets the current decimal separator
415
        String decSeparatorRegEx = decSeparatorRaw.replace(".", "\\.");
416

    
417
        ConversionResults results = new ConversionResults();
418

    
419
        //Get the matched pattern
420
        CoordinatePattern pattern;
421
        int ptrnnum = matchPattern(str);
422
        if (ptrnnum != -1) {
423
            pattern = patterns.get(ptrnnum);
424
        } else {
425
            pattern = new CoordinatePattern();
426
            pattern.description = "Unknown";
427
            pattern.pattern = "No pattern matched";
428
        }
429

    
430

    
431

    
432
        if (pattern.description.equals("Variation of DD.DDD")){
433

    
434
       	  	//Sets pattern machted, successful, pattern type and pattern info
435
        	initializeResult(results, pattern);
436

    
437
            //get sign
438
            sign = getSign(str);
439
            results.isLongitude = getIsLongitude(str);
440

    
441
            //Replace comma or dot with a current decimal separator
442
            str = fixDecimalSeparator(str);
443

    
444
            //Remove all the unwanted stuff
445
            str = removeSign(str);
446
            str = removeWhiteSpace(str);
447

    
448
            //Since this is already a decimal degree no spliting is needed
449
            dd =  Double.valueOf(str);
450

    
451
            checkDegreeRange(dd, results);
452
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
453

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

    
456
        	//Sets pattern machted, successful, pattern type and pattern info
457
        	initializeResult(results, pattern);
458

    
459
            //get sign
460
            sign = getSign(str);
461
            results.isLongitude = getIsLongitude(str);
462

    
463
            //Replace comma or dot with a current decimal separator
464
            str = fixDecimalSeparator(str);
465

    
466
            //Remove all the unwanted stuff
467
            str = removeSign(str);
468
            str = removeWhiteSpace(str);
469

    
470
            //do some further replacing
471
            //Replace degree symbol
472
            str = str.replaceAll("(\u00B0|\u00BA|D|d)", ":");
473

    
474
            //remove minute symbol
475
            str = str.replaceAll("("+ minuteUtf8 + "|'|M|m)", "");
476

    
477
            //Extract decimal part
478
            decimalBit = str.split(decSeparatorRegEx);
479

    
480
            //split degrees and minutes
481
            ddmm = decimalBit[0].split(":");
482

    
483

    
484
            //extract values from the strings
485
            dd = Integer.valueOf(ddmm[0]); //Degrees
486

    
487
            if (ddmm.length > 1){ //Minutes
488
                //check if the string is not empty
489
                if (ddmm[1] != "") {
490
                	mm = Integer.valueOf(ddmm[1]);
491
                }
492
            }
493

    
494
            if (decimalBit.length > 1){//DecimalSeconds
495
                //check if the string is not empty
496
                if (decimalBit[1] != "") {
497
                    mmm = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
498
                }
499
            }
500

    
501
            checkDegreeRange(dd, results);
502
            checkMinuteRange(mm, results);
503
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
504

    
505
        }else if (pattern.description.equals("Variation of DD(\u00B0|d)MM("+ minuteUtf8 + "|m)SS.SSS("+secondUtf8+"|s)")){
506

    
507
        	/*
508
             * Note:
509
             * This pattern allows the seconds to be specified with S, s or " or nothing at all
510
             * If the seconds are marked with "s" and there is no other indication of the hemisphere
511
             * the coordinate will be parsed as southern (negative).
512
             *
513
             * If the N / E / W / + indicator is found the coordinate will be parsed appropriately no matter
514
             * what is the second notation
515
            */
516

    
517
        	//Sets pattern matched, successful, pattern type and pattern info
518
        	initializeResult(results, pattern);
519

    
520
            //get sign
521
            sign = getSign(str);
522
            //TODO test S
523
            results.isLongitude = getIsLongitude(str);
524

    
525
            //Replace comma or dot with a current decimal separator
526
            str = fixDecimalSeparator(str);
527

    
528
            //Remove all the unwanted stuff
529
            str = removeSign(str);
530
            str = removeWhiteSpace(str);
531

    
532
            //remove second symbol (s is removed by the get sign method)
533
            //double apostrophe is not removed here as single apostrophe may mark minutes!
534
            //it's taken care of later after extracting the decimal part
535
            str = str.replaceAll("("+secondUtf8+"|\")", "");
536

    
537
            //do some further replacing
538
            //Replace degree symbol
539
            str = str.replaceAll("(\u00B0|\u00B0|D|d|"+ minuteUtf8 + "|'|M|m)",":");
540

    
541
            //Extract decimal part
542
            decimalBit = str.split(decSeparatorRegEx);
543

    
544
            //remove : from the decimal part [1]! This is needed when a double apostrophe was used to mark seconds
545
            if (decimalBit.length > 1)
546
            {
547
                decimalBit[1].replace(":", "");
548
            }
549

    
550
            //split degrees and minutes
551
            ddmmss = decimalBit[0].split(":");
552

    
553

    
554
            //extract values from the strings
555
            dd = Integer.valueOf(ddmmss[0]); //Degrees
556
            if (ddmmss.length > 1){//Minutes
557
                //check if the string is not empty
558
                if (ddmmss[1] != "") {
559
                	mm = Integer.valueOf(ddmmss[1]);
560
                }
561
            }
562
            if (ddmmss.length > 2){//Seconds
563
                //check if the string is not empty
564
                if (ddmmss[2] != "") {
565
                	ss = Integer.valueOf(Nz(ddmmss[2]).trim());
566
                }
567
            }
568
            if (decimalBit.length > 1) { //DecimalSeconds
569
                //check if the string is not empty
570
                if (decimalBit[1] != "") {
571
                    sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
572
                }
573
            }
574

    
575
            checkDegreeRange(dd, results);
576
            checkMinuteRange(mm, results);
577
            checkSecondRange(ss, results);
578

    
579
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
580

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

    
583
        	//Sets pattern machted, successful, pattern type and pattern info
584
        	initializeResult(results, pattern);
585

    
586
            //get sign
587
            sign = getSign(str);
588
            results.isLongitude = getIsLongitude(str);
589

    
590
            //Replace comma or dot with a current decimal separator
591
            str = fixDecimalSeparator(str);
592

    
593
            //Remove all the unwanted stuff
594
            str = removeSign(str);
595
            str = removeWhiteSpace(str);
596

    
597
            //Do some splitting
598
            decimalBit = str.split(decSeparatorRegEx);
599
            ddmmss = decimalBit[0].split(":");
600

    
601

    
602
            //extract values from the strings
603
            dd = Integer.valueOf(ddmmss[0]); //Degrees
604
            if (ddmmss.length > 1)//Minutes
605
            {
606
                //check if the string is not empty
607
                if (ddmmss[1] != "") { mm = Integer.valueOf(ddmmss[1]); }
608
            }
609
            if (ddmmss.length > 2) {//Seconds{
610
                //check if the string is not empty
611
                if (ddmmss[2] != "") {
612
                	ss = Integer.valueOf(ddmmss[2]);
613
                }
614
            }
615
            if (decimalBit.length > 1) { //DecimalSeconds
616
                //check if the string is not empty
617
                if (decimalBit[1] != "") {
618
                    sss = Double.valueOf(decimalBit[1]) / Math.pow(10, (decimalBit[1].length()));
619
                }
620
            }
621

    
622
            checkDegreeRange(dd, results);
623
            checkMinuteRange(mm, results);
624
            checkSecondRange(ss, results);
625

    
626
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
627

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

    
630
        	//Sets pattern machted, successful, pattern type and pattern info
631
        	initializeResult(results, pattern);
632

    
633

    
634
	        //get sign
635
	        sign = getCustomSign(str);
636

    
637
	        //TODO still needs to be adapted to custom pattern
638
	        results.isLongitude = getIsLongitude(str);
639

    
640

    
641
	        //Remove all the unwanted stuff
642
	        //Note: This method also replaces the symbols with ":"
643
	        //Note: In certain cases it may make the coord unparsable
644
	        str = removeCustomPatternParts(str);
645

    
646
	        str = removeWhiteSpace(str);
647

    
648
	        //Replace comma or dot with a current decimal separator
649
	        str = fixDecimalSeparator(str);
650

    
651
	        //remove the ":" here as it is not needed here for decimal degrees
652
	        str = str.replace(":", "");
653

    
654
	        try {
655
	            //Since this is already a decimal degree no spliting is needed
656
	            dd = Double.valueOf(str);
657
	        } catch (Exception e)  {
658
	            results.conversionSuccessful = false;
659
	            results.convertedCoord = 99999; //this is to mark an error...
660
	            results.conversionComments =
661
	                "It looks like the supplied pattern has some ambiguous elements and the parser was unable to parse the coordinate." +
662
	                "<br/>If the supplied symbols used for marking degrees, minutes or seconds contain hemisphere indicators, " +
663
	                "the parser is likely to fail or yield rubbish results even though the pattern itself has been recognised."
664
	                ;
665

    
666
	            //exit method
667
	            return results;
668
	        }
669

    
670
	        //Since this is already a decimal degree no spliting is needed
671
	        dd = Double.valueOf(str);
672

    
673
	        checkDegreeRange(dd, results);
674
	        doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
675

    
676

    
677
	    }else if (pattern.description.equals("Custom variation of DD:MM.MMM")){
678
           //-------------Customs patterns start here-------------
679

    
680
	    	//Sets pattern machted, successful, pattern type and pattern info
681
        	initializeResult(results, pattern);
682

    
683
            //get sign
684
            sign = getCustomSign(str);
685

    
686
            //TODO still needs to be adapted to custom pattern
687
	        results.isLongitude = getIsLongitude(str);
688

    
689

    
690

    
691
            //Remove all the unwanted stuff
692
            //Note: This method also replaces the symbols with ":"
693
            //Note: In certain cases it may make the coord unparsable
694
            str = removeCustomPatternParts(str);
695

    
696
            str = removeWhiteSpace(str);
697

    
698
            //Replace comma or dot with a current decimal separator
699
            str = fixDecimalSeparator(str);
700

    
701

    
702
            //Extract decimal part
703
            decimalBit = str.split(decSeparatorRegEx);
704

    
705
            //split degrees and minutes
706
            ddmm = decimalBit[0].split(":");
707

    
708

    
709
            try {
710
                //extract values from the strings
711
                dd = Integer.valueOf(ddmm[0]); //Degrees
712

    
713
                if (ddmm.length > 1){//Minutes
714
                    //check if the string is not empty
715
                    if (ddmm[1] != "") { mm = Integer.valueOf(ddmm[1]); }
716
                }
717

    
718
                if (decimalBit.length > 1){//DecimalSeconds
719
                    //check if the string is not empty
720
                    if (decimalBit[1] != ""){
721
                        //replace the ":" if any (may be here as a result of custom symbol replacement
722
                        decimalBit[1] = decimalBit[1].replace(":", "");
723

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

    
736
                //exit method
737
                return results;
738
            }
739

    
740

    
741
            checkDegreeRange(dd, results);
742
            checkMinuteRange(mm, results);
743
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
744

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

    
747
	    	//Sets pattern machted, successful, pattern type and pattern info
748
        	initializeResult(results, pattern);
749

    
750

    
751
            //get sign
752
            sign = getCustomSign(str);
753

    
754
            //TODO still needs to be adapted to custom pattern
755
	        results.isLongitude = getIsLongitude(str);
756

    
757

    
758
            //Remove all the unwanted stuff
759
            //Note: This method also replaces the symbols with ":"
760
            //Note: In certain cases it may make the coord unparsable
761
            str = removeCustomPatternParts(str);
762

    
763
            str = removeWhiteSpace(str);
764

    
765
            //Replace comma or dot with a current decimal separator
766
            str = fixDecimalSeparator(str);
767

    
768

    
769
            //Extract decimal part
770
            decimalBit = str.split(decSeparatorRegEx);
771

    
772
            //split degrees and minutes
773
            ddmmss = decimalBit[0].split(":");
774

    
775

    
776
            try {
777

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

    
807
                //exit method
808
                return results;
809
            }
810

    
811

    
812
            checkDegreeRange(dd, results);
813
            checkMinuteRange(mm, results);
814
            checkSecondRange(ss, results);
815

    
816
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
817

    
818
	    }else {   //default  : pattern not recognized
819
            results.patternRecognised = false;
820
            results.patternType = pattern.description;
821
            results.patternMatched = pattern.pattern;
822

    
823
            results.conversionSuccessful = false;
824
            results.convertedCoord = 99999; //this is to mark an error...
825

    
826
            results.conversionComments = "Coordinate pattern not recognised!";
827

    
828
 	    }
829

    
830
        //do the self check here
831
        results = selfTest(results);
832

    
833
        //return conversion results
834
        return results;
835
    }
836

    
837

    
838
	/**
839
     * @param string
840
     * @return
841
     */
842
    private String Nz(String string) {
843
        return CdmUtils.Nz(string);
844
    }
845

    
846

    
847
    /**
848
	 * @param sign
849
	 * @param dd
850
	 * @param mm
851
	 * @param ss
852
	 * @param sss
853
	 * @param results
854
	 */
855
	private void doConvertWithCheck(int sign, double dd, double mm, double mmm, double ss, double sss, ConversionResults results) {
856
		double dec;
857
		//Do the conversion if everything ok
858
		if (results.conversionSuccessful){
859
		    results.conversionComments = "Conversion successful.";
860

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

    
863
		    //one more check to ensure a coord does not exceed 180
864
		    if (dec > 180 | dec < -180){
865
		        results.conversionSuccessful = false;
866
		        results.convertedCoord = 99999; //this is to mark an error...
867
		        results.conversionComments += "Coordinate is either > 180 or < -180; ";
868
		    } else {
869
		        results.convertedCoord = dec;
870

    
871
		        results.conversionComments = "Conversion successful.";
872

    
873
		        //Check whether the coordinate exceeds +/- 90 and mark it in comments
874

    
875
		        if (dec <= 90 && dec >= -90 && (results.isLongitude == null || results.isLongitude == false) ) {
876
                	results.canBeLat = true;
877
                }else{
878
                	results.isLongitude = true;
879
                }
880
		    }
881
		}
882
	}
883

    
884

    
885
	/**
886
	 * @param ss
887
	 * @param results
888
	 */
889
	private void checkSecondRange(double ss, ConversionResults results) {
890
		if (ss > 59) {//seconds
891
		    results.conversionSuccessful = false;
892
		    results.convertedCoord = 99999; //this is to mark an error...
893
		    results.conversionComments += "Seconds fall outside the range: MM >= 60; ";
894
		}
895
	}
896

    
897

    
898
	/**
899
	 * @param mm
900
	 * @param results
901
	 */
902
	private void checkMinuteRange(double mm, ConversionResults results) {
903
		if (mm > 59) {//minutes
904
		    results.conversionSuccessful = false;
905
		    results.convertedCoord = 99999; //this is to mark an error...
906
		    results.conversionComments += "Minutes fall outside the range: MM > 59; ";
907
		}
908
	}
909

    
910

    
911
	/**
912
	 * @param dd
913
	 * @param results
914
	 */
915
	private void checkDegreeRange(double dd, ConversionResults results) {
916
		//do some additional checking if the coords fall into the range
917
		if (dd < -180 | dd > 180){  //degree may require another param specifying whether it's lat or lon...
918
		    results.conversionSuccessful = false;
919
		    results.convertedCoord = 99999; //this is to mark an error...
920
		    results.conversionComments += "Degrees fall outside the range: DD < -180 | DD > 180; ";
921
		}
922
	}
923

    
924

    
925
	/**
926
	 * @param str
927
	 * @return
928
	 */
929
	private Boolean getIsLongitude(String str) {
930
	    //This regex checks for the negative hemisphere indicator
931
		Pattern regexLatitudeNonAmbigous = Pattern.compile("(N|n)");
932
		Pattern regexLatitudeAmbigous = Pattern.compile("(S|s)");
933

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

    
939
        //if a positive indicator is found no need to search further
940
        if (regexLongitude.matcher(str).find()){
941
            return true;
942
        }else if (regexLatitudeNonAmbigous.matcher(str).find()){
943
        	return false;
944
        }else if (regexLatitudeAmbigous.matcher(str).find()){
945
        	Pattern regexLiteralUnits = Pattern.compile("(D|d|M|m)");
946

    
947
        	//if there are no other literal units we assume that S is a
948
        	//direction and not a second indicator
949
            if (! regexLiteralUnits.matcher(str).find()){
950
                return false;
951
            }else if (regexLatitudeAmbigous.matcher(str).groupCount() > 1){
952
            	return false;
953
            }else{
954
            	return null;
955
            }
956
        }else{
957
        	return null;
958
        }
959
	}
960

    
961

    
962
	/**
963
	 * Sets pattern machted, successful, pattern type and pattern info
964
	 * @param results
965
	 * @param pattern
966
	 */
967
	private void initializeResult(ConversionResults results,
968
			CoordinatePattern pattern) {
969
		//Pattern matched
970
		results.patternRecognised = true;
971

    
972
		//Matching pattern succeeded so intialy the parsing is ok
973
		results.conversionSuccessful = true;
974

    
975
		//pattern info
976
		results.patternType = pattern.description;
977
		results.patternMatched = pattern.pattern;
978
	}
979

    
980

    
981
    private ConversionResults selfTest(ConversionResults results){
982

    
983
        ConversionResults newresults = results;
984

    
985
        if (results.conversionSuccessful != false){
986
            int sign = 1;
987
            if (Math.signum(results.convertedCoord) < 0) {
988
            	sign = -1;
989
            }
990

    
991
            double decimalDegrees = sign * results.convertedCoord;
992
            int fullDegrees;
993

    
994
            double decimalMinutes;
995
            int fullMinutes;
996

    
997
            double decimalSeconds;
998
            int fullSeconds;
999

    
1000
            //Get full degrees
1001
            fullDegrees = (int)Math.floor(decimalDegrees);
1002

    
1003
            //get minutes
1004
            decimalMinutes = (decimalDegrees - fullDegrees) * 60;
1005
            fullMinutes = (int)Math.floor(decimalMinutes);
1006

    
1007
            decimalSeconds = (decimalMinutes - fullMinutes) * 60;
1008
            fullSeconds = (int)Math.floor(decimalSeconds);
1009

    
1010
            //save the test results
1011
            newresults.dd = fullDegrees;
1012
            newresults.mm = fullMinutes;
1013
            newresults.mmm = decimalSeconds;
1014
            newresults.ss = fullSeconds;
1015
            newresults.sss = decimalSeconds;
1016

    
1017
        }
1018

    
1019
        return newresults;
1020

    
1021
    }
1022

    
1023

    
1024

    
1025
    //------------ CUSTOM PATTERN BUILDER--------------
1026

    
1027
    public class CustomPatternIn {
1028
        public String north;
1029
        public String south;
1030
        public String east;
1031
        public String west;
1032

    
1033
        public String degreeSymbol;
1034
        public String minuteSymbol;
1035
        public String secondSymbol;
1036

    
1037
        public boolean caseInsensitive;
1038
        public boolean allowWhiteSpace;
1039
        public boolean priorityOverDefaultPatterns;
1040
        public boolean disableDefaultPatterns;
1041

    
1042
    }
1043

    
1044

    
1045
    private class CustomPattern{
1046

    
1047
        public List<CustomHemisphereIndicator> hemisphereIndicators;
1048

    
1049
        public String degreeSymbol;
1050
        public String minuteSymbol;
1051
        public String secondSymbol;
1052

    
1053
        public boolean caseInsensitive;
1054

    
1055
    }
1056

    
1057
    //global variable to be used if a custom pattern is used
1058
    private CustomPattern customPtrn;
1059

    
1060
    //escape some of the chars
1061
    private String escapeChars(String str){
1062
        // backslash - first so it is not messed when other escape chars are corrected for being used in a string
1063
        str = str.replace("\\", "\\\\");
1064

    
1065
        //dot and comma
1066
        str = str.replace(".", "\\.");
1067
        str = str.replace(",", "\\,");
1068

    
1069
        //brackets
1070
        str = str.replace("(", "\\(");
1071
        str = str.replace(")", "\\)");
1072
        str = str.replace("[", "\\[");
1073
        str = str.replace("]", "\\]");
1074
        str = str.replace("{", "\\{");
1075
        str = str.replace("}", "\\}");
1076

    
1077
        //other replacements
1078
        str = str.replace("^", "\\^");
1079
        str = str.replace("$", "\\$");
1080
        str = str.replace("+", "\\+");
1081
        str = str.replace("*", "\\*");
1082
        str = str.replace("?", "\\?");
1083
        str = str.replace("|", "\\|");
1084

    
1085
        return str;
1086
    }
1087

    
1088

    
1089
    //this implements sorting by using system.Icomparable - sorting is needed later when replacing
1090
    private class CustomHemisphereIndicator implements Comparable<CustomHemisphereIndicator> {
1091
        //private variables
1092
        private int m_length;
1093
        private String m_name;
1094
        private String m_indicator;
1095
        private boolean m_positive;
1096

    
1097
        //constructor
1098
        public CustomHemisphereIndicator(String name, String indicator, int length, boolean positive){
1099
            this.m_name = name;
1100
            this.m_indicator = indicator;
1101
            this.m_length = length;
1102
            this.m_positive = positive;
1103
        }
1104

    
1105
        //properties
1106

    
1107
        public String getName(){
1108
        	return this.m_name;
1109
        }
1110
        public void setName(String value){
1111
        	this.m_name = value;
1112
        }
1113

    
1114
        public String getIndicator(){
1115
            return this.m_indicator;
1116
        }
1117
        public void setIndicator(String value){
1118
            this.m_indicator = value;
1119
        }
1120

    
1121
        public int getLength(){
1122
            return this.m_length;
1123
        }
1124
        public void setLength(int value){
1125
            this.m_length = value;
1126
        }
1127

    
1128

    
1129
        public boolean getPositive(){
1130
            return this.m_positive;
1131
        }
1132
        public void setPositive(boolean value){
1133
            this.m_positive = value;
1134
        }
1135

    
1136
        /* Less than zero if this instance is less than obj.
1137
         * Zero if this instance is equal to obj.
1138
         * Greater than zero if this instance is greater than obj.
1139
         *
1140
         * This method uses the predefined method Int32.CompareTo
1141
         * */
1142

    
1143
        @Override
1144
        public int compareTo(CustomHemisphereIndicator ind){
1145

    
1146
	        //no need to rewrite the code again, we have Integer.compareTo ready to use
1147
	        return Integer.valueOf(this.getLength()).compareTo(Integer.valueOf(ind.getLength()));
1148
        }
1149
    }
1150

    
1151

    
1152

    
1153
    //This adds custom pattern to a list of already predefined patterns
1154
    //useful for batch conversions - allows for totally mixed input data (predefined & custom)
1155
    public void addCustomPattern(CustomPatternIn patternIn){
1156

    
1157
        //new custom pattern object - to pass the needed data farther
1158
        CustomPattern pattern = new CustomPattern();
1159

    
1160
        //keep indicators for parsing
1161
        List<CustomHemisphereIndicator> indicators = new ArrayList<CustomHemisphereIndicator>();
1162

    
1163
        //north
1164
        CustomHemisphereIndicator ind = new CustomHemisphereIndicator("North", patternIn.north, patternIn.north.length() ,true);
1165
        indicators.add(ind);
1166

    
1167
        //south
1168
        ind = new CustomHemisphereIndicator("South", patternIn.south, patternIn.south.length(), false);
1169
        indicators.add(ind);
1170

    
1171
        //east
1172
        ind = new CustomHemisphereIndicator("East", patternIn.east, patternIn.east.length(), true);
1173
        indicators.add(ind);
1174

    
1175
        //west
1176
        ind = new CustomHemisphereIndicator("West", patternIn.west, patternIn.west.length(), false);
1177
        indicators.add(ind);
1178

    
1179
        //sort the arraylist
1180
        Collections.sort(indicators, lengthComparator);
1181

    
1182

    
1183
        //add it to the pattern object
1184
        pattern.hemisphereIndicators = indicators;
1185

    
1186
        //case insensitive
1187
        pattern.caseInsensitive = patternIn.caseInsensitive;
1188

    
1189
        //keep symbols for parsing
1190
        pattern.degreeSymbol = patternIn.degreeSymbol;
1191
        pattern.minuteSymbol = patternIn.minuteSymbol;
1192
        pattern.secondSymbol = patternIn.secondSymbol;
1193

    
1194

    
1195
        //save the data
1196
        customPtrn = pattern;
1197

    
1198

    
1199
        //----------------build custom patterns----------------
1200

    
1201
        //prepare hemisphere indicators
1202
        String north = escapeChars(patternIn.north);
1203
        String south = escapeChars(patternIn.south);
1204
        String east = escapeChars(patternIn.east);
1205
        String west = escapeChars(patternIn.west);
1206

    
1207
        //prepare symbols
1208
        String degreesymbol = "";
1209
        if (patternIn.degreeSymbol != ""){
1210
            degreesymbol = "(" + escapeChars(patternIn.degreeSymbol) + ")?";
1211
        }
1212

    
1213
        String minutesymbol = "";
1214
        if (patternIn.minuteSymbol != ""){
1215
            minutesymbol = "(" + escapeChars(patternIn.minuteSymbol) + ")?";
1216
        }
1217

    
1218
        String secondsymbol = "";
1219
        if (escapeChars(patternIn.secondSymbol) != ""){
1220
            secondsymbol = "(" + escapeChars(patternIn.secondSymbol) + ")?";
1221
        }
1222

    
1223

    
1224
        //is the pattern to be case insensitive?
1225
        String CaseInsensitive = "";
1226
        if (patternIn.caseInsensitive){
1227
            CaseInsensitive = "(?i)";
1228
        }
1229

    
1230
        //allow whitespace
1231
        String WhiteSpace = "";
1232
        if (patternIn.allowWhiteSpace == true){
1233
            WhiteSpace = "(\\s)*";
1234
        }
1235

    
1236
        //hemisphere indicator
1237
        String HemisphereIndicator = "";
1238

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

    
1249
        //add east
1250
        if (north == "" & south == ""){
1251
            HemisphereIndicator += east;
1252
        } else {
1253
            if (east != ""){
1254
                HemisphereIndicator += "|" + east;
1255
            }
1256
        }
1257

    
1258
        //add west
1259
        if (north == "" & south == "" & east == ""){
1260
            HemisphereIndicator += west;
1261
        } else {
1262
            if (west != "") {
1263
                HemisphereIndicator += "|" + west;
1264
            }
1265
        }
1266

    
1267
        //add remaining bits if not empty
1268
        if (HemisphereIndicator != "") {
1269
            HemisphereIndicator = "(" + HemisphereIndicator + ")?";
1270
        }
1271

    
1272
        List<CoordinatePattern> customPatterns = new ArrayList<CoordinatePattern>();
1273

    
1274
        //create custom patterns based on the specified user's input
1275
        CoordinatePattern ptrn;
1276

    
1277
        //Custom variation of DD.DDD
1278
        ptrn = new CoordinatePattern();
1279
        ptrn.description = "Custom variation of DD.DDD";
1280
        ptrn.pattern  =
1281
            CaseInsensitive + "(^" +
1282
            WhiteSpace + HemisphereIndicator + WhiteSpace +
1283
            "(" +
1284
            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + "$)|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + "$)" +
1285
            ")" +
1286
            "|(^" + WhiteSpace +
1287
            "(" +
1288
            "(\\d{1,3}(\\.|\\,)?" + WhiteSpace + degreesymbol + WhiteSpace + ")|(\\d{1,3}(\\.|\\,)\\d+" + WhiteSpace + degreesymbol + WhiteSpace + ")" +
1289
            ")" +
1290
            HemisphereIndicator + WhiteSpace + "$" +
1291
            "))"
1292
            ;
1293
        customPatterns.add(ptrn);
1294

    
1295
        //Custom variation of DD:MM.MMM
1296
        ptrn = new CoordinatePattern();
1297
        ptrn.description = "Custom variation of DD:MM.MMM";
1298
        ptrn.pattern =
1299
            CaseInsensitive + "(^" +
1300
            WhiteSpace + HemisphereIndicator + WhiteSpace +
1301
            "(" +
1302
            "(\\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 + "$)" +
1303
            ")" +
1304
            "|(^" + WhiteSpace +
1305
            "(" +
1306
            "(\\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 + ")" +
1307
            ")" +
1308
            HemisphereIndicator + WhiteSpace + "$" +
1309
            "))"
1310
            ;
1311
        customPatterns.add(ptrn);
1312

    
1313
        //Custom variation of DD:MM:SS.SSS
1314
        ptrn = new CoordinatePattern();
1315
        ptrn.description = "Custom variation of DD:MM:SS.SSS";
1316
        ptrn.pattern =
1317
            CaseInsensitive + "(^" +
1318
            WhiteSpace + HemisphereIndicator + WhiteSpace +
1319
            "(" +
1320
            "(\\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 + "$)" +
1321
            ")" +
1322
            "|(^" + WhiteSpace +
1323
            "(" +
1324
            "(\\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 + ")" +
1325
            ")" +
1326
            HemisphereIndicator + WhiteSpace + "$" +
1327
            "))"
1328
            ;
1329
        customPatterns.add(ptrn);
1330

    
1331
        //check if the default patterns are to be used
1332
        if (patternIn.disableDefaultPatterns) {
1333
            patterns = customPatterns;
1334
        } else { //if all patterns are to be used check which set has the matching priority
1335

    
1336
            //check if the custom patterns are to have priority over the default ones
1337
            if (patternIn.priorityOverDefaultPatterns){
1338

    
1339
                //add default patterns to the custom patterns
1340
                for (int i = 0; i < patterns.size(); i++){
1341
                    customPatterns.add(patterns.get(i));
1342
                }
1343

    
1344
                //swap array lists
1345
                patterns = customPatterns;
1346

    
1347
            }else{
1348
                //add custom patterns to the default patterns
1349
                for (int i = 0; i < customPatterns.size(); i++){
1350
                    patterns.add(customPatterns.get(i));
1351

    
1352
                }
1353
            }
1354
        }
1355
    }
1356

    
1357
}
    (1-1/1)