001/*
002 * Shredzone Commons - suncalc
003 *
004 * Copyright (C) 2017 Richard "Shred" Körber
005 *   http://commons.shredzone.org
006 *
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
013 */
014package org.shredzone.commons.suncalc;
015
016import static java.lang.Math.*;
017import static org.shredzone.commons.suncalc.util.ExtendedMath.equatorialToHorizontal;
018import static org.shredzone.commons.suncalc.util.ExtendedMath.refraction;
019
020import org.shredzone.commons.suncalc.param.Builder;
021import org.shredzone.commons.suncalc.param.GenericParameter;
022import org.shredzone.commons.suncalc.param.LocationParameter;
023import org.shredzone.commons.suncalc.param.TimeParameter;
024import org.shredzone.commons.suncalc.util.BaseBuilder;
025import org.shredzone.commons.suncalc.util.JulianDate;
026import org.shredzone.commons.suncalc.util.Moon;
027import org.shredzone.commons.suncalc.util.Vector;
028
029/**
030 * Calculates the position of the moon.
031 */
032public class MoonPosition {
033
034    private final double azimuth;
035    private final double altitude;
036    private final double trueAltitude;
037    private final double distance;
038    private final double parallacticAngle;
039
040    private MoonPosition(double azimuth, double altitude, double trueAltitude, double distance, double parallacticAngle) {
041        this.azimuth = (toDegrees(azimuth) + 180.0) % 360.0;
042        this.altitude = toDegrees(altitude);
043        this.trueAltitude = toDegrees(trueAltitude);
044        this.distance = distance;
045        this.parallacticAngle = toDegrees(parallacticAngle);
046    }
047
048    /**
049     * Starts the computation of {@link MoonPosition}.
050     *
051     * @return {@link Parameters} to set.
052     */
053    public static Parameters compute() {
054        return new MoonPositionBuilder();
055    }
056
057    /**
058     * Collects all parameters for {@link MoonPosition}.
059     */
060    public interface Parameters extends
061            GenericParameter<Parameters>,
062            LocationParameter<Parameters>,
063            TimeParameter<Parameters>,
064            Builder<MoonPosition> {
065    }
066
067    /**
068     * Builder for {@link MoonPosition}. Performs the computations based on the
069     * parameters, and creates a {@link MoonPosition} object that holds the result.
070     */
071    private static class MoonPositionBuilder extends BaseBuilder<Parameters> implements Parameters {
072        @Override
073        public MoonPosition execute() {
074            if (!hasLocation()) {
075                throw new IllegalArgumentException("Geolocation is missing.");
076            }
077
078            JulianDate t = getJulianDate();
079
080            double phi = getLatitudeRad();
081            double lambda = getLongitudeRad();
082
083            Vector mc = Moon.position(t);
084            double h = t.getGreenwichMeanSiderealTime() + lambda - mc.getPhi();
085
086            Vector horizontal = equatorialToHorizontal(h, mc.getTheta(), mc.getR(), phi);
087
088            double hRef = refraction(horizontal.getTheta());
089
090            double pa = atan2(sin(h), tan(phi) * cos(mc.getTheta()) - sin(mc.getTheta()) * cos(h));
091
092            return new MoonPosition(
093                    horizontal.getPhi(),
094                    horizontal.getTheta() + hRef,
095                    horizontal.getTheta(),
096                    mc.getR(),
097                    pa);
098        }
099    }
100
101    /**
102     * Moon altitude above the horizon, in degrees.
103     * <p>
104     * {@code 0.0} means the moon's center is at the horizon, {@code 90.0} at the zenith
105     * (straight over your head). Atmospheric refraction is taken into account.
106     *
107     * @see #getTrueAltitude()
108     */
109    public double getAltitude() {
110        return altitude;
111    }
112
113    /**
114     * The true moon altitude above the horizon, in degrees.
115     * <p>
116     * {@code 0.0} means the moon's center is at the horizon, {@code 90.0} at the zenith
117     * (straight over your head).
118     *
119     * @see #getAltitude()
120     * @since 3.8
121     */
122    public double getTrueAltitude() {
123        return trueAltitude;
124    }
125
126    /**
127     * Moon azimuth, in degrees, north-based.
128     * <p>
129     * This is the direction along the horizon, measured from north to east. For example,
130     * {@code 0.0} means north, {@code 135.0} means southeast, {@code 270.0} means west.
131     */
132    public double getAzimuth() {
133        return azimuth;
134    }
135
136    /**
137     * Distance to the moon in kilometers.
138     */
139    public double getDistance() {
140        return distance;
141    }
142
143    /**
144     * Parallactic angle of the moon, in degrees.
145     */
146    public double getParallacticAngle() {
147        return parallacticAngle;
148    }
149
150    @Override
151    public String toString() {
152        StringBuilder sb = new StringBuilder();
153        sb.append("MoonPosition[azimuth=").append(azimuth);
154        sb.append("°, altitude=").append(altitude);
155        sb.append("°, true altitude=").append(trueAltitude);
156        sb.append("°, distance=").append(distance);
157        sb.append(" km, parallacticAngle=").append(parallacticAngle);
158        sb.append("°]");
159        return sb.toString();
160    }
161
162}