001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2001, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * NOTE: permission has been given to the JScience project (http://www.jscience.org)
018: * to distribute this file under BSD-like license.
019: */
020: package org.geotools.nature;
021:
022: // J2SE dependencies
023: import java.awt.geom.Point2D;
024: import java.text.DateFormat;
025: import java.text.ParseException;
026: import java.util.Date;
027: import java.util.TimeZone;
028:
029: /**
030: * Calcule la position du soleil relativement à la position de l'observateur.
031: * Cette classe reçoit en entrés les coordonnées spatio-temporelles de
032: * l'observateur, soit:
033: *
034: * <TABLE border='0'><TR><TD valign="top">
035: * <BR>
036: * <UL>
037: * <LI>La longitude (en degrées) de l'observateur;</LI>
038: * <LI>La latitude (en degrées) de l'observateur;</LI>
039: * <LI>La date et heure en heure universelle (GMT).</LI>
040: * </UL>
041: *
042: * La position du soleil calculée en sortie comprend les valeurs suivantes:
043: *
044: * <UL>
045: * <LI>L'azimuth du soleil (en degrés dans le sens des aiguilles d'une montre depuis le nord);</LI>
046: * <LI>L'élévation du soleil (en degrés par rapport a l'horizon).</LI>
047: * </UL>
048: * </TD>
049: *
050: * <TD><img src="doc-files/CelestialSphere.png"></TD>
051: * </TR></TABLE>
052: *
053: * Les algorithmes utilisés dans cette classe sont des adaptations des algorithmes
054: * en javascript écrit par le "National Oceanic and Atmospheric Administration,
055: * Surface Radiation Research Branch". L'application original est le
056: *
057: * <a href="http://www.srrb.noaa.gov/highlights/sunrise/azel.html">Solar Position Calculator</a>.
058: *
059: * <p>
060: * The approximations used in these programs are very good for years between
061: * 1800 and 2100. Results should still be sufficiently accurate for the range
062: * from -1000 to 3000. Outside of this range, results will be given, but the
063: * potential for error is higher.
064: *
065: * @since 2.1
066: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/nature/SunRelativePosition.java $
067: * @version $Id: SunRelativePosition.java 20874 2006-08-07 10:00:01Z jgarnett $
068: * @author Remi Eve
069: * @author Martin Desruisseaux
070: */
071: public class SunRelativePosition {
072: /**
073: * Number of milliseconds in a day.
074: */
075: private static final int DAY_MILLIS = 24 * 60 * 60 * 1000;
076:
077: /**
078: * Valeur affectée lorsque un resultat n'est pas calculable du
079: * fait de la nuit. Cette valeur concerne les valeurs de sorties
080: * {@link #elevation} et {@link #azimuth}.
081: */
082: private static final double DARK = Double.NaN;
083:
084: /**
085: * {@linkplain #getElevation Elevation angle} of astronomical twilight, in degrees.
086: * Astronomical twilight is the time of morning or evening when the sun is 18° below
087: * the horizon (solar elevation angle of -18°).
088: */
089: public static final double ASTRONOMICAL_TWILIGHT = -18;
090:
091: /**
092: * {@linkplain #getElevation Elevation angle} of nautical twilight, in degrees.
093: * Nautical twilight is the time of morning or evening when the sun is 12° below
094: * the horizon (solar elevation angle of -12°).
095: */
096: public static final double NAUTICAL_TWILIGHT = -12;
097:
098: /**
099: * {@linkplain #getElevation Elevation angle} of civil twilight, in degrees. Civil
100: * twilight is the time of morning or evening when the sun is 6° below the horizon
101: * (solar elevation angle of -6°).
102: */
103: public static final double CIVIL_TWILIGHT = -6;
104:
105: /**
106: * Sun's {@linkplain #getElevation elevation angle} at twilight, in degrees.
107: * Common values are defined for the
108: * {@linkplain #ASTRONOMICAL_TWILIGHT astronomical twilight} (-18°),
109: * {@linkplain #NAUTICAL_TWILIGHT nautical twilight} (-12°) and
110: * {@linkplain #CIVIL_TWILIGHT civil twilight} (-6°).
111: * If no twilight are defined, then this value is {@linkplain Double#NaN NaN}.
112: * The {@linkplain #getElevation elevation} and {@linkplain #getAzimuth azimuth} are
113: * set to {@linkplain Double#NaN NaN} when the sun elevation is below the twilight
114: * value (i.e. during night). The default value is {@link #CIVIL_TWILIGHT}.
115: */
116: private double twilight = CIVIL_TWILIGHT;
117:
118: /**
119: * Heure à laquelle le soleil est au plus haut dans la journée en millisecondes
120: * écoulées depuis le 1er janvier 1970.
121: */
122: private long noonTime;
123:
124: /**
125: * Azimuth du soleil, en degrés dans le sens des
126: * aiguilles d'une montre depuis le nord.
127: */
128: private double azimuth;
129:
130: /**
131: * Elévation du soleil, en degrés par rapport a l'horizon.
132: */
133: private double elevation;
134:
135: /**
136: * Geographic coordinate where current elevation and azimuth were computed.
137: * Value are in degrees of longitude or latitude.
138: */
139: private double latitude, longitude;
140:
141: /**
142: * Date and time when the current elevation and azimuth were computed.
143: * Value is in milliseconds ellapsed since midnight UTC, January 1st, 1970.
144: */
145: private long time = System.currentTimeMillis();
146:
147: /**
148: * {@code true} is the elevation and azimuth are computed, or {@code false}
149: * if they need to be computed. This flag is set to {@code false} when the date
150: * and/or the coordinate change.
151: */
152: private boolean updated;
153:
154: /**
155: * Calculate the equation of center for the sun. This value is a correction
156: * to add to the geometric mean longitude in order to get the "true" longitude
157: * of the sun.
158: *
159: * @param t number of Julian centuries since J2000.
160: * @return Equation of center in degrees.
161: */
162: private static double sunEquationOfCenter(final double t) {
163: final double m = Math.toRadians(sunGeometricMeanAnomaly(t));
164: return Math.sin(1 * m)
165: * (1.914602 - t * (0.004817 + 0.000014 * t))
166: + Math.sin(2 * m) * (0.019993 - t * (0.000101))
167: + Math.sin(3 * m) * (0.000289);
168: }
169:
170: /**
171: * Calculate the Geometric Mean Longitude of the Sun.
172: * This value is close to 0° at the spring equinox,
173: * 90° at the summer solstice, 180° at the automne equinox
174: * and 270° at the winter solstice.
175: *
176: * @param t number of Julian centuries since J2000.
177: * @return Geometric Mean Longitude of the Sun in degrees,
178: * in the range 0° (inclusive) to 360° (exclusive).
179: */
180: private static double sunGeometricMeanLongitude(final double t) {
181: double L0 = 280.46646 + t * (36000.76983 + 0.0003032 * t);
182: L0 = L0 - 360 * Math.floor(L0 / 360);
183: return L0;
184: }
185:
186: /**
187: * Calculate the true longitude of the sun. This the geometric mean
188: * longitude plus a correction factor ("equation of center" for the
189: * sun).
190: *
191: * @param t number of Julian centuries since J2000.
192: * @return Sun's true longitude in degrees.
193: */
194: private static double sunTrueLongitude(final double t) {
195: return sunGeometricMeanLongitude(t) + sunEquationOfCenter(t);
196: }
197:
198: /**
199: * Calculate the apparent longitude of the sun.
200: *
201: * @param t number of Julian centuries since J2000.
202: * @return Sun's apparent longitude in degrees.
203: */
204: private static double sunApparentLongitude(final double t) {
205: final double omega = Math.toRadians(125.04 - 1934.136 * t);
206: return sunTrueLongitude(t) - 0.00569 - 0.00478
207: * Math.sin(omega);
208: }
209:
210: /**
211: * Calculate the Geometric Mean Anomaly of the Sun.
212: *
213: * @param t number of Julian centuries since J2000.
214: * @return Geometric Mean Anomaly of the Sun in degrees.
215: */
216: private static double sunGeometricMeanAnomaly(final double t) {
217: return 357.52911 + t * (35999.05029 - 0.0001537 * t);
218: }
219:
220: /**
221: * Calculate the true anamoly of the sun.
222: *
223: * @param t number of Julian centuries since J2000.
224: * @return Sun's true anamoly in degrees.
225: */
226: private static double sunTrueAnomaly(final double t) {
227: return sunGeometricMeanAnomaly(t) + sunEquationOfCenter(t);
228: }
229:
230: /**
231: * Calculate the eccentricity of earth's orbit. This is the ratio
232: * {@code (a-b)/a} where <var>a</var> is the semi-major axis
233: * length and <var>b</var> is the semi-minor axis length. Value
234: * is 0 for a circular orbit.
235: *
236: * @param t number of Julian centuries since J2000.
237: * @return The unitless eccentricity.
238: */
239: private static double eccentricityEarthOrbit(final double t) {
240: return 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
241: }
242:
243: /**
244: * Calculate the distance to the sun in Astronomical Units (AU).
245: *
246: * @param t number of Julian centuries since J2000.
247: * @return Sun radius vector in AUs.
248: */
249: private static double sunRadiusVector(final double t) {
250: final double v = Math.toRadians(sunTrueAnomaly(t));
251: final double e = eccentricityEarthOrbit(t);
252: return (1.000001018 * (1 - e * e)) / (1 + e * Math.cos(v));
253: }
254:
255: /**
256: * Calculate the mean obliquity of the ecliptic.
257: *
258: * @param t number of Julian centuries since J2000.
259: * @return Mean obliquity in degrees.
260: */
261: private static double meanObliquityOfEcliptic(final double t) {
262: final double seconds = 21.448 - t
263: * (46.8150 + t * (0.00059 - t * (0.001813)));
264: return 23.0 + (26.0 + (seconds / 60.0)) / 60.0;
265: }
266:
267: /**
268: * Calculate the corrected obliquity of the ecliptic.
269: *
270: * @param t number of Julian centuries since J2000.
271: * @return Corrected obliquity in degrees.
272: */
273: private static double obliquityCorrected(final double t) {
274: final double e0 = meanObliquityOfEcliptic(t);
275: final double omega = Math.toRadians(125.04 - 1934.136 * t);
276: return e0 + 0.00256 * Math.cos(omega);
277: }
278:
279: /**
280: * Calculate the right ascension of the sun. Similar to the angular system
281: * used to define latitude and longitude on Earth's surface, right ascension
282: * is roughly analogous to longitude, and defines an angular offset from the
283: * meridian of the vernal equinox.
284: *
285: * <P align="center"><img src="doc-files/CelestialSphere.png"></P>
286: *
287: * @param t number of Julian centuries since J2000.
288: * @return Sun's right ascension in degrees.
289: */
290: private static double sunRightAscension(final double t) {
291: final double e = Math.toRadians(obliquityCorrected(t));
292: final double b = Math.toRadians(sunApparentLongitude(t));
293: final double y = Math.sin(b) * Math.cos(e);
294: final double x = Math.cos(b);
295: final double alpha = Math.atan2(y, x);
296: return Math.toDegrees(alpha);
297: }
298:
299: /**
300: * Calculate the declination of the sun. Declination is analogous to latitude
301: * on Earth's surface, and measures an angular displacement north or south
302: * from the projection of Earth's equator on the celestial sphere to the
303: * location of a celestial body.
304: *
305: * @param t number of Julian centuries since J2000.
306: * @return Sun's declination in degrees.
307: */
308: private static double sunDeclination(final double t) {
309: final double e = Math.toRadians(obliquityCorrected(t));
310: final double b = Math.toRadians(sunApparentLongitude(t));
311: final double sint = Math.sin(e) * Math.sin(b);
312: final double theta = Math.asin(sint);
313: return Math.toDegrees(theta);
314: }
315:
316: /**
317: * Calculate the Universal Coordinated Time (UTC) of solar noon for the given day
318: * at the given location on earth.
319: *
320: * @param lon longitude of observer in degrees.
321: * @param eqTime Equation of time.
322: * @return Time in minutes from beginnning of day in UTC.
323: */
324: private static double solarNoonTime(final double lon,
325: final double eqTime) {
326: return 720.0 + (lon * 4.0) - eqTime;
327: }
328:
329: /**
330: * Calculate the difference between true solar time and mean. The "equation
331: * of time" is a term accounting for changes in the time of solar noon for
332: * a given location over the course of a year. Earth's elliptical orbit and
333: * Kepler's law of equal areas in equal times are the culprits behind this
334: * phenomenon. See the
335: * <A HREF="http://www.analemma.com/Pages/framesPage.html">Analemma page</A>.
336: * Below is a plot of the equation of time versus the day of the year.
337: *
338: * <P align="center"><img src="doc-files/EquationOfTime.png"></P>
339: *
340: * @param t number of Julian centuries since J2000.
341: * @return Equation of time in minutes of time.
342: */
343: private static double equationOfTime(final double t) {
344: double eps = Math.toRadians(obliquityCorrected(t));
345: double l0 = Math.toRadians(sunGeometricMeanLongitude(t));
346: double m = Math.toRadians(sunGeometricMeanAnomaly(t));
347: double e = eccentricityEarthOrbit(t);
348: double y = Math.tan(eps / 2);
349: y *= y;
350:
351: double sin2l0 = Math.sin(2 * l0);
352: double cos2l0 = Math.cos(2 * l0);
353: double sin4l0 = Math.sin(4 * l0);
354: double sin1m = Math.sin(m);
355: double sin2m = Math.sin(2 * m);
356:
357: double etime = y * sin2l0 - 2 * e * sin1m + 4 * e * y * sin1m
358: * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m;
359:
360: return Math.toDegrees(etime) * 4.0;
361: }
362:
363: /**
364: * Computes the refraction correction angle.
365: * The effects of the atmosphere vary with atmospheric pressure, humidity
366: * and other variables. Therefore the calculation is approximate. Errors
367: * can be expected to increase the further away you are from the equator,
368: * because the sun rises and sets at a very shallow angle. Small variations
369: * in the atmosphere can have a larger effect.
370: *
371: * @param zenith The sun zenith angle in degrees.
372: * @return The refraction correction in degrees.
373: */
374: private static double refractionCorrection(final double zenith) {
375: final double exoatmElevation = 90 - zenith;
376: if (exoatmElevation > 85) {
377: return 0;
378: }
379: final double refractionCorrection; // In minute of degrees
380: final double te = Math.tan(Math.toRadians(exoatmElevation));
381: if (exoatmElevation > 5.0) {
382: refractionCorrection = 58.1 / te - 0.07 / (te * te * te)
383: + 0.000086 / (te * te * te * te * te);
384: } else {
385: if (exoatmElevation > -0.575) {
386: refractionCorrection = 1735.0
387: + exoatmElevation
388: * (-518.2 + exoatmElevation
389: * (103.4 + exoatmElevation
390: * (-12.79 + exoatmElevation * 0.711)));
391: } else {
392: refractionCorrection = -20.774 / te;
393: }
394: }
395: return refractionCorrection / 3600;
396: }
397:
398: /**
399: * Constructs a sun relative position calculator.
400: */
401: public SunRelativePosition() {
402: }
403:
404: /**
405: * Constructs a sun relative position calculator with the specified value
406: * for the {@linkplain #setTwilight sun elevation at twilight}.
407: *
408: * @param twilight The new sun elevation at twilight, or {@link Double#NaN}
409: * if no twilight value should be taken in account.
410: * @throws IllegalArgumentException if the twilight value is illegal.
411: */
412: public SunRelativePosition(final double twilight)
413: throws IllegalArgumentException {
414: setTwilight(twilight);
415: }
416:
417: /**
418: * Calculates solar position for the current date, time and location.
419: * Results are reported in azimuth and elevation in degrees.
420: */
421: private void compute() {
422: double latitude = this .latitude;
423: double longitude = this .longitude;
424:
425: // NOAA convention use positive longitude west, and negative east.
426: // Inverse the sign, in order to be closer to OpenGIS convention.
427: longitude = -longitude;
428:
429: // Compute: 1) Julian day (days ellapsed since January 1, 4723 BC at 12:00 GMT).
430: // 2) Time as the centuries ellapsed since January 1, 2000 at 12:00 GMT.
431: final double julianDay = Calendar.julianDay(this .time);
432: final double time = (julianDay - 2451545) / 36525;
433:
434: double solarDec = sunDeclination(time);
435: double eqTime = equationOfTime(time);
436: this .noonTime = Math.round(solarNoonTime(longitude, eqTime)
437: * (60 * 1000))
438: + (this .time / DAY_MILLIS) * DAY_MILLIS;
439:
440: // Formula below use longitude in degrees. Steps are:
441: // 1) Extract the time part of the date, in minutes.
442: // 2) Apply a correction for longitude and equation of time.
443: // 3) Clamp in a 24 hours range (24 hours == 1440 minutes).
444: double trueSolarTime = ((julianDay + 0.5) - Math
445: .floor(julianDay + 0.5)) * 1440;
446: trueSolarTime += (eqTime - 4.0 * longitude); // Correction in minutes.
447: trueSolarTime -= 1440 * Math.floor(trueSolarTime / 1440);
448:
449: // Convert all angles to radians. From this point until
450: // the end of this method, local variables are always in
451: // radians. Output variables ('azimuth' and 'elevation')
452: // will still computed in degrees.
453: longitude = Math.toRadians(longitude);
454: latitude = Math.toRadians(latitude);
455: solarDec = Math.toRadians(solarDec);
456:
457: double csz = Math.sin(latitude) * Math.sin(solarDec)
458: + Math.cos(latitude) * Math.cos(solarDec)
459: * Math.cos(Math.toRadians(trueSolarTime / 4 - 180));
460: if (csz > +1)
461: csz = +1;
462: if (csz < -1)
463: csz = -1;
464:
465: final double zenith = Math.acos(csz);
466: final double azDenom = Math.cos(latitude) * Math.sin(zenith);
467:
468: //////////////////////////////////////////
469: //// Compute azimuth in degrees ////
470: //////////////////////////////////////////
471: if (Math.abs(azDenom) > 0.001) {
472: double azRad = ((Math.sin(latitude) * Math.cos(zenith)) - Math
473: .sin(solarDec))
474: / azDenom;
475: if (azRad > +1)
476: azRad = +1;
477: if (azRad < -1)
478: azRad = -1;
479:
480: azimuth = 180 - Math.toDegrees(Math.acos(azRad));
481: if (trueSolarTime > 720) { // 720 minutes == 12 hours
482: azimuth = -azimuth;
483: }
484: } else {
485: azimuth = (latitude > 0) ? 180 : 0;
486: }
487: azimuth -= 360 * Math.floor(azimuth / 360);
488:
489: ////////////////////////////////////////////
490: //// Compute elevation in degrees ////
491: ////////////////////////////////////////////
492: final double refractionCorrection = refractionCorrection(Math
493: .toDegrees(zenith));
494: final double solarZen = Math.toDegrees(zenith)
495: - refractionCorrection;
496:
497: elevation = 90 - solarZen;
498: if (elevation < twilight) {
499: // do not report azimuth & elevation after twilight
500: azimuth = DARK;
501: elevation = DARK;
502: }
503: updated = true;
504: }
505:
506: /**
507: * Set the geographic coordinate where to compute the {@linkplain #getElevation elevation}
508: * and {@linkplain #getAzimuth azimuth}.
509: *
510: * @param longitude The longitude in degrees. Positive values are East; negative values are West.
511: * @param latitude The latitude in degrees. Positive values are North, negative values are South.
512: */
513: public void setCoordinate(double longitude, double latitude) {
514: if (latitude > +89.8)
515: latitude = +89.8;
516: if (latitude < -89.8)
517: latitude = -89.8;
518: if (latitude != this .latitude || longitude != this .longitude) {
519: this .latitude = latitude;
520: this .longitude = longitude;
521: this .updated = false;
522: }
523: }
524:
525: /**
526: * Set the geographic coordinate where to compute the {@linkplain #getElevation elevation}
527: * and {@linkplain #getAzimuth azimuth}.
528: *
529: * @param point The geographic coordinates in degrees of longitude and latitude.
530: */
531: public void setCoordinate(final Point2D point) {
532: setCoordinate(point.getX(), point.getY());
533: }
534:
535: /**
536: * Returns the coordinate used for {@linkplain #getElevation elevation} and
537: * {@linkplain #getAzimuth azimuth} computation. This is the coordinate
538: * specified during the last call to a {@link #setCoordinate(double,double)
539: * setCoordinate(...)} method.
540: */
541: public Point2D getCoordinate() {
542: return new Point2D.Double(longitude, latitude);
543: }
544:
545: /**
546: * Set the date and time when to compute the {@linkplain #getElevation elevation}
547: * and {@linkplain #getAzimuth azimuth}.
548: *
549: * @param date The date and time.
550: */
551: public void setDate(final Date date) {
552: final long time = date.getTime();
553: if (time != this .time) {
554: this .time = time;
555: this .updated = false;
556: }
557: }
558:
559: /**
560: * Returns the date used for {@linkplain #getElevation elevation} and
561: * {@linkplain #getAzimuth azimuth} computation. This is the date specified
562: * during the last call to {@link #setDate}.
563: */
564: public Date getDate() {
565: return new Date(time);
566: }
567:
568: /**
569: * Set the sun's {@linkplain #getElevation elevation angle} at twilight, in degrees.
570: * Common values are defined for the
571: * {@linkplain #ASTRONOMICAL_TWILIGHT astronomical twilight} (-18°),
572: * {@linkplain #NAUTICAL_TWILIGHT nautical twilight} (-12°) and
573: * {@linkplain #CIVIL_TWILIGHT civil twilight} (-6°).
574: * The {@linkplain #getElevation elevation} and {@linkplain #getAzimuth azimuth} are
575: * set to {@linkplain Double#NaN NaN} when the sun elevation is below the twilight
576: * value (i.e. during night). The default value is {@link #CIVIL_TWILIGHT}.
577: *
578: * @param twilight The new sun elevation at twilight, or {@link Double#NaN}
579: * if no twilight value should be taken in account.
580: * @throws IllegalArgumentException if the twilight value is illegal.
581: */
582: public void setTwilight(final double twilight)
583: throws IllegalArgumentException {
584: if (twilight < -90 || twilight > -90) {
585: // TODO: provides a better (localized) message.
586: throw new IllegalArgumentException(String.valueOf(twilight));
587: }
588: this .twilight = twilight;
589: this .updated = false;
590: }
591:
592: /**
593: * Returns the sun's {@linkplain #getElevation elevation angle} at twilight, in degrees.
594: * This is the value set during the last call to {@link #setTwilight}.
595: */
596: public double getTwilight() {
597: return twilight;
598: }
599:
600: /**
601: * Retourne l'azimuth en degrés.
602: *
603: * @return L'azimuth en degrés.
604: */
605: public double getAzimuth() {
606: if (!updated) {
607: compute();
608: }
609: return azimuth;
610: }
611:
612: /**
613: * Retourne l'élévation en degrés.
614: *
615: * @return L'élévation en degrés.
616: */
617: public double getElevation() {
618: if (!updated) {
619: compute();
620: }
621: return elevation;
622: }
623:
624: /**
625: * Retourne l'heure à laquelle le soleil est au plus haut. L'heure est
626: * retournée en nombre de millisecondes écoulées depuis le debut de la
627: * journée (minuit) en heure UTC.
628: */
629: public long getNoonTime() {
630: if (!updated) {
631: compute();
632: }
633: return noonTime % DAY_MILLIS;
634: }
635:
636: /**
637: * Retourne la date à laquelle le soleil est au plus haut dans la journée.
638: * Cette méthode est équivalente à {@link #getNoonTime} mais inclue le jour
639: * de la date qui avait été spécifiée à la méthode {@link #compute}.
640: */
641: public Date getNoonDate() {
642: if (!updated) {
643: compute();
644: }
645: return new Date(noonTime);
646: }
647:
648: /**
649: * Affiche la position du soleil à la date et coordonnées spécifiée.
650: * Cette application peut être lancée avec la syntaxe suivante:
651: *
652: * <pre>SunRelativePosition <var>[longitude]</var> <var>[latitude]</var> <var>[date]</var></pre>
653: *
654: * où <var>date</var> est un argument optionel spécifiant la date et l'heure.
655: * Si cet argument est omis, la date et heure actuelles seront utilisées.
656: */
657: public static void main(final String[] args) throws ParseException {
658: final DateFormat format = DateFormat.getDateTimeInstance(
659: DateFormat.SHORT, DateFormat.SHORT);
660: format.setTimeZone(TimeZone.getTimeZone("UTC"));
661: double longitude = 0;
662: double latitude = 0;
663: Date time = new Date();
664: switch (args.length) {
665: case 3:
666: time = format.parse(args[2]); // fall through
667: case 2:
668: latitude = Double.parseDouble(args[1]); // fall through
669: case 1:
670: longitude = Double.parseDouble(args[0]); // fall through
671: }
672: final SunRelativePosition calculator = new SunRelativePosition();
673: calculator.setDate(time);
674: calculator.setCoordinate(longitude, latitude);
675: System.out.print("Date (UTC): ");
676: System.out.println(format.format(time));
677: System.out.print("Longitude: ");
678: System.out.println(longitude);
679: System.out.print("Latitude: ");
680: System.out.println(latitude);
681: System.out.print("Elevation: ");
682: System.out.println(calculator.getElevation());
683: System.out.print("Azimuth: ");
684: System.out.println(calculator.getAzimuth());
685: System.out.print("Noon date: ");
686: System.out.println(format.format(calculator.getNoonDate()));
687: }
688: }
|