package net.sf.egonet.web.component; import java.awt.Color; import java.awt.Shape; import java.awt.image.BufferedImage; import java.awt.Paint; import java.awt.Stroke; import net.sf.egonet.network.Network; import net.sf.egonet.network.NetworkService; import net.sf.functionalj.tuple.PairUni; import edu.uci.ics.jung.algorithms.layout.Layout; import org.apache.commons.collections15.Transformer; import org.apache.wicket.markup.html.image.Image; import org.apache.wicket.markup.html.image.resource.DynamicImageResource; import org.apache.wicket.util.io.ByteArrayOutputStream; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; public class NetworkImage<N> extends Image { private Network<N> network; private NetworkService.LayoutOption layoutOption; private Color background; private Transformer<N,String> nodeLabeller; private Transformer<N,Paint> nodeColorizer; private Transformer<N,Shape> nodeShaper; private Transformer<PairUni<N>,Paint> edgeColorizer; private Transformer<PairUni<N>,Stroke> edgeSizer; private int imWidth; private int imHeight; private Layout<N, PairUni<N>> graphLayout; public NetworkImage(String id, Network<N> network) { super(id); this.network = network; this.layoutOption = null; this.background = null; this.nodeLabeller = null; this.nodeColorizer = null; this.nodeShaper = null; this.edgeColorizer = null; this.edgeSizer = null; this.imWidth = 1600; this.imHeight = 800; refresh(); } public void setLayoutOption(NetworkService.LayoutOption layoutOption) { this.layoutOption = layoutOption; createLayout(); } /* * Functions to retrieve/set the layout to be used for the network. If a survey * has multiple NETWORK questions that use the same adjacency expression and * node set, these functions can be used to make sure those questions have the * same graph layout when rendered. The same method could be used to ensure * that a single question shows the same layout each time the page is rendered, * rather than redrawing the graph each time. */ public void setLayout(Layout<N, PairUni<N>> graphLayout) { this.graphLayout = graphLayout; } private Layout<N,PairUni<N>> createLayout() { this.graphLayout = NetworkService.createLayout(NetworkImage.this.network, layoutOption); return graphLayout; } public Layout<N, PairUni<N>> getOrCreateLayout() { if (graphLayout == null) { createLayout(); } return this.graphLayout; } public void setBackground(Color color) { this.background = color; } /* * Transformers to modify graph-rendering, allowing per-node variation * of label/color/shape/size, and per-edge variation of color/width. */ public void setNodeLabeller(Transformer<N,String> nodeLabeller) { this.nodeLabeller = nodeLabeller; } public void setNodeColorizer(Transformer<N,Paint> nodeColorizer) { this.nodeColorizer = nodeColorizer; } public void setNodeShaper(Transformer<N,Shape> nodeShaper) { this.nodeShaper = nodeShaper; } public void setEdgeColorizer(Transformer<PairUni<N>,Paint> edgeColorizer) { this.edgeColorizer = edgeColorizer; } public void setEdgeSizer(Transformer<PairUni<N>,Stroke> edgeSizer) { this.edgeSizer = edgeSizer; } /* * Pixel dimensions of the rendered image. */ public void setDimensions(int width, int height) { if (width > 0 && height > 0) { this.imWidth = width; this.imHeight = height; } } public void refresh() { setImageResource(new DynamicImageResource() { protected byte[] getImageData() { return getJPEGFromBufferedImage( NetworkService.createImage( NetworkImage.this.network, getOrCreateLayout(), background, imWidth, imHeight, nodeLabeller, nodeColorizer, nodeShaper, edgeSizer, edgeColorizer)); } }); } public static byte[] getJPEGFromBufferedImage(BufferedImage image) { ByteArrayOutputStream os = new ByteArrayOutputStream(); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os); try { encoder.encode(image); return os.toByteArray(); } catch(Exception ex) { throw new RuntimeException("Failed to convert BufferedImage into JPEG",ex); } } }