001: package prefuse.demos;
002:
003: import java.awt.BorderLayout;
004: import java.awt.Color;
005: import java.awt.Dimension;
006: import java.awt.Insets;
007: import java.awt.event.ComponentAdapter;
008: import java.awt.event.ComponentEvent;
009: import java.awt.event.MouseAdapter;
010: import java.awt.event.MouseEvent;
011: import java.awt.geom.Rectangle2D;
012: import java.text.NumberFormat;
013: import java.util.Iterator;
014:
015: import javax.swing.BorderFactory;
016: import javax.swing.Box;
017: import javax.swing.BoxLayout;
018: import javax.swing.JFrame;
019: import javax.swing.JPanel;
020: import javax.swing.SwingConstants;
021:
022: import prefuse.Constants;
023: import prefuse.Display;
024: import prefuse.Visualization;
025: import prefuse.action.ActionList;
026: import prefuse.action.GroupAction;
027: import prefuse.action.RepaintAction;
028: import prefuse.action.assignment.ColorAction;
029: import prefuse.action.assignment.DataColorAction;
030: import prefuse.action.assignment.DataShapeAction;
031: import prefuse.action.filter.VisibilityFilter;
032: import prefuse.action.layout.AxisLabelLayout;
033: import prefuse.action.layout.AxisLayout;
034: import prefuse.controls.Control;
035: import prefuse.controls.ControlAdapter;
036: import prefuse.controls.ToolTipControl;
037: import prefuse.data.Table;
038: import prefuse.data.expression.AndPredicate;
039: import prefuse.data.expression.Predicate;
040: import prefuse.data.expression.parser.ExpressionParser;
041: import prefuse.data.io.DelimitedTextTableReader;
042: import prefuse.data.query.ListQueryBinding;
043: import prefuse.data.query.RangeQueryBinding;
044: import prefuse.data.query.SearchQueryBinding;
045: import prefuse.render.AxisRenderer;
046: import prefuse.render.Renderer;
047: import prefuse.render.RendererFactory;
048: import prefuse.render.ShapeRenderer;
049: import prefuse.render.AbstractShapeRenderer;
050: import prefuse.util.ColorLib;
051: import prefuse.util.FontLib;
052: import prefuse.util.UpdateListener;
053: import prefuse.util.ui.JFastLabel;
054: import prefuse.util.ui.JRangeSlider;
055: import prefuse.util.ui.JSearchPanel;
056: import prefuse.util.ui.UILib;
057: import prefuse.visual.VisualItem;
058: import prefuse.visual.VisualTable;
059: import prefuse.visual.expression.VisiblePredicate;
060: import prefuse.visual.sort.ItemSorter;
061:
062: /**
063: * @author <a href="http://jheer.org">jeffrey heer</a>
064: */
065: public class Congress extends JPanel {
066:
067: public static void main(String[] args) {
068: UILib.setPlatformLookAndFeel();
069:
070: JFrame f = demo();
071: f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
072: f.setVisible(true);
073: }
074:
075: public static JFrame demo() {
076: // load the data
077: Table t = null;
078: try {
079: t = new DelimitedTextTableReader().readTable("/fec.txt");
080: } catch (Exception e) {
081: e.printStackTrace();
082: System.exit(1);
083: }
084:
085: JFrame frame = new JFrame((char) 0x00a2 + " o n g r e s s");
086: frame.setContentPane(new Congress(t));
087: frame.pack();
088: return frame;
089: }
090:
091: // ------------------------------------------------------------------------
092:
093: private static final String TOTAL_RECEIPTS = "Total Receipts";
094: private static final String RECEIPTS = "Total Receipts";
095:
096: private String m_title = (char) 0x00a2 + "ongress";
097: private String m_totalStr;
098: private double m_totalMoney = 1000000000;
099: private int m_totalPeople = 10000;
100: private JFastLabel m_total = new JFastLabel(m_totalPeople
101: + " Candidates: " + m_totalMoney);
102: private JFastLabel m_details;
103:
104: private Visualization m_vis;
105: private Display m_display;
106: private Rectangle2D m_dataB = new Rectangle2D.Double();
107: private Rectangle2D m_xlabB = new Rectangle2D.Double();
108: private Rectangle2D m_ylabB = new Rectangle2D.Double();
109:
110: public Congress(Table t) {
111: super (new BorderLayout());
112:
113: // --------------------------------------------------------------------
114: // STEP 1: setup the visualized data
115:
116: final Visualization vis = new Visualization();
117: m_vis = vis;
118:
119: final String group = "by_state";
120:
121: // filter to show only candidates receiving more than $100,000
122: Predicate p = (Predicate) ExpressionParser.parse("["
123: + TOTAL_RECEIPTS + "] >= 100000");
124: VisualTable vt = vis.addTable(group, t, p);
125:
126: // add a new column containing a label string showing
127: // candidate name, party, state, year, and total receipts
128: vt.addColumn("label", "CONCAT(CAP(Candidate), ' (', "
129: + "CAP([Party Designation]), '-', [State Code], "
130: + "') ', Year, ': $', FORMAT([Total Receipts],2))");
131:
132: // add calculation for senators
133: vt.addColumn("Senate", "District <= 0");
134:
135: vis.setRendererFactory(new RendererFactory() {
136: AbstractShapeRenderer sr = new ShapeRenderer();
137: Renderer arY = new AxisRenderer(Constants.RIGHT,
138: Constants.TOP);
139: Renderer arX = new AxisRenderer(Constants.CENTER,
140: Constants.FAR_BOTTOM);
141:
142: public Renderer getRenderer(VisualItem item) {
143: return item.isInGroup("ylab") ? arY : item
144: .isInGroup("xlab") ? arX : sr;
145: }
146: });
147:
148: // --------------------------------------------------------------------
149: // STEP 2: create actions to process the visual data
150:
151: // set up dynamic queries, search set
152: RangeQueryBinding receiptsQ = new RangeQueryBinding(vt,
153: RECEIPTS);
154: ListQueryBinding yearsQ = new ListQueryBinding(vt, "Year");
155: SearchQueryBinding searchQ = new SearchQueryBinding(vt,
156: "Candidate");
157:
158: // construct the filtering predicate
159: AndPredicate filter = new AndPredicate(searchQ.getPredicate());
160: filter.add(yearsQ.getPredicate());
161: filter.add(receiptsQ.getPredicate());
162:
163: // set up the actions
164: AxisLayout xaxis = new AxisLayout(group, "State Code",
165: Constants.X_AXIS, VisiblePredicate.TRUE);
166: AxisLayout yaxis = new AxisLayout(group, RECEIPTS,
167: Constants.Y_AXIS, VisiblePredicate.TRUE);
168: //yaxis.setScale(Constants.LOG_SCALE);
169: yaxis.setRangeModel(receiptsQ.getModel());
170: receiptsQ.getNumberModel().setValueRange(0, 65000000, 0,
171: 65000000);
172:
173: xaxis.setLayoutBounds(m_dataB);
174: yaxis.setLayoutBounds(m_dataB);
175:
176: AxisLabelLayout ylabels = new AxisLabelLayout("ylab", yaxis,
177: m_ylabB);
178: NumberFormat nf = NumberFormat.getCurrencyInstance();
179: nf.setMaximumFractionDigits(0);
180: ylabels.setNumberFormat(nf);
181:
182: AxisLabelLayout xlabels = new AxisLabelLayout("xlab", xaxis,
183: m_xlabB, 15);
184: vis.putAction("xlabels", xlabels);
185:
186: // dems = blue, reps = red, other = gray
187: int[] palette = new int[] { ColorLib.rgb(150, 150, 255),
188: ColorLib.rgb(255, 150, 150),
189: ColorLib.rgb(180, 180, 180) };
190: DataColorAction color = new DataColorAction(group, "Party",
191: Constants.ORDINAL, VisualItem.STROKECOLOR, palette);
192:
193: int[] shapes = new int[] { Constants.SHAPE_RECTANGLE,
194: Constants.SHAPE_DIAMOND };
195: DataShapeAction shape = new DataShapeAction(group, "Senate",
196: shapes);
197:
198: Counter cntr = new Counter(group);
199:
200: ActionList draw = new ActionList();
201: draw.add(cntr);
202: draw.add(color);
203: draw.add(shape);
204: draw.add(xaxis);
205: draw.add(yaxis);
206: draw.add(ylabels);
207: draw.add(new ColorAction(group, VisualItem.FILLCOLOR, 0));
208: draw.add(new RepaintAction());
209: vis.putAction("draw", draw);
210:
211: ActionList update = new ActionList();
212: update.add(new VisibilityFilter(group, filter));
213: update.add(cntr);
214: update.add(xaxis);
215: update.add(yaxis);
216: update.add(ylabels);
217: update.add(new RepaintAction());
218: vis.putAction("update", update);
219:
220: UpdateListener lstnr = new UpdateListener() {
221: public void update(Object src) {
222: vis.run("update");
223: }
224: };
225: filter.addExpressionListener(lstnr);
226:
227: // --------------------------------------------------------------------
228: // STEP 4: set up a display and ui components to show the visualization
229:
230: m_display = new Display(vis);
231: m_display.setItemSorter(new ItemSorter() {
232: public int score(VisualItem item) {
233: int score = super .score(item);
234: if (item.isInGroup(group))
235: score += item.getInt(TOTAL_RECEIPTS);
236: return score;
237: }
238: });
239: m_display.setBorder(BorderFactory.createEmptyBorder(10, 10, 10,
240: 10));
241: m_display.setSize(700, 450);
242: m_display.setHighQuality(true);
243: m_display.addComponentListener(new ComponentAdapter() {
244: public void componentResized(ComponentEvent e) {
245: displayLayout();
246: }
247: });
248: displayLayout();
249:
250: m_details = new JFastLabel(m_title);
251: m_details.setPreferredSize(new Dimension(75, 20));
252: m_details.setVerticalAlignment(SwingConstants.BOTTOM);
253:
254: m_total.setPreferredSize(new Dimension(500, 20));
255: m_total.setHorizontalAlignment(SwingConstants.RIGHT);
256: m_total.setVerticalAlignment(SwingConstants.BOTTOM);
257:
258: ToolTipControl ttc = new ToolTipControl("label");
259: Control hoverc = new ControlAdapter() {
260: public void itemEntered(VisualItem item, MouseEvent evt) {
261: if (item.isInGroup(group)) {
262: m_total.setText(item.getString("label"));
263: item.setFillColor(item.getStrokeColor());
264: item.setStrokeColor(ColorLib.rgb(0, 0, 0));
265: item.getVisualization().repaint();
266: }
267: }
268:
269: public void itemExited(VisualItem item, MouseEvent evt) {
270: if (item.isInGroup(group)) {
271: m_total.setText(m_totalStr);
272: item.setFillColor(item.getEndFillColor());
273: item.setStrokeColor(item.getEndStrokeColor());
274: item.getVisualization().repaint();
275: }
276: }
277: };
278: m_display.addControlListener(ttc);
279: m_display.addControlListener(hoverc);
280:
281: // --------------------------------------------------------------------
282: // STEP 5: launching the visualization
283:
284: this .addComponentListener(lstnr);
285:
286: // details
287: Box infoBox = new Box(BoxLayout.X_AXIS);
288: infoBox.add(Box.createHorizontalStrut(5));
289: infoBox.add(m_details);
290: infoBox.add(Box.createHorizontalGlue());
291: infoBox.add(Box.createHorizontalStrut(5));
292: infoBox.add(m_total);
293: infoBox.add(Box.createHorizontalStrut(5));
294:
295: // set up search box
296: JSearchPanel searcher = searchQ.createSearchPanel();
297: searcher.setLabelText("Candidate: ");
298: searcher.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 0));
299:
300: // create dynamic queries
301: Box radioBox = new Box(BoxLayout.X_AXIS);
302: radioBox.add(Box.createHorizontalStrut(5));
303: radioBox.add(searcher);
304: radioBox.add(Box.createHorizontalGlue());
305: radioBox.add(Box.createHorizontalStrut(5));
306: radioBox.add(yearsQ.createRadioGroup());
307: radioBox.add(Box.createHorizontalStrut(16));
308:
309: JRangeSlider slider = receiptsQ.createVerticalRangeSlider();
310: slider.setThumbColor(null);
311: slider.setMinExtent(150000);
312: slider.addMouseListener(new MouseAdapter() {
313: public void mousePressed(MouseEvent e) {
314: m_display.setHighQuality(false);
315: }
316:
317: public void mouseReleased(MouseEvent e) {
318: m_display.setHighQuality(true);
319: m_display.repaint();
320: }
321: });
322:
323: vis.run("draw");
324: vis.run("xlabels");
325:
326: add(infoBox, BorderLayout.NORTH);
327: add(m_display, BorderLayout.CENTER);
328: add(slider, BorderLayout.EAST);
329: add(radioBox, BorderLayout.SOUTH);
330: UILib.setColor(this , ColorLib.getColor(255, 255, 255),
331: Color.GRAY);
332: slider.setForeground(Color.LIGHT_GRAY);
333: UILib.setFont(radioBox, FontLib.getFont("Tahoma", 15));
334: m_details.setFont(FontLib.getFont("Tahoma", 18));
335: m_total.setFont(FontLib.getFont("Tahoma", 16));
336: }
337:
338: public void displayLayout() {
339: Insets i = m_display.getInsets();
340: int w = m_display.getWidth();
341: int h = m_display.getHeight();
342: int iw = i.left + i.right;
343: int ih = i.top + i.bottom;
344: int aw = 85;
345: int ah = 15;
346:
347: m_dataB.setRect(i.left, i.top, w - iw - aw, h - ih - ah);
348: m_xlabB
349: .setRect(i.left, h - ah - i.bottom, w - iw - aw,
350: ah - 10);
351: m_ylabB.setRect(i.left, i.top, w - iw, h - ih - ah);
352:
353: m_vis.run("update");
354: m_vis.run("xlabels");
355: }
356:
357: private class Counter extends GroupAction {
358: public Counter(String group) {
359: super (group);
360: }
361:
362: public void run(double frac) {
363: double total = 0;
364: int count = 0;
365: VisualItem item = null;
366: Iterator items = m_vis.visibleItems(m_group);
367: while (items.hasNext()) {
368: item = (VisualItem) items.next();
369: total += item.getDouble("Total Receipts");
370: ++count;
371: }
372: m_totalMoney = total;
373: m_totalPeople = count;
374:
375: if (count == 1) {
376: m_totalStr = item.getString("label");
377: } else {
378: m_totalStr = count
379: + " Candidates receiving "
380: + NumberFormat.getCurrencyInstance().format(
381: total);
382: }
383: m_total.setText(m_totalStr);
384: }
385: }
386:
387: } // end of class ScatterPlot
|