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.util;
015
016import static java.lang.Math.max;
017import static java.lang.Math.toRadians;
018
019import java.time.Instant;
020import java.time.LocalDate;
021import java.time.LocalDateTime;
022import java.time.LocalTime;
023import java.time.ZoneId;
024import java.time.ZonedDateTime;
025import java.time.temporal.ChronoUnit;
026import java.util.Objects;
027
028import org.shredzone.commons.suncalc.param.GenericParameter;
029import org.shredzone.commons.suncalc.param.LocationParameter;
030import org.shredzone.commons.suncalc.param.TimeParameter;
031
032/**
033 * A base implementation of {@link LocationParameter} and {@link TimeParameter}.
034 * <p>
035 * For internal use only.
036 *
037 * @param <T>
038 *            Type of the final builder
039 */
040@SuppressWarnings("unchecked")
041public class BaseBuilder<T> implements GenericParameter<T>, LocationParameter<T>,
042        TimeParameter<T>, Cloneable {
043
044    private double lat = 0.0;
045    private double lng = 0.0;
046    private double height = 0.0;
047    private ZonedDateTime dateTime = ZonedDateTime.now();
048
049    @Override
050    public T on(ZonedDateTime dateTime) {
051        this.dateTime = Objects.requireNonNull(dateTime, "dateTime");
052        return (T) this;
053    }
054
055    @Override
056    public T on(LocalDateTime dateTime) {
057        Objects.requireNonNull(dateTime, "dateTime");
058        return on(ZonedDateTime.of(dateTime, this.dateTime.getZone()));
059    }
060
061    @Override
062    public T on(LocalDate date) {
063        Objects.requireNonNull(date, "date");
064        return on(ZonedDateTime.of(date, LocalTime.MIDNIGHT, dateTime.getZone()));
065    }
066
067    @Override
068    public T on(Instant instant) {
069        Objects.requireNonNull(instant, "instant");
070        return on(ZonedDateTime.ofInstant(instant, dateTime.getZone()));
071    }
072
073    @Override
074    public T on(int year, int month, int date, int hour, int minute, int second) {
075        return on(ZonedDateTime.of(year, month, date, hour, minute, second, 0, dateTime.getZone()));
076    }
077
078    @Override
079    public T now() {
080        return on(ZonedDateTime.now(dateTime.getZone()));
081    }
082
083    @Override
084    public T plusDays(int days) {
085        return on(dateTime.plusDays(days));
086    }
087
088    @Override
089    public T midnight() {
090        return on(dateTime.truncatedTo(ChronoUnit.DAYS));
091    }
092
093    @Override
094    public T timezone(ZoneId tz) {
095        Objects.requireNonNull(tz, "tz");
096        on(dateTime.withZoneSameLocal(tz));
097        return (T) this;
098    }
099
100    @Override
101    public T latitude(double lat) {
102        if (lat < -90.0 || lat > 90.0) {
103            throw new IllegalArgumentException("Latitude out of range, -90.0 <= " + lat + " <= 90.0");
104        }
105        this.lat = lat;
106        return (T) this;
107    }
108
109    @Override
110    public T longitude(double lng) {
111        if (lng < -180.0 || lng > 180.0) {
112            throw new IllegalArgumentException("Longitude out of range, -180.0 <= " + lng + " <= 180.0");
113        }
114        this.lng = lng;
115        return (T) this;
116    }
117
118    @Override
119    public T height(double h) {
120        this.height = max(h, 0.0);
121        return (T) this;
122    }
123
124    @Override
125    public T sameTimeAs(TimeParameter<?> t) {
126        if (! (t instanceof BaseBuilder)) {
127            throw new IllegalArgumentException("Cannot read the TimeParameter");
128        }
129        this.dateTime = ((BaseBuilder<?>) t).dateTime;
130        return (T) this;
131    }
132
133    @Override
134    public T sameLocationAs(LocationParameter<?> l) {
135        if (! (l instanceof BaseBuilder)) {
136            throw new IllegalArgumentException("Cannot read the LocationParameter");
137        }
138        BaseBuilder<?> origin = (BaseBuilder<?>) l;
139        this.lat = origin.lat;
140        this.lng = origin.lng;
141        this.height = origin.height;
142        return (T) this;
143    }
144
145    @Override
146    public T copy() {
147        try {
148            return (T) clone();
149        } catch (CloneNotSupportedException ex) {
150            throw new RuntimeException(ex); // Should never be thrown anyway
151        }
152    }
153
154    /**
155     * Returns the longitude.
156     *
157     * @return Longitude, in degrees.
158     */
159    public double getLongitude() {
160        return lng;
161    }
162
163    /**
164     * Returns the latitude.
165     *
166     * @return Latitude, in degrees.
167     */
168    public double getLatitude() {
169        return lat;
170    }
171
172    /**
173     * Returns the longitude.
174     *
175     * @return Longitude, in radians.
176     */
177    public double getLongitudeRad() {
178        return toRadians(lng);
179    }
180
181    /**
182     * Returns the latitude.
183     *
184     * @return Latitude, in radians.
185     */
186    public double getLatitudeRad() {
187        return toRadians(lat);
188    }
189
190    /**
191     * Returns the height, in meters above sea level.
192     *
193     * @return Height, meters above sea level
194     */
195    public double getHeight() {
196        return height;
197    }
198
199    /**
200     * Returns the {@link JulianDate} to be used.
201     *
202     * @return {@link JulianDate}
203     */
204    public JulianDate getJulianDate() {
205        return new JulianDate(dateTime);
206    }
207
208}