I have a JUNG-based application working and doing some very cool things but one capability I'd like to add is the ability to dump the current graph display to a JPEG file. I did look through the JUNG listserv archives and saw there was a discussion on this in April. It looks as if there was a problem when Jung moved from 1.5 to 1.6. I am currently using 1.7.0 and just started working with 1.7.1.
I saw in the 12/2004 FAQ that there was a question on this. It has some sample code which incidentally does not compile. The GraphDraw class is deprecated, the VisualizationViewer vv.getLayout().getCurrentSize() call needs to be either vv.getGraphLayout().getCurrentSize() or just vv.getSize(). I also see in the JUNG Visualization document dated September that there is another code segment at the end for dumping to an image file. Both that code segment as well as the ones in the FAQ suffer from another trickier problem. They call the VisualizationViewer paintComponent(Graphics2D) method which is protected. I want to run this outside of the VisualizationViewer's package and I was hoping to avoid subclassing VisualizationViewer but perhaps that's the only solution.
The other approach I've tried is to use the VisualizationViewer getGraphics() method since that's public.
BufferedImage bi = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = (Graphics2D)vv.getGraphics();
graphics.drawImage(bi,0,0,width,height,null);
ImageIO.write(bi,"JPEG",file);
And I do the...
vv.setDoubleBuffered(false);
before this and the...
vv.setDoubleBuffered(true);
after all this, as per notes in the JUNG Visualization documentation.
What I get out is a JPEG file of approximately the right XY dimension but with just black in it. Any help would be very much appreciated.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I use the following little method:
public static void writeJPEGImage(VisualizationViewer vv, String filename)
{
int width = vv.getWidth();
int height = vv.getHeight();
Color bg = vv.getBackground();
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_BGR);
Graphics2D graphics = bi.createGraphics();
graphics.setColor(bg);
graphics.fillRect(0, 0, width, height);
vv.paint(graphics);
java.lang.IllegalArgumentException: Width (0) and height (0) cannot be <= 0
at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:999)
at java.awt.image.BufferedImage.<init>(BufferedImage.java:290)
So I changed
int width = vv.getWidth();
int height = vv.getHeight();
to
int width = vv.getGraphLayout().getCurrentSize().width;
int height = vv.getGraphLayout().getCurrentSize().height;
I'm lost here
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Did you say before that when you called vv.getWidth() and
vv.getHeight(), you got values that were == 0 ?
If so, when you call vv.paint(g), the clip set on the
graphics will be of size 0 by 0, and you will not paint
anything.
Are you able to see the graph on the screen when you
are trying to make the image? If so, then maybe the
vv you are trying to get the image from is not the
same vv that is drawing on the screen.
Tom
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The image displays fine in an applet, but I can't dump it.
I got zeroes with vv.getWidth() and vv.getHeight() so I change it to vv.getGraphLayout().getCurrentSize().width and vv.getGraphLayout().getCurrentSize().height and got 600x600 (the image size).
How can I know if they're the same vv?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
As I said before, if vv.getWidth() and vv.getHeight()
return zeros, then vv.paint(Graphics g) is not
going to paint anything.
As to where the problem is, I cannot tell from what
you have told me. Perhaps if you posted the
code where you create your graph and your
VisualizationViewer it would be more clear.
Tom
>The image displays fine in an applet, but I can't >dump it.
>I got zeroes with vv.getWidth() and vv.getHeight() >so I change it to
>vv.getGraphLayout().getCurrentSize().width and
>vv.getGraphLayout().getCurrentSize().height and >got 600x600 (the image size).
>How can I know if they're the same vv?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
public Graficar(ResultSet rs) throws SQLException{
rs_aqui=rs;
rs_aqui.last();
longitud = rs_aqui.getRow();
rutas = new String[longitud];
vertices = new String[100];
for(int i=0; i<longitud; i++)
rutas[i]="";
for(int i=0; i<100; i++)
vertices[i]="";
for(int k=1; k<=longitud; k++)
{
rs_aqui.absolute(k);
rutas[k-1]=rs_aqui.getString(1); //rutas tiene las rutas distintas de la tabla
}
String patron=" ";
vertices[0]=rutas[0].substring(0, rutas[0].indexOf(' '));
for (int i=0; i<longitud; i++)
{
String aux = rutas[i];
auxiliar = aux.split(patron);
for (int j=0; j<auxiliar.length; j++)
{
if (searchArray(vertices, auxiliar[j]) == -1)
{
vertices[searchArray(vertices, "")] = auxiliar[j];
continue;
}
}
}
vertices_final= new String[searchArray(vertices, "")];
for (int i = 0; i < vertices_final.length; i++)
vertices_final[i]=vertices[i];
for (int i = 0; i < vertices_final.length; i++)
System.out.println(vertices_final[i]);
root = new DirectedSparseVertex();
graph = new SparseTree(root);
Vertex[] v = createVertices(root, vertices_final);
createEdges(v, vertices_final, rutas);
Layout layout = new TreeLayout(graph);
r = new PluggableRenderer();
Well, it didn't work the way you said but thinking about what you said on making sure the class Graficar is already put in a screen somewhere I made a JButton in the JFrame containing the Graficar Applet that dumps the image when you click it and it worked that way.
The odd thing is that I also tried modifying the Graficar class so it didn't extend a JApplet an only dumped the jpg image (which, BTW, is my real goal, the JApplet was just for debugging purposes) and I got the white image. Why is that?
BTW, the Graficar class is extended from another class. The way the program works right now is it extends a Frame to do SQL queries and it has a JButton called "Graficar" that extends the class Graficar in a new window.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Now I changed o the KKLayout and used the incrementsAreDone method like this:
while (!(layout.incrementsAreDone()));
writeJPEGImage(vv,"bgpmap_KKMod_solo.png");
It stops the applet until the graph is completely done, then it displays it, but the dump is still wrong.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I'll try that, right now my best solution was to add a window listener to the applet so when the window is closed it saves the image. It's working alright, but it looks a bit odd having a window opening and closing just for that.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have a JUNG-based application working and doing some very cool things but one capability I'd like to add is the ability to dump the current graph display to a JPEG file. I did look through the JUNG listserv archives and saw there was a discussion on this in April. It looks as if there was a problem when Jung moved from 1.5 to 1.6. I am currently using 1.7.0 and just started working with 1.7.1.
I saw in the 12/2004 FAQ that there was a question on this. It has some sample code which incidentally does not compile. The GraphDraw class is deprecated, the VisualizationViewer vv.getLayout().getCurrentSize() call needs to be either vv.getGraphLayout().getCurrentSize() or just vv.getSize(). I also see in the JUNG Visualization document dated September that there is another code segment at the end for dumping to an image file. Both that code segment as well as the ones in the FAQ suffer from another trickier problem. They call the VisualizationViewer paintComponent(Graphics2D) method which is protected. I want to run this outside of the VisualizationViewer's package and I was hoping to avoid subclassing VisualizationViewer but perhaps that's the only solution.
The other approach I've tried is to use the VisualizationViewer getGraphics() method since that's public.
BufferedImage bi = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = (Graphics2D)vv.getGraphics();
graphics.drawImage(bi,0,0,width,height,null);
ImageIO.write(bi,"JPEG",file);
And I do the...
vv.setDoubleBuffered(false);
before this and the...
vv.setDoubleBuffered(true);
after all this, as per notes in the JUNG Visualization documentation.
What I get out is a JPEG file of approximately the right XY dimension but with just black in it. Any help would be very much appreciated.
Hello Duane
I use the following little method:
public static void writeJPEGImage(VisualizationViewer vv, String filename)
{
int width = vv.getWidth();
int height = vv.getHeight();
Color bg = vv.getBackground();
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_BGR);
Graphics2D graphics = bi.createGraphics();
graphics.setColor(bg);
graphics.fillRect(0, 0, width, height);
vv.paint(graphics);
try
{
ImageIO.write(bi, "jpeg", new File(filename));
}
catch (Exception e)
{
e.printStackTrace();
}
}
Which works ok with Jung 1.7.1.
Please let me know if that resolves your issue.
Cheers,
Michael
Thanks Michael. That works perfectly.
Hi, I'm using the same method and all I'm getting is a white rectangle. What am I doing wrong?
Ah, wait, doing the exact same thing I get:
java.lang.IllegalArgumentException: Width (0) and height (0) cannot be <= 0
at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:999)
at java.awt.image.BufferedImage.<init>(BufferedImage.java:290)
So I changed
int width = vv.getWidth();
int height = vv.getHeight();
to
int width = vv.getGraphLayout().getCurrentSize().width;
int height = vv.getGraphLayout().getCurrentSize().height;
I'm lost here
Hi
Which version of JUNG are you using? i remember something with double buffering that posed a problem...
Cheers,
Michael
The latest one, 1.7.1. That's why I'm confused, it's supposed to work with this version.
Did you remember to call
vv.setDoubleBuffered( false )
?
If you don't, then it'll still 'double-buffer'. For your purposes, that'll print out an all-white frame.
Yes, my function is as follows:
public static void writeJPEGImage(VisualizationViewer vv, String filename)
{
vv.setDoubleBuffered(false);
int width = vv.getGraphLayout().getCurrentSize().width;
int height = vv.getGraphLayout().getCurrentSize().height;
Color bg = vv.getBackground();
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics2D graphics = bi.createGraphics();
graphics.setColor(bg);
graphics.fillRect(0, 0, width, height);
vv.paint(graphics);
try
{ ImageIO.write(bi, "jpeg", new File(filename)); }
catch (Exception e) { e.printStackTrace(); }
vv.setDoubleBuffered(true);
}
Did you say before that when you called vv.getWidth() and
vv.getHeight(), you got values that were == 0 ?
If so, when you call vv.paint(g), the clip set on the
graphics will be of size 0 by 0, and you will not paint
anything.
Are you able to see the graph on the screen when you
are trying to make the image? If so, then maybe the
vv you are trying to get the image from is not the
same vv that is drawing on the screen.
Tom
The image displays fine in an applet, but I can't dump it.
I got zeroes with vv.getWidth() and vv.getHeight() so I change it to vv.getGraphLayout().getCurrentSize().width and vv.getGraphLayout().getCurrentSize().height and got 600x600 (the image size).
How can I know if they're the same vv?
As I said before, if vv.getWidth() and vv.getHeight()
return zeros, then vv.paint(Graphics g) is not
going to paint anything.
As to where the problem is, I cannot tell from what
you have told me. Perhaps if you posted the
code where you create your graph and your
VisualizationViewer it would be more clear.
Tom
>The image displays fine in an applet, but I can't >dump it.
>I got zeroes with vv.getWidth() and vv.getHeight() >so I change it to
>vv.getGraphLayout().getCurrentSize().width and
>vv.getGraphLayout().getCurrentSize().height and >got 600x600 (the image size).
>How can I know if they're the same vv?
Well, here's the class Graficar, it takes sn SQL resultset and uses its data to create a graph:
public class Graficar extends JApplet{
GraphLabelRenderer graphLabelRenderer;
VisualizationViewer vv;
PluggableRenderer r;
SparseTree graph;
Vertex root;
int longitud, comparador;
ResultSet rs_aqui;
String[] rutas, vertices, auxiliar, vertices_final;
public Graficar(ResultSet rs) throws SQLException{
rs_aqui=rs;
rs_aqui.last();
longitud = rs_aqui.getRow();
rutas = new String[longitud];
vertices = new String[100];
for(int i=0; i<longitud; i++)
rutas[i]="";
for(int i=0; i<100; i++)
vertices[i]="";
for(int k=1; k<=longitud; k++)
{
rs_aqui.absolute(k);
rutas[k-1]=rs_aqui.getString(1); //rutas tiene las rutas distintas de la tabla
}
String patron=" ";
vertices[0]=rutas[0].substring(0, rutas[0].indexOf(' '));
for (int i=0; i<longitud; i++)
{
String aux = rutas[i];
auxiliar = aux.split(patron);
for (int j=0; j<auxiliar.length; j++)
{
if (searchArray(vertices, auxiliar[j]) == -1)
{
vertices[searchArray(vertices, "")] = auxiliar[j];
continue;
}
}
}
vertices_final= new String[searchArray(vertices, "")];
for (int i = 0; i < vertices_final.length; i++)
vertices_final[i]=vertices[i];
for (int i = 0; i < vertices_final.length; i++)
System.out.println(vertices_final[i]);
root = new DirectedSparseVertex();
graph = new SparseTree(root);
Vertex[] v = createVertices(root, vertices_final);
createEdges(v, vertices_final, rutas);
Layout layout = new TreeLayout(graph);
r = new PluggableRenderer();
vv = new VisualizationViewer(layout,r );
vv.setPickSupport(new ShapePickSupport());
vv.setBackground(Color.white);
Container content = getContentPane();
content.add(vv);
graphLabelRenderer = r.getGraphLabelRenderer();
StringLabeller sl = StringLabeller.getLabeller(graph);
for( int c=0; c<vertices_final.length;c++ ) {
try {
sl.setLabel( v[c], vertices_final[c] );
}
catch (StringLabeller.UniqueLabelException e){}
}
VertexStringer vstringer = sl;
r.setVertexStringer(vstringer);
r.setEdgePaintFunction(new PickableEdgePaintFunction(r, Color.black, Color.cyan));
//vv.setDoubleBuffered(false);
writeJPEGImage(vv, "bgp.png");
//vv.setDoubleBuffered(true);
}
/*AADIDO!!!!!!!!!!*/
private Vertex[] createVertices(Vertex root, String[] count) {
Vertex[] v = new Vertex[count.length];
v[0] = root;
for (int i = 1; i < count.length; i++) {
v[i] = graph.addVertex(new DirectedSparseVertex());
}
return v;
}
/**
* create edges for this demo graph
* @param v an array of Vertices to connect
*/
void createEdges(Vertex[] v, String[] vertices, String[] rutas) {
String[] auxiliar;
String aux, espacio=" ";
for (int i=0; i<rutas.length; i++)
{
aux=rutas[i];
auxiliar = aux.split(espacio);
for (int j=0; j<(auxiliar.length-1); j++)
{
if(vertices[0].equals(auxiliar[0]))
if(!v[searchArray(vertices,auxiliar[j])].isNeighborOf(v[searchArray(vertices,auxiliar[j+1])]))
graph.addEdge(new DirectedSparseEdge(v[searchArray(vertices,auxiliar[j])], v[searchArray(vertices,auxiliar[j+1])]));
}
}
}
int searchArray(String[] recibo, String hallar)
{
for (int i = 0; i < recibo.length; i++)
{
if(recibo[i].compareTo(hallar) == 0)
return i;
}
return -1;
}
public static void writeJPEGImage(VisualizationViewer vv, String filename)
{
/*int width = vv.getWidth();
int height = vv.getHeight();*/
vv.setDoubleBuffered(false);
int width = vv.getGraphLayout().getCurrentSize().width;
int height = vv.getGraphLayout().getCurrentSize().height;
Color bg = vv.getBackground();
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics2D graphics = bi.createGraphics();
graphics.setColor(bg);
graphics.fillRect(0, 0, width, height);
vv.paint(graphics);
try
{ ImageIO.write(bi, "png", new File(filename)); }
catch (Exception e) { e.printStackTrace(); }
vv.setDoubleBuffered(true);
}
}
You are trying to make your jpeg before the
VisualizationViewer is realized (drawn on some
graphics device).
Try adding this method to your Graficar class:
public void addNotify() {
super.addNotify();
writeJPEGImage(vv, "bgp.png");
}
That should make it defer drawing the image
until after the Graficar is put on a screen somewhere.
Tom
Erm, instead of png it should be jpg. I was trying with png and got the same results (just a white rectangle)
Well, it didn't work the way you said but thinking about what you said on making sure the class Graficar is already put in a screen somewhere I made a JButton in the JFrame containing the Graficar Applet that dumps the image when you click it and it worked that way.
The odd thing is that I also tried modifying the Graficar class so it didn't extend a JApplet an only dumped the jpg image (which, BTW, is my real goal, the JApplet was just for debugging purposes) and I got the white image. Why is that?
BTW, the Graficar class is extended from another class. The way the program works right now is it extends a Frame to do SQL queries and it has a JButton called "Graficar" that extends the class Graficar in a new window.
Now I changed o the KKLayout and used the incrementsAreDone method like this:
while (!(layout.incrementsAreDone()));
writeJPEGImage(vv,"bgpmap_KKMod_solo.png");
It stops the applet until the graph is completely done, then it displays it, but the dump is still wrong.
Have you tried putting your 'writeJPEGImage' call
inside the componentResized method of
a componentListener that you add to
the VisualizationViewer?
Tom
I'll try that, right now my best solution was to add a window listener to the applet so when the window is closed it saves the image. It's working alright, but it looks a bit odd having a window opening and closing just for that.