001 package edu.nrao.sss.astronomy; 002 003 import java.io.BufferedReader; 004 import java.io.IOException; 005 import java.io.InputStreamReader; 006 import java.io.UnsupportedEncodingException; 007 import java.net.MalformedURLException; 008 import java.net.URL; 009 import java.net.URLEncoder; 010 import java.text.SimpleDateFormat; 011 import java.util.Date; 012 013 import edu.nrao.sss.measure.ArcUnits; 014 import edu.nrao.sss.measure.Latitude; 015 import edu.nrao.sss.measure.Longitude; 016 import edu.nrao.sss.util.SourceNotFoundException; 017 018 /** 019 * IMCE's SkyBot Resolver source locator. 020 * <p> 021 * This locator sends a CGI query to IMCE 022 * (<a href="http://www.imcce.fr/imcce_en.html">Institut de Mechanique 023 * Celeste et de Calcul des Ephemerides</a>) and parses the response. 024 * The web address for the query is 025 * <a href="http://www.imcce.fr/webservices/skybot/skybotresolver_query.php"> 026 * http://www.imcce.fr/webservices/skybot/skybotresolver_query.php</a>. 027 * </p> 028 * <p> 029 * <b>Version Info:</b> 030 * <table style="margin-left:2em"> 031 * <tr><td>$Revision: 555 $</td></tr> 032 * <tr><td>$Date: 2007-04-24 09:05:24 -0600 (Tue, 24 Apr 2007) $</td></tr> 033 * <tr><td>$Author: dharland $</td></tr> 034 * </table></p> 035 * 036 * @author David M. Harland 037 * @since 2007-04-18 038 */ 039 public class SkyBotResolver 040 implements SourceLocator 041 { 042 private static final String CGI_SCRIPT = 043 "http://www.imcce.fr/webservices/skybot/skybotresolver_query.php"; 044 045 private static final String CONST_PARAMS = "?-out=object&-mime=text"; 046 047 private static final String QUERY_BASE = CGI_SCRIPT + CONST_PARAMS; 048 049 private static final char PARAM_DELIM = '&'; 050 051 private static final String ENCODING = "UTF-8"; 052 053 private static final String COMMENT_TOKEN = "#"; 054 055 private static final SimpleDateFormat DATE_FORMATTER = 056 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 057 058 private static final int RA_POS = 2; 059 private static final int DEC_POS = 3; 060 061 //============================================================================ 062 // 063 //============================================================================ 064 065 /* (non-Javadoc) 066 * @see SourceLocator#findPosition(java.lang.String, java.util.Date) 067 */ 068 public SkyPosition findPosition(String sourceName, Date time) 069 throws SourceNotFoundException 070 { 071 String skybotQuery = buildQuery(sourceName, time); 072 073 BufferedReader page = getPageReader(skybotQuery); 074 075 return getPositionFrom(page); 076 } 077 078 /* (non-Javadoc) 079 * SourceLocator#findPosition(java.lang.String) 080 */ 081 public SkyPosition findPosition(String sourceName) 082 throws SourceNotFoundException 083 { 084 String skybotQuery = buildQuery(sourceName); 085 086 BufferedReader page = getPageReader(skybotQuery); 087 088 return getPositionFrom(page); 089 } 090 091 //============================================================================ 092 // 093 //============================================================================ 094 095 /** Builds a query string based on name of body and time of "now". */ 096 private String buildQuery(String bodyName) 097 throws SourceNotFoundException 098 { 099 return buildQuery(bodyName, "now"); 100 } 101 102 /** Builds a query string based on name of body and time. */ 103 private String buildQuery(String bodyName, Date time) 104 throws SourceNotFoundException 105 { 106 return buildQuery(bodyName, DATE_FORMATTER.format(time)); 107 } 108 109 /** Builds a query string using the two parameters. */ 110 private String buildQuery(String bodyName, String dateText) 111 throws SourceNotFoundException 112 { 113 StringBuilder query = new StringBuilder(QUERY_BASE); 114 115 try 116 { 117 //Date 118 query.append(PARAM_DELIM) 119 .append("-ep=") 120 .append(URLEncoder.encode(dateText, ENCODING)); 121 122 //Body name 123 query.append(PARAM_DELIM) 124 .append("-obj=") 125 .append(URLEncoder.encode(bodyName, ENCODING)); 126 } 127 catch (UnsupportedEncodingException uee) 128 { 129 throw new SourceNotFoundException( 130 "Problem encoding date and/or name parameters for use in URL.", uee); 131 } 132 133 return query.toString(); 134 } 135 136 /** Turns the text into a URL then into a BufferedReader. */ 137 private BufferedReader getPageReader(String skybotAddress) 138 throws SourceNotFoundException 139 { 140 try 141 { 142 URL skybot = new URL(skybotAddress); 143 144 return new BufferedReader(new InputStreamReader(skybot.openStream())); 145 } 146 catch (MalformedURLException mue) 147 { 148 throw new SourceNotFoundException("Problem with this URL: " + 149 skybotAddress, mue); 150 } 151 catch (IOException ioe) 152 { 153 throw new SourceNotFoundException("Problem opening query results.", ioe); 154 } 155 } 156 157 /** Reads the page, looking for position information. */ 158 private SkyPosition getPositionFrom(BufferedReader page) 159 throws SourceNotFoundException 160 { 161 try 162 { 163 confirmFlagLine(page.readLine(), page); 164 165 page.readLine(); //Skip "ticket" line 166 167 confirmColumnHeadings(page.readLine()); 168 169 SkyPosition position = makePosition(page.readLine()); 170 171 page.close(); 172 173 return position; 174 } 175 catch (IOException ioe) 176 { 177 throw new SourceNotFoundException("Problem reading query results.", ioe); 178 } 179 catch (IllegalArgumentException iae) 180 { 181 throw new SourceNotFoundException("Unexpected results format.", iae); 182 } 183 } 184 185 /** 186 * Makes sure line contains "flag" and that value is not 0 or -1. 187 * 0 = source-not-found; -1 = error. 188 */ 189 private void confirmFlagLine(String line, BufferedReader page) 190 throws SourceNotFoundException 191 { 192 if (!line.contains("flag")) 193 throw new IllegalArgumentException("First line did not contain 'flag'."); 194 195 int flag = Integer.parseInt(line.substring(line.indexOf(": ")+2)); 196 197 if (flag < 1) 198 { 199 String explanation = ""; 200 201 while (true) 202 { 203 try { 204 line = page.readLine(); 205 } 206 catch (IOException ex) { 207 throw new SourceNotFoundException("Problem reading query results." 208 , ex); 209 } 210 if (line == null) 211 break; 212 213 if (!line.startsWith(COMMENT_TOKEN)) 214 { 215 explanation = line; 216 break; 217 } 218 } 219 220 throw new IllegalArgumentException(explanation); 221 } 222 } 223 224 /** Confirms that we understand the data in the results line. */ 225 private void confirmColumnHeadings(String line) 226 { 227 String[] columns = line.split(","); 228 229 if (!columns[RA_POS].contains("RA(h)")) 230 throw new IllegalArgumentException( 231 "Expected RA, in hours, in column " + RA_POS + " of " + line); 232 233 if (!columns[DEC_POS].contains("DE(deg)")) 234 throw new IllegalArgumentException( 235 "Expected RA, in hours, in column " + DEC_POS + " of " + line); 236 } 237 238 /** 239 * Parses skyBotLine and returns a sky position using the portion 240 * of the line that represents the RA and Dec. 241 */ 242 private SkyPosition makePosition(String skyBotLine) 243 { 244 SimpleSkyPosition position = 245 new SimpleSkyPosition(CelestialCoordinateSystem.EQUATORIAL, Epoch.J2000); 246 247 String[] tokens = skyBotLine.split("\\|"); 248 249 if (tokens.length != 7) 250 throw new IllegalArgumentException(skyBotLine); 251 252 position.setLongitude(Longitude.parse(tokens[RA_POS] + 253 ArcUnits.HOUR.getSymbol())); 254 255 position.setLatitude(Latitude.parse(tokens[DEC_POS] + 256 ArcUnits.DEGREE.getSymbol())); 257 return position; 258 } 259 260 //============================================================================ 261 // 262 //============================================================================ 263 /* 264 public static void main(String[] args) throws Exception 265 { 266 SkyBotResolver locator = new SkyBotResolver(); 267 268 String[] bodies = {"mars", "pluto", "ida", "ceres", "lunch", ""}; 269 270 for (int b=0; b < bodies.length; b++) 271 { 272 try 273 { 274 SkyPosition position = locator.findPosition(bodies[b]); 275 276 System.out.print(bodies[b] + ": "); 277 System.out.print(position.getLongitude().toStringHms()); 278 System.out.print(", "); 279 System.out.println(position.getLatitude().toStringDms()); 280 } 281 catch (SourceNotFoundException ex) 282 { 283 System.out.println(ex.getMessage() + " " + ex.getCause().getMessage()); 284 } 285 } 286 287 Calendar cal = Calendar.getInstance(); 288 289 for (int day=1; day <= 10; day++) 290 { 291 Date time = cal.getTime(); 292 293 SkyPosition position = locator.findPosition("Mars", time); 294 295 System.out.print(time + ": "); 296 System.out.print(position.getLongitude().toStringHms()); 297 System.out.print(", "); 298 System.out.println(position.getLatitude().toStringDms()); 299 300 cal.add(Calendar.DATE, 1); 301 } 302 } 303 */ 304 }