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]*)\u00B0";
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
){
321 Sexagesimal sexagesimal
= new Sexagesimal();
322 Double decimalDegree
= decimal
;
324 if (decimalDegree
< 0) {
325 sexagesimal
.direction
= Direction
.SOUTH
;
328 sexagesimal
.direction
= Direction
.NORTH
;
332 if (decimalDegree
< 0) {
333 sexagesimal
.direction
= Direction
.WEST
;
336 sexagesimal
.direction
= Direction
.EAST
;
340 // Decimal in \u00B0'" umrechnen
341 double d
= Math
.abs(decimalDegree
);
342 d
+= HALF_SECOND
; // add a second for rounding
343 sexagesimal
.degree
= (int) Math
.floor(d
);
344 sexagesimal
.minutes
= (int) Math
.floor((d
- sexagesimal
.degree
) * 60.0);
345 sexagesimal
.seconds
= (int) Math
.floor((d
- sexagesimal
.degree
- sexagesimal
.minutes
/ 60.0) * 3600.0);
347 if (sexagesimal
.seconds
== 0 && nullSecondsToNull
){
348 sexagesimal
.seconds
= null;
350 if (sexagesimal
.seconds
== null && nullMinutesToNull
){
351 sexagesimal
.minutes
= null;
354 // sexagesimal.decimalRadian = Math.toRadians(this.decimalDegree);
360 private Double
toDecimal(){
361 BigDecimal value
= BigDecimal
.valueOf(CdmUtils
.Nz(this.seconds
)).divide(SIXTY
, MC
).add
362 (BigDecimal
.valueOf(CdmUtils
.Nz(this.minutes
))).divide(SIXTY
, MC
).add
363 (BigDecimal
.valueOf(CdmUtils
.Nz(this.degree
)));
365 if (this.direction
== Direction
.WEST
|| this.direction
== Direction
.SOUTH
) {
366 value
= value
.negate( );
368 return value
.doubleValue( );
372 public String
toString(){
373 return toString(false);
375 public String
toString(boolean includeEmptySeconds
){
377 result
= String
.valueOf(CdmUtils
.Nz(degree
)) + "\u00B0";
378 if (seconds
!= null || minutes
!= null){
379 result
+= String
.valueOf(CdmUtils
.Nz(minutes
)) + "'";
381 if (seconds
!= null ){
382 if (seconds
!= 0 || includeEmptySeconds
){
383 result
+= String
.valueOf(CdmUtils
.Nz(seconds
)) + "\"";
394 public Sexagesimal
getLongitudeSexagesimal (){
395 boolean isLatitude
= false;
396 return Sexagesimal
.valueOf(longitude
, isLatitude
);
400 public Sexagesimal
getLatitudeSexagesimal (){
401 boolean isLatitude
= true;
402 return Sexagesimal
.valueOf(latitude
, isLatitude
);
406 public void setLatitudeSexagesimal(Sexagesimal sexagesimalLatitude
){
407 this.latitude
= sexagesimalLatitude
.toDecimal();
410 public void setLongitudeSexagesimal(Sexagesimal sexagesimalLongitude
){
411 this.longitude
= sexagesimalLongitude
.toDecimal();
415 public void setLatitudeByParsing(String string
) throws ParseException
{
416 this.setLatitude(parseLatitude(string
));
420 public void setLongitudeByParsing(String string
) throws ParseException
{
421 this.setLongitude(parseLongitude(string
));
425 public static Double
parseLatitude(String string
) throws ParseException
{
426 CoordinateConverter converter
= new CoordinateConverter();
427 ConversionResults result
= converter
.tryConvert(string
);
428 if (! result
.conversionSuccessful
|| (result
.isLongitude
!= null && result
.isLongitude
) ){
429 throw new ParseException("Latitude could not be parsed", 0);
431 return result
.convertedCoord
;
435 public static Double
parseLongitude(String string
) throws ParseException
{
436 CoordinateConverter converter
= new CoordinateConverter();
437 ConversionResults result
= converter
.tryConvert(string
);
438 if (! result
.conversionSuccessful
|| (result
.isLongitude
!= null && ! result
.isLongitude
)){
439 throw new ParseException("Longitude could not be parsed", 0);
441 return result
.convertedCoord
;
445 // ******************** GETTER / SETTER ********************************
447 public ReferenceSystem
getReferenceSystem(){
448 return this.referenceSystem
;
453 * @param referenceSystem referenceSystem
455 public void setReferenceSystem(ReferenceSystem referenceSystem
){
456 this.referenceSystem
= referenceSystem
;
459 public Double
getLongitude(){
460 return this.longitude
;
465 * @param longitude longitude
467 public void setLongitude(Double longitude
){
468 this.longitude
= longitude
;
471 public Double
getLatitude(){
472 return this.latitude
;
477 * @param latitude latitude
479 public void setLatitude(Double latitude
){
480 this.latitude
= latitude
;
483 public Integer
getErrorRadius(){
484 return this.errorRadius
;
489 * @param errorRadius errorRadius
491 public void setErrorRadius(Integer errorRadius
){
492 this.errorRadius
= errorRadius
;
495 // **************** toString *************************/
499 * Returns a string representation in sexagesimal coordinates.
502 public String
toSexagesimalString(boolean includeEmptySeconds
, boolean includeReferenceSystem
){
504 result
+= getLatitudeSexagesimal() == null ?
"" : getLatitudeSexagesimal().toString(includeEmptySeconds
);
505 result
= CdmUtils
.concat(", ", result
, getLongitudeSexagesimal() == null ?
"" : getLongitudeSexagesimal().toString(includeEmptySeconds
));
506 if (includeReferenceSystem
&& getReferenceSystem() != null){
507 String refSys
= CdmUtils
.isEmpty(getReferenceSystem().getLabel()) ?
"" : "(" + getReferenceSystem().getLabel() + ")";
508 result
= CdmUtils
.concat(" ", result
, refSys
);
514 //*********** CLONE **********************************/
517 * Clones <i>this</i> point. This is a shortcut that enables to
518 * create a new instance that differs only slightly from <i>this</i> point
519 * by modifying only some of the attributes.<BR>
520 * This method overrides the clone method from {@link DerivedUnitBase DerivedUnitBase}.
522 * @see java.lang.Object#clone()
525 public Point
clone(){
527 Point result
= (Point
)super.clone();
528 result
.setReferenceSystem(this.referenceSystem
);
529 //no changes to: errorRadius, latitude, longitude
531 } catch (CloneNotSupportedException e
) {
532 logger
.warn("Object does not implement cloneable");