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.*; 017 018import java.util.Calendar; 019import java.util.Date; 020import java.util.TimeZone; 021 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.param.TimeResultParameter; 026 027/** 028 * A base implementation of {@link LocationParameter} and {@link TimeParameter}. 029 * <p> 030 * For internal use only. 031 * 032 * @param <T> 033 * Type of the final builder 034 */ 035@SuppressWarnings("unchecked") 036public class BaseBuilder<T> implements GenericParameter<T>, LocationParameter<T>, 037 TimeParameter<T>, TimeResultParameter<T>, Cloneable { 038 039 private double lat = 0.0; 040 private double lng = 0.0; 041 private double height = 0.0; 042 private Calendar cal = createCalendar(); 043 private Unit unit = Unit.MINUTES; 044 045 @Override 046 public T on(int year, int month, int date) { 047 cal.clear(); 048 cal.set(year, month - 1, date); 049 return (T) this; 050 } 051 052 @Override 053 public T on(int year, int month, int date, int hour, int minute, int second) { 054 cal.clear(); 055 cal.set(year, month - 1, date, hour, minute, second); 056 return (T) this; 057 } 058 059 @Override 060 public T on(Date date) { 061 if (date == null) { //NOSONAR: safety null check 062 throw new NullPointerException(); 063 } 064 cal.setTime(date); 065 return (T) this; 066 } 067 068 @Override 069 public T on(Calendar calendar) { 070 if (calendar == null) { //NOSONAR: safety null check 071 throw new NullPointerException(); 072 } 073 on(calendar.getTime()); 074 timezone(calendar.getTimeZone()); 075 return (T) this; 076 } 077 078 @Override 079 public T plusDays(int days) { 080 cal.add(Calendar.DAY_OF_MONTH, days); 081 return (T) this; 082 } 083 084 @Override 085 public T today() { 086 now(); 087 midnight(); 088 return (T) this; 089 } 090 091 @Override 092 public T tomorrow() { 093 today(); 094 plusDays(1); 095 return (T) this; 096 } 097 098 @Override 099 public T now() { 100 return on(createCalendar()); 101 } 102 103 @Override 104 public T midnight() { 105 cal.set(Calendar.HOUR_OF_DAY, 0); 106 cal.set(Calendar.MINUTE, 0); 107 cal.set(Calendar.SECOND, 0); 108 cal.set(Calendar.MILLISECOND, 0); 109 return (T) this; 110 } 111 112 @Override 113 public T timezone(TimeZone tz) { 114 if (tz == null) { //NOSONAR: safety null check 115 throw new NullPointerException(); 116 } 117 cal.setTimeZone(tz); 118 return (T) this; 119 } 120 121 @Override 122 public T timezone(String id) { 123 return timezone(TimeZone.getTimeZone(id)); 124 } 125 126 @Override 127 public T utc() { 128 return timezone("UTC"); 129 } 130 131 @Override 132 public T localTime() { 133 return timezone(TimeZone.getDefault()); 134 } 135 136 @Override 137 public T at(double lat, double lng) { 138 latitude(lat); 139 longitude(lng); 140 return (T) this; 141 } 142 143 @Override 144 public T at(double[] coords) { 145 if (coords.length != 2 && coords.length != 3) { 146 throw new IllegalArgumentException("Array must contain 2 or 3 doubles"); 147 } 148 if (coords.length == 3) { 149 height(coords[2]); 150 } 151 return at(coords[0], coords[1]); 152 } 153 154 @Override 155 public T latitude(double lat) { 156 if (lat < -90.0 || lat > 90.0) { 157 throw new IllegalArgumentException("Latitude out of range, -90.0 <= " + lat + " <= 90.0"); 158 } 159 this.lat = lat; 160 return (T) this; 161 } 162 163 @Override 164 public T longitude(double lng) { 165 if (lng < -180.0 || lng > 180.0) { 166 throw new IllegalArgumentException("Longitude out of range, -180.0 <= " + lng + " <= 180.0"); 167 } 168 this.lng = lng; 169 return (T) this; 170 } 171 172 @Override 173 public T latitude(int d, int m, double s) { 174 return latitude(dms(d, m, s)); 175 } 176 177 @Override 178 public T longitude(int d, int m, double s) { 179 return longitude(dms(d, m, s)); 180 } 181 182 @Override 183 public T height(double h) { 184 this.height = max(h, 0.0); 185 return (T) this; 186 } 187 188 @Override 189 public T truncatedTo(Unit unit) { 190 if (unit == null) { //NOSONAR: safety null check 191 throw new NullPointerException(); 192 } 193 this.unit = unit; 194 return (T) this; 195 } 196 197 @Override 198 public T sameTimeAs(TimeParameter<?> t) { 199 if (! (t instanceof BaseBuilder)) { 200 throw new IllegalArgumentException("Cannot read the TimeParameter"); 201 } 202 this.cal = (Calendar) ((BaseBuilder<?>) t).cal.clone(); 203 return (T) this; 204 } 205 206 @Override 207 public T sameLocationAs(LocationParameter<?> l) { 208 if (! (l instanceof BaseBuilder)) { 209 throw new IllegalArgumentException("Cannot read the LocationParameter"); 210 } 211 BaseBuilder<?> origin = (BaseBuilder<?>) l; 212 this.lat = origin.lat; 213 this.lng = origin.lng; 214 this.height = origin.height; 215 return (T) this; 216 } 217 218 @Override 219 public T copy() { 220 try { 221 return (T) clone(); 222 } catch (CloneNotSupportedException ex) { 223 throw new RuntimeException(ex); // Should never be thrown anyway 224 } 225 } 226 227 /** 228 * Returns the longitude. 229 * 230 * @return Longitude, in degrees. 231 */ 232 public double getLongitude() { 233 return lng; 234 } 235 236 /** 237 * Returns the latitude. 238 * 239 * @return Latitude, in degrees. 240 */ 241 public double getLatitude() { 242 return lat; 243 } 244 245 /** 246 * Returns the longitude. 247 * 248 * @return Longitude, in radians. 249 */ 250 public double getLongitudeRad() { 251 return toRadians(lng); 252 } 253 254 /** 255 * Returns the latitude. 256 * 257 * @return Latitude, in radians. 258 */ 259 public double getLatitudeRad() { 260 return toRadians(lat); 261 } 262 263 /** 264 * Returns the height, in meters above sea level. 265 * 266 * @return Height, meters above sea level 267 */ 268 public double getHeight() { 269 return height; 270 } 271 272 /** 273 * Returns the {@link JulianDate} to be used. 274 * 275 * @return {@link JulianDate} 276 */ 277 public JulianDate getJulianDate() { 278 return new JulianDate((Calendar) cal.clone()); 279 } 280 281 /** 282 * Returns the {@link Unit} to truncate to. 283 * 284 * @return {@link Unit} 285 * @since 2.3 286 */ 287 public Unit getTruncatedTo() { 288 return unit; 289 } 290 291 /** 292 * Creates a new {@link Calendar} instance containing the current instant. 293 * <p> 294 * This method can be overriden on unit tests. 295 * 296 * @return {@link Calendar} instance 297 */ 298 protected Calendar createCalendar() { 299 return Calendar.getInstance(); 300 } 301 302 /** 303 * Converts dms to double. 304 * 305 * @param d 306 * Degrees. Sign is used for result. 307 * @param m 308 * Minutes. Sign is ignored. 309 * @param s 310 * Seconds and fractions. Sign is ignored. 311 * @return angle, in degrees 312 */ 313 private static double dms(int d, int m, double s) { 314 double sig = d < 0 ? -1.0 : 1.0; 315 return sig * ((abs(s) / 60.0 + abs(m)) / 60.0 + abs(d)); 316 } 317 318 @Override 319 protected Object clone() throws CloneNotSupportedException { 320 BaseBuilder<T> b = (BaseBuilder<T>) super.clone(); 321 b.cal = (Calendar) this.cal.clone(); 322 return b; 323 } 324 325}