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