001 package edu.nrao.sss.model.source.sort; 002 003 import java.util.Comparator; 004 import java.util.Date; 005 import java.util.HashMap; 006 import java.util.Map; 007 008 //import org.apache.log4j.Logger; 009 010 import static edu.nrao.sss.measure.FluxDensityUnits.JANSKY; 011 012 import edu.nrao.sss.measure.FluxDensity; 013 import edu.nrao.sss.model.resource.ReceiverBand; 014 import edu.nrao.sss.model.source.Source; 015 import edu.nrao.sss.model.source.SourceBrightness; 016 import edu.nrao.sss.model.source.SourceBrightnessFilter; 017 import edu.nrao.sss.model.source.SourceCatalogEntry; 018 import edu.nrao.sss.model.source.Subsource; 019 import edu.nrao.sss.sort.DoubleSortKey; 020 import edu.nrao.sss.sort.Orderable; 021 import edu.nrao.sss.sort.SortOrder; 022 023 /** 024 * Compares {@link Source sources} based on their flux density in a given 025 * receiver band. 026 * <p> 027 * <b>Version Info:</b> 028 * <table style="margin-left:2em"> 029 * <tr><td>$Revision$</td></tr> 030 * <tr><td>$Date$</td></tr> 031 * <tr><td>$Author$ (last person to modify)</td></tr> 032 * </table></p> 033 * 034 * @author David M. Harland 035 * @since 2008-10-10 036 */ 037 public class SourceFluxSortKey 038 implements Orderable, Comparator<SourceCatalogEntry> 039 { 040 //private static final Logger log = Logger.getLogger(SourceFluxSortKey.class); 041 042 private ReceiverBand receiverBand; 043 private Date queryTime; 044 private SourceBrightnessFilter sbFilter; 045 private JanskySorter sorter; 046 047 //See class SourceProximitySortKey in this pkg for reasons why we 048 //have three maps. 049 050 //This map will hold only those SCEs that have the same name as another 051 //SCE in the sort. If there are n SCEs with the same name, one will 052 //be in the fast map and the other n-1 will be here. 053 private Map<SourceCatalogEntry, Double> janskiesSlow; 054 055 //The first SCE seen with a given name will be held here. 056 //The SCE name is used as the key. 057 private Map<String, Double> janskiesFast; 058 059 //This map tell us whether the proximity for a given SCE is in the fast 060 //or slow map. For a given name, the SCE returned as a value by this 061 //map will have its proximity in the fast map. Any other SCEs with that 062 //name will have their proximities held in the slow map. 063 private Map<String, SourceCatalogEntry> fastMapEntries; 064 065 /** 066 * Creates a new key for sorting sources by their flux density at a 067 * particular receiver band. 068 */ 069 public SourceFluxSortKey() 070 { 071 janskiesSlow = new HashMap<SourceCatalogEntry, Double>(); 072 janskiesFast = new HashMap<String, Double>(); 073 fastMapEntries = new HashMap<String, SourceCatalogEntry>(); 074 075 sorter = new JanskySorter(); 076 receiverBand = ReceiverBand.EVLA_X; 077 queryTime = new Date(); 078 079 sbFilter = new SourceBrightnessFilter(); 080 sbFilter.setTime(queryTime); 081 sbFilter.setFrequency(receiverBand.getWidestRange()); 082 } 083 084 /** 085 * Returns the flux density this sort key did, or would, use for 086 * the given source for the receiver band specified in 087 * {@link #setReceiverBand(ReceiverBand)}. 088 * 089 * @param sce 090 * a celestial source. 091 * 092 * @return 093 * the flux density this sort key did, or would, use for 094 * the given source. 095 */ 096 public FluxDensity getFluxDensity(SourceCatalogEntry sce) 097 { 098 Double janskies = getPrefetchedJanksies(sce); 099 100 if (janskies == null) 101 janskies = fetchAndUpdateFlux(sce); 102 103 FluxDensity answer = new FluxDensity(janskies == null ? "0.0" : janskies.toString()); 104 105 return answer.normalize(); 106 } 107 108 private Double getPrefetchedJanksies(SourceCatalogEntry sce) 109 { 110 Double janskies = null; 111 112 String name = sce.getName(); 113 SourceCatalogEntry fastMapEntry = fastMapEntries.get(name); 114 115 //If the returned SCE is not null, we've seen an SCE w/ the same name 116 //as the one sent to this method. It may or may not be the same object. 117 if (fastMapEntry != null) 118 { 119 //If fastMapEntry equals sce, it means its flux value is 120 //in the fast map. If not equal, then either it is in the 121 //slow map, or it is the first time we've seen this sce 122 //(which happens to have the same name as one or more that 123 //we've already seen). 124 janskies = fastMapEntry.equals(sce) ? janskiesFast.get(name) 125 : janskiesSlow.get(sce); 126 } 127 128 return janskies; 129 } 130 131 /** 132 * Sets the receiver band for which flux densities will be sought 133 * from sources. 134 * 135 * @param newBand a receiver band. 136 */ 137 public void setReceiverBand(ReceiverBand newBand) 138 { 139 //Do not accept null position 140 if (newBand == null) 141 throw new IllegalArgumentException("Receiver band may not be null."); 142 143 receiverBand = newBand; 144 queryTime = new Date(); 145 146 sbFilter.setTime(queryTime); 147 sbFilter.setFrequency(receiverBand.getWidestRange()); 148 149 compareTime = 0L; 150 151 janskiesFast.clear(); 152 janskiesSlow.clear(); 153 fastMapEntries.clear(); 154 } 155 156 /* (non-Javadoc) 157 * @see edu.nrao.sss.sort.Orderable#setOrder(edu.nrao.sss.sort.SortOrder) 158 */ 159 public void setOrder(SortOrder newOrder) 160 { 161 sorter.setOrder(newOrder); 162 } 163 164 /* (non-Javadoc) 165 * @see edu.nrao.sss.sort.Orderable#getOrder() 166 */ 167 public SortOrder getOrder() 168 { 169 return sorter.getOrder(); 170 } 171 172 private long compareTime = 0L; 173 174 /* (non-Javadoc) 175 * @see Comparator#compare(Object, Object) 176 */ 177 public int compare(SourceCatalogEntry sce1, SourceCatalogEntry sce2) 178 { 179 long now = System.currentTimeMillis(); 180 181 //If we haven't done a comparison in awhile, it's probably a new sort 182 if ((now - compareTime) > 1000L) //1 second 183 { 184 queryTime = new Date(); 185 sbFilter.setTime(queryTime); 186 janskiesFast.clear(); 187 janskiesSlow.clear(); 188 fastMapEntries.clear(); 189 } 190 191 compareTime = now; 192 193 Double janskies1 = getPrefetchedJanksies(sce1); 194 Double janskies2 = getPrefetchedJanksies(sce2); 195 196 if (janskies1 == null) 197 janskies1 = fetchAndUpdateFlux(sce1); 198 199 if (janskies2 == null) 200 janskies2 = fetchAndUpdateFlux(sce2); 201 202 return sorter.compare(janskies1, janskies2); 203 } 204 205 private Double fetchAndUpdateFlux(SourceCatalogEntry sce) 206 { 207 Double answer = 0.0; 208 209 //Use the time to get source from what might be a lookup table 210 Source source = sce.get(queryTime); 211 212 //Source could be null if queryTime is earlier than earliest time 213 //in table. Put at end of the line. 214 if (source != null) 215 { 216 //Find source's largest flux density, across all subsources, that 217 //are valid at queryTime and that overlap the receiverBand's range. 218 for (Subsource ss : source.getSubsources()) 219 { 220 for (SourceBrightness sb : ss.getBrightnesses()) 221 { 222 if (sbFilter.allows(sb)) 223 { 224 double sbJanskies = sb.getPeakFluxDensity().toUnits(JANSKY).doubleValue(); 225 226 if (sbJanskies > answer) 227 answer = sbJanskies; 228 } 229 } 230 } 231 } 232 233 //Use the fast map if it does not already have an entry for an 234 //SCE with this name. Otherwise use the slow map. 235 String name = sce.getName(); 236 if (!fastMapEntries.containsKey(name)) 237 { 238 fastMapEntries.put(name, sce); 239 janskiesFast.put(name, answer); 240 } 241 else 242 { 243 janskiesSlow.put(sce, answer); 244 } 245 246 return answer; 247 } 248 249 private class JanskySorter extends DoubleSortKey implements Comparator<Double> 250 { 251 public int compare(Double d1, Double d2) 252 { 253 return compareObjects(d1, d2); 254 } 255 } 256 }