
The
International Outer Planets Watch (IOPW) is an informal organization of astronomers and planetary scientists who are interested in the study of the Solar System's giant planets, their atmospheres, satellites, rings, magnetospheres, aurorae, and other phenomena.
The Atmosphere Discipline of the IOPW is devoted to coordinate and encourage observations of the atmospheres of the giant planets and their satellites, from the Earth and from space, with an emphasis on time-variable phenomena.
For this purpose, there is a great collection of images taken with ground-based telescopes and almost all the times by amateur astronomers. Images taken with the Infra-red Telescope Facility (IRTF) in Hawaii are also available.
The real problem with this collection is that there isn't any information for each image. Reading the name of each file, we can only know the date. We can't know the time, the filter used or how many images has each file. We need some kind of order for all of this, and this order is provided by the PVOL software, which allows us, for example, to search specific atmospheric details or to look for images taken with a filter in the methane band.
In this first test of the system, we only used images of Jupiter from the IOPW (the first testing release published in the web will only have images of Saturn, because these are hosted in the UPV-EHU web server). The fields of the database are the ones that I selected for my investigation two years ago, so it is needed a more complex definition of the fields for the testing release. And the most important thing of this first version in the Summer School is that in each file we can have more than one image, something not really useful for our purpose.
What we should have is:
- Only one image in each file.
- A full functional database.
- A search method based in different latitude speeds rather than in the longitude systems.
- Images of Jupiter, Saturn, Neptune, Uranus… from the IOPW framework
- Extend this project to other collection of images:
- HST images in FITS format
- Voyager images in PDS-IMQ (compressed) format
- Galileo images in PDS-IMG (uncompressed) format
- ...
- A html form to allow the astronomers to upload the images directly
This project is being developed by Aleksander Morgado, undergraduate member of the Planetary Sciences Group (
GCP, Grupo de Ciencias Planetarias) in the University of the Basque Country (UPV-EHU).
Raymond Tam, working in Caltech, developed the VoTable response during the Summer School in Aspen.
The implementation of this service started the last days of August 2004, with the design of the web page. It was really simple using CSS in Macromedia Dreamweaver, and Photoshop for the logo and that stuff.
As this database server is free and very robust, it is a good way to have all the information stored. I had all the database of image data in Microsoft Access, so I used ODBC to export my tables to MySQL. Once all the information is in the DB server, typing all the queries by hand is an option, but there are more options to do the same, like MySQL Control Center (the one that I chose) or a simple connection with MS Access.
Java engine: JSPs and Java Beans
As we were going to use Java in the summer school, I decided to do the
inteligent part of the web page with JSPs and Java Beans. There are very good tutorials of JSP and Java beans in Spanish, so I am sure that there will be great tutorials in English. After visiting the Maroon Bells the Saturday before the Summer School, I started with the JSP page and the Java Bean. Here is the code for the JSP page:
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<html>
<head>
<title>RESULTS</title>
<link href="resources/styles.css" rel="stylesheet" type="text/css">
</head>
<body>
<jsp:useBean id="beandb" scope="request" class="mybeans.accessDb" />
<% beandb.setSDateq(request.getParameter("SDate"));
beandb.setFDateq(request.getParameter("FDate"));
beandb.setPlanetq(request.getParameter("Planet"));
beandb.setSortq(request.getParameter("Sort"));
beandb.setAuthorq(request.getParameter("Author"));
beandb.setNofIq(request.getParameter("NoI"));
beandb.setRangeq(request.getParameter("Range"));
beandb.setResultFormatq("HTML");
beandb.setSystemI(request.getParameter("SI"));
beandb.setSystemII(request.getParameter("SII"));
beandb.setSystemIII(request.getParameter("SIII"));
String result = beandb.makeSQL();
if (result.compareTo("")==0){%>
<b>ERROR in one or more fields</b><br>
<%}
else if ((result.substring(0,2)).compareTo("SE")==0)
{
%>
<form action="enginexml.jsp" method="POST" target="_blank">
<input name="SDate" type="hidden" value="<%= request.getParameter("SDate") %>">
<input name="FDate" type="hidden" value="<%= request.getParameter("FDate") %>">
<input name="Planet" type="hidden" value="<%= request.getParameter("Planet") %>">
<input name="Sort" type="hidden" value="<%= request.getParameter("Sort") %>">
<input name="Author" type="hidden" value="<%= request.getParameter("Author") %>">
<input name="NoI" type="hidden" value="<%= request.getParameter("NoI") %>">
<input name="Range" type="hidden" value="<%= request.getParameter("Range") %>">
<input name="SI" type="hidden" value="<%= request.getParameter("SI") %>">
<input name="SII" type="hidden" value="<%= request.getParameter("SII") %>">
<input name="SIII" type="hidden" value="<%= request.getParameter("SIII") %>">
<input type="submit" value="Get VOTable" name="B1" style="font-family: Trebuchet MS; font-size: 14 pt; font-weight: bold; color: #0000FF">
</form>
mySQL QUERY: <b><%= result %> </b><br>
<p> Searching for: <br>
Planet: <b> <%= beandb.getPlanetq() %> </b><br>
Initial date: <b> <%= beandb.getSDateq() %> </b> <br>
Final date: <b> <%= beandb.getFDateq() %> </b> <br>
Author: <b> <%= beandb.getAuthorq() %> </b> <br>
Range/Filter: <b> <%= beandb.getRangeq() %> </b><br>
Number of images: <b><%= beandb.getNofIq() %> </b><br>
System I longitude: <b><%= beandb.getSystemI() %> </b><br>
System II longitude: <b><%= beandb.getSystemII() %> </b><br>
System III longitude: <b><%= beandb.getSystemIII() %> </b><br>
<%= beandb.executeSQL() %>
</p>
<p> Results: <%= beandb.GetNumberOfResults() %> </p>
<%
}
else
{%>
<b> No database for <%= result %> yet </b><br>
<%
}
%>
</body>
</html>
If we want to return a VoTable instead of an html table, the JSP page should look like this:
<?xml version="1.0" encoding="utf-8"?>
<%@ page contentType="text/xml" %>
<jsp:useBean id="beandb" scope="request" class="mybeans.accessDb" />
<% beandb.setSDateq(request.getParameter("SDate"));
beandb.setFDateq(request.getParameter("FDate"));
beandb.setPlanetq(request.getParameter("Planet"));
beandb.setSortq(request.getParameter("Sort"));
beandb.setAuthorq(request.getParameter("Author"));
beandb.setNofIq(request.getParameter("NoI"));
beandb.setRangeq(request.getParameter("Range"));
beandb.setResultFormatq("VOTable");
beandb.setSystemI(request.getParameter("SI"));
beandb.setSystemII(request.getParameter("SII"));
beandb.setSystemIII(request.getParameter("SIII"));
String result = beandb.makeSQL();%>
<%=beandb.executeSQL()%>
%>
The code of the JSP page is not very long. The hard programming, like the connection to MySQL goes in the Java Bean. I didn't learn anything of JDBC to do the connection between Java and MySQL. There is a very useful tool called JDBCTest that gives you all the code you need.
Here is the code that you get using the driver for MySQL, a little bit modified so that you can access each field of the resulting table using
getField(int col, int row):
/*
* SkeletonDbReader.java
*
* Created on 17 de septiembre de 2004, 19:05
*/
package mybeans;
import java.beans.*;
import java.io.Serializable;
import java.sql.*; // SQL library!
import java.lang.*;
/**
* @author Aleks!
*/
public class SkeletonDbReader extends Object implements Serializable {
private String Query="";
private String DbUrl="jdbc:mysql://localhost/pvol-images";
private String Username="aleks";
private String Password="aleks";
private long numberOfResults;
private String[][] array;
public SkeletonDbReader() {
}
public void executeSQL()
{
// REGISTER DRIVER
try
{
Driver d = (Driver)Class.forName("com.mysql.jdbc.Driver").newInstance();
}
catch (Exception e)
{
System.out.println(e);
}
// GET CONNECTION
Connection con = null;
try
{
con = DriverManager.getConnection(DbUrl,Username,Password);
}
catch(Exception e)
{
System.out.println(e);
}
// GET CONNECTION WARNINGS
SQLWarning warning = null;
try
{
warning = con.getWarnings();
if (warning == null)
{
System.out.println("No Warnings");
// return;
}
while (warning != null)
{
System.out.println("Warning: "+warning);
warning = warning.getNextWarning();
}
}
catch (Exception e)
{
System.out.println(e);
}
// CREATE STATEMENT
java.sql.Statement stmt = null;
try
{
stmt = con.createStatement();
}
catch (Exception e)
{
System.out.println(e);
}
// EXECUTE QUERY
ResultSet results = null;
try
{
results = stmt.executeQuery(Query);
}
catch (Exception e)
{
System.out.println(e);
}
//StringBuffer buf = new StringBuffer();
try
{
ResultSet aux = results;
ResultSetMetaData rsmd = results.getMetaData();
int numCols = rsmd.getColumnCount();
int i, rowcount = 0;
while (aux.next())
{
rowcount++;
}
array = new String [numCols][rowcount];
// get column header info
for (i=1; i <= numCols; i++)
{
//buf.append(rsmd.getColumnLabel(i));
array[i][0] = rsmd.getColumnLabel(i);
}
rowcount = 1;
while (results.next())
{
// Loop through each column, getting the column
// data and displaying
for (i=1; i <= numCols; i++)
{
//buf.append(results.getString(i));
array[i][rowcount] = results.getString(i);
}
rowcount++;
}
numberOfResults = rowcount-1;
results.close();
}
catch (Exception e)
{
System.out.println(e);
}
}
public long getNumberOfResults() {
return this.numberOfResults;
}
public String getField(int col, int row)
{
return this.array[col][row];
}
}
Here are some functions that I programmed during the work session in the summer school to convert from Year/Month/Day/Hour/Minutes to Julian date and to get the Central Meridian longitude in the three different systems of Jupiter (needed to perform advanced searches).:
public double JulianDate(java.lang.String date, java.lang.String time)
{
int year, month, day, hour, minutes;
year = Integer.parseInt(date.substring(0,4));
month = Integer.parseInt(date.substring(5,7));
day = Integer.parseInt(date.substring(8,10));
hour = Integer.parseInt(time.substring(0,2));
minutes = Integer.parseInt(time.substring(3,5));
double HR = ((double)hour);
HR+= ((double)minutes)/60;
System.out.println("HR: "+HR);
int GGG = 1;
if (year < 1585) GGG=0;
double JD = -1* Math.floor(7*(Math.floor((month+9)/12)+year)/4);
int S = 1;
if ((month-9)<0) S=-1;
double A = Math.abs(month-9);
double J1 = Math.floor(year+S*Math.floor(A/7));
J1 = -1 * Math.floor((Math.floor(J1/100)+1)*3/4);
JD = JD + Math.floor(275*month/9)+day + (GGG*J1);
JD = JD + 1721027 + 2*GGG+367*year-0.5;
JD = JD + (HR/24);
return JD;
}
public float[] ComputeCMs(double JD)
{
float [] CMs = new float[3];
double mPI = 3.14159265359;
double lCM1,lCM2,lCM3;
double entero;
double decimal;
double jup_mean;
double eqn_center;
double angle;
double correction;
double aux;
jup_mean = (JD-2455636.938)*360/4332.89709;
aux = jup_mean*mPI/180;
eqn_center = 5.55 * Math.sin(aux);
angle = (JD - 2451870.628)*(360 / 398.884) - eqn_center;
correction = 11 * Math.sin(angle*mPI/180) + 5 * Math.cos(angle*mPI/180) - 1.25 * Math.cos(jup_mean*mPI/180) - eqn_center;
lCM1 = 156.84 + 877.8169147 * JD + correction;
lCM2 = 181.62 + 870.1869147 * JD + correction;
lCM3 = 272.29 + 870.4529147 * JD + correction;
long auxentero = (long)(lCM1/360);
entero = auxentero;
decimal = (lCM1/360)-entero;
CMs[0] = (float)(lCM1-(entero*360));
auxentero = (long)(lCM2/360);
entero = auxentero;
decimal = (lCM2/360)-entero;
CMs[1] = (float)(lCM2-(entero*360));
auxentero = (long)(lCM3/360);
entero = auxentero;
decimal = (lCM3/360)-entero;
CMs[2] = (float)(lCM3-(entero*360));
return CMs;
}
And here is the code that Raymond Tam wrote to convert the information readed from the database to VoTable format, using a dtd to validate the file.
if (resultFormatq.compareTo("VOTable")==0)
{
//String aux = "<?xml version=" + '"' + "1.0" + '"' + "?>";
// buf.append(aux);
String aux = "<!DOCTYPE VOTABLE SYSTEM " + '"' + "http://us-vo.org/xml/VOTable.dtd" + '"' + ">";
buf.append(aux);
aux = "<VOTABLE version=" + '"' + "1.0" + '"' + ">";
buf.append(aux);
buf.append("<DEFINITIONS>");
aux = "<COOSYS ID=" + '"' + "myJ2000" + '"' + " equinox=" + '"' + "2000." + '"' + " epoch=" + '"' + "2000." + '"' + " system=" + '"' + "eq_FKS"+ '"'+"/>";
buf.append(aux);
buf.append("</DEFINITIONS>");
buf.append("<RESOURCE>");
aux = "<TABLE name=" + '"' + "Planets" + '"' + ">";
buf.append(aux);
buf.append("<DESCRIPTION>");
buf.append("Output list of files");
buf.append("</DESCRIPTION>");
aux = "<FIELD name=" + '"' + "Date" + '"' + " ucd=" + '"' + "ID_MAIN" + '"' + " data=" + '"' + "char" + '"' + " arraysize=" + '"' + "20" + '"' + "/>";
buf.append(aux);
aux = "<FIELD name=" + '"' + "Start Time" + '"' + " data=" + '"' + "char" + '"' + " arraysize=" + '"' + "8" + '"' + "/>";
buf.append(aux);
aux = "<FIELD name=" + '"' + "End Time" + '"' + " data=" + '"' + "char" + '"' + " arraysize=" + '"' + "8" + '"' + "/>";
buf.append(aux);
aux = "<FIELD name=" + '"' + "Author" + '"' + " ucd=" + '"'+ "ID_AUTHOR" + '"' + " data=" + '"' + "char" + '"' + " arraysize=" + '"' + "20*" + '"' + "/>";
buf.append(aux);
aux = "<FIELD name=" + '"' + "Number of Images" + '"' + " ucd="+ '"'+ "NUMBER" + '"' + " data=" + '"' + "int" + '"' + " arraysize=" + '"' + "3" + '"' + "/>";
buf.append(aux);
aux = "<FIELD name=" + '"' + "range" + '"' + " ucd=" + '"'+ "EM" + '"' + " data=" + '"' + "char" + '"' + " arraysize=" + '"' + "30*" + '"' + "/>";
buf.append(aux);
aux = "<FIELD name=" + '"' + "Filename" + '"' + " ucd=" + '"'+ "ID_FILE" + '"' + " data=" + '"' + "char" + '"' + " arraysize=" + '"' + "50*" + '"' + "/>";
buf.append(aux);
buf.append("<DATA>");
while (results.next())
{
// Loop through each column, getting the column data and displaying
buf.append("<tr> <td>");
for (i=1; i <= numCols; i++) {
if (i > 1) buf.append("<td>");
if (i==7)
{
buf.append("href=" + '"' + "pvol-images/");
buf.append(results.getString(i));
buf.append('"');//+ " target="+ '"' +"_blank"+'"'+"> ");
}
else
{
buf.append(results.getString(i));
}
buf.append("</td>");
}
buf.append("</tr>");
rowcount++;
} // end while loop
buf.append("</DATA>");
buf.append("</TABLE>");
buf.append("</RESOURCE>");
buf.append("</VOTABLE>");
}
The testing release of the PVOL system will be available as soon as the database of Saturn images is ended. I will inform you about this.
If you have any question about this,
e-mail me!