Project

General

Profile

Download (47.8 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.log4j.Logger;
23

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

    
26
/**
27
 * @author a.mueller
28
 * @date 07.06.2010
29
 *
30
 */
31
public class CoordinateConverter {
32
	@SuppressWarnings("unused")
33
	private static final Logger logger = Logger.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
    //tests if a string matches one of the defined patterns
139
    private int matchPattern(String str){
140
        int recognised = -1;
141

    
142
        //match the string against each available patern
143
        for (int i = 0; i < patterns.size(); i++){
144

    
145
        	CoordinatePattern pattern = patterns.get(i);
146
        	Pattern regEx = Pattern.compile(pattern.pattern);
147
        	if (regEx.matcher(str).find()) {
148
        		recognised = i;
149
            	break;
150
            }
151

    
152
        }
153
        return recognised;
154
    }
155

    
156

    
157
    //gets sign of the coordinate (tests for presence of negative sign)
158
    private int getSign(String str){
159

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

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

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

    
181

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

    
188

    
189
        //search for the presence of indicators
190
        boolean hasPositive = false;
191
        boolean hasNegative = false;
192

    
193
        //keep previous negative indicators here
194
        List<String> previousNegatives = new ArrayList<String>();
195

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

    
199
            CustomHemisphereIndicator ind = customPtrn.hemisphereIndicators.get(x);
200

    
201
            //test here if the indicator exists (has length >0)
202
            if (ind.getLength() > 0){
203

    
204
                //check if the supplied pattern was marked as case insensitive?
205
                String caseInsensitive = "";
206

    
207
                if (customPtrn.caseInsensitive){
208
                    caseInsensitive = "(?i)";
209
                }
210

    
211
                //create a regex
212
                Pattern tempRegex = Pattern.compile(caseInsensitive + ind.getIndicator());
213

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

    
222
                        //check the previous negatives
223
                        if (previousNegatives.size() != 0){
224
                            boolean sameNegative = false;
225

    
226
                            for (int i = previousNegatives.size() - 1; i >= 0; i--){
227
                                if (ind.getIndicator() == previousNegatives.get(i)){
228
                                    sameNegative = true;
229
                                    break;
230
                                }
231
                            }
232

    
233
                            //mark as positive only if the previously found negative is the same
234
                            if (sameNegative){
235
                                hasPositive = true;
236
                            }
237

    
238
                        }else{ //if no negatives before it already marks the sign as positive
239
                            hasPositive = true;
240
                        }
241

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

    
256
                        hasNegative = true;
257

    
258
                    }
259
                }
260
            }
261

    
262
        }
263

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

    
268
        if (hasPositive){
269
            return 1;
270
        } else {
271
            if (hasNegative) {
272
                return -1;
273
            } else {
274
                return 1;
275
            }
276
        }
277
    }
278

    
279

    
280

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

    
288

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

    
296
        String regExReplaceComma = "(\\,|\\.)";
297
        str = str.replaceAll(regExReplaceComma, getDecimalSeparator());
298

    
299
        return str;
300
    }
301

    
302

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

    
310
    //removes custom sign indicators
311
    private String removeCustomPatternParts(String str){
312

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

    
319
        //CustomHemisphereIndicator is used here so another object does not have to be created
320
        //only for the string cleanning
321
        List<CustomHemisphereIndicator> stringsToRemove = customPtrn.hemisphereIndicators;
322

    
323
        //add degree symbol
324
        CustomHemisphereIndicator stringToRemove = new CustomHemisphereIndicator("Degree", customPtrn.degreeSymbol,customPtrn.degreeSymbol.length(), false);
325
        stringsToRemove.add(stringToRemove);
326

    
327
        //add minute symbol
328
        stringToRemove = new CustomHemisphereIndicator("Minute", customPtrn.minuteSymbol, customPtrn.minuteSymbol.length(), false);
329
        stringsToRemove.add(stringToRemove);
330

    
331
        //add second symbol
332
        stringToRemove = new CustomHemisphereIndicator("Second", customPtrn.secondSymbol, customPtrn.secondSymbol.length(), false);
333
        stringsToRemove.add(stringToRemove);
334

    
335
        //sort the list (by element's Length property)
336
        Collections.sort(stringsToRemove, lengthComparator);
337

    
338

    
339
//        ListSelectionEv.sort(lengthComparator);
340

    
341

    
342
        for (int x = stringsToRemove.size() - 1; x >= 0; x--){
343

    
344
            CustomHemisphereIndicator toBeRemoved = stringsToRemove.get(x);
345

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

    
352
                if (customPtrn.caseInsensitive){
353
                    CaseInsensitive = "(?i)";
354
                }
355

    
356
                //create regex for replacing
357
                String tempRegex = CaseInsensitive + toBeRemoved.getIndicator();
358

    
359

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

    
372

    
373

    
374
    //removes whitespace characters
375
    private String removeWhiteSpace(String str){
376
        str = str.replaceFirst("\\s+", "");
377
        return str;
378
    }
379

    
380

    
381
    //Object for the conversion results
382
    public class ConversionResults{
383
        public boolean patternRecognised;
384
        public String patternMatched;
385
        public String patternType;
386

    
387
        public boolean conversionSuccessful;
388
        public double convertedCoord;
389
        public boolean canBeLat;
390

    
391
        public String conversionComments;
392

    
393
        public Boolean isLongitude;
394

    
395
        public int dd;
396
        public int mm;
397
        public double mmm;
398
        public int ss;
399
        public double sss;
400

    
401
    }
402

    
403

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

    
410
        String decSeparatorRaw = String.valueOf(getDecimalSeparator()); //gets the current decimal separator
411
        String decSeparatorRegEx = decSeparatorRaw.replace(".", "\\.");
412

    
413
        ConversionResults results = new ConversionResults();
414

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

    
426

    
427

    
428
        if (pattern.description.equals("Variation of DD.DDD")){
429

    
430
       	  	//Sets pattern machted, successful, pattern type and pattern info
431
        	initializeResult(results, pattern);
432

    
433
            //get sign
434
            sign = getSign(str);
435
            results.isLongitude = getIsLongitude(str);
436

    
437
            //Replace comma or dot with a current decimal separator
438
            str = fixDecimalSeparator(str);
439

    
440
            //Remove all the unwanted stuff
441
            str = removeSign(str);
442
            str = removeWhiteSpace(str);
443

    
444
            //Since this is already a decimal degree no spliting is needed
445
            dd =  Double.valueOf(str);
446

    
447
            checkDegreeRange(dd, results);
448
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
449

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

    
452
        	//Sets pattern machted, successful, pattern type and pattern info
453
        	initializeResult(results, pattern);
454

    
455
            //get sign
456
            sign = getSign(str);
457
            results.isLongitude = getIsLongitude(str);
458

    
459
            //Replace comma or dot with a current decimal separator
460
            str = fixDecimalSeparator(str);
461

    
462
            //Remove all the unwanted stuff
463
            str = removeSign(str);
464
            str = removeWhiteSpace(str);
465

    
466
            //do some further replacing
467
            //Replace degree symbol
468
            str = str.replaceAll("(\u00B0|\u00BA|D|d)", ":");
469

    
470
            //remove minute symbol
471
            str = str.replaceAll("("+ minuteUtf8 + "|'|M|m)", "");
472

    
473
            //Extract decimal part
474
            decimalBit = str.split(decSeparatorRegEx);
475

    
476
            //split degrees and minutes
477
            ddmm = decimalBit[0].split(":");
478

    
479

    
480
            //extract values from the strings
481
            dd = Integer.valueOf(ddmm[0]); //Degrees
482

    
483
            if (ddmm.length > 1){ //Minutes
484
                //check if the string is not empty
485
                if (ddmm[1] != "") {
486
                	mm = Integer.valueOf(ddmm[1]);
487
                }
488
            }
489

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

    
497
            checkDegreeRange(dd, results);
498
            checkMinuteRange(mm, results);
499
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
500

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

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

    
513
        	//Sets pattern machted, successful, pattern type and pattern info
514
        	initializeResult(results, pattern);
515

    
516
            //get sign
517
            sign = getSign(str);
518
            //TODO test S
519
            results.isLongitude = getIsLongitude(str);
520

    
521
            //Replace comma or dot with a current decimal separator
522
            str = fixDecimalSeparator(str);
523

    
524
            //Remove all the unwanted stuff
525
            str = removeSign(str);
526
            str = removeWhiteSpace(str);
527

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

    
533
            //do some further replacing
534
            //Replace degree symbol
535
            str = str.replaceAll("(\u00B0|\u00B0|D|d|"+ minuteUtf8 + "|'|M|m)",":");
536

    
537
            //Extract decimal part
538
            decimalBit = str.split(decSeparatorRegEx);
539

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

    
546
            //split degrees and minutes
547
            ddmmss = decimalBit[0].split(":");
548

    
549

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

    
571
            checkDegreeRange(dd, results);
572
            checkMinuteRange(mm, results);
573
            checkSecondRange(ss, results);
574

    
575
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
576

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

    
579
        	//Sets pattern machted, successful, pattern type and pattern info
580
        	initializeResult(results, pattern);
581

    
582
            //get sign
583
            sign = getSign(str);
584
            results.isLongitude = getIsLongitude(str);
585

    
586
            //Replace comma or dot with a current decimal separator
587
            str = fixDecimalSeparator(str);
588

    
589
            //Remove all the unwanted stuff
590
            str = removeSign(str);
591
            str = removeWhiteSpace(str);
592

    
593
            //Do some splitting
594
            decimalBit = str.split(decSeparatorRegEx);
595
            ddmmss = decimalBit[0].split(":");
596

    
597

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

    
618
            checkDegreeRange(dd, results);
619
            checkMinuteRange(mm, results);
620
            checkSecondRange(ss, results);
621

    
622
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
623

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

    
626
        	//Sets pattern machted, successful, pattern type and pattern info
627
        	initializeResult(results, pattern);
628

    
629

    
630
	        //get sign
631
	        sign = getCustomSign(str);
632

    
633
	        //TODO still needs to be adapted to custom pattern
634
	        results.isLongitude = getIsLongitude(str);
635

    
636

    
637
	        //Remove all the unwanted stuff
638
	        //Note: This method also replaces the symbols with ":"
639
	        //Note: In certain cases it may make the coord unparsable
640
	        str = removeCustomPatternParts(str);
641

    
642
	        str = removeWhiteSpace(str);
643

    
644
	        //Replace comma or dot with a current decimal separator
645
	        str = fixDecimalSeparator(str);
646

    
647
	        //remove the ":" here as it is not needed here for decimal degrees
648
	        str = str.replace(":", "");
649

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

    
662
	            //exit method
663
	            return results;
664
	        }
665

    
666
	        //Since this is already a decimal degree no spliting is needed
667
	        dd = Double.valueOf(str);
668

    
669
	        checkDegreeRange(dd, results);
670
	        doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
671

    
672

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

    
676
	    	//Sets pattern machted, successful, pattern type and pattern info
677
        	initializeResult(results, pattern);
678

    
679
            //get sign
680
            sign = getCustomSign(str);
681

    
682
            //TODO still needs to be adapted to custom pattern
683
	        results.isLongitude = getIsLongitude(str);
684

    
685

    
686

    
687
            //Remove all the unwanted stuff
688
            //Note: This method also replaces the symbols with ":"
689
            //Note: In certain cases it may make the coord unparsable
690
            str = removeCustomPatternParts(str);
691

    
692
            str = removeWhiteSpace(str);
693

    
694
            //Replace comma or dot with a current decimal separator
695
            str = fixDecimalSeparator(str);
696

    
697

    
698
            //Extract decimal part
699
            decimalBit = str.split(decSeparatorRegEx);
700

    
701
            //split degrees and minutes
702
            ddmm = decimalBit[0].split(":");
703

    
704

    
705
            try {
706
                //extract values from the strings
707
                dd = Integer.valueOf(ddmm[0]); //Degrees
708

    
709
                if (ddmm.length > 1){//Minutes
710
                    //check if the string is not empty
711
                    if (ddmm[1] != "") { mm = Integer.valueOf(ddmm[1]); }
712
                }
713

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

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

    
732
                //exit method
733
                return results;
734
            }
735

    
736

    
737
            checkDegreeRange(dd, results);
738
            checkMinuteRange(mm, results);
739
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
740

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

    
743
	    	//Sets pattern machted, successful, pattern type and pattern info
744
        	initializeResult(results, pattern);
745

    
746

    
747
            //get sign
748
            sign = getCustomSign(str);
749

    
750
            //TODO still needs to be adapted to custom pattern
751
	        results.isLongitude = getIsLongitude(str);
752

    
753

    
754
            //Remove all the unwanted stuff
755
            //Note: This method also replaces the symbols with ":"
756
            //Note: In certain cases it may make the coord unparsable
757
            str = removeCustomPatternParts(str);
758

    
759
            str = removeWhiteSpace(str);
760

    
761
            //Replace comma or dot with a current decimal separator
762
            str = fixDecimalSeparator(str);
763

    
764

    
765
            //Extract decimal part
766
            decimalBit = str.split(decSeparatorRegEx);
767

    
768
            //split degrees and minutes
769
            ddmmss = decimalBit[0].split(":");
770

    
771

    
772
            try {
773

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

    
803
                //exit method
804
                return results;
805
            }
806

    
807

    
808
            checkDegreeRange(dd, results);
809
            checkMinuteRange(mm, results);
810
            checkSecondRange(ss, results);
811

    
812
            doConvertWithCheck(sign, dd, mm, mmm, ss, sss, results);
813

    
814
	    }else {   //default  : pattern not recognized
815
            results.patternRecognised = false;
816
            results.patternType = pattern.description;
817
            results.patternMatched = pattern.pattern;
818

    
819
            results.conversionSuccessful = false;
820
            results.convertedCoord = 99999; //this is to mark an error...
821

    
822
            results.conversionComments = "Coordinate pattern not recognised!";
823

    
824
 	    }
825

    
826
        //do the self check here
827
        results = selfTest(results);
828

    
829
        //return conversion results
830
        return results;
831
    }
832

    
833

    
834
	/**
835
     * @param string
836
     * @return
837
     */
838
    private String Nz(String string) {
839
        return CdmUtils.Nz(string);
840
    }
841

    
842

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

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

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

    
867
		        results.conversionComments = "Conversion successful.";
868

    
869
		        //Check whether the coordinate exceeds +/- 90 and mark it in comments
870

    
871
		        if (dec <= 90 && dec >= -90 && (results.isLongitude == null || results.isLongitude == false) ) {
872
                	results.canBeLat = true;
873
                }else{
874
                	results.isLongitude = true;
875
                }
876
		    }
877
		}
878
	}
879

    
880

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

    
893

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

    
906

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

    
920

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

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

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

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

    
957

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

    
968
		//Matching pattern succeeded so intialy the parsing is ok
969
		results.conversionSuccessful = true;
970

    
971
		//pattern info
972
		results.patternType = pattern.description;
973
		results.patternMatched = pattern.pattern;
974
	}
975

    
976

    
977
    private ConversionResults selfTest(ConversionResults results){
978

    
979
        ConversionResults newresults = results;
980

    
981
        if (results.conversionSuccessful != false){
982
            int sign = 1;
983
            if (Math.signum(results.convertedCoord) < 0) {
984
            	sign = -1;
985
            }
986

    
987
            double decimalDegrees = sign * results.convertedCoord;
988
            int fullDegrees;
989

    
990
            double decimalMinutes;
991
            int fullMinutes;
992

    
993
            double decimalSeconds;
994
            int fullSeconds;
995

    
996
            //Get full degrees
997
            fullDegrees = (int)Math.floor(decimalDegrees);
998

    
999
            //get minutes
1000
            decimalMinutes = (decimalDegrees - fullDegrees) * 60;
1001
            fullMinutes = (int)Math.floor(decimalMinutes);
1002

    
1003
            decimalSeconds = (decimalMinutes - fullMinutes) * 60;
1004
            fullSeconds = (int)Math.floor(decimalSeconds);
1005

    
1006
            //save the test results
1007
            newresults.dd = fullDegrees;
1008
            newresults.mm = fullMinutes;
1009
            newresults.mmm = decimalSeconds;
1010
            newresults.ss = fullSeconds;
1011
            newresults.sss = decimalSeconds;
1012

    
1013
        }
1014

    
1015
        return newresults;
1016

    
1017
    }
1018

    
1019

    
1020

    
1021
    //------------ CUSTOM PATTERN BUILDER--------------
1022

    
1023
    public class CustomPatternIn {
1024
        public String north;
1025
        public String south;
1026
        public String east;
1027
        public String west;
1028

    
1029
        public String degreeSymbol;
1030
        public String minuteSymbol;
1031
        public String secondSymbol;
1032

    
1033
        public boolean caseInsensitive;
1034
        public boolean allowWhiteSpace;
1035
        public boolean priorityOverDefaultPatterns;
1036
        public boolean disableDefaultPatterns;
1037

    
1038
    }
1039

    
1040

    
1041
    private class CustomPattern{
1042

    
1043
        public List<CustomHemisphereIndicator> hemisphereIndicators;
1044

    
1045
        public String degreeSymbol;
1046
        public String minuteSymbol;
1047
        public String secondSymbol;
1048

    
1049
        public boolean caseInsensitive;
1050

    
1051
    }
1052

    
1053
    //global variable to be used if a custom pattern is used
1054
    private CustomPattern customPtrn;
1055

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

    
1061
        //dot and comma
1062
        str = str.replace(".", "\\.");
1063
        str = str.replace(",", "\\,");
1064

    
1065
        //brackets
1066
        str = str.replace("(", "\\(");
1067
        str = str.replace(")", "\\)");
1068
        str = str.replace("[", "\\[");
1069
        str = str.replace("]", "\\]");
1070
        str = str.replace("{", "\\{");
1071
        str = str.replace("}", "\\}");
1072

    
1073
        //other replacements
1074
        str = str.replace("^", "\\^");
1075
        str = str.replace("$", "\\$");
1076
        str = str.replace("+", "\\+");
1077
        str = str.replace("*", "\\*");
1078
        str = str.replace("?", "\\?");
1079
        str = str.replace("|", "\\|");
1080

    
1081
        return str;
1082
    }
1083

    
1084

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

    
1093
        //constructor
1094
        public CustomHemisphereIndicator(String name, String indicator, int length, boolean positive){
1095
            this.m_name = name;
1096
            this.m_indicator = indicator;
1097
            this.m_length = length;
1098
            this.m_positive = positive;
1099
        }
1100

    
1101
        //properties
1102

    
1103
        public String getName(){
1104
        	return this.m_name;
1105
        }
1106
        public void setName(String value){
1107
        	this.m_name = value;
1108
        }
1109

    
1110
        public String getIndicator(){
1111
            return this.m_indicator;
1112
        }
1113
        public void setIndicator(String value){
1114
            this.m_indicator = value;
1115
        }
1116

    
1117
        public int getLength(){
1118
            return this.m_length;
1119
        }
1120
        public void setLength(int value){
1121
            this.m_length = value;
1122
        }
1123

    
1124

    
1125
        public boolean getPositive(){
1126
            return this.m_positive;
1127
        }
1128
        public void setPositive(boolean value){
1129
            this.m_positive = value;
1130
        }
1131

    
1132
        /* Less than zero if this instance is less than obj.
1133
         * Zero if this instance is equal to obj.
1134
         * Greater than zero if this instance is greater than obj.
1135
         *
1136
         * This method uses the predefined method Int32.CompareTo
1137
         * */
1138

    
1139
        @Override
1140
        public int compareTo(CustomHemisphereIndicator ind){
1141

    
1142
	        //no need to rewrite the code again, we have Integer.compareTo ready to use
1143
	        return Integer.valueOf(this.getLength()).compareTo(Integer.valueOf(ind.getLength()));
1144
        }
1145
    }
1146

    
1147

    
1148

    
1149
    //This adds custom pattern to a list of already predefined patterns
1150
    //useful for batch conversions - allows for totally mixed input data (predefined & custom)
1151
    public void addCustomPattern(CustomPatternIn patternIn){
1152

    
1153
        //new custom pattern object - to pass the needed data farther
1154
        CustomPattern pattern = new CustomPattern();
1155

    
1156
        //keep indicators for parsing
1157
        List<CustomHemisphereIndicator> indicators = new ArrayList<CustomHemisphereIndicator>();
1158

    
1159
        //north
1160
        CustomHemisphereIndicator ind = new CustomHemisphereIndicator("North", patternIn.north, patternIn.north.length() ,true);
1161
        indicators.add(ind);
1162

    
1163
        //south
1164
        ind = new CustomHemisphereIndicator("South", patternIn.south, patternIn.south.length(), false);
1165
        indicators.add(ind);
1166

    
1167
        //east
1168
        ind = new CustomHemisphereIndicator("East", patternIn.east, patternIn.east.length(), true);
1169
        indicators.add(ind);
1170

    
1171
        //west
1172
        ind = new CustomHemisphereIndicator("West", patternIn.west, patternIn.west.length(), false);
1173
        indicators.add(ind);
1174

    
1175
        //sort the arraylist
1176
        Collections.sort(indicators, lengthComparator);
1177

    
1178

    
1179
        //add it to the pattern object
1180
        pattern.hemisphereIndicators = indicators;
1181

    
1182
        //case insensitive
1183
        pattern.caseInsensitive = patternIn.caseInsensitive;
1184

    
1185
        //keep symbols for parsing
1186
        pattern.degreeSymbol = patternIn.degreeSymbol;
1187
        pattern.minuteSymbol = patternIn.minuteSymbol;
1188
        pattern.secondSymbol = patternIn.secondSymbol;
1189

    
1190

    
1191
        //save the data
1192
        customPtrn = pattern;
1193

    
1194

    
1195
        //----------------build custom patterns----------------
1196

    
1197
        //prepare hemisphere indicators
1198
        String north = escapeChars(patternIn.north);
1199
        String south = escapeChars(patternIn.south);
1200
        String east = escapeChars(patternIn.east);
1201
        String west = escapeChars(patternIn.west);
1202

    
1203
        //prepare symbols
1204
        String degreesymbol = "";
1205
        if (patternIn.degreeSymbol != ""){
1206
            degreesymbol = "(" + escapeChars(patternIn.degreeSymbol) + ")?";
1207
        }
1208

    
1209
        String minutesymbol = "";
1210
        if (patternIn.minuteSymbol != ""){
1211
            minutesymbol = "(" + escapeChars(patternIn.minuteSymbol) + ")?";
1212
        }
1213

    
1214
        String secondsymbol = "";
1215
        if (escapeChars(patternIn.secondSymbol) != ""){
1216
            secondsymbol = "(" + escapeChars(patternIn.secondSymbol) + ")?";
1217
        }
1218

    
1219

    
1220
        //is the pattern to be case insensitive?
1221
        String CaseInsensitive = "";
1222
        if (patternIn.caseInsensitive){
1223
            CaseInsensitive = "(?i)";
1224
        }
1225

    
1226
        //allow whitespace
1227
        String WhiteSpace = "";
1228
        if (patternIn.allowWhiteSpace == true){
1229
            WhiteSpace = "(\\s)*";
1230
        }
1231

    
1232
        //hemisphere indicator
1233
        String HemisphereIndicator = "";
1234

    
1235
        //add north if present
1236
        if (north == ""){
1237
            HemisphereIndicator += south;
1238
        }else{
1239
            HemisphereIndicator += north;
1240
            if (south != ""){
1241
                HemisphereIndicator += "|" + south;
1242
            }
1243
        }
1244

    
1245
        //add east
1246
        if (north == "" & south == ""){
1247
            HemisphereIndicator += east;
1248
        } else {
1249
            if (east != ""){
1250
                HemisphereIndicator += "|" + east;
1251
            }
1252
        }
1253

    
1254
        //add west
1255
        if (north == "" & south == "" & east == ""){
1256
            HemisphereIndicator += west;
1257
        } else {
1258
            if (west != "") {
1259
                HemisphereIndicator += "|" + west;
1260
            }
1261
        }
1262

    
1263
        //add remaining bits if not empty
1264
        if (HemisphereIndicator != "") {
1265
            HemisphereIndicator = "(" + HemisphereIndicator + ")?";
1266
        }
1267

    
1268
        List<CoordinatePattern> customPatterns = new ArrayList<CoordinatePattern>();
1269

    
1270
        //create custom patterns based on the specified user's input
1271
        CoordinatePattern ptrn;
1272

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

    
1291
        //Custom variation of DD:MM.MMM
1292
        ptrn = new CoordinatePattern();
1293
        ptrn.description = "Custom variation of DD:MM.MMM";
1294
        ptrn.pattern =
1295
            CaseInsensitive + "(^" +
1296
            WhiteSpace + HemisphereIndicator + WhiteSpace +
1297
            "(" +
1298
            "(\\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 + "$)" +
1299
            ")" +
1300
            "|(^" + 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
            HemisphereIndicator + WhiteSpace + "$" +
1305
            "))"
1306
            ;
1307
        customPatterns.add(ptrn);
1308

    
1309
        //Custom variation of DD:MM:SS.SSS
1310
        ptrn = new CoordinatePattern();
1311
        ptrn.description = "Custom variation of DD:MM:SS.SSS";
1312
        ptrn.pattern =
1313
            CaseInsensitive + "(^" +
1314
            WhiteSpace + HemisphereIndicator + WhiteSpace +
1315
            "(" +
1316
            "(\\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 + "$)" +
1317
            ")" +
1318
            "|(^" + 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
            HemisphereIndicator + WhiteSpace + "$" +
1323
            "))"
1324
            ;
1325
        customPatterns.add(ptrn);
1326

    
1327
        //check if the default patterns are to be used
1328
        if (patternIn.disableDefaultPatterns) {
1329
            patterns = customPatterns;
1330
        } else { //if all patterns are to be used check which set has the matching priority
1331

    
1332
            //check if the custom patterns are to have priority over the default ones
1333
            if (patternIn.priorityOverDefaultPatterns){
1334

    
1335
                //add default patterns to the custom patterns
1336
                for (int i = 0; i < patterns.size(); i++){
1337
                    customPatterns.add(patterns.get(i));
1338
                }
1339

    
1340
                //swap array lists
1341
                patterns = customPatterns;
1342

    
1343
            }else{
1344
                //add custom patterns to the default patterns
1345
                for (int i = 0; i < customPatterns.size(); i++){
1346
                    patterns.add(customPatterns.get(i));
1347

    
1348
                }
1349
            }
1350
        }
1351
    }
1352

    
1353
}
    (1-1/1)