KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > prefuse > demos > ZipDecode


1 package prefuse.demos;
2
3 import java.awt.Font JavaDoc;
4 import java.awt.Shape JavaDoc;
5 import java.awt.event.ComponentAdapter JavaDoc;
6 import java.awt.event.ComponentEvent JavaDoc;
7
8 import javax.swing.BorderFactory JavaDoc;
9 import javax.swing.JFrame JavaDoc;
10
11 import prefuse.Constants;
12 import prefuse.Display;
13 import prefuse.Visualization;
14 import prefuse.action.Action;
15 import prefuse.action.ActionList;
16 import prefuse.action.RepaintAction;
17 import prefuse.action.animate.ColorAnimator;
18 import prefuse.action.animate.LocationAnimator;
19 import prefuse.action.assignment.ColorAction;
20 import prefuse.action.layout.AxisLayout;
21 import prefuse.activity.Activity;
22 import prefuse.activity.ActivityAdapter;
23 import prefuse.activity.SlowInSlowOutPacer;
24 import prefuse.data.Schema;
25 import prefuse.data.Table;
26 import prefuse.data.Tuple;
27 import prefuse.data.event.TupleSetListener;
28 import prefuse.data.expression.FunctionExpression;
29 import prefuse.data.expression.FunctionTable;
30 import prefuse.data.expression.Predicate;
31 import prefuse.data.expression.parser.ExpressionParser;
32 import prefuse.data.io.DataIOException;
33 import prefuse.data.io.DelimitedTextTableReader;
34 import prefuse.data.query.SearchQueryBinding;
35 import prefuse.data.search.SearchTupleSet;
36 import prefuse.data.tuple.TupleSet;
37 import prefuse.render.DefaultRendererFactory;
38 import prefuse.render.ShapeRenderer;
39 import prefuse.render.LabelRenderer;
40 import prefuse.util.ColorLib;
41 import prefuse.util.FontLib;
42 import prefuse.util.PrefuseLib;
43 import prefuse.util.ui.JSearchPanel;
44 import prefuse.visual.VisualItem;
45 import prefuse.visual.VisualTable;
46
47 /**
48  * Re-implementation of Ben Fry's Zipdecode. Check out the
49  * original at <a HREF="http://acg.media.mit.edu/people/fry/zipdecode/">
50  * http://acg.media.mit.edu/people/fry/zipdecode/</a>.
51  *
52  * This demo showcases creating new functions in the prefuse expression
53  * language, creating derived columns, and provides an example of using
54  * a dedicated focus set of items to support more efficient data handling.
55  *
56  * @author <a HREF="http://jheer.org">jeffrey heer</a>
57  */

58 public class ZipDecode extends Display implements Constants {
59
60     public static final String JavaDoc ZIPCODES = "/zipcode.txt";
61     public static final String JavaDoc STATES = "/state.txt";
62     
63     // data groups
64
private static final String JavaDoc DATA = "data";
65     private static final String JavaDoc LABELS = "labels";
66     private static final String JavaDoc FOCUS = Visualization.FOCUS_ITEMS;
67     
68     public static class StateLookupFunction extends FunctionExpression {
69         private static Table s_states;
70         static {
71             try {
72                 s_states = new DelimitedTextTableReader().readTable(STATES);
73             } catch ( Exception JavaDoc e ) { e.printStackTrace(); }
74         }
75         
76         public StateLookupFunction() { super(1); }
77         public String JavaDoc getName() { return "STATE"; }
78         public Class JavaDoc getType(Schema s) { return String JavaDoc.class; }
79         public Object JavaDoc get(Tuple t) {
80             int code = s_states.index("code").get(param(0).getInt(t));
81             return s_states.getString(code, "alpha");
82         }
83     }
84     // add state function to the FunctionTable
85
static { FunctionTable.addFunction("STATE", StateLookupFunction.class); }
86     
87     
88     public ZipDecode(final Table t) {
89         super(new Visualization());
90         
91         // this predicate makes sure only the continental states are included
92
Predicate filter = (Predicate)ExpressionParser.parse(
93                 "state >= 1 && state <= 56 && state != 2 && state != 15");
94         VisualTable vt = m_vis.addTable(DATA, t, filter, getDataSchema());
95         // zip codes are loaded in as integers, so lets create a derived
96
// column that has correctly-formatted 5 digit strings
97
vt.addColumn("zipstr", "LPAD(zip,5,'0')");
98         // now add a formatted label to show within the visualization
99
vt.addColumn("label", "CONCAT(CAP(city),', ',STATE(state),' ',zipstr)");
100         
101         // create a filter controlling label appearance
102
Predicate loneResult = (Predicate)ExpressionParser.parse(
103                 "INGROUP('_search_') AND GROUPSIZE('_search_')=1 AND " +
104                 "LENGTH(QUERY('_search_'))=5");
105         
106         // add a table of visible city,state,zip labels
107
// this is a derived table, overriding only the fields that need to
108
// have unique values and inheriting all other data values from the
109
// data table. in particular, we want to inherit the x,y coordinates.
110
m_vis.addDerivedTable(LABELS, DATA, loneResult, getLabelSchema());
111         
112         // -- renderers -------------------------------------------------------
113

114         DefaultRendererFactory rf = new DefaultRendererFactory();
115         rf.setDefaultRenderer(new ShapeRenderer(1)); // 1 pixel rectangles
116
rf.add("INGROUP('labels')", new LabelRenderer("label") {
117             public Shape JavaDoc getShape(VisualItem item) {
118                 // set horizontal alignment based on x-coordinate position
119
setHorizontalAlignment(item.getX()>getWidth()/2 ? RIGHT:LEFT);
120                 // now return shape as usual
121
return super.getShape(item);
122             }
123         });
124         m_vis.setRendererFactory(rf);
125         
126         // -- actions ---------------------------------------------------------
127

128         ActionList layout = new ActionList();
129         layout.add(new AxisLayout(DATA, "lat", Y_AXIS));
130         layout.add(new AxisLayout(DATA, "lon", X_AXIS));
131         m_vis.putAction("layout", layout);
132         
133         // the update list updates the colors of data points and sets the visual
134
// properties for any labels. Color updating is limited only to the
135
// current focus items, ensuring faster performance.
136
final Action update = new ZipColorAction(FOCUS);
137         m_vis.putAction("update", update);
138         
139         // animate a change in color in the interface. this animation is quite
140
// short, only 200ms, so that it does not impede with interaction.
141
// color animation of data points looks only at the focus items,
142
// ensuring faster performance.
143
ActionList animate = new ActionList(200);
144         animate.add(new ColorAnimator(FOCUS, VisualItem.FILLCOLOR));
145         animate.add(new ColorAnimator(LABELS, VisualItem.TEXTCOLOR));
146         animate.add(new RepaintAction());
147         animate.addActivityListener(new ActivityAdapter() {
148             public void activityCancelled(Activity a) {
149                 // if animation is canceled, set colors to final state
150
update.run(1.0);
151             }
152         });
153         m_vis.putAction("animate", animate);
154         
155         // update items after a resize of the display, animating them to their
156
// new locations. this animates all data points, so is noticeably slow.
157
ActionList resize = new ActionList(1500);
158         resize.setPacingFunction(new SlowInSlowOutPacer());
159         resize.add(new LocationAnimator(DATA));
160         resize.add(new LocationAnimator(LABELS));
161         resize.add(new RepaintAction());
162         m_vis.putAction("resize", resize);
163         
164         // -- display ---------------------------------------------------------
165

166         setSize(720, 360);
167         setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
168         setBackground(ColorLib.getGrayscale(50));
169         setFocusable(false);
170         
171         // -- search ----------------------------------------------------------
172

173         // zipcode text search is performed using a prefix based search,
174
// provided by a search dynamic query. to make this application run
175
// more efficiently, we optimize data handling by taking all search
176
// results (both added and removed) and shuttling them into a
177
// focus set. we work with this reduced set for updating and
178
// animating color changes in the action definitions above.
179

180         // create a final reference to the focus set, so that the following
181
// search listener can access it.
182
final TupleSet focus = m_vis.getFocusGroup(FOCUS);
183         
184         // create the search query binding
185
SearchQueryBinding searchQ = new SearchQueryBinding(vt, "zipstr");
186         final SearchTupleSet search = searchQ.getSearchSet();
187         
188         // create the listener that collects search results into a focus set
189
search.addTupleSetListener(new TupleSetListener() {
190             public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) {
191                 m_vis.cancel("animate");
192                 
193                 // invalidate changed tuples, add them all to the focus set
194
focus.clear();
195                 for ( int i=0; i<add.length; ++i ) {
196                     ((VisualItem)add[i]).setValidated(false);
197                     focus.addTuple(add[i]);
198                 }
199                 for ( int i=0; i<rem.length; ++i ) {
200                     ((VisualItem)rem[i]).setValidated(false);
201                     focus.addTuple(rem[i]);
202                 }
203                 
204                 m_vis.run("update");
205                 m_vis.run("animate");
206             }
207         });
208         m_vis.addFocusGroup(Visualization.SEARCH_ITEMS, search);
209         
210         // create and parameterize a search panel for searching on zip code
211
final JSearchPanel searcher = searchQ.createSearchPanel();
212         searcher.setLabelText("zip>"); // the search box label
213
searcher.setShowCancel(false); // don't show the cancel query button
214
searcher.setShowBorder(false); // don't show the search box border
215
searcher.setFont(FontLib.getFont("Georgia", Font.PLAIN, 22));
216         searcher.setBackground(ColorLib.getGrayscale(50));
217         searcher.setForeground(ColorLib.getColor(100,100,75));
218         add(searcher); // add the search box as a sub-component of the display
219
searcher.setBounds(10, getHeight()-40, 120, 30);
220         
221         addComponentListener(new ComponentAdapter JavaDoc() {
222             public void componentResized(ComponentEvent JavaDoc e) {
223                 m_vis.run("layout");
224                 m_vis.run("update");
225                 m_vis.run("resize");
226                 searcher.setBounds(10, getHeight()-40, 120, 30);
227                 invalidate();
228             }
229         });
230         
231         // -- launch ----------------------------------------------------------
232

233         m_vis.run("layout");
234         m_vis.run("animate");
235     }
236     
237     private static Schema getDataSchema() {
238         Schema s = PrefuseLib.getVisualItemSchema();
239         s.setDefault(VisualItem.INTERACTIVE, false);
240         s.setDefault(VisualItem.FILLCOLOR, ColorLib.rgb(100,100,75));
241         return s;
242     }
243     
244     private static Schema getLabelSchema() {
245         Schema s = PrefuseLib.getMinimalVisualSchema();
246         s.setDefault(VisualItem.INTERACTIVE, false);
247         
248         // default font is 16 point Georgia
249
s.addInterpolatedColumn(
250                 VisualItem.FONT, Font JavaDoc.class, FontLib.getFont("Georgia",16));
251         
252         // default fill color should be invisible
253
s.addInterpolatedColumn(VisualItem.FILLCOLOR, int.class);
254         s.setInterpolatedDefault(VisualItem.FILLCOLOR, 0);
255         
256         s.addInterpolatedColumn(VisualItem.TEXTCOLOR, int.class);
257         // default text color is white
258
s.setInterpolatedDefault(VisualItem.TEXTCOLOR, ColorLib.gray(255));
259         // default start text color is fully transparent
260
s.setDefault(VisualItem.STARTTEXTCOLOR, ColorLib.gray(255,0));
261         return s;
262     }
263     
264     // ------------------------------------------------------------------------
265

266     public static void main(String JavaDoc[] args) {
267         String JavaDoc datafile = ZIPCODES;
268         if ( args.length > 0 )
269             datafile = args[0];
270         
271         try {
272             JFrame JavaDoc frame = demo(datafile);
273             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
274             frame.setVisible(true);
275         } catch ( Exception JavaDoc e ) {
276             e.printStackTrace();
277             System.exit(1);
278         }
279     }
280     
281     public static JFrame JavaDoc demo() {
282         try {
283             return demo(ZIPCODES);
284         } catch ( Exception JavaDoc e ) {
285             return null;
286         }
287     }
288     
289     public static JFrame JavaDoc demo(String JavaDoc table) throws DataIOException {
290         DelimitedTextTableReader tr = new DelimitedTextTableReader();
291         Table t = tr.readTable(table);
292         ZipDecode zd = new ZipDecode(t);
293         
294         JFrame JavaDoc frame = new JFrame JavaDoc("p r e f u s e | z i p d e c o d e");
295         frame.getContentPane().add(zd);
296         frame.pack();
297         return frame;
298     }
299     
300     public static class ZipColorAction extends ColorAction {
301         public ZipColorAction(String JavaDoc group) {
302             super(group, VisualItem.FILLCOLOR);
303         }
304         
305         public int getColor(VisualItem item) {
306             if ( item.isInGroup(Visualization.SEARCH_ITEMS) ) {
307                 return ColorLib.gray(255);
308             } else {
309                 return ColorLib.rgb(100,100,75);
310             }
311         }
312     }
313     
314 } // end of class ZipDecode
315
Popular Tags