View Javadoc

1   /*$Id: StdSqlMaker.java,v 1.6 2004/11/03 01:35:18 mch Exp $
2    * Created on 27-Nov-2003
3    *
4    * Copyright (C) AstroGrid. All rights reserved.
5    *
6    * This software is published under the terms of the AstroGrid
7    * Software License version 1.2, a copy of which has been included
8    * with this distribution in the LICENSE.txt file.
9    *
10   **/
11  package org.astrogrid.datacenter.queriers.sql;
12  
13  import java.io.FileInputStream;
14  import java.io.IOException;
15  import java.io.InputStream;
16  import java.io.StringWriter;
17  import java.util.StringTokenizer;
18  import javax.xml.parsers.ParserConfigurationException;
19  import javax.xml.transform.Transformer;
20  import javax.xml.transform.TransformerConfigurationException;
21  import javax.xml.transform.TransformerException;
22  import javax.xml.transform.TransformerFactory;
23  import javax.xml.transform.dom.DOMSource;
24  import javax.xml.transform.stream.StreamResult;
25  import javax.xml.transform.stream.StreamSource;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.astrogrid.config.ConfigException;
29  import org.astrogrid.config.SimpleConfig;
30  import org.astrogrid.datacenter.query.Adql074Writer;
31  import org.astrogrid.datacenter.query.Query;
32  import org.astrogrid.datacenter.query.QueryException;
33  import org.astrogrid.datacenter.query.condition.Function;
34  import org.astrogrid.datacenter.sky.Angle;
35  import org.astrogrid.util.DomHelper;
36  import org.w3c.dom.Document;
37  import org.w3c.dom.Element;
38  import org.xml.sax.SAXException;
39  
40  /***
41   * A 'standard' translator that creates 'standard' SQL
42   */
43  public class StdSqlMaker  extends AdqlSqlMaker {
44  
45     
46     private static final Log log = LogFactory.getLog(StdSqlMaker.class);
47     
48      /*** Returns the SQL condition for a circle based on the columns in the
49      * configuration file
50      */
51     public String makeSqlCircleCondition(Angle ra, Angle dec, Angle radius) {
52        String table = SimpleConfig.getSingleton().getString(CONE_SEARCH_TABLE_KEY);
53        
54        //get which columns given RA & DEC for cone searches
55        String raCol  = table+"."+SimpleConfig.getSingleton().getString(CONE_SEARCH_RA_COL_KEY);
56        String decCol = table+"."+SimpleConfig.getSingleton().getString(CONE_SEARCH_DEC_COL_KEY);
57        
58        return makeSqlCircleCondition(raCol, decCol, ra, dec, radius);
59     }
60     
61     /*** Returns the SQL condition expression for a circle.  This circle is
62      * 'flat' on the sphere, when vieweing the sphere from the center.  ie, the
63      * radius is a declination angle from the given RA & DEC point.  This means
64      * the circle is distorted in coordinate space
65      */
66     public String makeSqlCircleCondition(String raCol, String decCol, Angle ra, Angle dec, Angle radius) {
67        //Some db functions take radians, some degrees.  Fail if the config is not
68        //specified, so we force people to get it right...
69        boolean funcsInRads = SimpleConfig.getSingleton().getBoolean(DB_TRIGFUNCS_IN_RADIANS);
70        
71        String raColRad = makeColumnRadians(raCol);
72        String decColRad = makeColumnRadians(decCol);
73        
74        //naively, we could use the 'least squares' distance (pythagoros) to see if
75        //the objects are within radius distance. However this doesn't work well
76        //except very near the equator, and is useless over the poles.
77        
78        
79        //'haversine' distance formulae.  The correct one to use...
80        if (funcsInRads) {
81           return
82              makeSqlBoundsCondition(raCol, decCol, ra, dec, radius) + " AND "+
83              "("+
84              "(2 * ASIN( SQRT( "+
85              //surround dec with extra brackets in order to cope with negative decs
86              "POWER( SIN( ("+decColRad+" - ("+dec.asRadians()+") ) / 2 ) ,2) + "+
87              "COS("+dec.asRadians()+") * COS("+decColRad+") * "+
88              "POWER( SIN( ("+raColRad+" - "+ra.asRadians()+") / 2 ), 2) "+
89              "))) < "+radius.asRadians()+
90              ")";
91        }
92        else {
93           throw new UnsupportedOperationException("Not done degree funcs yet - do they exist?");
94        }
95        /***/
96     }
97  
98     /*** Returns a SQL condition expression to 'bound' a given circle. Otherwise circle queries
99      * query is likely to take a very very long time as it trawls through all the rows calculating
100     * the distance.
101     * <p> At the moment this is just a DEC-binding (which is easier than RA :-)
102     * @param raCol, decCol - the column names that contain the RA & DEC values of the objects
103     */
104     public String makeSqlBoundsCondition(String raCol, String decCol, Angle ra, Angle dec, Angle radius) {
105    
106        String colUnits = SimpleConfig.getSingleton().getString(CONE_SEARCH_COL_UNITS_KEY);
107       
108       if (dec.asDegrees() - radius.asDegrees() < -90) {
109          //circle includes north pole.  Add bounds as just a dec limit.
110          return "("+decCol+"<"+getAngleInColUnits(Angle.fromRadians(dec.asRadians()+radius.asRadians()))+")";
111       }
112    
113       if (dec.asDegrees() + radius.asDegrees() > +90) {
114          //circle includes south pole. Add bounds as just a dec limit
115          return "("+decCol+">"+getAngleInColUnits(Angle.fromRadians(dec.asRadians()-radius.asRadians()))+")";
116       }
117 
118       //make upper and lower limit
119       return "( ("+decCol+"<"+getAngleInColUnits(Angle.fromRadians(dec.asRadians()+radius.asRadians()))+") "+
120             " and "+
121               " ("+decCol+">"+getAngleInColUnits(Angle.fromRadians(dec.asRadians()-radius.asRadians()))+") )";
122     
123     }
124    
125    
126    /***
127     * Returns the right SQL to translate a conesearch column to radians
128     */
129    public String makeColumnRadians(String colName) {
130       String colUnits = SimpleConfig.getSingleton().getString(CONE_SEARCH_COL_UNITS_KEY).trim().toLowerCase();
131       
132       if (colUnits.equals("rad")) {
133          return colName;
134       }
135       else if (colUnits.equals("deg")) {
136          return "RADIANS("+colName+")";
137       }
138       else if (colUnits.equals("marcsec")) {
139          return "RADIANS("+colName+"*360000)";
140       }
141       else {
142          throw new ConfigException("Unknown units '"+colUnits+"' for conesearch columns, only 'rad', 'deg' or 'marcsec' supported");
143       }
144    }
145    
146    /*** Returns the given angle in the column's units.   Useful for making SQL a bit
147     * simpler - convert the angles to the column units rather than vice versa with functions */
148    public String getAngleInColUnits(Angle givenAngle) {
149       String colUnits = SimpleConfig.getSingleton().getString(CONE_SEARCH_COL_UNITS_KEY).trim().toLowerCase();
150       if (colUnits.equals("rad")) {
151          return ""+givenAngle.asRadians();
152       }
153       else if (colUnits.equals("deg")) {
154          return ""+givenAngle.asDegrees();
155       }
156       else if (colUnits.equals("marcsec")) {
157          return ""+(givenAngle.asArcSecs()*1000);
158       }
159       else {
160          throw new ConfigException("Unknown units '"+colUnits+"' for conesearch columns, only 'rad', 'deg' or 'marcsec' supported");
161       }
162    }
163    
164    
165    /***
166     * Constructs an SQL statement for the given ADQL document by getting the
167     * (super) ADQL/sql and replacing the region
168     */
169    public String makeSql(Query query) throws QueryException {
170       String sql = super.makeSql(query);
171       
172       sql = replaceRegion(sql);
173       return sql;
174    }
175    
176    
177    /***
178     * Replaces the REGION() function with the cone search SQL.
179     * This is a bit messy as we're reparsing the SQL to replace the function
180     * but we need access to the configuration for this...
181     */
182    public String replaceRegion(String sql) {
183       
184       int start = sql.toLowerCase().indexOf("region");
185       
186       if (start==-1) {
187          return sql;
188       }
189       
190       int end = sql.indexOf(")", start);
191       
192       int argStart=sql.indexOf("'", start);
193       int argEnd=sql.indexOf("'", argStart+1);
194       
195       String regionArg = sql.substring(argStart+1, end-1).trim().toLowerCase();
196       
197       StringTokenizer s = new StringTokenizer(regionArg, " ");
198       String shape = s.nextToken();
199       
200       if (shape.equals("circle")) {
201          //parse out arguments
202          String type = s.nextToken();
203          if (type.equals("j2000")) {
204             Angle ra = Angle.fromDegrees(Double.parseDouble(s.nextToken()));
205             Angle dec = Angle.fromDegrees(Double.parseDouble(s.nextToken()));
206             Angle radius = Angle.fromDegrees(Double.parseDouble(s.nextToken()));
207             
208             return sql.substring(0,start)+
209                makeSqlCircleCondition(ra, dec, radius)+
210                sql.substring(end+1);
211             
212          }
213          else if (type.equals("cartesian")) {
214             throw new UnsupportedOperationException("Can't yet do cartesian circles");
215          }
216          else {
217             throw new QueryException("Unknown circle type: "+type);
218          }
219          
220       }
221       else {
222          throw new QueryException("Unknown region shape: "+regionArg);
223       }
224    }
225 }
226 
227 
228 /*
229  $Log: StdSqlMaker.java,v $
230  Revision 1.6  2004/11/03 01:35:18  mch
231  PAL_MCH_Candidate2 merge Part II
232 
233  Revision 1.2.2.4  2004/11/01 12:25:29  mch
234  Fix for dec -90 to +90
235 
236  Revision 1.2.2.3  2004/10/27 00:43:39  mch
237  Started adding getCount, some resource fixes, some jsps
238 
239  Revision 1.2.2.2  2004/10/22 14:34:56  mch
240  fixes for limiting sql on ms sql server
241 
242  Revision 1.2.2.1  2004/10/22 09:05:15  mch
243  Moved SqlMakers back to server
244 
245  Revision 1.7.6.1  2004/10/21 16:14:21  mch
246  Changes to take home
247 
248  Revision 1.7  2004/10/18 13:11:30  mch
249  Lumpy Merge
250 
251  Revision 1.6.2.1  2004/10/15 19:59:05  mch
252  Lots of changes during trip to CDS to improve int test pass rate
253 
254  Revision 1.6  2004/10/13 01:30:58  mch
255  Added adqlsql (keeps CIRCLE)
256 
257  Revision 1.5  2004/10/12 22:45:45  mch
258  Added spaces around operators so SqlParser can work with it
259 
260  Revision 1.4  2004/10/08 15:19:36  mch
261  Removed unnecessary imports
262 
263  Revision 1.3  2004/10/07 10:34:44  mch
264  Fixes to Cone maker functions and reading/writing String comparisons from Query
265 
266  Revision 1.2  2004/10/06 22:24:58  mch
267  Fixed wrong brackets in replaceRegion
268 
269  Revision 1.1  2004/10/06 21:12:16  mch
270  Big Lump of changes to pass Query OM around instead of Query subclasses, and TargetIndicator mixed into Slinger
271 
272  Revision 1.1  2004/09/28 15:02:13  mch
273  Merged PAL and server packages
274 
275  Revision 1.24  2004/09/08 21:55:14  mch
276  Uncommented SQL/ADQL tests
277 
278  Revision 1.23  2004/09/07 13:22:26  mch
279  Throws better error if ADQL 0.5 is submitted
280 
281  Revision 1.22  2004/09/07 02:28:29  mch
282  Removed ADQL 0.5 tests
283 
284  Revision 1.21  2004/09/07 00:54:20  mch
285  Tidied up Querier/Plugin/Results, and removed deprecated SPI-visitor-SQL-translator
286 
287  Revision 1.20  2004/08/27 09:31:16  mch
288  Added limit, order by, some page tidying, etc
289 
290  Revision 1.19  2004/08/24 20:08:31  mch
291  Moved xslts to classpaths to fix ADQL-SQL translation problems, slightly improved query builder pages
292 
293  Revision 1.18  2004/08/24 19:06:44  mch
294  Improvements to JSP pages, lots to query building and translating
295 
296  Revision 1.17  2004/08/24 12:55:09  mch
297  Minor fixes to xslt translator
298 
299  Revision 1.16  2004/08/06 12:04:19  mch
300  Added unit description to conesearch columns to cope with ESO milliarcseconds (& others in future)
301 
302  Revision 1.6.10.1  2004/08/05 17:57:08  mch
303  Merging Itn06 fixes into Itn05
304 
305  Revision 1.15  2004/08/05 10:56:56  mch
306  Fix for negative dec
307 
308  Revision 1.14  2004/08/05 09:52:59  mch
309  Removed aql073 and added 08
310 
311  Revision 1.13  2004/08/02 11:35:03  mch
312  Commented out incorrect square bounds maker
313 
314  Revision 1.12  2004/07/14 18:04:25  mch
315  Fixed SQL/Angles
316 
317  Revision 1.11  2004/07/12 23:26:51  mch
318  Fixed (somewhat) SQL for cone searches, added tests to Dummy DB
319 
320  Revision 1.10  2004/07/12 14:12:04  mch
321  Fixed ADQL 0.7.4 xslt
322 
323  Revision 1.9  2004/07/07 19:33:59  mch
324  Fixes to get Dummy db working and xslt sheets working both for unit tests and deployed
325 
326  Revision 1.8  2004/07/06 18:48:34  mch
327  Series of unit test fixes
328 
329  Revision 1.7  2004/07/01 23:07:14  mch
330  Introduced metadata generator
331 
332  Revision 1.6  2004/03/17 21:03:20  mch
333  Added SQL transformation tests
334 
335  Revision 1.5  2004/03/17 18:03:20  mch
336  Added v0.8 ADQL
337 
338  Revision 1.4  2004/03/17 01:47:26  mch
339  Added v05 Axis web interface
340 
341  Revision 1.3  2004/03/14 16:55:48  mch
342  Added XSLT ADQL->SQL support
343 
344  Revision 1.2  2004/03/12 20:04:57  mch
345  It05 Refactor (Client)
346 
347  Revision 1.1  2004/03/12 04:45:26  mch
348  It05 MCH Refactor
349 
350  
351  */
352 
353 
354