Add a google maps component to the GeoLocation generator.
[zxing.git] / zxing.appspot.com / generator / src / com / google / zxing / web / generator / client / GeoLocationGenerator.java
1 /*
2  * Copyright (C) 2008 ZXing authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.google.zxing.web.generator.client;
18
19 import com.google.gwt.dom.client.Node;
20 import com.google.gwt.dom.client.NodeList;
21 import com.google.gwt.http.client.URL;
22 import com.google.gwt.maps.client.MapWidget;
23 import com.google.gwt.maps.client.control.SmallMapControl;
24 import com.google.gwt.maps.client.event.MapClickHandler;
25 import com.google.gwt.maps.client.event.MarkerDragEndHandler;
26 import com.google.gwt.maps.client.event.MapClickHandler.MapClickEvent;
27 import com.google.gwt.maps.client.geom.LatLng;
28 import com.google.gwt.maps.client.overlay.Marker;
29 import com.google.gwt.maps.client.overlay.MarkerOptions;
30 import com.google.gwt.user.client.ui.Button;
31 import com.google.gwt.user.client.ui.ChangeListener;
32 import com.google.gwt.user.client.ui.ClickListener;
33 import com.google.gwt.user.client.ui.Grid;
34 import com.google.gwt.user.client.ui.SimplePanel;
35 import com.google.gwt.user.client.ui.TextBox;
36 import com.google.gwt.user.client.ui.Widget;
37
38 /**
39  * A generator for geo location. It also accepts a google maps links and
40  * extracts the coordinates and query from the URL.
41  * 
42  * @author Yohann Coppel
43  */
44 public class GeoLocationGenerator implements GeneratorSource, ChangeListener {
45   private static final String LON_REGEXP = "[+-]?[0-9]+(.[0-9]+)?";
46   private static final String LAT_REGEXP = "[+-]?[0-9]+(.[0-9]+)?";
47   
48   Grid table = null;
49   TextBox latitude = new TextBox();
50   TextBox longitude = new TextBox();
51   TextBox query = new TextBox();
52   TextBox mapsLink = new TextBox();
53   MapWidget map = new MapWidget();
54   Marker mapMarker = null;
55   private ChangeListener changeListener;
56   
57   public GeoLocationGenerator(ChangeListener listener) {
58     this.changeListener = listener;
59     latitude.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
60     longitude.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
61     latitude.addChangeListener(listener);
62     latitude.addChangeListener(this);
63     longitude.addChangeListener(listener);
64     longitude.addChangeListener(this);
65     query.addChangeListener(listener);
66   }
67   
68   public String getName() {
69     return "Geo location";
70   }
71
72   public String getText() throws GeneratorException {
73     String que = getQueryField();
74     if (null != que && que.length() > 0) {
75       if (null == getLatitudeField()) {
76         latitude.setText("0");
77       }
78       if (null == getLongitudeField()) {
79         longitude.setText("0");
80       }
81     }
82     String lat = getLatitudeField();
83     String lon = getLongitudeField();
84     
85     if (que.length() > 0) {
86       return "geo:"+lat+","+lon+"?q="+que;
87     }
88
89     return "geo:"+lat+","+lon;
90   }
91
92   private String getQueryField() {
93     String que = query.getText();
94     que = que.replace("&", "%26");
95     return que;
96   }
97
98   private String getLongitudeField() throws GeneratorException {
99     String lon = longitude.getText();
100     if (!lon.matches(LON_REGEXP)) {
101       throw new GeneratorException("Longitude is not a correct value.");
102     }
103     double val = Double.parseDouble(lon);
104     if (val < -180 || val > 180) {
105       throw new GeneratorException("Longitude must be in [-180:180]");
106     }
107     return lon;
108   }
109
110   private String getLatitudeField() throws GeneratorException {
111     String lat = latitude.getText();
112     if (!lat.matches(LAT_REGEXP)) {
113       throw new GeneratorException("Latitude is not a correct value.");
114     }
115     double val = Double.parseDouble(lat);
116     if (val < -90 || val > 90) {
117       throw new GeneratorException("Latitude must be in [-90:90]");
118     }
119     return lat;
120   }
121
122   public Grid getWidget() {
123     if (table != null) {
124       return table;
125     }
126     table = new Grid(7, 2);
127     
128     table.setText(0, 0, "Latitude");
129     table.setWidget(0, 1, latitude);
130     table.setText(1, 0, "Longitude");
131     table.setWidget(1, 1, longitude);
132     table.setText(2, 0, "Query");
133     table.setWidget(2, 1, query);
134     table.setText(3, 0, "OR");
135     table.setText(3, 1, "enter a Google Maps link and click Fill:");
136     // looks like this:
137     // http://maps.google.com/?ie=UTF8&ll=40.741404,-74.00322&spn=0.001484,0.003101&z=18
138     Button fill = new Button("Fill &uarr;");
139     fill.addClickListener(new ClickListener() {
140       public void onClick(Widget sender) {
141         fillWithMaps();
142       }
143     });
144     table.setWidget(4, 0, fill);
145     table.setWidget(4, 1, mapsLink);
146     
147     map.setSize("256px", "256px");
148     map.addControl(new SmallMapControl());
149     map.addMapClickHandler(new MapClickHandler() {
150       public void onClick(MapClickEvent event) {
151         mapClick(event);
152       }
153     });
154     table.setText(5, 0, "OR");
155     table.setText(5, 1, "use the map to select a location:");
156     SimplePanel sp = new SimplePanel();
157     sp.add(map);
158     table.setWidget(6, 1, sp);
159     return table;
160   }
161
162   protected void mapClick(MapClickEvent event) {
163     latitude.setText("" + event.getLatLng().getLatitude());
164     longitude.setText("" + event.getLatLng().getLongitude());
165     setMapMarker(event.getLatLng().getLatitude(), event.getLatLng().getLongitude(), false);
166     changeListener.onChange(latitude);
167     changeListener.onChange(longitude);
168   }
169   
170   protected void mapMarkerMoved() {
171     latitude.setText("" + mapMarker.getLatLng().getLatitude());
172     longitude.setText("" + mapMarker.getLatLng().getLongitude());
173     changeListener.onChange(latitude);
174     changeListener.onChange(longitude);
175   }
176   
177   protected void setMapMarker(double lat, double lon, boolean zoomAndCenter) {
178     if (mapMarker != null) {
179       map.removeOverlay(mapMarker);
180     }
181     LatLng ll = LatLng.newInstance(lat, lon);
182     if (zoomAndCenter) {
183       map.setCenter(ll);
184       map.setZoomLevel(12);
185     }
186     if (mapMarker != null) {
187       mapMarker.setLatLng(ll);
188     } else {
189       MarkerOptions opt = MarkerOptions.newInstance();
190       opt.setDraggable(true);
191       mapMarker = new Marker(ll, opt);
192       mapMarker.addMarkerDragEndHandler(new MarkerDragEndHandler() {
193         public void onDragEnd(MarkerDragEndEvent event) {
194           mapMarkerMoved();
195         }
196       });
197     }
198     map.addOverlay(mapMarker);  
199   }
200
201   protected void fillWithMaps() {
202     String link = mapsLink.getText();
203     if (!link.matches("http://maps.google.com/.*")) {
204       return;
205     }
206     String q = "";
207     if (link.matches(".*&q=[^&]*&.*")) {
208       for (int i = link.indexOf("&q=") + 3;
209           i < link.length() && link.charAt(i) != '&'; ++i) {
210         q += link.charAt(i);
211       }
212       // special cases:
213       q = q.replace("+", " ");
214       q = q.replace("%26", "&");
215     }
216     
217     String lat = "";
218     String lon = "";
219     if (link.matches(".*&s?ll=[^&]*&.*")) {
220       boolean beforeComa = true;
221       int start = 0;
222       if (link.indexOf("&sll=") == -1) {
223         start = link.indexOf("&ll=") + 4;
224       } else {
225         start = link.indexOf("&sll=") + 5;
226       }
227       for (int i = start; i < link.length() && link.charAt(i) != '&'; ++i) {
228         if (beforeComa) {
229           if (link.charAt(i) == ',') {
230             beforeComa = false;
231           } else {
232             lat += link.charAt(i); 
233           }
234         } else {
235           lon += link.charAt(i);
236         }
237       }
238     }
239     
240     query.setText(URL.decode(q));
241     latitude.setText(lat);
242     longitude.setText(lon);
243     changeListener.onChange(latitude);
244     changeListener.onChange(longitude);
245     this.onChange(latitude);
246   }
247
248   public void validate(Widget widget) throws GeneratorException {
249     if (widget == latitude) getLatitudeField();
250     if (widget == longitude) getLongitudeField();
251   }
252
253   public void setFocus() {
254     latitude.setFocus(true);
255   }
256
257   public void onChange(Widget sender) {
258     if (sender == latitude || sender == longitude) {
259       try {
260         double lat = Double.parseDouble(getLatitudeField());
261         double lon = Double.parseDouble(getLongitudeField());
262         setMapMarker(lat, lon, true);
263       } catch (NumberFormatException e) {
264       } catch (GeneratorException e) {
265       }
266     }
267   }
268 }