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    }