View Javadoc
1   /*
2    * Copyright (c) 2003, The JUNG Authors
3    * All rights reserved.
4    * 
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
7    * 
8    */
9   package edu.uci.ics.jung.samples;
10  
11  import java.awt.BorderLayout;
12  import java.awt.Color;
13  import java.awt.Container;
14  import java.awt.Dimension;
15  import java.awt.Graphics;
16  import java.awt.Graphics2D;
17  import java.awt.event.ActionEvent;
18  import java.awt.event.ActionListener;
19  import java.awt.geom.AffineTransform;
20  import java.awt.geom.Point2D;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import javax.swing.ImageIcon;
27  import javax.swing.JApplet;
28  import javax.swing.JButton;
29  import javax.swing.JFrame;
30  import javax.swing.JPanel;
31  
32  import com.google.common.base.Function;
33  import com.google.common.base.Functions;
34  
35  import edu.uci.ics.jung.algorithms.layout.Layout;
36  import edu.uci.ics.jung.algorithms.layout.StaticLayout;
37  import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
38  import edu.uci.ics.jung.graph.Graph;
39  import edu.uci.ics.jung.graph.util.EdgeType;
40  import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
41  import edu.uci.ics.jung.visualization.Layer;
42  import edu.uci.ics.jung.visualization.VisualizationViewer;
43  import edu.uci.ics.jung.visualization.control.AbstractModalGraphMouse;
44  import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
45  import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
46  import edu.uci.ics.jung.visualization.control.ScalingControl;
47  import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
48  import edu.uci.ics.jung.visualization.renderers.GradientVertexRenderer;
49  import edu.uci.ics.jung.visualization.renderers.Renderer;
50  import edu.uci.ics.jung.visualization.renderers.BasicVertexLabelRenderer.InsidePositioner;
51  
52  
53  /**
54   * Shows a graph overlaid on a world map image.
55   * Scaling of the graph also scales the image background.
56   * @author Tom Nelson
57   * 
58   */
59  @SuppressWarnings("serial")
60  public class WorldMapGraphDemo extends JApplet {
61  
62      /**
63       * the graph
64       */
65      Graph<String, Number> graph;
66  
67      /**
68       * the visual component and renderer for the graph
69       */
70      VisualizationViewer<String, Number> vv;
71      
72  	Map<String,String[]> map = new HashMap<String,String[]>();
73     	List<String> cityList;
74  
75  
76      
77      /**
78       * create an instance of a simple graph with controls to
79       * demo the zoom features.
80       * 
81       */
82      public WorldMapGraphDemo() {
83          setLayout(new BorderLayout());
84          
85  		map.put("TYO", new String[] {"35 40 N", "139 45 E"});
86     		map.put("PEK", new String[] {"39 55 N", "116 26 E"});
87     		map.put("MOW", new String[] {"55 45 N", "37 42 E"});
88     		map.put("JRS", new String[] {"31 47 N", "35 13 E"});
89     		map.put("CAI", new String[] {"30 03 N", "31 15 E"});
90     		map.put("CPT", new String[] {"33 55 S", "18 22 E"});
91     		map.put("PAR", new String[] {"48 52 N", "2 20 E"});
92     		map.put("LHR", new String[] {"51 30 N", "0 10 W"});
93     		map.put("HNL", new String[] {"21 18 N", "157 51 W"});
94     		map.put("NYC", new String[] {"40 77 N", "73 98 W"});
95     		map.put("SFO", new String[] {"37 62 N", "122 38 W"});
96     		map.put("AKL", new String[] {"36 55 S", "174 47 E"});
97     		map.put("BNE", new String[] {"27 28 S", "153 02 E"});
98     		map.put("HKG", new String[] {"22 15 N", "114 10 E"});
99     		map.put("KTM", new String[] {"27 42 N", "85 19 E"});
100    		map.put("IST", new String[] {"41 01 N", "28 58 E"});
101    		map.put("STO", new String[] {"59 20 N", "18 03 E"});
102    		map.put("RIO", new String[] {"22 54 S", "43 14 W"});
103    		map.put("LIM", new String[] {"12 03 S", "77 03 W"});
104    		map.put("YTO", new String[] {"43 39 N", "79 23 W"});
105 
106    		cityList = new ArrayList<String>(map.keySet());
107 
108         // create a simple graph for the demo
109         graph = new DirectedSparseMultigraph<String, Number>();
110         createVertices();
111         createEdges();
112         
113         ImageIcon mapIcon = null;
114         String imageLocation = "/images/political_world_map.jpg";
115         try {
116             mapIcon = 
117             	    new ImageIcon(getClass().getResource(imageLocation));
118         } catch(Exception ex) {
119             System.err.println("Can't load \""+imageLocation+"\"");
120         }
121         final ImageIcon icon = mapIcon;
122 
123         Dimension layoutSize = new Dimension(2000,1000);
124         
125         Layout<String,Number> layout = new StaticLayout<String,Number>(graph,
126         		Functions.<String,String[],Point2D>compose(
127         				new LatLonPixelTransformer(new Dimension(2000,1000)),
128         				new CityTransformer(map))
129         		);
130 //        		new ChainedTransformer(new Function[]{
131 //        				new CityTransformer(map),
132 //        				new LatLonPixelTransformer(new Dimension(2000,1000))
133 //        		}));
134         	
135         layout.setSize(layoutSize);
136         vv =  new VisualizationViewer<String,Number>(layout,
137         		new Dimension(800,400));
138         
139         if(icon != null) {
140             vv.addPreRenderPaintable(new VisualizationViewer.Paintable(){
141                 public void paint(Graphics g) {
142                 	Graphics2D g2d = (Graphics2D)g;
143                 	AffineTransform oldXform = g2d.getTransform();
144                     AffineTransform lat = 
145                     	vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getTransform();
146                     AffineTransform vat = 
147                     	vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getTransform();
148                     AffineTransform at = new AffineTransform();
149                     at.concatenate(g2d.getTransform());
150                     at.concatenate(vat);
151                     at.concatenate(lat);
152                     g2d.setTransform(at);
153                     g.drawImage(icon.getImage(), 0, 0,
154                     		icon.getIconWidth(),icon.getIconHeight(),vv);
155                     g2d.setTransform(oldXform);
156                 }
157                 public boolean useTransform() { return false; }
158             });
159         }
160 
161         vv.getRenderer().setVertexRenderer(
162         		new GradientVertexRenderer<String,Number>(
163         				Color.white, Color.red, 
164         				Color.white, Color.blue,
165         				vv.getPickedVertexState(),
166         				false));
167         
168         // add my listeners for ToolTips
169         vv.setVertexToolTipTransformer(new ToStringLabeller());
170         vv.setEdgeToolTipTransformer(new Function<Number,String>() {
171 			public String apply(Number edge) {
172 				return "E"+graph.getEndpoints(edge).toString();
173 			}});
174         
175         vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
176         vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner());
177         vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO);
178         
179         final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
180         add(panel);
181         final AbstractModalGraphMouse graphMouse = new DefaultModalGraphMouse<String, Number>();
182         vv.setGraphMouse(graphMouse);
183         
184         vv.addKeyListener(graphMouse.getModeKeyListener());
185         vv.setToolTipText("<html><center>Type 'p' for Pick mode<p>Type 't' for Transform mode");
186         
187         final ScalingControl scaler = new CrossoverScalingControl();
188 
189         JButton plus = new JButton("+");
190         plus.addActionListener(new ActionListener() {
191             public void actionPerformed(ActionEvent e) {
192                 scaler.scale(vv, 1.1f, vv.getCenter());
193             }
194         });
195         JButton minus = new JButton("-");
196         minus.addActionListener(new ActionListener() {
197             public void actionPerformed(ActionEvent e) {
198                 scaler.scale(vv, 1/1.1f, vv.getCenter());
199             }
200         });
201 
202         JButton reset = new JButton("reset");
203         reset.addActionListener(new ActionListener() {
204 
205 			public void actionPerformed(ActionEvent e) {
206 				vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).setToIdentity();
207 				vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).setToIdentity();
208 			}});
209 
210         JPanel controls = new JPanel();
211         controls.add(plus);
212         controls.add(minus);
213         controls.add(reset);
214         add(controls, BorderLayout.SOUTH);
215     }
216     
217     /**
218      * create some vertices
219      * @param count how many to create
220      * @return the Vertices in an array
221      */
222     private void createVertices() {
223         for (String city : map.keySet()) {
224             graph.addVertex(city);
225         }
226     }
227 
228     /**
229      * create edges for this demo graph
230      * @param v an array of Vertices to connect
231      */
232     void createEdges() {
233      	
234     	for(int i=0; i<map.keySet().size()*1.3; i++) {
235     		graph.addEdge(new Double(Math.random()), randomCity(), randomCity(), EdgeType.DIRECTED);
236     	}
237     }
238     
239     private String randomCity() {
240     	int m = cityList.size();
241     	return cityList.get((int)(Math.random()*m));
242     }
243     
244     static class CityTransformer implements Function<String,String[]> {
245 
246     	Map<String,String[]> map;
247     	public CityTransformer(Map<String,String[]> map) {
248     		this.map = map;
249     	}
250 
251     	/**
252     	 * transform airport code to latlon string
253     	 */
254 		public String[] apply(String city) {
255 			return map.get(city);
256 		}
257     }
258     
259     static class LatLonPixelTransformer implements Function<String[],Point2D> {
260     	Dimension d;
261     	int startOffset;
262     	
263     	public LatLonPixelTransformer(Dimension d) {
264     		this.d = d;
265     	}
266     	/**
267     	 * transform a lat
268     	 */
269 		public Point2D apply(String[] latlon) {
270 			double latitude = 0;
271 			double longitude = 0;
272 			String[] lat = latlon[0].split(" ");
273 			String[] lon = latlon[1].split(" ");
274 			latitude = Integer.parseInt(lat[0]) + Integer.parseInt(lat[1])/60f;
275 			latitude *= d.height/180f;
276 			longitude = Integer.parseInt(lon[0]) + Integer.parseInt(lon[1])/60f;
277 			longitude *= d.width/360f;
278 			if(lat[2].equals("N")) {
279 				latitude = d.height / 2 - latitude;
280 				
281 			} else { // assume S
282 				latitude = d.height / 2 + latitude;
283 			}
284 			
285 			if(lon[2].equals("W")) {
286 				longitude = d.width / 2 - longitude;
287 				
288 			} else { // assume E
289 				longitude = d.width / 2 + longitude;
290 			}
291 			
292 			return new Point2D.Double(longitude,latitude);
293 		}
294     	
295     }
296 
297     public static void main(String[] args) {
298         // create a frome to hold the graph
299         final JFrame frame = new JFrame();
300         Container content = frame.getContentPane();
301         content.add(new WorldMapGraphDemo());
302         frame.pack();
303         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
304         frame.setVisible(true);
305     }
306 }