Model based implementation
I've put up a newer, Wicket 1.4 based solution on a gist at github. Provides the same functionality - a component that displays a JFreeChart with tooltips and clickable entities. This solution uses models and thus allows the graph to be redrawn when the model data changes.
http://gist.github.com/647285
Code Block |
---|
title | Example |
---|
borderStyle | solid |
---|
|
IModel<JFreeChartRenderingBean> chartModel = new AbstractReadOnlyModel<JFreeChartRenderingBean>(){
@Override
public JFreeChartRenderingBean getObject() {
return new JFreeChartRenderingBean(constructChart(), CHART_WIDTH, CHART_HEIGHT);
}
};
MappedJFreeChartPanel graphPanel = new MappedJFreeChartPanel("graphPanel", chartModel);
add(graphPanel);
|
Original Code
This page details a component that integrates a JFreeChart chart and wicket producing a clickable imagemap integrated with a wicket AjaxLink, and tooltips specified via the tooltip generator of the JFreeChart.
The major component used by simply constructing and adding to your parent panel is MappedChart:
Markup:
Code Block |
---|
title | MappedChart.html |
---|
borderStyle | solid |
---|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmnls="http://www.w3.org/1999/xhtml" xmnls:wicket="http://wicket.apache.org">
<wicket:panel>
<img wicket:id="image" />
<map wicket:id="imageMap" >
<area wicket:id = "areas" />
</map>
</wicket:panel>
</html>
|
Java Code:
Code Block |
---|
title | MappedChart.java |
---|
borderStyle | solid |
---|
|
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.EntityCollection;
/**
* Component that produces an image and associated image map from
* the given JFreeChart chart. Uses the JFreeChart tooltip generator
* to provide tooltips for the chart entities but does not use
* the JFreeChart URL generator, but instead calls an Ajax callback
* function/
*
* @author Jonny Wray
*
*/
public abstract class MappedChart extends Panel{
private static final long serialVersionUID = 4137002187344769160L;
public MappedChart(String panelId, JFreeChart chart, int width, int height){
super(panelId);
ChartImage image = new ChartImage("image", chart, width, height);
String mapName = getPath();
image.add(new AttributeModifier("usemap", true, new Model("#"+mapName)));
add(image);
DynamicImageMap imageMap = constructImageMap(image, mapName);
add(imageMap);
}
/**
* The callback method that is called when a specific image map entity is
* clicked on.
*
* @param target
* @param entity
*/
protected abstract void onClickCallback(AjaxRequestTarget target, ChartEntity entity);
private DynamicImageMap constructImageMap(ChartImage image, String mapName){
DynamicImageMap imageMap = new DynamicImageMap("imageMap", mapName);
EntityCollection entities = image.getRenderingInfo().getEntityCollection();
if (entities != null) {
int count = entities.getEntityCount();
for (int i = count - 1; i >= 0; i--) {
final ChartEntity entity = entities.getEntity(i);
imageMap.addArea(entity.getShapeType(), entity.getShapeCoords(), entity.getToolTipText(), new AjaxLink("link"){
private static final long serialVersionUID = -7982198051678987986L;
@Override
public void onClick(AjaxRequestTarget target) {
onClickCallback(target, entity);
}
});
}
}
return imageMap;
}
}
|
Supporting code:
Code Block |
---|
title | MapArea.java |
---|
borderStyle | solid |
---|
|
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.IAjaxCallDecorator;
import org.apache.wicket.ajax.calldecorator.CancelEventIfNoAjaxDecorator;
import org.apache.wicket.ajax.markup.html.IAjaxLink;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.model.IModel;
/**
* A mapped area segment of an image map that adds an Ajax link
* to the area as well as a regular tooltip.
*
* @author Jonny Wray
*
*/
public class MapArea extends WebMarkupContainer{
private static final long serialVersionUID = -135521429660733572L;
private String shape;
private String coords;
private String tooltipText;
/**
* Construct the map area
*
* @param id Component identifier
* @param model Model
* @param shape The specific area shape
* @param coords The coordinates of the area as a comma separated list
* @param tooltipText The tooltip text, or null to not include it
* @param linkCallback The link callback function called when the area is click, or null to have no link functionality
*/
public MapArea(String id, IModel model, String shape, String coords, String tooltipText, final IAjaxLink linkCallback) {
super(id, model);
this.shape = shape;
this.coords = coords;
this.tooltipText = tooltipText;
if(linkCallback != null){
add(new AjaxEventBehavior("onclick"){
private static final long serialVersionUID = 2615093257359874075L;
@Override
protected void onEvent(AjaxRequestTarget target) {
linkCallback.onClick(target);
}
protected IAjaxCallDecorator getAjaxCallDecorator(){
return new CancelEventIfNoAjaxDecorator();
}
});
}
setOutputMarkupId(true);
}
/**
* Construct the map area
*
* @param id Component identifier
* @param shape The specific area shape
* @param coords The coordinates of the area as a comma separated list
* @param tooltipText The tooltip text, or null to not include it
* @param linkCallback The link callback function called when the area is click, or null to have no link functionality
*/
public MapArea(String id, String shape, String coords, String tooltipText, final IAjaxLink linkCallback) {
this(id, null, shape, coords, tooltipText, linkCallback);
}
@Override
protected void onComponentTag(final ComponentTag tag){
super.onComponentTag(tag);
assert tag.getName().equals("area");
tag.put("shape", shape);
tag.put("coords", coords);
tag.put("href", "#");
if(tooltipText != null && !tooltipText.isEmpty()){
tag.put("title", tooltipText);
}
}
}
|
Code Block |
---|
title | DynamicImageMap.java |
---|
borderStyle | solid |
---|
|
import org.apache.wicket.ajax.markup.html.IAjaxLink;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.repeater.RepeatingView;
/**
* Produces markup for an image map HTML element with
* repeating mapped areas.
*
* @author Jonny Wray
*
*/
public class DynamicImageMap extends WebMarkupContainer{
private static final long serialVersionUID = 8859550289557897390L;
private String mapName;
private RepeatingView areas;
private int areaCounter = 0;
public DynamicImageMap(final String id, String mapName){
super(id);
this.mapName = mapName;
areas = new RepeatingView("areas");
add(areas);
}
public void addArea(String shape, String coords, String tooltipText, IAjaxLink linkCallback){
MapArea area = new MapArea(Integer.toString(areaCounter++), shape, coords, tooltipText, linkCallback);
areas.add(area);
}
@Override
protected void onComponentTag(final ComponentTag tag){
super.onComponentTag(tag);
assert tag.getName().equals("map");
tag.put("name", mapName);
}
}
|
Code Block |
---|
title | ChartImage.java |
---|
borderStyle | solid |
---|
|
import java.awt.image.BufferedImage;
import org.apache.wicket.Resource;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.image.resource.DynamicImageResource;
import org.apache.wicket.protocol.http.WebResponse;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
/**
* Wicket Image constructed from a JFreeChart and exposing the
* rendering information to allow image map creation
*
* @author Jonny Wray
*
*/
public class ChartImage extends Image {
private static final long serialVersionUID = -7165602010769784429L;
private int width;
private int height;
private JFreeChart chart;
private transient BufferedImage image;
private transient ChartRenderingInfo renderingInfo;
public ChartImage(String id, JFreeChart chart, int width, int height){
super(id);
this.width = width;
this.height = height;
this.chart = chart;
}
private BufferedImage createBufferedImage(){
if(image == null){
renderingInfo = new ChartRenderingInfo();
image = chart.createBufferedImage(width, height, renderingInfo);
}
return image;
}
public ChartRenderingInfo getRenderingInfo(){
if(renderingInfo == null){
createBufferedImage();
}
return renderingInfo;
}
@Override
protected Resource getImageResource() {
return new DynamicImageResource(){
private static final long serialVersionUID = -4386816651419227671L;
@Override
protected byte[] getImageData() {
return toImageData(createBufferedImage());
}
@Override
protected void setHeaders(WebResponse response) {
if (isCacheable()) {
super.setHeaders(response);
}
else {
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
}
}
};
}
}
|