View Javadoc

1   /*
2    * $Id IndexGenerator.java $
3    *
4    */
5   
6   package org.astrogrid.datacenter.queriers.fits;
7   
8   
9   /***
10   * A standalone tool that generates an index based on the fits files it is
11   * is pointed at.
12   *
13   * @author M Hill
14   */
15  
16  import java.io.*;
17  import org.astrogrid.datacenter.fits.*;
18  
19  import java.net.HttpURLConnection;
20  import java.net.MalformedURLException;
21  import java.net.URL;
22  import java.text.DateFormat;
23  import java.text.ParseException;
24  import java.text.SimpleDateFormat;
25  import java.util.Date;
26  import java.util.Enumeration;
27  import java.util.Locale;
28  import javax.xml.parsers.ParserConfigurationException;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.astrogrid.datacenter.queriers.QuerierPluginException;
32  import org.astrogrid.util.DomHelper;
33  import org.w3c.dom.Document;
34  import org.xml.sax.SAXException;
35  
36  
37  public class IndexGenerator
38  {
39     /*** this is meant to run as a command-line program, so output should go to the
40      * console, but it uses routines that use commons-logging anyway...
41      */
42     public static Log log = LogFactory.getLog(IndexGenerator.class);
43     
44     /*** Should we test for extensions.  There is no standard way of checking if
45      * a FITS file has extensions (the EXTEND keyword merely says that it *may*
46      * have extensions), and for large FITS files where you *know* there are no
47      * extensions, this can take some time.  So set this to false */
48     public boolean checkForExtensions = true;
49  
50     /*** Configures which axis is the RA (if any) */
51     public int raAxis = -1;
52  
53     /*** Configures which axis is the DEC (if any) */
54     public int decAxis = -1;
55  
56     /*** Configures the format of dates in the fits file headers so they can be parsed*/
57     public DateFormat fitsDateFormat = DateFormat.getDateTimeInstance();
58  
59     /*** Defines the format of the dates in the index - although they will also be output in seconds */
60     public SimpleDateFormat indexDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
61  
62     /*** The root element */
63     public static final String ROOT_START = "<FitsIndex>";
64     public static final String ROOT_END = "</FitsIndex>";
65     
66     /***
67      * Generates a single index FitsFile 'snippet' for the FITS file at the
68      * given url
69      */
70     public String makeIndexSnippet(URL fitsUrl) throws IOException
71     {
72        assert fitsUrl != null;
73        
74        log.info("Examining file "+fitsUrl+"...");
75        return makeIndexSnippet(new FitsStreamReader(fitsUrl), fitsUrl.toString());
76     }
77     
78     /***
79      * Generates an index 'snippet' for the FITS file at the
80      * given url
81      * @todo tidy up so that, for example, multiline comments become one tag
82      * @TODO max pixels are WRONG
83      */
84     public String makeIndexSnippet(FitsReader reader, String filename) throws IOException
85     {
86        StringBuffer snippet = new StringBuffer();
87        
88        FitsHeader header = new FitsHeader();
89        reader.readHeaderKeywords(header, null);
90  
91        snippet.append(makeIndexSnippet(header, filename));
92  
93        FitsHdu primaryHdu = new FitsHdu(header);
94  
95        //now look for extensions
96        if ((checkForExtensions) && primaryHdu.getHeader().getValue("EXTEND").equals("T"))
97        {
98           //read data into HDU
99           reader.readData(primaryHdu);
100 
101          try {
102             //*might* be extensions...
103             for (int i=0;i<4;i++)
104             {
105                log.trace(""); //seperate headers
106                log.trace(""); //seperate header-reading trace code
107             
108                header = new FitsHeader();
109                reader.readHeaderKeywords(header, null);
110          
111                snippet.append(makeIndexSnippet(header, filename));
112          
113                FitsHdu extHdu = new FitsHdu(header);
114          
115                reader.readData(extHdu);
116             }
117          }
118          catch (FitsFormatException ffe) {
119             log.warn("Fits format exception: "+ffe+" assuming no extension headers");
120          }
121       }
122       return snippet.toString();
123    }
124    
125    /**
126     * Generates an index snippet for the given header
127     */
128    public String makeIndexSnippet(FitsHeader header, String fileLocation) throws IOException
129    {
130       if(header == null) {
131         return "";
132       }
133                                 
134       //run trhough all keywords adding them
135       StringBuffer keywordSnippet = new StringBuffer();
136       String key = null;
137       String val = null;
138       
139       Enumeration enum = header.getKeywords();
140       while (enum.hasMoreElements())
141       {
142          FitsKeyword keyword = (FitsKeyword) enum.nextElement();
143          val = keyword.getValue();
144          
145          //could probably do with tidying this up a bit, for example so that multiline comments become one tag
146          if (val == null) {
147             keywordSnippet.append("      <"+keyword.getKey()+"/>\n");
148          } else {
149             try {
150                Date dateVal = fitsDateFormat.parse(val);
151                keywordSnippet.append("      <"+keyword.getKey()+" units='s'>"+dateVal.getTime()+"</"+keyword.getKey()+">\n");
152                val = indexDateFormat.format(dateVal);
153             } catch (ParseException e) {
154                //ignore
155             }
156             keywordSnippet.append("      <"+keyword.getKey()+">"+val+"</"+keyword.getKey()+">\n");
157          }
158       }
159       
160       
161       //assemble snippet
162       String snippet = "<FitsFile>\n"+
163                "   <Filename>"+fileLocation+"</Filename>\n"+
164                makeCoverageSnippet(header)+
165                "   <Keywords>\n"+
166                keywordSnippet.toString()+
167                "   </Keywords>\n"+
168                "</FitsFile>\n";
169       
170       validate(snippet); //debug - test snippet
171       
172       return snippet;
173    }
174 
175    /***
176     * Makes a Coverage snippet from the keywords in the given header, depending
177     * on the raAxis, decAxis settings */
178    private String makeCoverageSnippet(FitsHeader header) {
179       if ((header.getNumAxis() >= 2) && (raAxis != -1) && (decAxis != -1)) {
180          //return "";
181       
182          //work out coverage.  This is not a straightforward rectangle, as the
183          //image may be rotated and possibly even skewed.  So just give it as a
184          //polygon with 4 points.
185          FitsWCS wcsCalculator = new FitsWCS(header);
186          double maxPixelRA  = header.get("NAXIS"+raAxis).toInt();
187          double maxPixelDec = header.get("NAXIS"+decAxis).toInt();
188    
189          double[] point1 = wcsCalculator.toWCS( new double[] {0,0});
190          double[] point2 = wcsCalculator.toWCS( new double[] {0,maxPixelDec});
191          double[] point3 = wcsCalculator.toWCS( new double[] {maxPixelRA,maxPixelDec});
192          double[] point4 = wcsCalculator.toWCS( new double[] {maxPixelRA,0});
193          
194          //NB origin pixels might not be the min RA & DEC - the picture might be 'upside down'
195          return
196             "   <Coverage shape='Polygon'>\n"+
197             "      <Point><RA>"+point1[0]+"</RA><Dec>"+point1[1]+"</Dec></Point>\n"+
198             "      <Point><RA>"+point2[0]+"</RA><Dec>"+point2[1]+"</Dec></Point>\n"+
199             "      <Point><RA>"+point3[0]+"</RA><Dec>"+point3[1]+"</Dec></Point>\n"+
200             "      <Point><RA>"+point4[0]+"</RA><Dec>"+point4[1]+"</Dec></Point>\n"+
201             "   </Coverage>\n";
202       }
203       return "";
204    }
205    /*** Checks that the given snippet is valid XML */
206    public static void validate(String snippet) throws IOException
207    {
208      try
209      {
210         DomHelper.newDocument(new ByteArrayInputStream(snippet.getBytes()));
211      }
212      catch (org.xml.sax.SAXException e) { throw new IOException(e.toString()); }
213      catch (javax.xml.parsers.ParserConfigurationException e) { throw new IOException(e.toString()); }
214       
215    }
216 
217    /***
218     * Generates an index XML file for the FITS files at the given urls
219     */
220    public String generateIndex(Object[] urls) throws IOException
221    {
222       StringBuffer index = new StringBuffer(ROOT_START+"\n");
223       
224       for (int i=0;i<urls.length;i++)
225       {
226          assert urls[i] != null;  //or could report it and continue?
227          index.append(makeIndexSnippet((URL)urls[i]));
228       }
229 
230       index.append(ROOT_END+"\n");
231       
232       return index.toString();
233       
234    }
235    
236    /***
237     * Generates an index XML file for the FITS files at the URLs listed in the
238     * given file, writing them out to the target stream
239     */
240    public void generateIndex(InputStream urlsIn, Writer out) throws IOException
241    {
242       PrintWriter dout = new PrintWriter(new BufferedWriter(out));
243       dout.println(ROOT_START);
244       
245       BufferedReader in = new BufferedReader(new InputStreamReader(urlsIn));
246 
247       String line = null;
248       while( (line = in.readLine()) != null) {
249          try {
250             dout.println(makeIndexSnippet(new URL(line)).getBytes());
251             dout.flush(); //so that if we crash we can see where we got to
252          } catch (IOException ioe) {
253             //log as an error but try next URL
254             log.error(ioe+", processing URL "+line, ioe);
255          }
256       }
257       in.close();
258       dout.println(ROOT_END);
259       dout.flush();
260    }
261 
262    /***
263     * Generates an index XML file for the FITS files at the given URLs, writing it out to the target stream
264     */
265    public void generateIndex(URL[] urls, Writer out) throws IOException
266    {
267       PrintWriter dout = new PrintWriter(new BufferedWriter(out));
268       dout.println(ROOT_START);
269       
270       for (int i=0;i<urls.length;i++)
271       {
272          assert urls[i] != null : "No URLs Given";  //or could report it and continue?
273          dout.println(makeIndexSnippet(urls[i]));
274          dout.flush();
275       }
276       dout.println(ROOT_END);
277       dout.flush();
278    }
279    
280    /***
281      * Test harness
282      */
283     public static void main(String args[]) throws IOException, MalformedURLException
284     {
285 //      System.out.print(generateIndex(new URL[] {
286 //                                new URL("file://fannich/mch/fits/ngc6946/r169093.fit")
287 //                             }));
288       //this is a bit of a botch so that if there's no log4j.properties around, it
289       //makes one
290       IndexGenerator generator = new IndexGenerator();
291       
292       Locale.setDefault(Locale.UK);
293       Document indexDoc = null;
294       if(args == null || args.length < 2) {
295          System.out.println("java IndexGenerator -f <filename of urls (one url per line)> or");
296          System.out.println("java IndexGenerator -u <URLs seperated by spaces>");
297          return;
298       }
299       String indexFile = null;
300       if("-f".equals(args[0])) {
301          StringWriter out = new StringWriter();
302          generator.generateIndex(new FileInputStream(args[1]), out);
303          indexFile = out.toString();
304       } else if("-u".equals(args[0])) {
305          Object []fitsURLS = new Object[(args.length-1)];
306          for(int i = 1;i < args.length;i++) {
307             fitsURLS[(i-1)] = new URL(args[i]);
308          }
309          indexFile = generator.generateIndex(fitsURLS);
310          log.trace(indexFile);
311       }
312       
313       if(indexFile != null) {
314          try {
315             indexDoc = DomHelper.newDocument(indexFile);
316          }catch(ParserConfigurationException pce) {
317             throw new RuntimeException("Server configuration error",pce);
318          }catch(SAXException se) {
319             throw new QuerierPluginException("FitsQuerierPlugin index not valid xml",se);
320          }
321       }
322       
323       long fileTime = System.currentTimeMillis();
324       if(indexDoc != null) {
325          
326          FileWriter fw = new FileWriter(String.valueOf(fileTime) + ".xml");
327          PrintWriter pw = new PrintWriter(fw);
328          DomHelper.DocumentToWriter(indexDoc,pw);
329          pw.flush();
330          fw.close();
331          pw.close();
332          System.out.println("Successfull creation of fits file index, it was created as = " + String.valueOf(fileTime) + ".xml");
333          System.out.println("Now the file must be uploaded to the eXist xml database to be used for querying the file");
334          System.out.println("You may do this manually or this program can do it automatically by calling \"java -s IndexGenerator <filename>\" by using the file name just created");
335          
336       }
337 
338       if("-s".equals(args[0])) {
339          try {
340             indexDoc = DomHelper.newDocument(new File(args[1]));
341          }catch(ParserConfigurationException pce) {
342             throw new RuntimeException("Server configuration error",pce);
343          }catch(SAXException se) {
344             throw new QuerierPluginException("FitsQuerierPlugin index not valid xml",se);
345          }
346          if(indexDoc != null) {
347    
348             InputStreamReader consoleReader = new InputStreamReader(System.in);
349             BufferedReader consoleInput = new BufferedReader(consoleReader);
350    
351             
352             
353             System.out.println("This program will now ask three questions to do this automatically and it will update or insert the new xml file for queyring");
354             System.out.println("What is the name of the file you wish for it to live in the db?");
355             System.out.println("Please keep it unique, but remember it so you can update it later.  Now the Name?");
356             String line = null;
357             String fileName = null;
358             URL eXistLocation = null;
359             while( (line = consoleInput.readLine()) != null) ;
360             line = line.replaceAll("[^//w*]","_");
361             if(!line.endsWith(".xml")) line += ".xml";
362             fileName = line;
363             System.out.println("What is the url to the exist database? ex: http://localhost:8080/exist  is a typical url");
364             System.out.println("We only need it up to the \"exist\" part");
365             while( (line = consoleInput.readLine()) != null) ;
366             
367             if(!line.endsWith("/")) {
368                line += "servlet/db/dcfitsfiles/" + fileName;
369             }
370             else {
371                line += "/servlet/db/dcfitsfiles/" + fileName;
372             }
373             eXistLocation = new URL(line);
374             System.out.println("The Full URL to Exist is\n: " + line + "\n");
375             System.out.println("Remember this url you may need it on the final configuration of your datacenter");
376             updateFile(eXistLocation,indexFile);
377          }
378       }
379     }
380     
381     private static void updateFile(URL urlLocation, String contents) throws IOException {
382        HttpURLConnection huc = (HttpURLConnection)
383                                 urlLocation.openConnection();
384        huc.setRequestProperty("Content-Type", "text/xml");
385        huc.setDoOutput(true);
386        huc.setRequestMethod("PUT");
387        huc.connect();
388        try {
389           DataOutputStream dos = new DataOutputStream(huc.getOutputStream());
390           DomHelper.DocumentToStream(DomHelper.newDocument(contents),dos);
391           dos.flush();
392           dos.close();
393           huc.disconnect();
394        } catch (javax.xml.parsers.ParserConfigurationException e) {
395           throw new IOException(e.toString());
396        } catch (org.xml.sax.SAXException e) {
397           throw new IOException(e.toString());
398        }
399     }
400     
401 }
402 
403 /*
404 $Log: IndexGenerator.java,v $
405 Revision 1.3  2004/11/09 17:42:22  mch
406 Fixes to tests after fixes for demos, incl adding closable to targetIndicators
407 
408 Revision 1.2  2004/10/18 13:11:30  mch
409 Lumpy Merge
410 
411 Revision 1.1.6.1  2004/10/15 19:59:06  mch
412 Lots of changes during trip to CDS to improve int test pass rate
413 
414 Revision 1.1  2004/09/28 15:02:13  mch
415 Merged PAL and server packages
416 
417 Revision 1.21  2004/09/08 15:45:33  mch
418 Fix for unconfigured ra/dec axis
419 
420 Revision 1.20  2004/09/07 14:52:10  mch
421 Fixes etc for SEC
422 
423 Revision 1.19  2004/09/07 13:50:43  mch
424 Added fix so generator continues even if there's a problem reading a file
425 
426 Revision 1.18  2004/09/07 09:48:34  mch
427 Logging updates
428 
429 Revision 1.17  2004/09/07 01:39:27  mch
430 Moved email keys from TargetIndicator to Slinger
431 
432 Revision 1.16  2004/09/07 00:54:20  mch
433 Tidied up Querier/Plugin/Results, and removed deprecated SPI-visitor-SQL-translator
434 
435 Revision 1.15  2004/09/06 21:38:34  mch
436 Changed to take InputStream
437 
438 Revision 1.14  2004/09/06 21:20:01  mch
439 Factored out generateIndex for a filename for tests
440 
441 Revision 1.13  2004/08/05 15:14:22  KevinBenson
442 small bug fix in the FitsREsults.  And now uses dates was teh result of the mber of kevin-dev-03-08-04
443 
444 Revision 1.11.2.1  2004/08/05 15:10:35  KevinBenson
445 Changes to look for dates and make dates into UTC dates.
446 
447 Revision 1.11  2004/07/29 11:17:44  KevinBenson
448 small change to check that the header is null return an empty string
449 
450 Revision 1.10  2004/07/29 11:15:22  KevinBenson
451 *** empty log message ***
452 
453 Revision 1.9  2004/07/29 11:05:45  KevinBenson
454 small change to make sure the header is not null
455 
456 Revision 1.8  2004/07/26 13:53:44  KevinBenson
457 Changes to Fits to do an xquery on an xml file dealing with fits data.
458 Small xsl style sheet to make the xql which will get the filename element
459 
460 Revision 1.7.4.2  2004/07/26 08:53:40  KevinBenson
461 Still need to make a few more corrections, but wanted to check this in now.
462 It is the fits querier that now uses exist for doing adql->xquery
463 
464 Revision 1.7.4.1  2004/07/16 09:02:25  KevinBenson
465 small change for IndexGenerator to use arguments for it's files and urls.  And
466 Fitskeyword to handle dates.
467 
468 Revision 1.7  2004/07/12 23:26:14  mch
469 Botch fix for no extensions
470 
471 Revision 1.6  2004/03/12 04:45:26  mch
472 It05 MCH Refactor
473 
474 Revision 1.5  2004/03/08 00:31:28  mch
475 Split out webservice implementations for versioning
476 
477 Revision 1.4  2004/01/13 00:33:14  nw
478 Merged in branch providing
479 * sql pass-through
480 * replace Certification by User
481 * Rename _query as Query
482 
483 Revision 1.3.4.1  2004/01/08 09:43:41  nw
484 replaced adql front end with a generalized front end that accepts
485 a range of query languages (pass-thru sql at the moment)
486 
487 Revision 1.3  2003/12/15 14:34:02  mch
488 Added trace code, extra fits file
489 
490 Revision 1.2  2003/11/28 19:57:15  mch
491 Cone Search now works
492 
493 Revision 1.1  2003/11/28 18:22:18  mch
494 IndexGenerator now working
495 
496 */
497 
498