Project

General

Profile

Download (15.1 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 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

    
10
package eu.etaxonomy.cdm.model.location;
11

    
12
import java.io.Serializable;
13
import java.math.BigDecimal;
14
import java.math.MathContext;
15
import java.math.RoundingMode;
16
import java.text.ParseException;
17
import java.util.regex.Matcher;
18
import java.util.regex.Pattern;
19

    
20
import javax.persistence.Embeddable;
21
import javax.persistence.FetchType;
22
import javax.persistence.ManyToOne;
23
import javax.persistence.Transient;
24
import javax.xml.bind.annotation.XmlAccessType;
25
import javax.xml.bind.annotation.XmlAccessorType;
26
import javax.xml.bind.annotation.XmlElement;
27
import javax.xml.bind.annotation.XmlIDREF;
28
import javax.xml.bind.annotation.XmlRootElement;
29
import javax.xml.bind.annotation.XmlSchemaType;
30
import javax.xml.bind.annotation.XmlType;
31

    
32
import org.apache.log4j.Logger;
33

    
34
import eu.etaxonomy.cdm.common.CdmUtils;
35
import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
36
import eu.etaxonomy.cdm.strategy.parser.location.CoordinateConverter;
37
import eu.etaxonomy.cdm.strategy.parser.location.CoordinateConverter.ConversionResults;
38

    
39
/**
40
 * @author m.doering
41
 * @version 1.0
42
 * @created 08-Nov-2007 13:06:44
43
 */
44
@XmlAccessorType(XmlAccessType.FIELD)
45
@XmlType(name = "Point", propOrder = {
46
    "longitude",
47
    "latitude",
48
    "errorRadius",
49
    "referenceSystem"
50
})
51
@XmlRootElement(name = "Point")
52
@Embeddable
53
public class Point implements Cloneable, Serializable {
54
	private static final long serialVersionUID = 531030660792800636L;
55
	private static final Logger logger = Logger.getLogger(Point.class);
56
	
57
	//TODO was Float but H2 threw errors
58
	@XmlElement(name = "Longitude")
59
	private Double longitude;
60
	
61
	@XmlElement(name = "Latitude")
62
	private Double latitude;
63
	
64
	//in Meters
65
	@XmlElement(name = "ErrorRadius")
66
	private Integer errorRadius = 0;
67
	
68
	@XmlElement(name = "ReferenceSystem")
69
	@XmlIDREF
70
	@XmlSchemaType(name = "IDREF")
71
	@ManyToOne(fetch = FetchType.LAZY)
72
	private ReferenceSystem referenceSystem;
73
	
74
	
75
//******************** FACTORY METHODS ****************************	
76

    
77
	/**
78
	 * Factory method
79
	 * @return
80
	 */
81
	public static Point NewInstance(){
82
		return new Point();
83
	}
84
	
85
	/**
86
	 * Factory method
87
	 * @return
88
	 */
89
	public static Point NewInstance(Double longitude, Double latitude, ReferenceSystem referenceSystem, Integer errorRadius){
90
		Point result = new Point();
91
		result.setLongitude(longitude);
92
		result.setLatitude(latitude);
93
		result.setReferenceSystem(referenceSystem);
94
		result.setErrorRadius(errorRadius);
95
		return result;
96
	}
97
	
98
// ******************** CONSTRUCTOR ***************************
99
	
100
	/**
101
	 * Constructor
102
	 */
103
	public Point() {
104
	}
105
	
106
//************** Sexagesimal /decimal METHODS *******************	
107
	
108
	public enum Direction {
109
	    WEST {
110
	 
111
	        @Override
112
	        public String toString() {
113
	            return "W";
114
	        }
115
	    },
116
	    EAST {
117
	 
118
	        @Override
119
	        public String toString() {
120
	            return "E";
121
	        }
122
	    },
123
	    NORTH {
124
	 
125
	        @Override
126
	        public String toString() {
127
	            return "N";
128
	        }
129
	    },
130
	    SOUTH {
131
	 
132
	        @Override
133
	        public String toString() {
134
	            return "S";
135
	        }
136
	    };
137
	}
138
	
139
	public static final class CoordinateParser {
140
		 
141
	    /**
142
	     * Pattern zum parsen von Sexagesimalen Grad: 145°
143
	     */
144
	    private static final String DEGREE_REGEX = "([0-9]*)\u00B0";
145
	    /**
146
	     * Pattern zum parsen von Sexagesimalen Minuten: 65'
147
	     */
148
	    private static final String MINUTES_REGEX = "(?:([0-9]*)')?";
149
	    /**
150
	     * Pattern zum parsen von Sexagesimalen Sekunden: 17"
151
	     */
152
	    private static final String SECONDS_REGEX = "(?:([0-9]*)(?:''|\"))?";
153
	    /**
154
	     * Himmelsrichtung Längengrad
155
	     */
156
	    private static final String LONGITUDE_DIRECTION_REGEX = "([OEW])";
157
	    /**
158
	     * Himmelsrichtung Breitengrad
159
	     */
160
	    private static final String LATITUDE_DIRECTION_REGEX = "([NS])";
161
	 
162
	    /**
163
	     * Pattern zum Parsen von Breitengraden.
164
	     */
165
	    private static final Pattern LATITUDE_PATTERN = Pattern
166
	            .compile(DEGREE_REGEX + MINUTES_REGEX + SECONDS_REGEX
167
	                    + LATITUDE_DIRECTION_REGEX);
168
	 
169
	    /**
170
	     * Pattern zum Parsen von Längengraden.
171
	     */
172
	    private static final Pattern LONGITUDE_PATTERN = Pattern
173
	            .compile(DEGREE_REGEX + MINUTES_REGEX + SECONDS_REGEX
174
	                    + LONGITUDE_DIRECTION_REGEX);
175
	 
176
	    private CoordinateParser() {
177
	        throw new AssertionError( );
178
	    }
179
	 
180
	    /**
181
	     * Parst einen Breitengrad der Form<br>
182
	     * G°M'S""(OEW)<br>
183
	     * Die Formen<br>
184
	     * G°(OEW)<br>
185
	     * G°M'(OEW)<br>
186
	     * sind ebenfalls erlaubt.
187
	     *
188
	     * @param strg
189
	     * @return Die geparsten Koordinaten
190
	     * @throws ParseException
191
	     *             Wenn eine Fehler beim Parsen aufgetreten ist.
192
	     */
193
	    public static Sexagesimal parseLatitude(final String strg)
194
	            throws ParseException {
195
	        return parseCoordinates(strg, LATITUDE_PATTERN);
196
	    }
197
	 
198
	    /**
199
	     * Parst einen Längengrad der Form<br>
200
	     * G°M'S"(NS)<br>
201
	     * Die Formen<br>
202
	     * G°(NS)<br>
203
	     * G°M'(NS)<br>
204
	     * sind ebenfalls erlaubt.
205
	     *
206
	     * @param strg
207
	     * @return Die geparsten Koordinaten
208
	     * @throws ParseException
209
	     *             Wenn eine Fehler beim Parsen aufgetreten ist.
210
	     */
211
	    public static Sexagesimal parseLongitude(final String strg)
212
	            throws ParseException {
213
	        return parseCoordinates(strg, LONGITUDE_PATTERN);
214
	    }
215
	 
216
	   
217
	    /**
218
	     * Not used at the moment. Use CoordinateConverter instead.
219
	     * @param strg
220
	     * @param pattern
221
	     * @return
222
	     * @throws ParseException
223
	     */
224
	    private static Sexagesimal parseCoordinates(final String strg, final Pattern pattern) throws ParseException {
225
	        if (strg == null) {
226
	            throw new java.text.ParseException("Keine Koordinaten gegeben.", -1);
227
	        }
228
	        final Matcher matcher = pattern.matcher(strg);
229
	        if (matcher.matches( )) {
230
	            if (matcher.groupCount( ) == 4) {
231
	                // Grad
232
	                String tmp = matcher.group(1);
233
	                int degree = Integer.parseInt(tmp);
234
	 
235
	                // Optional minutes
236
	                tmp = matcher.group(2);
237
	                int minutes = Sexagesimal.NONE;
238
	                if (tmp != null) {
239
	                    minutes = Integer.parseInt(tmp);
240
	                }
241
	 
242
	                // Optional seconds
243
	                tmp = matcher.group(3);
244
	                int seconds = Sexagesimal.NONE;
245
	                if (tmp != null) {
246
	                    seconds = Integer.parseInt(tmp);
247
	                }
248
	 
249
	                // directions
250
	                tmp = matcher.group(4);
251
	                final Direction direction;
252
	                if (tmp.equals("N")) {
253
	                    direction = Direction.NORTH;
254
	                }
255
	                else if (tmp.equals("S")) {
256
	                    direction = Direction.SOUTH;
257
	                }
258
	                else if (tmp.equals("E") || tmp.equals("O")) {
259
	                    direction = Direction.EAST;
260
	                }
261
	                else if (tmp.equals("W")) {
262
	                    direction = Direction.WEST;
263
	                }
264
	                else {
265
	                    direction = null;
266
	                }
267
	                return Sexagesimal.NewInstance(degree, minutes, seconds, direction);
268
	            }
269
	            else {
270
	                throw new java.text.ParseException(
271
	                        "Die Koordinaten-Darstellung ist fehlerhaft: " + strg,
272
	                        -1);
273
	            }
274
	        }
275
	        else {
276
	            throw new java.text.ParseException(
277
	                    "Die Koordinaten-Darstellung ist fehlerhaft: " + strg, -1);
278
	        }
279
	    }
280
	 
281
	}
282
	
283
	
284
	private static final BigDecimal SIXTY = BigDecimal.valueOf(60.0);
285
	private static final MathContext MC = new MathContext(34, RoundingMode.HALF_UP);
286
	private static final double HALF_SECOND = 1. / 7200.;
287
	
288
	//see http://www.tutorials.de/forum/archiv/348596-quiz-10-zeja-java.html
289
	public static class Sexagesimal{
290
		public static Sexagesimal NewInstance(Integer degree, Integer minutes, Integer seconds, Direction direction){
291
			Sexagesimal result = new Sexagesimal();
292
			result.degree = degree; result.minutes = minutes; result.seconds = seconds;
293
			return result;
294
		}
295
		
296
		public static final int NONE = 0;
297
		public Integer degree;
298
		public Integer minutes;
299
		public Integer seconds;
300
//		public Double tertiers;
301
		
302
		public Direction direction;
303

    
304

    
305
		public boolean isLatitude(){
306
			return (direction == Direction.WEST) || (direction == Direction.EAST) ;
307
		}
308
		public boolean isLongitude(){
309
			return ! isLatitude();
310
		}
311
		
312
		
313
		public static Sexagesimal valueOf(Double decimal, boolean isLatitude){
314
			return valueOf(decimal, isLatitude, false, false);
315
		}
316
		
317
		public static Sexagesimal valueOf(Double decimal, boolean isLatitude, boolean nullSecondsToNull, boolean nullMinutesToNull){
318
			Sexagesimal sexagesimal = new Sexagesimal(); 
319
			Double decimalDegree = decimal;
320
		        if (isLatitude) {
321
		        	if (decimalDegree < 0) {
322
		            	sexagesimal.direction = Direction.SOUTH;
323
		            }
324
		            else {
325
		            	sexagesimal.direction = Direction.NORTH;
326
		            }
327
		        }
328
		        else {
329
		        	if (decimalDegree < 0) {
330
			               sexagesimal.direction = Direction.WEST;
331
			            }
332
			            else {
333
			            	sexagesimal.direction = Direction.EAST;
334
			            }
335
		        }
336
		 
337
		        // Decimal in \u00B0'" umrechnen
338
		        double d = Math.abs(decimalDegree);
339
		        d += HALF_SECOND; // add a second for rounding
340
		        sexagesimal.degree = (int) Math.floor(d);
341
		        sexagesimal.minutes = (int) Math.floor((d - sexagesimal.degree) * 60.0);
342
		        sexagesimal.seconds = (int) Math.floor((d - sexagesimal.degree - sexagesimal.minutes / 60.0) * 3600.0);
343
		 
344
		        if (sexagesimal.seconds == 0 && nullSecondsToNull){
345
		        	sexagesimal.seconds = null;
346
		        }
347
		        if (sexagesimal.seconds == null && nullMinutesToNull){
348
		        	sexagesimal.minutes = null;
349
		        }
350
		        
351
		       // sexagesimal.decimalRadian = Math.toRadians(this.decimalDegree);
352
		        return sexagesimal;
353
		}
354

    
355
		
356
		
357
		private Double toDecimal(){
358
			BigDecimal value = BigDecimal.valueOf(CdmUtils.Nz(this.seconds)).divide(SIXTY, MC).add
359
				(BigDecimal.valueOf(CdmUtils.Nz(this.minutes))).divide(SIXTY, MC).add
360
				(BigDecimal.valueOf(CdmUtils.Nz(this.degree)));
361

    
362
	        if (this.direction == Direction.WEST || this.direction == Direction.SOUTH) {
363
	            value = value.negate( );
364
	        }
365
	        return value.doubleValue( );
366
		}
367

    
368
		@Override
369
		public String toString(){
370
			return toString(false);
371
		}
372
		public String toString(boolean includeEmptySeconds){
373
			String result;
374
			result = String.valueOf(CdmUtils.Nz(degree)) + "\u00B0";
375
			if (seconds != null || minutes != null){
376
				result += String.valueOf(CdmUtils.Nz(minutes)) + "'";
377
			}
378
			if (seconds != null ){
379
				if (seconds != 0 || includeEmptySeconds){
380
					result += String.valueOf(CdmUtils.Nz(seconds)) + "\"";
381
				}
382
			}
383
			result += direction; 
384
			return result;
385
		}
386
		
387
	}
388
	
389
	
390
	@Transient
391
	public Sexagesimal getLongitudeSexagesimal (){
392
		boolean isLatitude = false;
393
		return Sexagesimal.valueOf(longitude, isLatitude);
394
	}
395

    
396
	@Transient
397
	public Sexagesimal getLatitudeSexagesimal (){
398
		boolean isLatitude = true;
399
		return Sexagesimal.valueOf(latitude, isLatitude);
400
	}
401
	
402
	@Transient
403
	public void setLatitudeSexagesimal(Sexagesimal sexagesimalLatitude){
404
		this.latitude = sexagesimalLatitude.toDecimal();
405
	}
406
	@Transient
407
	public void setLongitudeSexagesimal(Sexagesimal sexagesimalLongitude){
408
		this.longitude = sexagesimalLongitude.toDecimal();
409
	}
410
	
411
	@Transient
412
	public void setLatitudeByParsing(String string) throws ParseException{
413
		this.setLatitude(parseLatitude(string));
414
	}
415
	
416
	@Transient
417
	public void setLongitudeByParsing(String string) throws ParseException{
418
		this.setLongitude(parseLongitude(string));
419
	}
420
	
421
	
422
	public static Double parseLatitude(String string) throws ParseException{
423
		CoordinateConverter converter = new CoordinateConverter();
424
		ConversionResults result = converter.tryConvert(string);
425
		if (! result.conversionSuccessful || result.isLongitude  ){
426
			throw new ParseException("Latitude could not be parsed", 0);
427
		}else{
428
			return result.convertedCoord;
429
		}
430
	}
431
	
432
	public static Double parseLongitude(String string) throws ParseException{
433
		CoordinateConverter converter = new CoordinateConverter();
434
		ConversionResults result = converter.tryConvert(string);
435
		if (! result.conversionSuccessful || ! result.isLongitude){
436
			throw new ParseException("Longitude could not be parsed", 0);
437
		}else{
438
			return result.convertedCoord;
439
		}
440
	}
441

    
442
// ******************** GETTER / SETTER ********************************	
443
	
444
	public ReferenceSystem getReferenceSystem(){
445
		return this.referenceSystem;
446
	}
447

    
448
	/**
449
	 * 
450
	 * @param referenceSystem    referenceSystem
451
	 */
452
	public void setReferenceSystem(ReferenceSystem referenceSystem){
453
		this.referenceSystem = referenceSystem;
454
	}
455

    
456
	public Double getLongitude(){
457
		return this.longitude;
458
	}
459

    
460
	/**
461
	 * 
462
	 * @param longitude    longitude
463
	 */
464
	public void setLongitude(Double longitude){
465
		this.longitude = longitude;
466
	}
467

    
468
	public Double getLatitude(){
469
		return this.latitude;
470
	}
471

    
472
	/**
473
	 * 
474
	 * @param latitude    latitude
475
	 */
476
	public void setLatitude(Double latitude){
477
		this.latitude = latitude;
478
	}
479

    
480
	public Integer getErrorRadius(){
481
		return this.errorRadius;
482
	}
483

    
484
	/**
485
	 * 
486
	 * @param errorRadius    errorRadius
487
	 */
488
	public void setErrorRadius(Integer errorRadius){
489
		this.errorRadius = errorRadius;
490
	}
491
	
492
// **************** toString *************************/
493
	
494
	
495
	/**
496
	 * Returns a string representation in sexagesimal coordinates.
497
	 * @return
498
	 */
499
	public String toSexagesimalString(boolean includeEmptySeconds, boolean includeReferenceSystem){
500
		String result = "";
501
		result += getLatitudeSexagesimal() == null ? "" : getLatitudeSexagesimal().toString(includeEmptySeconds);
502
		result = CdmUtils.concat(", ", result, getLongitudeSexagesimal() == null ? "" : getLongitudeSexagesimal().toString(includeEmptySeconds));
503
		if (includeReferenceSystem && getReferenceSystem() != null){
504
			String refSys = CdmUtils.isEmpty(getReferenceSystem().getLabel()) ? "" : "(" + getReferenceSystem().getLabel() + ")";
505
			result = CdmUtils.concat(" ", result, refSys);
506
		}
507
		return result;
508
	}
509
	
510
	
511
//*********** CLONE **********************************/	
512
	
513
	/** 
514
	 * Clones <i>this</i> point. This is a shortcut that enables to
515
	 * create a new instance that differs only slightly from <i>this</i> point
516
	 * by modifying only some of the attributes.<BR>
517
	 * This method overrides the clone method from {@link DerivedUnitBase DerivedUnitBase}.
518
	 * 
519
	 * @see java.lang.Object#clone()
520
	 */
521
	@Override
522
	public Point clone(){
523
		try{
524
			Point result = (Point)super.clone();
525
			result.setReferenceSystem(this.referenceSystem);
526
			//no changes to: errorRadius, latitude, longitude
527
			return result;
528
		} catch (CloneNotSupportedException e) {
529
			logger.warn("Object does not implement cloneable");
530
			e.printStackTrace();
531
			return null;
532
		}
533
	}
534

    
535

    
536
}
(5-5/10)