001 /* 002 * Zmanim Java API 003 * Copyright (C) 2004-2007 Eliyahu Hershfeld 004 * 005 * This program is free software; you can redistribute it and/or modify it under the terms of the 006 * GNU General Public License as published by the Free Software Foundation; either version 2 of the 007 * License, or (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 010 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 011 * General Public License for more details. 012 * 013 * You should have received a copy of the GNU General Public License along with this program; if 014 * not, write to the Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 015 * 02111-1307, USA or connect to: http://www.fsf.org/copyleft/gpl.html 016 */ 017 package net.sourceforge.zmanim.util; 018 019 import java.util.TimeZone; 020 021 /** 022 * A class that contains location information such as latitude and longitude 023 * required for astronomical calculations. The elevation field is not used by 024 * most calculation engines and would be ignored if set. Check the documentation 025 * for specific implementations of the {@link AstronomicalCalculator} to see if 026 * elevation is calculated as part o the algorithm. 027 * 028 * @author © Eliyahu Hershfeld 2004 - 2007 029 * @version 1.1 030 */ 031 public class GeoLocation { 032 private double latitude; 033 034 private double longitude; 035 036 private String locationName; 037 038 private TimeZone timeZone; 039 040 private double elevation; 041 042 /** 043 * Method to get the elevation in Meters. 044 * 045 * @return Returns the elevation in Meters. 046 */ 047 public double getElevation() { 048 return elevation; 049 } 050 051 /** 052 * Method to set the elevation in Meters <b>above </b> sea level. 053 * 054 * @param elevation 055 * The elevation to set in Meters. An IllegalArgumentException 056 * will be thrown if the value is a negative. 057 */ 058 public void setElevation(double elevation) { 059 if (elevation < 0) { 060 throw new IllegalArgumentException("Elevation can not be negative"); 061 062 } 063 this.elevation = elevation; 064 } 065 066 /** 067 * GeoLocation constructor with parameters for all required fields. 068 * 069 * @param name 070 * The location name for display use such as "Lakewood, 071 * NJ" 072 * @param latitude 073 * the latitude in a double format such as 40.095965 for Lakewood, 074 * NJ 075 * @param longitude 076 * double the longitude in a double format such as -74.222130 for 077 * Lakewood, NJ. <br/> <b>Note: </b> For longitudes east of the 078 * <a href="http://en.wikipedia.org/wiki/Prime_Meridian">Prime 079 * Meridian </a> (Greenwich), a negative value should be used. 080 * @param timeZone 081 * the <code>TimeZone</code> for the location. 082 */ 083 public GeoLocation(String name, double latitude, double longitude, 084 TimeZone timeZone) { 085 this(name, latitude, longitude, 0, timeZone); 086 this.setLocationName(name); 087 this.setLatitude(latitude); 088 this.setLongitude(longitude); 089 this.setTimeZone(timeZone); 090 } 091 092 /** 093 * GeoLocation constructor with parameters for all required fields. 094 * 095 * @param name 096 * The location name for display use such as "Lakewood, 097 * NJ" 098 * @param latitude 099 * the latitude in a double format such as 40.095965 for Lakewood, 100 * NJ 101 * @param longitude 102 * double the longitude in a double format such as -74.222130 for 103 * Lakewood, NJ. <br/> <b>Note: </b> For longitudes east of the 104 * <a href="http://en.wikipedia.org/wiki/Prime_Meridian">Prime 105 * Meridian </a> (Greenwich), a negative value should be used. 106 * @param elevation 107 * the elevation above sea level in Meters. Elevation is not used 108 * in most algorithms used for calculating sunrise and set. 109 * @param timeZone 110 * the <code>TimeZone</code> for the location. 111 */ 112 public GeoLocation(String name, double latitude, double longitude, 113 double elevation, TimeZone timeZone) { 114 setLocationName(name); 115 setLatitude(latitude); 116 setLongitude(longitude); 117 setElevation(elevation); 118 setTimeZone(timeZone); 119 } 120 121 /** 122 * Default GeoLocation constructor will set location to the Prime Meridian 123 * at Greenwich, England and a TimeZone of GMT. The longitude will be set to 0 and 124 * the latitude will be 51.4772 to match the location of the <a 125 * href="http://www.rog.nmm.ac.uk">Royal Observatory, Greenwich </a>. No 126 * daylight savings time will be used. 127 */ 128 public GeoLocation() { 129 setLocationName("Greenwich, England"); 130 setLongitude(0); // should not be needed 131 setLatitude(51.4772); 132 setTimeZone(TimeZone.getTimeZone("GMT")); 133 } 134 135 /** 136 * Method to set the latitude in a double format. 137 * 138 * @param latitude 139 * The degrees of latitude to set in a double format between -90° 140 * and 90°. An IllegalArgumentException will be thrown if the 141 * value exceeds the limit. For example 40.095965 would be used for 142 * Lakewood, NJ. 143 */ 144 public void setLatitude(double latitude) { 145 if (latitude > 90 || latitude < -90) { 146 throw new IllegalArgumentException( 147 "Latitude must be between -90 and 90"); 148 } 149 this.latitude = latitude; 150 } 151 152 /** 153 * Method to set the latitude in degrees, minutes and seconds. 154 * 155 * @param degrees 156 * The degrees of latitude to set between -90 and 90. An 157 * IllegalArgumentException will be thrown if the value exceeds 158 * the limit. For example 40 would be used for Lakewood, NJ. 159 * @param minutes 160 * @param seconds 161 * @param direction 162 * N for north and S for south. An IllegalArgumentException will 163 * be thrown if the value is not S or N. 164 */ 165 public void setLatitude(int degrees, int minutes, double seconds, 166 String direction) { 167 double tempLat = degrees + ((minutes + (seconds / 60.0)) / 60.0); 168 if (tempLat > 90 || tempLat < 0) { 169 throw new IllegalArgumentException( 170 "Latitude must be between 0 and 90. Use direction of S instead of negative."); 171 } 172 if (direction.equals("S")) { 173 tempLat *= -1; 174 } else if (!direction.equals("N")) { 175 throw new IllegalArgumentException( 176 "Latitude direction must be N or S"); 177 } 178 this.latitude = tempLat; 179 } 180 181 /** 182 * @return Returns the latitude. 183 */ 184 public double getLatitude() { 185 return latitude; 186 } 187 188 /** 189 * Method to set the longitude in a double format. 190 * 191 * @param longitude 192 * The degrees of longitude to set in a double format between 193 * -180° and 180°. An IllegalArgumentException will be thrown if 194 * the value exceeds the limit. For example -74.222130 would be 195 * used for Lakewood, NJ. Note: for longitudes east of the <a 196 * href="http://en.wikipedia.org/wiki/Prime_Meridian">Prime 197 * Meridian </a> (Greenwich) a negative value should be used. 198 */ 199 public void setLongitude(double longitude) { 200 if (longitude > 180 || longitude < -180) { 201 throw new IllegalArgumentException( 202 "Longitude must be between -180 and 180"); 203 } 204 this.longitude = longitude; 205 } 206 207 /** 208 * Method to set the longitude in degrees, minutes and seconds. 209 * 210 * @param degrees 211 * The degrees of longitude to set between -180 and 180. An 212 * IllegalArgumentException will be thrown if the value exceeds 213 * the limit. For example -74 would be used for Lakewood, NJ. 214 * Note: for longitudes east of the <a 215 * href="http://en.wikipedia.org/wiki/Prime_Meridian">Prime 216 * Meridian </a> (Greenwich) a negative value should be used. 217 * @param minutes 218 * @param seconds 219 * @param direction 220 * E for east of the Prime Meridian or W for west of it. An 221 * IllegalArgumentException will be thrown if the value is not E 222 * or W. 223 */ 224 public void setLongitude(int degrees, int minutes, double seconds, 225 String direction) { 226 double longTemp = degrees + ((minutes + (seconds / 60.0)) / 60.0); 227 if (longTemp > 180 || longitude < 0) { 228 throw new IllegalArgumentException( 229 "Longitude must be between 0 and 180. Use the "); 230 } 231 if (direction.equals("W")) { 232 longTemp *= -1; 233 } else if (!direction.equals("E")) { 234 throw new IllegalArgumentException( 235 "Longitude direction must be E or W"); 236 } 237 this.longitude = longTemp; 238 } 239 240 /** 241 * @return Returns the longitude. 242 */ 243 public double getLongitude() { 244 return longitude; 245 } 246 247 /** 248 * @return Returns the location name. 249 */ 250 public String getLocationName() { 251 return locationName; 252 } 253 254 /** 255 * @param name 256 * The setter method for the display name. 257 */ 258 public void setLocationName(String name) { 259 this.locationName = name; 260 } 261 262 /** 263 * @return Returns the timeZone. 264 */ 265 public TimeZone getTimeZone() { 266 return timeZone; 267 } 268 269 /** 270 * @param timeZone 271 * The timeZone to set. 272 */ 273 public void setTimeZone(TimeZone timeZone) { 274 this.timeZone = timeZone; 275 } 276 277 /** 278 * A method that returns an XML formatted <code>String</code> representing 279 * the serialized <code>Object</code>. Very similar to the toString 280 * method but the return value is in an xml format. The format currently 281 * used (subject to change) is: 282 * 283 * <pre> 284 * <GeoLocation> 285 * <LocationName>Lakewood, NJ</LocationName> 286 * <Latitude>-74.222130°</Latitude> 287 * <Longitude>-74.222130°</Longitude> 288 * <Elevation>0 Meters</Elevation> 289 * <TimezoneName>America/New_York</TimezoneName> 290 * <TimeZoneDisplayName>Eastern Standard Time</TimeZoneDisplayName> 291 * <TimezoneGMTOffset>-5</TimezoneGMTOffset> 292 * <TimezoneDSTOffset>1</TimezoneDSTOffset> 293 * </GeoLocation> 294 * </pre> 295 * 296 * @return The XML formatted <code>String</code>. 297 */ 298 public String toXML() { 299 StringBuffer sb = new StringBuffer(); 300 sb.append("<GeoLocation>\n"); 301 sb.append("\t<LocationName>").append(getLocationName()).append( 302 "</LocationName>\n"); 303 sb.append("\t<Latitude>").append(getLatitude()).append("°").append( 304 "</Latitude>\n"); 305 sb.append("\t<Longitude>").append(getLongitude()).append("°").append( 306 "</Longitude>\n"); 307 sb.append("\t<Elevation>").append(getElevation()).append(" Meters") 308 .append("</Elevation>\n"); 309 sb.append("\t<TimezoneName>").append(getTimeZone().getID()).append( 310 "</TimezoneName>\n"); 311 /* 312 * sb.append("\t<TimeZoneDisplayName>").append( 313 * getTimeZone().getDisplayName()).append( "</TimeZoneDisplayName>\n"); 314 */ 315 sb.append("\t<TimezoneGMTOffset>").append( 316 getTimeZone().getRawOffset() / 3600000).append( 317 "</TimezoneGMTOffset>\n"); 318 sb.append("\t<TimezoneDSTOffset>").append( 319 getTimeZone().getDSTSavings() / 3600000).append( 320 "</TimezoneDSTOffset>\n"); 321 sb.append("</GeoLocation>"); 322 return sb.toString(); 323 } 324 325 /** 326 * @see java.lang.Object#equals(Object) 327 */ 328 public boolean equals(Object object) { 329 if (this == object) 330 return true; 331 if (!(object instanceof GeoLocation)) 332 return false; 333 GeoLocation geo = (GeoLocation) object; 334 return Double.doubleToLongBits(latitude) == Double 335 .doubleToLongBits(geo.latitude) 336 && Double.doubleToLongBits(longitude) == Double 337 .doubleToLongBits(geo.longitude) 338 && elevation == geo.elevation 339 && (locationName == null ? geo.locationName == null 340 : locationName.equals(geo.locationName)) 341 && (timeZone == null ? geo.timeZone == null : timeZone 342 .equals(geo.timeZone)); 343 } 344 345 /** 346 * @see java.lang.Object#hashCode() 347 */ 348 public int hashCode() { 349 350 int result = 17; 351 long latLong = Double.doubleToLongBits(latitude); 352 long lonLong = Double.doubleToLongBits(longitude); 353 long elevLong = Double.doubleToLongBits(elevation); 354 int latInt = (int) (latLong ^ (latLong >>> 32)); 355 int lonInt = (int) (lonLong ^ (lonLong >>> 32)); 356 int elevInt = (int) (elevLong ^ (elevLong >>> 32)); 357 result = 37 * result + getClass().hashCode(); 358 result += 37 * result + latInt; 359 result += 37 * result + lonInt; 360 result += 37 * result + elevInt; 361 result += 37 * result 362 + (locationName == null ? 0 : locationName.hashCode()); 363 result += 37 * result + (timeZone == null ? 0 : timeZone.hashCode()); 364 return result; 365 } 366 367 /** 368 * @see java.lang.Object#toString() 369 */ 370 public String toString() { 371 StringBuffer sb = new StringBuffer(); 372 sb.append("\nLocation Name:\t\t\t").append(getLocationName()); 373 sb.append("\nLatitude:\t\t\t").append(getLatitude()).append("°"); 374 sb.append("\nLongitude:\t\t\t").append(getLongitude()).append("°"); 375 sb.append("\nElevation:\t\t\t").append(getElevation()) 376 .append(" Meters"); 377 sb.append("\nTimezone Name:\t\t\t").append(getTimeZone().getID()); 378 /* 379 * sb.append("\nTimezone Display Name:\t\t").append( 380 * getTimeZone().getDisplayName()); 381 */ 382 sb.append("\nTimezone GMT Offset:\t\t").append( 383 getTimeZone().getRawOffset() / 3600000); 384 sb.append("\nTimezone DST Offset:\t\t").append( 385 getTimeZone().getDSTSavings() / 3600000); 386 return sb.toString(); 387 } 388 }