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 }