2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.model
.location
;
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
;
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
;
32 import org
.apache
.log4j
.Logger
;
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
;
42 * @created 08-Nov-2007 13:06:44
44 @XmlAccessorType(XmlAccessType
.FIELD
)
45 @XmlType(name
= "Point", propOrder
= {
51 @XmlRootElement(name
= "Point")
53 public class Point
implements Cloneable
, Serializable
{
54 private static final long serialVersionUID
= 531030660792800636L;
55 private static final Logger logger
= Logger
.getLogger(Point
.class);
57 //TODO was Float but H2 threw errors
58 @XmlElement(name
= "Longitude")
59 private Double longitude
;
61 @XmlElement(name
= "Latitude")
62 private Double latitude
;
65 @XmlElement(name
= "ErrorRadius")
66 private Integer errorRadius
= 0;
68 @XmlElement(name
= "ReferenceSystem")
70 @XmlSchemaType(name
= "IDREF")
71 @ManyToOne(fetch
= FetchType
.LAZY
)
72 private ReferenceSystem referenceSystem
;
75 //******************** FACTORY METHODS ****************************
81 public static Point
NewInstance(){
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
);
98 // ******************** CONSTRUCTOR ***************************
106 //************** Sexagesimal /decimal METHODS *******************
108 public enum Direction
{
112 public String
toString() {
119 public String
toString() {
126 public String
toString() {
133 public String
toString() {
139 public static final class CoordinateParser
{
142 * Pattern zum parsen von Sexagesimalen Grad: 145°
144 private static final String DEGREE_REGEX
= "([0-9]*)°";
146 * Pattern zum parsen von Sexagesimalen Minuten: 65'
148 private static final String MINUTES_REGEX
= "(?:([0-9]*)')?";
150 * Pattern zum parsen von Sexagesimalen Sekunden: 17"
152 private static final String SECONDS_REGEX
= "(?:([0-9]*)(?:''|\"))?";
154 * Himmelsrichtung Längengrad
156 private static final String LONGITUDE_DIRECTION_REGEX
= "([OEW])";
158 * Himmelsrichtung Breitengrad
160 private static final String LATITUDE_DIRECTION_REGEX
= "([NS])";
163 * Pattern zum Parsen von Breitengraden.
165 private static final Pattern LATITUDE_PATTERN
= Pattern
166 .compile(DEGREE_REGEX
+ MINUTES_REGEX
+ SECONDS_REGEX
167 + LATITUDE_DIRECTION_REGEX
);
170 * Pattern zum Parsen von Längengraden.
172 private static final Pattern LONGITUDE_PATTERN
= Pattern
173 .compile(DEGREE_REGEX
+ MINUTES_REGEX
+ SECONDS_REGEX
174 + LONGITUDE_DIRECTION_REGEX
);
176 private CoordinateParser() {
177 throw new AssertionError( );
181 * Parst einen Breitengrad der Form<br>
186 * sind ebenfalls erlaubt.
189 * @return Die geparsten Koordinaten
190 * @throws ParseException
191 * Wenn eine Fehler beim Parsen aufgetreten ist.
193 public static Sexagesimal
parseLatitude(final String strg
)
194 throws ParseException
{
195 return parseCoordinates(strg
, LATITUDE_PATTERN
);
199 * Parst einen Längengrad der Form<br>
204 * sind ebenfalls erlaubt.
207 * @return Die geparsten Koordinaten
208 * @throws ParseException
209 * Wenn eine Fehler beim Parsen aufgetreten ist.
211 public static Sexagesimal
parseLongitude(final String strg
)
212 throws ParseException
{
213 return parseCoordinates(strg
, LONGITUDE_PATTERN
);
218 * Not used at the moment. Use CoordinateConverter instead.
222 * @throws ParseException
224 private static Sexagesimal
parseCoordinates(final String strg
, final Pattern pattern
) throws ParseException
{
226 throw new java
.text
.ParseException("Keine Koordinaten gegeben.", -1);
228 final Matcher matcher
= pattern
.matcher(strg
);
229 if (matcher
.matches( )) {
230 if (matcher
.groupCount( ) == 4) {
232 String tmp
= matcher
.group(1);
233 int degree
= Integer
.parseInt(tmp
);
236 tmp
= matcher
.group(2);
237 int minutes
= Sexagesimal
.NONE
;
239 minutes
= Integer
.parseInt(tmp
);
243 tmp
= matcher
.group(3);
244 int seconds
= Sexagesimal
.NONE
;
246 seconds
= Integer
.parseInt(tmp
);
250 tmp
= matcher
.group(4);
251 final Direction direction
;
252 if (tmp
.equals("N")) {
253 direction
= Direction
.NORTH
;
255 else if (tmp
.equals("S")) {
256 direction
= Direction
.SOUTH
;
258 else if (tmp
.equals("E") || tmp
.equals("O")) {
259 direction
= Direction
.EAST
;
261 else if (tmp
.equals("W")) {
262 direction
= Direction
.WEST
;
267 return Sexagesimal
.NewInstance(degree
, minutes
, seconds
, direction
);
270 throw new java
.text
.ParseException(
271 "Die Koordinaten-Darstellung ist fehlerhaft: " + strg
,
276 throw new java
.text
.ParseException(
277 "Die Koordinaten-Darstellung ist fehlerhaft: " + strg
, -1);
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.;
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
;
296 public static final int NONE
= 0;
297 public Integer degree
;
298 public Integer minutes
;
299 public Integer seconds
;
300 // public Double tertiers;
302 public Direction direction
;
305 public boolean isLatitude(){
306 return (direction
== Direction
.WEST
) || (direction
== Direction
.EAST
) ;
308 public boolean isLongitude(){
309 return ! isLatitude();
313 public static Sexagesimal
valueOf(Double decimal
, boolean isLatitude
){
314 return valueOf(decimal
, isLatitude
, false, false);
317 public static Sexagesimal
valueOf(Double decimal
, boolean isLatitude
, boolean nullSecondsToNull
, boolean nullMinutesToNull
){
318 Sexagesimal sexagesimal
= new Sexagesimal();
319 Double decimalDegree
= decimal
;
321 if (decimalDegree
< 0) {
322 sexagesimal
.direction
= Direction
.SOUTH
;
325 sexagesimal
.direction
= Direction
.NORTH
;
329 if (decimalDegree
< 0) {
330 sexagesimal
.direction
= Direction
.WEST
;
333 sexagesimal
.direction
= Direction
.EAST
;
337 // Decimal in °'" 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);
344 if (sexagesimal
.seconds
== 0 && nullSecondsToNull
){
345 sexagesimal
.seconds
= null;
347 if (sexagesimal
.seconds
== null && nullMinutesToNull
){
348 sexagesimal
.minutes
= null;
351 // sexagesimal.decimalRadian = Math.toRadians(this.decimalDegree);
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
)));
362 if (this.direction
== Direction
.WEST
|| this.direction
== Direction
.SOUTH
) {
363 value
= value
.negate( );
365 return value
.doubleValue( );
369 public String
toString(){
371 result
= String
.valueOf(CdmUtils
.Nz(degree
)) + "°";
372 if (seconds
!= null || minutes
!= null){
373 result
+= String
.valueOf(CdmUtils
.Nz(minutes
)) + "'";
375 if (seconds
!= null){
376 result
+= String
.valueOf(CdmUtils
.Nz(seconds
)) + "\"";
386 public Sexagesimal
getLongitudeSexagesimal (){
387 boolean isLatitude
= false;
388 return Sexagesimal
.valueOf(longitude
, isLatitude
);
392 public Sexagesimal
getLatitudeSexagesimal (){
393 boolean isLatitude
= true;
394 return Sexagesimal
.valueOf(longitude
, isLatitude
);
398 public void setLatitudeSexagesimal(Sexagesimal sexagesimalLatitude
){
399 this.latitude
= sexagesimalLatitude
.toDecimal();
402 public void setLongitudeSexagesimal(Sexagesimal sexagesimalLongitude
){
403 this.longitude
= sexagesimalLongitude
.toDecimal();
407 public void setLatitudeByParsing(String string
) throws ParseException
{
408 this.setLatitude(parseLatitude(string
));
412 public void setLongitudeByParsing(String string
) throws ParseException
{
413 this.setLongitude(parseLongitude(string
));
417 public static Double
parseLatitude(String string
) throws ParseException
{
418 CoordinateConverter converter
= new CoordinateConverter();
419 ConversionResults result
= converter
.tryConvert(string
);
420 if (! result
.conversionSuccessful
|| result
.isLongitude
){
421 throw new ParseException("Latitude could not be parsed", 0);
423 return result
.convertedCoord
;
427 public static Double
parseLongitude(String string
) throws ParseException
{
428 CoordinateConverter converter
= new CoordinateConverter();
429 ConversionResults result
= converter
.tryConvert(string
);
430 if (! result
.conversionSuccessful
|| ! result
.isLongitude
){
431 throw new ParseException("Longitude could not be parsed", 0);
433 return result
.convertedCoord
;
437 // ******************** GETTER / SETTER ********************************
439 public ReferenceSystem
getReferenceSystem(){
440 return this.referenceSystem
;
445 * @param referenceSystem referenceSystem
447 public void setReferenceSystem(ReferenceSystem referenceSystem
){
448 this.referenceSystem
= referenceSystem
;
451 public Double
getLongitude(){
452 return this.longitude
;
457 * @param longitude longitude
459 public void setLongitude(Double longitude
){
460 this.longitude
= longitude
;
463 public Double
getLatitude(){
464 return this.latitude
;
469 * @param latitude latitude
471 public void setLatitude(Double latitude
){
472 this.latitude
= latitude
;
475 public Integer
getErrorRadius(){
476 return this.errorRadius
;
481 * @param errorRadius errorRadius
483 public void setErrorRadius(Integer errorRadius
){
484 this.errorRadius
= errorRadius
;
487 //*********** CLONE **********************************/
490 * Clones <i>this</i> point. This is a shortcut that enables to
491 * create a new instance that differs only slightly from <i>this</i> point
492 * by modifying only some of the attributes.<BR>
493 * This method overrides the clone method from {@link DerivedUnitBase DerivedUnitBase}.
495 * @see java.lang.Object#clone()
498 public Point
clone(){
500 Point result
= (Point
)super.clone();
501 result
.setReferenceSystem(this.referenceSystem
);
502 //no changes to: errorRadius, latitude, longitude
504 } catch (CloneNotSupportedException e
) {
505 logger
.warn("Object does not implement cloneable");