JPARSEC wiki
Table of Contents

A PDF version of the following document is available here.

JPARSEC wiki

This page is a wiki or quick guide to help new users getting started with JPARSEC at source code level. If you are only interested in the tools with a graphical user interface, this page is not for you. If you would like to develope code (models, or simply to call JPARSEC functions) then keep reading.

Before starting, there's something I would like to clarify. JPARSEC code is a GPL project and this means that any derived work must be published as GPL code as well, so that anyone can download it, use it, and modify it in any way. In terms of science production this doesn't mean that you must publish any little piece of code that uses JPARSEC (although you should), but you must publish any no little piece of code like a new model or anything relevant in terms of scientific value that is produced using JPARSEC. In my case I publish almost everything, from observational data to the input files for the models (see the Science section), that include usually the observational data (maybe modified or reduced) and also all the input parameters for the models. This means that any of the results in the publications I have collaborated in can be reproduced easily.

You don't need to work in the same way as me, but in case your intention is to develope tools and not to put them available freely to the community (without mails, registration, or similar), please don't use JPARSEC. In addition, the code described here, although being developed with extreme care, is not guarranteed in any form, so use it at you own risk.

Configuring Eclipse and JPARSEC

In this page I assume you have already read everything at the JPARSEC main page (especially the section about installing JPARSEC in Eclipse) and (hopefully) you have the library installed in Eclipse (or your favorite IDE), including all dependencies and the example programs. If this is the case you can go directly to next section, otherwise a basic quick guide is detailed next.

The installation of Eclipse is easy, since the program is distributed as a .zip compressed file. There are many versions of Eclipse in the download section, but the 'Classic' and 'EE' will fit the needs of most users. Just download, uncompress it into your favorite folder, and launch it. In the first execution the program will ask for the workspace directory where all your projects will be saved. I usually set it as eclipse/workspace, creating a 'workspace' directory inside the eclipse directory.

Next step is to select File - New - Java project to create a new Java project. In case you don't see a JRE in the template you have to configure your Java JRE/s (or maybe install one if you don't have any in your system). JPARSEC is tested to work fine on both OpenJDK and Oracle's versions of Java, although the Oracle version is 15% to 20% faster. When defining the project I usually select in the layout to keep source (.java) and compiled (.class) files in the project root, since it is easier for me to manage the files instead of having them spread in different directories. Once you have the project created you can clic the right button of the mouse on the project directory (in the package explorer) to create a new java file or a new package (directory to distribute your java source files).

You can install the entire source code of JPARSEC (keep reading) or only the sample programs to test them (next paragraph). In case you want to install JPARSEC source you can simply copy the directory 'jparsec' with all its subdirectories to eclipse/workspace/<Your Project>, or even to copy your directory and paste it in the root directory of your project in the package explorer of Eclipse (one of the options with the right button is to paste). The directory structure of jparsec is located inside the jparsec.jar file, which is a standard .zip compressed file, so you may need to copy it and change the extension to .zip before uncompressing (besides jparsec directory you will see other files you can ignore). For a successfull compilation it is necessary to solve the errors due to having unsatisfied dependencies. With the right button of the mouse on the root directory of your project select Build - Configure Build Path, and add as dependencies all the .jar files you have previously download from http://conga.oan.es/~alonso/jparsec/lib/ (download is automatic when installing the set of GUI tools from the instructions at the main JPARSEC page, except for jpl_ephem.jar and the .jar files at http://conga.oan.es/~alonso/jparsec/lib/otherDependencies/, which should be downloaded by hand, otherwise you should download each of them by hand), including the .jar files in the 'cds' subdirectory but not in the 'old' and 'jogl' ones neither jparsec.jar file. Eclipse allows you to organize the dependencies by creating 'user libraries' (groups of .jar files), but the easiest way is to add all dependencies with the 'Add External JARs' option located in the Libraries tab. The additional .jar files required not downloaded with the manager should be copied to another directory, not the /lib inside the manager, because the manager will remove any strange file not strictly required as depedency before running any of the models currently distributed.

To install the sample programs you can repeat the above process for a new project. In case you have the previous project with the JPARSEC source, then in the Configure Build Section add as dependency only the previous project entirely, that will be enough to solve all dependencies in a new project using JPARSEC. In case you skipped previous step, follow a similar process and copy only the examples to your project without JPARSEC source code. To solve dependencies add all .jar files to that project as in the previous paragraph, but in this case including jparsec.jar (javadoc documentation is included on it). This second option is faster, but less efficient if you plan to develope other projects that depend on JPARSEC.

In Internet it is easy to find tutorials explaining how to use Eclipse to develope. At first sight it seems rather complex but only some of its multiple features are really useful and important to know when writting code. For a Java tutorial you can search Internet and download free books, for instance the Thinking in Java or any other. JPARSEC is written considering most of the recommendations about code conventions to write a clean and easy to understand code, so I recommend you to read also that document and do some practice.

A first program

Besides JPARSEC a set of examples are also distributed. The examples are designed to provide a basic demostration of the capabilities of the library. In this section we will see a first program demo, and later I will explain some details about the organization and philosophy of JPARSEC and how are you supposed to use and take advantage of it.

The code below shows the class StartPoint included in the examples as jparsec.examples.simple.StartPoint. It is a basic program that does absolutely nothing besides constructing a correct Java class and importing all packages from JPARSEC (and printing 'Hellow world!' to the console). A static variable and a static subroutine are defined but nothing is done with them. Static fields/methods are called with the name of the file or class-dot-the name of the method/variable, while non-static stuff (instances) are called using the object representing an instance of that class (and the definitions of those methods/variables don't contain the static flag). Distinction between static and non static stuff in a class is something object-oriented so I recommend you to join a Java course or read books in case you don't understand, although maybe at the end of the wiki you will understand. Anyway, it is a basic and important point so I prefer to mention it here. Static content is useful for operations not requiring values from a given instance of that class, the most basic idea is to hold constant values that will be available to all instances and even without creating one if you access them in a static way. During the wiki in some static (or even non-static) fields you will see the final clause as well. final fields are those that cannot change, very useful for constants (speed of light or whatever) when you want to be sure that no code will be allowed to change the value of that variable. Below there is a subsection explaining the basics about instances (objects), variables, and other important things about Java and object oriented languages in general.

The program itself is inside the method public static void main(String args[]), that includes an array of Strings as input parameters but does nothing with them. This main method is a special Java construction to identify the main method of a program, and must be defined exactly as it is shown so that in Eclipse (in the Run - Run as option) you can execute the 'main' program. Eclipse allows you to modify the input parameters used to call the main program, but this is done by hand in the Run Configurations option (or calling the program from a console) and it is not efficient, so I (almost) never use this feature.

StartPoint.java
import jparsec.astronomy.*;
import jparsec.astrophysics.gildas.*;
import jparsec.astrophysics.photometry.*;
import jparsec.ephem.*;
import jparsec.ephem.EphemerisElement.FRAME;
import jparsec.ephem.Target.TARGET;
import jparsec.ephem.moons.*;
import jparsec.ephem.planets.*;
import jparsec.ephem.planets.imcce.*;
import jparsec.ephem.probes.*;
import jparsec.ephem.stars.*;
import jparsec.ephem.event.*;
import jparsec.ephem.event.MainEvents.EVENT_TIME;
import jparsec.graph.*;
import jparsec.graph.chartRendering.*;
import jparsec.graph.chartRendering.Graphics.ANAGLYPH_COLOR_MODE;
import jparsec.graph.chartRendering.SatelliteRenderElement.PLANET_MAP;
import jparsec.graph.chartRendering.frame.*;
import jparsec.graphic.*;
import jparsec.io.*;
import jparsec.io.image.*;
import jparsec.math.*;
import jparsec.math.matrix.*;
import jparsec.model.*;
import jparsec.observer.*;
import jparsec.observer.Country.COUNTRY;
import jparsec.time.*;
import jparsec.time.TimeElement.SCALE;
import jparsec.time.calendar.*;
import jparsec.util.*;
import jparsec.vo.*;
 
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.*;
import java.net.*;
import java.util.*;
 
/**
 * An example that does nothing, but serves as starting point to create programs.
 * @author T. Alonso Albi - OAN (Spain)
 * @version 1.0
 */
public class StartPoint {
 
	/** An static variable. */
	public static double variable1 = 0;
 
	/**
	 * A subroutine or method.
	 */
	public static void subroutine1()
	throws JPARSECException	{
		System.out.println("Hellow world!");		
	}
 
	/**
	 * A program.
	 * @param args To be used/unused.
	 */
	public static void main(String args[])
	{
		try {
			// Lets do nothing
			StartPoint.subroutine1();
		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

The first line of the code should read 'package …;', where … is the path to the current package or directory where the class is located. In the example is jparsec.examples.simple, but in the above code I have omitted that line so that you can paste the above code in a new class called StartPoint.java, located in the root of your project (without creating any package at all), where the package instruction is not required. All code lines must end with ';', and you can spread a line of code in different lines (rows) in the file.

Note that the main program contains a try - catch statement used to execute a given code and catch any possible error that is produced inside. In case an error is found, its trace is written to the console and the program continues after the code { … } located in the catch block, which basically means that the program ends.

Previous example is a basic construction required for any program. In the next sections I will put only code snippets that should be located inside the try - catch block, without the rest of stuff. Of course, you can choose to put code in different methods (to keep the forthcomming examples organized to call them from the main method) or include it directly in the try - catch block, as you prefer. Obviously the above example can be simplified by eliminating the method called from inside the main program. Once you have executed you code with Run - Run as - Java application, you can quickly execute it again after modifying the code and saving it (with Ctrl + S) with the F11 key. Code compilation is done by Eclipse automatically.

Errors will appear as red marks on the left, and warnings (like classes/packages imported but not used) as yellow marks. Putting the mouse on them will give you some information about the problem. Instructions are written in colors to help code reading. In the above example comments are in grey color and are of two types: lines starting with // to comment something inside the methods, and lines starting/ending with /** ... */ for comments that will appear in the so-called javadoc. The javadoc is the documentation you can see in Eclipse about any of the JPARSEC methods by moving the mouse to the instruction and waiting for a second or so (the corresponding Java code, in case you installed JPARSEC source, can be read by clicking on the method and pressing F3). It can also be exported to a set of HTML pages like these for JPARSEC. To document your programs with their input parameters and to keep that documentation updated is something extremely important to be able (you or any other) to continue working in your programs and models. Inside the simple comments with // you can set marks in your code (in blue on the right edge) like TODO, FIXME, and others. Certain tabs or views in Eclipse, like the Tasks one, will list all current pending tasks in your programs marked by you with the mentioned marks.

Basics about variables and objects, and best practices (Enum, unit conversions)

A double value is defined using, for instance, 'double value = 0;', and can be located within the class or inside the main program. Inside the main program means 'value' is available only there. Within the class means the value belongs to the class and can be used inside the class everywhere. In the second case some other attributes can be added to the variable definition. The value can be private to be sure it is not available from another class (Java file) outside this one, and public to be available from other classes. The attribute final means its value cannot change. And the static attribute means it is not necessary to create an instance of the class (object) to access the variable. A basic example of a class containing public static final double values is jparsec.math.Constant, that contains a large amount of physical constants used within the entire library. An example of a simple class containing some non static variables is CityElement, that holds the basic properties of any city object, like longitude, latitude, and others. Non static means you can define multiple CityElement objects, each of them with a particular value of longitude, latitude, …, different from the others. For that purpose the code would be something like CityElement myCity = new CityElement(…values…), where the values to provide must match any of the constructors for the class defined at the beginning of the CityElement class, as public CityElement(…values…) { … }. Any static variable defined in the class will have the same value for all these objects in a given point in your program. Obviously, depending on the nature of the variable it will be more useful to have it as static or as non static. Objects can also be nested one inside another. For instance, jparsec.time.AstroDate defines dates using year, month, etc, and a TimeElement object uses an AstroDate object for the date and a SCALE enumeration value (another useful feature) to define the time scale for the AstroDate object, so that the time instant is completely defined by TimeElement and time conversions are possible. AstroDate is still useful since often it is not necessary to consider the time scale. Another example is the ObserverElement object, that can be created from a CityElement object or as a new object by giving the values for the fields. Eclipse will list all possible constructors if you write ObserverElement myObserver = new ObserverElement();, clic with the mouse inside the '()', and press CTRL + space bar. Private fields are not accesible (unless your main program is defined in the class the instance belongs to), which in case of non static fields allows to set hidden variables for the instance. The general convention is to set all possible things as private, so that when developing you will only see what you need to see, which means less confusion.

An enumeration is a special object that has some possible values and only those. In the TimeElement object one possibility would be to set the time scale as an integer value, so that 0 means Local Time, 1 Universal Time, and so on. This is 'typical' is scientific libraries written in Fortran, that needs a lot of code to check for incorrect values and forces you to read the documentation to search for the integer you need to put in. Another problem also is that in this case you can input 999999 for the time scale, which means nothing, but it could be done. To avoid that the enum set of values guarranties that only one of those values, with a perfect name definition (not '0' or '12345') can be set as input when defining the object, which means the code it is much more clear and easy to read, and less error prone. In case of having several input values like the previous one another confussion would be the ordering of the input parameters, something like … 1, 5, 7, …. each of them meaning something internaly, but with the possibility of inserting wrongly …. 5, 1, 7 … or any other combination, which would be an error very difficult to find. Enum values avoid this. Another example of code quality would be unit conversions or general calculations using jparsec.math.Constant. Imagine you want to convert days to seconds and later mix this with other conversions or calculations. You can multiply by the correct value or use Constant.SECONDS_PER_DAY, which is 'physically' more consistent and clear since you want to convert one thing to another, not just 'multiply' it by a given value that, by error, could be modified, producing another error very difficult to find.

In JPARSEC, virtually any field with a limited set of possible values (time scales, countries, planets and satellites, …) is defined as a Enumeration, so it is not possible to give an incorrect value.

Basic common objects to work with JPARSEC

JPARSEC contains a lot of classes, but a large number of them uses the same main objects as input parameters in their methods. These objects are a TimeElement object to store a given time, and ObserverElement object for the observer, and a EphemerisElement object for the ephemerides properties. There are lots of tasks not demanding these objects, but for complex enough programs you can hardly live without them.

Check for instance the basic example at jparsec.examples.simple.Time. You will see a code like this:

AstroDate astro = new AstroDate(2006, AstroDate.JANUARY, 1, 0, 0, 0);
TimeElement time = new TimeElement(astro, TimeElement.SCALE.BARYCENTRIC_DYNAMICAL_TIME);
CityElement city = City.findCity("Madrid");
ObserverElement observer = ObserverElement.parseCity(city);
EphemerisElement eph = new EphemerisElement();
 
double TTminusUT1 = TimeScale.getTTminusUT1(time, observer);
System.out.println("TT-UT1: "+TTminusUT1+" s");
 
double jd_TDB = astro.jd();
double jd_LT = TimeScale.getJD(time, observer, eph, TimeElement.SCALE.LOCAL_TIME);

In this example a TimeElement object is created using an AstroDate object and an identifier to select the time scale in which the AstroDate object is given. It sounds extrange but it isn't: the time is defined by a date and a time scale in which the date is given (local time, universal, terrestrial time, barycentric dynamical time …). The AstroDate instance is a help to create a TimeElement object, but AstroDate doesn't take the time scale in consideration. Exploring the documentation you will see there are some other ways to create an AstroDate object or a TimeElement object with their constructors, for instance using directly a Julian day contained in a double value.

With the observer the situation is similar. You can create an observer by giving the values for longitude, latitude, and other fields using ObserverElement observer = new ObserverElement(… parameters …);, or you can use other supporting classes to do that faster. Support classes are a CityElement and an ObservatoryElement. These classes simply holds the data for the observer in different fields, and to search and retrieve an object of any of these classes the methods contained in the classes City and Observatory should be used (although you can create an object representing a city or an observatory as in the previous example with the ObserverElement object). In all cases you can move the mouse to the different elements in the construction to access the documentation for the method directly, as it is written in the source files in JPARSEC. You can also do the same from the current cursor location with Ctrl + space bar. In case you installed JPARSEC source code as a project you can select with the cursor a given method that belongs to JPARSEC and check it source code by pressing F3. I strongly recommend you to move the mouse to the expressions TimeElement, ObserverElement, and EphemerisElement (both at the beginning of the line and after the 'new' when they are used as constructors, as well as in method City.findCity for instance) to see the documentation. With some practice you will see that Eclipse is designed to help a lot when writing code and correcting bugs.

The observer object contains more properties besides those directly entered in the provided constructors. For instance, ambient temperature and atmospheric pressure will affect (slightly) the output results for the apparent elevation of a source, when applying the refraction correction. Since they are private you cannot set the temperature, for instance, using observer.temperature = 100;, but you can use the provided method observer.setTemperature(100);, which is equivalent.

The example simply calculates the difference between Terrestrial Time and Universal Time UT1, and calculates the Julian day in TDB (barycentric dynamical time) and local time. The observer is obviously required to correct the date for the time zone and daylight saving time of the observer, but the library takes care of all this, you only have to call the TimeScale.getJD method. There are multiple possibilities to use these objects in different kind of calculations. Here I summarize some of them. The examples showing sky and planetary rendering make use of some classes to create pictures or charts, that will be mentioned and explained in later sections.

Ephemerides calculations

Here is a simple example from jparsec.examples.simple.EphemTest1 to obtain the apparent position of Saturn. You can use the same time and observer objects defined in the previous example. The EphemElement object contains fields for every property (RA, DEC, and so on, you can explore them writing 'ephem.' below the line that creates it), and ConsolReport is a help class with methods to quickly report results to the console, from String arrays to the fields contained in an EphemElement object. As you will see more clearly in the next section, JPARSEC is not only an astronomical/astrophysical library, but also a library with lots of general methods to help manipulating data efficiently.

EphemerisElement eph = new EphemerisElement(TARGET.SATURN, EphemerisElement.COORDINATES_TYPE.APPARENT, 
	EphemerisElement.EQUINOX_OF_DATE, EphemerisElement.TOPOCENTRIC, EphemerisElement.REDUCTION_METHOD.IAU_2006, 
	EphemerisElement.FRAME.DYNAMICAL_EQUINOX_J2000, EphemerisElement.ALGORITHM.MOSHIER);
 
// Include rise/set/transit calculations
boolean full_ephem = true;
 
// Calculate
EphemElement ephem = Ephem.getEphemeris(time, observer, eph, full_ephem);
 
// Report results
ConsoleReport.fullEphemReportToConsole(ephem);

Run it using Run - Run as Java application, and the output you will see in the Console tab in Eclipse will be:

Saturn Right ascension: 08h 50m 10.6658s
Saturn Declination: 18º 21' 59.992"
Saturn Distance: 8.23828 AU
Saturn Rise: 2006-01-01 20:10:34 LT
Saturn Transit: 2006-01-01 03:22:34 LT
Saturn Transit elevation: 67º 57' 17.361"
Saturn Set: 2006-01-01 10:30:21 LT
Saturn Light time: 4110.9404 s
Saturn Constellation: Cancer
Saturn Elongation: 150º 29' 28.556"
Saturn Phase angle: -3º 2' 55.855"
Saturn Phase: 0.999
Saturn Heliocentric ecliptic longitude: 126º 52' 15.691"
Saturn Heliocentric ecliptic latitude: 34' 1.710"
Saturn Heliocentric distance: 9.10708
Saturn Azimuth: 114º 47' 44.431"
Saturn Elevation: 52º 4' 34.955"
Saturn Defect of illumination: 0.007"
Saturn Angular radius: 10.087"
Saturn Magnitude: -0.164
Saturn Surface brightness: 6.892
Saturn Pole right ascension: 40º 51' 23.230"
Saturn Pole declination: 83º 33' 43.471"
Saturn Subsolar longitude: 74º 21' 38.047"
Saturn Subsolar latitude: -23º 6' 1.044"
Saturn Paralactic angle: -46º 44' 15.194"
Saturn Bright limb angle: 104º 29' 54.697"
Saturn Position angle of axis: -6º 46' 12.092"
Saturn Position angle of pole: -21º 50' 8.622"
Saturn Longitude of central meridian system I: 246º 40' 31.873"
Saturn Longitude of central meridian system III: 71º 21' 11.380"

You can obtain ephemerides for natural satellites using a satellite as target body. Writting 'TARGET.' will show you a list of possible targets. When selecting a natural satellite you should add eph.algorithm = ALGORITHM.NATURAL_SATELLITE; or change MOSHIER to NATURAL_SATELLITE in the constructor. For comets and asteroids you have to deal with the database of orbital elements using OrbitEphem class. You can see an example at jparsec.examples.medium.EphemTest2MinorObjects.

Anyway, since this requires 'a little work' from the user there's a solution: to call the method Ephem.setEphemerisElementAccordingToBodyType with a given source name (comet, asteroid, whatever) and let JPARSEC check automatically the kind of object selected and to adjust the ephemeris properties accordingly. Another even easier possibility is the following code:

EphemElement ephem = Ephem.getEphemeris(TARGET.SATURN.getName(), time, observer, eph, true);

Previous code assumes you have previously initialized the time, observer, and ephemeris objects. The first field is the source name, which can be a comet/asteroid, star, or even deep sky object besides a planet. The eph object is adapted to compute ephemerides for these different kind of objects, although the eph object used in the call is not modified. Next section provides an even shorter code of this example using a LocationElement object.

The ephemerides object contains interesting properties. For instance, ephemerides can be apparent, astrometric, or geometric; the reduction method can be changed to obtain ephemerides using the algorithms recommended by the IAU in 1980 or 2000 (besides other methods, allowing to match the output results from programs or ephemerides servers applying old algorithms), and the planetary algorithms by Moshier can be changed to apply other methods including JPL integrations (you will need the jpl_ephem.jar dependency file). As with the observer object some properties of the ephemeris object can be modified to disable certain corrections. For instance, EOP (Earth Orientation Parameters) used for precise time correction and polar motion can be disabled to speed-up calculations, calling the method optimizeForSpeed() on an EphemerisElement object.

Coordinate conversions

Put the following line after the previous code to write to the console the position of Saturn in galactic coordinates. This example shows how to combine several calls into the same line. The class LocationElement can store longitude (or RA), latitude (or DEC), and distance (and includes among others methods to calculate angular distances), and is used in this example (see the toString() method that quickly returns the position as a string), although no explicit mention appear to it in the code. Of course you can use CoordinateSystem for converting to ecliptic or horizontal coordinates.

System.out.println("Galactic coordinates: "+CoordinateSystem.equatorialToGalactic(ephem.getEquatorialLocation(), 
	time, observer, eph).toString());

CoordinateSystem can convert between equatorial, ecliptic, galactic, and horizontal (observer dependent) coordinates. The above code uses the already calculated ephemerides, but there is a very simple approach to obtain the current apparent equatorial position of a body, replacing ephem.getEquatorialLocation() by the following loc object:

LocationElement loc = new LocationElement(TARGET.MARS.getName(), true);

The true value above is to return apparent coordinates referred to the true equinox of date. Note the last method does not require an observer or date: observer will be supposed to be at the Earth's center, and time will be the current computer time assumed to be local time. The method TARGET.MARS.getName() will return “Mars” in case the language for the library is selected as English, which is the default. In case you call Translate.setDefaultLanguage(LANGUAGE.SPANISH); at the beginning it will returns “Marte”, but the ephemerides will be the same. Note this also applies to any other calculation that returns strings (including charting) in later subsections, that can be obtained in Spanish or in English.

Astronomical events

The package jparsec.ephem.event contains classes to calculate some astronomical events like eclipses, events involving natural satellites (transits, shadows, and even mutual phenomena) with extreme accuracy. Ephemerides implementation in JPARSEC follows only state-of-the-art algorithms with a nominal accuracy (when using things in the right way in the EphemerisElement object and applying the ephemerides method using JPL integration) better than 0.1 mas.

MainEvents

The classes I will show here are MainEvents (to return general events for the year or the planets), MoonEvents (for natural satellites), and SolarEclipse and LunarEclipse classes. For MainEvents, a very simple test case is to obtain the next Venus transit between a given date (in a previous AstroDate object named astro) and the next two years. Set that date to January, 2012 to get a result.

SimpleEventElement see = MainEvents.getMercuryOrVenusTransit(TARGET.VENUS, astro.jd(), astro.jd()+2*365.25, true); 
if (see != null) System.out.println("Venus transit on jd "+ see.time+"/"+TimeFormat.formatJulianDayAsDateAndTime(TimeScale.getJD(new TimeElement(see.time, SCALE.BARYCENTRIC_DYNAMICAL_TIME), observer, eph, SCALE.LOCAL_TIME), SCALE.LOCAL_TIME));

Add the following lines after the previous ones to calculate other results. For instance:

see = MainEvents.SaturnRingsMaximumAperture(astro.jd(), EVENT_TIME.NEXT);
if (see != null) System.out.println("Next time Saturn rings in maximum aperture on jd "+ see.time+" TT / "+TimeFormat.formatJulianDayAsDateAndTimeOnlyMinutes(TimeScale.getJD(new TimeElement(see.time, SCALE.TERRESTRIAL_TIME), observer, eph, SCALE.UNIVERSAL_TIME_UT1), SCALE.UNIVERSAL_TIME_UT1)+". Details: "+see.details);
 
boolean maximumAccuracy = true;
see = MainEvents.getPlanetaryEvent(TARGET.MARS, astro.jd(), SimpleEventElement.EVENT.PLANET_MINIMUM_DISTANCE_FROM_SUN, EVENT_TIME.NEXT, maximumAccuracy);
if (see != null) System.out.println("Next Mars perihelion on jd "+ see.time+" TT / "+TimeFormat.formatJulianDayAsDateAndTimeOnlyMinutes(TimeScale.getJD(new TimeElement(see.time, SCALE.TERRESTRIAL_TIME), observer, eph, SCALE.UNIVERSAL_TIME_UT1), SCALE.UNIVERSAL_TIME_UT1)+". Details: "+see.details);

MainEvents contains a large set of methods to calculate the dates of different phenomena, and LunarEvent focuses on events related to our Moon but not considered as main events (like eclipses), for instance librations, perigee/apogee, and maximum/minimum declination.

MoonEvents

MoonEvents returns natural satellite events. You will need two TimeElement objects to return events between a range of dates. Possible events are those returned by getPhenomena() and getMutualPhenomena(). The first case are events between one satellite and its mother planet, the second mutual events between the satellites. In both cases the exact returned times depends on how you consider that the event starts/ends, something that can be controlled with the method MoonEvent.setEventDefinition. The lines used to show the results are taken from the example jparsec.examples.advanced.MoonOcultation (SatellitePaths is another example). Both of them creates charts, although I will explain them later. For instance:

AstroDate astroi = new AstroDate(2454948.7783598904-0.1); // (2009, AstroDate.APRIL, 27, 5, 0, 0);
AstroDate astrof = new AstroDate(2454948.7826654227+0.1); // (2009, AstroDate.APRIL, 27, 8, 0, 0);
 
TimeElement timei = new TimeElement(astroi, TimeElement.SCALE.BARYCENTRIC_DYNAMICAL_TIME);
TimeElement timef = new TimeElement(astrof, TimeElement.SCALE.BARYCENTRIC_DYNAMICAL_TIME);
 
EphemerisElement eph = new EphemerisElement(TARGET.JUPITER, EphemerisElement.COORDINATES_TYPE.APPARENT,
		EphemerisElement.EQUINOX_OF_DATE, EphemerisElement.GEOCENTRIC, EphemerisElement.REDUCTION_METHOD.IAU_1976,
		EphemerisElement.FRAME.ICRF);
eph.algorithm = EphemerisElement.ALGORITHM.JPL_DE405;
 
MoonEvent me = new MoonEvent(timei, observer, eph, timef, 30, 1, true);
MoonEventElement ev[] = me.getMutualPhenomena(false);
 
// Show results
System.out.println("FOUND EVENTS");
for (int i=0; i<ev.length; i++)
{
	AstroDate astroI = new AstroDate(ev[i].startTime);
	TimeElement timeI = new TimeElement(astroI, TimeElement.SCALE.BARYCENTRIC_DYNAMICAL_TIME);
	double jdI = TimeScale.getJD(timeI, observer, eph, TimeElement.SCALE.BARYCENTRIC_DYNAMICAL_TIME);
	AstroDate astroI2 = new AstroDate(jdI);
	String eDateI = astroI2.getYear()+"-"+astroI2.getMonth()+"-"+astroI2.getDay();
	String eTimeI = astroI2.getHour()+":"+astroI2.getMinute()+":"+astroI2.getRoundedSecond();
 
	AstroDate astroF = new AstroDate(ev[i].endTime);
	TimeElement timeF = new TimeElement(astroF, TimeElement.SCALE.BARYCENTRIC_DYNAMICAL_TIME);
	double jdF = TimeScale.getJD(timeF, observer, eph, TimeElement.SCALE.BARYCENTRIC_DYNAMICAL_TIME);
	AstroDate astro2F = new AstroDate(jdF);
	String eDateF = astro2F.getYear()+"-"+astro2F.getMonth()+"-"+astro2F.getDay();
	String eTimeF = astro2F.getHour()+":"+astro2F.getMinute()+":"+astro2F.getRoundedSecond();
 
	String visible = "YES";
	if ((ev[i].elevation * Constant.RAD_TO_DEG) < 20) visible = "DIFFICULT";
	if ((ev[i].elevation * Constant.RAD_TO_DEG) < 5) visible = "NO";
	if (!ev[i].visibleFromEarth) visible = "NO*";
	System.out.println(ev[i].mainBody.getName()+" & "+ MoonEventElement.EVENTS[ev[i].eventType.ordinal()]+" by & "+ev[i].secondaryBody.getName()+" & "+eDateI+" & "+eTimeI+" & "+eTimeF+" & "+ev[i].details+" & "+visible);
 
	// Return a chart of the event. A magnitude of 100 is a default value when the entire
	// satellite is eclipsed/occulted.
	CreateChart ch = me.getLightCurve(ev[i], TimeElement.SCALE.UNIVERSAL_TIME_UTC, true, false);
	ch.showChartInJFreeChartPanel();
}
// IMCCE: start at 6 40 51, end at 6 47  3, TDB (ftp://ftp.imcce.fr/pub/ephem/satel/phemu09)

As you can see more than 50% of the code is just for formatting the output events. Some lines can be simplified, but the above code allows to modify the output time scale for the times of the events by modifying the lines creating the jdI and jdF values. The last line shows the results from calculations done at IMMCE in Paris. As you will see, JPARSEC gives a result in the same second.

On April 27, 2009, Io was occulted by Ganimede. The following chart shows this phenomena as it can be shown using MoonOcultation and SatellitePaths examples.


 

LunarEclipse and SolarEclipse

Here is a piece of code to return the next lunar eclipse after a given date defined by a TimeElement object called time.

// Reduce (unnecesary) accuracy to improve performance
//eph.preferPrecisionInEphemerides = false;
//eph.correctForEOP = false;
//eph.correctForPolarMotion = false;
eph.optimizeForSpeed();
eph.targetBody = TARGET.Moon;
 
// Obtain accurate events for the next lunar/solar eclipse
LunarEclipse le = new LunarEclipse(time, observer, eph);
double jdMax  = le.getEclipseMaximum();
TimeElement t_max = new TimeElement(jdMax, SCALE.BARYCENTRIC_DYNAMICAL_TIME);
System.out.println(le.getEclipseType() + " lunar eclipse on " + TimeFormat
	.formatJulianDayAsDateAndTime(jdMax, SCALE.BARYCENTRIC_DYNAMICAL_TIME) + ". In TDB:");
 
MoonEventElement[] events = le.getEvents();
for (int i = 0; i < events.length; i++)
{
	if (events[i].startTime != 0.0)
		System.out.println("From "+TimeFormat.formatJulianDayAsDateAndTime(events[i].startTime, null) + " to "+
			TimeFormat.formatJulianDayAsDateAndTime(events[i].endTime, null)+" (" + events[i].details + ")");
}

For a solar eclipse the idea is identical, but using SolarEclipse class. See jparsec.examples.medium.EclipseTest for an example. The ephemerides properties (eph object) define if the calculations will be topocentric (using the supplied ObserverElement object) or geocentric. In the previous code the jparsec.ephem.event.Saros class is used to obtain the Saros series data for the eclipse. That class can also calculate eclipse dates very quickly:

AstroDate astro = new AstroDate(2007, AstroDate.JANUARY, 1, 0, 0, 0);
TimeElement time = new TimeElement(astro, SCALE.UNIVERSAL_TIME_UTC);
EphemerisElement eph = new EphemerisElement(TARGET.Moon, EphemerisElement.COORDINATES_TYPE.APPARENT,
	EphemerisElement.EQUINOX_OF_DATE, EphemerisElement.GEOCENTRIC, EphemerisElement.REDUCTION_METHOD.IAU_2006,
	EphemerisElement.FRAME.ICRF, EphemerisElement.ALGORITHM.MOSHIER);
CityElement city = City.findCity("Madrid");
ObserverElement observer = ObserverElement.parseCity(city);
 
System.out.println("SAROS TABLE OF ECLIPSES");
double jd0 = astro.jd();
double jd1 = jd0 + 18 * 365.25;
ArrayList<double[]> v = Saros.getAllEclipses(jd0, jd1);
 
// Create formatted output
System.out.println("Eclipse       Type         Date       JD max  Saros    Inex   Eclipse #/total");
for (int i = 0; i < v.size(); i++)
{
	double element[] = v.get(i);
	double jd = element[0];
	int ecl_type = (int) element[1];
	int type = (int) element[2];
	int sarosN = (int) element[3];
	int inex = (int) element[4];
	int ecl_n = (int) element[5];
	int max_n = (int) element[6];
 
	String separator = "   ";
	String line = FileIO.addSpacesBeforeAString(Saros.getEclipsedTarget(SimpleEventElement.EVENT.values()[ecl_type]).getName(), 5) + separator;
	line += FileIO.addSpacesBeforeAString(Saros.getEclipseTypeAsString(SolarEclipse.ECLIPSE_TYPE.values()[type]), 10) + separator;
	line += TimeFormat.formatJulianDayAsDate(jd) + separator;
	line += FileIO.addSpacesAfterAString(Functions.formatValue(jd, 1), 10) + separator;
	line += FileIO.addSpacesAfterAString("" + sarosN, 3) + separator + separator;
	line += FileIO.addSpacesAfterAString("" + inex, 2) + separator + ecl_n;
	if (max_n != -1) line += "/" + max_n;
	System.out.println(line);
}

Previous code will output the following table:

SAROS TABLE OF ECLIPSES
Eclipse       Type         Date       JD max  Saros    Inex   Eclipse #/total
 Moon        total   2007-03-03    2454163.5    123      50   52/72
  Sun      partial   2007-03-19    2454178.6    149      32   20/71
 Moon        total   2007-08-28    2454340.9    128      42   40/71
  Sun      partial   2007-09-11    2454355.0    154      24   6/71
  Sun      partial   2008-02-07    2454503.7    121      77   60/71
 Moon        total   2008-02-21    2454517.6    133      34   26/71
  Sun      partial   2008-08-01    2454679.9    126      69   47/72
 Moon      partial   2008-08-16    2454695.4    138      26   28/82
  Sun      annular   2009-01-26    2454857.8    131      61   50/70
 Moon    penumbral   2009-02-09    2454872.1    143      18   17/72
 Moon    penumbral   2009-07-07    2455019.9    110      71   71/72
  Sun        total   2009-07-22    2455034.6    136      53   37/71
 Moon    penumbral   2009-08-06    2455049.5    148      10   3/70
 Moon    penumbral   2009-12-31    2455197.3    115      63   57/72
  Sun      annular   2010-01-15    2455211.8    141      45   23/70
 Moon      partial   2010-06-26    2455374.0    120      55   57/83
  Sun      partial   2010-07-11    2455389.3    146      37   27/76
 Moon        total   2010-12-21    2455551.8    125      47   48/72
  Sun      partial   2011-01-04    2455565.9    151      29   14/72
  Sun      partial   2011-06-01    2455714.4    118      82   68/72
 Moon        total   2011-06-15    2455728.3    130      39   34/71
  Sun      partial   2011-07-01    2455743.9    156      21   1/69
  Sun      partial   2011-11-25    2455890.8    123      74   53/70
 Moon        total   2011-12-10    2455906.1    135      31   23/71
  Sun      annular   2012-05-20    2456068.5    128      66   58/73
 Moon      partial   2012-06-04    2456083.0    140      23   24/77
  Sun        total   2012-11-13    2456245.4    133      58   45/72
 Moon    penumbral   2012-11-28    2456260.1    145      15   11/71
 Moon    penumbral   2013-04-25    2456408.3    112      68   65/72
  Sun      annular   2013-05-10    2456422.5    138      50   31/70
 Moon    penumbral   2013-10-18    2456584.5    117      60   52/71
  Sun        total   2013-11-03    2456600.0    143      42   23/72
 Moon        total   2014-04-15    2456762.8    122      52   56/74
  Sun      partial   2014-04-29    2456776.8    148      34   21/75
 Moon        total   2014-10-08    2456939.0    127      44   42/72
  Sun      partial   2014-10-23    2456954.4    153      26   9/70
  Sun      partial   2015-03-20    2457101.9    120      79   61/71
 Moon        total   2015-04-04    2457117.0    132      36   30/71
  Sun      partial   2015-09-13    2457278.8    125      71   54/73
 Moon        total   2015-09-28    2457293.6    137      28   26/78
  Sun        total   2016-03-09    2457456.6    130      63   52/73
 Moon    penumbral   2016-03-23    2457471.0    142      20   18/73
  Sun      annular   2016-09-01    2457632.9    135      55   39/71
 Moon    penumbral   2016-09-16    2457648.3    147      12   8/70
 Moon    penumbral   2017-02-11    2457795.5    114      65   59/71
  Sun      annular   2017-02-26    2457811.1    140      47   29/71
 Moon      partial   2017-08-07    2457973.3    119      57   61/82
  Sun        total   2017-08-21    2457987.3    145      39   22/77
 Moon        total   2018-01-31    2458150.1    124      49   49/73
  Sun      partial   2018-02-15    2458165.4    150      31   17/71
  Sun      partial   2018-07-13    2458312.6    117      84   69/71
 Moon        total   2018-07-27    2458327.3    129      41   38/71
  Sun      partial   2018-08-11    2458341.9    155      23   6/71
  Sun      partial   2019-01-06    2458489.6    122      76   58/70
 Moon        total   2019-01-21    2458504.7    134      33   27/72
  Sun      partial   2019-07-02    2458667.3    127      68   58/82
 Moon      partial   2019-07-16    2458681.4    139      25   21/79
  Sun      annular   2019-12-26    2458843.7    132      60   46/71
 Moon    penumbral   2020-01-10    2458859.3    144      17   16/71
 Moon    penumbral   2020-06-05    2459006.3    111      70   67/71
  Sun      annular   2020-06-21    2459021.8    137      52   36/70
 Moon    penumbral   2020-07-05    2459035.7    149      9    3/71
 Moon    penumbral   2020-11-30    2459183.9    116      62   58/73
  Sun        total   2020-12-14    2459198.2    142      44   23/72
 Moon      partial   2021-05-26    2459361.0    121      54   55/82
  Sun      partial   2021-06-10    2459376.0    147      36   23/80
 Moon        total   2021-11-19    2459537.9    126      46   45/70
  Sun      partial   2021-12-04    2459552.8    152      28   13/70
  Sun      partial   2022-04-30    2459700.4    119      81   66/71
 Moon        total   2022-05-16    2459715.7    131      38   34/72
  Sun      partial   2022-05-30    2459730.0    157      20   0/70
  Sun      partial   2022-10-25    2459878.0    124      73   55/73
 Moon        total   2022-11-08    2459892.0    136      30   20/72
  Sun      annular   2023-04-20    2460054.7    129      65   52/80
 Moon    penumbral   2023-05-05    2460070.2    141      22   24/72
  Sun      annular   2023-10-14    2460232.2    134      57   44/71
 Moon    penumbral   2023-10-28    2460246.4    146      14   11/72
 Moon    penumbral   2024-03-25    2460394.8    113      67   64/71
  Sun        total   2024-04-08    2460409.3    139      49   30/71
 Moon    penumbral   2024-09-18    2460571.6    118      59   52/73
  Sun      annular   2024-10-02    2460586.3    144      41   17/70

An important remark is that ephemerides computation in JPARSEC are independent from the observer position. The observer can be on Earth, on the surface of other body, or in the middle of the Solar System. Check this (advanced) piece of code:

// Partial occultation of Sun by Phobos on Mars
System.out.println("");
System.out.println("Testing the eclipse of Phobos on Mars, from Spirit landing site");
astro = new AstroDate(2005, 11, 27, 2, 15, 0);
time = new TimeElement(astro, SCALE.UNIVERSAL_TIME_UTC);
observer = ObserverElement.parseExtraterrestrialObserver(new ExtraterrestrialObserverElement("Mars (Gusev crater)", TARGET.MARS, 
	new LocationElement(-175.4785 * Constant.DEG_TO_RAD, -14.5718*Constant.DEG_TO_RAD, 1.0)));
eph = new EphemerisElement(TARGET.Phobos, EphemerisElement.COORDINATES_TYPE.APPARENT,
					EphemerisElement.EQUINOX_OF_DATE, EphemerisElement.TOPOCENTRIC, EphemerisElement.REDUCTION_METHOD.IAU_2006,
					EphemerisElement.FRAME.ICRF, EphemerisElement.ALGORITHM.MOSHIER);
LunarEclipse le = new LunarEclipse(time, observer, eph, 10);
jdMax  = le.getEclipseMaximum(); 
TimeElement t_max = new TimeElement(jdMax, SCALE.BARYCENTRIC_DYNAMICAL_TIME);
double jdUT_max = TimeScale.getJD(t_max, observer, eph, SCALE.UNIVERSAL_TIME_UT1);
 
// Eclipse of Phobos on November 27, 2005, between 2:14 and 2:17 UTC. Using ephemerides from the Earth the time when the eclipse 
// reaches 100% is around 2:21:15 UTC, that corrected from light-time effects goes to 2:16:50 UTC. Here result is exactly that,
// so it's fully consistent.
System.out.println(le.getEclipseType() + " moon eclipse on " + TimeFormat
	.formatJulianDayAsDateAndTime(jdUT_max, SCALE.UNIVERSAL_TIME_UT1) + ". In UT1:");
events = le.getEvents();
for (int i = 0; i < events.length; i++)
{
	if (events[i].startTime != 0.0) {
		TimeElement t_init = new TimeElement(events[i].startTime, SCALE.BARYCENTRIC_DYNAMICAL_TIME);
		double jdUT_init = TimeScale.getJD(t_init, observer, eph, SCALE.UNIVERSAL_TIME_UT1);
		TimeElement t_end = new TimeElement(events[i].endTime, SCALE.BARYCENTRIC_DYNAMICAL_TIME);
		double jdUT_end = TimeScale.getJD(t_end, observer, eph, SCALE.UNIVERSAL_TIME_UT1);
 
		System.out.println("From "+TimeFormat.formatJulianDayAsDateAndTime(jdUT_init, SCALE.UNIVERSAL_TIME_UT1) + " to "+
			TimeFormat.formatJulianDayAsDateAndTime(jdUT_end, SCALE.UNIVERSAL_TIME_UT1)+" (" + events[i].details + ")");
	}
}
 
// Partial occultation of Sun by Phobos on Mars
System.out.println("");
System.out.println("Testing the partial solar occultation by Phobos on Mars, from Curiosity landing site");
astro = new AstroDate(2012, 9, 13, 5, 15, 0);
time = new TimeElement(astro, SCALE.UNIVERSAL_TIME_UTC);
observer = ObserverElement.parseExtraterrestrialObserver(new ExtraterrestrialObserverElement("Mars (Gale crater)", TARGET.MARS, 
	new LocationElement(-137.4417 * Constant.DEG_TO_RAD, -4.5895*Constant.DEG_TO_RAD, 1.0)));
eph.targetBody = TARGET.Phobos;
SolarEclipse se = new SolarEclipse(time, observer, eph, 0.2);
jdMax  = se.getEclipseMaximum();
t_max = new TimeElement(jdMax, SCALE.BARYCENTRIC_DYNAMICAL_TIME);
jdUT_max = TimeScale.getJD(t_max, observer, eph, SCALE.UNIVERSAL_TIME_UT1);
 
System.out.println(se.getEclipseType() + " solar eclipse on " + TimeFormat
	.formatJulianDayAsDateAndTime(jdUT_max, SCALE.UNIVERSAL_TIME_UT1) + ". In UT1:");
events = se.getEvents();
for (int i = 0; i < events.length; i++)
{
	if (events[i].startTime != 0.0) {
		TimeElement t_init = new TimeElement(events[i].startTime, SCALE.BARYCENTRIC_DYNAMICAL_TIME);
		double jdUT_init = TimeScale.getJD(t_init, observer, eph, SCALE.UNIVERSAL_TIME_UT1);
		TimeElement t_end = new TimeElement(events[i].endTime, SCALE.BARYCENTRIC_DYNAMICAL_TIME);
		double jdUT_end = TimeScale.getJD(t_end, observer, eph, SCALE.UNIVERSAL_TIME_UT1);
 
		System.out.println("From "+TimeFormat.formatJulianDayAsDateAndTime(jdUT_init, SCALE.UNIVERSAL_TIME_UT1) + " to "+
			TimeFormat.formatJulianDayAsDateAndTime(jdUT_end, SCALE.UNIVERSAL_TIME_UT1)+" (" + events[i].details + ")");
 
		// Show decimals in seconds
		AstroDate ini = new AstroDate(jdUT_init);
		AstroDate end = new AstroDate(jdUT_end);
		AstroDate max = new AstroDate(jdUT_max);
		System.out.println(ini.getSeconds()+"/"+end.getSeconds()+"/"+max.getSeconds());
	}
}

Previous code block contains two examples that return the circumstances of the eclipse of Phobos (produced by Mars) on the surface of Mars, as it was observed by the Spirit rover on Mars, and also the partial solar eclipse produced by Phobos as it was seen by the Curiosity rover. A detailed post with these and other examples is in my blog here, showing great consistency between the observations and the sky and planetary renderings produced by JPARSEC.

For quick information about eclipses the MainEvents class can also be used, but the flexibility and possibility of advanced calculations for an observer located outside the Earth will not be available. For instance:

see = MainEvents.MoonPhaseOrEclipse(astro.jd(), SimpleEventElement.EVENT.MOON_LUNAR_ECLIPSE, MainEvents.EVENT_TIME.CLOSEST);
if (see != null) System.out.println("Lunar eclipse jd "+ see.time+"/"+TimeFormat.formatJulianDayAsDateAndTime(see.time, SCALE.TERRESTRIAL_TIME) + ". Details: "+see.details);

Sky and planetary rendering

In this section we will see the beautiful charts that can be produced with very little effort with JPARSEC.

Eclipses

The eclipses in the previous subsection can be rendered using jparsec.graph.chartRendering.frame.EclipseRendering or jparsec.graph.chartRendering.RenderEclipse classes. The example jparsec.examples.medium.EclipseTest is an example of how to use the second option, which is less easy than the first but shows how the rendering pipeline is designed in JPARSEC: you need to create a jparsec.graph.chartRendering.Graphics object (not the Java intrinsic one!) of type AWTGraphics to create generic charts of the sky. This apparently strange approach is due to the fact that my Graphics object contains two implementations: AWTGraphics for desktop platform and AndroidGraphics for Android tablets/phones. My implementation is very generic and the drawing algorithms called from RenderEclipse (or RenderSky and RenderPlanet classes) to a generic Graphics are later redirected to one or the other depending on the platform used. You can create astronomical programs for Android with JPARSEC, but details about this should be delayed now.

I will not copy the same code as that in the mentioned class, just see the beautiful charts that can be generated:


 

Another example using spherical coordinates:


 

See a detailed post in my blog here.

The first and easier option is to use the EclipseRendering class previously mentioned. The code to create an eclipse chart would be:

// Translate.setDefaultLanguage(LANGUAGE.SPANISH);
 
// Solar eclipse
AstroDate astro = new AstroDate(2005, AstroDate.OCTOBER, 3, 9, 0, 0);
// Lunar eclipse
//AstroDate astro = new AstroDate(2011, AstroDate.JUNE, 15, 20, 0, 0);
 
// Main objects
TimeElement time = new TimeElement(astro, SCALE.UNIVERSAL_TIME_UT1);
ObserverElement observer = ObserverElement.parseCity(City.findCity("Madrid", COUNTRY.Spain, false));
EphemerisElement eph = new EphemerisElement(TARGET.SUN, EphemerisElement.COORDINATES_TYPE.APPARENT,
	EphemerisElement.EQUINOX_OF_DATE, EphemerisElement.TOPOCENTRIC, EphemerisElement.REDUCTION_METHOD.IAU_2009,
	EphemerisElement.FRAME.DYNAMICAL_EQUINOX_J2000, EphemerisElement.ALGORITHM.MOSHIER);
 
// Set some properties in other classes used by RenderEclipse
RenderPlanet.MAXIMUM_TEXTURE_QUALITY_FACTOR = 2f; // Better quality
RenderSatellite.ALLOW_SPLINE_RESIZING = false; // Improve performance
// Following line is not required since it is used internally in RenderEclipse class,
// but I put it here to show how to improve performance when calculating ephemerides
eph.optimizeForSpeed(); // Improve performance
 
// Set map and anaglyph properties
PLANET_MAP map = PLANET_MAP.MAP_FLAT;
// map.zoomFactor = 1.0f;
map.EarthMapSource = PLANET_MAP.EARTH_MAP_POLITICAL;
ANAGLYPH_COLOR_MODE colorMode = ANAGLYPH_COLOR_MODE.NO_ANAGLYPH;
boolean isSolarEclipse = true;
// Following line sets title to "Solar eclipse" for a solar eclipse, and "Lunar eclipse" for
// a lunar one. It also translates it in case Spanish is selected
String title = Translate.translate(isSolarEclipse? "Solar eclipse": "Lunar eclipse");
 
// Create the chart
EclipseRendering eclRender = new EclipseRendering(time, observer, eph, isSolarEclipse, map, 600, 800, colorMode, title);
Picture pic = new Picture(eclRender.createBufferedImage());
pic.show(title);

In the above code the EclipseRendering object shown at the end is responsible of rendering the eclipse simply calling its createBufferedImage method. This generates an image which is the argument to construct a Picture object that can be shown in the screen or written to disk (Picture class will be described later). The properties for the chart are entered in the EclipseRendering constructor one after another. The map can be changed to show a realistic or a political map (or no map at all), and the chart can be generated in a normal way or in 3d (anaglyph and SBS formats). Besides some quality/performance options and the main three objects required, at the begining of the code you can choose between two dates to test a solar or a lunar eclipse (in the second case you have to change isSolarEclipse to false).

This approach is very similar to the one used next to render the sky and planets, although in those cases the properties for the chart are more, so they are stored in a specific class instead of added to the constructor directly.

Planets

The example jparsec.examples.medium.planetaryRenderingTest will show a window with the rendering of Jupiter and its satellites during the triple transit event in 2004. The mouse can be moved on the planet or any satellite to obtain the planetographic coordinates on it. Clicking with the mouse will identify a given surface feature, although you will have to change the planet to Mars (or clic on different points on the surface of Ganimede or Io) since giant planets have no visible surface features (the GRS or Great Red Spot is an exception on Jupiter, however). A relevant code piece here shows how to setup the planetary rendering process using an instance of PlanetaryRendering, which automatically handles things related to frame creation and update, without the need in this case to create a Graphics object (PlanetaryRendering class is not available in the Android version of JPARSEC). The PlanetRenderElement contains the properties or how to render the scene, and the ussual objects TimeElement, ObserverElement, and EphemerisElement the other basic required properties. This piece also shows how to handle mouse events and react to them, so that the console shows always the planetographic coordinates. Mouse or key event handling is something Java-related, not too difficult but also not easy in you don't have enough experience with Java.


 

// Define properties of the planetary rendering and associate the telescope to it. The last boolean
// sets if difraction will be considered. If true the render will appear more or less as through
// a telescope (optically perfect, but simulating resolution and chromatic aberration). This 
// requires more computer time
int width = 1200, height = 900;
eph.targetBody = TARGET.JUPITER;
boolean difraction = false;
PlanetRenderElement render = new PlanetRenderElement(width, height, true, true, true, false, true, difraction);
render.telescope = TelescopeElement.SCHMIDT_CASSEGRAIN_20cm;
EphemElement planetEphem = Ephem.getEphemeris(time, observer, eph, false);
render.telescope.ocular.focalLength = TelescopeElement.getOcularFocalLengthForCertainField(planetEphem.angularRadius * 4, render.telescope);
 
// Use high quality output
render.highQuality = true;
RenderPlanet.MAXIMUM_TEXTURE_QUALITY_FACTOR = 2;
 
// For 3d mode
//render.anaglyphMode = ANAGLYPH_COLOR_MODE.DUBOIS_RED_CYAN;
//render.anaglyphMode.setEyeSeparation(1);
 
// For a more natural look
//render.showLabels = false;
//render.axes = render.axesNOSE = false;
 
// Define the rendering using the support class PlanetaryRendering, can be done in other
// (more elaborated and flexible) ways, but this is the direct mode
final PlanetaryRendering renderPlanet = new PlanetaryRendering(time, observer, eph, render, "Planet rendering");
final boolean considerSatellites = true;
final int minimumSize = 1;
final double maxDist = 2;
 
if (render.anaglyphMode != ANAGLYPH_COLOR_MODE.TRUE_3D_MODE_LEFT_RIGHT_HALF_WIDTH) {
	// Add mouse position listener to show the planetographic position when the mouse is on Jupiter
	renderPlanet.getPanel().addMouseMotionListener(new MouseMotionAdapter() {
		public void mouseDragged(MouseEvent m) {
		}
		public void mouseMoved(MouseEvent m) {
			try {
				int x = m.getX(), y = m.getY();
				LocationElement loc0 = renderPlanet.getRenderPlanetObject().getPlanetographicPosition(x, y, 3, considerSatellites);
				if (loc0 != null) {
					TARGET target = renderPlanet.getRenderPlanetObject().getPlanetInScreenCoordinates(x, y, considerSatellites, minimumSize);
					String name = renderPlanet.getRenderPlanetObject().identifyFeature(x, y, true, minimumSize, maxDist);
					String feature = "";
					if (name != null) feature = ". Feature found: "+name+".";
					System.out.println("Mouse position "+x+", "+y+". Planetographic position ("+target.getName()+"): "+Functions.formatAngle(loc0.getLongitude(), 1)+", "+Functions.formatAngle(loc0.getLatitude(), 1)+feature);
 
					// Invert transform
					float p[] = renderPlanet.getRenderPlanetObject().getScreenCoordinatesOfPlanetographicPosition(loc0, target, 3, true);
					if (p != null) System.out.println(p[0]+"/"+p[1]);
				}
			} catch (JPARSECException e) {
				e.printStackTrace();
			}
		}
	});
 
	// Add mouse clic listener to show features on the objects. In giant planets like Jupiter
	// there is obviously no feature to show, try using their satellites (or Mars).
	renderPlanet.getPanel().addMouseListener(new MouseAdapter() {
		public void mouseClicked(MouseEvent m) {
			try {
				int x = m.getX(), y = m.getY();
				TARGET target = renderPlanet.getRenderPlanetObject().getPlanetInScreenCoordinates(x, y, considerSatellites, minimumSize);
				if (target != TARGET.NOT_A_PLANET) {
					String object = target.getName();
					String feature = renderPlanet.getRenderPlanetObject().identifyFeature(x, y, considerSatellites, minimumSize, 2); // Closest feature within 2 deg
					if (feature != null) {
						System.out.println("Found feature \""+feature+"\" on "+object+" in position "+x+"/"+y);							
					} else {
						System.out.println("No feature found on "+object+" in position "+x+"/"+y);
					}
				} else {
					Object search[] = renderPlanet.getRenderPlanetObject().getClosestPlanetInScreenCoordinates(x, y, considerSatellites);
					target = (TARGET) search[0];
					System.out.println("Closest object to this position is "+target.getName());
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}				
	});
}
 
renderPlanet.showRendering();

The rendering pipeline implemented in JPARSEC is even more generic since it allows to render planets and the sky by considering the distance to the objects rendered. This allows 3d renderings in a variety of ways, from anaglyph mode to the SBS format used in movies. The orientation of the charts can also be inverted in horizontal and/or vertical to show the planets or the sky as they would be visible through different kind of telescopes. Below I show a 3d chart of Saturn in anaglyph mode, for red-cyan glasses.


 

By rendering consecutive frames at constant time steps and combining them, it is possible to create videos showing different phenomena. An extreme example of this can be seen in the old video directory of the ephemerides server, which is one of my projects applying JPARSEC. Other examples can be seen in my blog, for instance here and here.

Sky

The SkyRenderingTest in the examples.medium package shows how to render the sky. Instead of calling skyRender.showRendering(); to show the rendering, which would be the equivalent instruction as in the previous planetary rendering example, I create an instance of Picture by passing as parameter a BufferedImage object (an image) representing the rendering. This approach allows not only to show the image in the screen, but also to export it as png or jpg (write method for any instance of Picture class), among other formats, even without showing it in the screen.

JPARSEC includes a planetarium-like application in jparsec.graph.SkyChart, which is used by the examples applets located in jparsec.applet. The programs in both classes allows a very efficient and complete rendering of the sky and planets. The implementation of SkyChart can reach more than 120 fps in a Core i5 CPU under Linux, exceeding clearly the performance of many planetariums based on C++. If someone says to you that Java is slow, that guy doesn't know enough about Java or has no idea about how to code in Java efficiently. In the JVisualVM section close to the end of this wiki I will explain how easy is to improve the performance of Java programs compared to other programming languages.

Another, more advanced example is located in jparsec.examples.advanced.SkyPoster (and SkyAnimation). The main feature of SkyPoster is the possibility of exporting PDF charts of the sky in vector graphics format, so that the chart in PDF format can be printed at any resolution without losing quality. You will need the iText library in the build path to make this example work (download the three iText*.jar files at http://conga.oan.es/~alonso/jparsec/jparsecslideshow/). A more elaborated example would be a star atlas in pdf format, a little project available in the 'Other projects' page. A rendering example is below.


 

Artificial Satellites

Have a look at the example at jparsec.examples.medium.SatelliteRenderingTest. Some lines can be uncommented to change the observer to Mars or any other planet. For the ephemerides of these bodies JPARSEC implements the methods SDP/SGP4, SDP/SGP8, and the algorithms by James Miller (PLAN-13 program). The first part of the code shows how to obtain the ephemerides for the ISS from the Earth.


 

Stars

The package jparsec.ephem.stars contain classes for different calculation related to the movement of stars, or double/variable stars. Ephemerides for known orbits of double stars are supported using elliptic elements, similar to the ephemerides of comets and asteroids.

Trajectories for stars can be calculated accurately. Some examples are given in my blog here.

Instead of showing an example of ephemerides computation for stars, which would be very similar to that of a planet, I will show here just an example calculation for the components of the movement of a star in our galaxy:

StarElement star = new StarElement("HD 6755", Functions.parseRightAscension(1, 9, 42.3), 
	Functions.parseDeclination("61", 32, 49.5), 139, 0, (float) (628.42 * 1.0E-3 * Constant.ARCSEC_TO_RAD), 
	(float) (76.65 * 1.0E-3 * Constant.ARCSEC_TO_RAD), -321.4f, Constant.J2000, FRAME.FK5);
double uvw[] = StarEphem.getGalacticMotionUVW(star, true);
System.out.println(ConsoleReport.doubleArrayReport(new String[] {"u = xxxx.x km/s", "v = xxxx.x km/s", "w = xxxx.x km/s"}, uvw));

Output in the console is shown below. The doubleArrayReport is a useful class to report values combined with text (a better example will appear later in the section about general calculations related to stars and cosmology). To know the meaning of the u, v, w components just put the mouse on the call to getGalacticMotionUVW in Eclipse to show the javadoc of that method.

u = -142.7
v = -483.5
w = 93.2

Space probes

JPARSEC contains a database of almost all space probes launched so far, that can be used to show in the sky or in an orrery-like chart the history of space exploration. The class jparsec.ephem.probes.Spacecraft implements the ephemerides, and here is an example with its console output:

// Main objects
AstroDate astro = new AstroDate(1996, AstroDate.JANUARY, 1);
TimeElement time = new TimeElement(astro, SCALE.UNIVERSAL_TIME_UT1);
CityElement city = City.findCity("Madrid");
ObserverElement observer = ObserverElement.parseCity(city);
EphemerisElement eph = new EphemerisElement(TARGET.JUPITER, EphemerisElement.COORDINATES_TYPE.APPARENT,
	EphemerisElement.EQUINOX_OF_DATE, EphemerisElement.TOPOCENTRIC, EphemerisElement.REDUCTION_METHOD.WILLIAMS_1994,
	EphemerisElement.FRAME.ICRF);
 
// Show ephemeris for Jupiter
EphemElement ephem = Ephem.getEphemeris(time, observer, eph, true);
ConsoleReport.basicEphemReportToConsole(ephem);
 
// Now for the Galileo spacecraft when it reached Jupiter
int probe = Spacecraft.searchProbe("Galileo-10");
eph.targetBody = TARGET.NOT_A_PLANET;
eph.algorithm = EphemerisElement.ALGORITHM.PROBE;
eph.orbit = (OrbitalElement) Spacecraft.getProbeElement(probe);
ephem = OrbitEphem.orbitEphemeris(time, observer, eph);
ConsoleReport.basicEphemReportToConsole(ephem);
Jupiter Right ascension: 17h 57m 43.6848s
Jupiter Declination: -23º 11' 25.861"
Jupiter Distance: 6.23381 AU
Jupiter Rise: 1996-01-01 07:54:04 LT
Jupiter Transit: 1996-01-01 12:31:23 LT
Jupiter Set: 1996-01-01 17:08:43 LT
Jupiter Elongation: 10º 21' 52.875"
Jupiter Phase angle: -1º 55' 26.002"
Jupiter Constellation: Sagittarius
Jupiter Azimuth: 20º 25' 56.379"
Jupiter Elevation: -71º 50' 36.011"
Jupiter Magnitude: -1.658

Galileo-10 Right ascension: 17h 57m 19.3897s
Galileo-10 Declination: -23º 11' 19.731"
Galileo-10 Distance: 6.22769 AU
Galileo-10 Elongation: 10º 27' 27.902"
Galileo-10 Phase angle: -1º 56' 35.189"
Galileo-10 Constellation: Sagittarius
Galileo-10 Azimuth: 20º 42' 58.322"
Galileo-10 Elevation: -71º 48' 52.647"
Galileo-10 Magnitude: 0.000

Spacecraft names are organized as name-phase, where phase is a number identifying the orbit phase. A phase of 10 means the tenth set of orbital elements in the history of the spacecraft (after eventual gravitational assistances or orbit corrections with the rocket or engine of the spacecraft). The class provides methods to get the names and the phase of each spacecraft active at a given time.

For accurate ephemerides of spacecrafts for a given date the Horizons web server is the best source, the ephemerides in JPARSEC are only approximate.

Remarks about ephemerides and accuracy

The ephemerides object is the key to the particular results of ephemerides you will obtain. Setting a correct or consistent set of values is very important. There are many choices to do depending on the results you prefer to obtain, and everything is more or less explained in the documentation.

EphemerisElement eph = new EphemerisElement(
	TARGET.JUPITER, // The target body
	EphemerisElement.COORDINATES_TYPE.APPARENT, // apparent, astrometric, or geometric
	EphemerisElement.EQUINOX_OF_DATE, // equinox of date, or fixed equinox (J2000 for instance)
	EphemerisElement.TOPOCENTRIC, // topocentric for a observer, or geocentric (Earth's center) 
	EphemerisElement.REDUCTION_METHOD.IAU_2006, // IAU resolutions, Laskar, JPL and other algorithms
	EphemerisElement.FRAME.ICRF, // ICRF frame, dynamical equinox, FK5, FK4
	EphemerisElement.ALGORITHM_MOSHIER); // Moshier, JPL ephemerides, Series96, ELP, VSOP, ...

Coordinates type: ephemerides can be apparent, astrometric, or geometric. Unless you want to do some particular special computation, always choose apparent. Star atlasses use astrometric positions so that aberration or nutation are not considered, showing positions that can be compared earch other during the year.

Equinox: they can be referred to the equinox of the date of the computation or a fixed equinox, choose the first always unless you want to create charts or computations for a given equinox like J2000 (this is the equinox to which the positions of the objects is referred to in most sky atlasses, and the value in this field in the Julian day of the equinox).

Topocentric or geocentric: ephemerides can be topocentric (for the selected observer) or geocentric (for an hypothetical observer at the center of the Earth, neglecting the observer object), so normally you will choose always topocentric.

Reduction method: the reduction method controls the set of algorithms used for different corrections (precesion, nutation, sidereal time or Earth's rotation), and they have evolved with time and different IAU resolutions. Some ephemerides servers still use old algorithms like those of Laskar (1986), so to reproduce their results you have to choose here the same reduction algorithms. The current recommendation is IAU_2006 or IAU_2009 (IAU 2009 will change the longitude of central meridian in some bodies, something you will probably find in disagreement with most other sources not implementing the current IAU recommendations).

Frame: the output ephemerides can be referred to different frames, which can be described as the orientation of the coordinate axes. This orientation is currently defined using as reference extragalactic (extremely far and point-like) sources observed with extremely high resolution, so the current orientation (ICRF) is slightly different from previous ones defined with less accuracy. Orientation is not fixed with time, the axes rotates with precession or even rotation around the galaxy, so more accurate frames will be available in the future.

Algorithm: there are different algorithms for planetary ephemerides implemented in JPARSEC. The Moshier one is the best for a reasonably fast and accurate result. The latest JPL ones (for instance DE430) are more accurate, but to use them you need the jpl_ephem.jar dependency, and even in this case ephemerides are only available between 1900 and 2100 (for other dates you must download large files). There are other old algorithms like VSOP and Series96 for planets, and ELP2000 for the Moon. Series96 can be more accurate compared to Moshier, but it is limited also to 1900-2100 range. Although slower, ELP2000 could be also more accurate than Moshier, but only close to year 2000. The recommendation is to use always Moshier. For a consistent use of algorithms take into account that Moshier and JPL algorithms uses the ICRF frame as reference (selecting a different one means a frame rotation will be done), while VSOP/ELP2000/Series96 uses the dynamical equinox of J2000. There are other values for the algorithm, only used for stars, comets, asteroids, and natural satellites.

Respect accuracy, the reduction done inside JPARSEC is accurate to better than one milliarcsecond. But of course, the actual accuracy depends on the values used above, and in practice will be less usually since the values returned with the particular algorithm selected are never 'exact'. So only the nominal accuracy is better than one milliarcescond. In fact, JPARSEC is in the same level or better than other libraries like NOVAS or SOFA. NOVAS is somewhat limited, most things possible with JPARSEC are not possible with NOVAS, while SOFA is just a collection of algorithms, with no instructions on how to use them to return the position of a planet.

There are other interesting Java libraries, but JPARSEC is the only one focused at being complete for computing general ephemerides of different bodies. For a better implementation you can try the SPICE ephemerides used at NASA for flight operations with spacecrafts, but they are quite difficult to use and handle.

Basic support classes for different general operations

In this section I will explain some of the most importat classes inside JPARSEC for different kind of calculations. All them can be considered as support or help classes for different scientific tasks, including data manipulation, fitting, file reading/writing, creation of documents, and charting.

Input/Output

The jparsec.io package contains classes to read and write files, including some data manipulation (to read different fields using separators) with jparsec.io.FileIO class. FileFormatElement and ReadFormat allow to read a file formatted by different ranges of columns. To further manipulate data there are also other specific classes like jparsec.graph.DataSet and jparsec.ephem.Functions.

Resource files

JPARSEC contains a lot of resources files (text files, images, …) with the data required to simulate the sky, show textures of deep sky objects, or hold the list of cities for the observer, among many other things. These files are located inside the .jar files (the dependencies of the library, including the main library jparsec.jar). They are not required to compile the code, but obviously they are required to run any example using any of these files. You may wonder how to read them.

// A resource file is a file located in the classpath (inside any 
// of the dependency files with .jar extension)
String resourcePath = "jparsec/time/leapSeconds.txt"; // The leap seconds file located inside jparsec.jar
//resourcePath = OrbitEphem.PATH_TO_MPC_BRIGHT_ASTEROIDS_FILE; // inside orbital_elements.jar
//resourcePath = FileIO.DATA_SKY_DIRECTORY + "star_names.txt"; // inside sky.jar
 
String lines[] = DataSet.arrayListToStringArray(ReadFile.readResource(resourcePath));
ConsoleReport.stringArrayReport(lines);
//WriteFile.writeAnyExternalFile("PathToAnExternalFile.txt", lines, ReadFile.ENCODING_UTF_8);
 
resourcePath = FileIO.DATA_TEXTURES_DIRECTORY + "Earth.jpg"; // inside textures.jar, with hundreds of other images
Picture pic = new Picture(ReadFile.readImageResource(resourcePath));
pic.show("This is the Earth map");
//pic.write("PathToAnExternalImage.jpg");

The previous code shows how to read a resource file which can be a text file or an image file, and how to export it to an external file in your hard drive, outside the .jar file.

Reading external files

Files can be read with ReadFile class, and written with WriteFile. ReadFile can read an external file in the file system or a file inside any of the .jar files in the dependencies (with readResource method). For files located in Internet GeneralQuery can be used (in the VO package I will describe later).

ReadFile returns an ArrayList object containing Strings with each of the lines in the file. You can easily iterate in each of the lines to read different fields, and for this task the jparsec.io.FileIO class is useful in case the data is spread in each line using a given character/s as separator. Note 'myFile' is the complete path to the file, starting with /home… in Linux systems or, usually, C:/… in Windows.

ArrayList<String> lines = ReadFile.readAnyExternalFile("myFile");
String separator = ",";
boolean skip = false;
for (int i=0; i<lines.size(); i++) {
	String field1 = FileIO.getField(1, lines.get(i), separator, skip).trim();
	String field2 = FileIO.getField(2, lines.get(i), separator, skip);
}

Previous example reads two fields in each line of an input file, supposing the separator for the fields is a comma. In the example the first field is trimmed to remove possible leading/trailing blank spaces. When the separator is a blank space it could be useful to set skip to true so that several consecutive separators are treated as only one. You will see other useful methods in FileIO related to getField, like getRestAfterField and getRestBeforeField. Other methods allows to get a list of files in a given path, remove files, or to launch a file chooser panel. Other important methods are getFileSeparator and getLineSeparator, that provides the System-dependent character representing the file separator (/ in Linux and Mac, \ in Windows) and the line separator. The first value should be used always to construct the output paths to write files using later the WriteFile class, and the second value as the carriage-return after a line inside a text file (for instance, with String lines = line1 + FileIO.getLineSeparator() + line2), so that these system dependent things are done properly in each operating system.

OK, but what happens in case the records in each line of the file are distributed in certain range of columns, like in a formatted file? The above could work fine but you will prefer to be sure you are reading exactly the range of columns required for each field. For this task you can create an array of FileFormatElement objects, each of them taking as parameters in the constructor the range of columns and an identifier or name for the field. First column is 1 in this object (in Java the first index is usually 0), since in the file definitions this is the most common convention. In the example below I define the format of the IRAM catalog of radiosources (.sou format), using as identifiers some String constants located in SkyRenderElement class (to render external catalogs). This array is here only for clarity, since it is copied from the class jparsec.io.FileFormatElement, that contains definitions for some useful file formats.

/**
 * The format to read .sou files of radiosources from IRAM, using the constants for
 * external catalogs defined in {@linkplain SkyRenderElement}.
 */
FileFormatElement[] IRAM_SOU_FORMAT = new FileFormatElement[] {
	new FileFormatElement(1, 9, SkyRenderElement.EXTERNAL_CATALOG_FIELD_NAME1),
	new FileFormatElement(11, 12, SkyRenderElement.EXTERNAL_CATALOG_FIELD_COORDINATES_TYPE),
	new FileFormatElement(15, 18, SkyRenderElement.EXTERNAL_CATALOG_FIELD_EQUINOX_YEAR),
	new FileFormatElement(20, 21, SkyRenderElement.EXTERNAL_CATALOG_FIELD_RA_H),
	new FileFormatElement(23, 24, SkyRenderElement.EXTERNAL_CATALOG_FIELD_RA_M),
	new FileFormatElement(26, 32, SkyRenderElement.EXTERNAL_CATALOG_FIELD_RA_S),
	new FileFormatElement(36, 38, SkyRenderElement.EXTERNAL_CATALOG_FIELD_DEC_D),
	new FileFormatElement(40, 41, SkyRenderElement.EXTERNAL_CATALOG_FIELD_DEC_M),
	new FileFormatElement(43, 48, SkyRenderElement.EXTERNAL_CATALOG_FIELD_DEC_S),
	new FileFormatElement(53, 99, SkyRenderElement.EXTERNAL_CATALOG_FIELD_DETAILS)
};
ReadFormat rf = new ReadFormat(IRAM_SOU_FORMAT);
ArrayList<String> lines = ReadFile.readAnyExternalFile("myFile");
for (int i=0; i<lines.size(); i++) {
	String name = rf.readField(lines.get(i), SkyRenderElement.EXTERNAL_CATALOG_FIELD_NAME1);
	int equinox = rf.readInteger(lines.get(i), SkyRenderElement.EXTERNAL_CATALOG_FIELD_EQUINOX_YEAR);
}

The idea is to create a ReadFormat object that will allow us to read any of the fields of the file in different formats (String, int, double values, …). To read a field select the adequate method to call based on the desired output data type and pass the line to read and the name of the field. This approach allows to define file formats independently and to easily detect errors in case a given field should be a number and the file does not contain a valid number in that range of columns. In addition, a method in WriteFile allows to return a String line representing a set of values written in a format specified in an array of FileFormatElement objects.

Working with values

The class jparsec.ephem.Functions is another useful class for formatting angles and doing simple calculations with vectors. Specific vector and matrix operations are supported in the jparsec.math package, but sometimes it is useful (and the program performs much faster) in case some simple operations are performed with just arrays of values, so a class like Functions is quite useful.

String sra = "12h 05m 3.1s";
double ra = Functions.parseRightAscension(sra);
System.out.println(Functions.formatRA(ra, 1));
 
System.out.println(Functions.fmt(astro.getHour(), 2)+":"+Functions.fmt(astro.getMinute(), 2));

The example above transforms a right ascension from a String to a double value (in radians, which is the standard unit for angles used in almost the entire library), and later formats this double again as a String. In case you read RA and DEC it is useful to construct a LocationElement object and use its toStringAsEquatorialLocation() method to get a String with RA and DEC, but Functions allows more flexibility. Other methods in Functions include rotation around x, y, or z axes, dot/cross product, sum/subtract vectors, rounding, radians/degrees normalization, among others.

The example also prints the hour contained in an AstroDate instance named astro. For time/date formatting there are specific classes in jparsec.time package. For instance the method TimeFormat.formatJulianDayAsDateAndTime used in previous code snippets.

Working with compressed files

The class jparsec.graph.DataSet is the main class to deal with data. For instance, the process of getting fields can be inverted with DataSet.toString(array, separator). To manipulate the fields in a file you can use DataSet.arrayListToStringArray(v) to transform the read file to a string array, and then DataSet.addRowInTable(table, newRow, rowIndex) to add a row to the data, or DataSet.extractColumnFromTable(table, separator, columnIndex) to extract a column. There are many more methods available so you can do a lot of operations easily and fast with DataSet class, mainly with data in String form.

A possible example to put some of these functions in action is the class jparsec.util.Update. This class contains methods that downloads updated orbital elements from Internet, transform these data, and save them to disk by replacing automatically the contents of some dependencies (.jar files). This class also shows how to deal with .zip formatted files from JPARSEC, using jparsec.io.Zip class. At the end of this document some additional details are given about how to update some of these files.

Maths

The package jparsec.math and its subpackage jparsec.math.matrix contain classes aimed to working with different kind of math elements. The matrix subpackage contains a Matrix class to create matrices and different methods to obtain eigenvectors and eigenvalues. The main math package includes the classes Complex to work with complex values, DoubleVector to work with vectors (dot and cross product and so on), and Polynomial to work with polynomials of real or complex values.

Operations

Different math operations are provided in classes Integration, Derivation, Root (to obtain the zeros of any function), Interpolation, and MeanValue (to obtain the average of a set of physical measurements considering their values and their errors). Their use should be quite straightforward, and anyway inside each of these classes you will find a main program (usually at the end of each class) showing a test case. The idea is to create an object using any of the constructors, that uses some arrays, and then any of the methods desired to obtain a result. A very simple example would be:

double[] x = new double[] {12.0, 16.0, 20.0};
double[] y = new double[] {1.3814294, 1.3812213, 1.3812453};
Interpolation interp = new Interpolation(x, y, false);
double result = interp.linearInterpolation(14);

Previous example is for Interpolation class, that also allows spline interpolation among other methods.

Data fitting

For function and data fitting you have the classes LinearFit, GenericFit, and Regression. LinearFit is specialized on linear fitting, providing the possibility of returning invalid points in case you want to eliminate them from the input data. GenericFit is a simple and fast class to fit data to and arbitrary function of, at most, three independent variables. Regression is a class adapted from the maths/science library by Michael Thomas Flanagan to fit input data to any kind of function. Regression is used in JPARSEC for gaussian fitting and decomposition of spectra. Here is an example of using GenericFit and Regression to fit two datasets to two fitting functions:

System.out.println("Let's fit a set of data to the function p1 * Math.sin(x) + p2 * Math.sin(2.0*x) + p3 * Math.sin(3.0*x)");
 
double x[] = new double[] {3, 20, 34, 50, 75, 88, 111, 129, 143, 160, 183, 200, 218, 230,
	248, 269, 290, 303, 320, 344};
double y[] = new double[] {0.0433, 0.2532, 0.3386, 0.3560, 0.4983, 0.7577, 1.4585, 1.8628,
	1.8264, 1.2431, -0.2043, -1.2431, -1.8422, -1.8726, -1.4889, -0.8372, -0.4377, -0.3640,
	-0.3508, -0.2126};
x = Functions.scalarProduct(x, Constant.DEG_TO_RAD);
 
GenericFit g = new GenericFit(x, y, "Math.sin(x)", "Math.sin(2.0*x)", "Math.sin(3.0*x)");
ConsoleReport.doubleArrayReport(g.fit(), "f2.2"); // 1.2, -0.77, 0.39
 
System.out.println();
 
// Now the same using Regression
System.out.println("Now using Regression class");
Regression regression = new Regression(x, y, DataSet.getSetOfValues(0.0, 0.0, x.length, false));
String function = "p1*Math.sin(x)+p2*Math.sin(2.0*x)+p3*Math.sin(3.0*x)";
double initialValues[] = DataSet.getSetOfValues(1.0, 1.0, 3, false); // Suppose we have no idea of the results
initialValues[1] = -1; // But we know second value is negative (Help required with correct sign!)
regression.customFunction(function, initialValues);
ConsoleReport.doubleArrayReport(regression.getBestEstimates(), "f2.4");
System.out.println("Converge ? "+regression.convergence());
 
System.out.println("");
double out[] = regression.getBestEstimates();
double out2[] = g.fit();
String[] param = new String[] {"p1 "+out[0], "p2 "+out[1], "p3 "+out[2]};
String[] param2 = new String[] {"p1 "+out2[0], "p2 "+out2[1], "p3 "+out2[2]};
System.out.println("x, y, y estimate (GenericFit), y estimate (Regression)");
for (int i=0; i<x.length; i++) {
	Evaluation eval = new Evaluation(function, DataSet.addStringArray(param, new String[] {"x "+x[i]}));
	Evaluation eval2 = new Evaluation(function, DataSet.addStringArray(param2, new String[] {"x "+x[i]}));
	System.out.println(x[i]+" "+y[i]+" "+eval.evaluate()+" "+eval2.evaluate());
}
 
System.out.println("");			
System.out.println("ANOTHER EXAMPLE");			
System.out.println("");			
System.out.println("Now more simple a*sqrt(x)+b*x+c with GenericFit");
x = new double[] {0, 1, 2, 3, 4, 5};
y = new double[] {0, 1.2, 1.4, 1.7, 2.1, 2.2};
g = new GenericFit(x, y, "Math.sqrt(x)", "x", "1");
ConsoleReport.doubleArrayReport(g.fit(), "f2.4"); // 1.016
 
System.out.println("Now using Regression class");
regression = new Regression(x, y, DataSet.getSetOfValues(0.0, 0.0, x.length, false));
function = "p1*Math.sqrt(x)+p2*x+p3";
initialValues = DataSet.getSetOfValues(1.0, 1.0, 3, false); // Suppose we have no idea of the results
regression.customFunction(function, initialValues);
ConsoleReport.doubleArrayReport(regression.getBestEstimates(), "f2.4"); // Second value is negative, but little enough

The output from previous code to the console is:

Let's fit a set of data to the function p1 * Math.sin(x) + p2 * Math.sin(2.0*x) + p3 * Math.sin(3.0*x)
01.2000
-0.7700
00.3900

Now using Regression class
01.2000
-0.7700
00.3900
Converge ? true

x, y, y estimate (GenericFit), y estimate (Regression)
0.05235987755982989 0.0433 0.04332725048955031 0.043327151436987434
0.3490658503988659 0.2532 0.2532377614450851 0.2532366822218819
0.5934119456780721 0.3386 0.33859342668861836 0.3385905090532438
0.8726646259971648 0.356 0.35597132763271244 0.3559653689090667
1.3089969389957472 0.4983 0.4983575614220297 0.4983484281559298
1.53588974175501 0.7577 0.7577062789293038 0.7576980903776669
1.9373154697137058 1.4585 1.458471912665026 1.4584700610870778
2.251474735072685 1.8628 1.8627981843680141 1.8628022697055493
2.4958208303518914 1.8264 1.826436301485139 1.8264429261211341
2.792526803190927 1.2431 1.243112928479148 1.2431185784039989
3.193952531149623 -0.2043 -0.20429819715627218 -0.2042991923804106
3.490658503988659 -1.2431 -1.2431129284791473 -1.243118578403998
3.8048177693476384 -1.8422 -1.8421946712737256 -1.8422012090687565
4.014257279586958 -1.8726 -1.87254806980854 -1.872552420737841
4.328416544945937 -1.4889 -1.4888808769480169 -1.4888793772699465
4.694935687864747 -0.8372 -0.8372362080417317 -0.8372285486918734
5.061454830783556 -0.4377 -0.437704103479464 -0.4376951727673529
5.288347633542818 -0.364 -0.36400466514872726 -0.3639973948714376
5.585053606381854 -0.3508 -0.3508109150256756 -0.350806922340942
6.003932626860494 -0.2126 -0.2125617018064646 -0.212560955779731

ANOTHER EXAMPLE

Now more simple a*sqrt(x)+b*x+c with GenericFit
01.1494
-0.0761
00.0179
Now using Regression class
01.1493
-0.0761
00.0179

The example jparsec.examples.medium.Fitting contains more examples with graphic output as charts. The result of executing it is the following set of charts:


 

Current version of JPARSEC provides methods to return charts of the data and the fittings for the different classes mentioned above, that makes very easy to produce the previous charts.

Evaluating math expressions

The class Evaluation is a handy class to evaluate functions f(x), f(x, y, z, …), for an arbitrary number (and name) of the variables. It uses the built-in functions in Java 1.6 to evaluate math expressions through javascript. Here is an example:

double x = 1.0, y = 2.0, z = 3.0;
String function = "x*x+y*y+z*z";
Evaluation eval = new Evaluation();
double val = eval.evaluateMathFunction(function, x, y, z);
System.out.println("f(x,y,z) = "+function);
System.out.println("f("+x+", "+y+", "+z+") = "+val);

The evaluation of math expressions is used internally to fit data to generic functions given as strings. The implementation in Evaluate class is quite flexible, allowing features not available in Java itself like the evaluation of logic expressions. For instance:

String f = "2*Math.sqrt(16)*(Math.sqrt(x)<2)+Math.pow(x,3)*(Math.abs(x-2)<0)";
String variables[] = new String[] { "x 2" };
Evaluation eval = new Evaluation(f, variables);			
double yy = eval.evaluate();
System.out.println("Expression: "+f);
System.out.println("(x=2) => " + yy);

The result of the previous program is 8, because since x is set to 2 the contribution to the result comes only from the first term in f (logic expressions are evaluated to 1 if they are true, and 0 if they are false). Logic expressions allow to set step functions in different ranges of values. The main program in Evaluate class contains a more complex example.

Constants

There two remarkable additional classes in jparsec.math: Constant and CGSConstant. They provide some useful physical constants to be used inside any program, fully updated using the CODATA website. Any application should use these constants to ensure the latest official values are used, and that they are the same along the execution of your programs. CGSCConstant contains the most important constants in CGS units.

Trigonometric functions

Trigonometric functions are supported by Java using the Math package, which is part of the esential java.lang package and hence it is not necessary to explicitly import it in a Java class. However, those functions are implemented using very strict methods for maximum accuracy and to ensure the same exact result is returned in every operating system and processor. Compared to C and Fortran where the implementation is less strict, trigonometric functions in Java are usually quite slow.

Speed can be a problem when lots of trigonometric operations are required for a given task, specially tasks not demanding this extreme accuracy. This is the case for charting the sky and planets, where these functions are used all the time to rotate and project coordinates. To solve this problem there's the class jparsec.math.FastMath, that implements very fast but approximate math functions very useful for fast coordinate rotations. Some methods in JPARSEC use these methods to provide alternative implementations of a given operation in a fast and approximate way, in case to call that task thousands of times is required.

Basic charting

JPARSEC contains multiple classes for different kind of 2D/3D charts. For 2D charting the libraries integrated within JPARSEC are JFreeChart (using CreateChart class) and SGT (using CreateGridChart). For 3D charting the libraries are JMathPlot (CreateChart3D), SurfacePlotter (CreateSurface3D), and VisAD (CreateVISADChart). Among all these libraries VisAD is the only one using Java3D, so to use it you need Java3D installed, as well as drivers for your graphics card. The set of tools distributed with JPARSEC can optionally install Java 1.6, including Java3D. There are other, more complex components that will be presented later in the science section.

As mentioned previously, the logic behind charting is that you create, for instance, a 2D chart by instantiating a ChartElement object called chart (or a SimpleChartElement for a quick creation of a one-series chart). Then, you can show that 2D chart using one library or another, for instance CreateChart ch = new CreateChart(chart); in case of the JFreeChart library. JFreeChart is the only way to show 2D charts, but for 3D scatter charts (creating a ChartElement3D object) you can use JMathPlot and VisAD, and for grid or surface charts (GridChartElement object) you can use SGT, SurfacePlotter, CreateChart3D, and also VisAD.

Above I classify a grid chart as a 2D chart since SGT is a 2D library, but a grid chart is in fact a 3D chart showing a surface and the vertical levels codified as colors. To instantiate a grid chart you can use a GridChartElement object, which is the argument to be passed to the constructor of a CreateGridChart, CreateSurface3D, CreateVISADChart, or CreateChart3D.

The charts below show each of these components in action, for the code snippets shown above each of them. The methods available in each of the mentioned objects (CreateChart, CreateChart3D, CreateGridChart, CreateSurface3D, and CreateVISADChart) allow different visualization possibilities (with/out advanced controls, panel with buttons, and so on). In all cases rotation and zooming with mouse is allowed. In CreateGridChart rotation is not possible, but you can obtain coordinates/intensity for each point as it is shown in the corresponding example. Export to different graphics format is possible, from usual formats like PNG or JPG to vector graphics formats like PDF, SVG, and EPS (except in VisAD, which does not support this since it uses Java3D). I recommend you to look at the examples jparsec.examples.medium.ChartTest and jparsec.examples.medium.Chart3dTest. The first one shows how to create a 'real-time' chart with pairs of values being added to the chart at constant time intervals.

CreateChart

// Charts can be created using a ChartElement or a SimpleChartElement		
SimpleChartElement chart2 = new SimpleChartElement(ChartElement.TYPE.XY_CHART,
	ChartElement.SUBTYPE.XY_SCATTER, new double[]
	{ 1, 2, 3, 4}, new double[]
	{ 1, 4, 8, 17 }, "TITLE", "X_LABEL", "Y_LABEL", "LEGEND", true, false, 400, 300);
CreateChart ch2 = new CreateChart(chart2);
 
// The ussual way to show the chart is the following line.
ch2.showChartInJFreeChartPanel();
 
// Chart is initially shown as it is, with no regression line
// Regression can be defined using ChartSeriesElement (see example below), but not 
// with SimpleChartElement. To do so we simply recover the chart object, then the series,
// and then we set the regression type. Finally we update the chart.
// This is for a polynomial fit, degree 2 by default but can be modified.
ch2.getChartElement().series[0].regressionType = REGRESSION.POLYNOMIAL;
// This is for a generic fit to a set of f(x)
// ch2.getChartElement().series[0].regressionType = REGRESSION.GENERIC_FIT;
// ch2.getChartElement().series[0].regressionType.setGenericFitFunctions(new String[] {
//		"Math.sin(x)", "x", "1"
//});
ch2.getChartElement().series[0].regressionType.setShowEquation(true);
ch2.updateChart();


 

Another example, more elaborated, showing a chart of exoplanet masses as function of the mass of the host star. First two lines of the listing should go into the main method, the rest are external methods. It also shows some data manipulation.

CreateChart ch5 = ChartTest.graphAllExoplanets(800, 600);
ch5.showChartInJFreeChartPanel();
 
// EXTERNAL METHOD
 
/**
 * Creates a chart with all the exoplanets available in file extrasolarPlanets.txt
 * located in orbital_elements.jar, inside the dependencies. Chart is exoplanet mass 
 * as function of star mass. Data is from http://exoplanetarchive.ipac.caltech.edu.
 * 
 * @param size_x Image width.
 * @param size_y Image height.
 * @return Chart Object.
 * @throws JPARSECException
 */
public static CreateChart graphAllExoplanets(int size_x, int size_y) throws JPARSECException
{
	// Read catalog
	String lines[] = DataSet.arrayListToStringArray(ReadFile.readResource(FileIO.DATA_ORBITAL_ELEMENTS_DIRECTORY + "extrasolarPlanets.txt"));
	// Another possibility is to download the entire data updated, using following line (requires a few seconds)
	// String lines[] = DataSet.toStringArray(GeneralQuery.query("http://exoplanetarchive.ipac.caltech.edu/cgi-bin/nstedAPI/nph-nstedAPI?table=exoplanets&select=pl_hostname,ra,dec,st_dist,st_mass,pl_masse,pl_rade,pl_trandur,pl_trandep,pl_orbeccen,pl_orbincl,pl_orblper,pl_orbsmax,pl_orbtper,pl_pelink,pl_name,st_vj&order=pl_masse&format=ascii"), FileIO.getLineSeparator());
 
	// Set the ReadFormat object with the adequate format
	ReadFormat rf = new ReadFormat(FileFormatElement.EXTRASOLAR_PLANETS);
	// Note these constants are set in FileFormatElement.EXTRASOLAR_PLANETS. See JPARSEC code for details.
	String planetMassEarthUnits = "PLANET_MASS_EARTH", starMassMsunUnits = "STAR_MASS", planetName = SkyRenderElement.EXTERNAL_CATALOG_FIELD_NAME1;
	// Check the SKIP_LINES field to skip header lines if required
	int line0 = 0;
	try { line0 = rf.getField("SKIP_LINES").endingPosition; } catch (Exception exc) {}
 
	double toJupiterMass = (TARGET.JUPITER.relativeMass / TARGET.EARTH.relativeMass); // Earth mass to Jupiter mass transform
 
	// Read x, y, and pointers for the chart
	ArrayList<String> x = new ArrayList<String>();
	ArrayList<String> y = new ArrayList<String>();
	ArrayList<String> pointers = new ArrayList<String>();
	for (int i = line0; i < lines.length; i++)
	{
		String line = lines[i];
		String fy = rf.readField(line, planetMassEarthUnits).trim();
		String fx = rf.readField(line, starMassMsunUnits).trim();
		if (!line.startsWith("!") && !fx.equals("null") && !fy.equals("null"))
		{
			x.add(fx);
			y.add(fy);
 
			double starM = Double.parseDouble(fx);
			if (starM < 0.3 || starM >= 2) {
				double mass = Double.parseDouble(fy) * toJupiterMass;
				pointers.add("("+fx+","+(float) mass+") " + rf.readField(line, planetName).trim());
			}
		}
	}
 
	// Pass y values from Earth masses to Jupiter masses
	String yvalues[] = DataSet.toStringValues(Functions.scalarProduct(DataSet.toDoubleValues(DataSet.arrayListToStringArray(y)), toJupiterMass));
 
	// Now construct the chart
	Shape shape_obs = ChartSeriesElement.SHAPE_CIRCLE;
	ChartSeriesElement series_obs = new ChartSeriesElement(DataSet.arrayListToStringArray(x), yvalues, 
		DataSet.getSetOfValues(0.0, 0.0, x.size(), false), DataSet.getSetOfValues(0.1, 0.1, y.size(), false), "SERIES1", false, Color.BLACK, 
		shape_obs, ChartSeriesElement.REGRESSION.NONE);
	series_obs.pointers = DataSet.arrayListToStringArray(pointers);
	series_obs.showShapes = true;
	series_obs.showErrorBars = true;
 
	ChartSeriesElement series[] = new ChartSeriesElement[] { series_obs };
 
	ChartElement chart = new ChartElement(series, ChartElement.TYPE.XY_CHART, ChartElement.SUBTYPE.XY_SCATTER,
		"     Exoplanet mass as function of star mass", "Star mass (M_{@SUN})", "Exoplanet mass (M_{jupiter})", false,
		size_x, size_y);
	chart.showErrorBars = false;
 
	chart.xAxisInLogScale = true;
	chart.yAxisInLogScale = true;
 
	return new CreateChart(chart);
}


 

This example also shows how to insert special characters like the symbol of the Sun (@SUN), or Greek alphabet (@alpha for α and @ALPHA for its capital counterpart), as well as subscript (_{sub}) and superscript (^{sup}). The later is somewhat similar to Latex. Reference about all possible commands appear in jparsec.graph.TextLabel class (see it in the javadoc more confortably). In essence, the (just main) commands are to change colors within a text (with @RED for red, @GREEN, @BLUE, @BLACK, @WHITE, @CYAN, @GRAY, @MAGENTA, @ORANGE, @PINK, @YELLOW), change text style (@PLAIN, @ITALIC, @BOLD), and to change text size (@SIZE20 for size 20, or @SIZE+5 or @SIZE-5 to increase/reduce text size). An additional command is @LATEX{formula}, in which formula can be a formula written in Latex format to be rendered on the chart. See the documentation and examples in class jparsec.math.LATEXFormula for details. Examples images are shown here, and an example of a Latex formula is shown later in the section about the different kind of charts you can obtain from JPARSEC. Another more freaky feature is to represent clock-style digits (the time or angles for instance) using @CLOCK{12h 180º}. An example of this is given in the panel showing telescope control close to the end of this document.

Scatter and surface (2d) plots can be directly exported to Gildas. Just add the line ch5.exportAsScriptForGILDAS(”/home/alonso/exoplanets.greg”, GILDAS_LEYEND.TOP_RIGHT_CORNER, false); to the previous example and you will automatically obtain the Gildas script that will produce the following image. The script is well commented so you can modify it with easy.


 

The ChartElement object used to hold the chart information contains more properties that can be modified. For instance, the boolean fields xAxisInverted and yAxisInverted, set by default to false, can be set to true to show the corresponding axis inverted. This will later apply also to the Gildas export.

CreateChart3D

This class uses JMathPlot to show basic 3d charts corresponding to surfaces or scatter plots. An example with a surface is provided below, here I will show the case of a set of points that comes from the class jparsec.examples.medium.SolarNeighbourhood. It shows all stars within 10 pc from the Sun.

// Limit distance in pc for the stars in each axis
double limitDistance = 10;
 
// Initialize position vectors
double x[] = new double[] {0};
double y[] = new double[] {0};
double z[] = new double[] {0};
String pointer[] = new String[] {"1   @REDSUN"};
 
// Read file of stars (only apparent magnitude < 6.5)
ReadFile reStar = new ReadFile();
reStar.setPath(FileIO.DATA_STARS_SKY2000_DIRECTORY + "JPARSEC_Sky2000.txt");
reStar.setFormat(ReadFile.FORMAT.JPARSEC_SKY2000);
reStar.readFileOfStars();
 
// Read file of star names
ArrayList<String> names = ReadFile.readResource(FileIO.DATA_SKY_DIRECTORY + "star_names.txt");
 
// Add stars to the arrays
StarElement readStars[] = (StarElement[]) reStar.getReadElements();
int index = pointer.length;
for (int iii = 0; iii < readStars.length; iii++)
{
	StarElement star = readStars[iii];
	if (star.getDistance() < limitDistance) {
		LocationElement loc = new LocationElement(star.rightAscension, star.declination, star.getDistance());
		double pos[] = loc.getRectangularCoordinates();
		x = DataSet.addDoubleArray(x, new double[] {pos[0]});
		y = DataSet.addDoubleArray(y, new double[] {pos[1]});
		z = DataSet.addDoubleArray(z, new double[] {pos[2]});
 
		index ++;
		String name = "";
		if (!star.name.equals("")) {
			int bracket1 = star.name.indexOf("(");
			int bracket2 = star.name.indexOf(")");
			if (bracket1 >= 0 && bracket2 >= 0)
			{
				String name2 = star.name.substring(bracket1 + 1, bracket2);
				name = name2;
				for (int n = 0; n < names.size(); n++)
				{
					String line = names.get(n);
					int aa = line.toLowerCase().indexOf(name2.toLowerCase());
					if (aa >= 0)
					{
						int f = 2; // 3 for Spanish names
						name = FileIO.getField(f, line, "  ", true);
						break;
					}
				}
			}
		}
 
		pointer = DataSet.addStringArray(pointer, new String[] {""+(index)+"   @BLUE"+name});
	}
}
 
// Create chart and show it
double maxDist = limitDistance * Math.sqrt(3.0);
ChartSeriesElement3D series = new ChartSeriesElement3D(x, y, z, "Stars with distance < "+(float) maxDist+" pc");
series.pointers = pointer;
series.isBarPlot = true;
ChartElement3D chart = new ChartElement3D(new ChartSeriesElement3D[] {series}, 
	"Stars in the solar neighbourhood", "p_{x} (pc)", "p_{y} (pc)", "p_{z} (pc)");
CreateChart3D c = new CreateChart3D(chart);
c.showChart(500, 500);


 

CreateGridChart and CreateVISADChart

A grid chart is a 3d surface that can be created in different ways. You can interpolate a set of 3d points defining the surface (see method GridChartElement.getSurfaceFromPoints), or you can construct the points by evaluating a function of 2 variables z = f(x, y) (with GridChartElement.createDataFromFunction). In addition, different methods within the JPARSEC library can return a 2D matrix of values, for instance the Matrix class, an image read by Picture or FitsIO classes, or, as in the example below, the difraction pattern of a telescope using jparsec.astronomy.Difraction class.

// Define as chart the difraction pattern of a Newton 20cm telescope
TelescopeElement telescope = TelescopeElement.NEWTON_20cm;
int field = 5;
double dat[][] = Difraction.pattern(telescope, field);
GridChartElement chart = new GridChartElement("Difraction pattern",
	"offsetX", "offsetY", "RelativeIntensity", GridChartElement.COLOR_MODEL.RED_TO_BLUE, 
	new double[] {-field, field, -field, field}, dat, 
	new double[] {0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}, 400);
chart.type = GridChartElement.TYPE.RASTER_CONTOUR;
chart.opacity = GridChartElement.OPACITY.OPAQUE;
 
// Using SGT
CreateGridChart cs0 = new CreateGridChart(chart);
cs0.showChartInSGTpanel(true);
 
// Using SurfacePlotter
CreateSurface3D cs1 = new CreateSurface3D(chart);
cs1.show(500, 500, "Surface test 1", true);
 
// Show using JMathPlot (Java2d), with mouse control and wheel zoom
ChartSeriesElement3D series = new ChartSeriesElement3D(chart);
ChartElement3D chart3d = new ChartElement3D(new ChartSeriesElement3D[] {series}, 
	"Difraction pattern", "offset x (\")", "offset y (\")", "@SIZE20I_{relative} (|@PSI|^{2})");
chart3d.showToolbar = true;
chart3d.showLegend = false;
chart3d.showTitle = false;
//chart.showGridX = chart.showGridY = chart.showGridZ = false;
CreateChart3D c = new CreateChart3D(chart3d);
c.showChart(500, 500);
System.out.println(c.getIntensityAt(2, 2, series.legend));
 
// Show using VISAD (Java3d), with mouse controls and zoom with MAYS hold on
CreateVISADChart vt = new CreateVISADChart(chart);
vt.show(600, 600);


 

There's a more advanced VisAD chart implemented in the class CreateVISADIsoSurfaceChart, but I will speak about this class later in the science section.

CreateJZY3DChart

Another 3d library for scientific plotting is jzy3d, for which I have written a wrapper in class CreateJZY3DChart. Support is not complete, but it is possible to use the main features like scatter and surface plots, including contour levels. JZY3D is a good library, but has some deficiencies compared to other libraries described above for scientific plotting. For me the combination of SGT and SurfacePlotter is more flexible and gives better image quality, with the possibility of exporting charts to vector graphics formats, and without requiring 20 additional files in the classpath for JOGL. In addition, JZY3D seems to contain some bugs/limitations when computing the contours or the surface collisions.

Support is hence experimental and will be improved (or not) in later versions of JPARSEC.

SkyChart

The jparsec.graph.SkyChart class is a special component used to render sky and planets, including the typical options found in planetarium programs in a popup menu shown when clicking with the right button of the mouse. You can launch the main program in that class or use the applets in jparsec.applet, a package that also belongs to the examples. The applet is available online from my projects page, including an extended documentation on the features available and how to use them. I will not show charts or write more about the SkyChart component since I could fill pages and more pages, just launch the mentioned applet and read its instructions. Only mention the interesting features you will not find in almost any other planetarium (specially online!), like simulating the sky from other planets or showing the history of space exploration. The main program contains some code commented with an example of how to use ExtraterrestrialObserverElement class to simulate the sky or obtain ephemerides of bodies for an observer located outside the Earth.

Images

The jparsec.io.image package contain classes to work with different image formats. Picture class is used with common image formats, while FitsIO is designed to manipulate .fits files, a format widely extended in astronomy. Charts shown in previous sections can easily be exported to any of the common formats or to vector graphics, as well as used to create an instance of Picture with the chart.

Common graphics formats

For the classic formats like jpg, png, bmp, and gif you can use the Picture class. This class can also export to pdf, eps, and svg image formats. In case you use an instance of Picture (loading an image) the export to pdf, eps, and svg is done without using vector graphics. In case you use the static methods (Picture.create…) to export you can obtain a vector graphics output in case you provide an instance whose class includes a method to draw it using a Graphics2D object. An example of the later case is given in the section about diagrams.

The class contains a considerable number of methods to modify the images, including filters to change the size, horizontal/vertical flip, modify the brightness/contrast, change colors and transparency, transform to grayscale, remove noise, and convolve with different kernels. The class ImageSplineTransform can also be used to interpolate inside an image. The images can be instantiated from files as well as from integer arrays containing the set of RGB colors. In case you want to transfer the images encoded as text in base64 (so you can, for instance, integrate them later in html reports without having to load the images as external files) you can use the Base64Coder class.

Fits files

To work with .fits images you have the class FitsIO, which uses the help classes FitsBinaryTable and WCS. FitsBinaryTable is used to deal with binary tables inside a .fits file, and WCS with the World Coordinate System. The main method of FitsIO class contains an example program from which I put here some code. Of course you need first a .fits file containing an image, you can easily find one free in Internet.

// Select a .fits file
String s = "/home/alonso/java/librerias/40m/fits/opt_Vega467.fits";
 
FitsIO fio = new FitsIO(s);
System.out.println(fio.toString());
 
int imageNumber = 0;
short data[][] = (short[][]) fio.getData(imageNumber);
 
System.out.println(data[0].length);
System.out.println(data.length);
if (data != null)
{
	for (int j=0; j<data[0].length; j++)
	{
		String line = "";
		for (int i=0; i<data.length; i++)
		{
			line += data[i][j]+"  ";
		}
		System.out.println(line);
	}
} else {
	System.out.println("NO DATA !!!!!");
}
System.out.println(ImageHeaderElement.toString(fio.getHeader(imageNumber), 25, 10));
 
PICTURE_LEVEL level = PICTURE_LEVEL.LINEAR_INTERPOLATION; // .CUSTOM;
level.NaN = 255;
level.formula = "25 + 200*(x-min)/(max-min)";
Picture p = fio.getPicture(imageNumber, level, true);
 
SExtractor sex = fio.solveSources(imageNumber, 3, 5);
System.out.println(sex.toString());
Graphics2D g = (Graphics2D) p.getImage().getGraphics();
g.setColor(Color.RED);
for (int i=0; i<sex.getNumberOfSources(); i++)
{
	int r = sex.getDetectionWidth(i);
	int x = (int) (sex.getX(i).getValue()-r/2.0+0.5) - 1;
	int y = (int) (sex.getY(i).getValue()-r/2.0+0.5) - 1;
	g.drawOval(x, y, r, r);
}
 
// Print the equatorial position of the first (brightest) source detected by SExtractor
System.out.println(fio.getWCS(imageNumber).getSkyCoordinates(new Point2D.Double(sex.getX(0).getValue(), sex.getY(0).getValue())).toStringAsEquatorialLocation());
 
p.show(fio.getHDU(imageNumber).getAuthor());

Previous program will load a .fits file in the local system. It assumes there's an image in the HDU number 0 (the first HDU in the .fits file) containing a 2d array of short-type data. The data will be presented to the console, and later the header of this image. Picture class is used to show the image to screen, using linear interpolation to transform values in the .fits file (the minimum intensity value in the .fits is set to color 0, and the maximum to 255, for the 8-bit gray image supported in Java). This formula can be customized as it is shown in the above code. Next, SExtractor (a program to extract sources from astronomical images) is used to retrieve all sources in the image, and a red oval is drawn around all sources above 3 sigma level. SExtractor will de described later in the science section, it requires the Fortran program itself to be installed.

Previous example also shows how to use the WCS (World Coordinate System) to obtain the position of a given pixel in the image. You can use WCS wcs = fio.getWCS(imageNumber) to obtain a WCS instance called wcs to do pixel ←→ sky conversions or, as shown in the example, do everything in just one line of code to obtain the position of the brightest source.

Check now the following more simple example. It loads a jpg file to a Picture object, and the arrays of R, G, and B colors are used to create 3 HDUs for a .fits file, including some info in the header. The original jpg image is later recovered from the HDUs. You can retrieve and set the data in the HDUs with the methods available in FitsIO class, so processing of the image in .fits file is a question of processing the array themselves. For this task the class jparsec.astrophysics.Table is useful, as we will see later.

// Separate an image into rgb hdus
String s = "/home/alonso/java/soporte/2013/testeoFits/Hs-2009-14-a-web.jpg";
Picture pic = new Picture(s);
int img[][][] = pic.getImageAsArray(); // as rgba, x, y
 
// Construct a basic fits header and add some info to it
ImageHeaderElement header[] = ImageHeaderElement.getFitsHeader(img[0]);
header = ImageHeaderElement.addHeaderEntry(header, new ImageHeaderElement("LATOBS", "32:11:56", "as a test"));
header = ImageHeaderElement.addHeaderEntry(header, new ImageHeaderElement("LONGOBS", "110:56", "as a test"));
 
// Insert data into the fits
FitsIO rgb = new FitsIO();
for (int i=0; i<3; i++) {
	rgb.addHDU(FitsIO.createHDU(img[i], header));
}
 
// Write fits file
rgb.writeEntireFits("/home/alonso/test.fits");
 
// Check header
header = rgb.getHeader(0);
System.out.println(ImageHeaderElement.toString(header));
 
// Reconstruct original image
int[][] cr = (int[][]) rgb.getData(0);
int[][] cg = (int[][]) rgb.getData(1);
int[][] cb = (int[][]) rgb.getData(2);
pic = new Picture(cr, cg, cb, null);
pic.show("Original image");

Previous example is included in the main method at jparsec.io.image.FitsIO class. In that implementation the fits file is written using data of type short instead of integer arrays used above. Integers have a huge range of values and each value requires 4 bytes or 32 bits of hard disk memory. With short the range is from -32768 to +32767, and each value requires 2 bytes. Since the data in the RGB arrays range from 0 to 255, it is possible to save memory using short values, or even save more using byte data (from -128 to +127). The methods in DataSet can be used to transform the integer arrays to byte adding an offset of -128 to keep original data (0 to 255) in the adequate range (-128 to 127). This is left as an exercise for the reader.

Another example related to fits files is given in the main method of the class jparsec.io.image.FitsBinaryTable. The example shows how to create a binary (or ASCII) table in a fits file and how to read it. A set of ImageHeaderElement objects are used in JPARSEC to store the cards of the main header of the fits file and the header of the binary table. A fits file should never contain a binary table in the first plain (HDU) of the file, it must be an image (maybe with null or no data for the image). The table is written and returned using String arrays as shown in the example:

ImageHeaderElement primaryHeader[] = FitsBinaryTable.parseHeader(new String[] {
       	"SIMPLE  L  T",
       	"NAXIS  I  0",
       	"BITPIX  I  32",
       	"EXTEND  L  T",
	"TELESCOP 13A OAN", // format is entry name, entry format (fits convention), value, / comment (optional)
	"ORIGIN A OAN",
	"CREATOR A T. Alonso Albi - OAN, JPARSEC-package",
	"COMMENT A Testing phase",
	"OBSERVER  A  Tomás"
});			
 
ImageHeaderElement header[] = FitsBinaryTable.parseHeader(new String[] {
       	"EXTNAME  A  MY TABLE",
       	"TABLEREV  I  1"
});
String table[][] = new String[][] {
	new String[] {"col1", "col2", "col3", "col4", "col5"},
	new String[] {"col1row1", "col2row1", "col3row1", "col4row1", "col5row1"},
	new String[] {"col1row2", "col2row2", "col3row2", "col4row2", "col5row2"},
	new String[] {"col1row3", "col2row3", "col3row3", "col4row3", "col5row3"}
};
 
FitsIO f = new FitsIO(FitsIO.createHDU(null, primaryHeader)); // First HDU must be an image, never a table
f.addHDU(FitsBinaryTable.createBinaryTable(header, table));
 
int n = f.getNumberOfPlains();
System.out.println();
System.out.println("Found "+n+" HDUs:");
System.out.println(f.toString());
 
for (int i=0; i<n; i++)
{
	System.out.println("HDU # "+(i+1));		        
	header = f.getHeader(i);
	System.out.println(ImageHeaderElement.toString(header));
 
	if (f.isBinaryTable(i)) {
		table = FitsBinaryTable.getBinaryTable(f.getHDU(i));
		ConsoleReport.stringArrayReport(table, table[0].length+"a15");
	}
	if (f.isAsciiTable(i)) {
		table = FitsBinaryTable.getAsciiTable(f.getHDU(i));
		ConsoleReport.stringArrayReport(table, table[0].length+"a15");
	}
}

The method FitsBinaryTable.parseHeader is a simple way to parse a header writen as a String array. Maybe not very sophisticate or efficient, but at least simple. The cards are given in each line with a name, a data type (A for string, L for logic or boolean, J for integer, I for short, E for float, and D for double), a value, and optionally you can add / and a comment for the card. As separator you can use one or several blank spaces. With the table is the same, but requires two dimensions. To create later the table is very simple, using either the createBinaryTable or the createAsciiTable methods.

Drawings

There's also a generic class for drawings called Draw in jparsec.io.image package. It is designed as a help class to create simple drawings programatically, combining lines, arcs, and text. The class is suitable to create basic diagrams and to export them to vector graphics optionally. In the following example the drawing method is separated from the code in the main method so that it can be called both to show the image to the screen and to export to SVG using the freeHEP library. For simplicity I include here the complete code of the main program.

private static void doDraw(Draw draw) throws JPARSECException {
	draw.setPenRadius(0.01);
	draw.square(.2, .8, .1);
	draw.filledSquare(.8, .8, .2);
	draw.circle(.8, .2, .2);
 
	draw.setPenColor(Color.MAGENTA);
	draw.setPenRadius(.02);
	draw.arc(.8, .2, .1, 200, 45);
 
	// draw a blue diamond
	draw.setPenColor(Color.BLUE);
	double[] x = { .1, .2, .3, .2 };
	double[] y = { .2, .3, .2, .1 };
	draw.filledPolygon(x, y);
 
	// text
	draw.setPenColor(Color.BLACK);
	draw.text(0.2, 0.5, "black text", true);
	draw.setPenColor(Color.WHITE);
	draw.text(0.8, 0.8, "white text", true);
	draw.setPenColor(Color.BLACK);
}
 
/** 
 * For unit testing only.
 * @param args Not used.
 */
public static void main(String[] args) {
	try {
		int w = 500, h = 500;
		Draw draw = new Draw(w, h);
		doDraw(draw);
		draw.getPicture().show("My draw");
 
		// Example of SVG export
		File plotFile = new File("/home/alonso/test.svg");
		Dimension size = new Dimension(w, h);
 
		// Create the SVGGraphics2D object
		org.freehep.graphicsio.svg.SVGGraphics2D svgGraphics = new org.freehep.graphicsio.svg.SVGGraphics2D(plotFile, size);
		svgGraphics.startExport();
 
		// Export to vector graphics
		Draw svgDraw = new Draw(svgGraphics, size.width, size.height);
		doDraw(svgDraw);
 
		// Close SVG export
		svgGraphics.endExport();
	} catch (Exception exc) {
		exc.printStackTrace();
	}
}

The freeHEP library is distributed as one of the .jar dependencies of JPARSEC. It also provides eps and pdf export, useful for relatively simple drawings and for the charts presented in previous sections. For sky rendering I found freeHEP to be not enough robust, and that's the reason to use iText library for pdf export of sky rendering in a previous section (SkyPoster example). The image output from the previous code is:


 

Diagrams

The class jparsec.graph.CreateDitaaChart can be used to create more expecific diagrams in Java by means of the ditaa tool. The following ASCII code:

                              +-------------+                                                                                                                                                      
                              |{io}c897     |                                                                                                                                                      
                              |   Observed  |                                                                                                                                                      
                              |   Herschel  +----------------------------------------------------------------------------------------------------------------\                                     
                              |   spectra   |                                                                                                                |                                     
                              |             |                                                                                                                |                                     
                              +-------------+                                                                                                                |                                     
                                                                                                                                                      +------*------+                              
                                                                                                                                                      |{c}cDEF      |                              
                                                                                                                                                      |             |                              
       /-----------------------------------------------------------------------------------------\                                                    |   Fitted?   *-------\                      
       |                                                                                         |                                                    |             |       |                      
       |                                                                                         |                                                    |             |       |                      
       |                                                                                         v                                                    +------*------+       |                      
+------+------+               +-------------+               +-------------+               +------+------+               +-------------+                      |              |       N              
|{d}c897      |               |{o}cAAF      |               |{d}c789      |               |{o}cAAF      |               |cFFF         |                      |              |                      
|  Profiles   |               |   Chemical  |               |  Modeled    |               |   Radiative |               |             |                      |              |                      
|by N. Crimier+-------------->|    model    +-------------->|  abundance  +-------------->|   transfer  +-------------->|   Spectra   +----------------------/              |                      
|T(r), @rho(r)|               | (P. Caselli)|               |  profiles   |               |   model     |               |             |                                     |                      
|             |               |             |               |             |               |             |               |             |                                     |                      
+-------------+               +------*------+               +-------------+               +-------------+               +-------------+                                     |                      
                                     |                                                                                                                                      |                      
                                     |                                                                                                                                      |                      
                                     |                                                                                                                                      |                      
                                     \-----------------------------=-----------------------------<--------------------------------------------=-----------------------------/                      
                                                                                                                                                                                                   
                                                                                                                                                                                                   
                                                                                                                                                                                                   
                                                                                                                                                             Chemical effects                      

is transfomed by ditaa into the image shown below. There are other java libraries to create similar kind of plots, but I found the sthetics and quality of the output by ditaa better than the others.


 

To write an ASCII code like the previous one can be hard, so in JPARSEC there's a handy way to do that using the class DitaaBoxElement. The code to generate the previous chart is shown next. It is not intended to work fine in all cases, but at least it will help to create complex charts even in case some manual editing is required later. It also has the advantage of allowing a quick reorganization of the chart in case you want to move the elements. The latest commented lines shows the interesting possibility of exporting the chart to a vector graphics format in just one code line.

// Global layout for the diagram, with the
// position of the boxes and the arrows to show.
// Each number is the index for a box defined later.
// This allows to reorganize the chart very quickly
String diagram[] = new String[] {
	"  0-------|",
	"|-----v   1-",
	"2-3-4-5-6-||7",
	"  |-=-<--=--",
	"          89"
};
boolean dashed = false;
// Definition for each box
DitaaBoxElement db[] = new DitaaBoxElement[] {
	new DitaaBoxElement(new String[] {"Observed", "Herschel", "spectra"}, "c897", TYPE.INPUT_OUTPUT, "W", dashed),
	new DitaaBoxElement(new String[] {"Fitted?"}, "cDEF", TYPE.QUESTION, "nsw", dashed),
	new DitaaBoxElement(new String[] {"  Profiles", "by N. Crimier", "T(r), @rho(r)"}, "c897", TYPE.DOCUMENT, "NW", dashed),
	new DitaaBoxElement(new String[] {"  Chemical", "   model", "(P. Caselli)"}, "cAAF", TYPE.ELLIPSE, "Ws", dashed),
	new DitaaBoxElement(new String[] {"Modeled", "abundance", "profiles"}, "c789", TYPE.DOCUMENT, "W", dashed),
	new DitaaBoxElement(new String[] {" Radiative", " transfer", " model"}, "cAAF", TYPE.ELLIPSE, "WN", dashed),
	new DitaaBoxElement(new String[] {"Spectra "}, "cFFF", TYPE.SIMPLE, "W", dashed),
	new DitaaBoxElement(new String[] {"N"}, "cFFF", TYPE.NO_BOX, "E", false),
	new DitaaBoxElement(new String[] {"Chemical"}, "cFFF", TYPE.NO_BOX, "NW", false),
	new DitaaBoxElement(new String[] {" effects"}, "cFFF", TYPE.NO_BOX, "NE", false)
};
// Width and height for the boxes
int w = 15, h = 7;
 
// Construct and show the chart
CreateDitaaChart qd = new CreateDitaaChart(diagram, db, w, h);
qd.setTransparent(false);
System.out.println(qd.diagram);
Picture pic = new Picture(qd.ditaaRenderImage());
pic.show("My diagram");
 
/*
// Optional export to vector graphics
Picture.createSVGPicture("/home/alonso/test.svg", qd, "draw", pic.getWidth(), pic.getHeight());
Picture.createEPSPicture("/home/alonso/test.eps", qd, "draw", pic.getWidth(), pic.getHeight());
Picture.createPDFPicture("/home/alonso/test.pdf", qd, "draw", pic.getWidth(), pic.getHeight());
*/

Latex formulae

JPARSEC also supports the rendering of Latex formulae using the class jparsec.math.LATEXFormula, which is a bridge to use JMathTex library with easy. An example is given in its main method:

String expression = LATEXFormula.getIntegral("t=0", "2pi")+LATEXFormula.getFraction("sqrt(t)",
	LATEXFormula.getBetweenParentesis("1+cos^2(t)"))+" dt";
LATEXFormula formula = new LATEXFormula(expression, LATEXFormula.STYLE_DISPLAY,
	LATEXFormula.SIZE_HUGE);
// Or, alternatively
// formula.setCode("\\int_{t=0}^{2\\pi}\\frac{\\sqrt{t}}{(1+\\mathrm{cos}^2{t})}\\nbspdt");
System.out.println(formula.getCode());
BufferedImage image = formula.getAsImage();
 
Picture p = new Picture(image);
p.show("LATEX formula");

The output to the console and the image are:

\int_{t=0}^{2\pi}\frac{\sqrt{t}}{(1+\mathrm{cos}^2{t})}\nbspdt


 

This class can be used to create charts showing equations or simply Latex glyphs and text. Complete Latex documents (to be later compiled) can be created using the class LATEXReport, including equations with the writeRawText method for an instance of that class. LATEXReport is described below.

Measures

It is important in science to make calculations considering the noise or errors and to propagate them properly when one needs to obtain the average signal by combining several images, for instance. JPARSEC support the treatment of measures using the jparsec.astrophysics.MeasureElement class, that requires a value, and error, and a unit to be defined. For images the jparsec.astrophysics.Table class can be used to operate with 1d/2d/3d arrays of measures, representing images or spectra.

Individual measures

Individual measures constructed using MeasureElement can be added, multiplied, powered, and inverted with error propagation included using standard conventions. An individual measurement can later be transformed to other compatible units (m to pc for instance), using the methods available for an instance of MeasureElement. These methods uses the class jparsec.math.Converter, which is an intermediate step to a library created by CDS for unit conversion. The class MeasureElement contains some static constants with useful measures of physical constants.

MeasureElement contains a useful method toString() to obtain a string representation of a measure. This representation takes into account the error to write only the significant digits, using standard conventions. In short, when the error can be reduced to a number which is 25 or less, the error (and the value of the measure) will have these two significant digits. In case the error is greater, it will have only one. For instance: 56.789… +/- 12.3… should be writen as 57 +/- 13, and 56.789… +/- 34.5… as 60 +/- 40. The toString() method can be extremely useful to create tables with thousands of values, for instance for a Latex report, as we will see later. For now, check this code snippet:

MeasureElement me0 = new MeasureElement(156.4E-83, 18.44E-83, MeasureElement.UNIT_Y_JY);
System.out.println(me0.toString());
 
MeasureElement me1 = new MeasureElement(1.0, 1.0, MeasureElement.UNIT_Y_JY);
MeasureElement me2 = new MeasureElement(1.0, 1.0, MeasureElement.UNIT_Y_MAG_JOHNSON_I);
 
System.out.println("(1) = "+me1.value+" +/- "+me1.error+" / "+me1.unit);
System.out.println("(2) = "+me2.value+" +/- "+me2.error+" / "+me2.unit);
 
me1.add(me2);
System.out.println("(1) + (2) = "+me1.value+" +/- "+me1.error+" / "+me1.unit+" = "+me1.toString());

The output of this code is:

156E-83 +/- 19E-83 Jy
(1) = 1.0 +/- 1.0 / Jy
(2) = 1.0 +/- 1.0 / JOHNSON I
(1) + (2) = 968.4004244449982 +/- 891.0092796748264 / Jy = 1000 +/- 900 Jy

The class jparsec.math.MeanValue also accept an array of MeasureElement objects to calculate different kind of statistical medians.

Working with images and noise

The same operations allowed in MeasureElement are also supported in jparsec.astrophysics.Table class, but they will be applied to an array of MeasureElement objects representing a 1d spectrum, a 2d image, or a 3d cube of data. The constructors of a Table object are flexible to allow instantiation from different data types, so you can create a Table from a fits file with easy and recover the new data after manipulation in the same data type, to replace the values in the fits file.

A very useful feature in Table class is to set a mask to some of the values in the input data. The mask can be applied to individual pixels or groups of pixels above or below certain intensity level. Masked pixels will not change their values when an operation is applied.

Here is an example of Table showing different formatting methods in JPARSEC for the data.

Table table1 = new Table(new double[] {1, 2, 3, 4}, "s");
Table table2 = new Table(new double[] {-1, -2, -3, -4}, "hr");
 
table1.addMask(new boolean[] {false, false, false, true});
System.out.println(table1.toString());
table1.add(table2);
 
System.out.println(table1.toString());
System.out.println(table1.equals(table2));
 
// 2d test of formatting
double[][] p = Difraction.pattern(TelescopeElement.SCHMIDT_CASSEGRAIN_20cm, 3);
table1 = new Table(p, "Jy");
String table1s = table1.toString(" ", "f1.2");
table1s = DataSet.replaceAll(table1s, ".00", "   ", false);
System.out.println(table1s);
System.out.println("Same using Matrix class ...");
Matrix m = new Matrix(p);
System.out.println(m.toString());
System.out.println("Same using FileFormatElement ...");
FileFormatElement format[] = new FileFormatElement[p[0].length];
for (int i=0; i<format.length; i++) {
	int a = i*5+1, b = a + 3;
	format[i] = new FileFormatElement(a, b, "field"+i);
}
table1s = table1.toString(format);
table1s = DataSet.replaceAll(table1s, ".00", "   ", false);
System.out.println(table1s);
 
int max[] = table1.getMaximumIndex(true);
System.out.println("Max is "+table1.getMaximum()+" at "+max[0]+", "+max[1]+", "+max[2]);
int min[] = table1.getMinimumIndex(true);
System.out.println("Min is "+table1.getMinimum()+" at "+min[0]+", "+min[1]+", "+min[2]);
 
for (double r = 1; r< 15; r++) {
	Table ring = table1.getFluxAround(15, 15, r-1, r, false);
	System.out.println("Total flux in ring from "+(r-1)+" to "+r+": "+ring.get(0, 0, 0));
}

Note in this program the image is formatted using Table class, but also with Matrix and FileFormatElement, with identical results. The end of the example shows calculation of integrated intensities in different rings around the central position. The output of the previous program is presented below.

1.0   2.0   3.0   4.0   

-3599.0   -7198.0   -10797.0   4.0   

false
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    
0    0    0    0    0    0    0    0    0    0    0    0    0.01 0.01 0.01 0.01 0.01 0.01 0.01 0    0    0    0    0    0    0    0    0    0    0    0    
0    0    0    0    0    0    0    0    0    0    0.01 0.02 0.02 0.03 0.03 0.03 0.03 0.03 0.02 0.02 0.01 0    0    0    0    0    0.01 0.01 0    0    0    
0    0    0    0    0    0    0    0    0.01 0.02 0.03 0.04 0.05 0.06 0.06 0.06 0.06 0.05 0.05 0.04 0.03 0.01 0.01 0    0    0    0    0.01 0    0    0    
0    0    0    0    0    0    0    0.01 0.02 0.03 0.05 0.06 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.06 0.05 0.03 0.02 0.01 0    0    0    0    0.01 0    0    
0    0    0    0    0    0    0.01 0.02 0.04 0.05 0.06 0.07 0.07 0.06 0.06 0.06 0.06 0.06 0.07 0.07 0.06 0.05 0.04 0.02 0.01 0    0    0    0    0    0    
0    0.01 0    0    0    0.01 0.02 0.04 0.05 0.06 0.06 0.05 0.04 0.03 0.02 0.02 0.02 0.03 0.04 0.06 0.06 0.07 0.06 0.04 0.02 0.01 0    0    0    0    0    
0.01 0    0    0    0    0.01 0.03 0.05 0.06 0.06 0.05 0.03 0.01 0    0    0    0    0    0.01 0.03 0.05 0.07 0.07 0.06 0.04 0.02 0    0    0    0    0    
0.01 0    0    0    0.01 0.03 0.05 0.06 0.06 0.05 0.02 0    0    0.02 0.03 0.04 0.03 0.01 0    0    0.03 0.05 0.07 0.07 0.05 0.03 0.01 0    0    0    0    
0.01 0    0    0    0.02 0.04 0.06 0.07 0.06 0.03 0    0    0.04 0.10 0.16 0.18 0.16 0.10 0.04 0    0.01 0.03 0.06 0.08 0.07 0.05 0.02 0.01 0    0    0    
0    0    0    0.01 0.03 0.05 0.07 0.07 0.05 0.01 0    0.04 0.14 0.27 0.38 0.42 0.37 0.26 0.13 0.04 0    0.02 0.05 0.08 0.08 0.06 0.03 0.01 0    0    0    
0    0    0    0.01 0.03 0.06 0.08 0.07 0.04 0    0.01 0.10 0.26 0.46 0.63 0.69 0.63 0.46 0.26 0.09 0.01 0.01 0.04 0.08 0.09 0.07 0.04 0.01 0    0    0    
0    0    0    0.01 0.04 0.07 0.09 0.07 0.03 0    0.03 0.15 0.37 0.63 0.83 0.91 0.83 0.62 0.36 0.15 0.03 0    0.03 0.07 0.09 0.07 0.04 0.02 0    0    0    
0    0    0    0.02 0.04 0.07 0.09 0.07 0.03 0    0.03 0.17 0.41 0.69 0.91 1    0.91 0.69 0.41 0.17 0.03 0    0.03 0.07 0.09 0.07 0.04 0.02 0    0    0    
0    0    0    0.02 0.04 0.07 0.09 0.07 0.03 0    0.03 0.15 0.36 0.62 0.83 0.91 0.83 0.63 0.37 0.15 0.03 0    0.03 0.07 0.09 0.07 0.04 0.01 0    0    0    
0    0    0    0.01 0.04 0.07 0.09 0.08 0.04 0.01 0.01 0.09 0.26 0.46 0.63 0.69 0.63 0.46 0.26 0.10 0.01 0    0.04 0.07 0.08 0.06 0.03 0.01 0    0    0    
0    0    0    0.01 0.03 0.06 0.08 0.08 0.05 0.02 0    0.04 0.13 0.26 0.37 0.42 0.38 0.27 0.14 0.04 0    0.01 0.05 0.07 0.07 0.05 0.03 0.01 0    0    0    
0    0    0    0.01 0.02 0.05 0.07 0.08 0.06 0.03 0.01 0    0.04 0.10 0.16 0.18 0.16 0.10 0.04 0    0    0.03 0.06 0.07 0.06 0.04 0.02 0    0    0    0.01 
0    0    0    0    0.01 0.03 0.05 0.07 0.07 0.05 0.03 0    0    0.01 0.03 0.04 0.03 0.02 0    0    0.02 0.05 0.06 0.06 0.05 0.03 0.01 0    0    0    0.01 
0    0    0    0    0    0.02 0.04 0.06 0.07 0.07 0.05 0.03 0.01 0    0    0    0    0    0.01 0.03 0.05 0.06 0.06 0.05 0.03 0.01 0    0    0    0    0.01 
0    0    0    0    0    0.01 0.02 0.04 0.06 0.07 0.06 0.06 0.04 0.03 0.02 0.02 0.02 0.03 0.04 0.05 0.06 0.06 0.05 0.04 0.02 0.01 0    0    0    0.01 0    
0    0    0    0    0    0    0.01 0.02 0.04 0.05 0.06 0.07 0.07 0.06 0.06 0.06 0.06 0.06 0.07 0.07 0.06 0.05 0.04 0.02 0.01 0    0    0    0    0    0    
0    0    0.01 0    0    0    0    0.01 0.02 0.03 0.05 0.06 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.06 0.05 0.03 0.02 0.01 0    0    0    0    0    0    0    
0    0    0    0.01 0    0    0    0    0.01 0.01 0.03 0.04 0.05 0.05 0.06 0.06 0.06 0.06 0.05 0.04 0.03 0.02 0.01 0    0    0    0    0    0    0    0    
0    0    0    0.01 0.01 0    0    0    0    0    0.01 0.02 0.02 0.03 0.03 0.03 0.03 0.03 0.02 0.02 0.01 0    0    0    0    0    0    0    0    0    0    
0    0    0    0    0    0    0    0    0    0    0    0    0.01 0.01 0.01 0.01 0.01 0.01 0.01 0    0    0    0    0    0    0    0    0    0    0    0    
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    

Same using Matrix class ...
 0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
 0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
 0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
 0    0    0    0    0    0    0    0    0    0    0    0    0.01 0.01 0.01 0.01 0.01 0.01 0.01 0    0    0    0    0    0    0    0    0    0    0    0   
 0    0    0    0    0    0    0    0    0    0    0.01 0.02 0.02 0.03 0.03 0.03 0.03 0.03 0.02 0.02 0.01 0    0    0    0    0    0.01 0.01 0    0    0   
 0    0    0    0    0    0    0    0    0.01 0.02 0.03 0.04 0.05 0.06 0.06 0.06 0.06 0.05 0.05 0.04 0.03 0.01 0.01 0    0    0    0    0.01 0    0    0   
 0    0    0    0    0    0    0    0.01 0.02 0.03 0.05 0.06 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.06 0.05 0.03 0.02 0.01 0    0    0    0    0.01 0    0   
 0    0    0    0    0    0    0.01 0.02 0.04 0.05 0.06 0.07 0.07 0.06 0.06 0.06 0.06 0.06 0.07 0.07 0.06 0.05 0.04 0.02 0.01 0    0    0    0    0    0   
 0    0.01 0    0    0    0.01 0.02 0.04 0.05 0.06 0.06 0.05 0.04 0.03 0.02 0.02 0.02 0.03 0.04 0.06 0.06 0.07 0.06 0.04 0.02 0.01 0    0    0    0    0   
 0.01 0    0    0    0    0.01 0.03 0.05 0.06 0.06 0.05 0.03 0.01 0    0    0    0    0    0.01 0.03 0.05 0.07 0.07 0.06 0.04 0.02 0    0    0    0    0   
 0.01 0    0    0    0.01 0.03 0.05 0.06 0.06 0.05 0.02 0    0    0.02 0.03 0.04 0.03 0.01 0    0    0.03 0.05 0.07 0.07 0.05 0.03 0.01 0    0    0    0   
 0.01 0    0    0    0.02 0.04 0.06 0.07 0.06 0.03 0    0    0.04 0.1  0.16 0.18 0.16 0.1  0.04 0    0.01 0.03 0.06 0.08 0.07 0.05 0.02 0.01 0    0    0   
 0    0    0    0.01 0.03 0.05 0.07 0.07 0.05 0.01 0    0.04 0.14 0.27 0.38 0.42 0.37 0.26 0.13 0.04 0    0.02 0.05 0.08 0.08 0.06 0.03 0.01 0    0    0   
 0    0    0    0.01 0.03 0.06 0.08 0.07 0.04 0    0.01 0.1  0.26 0.46 0.63 0.69 0.63 0.46 0.26 0.09 0.01 0.01 0.04 0.08 0.09 0.07 0.04 0.01 0    0    0   
 0    0    0    0.01 0.04 0.07 0.09 0.07 0.03 0    0.03 0.15 0.37 0.63 0.83 0.91 0.83 0.62 0.36 0.15 0.03 0    0.03 0.07 0.09 0.07 0.04 0.02 0    0    0   
 0    0    0    0.02 0.04 0.07 0.09 0.07 0.03 0    0.03 0.17 0.41 0.69 0.91 1    0.91 0.69 0.41 0.17 0.03 0    0.03 0.07 0.09 0.07 0.04 0.02 0    0    0   
 0    0    0    0.02 0.04 0.07 0.09 0.07 0.03 0    0.03 0.15 0.36 0.62 0.83 0.91 0.83 0.63 0.37 0.15 0.03 0    0.03 0.07 0.09 0.07 0.04 0.01 0    0    0   
 0    0    0    0.01 0.04 0.07 0.09 0.08 0.04 0.01 0.01 0.09 0.26 0.46 0.63 0.69 0.63 0.46 0.26 0.1  0.01 0    0.04 0.07 0.08 0.06 0.03 0.01 0    0    0   
 0    0    0    0.01 0.03 0.06 0.08 0.08 0.05 0.02 0    0.04 0.13 0.26 0.37 0.42 0.38 0.27 0.14 0.04 0    0.01 0.05 0.07 0.07 0.05 0.03 0.01 0    0    0   
 0    0    0    0.01 0.02 0.05 0.07 0.08 0.06 0.03 0.01 0    0.04 0.1  0.16 0.18 0.16 0.1  0.04 0    0    0.03 0.06 0.07 0.06 0.04 0.02 0    0    0    0.01
 0    0    0    0    0.01 0.03 0.05 0.07 0.07 0.05 0.03 0    0    0.01 0.03 0.04 0.03 0.02 0    0    0.02 0.05 0.06 0.06 0.05 0.03 0.01 0    0    0    0.01
 0    0    0    0    0    0.02 0.04 0.06 0.07 0.07 0.05 0.03 0.01 0    0    0    0    0    0.01 0.03 0.05 0.06 0.06 0.05 0.03 0.01 0    0    0    0    0.01
 0    0    0    0    0    0.01 0.02 0.04 0.06 0.07 0.06 0.06 0.04 0.03 0.02 0.02 0.02 0.03 0.04 0.05 0.06 0.06 0.05 0.04 0.02 0.01 0    0    0    0.01 0   
 0    0    0    0    0    0    0.01 0.02 0.04 0.05 0.06 0.07 0.07 0.06 0.06 0.06 0.06 0.06 0.07 0.07 0.06 0.05 0.04 0.02 0.01 0    0    0    0    0    0   
 0    0    0.01 0    0    0    0    0.01 0.02 0.03 0.05 0.06 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.06 0.05 0.03 0.02 0.01 0    0    0    0    0    0    0   
 0    0    0    0.01 0    0    0    0    0.01 0.01 0.03 0.04 0.05 0.05 0.06 0.06 0.06 0.06 0.05 0.04 0.03 0.02 0.01 0    0    0    0    0    0    0    0   
 0    0    0    0.01 0.01 0    0    0    0    0    0.01 0.02 0.02 0.03 0.03 0.03 0.03 0.03 0.02 0.02 0.01 0    0    0    0    0    0    0    0    0    0   
 0    0    0    0    0    0    0    0    0    0    0    0    0.01 0.01 0.01 0.01 0.01 0.01 0.01 0    0    0    0    0    0    0    0    0    0    0    0   
 0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
 0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
 0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   

Same using FileFormatElement ...
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
0    0    0    0    0    0    0    0    0    0    0    0    0.01 0.01 0.01 0.01 0.01 0.01 0.01 0    0    0    0    0    0    0    0    0    0    0    0   
0    0    0    0    0    0    0    0    0    0    0.01 0.02 0.02 0.03 0.03 0.03 0.03 0.03 0.02 0.02 0.01 0    0    0    0    0    0.01 0.01 0    0    0   
0    0    0    0    0    0    0    0    0.01 0.02 0.03 0.04 0.05 0.06 0.06 0.06 0.06 0.05 0.05 0.04 0.03 0.01 0.01 0    0    0    0    0.01 0    0    0   
0    0    0    0    0    0    0    0.01 0.02 0.03 0.05 0.06 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.06 0.05 0.03 0.02 0.01 0    0    0    0    0.01 0    0   
0    0    0    0    0    0    0.01 0.02 0.04 0.05 0.06 0.07 0.07 0.06 0.06 0.06 0.06 0.06 0.07 0.07 0.06 0.05 0.04 0.02 0.01 0    0    0    0    0    0   
0    0.01 0    0    0    0.01 0.02 0.04 0.05 0.06 0.06 0.05 0.04 0.03 0.02 0.02 0.02 0.03 0.04 0.06 0.06 0.07 0.06 0.04 0.02 0.01 0    0    0    0    0   
0.01 0    0    0    0    0.01 0.03 0.05 0.06 0.06 0.05 0.03 0.01 0    0    0    0    0    0.01 0.03 0.05 0.07 0.07 0.06 0.04 0.02 0    0    0    0    0   
0.01 0    0    0    0.01 0.03 0.05 0.06 0.06 0.05 0.02 0    0    0.02 0.03 0.04 0.03 0.01 0    0    0.03 0.05 0.07 0.07 0.05 0.03 0.01 0    0    0    0   
0.01 0    0    0    0.02 0.04 0.06 0.07 0.06 0.03 0    0    0.04 0.10 0.16 0.18 0.16 0.10 0.04 0    0.01 0.03 0.06 0.08 0.07 0.05 0.02 0.01 0    0    0   
0    0    0    0.01 0.03 0.05 0.07 0.07 0.05 0.01 0    0.04 0.14 0.27 0.38 0.42 0.37 0.26 0.13 0.04 0    0.02 0.05 0.08 0.08 0.06 0.03 0.01 0    0    0   
0    0    0    0.01 0.03 0.06 0.08 0.07 0.04 0    0.01 0.10 0.26 0.46 0.63 0.69 0.63 0.46 0.26 0.09 0.01 0.01 0.04 0.08 0.09 0.07 0.04 0.01 0    0    0   
0    0    0    0.01 0.04 0.07 0.09 0.07 0.03 0    0.03 0.15 0.37 0.63 0.83 0.91 0.83 0.62 0.36 0.15 0.03 0    0.03 0.07 0.09 0.07 0.04 0.02 0    0    0   
0    0    0    0.02 0.04 0.07 0.09 0.07 0.03 0    0.03 0.17 0.41 0.69 0.91 1    0.91 0.69 0.41 0.17 0.03 0    0.03 0.07 0.09 0.07 0.04 0.02 0    0    0   
0    0    0    0.02 0.04 0.07 0.09 0.07 0.03 0    0.03 0.15 0.36 0.62 0.83 0.91 0.83 0.63 0.37 0.15 0.03 0    0.03 0.07 0.09 0.07 0.04 0.01 0    0    0   
0    0    0    0.01 0.04 0.07 0.09 0.08 0.04 0.01 0.01 0.09 0.26 0.46 0.63 0.69 0.63 0.46 0.26 0.10 0.01 0    0.04 0.07 0.08 0.06 0.03 0.01 0    0    0   
0    0    0    0.01 0.03 0.06 0.08 0.08 0.05 0.02 0    0.04 0.13 0.26 0.37 0.42 0.38 0.27 0.14 0.04 0    0.01 0.05 0.07 0.07 0.05 0.03 0.01 0    0    0   
0    0    0    0.01 0.02 0.05 0.07 0.08 0.06 0.03 0.01 0    0.04 0.10 0.16 0.18 0.16 0.10 0.04 0    0    0.03 0.06 0.07 0.06 0.04 0.02 0    0    0    0.01
0    0    0    0    0.01 0.03 0.05 0.07 0.07 0.05 0.03 0    0    0.01 0.03 0.04 0.03 0.02 0    0    0.02 0.05 0.06 0.06 0.05 0.03 0.01 0    0    0    0.01
0    0    0    0    0    0.02 0.04 0.06 0.07 0.07 0.05 0.03 0.01 0    0    0    0    0    0.01 0.03 0.05 0.06 0.06 0.05 0.03 0.01 0    0    0    0    0.01
0    0    0    0    0    0.01 0.02 0.04 0.06 0.07 0.06 0.06 0.04 0.03 0.02 0.02 0.02 0.03 0.04 0.05 0.06 0.06 0.05 0.04 0.02 0.01 0    0    0    0.01 0   
0    0    0    0    0    0    0.01 0.02 0.04 0.05 0.06 0.07 0.07 0.06 0.06 0.06 0.06 0.06 0.07 0.07 0.06 0.05 0.04 0.02 0.01 0    0    0    0    0    0   
0    0    0.01 0    0    0    0    0.01 0.02 0.03 0.05 0.06 0.07 0.07 0.07 0.07 0.07 0.07 0.07 0.06 0.05 0.03 0.02 0.01 0    0    0    0    0    0    0   
0    0    0    0.01 0    0    0    0    0.01 0.01 0.03 0.04 0.05 0.05 0.06 0.06 0.06 0.06 0.05 0.04 0.03 0.02 0.01 0    0    0    0    0    0    0    0   
0    0    0    0.01 0.01 0    0    0    0    0    0.01 0.02 0.02 0.03 0.03 0.03 0.03 0.03 0.02 0.02 0.01 0    0    0    0    0    0    0    0    0    0   
0    0    0    0    0    0    0    0    0    0    0    0    0.01 0.01 0.01 0.01 0.01 0.01 0.01 0    0    0    0    0    0    0    0    0    0    0    0   
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   
0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0   

Max is 1.0 +/- 0.0 at 0, 15, 15
Min is 1.219568436597598E-8 +/- 0.0 at 0, 1, 28
Total flux in ring from 0.0 to 1.0: 1.0 +/- 0.0
Total flux in ring from 1.0 to 2.0: 6.9903719880958395 +/- 0.0
Total flux in ring from 2.0 to 3.0: 9.608771938370078 +/- 0.0
Total flux in ring from 3.0 to 4.0: 6.703399355128226 +/- 0.0
Total flux in ring from 4.0 to 5.0: 3.2582753193955334 +/- 0.0
Total flux in ring from 5.0 to 6.0: 0.8294618177157163 +/- 0.0
Total flux in ring from 6.0 to 7.0: 0.1743781277747724 +/- 0.0
Total flux in ring from 7.0 to 8.0: 1.7174758876105507 +/- 0.0
Total flux in ring from 8.0 to 9.0: 3.711903750612772 +/- 0.0
Total flux in ring from 9.0 to 10.0: 3.910214272343575 +/- 0.0
Total flux in ring from 10.0 to 11.0: 3.4162632402820745 +/- 0.0
Total flux in ring from 11.0 to 12.0: 1.6618970134182787 +/- 0.0
Total flux in ring from 12.0 to 13.0: 0.5842988080980629 +/- 0.0
Total flux in ring from 13.0 to 14.0: 0.06040490433977885 +/- 0.0

For a 2d image you can use a .fits file as source. The following example loads an image into a Table object, clones that Table object, and subtract the second to the first Table object, producing a new Table with all values as 0. The new data is used to replace the old set in the fits file:

// Test with 2d image
String s = "/home/alonso/java/librerias/masymas/tres-3/TRES-3-025-070725-.fit";
FitsIO fio = new FitsIO(s);
int imageNumber = 0;
short data[][] = (short[][]) fio.getData(imageNumber);
Picture p1 = fio.getPicture(imageNumber, PICTURE_LEVEL.LINEAR_INTERPOLATION, true);
p1.show("my fits");
Table table1 = new Table(new int[][][] {DataSet.toIntArray(data, (int)fio.getBZero(imageNumber))}, "Jy");
Table table2 = table1.clone();
System.out.println("Maximum table1: "+table1.getMaximum().toString());
System.out.println("Maximum table2: "+table2.getMaximum().toString());
System.out.println("table1 = table2 ? "+table1.equals(table2));
table1.subtract(table2);
fio.setData(DataSet.toShortArray(table1.getValues()[0]), imageNumber);
Picture p2 = fio.getPicture(imageNumber, PICTURE_LEVEL.LINEAR_INTERPOLATION, true);
p2.show("my modified fits");
System.out.println("Maximum table1: "+table1.getMaximum().toString());
System.out.println("Maximum table2: "+table2.getMaximum().toString());
System.out.println("table1 = table2 ? "+table1.equals(table2));

Of course the example makes no sense in real use, it is shown only as an example I did for testing. The output to the console of this program is:

Maximum table1: 31179.0 +/- 0.0
Maximum table2: 31179.0 +/- 0.0
table1 = table2 ? true
Maximum table1: 0.0 +/- 0.0
Maximum table2: 31179.0 +/- 0.0
table1 = table2 ? false

Reports in Latex and HTML

The classes jparsec.io.HTMLReport and jparsec.io.LATEXReport allows the generation of complete HTML and Latex documents. The use of both classes is very similar, with methods to write a header, begin and end the body, and to end the document. Inside the body you can show text, images, and tables.

HTML documents

Here is a simple example using HTMLReport:

int w = 800, h = 800;
String s = "http://conga.oan.es/~alonso/lib/exe/fetch.php?media=jupitertripleeclipse.png";
Picture pic = new Picture(s);
pic.getScaledInstance(w, h, true);
 
HTMLReport html = new HTMLReport();
html.setTextColor(HTMLReport.COLOR_WHITE);
html.setTextSize(SIZE.LARGE);
html.writeHeader("Title");
html.beginBody(HTMLReport.COLOR_BLACK);
html.writeAVeryBigMainTitle("Jupiter data");
html.writeHorizontalLine();
html.writeParagraph("Jupiter is bla bla bla ...");
String width = ""+pic.getWidth(), height = ""+pic.getHeight(), align = "center", border = "0", alt = "", src = s;
html.writeImageToHTML(width, height, align, border, alt, src);
html.endBody();
html.endDocument();
 
HTMLRendering dlg = new HTMLRendering("Window title", html.getCode(), pic.getImage(), true);
dlg.setSize(w, h);
dlg.setModal(true);
dlg.setVisible(true);

Previous code first loads an image of Jupiter and resizes it maintaining the image ratio to the width of the desired HTML document in pixels. Then the HTMLReport object is created, and a header is written passing a document title (that would be visible in case the document is loaded in a browser). Then the body begins, text/images are written, and the body and document ends. The font size and color, as well as the background color (beginBody method), are set to get the desired output. One possibility is to call the WriteFile class to write the content of html.getCode(), and later even call the default system program to open an html file using ApplicationLauncher class, but for HTML documents JPARSEC includes an HTMLRendering class, that uses Java built-in features to render the html document. This will only work with basic documents.

Another example is given in jparsec.examples.medium.ImageGallery, showing how to convert a directory with images into an HTML page with a list of clickable images, like a gallery.

Latex documents

For a Latex report the LATEXReport class should be used. One possibility is to take the previous code and change the call to HTMLReport in the Constructor to LATEXReport. You will see few errors, that can be solved by removing the call to setTextSize, removing the background color in beginBody, and replacing writeImageToHTML by writeImage. With just that, the output wil be latex code.

A more specific example is given in the example jparsec.research.GenericModel. The class has a calculate method that simply returns a spectrum generated using random numbers and the input values provided to the instance of the model. The spectrum is exported to 30m and png formats, and also showed on the screen. Then a latex document is created including the spectrum image and a table with the x and y values of the spectrum, and finally the latex document is exported to a .tex file, compiled to pdf, and shown in the screen using the default pdf viewer of the system. All this from Java code.

// Instantiate the model
GenericModel model = new GenericModel(1.0, 2.0, 3.0);
 
// Execute the model
model.calculate();
 
// Obtain the output spectrum
Spectrum s = model.getSpectrum();
 
// Operate with the output spectrum
String output = "/home/alonso/myModel";
Spectrum30m s30m = new Spectrum30m(s);
s30m.writeAs30m(output+".30m"); // To write the spectrum as a .30m file for GILDAS software
CreateChart chart = s30m.getChart(800, 600, Spectrum30m.XUNIT.VELOCITY_KMS);
chart.showChartInJFreeChartPanelWithAdvancedControls();
chart.chartAsPNGFile(output+".png");
 
// Export results to LATEX
LATEXReport latex = new LATEXReport();
 
String title = "Results from my model";
latex.writeHeader(title);
latex.beginBody();
 
latex.writeBigSkip();
latex.writeParagraph("This paragraph is to describe my model and input parameters...");
latex.writeLongTableHeader("This is my table", "lll");
latex.writeRowInTable(new String[] {"Channel", "Velocity", "Flux"}, null, "LEFT", null);
latex.writeRowInTable(new String[] {"#", "(km/s)", "(K)"}, null, "LEFT", null);
latex.writeHorizontalLine();
for (int i=0; i<s30m.getSpectrumData().length; i++)
{					
	String columns[] = new String[] {""+(i+1), s.spectrum[i].x.toString(), s.spectrum[i].y.toString()};
	latex.writeRowInTable(columns, null, "LEFT", null);
}
latex.endLongTable("table1");
 
latex.writeImageWithCaption("100%", "80%", "CENTER", output+".png", "Model chart", "chart");
 
latex.endBody();
latex.endDocument();
 
WriteFile.writeAnyExternalFile(output+".tex", latex.getCode());
 
// Create pdf file.
LATEXReport.compileLatexToPDF(output+".tex");
 
// Show file
ApplicationLauncher.launchDefaultViewer(output+".pdf");

Since the spectrum is created from a set of MeasureElement objects for the x and y axes, this example shows how to export to Latex a large set of numbers, with their errors correctly formatted.

For good quality scientific tables it is common to show the point representing the decimal position at the same horizontal position for the values in a given column. This can be accomplished using LATEXReport (see the main method in this class), but the correct way is to import the siunitx package and use as column aligment the value S instead of l or r. The second way is straightforward and not directly supported in JPARSEC since siunitx is a package not always found or installed in a normal installation of Latex.

LATEXReport also supports beamer presentations, using the appropriate methods to initialize the header of the file and to create the different slides. There are methods to separate the content using different columns, allowing to produce good quality PDF presentations. An example can be found in the other projects section (http://conga.oan.es/~alonso/doku.php?id=projects) with the star atlas in PDF format available for downloading and created with beamer.

Other

There are other classes that can eventually become useful for different kind of calculations, which doesn't fit into previous classification. Among the classes or possibilities still not commented in any of the sections above, here I will only speak shortly about the Star and CalendarGenericConversion classes. I will mention also the possibility of creating interactive tables with row sorting features in a few lines of code.

General stellar/cosmological calculations

The class jparsec.astronomy.Star contains a large number of simple but useful formulae for calculations involving stellar magnitudes and luminosities, black body radiation, and many other formulae for redshift or cosmological models. Here is an example:

double H0 = 71, WM = 0.27, WV = 0.73, z = 3;
String values[] = new String[] {
	"Cosmology model for H0 = xx.x, WM = xx.xx, WV = xx.xx, and z = xx.",
	"",
	"It is now xx.xxx Gyr since the Big Bang.",
	"The age at redshift z was xx.xxx Gyr.",
	"The light travel time was xx.xxx Gyr.",
	"The comoving radial distance, which goes into Hubble's law, is xxxxx.x Mpc or xxx.xxx Gly.",
	"The comoving volume within redshift z is xxxx.xxx Gpc3.",
	"The angular size distance DA is xxxx.x Mpc or xx.xxxx Gly.",
	"This gives a scale of xx.xxx kpc/\".",
	"The luminosity distance DL is xxxxx.x Mpc or xx.xxx Gly." 
};
System.out.println(ConsoleReport.doubleArrayReport(values, DataSet.addDoubleArray(new double[] {H0,WM, WV, z}, Star.cosmology(H0, WM, WV, z))));
System.out.println("Input z was "+Star.getRedshiftFromLightTravelTime(H0, WM, WV, Star.cosmology(H0, WM, WV, z)[2]));

Note the useful doubleArrayReport method of the ConsoleReport class, replacing text with the values formatted with the desired number of decimal places. The output is:

Cosmology model for H0 = 71.0, WM = 0.27, WV = 0.73, and z = 3.

It is now 13.666 Gyr since the Big Bang.
The age at redshift z was 2.190 Gyr.
The light travel time was 11.476 Gyr.
The comoving radial distance, which goes into Hubble's law, is 6460.6 Mpc or 21.072 Gly.
The comoving volume within redshift z is 1129.524 Gpc3.
The angular size distance DA is 1615.1 Mpc or 5.2678 Gly.
This gives a scale of 7.830 kpc/".
The luminosity distance DL is 25841.7 Mpc or 84.285 Gly.

Input z was 2.9999993032114554

Calendar conversion

The class CalendarGenericConversion in jparsec.time.calendar package allows easy conversion of dates between 15 calendars that represents the date as a year, a month, and a day. For the rest of calendars, for instance the Chinese one, you have to deal with the specific class in this package that implements that calendar. An example using this generic conversion would be:

int year = 2000, month = 1, day = 1;
CALENDAR input_calendar = CalendarGenericConversion.CALENDAR.GREGORIAN;
CALENDAR output_calendar = CalendarGenericConversion.CALENDAR.ISLAMIC;
 
int date[] = CalendarGenericConversion.GenericConversion(input_calendar, output_calendar, year, month, day);
 
System.out.println(year + "/" + month + "/" + day + " in " + CalendarGenericConversion.CALENDAR_NAMES[input_calendar.ordinal()] + " is ...");
System.out.println(date[0] + "/" + date[1] + "/" + date[2] + " in " + CalendarGenericConversion.CALENDAR_NAMES[output_calendar.ordinal()]);
 
// Now get the next new year in the output calendar
date[0]++;
date[1] = date[2] = 1;
System.out.println("Next new year (" + date[0] + "/" + date[1] + "/" + date[2] + ") in " + CalendarGenericConversion.CALENDAR_NAMES[output_calendar.ordinal()] + " calendar is ...");
 
// And convert it back to the input one
int date_back[] = CalendarGenericConversion.GenericConversion(output_calendar, input_calendar, date[0], date[1], date[2]);
System.out.println(date_back[0] + "/" + date_back[1] + "/" + date_back[2] + " in " + CalendarGenericConversion.CALENDAR_NAMES[input_calendar.ordinal()] + ".");

The output is:

2000/1/1 in Gregorian is ...
1420/9/24 in Islamic (arithmetic)
Next new year (1421/1/1) in Islamic (arithmetic) calendar is ...
2000/4/6 in Gregorian.

(Swing) Tables

From the programming point of view one the best ways to represent information interactively is to show tables with the possibility of sorting the values by each of the columns, changing the column widths and colors, and so on. In Java, Swing is the native library used to create and show dialogs and their contents (buttons, lists, tables, and so on). To manipulate tables is something usually embarrasing, so in JPARSEC there's a class at jparsec.graph.chartRendering.frame.JTableRendering designed to simplify the process of creating tables, and to allow the reuse of many lines of code to create simple interactive tables.

The test directory of JPARSEC at bitbucket (see https://bitbucket.org/talonsoalbi/jparsec/overview) includes a main method with the following example:

String columns[] = new String[] {"c1", "c2", "c3", "c4", "c5"};
boolean editable[] = null; // All false except boolean
Class<?> classes[] = new Class<?>[] {Boolean.class, Integer.class, String.class, String.class, String.class};
String table[][] = new String[11][columns.length];
for (int i=0; i<table.length; i++) {
	for (int j=0; j<table[0].length; j++) {
		table[i][j] = "r"+(i+1)+"c"+(j+1);
	}				
	table[i][0] = "true";
	table[i][1] = ""+(i+1);
}
 
JTableRendering jt = new JTableRendering(columns, classes, editable, table);
jt.setRowColor(4, new String[] {"r1c5", "r3c5", "r5c5"}, new Color[] {Color.RED, Color.GREEN, Color.BLUE});
jt.setColumnWidth(new int[] {30, 30, 50, 100, 30});
 
JFrame frame = new JFrame("My Table");
frame.setPreferredSize(new Dimension(400, 300));
frame.add(new JScrollPane(jt.getComponent()));
frame.pack();
frame.setVisible(true);

This example sets the titles for the different columns, the data type inside each column (used to properly sort its values), and the values in each row and column for the data. In case some data type is set to null the class will try to parse it to an angle or a date, formatted with the methods in classes Functions and TimeElement. Once everything is set the table is created and used in a JFrame (Swing component) to show the table on the screen:


 

Doing science

For scientific research JPARSEC includes some tools to analyze and manipulate spectra and cubes of data, which are very common data in radioastronomy. Both types of data can be analyzed using the tools 30mExplorer and DataCube (GUI tools downloadable from the main JPARSEC page), but here I will show at source level how to do that and also some specific charting possibilities not offered in those tools. The second subsection will show how to use certain models, for instance the RADEX implementation in Java included in JPARSEC, or the PCAElement class to apply the Principal Component Analysis method. Here I will also show the possibility of interoperating with Gildas from Java using the pyGildas extension. The third subsection will show the possibilities of the VO (Virtual Observatory) package included in JPARSEC.

Spectra and cubes

In this section I will explain how to read astronomical spectra, images, and cubes of data, and how to treat with them and analyse the data.

Reading images and cubes

Images and cubes in Gildas format are written as files with .gdf and .lmv extensions. This format is natively supported in JPARSEC with the LMVCube class:

LMVCube lmv = new LMVCube(myLMVfile);

There is also the possibility of generating a LMVCube object by providing all the required fields in the constructor, including the cube of data itself. This flexibility allows to create cubes from data contained in external files with easy, to write them later to lmv using the method write(path). Once read, the lmv object allow access to different fields containing the header information about the source.

The implementation in LMVCube contains several getCubeData methods that allow to read the entire cube at once, to read it with a given maximum resolution for each axis (RA, DEC, and velocity), or to read a given velocity plane only. By using these methods with care it is possible to read very big files without out of memory errors. In case the cube is not very big, it is recommended to call setCubeData(getCubeData()) to set the data to a fixed array, so that the lmv file will not be read again and hence all reading operations will be much faster. LMVCube provides a lot of methods to scale intensity, rescale the cube of data, flip axes, obtain WCS coordinates, convolution, return strips, get integrated intensity, and create charts.

Reading spectra

Spectra in Gildas format (.30m files) can be read using Gildas30m class. The first thing to do is to retrieve a list of spectrums in the file with getListOfSpectrums. This method returns a list of integer values identifying the scan number of each spectrum in the file. The getSpectrum method will return a spectrum for a given scan number, so the first spectrum in the file can be returned using getSpectrum(list[0]). The spectrum returned is a Spectrum30m object, that can be used to return the header for the observation (getHeader method) or the complete list of parameters and keys associated to the observation (getKeys and getValues methods). In the following example all this is put into code, including a chart at the end of the first spectrum in a .30m file.

String path = "/home/alonso/reduccion/2006/hot_cores/n7129/n7129.30m";
Gildas30m g30m = new Gildas30m(path);
int list[] = g30m.getListOfSpectrums(true);
 
System.out.println("List of scans (index position, scan number)");
for (int i=0; i<list.length; i++)
{
	System.out.println(i+" = "+list[i]);
}
 
int index = 0; // Take first spectrum
Spectrum30m s = g30m.getSpectrum(list[index]);
String keys[] = s.getKeys();
Parameter p[] = s.getValues();
 
System.out.println();
System.out.println("List of keys (key name, value, description)");
for (int i=0; i<keys.length; i++)
{
	System.out.println(keys[i] +" = "+p[i].value+" ("+p[i].description+")");
}
 
// Header
SpectrumHeader30m sh = s.getHeader();
Parameter header[] = (Parameter[]) sh.getHeaderParameters();
 
System.out.println();
System.out.println("List of header parameters (description, value)");
for (int i=0; i<header.length; i++)
{
	System.out.println(header[i].description+" = "+header[i].value);
}
 
// Create and show a chart
CreateChart ch = s.getChart(400, 400, XUNIT.VELOCITY_KMS);
ch.showChartInJFreeChartPanel();

The output of the execution of this example into the console is also here.

List of scans (index position, scan number)
0 = 4188
1 = 4189
2 = 4190
3 = 4191
4 = 4192
5 = 4248
6 = 4249
7 = 4250
8 = 4251
9 = 4252
10 = 4278
11 = 4279
12 = 4280
13 = 4281
14 = 4282
15 = 4308
16 = 4309
17 = 4310
18 = 4311
19 = 4312
20 = 5795
21 = 5792
22 = 5796
23 = 5793
24 = 5797
25 = 5794
26 = 6741
27 = 6742
28 = 6743
29 = 6744
30 = 6737
31 = 6739
32 = 6740

List of keys (key name, value, description)
altitud = 2850.0 (Site elevation)
azimu = 6.5527854 (Azimuth)
bad = -1000.0 (Blanking value)
beameff = 0.743 (Beam efficiency)
beta = 1.1529043912887573 (Beta)
betaoff = 0.0 (Offset in beta)
count1 = 4.96203968E8 (Power of atmosphere)
count2 = 4.96203968E8 (Power of chopper)
count3 = 4.96203968E8 (Power of cold)
elevat = 0.30573028 (Elevation)
epoch = 2000.0 (Epoch of coordinates)
factor = 415.94904 (Applied calibration factor)
forweff = 0.95 (Forward efficiency)
freqoff = 0.0 (Frequency offset)
fresol = 0.0390625 (Frequency resolution)
gain_im = 0.0032 (Image/Signal gain ratio)
h2omm = 7.443017 (mm of water vapor)
image = 115354.96102801018 (Image frequency)
integ = 1800.0 (Integration time)
lambda = 5.685533046722412 (Lambda)
lamboff = 0.0 (Offset in lambda)
lat = 0.0 (Latitude of the observatory)
latoff = 0.0 (Latitude offset for sky measurement)
line = C17O(1-0)    (Name of the line)
lon = 0.0 (Longitude of the observatory)
lonoff = 1.9463697E23 (Longitude offset for sky measurement)
lsttim = 3.2216349802493487 (LST of observation)
mode = 1 (Calibration mode)
nchan = 1793 (Number of channels)
pamb = 727.1604 (Ambient pressure)
proj = 7 (Projection system)
refchan = 900.2000122070312 (Reference channel)
rfreq = 112359.27699999999 (Rest Frequency)
sigma = 0.09761541336774826 (RMS of the spectrum)
source = N7129S       (Source of the spectrum)
tamb = 282.42038 (Ambient temperature)
tatmsig = 265.79092 (Atmosphere temperature in signal band)
tau = 0.16619755 (Opacity)
tauima = 0.30409575 (Opacity in image band)
tausig = 0.16619755 (Opacity in signal band)
tchop = 299.2 (Chopper temperature)
tcold = 80.8 (Cold temperature)
trec = 264.81064 (Receiver temperature)
tsys = 497.19025 (System temperature)
uttime = 5.119034763262699 (UT of observation)
veltype = 1 (Type of velocity)
voff = -10.0 (Velocity at reference channel)
vres = -0.10422497 (Velocity resolution)

List of header parameters (description, value)
Observation number = 4188
Number of block = 99
Version of the spectrum = 2
Source of the spectrum = N7129S      
Name of the line = C17O(1-0)   
Backend of the telescope = 30M-V01-A100
Date of observation = -7388
Date of reduction = -7388
Offset on X = 0.0
Offset on Y = 0.0
Type of coordinates = 2
Type of data = 0
Quality of data = 0
Scan number = 5281
Position angle = 0.0

Spectrum30m contains methods to manipulate the data in the spectrum and to transform between different 'coordinates' in it: frequency, velocity, channel number, and image frequency. For velocity it is possible to correct it for relativistic effects, so that the width of the channel changes with frequency, or not. This will match the values of velocity returned in recent or old versions of Gildas. JPARSEC support both new and old versions of .30m files, including reading/writting those with thousands of channels. You should not find out of memory errors when reading large files, since Java's RandomAccessFile is used instead of BufferedReader, which stores everything in memory producing out of memory errors.

A possible situation is when you have a txt file in some format with the velocities, frequencies, and intensities of the spectrum, for instance in km/s, GHz, and K, as three columns separated by a comma. In case you want to transform this ASCII file to a Spectrum30m object you can use a program like the following one. For a more complete example see the class jparsec.research.Create30mFilesFromHerschel located in the examples.

String myTXTfile = "..."; // Path to the txt file
String molecule = "CO-17"; // molecule name as given in the catalog
int tranIndex = 1; // 0 for the 1-0 line, 1 for the 2-1 line, and so on
double sourceVel = 11.6; 	// This is used to correct the frequencies for source velocity, in case input file 
				// provides the values for a 0 velocity source (as HIPE does)
 
String data[] = DataSet.arrayListToStringArray(ReadFile.readAnyExternalFile(myTXTfile));
int skipHeader = 4; // Skip first 4 lines
int size = data.length - skipHeader;
FluxElement fluxes[] = new FluxElement[size];
double refVel = 0, velRes = 0, refFreq = 0;
for (int j=skipHeader; j<data.length; j++)
{
	// Format of file is velocity, frequency, Tmb, separated by comma
	double vel = Double.parseDouble(FileIO.getField(1, data[j], ",", false)); // km/s
	double freq = Double.parseDouble(FileIO.getField(2, data[j], ",", false)); // GHz
	double tmb = Double.parseDouble(FileIO.getField(3, data[j], ",", false)); // K
 
	// Construct the pair of measures for x axis (channel number) and y axis (K), and use
	// them for the flux element object
	MeasureElement mx = new MeasureElement(j-skipHeader+1, 0, null);
	MeasureElement my = new MeasureElement(tmb, 0, MeasureElement.UNIT_Y_K);
	fluxes[j-skipHeader] = new FluxElement(mx, my);
 
	// Just to obtain reference velocity and frequency, and velocity resolution, required later.
	if (j == skipHeader) refVel = vel;
	if (j == skipHeader) refFreq = freq * 1000.0;
	if (j == skipHeader+1) velRes = vel-refVel;
}
 
// Now construct the generic spectrum object from the set of fluxes, and set
// other properties on it
Spectrum sp = new Spectrum(fluxes);
 
// Now we need the rest frequency of the line, so it is necessary to read the catalog
int maxT = 1500;
boolean useJPL = true;
String m = CatalogRead.getMoleculeFileName(CatalogRead.getMolecule(molecule, useJPL));
String tran[] = null;
if (useJPL) {
	tran = DataSet.arrayListToStringArray(CatalogRead.readJPLtransitions(m, maxT));				
} else {
	tran = DataSet.arrayListToStringArray(CatalogRead.readCOLOGNEtransitions(m, maxT));
}
 
// In case of N2H+ eliminate 1st and 3rd hyperfine compounds and keep only the brightest one,
// required to maintain a correct match between index of the array and transition 1-0, 2-1, 
// and so on.
if (molecule.startsWith("NNH+") || molecule.startsWith("N2H+")) {
	tran = DataSet.eliminateRowFromTable(tran, 1);
	tran = DataSet.eliminateRowFromTable(tran, 2);
}
ReadFormat rf = new ReadFormat(CatalogRead.JPL_COLOGNE_FORMAT);
double freq = rf.readDouble(tran[tranIndex], CatalogRead.JPL_COLOGNE_FORMAT_FIELDS[CatalogRead.JPL_COLOGNE_FORMAT_FREQUENCY_INDEX]); // MHz
 
// Correct frequency values for source velocity, since Herschel gives 
// the frequency for a 0 velocity source.
double delta = (0 - sourceVel) / velRes;
double fres = - velRes * freq / (Constant.SPEED_OF_LIGHT * 0.001);
refFreq += delta * fres;
 
// Set fields in spectrum object
sp.line = molecule+" "+(tranIndex+1)+"-"+(tranIndex);
sp.referenceVelocity = refVel;
sp.velocityResolution = velRes;
sp.referenceChannel = 1;
sp.referenceFrequency = refFreq;
sp.observationNumber = 0;
sp.offsetX = sp.offsetY = 0;
sp.backend = "HERSCHEL";
sp.source = "OMC2-FIR4";
sp.epochJD = 2451545.0;
sp.scanNumber = 1;
 
// Now we have set all required fields for the generic
// spectrum object, we can get the 30m spectrum object
Spectrum30m spectrum = new Spectrum30m(sp);

The Spectrum30m object 'spectrum' created at the end can be shown in the screen, exported as .30m or .fits, or whatever you want using the corresponding methods. An interactive option to transform a table into a .30m file is given in one of the options of the Tools menu of the 30mExplorer tool, one of the GUI tools developed with JPARSEC and freely available.

Reading OTF (on the fly) maps

To analyze OTF maps with JPARSEC they should be converted from the set of .30m observations to a .lmv file. An example showing this conversion is located at jparsec.research.MapOnTheFly_toGildas_LMV, inside the set of examples. It also shows how to do basic processing of each scan, passing a baseline, and how to launch one of the analysis tools, a GridSpectrumVISADChart chart.

Analyzing spectra

Analyzing spectra can be considered mainly as decomposing an spectra in Gaussian components and identifying lines. For both tasks it is strongly recommended to use the 30mExplorer tool distributed with JPARSEC, that includes a GUI to do these and other spectra reduction/analysis tasks. Here I will show only how to do decomposition and line identification using source code.

Both tasks are done using the ProcessSpectrum class located at jparsec.astrophysics.gildas package. Suppose you have a file called out.30m containing one spectrum. The basic code for Gaussian decomposition is:

String file = "/home/alonso/colaboraciones/Rafael/2013/largeProgram/out.30m";
 
Gildas30m g30m = new Gildas30m(file);
int list[] = g30m.getListOfSpectrums(true);
Spectrum30m sp = g30m.getSpectrum(list[0]);
 
ProcessSpectrum.TIMES_SIGMA = 5.0;
ProcessSpectrum ps = new ProcessSpectrum(sp);
SpectrumLine[] lines = ProcessSpectrum.reduceSpectrum(sp);
XUNIT xUnit = XUNIT.VELOCITY_KMS;
jparsec.graph.CreateChart ch = sp.getChart(500, 500, xUnit);
if (lines != null) {
	System.out.println("Found "+lines.length+" lines");
	for (int i=0; i<lines.length; i++) {
		System.out.println(i+" ("+lines[i].minChannel+"-"+lines[i].maxChannel+"): vel = "+lines[i].vel+" km/s, peak = "+lines[i].peakT+" K, width = "+lines[i].width+" km/s, area = "+lines[i].area+" K km/s");
		ChartSeriesElement series = ps.getGaussianFit(lines[i].getGaussianParameters(), xUnit);
		series.legend = "Fit to line "+(i+1);
		ch.addSeries(series);
	}
}
ch.showChartInJFreeChartPanel();

Previous code will search for all Gaussian components above 5 sigma in the input spectrum. Default value is 3 sigma, but this ussually produces too much components. This example also creates a chart showing all the lines fitted in the spectrum. The console output and the chart for my spectrum are:

Found 52 lines
0 (2512-2532): vel = 3850.9288169836177 km/s, peak = 2.756570010650598 K, width = 5.507499844690069 km/s, area = 16.16053487524979 K km/s
1 (8861-8886): vel = 57.61158783131574 km/s, peak = 2.30373327211382 K, width = 5.107885362668338 km/s, area = 12.525802123171148 K km/s
2 (2497-2516): vel = 3857.3808702041156 km/s, peak = 2.2370176586151174 K, width = 3.876915083532543 km/s, area = 9.231832395259211 K km/s
3 (15608-15635): vel = -3976.478544464152 km/s, peak = 1.6405933280231635 K, width = 5.811966955014303 km/s, area = 10.149772023403 K km/s
4 (8861-8870): vel = 59.89731735832962 km/s, peak = 0.6676027470216196 K, width = 1.5679173003474367 km/s, area = 1.1142264848841783 K km/s
5 (2529-2551): vel = 3840.8250710691564 km/s, peak = 0.6015972141789907 K, width = 5.4268012088774435 km/s, area = 3.4752170934528404 K km/s
6 (8860-8865): vel = 61.399308743308836 km/s, peak = -2.933512983347183 K, width = 0.3294546888284634 km/s, area = -1.0287643773735013 K km/s
7 (15605-15619): vel = -3972.42365145907 km/s, peak = -0.3990551149193257 K, width = 1.7446896900952378 km/s, area = -0.7411110465439701 K km/s
8 (19586-19611): vel = -6352.917488445518 km/s, peak = 0.34073170369856987 K, width = 6.859756401629953 km/s, area = 2.488017602324485 K km/s
9 (15613-15623): vel = -3975.0219626955145 km/s, peak = 0.36261560251352404 K, width = 1.9311535885254714 km/s, area = 0.7454105110866064 K km/s
10 (2513-2523): vel = 3852.443519971743 km/s, peak = 0.5563465299901357 K, width = 2.0012815444405194 km/s, area = 1.1851840117777046 K km/s
11 (8858-8866): vel = 62.34256034362199 km/s, peak = -0.27532088188350784 K, width = 1.4477338334666199 km/s, area = -0.42428735243968885 K km/s
12 (8859-8866): vel = 61.0607738928178 km/s, peak = 0.4832606190315401 K, width = 2.3974414385565996 km/s, area = 1.2332798154363454 K km/s
13 (8865-8877): vel = 57.191683493890345 km/s, peak = -0.4535873009844655 K, width = 1.9719172144852228 km/s, area = -0.9520982692119876 K km/s
14 (2510-2518): vel = 3853.643437532535 km/s, peak = -0.11533889796769639 K, width = 1.5490480785922398 km/s, area = -0.19018353043335834 K km/s
15 (6819-6843): vel = 1276.2537009999683 km/s, peak = 0.2554550310656791 K, width = 6.178760600453398 km/s, area = 1.6801499332887853 K km/s
16 (15617-15630): vel = -3981.162161923536 km/s, peak = 0.1486723008138999 K, width = 1.8105894275441434 km/s, area = 0.2865380181579648 K km/s
17 (15617-15628): vel = -3977.584682861641 km/s, peak = -0.2677551463758094 K, width = 2.1603416502167843 km/s, area = -0.6157330647727969 K km/s
18 (8860-8865): vel = 61.28846685400889 km/s, peak = -0.959307336577451 K, width = 0.16826668644624815 km/s, area = -0.17182569871284722 K km/s
19 (2520-2532): vel = 3845.951303277676 km/s, peak = 0.4101031365045481 K, width = 1.5220522336741038 km/s, area = 0.664438605009911 K km/s
20 (2517-2527): vel = 3847.3094460425123 km/s, peak = 0.36934697382439596 K, width = 1.4193878811105862 km/s, area = 0.5580432355174016 K km/s
21 (8859-8864): vel = 61.22638033350529 km/s, peak = 0.08909182749404372 K, width = 1.3856874687713676 km/s, area = 0.1314121035300178 K km/s
22 (8870-8886): vel = 52.1671291914779 km/s, peak = 0.2987705709040187 K, width = 4.998011873398536 km/s, area = 1.5895248087954108 K km/s
23 (827-851): vel = 4854.294660644765 km/s, peak = 0.12392428233524437 K, width = 6.220716961060215 km/s, area = 0.8205953739425048 K km/s
24 (2507-2515): vel = 3855.3553900242273 km/s, peak = 0.11088622370285783 K, width = 1.4671933181472332 km/s, area = 0.17317976429103007 K km/s
25 (15626-15643): vel = -3984.1665612423376 km/s, peak = 0.13876025728823602 K, width = 6.628904028746144 km/s, area = 0.9791270257457164 K km/s
26 (2584-2601): vel = 3807.1735976241657 km/s, peak = 0.14692258898161104 K, width = 4.8590308011425245 km/s, area = 0.7599244797198361 K km/s
27 (2502-2510): vel = 3860.365139854197 km/s, peak = -0.2190709175072614 K, width = 1.4277090238645511 km/s, area = -0.33293284488809355 K km/s
28 (2503-2510): vel = 3858.7494678239136 km/s, peak = 0.20282162106965182 K, width = 1.3097366135147144 km/s, area = 0.2827681093250073 K km/s
29 (15612-15618): vel = -3973.212726298198 km/s, peak = -0.1828215482310059 K, width = 0.4542240191692601 km/s, area = -0.0883954046864939 K km/s
30 (2506-2512): vel = 3856.6884751243283 km/s, peak = -0.14157939711721487 K, width = 1.4808628474408525 km/s, area = -0.22317580311925073 K km/s
31 (8852-8863): vel = 64.30362115540846 km/s, peak = -0.09279649914485644 K, width = 1.4905446689571105 km/s, area = -0.1472342329119669 K km/s
32 (7498-7519): vel = 871.1482456835365 km/s, peak = 0.08073042778025251 K, width = 5.201123291241983 km/s, area = 0.44695789464625174 K km/s
33 (2529-2540): vel = 3839.88232727288 km/s, peak = -0.06260511359386252 K, width = 1.7281100224493349 km/s, area = -0.1151631159537177 K km/s
34 (2529-2538): vel = 3842.379209996136 km/s, peak = 0.09177056027824708 K, width = 1.5524079451695938 km/s, area = 0.15164966319605 K km/s
35 (8863-8873): vel = 55.983315364096136 km/s, peak = -0.12759938142259702 K, width = 1.0417172565636201 km/s, area = -0.1414915934981324 K km/s
36 (8863-8872): vel = 58.12687354551372 km/s, peak = 0.00166559208378546 K, width = 6.310516475496903 km/s, area = 0.011188342771253622 K km/s
37 (13376-13401): vel = -2640.4511296650903 km/s, peak = 0.0554925506811867 K, width = 5.158421995515191 km/s, area = 0.3047079358158977 K km/s
38 (15603-15615): vel = -3970.599621737508 km/s, peak = -0.07099065558690296 K, width = 1.935678336761315 km/s, area = -0.146273814386266 K km/s
39 (8871-8880): vel = 53.658752415730156 km/s, peak = 0.11601290837734012 K, width = 1.607154589359765 km/s, area = 0.1984705976131765 K km/s
40 (8879-8905): vel = 46.53723913527 km/s, peak = 0.07396593070603337 K, width = 5.689651352741548 km/s, area = 0.4479706812155555 K km/s
41 (13566-13588): vel = -2755.346478631139 km/s, peak = 0.0609556968942811 K, width = 4.597140335174952 km/s, area = 0.2982869630629043 K km/s
42 (2524-2535): vel = 3845.9576966254917 km/s, peak = 0.3210227762908429 K, width = 0.7063833535547676 km/s, area = 0.2413840183111223 K km/s
43 (5376-5397): vel = 2137.5488955374826 km/s, peak = 0.04552761394534797 K, width = 5.858826958042293 km/s, area = 0.28393424230254133 K km/s
44 (2539-2570): vel = 3836.545526291441 km/s, peak = 0.07742285067676473 K, width = 2.279579695324295 km/s, area = 0.18786944307891795 K km/s
45 (19582-19597): vel = -6348.561903635197 km/s, peak = -0.09142935994943517 K, width = 2.659004970168728 km/s, area = -0.2587837719846533 K km/s
46 (19582-19595): vel = -6346.632505994318 km/s, peak = -0.0230656886115275 K, width = 1.8174094705394719 km/s, area = -0.044622245548025456 K km/s
47 (15610-15622): vel = -3972.140270180332 km/s, peak = 0.32272791539600815 K, width = 0.35698744884880046 km/s, area = 0.12263704858400273 K km/s
48 (832-841): vel = 4856.940755144108 km/s, peak = 0.07938705514727758 K, width = 1.5892062131243765 km/s, area = 0.13429571525661865 K km/s
49 (6818-6827): vel = 1280.100473093503 km/s, peak = -0.07885923146337577 K, width = 2.1868666136747685 km/s, area = -0.183572255835978 K km/s
50 (15612-15622): vel = -3974.4733656254502 km/s, peak = 0.012929505773035808 K, width = 2.2574959684135667 km/s, area = 0.031069990320812174 K km/s
51 (8875-8887): vel = 51.05871096457421 km/s, peak = -0.0892720248345044 K, width = 2.3848575096243443 km/s, area = -0.22662615552219922 K km/s


 

The previous chart shows just a detail with some of the components of the Gaussian decomposition. As you see, even using 5 sigma the number of components is ussually excesive. Using 5 sigma means the entire spectrum will be fitted until residuals are below 5 sigma in all velocities, and hence when the lines are asymmetric the number of components grow. Decomposition, however, is perfect for all lines showing one or two components, and usually correct also for three components. In practice it is necessary to iterate the decomposition by substracting a different component from the spectrum and fitting the other ones, so that the decomposition will converge to a set of Gaussians that minimizes the residuals. One this is done, Regression class can be used to refit the number of Gaussians found simultaneously, although the results will be identical, maybe some variation in the errors can result.

The SpectrumLine array called lines contains the list of fitted lines, including the frequencies, velocities, areas, … and their errors. In your program you can use LATEXReport to generate a complete table including these values as formatted MeasureElement objects. To identify a given line you only need to take the frequency and call the appropriate method of ProcessSpectrum:

double freq = 102501, width = 10, maxT = 30000, maxrint = -7;
boolean jpl = true, splatalogue = true, onlyAtm = false, onlyLessProbable = false;
String out[] = ProcessSpectrum.identifyLine(freq, width, maxT, maxrint, jpl, splatalogue, onlyAtm, onlyLessProbable);
ConsoleReport.stringArrayReport(out);

Previous example will output to the console all lines less than 5 MHz from 102501 MHz, with intensity in the catalog above -7 (in log units), and temperature of the upper level below 30000 K. The query will use splatalogue (Internet connection required), including atmosferic lines. The result is:

^{13}CH_{3}CH_{2}CN:  102496.89630 (0.0272)  |  -5.32500 (CDMS)  |  -  |  166.94000  |  171.85907  |  33( 3,31)-32( 4,28)  |  ^{13}CH_{3}CH_{2}CN 33(3,31)-32(4,28)   freq=102496.89630
g-CH_{3}CH_{2}OH:  102498.89100 (0.05)  |  -6.00340 (JPL)  |  -  |  59.19390  |  64.11307  |   6( 3, 3)- 7( 2, 5), v_{t}= 1- 0  |  g-CH_{3}CH_{2}OH 6(3, 3)- 7(2, 5), v_{t}=1- 0   freq=102498.89100
CH_{3}CCH v = 0:  102499.01870 (0)  |  -5.33630 (CDMS)  |  -  |  134.06920  |  138.98837  |   6( 5)- 5( 5)  |  CH_{3}CCH v =0 6(5)- 5(5)   freq=102499.01870
CH_{3}CCH v = 0:  102499.11000 (0.09)  |  -5.15850 (JPL)  |  -  |  133.67090  |  138.59009  |  6(5)-5(5)  |  CH_{3}CCH v =0 6(5)-5(5)   freq=102499.11000
H_{2}CCCHCN:  102500.43330 (15.3501)  |  -6.00570 (CDMS)  |  -  |  500.00050  |  504.91974  |  75( 4,72)-74( 5,69)  |  H_{2}CCCHCN 75(4,72)-74(5,69)   freq=102500.43330
CH_{3}OCHO v=1:  102503.10500 (0.01)  |  -5.15540 (JPL)  |  -  |  143.66150  |  148.58087  |   8( 2, 6)- 7( 2, 5) E  |  CH_{3}OCHO v=1 8(2, 6)- 7(2, 5) E   freq=102503.10500
g'Ga-(CH_{2}OH)_{2}:  102503.67630 (0.0052)  |  -6.60020 (CDMS)  |  -  |  131.08290  |  136.0023  |  27( 4,24) v= 1  -  26( 5,21) v= 1  |  g'Ga-(CH_{2}OH)_{2} 27(4,24) v=1 - 26(5,21) v=1   freq=102503.67630
CH_{2}CHCN v= 0:  102505.49000 (0.1)  |  -5.81030 (JPL)  |  -  |  326.90890  |  331.8284  |  44( 3,41)-44( 3,42)  |  CH_{2}CHCN v=0 44(3,41)-44(3,42)   freq=102505.49000
CH_{2}CHCN v= 0:  102505.49000 (0.05)  |  -5.89290 (CDMS)  |  -  |  326.90892  |  331.8284  |  44( 3,41)-44( 3,42)  |  CH_{2}CHCN v=0 44(3,41)-44(3,42)   freq=102505.49000
c-HCCCH  v=0 :  102505.94530 (0.0145)  |  -3.19940 (JPL)  |  -  |  91.59750  |  96.51701  |   9( 6, 3)- 9( 5, 4)  |  c-HCCCH v=0  9(6, 3)- 9(5, 4)   freq=102505.94530
c-HCCCH  v=0 :  102505.95630 (0.0036)  |  -3.22510 (CDMS)  |  -  |  91.59750  |  96.51701  |   9( 6, 3)- 9( 5, 4)  |  c-HCCCH v=0  9(6, 3)- 9(5, 4)   freq=102505.95630

Of course, to select which of them is the one observed is a task for the astronomer.

To present spectra in the screen it is possible to use the jparsec.graph.SpectraChart class. It is not as complete as the 30mExplorer tools, but can be used to show spectra easily. SpectraChart is also allowed in the JPARSEC slide show tools, so that it can be used in presentations. Here is an example code:

// The set of 30m files
String files[] = new String[] {
	"/home/alonso/reduccion/2006/hot_cores/cb3/fich_cb3_map.30m",
	"/home/alonso/reduccion/2006/hot_cores/cepe/fich_cepe.30m",
	"/home/alonso/reduccion/2006/hot_cores/ic1396/fich_ic1396.30m",
	"/home/alonso/reduccion/2006/hot_cores/serp-firs1/fich_serp-firs1.30m",
	"/home/alonso/reduccion/2011/observacionesSep2010/PVCEP_reduced.30m",
	"/home/alonso/reduccion/2011/observacionesSep2010/HKORI_reduced.30m"
};
int w = 630, h = 430;
final SpectraChart s = new SpectraChart(files, w, h, 12, 0, null, null, false, false);
 
// Show into a JFrame allowing interactive resizing			
final JFrame f = new JFrame();
//f.setUndecorated(true);
f.add(s.getComponent());
f.setPreferredSize(new Dimension(w, h));
f.pack();
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.addComponentListener(new ComponentAdapter() {
	@Override
	public void componentResized(ComponentEvent e) {
		s.getComponent().setSize(f.getSize());
	}
});

The output panel is (with language set to Spanish):


 

Analyzing images and cubes

There are two main components to analyze cubes of data, very similar to the ones available in the DataCube GUI tool. The classes are jparsec.graph.VISADChart and jparsec.graph.GridSpectrumVISADChart. The first one combines an SGT chart showing a given velocity plane or the integrated intensity with a VISAD chart allowing to cut the cube in 3d. The second one adds a panel showing the spectrum towards a given direction.

Here is an example of a VISADChart panel attached to a JFrame:

int w = 640;
final VISADChart v = new VISADChart("/home/alonso/reduccion/2010/reajusteRMonConDataCube/rmon_K.lmv", 0, 0, w);
 
final JFrame frame = new JFrame("Example");
final JPanel panel = v.getComponent();
frame.add(panel);
frame.pack();
frame.setResizable(true);
frame.setVisible(true);
frame.setSize(new Dimension(w, w));
frame.addComponentListener(new ComponentAdapter() {
	@Override
	public void componentResized(ComponentEvent e) {
		panel.setBounds(0, 0, frame.getWidth(), frame.getHeight());
	}
});
panel.addComponentListener(new ComponentAdapter() {
	@Override
	public void componentResized(ComponentEvent e) {
		v.update();
	}
});

With the cube mentioned in the path the output is the following interactive component:


 

Here is the example for the other component, including the spectrum:

 


 

Another analysis tool is the CreateVISADIsoSurfaceChart class, that uses the VisAD library to show two cubes (the idea is to show one observation and a model, or maybe two observations with the emission for different molecules) in such a way that the surface with a given intensity level is shown instead of the emission itself. A bar at the bottom allows to modify the intensity of the iso surface so that you can see the surface/s emitting more or less emission in both cubes. The DataCube GUI tool allows to do this directly for the observation and the model, but with Java code it is also quite easy:

String file = "/home/alonso/reduccion/2010/reajusteRMonConDataCube/rmon_K.lmv";
String file2 = "/home/alonso/reduccion/2010/reajusteRMonConDataCube/rmon_modelo_K.lmv";
LMVCube lmv = new LMVCube(file);
LMVCube lmv2 = new LMVCube(file2);
 
// Limit the size of both cubes to the same values
int maxS = 12;
lmv.setCubeData(lmv.getCubeData(maxS, maxS, maxS));
lmv2.setCubeData(lmv2.getCubeData(maxS, maxS, maxS));
maxS = lmv.axis3Dim;
lmv.resample(maxS, maxS, false);
lmv2.resample(maxS, maxS, false);
System.out.println(lmv.axis3Dim);
 
// Just for testing: replace the second cube with a sphere
int s = lmv2.getCubeData().length;
float synthetic[][][] = new float[s][s][s];
for (int i=0; i<synthetic.length; i++) { // vel
	for (int j=0; j<synthetic[0].length; j++) { // ra
  		for (int k=0; k<synthetic[0][0].length; k++) { // dec
  			double dx = synthetic.length / 2.0 - i;
  			double dy = synthetic[0].length / 2.0 - j;
  			double dz = synthetic[0][0].length / 2.0 - k;
  			synthetic[i][j][k] = maxS / 2f - (float) Math.sqrt(dx * dx + dy * dy + dz * dz);
  		}
	}
}
lmv2.setCubeData(synthetic);
 
// Create the 2 cube objects
float v0 = lmv.getv0();
float vf = lmv.getvf();
float x0 = (float) (lmv.getx0() * Constant.RAD_TO_ARCSEC);
float xf = (float) (lmv.getxf() * Constant.RAD_TO_ARCSEC);
float y0 = (float) (lmv.gety0() * Constant.RAD_TO_ARCSEC);
float yf = (float) (lmv.getyf() * Constant.RAD_TO_ARCSEC);
VISADCubeElement cube = new VISADCubeElement(lmv.getCubeData(),
	  new float[] {x0, xf, y0, yf, v0, vf},
	  "OFFSET_RA", VISADCubeElement.UNIT.ARCSEC,
	  "OFFSET_DEC", VISADCubeElement.UNIT.ARCSEC,
	  "Velocity", VISADCubeElement.UNIT.KILOMETER_PER_SECOND,
	  "FLUX", VISADCubeElement.UNIT.KELVIN);
VISADCubeElement cube2 = new VISADCubeElement(lmv2.getCubeData(),
	  new float[] {x0, xf, y0, yf, v0, vf},
	  "OFFSET_RA", VISADCubeElement.UNIT.ARCSEC,
	  "OFFSET_DEC", VISADCubeElement.UNIT.ARCSEC,
	  "Velocity", VISADCubeElement.UNIT.KILOMETER_PER_SECOND,
	  "FLUX", VISADCubeElement.UNIT.KELVIN);
 
// Create the chart and show it
CreateVISADIsoSurfaceChart vc = new CreateVISADIsoSurfaceChart(cube, cube2, lmv.line+" datacube of "+lmv.sourceName);
vc.show(800, 600);

In the previous code the second cube, the one representing the model, is later replaced by a sphere so that intensity is maximum at the center. This is just to show more clearly how VisAD manages a perfect, regular emission profile. The output when using an observation and a model is something like this figure:


 

For .fits files the FitsIO class can be used. Currently there's support for reading and writting spectra and images in .fits format, but not for cubes of data. An illustrative example could be to read a .fits image, show it into a panel using the SGT library (with mouse zoom support), and show into the console the coordinates and flux at the mouse position. This is done in jparsec.examples.advanced.FitsWCS example.

Models

In this section I will talk about specific models available inside the JPARSEC library. RADEX and dust opacity are some examples. I will also talk about specific utilities that can be very useful if they are called from other models. One example with which I will start in the next subsection is the PyGildas extension.

PyGildas extension

PyGildas extension is a powerful tool to interact between Python and Gildas, supported since Gildas dec08b version, in both Linux and Mac. Since Python can be called from Java (without Jython I mean), it can be used to control Gildas from Java code. This can be used to execute scripts and create .ps charts. Imagine you develope a model and you need to create .ps charts, or to import the output of it in Gildas to execute a given script to return a given value, something like a rms of the fit. In case you need to call Gildas a lot, you can develope the model with JPARSEC and call Gildas using the jparsec.astrophysics.gildas.PyGildas class. You only need to setup your environment (with the contents of the GAG_ROOT_DIR and the GAG_EXEC_SYSTEM variables, or the path to the file, maybe a .bashrc, where they are defined) and to provide the script to execute in an instance of PyGildas. It is possible to control class (using pyclass in the instance of pyGildas), greg (pygreg), mapping (pymapping), astro (pyastro), or to use the sic (pysic) interface. Here is an example:

pyGildas gildas = new pyGildas(
		"/home/gildas/gildas-exe-jan13a", "pc-debian6-gfortran", // OAN desktop
		"/home/alonso/reduccion/2010/reajusteRMonConDataCube", "pyclass");
ConsoleReport.stringArrayReport(gildas.script);
 
String script[] = DataSet.arrayListToStringArray(ReadFile.readAnyExternalFile("/home/alonso/reduccion/2010/reajusteRMonConDataCube/fitDisk.class"));
String parameters[] = null;
String variables[] = new String[] {"rms"};
String out[] = gildas.executeScript(script, parameters, variables);
 
if (out == null) {
	System.out.println("AN ERROR OCCURED");
} else {
	System.out.println("OUTPUT");
	System.out.println(gildas.getConsoleOutput());
	System.out.println("ERROR");
	System.out.println(gildas.getErrorOutput());
	System.out.println("EXECUTED SCRIPT");
	ConsoleReport.stringArrayReport(gildas.getLastScriptExecuted());
	if (variables != null) {
		System.out.println("OUTPUT VARIABLES");
		for (int i=0; i<variables.length; i++)
		{
			System.out.println(variables[i]+" = "+gildas.getVariableValue(variables[i]));
		}
	}
}

The output of this code is:

PYGILDAS TEST
#!/bin/bash
export GAG_ROOT_DIR=/home/gildas/gildas-exe-jan13a
export GAG_EXEC_SYSTEM=pc-debian6-gfortran
source $GAG_ROOT_DIR/etc/bash_profile
cd /home/alonso/reduccion/2010/reajusteRMonConDataCube
python <<-exit
import pyclass
pyclass.setgdict(globals())
OUTPUT
W-GLOBAL>SIC,  Session is not interactive, EDIT and MEMORY off
PygildasWarning: 'lambda' is a Python command (could not assign).
Importing all SIC variables into Python...
PygildasWarning: 'lambda' is a Python command (could not assign).
... done.
I-GDF,  Converting from EEEI to IEEE
I-CONVERT,  File is  [EEEI to IEEE]
I-INPUT,  rmon.30m successfully opened
I-FIND,  116 observations found
W-FIND,  Current index contains old format data
...
...
...
RMS             =    6.847298                ! Real    GLOBAL     
<SicVar instance>
array(6.8472981452941895, dtype=float32)
F-DEALLOCATE_CLASS,  Could not deallocate memory 1

ERROR

EXECUTED SCRIPT
#!/bin/bash
export GAG_ROOT_DIR=/home/gildas/gildas-exe-jan13a
export GAG_EXEC_SYSTEM=pc-debian6-gfortran
source $GAG_ROOT_DIR/etc/bash_profile
cd /home/alonso/reduccion/2010/reajusteRMonConDataCube
python <<-exit
import pyclass
pyclass.setgdict(globals())
pyclass.comm('@.jparsec2')
print rms
exit()
exit
OUTPUT VARIABLES
rms = 6.8472981452941895

Another useful case is the fully automatic reduction of spectral observations. From Java it is possible to do a decomposition of the spectra and to identify the lines, but before that the spectra must be reduced. PyGildas allow to call the methods in class for this purpose from Java, like average or stitch.

Reading JPL/CDMS catalogs

To read JPL or CDMS catalogs is something also very useful in models, specially in radiative transfer ones. The class jparsec.io.CatalogRead allows to easily return molecules and transitions. This simple example will show how it is done:

boolean jpl = true; // To use JPL, false for CDMS
 
ArrayList<String> v = null;
int hcn = 43; // index of HCN in JPL
if (jpl) {
	v = CatalogRead.readJPLcatalog();
} else {
	v = CatalogRead.readCOLOGNEcatalog();
	hcn = 45; // index of HCN in CDMS
}
 
System.out.println(DataSet.arrayListToString(v));
 
System.out.println(v.get(hcn)+":");
String m = CatalogRead.getMoleculeFileName(CatalogRead.getMolecule(v.get(hcn), jpl));
if (jpl) {
	v = CatalogRead.readJPLtransitions(m, 0);
} else {
	v = CatalogRead.readCOLOGNEtransitions(m, 0);					
}
System.out.println(DataSet.arrayListToString(v));

As you see, the program presents a list of all available molecules in the catalog, and then all available transitions for HCN molecule. Here is the output for JPL catalog:

  1001 H-atom            1 0.6021 0.6021 0.6021 0.6021 0.6021 0.6021 0.6021 1
  2001 D-atom            1 0.7782 0.7782 0.7782 0.7782 0.7782 0.7782 0.7782 1
  3001 HD                1 0.7004 0.5864 0.4315 0.1962 0.0406 0.0014 0.0001 2
  4001 H2D+             19 1.8682 1.6944 1.4398 0.9880 0.4916 0.0845 0.0016 1
  7001 Li-6-H           33 1.4493 1.3248 1.1515 0.8603 0.5803 0.3226 0.1138 1
  8001 LiH              40 1.4581 1.3336 1.1602 0.8688 0.5883 0.3297 0.1188 1
  8002 Li-6-D           40 1.6901 1.5631 1.3878 1.0921 0.8029 0.5263 0.2755 1
  9001 LiD              40 1.7060 1.5788 1.4034 1.1075 0.8173 0.5403 0.2876 1
 12001 C-atom            2 0.8939 0.8744 0.8363 0.7295 0.5494 0.3075 0.0962 2
 13001 C-13-atom         7 1.1950 1.1754 1.1374 1.0305 0.8504 0.6085 0.3972 2
 13002 CH              324 2.0822 1.9608 1.7924 1.5175 1.2752 1.0864 0.9523 1
 13003 CH+               9 1.1861 1.0641 0.8942 0.6125 0.3512 0.1342 0.0178 2
 14001 N-atom            2 1.0792 1.0792 1.0792 1.0792 1.0792 1.0792 1.0792 1
 14002 N-atom-D-st       6 1.4700 1.4676 1.4629 1.4495 1.4246 1.3827 1.3247 3
 14003 13CH            648 2.3562 2.2249 2.0368 1.7027 1.3423 0.9177 0.3133 1* 
 14004 CD              188 2.5032 2.3751 2.1934 1.8805 1.5718 1.2928 1.0729 1* 
 15001 NH             1416 2.3736 2.2521 2.0834 1.8051 1.5511 1.3511 1.2637 1*
 16001 O-atom            2 0.8287 0.8010 0.7612 0.7123 0.6996 0.6990 0.6990 2
 17001 OH             1160 1.9111 1.7803 1.6036 1.3570 1.2313 1.2042 1.2022 4*
 17002 NH3             446 2.7627 2.5780 2.3162 1.8725 1.4410 1.0512 0.7322 3
 17003 CH3D            143 2.9068 2.7198 2.4568 2.0093 1.5674 1.1479 0.7752 3* 
 17004 NH3-v2          442 2.7627 2.5780 2.3162 1.8725 1.4410 1.0512 0.7322 1
 17005 OH-v1,2        1765 1.9111 1.7803 1.6036 1.3570 1.2313 1.2042 1.2022 1*
 18001 OD              912 2.3272 2.1858 1.9889 1.6899 1.4899 1.3983 1.3804 1
 18002 N-15-H3         235 2.7603 2.6544 2.3143 1.8734 1.4420 1.0522 0.7336 2
 18003 H2O            3086 2.2507 2.0645 1.8040 1.3649 0.9335 0.4819 0.0994 6* 
 18004 NH2D           5036 3.5787 3.3818 3.1117 2.6378 2.1367 1.6068 1.1219 1
 18005 H2O v2,2v2,v  28832 2.2507 2.0645 1.8040 1.3649 0.9335 0.4819 0.0994 4* 
 18006 13CH3D          143 2.9073 2.7203 2.4573 2.0098 1.5679 1.1483 0.7756 1* 
 19001 HO-18           295 1.9135 1.7826 1.6056 1.3584 1.2318 1.2043 1.2022 2
 19002 HDO            1401 2.1669 1.9803 1.7185 1.2753 0.8421 0.4332 0.1252 3
 19003 H2O-17          404 2.2524 2.0664 1.8059 1.3668 0.9353 0.4835 0.0999 1
 19004 H3O+            304 2.7161 2.5144 2.2407 1.7963 1.4259 1.2018 1.0650 2* 
 19005 H3O+ v1,v3,v   1973 2.7161 2.5144 2.2407 1.7963 1.4259 1.2018 1.0650 1* 
 20001 D2O            1137 3.0190 2.8322 2.5696 2.1242 1.6863 1.2685 0.9367 2
 20002 HF                8 1.0213 0.9009 0.7343 0.4622 0.2212 0.0524 0.0024 1
 20003 H2O-18          726 2.2544 2.0682 1.8076 1.3685 0.9369 0.4849 0.1004 1
 21001 HDO-18          952 2.1724 1.9858 1.7239 1.2806 0.8472 0.4378 0.1274 1
 21002 DF               20 1.2917 1.1691 0.9978 0.7118 0.4416 0.2043 0.0443 1
 25001 CCH             114 2.7591 2.6344 2.4589 2.1598 1.8628 1.5699 1.2852 1
 25002 NaH              52 1.6427 1.5166 1.3417 1.0468 0.7590 0.4852 0.2405 1
 26001 CN, v = 0, 1    874 2.8222 2.6976 2.5223 2.2238 1.9280 1.6376 1.3579 2
 26002 C2H2           2066 2.6236 2.4513 2.2534 1.9508 1.6530 1.3585 1.0707 1* 
 27001 HCN              40 2.6277 2.5031 2.3276 2.0286 1.7317 1.4389 1.1545 2
 27002 HNC              49 2.1409 2.0162 1.8407 1.5418 1.2449 0.9523 0.6683 2
 27003 HCN-v2          132 2.1506 2.0260 1.8505 1.5515 1.2546 0.9618 0.6774 1
 27004 C-13-N         1218 3.1415 3.0169 2.8416 2.5429 2.2468 1.9556 1.6746 1
 28001 CO               91 2.0369 1.9123 1.7370 1.4386 1.1429 0.8526 0.5733 4*
 28002 HC-13-N          45 2.6389 2.5145 2.3390 2.0399 1.7428 1.4498 1.1650 1
 28003 HCN-15           35 2.1634 2.0388 1.8633 1.5642 1.2671 0.9741 0.6892 1
 28004 DCN              54 2.7151 2.5906 2.4150 2.1156 1.8179 1.5236 1.2361 1
 28005 HNC-13           34 2.1580 2.0336 1.8581 1.5590 1.2620 0.9691 0.6844 1
 28006 HN-15-C          33 2.1494 2.0249 1.8494 1.5504 1.2534 0.9607 0.6763 1
 28007 DNC              53 2.2156 2.0909 1.9153 1.6160 1.3184 1.0245 0.7377 2
 28008 HCNH+            74 2.2281 2.1034 1.9279 1.6285 1.3309 1.0368 0.7496 1
 28009 CO+              88 2.3278 2.2034 2.0281 1.7297 1.4342 1.1441 0.8653 1
 28010 DCCD           6940 2.7114 2.5043 2.2785 1.9673 1.6685 1.3722 1.0807 1* 
 29001 C-13-O           60 2.0564 1.9318 1.7565 1.4579 1.1620 0.8712 0.5909 3*
 29002 HCO+ v=0,1,2    373 2.1643 2.0276 1.8481 1.5488 1.2519 0.9591 0.6748 4*
 29003 CH2NH          2957 3.7703 3.5798 3.3191 2.8695 2.4206 1.9745 1.5341 2
 29004 HCO            2454 3.4806 3.2930 3.0286 2.5779 2.1298 1.6901 1.3077 1
 29005 NNH+             34 3.0831 2.9586 2.7832 2.4842 2.1875 1.8952 1.6116 1
 29006 CO-17            50 2.0480 1.9234 1.7481 1.4496 1.1537 0.8632 0.5833 2*
 29007 HOC+             40 2.1466 2.0219 1.8464 1.5474 1.2505 0.9578 0.6735 1
 30001 CO-18            60 2.0580 1.9334 1.7581 1.4595 1.1636 0.8728 0.5924 3*
 30002 HC-13-O+         40 2.1599 2.0353 1.8598 1.5607 1.2637 0.9707 0.6859 3
 30003 DCO+           1041 2.7335 2.5972 2.4176 2.1179 1.8202 1.5258 1.2382 3
 30004 H2CO           3506 3.4598 3.2724 3.0086 2.5584 2.1094 1.6501 1.1399 2*
 30005 C-13-H2NH       439 3.3037 3.1163 2.8522 2.4006 1.9491 1.4975 1.0460 1
 30006 CH2N-15-H       440 3.3044 3.1170 2.8529 2.4013 1.9498 1.4982 1.0467 1
 30007 CH2ND          1834 3.8562 3.6688 3.4053 2.9094 2.4740 1.9667 1.6373 1
 30008 NO             1909 3.0643 2.9122 2.6922 2.3216 1.9977 1.7160 1.4604 1
 30009 NND+             41 2.6881 2.5634 2.3878 2.0886 1.7911 1.4972 1.2106 1
 30010 HOC-13+          40 2.1651 2.0404 1.8649 1.5657 1.2686 0.9756 0.6906 1
 30011 NO+             154 2.4994 2.3749 2.1997 1.9014 1.6059 1.3159 1.0374 1
 30012 DOC+             40 2.2152 2.0905 1.9149 1.6155 1.3180 1.0241 0.7373 1
 31001 HCO-18+          34 2.1678 2.0432 1.8677 1.5686 1.2716 0.9784 0.6934 2
 31002 H2C-13-O       2309 3.4707 3.2833 3.0194 2.5692 2.1201 1.6607 1.1501 2*
 31003 HDCO           4204 3.2854 3.0993 2.8365 2.3868 1.9381 1.4921 1.0518 1
 31004 HO-18-C+         40 2.1607 2.0360 1.8605 1.5614 1.2644 0.9714 0.6866 1
 31005 HNO           10293 3.4396 3.2530 2.9892 2.5391 2.0911 1.6476 1.2363 1
 31006 Methoxy       10936 4.2925 4.1277 3.8637 3.3579 2.8474 2.3952 2.0075 1* 
 31007 D13CO+           40 2.2484 2.1237 1.9481 1.6486 1.3509 1.0564 0.7686 1* 
 31008 CH3NH2        28298 5.0959 4.9083 4.6439 4.1920 3.7401 3.2882 2.8363 1* 
 32001 O2              237 2.3398 2.2152 2.0398 1.7419 1.4477 1.1618 0.8960 3
 32002 O2-v1           237 2.3398 2.2152 2.0398 1.7419 1.4477 1.1618 0.8960 4
 32003 CH3OH          4665 4.1471 3.8758 3.5702 3.0777 2.5480 2.0323 1.5604 2*
 32004 H2CO-18         449 3.4796 3.2922 3.0281 2.5789 2.1299 1.6703 1.1596 1
 32005 O2-snglt-dlta    67 2.1748 2.0521 1.8804 1.5929 1.3187 1.0702 0.8686 1
 32006 D2CO           3682 4.0582 3.8707 3.6068 3.1562 2.7069 2.2606 1.8283 1
 32007 DNO           14739 3.6086 3.4226 3.1589 2.7086 2.2601 1.8149 1.3783 1
 33001 HO2           21954 3.6410 3.4536 3.1896 2.7391 2.2904 1.8464 1.4408 3
 33002 O-17-O        10787 3.4302 3.3051 3.1290 2.8280 2.5277 2.2297 1.9397 1
 33003 SH v=0,1        646 2.0547 1.9137 1.7355 1.4902 1.3132 1.2227 1.2045 1*
 34001 O-18-O          400 2.6638 2.5387 2.3625 2.0615 1.7610 1.4627 1.1721 3
 34002 H2S            1525 2.7113 2.5244 2.2619 1.8164 1.3778 0.9395 0.4640 3
 34003 PH3             728 2.9052 2.7221 2.4606 2.0132 1.5705 1.1435 0.7631 3
 34004 H2O2          38357 3.9906 3.7605 3.4494 2.9521 2.4608 1.9535 1.4262 4
 34005 SD             1125 2.4965 2.3494 2.1600 1.8837 1.6518 1.4802 1.3960 1*
 35001 HDS            1138 2.6344 2.4486 2.1859 1.7384 1.2957 0.8628 0.4516 1
 36001 HCl             137 1.9211 1.8018 1.6369 1.3687 1.1283 0.9337 0.8121 2
 37001 DCl             228 2.1940 2.0701 1.8963 1.6026 1.3166 1.0462 0.8085 1
 37002 C3H            4990 3.7982 3.6648 3.4714 3.1217 2.7380 2.3227 1.9243 1
 37003 c-C3H          2973 4.0152 3.8334 3.5728 3.1290 2.6964 2.2837 1.9101 1
 38001 HCl-37          137 1.9218 1.8024 1.6375 1.3692 1.1288 0.9340 0.8123 2
 38002 c-C3H2        30436 4.1215 3.9226 3.6548 3.2035 2.7535 2.3050 1.8598 2
 38003 C3D            1560 3.5539 3.4251 3.2396 2.9107 2.5578 2.1682 1.7477 1
 38004 c-CCC-13-H     6541 4.1906 4.0663 3.8572 3.4405 3.0093 2.5963 2.2222 1
 38005 c-C-13-CCH     9753 4.4919 4.3666 4.1549 3.7290 3.2797 2.8312 2.3863 1
 38006 c-C3D          6001 3.7823 3.7191 3.6021 3.3146 2.9330 2.5248 2.1482 1
 39001 c-HC-13-CCH   17768 3.8166 3.6293 3.3652 2.9143 2.4642 2.0157 1.5703 2
 39002 c-HCC-13-CH    6892 3.8216 3.6343 3.3703 3.9193 2.4692 2.0206 1.5752 2
 39003 c-C3HD        21610 3.8693 3.6822 3.4181 2.9671 2.5169 2.0681 1.6222 2
 39004 DCl-37          228 2.1953 2.0714 1.8976 1.6038 1.3178 1.0473 0.8094 1
 39005 NaO            6440 3.8468 3.6716 3.4368 3.0548 2.7151 2.4198 2.1438 1* 
 40001 CH3CCH          813 3.7347 3.5471 3.2835 2.8323 2.3825 1.9458 1.5368 1
 40002 NaOH             91 2.6980 2.5729 2.3968 2.0961 1.7961 1.4973 1.2009 1
 40003 SiC             982 3.1953 3.0437 2.8242 2.4233 2.0148 1.6657 1.3974 1
 40004 SiC-v1          703 3.1953 3.0437 2.8242 2.4233 2.0148 1.6657 1.3974 1
 40005 KH               40 1.8000 1.6721 1.4960 1.1989 0.9070 0.6248 0.3624 1
 40006 C2O             354 3.2110 3.0861 2.9100 2.6092 2.3087 2.0089 1.7106 1*
 40007 MgO              88 2.5736 2.4409 2.2621 1.9614 1.6619 1.3640 1.0693 1*
 41001 CH3CN          1728 4.0051 3.8176 3.5534 3.1021 2.6523 2.2157 1.8068 4* 
 41002 CH3CC-13-H      822 3.7479 3.5603 3.2966 2.8455 2.3956 1.9589 1.5498 1
 41003 CH3C-13-CH      813 3.7349 3.5473 3.2837 2.8325 2.3826 1.9460 1.5370 1
 41004 C-13-H3CCH      821 3.7467 3.5591 3.2954 2.8443 2.3944 1.9577 1.5487 1
 41005 CH3CCD          822 4.2353 4.0480 3.7882 3.3509 2.9005 2.4631 2.0537 2
 41006 CH2DCCH         223 3.6463 3.4586 3.2131 2.9566 2.6131 2.1936 1.7472 2
 41007 SiC-13         2417 3.5156 3.3674 3.1488 2.7474 2.3381 1.9874 1.7165 1
 41008 CaH             387 2.2998 2.1753 2.0007 1.7051 1.4157 1.1384 0.8861 1
 41009 CH3NC          1798 3.9677 3.7802 3.5161 3.0649 2.6152 2.1785 1.7698 1
 41010 CH3CN v8=1    20502 4.6136 4.3733 4.0569 3.5801 3.1294 2.6928 2.2839 1*
 42001 CH3CN-15       2755 4.0184 3.8308 3.5667 3.1154 2.6655 2.2288 1.8199 2*
 42002 CH2CO           835 4.0138 3.8218 3.5358 3.1092 2.6600 2.1965 1.6774 1
 42003 NH2CN         13898 4.2529 4.0503 3.7582 3.2381 2.7043 2.1844 1.6445 1
 42004 CaD             501 2.7628 2.6379 2.4624 2.1639 1.8687 1.5797 1.3032 1
 42005 K-41-H           40 1.8000 1.6720 1.4960 1.1990 0.9071 0.6249 0.3624 1
 42006 C-13-H3CN      9015 4.4949 4.3074 4.0433 3.5920 3.1421 2.7054 2.2965 1*
 42007 CH3C-13-N      8951 4.4824 4.2949 4.0308 3.5795 3.1297 2.6930 2.2842 1*
 42008 CH2DCN        19256 3.9632 3.7757 3.5115 3.0602 2.6093 2.1593 1.7109 1*
 42009 H2CSi           670 3.7963 3.6097 3.3454 2.8938 2.4441 2.0124 1.6312 1*
 42010 SiN             614 3.2345 3.1109 2.9353 2.6351 2.3361 2.0390 1.7461 1*
 43001 CHDCO           886 3.8250 3.6375 3.3700 2.9184 2.4710 2.0218 1.5740 1
 43002 HNCO           6078 3.8913 3.7096 3.4475 2.9969 2.5473 2.1122 1.7509 1
 43003 AlO            1023 3.5940 3.4691 3.2931 2.9926 2.6930 2.3947 2.0994 1
 43004 CP              397 3.0212 2.8964 2.7206 2.4206 2.1217 1.8251 1.5329 1*
 44001 CS               51 2.4074 2.2829 2.1071 1.8072 1.5084 1.2118 0.9199 2
 44002 SiO              31 2.4582 2.3342 2.1594 1.8593 1.5602 1.2632 0.9703 2
 44003 CH3CHO        36072 4.9222 4.6941 4.3591 3.8126 3.3334 2.8810 2.4316 2*
 44004 N2O             104 2.6974 2.5725 2.3965 2.0961 1.7962 1.4975 1.2011 2* 
 44006 DNCO           5504 3.9755 3.8265 3.5931 3.1548 2.7051 2.2574 1.8327 1
 44007 HN-15-CO        888 3.4117 3.2402 2.9845 2.5348 2.0843 1.6466 1.2793 1
 44008 HNC-13-O       4332 3.8722 3.7016 3.4466 2.9974 2.5478 2.1126 1.7510 1
 44009 N2O-v2          254 2.6974 2.5725 2.3965 2.0961 1.7962 1.4975 1.2011 2* 
 44010 HCP              34 2.4942 2.3710 2.1955 1.8953 1.5961 1.2988 1.0052 1
 44011 AlOH            766 3.3780 3.2530 3.0771 2.7766 2.4770 2.1788 1.8836 1
 44012 N2O-2v2         293 2.6974 2.5725 2.3965 2.0961 1.7962 1.4975 1.2011 2* 
 44013 C3H8         183835 6.1974 5.8585 5.4628 4.9228 4.4622 4.0118 3.5630 2* 
 45001 C-13-S           29 2.4324 2.3080 2.1321 1.8321 1.5331 1.2364 0.9440 2
 45002 Si-29-O          66 2.4637 2.3408 2.1649 1.8647 1.5656 1.2686 0.9755 1
 45003 NH2CHO         3476 4.4647 4.2667 4.0134 3.5603 3.1117 2.6620 2.2141 2
 45005 HCS+             64 2.4676 2.3427 2.1669 1.8668 1.5677 1.2706 0.9775 1
 45006 HNCO-18        4929 3.9097 3.7316 3.4713 3.0208 2.5712 2.1359 1.7741 1
 45007 NN-15-O         104 2.6974 2.5725 2.3966 2.0961 1.7962 1.4975 1.2011 2* 
 45008 N-15-NO         106 2.7123 2.5874 2.4114 2.1109 1.8110 1.5122 1.2157 2* 
 45009 DCP              34 2.5620 2.4407 2.2657 1.9655 1.6660 1.3681 1.0734 1
 45010 HOCO+          1745 3.3749 3.2288 2.9990 2.5630 2.1126 1.6693 1.2847 1
 45011 AlOD             70 2.6449 2.5199 2.3439 2.0434 1.7436 1.4452 1.1494 1
 45012 O-17-CO         285 3.5195 3.3946 3.2187 2.9181 2.6181 2.3192 2.0224 1
 45013 PN v=0-4       1637 2.9037 2.7782 2.6023 2.3023 2.0034 1.7067 1.4144 1*
 46001 CS-34            34 2.4144 2.2899 2.1141 1.8141 1.5153 1.2187 0.9267 2
 46002 Si-30-O          67 2.4689 2.3449 2.1700 1.8699 1.5708 1.2736 0.9805 1
 46003 H2CS            517 3.7770 3.5753 3.3199 2.8751 2.4243 1.9599 1.4397 1
 46004 C2H5OH        52180 4.6588 4.4539 4.1557 3.6135 3.0304 2.4638 1.9804 4*
 46005 HCOOH          1888 3.9486 3.7612 3.4971 3.0357 2.5949 2.1461 1.6984 1
 46006 NO2           16444 4.1306 3.9426 3.6780 3.2263 2.7755 2.3260 1.8791 2
 46007 N2O-18          107 2.7224 2.5975 2.4215 2.1210 1.8210 1.5222 1.2256 2* 
 46008 CH3OCH3       21735 5.3580 5.1838 4.9253 4.4746 4.0238 3.5737 3.1254 1
 46009 AlF            1188 3.3579 3.2330 3.0570 2.7565 2.4570 2.1590 1.8640 1
 46010 NS             2402 3.3385 3.1789 2.9576 2.6138 2.3057 2.0023 1.6965 1
 46011 DOCO+           677 3.5187 3.3761 3.1508 2.7184 2.2682 1.8184 1.3807 1
 46012 HOC-13-O+      1735 3.3752 3.2290 2.9992 2.5632 2.1128 1.6695 1.2848 1
 46013 O-18-CO          91 2.7535 2.6286 2.4526 2.1521 1.8520 1.5530 1.2561 1
 47001 H2C-13-S        110 3.7938 3.6064 3.3253 2.8908 2.4222 1.9743 1.4566 1
 47002 HC-13-OOH     16515 3.9558 3.7682 3.5040 3.0527 2.6021 2.1524 1.7047 2* 
 47003 DCOOH         21405 4.0233 3.8357 3.5715 3.1203 2.6695 2.2198 1.7720 2* 
 47004 HCOOD         18903 3.9988 3.8113 3.5471 3.0958 2.6451 2.1954 1.7475 2* 
 47005 PO+ v=0-4       239 2.4260 2.3007 2.1249 1.8248 1.5260 1.2293 0.9370 1*
 47006 PO              743 3.1875 3.0284 2.8083 2.4678 2.1659 1.8745 1.5928 1*
 47007 HONO          54411 4.0778 3.8606 3.5486 3.0215 2.5391 2.0866 1.6391 1* 
 47008 cis-DCOOH     22490 4.0147 3.8272 3.5630 3.1117 2.6610 2.2112 1.7633 1* 
 47009 cis-HCOOD     17502 3.9714 3.7839 3.5197 3.0684 2.6176 2.1676 1.7198 1* 
 47010 cis-HC-13-OO  15887 3.9418 3.7542 3.4900 3.0387 2.5880 2.1383 1.6906 1* 
 48001 SO              330 2.9295 2.8009 2.6175 2.2956 1.9559 1.5897 1.2015 1
 48002 SO-v1           261 2.9295 2.8009 2.6175 2.2956 1.9559 1.5897 1.2015 1
 48003 H2CS-34         111 3.7842 3.6593 3.3326 2.8811 2.4137 1.9652 1.4473 1
 48004 O3             7133 3.5505 3.3484 3.0787 2.6268 2.1762 1.7267 1.2796 4* 
 48005 O3-v2          4911 3.5505 3.3484 3.0787 2.6268 2.1762 1.7267 1.2796 4* 
 48006 O3-v1,3        9619 3.5505 3.3484 3.0787 2.6268 2.1762 1.7267 1.2796 4* 
 48007 O3-2v2         3014 3.5505 3.3484 3.0787 2.6268 2.1762 1.7267 1.2796 3* 
 48008 O3-v1,3+v2    10912 3.5506 3.3484 3.0787 2.6268 2.1762 1.7267 1.2796 1
 48009 NS-34          2364 3.3463 3.1867 2.9655 2.6217 2.3136 2.0102 1.7045 1
 48010 SO+             194 2.8024 2.6485 2.4461 2.1352 1.8391 1.5486 1.2687 1*
 48011 CH3OOH        46191 5.0272 4.8346 4.5605 4.0812 3.5811 3.0567 2.5303 1* 
 49001 O3-sym-O-17   26092 4.3181 4.1306 3.8665 3.4159 2.9676 2.5243 2.0930 2
 49002 O3-asym-O-17  52613 4.6243 4.4367 4.1725 3.7217 3.2727 2.8276 2.3912 2
 49003 C4H             742 3.7206 3.5959 3.4198 3.1190 2.8183 2.5182 2.2189 1
 49004 MgCCH           274 3.3976 3.2727 3.0966 2.7956 2.4950 2.1949 1.8957 1
 50001 S-34-O          280 2.9380 2.8093 2.6260 2.3039 1.9640 1.5972 1.2081 1
 50002 SO-18           179 2.9626 2.8296 2.6500 2.3280 1.9873 1.6191 1.2275 1
 50003 O3-sym-O-18    3184 3.5472 3.3599 3.0959 2.6453 2.1947 1.7452 1.2980 2
 50004 O3-asym-O-18   7304 3.8582 3.6708 3.4065 2.9562 2.5055 2.0560 1.6086 2
 50005 O3-s-O18-v2    2387 3.5472 3.3599 3.0959 2.6453 2.1947 1.7452 1.2980 1
 50006 O3-a-O18-v2    4213 3.8582 3.6708 3.4065 2.9562 2.5055 2.0560 1.6086 1
 50007 CH3Cl-35       6386 4.4501 4.2530 3.9857 3.5324 3.0811 2.6472 2.2405 3* 
 50008 C3N            1351 3.8798 3.7550 3.5790 3.2781 2.9775 2.6774 2.3782 1
 50009 MgCN            273 3.3904 3.2655 3.0893 2.7884 2.4878 2.1877 1.8885 1
 50010 MgNC            269 3.3220 3.1969 3.0208 2.7119 2.4193 2.1194 1.8205 1
 51001 HCCCN           139 3.6154 3.4905 3.3144 3.0135 2.7129 2.4127 2.1134 3*
 51002 ClO            2585 3.5250 3.3632 3.1537 2.8384 2.5457 2.2632 1.9987 3
 51003 ClO-v1         2112 3.5250 3.3632 3.1537 2.8384 2.5457 2.2632 1.9987 1
 51004 HCCNC           563 3.5770 3.4522 3.2762 2.9753 2.6747 2.3746 2.0754 1
 51005 HCCNC-v7        291 3.5770 3.4522 3.2762 2.9753 2.6747 2.3746 2.0754 1
 51006 HCCNC-v6        291 3.5770 3.4522 3.2762 2.9753 2.6747 2.3746 2.0754 1
 51007 HCCNC-v5        278 3.5770 3.4522 3.2762 2.9753 2.6747 2.3746 2.0754 1
 51008 HNCCC           574 3.6039 3.4792 3.3032 3.0023 2.7017 2.4015 2.1022 1
 52001 HCCC-13-N       152 3.6170 3.4922 3.3162 3.0153 2.7147 2.4145 2.1152 1
 52002 HCC-13-CN       146 3.6171 3.4923 3.3162 3.0154 2.7148 2.4146 2.1152 1
 52003 HC-13-CCN       144 3.6289 3.5041 3.3280 3.0272 2.7265 2.4263 2.1269 1
 52004 HCCCN-15         99 3.1509 3.0261 2.8501 2.5492 2.2486 1.9484 1.6490 1
 52005 DCCCN           156 3.6477 3.5228 3.3468 3.0460 2.7453 2.4450 2.1456 1
 52006 HOCl          40352 3.9788 3.7911 3.5268 3.0756 2.6252 2.1779 1.7662 4* 
 52007 SiCC            304 3.4817 3.3737 3.1906 2.8021 2.3602 1.9105 1.4628 2
 52008 CCCO             51 3.1132 2.9888 2.8129 2.5121 2.2115 1.9114 1.6121 1
 52009 CH3Cl-37       6438 4.4571 4.2600 3.9927 3.5394 3.0881 2.6541 2.2488 3*
 52010 CH2F2         11942 4.7435 4.5317 4.2551 3.8010 3.3502 2.9003 2.4522 1
 52011 CH2F2-v4       7808 4.7435 4.5317 4.2551 3.8010 3.3502 2.9003 2.4522 1
 52012 DNCCC          3098 4.1065 3.9819 3.8059 3.5051 3.2044 2.9042 2.6048 1
 52013 CNCN           1105 4.0365 3.9117 3.7356 3.4348 3.1342 2.8342 2.5350 2*
 53001 C2H3CN        75697 5.0769 4.8334 4.5016 3.9982 3.5418 3.0909 2.6410 4*
 53002 Cl-37-O        2624 3.5327 3.3706 3.1611 2.8457 2.5528 2.2700 2.0049 3
 53003 C-13-CCO         99 3.1304 3.0054 2.8294 2.5285 2.2279 1.9277 1.6284 1
 53004 CC-13-CO         99 3.1164 2.9915 2.8154 2.5146 2.2139 1.9138 1.6145 1
 53005 CCC-13-O         99 3.1152 2.9902 2.8142 2.5133 2.2127 1.9126 1.6133 1
 53006 Cl-37-O-v1     2132 3.5327 3.3706 3.1611 2.8457 2.5528 2.2700 2.0049 1
 53007 C2H3NC         9362 4.3574 4.1839 3.9262 3.4753 3.0239 2.5731 2.1233 1
 53008 HNCCN+           99 3.1486 3.0240 2.8480 2.5471 2.2465 1.9463 1.6469 1*
 54001 CH2CHC-13-N     118 4.4205 4.2331 3.9690 3.5174 3.0659 2.5545 2.1409 2
 54002 CH2C-13-HCN     117 4.4259 4.2385 3.9744 3.5228 3.0713 2.5598 2.1462 2
 54003 C-13-H2CHCN     116 4.4328 4.4254 3.9813 3.5297 3.0782 2.5645 2.1514 2
 54004 CH2CDCN        6381 4.4739 4.2866 4.0222 3.5704 3.1190 2.6682 2.2182 2*
 54005 HOCl-37       40627 3.9864 3.7987 3.5344 3.0832 2.6328 2.1854 1.7736 4* 
 54006 CCCO-18          99 3.1361 3.0112 2.8351 2.5342 2.2336 1.9334 1.6341 1
 54007 HCCCHO         8407 4.2914 4.1379 3.8988 3.4558 3.0044 2.5535 2.1035 1
 54008 CH2CHC-15-N    8541 4.4215 4.2425 3.9813 3.5298 3.0784 2.6275 2.1775 1* 
 55001 C2H5CN        64344 4.5732 4.3854 4.1209 3.6691 3.2178 2.7669 2.3169 5*
 55002 CaCH3          5775 4.6761 4.4883 4.2239 3.7723 3.3226 2.8883 2.4814 1*
 56001 CH3CH2C-13-N   1183 5.0510 4.8530 4.5996 4.1024 3.6647 3.1505 2.8159 2
 56002 CH3C-13-H2CN   1252 5.0557 4.8577 4.6043 4.1071 3.6694 3.1552 2.8206 2
 56003 C-13-H3CH2CN   1183 5.0618 4.8638 4.6104 4.1132 3.6755 3.1612 2.8267 2
 56004 C2H5CN-15      1621 4.5850 4.3976 4.1335 3.5984 3.1882 2.7768 2.3302 1
 56005 CH2DCH2CN-s    1166 5.0748 4.8768 4.6232 4.1717 3.7202 3.2523 2.7839 2
 56006 CH2DCH2CN-a    1286 5.0830 4.8850 4.6316 4.1799 3.7284 3.2618 2.7947 2
 56007 CCS             563 3.4539 3.3264 3.1450 2.8286 2.4980 2.1426 1.7518 1
 56008 C2H3CHO       24051 4.4085 4.2407 3.9959 3.5540 3.1030 2.6523 2.2023 1
 56009 MgS              99 2.8934 2.7683 2.5922 2.2913 1.9910 1.6914 1.3933 1*
 56010 HCCCH2OH     104073 4.8136 4.6222 4.3448 3.8547 3.3400 2.8075 2.2976 1* 
 57001 C-13CS         1013 3.7748 3.6472 3.4658 3.1493 2.8185 2.4628 2.0712 1
 57002 CC-13S         1015 3.7570 3.6294 3.4481 3.1317 2.8010 2.4456 2.0547 1
 57003 HCCCH2OD      22458 4.6839 4.5595 4.3546 3.9323 3.4587 2.9605 2.4376 1* 
 58001 CCS-34          565 3.4635 3.3359 3.1546 2.8381 2.5074 2.1519 1.7607 1
 58002 NaCl             99 2.9835 2.8583 2.6820 2.3810 2.0804 1.7805 1.4819 1
 58003 CH3COCH3      61834 7.1904 6.7212 6.1567 5.1290 4.7774 4.3268 3.9163 1* 
 60001 OCS              99 3.0121 2.8874 2.7113 2.4104 2.1100 1.8101 1.5113 2
 60002 SiS              97 2.8382 2.7136 2.5376 2.2369 1.9366 1.6372 1.3396 1
 60003 CH3OCHO       61521 5.3002 5.0832 4.7714 4.2442 3.7613 3.3077 2.8578 2*
 60005 NaCl-37          99 2.9929 2.8677 2.6914 2.3904 2.0898 1.7899 1.4912 1
 60006 HCOCH2OH      73527 4.8335 4.5751 4.2127 3.6658 3.2008 2.7498 2.3001 1*
 61001 OC-13-S          99 3.0135 2.8887 2.7126 2.4119 2.1114 1.8115 1.5127 2
 61002 Si-29-S          98 2.8462 2.7217 2.5456 2.2449 1.9446 1.6452 1.3475 1
 61003 C5H            2594 4.2860 4.1602 3.9715 3.6294 3.2620 2.8809 2.5298 1
 61004 NH2CH2CH2OH  282599 5.1509 4.8766 4.4797 3.8392 3.3150 2.8554 2.4053 1*
 62001 OC-34-S          99 3.0228 2.8908 2.7220 2.4212 2.1207 1.8208 1.5219 2
 62002 O-18-CS          99 3.0398 2.9151 2.7389 2.4382 2.1377 1.8377 1.5388 2
 62003 Si-30-S          99 2.8539 2.7293 2.5532 2.2525 1.9523 1.6528 1.3550 1
 62004 SiS-34           99 2.8502 2.7257 2.5496 2.2489 1.9486 1.6492 1.3515 1
 62005 AlCl          11525 4.3144 4.1894 4.0132 3.7123 3.4119 3.1121 2.8138 1
 62006 C5D            4436 4.4842 4.3601 4.1723 3.8303 3.4630 3.0818 2.7305 1
 63001 HNO3         129631 5.0424 4.7830 4.4802 4.0205 3.5694 3.1189 2.6697 4*
 63002 HNO3-v7        8821 5.0424 4.7830 4.4802 4.0205 3.5694 3.1189 2.6697 2*
 63003 HNO3-v9       83672 5.0424 4.7830 4.4802 4.0205 3.5694 3.1189 2.6697 2*
 63004 HNO3-v6       66984 5.0424 4.7830 4.4802 4.0205 3.5694 3.1189 2.6697 2*
 63005 HNO3-v8       58560 5.0424 4.7830 4.4802 4.0205 3.5694 3.1189 2.6697 2*
 63006 HNO3-v5/2v9   34916 5.0424 4.7830 4.4802 4.0205 3.5694 3.1189 2.6697 2*
 63007 PS             2340 3.5468 3.3813 3.1691 2.8501 2.5501 2.2529 1.9596 1*
 63008 PO2            7323 4.3193 4.1331 3.8684 3.4159 2.9641 2.5130 2.0629 1*
 63009 HOONO        168036 4.9240 4.7364 4.4721 4.0205 3.5694 3.1189 2.6695 2* 
 64001 S2              174 2.9954 2.8607 2.6651 2.3091 1.9145 1.4876 1.0947 2
 64002 SO2           13573 3.7722 3.5845 3.3202 2.8687 2.4177 1.9677 1.5194 4*
 64003 AlCl-37       11326 4.3248 4.1997 4.0235 3.7226 3.4222 3.1224 2.8240 1
 64004 C4O             535 3.9012 3.7760 3.5994 3.2969 2.9931 2.6866 2.3749 1*
 64005 SO2-v2         9225 3.7685 3.5808 3.3164 2.8649 2.4139 1.9639 1.5156 1*
 64006 H35ClCO      113626 4.8285 4.6409 4.3765 3.9249 3.4737 3.0231 2.5736 1* 
 64007 H15NO3        36935 4.4465 4.2590 3.9948 3.5433 3.0923 2.6419 2.1926 1*
 64008 DNO3          56528 4.4697 4.2822 4.0180 3.5665 3.1155 2.6650 2.2157 1* 
 65001 S-33-O2       19048 4.3781 4.1904 3.9260 3.4745 3.0236 2.5736 2.1253 1*
 65002 H18ONO2       40392 4.4709 4.2834 4.0191 3.5677 3.1166 2.6662 2.2168 1* 
 65003 HON18OO       44692 4.4710 4.2835 4.0193 3.5679 3.1168 2.6663 2.2170 1* 
 65004 HONO18O       17737 4.4708 4.2833 4.0191 3.5677 3.1166 2.6661 2.2168 1* 
 66001 COF2          23090 4.7883 4.6008 4.3366 3.8852 3.4341 2.9836 2.5342 1
 66002 S-34-O2       11894 3.7797 3.5919 3.3276 2.8761 2.4252 1.9751 1.5268 2*
 66003 CaNC            259 3.4903 3.3658 3.1895 2.8884 2.5875 2.2871 1.9876 1
 66004 SOO-18         9758 4.1040 3.9163 3.6520 3.2004 2.7495 2.2994 1.8509 1*
 66005 H37ClCO      113891 4.8384 4.6508 4.3864 3.9348 3.4836 3.0330 2.5835 1* 
 67001 OCl-35-O      57232 4.7203 4.5492 4.2856 3.8365 3.3906 2.9506 2.5223 2*
 68001 CCCS             99 3.3309 3.2093 3.0341 2.7332 2.4325 2.1320 1.8320 1
 69001 OCl-37-O      49388 4.7195 4.5429 4.2827 3.8315 3.3805 2.9304 2.4819 2*
 69002 C3H7CN       131349 5.1410 4.9350 4.6282 4.0863 3.5714 3.1062 2.6550 1
 70001 CCCS-34          99 3.3410 3.2198 3.0448 2.7439 2.4431 2.1426 1.8426 1
 71001 MnO           12510 4.1718 4.0466 3.8686 3.5617 3.2500 2.9313 2.6068 1*
 71002 NF3            4033 4.4444 4.2569 3.9927 3.5413 3.0905 2.6406 2.1923 1* 
 72001 CaS             382 3.1250 2.9726 2.7779 2.4716 2.1709 1.8708 1.5718 1* 
 73001 C6H            3031 4.4884 4.3862 4.2192 3.8961 3.5465 3.1726 2.7971 1
 74001 KCl              99 3.2114 3.0871 2.9108 2.6097 2.3089 2.0085 1.7088 1
 74002 C2H5OOCH      60671 5.0795 4.8985 4.6152 4.0883 3.5602 3.0782 2.6249 1
 74003 C3H6O2        60054 5.5087 5.3282 5.0667 4.6115 4.1519 3.6851 3.2051 1* 
 75001 HCCCCCN          99 3.6717 3.5206 3.3645 3.0697 2.7688 2.4680 2.1675 1
 75002 H2NCH2COOH I  68326 5.0965 5.0023 4.8368 4.4680 4.0315 3.5809 3.1305 1
 75003 H2NCH2COOH II 44300 5.0965 5.0023 4.8368 4.4680 4.0315 3.5809 3.1305 1
 75004 H2NCO2CH3    140822 6.1249 5.9100 5.6004 5.0721 4.5838 4.1283 3.6774 1* 
 76001 HCCCCC-13-N      99 3.6760 3.5376 3.3686 3.0740 2.7731 2.4723 2.1718 1
 76002 HCCCC-13-CN      99 3.6721 3.5337 3.3649 3.0700 2.7692 2.4684 2.1679 1
 76003 HCCC-13-CCN      99 3.6721 3.5337 3.3650 3.0701 2.7693 2.4685 2.1680 1
 76004 HCC-13-CCCN      99 3.6762 3.5378 3.3687 3.0741 2.7733 2.4725 2.1720 1
 76005 HC-13-CCCCN      99 3.6831 3.5441 3.3752 3.0811 2.7803 2.4795 2.1789 1
 76006 HCCCCCN-15       99 3.6825 3.5435 3.3747 3.0804 2.7796 2.4788 2.1783 1
 76007 DCCCCCN          99 3.6918 3.5520 3.3833 3.0897 2.7889 2.4881 2.1875 1
 76008 KCl-37           99 3.2237 3.0995 2.9234 2.6222 2.3214 2.0210 1.7213 1
 76009 C4Si            229 3.6104 3.4854 3.3093 3.0083 2.7074 2.4066 2.1062 1
 76010 C5O              99 3.6604 3.5354 3.3593 3.0583 2.7574 2.4566 2.1561 1*
 79001 HOONO2        50775 5.0793 4.9375 4.6972 4.2216 3.7512 3.2982 2.8469 1
 80001 HBr-79          143 2.0146 1.8941 1.7271 1.4527 1.2017 0.9900 0.8410 2
 80002 ZnO             259 2.6783 2.5443 2.3647 2.0638 1.7639 1.4654 1.1694 1* 
 81001 Cl-35-NO2      3520 4.9422 4.7423 4.4680 4.0122 3.5608 3.1099 2.6597 1*
 82001 HBr-81          143 2.0147 1.8943 1.7272 1.4528 1.2018 0.9901 0.8410 2
 88001 C6O             567 4.3424 4.2169 4.0398 3.7359 3.4290 3.1165 2.7931 1*
 89001 Sr-88-H         391 2.3668 2.2409 2.0658 1.7694 1.4782 1.1975 0.9381 1
 90001 Sr-88-D         922 2.8419 2.7112 2.5336 2.2345 1.9384 1.6476 1.3673 1
 90002 C3O3H6 - DHA  79158 5.5789 5.3479 4.9968 4.3768 3.8317 3.3660 2.9147 1*
 92001 C5S              99 3.7183 3.6406 3.5066 3.2277 2.9280 2.6271 2.3264 1
 94001 C5-34-S          99 3.7244 3.6480 3.5158 3.2385 2.9389 2.6381 2.3374 1
 95001 Br-79-O        1892 3.5956 3.4672 3.2900 2.9885 2.6867 2.3844 2.0804 2*
 95002 Br-79-O v=1    1314 3.5956 3.4672 3.2900 2.9885 2.6867 2.3844 2.0804 1*
 96001 HOBr-79        9898 4.1320 3.9443 3.6800 3.2285 2.7777 2.3296 1.9163 1
 96002 S3            12039 4.5007 4.3194 4.0568 3.6051 3.1535 2.7024 2.2517 1* 
 97001 Br-81-O        1892 3.5974 3.4690 3.2918 2.9903 2.6886 2.3862 2.0823 2*
 97002 Cl-35-ONO2    78323 5.7884 5.5709 5.2587 4.7332 4.2527 3.7991 3.3484 2*
 97003 Br-81-O v=1    1313 3.5974 3.4690 3.2918 2.9903 2.6886 2.3862 2.0823 1*
 98001 H2SO4         20939 5.1930 5.0055 4.7413 4.2898 3.8385 3.3876 2.9375 3*
 98002 HOBr-81        9920 4.1339 3.9462 3.6818 3.2304 2.7796 2.3315 1.9182 1
 99001 Cl-37-ONO2    49505 5.7993 5.5818 5.2697 4.7441 4.2637 3.8101 3.3593 2*
 99002 HC7N            518 4.2958 4.2417 4.1427 3.9079 3.6185 3.3179 3.0171 1
100001 C7O              99 4.0379 3.9130 3.7369 3.4359 3.1349 2.8339 2.5331 1*
102001 ClOOCl        17266 5.6303 5.4429 5.1793 4.7274 4.2757 3.8244 3.3735 1
102002 ClClOO        15078 4.9562 4.7685 4.5040 4.0522 3.6007 3.1496 2.6991 1* 
104001 Cl-37-OOCl    17482 5.6427 5.4552 5.1915 4.7398 4.2880 3.8367 3.3858 1
111001 OBr-79-O      52631 4.9645 4.7773 4.5135 4.0637 3.6164 3.1738 2.7403 1*
112001 Se-80-O2       7484 4.0326 3.8449 3.5805 3.1288 2.6777 2.2273 1.7782 1
112002 C8O             644 4.6672 4.5413 4.3631 4.0560 3.7429 3.4182 3.0722 1*
113001 OBr-81-O      52840 4.9663 4.7779 4.5154 4.0656 3.6183 3.1757 2.7422 1*
123001 HC9N             99 3.9030 3.8724 3.8137 3.6572 3.4191 3.1285 2.8278 1
124001 C9O             100 4.3271 4.2030 4.0270 3.7260 3.4250 3.1240 2.8231 1*
128001 S4            26640 5.1824 5.0196 4.7685 4.3192 3.8677 3.4163 2.9653 1* 
147001 HC11N            99 4.5521 4.4379 4.2663 3.9659 3.6648 3.3638 3.0629 2*
 27001 HCN              40 2.6277 2.5031 2.3276 2.0286 1.7317 1.4389 1.1545 2:
   88630.4160   .0010 -2.9588 2     .0000  3 -27001 102 1 1         0 1         
   88631.8470   .0010 -2.7370 2     .0000  5 -27001 102 1 2         0 1         
   88633.9360   .0010 -3.4359 2     .0000  1 -27001 102 1 0         0 1         
  177259.6770   .0020 -2.6671 2    2.9564  5 -27001 102 2 2         1 2         
  177259.9230   .0020 -2.5422 2    2.9564  3 -27001 102 2 1         1 0         
  177261.1100   .0020 -2.1900 2    2.9564  5 -27001 102 2 2         1 1         
  177261.2230   .0020 -1.9189 2    2.9564  7 -27001 102 2 3         1 2         
  177262.0122   .0011 -3.8432 2    2.9564  3  27001 102 2 1         1 2         
  177263.4450   .0020 -2.6671 2    2.9564  3 -27001 102 2 1         1 1         
  265886.1800   .5500 -1.0751 2    8.8692 21 -27001 101 3           2           
  354505.4759   .0083  -.7220 2   17.7382 27  27001 101 4           3           
  443116.1554   .0176  -.4591 2   29.5633 33  27001 101 5           4           
  531716.3875   .0338  -.2556 2   44.3440 39  27001 101 6           5           
  620304.0952   .0628  -.0950 2   62.0802 45  27001 101 7           6           
  708877.2081   .1137   .0326 2   82.7713 51  27001 101 8           7           
  797433.6638   .1994   .1334 2  106.4169 57  27001 101 9           8           
  885971.4087   .3360   .2119 2  133.0164 63  27001 10110           9           
  974488.4000   .8000   .2711 2  162.5693 69 -27001 10111          10           
 1062982.6043   .8481   .3133 2  195.0747 75  27001 10112          11           
 1151452.0030  1.2780   .3403 2  230.5320 81  27001 10113          12           
 1239894.5896  1.8690   .3534 2  268.9403 87  27001 10114          13           
 1328308.3723  2.6622   .3536 2  310.2987 93  27001 10115          14           
 1416691.3754  3.7052   .3419 2  354.6063 99  27001 10116          15           
 1505041.6397  5.0522   .3189 2  401.8620105  27001 10117          16           
 1593357.2244  6.7650   .2852 2  452.0648111  27001 10118          17           
 1681636.2074  8.9130   .2414 2  505.2135117  27001 10119          18           
 1769876.6871 11.5739   .1878 2  561.3068123  27001 10120          19           
 1858076.7832 14.8338   .1248 2  620.3436129  27001 10121          20           
 1946234.6379 18.7882   .0527 2  682.3223135  27001 10122          21           
 2034348.4171 23.5422  -.0282 2  747.2417141  27001 10123          22           
 2122416.3112 29.2108  -.1177 2  815.1003147  27001 10124          23           
 2210436.5368 35.9195  -.2156 2  885.8965153  27001 10125          24           
 2298407.3371 43.8047  -.3217 2  959.6287159  27001 10126          25           
 2386326.9838 53.0145  -.4358 2 1036.2953165  27001 10127          26           
 2474193.7775 63.7085  -.5579 2 1115.8946171  27001 10128          27           
 2562006.0495 76.0588  -.6876 2 1198.4249177  27001 10129          28           
 2649762.1623 90.2502  -.8250 2 1283.8842183  27001 10130          29           
 2737460.5112106.4809  -.9700 2 1372.2707189  27001 10131          30           
 2825099.5251124.9625 -1.1223 2 1463.5826195  27001 10132          31           
 2912677.6678145.9210 -1.2820 2 1557.8178201  27001 10133          32     

RADEX

JPARSEC includes a Java implementation of RADEX, which is a 1D non-LTE radiative transfer model useful for quick calculations. The use is very simple, passing to the instance the values you are used to put in a Fortran program or in the RADEX web interface:

int hco = DataSet.getIndex(RADEX.MOLECULE_ATOM_NAMES, "HCO+");
// mol, col den, line width. tkin, Tbg, nu min, nu max, nro partn=1;
// partner id, col den partn, geometry
RADEX radex = new RADEX(hco, 1.0E13, 1, 20, 2.73, 88, 500.0,
	PARTNER.H2, 1.0e4, METHOD.UNIFORM_SPHERE, true);
 
String sep = "   \t";
int ndecimals = 3;
System.out.println("Line"+sep+"Eup   "+sep+"Frequency"+sep+"Wavelength"+sep+"Tex   "+sep+"Opacity"+sep+"Tantenna"+sep+"Flux   "+sep+"Flux in CGS");
for (int i = 0; i<radex.getNumberOfTransitions(); i++)
{
	double wavel = CGSConstant.SPEED_OF_LIGHT / radex.getFrequency(i) / 1.0E5; // unit =  micron
	double ergs  = radex.getFluxInCGS(i);
 
	String element = radex.getName(i) + sep;
	element += Functions.formatValue(radex.getUpperLevelEnergy(i), ndecimals)+sep;
	element += Functions.formatValue(radex.getFrequency(i), ndecimals)+sep;
	element += Functions.formatValue(wavel, ndecimals)+sep;
	element += Functions.formatValue(radex.getExcitationTemperature(i), ndecimals)+sep;
	element += Functions.formatValue(radex.getOpacity(i), ndecimals)+sep;
	element += Functions.formatValue(radex.getAntennaTemperature(i), ndecimals)+sep;
	element += Functions.formatValue(radex.getFlux(i), ndecimals)+sep;
	element += ergs; //Functions.formatVALUE(ergs, ndecimals);
 
	System.out.println(element);
 
	JPARSECException.showWarnings();
}

The RADEX instance calls to the underlying calculation tree, so the rest of the example are just lines to put the results in a clear way. Output is:

Line   	Eup      	Frequency   	Wavelength   	Tex      	Opacity   	Tantenna   	Flux      	Flux in CGS
1-0   	4.280   	89.189   	3361.334   	4.507   	4.690   	1.559   	1.659   	1.5158067870103234E-8
2-1   	12.840   	178.375   	1680.686   	3.750   	5.314   	0.580   	0.618   	4.515563843032859E-8
3-2   	25.680   	267.558   	1120.478   	3.705   	0.877   	0.173   	0.185   	4.5525234119630805E-8
4-3   	42.800   	356.734   	840.380   	6.069   	0.035   	0.037   	0.039   	2.281108042932171E-8
5-4   	64.200   	445.903   	672.327   	9.705   	0.002   	0.007   	0.007   	8.016352185237346E-9

Dust opacity

JPARSEC includes code for the computation of dust properties using the Mie theory. Scattering and extinction can be computed with the corresponding classes jparsec.model.MieTheory and jparsec.model.Extinction. The class jparsec.model.DustOpacity is more useful since it uses the already computed dust properties (located in the file dust.jar, one of the dependencies of JPARSEC) to return the properties of a given grain or a mixture of them, including charts. These data are files previously computed for certain grain size distributions, so you should not go to grain distributions outside these values (0.1 to 10000 microns, in log scale).

// Grain size distributions
double size[] = new double[] {1, 100, 10000};
Color col[] = new Color[] {Color.RED, Color.GREEN, Color.BLUE, Color.BLACK};
// The two grain types for the mixture
int grain1 = DustOpacity.GRAIN_GRAPHITE;
int grain2 = DustOpacity.GRAIN_ASTRONOMICAL_SILICATE;
double p = 3.5;
double max = size[0];
 
// 0.14 and 0.86 and the % of each grain type
DustOpacity dust1 = new DustOpacity(grain1, p, max, 0.14);
DustOpacity dust2 = new DustOpacity(grain2, p, max, 0.86);
 
ChartSeriesElement series[] = new ChartSeriesElement[size.length];
for (int i=0; i<size.length; i++) {
	dust1.sizeMax = size[i];
	dust2.sizeMax = size[i];
 
	CreateChart charts[] = DustOpacity.getOpacityAndBetaCharts(dust1, dust2, 0.1, 10000.0, true, true);
	ChartElement chartElem = charts[0].getChartElement(); // Return only the opacity chart here
	ChartSeriesElement s[] = chartElem.series;
	for (int j=0; j<s.length; j++) {
		s[j].showLines = true;
		s[j].showShapes = false;
		s[j].legend = ""+dust1.sizeMax;
		s[j].color = col[i];
	}
	series[i] = s[2];
}
series[0].legend += " (@mum)";
series[2].legend = "10^{4}";
 
// Construct the new chart
ChartElement chart = new ChartElement(series, ChartElement.TYPE.XY_CHART, ChartElement.SUBTYPE.XY_SCATTER,
	"Opacity of a mixture of grains for different grain size distributions", 
	"Wavelength (@mum)", "Opacity (cm^{2} g^{-1})", false, 
	600, 600);
chart.showErrorBars = false;
chart.xTickLabels = ChartElement.TICK_LABELS.LOGARITHM_VALUES;
chart.yTickLabels = ChartElement.TICK_LABELS.LOGARITHM_VALUES;
 
chart.xAxisInLogScale = true;
chart.yAxisInLogScale = true;
 
CreateChart c = new CreateChart(chart);
c.showChartInJFreeChartPanel();

Previous example will show the opacity of a mixture of grains for three grain size distributions. The output chart is:


 

Rotational diagrams

Rotational diagrams are another useful tool to calculate the physical conditions of the circumstellar gas. They can be created using the DiagRot and 30mExplorer GUI tools, but here we will see how to create one diagram with Java code. The class for these calculations is jparsec.model.RotationalDiagram. An instance of this class needs the name of the molecule in a given catalog (JPL or CDMS), the transition names, the areas of the lines, and the errors in those areas. Molecule name can be the complete name of just the entry index number (41001 in the example below is valid instead of 41001 H3CN). For the transitions the easiest way is to use the frequency to identify it, but you don't need to add all decimals unless confusion with several very close transitions could occur.

boolean jpl_cat = true;
boolean spline = true;
String molecule_name = "41001 CH3CN";
String transition_names[] = new String[] {"91987.0540", "239137.9312"};
double area[] = new double[] { 11.5*0.5*0.3996, 73.5*0.5*0.3594*0.24};
double area_error[] = new double[] { 2.0*0.3996, 11*0.5*0.3594*0.14};
 
// Apply beam filling
/*for (int i=0; i<area.length; i++) {
	area[i] *= Math.pow(DataSet.parseDouble(transition_names[1]) / DataSet.parseDouble(transition_names[i]), 2.0);
	area_error[i] *= Math.pow(DataSet.parseDouble(transition_names[1]) / DataSet.parseDouble(transition_names[i]), 2.0);
}
*/			
RotationalDiagram dr = new RotationalDiagram(molecule_name, transition_names, jpl_cat, spline, area, area_error);
 
MeasureElement trot = new MeasureElement(dr.getTrot(), dr.getTrotError(), "K");
MeasureElement cden = new MeasureElement(dr.getColumnDensity(), dr.getColumnDensityError(), "(cm^-2)");
 
System.out.println("TROT:           " + trot.toString());
System.out.println("COLUMN DENSITY: " + cden.toString());
for (int i = 0; i < dr.getX().length; i++)
{
	MeasureElement x = new MeasureElement(dr.getX()[i], dr.getXErrors()[i], "K");
	MeasureElement y = new MeasureElement(dr.getY()[i], dr.getYErrors()[i], "(Log Nu/gu)");
	System.out.println("x = "+x.toString()+", y = " + y.toString());
}

Output is:

TROT:           35 +/- 8 K
COLUMN DENSITY: 8E+13 +/- 9E+13 (cm^-2)
x = 13.2441690 +/- 0.0000024 K, y = 25.7 +/- 0.4 (Log Nu/gu)
x = 80342863300E-9 +/- 90E-9 K, y = 23.76 +/- 0.09 (Log Nu/gu)

In case of transitions at exactly the same frequency the quantum numbers can be added to the frequency as they appear in the JPL or CDMS catalog, to search for one of them. DiagRot allows to use several transitions close in frequency at a time, distributing the observed area into the theoretical areas corresponding to each of the overlapping lines by considering the theoric intensity ratios for them.

Principal Component Analysis

The Principal Component Analysis is a powerful technique to study the mutual dependency of many parameters coming from models or observations. It allows to reduce the number of dimensions that affect a set of values by finding trends or mutual dependencies and returning the new dataset calculated for less dimensions compared to the input ones.

For instance, lets say we have a table of (x, y) values, and we don't know if there is a linear relation between x and y. In such case, y will be function of x and we will only need the set of x values, since y can be computed from x. The best way is to put the example:

// PCA test
String dataset[] = new String[] {
	"x y",
	"2.5 2.4",
	"0.5 0.7",
	"2.2 2.9",
	"1.9 2.2",
	"3.1 3.0",
	"2.3 2.7",
	"2 1.6",
	"1 1.1",
	"1.5 1.6",
	"1.1 0.9"
};
boolean withZ = false;
int f1 = 1;
 
double x[] = new double[dataset.length-1];
double y[] = new double[dataset.length-1];
double z[] = new double[dataset.length-1];
for (int i=1; i<dataset.length; i++) {
	x[i-1] = Double.parseDouble(FileIO.getField(f1, dataset[i], " ", true));
	y[i-1] = Double.parseDouble(FileIO.getField(f1 + 1, dataset[i], " ", true));
	if (withZ) z[i-1] = Double.parseDouble(FileIO.getField(f1 + 2, dataset[i], " ", true));
}
ArrayList<double[]> data = new ArrayList<double[]>();
data.add(x);
data.add(y);
if (withZ) data.add(z);
 
PCAElement pca = new PCAElement(data);
System.out.println("Input data");
System.out.println(new Matrix(pca.getOriginalData()).toString());
System.out.println("U");
System.out.println(new Matrix(pca.getSingularVectors()).toString());
System.out.println("Singular values");
System.out.println(new Matrix(pca.getSingularValues()).toString());
System.out.println("V");
System.out.println(pca.getSingularValueDecomposition().getV().toString());
 
int n = 1;
System.out.println("Data reproduced to "+n+" component/s");
System.out.println(new Matrix(pca.reproduceOriginalData(n)).toString());
pca.getChart("title", "x", "y", "z", "leyend", 1, 0, 2).showChart(500, 500);
pca.reproduceOriginalDataAsChart(n, "Reproduced dataset n = "+n, "x", "y", "z", "leyend", 1, 0, 2).showChart(500, 500);
 
System.out.println("NEW Xs");
ConsoleReport.doubleArrayReport(pca.getNewValues(0), "f2.9");
System.out.println("NEW Ys");
ConsoleReport.doubleArrayReport(pca.getNewValues(1), "f2.9");

Output is:

Input data
 2.5  0.5  2.2  1.9  3.1  2.3  2    1    1.5  1.1 
 2.4  0.7  2.9  2.2  3    2.7  1.6  1.1  1.6  0.9 

U
 -0.68 -0.74
 -0.74 0.68

Singular values
 3.4 
 0.66

V
 -0.24 -0.26 -0.93  0     0     0     0     0     0     0   
  0.53 -0.01 -0.13  0     0     0     0     0     0     0   
 -0.23 -0.96  0.33  0     0     0     0     0     0     0   
 -0.07  0    -0.02  1     0     0     0     0     0     0   
 -0.51  0.01 -0.13  0     1     0     0     0     0     0   
 -0.26  0.01 -0.07  0     0     1     0     0     0     0   
  0.01  0     0     0     0     0     1     0     0     0   
  0.34 -0.01  0.09  0     0     0     0     1     0     0   
  0.13  0     0.03  0     0     0     0     0     1     0   
  0.35 -0.01  0.09  0     0     0     0     0     0     1   

Data reproduced to 1 component/s
 2.37 0.61 2.48 2    2.95 2.43 1.74 1.03 1.51 0.98
 2.52 0.6  2.64 2.11 3.14 2.58 1.84 1.07 1.59 1.01

NEW Xs
-0.827970186
01.777580325
-0.992197494
-0.274210416
-1.675801419
-0.912949103
00.099109437
01.144572164
00.438046137
01.223820555
NEW Ys
-0.175115307
00.142857227
00.384374989
00.130417207
-0.209498461
00.175282444
-0.349824698
00.046417258
00.017764630
-0.162675287

Singular values are 3.4 and 0.7, 2 values since input data has 2 dimensions. Since the greatest one is much larger than the second, we can regenerate the input data using only the 3.4 singular value, and neglecting the contribution from the 0.7 one. The data reproduced to one component is close to the input data, and can be used as an approximation. In case of reproducing data using both components the reproduced values will be exactly the same as the input ones. Here are the charts produced:


 

This is a very simple case. More interesting would be when you have docens of properties for a given source and you want to know how many of them (and which of them!) are independent from the rest.

This implementation is based on the singular value decomposition (another name for this method). More exactly, the tutorial located here was used as reference. You will find more details there if you need more help.

Virtual Observatory

The Virtual Observatory (VO) package contains useful classes mainly to access web services. Here I will summarize how to use this package to do queries to Simbad, Vizier, ADS, CDS, and SkyView. I will also show how to use SExtractor to automatically retrieve the sources in an image. Other classes allow to do FTP/SSH stuff, send mails, create/read RSS feeds, to call the service of stellar evolutionary tracks by Siess, and to manipulate VO tables. All these classes will not be described here.

Query Simbad and Vizier

Querying Simbad and Vizier is extremely easy. Check this simple code line:

SimbadElement se = SimbadQuery.query("HD259431");
System.out.println(se.toString());

The SimbadElement object stores in different fields the information retrieved from Simbad, although as in other cases shown before the toString() method is useful to present the results to the console. Output should be:

Name: HD259431	
Right ascension: 06h 33m 05.1906s
Declination: 10º 19' 19.987"
Type: null
Spectral type: B6
B-V: unknown
dRA: -1.1679103E-8 (rad/yr)
dDEC: -1.3186932E-8 (rad/yr)
dVr: 17.0 (km/s)
Parallax: 5.78 (mas)

For Vizier some VOTable manipulation is inevitable, although JPARSEC allows to easily parse it. Check the following example. First it performs a box search (last boolean to true for a cone search) in 10 arcseconds around RR Tau, retrieving data for all catalogs in Vizier (the null value can be modified to retrieve data for a given specific catalog). The XML document is written to the console, and later parsed to obtain a VO table. This VO table is again parsed to return a list of VizierElement objects that stores the useful information in JPARSEC. And finally, that data is presented in the console.

String name = "RR Tau";
String doc = VizierQuery.query(name, null, 10, false);
System.out.println(doc);
 
SavotVOTable table = VizierQuery.toVOTable(doc);
 
VizierElement[] vizier = VizierQuery.readVOTable(table, null);
System.out.println("Obtained data from "+vizier.length+" catalogs");
String sep = ", ", sep2 = FileIO.getLineSeparator() + "      ";
for (int index = 0; index < vizier.length; index ++) {
	System.out.println(vizier[index].catalogName+" ("+vizier[index].catalogAuthor+"): "+vizier[index].catalogDescription);
	System.out.println("   "+DataSet.toString(vizier[index].dataFields, sep));
	for (int i=0; i<vizier[index].data.size(); i++)
	{
		System.out.println("   "+DataSet.toString(vizier[index].data.get(i), sep));
	}
	String flux[] = vizier[index].getFluxes(null, true, true);
	System.out.println("      ALL FLUXES: " + sep2 + DataSet.toString(flux, sep2));
}

Here is the output to the console, without the raw XML document returned by Vizier since it is very long.

Obtained data from 11 catalogs
I/259 (Hog et al. 2000): *The Tycho-2 main catalogue
   _r, _RAJ2000, _DEJ2000, TYC1, TYC2, TYC3, pmRA, pmDE, BTmag, VTmag, HIP, RA(ICRS), DE(ICRS)
   0.096, 84.877129, 26.374176, 1869, 335, 1, 0.3, 0.0, 12.293, 11.371, , 84.87712778, 26.37417611
      ALL FLUXES: 
      4.399999976158142E-5   53.72580978204874   0.0   TYCHO-2 B BAND   1.0
      5.50000011920929E-5   107.776723281662   0.0   TYCHO-2 V BAND   1.0
I/284 (Monet+ 2003): The Whole-Sky USNO-B1.0 Catalog of 1,045,913,669 sources
   _r, _RAJ2000, _DEJ2000, USNO-B1.0, RAJ2000, DEJ2000, e_RAJ2000, e_DEJ2000, Epoch, pmRA, pmDE, Ndet, B1mag, R1mag, B2mag, R2mag, Imag
   0.093, 084.877128, +26.374175, 1163-0084949, 084.877128, +26.374175, 0, 0, 2000.0, 0, 0, 0, 12.17, 10.83, 11.71, 10.79, 10.41
      ALL FLUXES: 
      4.399999976158142E-5   60.17040990838594   0.0   USNO B BAND   1.0
      6.99999988079071E-5   140.1414141504107   0.0   USNO R BAND   1.0
      8.999999761581421E-5   166.57363902799688   0.0   USNO I BAND   1.0
II/199A (Wendker 1995): Positions and designations of the stars
   _r, _RAJ2000, _DEJ2000, Seq, Name, Alias, Det, RA1950, DE1950, Data, _RA.icrs, _DE.icrs
   1.541, 084.87722, +26.37374, 1102, RR Tau, AS 103, , 05 36 23.840, +26 20 49.20, Data, 05 39 30.533, +26 22 25.45
      ALL FLUXES: 
      
II/246 (Cutri et al. 2003): The Point Source catalogue of 470,992,970 sources. % Additions to 2MASS Intro (last modif: 27-Nov-2007)
   _r, _RAJ2000, _DEJ2000, RAJ2000, DEJ2000, 2MASS, Jmag, e_Jmag, Hmag, e_Hmag, Kmag, e_Kmag, Qflg, Rflg, Bflg, Cflg, Xflg, Aflg
   0.007, 084.877152, +26.374159, 084.877152, +26.374159, 05393051+2622269, 9.685, 0.020, 8.416, 0.016, 7.389, 0.023, AAA, 211, 111, 000, 0, 0
      ALL FLUXES: 
      1.235000014305115E-4   213.05332533750283   3.9245872874795578   2MASS J BAND   2.0
      1.6619999408721924E-4   440.4533897033075   6.490761019453011   2MASS H BAND   2.0
      2.1589999198913575E-4   738.4659294172566   15.643501894993548   2MASS Ks BAND   2.0
V/114 (Egan et al. 2003): The complete MSX6C catalogue in the Galactic Plane (|b|<=6{deg})
   _r, _RAJ2000, _DEJ2000, MSX6C, RAJ2000, DEJ2000, ePos, B1, q_B1, B2, q_B2, A, q_A, C, q_C, D, q_D, E, q_E
   1.1, 084.8768, +26.3741, G181.4668-02.4990, 084.8768, +26.3741, 0.5, -2.938e+01, 0, -1.586e+01, 0, 1.547e+00, 4, 1.658e+00, 1, 1.351e+00, 1, -3.315e+00, 0
      ALL FLUXES: 
      8.279999732971192E-4   1547.0   0.0   MSX6C A BAND   18.3
      0.0012130000114440919   1658.0000000000002   0.0   MSX6C C BAND   18.3
      0.0014649999618530274   1351.0   0.0   MSX6C D BAND   18.3
VI/110 (NASA-ESA 2000): The INES (IUE Newly Extracted Spectra)\vizContent{spectrum}
   _r, _RAJ2000, _DEJ2000, Obs, ExpTime, Object, RA2000, DE2000, IUEClass, Spectrum
   1.7, 084.8772, +26.3737, 1993-10-07T22:42:36, 1799.536, BD+26 887a, 084.8772, +26.3737, 34, INES Spectrum
   1.7, 084.8772, +26.3737, 1993-10-08T02:55:40, 3599.319, BD+26 887a, 084.8772, +26.3737, 34, INES Spectrum
   1.7, 084.8772, +26.3737, 1994-01-23T23:35:13, 2699.551, BD+26 887a, 084.8772, +26.3737, 34, INES Spectrum
   1.7, 084.8772, +26.3737, 1993-10-07T23:22:22, 10798.193, BD+26 887a, 084.8772, +26.3737, 34, INES Spectrum
      ALL FLUXES: 
      
VI/111 (ISO data center 2004): *The ISO Observation Log
   _r, _RAJ2000, _DEJ2000, Target, AOT, TDT, RAJ2000, DEJ2000, abs, oLen
   1.5, 084.8769, +26.3738, RR TAU, PHT22, 86603161, 084.8769, +26.3738, +, 640
   1.5, 084.8769, +26.3738, RR TAU, PHT40, 86603163, 084.8769, +26.3738, +, 364
   1.5, 084.8769, +26.3738, RR TAU, PHT03, 86603165, 084.8769, +26.3738, +, 586
   1.7, 084.8770, +26.3737, RR TAU, PHT03, 67000865, 084.8770, +26.3737, +, 588
   2.1, 084.8770, +26.3736, RR TAU, PHT40, 67000863, 084.8770, +26.3736, +, 364
   2.2, 084.8769, +26.3736, RR TAU, PHT22, 67000861, 084.8769, +26.3736, +, 640
      ALL FLUXES: 
      
B/iram (Dan, Neri 2006): The Plateau de Bure Interferometer Observation Log between 1991-12-01 and 2012-03-31
   _r, _RAJ2000, _DEJ2000, Prog, Name, Obs, tos, Type, Vel, n_Vel, Flow, n_Flow, Fhigh, n_Fhigh, Conf, RAJ2000, DEJ2000
   0.64, 084.87721, +26.37399, R077, RRTAU, 2008-04-13, 2700, DET, 0.0, L, 210719, L, 211703, L, 6Cq, 05:39:30.53, +26:22:26.4
   0.64, 084.87721, +26.37399, SE80, RRTAU, 2009-01-31, 5445, DET, 0.0, L, 96160, L, 97144, L, 6Aq, 05:39:30.53, +26:22:26.4
   0.64, 084.87721, +26.37399, SE80, RRTAU, 2009-02-01, 2655, DET, 0.0, L, 96160, L, 97144, L, 6Aq, 05:39:30.53, +26:22:26.4
   0.64, 084.87721, +26.37399, SE80, RRTAU, 2009-02-15, 12150, DET, 0.0, L, 85750, L, 86735, L, 6Aq, 05:39:30.53, +26:22:26.4
   0.64, 084.87721, +26.37399, TB17, RRTAU, 2009-10-16, 5400, DET, 0.0, L, 230046, L, 231030, L, 5Dq, 05:39:30.53, +26:22:26.4
   0.64, 084.87721, +26.37399, TB17, RRTAU, 2009-10-16, 4365, DET, 0.0, L, 230046, L, 231030, L, 5Dq, 05:39:30.53, +26:22:26.4
      ALL FLUXES: 
      
B/iram (Dan, Neri 2006): List of observations at 30m instrument between 2009-09-30 and 2011-12-31 {\em(calibration observations not included)}
   _r, _RAJ2000, _DEJ2000, Name, RAJ2000, DEJ2000, Obs, scan, obsMode, tau, receiv, line, Freq
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 167, onOff, 1.026, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 167, onOff, 1.026, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 168, onOff, 0.926, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 168, onOff, 0.926, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 169, onOff, 1.007, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 169, onOff, 1.007, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 171, onOff, 0.984, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 171, onOff, 0.984, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 172, onOff, 1.033, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 172, onOff, 1.033, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 173, onOff, 0.949, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 173, onOff, 0.949, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 175, onOff, 0.985, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 175, onOff, 0.985, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 176, onOff, 0.939, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 176, onOff, 0.939, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 177, onOff, 0.970, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 177, onOff, 0.970, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 179, onOff, 1.007, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 179, onOff, 1.007, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 180, onOff, 1.088, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 180, onOff, 1.088, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 181, onOff, 1.200, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 181, onOff, 1.200, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 183, onOff, 1.124, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 183, onOff, 1.124, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 184, onOff, 1.081, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 184, onOff, 1.081, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 185, onOff, 1.057, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 185, onOff, 1.057, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 187, onOff, 0.977, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 187, onOff, 0.977, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 188, onOff, 1.104, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 188, onOff, 1.104, E230, 12CO(2-1), 230.5380
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 189, onOff, 1.100, E090, CN(1-0), 113.4910
   0.06, 084.87717, +26.37417, RRTAU, 05 39 30.52, +26 22 27.0, 2010-09-15, 189, onOff, 1.100, E230, 12CO(2-1), 230.5380
      ALL FLUXES: 
      
J/ApJS/147/305 (Valenti et al. 2003): Pre-main-sequence stars observed by IUE with LW cameras
   _r, _RAJ2000, _DEJ2000, Seq, u_Seq, HBC, Name, OName, SpType, Cat, n_Cat, N, sp, SimbadName, Plot, _RA, _DE
   0.64, 084.87721, +26.37399, 108, , 170, RR Tau, AS 103, B8,9e{alpha}, HAEBE, , 3, , RR Tau, Plot, 084.87721, +26.37399
      ALL FLUXES: 
      
J/A+A/281/161 (Altenhoff et al. 1994): Radio emission from stars at 250GHz {\em(tables 4, 5, 6, 7, 8, 10 of paper)}
   _r, _RAJ2000, _DEJ2000, St, Name, SpType, l_S(250), S(250), e_S(250), l_alf, alf, e_alf, u_alf, Rem, SimbadName, _RA, _DE
   0.64, 084.87721, +26.37399, pMS, RR Tau, , <, 12.0, , , , , , , RR Tau, 084.87721, +26.37399
      ALL FLUXES: 
      0.11991700439453126   12.0   0.0   Radio emision from stars at 250GHz (30m)   10.0

It is very important to note that the parsing to the set of VizierElement objects depends on the actual catalogs supported for parsing. The supported catalogs are defined in VizierElement class, and are around 15 catalogs. The rest of catalogs in the XML document are ignored. To support other catalogs you will have to add by hand the correct entries in VizierElement class and to add the new catalog to the array called ALL, that contains all supported catalogs. This is required to map the names of the fields (that are different in each catalog, even when they mean the same) to the type of data that contains. For instance, J2000 right ascension is identified in VizierElement as RAJ2000, and called in Vizier RAJ2000 in some catalogs and _RA in others.

Query ADS abstracts

// Get all my abstracts as bibtex entries between a given year interval
String author = "Alonso-Albi, T.";
int year0 = 2012, yearf = 2012;
String query = ADSQuery.getAuthorQuery(author, year0, yearf);
 
String out = ADSQuery.query(query);
System.out.println(out);
 
// Now show the abstracts and download the PDFs	
String o[] = DataSet.toStringArray(out, FileIO.getLineSeparator(), true);
ADSElement ads = null;
for (int i=0; i< o.length; i++) {
	if (o[i].trim().startsWith("@")) {
		int n = o[i].indexOf("{");
		String abs = o[i].substring(n+1);
		if (abs.endsWith(",")) abs = abs.substring(0, abs.length()-1);
 
		ads = new ADSElement(abs);
	}
	if (o[i].trim().startsWith("title") && ads != null) {
		String title = o[i].trim();
		title = title.substring(title.indexOf("{")+1);
		title = title.substring(0, title.lastIndexOf("}"));    				
		System.out.println();
		System.out.println("*** "+title+" ***");
		System.out.println(ads.getAbstract());
		//ads.getArticle("/home/alonso/"+title+".pdf", 30000);
	}
}
Query Results from the ADS Database


Retrieved 2 abstracts, starting with number 1.  Total number selected: 2.

@ARTICLE{2012A&A...543A..27G,
   author = {{Ginard}, D. and {Gonz{\'a}lez-Garc{\'{\i}}a}, M. and {Fuente}, A. and 
	{Cernicharo}, J. and {Alonso-Albi}, T. and {Pilleri}, P. and 
	{Gerin}, M. and {Garc{\'{\i}}a-Burillo}, S. and {Ossenkopf}, V. and 
	{Rizzo}, J.~R. and {Kramer}, C. and {Goicoechea}, J.~R. and 
	{Pety}, J. and {Bern{\'e}}, O. and {Joblin}, C.},
    title = "{Spectral line survey of the ultracompact HII region Monoceros R2}",
  journal = {\aap},
archivePrefix = "arXiv",
   eprint = {1203.6466},
 primaryClass = "astro-ph.SR",
 keywords = {surveys, stars: formation, ISM: molecules, line: identification, astrochemistry, ISM: individual objects: Mon R2},
     year = 2012,
    month = jul,
   volume = 543,
      eid = {A27},
    pages = {A27},
      doi = {10.1051/0004-6361/201118347},
   adsurl = {http://adsabs.harvard.edu/abs/2012A%26A...543A..27G},
  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}

@ARTICLE{2012A&A...540A..75F,
   author = {{Fuente}, A. and {Caselli}, P. and {McCoey}, C. and {Cernicharo}, J. and 
	{Johnstone}, D. and {Fich}, M. and {van Kempen}, T. and {van Dishoeck}, E. and 
	{Y{\i}ld{\i}z}, U. and {Visser}, R. and {Kristensen}, L. and 
	{Alonso-Albi}, T. and {Herpin}, F. and {Tisi}, S.},
    title = "{The abundance of C$^{18}$O and HDO in the envelope and hot core of the intermediate mass protostar NGC 7129 FIRS 2}",
  journal = {\aap},
archivePrefix = "arXiv",
   eprint = {1202.5891},
 primaryClass = "astro-ph.GA",
 keywords = {astrochemistry, ISM: molecules, stars: formation, ISM: individual objects: NGC 7129 FIRS 2},
     year = 2012,
    month = apr,
   volume = 540,
      eid = {A75},
    pages = {A75},
      doi = {10.1051/0004-6361/201118478},
   adsurl = {http://adsabs.harvard.edu/abs/2012A%26A...540A..75F},
  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}



*** Spectral line survey of the ultracompact HII region Monoceros R2 ***
Context. Ultracompact (UC) Hii regions constitute one of the earliest
phases in the formation of a massive star and are characterized by
extreme physical conditions (G<SUB>0</SUB> &gt; 10<SUP>5</SUP> Habing
field and n &gt; 10<SUP>6</SUP> cm<SUP>-3</SUP>). The UC Hii Mon R2 is
the closest example and an excellent target to study the chemistry in
these complex regions. <BR /> Aims: Our goal is to investigate the
chemistry of the molecular gas around UC Hii Mon R2 and the variations
caused by the different local physical conditions. <BR /> Methods: We
carried out 3 mm and 1 mm spectral surveys using the IRAM 30-m telescope
towards three positions that represent different physical environments
in Mon R2: (i) the ionization front (IF) at (0″, 0″), and
two peaks in the molecular cloud; (ii) molecular Peak 1 (hereafter MP1)
at the offset (+15″, -15″); and (iii) molecular Peak 2
(hereafter MP2) at the farther offset (0″, 40″). In
addition, we carried out extensive modeling to explain the chemical
differences between the three observed regions. <BR /> Results: We
detected more than 30 different species (including isotopologues and
deuterated compounds). In particular, we detected SO<SUP>+</SUP> and
C<SUB>4</SUB>H confirming that ultraviolet (UV) radiation plays an
important role in the molecular chemistry of this region. In agreement
with this interpretation, we detected the typical photo-dissociation
region (PDR) molecules CN, HCN, HCO, C<SUB>2</SUB>H, and
c-C<SUB>3</SUB>H<SUB>2</SUB>. There are chemical differences between the
observed positions. While the IF and the MP1 have a chemistry similar to
that found in high UV field and dense PDRs such as the Orion Bar, the
MP2 is similar to lower UV/density PDRs such as the Horsehead nebula.
Our chemical modeling supports this interpretation. In addition to the
PDR-like species, we detected complex molecules such as
CH<SUB>3</SUB>CN, H<SUB>2</SUB>CO, HC<SUB>3</SUB>N, CH<SUB>3</SUB>OH,
and CH<SUB>3</SUB>C<SUB>2</SUB>H that are not usually found in PDRs. The
sulfur compounds CS, HCS<SUP>+</SUP>, C<SUB>2</SUB>S, H<SUB>2</SUB>CS,
SO, and SO<SUB>2</SUB> and the deuterated species DCN and C<SUB>2</SUB>D
were also identified. The origin of these complex species requires
further study. The observed deuteration fractionations, [DCN]/[HCN] ~
0.03 and [C<SUB>2</SUB>D]/[C<SUB>2</SUB>H] ~ 0.05, are among the highest
in warm regions. <BR /> Conclusions: Our results show that the high
UV/dense PDRs have a different chemistry from the low UV case. Some
abundance ratios such as [CO<SUP>+</SUP>]/[HCO<SUP>+</SUP>] or
[HCO]/[HCO<SUP>+</SUP>] are good diagnostics for differentiating between
them. In Mon R2, we have the two classes of PDRs, a high UV PDR towards
the IF and the adjacent molecular bar, and a low-UV PDR, which extends
towards the north-west following the border of the cloud.<p/>
Appendices A and B are available in electronic form at <A
href="http://www.aanda.org">http://www.aanda.org</A>


*** The abundance of C$^{18}$O and HDO in the envelope and hot core of the intermediate mass protostar NGC 7129 FIRS 2 ***
Context. NGC 7129 FIRS 2 is a young intermediate-mass (IM) protostar,
which is associated with two energetic bipolar outflows and displays
clear signs of the presence of a hot core. It has been extensively
observed with ground based telescopes and within the WISH guaranteed
time Herschel key program. <BR /> Aims: This paper is dedicated to the
modeling of the C<SUP>18</SUP>O and HDO lines in NGC 7129 FIRS 2. Our
goal is to investigate the chemistry in the envelope and hot core of
this IM protostar. <BR /> Methods: We present new observations of the
C<SUP>18</SUP>O 3 → 2 and the HDO 3<SUB>12</SUB> →
2<SUB>21</SUB> lines towards NGC 7129 FIRS 2. Combining these
observations with Herschel data and modeling their emissions, we
constrain the C<SUP>18</SUP>O and HDO abundance profiles across the
protostellar envelope. In particular, we derive the abundance of
C<SUP>18</SUP>O and HDO in the hot core. <BR /> Results: The intensities
of the C<SUP>18</SUP>O lines are well reproduced assuming that the
C<SUP>18</SUP>O abundance decreases through the protostellar envelope
from the outer edge towards the centre until the point where the gas and
dust reach the CO evaporation temperature (≈20-25 K) where the
C<SUP>18</SUP>O is released back to the gas phase. Once the
C<SUP>18</SUP>O is released to the gas phase, the modelled
C<SUP>18</SUP>O abundance is found to be ≈ 1.6 ×
10<SUP>-8</SUP>, which is a factor of 10 lower than the reference
abundance. This result is supported by the non-detection of
C<SUP>18</SUP>O 9 → 8, which proves that even in the hot core
(T<SUB>k</SUB> &gt; 100 K) the CO abundance must be 10 times lower than
the reference value. Several scenarios are discussed to explain this
C<SUP>18</SUP>O deficiency. One possible explanation is that during the
pre-stellar and protostellar phase, the CO is removed from the grain
mantles by reactions to form more complex molecules. Our HDO modeling
shows that the emission of HDO 3<SUB>12</SUB> → 2<SUB>21</SUB> line
is maser and comes from the hot core (T<SUB>k</SUB> &gt; 100 K).
Assuming the physical structure derived by Crimier et al. (2010), we
determine a HDO abundance of ~0.4-1 × 10<SUP>-7</SUP> in the hot
core of this IM protostar. Conclusions.Herschel data combined with
ground based observations have allowed us to estimate the
C<SUP>18</SUP>O and HDO abundance in the protostellar envelope and hot
core of an IM protostar. The HDO abundance in the hot core is ~0.4-1
× 10<SUP>-7</SUP>, similar to that found in the hot corinos NGC
1333 IRAS 2A and IRAS 16293-2422. The C<SUP>18</SUP>O abundance, at ≈
1.6 × 10<SUP>-8</SUP>, is a factor of 10 lower than the reference
value.<p/>
Herschel is an ESA space observatory with science instruments provided
by European-led Principal Investigator consortia and with important
participation from NASA.

Query CDS for coordinate transforms

Coordinate transformations can be done internally with JPARSEC, but also using the CDS web service. The CDS service allows to transform coordinates based on the Bessel epoch (FK4 system) to Julian ones (FK5 system). For instance, B1950 to J2000 or the opposite, although the years can be 1950 and 2000 or any others. CDS supports also the ICRS, ecliptic, galactic, and supergalactic systems. Let's see an example.

LocationElement loc = new LocationElement(
	Functions.parseRightAscension("06h 05m 22.0s"),
	Functions.parseDeclination("-06d 22' 25.0''"),
	1.0);
 
// Input/output epochs for coordinates set as years
double inputEpoch = 1950.0; // Bessel
double outputEpoch = 2000.0; // Julian
 
// Using CDS
System.out.println("CDS");
 
// ->
LocationElement out = CDSQuery.query(CDSQuery.FRAME.FK4, CDSQuery.FRAME.FK5, 
	loc, CDSQuery.PRECISION.MAS, inputEpoch, outputEpoch);
 
System.out.println(Functions.formatRA(out.getLongitude()));
System.out.println(Functions.formatDEC(out.getLatitude()));
 
// <-
out = CDSQuery.query(CDSQuery.FRAME.FK5, CDSQuery.FRAME.FK4, 
	out, CDSQuery.PRECISION.MAS, outputEpoch, inputEpoch);
 
System.out.println(Functions.formatRA(out.getLongitude()));
System.out.println(Functions.formatDEC(out.getLatitude()));
 
// Now with JPARSEC
System.out.println("JPARSEC");
 
// Obtain input/output epochs as JDs
double inEpoch = Constant.B1950 + (inputEpoch - 1950.0) * Constant.TROPICAL_YEAR;
double ouEpoch = Constant.J2000 + (outputEpoch - 2000.0) * 0.01 * Constant.JULIAN_DAYS_PER_CENTURY;
REDUCTION_METHOD method = EphemerisElement.REDUCTION_METHOD.IAU_1976; // Should be IAU 1976 to match CDS output
EphemerisElement eph = new EphemerisElement();
eph.ephemMethod = method;
 
// ->
double c[] = Precession.FK4_BxxxxToFK5_Jxxxx(loc.getRectangularCoordinates(), ouEpoch, inEpoch, eph);
out = LocationElement.parseRectangularCoordinates(c);
 
System.out.println(Functions.formatRA(out.getLongitude(), 5));
System.out.println(Functions.formatDEC(out.getLatitude(), 4));
 
// <-
out = LocationElement.parseRectangularCoordinates(Precession.FK5_JxxxxToFK4_Bxxxx(c, ouEpoch, inEpoch, eph));
System.out.println(Functions.formatRA(out.getLongitude(), 5));
System.out.println(Functions.formatDEC(out.getLatitude(), 4));

Previous code will convert B1950 to J2000 coordinates and then the resulting J2000 position back to B1950, using CDS and then JPARSEC. Results are identical as you can see below. JPARSEC allows more flexibility and accuracy, and CDS requires Internet connection.

CDS
06h 07m 48.2727s
-06º 22' 53.763"
06h 05m 22.0000s
-06º 22' 25.000"
JPARSEC
06h 07m 48.27269s
-06º 22' 53.7628"
06h 05m 22.00000s
-06º 22' 25.0000"

I have put more decimal places in the output from JPARSEC, but those from CDS are exactly the same for those number of decimals. You will need around 7 decimal places to start to see discrepancies between CDS and JPARSEC, and some indications of the better accuracy of JPARSEC when reverting back the correction to obtain exactly the same equatorial location used when the program started.

Query SkyView to create RGB images

In the section images - fits files I showed an example of separating an image into RGB channels to create a .fits files, and combine it back again. Another idea would be to automatically download the RGB images for a given source using SkyView, and then to combine them to create an RGB image of the source, using different surveys or bands for each channel. Check this simple piece of code:

// Main input values
String source = "M31"; // to be solved by Simbad
double field = 3; // degrees
int width = 1000; // pixels
 
// General properties for the queries
SKYVIEW_SURVEY surveyR = SKYVIEW_SURVEY.DSS2B; 
SKYVIEW_SURVEY surveyG = SKYVIEW_SURVEY.DSS2IR; 
SKYVIEW_SURVEY surveyB = SKYVIEW_SURVEY.IRAS60;
SKYVIEW_COORDINATE scoord = SKYVIEW_COORDINATE.EQUATORIAL_J2000;
SKYVIEW_PROJECTION sproj = SKYVIEW_PROJECTION.ZENITHAL_EQUAL_AREA;
SKYVIEW_LUT_TABLE slut = SKYVIEW_LUT_TABLE.GRAY;
SKYVIEW_INTENSITY_SCALE sscale = SKYVIEW_INTENSITY_SCALE.LINEAR;
boolean invertLevels = false, grid = false;
String catalog = null, contours = null;
int timeout = 30000; // ms
 
// Create the string queries
String queryR = GeneralQuery.getQueryToSkyView(source, surveyR, field, width, invertLevels, grid, scoord, sproj, slut, sscale, catalog, contours);
String queryG = GeneralQuery.getQueryToSkyView(source, surveyG, field, width, invertLevels, grid, scoord, sproj, slut, sscale, catalog, contours);
String queryB = GeneralQuery.getQueryToSkyView(source, surveyB, field, width, invertLevels, grid, scoord, sproj, slut, sscale, catalog, contours);
 
// Return the images
Picture picR = new Picture(GeneralQuery.queryImage(queryR, timeout));
Picture picG = new Picture(GeneralQuery.queryImage(queryG, timeout));
Picture picB = new Picture(GeneralQuery.queryImage(queryB, timeout));
 
// Combine the three images
Picture pic = new Picture(picR.getImageAsByteArray(0), picG.getImageAsByteArray(1), picB.getImageAsByteArray(2), null);
pic.show(source);
pic.write("/home/alonso/"+source+".png");

This code uses the B channel for the IRAS 60 micron survey, which is the one with the longest wavelength. The ussual way is to do the opposite and assign it the R channel, but I found the result as it is shown above more dramatic, since the output image has a red background due to the intense emission in the B band. The infrared emission is visible as purple in the espectacular output image:


 

Previous example requires a minute or so to download the images from SkyView. Another example would be to generate a realistic RGB view of an object, using the DSS2 survey with the IR, R, and B bands. Applying this to the M51 galaxy the output is:


 

Using SExtractor

SExtractor is another powerful tool to extract a catalog of sources from a given image. You should have this tool installed in your system, and currently it is only tested on Linux, although in Mac it should also work. Here is an example.

String dir = "/home/alonso/java/librerias/masymas/tres-3/";
String file = "TRES-3-025-070725-.fit"; // Input fits file at 'dir'
String config = "machine.config"; // Default configuration file of SExtractor, should be at 'dir'
 
SExtractor sex = new SExtractor(dir, config);
sex.execute(file);
 
// Show image and the brightest detected sources
FitsIO f = new FitsIO(dir+file);
Picture p = f.getPicture(0, PICTURE_LEVEL.LINEAR_INTERPOLATION, true);
Graphics2D g = (Graphics2D) p.getImage().getGraphics();
g.setColor(Color.RED);
for (int i=0; i<sex.getNumberOfSources(); i++)
{
	int r = sex.getDetectionWidth(i);
	int x = (int) (sex.getX(i).getValue()-r/2.0+0.5) - 1;
	int y = (int) (sex.getY(i).getValue()-r/2.0+0.5) - 1;
	g.drawOval(x, y, r, r);
}
 
System.out.println(sex.toString());
p.show(file);
p.write("/home/alonso/test.png");

Previous code will run SExtractor from command-line using the default machine.config file (that should be located in the same directory of the .fits file, although if not one is automatically created). This machine.config file contains the input parameters for SExtractor, and there's a method in the SExtractor class to create a new one with the parameters adapted to a given telescope and camera. A new default.param file with the required output parameters from SExtrator is created in each execution of the program. The example then reads the catalog returned by SExtractor and draws a circle around each of the sources detected, showing the image in the screen. A string representation of the instance is written to the console, including the list of sources detected. The output from the console and the image are below.

Detected 31 sources over a background flux of 1459.7. Maximum flux: 1303730.0
X (pixels)               Y (pixels)               size (pixels)            Flux                     
28.6890 (0.0011)         154.6620 (0.0009)        1.7x1.4                  1303700 (800)            
38.567 (0.003)           84.1710 (0.0024)         1.4x1.1                  146300 (300)             
300.286 (0.004)          181.476 (0.003)          1.4x1.0                  114430 (250)             
115.661 (0.005)          229.505 (0.004)          1.3x1.0                  67210 (210)              
454.430 (0.006)          248.532 (0.004)          1.4x0.9                  61730 (200)              
101.629 (0.007)          281.876 (0.005)          1.3x0.9                  34480 (160)              
16.682 (0.008)           271.333 (0.005)          1.3x0.9                  32480 (160)              
393.237 (0.008)          273.353 (0.005)          1.3x0.9                  31600 (160)              
172.752 (0.008)          333.981 (0.006)          1.2x0.9                  27260 (150)              
236.723 (0.009)          244.072 (0.007)          1.3x0.9                  24870 (150)              
356.221 (0.009)          164.456 (0.006)          1.2x0.9                  23760 (140)              
274.931 (0.011)          222.544 (0.007)          1.2x0.9                  19690 (140)              
231.592 (0.012)          347.433 (0.008)          1.2x0.9                  15680 (130)              
475.835 (0.013)          270.743 (0.009)          1.2x0.8                  12780 (120)              
77.895 (0.013)           59.286 (0.009)           1.1x0.9                  11780 (120)              
156.269 (0.014)          302.174 (0.009)          1.1x0.8                  10910 (110)              
471.933 (0.013)          337.526 (0.010)          1.1x0.9                  10600 (110)              
217.855 (0.012)          366.253 (0.009)          1.1x0.8                  10430 (110)              
435.132 (0.015)          350.420 (0.011)          1.1x0.8                  8630 (110)               
93.479 (0.015)           482.023 (0.011)          1.0x0.8                  6960 (90)                
13.281 (0.017)           179.045 (0.011)          1.0x0.8                  6800 (90)                
262.967 (0.016)          233.946 (0.011)          1.0x0.7                  6260 (90)                
510.579 (0.018)          75.100 (0.013)           1.0x0.8                  5210 (90)                
40.499 (0.018)           111.270 (0.013)          0.9x0.7                  4770 (90)                
62.437 (0.019)           352.688 (0.014)          0.9x0.7                  4470 (90)                
218.882 (0.021)          245.850 (0.013)          1.0x0.7                  4350 (90)                
219.471 (0.020)          14.959 (0.017)           0.8x0.7                  3070 (80)                
112.102 (0.019)          221.560 (0.012)          0.7x0.5                  2740 (70)                
235.882 (0.020)          453.691 (0.019)          0.7x0.7                  2530 (70)                
364.49 (0.03)            193.302 (0.013)          0.9x0.5                  2300 (70)                
77.303 (0.023)           274.010 (0.024)          0.7x0.7                  2070 (70)  


 

Later a practical use of SExtractor is shown with a program that produces the HR diagram of the globular cluster M12 from images taken by the ESO's VLT telescope.

Advanced use of JPARSEC

JPARSEC can be considered as a core tool to develope complex projects based on it. Here I will summarize some possible uses of JPARSEC beyond the development of simple programs to do specific tasks calling mainly JPARSEC functions. Among the possible software development projects I can think about, the main ones are to create models with GUI (Graphical User Interface), to develope Android projects, and to control telescopes and cameras. Some possible applications in science are listed also, although here there are many more possibilities depending on the field of research.

Tools developed with JPARSEC

Ephemerides server

There's a complete online ephemerides server located here. This server provides a lot of information about the sky as seen from Madrid or Tenerife (Spain), but can also provides charts for observers located at any other place. I would emphasize the quality and accuracy of the sky and planetary charts, simply above the quality of any other online resource I've seen. Respect accuracy you can follow satellite and shadow transits, mutual eclipses and occultations, solar/lunar eclipses, stellar occultations, and many other phenomena.

The 'Sky now' tab shows the current sky. If you clic twice on the second image you will see an animation of the sky for the current date as seen from Madrid or Tenerife:


 

The links tab gives access to more information, and also to a form to query ephemerides for a specific object. Everything (calculations and charts) is generated on the fly, except the sky animation which is automatically generated each midnight.

Respect to the implementation of the ephemerides server itself, there's an old, outdated document describing it here.

To generate the gif animation I use the AnimatedGifEncoder class you will find here, and the following Java code. getSky simply returns a frame to be encoded in the animation. At the end Gimp is used to optimize the size of the .gif file.

AnimatedGifEncoder e = new AnimatedGifEncoder();
e.start(outputFileName);
e.setDelay(2000);
e.setRepeat(0);
e.setQuality(1);
int nImg = 60*24, step = 60; // 1 day, 1 hour step
for (int i=0; i<=nImg; i=i+step) {
	double time = i * 24.0 / (60.0 * 24.0);
	int h = (int)time;
	int m = (int)((time-h)*60.0+0.5);
	args = new String[] {"all", year, month, day, ""+h, ""+m, ""+width, ""+width, place, "language="+lang};
	BufferedImage img = getSky(args);
 
	String name = "jparsec_skyFull_"+place+"_"+year+"_"+month+"_"+day+"_"+h+"_"+m;
	name = DataSet.replaceAll(name, "-", "_", true);
	if (img == null) {
		System.out.println("Could not generate image "+name);
	} else {
		try {
			Picture pic = new Picture(img);
			e.addFrame(pic.getImage());
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		System.out.println("Added image "+name);
	}
}
e.finish();
 
// Reduce file size automatically using gimp
String newPath = "/home/alonso/"+FileIO.getFileNameFromPath(outputFileName);
String gimp = "gimp -i -b \"(let* ( (image (car (file-gif-load RUN-NONINTERACTIVE \\\""+outputFileName+"\\\" \\\""+outputFileName+"\\\") ) ) ) (gimp-convert-indexed image 0 0 255 TRUE TRUE \\\"\\\") (file-gif-save RUN-NONINTERACTIVE image 0 \\\""+newPath+"\\\" \\\""+newPath+"\\\" 0 1 2000 0) )\" -b \"(gimp-quit 0)\"";
System.out.println("Compressing gif with gimp ... (executing "+gimp+")");
String script = "/home/alonso/gimp.sh";
WriteFile.writeAnyExternalFile(script, gimp);
Process p = ApplicationLauncher.executeCommand("chmod +x "+script);
p.waitFor();
p = ApplicationLauncher.executeCommand(script);
p.waitFor();
FileIO.deleteFile(script);

The video file is generated by rendering consecutive frames and applying at the end a script to transform them to a video. It is a very long program not documented or written properly, so it is not practical or adequate to put it here. The ephemerides server also puts into production the Feed class in the VO package to create RSS feeds (for astronomical events, not news).

Sky applet (and ClearSKY program)

In the ephemerides server and in the Other projects section it is possible to access the applet to explore the sky and planets. This program has evolved during years towards a quite interesting planetarium, full of useful options including the prediction of astronomical phenomena and charts of solar/lunar eclipses. The applet can query the UCAC3 catalog online directly to show stars up to magnitude 16 in fields of view below 3 degrees, which is a quite impressive feature to show globular clusters:


 

Another interesting feature recently added include the simulation of the Solar System from any point, not only the Earth. This includes simulating the sky from Mars or any other body, the simulation from any point within the Solar System, and also from the ecliptic north pole. The last case is useful to visualize the history of space exploration with most space probes launched so far.

The applet is launched using an html page that also includes complete documentation for the user.

Models/tools with GUI

There are currently six public tools with GUIs developed and available for downloading from the main JPARSEC page, that includes a basic description of each tool. Four of them are models for calculations and research, another is a presentation tool, and the last one the ClearSKY planetarium previously described (since current version is equivalent to the applet itself). I will not describe here the presentation tool, it is probably quite complicated, but after creating a few presentations with in I would not change back again to Latex or Powerpoint. An older, precursor version of the presentation tool is the S5Reloaded program, that can still be useful to export S5 Reloaded presentations (created for instance throught my DokuWiki plugin) to PDF.

The tools are relatively old-fashion, I would say. Components like buttons or lists are created using Swing direcly. I have started using MiG Layout project recently, which helps a lot to design GUIs. Since the source code is free and located inside the .jar files, I recommend you to study the code in case you would like to start developing your own GUIs. You should take into account that code documentation and quality is something I take seriously only in JPARSEC, and the code inside these tools is not very clean.


 

JPARSEC is designed to allow an easy distribution of these kind of tools to the user, including automatic updates. The jparsec.util.Module class can be used to create and maintain an object representing the program version, and the list and versions of each of the dependencies. This class is in fact used by jparsec.util.Version to maintain the current version and list of changes in JPARSEC. A binary file representing the corresponding Module object for JPARSEC or a given tool is written to disk (and distributed in the same directory where JPARSEC or other tools can be downloaded), so that by reading it it's possible to know which is the latest version of the program and their dependencies. It is probably uncommon to do this at source level, others would prefer to maintain the list of changes and the versions using a CVS-like tool, but … I prefer to keep most if not all inside the source code… And this also allows the JPARSEC Manager tool (the tool to install/update/launch the models) to present that list of changes when an update takes place.

Posible applications in science

It is difficult to write here a representative list of possibilities when using JPARSEC for astronomical research. One possibility I can think now is to access an online service to download many images of differente sources, then perform automatic photometry using SExtractor, and then create a complete PDF with the results using the LATEXReport class. For the image queries you only need the list of source names, since SimbadQuery can be used to solve them and retrieve their J2000 coordinates. In case the images are not in .fits format you can convert them and even add a WCS header.

I have used the ADSQuery class to retrieve a list of publications, including the PDFs and the abstracts, to create a formatted list of publications for a CV. In case you have many articles for your CV you can save many hours of hard and boring work.

The LATEXReport class can be useful to create automatic reports full of information. I use this to create a report of the work in an entire year. Another possible use is to create an astronomical almanac automatically, or to export Latex tables with the correct representation of measures (MeasureElement class) in the form value (error). The later case is interesting when you have thousands of measures you obviously cannot format yourself by hand in a reasonable short amount of time.

Another use I remember is to work with SEDFit tool from Java code to call Vizier for data on a sample of B-type stars not located in star forming regions. The idea was to check if any of these sources shows infrared excess.

Obviously, many more specific tasks are possible, but they depend on the field of research.

Below an example exercise to obtain the HR diagram of a globular cluster is detailed.

Obtaining the HR diagram of a globular cluster

As an example exercise I include here a program to obtain the HR diagram of the globular cluster M12. The exercise is a partial implementation of the fourth ESO astronomical exercise for students available here. Some .fits files for B and V bands are available for downloading, obtained by VLT telescope. The program I present here reads those files, solves all sources in them, and performs a cross-match of the stars in both images. From the catalog of resulting stars (around 2000) the B-V color indexes and effective temperatures are computed, and then the HR diagram is returned.

package jparsec.research;
 
import jparsec.astronomy.*;
import jparsec.graph.*;
import jparsec.io.image.*;
import jparsec.util.*;
import jparsec.vo.*;
 
import java.awt.*;
 
/**
 * An example of a program to obtain the HR diagram of a cluster. Test program included
 * uses the data from ESO at http://www.spacetelescope.org/projects/fits_liberator/m12data/.
 * See also the complete PDF exercise located at http://www.astroex.org/english/exercise4/.
 * @author T. Alonso Albi - OAN (Spain)
 * @version 1.0
 */
public class HR_DiagramOfCluster {
 
	/** Source name. */
	public String source;
	/** Path to the .fits image containing the B (blue) observation. */
	public String pathToBimage;
	/** Path to the .fits image containing the V (visible) observation. */
	public String pathToVimage;
 
	/**
	 * Constructor of the model. The model is not executed.
	 * @param s Source name.
	 * @param pathB Path to B image.
	 * @param pathV Path to the V image.
	 */
	public HR_DiagramOfCluster(String s, String pathB, String pathV)
	{
		this.source = s;
		this.pathToBimage = pathB;
		this.pathToVimage = pathV;
	}
 
 
	/**
	 * Executes the program and returns the HR diagram.
	 * @return The HR diagram for the cluster.
	 * @throws JPARSECException If an error occurs.
	 */
	public CreateChart calculate()
	throws JPARSECException	{
		// Read and process the fits files
		int hduIndex = 0, minArea = 3, sigma = 5;
		FitsIO fioB = new FitsIO(pathToBimage);
		System.out.println("Solving sources in B image ...");
		SExtractor sexB = fioB.solveSources(hduIndex, minArea, sigma);
		System.out.println("   Found "+sexB.getNumberOfSources());
 
		FitsIO fioV = new FitsIO(pathToVimage);
		System.out.println("Solving sources in V image ...");
		SExtractor sexV = fioV.solveSources(hduIndex, minArea, sigma);
		System.out.println("   Found "+sexV.getNumberOfSources());
 
		// Maximum error in pixels to match a star in the B image with its corresponding in the V image
		double tolerance = 0.5;
 
		// Here I assume B and V magnitudes for the brightest star in the B image, since in the 
		// SExtractor objects its index will be 0. For V image the star is identified/searched.
		// Values here are approximate for the brightest star, assumed to have B-V = 1. Real 
		// values require calibration data from the instrument.
		double brightestBstar = 13.2, brightestVstar = 12.2;
		double dmB = brightestBstar - Star.getMagnitudeDifference(sexB.getFlux(0).getValue(), 1);
		int index = identifyStar(sexB, sexV, 0, tolerance);
		double dmV = brightestVstar - Star.getMagnitudeDifference(sexV.getFlux(index).getValue(), 1);
 
		// Obtain list of B-V color indexes
		double BminusV[] = new double[0];
		double Mv[] = new double[0];
		for (int i=0; i<sexB.getNumberOfSources(); i++) {
			index = identifyStar(sexB, sexV, i, tolerance);
			if (index >= 0) {
				double fluxB = sexB.getFlux(i).getValue(), fluxV = sexV.getFlux(index).getValue();
				double B = Star.getMagnitudeDifference(fluxB, 1.0) + dmB;
				double V = Star.getMagnitudeDifference(fluxV, 1.0) + dmV;
				double BV = B - V;
				BminusV = DataSet.addDoubleArray(BminusV, new double[] {BV});
				Mv = DataSet.addDoubleArray(Mv, new double[] {V});
			}
		}
		System.out.println(BminusV.length+" sources cross matched from a total number of "+sexB.getNumberOfSources());
 
		// Complete data for the HR diagram
		double T[] = new double[BminusV.length];
		for (int i=0; i<BminusV.length; i++) {
			T[i] = Star.getStarTeff(BminusV[i], Star.LUMINOSITY_CLASS.MAIN_SEQUENCE_V);
		}
 
		// Create a chart
		ChartSeriesElement s = new ChartSeriesElement(T, Mv, null, null, "HR diagram of " + source, true, 
				Color.BLACK, ChartSeriesElement.SHAPE_CIRCLE, ChartSeriesElement.REGRESSION.NONE);
		ChartElement chart = new ChartElement(new ChartSeriesElement[] {s}, ChartElement.TYPE.XY_CHART, ChartElement.SUBTYPE.XY_SCATTER, 
				"HR diagram of " + source, "Effective temperature (K)", "Apparent magnitude", false, 800, 590);
		chart.xAxisInverted = true;
		chart.yAxisInverted = true;
		CreateChart c = new CreateChart(chart);
		return c;
	}
 
	// Identify a star in B in position index with one in V, for a given tolerance. -1 is returned if a match is not found.
	private int identifyStar(SExtractor B, SExtractor V, int index, double tolerance) throws JPARSECException {
		double x = B.getX(index).getValue();
		double y = B.getY(index).getValue();
		double minDist = -1;
		int out = -1;
		for (int i=0; i<V.getNumberOfSources(); i++) {
			double dist = Math.hypot(x - V.getX(i).getValue(), y - V.getY(i).getValue());
			if (dist < minDist || minDist == -1) {
				minDist = dist;
				out = i;
			}
		}
		if (minDist > tolerance) return -1;
		return out;
	}
 
	/**
	 * A program to use the model.
	 * @param args Ignored arguments.
	 */
	public static void main(String args[])
	{
		// TODO:
		// Cross mathing algorithm not optimized. Execution in 30s or less anyway
		// Errors not taken into account (SExtractor provides them, but they are ignored in this program)
		// Extinction not considered
		// Reference stars / photometric calibration required for better accuracy
		try {
			String pB = "/home/alonso/eclipse/libreria_jparsec/exerciseGlobularClustersTest/Bcomb.fits";
			String pV = "/home/alonso/eclipse/libreria_jparsec/exerciseGlobularClustersTest/Vcomb.fits";
			String source = "M12";
			HR_DiagramOfCluster dc = new HR_DiagramOfCluster(source, pB, pV);
			CreateChart c = dc.calculate();
			c.showChartInJFreeChartPanel();
		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

To obtain the V and B magnitudes for each star from the B and V fluxes returned by SExtractor I assume that the brightest star in the B image has mV = 12.2 and mB = 13.2. This is just approximate and for accurate results it is necessary to use the calibration data from the instrument to calculate those magnitudes. Unfortunately, that calibration data is not available inside the .fits files. Assuming previous values the method Star.getMagnitudeDifference is used to obtain the magnitude offset between a star with a given flux and a star with flux = 1 (usually called 0-magnitude correction). The data is used for calibrating the transformation flux B → mag(B) and flux V → mag(V) independently.

The output to the console and the image returned by the program is shown below. As you can see at the end of the code some tasks remain to be done, including optimizations, propagation of errors, and further calculations to obtain the distance to the cluster, which is the object of the ESO exercise. However, my intenction with this example is to show how this can be done in JPARSEC (specially the hard work), reading large .fits files and solving all sources in them. The ESO document propose to obtain the HR diagram from a representative (and limited) sample of stars, which is reasonable when the work is done by hand, but all stars can be computed with JPARSEC to do things faster, better, and without errors. In fact, I wrote this program in just 30 minutes, no issue with .fits reading/solving was found.

Solving sources in B image ...
   Found 3721
Solving sources in V image ...
   Found 6109
1907 source cross matched from a total number of 3721


 

Telescope control

Telescope (and camera) control, as well as tools to perform automatic observations and data reduction is a long span project I have. The idea is to provide complete control and data reduction for the ClearSKY Java program, in a clear and simple way. The final idea is to design a set of observations for a given night, setting the set of sources, and leaving the telescope alone. Depending on the observation the automatic reduction will later take care of constructing light curves or whatever.

Currently the jparsec.io.device package contains mainly support for some popular Meade and Celestron telescopes, using a serial (RS232) interface. The implementation is very complete and quite robust, although I have only tested it on a Meade telescope, Celestron support is only on best effort. In addition, the interface GenericTelescope I use to set the possible commands is also implemented in the VirtualTelescope class, that allows to use a virtual telescope that emulates a real one.


 

The above image shows the virtual telescope in action. The panel is written using the MiG Layout manager project, with some fancy and recent additions to JPARSEC (TextLabel class) to render digital clock-like numbers. At the top a set of icons are used to visually show the status of the telescope: on/off, aligned, polar/azimuthal mounted, tracking, moving (in RA/DEC like in a goto, without considering tracking as movement), the dome, and the weather station.

A similar interface as that for the telescopes is implemented for the cameras, supporting currently digital SLR cameras using the gphoto2 project under Linux, and webcams using mplayer also in Linux. With the VirtualBox program it is possible to use the software of any Canon SLR, which is great, but to automatically shot many observations a specific control like this is required. I wouldn't discard adding support for more telescopes or CCD cameras, but for now it is out of the scope. Other devices are implemented also as virtual, like the dome or the weather station. Currently there's no specific implementation or driver for a real device of these types.


 

The telescope and observations control panel are launched from the main method of the class jparsec.io.device.TelescopeControlPanel, using the following code:

JFrame app = new JFrame(Translate.translate(1127));
app.addWindowListener(new WindowAdapter() {
	public void windowClosing(WindowEvent evt) {
		System.exit(0);
	}
});
app.setIconImage(ReadFile.readImageResource(FileIO.DATA_IMAGES_DIRECTORY+"telescope_transparentOK.png"));
 
// Set the hardware
TELESCOPE_MODEL telescopeModel = TELESCOPE_MODEL.VIRTUAL_TELESCOPE_EQUATORIAL_MOUNT; //.MEADE_AUTOSTAR;
DOME_MODEL domeModel = DOME_MODEL.VIRTUAL_DOME;
CAMERA_MODEL cameraModel[] = new CAMERA_MODEL[] {CAMERA_MODEL.VIRTUAL_CAMERA}; //CAMERA_MODEL.CANON_EOS_40D_400D_50D_500D_1000D;
WEATHER_STATION_MODEL weatherStation = WEATHER_STATION_MODEL.VIRTUAL_WEATHER_STATION;
ObservationManager obsManager = new ObservationManager("/home/alonso/", "today", telescopeModel, cameraModel, domeModel, weatherStation);
obsManager.setCameraMinimumIntervalBetweenShots(0, 20);
obsManager.setCombineMethod(COMBINATION_METHOD.MEDIAN);
obsManager.setInterpolationMethod(INTERPOLATION.BICUBIC);
obsManager.setDrizzleMethod(DRIZZLE.NO_DRIZZLE);
obsManager.setTelescopeParkPosition(new LocationElement(0, Constant.PI_OVER_TWO, 1)); // Park to the zenith
// Ports for telescope and camera are set to null to automatically scan and select the first one available
boolean addSky = true;
 
TelescopeControlPanel tcp = new TelescopeControlPanel(obsManager, addSky);
Dimension d = tcp.getPreferredSize();
 
// Border + window title
d.height += 80;
d.width += 10;
 
app.add(tcp);
app.setSize(d);
app.setVisible(true);
 
if (obsManager.reductionPossible()) {
	JFrame app2 = new JFrame(Translate.translate(1188));
	app2.addWindowListener(new WindowAdapter() {
		public void windowClosing(WindowEvent evt) {
			System.exit(0);
		}
	});
	app2.setIconImage(ReadFile.readImageResource(FileIO.DATA_IMAGES_DIRECTORY+"planetaryNeb_transparentOK.png"));
	Dimension d2 = obsManager.getPreferredSize();
	d2.height += 80;
	d2.width += 10;
	app2.add(obsManager);
	app2.setSize(d2);
	app2.setVisible(true);
}

Since this is a long term project the addings to the JPARSEC library or ClearSKY program will be relatively slow, but hopefully with constant improvements. I will give maximum priority to Linux operating system, although the current implementation of telescope control is cross platform and should work on Windows and Mac.

Android development

Another project I have is to develope a planetarium for Android. Initial developments are documented here and here. The application program can be downloaded from the JPARSEC page, but since this project is already in production phase the recommended way is to download the latest version from the ClearSky Free page at Google Play. The program requires Android 2.3 or later.


 

JPARSEC is 'Android ready' for any project you could think about. Just remember that since it is GPL you must distribute the source code of your program and, in case of publishing the program in Google Play, it should be free or 0-cost for the users. The code for the first alpha of the ClearSKY project can be downloaded from the main JPARSEC page, although the code for the AndroidGraphics class is (still) not public.

Final recomendations

This document shows that JPARSEC is designed as a set of reusable and flexible bricks that can be interconnected to develope from very simple to quite complex programs, performing complex operations with a minimum number of lines of code. I have described the most important features of JPARSEC in detail, skipping the less important ones you will probably don't need or can explore yourself easily (astrometry, artificial satellites, ephemerides and renderings from outside the Earth, some math and many data handling (DataSet class) functions, clipboard and print operations, VO utilities like feeds, FTP, mails, VO tables …). Many tools are general and not astronomy-related, while others are specially designed for astronomy and astrophysics, and some of them specific to radioastronomy and star formation. Obviously this is my field of research, but JPARSEC can be easily extended to include other fields.

From time to time I explore Internet searching for different free Java libraries to do whatever it is useful in other fields. In case I see potential use in astrophysics I test the library, create some examples, and in case I find it useful I include it in JPARSEC, maybe writing a class to call that library in a more confortable way, and interconecting it with the rest of JPARSEC.

The development of JPARSEC started just after I joined Observatorio Astronomico Nacional for a PhD grant. I was at that time quite sure about how to focus my research, but could not anticipate the productivity that this project would give me, and how much I would learn with it. During these years, currently more than 10, I've spent a large fraction of my time developing and organizing these reusable bricks, to inmediately take advantage of them by reducing the time needed to do lots of tasks that others do by hand or with less automated programs (or can't at all). At the same time, this project have given me the opportunity to collaborate with people interested in JPARSEC, from amateur astronomers and software developers to an engineer at JPL working in the Mars Curiosity rover mission. With JPARSEC I feel I can work more efficiently (with less errors and more productivity and quality), I can spread my work into multiple areas (from science research to ephemerides servers, amateur astronomy, or divulgation), and I can collaborate with others (providing useful algorithms or taking also useful ideas/algorithms from others). The result of this is a high degree of satisfaction with the results of applying this working methodology, and the hope that all this work will be used and extended by others in the future.

In the following subsections I suggest some tips for an efficient and productive use of JPARSEC. Problems not discussed above include how to share data between the different classes inside a Java application without creating a lot of spreaded 'public static' content, how to save Java objects to disk and retrieve it, how to set different levels of logging in an application to get debug messages, how to improve performance and reduce memory requirements, and how to update JPARSEC (although updates are automatic if you use the GUI tools and you can always download an updated version by hand). In that last subsection I also mention how to support other languages in JPARSEC.

Logging with JPARSEC's Logger class

To set debug or info messages inside a program or library is a good thing. In Java there are libraries for these purpose, but in my case I don't like to add external dependencies to my programs unless they are useful extensions. I won't add a logging library just to be able to see debug messages, I prefer to have my own class for that and avoid more and more .jar files in the classpath. This is why there's a jparsec.util.Logger class in JPARSEC, but of course you can prefer to use an external library if you are used to.

In any place of your program you can write something like this:

Logger.setLoggerLevel(LEVEL.WARNING);
Logger.log(LEVEL.TRACE_LEVEL1, "This is a trace message of lowest priority");
Logger.log(LEVEL.INFO, "This is an info message, also not shown");
Logger.log(LEVEL.WARNING, "Warning, you will see this");
Logger.log(LEVEL.ERROR, "Error!");

First line sets the logging level in your application, it can be modified in other parts of the program execution. The rest of lines adds debug messages of different levels in importance. Logger includes up to 7 levels: TRACE_LEVEL1, TRACE_LEVEL2, TRACE_LEVEL3, CONFIG, INFO, WARNING, ERROR.

Logger includes the possibility of writting all logs to an external file. By default this is not enabled, but you can add at the beggining of your program a line like this:

Logger.configFile = FileIO.getTemporalDirectory() + "log.txt";

In this case the Logger will write the messages to a log.txt file located in the system's default temporal directory. In Linux it will be /tmp/log.txt. Of course you can set it to your home or documents folder.

Trace levels can be used inside loops to check some values (although take into account that this can slowdown your program, specially when writting large amount of lines to a file), info level to show general information, and so on. Setting the logging level to warning means than only warnings and errors will be show in the console or written to the log file.

Sharing data inside an application or from files

To share data between the different classes in an application is very useful. One way to achieve this is to set static content in some class/classes of type public, to allow access from any other class. This approach works well for a small program but it is not good idea for more elaborated applications.

JPARSEC contains a class jparsec.util.DataBase for this task. The DataBase allows to add objects or data using an identifier and selecting if the data should be hold in memory or in disk, generating in the last case files in the system's temporal directory (not recommended since this is quite slow). The data is saved with the identifier of the data and also that of the thread creating the data. Later the data can be recovered from the same thread or from any other, but using different methods in DataBase class. In Java you should not assume that the thread reading the data will be always the same as the one that wrote it. It is true as a general rule, but not always the case in applications with a graphical user interface that responds to mouse or key events, since Java internally uses several threads for different tasks or interactions.

String id = "myData";
if (DataBase.dataExists(id, false)) id = "anotherName";
DataBase.addData(id, new String[] {"1", "2", "3"}, true);
String list[] = (String[]) DataBase.getData(id);

In previous example a list of three numbers are saved as a String array with new myData, and later returned back. In case there's some data already with that identifier the data will be replaced, so in case you don't wan't that you must change the identifier and check if it is used or not. Take into account that some identifiers are used inside the JPARSEC library and replacing them can produce errors or wrong results. The DataBase class also allows to give a 'life time' to the objects stored, so that in case they are retrieved after a given amount of time, the data is deleted and null (no data) is returned. You can replace your data with null to remove them, and in case the data is stored in files they are created as temporal files, so they are automatically deleted when your program finishes.

You can put here any Java or JPARSEC objects with one condition (in case you use files and not memory): they should be serializable objects. A serializable object can be written to a binary file with all their properties, and later recovered back in exactly the same state. This happens with almost all JPARSEC objects and many Java ones, but to recover in this way frame or image objects as a whole is not possible. Serialization is used in DataBase when writing to files, and also in the models with graphical user interface to write the status of the program and recover it. The JPARSEC class for these tasks is jparsec.io.Serialization.

With Serialization you can write an object to disk and read it back, unless an exception is thrown in the writting process in case the object is not serializable. In this case you will have to write only the portions of this object that are serializable, as an array of objects for instance, and write a method to create a new object by including the serializable portions. In fact, using this process you can convert a non serializable object into serializable, since Java defines a way to do this in case you implement the interface java.util.Serializable, writting the appropriate methods. An example is the class jparsec.graph.CreateChart class. When defining the class you will see it implements the Serializable interface, more exactly in the following code included in CreateChart:

private void writeObject(ObjectOutputStream out) throws IOException {
	out.writeObject(this.chart_elem);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
	this.chart_elem = (ChartElement) in.readObject();
	try {
		JFreeChart chart = createChart(this.chart_elem);
		this.setChart(chart);
	} catch (Exception exc) { }
}

The idea is that a chart is defined by a ChartElement object, which is serializable. An option is to write to disk the ChartElement object, but in case someone wants to write a CreateChart, Java will write in fact only the ChartElement, and later it will create the internal objects required in CreateChart using the ChartElement as the unique source. As I have said above, for serializable objects this is not necessary.

Serialization will allow you to save an object representing the status of your model, for instance, and read it back. One problem is when your class definition changes between when you wrote it and when you want to read it back (new field names), but there are solutions to this problem besides saving old versions of your program. Some people prefer to deal with XML code to store properties, but this is out of the scope of this section.

Improving performance and memory requirements with JVisualVM

Java and Eclipse provides a lot of useful tools to help developing code in a relatively clean and fast way, in case you decide to use these features. Of course, you can always write terrible code in Java or any other languague, but to write terrible code in Java is, let's say, not as easy as with Fortran or C. Respect performance and memory handling in Java the same can be applied. Memory handling is automatically done by Java itself, so compared to C++ you have less work here, but there's always some margin to reduce the memory that you program consumes. The main problem is to know which methods are consuming more CPU time or which objects are consuming more memory, so that you can work in specific parts of your code to get a relatively quick and important performance boost. For this task a profiling tool like JVisualVM is extremely useful.

JVisualVM can be launched from command line with the command jvisualvm. In case you have Java installed but you don't see this command, search for jvisualvm in the jre/bin/jvisualvm or install a complete version (JDK) from Oracle's website.


 

Previous image shows an example with the SkyChart component. JVisualVM can run in Sampler or in Profiler modes, the last one giving more accurate and rich information. For first profilings the Sampler is enough, but I recommend to click on the Snapshot button to analize the results throughoutly, specially in the Profiler mode.

In my blog I have a large number of posts showing the evolution of the optimizations in the SkyChart component. Speed was at the beginning around 15-20 fps (after some optimizations), and the methods consuming most of the CPU time were those writen by me (loops or whatever). After a few months of hard work SkyChart now reach 120 fps, and the CPU time is spent mostly on drawing operations which belongs to internal Java implementation. 120 fps is really impressive considering that everything is done in CPU, without any work delegated to the GPU (graphics card). In the chart above you see 28% in a renderize() method (there's a drawImage there spending that 28%), another 12% in projectPosition() (for stereographic coordinates, using a class called FastMath to apply very fast but approximate trigonometric functions), and the rest of CPU is spent again on drawing (more than 80% in total). In fact, I had to replace some of those intrinsic drawing methods by faster implementations with slightly less visual quality to reach the maximum possible performance when dragging the sky with the mouse. Memory consumption was also reduced by a factor 8 at the same time. Without this the Android planetarium would never had been possible.

JVisualVM can open any Java program currently running, so you can check other programs for CPU or memory bottlenecks.

Updating and configuring JPARSEC

Updating JPARSEC is mostly my own responsibility, but in some cases you may want to update some files yourself. If you open jparsec.util.Update class you will see the code I use for updating jparsec. This involves updating jparsec.jar, eop.jar, sunspot.jar, orbital_elements.jar, and sky.jar. JPL and COLOGNE catalogs are not updated anymore because it seems that JPL changed the format of the catalog or decided to eliminate the information about hyperfine transitions in some molecules.

First lines in Update class show links to Internet to update certain things by hand. I usually start with them (updating the txt files in jparsec.time package), and later I execute Update class and test the new files with the SkyChart component. Before any release I also execute the tests in jparsec.test.Test class.

There are some configuration options available in the class jparsec.util.Configuration. They are not very important, but you may want to modify some values. For instance, the catalog of orbital elements of comets/asteroids is updated 2 or 3 times during the year (orbital_elements.jar file), but JPARSEC is configured to try to download updated orbital elements for these bodies in intervals of 15 days, saving the elements to a file in a temporal directory. In case elements in orbital_elements.jar are old the temporal file is used, but in case that file is also old, a new one is downloaded. The rules applied in this process can be customized.

The jparsec.util.Translate class is responsible for all string translations in JPARSEC. Currently only Spanish and English are supported. In case you want to add a new language you have to create a txt file in jparsec.util package, translate each line into the desired language, and add an entry to the LANGUAGE enum at the beginning of Translate class. In case the language is, let's say, French, add the enum FRENCH and create a file named french.txt (and email me that file!!!).

Here is an example on how to use the Update class.

Configuration.FORCE_JPARSEC_LIB_DIRECTORY = "/home/alonso/eclipse/workspace/mis_programas/jparsec/manager/lib/";
AstroDate astro = new AstroDate();
 
// orbital_elements.jar
Update.updateOrbitalElementsFromMPC();
Update.updateOrbitalElementsOfNaturalSatellites();
Update.updateSizeAndMagnitudeOfArtificialSatellites();
Update.updateOrbitsOfVisualBinaryStars();
 
// eop.jar
Update.updateEOPparameters();
 
// sky.jar
Update.updatePadovaAsiagoSNcat();
Update.updateNovae();
 
// sunspot.jar
Update.updateSunSpotsDatabase(astro.getYear());
Update.updateSunSpotsDatabase(astro.getYear() - 1);

The previous code first forces the library to use as reference the path to the dependencies located at the /lib directory of the jparsec manager. When a new version of the library is released, it is enough to execute the manager to update the orbital elements or whatever file in located in this library, in case a new version of that file is available. In case you selected these files for the build path of jparsec and your projects in Eclipse, the configuration of your projects will be also updated. In case you prefer to update some of those files by yourself you can use code like the previous one, at your own risk (you should backup the files to be updated first).

In case you are reading this it seems you succesfully reached the end of this tutorial. I hope you will find JPARSEC useful for your research and developments. If you have any comment, problem, or suggestion when using JPARSEC just ask in the discussion box below or send a message to:

t.alonso@oan.es

 

Happy coding!

Tomás Alonso, June 2013

Discussion

Enter your comment
 
 
jparsec_wiki.txt · created: 2013/06/05 16:21 (Last modified 2017/10/12 13:05) by Tomás Alonso Albi
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki