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 distance;
037    private final double parallacticAngle;
038
039    private MoonPosition(double azimuth, double altitude, double distance, double parallacticAngle) {
040        this.azimuth = (toDegrees(azimuth) + 180.0) % 360.0;
041        this.altitude = toDegrees(altitude);
042        this.distance = distance;
043        this.parallacticAngle = toDegrees(parallacticAngle);
044    }
045
046    /**
047     * Starts the computation of {@link MoonPosition}.
048     *
049     * @return {@link Parameters} to set.
050     */
051    public static Parameters compute() {
052        return new MoonPositionBuilder();
053    }
054
055    /**
056     * Collects all parameters for {@link MoonPosition}.
057     */
058    public interface Parameters extends
059            GenericParameter<Parameters>,
060            LocationParameter<Parameters>,
061            TimeParameter<Parameters>,
062            Builder<MoonPosition> {
063    }
064
065    /**
066     * Builder for {@link MoonPosition}. Performs the computations based on the
067     * parameters, and creates a {@link MoonPosition} object that holds the result.
068     */
069    private static class MoonPositionBuilder extends BaseBuilder<Parameters> implements Parameters {
070        @Override
071        public MoonPosition execute() {
072            JulianDate t = getJulianDate();
073
074            double phi = getLatitudeRad();
075            double lambda = getLongitudeRad();
076
077            Vector mc = Moon.position(t);
078            double h = t.getGreenwichMeanSiderealTime() + lambda - mc.getPhi();
079
080            Vector horizontal = equatorialToHorizontal(h, mc.getTheta(), mc.getR(), phi);
081
082            double hRef = refraction(horizontal.getTheta());
083
084            double pa = atan2(sin(h), tan(phi) * cos(mc.getTheta()) - sin(mc.getTheta()) * cos(h));
085
086            return new MoonPosition(horizontal.getPhi(), horizontal.getTheta() + hRef, mc.getR(), pa);
087        }
088    }
089
090    /**
091     * Moon altitude above the horizon, in degrees.
092     * <p>
093     * {@code 0.0} means the moon's center is at the horizon, {@code 90.0} at the zenith
094     * (straight over your head).
095     */
096    public double getAltitude() {
097        return altitude;
098    }
099
100    /**
101     * Moon azimuth, in degrees, north-based.
102     * <p>
103     * This is the direction along the horizon, measured from north to east. For example,
104     * {@code 0.0} means north, {@code 135.0} means southeast, {@code 270.0} means west.
105     */
106    public double getAzimuth() {
107        return azimuth;
108    }
109
110    /**
111     * Distance to the moon in kilometers.
112     */
113    public double getDistance() {
114        return distance;
115    }
116
117    /**
118     * Parallactic angle of the moon, in degrees.
119     */
120    public double getParallacticAngle() {
121        return parallacticAngle;
122    }
123
124    @Override
125    public String toString() {
126        StringBuilder sb = new StringBuilder();
127        sb.append("MoonPosition[azimuth=").append(azimuth);
128        sb.append("°, altitude=").append(altitude);
129        sb.append("°, distance=").append(distance);
130        sb.append(" km, parallacticAngle=").append(parallacticAngle);
131        sb.append("°]");
132        return sb.toString();
133    }
134
135}