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