KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > prefuse > action > assignment > DataSizeAction


1 package prefuse.action.assignment;
2
3 import java.util.logging.Logger JavaDoc;
4
5 import prefuse.Constants;
6 import prefuse.data.tuple.TupleSet;
7 import prefuse.util.DataLib;
8 import prefuse.util.MathLib;
9 import prefuse.util.PrefuseLib;
10 import prefuse.visual.VisualItem;
11
12 /**
13  * <p>
14  * Assignment Action that assigns size values for a group of items based upon
15  * a data field. This action can be used to automatically vary item's on screen
16  * sizes proportionally to an underlying data value. Sizes can be assigned along
17  * a continuous scale, or can be binned into discrete size groups. Both 1D
18  * (length) and 2D (area) encodings are supported by this function.
19  * 2D is assumed by default; use the setIs2DArea method to change this.</p>
20  *
21  * <p>
22  * The size assignments for numerical data are continuous by default, but can
23  * be binned into a few discrete steps (see {@link #setBinCount(int)}).
24  * Quantitative data can also be sized on different numerical scales. The
25  * default scale is a linear scale (specified by
26  * {@link Constants#LINEAR_SCALE}), but logarithmic and square root scales can
27  * be used (specified by {@link Constants#LOG_SCALE} and
28  * {@link Constants#SQRT_SCALE} respectively. Finally, the scale can be broken
29  * into quantiles, reflecting the statistical distribution of the values rather
30  * than just the total data value range, using the
31  * {@link Constants#QUANTILE_SCALE} value. For the quantile scale to work, you
32  * also need to specify the number of bins to use (see
33  * {@link #setBinCount(int)}). This value will determine the number of
34  * quantiles that the data should be divided into.
35  * </p>
36  *
37  * <p>
38  * By default, the maximum size value is determined automatically from the
39  * data, faithfully representing the scale differences between data values.
40  * However, this can sometimes result in very large differences. For
41  * example, if the minimum data value is 1.0 and the largest is 200.0, the
42  * largest items will be 200 times larger than the smallest. While
43  * accurate, this may not result in the most readable display. To correct
44  * these cases, use the {@link #setMaximumSize(double)} method to manually
45  * set the range of allowed sizes. By default, the minimum size value is
46  * 1.0. This too can be changed using the {@link #setMinimumSize(double)}
47  * method.
48  * </p>
49  *
50  * @author <a HREF="http://jheer.org">jeffrey heer</a>
51  */

52 public class DataSizeAction extends SizeAction {
53     
54     protected static final double NO_SIZE = Double.NaN;
55     
56     protected String JavaDoc m_dataField;
57     
58     protected double m_minSize = 1;
59     protected double m_sizeRange;
60     
61     protected int m_scale = Constants.LINEAR_SCALE;
62     protected int m_bins = Constants.CONTINUOUS;
63     
64     protected boolean m_inferBounds = true;
65     protected boolean m_inferRange = true;
66     protected boolean m_is2DArea = true;
67     protected double[] m_dist;
68     
69     protected int m_tempScale;
70     
71     /**
72      * Create a new DataSizeAction.
73      * @param group the data group to process
74      * @param field the data field to base size assignments on
75      */

76     public DataSizeAction(String JavaDoc group, String JavaDoc field) {
77         super(group, NO_SIZE);
78         m_dataField = field;
79     }
80
81     /**
82      * Create a new DataSizeAction.
83      * @param group the data group to process
84      * @param field the data field to base size assignments on
85      * @param bins the number of discrete size values to use
86      */

87     public DataSizeAction(String JavaDoc group, String JavaDoc field, int bins) {
88         this(group, field, bins, Constants.LINEAR_SCALE);
89     }
90
91     /**
92      * Create a new DataSizeAction.
93      * @param group the data group to process
94      * @param field the data field to base size assignments on
95      * @param bins the number of discrete size values to use
96      * @param scale the scale type to use. One of
97      * {@link prefuse.Constants#LINEAR_SCALE},
98      * {@link prefuse.Constants#LOG_SCALE},
99      * {@link prefuse.Constants#SQRT_SCALE}, or
100      * {@link prefuse.Constants#QUANTILE_SCALE}. If a quantile scale is
101      * used, the number of bins must be greater than zero.
102      */

103     public DataSizeAction(String JavaDoc group, String JavaDoc field, int bins, int scale) {
104         super(group, NO_SIZE);
105         m_dataField = field;
106         setScale(scale);
107         setBinCount(bins);
108     }
109     
110     // ------------------------------------------------------------------------
111

112     /**
113      * Returns the data field used to encode size values.
114      * @return the data field that is mapped to size values
115      */

116     public String JavaDoc getDataField() {
117         return m_dataField;
118     }
119     
120     /**
121      * Set the data field used to encode size values.
122      * @param field the data field to map to size values
123      */

124     public void setDataField(String JavaDoc field) {
125         m_dataField = field;
126     }
127     
128     /**
129      * Returns the scale type used for encoding size values from the data.
130      * @return the scale type. One of
131      * {@link prefuse.Constants#LINEAR_SCALE},
132      * {@link prefuse.Constants#LOG_SCALE},
133      * {@link prefuse.Constants#SQRT_SCALE},
134      * {@link prefuse.Constants#QUANTILE_SCALE}.
135      */

136     public int getScale() {
137         return m_scale;
138     }
139     
140     /**
141      * Set the scale (linear, square root, or log) to use for encoding size
142      * values from the data.
143      * @param scale the scale type to use. This value should be one of
144      * {@link prefuse.Constants#LINEAR_SCALE},
145      * {@link prefuse.Constants#SQRT_SCALE},
146      * {@link prefuse.Constants#LOG_SCALE},
147      * {@link prefuse.Constants#QUANTILE_SCALE}.
148      * If {@link prefuse.Constants#QUANTILE_SCALE} is used, the number of
149      * bins to use must also be specified to a value greater than zero using
150      * the {@link #setBinCount(int)} method.
151      */

152     public void setScale(int scale) {
153         if ( scale < 0 || scale >= Constants.SCALE_COUNT )
154             throw new IllegalArgumentException JavaDoc(
155                     "Unrecognized scale value: "+scale);
156         m_scale = scale;
157     }
158     
159     /**
160      * Returns the number of "bins" or distinct categories of sizes
161      * @return the number of bins.
162      */

163     public int getBinCount() {
164         return m_bins;
165     }
166
167     /**
168      * Sets the number of "bins" or distinct categories of sizes
169      * @param count the number of bins to set. The value
170      * {@link Constants#CONTINUOUS} indicates not to use any binning. If the
171      * scale type set using the {@link #setScale(int)} method is
172      * {@link Constants#QUANTILE_SCALE}, the bin count <strong>must</strong>
173      * be greater than zero.
174      */

175     public void setBinCount(int count) {
176         if ( m_scale == Constants.QUANTILE_SCALE && count <= 0 ) {
177             throw new IllegalArgumentException JavaDoc(
178                 "The quantile scale can not be used without binning. " +
179                 "Use a bin value greater than zero.");
180         }
181         m_bins = count;
182     }
183     
184     /**
185      * Indicates if the size values set by this function represent 2D areas.
186      * That is, if the size is a 2D area or a 1D length. The size value will
187      * be scaled appropriately to facilitate better perception of size
188      * differences.
189      * @return true if this instance is configured for area sizes, false for
190      * length sizes.
191      * @see prefuse.util.PrefuseLib#getSize2D(double)
192      */

193     public boolean is2DArea() {
194         return m_is2DArea;
195     }
196     
197     /**
198      * Sets if the size values set by this function represent 2D areas.
199      * That is, if the size is a 2D area or a 1D length. The size value will
200      * be scaled appropriately to facilitate better perception of size
201      * differences.
202      * @param isArea true to configure this instance for area sizes, false for
203      * length sizes
204      * @see prefuse.util.PrefuseLib#getSize2D(double)
205      */

206     public void setIs2DArea(boolean isArea) {
207         m_is2DArea = isArea;
208     }
209
210     /**
211      * Gets the size assigned to the lowest-valued data items, typically 1.0.
212      * @return the size for the lowest-valued data items
213      */

214     public double getMinimumSize() {
215         return m_minSize;
216     }
217
218     /**
219      * Sets the size assigned to the lowest-valued data items. By default,
220      * this value is 1.0.
221      * @param size the new size for the lowest-valued data items
222      */

223     public void setMinimumSize(double size) {
224         if ( Double.isInfinite(size) ||
225              Double.isNaN(size) ||
226              size <= 0 )
227         {
228            throw new IllegalArgumentException JavaDoc("Minimum size value must be a "
229                    + "finite number greater than zero.");
230         }
231         
232         if ( m_inferRange ) {
233             m_sizeRange += m_minSize - size;
234         }
235         m_minSize = size;
236     }
237     
238     /**
239      * Gets the maximum size value that will be assigned by this action. By
240      * default, the maximum size value is determined automatically from the
241      * data, faithfully representing the scale differences between data values.
242      * However, this can sometimes result in very large differences. For
243      * example, if the minimum data value is 1.0 and the largest is 200.0, the
244      * largest items will be 200 times larger than the smallest. While
245      * accurate, this may not result in the most readable display. To correct
246      * these cases, use the {@link #setMaximumSize(double)} method to manually
247      * set the range of allowed sizes.
248      * @return the current maximum size. For the returned value to accurately
249      * reflect the size range used by this action, either the action must
250      * have already been run (allowing the value to be automatically computed)
251      * or the maximum size must have been explicitly set.
252      */

253     public double getMaximumSize() {
254         return m_minSize + m_sizeRange;
255     }
256     
257     /**
258      * Set the maximum size value that will be assigned by this action. By
259      * default, the maximum size value is determined automatically from the
260      * data, faithfully representing the scale differences between data values.
261      * However, this can sometimes result in very large differences. For
262      * example, if the minimum data value is 1.0 and the largest is 200.0, the
263      * largest items will be 200 times larger than the smallest. While
264      * accurate, this may not result in the most readable display. To correct
265      * these cases, use the {@link #setMaximumSize(double)} method to manually
266      * set the range of allowed sizes.
267      * @param maxSize the maximum size to use. If this value is less than or
268      * equal to zero, infinite, or not a number (NaN) then the input value
269      * will be ignored and instead automatic inference of the size range
270      * will be performed.
271      */

272     public void setMaximumSize(double maxSize) {
273         if ( Double.isInfinite(maxSize) ||
274              Double.isNaN(maxSize) ||
275              maxSize <= 0 )
276         {
277             m_inferRange = true;
278         } else {
279             m_inferRange = false;
280             m_sizeRange = maxSize-m_minSize;
281         }
282     }
283     
284     /**
285      * This operation is not supported by the DataSizeAction type.
286      * Calling this method will result in a thrown exception.
287      * @see prefuse.action.assignment.SizeAction#setDefaultSize(double)
288      * @throws UnsupportedOperationException
289      */

290     public void setDefaultSize(double defaultSize) {
291         throw new UnsupportedOperationException JavaDoc();
292     }
293     
294     // ------------------------------------------------------------------------
295

296     /**
297      * @see prefuse.action.EncoderAction#setup()
298      */

299     protected void setup() {
300         TupleSet ts = m_vis.getGroup(m_group);
301         
302         // cache the scale value in case it gets changed due to error
303
m_tempScale = m_scale;
304         
305         if ( m_inferBounds ) {
306             if ( m_scale == Constants.QUANTILE_SCALE && m_bins > 0 ) {
307                 double[] values =
308                     DataLib.toDoubleArray(ts.tuples(), m_dataField);
309                 m_dist = MathLib.quantiles(m_bins, values);
310             } else {
311                 // check for non-binned quantile scale error
312
if ( m_scale == Constants.QUANTILE_SCALE ) {
313                     Logger.getLogger(getClass().getName()).warning(
314                             "Can't use quantile scale with no binning. " +
315                             "Defaulting to linear scale. Set the bin value " +
316                             "greater than zero to use a quantile scale.");
317                     m_scale = Constants.LINEAR_SCALE;
318                 }
319                 m_dist = new double[2];
320                 m_dist[0]= DataLib.min(ts, m_dataField).getDouble(m_dataField);
321                 m_dist[1]= DataLib.max(ts, m_dataField).getDouble(m_dataField);
322             }
323             if ( m_inferRange ) {
324                 m_sizeRange = m_dist[m_dist.length-1]/m_dist[0] - m_minSize;
325             }
326         }
327     }
328     
329     /**
330      * @see prefuse.action.EncoderAction#finish()
331      */

332     protected void finish() {
333         // reset scale in case it needed to be changed due to errors
334
m_scale = m_tempScale;
335     }
336     
337     /**
338      * @see prefuse.action.assignment.SizeAction#getSize(prefuse.visual.VisualItem)
339      */

340     public double getSize(VisualItem item) {
341         // check for any cascaded rules first
342
double size = super.getSize(item);
343         if ( !Double.isNaN(size) ) {
344             return size;
345         }
346         
347         // otherwise perform data-driven assignment
348
double v = item.getDouble(m_dataField);
349         double f = MathLib.interp(m_scale, v, m_dist);
350         if ( m_bins < 1 ) {
351             // continuous scale
352
v = m_minSize + f * m_sizeRange;
353         } else {
354             // binned sizes
355
int bin = f < 1.0 ? (int)(f*m_bins) : m_bins-1;
356             v = m_minSize + bin*(m_sizeRange/(m_bins-1));
357         }
358         // return the size value. if this action is configured to return
359
// 2-dimensional sizes (ie area rather than length) then the
360
// size value is appropriately scaled first
361
return m_is2DArea ? PrefuseLib.getSize2D(v) : v;
362     }
363     
364 } // end of class DataSizeAction
365
Popular Tags