/[projects]/android/TrainInfoService/src/dk/thoerup/traininfoservice/db/StationDAO.java
ViewVC logotype

Contents of /android/TrainInfoService/src/dk/thoerup/traininfoservice/db/StationDAO.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1790 - (show annotations) (download)
Wed Apr 11 10:11:38 2012 UTC (12 years, 1 month ago) by torben
File size: 11878 byte(s)
Also extract aliases from DB to XML response
1 package dk.thoerup.traininfoservice.db;
2
3 import java.sql.Array;
4 import java.sql.Connection;
5 import java.sql.PreparedStatement;
6 import java.sql.ResultSet;
7 import java.sql.SQLException;
8 import java.sql.Statement;
9 import java.util.Collections;
10 import java.util.Comparator;
11 import java.util.logging.Logger;
12
13 import dk.thoerup.android.traininfo.common.StationBean;
14 import dk.thoerup.android.traininfo.common.StationEntry;
15
16 public class StationDAO {
17
18 private interface StatementParamSetter {
19 public void setParams(PreparedStatement stmt) throws SQLException ;
20 }
21 private class NullSetter implements StatementParamSetter {
22 @Override
23 public void setParams(PreparedStatement stmt) throws SQLException {}
24 }
25
26 public static class NostationException extends Exception {
27 private static final long serialVersionUID = 1L;
28 }
29
30 final static int LOCATION_LIMIT = 8;
31 static final Logger logger = Logger.getLogger(StationDAO.class.getName());
32
33
34 private StationEntry convertSingleRow(ResultSet res) throws SQLException {
35 StationEntry station = new StationEntry();
36
37 station.setId( res.getInt(1) );
38 station.setName( res.getString(2) );
39 station.setLatitude( res.getDouble(3) );
40 station.setLongitude( res.getDouble(4) );
41 station.setRegional( res.getString(5) );
42 station.setStrain( res.getString(6) );
43 station.setMetro( res.getString(7) );
44 station.setAddress( res.getString(8) );
45 station.setCalcdist( (int)res.getDouble(9) );
46
47 station.setIsRegional( station.getRegional() != null );
48 station.setIsStrain( station.getStrain() != null );
49 station.setIsMetro( station.getMetro() != null );
50
51 Array aliases = res.getArray( 10);
52 if (aliases != null) {
53 station.setAliases( (String[]) aliases.getArray() );
54 } else {
55 String[] emptyArray = {};
56 station.setAliases( emptyArray );
57 }
58
59 return station;
60 }
61
62 private StationBean convertResultset(ResultSet res) throws SQLException {
63 StationBean stations = new StationBean();
64 while (res.next()) {
65 stations.entries.add( convertSingleRow(res) );
66 }
67 return stations;
68
69 }
70
71 private StationBean fetchStations(String SQL, StatementParamSetter setter) throws SQLException {
72 StationBean stations;
73 Connection conn = null;
74 PreparedStatement stmt = null;
75 ResultSet res = null;
76 try {
77 conn = DBConnection.getConnection();
78 stmt = conn.prepareStatement(SQL);
79
80 setter.setParams(stmt);
81
82 res = stmt.executeQuery();
83 stations = convertResultset(res);
84
85 } finally {
86 if (res != null)
87 res.close();
88 if (stmt != null)
89 stmt.close();
90 if (conn!= null)
91 conn.close();
92 }
93 return stations;
94
95 }
96
97 public StationEntry getById(int id) throws SQLException,NostationException {
98 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog,stationcode_metro,address,0.0,aliases " +
99 "FROM trainstations WHERE id=" + id + " AND enabled=true";
100
101 StationBean stations = fetchStations(SQL, new NullSetter() );
102
103 if (stations.entries.size() > 0 ) {
104 return stations.entries.get(0);
105 } else {
106 throw new NostationException();
107 }
108 }
109
110
111
112 /*
113 * this code requires theses statements are run on database in order to do ILIKE searches against aliases (which is defines as array of varchar(64) )
114 * create function rlike(text,text) returns bool as
115 * 'select $2 ilike $1' language sql strict immutable;
116 * create operator ~~~ (procedure = rlike, leftarg = text, rightarg = text, commutator = ~~);
117 */
118 public StationBean getByNameNormal(final String name) throws SQLException {
119 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, 0.0,aliases " +
120 "FROM trainstations " +
121 "WHERE (name ILIKE ? OR ? ~~~ ANY(aliases)) AND enabled = true " +
122 "ORDER BY name ";
123
124 class NameSetter implements StatementParamSetter {
125 @Override
126 public void setParams(PreparedStatement stmt) throws SQLException {
127 stmt.setString(1, name + "%");
128 stmt.setString(2, name + "%");
129 }
130 }
131
132 return fetchStations(SQL, new NameSetter() );
133 }
134
135
136 public StationBean getByNameFuzzy(final String name) throws SQLException {
137 String SQL = "SELECT * FROM (" +
138 " SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, 0.0, aliases, " +
139 " levenshtein(lower(name),lower(?) ) as leven " +
140 " FROM trainstations " +
141 " WHERE enabled = true ) as lev2 " +
142 "WHERE (leven <= 3) " +
143 "ORDER BY leven " +
144 "LIMIT 1";
145
146 class NameSetter implements StatementParamSetter {
147 @Override
148 public void setParams(PreparedStatement stmt) throws SQLException {
149 stmt.setString(1, name );
150 }
151 }
152
153 StationBean stations = fetchStations(SQL, new NameSetter() );
154 stations.fuzzystrmatch = true;
155 return stations;
156 }
157
158 private String removeSuffix(String str, String suffix) {
159 if (str.endsWith(suffix)) {
160 return str.substring(0, str.length() - suffix.length() );
161 } else {
162 return str;
163 }
164 }
165
166 public StationBean getByName(String name) throws SQLException {
167 name = removeSuffix(name, " st.");
168 name = removeSuffix(name, " st");
169 name = removeSuffix(name, " station");
170
171 StationBean stations = getByNameNormal(name);
172
173 if (stations.entries.size() == 0) {
174 stations = getByNameFuzzy(name);
175
176 logger.info("getByName failover: " + name + "(" + (stations.entries.size() >0) + ")" );
177 }
178 return stations;
179 }
180
181
182
183 //Latitude (horizonal), longitude(vertical) so
184 // 1 degree latitude is ~ 111320 meters, since the distance between the horizonal lines is always the same
185 // 1 degree longitude is ~111320 meters at equator but gets shorter as we get closer to the poles.
186 // so 1 degree longitude is 64.5 km at denmarks southern point (gedser=54.55,11.95)
187 // and is 59.4km at northern point (skagen = 57.75,10.65)
188 // The "hack" with max 0.4 degrees latitude and 0.75 degrees longitude is only valid since we only service danish trains,
189 // in denmark 0.4dg latitude ~ 44km, 0.75dg longitude ~ 47km
190
191 // the ultra fast method (and only slightly inaccurate as long as we only cover a limited geographically area)
192 // is using an aproximation of the length of 1 latitude degree and 1 longitude degree and just use pythagoras to
193 // calculate the distance:
194 // sqrt( power(abs(latitude-?)*111320, 2) + power(abs(longitude-?)*63000,2) )::int as calcdist
195
196 public StationBean getByLocationWorker(final double latitude, final double longitude, final boolean geolimit) throws SQLException {
197
198 String limitExpression = (geolimit == true) ? "AND abs(latitude-?)<0.4 AND abs(longitude-?)<0.75 " : "";
199
200 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, " +
201 "earth_distance( earth_coord, ll_to_earth(?,?))::int AS calcdist,aliases " +
202 "FROM trainstations " +
203 "WHERE enabled = true " + limitExpression +
204 "ORDER BY calcdist ASC " +
205 "LIMIT " + LOCATION_LIMIT;
206
207
208 class LatlongSetter implements StatementParamSetter {
209 @Override
210 public void setParams(PreparedStatement stmt) throws SQLException {
211 stmt.setDouble(1, latitude);
212 stmt.setDouble(2, longitude);
213 if (geolimit == true) {
214 stmt.setDouble(3, latitude);
215 stmt.setDouble(4, longitude);
216 }
217 }
218 }
219
220 return fetchStations(SQL, new LatlongSetter() );
221 }
222
223 public StationBean getByLocation(double latitude, double longitude) throws SQLException {
224 StationBean result = getByLocationWorker(latitude, longitude, true);
225
226 if (result.entries.size() < LOCATION_LIMIT) { //failover
227 logger.info("getByLocation failover: " +latitude + "," + longitude);
228
229 result = getByLocationWorker(latitude, longitude, false);
230 }
231
232 return result;
233 }
234
235
236
237 public StationBean getByList(String list) throws SQLException {
238 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog,stationcode_metro, address,0.0,aliases " +
239 "FROM trainstations " +
240 "WHERE id IN " + list + " AND enabled = true " +
241 "ORDER BY name ";
242
243 return fetchStations(SQL, new NullSetter() );
244 }
245
246
247
248 public StationEntry getSimpleByName(final String name) throws SQLException {
249 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, 0.0,aliases " +
250 "FROM trainstations " +
251 "WHERE name = ? AND enabled = true " +
252 "LIMIT 1 ";
253
254 class NameSetter implements StatementParamSetter {
255 @Override
256 public void setParams(PreparedStatement stmt) throws SQLException {
257 stmt.setString(1, name );
258 }
259 }
260
261 StationBean stations = fetchStations(SQL, new NameSetter() );
262
263 if (stations.entries.size() == 1) {
264 return stations.entries.get(0);
265 } else {
266 return null;
267 }
268 }
269
270 Comparator<StationEntry> nameComparator = new Comparator<StationEntry>() {
271 @Override
272 public int compare(StationEntry arg0, StationEntry arg1) {
273 return arg0.getName().compareTo( arg1.getName() );
274 }
275 };
276
277 //used to create full dump in order to populate Google Appengine DB
278 //after 1.1.0 also used to populate client-side station list
279 public StationBean dumpAll() throws SQLException {
280
281 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog,stationcode_metro,address,0.0,aliases " +
282 "FROM trainstations WHERE enabled = true";
283
284
285 StationBean stations = fetchStations(SQL, new NullSetter() );
286 Collections.sort( stations.entries,nameComparator );
287
288 return stations;
289
290 /*
291 Connection conn = null;
292 Statement stmt = null;
293 ResultSet res = null;
294
295
296 try {
297 conn = DBConnection.getConnection();
298
299 stmt = conn.createStatement();
300 res = stmt.executeQuery(SQL);
301
302 // Does mostly the same as convertResultset()
303 StationBean stations = new StationBean();
304 while (res.next()) {
305 StationEntry entry = convertSingleRow(res);
306
307 Array arr = res.getArray(10);
308 if (arr != null) {
309 String[] aliases = (String[]) arr.getArray();
310 entry.setAliases(aliases);
311 }
312
313 stations.entries.add( entry );
314
315 }
316 Collections.sort( stations.entries,nameComparator );
317 return stations;
318
319
320 } finally {
321 if (res != null)
322 res.close();
323 if (stmt != null)
324 stmt.close();
325 if (conn != null)
326 conn.close();
327 }*/
328
329 }
330
331 @Deprecated
332 public static String getStationName(int stationID) {
333 String station = "";
334
335 Connection conn = null;
336 try {
337 conn = DBConnection.getConnection();
338 Statement stmt = conn.createStatement();
339 ResultSet rs = stmt.executeQuery("SELECT name FROM trainstations WHERE id=" + stationID);
340 if (rs.next()) {
341 station = rs.getString(1);
342 }
343
344 } catch (Exception e) {
345 } finally {
346 try {
347 if (conn != null && !conn.isClosed())
348 conn.close();
349 } catch (Exception e) {}
350 }
351
352 return station;
353 }
354
355 public boolean hasDisabledStation(final String name) throws SQLException {
356 String SQL = "Select count(*) as antal from trainstations where name = '" + name + "' and enabled=false " ;
357
358 Connection conn = DBConnection.getConnection();
359 Statement stmt = conn.createStatement();
360 ResultSet rs = stmt.executeQuery( SQL );
361
362 rs.next();
363
364 int antal = rs.getInt(1);
365
366 rs.close();
367 stmt.close();
368 conn.close();
369
370 return (antal > 0);
371 }
372
373
374 @Deprecated
375 public int getIdByName(final String name) throws SQLException {
376 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, 0.0 " +
377 "FROM trainstations " +
378 "WHERE name = ? AND enabled = true " +
379 "LIMIT 1 ";
380
381 class NameSetter implements StatementParamSetter {
382 @Override
383 public void setParams(PreparedStatement stmt) throws SQLException {
384 stmt.setString(1, name );
385 }
386 }
387
388 StationBean stations = fetchStations(SQL, new NameSetter() );
389
390 if (stations.entries.size() == 1) {
391 return stations.entries.get(0).getId();
392 } else {
393 return -1;
394 }
395 }
396 }

  ViewVC Help
Powered by ViewVC 1.1.20