package sedplugin.plugin;
import java.awt.Color;
import java.io.File;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.astrogrid.samp.Client;
import org.astrogrid.samp.Message;
import org.astrogrid.samp.Metadata;
import org.astrogrid.samp.Response;
import org.astrogrid.samp.client.DefaultClientProfile;
import org.astrogrid.samp.client.HubConnector;
import org.astrogrid.samp.client.ResultHandler;
import org.astrogrid.samp.client.SampException;
import sedplugin.errors.ErrorsLog;
import sedplugin.errors.ErrorsNotifier;
import sedplugin.errors.SEDException;
import sedplugin.graphe.FrameOfReference;
import sedplugin.graphe.Serie;
import sedplugin.interfaceGraphique.ISEDPluginGI;
import sedplugin.interfaceGraphique.SEDFrame;
import sedplugin.sed.SED;
import sedplugin.sed.source.SEDSource;
import sedplugin.sed.source.SEDSourceListener;
import sedplugin.sed.source.SourcesGroup;
import sedplugin.sed.source.aladin.PhotometrySource;
import sedplugin.sed.source.operation.Operation;
import sedplugin.sed.unit.XAxis;
import sedplugin.sed.unit.YAxis;
import cds.aladin.AladinException;
import cds.aladin.AladinPlugin;
import cds.tools.VOApp;
/**
* Aladin plugin to create, display and manage SED (Spectrum Energy Distribution)
* extracted from the images loaded in Aladin.
*
* @author Grégory Mantelet (CDS)
* @version 06/2012
*/
public class SEDPlugin extends AladinPlugin implements SEDSourceListener {
/** PLUGIN VERSION METADATA */
public final static String VERSION = "v2.0 - 15/06/2012 - Aladin v7.510";
/** SEDPlugin ICONS DIRECTORY */
public final static String ICONS_DIR = "/sedplugin/interfaceGraphique/icons/";
/** SEDPlugin MAIN DIRECTORY */
public final static String SEDPLUGIN_DIR = System.getProperty("user.home")+File.separatorChar+".aladin"+File.separatorChar+"SEDPlugin";
/** PATH OF THE SEDPlugin CONFIGURATION FILE */
public final static String SEDPLUGIN_CONF_FILE = SEDPLUGIN_DIR+File.separatorChar+"SEDPlugin.conf";
/** MType used to send a VOTable via SAMP. */
protected final static String SEND_VOTABLE = "table.load.votable";
/** MType used to send a SSA via SAMP. */
protected final static String SEND_SSA = "spectrum.load.ssa-generic";
/** Number of file sent via SAMP. Used to build the name of the file which will be read by the SAMP destination application.*/
protected static int nbFileSentBySamp = 0;
/** The SAMP connection between the plugin and a SAMP hub. */
protected HubConnector sampConnector = null;
/** Indicates whether the plugin is already running. */
protected boolean running = false;
protected AladinProxy proxy = null;
protected XAxis abscissa = null;
protected YAxis ordinate = null;
protected SEDSourcesList sedSources = null;
protected ISEDPluginGI gui = null;
public SEDPlugin() { ; }
/* ****************** */
/* PLUGIN DESCRIPTION */
/* ****************** */
@Override
public String menu() { return "SEDPlugin"; }
@Override
public String category() { return "Image"; }
@Override
public String description() { return " SEDPlugin is an Aladin plugin which allows to generate interactively SED (Spectral Energy Distribution) from images loaded in Aladin.\n\n Built spectrums can be exported in several formats (ASCII, XML, VOTable) or they can be sent to SAMP compatible application (i.e. VOSpec, Splat-VO, Topcat)."; }
@Override
public String author() { return "Gregory Mantelet [CDS]"; }
@Override
public String version() { return "v2.0 - 15/06/2012 - Aladin v7.510"; }
@Override
public String url() { return "http://aladin.u-strasbg.fr/java/Plugins/SEDPlugin.jar"; }
@Override
public String scriptCommand() { return "sedPlugin"; }
@Override
public String scriptHelp() { return " USAGE: "+scriptCommand()+"\nStart the plugin \"SEDPlugin\"."; }
@Override
public boolean isRunning() { return running; }
/* ************************** */
/* INIT & FINALIZE THE PLUGIN */
/* ************************** */
@Override
public void exec() throws AladinException {
if (running) return;
running = true;
// Load user preferences:
UserPreferences.setCurrentInstance(new File(SEDPLUGIN_CONF_FILE));
// SAMP:
registerToSamp();
// Initialize the units:
try{
abscissa = new XAxis();
ordinate = new YAxis(abscissa);
}catch(SEDException se){
se.printStackTrace();
running = false;
return;
}
sedSources = new SEDSourcesList(5);
// Initialize the connection with Aladin:
proxy = new AladinProxy(aladin, this);
// Initialize the GUI:
gui = new SEDFrame(this, new FrameOfReference(abscissa, ordinate));
ErrorsLog.getCurrentInstance().setNotifier((ErrorsNotifier)gui);
proxy.setLoaderListener(gui.getAladinProgressListener());
gui.setVisible(true);
//aladin.addObserver(proxy, VOApp.POSITION);
aladin.addObserver(proxy, VOApp.MEASURE);
aladin.addObserver(proxy, VOApp.STACKEVENT);
// Synchronize the plugin with Aladin immediately:
proxy.synchronize();
}
@Override
public void cleanup() {
if (!running) return;
gui.close();
running = false;
// Unregister the plugin from SAMP:
unregisterFromSamp();
aladin.addObserver(proxy, 0);
proxy.stopSynchronizing();
proxy = null;
sedSources = null;
abscissa = null;
ordinate = null;
// Save user preferences:
UserPreferences.saveCurrentInstance(new File(SEDPLUGIN_CONF_FILE));
// Write an errors log if asked:
if (UserPreferences.getCurrentInstance().saveErrorsLog)
ErrorsLog.getCurrentInstance().writeLog(new File(SEDPLUGIN_DIR));
// Clear the errors manager:
ErrorsLog.getCurrentInstance().clear();
ErrorsLog.getCurrentInstance().setNotifier(null);
ErrorsLog.setCurrentInstance(null);
UserPreferences.setCurrentInstance((UserPreferences)null);
super.cleanup();
}
/**
* Gets the X unit used by all the SEDs of this plugin.
*
* @return The X unit.
*/
public final XAxis getXAxis(){
return abscissa;
}
/**
* Gets the Y unit used by all the SEDs of this plugin.
*
* @return The Y unit.
*/
public final YAxis getYAxis(){
return ordinate;
}
/**
* Gets the object which manages the interaction with Aladin.
*
* @return The Aladin interaction manager.
*/
public final AladinProxy getAladinProxy(){
return proxy;
}
/* ********************** */
/* SED SOURCES MANAGEMENT */
/* ********************** */
/**
* Adds the given source in the list of all managed sources, and
* adds itself as listener of the given source.
*
* @param src The SED source to add.
* @return true if the source has been successfully added, false otherwise.
*/
public boolean addSEDSource(final SEDSource src){
if (src != null && sedSources.add(src)){
src.addSEDSourceListener(this);
return true;
}
return false;
}
/**
* Gets the index-th SED source.
*
* @param index The index of the source to get.
* @return The corresponding SED source.
*/
public final SEDSource getSEDSource(final int index) {
return sedSources.get(index);
}
/**
* Gets an iterator on all managed SED sources.
* @return An iterator of {@link SEDSource}.
*/
public final Iterator getSEDSources(){
return sedSources.iterator();
}
/**
* Gets an array which gathers all managed SED sources.
* @return All managed SED sources.
*/
public SEDSource[] getAllSEDSources() {
return sedSources.toArray();
}
/**
* Gets the number of all managed SED sources.
* @return The number of sources.
*/
public final int getNbSEDSources(){
return sedSources.size();
}
/**
* Removes the given source from the list of all managed sources,
* and removes itself from the listeners of the given source.
*
* @param sedSrc The SED source to remove.
* @return true if the source has been successfully removed, false otherwise (especially if this source is not managed in this plugin).
*/
public final boolean removeSEDSource(final SEDSource sedSrc){
if (sedSrc != null){
boolean removed = sedSources.remove(sedSrc);
sedSrc.removeSEDSourceListener(this);
if (sedSrc instanceof Operation)
((Operation)sedSrc).unwatchSEDs();
//hideSEDs(sedSrc);
return removed;
}else
return false;
}
/**
* Removes the index-th source from the list of all managed sources,
* and removes itself from the listeners of this source.
*
* @param index The index of the SED source to remove.
* @return true if the source has been successfully removed, false otherwise (especially if this source is not managed in this plugin).
*/
public final boolean removeSEDSource(final int index){
SEDSource sedSrc = sedSources.remove(index);
sedSrc.removeSEDSourceListener(this);
//hideSEDs(sedSrc);
return (sedSrc != null);
}
/**
* Removes all the specified sources from the list of all managed sources,
* and removes itself from the listeners of these sources.
*
* @param indexes The indexes of the SED sources to remove.
*/
public final void removeSEDSources(final int[] indexes){
for(int i=indexes.length-1; i>=0; i--){
removeSEDSource(i);
}
}
/* *************************** */
/* SED SOURCE LISTENER METHODS */
/* *************************** */
public void sedAdded(SED newSed, SEDSource src) {
showSED(newSed);
if (UserPreferences.getCurrentInstance().whenDeactivatingInteractions() == UserPreferences.AFTER_EACH_ONE)
gui.enableAladinSync(false);
}
public void sedRemoved(SED removedSed, SEDSource src) {
if (removedSed != null && removedSed.isEmpty())
hideSED(removedSed);
}
/* ************** */
/* SED MANAGEMENT */
/* ************** */
public final Iterator getSEDs(){
return sedSources.getSEDs();
}
public final int getNbSEDs(){
int count = 0;
Iterator it = getSEDs();
while(it.hasNext()){
count++;
it.next();
}
return count;
}
public final void showSED(final SED sed){
if (sed != null)
gui.addSerie(sed);
}
public final void hideSED(final SED sed){
if (sed != null){
// Remove the corresponding serie from the drawn graph:
gui.removeSerie(sed);
// Update its sources...
SEDSource src = sed.getSource();
if (src != null){
// If its source is an photometry connected to Aladin, only freeze the source so that avoiding any automatic refresh:
if (src instanceof PhotometrySource && !((PhotometrySource)src).isDisconnectedFromAladin()){
src.freeze(true);
src.removeSED();
} // Else, merely remove the source:
else
removeSEDSource(src);
}
}
}
public final void hideSEDs(final SED[] seds){
for(SED s : seds)
hideSED(s);
}
public final void hideSEDs(final SEDSource sedSrc){
if (sedSrc == null)
return;
if (sedSrc instanceof SourcesGroup){
Iterator it = ((SourcesGroup)sedSrc).getSEDs();
while(it.hasNext())
hideSED(it.next());
}else
hideSED(sedSrc.getSED());
}
/**
* Gets the color of the corresponding {@link Serie} on the graph.
*
* @return The SED color on the graph.
*/
public final Color getColor(final SED sed){
Serie serie = gui.getSerie(sed);
return (serie == null) ? null : serie.getColor();
}
/* ***************** */
/* IMAGES MANAGEMENT */
/* ***************** */
/**
* Adds the given Aladin image into the GUI.
* Actually, this image is added into the images list of the Characterization editor.
*
* @param img The Aladin image to add.
*
* @see SEDFrame#addImage(AladinImage)
*/
public void addImage(final AladinImage img){
if (img != null)
gui.addImage(img);
}
/**
* Removes the given Aladin image from the GUI.
* Actually, this image is removed from the images list of the Characterization editor.
*
* @param img The Aladin image to remove.
*
* @see SEDFrame#removeImage(AladinImage)
*/
public void removeImage(final AladinImage img){
if (img != null)
gui.removeImage(img);
}
/* ************* */
/* SAMP MANAGING */
/* ************* */
/**
* Tells whether SEDPlugin is connected to SAMP:
* @return true if SEDPlugin is connected, false else.
*/
public final boolean isSampConnected(){
return sampConnector != null && sampConnector.isConnected();
}
/**
* Ensures the SEDPlugin is connected to SAMP.
* @return true if SEDPlugin is connected, false else.
*/
private final boolean registerToSamp(){
if (!isSampConnected()){
// Create a SAMP hub connection:
sampConnector = new HubConnector(DefaultClientProfile.getProfile());
// Set SAMP client metadata:
Metadata metadata = new Metadata();
metadata.setName("Aladin/SED Plugin");
metadata.setDescriptionText("Aladin plugin to generate SED from loaded images.");
metadata.setIconUrl("http://www.utsn.net/images/chart.gif");
sampConnector.declareMetadata(metadata);
// Set subscriptions set (no one):
sampConnector.declareSubscriptions(sampConnector.computeSubscriptions());
// Register the Plugin in SAMP:
sampConnector.setActive(true);
sampConnector.setAutoconnect(3);
}
if (!isSampConnected())
ErrorsLog.getCurrentInstance().addException(new SEDException("No SAMP connection", "SAMP registering", "SEDPlugin is NOT registered to SAMP !"));
return isSampConnected();
}
/**
* Unregisters this plugin from SAMP.
* @return true if SEDPlugin is unregistered, false otherwise.
*/
private final boolean unregisterFromSamp(){
sampConnector.setActive(false);
sampConnector = null;
// Delete ".exportSamp" file:
File f;
for(int i=0; itable.load.votable or spectrum.load.ssa-generic) to another map. This second map associates a client ID to an Object array containing its name then its icon URL.
*/
@SuppressWarnings("unchecked")
public final Map> getSampClients(){
if (isSampConnected()){
try {
HashMap> mtypeToClients = new HashMap>();
boolean networkOK = SEDPlugin.hasInternetConnection();
// Interesting MTypes:
String[] mtypes = new String[]{SEND_VOTABLE, SEND_SSA};
// All clients of SAMP:
Map allClients = sampConnector.getClientMap();
// Added clients:
ArrayList vAdded = new ArrayList();
// Get clients of each interesting mtype:
for(int i=0; i itIds = sampConnector.getConnection().getSubscribedClients(mtypes[i]).keySet().iterator();
// maps clients ID with their name:
HashMap clientsTmp = new HashMap();
while(itIds.hasNext()){
String id = itIds.next();
Metadata metadata = ((Client)allClients.get(id)).getMetadata();
if (!vAdded.contains(id)){
clientsTmp.put(id, new Object[]{metadata.getName(), (networkOK ? metadata.getIconUrl() : null)});
vAdded.add(id);
}
}
// associates the current mtype with the just building map:
mtypeToClients.put(mtypes[i], clientsTmp);
}
return mtypeToClients;
} catch (SampException e) {
e.printStackTrace();
ErrorsLog.getCurrentInstance().addException(new SEDException(e, "Getting SAMP receivers list"));
}
}
return new HashMap>();
}
/**
* Gets the name of the next file sent by SAMP.
* This name is: "spectrumSEDPluginForSamp" followed by an auto-incremented integer and ".xml".
*
* @return The name of the next file sent by SAMP.
*/
public static final String getNextSendFileName(){
return "spectrumSEDPluginForSamp"+nbFileSentBySamp+".xml";
}
/**
* Sends a spectrum (saved in the given file and named with the given label) to the given client in function of the given MType.
* @param mtype The MType to use to send the spectrum.
* @param clientId The receiver of the spectrum.
* @param f The spectrum to send.
* @param name Its name/title.
*/
public void sendTo(String mtype, String clientId, File f, String name){
if (mtype.equals(SEND_VOTABLE))
sendVOTable(clientId, f, name);
else if (mtype.equals(SEND_SSA))
sendSSA(clientId, f, name);
else
ErrorsLog.getCurrentInstance().addException(new SEDException("Nothing has been sent", "Sending the spectrum named \""+name+"\" via SAMP", "The spectrum hasn't been sent because the given MType is unknown: \""+mtype+"\"."));
}
/**
* Sends via SSA a spectrum (saved in the given file and named with the given label) to the client whose the ID is given.
* @param clientId The ID of the receiver of the spectrum.
* @param f The file which contains the spectrum to send.
* @param name The name/title of the spectrum to send.
*/
public void sendSSA(String clientId, File f, String name){
if (isSampConnected() && clientId != null && clientId.trim().length()>0){
// Build SSA metadata:
Map ssaMeta = new HashMap();
ssaMeta.put("VOX:Image_Title", name);
ssaMeta.put("DATA_LINK", "file:"+f.getAbsolutePath());
// Build message:
Message msg = new Message(SEND_SSA);
msg.addParam("url", "file:"+f.getAbsolutePath());
msg.addParam("meta", ssaMeta);
msg.addParam("spectrum-id", name);
msg.addParam("name", name);
// Send message:
try {
sampConnector.call(clientId, msg, new ResultHandler(){
public void done() {
nbFileSentBySamp++;
}
public void result(Client arg0, Response arg1) {;}
}, 10);
}catch (SampException e){
ErrorsLog.getCurrentInstance().addException(new SEDException("Nothing has been sent", "Sending the spectrum named \""+name+"\" in SSA via SAMP", "The spectrum hasn't been sent because: "+e.getMessage()));
}
}
}
/**
* Sends in VOTable format a spectrum (saved in the given file and named with the given label) to the client whose the ID is given.
* @param clientId The ID of the receiver.
* @param f The file which contains the spectrum to send.
* @param name The name/title of the spectrum to send.
*/
public void sendVOTable(String clientId, File f, String name){
if (isSampConnected() && clientId != null && clientId.trim().length()>0){
// Build message:
Message msg = new Message(SEND_VOTABLE);
msg.addParam("name", name);
msg.addParam("table-id", name);
msg.addParam("url", "file:"+f.getAbsolutePath());
// Send message:
try {
sampConnector.call(clientId, msg, new ResultHandler(){
public void done() {
nbFileSentBySamp++;
}
public void result(Client arg0, Response arg1) {;}
}, 10);
} catch (SampException e) {
ErrorsLog.getCurrentInstance().addException(new SEDException("Nothing has been sent", "Sending the spectrum named \""+name+"\" in VOTable via SAMP", "The spectrum hasn't been sent because: "+e.getMessage()));
}
}
}
/**
* Tells whether there is a connection to Internet.
*
* @return true if there is a connection, false otherwise.
*/
public static final boolean hasInternetConnection(){
boolean networkOK = false;
try{
Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
while(interfaces.hasMoreElements()){
NetworkInterface interfac = interfaces.nextElement();
if (!interfac.isLoopback())
networkOK = interfac.isUp() || networkOK;
}
/*if (networkOK)
networkOK = InetAddress.getByName("aladin.u-strasbg.fr").isReachable(1500);*/
}catch(Exception ex){
networkOK = false;
}
return networkOK;
}
}