001 package edu.nrao.sss.model.project.scan; 002 003 import javax.xml.bind.annotation.XmlTransient; 004 005 import edu.nrao.sss.measure.Angle; 006 import edu.nrao.sss.measure.TimeDuration; 007 008 /** 009 * A position used by a {@link PointingScan}. 010 * <p> 011 * This position may be used as either an offset or an absolute position 012 * by its containing client. Likewise, the interpretation 013 * of the position angles (as declinations, altitudes, or latitudes, 014 * for example) is left to the clients of this class.</p> 015 * <p> 016 * <b>CVS Info:</b> 017 * <table style="margin-left:2em"> 018 * <tr><td>$Revision: 1494 $</td></tr> 019 * <tr><td>$Date: 2008-08-14 13:51:17 -0600 (Thu, 14 Aug 2008) $</td></tr> 020 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 021 * </table></p> 022 * 023 * @author David M. Harland 024 * @since 2006-07-10 025 */ 026 public class PointingPosition 027 implements Cloneable 028 { 029 private static final ScanTimeType DEFAULT_TYPE = 030 ScanTimeType.ON_SOURCE_SIDEREAL; 031 032 private Angle longitude; 033 private Angle latitude; 034 private TimeDuration timeAtPosition; 035 private ScanTimeType durationType; 036 037 /** Creates a new instance. */ 038 public PointingPosition() 039 { 040 longitude = new Angle(); 041 latitude = new Angle(); 042 timeAtPosition = new TimeDuration(); 043 durationType = DEFAULT_TYPE; 044 } 045 046 //============================================================================ 047 // PERSISTED PROPERTIES 048 //============================================================================ 049 050 /** 051 * Sets the latitudinal angle of this position. 052 * <p> 053 * If {@code latitude} is <i>null</i>, it will be treated as 054 * an non-null latitude of size zero.</p> 055 * 056 * @param latitude the latitudinal angle of this position. 057 */ 058 public void setLatitude(Angle latitude) 059 { 060 this.latitude = (latitude == null) ? new Angle() : latitude; 061 } 062 063 /** 064 * Returns the latitudinal angle of this position. 065 * <p> 066 * The returned value is guaranteed to be non-null. It is also the 067 * angle that is held internally by this position, so any changes 068 * made to the returned angle will be reflected in this object.</p> 069 * 070 * @return the latitudinal angle of this position. 071 */ 072 public Angle getLatitude() 073 { 074 return latitude; 075 } 076 077 /** 078 * Sets the longitudinal angle of this position. 079 * <p> 080 * If {@code longitude} is <i>null</i>, it will be treated as 081 * an non-null longitude of size zero.</p> 082 * 083 * @param longitude the longitudinal angle of this position. 084 */ 085 public void setLongitude(Angle longitude) 086 { 087 this.longitude = (longitude == null) ? new Angle() : longitude; 088 } 089 090 /** 091 * Returns the longitudinal angle of this position. 092 * <p> 093 * The returned value is guaranteed to be non-null. It is also the 094 * angle that is held internally by this position, so any changes 095 * made to the returned angle will be reflected in this object.</p> 096 * 097 * @return the longitudinal angle of this position. 098 */ 099 public Angle getLongitude() 100 { 101 return longitude; 102 } 103 104 /** 105 * Sets the amount of time that the containing scan should spend at this 106 * position. 107 * <p> 108 * If {@code duration} is <i>null</i>, it will be treated as 109 * an non-null duration of size zero.</p> 110 * 111 * @param duration the amount of time that should be spent at this position. 112 */ 113 public void setTimeAtPosition(TimeDuration duration) 114 { 115 timeAtPosition = (duration == null) ? new TimeDuration() : duration; 116 } 117 118 /** 119 * Returns the amount of time that the containing scan should spend at this 120 * position. 121 * <p> 122 * The returned value is guaranteed to be non-null. It is also the 123 * duration that is held internally by this offset, so any changes 124 * made to the returned duration will be reflected in this object.</p> 125 * 126 * @return the amount of time that should be spent at this position. 127 */ 128 public TimeDuration getTimeAtPosition() 129 { 130 return timeAtPosition; 131 } 132 133 /** 134 * Sets a new duration type for this position. 135 * 136 * @param newType 137 * the new duration type for this position. 138 * If this value is <i>null</i>, the default duration type of 139 * <tt>ON_SOURCE_SIDEREAL</tt> will be used. 140 * This value must have its <tt>isDuration()</tt> method 141 * return <i>true</i>. If it does not, an 142 * <tt>IllegalArgumentException</tt> is thrown. 143 * 144 * @throws IllegalArgumentException 145 * if {@code newType} is not a duration type. 146 */ 147 public void setDurationType(ScanTimeType newType) 148 { 149 if (newType == null) 150 newType = DEFAULT_TYPE; 151 152 if (!newType.isDuration()) 153 throw new IllegalArgumentException("newType '" + newType + 154 "' must be a duration type."); 155 156 durationType = newType; 157 } 158 159 /** 160 * Returns the type of duration used by this position. 161 * This method helps clients interpret the value returned by 162 * {@link #getTimeAtPosition()}. 163 * <p> 164 * The returned type will be non-null and its <tt>isDuration()</tt> method 165 * will always return <i>true</i>.</p> 166 * 167 * @return 168 * the type of duration used by this position. 169 */ 170 public ScanTimeType getDurationType() 171 { 172 return durationType; 173 } 174 175 //============================================================================ 176 // CONVENIENCE METHODS 177 //============================================================================ 178 179 /** 180 * A convenience method for setting the time spent at this position. 181 * This position will <i>not</i> hold a reference to <tt>newSpec</tt>. 182 * Instead, this method will use its 183 * {@link ScanTimeSpecification#getTimeType() getTimeType()} and 184 * {@link ScanTimeSpecification#getDuration() getDuration()} methods 185 * to set the time at position and duration type of this object. 186 * 187 * @param newSpec 188 * the provider of the time at position and duration type values for 189 * this position. A value of <i>null</i> will result in a 190 * {@code NullPointerException}. 191 * 192 * @throws IllegalArgumentException 193 * if the time type held by {@code newSpec} is not a duration type. 194 */ 195 @XmlTransient 196 public void setTimeSpec(ScanTimeSpecification newSpec) 197 { 198 setDurationType(newSpec.getTimeType()); 199 setTimeAtPosition(newSpec.getDuration()); 200 } 201 202 /** 203 * A convenience method for fetching the time spent at this position. 204 * The returned object is guaranteed to be non-null. 205 * It is not referenced internally by this position, so any changes made 206 * to it by clients will not affect this object. 207 * 208 * @return 209 * the time at offset and duration type of this offset. 210 */ 211 public ScanTimeSpecification getTimeSpec() 212 { 213 ScanTimeSpecification spec = new ScanTimeSpecification(); 214 215 spec.set(durationType, timeAtPosition); 216 217 return spec; 218 } 219 220 //============================================================================ 221 // 222 //============================================================================ 223 224 /** 225 * Returns a text representation of this pointing position. 226 * The returned string is of the form <i>longitude,latitude,timeAtPosition</i>. 227 */ 228 @Override 229 public String toString() 230 { 231 StringBuilder buff = new StringBuilder(); 232 233 buff.append(longitude); 234 buff.append(',').append(latitude); 235 buff.append(',').append(timeAtPosition); 236 buff.append(',').append(durationType); 237 238 return buff.toString(); 239 } 240 241 /** 242 * Returns a position that is a copy of this one. 243 * <p> 244 * If anything goes wrong during the cloning procedure, 245 * a {@code RuntimeException} will be thrown.</p> 246 */ 247 @Override 248 public PointingPosition clone() 249 { 250 PointingPosition clone = null; 251 252 try 253 { 254 clone = (PointingPosition)super.clone(); 255 256 clone.latitude = this.latitude.clone(); 257 clone.longitude = this.longitude.clone(); 258 clone.timeAtPosition = this.timeAtPosition.clone(); 259 } 260 catch (Exception ex) 261 { 262 throw new RuntimeException(ex); 263 } 264 265 return clone; 266 } 267 268 /** Returns <i>true</i> if {@code o} is equal to this position. */ 269 @Override 270 public boolean equals(Object o) 271 { 272 //Quick exit if o is null 273 if (o == null) 274 return false; 275 276 //Quick exit if o is this object 277 if (o == this) 278 return true; 279 280 //Quick exit if classes are different 281 if (!o.getClass().equals(this.getClass())) 282 return false; 283 284 //A safe cast if we got this far 285 PointingPosition other = (PointingPosition)o; 286 287 //Compare attributes 288 return 289 this.durationType.equals(other.durationType) && 290 this.latitude.equals(other.latitude) && 291 this.longitude.equals(other.longitude) && 292 this.timeAtPosition.equals(other.timeAtPosition); 293 } 294 295 /** Returns a hash code value for this position. */ 296 @Override 297 public int hashCode() 298 { 299 //Taken from the Effective Java book by Joshua Bloch. 300 //The constants 17 & 37 are arbitrary & carry no meaning. 301 int result = 17; 302 303 result = 37 * result + latitude.hashCode(); 304 result = 37 * result + longitude.hashCode(); 305 result = 37 * result + durationType.hashCode(); 306 result = 37 * result + timeAtPosition.hashCode(); 307 308 return result; 309 } 310 }