/[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 1574 - (show annotations) (download)
Sat Jul 9 17:18:59 2011 UTC (12 years, 10 months ago) by torben
File size: 11026 byte(s)
Postgres can't do a proper sort when name contains scandinavian letters, but java can :)
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 return station;
52 }
53
54 private StationBean convertResultset(ResultSet res) throws SQLException {
55 StationBean stations = new StationBean();
56 while (res.next()) {
57 stations.entries.add( convertSingleRow(res) );
58 }
59 return stations;
60
61 }
62
63 private StationBean fetchStations(String SQL, StatementParamSetter setter) throws SQLException {
64 StationBean stations;
65 Connection conn = null;
66 PreparedStatement stmt = null;
67 ResultSet res = null;
68 try {
69 conn = DBConnection.getConnection();
70 stmt = conn.prepareStatement(SQL);
71
72 setter.setParams(stmt);
73
74 res = stmt.executeQuery();
75 stations = convertResultset(res);
76
77 } finally {
78 if (res != null)
79 res.close();
80 if (stmt != null)
81 stmt.close();
82 if (conn!= null)
83 conn.close();
84 }
85 return stations;
86
87 }
88
89 public StationEntry getById(int id) throws SQLException,NostationException {
90 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog,stationcode_metro,address,0.0 " +
91 "FROM trainstations WHERE id=" + id + " AND enabled=true";
92
93 StationBean stations = fetchStations(SQL, new NullSetter() );
94
95 if (stations.entries.size() > 0 ) {
96 return stations.entries.get(0);
97 } else {
98 throw new NostationException();
99 }
100 }
101
102
103
104 /*
105 * 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) )
106 * create function rlike(text,text) returns bool as
107 * 'select $2 ilike $1' language sql strict immutable;
108 * create operator ~~~ (procedure = rlike, leftarg = text, rightarg = text, commutator = ~~);
109 */
110 public StationBean getByNameNormal(final String name) throws SQLException {
111 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, 0.0 " +
112 "FROM trainstations " +
113 "WHERE (name ILIKE ? OR ? ~~~ ANY(aliases)) AND enabled = true " +
114 "ORDER BY name ";
115
116 class NameSetter implements StatementParamSetter {
117 @Override
118 public void setParams(PreparedStatement stmt) throws SQLException {
119 stmt.setString(1, name + "%");
120 stmt.setString(2, name + "%");
121 }
122 }
123
124 return fetchStations(SQL, new NameSetter() );
125 }
126
127
128 public StationBean getByNameFuzzy(final String name) throws SQLException {
129 String SQL = "SELECT * FROM (" +
130 " SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, 0.0, " +
131 " levenshtein(lower(name),lower(?) ) as leven " +
132 " FROM trainstations " +
133 " WHERE enabled = true ) as lev2 " +
134 "WHERE (leven <= 3) " +
135 "ORDER BY leven " +
136 "LIMIT 1";
137
138 class NameSetter implements StatementParamSetter {
139 @Override
140 public void setParams(PreparedStatement stmt) throws SQLException {
141 stmt.setString(1, name );
142 }
143 }
144
145 StationBean stations = fetchStations(SQL, new NameSetter() );
146 stations.fuzzystrmatch = true;
147 return stations;
148 }
149
150 private String removeSuffix(String str, String suffix) {
151 if (str.endsWith(suffix)) {
152 return str.substring(0, str.length() - suffix.length() );
153 } else {
154 return str;
155 }
156 }
157
158 public StationBean getByName(String name) throws SQLException {
159 name = removeSuffix(name, " st.");
160 name = removeSuffix(name, " st");
161 name = removeSuffix(name, " station");
162
163 StationBean stations = getByNameNormal(name);
164
165 if (stations.entries.size() == 0) {
166 stations = getByNameFuzzy(name);
167
168 logger.info("getByName failover: " + name + "(" + (stations.entries.size() >0) + ")" );
169 }
170 return stations;
171 }
172
173
174
175 //Latitude (horizonal), longitude(vertical) so
176 // 1 degree latitude is ~ 111320 meters, since the distance between the horizonal lines is always the same
177 // 1 degree longitude is ~111320 meters at equator but gets shorter as we get closer to the poles.
178 // so 1 degree longitude is 64.5 km at denmarks southern point (gedser=54.55,11.95)
179 // and is 59.4km at northern point (skagen = 57.75,10.65)
180 // The "hack" with max 0.4 degrees latitude and 0.75 degrees longitude is only valid since we only service danish trains,
181 // in denmark 0.4dg latitude ~ 44km, 0.75dg longitude ~ 47km
182
183 // the ultra fast method (and only slightly inaccurate as long as we only cover a limited geographically area)
184 // is using an aproximation of the length of 1 latitude degree and 1 longitude degree and just use pythagoras to
185 // calculate the distance:
186 // sqrt( power(abs(latitude-?)*111320, 2) + power(abs(longitude-?)*63000,2) )::int as calcdist
187
188 public StationBean getByLocationWorker(final double latitude, final double longitude, final boolean geolimit) throws SQLException {
189
190 String limitExpression = (geolimit == true) ? "AND abs(latitude-?)<0.4 AND abs(longitude-?)<0.75 " : "";
191
192 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, " +
193 "earth_distance( earth_coord, ll_to_earth(?,?))::int AS calcdist " +
194 "FROM trainstations " +
195 "WHERE enabled = true " + limitExpression +
196 "ORDER BY calcdist ASC " +
197 "LIMIT " + LOCATION_LIMIT;
198
199
200 class LatlongSetter implements StatementParamSetter {
201 @Override
202 public void setParams(PreparedStatement stmt) throws SQLException {
203 stmt.setDouble(1, latitude);
204 stmt.setDouble(2, longitude);
205 if (geolimit == true) {
206 stmt.setDouble(3, latitude);
207 stmt.setDouble(4, longitude);
208 }
209 }
210 }
211
212 return fetchStations(SQL, new LatlongSetter() );
213 }
214
215 public StationBean getByLocation(double latitude, double longitude) throws SQLException {
216 StationBean result = getByLocationWorker(latitude, longitude, true);
217
218 if (result.entries.size() < LOCATION_LIMIT) { //failover
219 logger.info("getByLocation failover: " +latitude + "," + longitude);
220
221 result = getByLocationWorker(latitude, longitude, false);
222 }
223
224 return result;
225 }
226
227
228
229 public StationBean getByList(String list) throws SQLException {
230 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog,stationcode_metro, address,0.0 " +
231 "FROM trainstations " +
232 "WHERE id IN " + list + " AND enabled = true " +
233 "ORDER BY name ";
234
235 return fetchStations(SQL, new NullSetter() );
236 }
237
238
239
240 public StationEntry getSimpleByName(final String name) throws SQLException {
241 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, 0.0 " +
242 "FROM trainstations " +
243 "WHERE name = ? AND enabled = true " +
244 "LIMIT 1 ";
245
246 class NameSetter implements StatementParamSetter {
247 @Override
248 public void setParams(PreparedStatement stmt) throws SQLException {
249 stmt.setString(1, name );
250 }
251 }
252
253 StationBean stations = fetchStations(SQL, new NameSetter() );
254
255 if (stations.entries.size() == 1) {
256 return stations.entries.get(0);
257 } else {
258 return null;
259 }
260 }
261
262 Comparator<StationEntry> nameComparator = new Comparator<StationEntry>() {
263 @Override
264 public int compare(StationEntry arg0, StationEntry arg1) {
265 return arg0.getName().compareTo( arg1.getName() );
266 }
267 };
268
269 //used to create full dump in order to populate Google Appengine DB
270 //after 1.1.0 also used to populate client-side station list
271 public StationBean dumpAll() throws SQLException {
272
273 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog,stationcode_metro,address,0.0,aliases " +
274 "FROM trainstations WHERE enabled = true";
275
276 Connection conn = null;
277 Statement stmt = null;
278 ResultSet res = null;
279
280
281 try {
282 conn = DBConnection.getConnection();
283
284 stmt = conn.createStatement();
285 res = stmt.executeQuery(SQL);
286
287 // Does mostly the same as convertResultset()
288 StationBean stations = new StationBean();
289 while (res.next()) {
290 StationEntry entry = convertSingleRow(res);
291
292 Array arr = res.getArray(10);
293 if (arr != null) {
294 String[] aliases = (String[]) arr.getArray();
295 entry.setAliases(aliases);
296 }
297
298 stations.entries.add( entry );
299
300 }
301 Collections.sort( stations.entries,nameComparator );
302 return stations;
303
304
305 } finally {
306 if (res != null)
307 res.close();
308 if (stmt != null)
309 stmt.close();
310 if (conn != null)
311 conn.close();
312 }
313
314 }
315
316 @Deprecated
317 public static String getStationName(int stationID) {
318 String station = "";
319
320 Connection conn = null;
321 try {
322 conn = DBConnection.getConnection();
323 Statement stmt = conn.createStatement();
324 ResultSet rs = stmt.executeQuery("SELECT name FROM trainstations WHERE id=" + stationID);
325 if (rs.next()) {
326 station = rs.getString(1);
327 }
328
329 } catch (Exception e) {
330 } finally {
331 try {
332 if (conn != null && !conn.isClosed())
333 conn.close();
334 } catch (Exception e) {}
335 }
336
337 return station;
338 }
339
340
341
342 @Deprecated
343 public int getIdByName(final String name) throws SQLException {
344 String SQL = "SELECT id,name,latitude,longitude,stationcode_fjrn,stationcode_stog, stationcode_metro, address, 0.0 " +
345 "FROM trainstations " +
346 "WHERE name = ? AND enabled = true " +
347 "LIMIT 1 ";
348
349 class NameSetter implements StatementParamSetter {
350 @Override
351 public void setParams(PreparedStatement stmt) throws SQLException {
352 stmt.setString(1, name );
353 }
354 }
355
356 StationBean stations = fetchStations(SQL, new NameSetter() );
357
358 if (stations.entries.size() == 1) {
359 return stations.entries.get(0).getId();
360 } else {
361 return -1;
362 }
363 }
364 }

  ViewVC Help
Powered by ViewVC 1.1.20