Klassen des Pakets AstroOL: Zur Doku-Startseite Zurück zur Homepage

Klasse Calculator

Javadoc von Calculator   Calculator.java herunterladen

package AstroOL;
import java.util.*;
import java.net.*;
import java.io.*;

/** Diese Klasse bietet "billige" Hilfsrechnungen, wie sie auf dem
   Client leicht ausgeführt werden können, zum Beispiel
   Kalenderberechnungen und formatierte Ein- und Ausgabe.
   <p>
   Instanzen von <code>Calculator</code> dienen als Horoskopberechner,
   da die Klasse das Interface ICalculator implementiert. Die in dieser
   Klasse definierte Standardimplementierung
   ruft zur Berechnung der Planeten und Häuser ein Servlet auf.
   <p>
   Darüberhinaus enthält die Klasse Basismethoden, die eigentlich
   in java.lang.Math oder java.util.* gehören, dort aber entweder
   nicht vorhanden sind oder zu einem JDK-Release > 1.1, so dass
   Klassen, die diese Methoden verwenden, für Benutzer des Microsoft
   Internet Explorer 5.5 mit der Microsoft VM unbenutzbar wären.
   Beispiele: {@link #sgn Signumfunktion}, {@link #sort Sortieralgorithmus
   für lange Ganzzahlen}.
   <p>
   Die Rechenfunktionen sind, genau wie die von java.lang.Math,
   rein statisch (ohne Instanz-Erzeugung). */
public class Calculator implements ICalculator {

/** Methode für Tests (wird nur bei der Entwicklung verwendet) */
    public static void main( String[] argv ) {
      double[] tp = new double[3];
//      String timePlace = argv[0];
      System.out.println( dms(0d,Calculator.GEO) );
//    String timePlace = "1.5.2002 0h 0m 1s 4E 5' 6\" 47N 12' 32\"";
//      parseTimePlace( timePlace, tp );
//      System.out.println( timePlace );
//      System.out.println( timePlace = renderTimePlace(tp, Calculator.SEC) );
//      parseTimePlace( timePlace, tp );
//      System.out.println( renderTimePlace(tp, Calculator.SEC) );

      }

/** Umwandlungsfaktor Bogenmass in Grad (57.295...) */
     public static final double toDeg   = .57295779513082320876798154814e2;
/** Umwandlungsfaktor Grad in Bogenmass (0.01745...) */
     public static final double toRad   = .174532925199432957692369076849e-1;

/** Ziffern-String für Formatierungen (Wert "0123456789").
    Wird verwendet, um Eingabestrings zu analysieren.
    */
    public static final String digit = "0123456789";

/** {@link #dms dms-Funktion}: Ausgabe ist sekundengenau gewünscht. <p>
    Spezifikation für die Funktion {@link #dms dms()}.
    Die gewünschten Werte müssen, mit einem logischen Oder
    verknüpft, als Bitmaske für den Wert format der Methode
    {@link #dms dms()} übergeben werden.  */
    public static final long SEC = 0x01;

/** {@link #dms dms-Funktion}: Ausgabe als Winkel zwischen 0 und 360°. <br>
    Untypisierter - "geometrischer" Winkelwert des Vollkreises.
    <p>Spezifikation für die Funktion {@link #dms dms()}.
    Die gewünschten Werte müssen, mit einem logischen Oder
    verknüpft, als Bitmaske für den Wert format der Methode
    {@link #dms dms()} übergeben werden.  */
    public static final long GEO = 0x02;

/** {@link #dms dms-Funktion}: Ausgabe einer ekliptikale Länge. <br>
    Je nach Angabe von SEC
    wird die Ausgabe in der Form 13SG21 (minutengenaue
    Ausgabe) oder 13°21'22" SG (sekundengenaue Ausgabe)
    erfolgen. Bei der minutengenauen Ausgabe wird das
    Tierkreiszeichen also als Trenner zwischen Grad- und
    Minutenwert behandelt.<p>
    Spezifikation für die Funktion {@link #dms dms()}.
    Die gewünschten Werte müssen, mit einem logischen Oder
    verknüpft, als Bitmaske für den Wert format der Methode
    {@link #dms dms()} übergeben werden.  */
    public static final long ECL = 0x04;

/** {@link #dms dms-Funktion}: Ausgabe einer geographischen Breite. <br>
    Je nach Angabe von SEC wird die Ausgabe in
    der Form 13S22 (minutengenaue Ausgabe) oder
    13°22'31" S (sekundengenaue Ausgabe) erfolgen.
    Positive Werte werden als nördliche, negative als
    südliche Breiten interpretiert. <p>
    Spezifikation für die Funktion {@link #dms dms()}.
    Die gewünschten Werte müssen, mit einem logischen Oder
    verknüpft, als Bitmaske für den Wert format der Methode
    {@link #dms dms()} übergeben werden.  */
    public static final long LAT = 0x08;

/** {@link #dms dms-Funktion}: Ausgabe einer geographischen Länge. <br>
    Je nach Angabe von SEC wird die Ausgabe in
    der Form 13E22 (minutengenaue Ausgabe) oder
    13°22'31" E (sekundengenaue Ausgabe) erfolgen.
    Positive Werte werden als östliche, negative als
    westliche Längen interpretiert.<p>
    Spezifikation für die Funktion {@link #dms dms()}.
    Die gewünschten Werte müssen, mit einem logischen Oder
    verknüpft, als Bitmaske für den Wert format der Methode
    {@link #dms dms()} übergeben werden.  */
    public static final long LONG = 0x10;

/** {@link #dms dms-Funktion}: Zeitangabe (Sternzeit, Ortszeit o.ä.). <br>
    Je nach
    Angabe von SEC wird 13h22m oder 13h22m21s ausgegeben.<p>
    Spezifikation für die Funktion {@link #dms dms()}.
    Die gewünschten Werte müssen, mit einem logischen Oder
    verknüpft, als Bitmaske für den Wert format der Methode
    {@link #dms dms()} übergeben werden.  */
    public static final long TIME = 0x20;

/** {@link #dms dms-Funktion}: Keine Rundung. <br>
    Standardmässig wird auf die nächste
    Bogenminute oder -sekunde, je nach gewünschter Genauigkeit,
    gerundet. Die Rundung lässt sich durch den
    Formatierer NO_ROUND abschalten.
    Für astrologische Längen gilt bei eingeschalteter Rundung
    die zusätzliche Rundungsregel, dass an der Grenze eines
    Tierkreiszeichens
    niemals zum nächsten Zeichen hin aufgerundet wird. Ein
    Wert von 29°59.5' AR wird beispielsweise auf 29°59' AR
    abgerundet. Hier gilt die Regel, dass die Information,
    in welchem Zeichen sich das Objekt befindet, für den
    Astrologen wichtiger ist als die Ü;berschreitung des Rundungsfehlers
    von 0.5' (oder 0.5" bei Rundung auf Bogensekunden).<p>
    Spezifikation für die Funktion {@link #dms dms()}.
    Die gewünschten Werte müssen, mit einem logischen Oder
    verknüpft, als Bitmaske für den Wert format der Methode
    {@link #dms dms()} übergeben werden.  */
    public static final long NO_ROUND = 0x40;

/** Default-Breite, falls keine Breite von aussen mitgegeben wurde
    (47,36 Grad Nord) */
    public static final double DEFAULT_LAT = 47.36;
/** Default-Länge, falls keine Länge von aussen mitgegeben wurde
    (8,55 Grad Ost) */
    public static final double DEFAULT_LON = 8.55;

/** Kurznamen der Planeten für Listausgaben.
    Je zwei Zeichen des Strings stehen für einen Planeten,
    wobei die folgende internationale Nomenklatur verwendet wird:
    <p>
    <table align="center" border="1" cellspacing="0">
    <tr><th>Index</th><th>Kurzform</th><th>Planet</th></tr>
    <tbody><tr><td>0</td><td align=center><code><b>SO</b></code></td><td>Sonne</td></tr>
    <tbody><tr><td>2</td><td align=center><code><b>MO</b></code></td><td>Mond</td></tr>
    <tbody><tr><td>4</td><td align=center><code><b>ME</b></code></td><td>Merkur</td></tr>
    <tbody><tr><td>6</td><td align=center><code><b>VE</b></code></td><td>Venus</td></tr>
    <tbody><tr><td>8</td><td align=center><code><b>MA</b></code></td><td>Mars</td></tr>
    <tbody><tr><td>10</td><td align=center><code><b>JU</b></code></td><td>Jupiter</td></tr>
    <tbody><tr><td>12</td><td align=center><code><b>SA</b></code></td><td>Saturn</td></tr>
    <tbody><tr><td>14</td><td align=center><code><b>UR</b></code></td><td>Uranus</td></tr>
    <tbody><tr><td>16</td><td align=center><code><b>NE</b></code></td><td>Neptun</td></tr>
    <tbody><tr><td>18</td><td align=center><code><b>PL</b></code></td><td>Pluto</td></tr>
    </table>
    */
    public static final String pknam = "SOMOMEVEMAJUSAURNEPL";

/** Kurznamen der Tierkreiszeichen für Listausgaben.
    Je zwei Zeichen des Strings stehen für ein Tierkreiszeichen,
    wobei die folgenden, international üblichen Abkürzungen
    verwendet werden:
    <p>
    <table align="center" border cellspacing="0">
    <tr><th>Index</th><th>Kurzform</th><th>Zeichen</th></tr>
    <tr><td align=right>0</td><td align=center><code><b>AR</b></code></td><td>Widder (Aries)</td></tr>
    <tr><td align=right>2</td><td align=center><code><b>TA</b></code></td><td>Stier (Taurus)</td></tr>
    <tr><td align=right>4</td><td align=center><code><b>GE</b></code></td><td>Zwillinge (Gemini)</td></tr>
    <tr><td align=right>6</td><td align=center><code><b>CN</b></code></td><td>Krebs (Cancer)</td></tr>
    <tr><td align=right>8</td><td align=center><code><b>LE</b></code></td><td>Löwe (Leo)</td></tr>
    <tr><td align=right>10</td><td align=center><code><b>VI</b></code></td><td>Jungfrau (Virgo)</td></tr>
    <tr><td align=right>12</td><td align=center><code><b>LI</b></code></td><td>Waage (Libra)</td></tr>
    <tr><td align=right>14</td><td align=center><code><b>SC</b></code></td><td>Skorpion (Scorpio)</td></tr>
    <tr><td align=right>16</td><td align=center><code><b>SG</b></code></td><td>Schütze (Sagittarius)</td></tr>
    <tr><td align=right>18</td><td align=center><code><b>CP</b></code></td><td>Steinbock (Capricornus)</td></tr>
    <tr><td align=right>20</td><td align=center><code><b>AQ</b></code></td><td>Wassermann (Aquarius)</td></tr>
    <tr><td align=right>22</td><td align=center><code><b>PS</b></code></td><td>Fische (Pisces)</td></tr>
    </table>
     */
     public static final String sknam = "ARTAGECNLEVILISCSGCPAQPS";

/** Servlet-URL - wird benötigt für die Implementierung des Interface ICalculator */
     private String servletURL;


/** Servlet-URL, aufbewahren für Aufrufe der ICalculator.getPlanetsAndHouses()-Methode */
     public void setServletURL(String iServletURL) {
       this.servletURL = iServletURL;
       }


/** Servlet-URL zurückgeben */
     public String getServletURL() {
       return this.servletURL;
       }

/** Absolutbetrag einer Gleitkommazahl (Funktion ist nicht in java.lang.Math
    implementiert!).
     */
     public static double fabs( double x ) {
       return (x >= 0 ) ? x : -x ;
       }

/** Signumfunktion einer Gleitkommazahl (Funktion ist nicht in java.lang.Math
    implementiert!). <p>
    Die Signumfunktion hat drei mögliche Werte: 0,1 und -1. Sie liefert
    <ul>
    <li> 1 für positive Zahlen,
    <li> -1 für negative Zahlen und
    <li> 0 für die Null.
    </ul>
    @param x Argument der Signumfunktion, eine doppeltgenaue Gleitkommazahl
    @return Wert der Signumfunktion von x -- ein Byte genügt, um das Ergebnis
    zu halten.
     */
     public static byte sgn( double x ) {
       return (byte)((x >= 0 ) ? 1 : (( x == 0) ? 0 : -1));
       }

/** Reduktion einer Gleitkommazahl x auf das Intervall [0,360). <br>
    Die Funktion liefert eine Zahl zwischen 0 und 360°, die denselben
    Winkelwert repräsentiert wie das Argument.
    @param x Eine doppeltgenaue Gleitkommazahl
    @return Ergebnis der Reduktion
    */
    public static double fmod360( double x ) {
      return ( x / 360d - Math.floor( x / 360d ) )* 360d;
      }

/** Reduktion einer Gleitkommazahl x auf das Intervall [0,a). <br>
    Die Funktion liefert eine Zahl zwischen 0 und a, die denselben
    Wert modulo a repräsentiert wie das Argument x.
    @param x Eine doppeltgenaue Gleitkommazahl
    @param a Modulo-Wert
    @return Ergebnis der Reduktion
    */
    public static double fmod( double x, double a ) {
      return ( x / a - Math.floor( x / a ) )* a;
      }


/** Abstand einer Gleitkommazahl von der nächstkleineren Ganzzahl */
     public static double fract( double x ) {
         return x - Math.floor( x );
       }

/** Hilfsfunktion zur Berechnung des Winkelabstandes.
    Es wird die Differenz x - y auf das Intervall [-180,+180)
    reduziert berechnet. Diese Reduktion ist zur Berechnung
    von Abständen im Kreis nützlich.
    @param x Erster Argument in Grad
    @param y Zweites Argument in Grad
    @return Winkelabstand x - y, reduziert auf [-180,+180)
    */
     public static double arcdiff( double x, double y) {
         double z = x - y;
         return ( (z >= -180) && (z < 180)) ? z :
           z - Math.floor((z+180)/360.)*360;
       }

/** Wie arcdiff(), jedoch wird der Absolutbetrag des
    Winkelabstandes zurückgegeben, also eine Zahl zwischen
    0 und 180 Grad.
    @param x Erster Argument in Grad
    @param y Zweites Argument in Grad
    @return Winkelabstand x - y, reduziert auf [0,+180)
    */
     public static double arcdiffAbs( double x, double y) {
         double z = x - y;
         if ((z <= -180) || (z > 180))
           z -= Math.floor((z+180)/360.)*360;
         return (z >= 0) ? z : -z;
       }

/** Wrapper für ArcusTangens im Gradmass */
     public static double atan2( double x, double y ) {
         return( Math.atan2( x, y) * toDeg);
         }

/** Wrapper für ArcusTangens im Gradmass */
     public static double atan( double x ) {
         return( Math.atan( x ) * toDeg);
         }

/** Wrapper für ArcusSinus im Gradmass */
     public static double asin( double x ) {
         return( Math.asin( x) * toDeg);
         }

/** Wrapper für ArcusCosinus im Gradmass */
     public static double acos( double x ) {
         return( Math.acos( x) * toDeg);
         }

/** Wrapper für Cosinus im Gradmass */
     public static double cos( double x ) {
         return( Math.cos( x*toRad));
         }

/** Wrapper für Sinus im Gradmass */
     public static double sin( double x ) {
         return( Math.sin( x*toRad));
         }

/** Wrapper für Tangens im Gradmass */
     public static double tan( double x ) {
         return( Math.tan( x*toRad));
         }

/** Gradzahl ins Grad-Minuten-Sekundensystem wandeln. <p>
    Wird die Funktion dms() mit nur einem Parameter aufgerufen,
    so wandelt sie diesen Parameterwert in eine <i>Gradzahl</i>
    um, notiert im Grad/Minuten/Sekundensystem. Das heisst,
    defaultmässig sind für die Konvertierung die Optionen
    {@link #GEO GEO} und {@link #SEC SEC} aktiv.
    @param x Gleitkommazahl
    @return String, der die Gleitkommazahl x im
    Sexagesimalsystem darstellt. */
        public static String dms(double x) {
          return dms(x, GEO | SEC);
        }

/** Dezimalzahl ins Sexagesimalsystem wandeln. <p>
    Mit dem zweiten Parameter der Funktion hat man Einfluss auf
    die Formatierung. Er bestimmt, ob die übergebene
    Dezimalzahl eine {@link #GEO Gradzahl im Vollkreis} ist,
    eine {@link #ECL ekliptikale Längenangabe}, eine
    {@link #LONG geographische Länge}, {@link #LAT geographische
    Breite} oder eine {@link #TIME Zeitangabe}. Durch
    Hinzuaddieren der Option {@link #SEC SEC} wird sekunden-
    statt der defaultmässig minutengenauen Ausgabe angefordert,
    durch Addieren von {@link #NO_ROUND NO_ROUND} wird das
    Runden verhindert, das Ergebnis wird auf die gewünschte
    Genauigkeit abgeschnitten.
    @param x Gleitkommazahl
    @param format Summe von einer oder mehreren Formatierungskonstanten
    @return String, der die Gleitkommazahl im Sexagesimalsystem
    darstellt.
     */
        public static String dms(double x, long format) {
          StringBuffer sb = new StringBuffer("");
          double y;
          int d,m,s,s1,s2,m1,m2;
          int sign = ( x >= 0 ) ? 1 : -1;
          String dc = "°", mc = "'", sc = "\"";

          if ((format & NO_ROUND) == NO_ROUND )
            y = x;
          else {
            if ((format & SEC) == SEC)
              y = (sign*x+.5/3600)*sign; /* Runden auf Bogensekunde */
            else
              y = (sign*x+.5/60)*sign; /* Runden auf Bogenminute */
            }

          if ((format & LONG)==LONG)  {
             while (y > 180 || y <= -180)
               if ( y > 0 ) y -= 360;
               else y += 360;
             sign = ( y >= 0) ? 1 : -1 ;
            }

         y*= sign;
         y = fmod360(y);
         d = (int) y;
         m = (int) (60*(y-Math.floor(y)));
         m1 = m/10;
         m2 = m-10*m1;
         s = (int)(3600*(y-(double)d-(double)m/60.));
         s1 = s/10;
         s2 = s-10*s1;

         if ((format & ECL)==ECL)
           if ((format & SEC)==0) {
             dc = sknam.substring(2*(d/30),2*(d/30)+2);
             mc = "";
             d  %= 30;
             }
           else {
             sc = sc + " " + sknam.substring(2*(d/30),2*(d/30)+2);
             d %= 30;
             }


         if ((format & TIME) == TIME) {
           dc = "h";
           mc = "m";
           sc = "s";
           }

         if ((format & LONG) == LONG) {
           dc = ( sign >= 0 ) ? "E" : "W";
           if ((format & SEC) == 0)
             mc = "";
           }

          if ((format & LAT) == LAT) {
           dc = ( sign >= 0 ) ? "N" : "S";
           if ((format & SEC) == 0)
             mc = "";
           }

         sb.append(Integer.toString(d).trim());
         sb.append(dc);
         sb.append(digit.charAt(m1));
         sb.append(digit.charAt(m2));
         sb.append(mc);

         if ((format & SEC) == SEC) {
           sb.append(digit.charAt(s1));
           sb.append(digit.charAt(s2));
           sb.append(sc);
           }

         if (d < 10) sb.insert(0,' ');
         if (((format & (ECL | TIME | LAT)) == 0) && (d < 100)) sb.insert(0,' ');

         return sb.toString();
        }


/** Julianisches Datum aus der aktuellen Systemzeit ermitteln.
    @return Julianisches Datum
    */
    public static double thisjd()
       {
       double xj = 0.;

       java.util.Date date = new Date();
// Systemroutine für aktuelle Zeit in Millisekunden
       return 2440587.5 + (double) date.getTime() / 86400000;
       }


/** Julianisches Datum in Kalenderdatum konvertieren. <p>
Das Julianische Datum (fortlaufende Tageszahl) wird in ein
"lesbares" Kalenderdatum konvertiert. Es wird ab dem 14.10.1582
der Gregorianische, vor diesem Datum der Julianische Kalender
zugrundegelegt.
@param jd Julianisches Datum
@return Array mit drei Integers für das Datum:
   date[0] = Jahr,
   date[1] = Monat,
   date[2] = Tag. */
      public static int[] calendarDate( double jd) {
        int[] date = new int[3];
        double a,b,c,d,e,f;
      a = Math.floor(jd + .5);
      if (a < 2299161.)
       c = a + 1524.;
      else {
       b = Math.floor((a-1867216.25)/36524.25);
       c = a + b - Math.floor(b/4.) + 1525.;
      }
      d = Math.floor((c-122.1)/365.25);
      e = Math.floor(365.25*d);
      f = Math.floor((c-e)/30.6001);
      date[2] = (int) (c - e - Math.floor(30.6001*f));
      date[1] = (int) (f - 1 - (int)(f/14)*12);
      date[0] = (int) (d - 4715 - (int)((7+date[1])/10));
      return date;

      }

/** Julianisches Datum in Kalenderdatum als String umrechnen */
      public static String date(double jd) {
        int[] ymd = calendarDate(jd);
        String rs = ymd[2]+"."+ymd[1]+"."+ymd[0];
        return rs;
        }

/** Kalenderdatum in Julianisches Datum wandeln. <p>
Es wird ab dem 14.10.1582
der Gregorianische, vor diesem Datum der Julianische Kalender
zugrundegelegt.
@param year Kalenderjahr
@param month Kalendermonat
@param day Kalendertag
@param et Ephemeridenzeit oder auch Weltzeit
@return Julianisches Datum; je nach Zeitart des Parameters et ist auch
das Julianische Datum in Ephemeriden- oder Weltzeit zu verstehen. Es
findet keine interne Konvertierung von Weltzeit in Ephemeridenzeit statt.
 */
      public static double julianDate (int year, int month, int day, double et)
      {
      double y,m,b,jd;
/* --- calendar date --> julian date */
      y = (double) year;
      m = (double) month;
      if (m <= 2.) {
        m = m + 12.;
        y = y - 1.;
      }
      b = -2.;
/* - from this date on use greg:  ]]]]]]]]]] = 14.10.1582 */
      if (day/370+month/12+year > 1582.87117)
       b = Math.floor(y/400.)- Math.floor(y/100.);
      jd = Math.floor(365.25*y) + Math.floor(30.6001*(m+1)) + b +
      1720996.5e0 + (double) day + et/24.;
      return jd;
      }

/** Planeten und Häuser durch Aufruf eines Berechnungs-Servlets im
    Backend starten. Die angegebene URL muss einen Response vom Typ <tt>text/plain</tt>
    zurückgeben, der aus zwei komma-separierten Listen von
    Gleitkommazahlen besteht. Die erste dieser Listen besteht
    aus den zehn ekliptikalen Längen der Planeten. Die zweite
    besteht aus den zwölf Häuserspitzen. Für die Häuser besteht die
    Konvention, die erste Hausspitze ins Element h[1] (und nicht
    etwa h[0]) zu setzen */
    public static synchronized boolean getPlanetsAndHouses( String urlstring,
                                               double[] pl,
                                               double[] h ) {

        URL url;
        BufferedReader in;
        boolean success=false;

        try {
          url = new URL( urlstring );
          in = new BufferedReader(new InputStreamReader( url.openStream() ));
          success = analyzeString(in,h,pl);
          }
        catch( MalformedURLException e2) {
          e2.printStackTrace();
          return false;
          }
        catch( IOException e1) {
          e1.printStackTrace();
          return false;
          }
        return success;
      }


/** Implementierung der Methode ICalculator.getPlanetsAndHouses() */
     public boolean getPlanetsAndHouses( double jd_ut,
                                         double lon,
                                         double lat,
                                         double[] pl,
                                         double[] h) {
        String url = servletURL
          + "?planets&houses&jd=" + jd_ut + "&lon=" + lon + "&lat=" + lat;
        boolean success = getPlanetsAndHouses( url, pl, h);
        return success;
       }


/** Implementierung der Interface-Methode zur Berechnung eines einzelnen Planeten. */
  public double[] getPlanet( double jd_ut,
                             int planet,
                             StringBuffer serr) {

// Vorläfig kommt hier die Ableitung des Einzelwertes aus der gesamten Gruppe hin
// Mittelfristig ist dies durch eine effizientere Servletmethode zu ersetzen.

    double[] pl = new double[10];
    double[] h  = new double[13];
                    getPlanetsAndHouses( jd_ut,
                                         0d,
                                         0d,
                                         pl,
                                         h);
    return new double[] {pl[planet],0d,0d,0d,0d,0d};

    }



/** Implementierung der Interface-Methode zur Hausberechnung. */
  public double getHouse( double jd_ut,
                          double lon,
                          double lat,
                          double houseNumber,
                          StringBuffer serr) {

    double[] pl = new double[10];
    double[] h  = new double[13];
                 getPlanetsAndHouses( jd_ut,
                                      lon,
                                      lat,
                                      pl,
                                      h);
      return h[(int)houseNumber];
    }


/** Einen String, der die Planeten- und Hauspositionen enthält, analysieren.
   Es wird ein String (genauer: ein BufferedReader) ausgewertet, der,
   auf die Schlüsselwörter "planets:" und "houses:" folgend, Listen
   komma-getrennter Gleitkommazahlen enthält. Die Gleitkommazahlen werden
   in entsprechende Arrays h[] und pl[] vom Typ double[] eingelesen.
   Etwaige Fehler- oder Warnmeldungen (Schlüsselwörter "error:" oder
   "warning:") werden auf der Konsole ausgegeben. Für die Häuser ist die
   Besonderheit zu beachten, dass das erste Haus in h[1] und nicht in h[0],
   allgemeiner das n.Haus in h[n] (und nicht in h[n-1]) geschrieben wird.
   Die Arrays h[] bzw. pl[] müssen vom aufrufenden Programm bereits erzeugt
   worden sein, und zwar mindestens von den Dimensionen 13 bzw. 10.
   @param in BufferedReader, der den Input enthält
   @param h Array für die Hauspositionen, mindestens double[13]
   @param pl Array für die Planetenpositionen, mindestens double[10]
   @return Wahr, falls die Konvertierung erfolgreich war.
   */
   public static boolean analyzeString( BufferedReader in, double[] h, double[] pl ) {
          int offset;
          String line;
          boolean planets = false, houses = false;
// Ergebnis analysieren
          try {
          while ((line = in.readLine()) != null) {
            if ((offset = line.indexOf("error:")) >= 0) {
              System.out.println( line );
              continue;
              }
            if ((offset = line.indexOf("warning:")) >= 0) {
              System.out.println( line );
              continue;
              }
            if ((offset = line.indexOf("planets:")) >= 0)
              if (parseDoubleList(line.substring(offset+8),pl) != 10) return false;
              else planets = true;
            if ((offset = line.indexOf("houses:")) >= 0) {
              if (parseDoubleList(line.substring(offset+7),h) != 12) return false;
              else {
                houses = true;
// Rechts-Shift der Häuser
                for( int i = 11; i >= 0; i--) h[i+1] = h[i];
                h[0] = 0.;
                }
              }
            }
           }
          catch( IOException e1) {
          return false;
          }
          return planets & houses;
         }

/** Sortieren eines Arrays von langen Ganzzahlen. Der
    Sortieralgorithmus wird hier für
    den elementaren Datentyp <em>long</em> explizit codiert,
    da die benötigte Methode Arrays.sort() erst ab JDK 1.2
    zur Verfügung steht, was der Verwendung in "mittelalten
    Browsern", z.B. Internet Explorer 5.5, im Wege steht.
    Es wäre stattdessen möglich gewesen,
    einen bereits seit JDK 1.0 bestehenden Sortieralgorithmus für
    generische Objekte zu verwenden. Die Instanziierung der
    entsprechenden Wrapperobjekte ( Long() ), die für den generischen
    Algorithmus erforderlich wäre, kostet wertvolle Rechenzeit. Durch
    Ausformulieren des Algorithmus für den Typ long lässt sich
    diese Zeit sparen.
    @param a Array von langen Ganzzahlen
*/
   public static void sort(long a[]) {
        sort(a, 0, a.length-1);
    }

/** Sortieren eines Array-Teils.
    Die Methode wird in dieser Signatur zur Implementierung
    des QuickSort-Algorithmus verwendet.
    @param a Array von langen Ganzzahlen
    @param lo0 Untergrenze des zu sortierenden Bereichs von a
    @param hi0 Obergrenze des zu sortierenden Bereichs von a
    */
    public static void sort(long a[], int lo0, int hi0) {

    long T, pivot;
      int lo = lo0;
      int hi = hi0;

        if (lo >= hi) {
        return;
    }
        else if( lo == hi - 1 ) {
            if (a[lo] > a[hi]) {
                T = a[lo];
                a[lo] = a[hi];
                a[hi] = T;
            }
            return;
    }


          pivot = a[(lo + hi) / 2];
        a[(lo + hi) / 2] = a[hi];
        a[hi] = pivot;

        while( lo < hi ) {
            while (a[lo] <= pivot && lo < hi) lo++;
            while (pivot <= a[hi] && lo < hi ) hi--;
            if( lo < hi ) {
                T = a[lo];
                a[lo] = a[hi];
                a[hi] = T;
            }

      }

      a[hi0] = a[hi];
      a[hi] = pivot;
        sort(a, lo0, lo-1);
        sort(a, hi+1, hi0);
    }

  /** Einen Array von doppeltgenauen Gleitkommazahlen in eine
  komma-separierte Liste verwandeln. Die Zahlen werden auf vier Nachkommastellen
  gerundet.
  @param x Der Array
  @param i0 Index des ersten Arrayelements, das in die Liste gesetzt werden soll
  @param i1 Index des letzten Arrayelements, das in die Liste gesetzt werden soll
  @return String, der die Liste */
  public static String doubleList( double[] x, int i0, int i1) {
    String list = new String();
    for (int i=i0; i < i1; i++) {
      list += Double.toString(Math.floor(x[i]*10000d+.5d)/10000d);
      if (i < x.length - 1) list += ",";
      }
    return list;
    }

  /** Einen Array von doppeltgenauen Gleitkommazahlen in eine
  komma-separierte Liste verwandeln. Die Zahlen werden auf vier Nachkommastellen
  gerundet. Der Array wird ab dem Element mit dem Index i0 bis zum Ende gewandelt.
  @param x Der Array
  @param i0 Index des ersten Arrayelements, das in die Liste gesetzt werden soll
  @return Liste */
  public static String doubleList( double[] x, int i0) {
    return doubleList( x, i0, x.length );
    }

  /** Einen Array von doppeltgenauen Gleitkommazahlen in eine
  komma-separierte Liste verwandeln. Die Zahlen werden auf vier Nachkommastellen
  gerundet.
  @param x Der Array
  @return Liste */
  public static String doubleList( double[] x ) {
    return doubleList(x,0,x.length);
    }

  /** Aus einer komma-separierten Liste ein Array von doppeltgenauen
  Gleitkommazahlen ableiten.
  @param in Eingabestring. Beispiel: "1.23e5,1.23,0.5,,"
  @param out Ergebnis. Wegen der Wertübergabe muss der Array bereits vom
  Aufrufer initialisiert worden sein. Es werden solange Werte in den
  Ergebnis-Array übertragen, bis das letzte Element der Ergebnisliste
  oder das letzte Element im Eingabestring erreicht ist, oder bis ein
  Konvertierungsfehler beim Interpretieren eines String-Teils als
  Gleitkommazahl auftritt.
  @return Die Anzahl der erfolgreich vom Eingabestring in die Liste
  übertragenen Werte
  Das Beispiel ergibt die Liste [1.23e5,1.23,0.5,0,0]
  */
  public static int parseDoubleList(String in, double[] out) {
    int i=0,lastOffset = 0,nextIndex = -1;
    if (in == null) return 0;
    if ((out == null) || (out.length == 0)) return 0;
    do {
      nextIndex = in.indexOf(',',lastOffset);
      if (in.charAt(lastOffset) != ',')
        try {
          out[i] = Double.valueOf(
            (nextIndex >= 0) ? in.substring(lastOffset,nextIndex) :
                               in.substring(lastOffset) ).doubleValue();
          i++;
          }
        catch(NumberFormatException ex) {
          return i;
         }
      else
        out[i++] = 0.; // der Fall ",,"
      if (i == out.length) return i;
      if (nextIndex < 0) return i;
      lastOffset = nextIndex + 1;
      } while (true);
  }

  /** Aus einem Eingabestring Zeit und Ort ermitteln.
    @param in Eingabestring. Folgende Arten von Eingaben sind
    beispielsweise möglich:
    <ul>
    <li><code>19.2.1964 22:25 7E16 52N20</code>
    <li><code>19.2.1964 7h 7E16'12" 52N19'46</code>
    <li><code>19.2.1964 7E16' 22h25m 52N19'46"</code>
    </ul>
    <div style="left-margin:1cm">
    Die Reihenfolge der einzelnen Parameter Datum, Zeit, Länge, Breite ist also
    beliebig.
    </div>
    <p>Es wäre bequem möglich, an dieser Stelle mit regulären
    Ausdrücken zu arbeiten. Da hierfür in Java jedoch eine separate
    Bibliothek benötigt wird und die Umwandlung auch auf dem Client
    erfolgen muss (Load klein halten!), wurde die Interpretation des
    Zeit/Ort-Strings hier ausprogrammiert.
    <p>Ausserdem gilt es, den von Microsoft hingeworfenen Java-
    Fehdehandschuh aufzunehmen und die besonders stumpfsinnige
    Herausforderung anzunehmen, dass die Funktion - wie dieses ganze
    Applet - unter Java 1.1.3 noch lauffähig sein muss, damit es
    auch mit dem Internet Explorer funktioniert. Dies schliesst
    auch so elementare Methoden wie beispielsweise StringBuffer.replace() aus.
  @param tp Ergebnis - ein Array von drei Gleitkommazahlen mit
    folgenden Inhalten:
    <p>
    <table border align="center">
    <tr><td>0</td><td>Julianisches Datum</td></tr>
    <tr><td>1</td><td>Ö;stliche Länge</td></tr>
    <tr><td>2</td><td>Nördliche Breite</td></tr>
    </table>
    @return Wahr, wenn das Lesen erfolgreich ausgeführt werden konnte.
  */
 public static boolean parseTimePlace(String in, double[] tp) {
    StringBuffer sb = new StringBuffer("");
    boolean finished = false,
            isDigit = false,
            rc;
    double pow60[] = { 1., 60., 3600. };
    double ut = 0.;
    int signLon = 0, signLat = 0, day = 1, month = 1, year = 2000;
    int index = 0, i = 0, j = 0, numbers[] = { 0,0,0 }, length = 0;
    char spec = ' ';

    if (in == null) return false;

    StringBuffer ib = new StringBuffer( in );

// String "now" hat Sonderbedeutung
    if (in.equals("now")) { tp[0] = thisjd();
                            tp[1] = DEFAULT_LON;
                            tp[2] = DEFAULT_LAT;
                            return true; }

// Wenn das Format aus dem Cookie kommt, können Leerzeichen durch
// den String "xspx" und Umbrüche durch "xcrx" codiert sein
    while ( (i = ib.toString().indexOf("xcrx")) >= 0 ) {
      sb.setLength(0);
      sb.append(ib.toString().substring(i+4));
      ib.setLength(i);
      ib.append("\n");
      ib.append(sb.toString());
      }
    while ( (i = ib.toString().indexOf("xspx")) >= 0 ) {
      sb.setLength(0);
      sb.append(ib.toString().substring(i+4));
      ib.setLength(i);
      ib.append(" ");
      ib.append(sb.toString());
      }

// Es interessiert im Cookie-Fall nur der Abschnitt zwischen
// dem Code-Wort "timePlace" und dem nächsten Umbruch
    i = ib.toString().indexOf("timePlace");
    if (i >= 0) {
      j = ib.toString().indexOf( "\n", i );
      ib = new StringBuffer( ib.toString().substring( i+9, j ));
      }

// Leerzeichen am Schluss "trimmen"
    j = ib.length() - 1;
    while ((j>=0) && (" \t\n".indexOf(ib.charAt(j))>=0)) j--;
    if (j <= 0) return false;
    ib.setLength(j+1);

// Hauptschleife
    i = 0;
    sb.setLength(0);
    while ( index < ib.length() ) {
      finished = false;
      if ( (digit.indexOf(ib.charAt(index)) >= 0) || (ib.charAt(index)=='-') )
        {
        isDigit = true;
        sb.append(ib.charAt(index));
        }
      else
        {
        isDigit = false;
        if ("hms".indexOf(ib.charAt(index)) >= 0) {
          spec = 't';
          }
        else if ("WEwe".indexOf(ib.charAt(index)) >= 0) {
          spec = 'l';
          signLon = ( "Ww".indexOf(ib.charAt(index)) >= 0) ? -1 : 1 ;
          }
        else if ("NSn".indexOf(ib.charAt(index)) >= 0) {
          spec = 'b';
          signLat = ( "Nn".indexOf(ib.charAt(index)) >= 0) ? 1 : -1 ;
          }
        else if (ib.charAt(index) == '.') {
          while ((index + 1 <= ib.length()) && (ib.charAt(index+1) == ' ')) index++;
          if ("tlb".indexOf(spec) < 0) spec = 'k';
          }
        else if (ib.charAt(index) == ':') {
          if (spec == ' ') spec = 't';
          while ((index + 1 <= ib.length()) && (ib.charAt(index+1) == ' ')) index++;
          }
        else if (" ,\t\n".indexOf(ib.charAt(index)) >= 0) {
          finished = true;
// Innerhalb eines "Komplexes" wie h/m/s oder E'" oder N'" ist ein
// Leerzeichen auch möglich, ohne den Komplex zu beenden.
          if ("tlb".indexOf(spec) >= 0 ) {
// Vorspulen bis zum nächsten Trenner
            for (j = index + 1; (j < ib.length()) && (" ,\t\n0123456789".indexOf(ib.charAt(j)) >=0); j++ ) ;
            if (j < ib.length()) {
                if ( (spec == 't' && "ms:".indexOf(ib.charAt(j)) >= 0) ||
                     (spec == 'b' && "'\"NnS".indexOf(ib.charAt(j)) >= 0) ||
                     (spec == 'l' && "'\"EeWw".indexOf(ib.charAt(j)) >= 0) ) {
                finished = false;
                index++;
                continue;
                }
              }
            }
          }
         else if (ib.charAt(index) == '\"') {
           finished = true;
           }
        }


      if ( index + 1 == ib.length() ) finished = true;

      if ( !isDigit || finished ) {
       if (sb.length() != 0) {
         numbers[i] = Integer.parseInt(sb.toString());
         sb.setLength(0);
         i++;
         }
       }

      index++;

// Ein Teilausdruck wurde abgeschlossen - Ergebnis übertragen:
      if (finished) {
        if (i == 0) continue;
        switch(spec) {
           case 'l':
             tp[1] = 0;
             for (j = 0; j < i; j++)
               tp[1] += ((double) numbers[j] ) / pow60[j];
             tp[1] *= signLon;
             break;
           case 'b':
             tp[2] = 0;
             for (j = 0; j < i; j++)
               tp[2] += ((double) numbers[j] ) / pow60[j];
             tp[2] *= signLat;
             break;
           case 'k':
             day   =  ( i >= 0 ) ? numbers[0] : 0;
             month =  ( i >= 1 ) ? numbers[1] : 0;
             year  =  ( i >= 2 ) ? numbers[2] : 0;
             break;
           case 't':
             ut = 0.;
             for (j = 0; j < i; j++)
               ut += ((double) numbers[j] ) / pow60[j];
             break;
           }
// Initialisieren für das Interpretieren des nächsten Teilausdrucks
         i = 0;
         sb.setLength(0);
         numbers[0] = numbers[1] = numbers[2] = 0;
         continue;
        }
      }  // Ende Hauptschleife


// Abschliessend Julianisches Datum errechnen
       if ( (day != 0) && (month != 0) && (year != 0) ) {
         tp[0] = julianDate(year, month, day, ut);
         rc = true;
         }
       else {
         rc = false;
         }
      return rc;
  }

/** Zeit und Ort in einen lesbaren String verwandeln.
    @param jd Julianisches Datum
    @param lon Geographische Länge (östlich ist positiv)
    @param lat Geographische Breite (nördlich ist positiv)
    @return String, der Datum im Kalenderformat, Zeit, Länge und
    Breite im Sexagesimalsystem enthält
     */
  public static String renderTimePlace( double jd, double lon, double lat) {
    double[] tp = new double[3];
    tp[0] = jd;
    tp[1] = lon;
    tp[2] = lat;
    return renderTimePlace( tp );
    }

/** wie {@link #renderTimePlace(double,double,double)}, jedoch mit
Strings als Importparametern. F&uumlr den Aufruf mit Javascript. */
  public String renderTimePlace( String jdS, String lonS, String latS) {
    return renderTimePlace( Double.valueOf(jdS).doubleValue(),
                            Double.valueOf(lonS).doubleValue(),
                            Double.valueOf(latS).doubleValue());
    }


/** Zeit und Ort in einen lesbaren String verwandeln.
    @param jd Julianisches Datum
    @param lon Geographische Länge (östlich ist positiv)
    @param lat Geographische Breite (nördlich ist positiv)
    @param format Lange Ganzzahl mit Formatierungsanweisungen für die
    Sexagesimalzahlen, z.B. {@link #SEC SEC} für Sekundengenauigkeit
    oder {@link #NO_ROUND NO_ROUND} zum Unterlassen des Rundens.
    @return String, der Datum im Kalenderformat, Zeit, Länge und
    Breite im Sexagesimalsystem enthält */
  public static String renderTimePlace( double jd, double lon, double lat, long format) {
    double[] tp = new double[3];
    tp[0] = jd;
    tp[1] = lon;
    tp[2] = lat;
    return renderTimePlace( tp, format );
    }

/** Zeit und Ort in einen lesbaren String verwandeln.
    @param tp Array von drei Gleitkommazahlen: tp[0] enthält
    das Julianische Datum, tp[1] die Länge und tp[2] die Breite
    @return String, der Datum im Kalenderformat, Zeit, Länge und
    Breite im Sexagesimalsystem enthält */
  public static String renderTimePlace( double[] tp ) {
    return renderTimePlace( tp, 0 );
    }

/** Zeit und Ort in einen lesbaren String verwandeln.
    @param tp Array von drei Gleitkommazahlen: tp[0] enthält
    das Julianische Datum, tp[1] die Länge und tp[2] die Breite
    @param format Lange Ganzzahl mit Formatierungsanweisungen für die
    Sexagesimalzahlen, z.B. {@link #SEC SEC} für Sekundengenauigkeit
    oder {@link #NO_ROUND NO_ROUND} zum Unterlassen des Rundens.
    @return String, der Datum im Kalenderformat, Zeit, Länge und
    Breite im Sexagesimalsystem enthält */
  public static String renderTimePlace( double[] tp, long format ) {
    int[] ymd = calendarDate(tp[0]);
    StringBuffer sb = new StringBuffer("");
    sb.append(ymd[2] + "." + ymd[1] + "." + ymd[0] + " ");
    if (ymd[2] < 10)   sb.insert(0, ' ');
    if (ymd[1] < 10)   sb.insert(3, ' ');
    if (ymd[0] < 1000) sb.insert(6, ' ');
    if (ymd[0] < 100)  sb.insert(6, ' ');
    if (ymd[0] < 10)   sb.insert(6, ' ');
    sb.append(dms((tp[0]-.5-Math.floor(tp[0]-.5))*24,TIME | format) + " ");
    sb.append(dms(tp[1],LONG | format) + " ");
    sb.append(dms(tp[2],LAT | format ) + " ");
    return sb.toString();
    }


}   // class Calculator


Zum Seitenanfang Lizenzbedingungen Der Quellcode wird mit dem GNU source-highlight 1.7 dargestellt