The client is a javax.swing.JFrame. When the frame is opened, it opens a new Thread calling the server and fetching the JSON data (I previously described a JSON parser here). Once the data have been fetched, it can be only drawn in the Swing-Thread (all code that might affect or depend on the state of that component should be executed in this event-dispatching thread), that's why the drawing area is painted inside a
SwingUtilities.invokeLater
call.import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.RenderingHints;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.lindenb.json.Parser;
import org.lindenb.swing.SwingUtils;
/**
* JAVA-Swing implementation of http://plindenbaum.blogspot.com/2009/07/ajaxphpmysqlcanvas-drawing-circular.html
* This is just a proof of Concept
*
* @author lindenb
*
*/
public class CircularGenome extends JFrame
{
private static final double CHR1_LENGTH =248000000.0;
private static final long serialVersionUID = 1L;
/**
* Drawing thread. call the JSON server and draw the density
*
*/
private class ParseMapping
implements Runnable
{
//json url to be called
private String url;
private int step;//UGLY, are we drawing snp of genes ?
private List<Integer> counts=null;
ParseMapping(int step,String url)
{
this.url=url;
this.step=step;
}
@Override
public void run()
{
try
{
Parser parser= new Parser();
//call the PHP server and retrieve the density of objects for this track
Object o=parser.parse(new URL(this.url).openStream());
Map<?,?> map=Map.class.cast(o);
List<?> L=List.class.cast(map.get("counts"));
this.counts= new ArrayList<Integer>(L.size());
for(Object c:L)
{
this.counts.add(Number.class.cast(Map.class.cast(c).get("count")).intValue());
}
/*
* SwingThread: Once a Swing component has been realized,
* all code that might affect or depend on the state of that component
* should be executed in the event-dispatching thread.
*/
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
Graphics2D g= offscreen.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double radius= area.getWidth()/2.0;
double r1= radius/2.0;
if(step==1)
{
r1+= 2+radius/4.0;
}
//get max densisty
double max=0;
for(int i=0;i< counts.size();++i)
{
if(counts.get(i) > max) max= counts.get(i);
}
//loop over the items
for(int i=0;i< counts.size();++i)
{
double a1= Math.PI*2.0*i/counts.size();
double a2= Math.PI*2.0*(i+1)/counts.size();
double r2= r1+(counts.get(i)/max)*(radius/4.0);
//draw the item
GeneralPath path= new GeneralPath();
path.moveTo( radius + Math.cos(a1)*r1, radius + Math.sin(a1)*r1);
path.lineTo( radius + Math.cos(a1)*r2, radius + Math.sin(a1)*r2);
path.lineTo( radius + Math.cos(a2)*r2, radius + Math.sin(a2)*r2);
path.lineTo( radius + Math.cos(a2)*r1, radius + Math.sin(a2)*r1);
path.closePath();
g.setColor(step==0?Color.RED:Color.YELLOW);
g.fill(path);
g.setColor(Color.BLACK);
g.draw(path);
}
g.dispose();
//repaint the drawing area
area.repaint();
if(step!=0) return;
//call a new Thread for the Gene
Thread t= new Thread(new ParseMapping(1,
"http://localhost/lindenb/ucsc.php?length="+windowLength+
"&database=knownGene")
);
t.start();
}
});
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
/** offscreen image where we paint the tracks */
private BufferedImage offscreen=null;
/** drawing area */
private JPanel area=null;
/** step size */
private int windowLength;
public CircularGenome()
{
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(50, 50, 800, 800);
setResizable(false);
area=new JPanel(null)
{
private static final long serialVersionUID = 1L;
//paint the drawing area in this panel
@Override
protected void paintComponent(Graphics g) {
g.drawImage(getOffscreen(),0, 0,area);
}
};
area.setOpaque(true);
setContentPane(area);
/* once the window is opened,
* call the first thread
*/
addWindowListener(new WindowAdapter()
{
@Override
public void windowOpened(WindowEvent e)
{
double perimeter= 2*Math.PI*(area.getWidth()/4.0);
windowLength = (int)Math.round(CHR1_LENGTH/perimeter)*4;
Thread t= new Thread(new ParseMapping(0,
"http://localhost/lindenb/ucsc.php?length="+windowLength+"&database=snp129")
);
t.start();
}
});
}
/** get the offscreen picture, create it, if it doesn't exist */
private BufferedImage getOffscreen()
{
if(this.offscreen==null)
{
this.offscreen=new BufferedImage(
area.getWidth(),
area.getHeight(),
BufferedImage.TYPE_INT_RGB
);
//prepare the picture, add a gradient for the background
LinearGradientPaint paint= new LinearGradientPaint(
this.getWidth()/2f,0,this.getWidth()/2f,this.getHeight(),
new float[]{0f,1f},
new Color[]{Color.WHITE,Color.BLACK}
);
Graphics2D g= this.offscreen.createGraphics();
g.setPaint(paint);
g.fillRect(0, 0, area.getWidth(), area.getHeight());
g.dispose();
}
return this.offscreen;
}
public static void main(String[] args) {
try {
CircularGenome g= new CircularGenome();
SwingUtils.show(g);
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.RenderingHints;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.lindenb.json.Parser;
import org.lindenb.swing.SwingUtils;
/**
* JAVA-Swing implementation of http://plindenbaum.blogspot.com/2009/07/ajaxphpmysqlcanvas-drawing-circular.html
* This is just a proof of Concept
*
* @author lindenb
*
*/
public class CircularGenome extends JFrame
{
private static final double CHR1_LENGTH =248000000.0;
private static final long serialVersionUID = 1L;
/**
* Drawing thread. call the JSON server and draw the density
*
*/
private class ParseMapping
implements Runnable
{
//json url to be called
private String url;
private int step;//UGLY, are we drawing snp of genes ?
private List<Integer> counts=null;
ParseMapping(int step,String url)
{
this.url=url;
this.step=step;
}
@Override
public void run()
{
try
{
Parser parser= new Parser();
//call the PHP server and retrieve the density of objects for this track
Object o=parser.parse(new URL(this.url).openStream());
Map<?,?> map=Map.class.cast(o);
List<?> L=List.class.cast(map.get("counts"));
this.counts= new ArrayList<Integer>(L.size());
for(Object c:L)
{
this.counts.add(Number.class.cast(Map.class.cast(c).get("count")).intValue());
}
/*
* SwingThread: Once a Swing component has been realized,
* all code that might affect or depend on the state of that component
* should be executed in the event-dispatching thread.
*/
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
Graphics2D g= offscreen.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double radius= area.getWidth()/2.0;
double r1= radius/2.0;
if(step==1)
{
r1+= 2+radius/4.0;
}
//get max densisty
double max=0;
for(int i=0;i< counts.size();++i)
{
if(counts.get(i) > max) max= counts.get(i);
}
//loop over the items
for(int i=0;i< counts.size();++i)
{
double a1= Math.PI*2.0*i/counts.size();
double a2= Math.PI*2.0*(i+1)/counts.size();
double r2= r1+(counts.get(i)/max)*(radius/4.0);
//draw the item
GeneralPath path= new GeneralPath();
path.moveTo( radius + Math.cos(a1)*r1, radius + Math.sin(a1)*r1);
path.lineTo( radius + Math.cos(a1)*r2, radius + Math.sin(a1)*r2);
path.lineTo( radius + Math.cos(a2)*r2, radius + Math.sin(a2)*r2);
path.lineTo( radius + Math.cos(a2)*r1, radius + Math.sin(a2)*r1);
path.closePath();
g.setColor(step==0?Color.RED:Color.YELLOW);
g.fill(path);
g.setColor(Color.BLACK);
g.draw(path);
}
g.dispose();
//repaint the drawing area
area.repaint();
if(step!=0) return;
//call a new Thread for the Gene
Thread t= new Thread(new ParseMapping(1,
"http://localhost/lindenb/ucsc.php?length="+windowLength+
"&database=knownGene")
);
t.start();
}
});
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
/** offscreen image where we paint the tracks */
private BufferedImage offscreen=null;
/** drawing area */
private JPanel area=null;
/** step size */
private int windowLength;
public CircularGenome()
{
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(50, 50, 800, 800);
setResizable(false);
area=new JPanel(null)
{
private static final long serialVersionUID = 1L;
//paint the drawing area in this panel
@Override
protected void paintComponent(Graphics g) {
g.drawImage(getOffscreen(),0, 0,area);
}
};
area.setOpaque(true);
setContentPane(area);
/* once the window is opened,
* call the first thread
*/
addWindowListener(new WindowAdapter()
{
@Override
public void windowOpened(WindowEvent e)
{
double perimeter= 2*Math.PI*(area.getWidth()/4.0);
windowLength = (int)Math.round(CHR1_LENGTH/perimeter)*4;
Thread t= new Thread(new ParseMapping(0,
"http://localhost/lindenb/ucsc.php?length="+windowLength+"&database=snp129")
);
t.start();
}
});
}
/** get the offscreen picture, create it, if it doesn't exist */
private BufferedImage getOffscreen()
{
if(this.offscreen==null)
{
this.offscreen=new BufferedImage(
area.getWidth(),
area.getHeight(),
BufferedImage.TYPE_INT_RGB
);
//prepare the picture, add a gradient for the background
LinearGradientPaint paint= new LinearGradientPaint(
this.getWidth()/2f,0,this.getWidth()/2f,this.getHeight(),
new float[]{0f,1f},
new Color[]{Color.WHITE,Color.BLACK}
);
Graphics2D g= this.offscreen.createGraphics();
g.setPaint(paint);
g.fillRect(0, 0, area.getWidth(), area.getHeight());
g.dispose();
}
return this.offscreen;
}
public static void main(String[] args) {
try {
CircularGenome g= new CircularGenome();
SwingUtils.show(g);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The result looks like the same as in the previous javascript client.
No I wonder it it would be worth trying to implement this using Java-FX.
That's it.
Pierre
No comments:
Post a Comment