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