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.toDegrees;
017import static java.lang.Math.toRadians;
018import static org.shredzone.commons.suncalc.util.ExtendedMath.equatorialToHorizontal;
019import static org.shredzone.commons.suncalc.util.ExtendedMath.refraction;
020
021import org.shredzone.commons.suncalc.param.Builder;
022import org.shredzone.commons.suncalc.param.GenericParameter;
023import org.shredzone.commons.suncalc.param.LocationParameter;
024import org.shredzone.commons.suncalc.param.TimeParameter;
025import org.shredzone.commons.suncalc.util.BaseBuilder;
026import org.shredzone.commons.suncalc.util.JulianDate;
027import org.shredzone.commons.suncalc.util.Sun;
028import org.shredzone.commons.suncalc.util.Vector;
029
030/**
031 * Calculates the position of the sun.
032 */
033public class SunPosition {
034
035    private final double azimuth;
036    private final double altitude;
037    private final double trueAltitude;
038    private final double distance;
039
040    private SunPosition(double azimuth, double altitude, double trueAltitude, double distance) {
041        this.azimuth = (toDegrees(azimuth) + 180.0) % 360.0;
042        this.altitude = toDegrees(altitude);
043        this.trueAltitude = toDegrees(trueAltitude);
044        this.distance = distance;
045    }
046
047    /**
048     * Starts the computation of {@link SunPosition}.
049     *
050     * @return {@link Parameters} to set.
051     */
052    public static Parameters compute() {
053        return new SunPositionBuilder();
054    }
055
056    /**
057     * Collects all parameters for {@link SunPosition}.
058     */
059    public interface Parameters extends
060            GenericParameter<Parameters>,
061            LocationParameter<Parameters>,
062            TimeParameter<Parameters>,
063            Builder<SunPosition> {
064    }
065
066    /**
067     * Builder for {@link SunPosition}. Performs the computations based on the parameters,
068     * and creates a {@link SunPosition} object that holds the result.
069     */
070    private static class SunPositionBuilder extends BaseBuilder<Parameters> implements Parameters {
071        @Override
072        public SunPosition execute() {
073            JulianDate t = getJulianDate();
074
075            double lw = toRadians(-getLongitude());
076            double phi = toRadians(getLatitude());
077            Vector c = Sun.position(t);
078            double h = t.getGreenwichMeanSiderealTime() - lw - c.getPhi();
079            Vector horizontal = equatorialToHorizontal(h, c.getTheta(), c.getR(), phi);
080            double hRef = refraction(horizontal.getTheta());
081
082            return new SunPosition(horizontal.getPhi(),
083                            horizontal.getTheta() + hRef,
084                            horizontal.getTheta(),
085                            horizontal.getR());
086        }
087    }
088
089    /**
090     * The visible sun altitude above the horizon, in degrees.
091     * <p>
092     * {@code 0.0} means the sun's center is at the horizon, {@code 90.0} at the zenith
093     * (straight over your head). Atmospheric refraction is taken into account.
094     *
095     * @see #getTrueAltitude()
096     */
097    public double getAltitude() {
098        return altitude;
099    }
100
101    /**
102     * The true sun altitude above the horizon, in degrees.
103     * <p>
104     * {@code 0.0} means the sun's center is at the horizon, {@code 90.0} at the zenith
105     * (straight over your head).
106     *
107     * @see #getAltitude()
108     * @since 2.3
109     */
110    public double getTrueAltitude() {
111        return trueAltitude;
112    }
113
114    /**
115     * Sun azimuth, in degrees, north-based.
116     * <p>
117     * This is the direction along the horizon, measured from north to east. For example,
118     * {@code 0.0} means north, {@code 135.0} means southeast, {@code 270.0} means west.
119     */
120    public double getAzimuth() {
121        return azimuth;
122    }
123
124    /**
125     * Sun's distance, in kilometers.
126     */
127    public double getDistance() {
128        return distance;
129    }
130
131    @Override
132    public String toString() {
133        StringBuilder sb = new StringBuilder();
134        sb.append("SunPosition[azimuth=").append(azimuth);
135        sb.append("°, altitude=").append(altitude);
136        sb.append("°, true altitude=").append(trueAltitude);
137        sb.append("°, distance=").append(distance).append(" km]");
138        return sb.toString();
139    }
140
141}