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.*;
017
018import org.shredzone.commons.suncalc.param.Builder;
019import org.shredzone.commons.suncalc.param.GenericParameter;
020import org.shredzone.commons.suncalc.param.TimeParameter;
021import org.shredzone.commons.suncalc.util.BaseBuilder;
022import org.shredzone.commons.suncalc.util.JulianDate;
023import org.shredzone.commons.suncalc.util.Moon;
024import org.shredzone.commons.suncalc.util.Sun;
025import org.shredzone.commons.suncalc.util.Vector;
026
027/**
028 * Calculates the illumination of the moon.
029 */
030public class MoonIllumination {
031
032    private final double fraction;
033    private final double phase;
034    private final double angle;
035
036    private MoonIllumination(double fraction, double phase, double angle) {
037        this.fraction = fraction;
038        this.phase = phase;
039        this.angle = angle;
040    }
041
042    /**
043     * Starts the computation of {@link MoonIllumination}.
044     *
045     * @return {@link Parameters} to set.
046     */
047    public static Parameters compute() {
048        return new MoonIlluminationBuilder();
049    }
050
051    /**
052     * Collects all parameters for {@link MoonIllumination}.
053     */
054    public interface Parameters extends
055            GenericParameter<Parameters>,
056            TimeParameter<Parameters>,
057            Builder<MoonIllumination> {
058    }
059
060    /**
061     * Builder for {@link MoonIllumination}. Performs the computations based on the
062     * parameters, and creates a {@link MoonIllumination} object that holds the result.
063     */
064    private static class MoonIlluminationBuilder extends BaseBuilder<Parameters> implements Parameters {
065        @Override
066        public MoonIllumination execute() {
067            JulianDate t = getJulianDate();
068            Vector s = Sun.position(t);
069            Vector m = Moon.position(t);
070            double phi = PI - acos(m.dot(s) / (m.getR() * s.getR()));
071            Vector sunMoon = m.cross(s);
072            double angle = atan2(
073                    cos(s.getTheta()) * sin(s.getPhi() - m.getPhi()),
074                    sin(s.getTheta()) * cos(m.getTheta()) - cos(s.getTheta()) * sin(m.getTheta()) * cos(s.getPhi() - m.getPhi())
075            );
076
077            return new MoonIllumination(
078                            (1 + cos(phi)) / 2,
079                            toDegrees(phi * signum(sunMoon.getTheta())),
080                            toDegrees(angle));
081        }
082    }
083
084    /**
085     * Illuminated fraction. {@code 0.0} indicates new moon, {@code 1.0} indicates full
086     * moon.
087     */
088    public double getFraction() {
089        return fraction;
090    }
091
092    /**
093     * Moon phase. Starts at {@code -180.0} (new moon, waxing), passes {@code 0.0} (full
094     * moon) and moves toward {@code 180.0} (waning, new moon).
095     * <p>
096     * Note that for historical reasons, the range of this phase is different to the
097     * moon phase angle used in {@link MoonPhase}.
098     */
099    public double getPhase() {
100        return phase;
101    }
102
103    /**
104     * The angle of the moon illumination relative to earth. The moon is waxing if the
105     * angle is negative, and waning if positive.
106     * <p>
107     * By subtracting {@link MoonPosition#getParallacticAngle()} from {@link #getAngle()},
108     * one can get the zenith angle of the moons bright limb (anticlockwise). The zenith
109     * angle can be used do draw the moon shape from the observer's perspective (e.g. the
110     * moon lying on its back).
111     */
112    public double getAngle() {
113        return angle;
114    }
115
116    /**
117     * The closest {@link MoonPhase.Phase} that is matching the moon's angle.
118     *
119     * @return Closest {@link MoonPhase.Phase}
120     * @since 2.12
121     */
122    public MoonPhase.Phase getClosestPhase() {
123        return MoonPhase.Phase.toPhase(phase + 180.0);
124    }
125
126    @Override
127    public String toString() {
128        StringBuilder sb = new StringBuilder();
129        sb.append("MoonIllumination[fraction=").append(fraction);
130        sb.append(", phase=").append(phase);
131        sb.append("°, angle=").append(angle);
132        sb.append("°]");
133        return sb.toString();
134    }
135
136}