Building Client-Side VO Applications in Java
In this exercise we look at how to build VO client applications in Java. The primary focus is on finding and retrieving astronomical data (images, spectra, etc.) via the data access layer. First we examine the Java-based VO client library which we will use for our applications. We then build and execute several applications which demonstrate the use of the library to build simple client data analysis applications.
Java is used for these exercises due to its platform independence, so that we don't have to be concerned about platform issues in running the demonstration applications, and because Java is the defacto standard language for VO system software. For science applications one ideally wants to be able to develop in a range of standard languages and environments. While some support for other languages (Python, C/C++, Perl, etc.) and environments (IRAF, IDL, etc.) is available, we are only just getting started developing VO client-side support for such a wide range of environments. Our sample applications should be simple enough to demonstrate the client side of VO without need to understand the details of Java.
Java VO Client Interface V1.0
The dalclient package provides a first cut at an interface to the VO for writing client data analysis applications. Applications deal with data discovery queries via an object API which implements the data model defined by each DAL service, providing object-oriented access to metadata describing generic catalogs, images, spectra, and so forth. This provides a high level, random access interface, while isolating the application from the details of the underlying wire protocol, including (so far as possible) the specific protocol version used by a service. A table-oriented interface supporting both the VOTable and CSV formats is also provided.
This first version supports only Java, and functionality is limited to queries and data retrieval via the current VO Data Access Layer (DAL) services. Future versions will provide equivalent functionality in multiple languages, and integrated support for registry queries for automated service discovery plus the ability to simultaneously query multiple services and merge the results. While the interface already supports building queries against multiple services, the current implementation will only query the first such service specified.
Requirements
This code was developed for Java 1.4.2 and should work with Java
1.4 or later versions. The VOTWrap class from JHU is used
for VOTable access. This internally uses either of the JAVOT or SAVOT
packages to parse and access VOTables.
Interface Summary
This section provides an overview of the dalclient package,
including a brief guide to basic usage.
Reference documentation in Javadoc format is also available.
The dalclient interface consists of a generic DAL
base class, implementing the basic parameter-based query, which is
then subclassed for each type of data supported by the DAL interfaces.
Hence basic catalog access via the cone search service is provided by
the ConeQuery classes, and image access via the simple image
access protocol is provided by the SiapQuery classes.
ConeQuery Classes
The ConeQuery classes implement the simple cone search
query, used for generic catalog access. Since there is no data model for a
generic catalog query, the query response object for a cone search uses the
UCD of each returned table field as the lookup key. For each row of the
returned table one can use the getAttribute
method to fetch catalog fields directly using the UCD as the key.
cone = new ConeConnection() # Connection context
cone = new ConeConnection(service-url)
cone.addService(service-url)
cone.getServiceCount()
cone.getServiceURL(index)
query = cone.getConeQuery() # Query context
query = cone.getConeQuery(ra, dec, sr)
query.addParameter(name, value)
qr = query.execute() # VOTable -> Dataset|Record
query.executeCSV() # VOTable -> CSV
query.executeCSV(fname)
vot = query.executeVOTable() # query as VOTable
is = query.executeRAW() # raw XML
SiapQuery Classes
The SiapQuery classes implement the simple image access
query, used to discover candidate image datasets which can subsequently be
downloaded if desired. In this case the query response implements the SIAP
data model, which defines a number of standard attributes used to describe
each candidate image dataset. The getDataset
method can be used to retrieve the image associated with a given row of
the query response.
siap = new SiapConnection()
siap = new SiapConnection(service-url)
siap.addService(service-url)
siap.getServiceCount()
siap.getServiceURL(index)
query = siap.getSiapQuery()
query = siap.getSiapQuery(ra, dec, size)
query = siap.getSiapQuery(ra, dec, size, format)
query = siap.getSiapQuery(ra, dec, ra_size, dec_size)
query = siap.getSiapQuery(ra, dec, ra_size, dec_size, format)
query.addParameter(name, value)
qr = query.execute() # VOTable -> Dataset
query.executeCSV()
query.executeCSV(fname)
vot = query.executeVOTable()
is = query.executeRAW()
SsapQuery Classes
Support for the simple spectral access protocol (SSAP) will be added once the interface is stable (the target for this is late 2005). The interface will be similar to that for cone and SIAP, but the query parameters will be somewhat different, allowing queries by spectral and time bandpass as well as other parameters. The returned dataset metadata as captured in the query response will differ significantly as the data models have evolved significantly since SIAP was introduced.
QueryResponse Class
The response to a DAL query can be processed directly as a VOTable, the
generic table container format used by the wire protocol. This requires
knowledge within the client code of the details of the DAL protocol
used, but provides a generic way to process the results from any query.
The QueryResponse class goes one step further and provides
an object API based on the data model for a particular object as defined
by the DAL interfaces (catalog, image, spectrum, etc.). This permits
an application to interact with a remote data object service without
detailed knowledge of the underlying DAL protocol used. In addition
since the query response table is stored in memory in a hash table,
efficient random access by keyword is provided.
The QueryResponse class is a sequence of dataset descriptors
corresponding to the rows of the query response VOTable. Each dataset is
described by a set of standard data model attributes. A data provider
may add additional non-standard attributes which will flow through all
layers the interface to the client code. These are not visible in the
standard data model via the normal query-by-attribute interface, but can
be accessed by traversing the response record as a list.
Get dataset descriptor:
qr.getRecordCount() # number of descriptors
rec = qr.getRecord(i) # get a descriptor
Access by dataset attribute:
v = rec.getAttribute(attrname) # may return null
v.boolValue()
v.intValue()
v.floatValue()
v.doubleValue()
v.stringValue()
Get dataset file corresponding to descriptor:
rec.getDataset(path) # fetch data to given path
path = rec.getDataset() # fetch to autogenerated path
The getDataset method may optionally be used to generate and
retrieve the dataset described by a given query response record. A filename
will be automatically generated if one is not provided. An attempt is
made to determine the type of data returned (FITS, JPEG, etc.) and set
the file extension automatically, however this is not always possible.
Basic Usage
Regardless of the type of data being accessed, the basic interface is the same:
- Create a new connection. This defines the set of services to be queried.
- Compose the query. In many cases this can be done with a
single
getQuerymethod.
To customize the query additional query parameters can be added usingaddParameter. - Execute the query. The query is executed and the query response
VOTable is decoded into a
QueryReponseobject. - Navigate to the desired record of the query response and use a series
of
getAttributecalls to examine the dataset or catalog row in question. - If desired, invoke the
getDatasetmethod on a query record to generate and download the associated dataset.
Sample Client Programs
The "dalclient" directory in the summer school software release includes
both the source code for the initial version of the dalclient
package as well as a number of sample Java programs illustrating how to
use the interface. Note these are not intended to be real applications!
They have intentionally been kept quite simple, to demonstrate how to
build client VO applications without being distracted by details.
To get started, open a terminal window (the details will depend upon which platform you are using) and navigate to the dalclient directory in the NVO summer school software release:
% cd $NVOSS_HOME/java/dev/dalclient
% ls
CVS QueryRecord.java chart.java siap1.java
ConeConnection.java QueryResponse.java cone1.java siap2.java
ConeQuery.java README cone2.java siap3.java
DALConnection.java SiapConnection.java cone3.java siap4.java
DALQuery.java SiapQuery.java doc
QRAttribute.java build.xml package.html
Before we can run any of the test programs we need to build the package. For Java this is done with the ant tool:
% ant
Buildfile: build.xml
init:
[mkdir] Created dir: D:\nvo\nvoss2005\java\dev\dalclient\classes
compile:
[echo] building
[javac] Compiling 17 source files to D:\nvo\nvoss2005\java\dev\dalclient\classes
BUILD SUCCESSFUL
Total time: 6 seconds
Now we can try out the demonstration client applications. In what follows, all of the applications can be run either with arguments on the command line, or with no arguments in which case a set of built-in arguments are used. To simplify things we mostly use the no-args option here, but later we can try re-running the applications at other positions on the sky, or querying other services, to dyamically explore the VO. In all cases we are accessing real VO services, hence a good Internet connection is required to run the applications.
Application Cone1
Call a cone search service and print selected fields of the table.
Usage: cone1 ra dec sr [serviceURL]
Our first application performs a simple cone search and prints selected
fields using the ConeQuery class to access selected table
columns directly via their UCD tag. To run the cone1
application type java dalclient.cone1 as in the following
example (note one must include the package name, "dalclient" in this case):
% java dalclient.cone1
# Query: http://chart.stsci.edu/GSCVO/GSC22VO.jsp?RA=12.0&DEC=12.0&SR=0.1
# returns 36 records containing 14 attributes each
# --- Summary output ---
id=N3233000:4280 ra=12.06964142 dec=11.93743758 class=Star
id=N3233000:4440 ra=12.00563246 dec=11.98930768 class=Star
id=N3233000:4396 ra=12.04016664 dec=11.97401059 class=Star
id=N3233000:4174 ra=11.97466466 dec=11.90447683 class=Star
id=N3233000:4182 ra=11.98926385 dec=11.90476293 class=Star
id=N3233000:4321 ra=12.0236914 dec=11.95191743 class=Star
id=N3233000:90 ra=11.98445133 dec=11.95106419 class=Star
id=N3233000:4184 ra=12.01401469 dec=11.90488566 class=Star
id=N3233000:4266 ra=12.02581304 dec=11.93266205 class=Star
id=N3233000:94 ra=12.02150008 dec=11.93815298 class=Star
id=N3233000:4274 ra=12.0502686 dec=11.93381342 class=Star
id=N3233000:96 ra=12.01875118 dec=11.93194831 class=Star
id=N3233000:4289 ra=11.98131438 dec=11.94162927 class=Non star
id=N3233000:4322 ra=11.9158309 dec=11.9526625 class=Non star
id=N3233000:95 ra=11.94963356 dec=11.93363352 class=Star
id=N3233000:4327 ra=11.91464261 dec=11.95481215 class=Non star
id=N3233000:4626 ra=11.92744157 dec=12.04707863 class=Star
id=N3233000:4588 ra=11.90452932 dec=12.0284553 class=Non star
id=N3233000:4465 ra=11.9564025 dec=11.99919811 class=Non star
If we look at the actual VOTable returned by the service for this cone search we see the following fields:
<TABLE><FIELD ID="ID" ucd="ID_MAIN" datatype="char" arraysize="14*"/> <FIELD ID="RA" ucd="POS_EQ_RA_MAIN" datatype="double" unit="deg"/> <FIELD ID="Dec" ucd="POS_EQ_DEC_MAIN" datatype="double" unit="deg"/> <FIELD ID="RAErr" ucd="ERROR" datatype="double" unit="deg"/> <FIELD ID="DecErr" ucd="ERROR" datatype="double" unit="deg"/> <FIELD ID="Epoch" ucd="TIME_EPOCH" datatype="double" unit="deg"/> <FIELD ID="Fmag" ucd="PHOT_PHG_R" datatype="double" unit="mag"/> <FIELD ID="FmagErr" ucd="ERROR" datatype="double" unit="mag"/> <FIELD ID="Jmag" ucd="PHOT_PHG_B" datatype="double" unit="mag"/> <FIELD ID="JmagErr" ucd="ERROR" datatype="double" unit="mag"/> <FIELD ID="Vmag" ucd="PHOT_PHG_V" datatype="double" unit="mag"/> <FIELD ID="VmagErr" ucd="ERROR" datatype="double" unit="mag"/> <FIELD ID="Nmag" ucd="PHOT_PHG_N" datatype="double" unit="mag"/> <FIELD ID="NmagErr" ucd="ERROR" datatype="double" unit="mag"/> <FIELD ID="classification" ucd="CLASS_OBJECT" datatype="char" arraysize="*"/> <FIELD ID="semiMajor" ucd="EXTENSION_RAD" datatype="double"/> <FIELD ID="eccentricity" ucd="PHYS_ECCENTRICITY" datatype="double"/> <FIELD ID="positionAngle" ucd="POS_POS-ANG" datatype="double" unit="deg"/> <FIELD ID="status" ucd="CODE_QUALITY" datatype="double"/>
Now we can see that indeed only selected fields of the table are
being output. To see how this is done we can examine the code for
cone1. The most important bits are as follows:
// Get a new connection to the service.
ConeConnection cone = new ConeConnection(service);
// Form the query.
ConeQuery query = cone.getConeQuery(ra, dec, sr);
// Execute the query and fetch results.
System.out.println("# Query: " + query.getQueryString(0));
QueryResponse qr = query.execute();
if (qr.getRecordCount() <= 0) {
System.out.println("no records matched");
System.exit(1);
}
// Summarize and print selected query results.
for (int i=0; i < qr.getRecordCount(); i++) {
QueryRecord r = qr.getRecord(i);
String s_id, s_ra, s_dec, s_class;
QRAttribute v;
s_id = ((v = r.getAttribute("ID_MAIN")) != null) ?
v.stringValue() : "<none>";
s_ra = ((v = r.getAttribute("POS_EQ_RA_MAIN")) != null) ?
v.stringValue() : "<unknown>";
s_dec = ((v = r.getAttribute("POS_EQ_DEC_MAIN")) != null) ?
v.stringValue() : "<unknown>";
s_class = ((v = r.getAttribute("CLASS_OBJECT")) != null) ?
v.stringValue() : "<unknown>";
System.out.println("id=" + s_id + "\tra=" + s_ra +
"\tdec=" + s_dec + "\tclass=" + s_class);
}
As we can see from this code, the client application is able to retrieve
the interesting bits of the table using the getAttribute
method to directly access the fields by their UCD tag, without having to
be concerned about the details of the underlying encoding in VOTable.
Application Cone2
Call a cone search service and print the results table in CSV format.
Usage: cone2 ra dec sr [serviceURL]
To run the application type java dalclient.cone2:
% java dalclient.cone2
# Query: http://chart.stsci.edu/GSCVO/GSC22VO.jsp?RA=12.0&DEC=12.0&SR=0.1
ID_MAIN,POS_EQ_RA_MAIN,POS_EQ_DEC_MAIN,ERROR,ERROR,TIME_EPOCH,PHOT_PHG_R,ERROR,P
HOT_PHG_B,ERROR,PHOT_PHG_V,ERROR,PHOT_PHG_N,ERROR,CLASS_OBJECT,EXTENSION_RAD,PHY
S_ECCENTRICITY,POS_POS-ANG,CODE_QUALITY
N3233000:4280,12.06964142,11.93743758,0.430478,0.539791,1990.784302,16.29,0.43,1
7.74,0.42,99.9,99.9,99.9,99.0,Star,2.9,0.11,6.08,1011202
N3233000:4440,12.00563246,11.98930768,0.430478,0.539791,1990.784302,17.41,0.43,9
9.9,99.9,99.9,99.9,99.9,99.0,Star,2.63,0.17,155.63,1010202
N3233000:4396,12.04016664,11.97401059,0.430478,0.539791,1990.784302,16.5,0.43,18
.69,0.42,99.9,99.9,99.9,99.0,Star,2.79,0.07,175.67,1011202
N3233000:4174,11.97466466,11.90447683,0.430478,0.539791,1990.784302,18.17,0.44,9
9.9,99.9,99.9,99.9,99.9,99.0,Star,2.31,0.17,121.79,1010202
N3233000:4182,11.98926385,11.90476293,0.430478,0.539791,1990.784302,15.31,0.43,1
6.8,0.42,99.9,99.9,99.9,99.0,Star,3.25,0.02,169.14,1011202
N3233000:4321,12.0236914,11.95191743,0.430478,0.539791,1990.784302,16.81,0.43,17
.75,0.42,99.9,99.9,99.9,99.0,Star,2.71,0.07,45.0,1011202
In this case we see the entire table, formatted as CSV. This is convenient for manipulating the data in non-astronomy tools such as a spreadsheet program. The code in this case is very simple as the format conversion from VOTable to CSV is handled entirely by the VO client library:
// Get a new connection to the service.
ConeConnection cone = new ConeConnection(service);
// Form the query.
ConeQuery query = cone.getConeQuery(ra, dec, sr);
// Execute the query and fetch results.
System.out.println("# Query: " + query.getQueryString(0));
query.executeCSV();
In this case the executeCSV() method is used, which executes
the query and prints the results in CSV format to the standard output.
Other methods are available if the application wants to capture the output
and process it further.
Application Cone3
Call a cone search service and return the results as a VOTable.
Usage: cone3 ra dec sr [fname [serviceURL]]
Here we go one level deeper into the cone search protocol, and capture the native VOTable-formatted output of the query in a file:
% java dalclient.cone3
# Query: http://chart.stsci.edu/GSCVO/GSC22VO.jsp?RA=12.0&DEC=12.0&SR=0.1
# copying query result VOTable to file cone3.xml
In this case the VOTable is left in the file "cone3.xml". Any application
which operates upon VOTable can be used to examine the file, e.g.,
topcat, mirage, or any generic program which
manipulates XML files. Here is how the output VOTable looks rendered in
topcat:

Note this is the same VOTable which we encountered in application
cone1 above.
Application Siap1
Call a SIAP service and print selected fields of the result table.
Usage: siap1 ra dec size [serviceURL]
Our first image access application executes a simple positional query to a SIAP service and prints selected fields of the query response table:
% java dalclient.siap1
# Query: http://skyview.gsfc.nasa.gov/cgi-bin/vo/sia.pl?POS=12.0,0.0&SIZE=0.5
# returns 34 records containing 11 attributes each
# --- Summary output ---
ra=12.0 dec=0.0 [300,300] <unknown> Digitized Sky Survey 1
ra=12.0 dec=0.0 [300,300] <unknown> Digitized Sky Survey 1
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT PSPC Pointed Observations
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT PSPC Pointed Observations
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT PSPC Pointed Observations
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT PSPC Pointed Observations
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT All Sky Survey Broad-Band
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT All Sky Survey Broad-Band
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT All Sky Survey (Hard)
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT All Sky Survey (Hard)
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT All Sky Survey (Soft)
ra=12.0 dec=0.0 [300,300] <unknown> ROSAT All Sky Survey (Soft)
ra=12.0 dec=0.0 [300,300] <unknown> Faint Images of the Radio Sky at
ra=12.0 dec=0.0 [300,300] <unknown> Faint Images of the Radio Sky at
ra=12.0 dec=0.0 [300,300] <unknown> NRAO VLA Sky Survey
ra=12.0 dec=0.0 [300,300] <unknown> NRAO VLA Sky Survey
ra=12.0 dec=0.0 [300,300] <unknown> SFD Dust Map
ra=12.0 dec=0.0 [300,300] <unknown> SFD Dust Map
We can tell from this output that we are querying a "mosaic" service since all the candidate images have the requested position and are the same size, even though they come from multiple data collections. The field shown as "<unknown>" in the output is the image format field, which for some reason this service did not return in the query response.
The following is the the part of the code for siap1 which
accesses the image attributes:
s_ra = ((v = r.getAttribute("RA")) != null) ?
v.stringValue() : "";
s_dec = ((v = r.getAttribute("DEC")) != null) ?
v.stringValue() : "";
s_naxes = ((v = r.getAttribute("Naxes")) != null) ?
v.stringValue() : "";
s_naxis = ((v = r.getAttribute("Naxis")) != null) ?
v.stringValue() : "";
s_format = ((v = r.getAttribute("ImageFormat")) != null) ?
v.stringValue() : "";
s_title = ((v = r.getAttribute("Title")) != null) ?
v.stringValue() : (((v = r.getAttribute("ID_MAIN")) != null) ?
v.stringValue() : "");
Something new is going on here, which we did not see with the cone search. For SIA the application is able to access image attributes by the name of the corresponding attribute of the SIA data model, in this case "RA", "DEC", "Naxes", "Naxis", "ImageFormat", and "Title". The client library is automatically mapping the UCD tags used in the SIA V1.0 protocol to the associated fields of the data model, thereby shielding the application from the details of the protocol and protocol version used. The only exception is "ID_MAIN", which is a UCD. In this case the application is being clever and is looking for an ID_MAIN field to use to describe the image, in the event that "Title" is not given.
Application Siap2
Call a SIA service and print the results in CSV format.
Usage: siap2 ra dec size [serviceURL]
As with the cone service, we can execute an SIAP service and output the results in CSV format:
% java dalclient.siap2
# Query: http://skyview.gsfc.nasa.gov/cgi-bin/vo/sia.pl?POS=12.0,0.0&SIZE=0.5
Title,RA,DEC,Naxes,Naxis,Scale,Format,PixFlags,AccessReference,Filesize,VOX:LogicalName
Digitized Sky Survey 1,12.0,0.0,2,300 300,-0.00166666666666667 0.00166666666666667,image/fits,F,http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=Digitized%20Sky%20Survey&VCOORD=12.0%2C0.0&SFACTR=0.5&RETURN=FITS,362880,1
Digitized Sky Survey 1,12.0,0.0,2,300 300,-0.00166666666666667 0.00166666666666667,image/gif,F,http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=Digitized%20Sky%20Survey&VCOORD=12.0%2C0.0&SFACTR=0.5&RETURN=GIF,90000,1
ROSAT PSPC Pointed Observations (2 deg field),12.0,0.0,2,300 300,-0.00166666666666667 0.00166666666666667,image/fits,F,http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=PSPC%202.0%20Deg-Inten&VCOORD=12.0%2C0.0&SFACTR=0.5&RETURN=FITS,362880,2
ROSAT PSPC Pointed Observations (2 deg field),12.0,0.0,2,300 300,-0.00166666666666667 0.00166666666666667,image/gif,F,http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=PSPC%202.0%20Deg-Inten&VCOORD=12.0%2C0.0&SFACTR=0.5&RETURN=GIF,90000,2
ROSAT PSPC Pointed Observations (1 deg field),12.0,0.0,2,300 300,-0.00166666666666667 0.00166666666666667,image/fits,F,http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=PSPC%201.0%20Deg-Inten&VCOORD=12.0%2C0.0&SFACTR=0.5&RETURN=FITS,362880,3
The nice thing about CSV is that it is an open standard, allowing a wide range of tools to be used to work with data in this form. For example, we can import the output of this query directly into a spreadsheet program and operate upon it in various ways. Here is what the result of our standard test query looks like in Microsoft Excel:

Application Siap3
Call a SIA service and return the output VOTable in a file.
Usage: siap3 ra dec size [fname serviceURL]]
Nothing fancy here. As with the cone3 application, this
demonstrates the use of the SIAP query class to capture the results of the
query in a file in VOTable format.
% java dalclient.siap3
# Query: http://skyview.gsfc.nasa.gov/cgi-bin/vo/sia.pl?POS=12.0,0.0&SIZE=0.5
# copying query result VOTable to file siap3.xml
The source code for siap3 provides an illustration of how
to use an InputStream to capture and process the raw output
of the query:
// Get a new connection to the service.
SiapConnection siap = new SiapConnection(service);
// Form the query.
SiapQuery query = siap.getSiapQuery(ra, dec, size);
// Execute the query.
InputStream in = query.executeRaw();
// Copy the query result VOTable to the name output file, overwriting
// it if the file already exists.
File file = new File(fname); file.delete();
FileOutputStream out = new FileOutputStream(file);
byte buf[] = new byte[4096]; int n;
while ((n = in.read(buf, 0, buf.length)) > 0)
out.write(buf, 0, n);
Application Siap4
Call a SIA service and download the first MaxImages images referenced
in the query response table.
Usage: siap4 ra dec size [format [maximages [serviceURL]]]
This application goes one step further and demonstrates how to download selected images using the SIA protocol. For our test program we merely issue the query and blindly download the first 5 images listed in the query response. In a real application one would normally want to apply some client-specific heuristic to select interesting datasets from the query response list.
% java dalclient.siap4
# Query: http://skyview.gsfc.nasa.gov/cgi-bin/vo/sia.pl?POS=12.0,10.0&SIZE=0.2&FORMAT=ALL
# returns 34 records containing 11 attributes each
Downloading images:
Downloading: http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=Digitized%20Sky%20Survey&VCOORD=12.0%2C10.0&SFACTR=0.2&RETURN=FITS
Downloaded ./data15658.fits
Downloading: http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=Digitized%20Sky%20Survey&VCOORD=12.0%2C10.0&SFACTR=0.2&RETURN=GIF
Downloaded ./data15659.gif
Downloading: http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=PSPC%202.0%20Deg-Inten&VCOORD=12.0%2C10.0&SFACTR=0.2&RETURN=FITS
Downloaded ./data15660.fits
Downloading: http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=PSPC%202.0%20Deg-Inten&VCOORD=12.0%2C10.0&SFACTR=0.2&RETURN=GIF
Downloaded ./data15661.gif
Downloading: http://skyview.gsfc.nasa.gov/cgi-bin/pskcall?SURVEY=PSPC%201.0%20Deg-Inten&VCOORD=12.0%2C10.0&SFACTR=0.2&RETURN=FITS
Downloaded ./data15662.fits
By default the library puts the images in the current working directory, assigning an automatically-generated file name. If we examine the downloaded images we see the following:
% ls -l data*
-rw-rw-r-- 1 dtody dtody 368640 Sep 9 12:56 data15658.fits
-rw-rw-r-- 1 dtody dtody 62594 Sep 9 12:56 data15659.gif
-rw-rw-r-- 1 dtody dtody 365760 Sep 9 12:56 data15660.fits
-rw-rw-r-- 1 dtody dtody 0 Sep 9 12:56 data15661.gif
-rw-rw-r-- 1 dtody dtody 365760 Sep 9 12:56 data15662.fits
Note that the library has automatically assigned FITS and GIF file extensions to the downloaded images. This is based on the image format specified in the query response metadata. If this is missing for some reason, a ".tmp" file extension will be assigned. We also note that one of the GIF files has a zero length - no data was returned. Probably there was no data available for the given survey (PSPC) in the given region on the sky. In this case the service should not have indicated that data was available, but computation of the overlap of a ROI with the sky coverage of a survey can be complex and false matches can occasionally occur. Ideally the chain of software should have detected the error somewhere along the line, but that did not happen in this case.
Application "Chart"
Call the SDSS finding chart service and download a graphics image
of the given coordinates and size.
Usage: chart ra dec size [serviceURL]
Blindly returning the first N images from a SIAP query is generally not all that useful. Some services however will only return at most one image in response to a query, in which case we can write a simple program to issue the query and download the referenced image. An example of such a service is the SDSS color finding chart service, which dynamically generates JPEG image cutouts for any region for which SDSS has coverage.
% java dalclient.chart
# Query: http://casjobs.sdss.org/vo/DR4SIAP/SIAP.asmx/getSiapInfo?&FORMAT=image/jpeg&BANDPASS=*&POS=258.127,64.017&SIZE=0.4&opt=G
Downloaded ./data30091.jpeg
By default the test program downloads a galaxy cluster from the SDSS-C4 galaxy cluster catalog (Miller et. al. 2005). If we examine the image returned we can clearly see that the cluster is present (although the cluster appears to be off-center, the mean coordinates are used, which put the member galaxy with the highest local density at the center):

Lets looks at the source code for chart to see how this
works:
// Get a new connection to the service.
SiapConnection siap = new SiapConnection(service);
// Form the query.
SiapQuery query = siap.getSiapQuery(ra, dec, size);
// Enable the graphics overlay (SDSS specific parameter).
query.addParameter("opt", "G");
// Execute the query and fetch results.
System.out.println("# Query: " + query.getQueryString(0));
QueryResponse qr = query.execute();
if (qr.getRecordCount() <= 0) {
System.out.println("no images found");
System.exit(1);
}
// Download the image.
QueryRecord r = qr.getRecord(0);
String path = r.getDataset();
if (path != null)
System.out.println("Downloaded " + path);
else
System.out.print("Download failed");
We see two new things here. First, we have used the
addParameter method to add a service-specific query parameter
("opt=G") to the query. This is supposed to tell the finding chart
service to draw a coordinate overlay grid on the generated graphic.
If we look at the query string as echoed in the output above we see that
the parameter was successfully added to the query. However, no coordinate
overlay grid is drawn. Evidently the "opt" parameter is only supported by
the Web-service version of the service, but nonetheless this demonstrates
how to add specialized parameters to a query.
The second thing this code snippet demonstrates is how to download
a dataset given a dataset descriptor record. This is done using the
getDataset method, and is trivial assuming the SIAP dataset
(image) descriptor contains a valid access reference. A file pathame can
be specified to control where the image goes, otherwise the file will be put
in the current working directory using an automatically generated filename.
In our sample code we are performing almost no error checking, and the
program will probably bomb with a Java exception if anything goes wrong.
Production code would require more robust error detection and recovery.
That concludes our walkthrough of the dalclient code! Although our
sample programs are pretty simple, they should serve to illustrate how to
use the dalclient library to build real client applications.
Doug Tody (NRAO, NVO) September 2005
