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.Duration; 020import java.time.Instant; 021import java.time.LocalDate; 022import java.time.LocalDateTime; 023import java.time.LocalTime; 024import java.time.ZoneId; 025import java.time.ZonedDateTime; 026import java.time.temporal.ChronoUnit; 027import java.util.Objects; 028 029import edu.umd.cs.findbugs.annotations.Nullable; 030import org.shredzone.commons.suncalc.param.GenericParameter; 031import org.shredzone.commons.suncalc.param.LocationParameter; 032import org.shredzone.commons.suncalc.param.TimeParameter; 033import org.shredzone.commons.suncalc.param.WindowParameter; 034 035/** 036 * A base implementation of {@link LocationParameter}, {@link TimeParameter}, and 037 * {@link WindowParameter}. 038 * <p> 039 * For internal use only. 040 * 041 * @param <T> 042 * Type of the final builder 043 */ 044@SuppressWarnings("unchecked") 045public class BaseBuilder<T> implements GenericParameter<T>, LocationParameter<T>, 046 TimeParameter<T>, WindowParameter<T>, Cloneable { 047 048 private @Nullable Double lat = null; 049 private @Nullable Double lng = null; 050 private double elevation = 0.0; 051 private ZonedDateTime dateTime = ZonedDateTime.now(); 052 private boolean reverse = false; 053 private Duration duration = Duration.ofDays(365L); 054 055 @Override 056 public T on(ZonedDateTime dateTime) { 057 this.dateTime = Objects.requireNonNull(dateTime, "dateTime"); 058 return (T) this; 059 } 060 061 @Override 062 public T on(LocalDateTime dateTime) { 063 Objects.requireNonNull(dateTime, "dateTime"); 064 return on(ZonedDateTime.of(dateTime, this.dateTime.getZone())); 065 } 066 067 @Override 068 public T on(LocalDate date) { 069 Objects.requireNonNull(date, "date"); 070 return on(ZonedDateTime.of(date, LocalTime.MIDNIGHT, dateTime.getZone())); 071 } 072 073 @Override 074 public T on(Instant instant) { 075 Objects.requireNonNull(instant, "instant"); 076 return on(ZonedDateTime.ofInstant(instant, dateTime.getZone())); 077 } 078 079 @Override 080 public T on(int year, int month, int date, int hour, int minute, int second) { 081 return on(ZonedDateTime.of(year, month, date, hour, minute, second, 0, dateTime.getZone())); 082 } 083 084 @Override 085 public T now() { 086 return on(ZonedDateTime.now(dateTime.getZone())); 087 } 088 089 @Override 090 public T plusDays(int days) { 091 return on(dateTime.plusDays(days)); 092 } 093 094 @Override 095 public T midnight() { 096 return on(dateTime.truncatedTo(ChronoUnit.DAYS)); 097 } 098 099 @Override 100 public T timezone(ZoneId tz) { 101 Objects.requireNonNull(tz, "tz"); 102 on(dateTime.withZoneSameLocal(tz)); 103 return (T) this; 104 } 105 106 @Override 107 public T latitude(double lat) { 108 if (lat < -90.0 || lat > 90.0) { 109 throw new IllegalArgumentException("Latitude out of range, -90.0 <= " + lat + " <= 90.0"); 110 } 111 this.lat = lat; 112 return (T) this; 113 } 114 115 @Override 116 public T longitude(double lng) { 117 if (lng < -180.0 || lng > 180.0) { 118 throw new IllegalArgumentException("Longitude out of range, -180.0 <= " + lng + " <= 180.0"); 119 } 120 this.lng = lng; 121 return (T) this; 122 } 123 124 @Override 125 public T elevation(double h) { 126 this.elevation = max(h, 0.0); 127 return (T) this; 128 } 129 130 public T limit(Duration duration) { 131 Objects.requireNonNull(duration, "duration"); 132 this.duration = duration; 133 if (duration.isNegative()) { 134 reverse(); 135 } 136 return (T) this; 137 } 138 139 public T reverse() { 140 reverse = true; 141 return (T) this; 142 } 143 144 public T forward() { 145 reverse = false; 146 return (T) this; 147 } 148 149 @Override 150 public T sameTimeAs(TimeParameter<?> t) { 151 if (! (t instanceof BaseBuilder)) { 152 throw new IllegalArgumentException("Cannot read the TimeParameter"); 153 } 154 this.dateTime = ((BaseBuilder<?>) t).dateTime; 155 return (T) this; 156 } 157 158 @Override 159 public T sameLocationAs(LocationParameter<?> l) { 160 if (! (l instanceof BaseBuilder)) { 161 throw new IllegalArgumentException("Cannot read the LocationParameter"); 162 } 163 BaseBuilder<?> origin = (BaseBuilder<?>) l; 164 this.lat = origin.lat; 165 this.lng = origin.lng; 166 this.elevation = origin.elevation; 167 return (T) this; 168 } 169 170 @Override 171 public T sameWindowAs(WindowParameter<?> w) { 172 if (! (w instanceof BaseBuilder)) { 173 throw new IllegalArgumentException("Cannot read the WindowParameter"); 174 } 175 BaseBuilder<?> origin = (BaseBuilder<?>) w; 176 this.duration = origin.duration; 177 this.reverse = origin.reverse; 178 return (T) this; 179 } 180 181 @Override 182 public T copy() { 183 try { 184 return (T) clone(); 185 } catch (CloneNotSupportedException ex) { 186 throw new RuntimeException(ex); // Should never be thrown anyway 187 } 188 } 189 190 /** 191 * Returns the longitude. 192 * 193 * @return Longitude, in degrees. 194 */ 195 public double getLongitude() { 196 if (lng == null) { 197 throw new IllegalStateException("longitude is not set"); 198 } 199 return lng; 200 } 201 202 /** 203 * Returns the latitude. 204 * 205 * @return Latitude, in degrees. 206 */ 207 public double getLatitude() { 208 if (lat == null) { 209 throw new IllegalStateException("latitude is not set"); 210 } 211 return lat; 212 } 213 214 /** 215 * Returns the longitude. 216 * 217 * @return Longitude, in radians. 218 */ 219 public double getLongitudeRad() { 220 return toRadians(getLongitude()); 221 } 222 223 /** 224 * Returns the latitude. 225 * 226 * @return Latitude, in radians. 227 */ 228 public double getLatitudeRad() { 229 return toRadians(getLatitude()); 230 } 231 232 /** 233 * Returns the elevation, in meters above sea level. 234 * 235 * @return Elevation, meters above sea level 236 */ 237 public double getElevation() { 238 return elevation; 239 } 240 241 /** 242 * Returns the {@link JulianDate} to be used. 243 * 244 * @return {@link JulianDate} 245 */ 246 public JulianDate getJulianDate() { 247 return new JulianDate(dateTime); 248 } 249 250 /** 251 * Returns {@code true} if a geolocation has been set. 252 * 253 * @since 3.9 254 */ 255 public boolean hasLocation() { 256 return lat != null && lng != null; 257 } 258 259 /** 260 * Unset the geolocation. 261 * 262 * @since 3.9 263 */ 264 public void clearLocation() { 265 lat = null; 266 lng = null; 267 } 268 269 /** 270 * Returns the duration of the time window. 271 * 272 * @since 3.11 273 */ 274 public Duration getDuration() { 275 if (reverse != duration.isNegative()) { 276 return duration.negated(); 277 } 278 return duration; 279 } 280 281}