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