001 package edu.nrao.sss.model.source; 002 003 import java.net.MalformedURLException; 004 import java.net.URL; 005 import java.util.Date; 006 007 import javax.xml.bind.annotation.XmlAttribute; 008 import javax.xml.bind.annotation.XmlElement; 009 import javax.xml.bind.annotation.XmlType; 010 011 import edu.nrao.sss.astronomy.StokesParameter; 012 import edu.nrao.sss.measure.FluxDensity; 013 import edu.nrao.sss.measure.FrequencyRange; 014 import edu.nrao.sss.measure.TimeInterval; 015 import edu.nrao.sss.util.Identifiable; 016 017 /** 018 * The brightness of an astronomical source. 019 * <p> 020 * <b>Version Info:</b> 021 * <table style="margin-left:2em"> 022 * <tr><td>$Revision: 1709 $</td></tr> 023 * <tr><td>$Date: 2008-11-14 11:22:37 -0700 (Fri, 14 Nov 2008) $</td></tr> 024 * <tr><td>$Author: dharland $</td></tr> 025 * </table></p> 026 * 027 * @author David M. Harland 028 * @since 2006-04-04 029 */ 030 @XmlType 031 public abstract class SourceBrightness 032 implements Identifiable, Cloneable, Comparable<SourceBrightness> 033 { 034 static URL DEFAULT_URL; 035 static 036 { 037 try { DEFAULT_URL = new URL("http://www.nrao.edu/brokenLink.html"); } 038 catch (MalformedURLException ex) { DEFAULT_URL = null; } 039 } 040 041 //IDENTIFICATION 042 @XmlAttribute 043 private long id; 044 045 BrightnessDistribution distributionType; 046 StokesParameter polarization; 047 private TimeInterval validTime; 048 FrequencyRange validFrequency; 049 FluxDensity peakFluxDensity; 050 FluxDensity totalFluxDensity; 051 double majorAxisDiameter; 052 double minorAxisDiameter; 053 double positionAngle; 054 double limbDarkening; 055 URL brightnessFile; 056 057 private VlaFluxObservation vlaObservation; 058 059 /** 060 * Returns a source brightness instance for the distribution of the given type. 061 * 062 * @param type the brightness distribution for which a source brightness 063 * instance is desired. 064 * 065 * @return a new source brightness of the given type. 066 */ 067 public static SourceBrightness createBrightness(BrightnessDistribution type) 068 { 069 switch(type) 070 { 071 case CLEAN_COMPONENTS_FILE: return new CleanFileBrightness(); 072 case DISK: return new DiskBrightness(); 073 case FITS_FILE: return new FitsFileBrightness(); 074 case GAUSSIAN: return new GaussianBrightness(); 075 case POINT: return new PointBrightness(); 076 case UNKNOWN: return new UnknownBrightness(); 077 default: return new UnknownBrightness(); //SourceBrightness(); 078 } 079 } 080 081 /** 082 * Creates a pair of source brightnesses based on {@code fluxRec}. 083 * <p> 084 * The returned array will have exactly two brightnesses. The element 085 * at index zero will hold a brightness for left circular polarization; 086 * the element at index one will hold a brightness for right circular 087 * polarization.</p> 088 * 089 * @param fluxRec an observation of the flux of a source performed by the VLA. 090 * 091 * @return a pair of source brightnesses based on {@code fluxRec}. 092 */ 093 public static SourceBrightness[] createBrightnesses(VlaFluxObservation fluxRec) 094 { 095 SourceBrightness[] result = fluxRec.makeBrightnesses(); 096 097 for (SourceBrightness sb : result) 098 sb.vlaObservation = fluxRec; 099 100 return result; 101 } 102 103 /** Creates a new instance. */ 104 SourceBrightness() 105 { 106 validTime = new TimeInterval(); 107 validFrequency = new FrequencyRange(); 108 peakFluxDensity = new FluxDensity(); 109 totalFluxDensity = new FluxDensity(); 110 111 initialize(); 112 } 113 114 /** Initializes the instance variables of this class. */ 115 private void initialize() 116 { 117 id = Identifiable.UNIDENTIFIED; 118 119 distributionType = BrightnessDistribution.UNKNOWN; 120 polarization = StokesParameter.getDefault(); 121 majorAxisDiameter = 0.0; 122 minorAxisDiameter = 0.0; 123 positionAngle = 0.0; 124 limbDarkening = 0.0; 125 brightnessFile = DEFAULT_URL; 126 } 127 128 /** 129 * Resets this brightness to its initial state. A reset brightness 130 * has the same state as a new brightness. 131 */ 132 public void reset() 133 { 134 initialize(); 135 136 validTime.reset(); 137 validFrequency.reset(); 138 peakFluxDensity.reset(); 139 totalFluxDensity.reset(); 140 141 vlaObservation = null; 142 } 143 144 //============================================================================ 145 // IDENTIFICATION 146 //============================================================================ 147 148 /* (non-Javadoc) 149 * @see edu.nrao.sss.model.util.Identifiable#getId() 150 */ 151 public Long getId() 152 { 153 return id; 154 } 155 156 @SuppressWarnings("unused") 157 private void setId(Long id) 158 { 159 this.id = id; 160 } 161 162 /** 163 * Resets this brightness's ID 164 * to a value that represents the unidentified state. 165 */ 166 void clearId() 167 { 168 this.id = Identifiable.UNIDENTIFIED; 169 } 170 171 /** 172 * Returns the distribution type of this brightness. 173 * 174 * @return the distribution type of this brightness. 175 */ 176 public BrightnessDistribution getDistributionType() 177 { 178 return distributionType; 179 } 180 181 /** 182 * Returns the polarization of this source brightness. 183 * @return the polarization of this source brightness. 184 */ 185 public StokesParameter getPolarization() 186 { 187 return polarization; 188 } 189 190 //============================================================================ 191 // TIME, FREQUENCY, & FLUX DENSITY 192 //============================================================================ 193 194 /** 195 * Sets the interval of time for which this brightness is valid. 196 * <p> 197 * If {@code interval} is <i>null</i>, it will be treated as 198 * an non-null interval of zero length.</p> 199 * 200 * @param interval the interval of time for which this brightness is valid. 201 */ 202 public void setValidTime(TimeInterval interval) 203 { 204 if (interval == null) 205 validTime.reset(); 206 else 207 validTime = interval; 208 } 209 210 /** 211 * Returns the interval of time for which this brightness is valid. 212 * <p> 213 * The return value is guaranteed to be non-null. It is 214 * also the range that is held internally by this source 215 * brightness, so any changes made to the returned range 216 * will be reflected in this object.</p> 217 * 218 * @return the interval of time for which this brightness is valid. 219 */ 220 public TimeInterval getValidTime() 221 { 222 return validTime; 223 } 224 225 /** 226 * Returns <i>true</i> if this brightness is valid for 227 * the given point in time. 228 * 229 * @param time the point in time to be checked. 230 */ 231 public boolean isValidFor(Date time) 232 { 233 return validTime.contains(time); 234 } 235 236 /** 237 * Returns the frequency range for which this source brightness 238 * is valid. 239 * <p> 240 * The return value is guaranteed to be non-null. It is 241 * also the range that is held internally by this source 242 * brightness, so any changes made to the returned range 243 * will be reflected in this object.</p> 244 * 245 * @return the valid frequency range for this source velocity. 246 */ 247 public FrequencyRange getValidFrequency() 248 { 249 return validFrequency; 250 } 251 252 /** 253 * Returns the peak flux density of this source brightness. 254 * <p> 255 * The returned value is guaranteed to be non-null. 256 * It is also the peak flux density that is held internally by this 257 * source brightness, so any changes made to the returned 258 * flux density will be reflected in this object.</p> 259 * 260 * @return the peak flux density of this source brightness. 261 */ 262 public FluxDensity getPeakFluxDensity() 263 { 264 return peakFluxDensity; 265 } 266 267 /** 268 * Returns the total flux density of this source brightness. 269 * <p> 270 * The returned value is guaranteed to be non-null. 271 * It is also the total flux density that is held internally by this 272 * source brightness, so any changes made to the returned 273 * flux density will be reflected in this object.</p> 274 * 275 * @return the total flux density of this source brightness. 276 */ 277 public FluxDensity getTotalFluxDensity() 278 { 279 return totalFluxDensity; 280 } 281 282 //============================================================================ 283 // AREA ATTRIBUTES 284 //============================================================================ 285 286 /** 287 * Returns the diameter of the major axis of this source brightness in A.U. 288 * @return the diameter of the major axis of this source brightness in A.U. 289 */ 290 public double getMajorAxisDiameter() 291 { 292 return majorAxisDiameter; 293 } 294 295 /** 296 * Returns the diameter of the minor axis of this source brightness in A.U. 297 * @return the diameter of the minor axis of this source brightness in A.U. 298 */ 299 public double getMinorAxisDiameter() 300 { 301 return minorAxisDiameter; 302 } 303 304 /** 305 * Returns the position angle of this source brightness in degrees. 306 * The position angle is based on the major axis. A north-south orientation 307 * of the major axis is taken to be zero degrees. Positive angles are 308 * measured eastward from north. 309 * 310 * @return the position angle of this source brightness in degrees. 311 */ 312 public double getPositionAngle() 313 { 314 return positionAngle; 315 } 316 317 /** 318 * Returns the limb darkening of this source brightness. 319 * <p> 320 * The value returned is a measure of shape.</p> 321 * 322 * @return the limb darkening of this source brightness. 323 */ 324 public double getLimbDarkening() 325 { 326 return limbDarkening; 327 } 328 329 //============================================================================ 330 // FILE 331 //============================================================================ 332 333 /** 334 * Returns <i>true</i> if this source brightness if based on the contents of 335 * a file, <i>false</i> otherwise. 336 * 337 * @return <i>true</i> if this source brightness if based on the contents of 338 * a file. 339 */ 340 public boolean isFileBased() 341 { 342 return distributionType.equals(BrightnessDistribution.FITS_FILE) || 343 distributionType.equals(BrightnessDistribution.CLEAN_COMPONENTS_FILE); 344 } 345 346 /** 347 * Returns the file upon which this source brightness is based, if any. 348 * If this source is not file based, a phony URL will be returned. 349 * 350 * @return the file upon which this source brightness is based, if any. 351 */ 352 public URL getBrightnessFile() 353 { 354 return brightnessFile; 355 } 356 357 //============================================================================ 358 // OBSERVATIONS 359 //============================================================================ 360 361 /** 362 * Returns text that represents the observational data upon which this 363 * brightness is based. If there is no such data, the empty string 364 * (<tt>""</tt>) will be returned. 365 * 366 * @return text representation of an observation of this brightness. 367 */ 368 public String getObservation() 369 { 370 return vlaObservation == null ? "" : vlaObservation.toString(); 371 } 372 373 /** 374 * Returns the VLA flux data upon which this brightness is based. 375 * @return the VLA flux data upon which this brightness is based. 376 */ 377 @XmlElement 378 public VlaFluxObservation getVlaObservation() 379 { 380 return vlaObservation; 381 } 382 383 /** Here for persistence mechanisms, such as Hibernate & JAXB. */ 384 @SuppressWarnings("unused") 385 private void setVlaObservation(VlaFluxObservation newObs) 386 { 387 vlaObservation = newObs; 388 } 389 390 //============================================================================ 391 // 392 //============================================================================ 393 394 /** 395 * Returns a source brightness that is equal to this one. 396 * <p> 397 * If anything goes wrong during the cloning procedure, 398 * a {@code RuntimeException} will be thrown.</p> 399 */ 400 @Override 401 public SourceBrightness clone() 402 { 403 SourceBrightness clone = null; 404 405 try 406 { 407 //This line takes care of the primitive & immutable fields properly 408 clone = (SourceBrightness)super.clone(); 409 410 //We do NOT want the clone to have the same ID as the original. 411 //The ID is here for the persistence layer; it is in charge of 412 //setting IDs. To help it, we put the clone's ID in the uninitialized 413 //state. 414 clone.id = Identifiable.UNIDENTIFIED; 415 416 //This works because getValidFrequency already returns a clone 417 clone.validFrequency = this.getValidFrequency(); 418 clone.setValidTime(this.getValidTime().clone()); 419 clone.peakFluxDensity = this.peakFluxDensity.clone(); 420 clone.brightnessFile = new URL(this.brightnessFile.toString()); 421 422 if (this.vlaObservation != null) 423 clone.vlaObservation = this.vlaObservation.clone(); 424 } 425 catch (Exception ex) 426 { 427 throw new RuntimeException(ex); 428 } 429 430 return clone; 431 } 432 433 /** Returns <i>true</i> if {@code o} is equal to this source brightness. */ 434 @Override 435 public boolean equals(Object o) 436 { 437 //Quick exit if o is this 438 if (o == this) 439 return true; 440 441 //Quick exit if o is null 442 if (o == null) 443 return false; 444 445 //Quick exit if classes are different 446 if (!o.getClass().equals(this.getClass())) 447 return false; 448 449 SourceBrightness other = (SourceBrightness)o; 450 451 return other.distributionType.equals(this.distributionType) && 452 other.polarization.equals(this.polarization) && 453 other.majorAxisDiameter == this.majorAxisDiameter && 454 other.minorAxisDiameter == this.minorAxisDiameter && 455 other.positionAngle == this.positionAngle && 456 other.limbDarkening == this.limbDarkening && 457 other.brightnessFile.toString() 458 .equals(this.brightnessFile.toString()) && 459 other.peakFluxDensity.equals(this.peakFluxDensity) && 460 other.validFrequency.equals(this.validFrequency) && 461 other.validTime.equals(this.validTime) && 462 objectsAreEqual(other.vlaObservation, this.vlaObservation); 463 } 464 465 private boolean objectsAreEqual(Object thisOne, Object thatOne) 466 { 467 return (thisOne == null) ? (thatOne == null) : thisOne.equals(thatOne); 468 } 469 470 /** Returns a hash code value for this source brightness. */ 471 @Override 472 public int hashCode() 473 { 474 //Taken from the Effective Java book by Joshua Bloch. 475 //The constants 17 & 37 are arbitrary & carry no meaning. 476 int result = 17; 477 478 result = 37 * result + distributionType.hashCode(); 479 result = 37 * result + polarization.hashCode(); 480 result = 37 * result + validTime.hashCode(); 481 result = 37 * result + validFrequency.hashCode(); 482 result = 37 * result + brightnessFile.toString().hashCode(); 483 result = 37 * result + peakFluxDensity.hashCode(); 484 485 if (vlaObservation != null) 486 result = 37 * result + vlaObservation.hashCode(); 487 488 result = 37 * result + new Double(majorAxisDiameter).hashCode(); 489 result = 37 * result + new Double(minorAxisDiameter).hashCode(); 490 result = 37 * result + new Double(positionAngle).hashCode(); 491 result = 37 * result + new Double(limbDarkening).hashCode(); 492 493 return result; 494 } 495 496 /** 497 * Compares this brightness to {@code other} for order. 498 * <p> 499 * One brightness is deemed to be "less than" the other if it has 500 * a valid frequency range that is less than that of the other. 501 * In the case that two brighnesses have the same valid frequency range, 502 * the valid time range is used as a tie-breaker. If these are the 503 * same other attributes are compared until a difference is found. 504 * If none are found, the return value is zero.</p> 505 * 506 * @param other the brightness to which this one is compared. 507 * 508 * @return a negative integer, zero, or a positive integer as this brightness 509 * is less than, equal to, or greater than the other brightness. 510 */ 511 public int compareTo(SourceBrightness other) 512 { 513 //First compare the valid frequency ranges 514 int answer = this.validFrequency.compareTo(other.validFrequency); 515 if (answer != 0) 516 return answer; 517 518 //Valid time intervals 519 answer = this.validTime.compareTo(other.validTime); 520 if (answer != 0) 521 return answer; 522 523 //Polarization 524 answer = this.polarization.compareTo(other.polarization); 525 if (answer != 0) 526 return answer; 527 528 //Distribution type 529 answer = this.distributionType.compareTo(other.distributionType); 530 if (answer != 0) 531 return answer; 532 533 //Flux density 534 answer = this.peakFluxDensity.compareTo(other.peakFluxDensity); 535 if (answer != 0) 536 return answer; 537 538 //File name 539 answer = this.brightnessFile.toString() 540 .compareTo(other.brightnessFile.toString()); 541 if (answer != 0) 542 return answer; 543 544 answer = (int)Math.signum(this.majorAxisDiameter - other.majorAxisDiameter); 545 if (answer != 0) 546 return answer; 547 548 answer = (int)Math.signum(this.minorAxisDiameter - other.minorAxisDiameter); 549 if (answer != 0) 550 return answer; 551 552 answer = (int)Math.signum(this.positionAngle - other.positionAngle); 553 if (answer != 0) 554 return answer; 555 556 answer = (int)Math.signum(this.limbDarkening - other.limbDarkening); 557 if (answer != 0) 558 return answer; 559 560 //No differences found 561 return 0; 562 } 563 }