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