# HG changeset patch # User Felix Wolfsteller # Date 1331297843 0 # Node ID 5d5457a1bd5ffe8507a5ba92a6c82227fcf764fc # Parent 046d5c82ea0c1f4f2d7e958ccf18ab3383ee082a Stubby legend aggregation. flys-artifacts/trunk@4136 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r 046d5c82ea0c -r 5d5457a1bd5f flys-artifacts/ChangeLog --- a/flys-artifacts/ChangeLog Thu Mar 08 14:25:37 2012 +0000 +++ b/flys-artifacts/ChangeLog Fri Mar 09 12:57:23 2012 +0000 @@ -1,3 +1,10 @@ +2012-03-09 Felix Wolfsteller + + Partial for flys/issue358, rough stub for legenditem aggregation. + + * src/main/java/de/intevation/flys/exports/XYChartGenerator.java: + Added rough stub for legend aggregation. + 2012-03-08 Ingo Weinzierl + doc/conf/themes.xml: Fixed broken XML syntax. diff -r 046d5c82ea0c -r 5d5457a1bd5f flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java --- a/flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java Thu Mar 08 14:25:37 2012 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java Fri Mar 09 12:57:23 2012 +0000 @@ -10,7 +10,9 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.LinkedHashMap; import java.util.Map; import org.w3c.dom.Document; @@ -19,6 +21,7 @@ import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; +import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.annotations.XYBoxAnnotation; import org.jfree.chart.annotations.XYLineAnnotation; @@ -231,6 +234,8 @@ // These have to go after the autozoom. addAnnotationsToRenderer(plot); + aggregateLegendEntries(plot); + return chart; } @@ -1022,6 +1027,151 @@ } + /** + * Create a hash from a legenditem. + * This hash can then be used to merge legend items labels. + * @return hash for given legenditem to identify mergeables. + */ + public static String legendItemHash(LegendItem li) { + // TODO Do proper implementation. Ensure that only mergable sets are created. + // getFillPaint() + // getFillPaintTransformer() + // getLabel() + // getLine() + // getLinePaint() + // getLineStroke() + // getOutlinePaint() + // getOutlineStroke() + // Shape getShape() + // String getToolTipText() + // String getURLText() + // boolean isLineVisible() + // boolean isShapeFilled() + // boolean isShapeOutlineVisible() + // boolean isShapeVisible() + return li.getLinePaint().toString(); + } + + + /** + * Create a label for multiple items. + * + * For example from "W(Q=100)" and "W(Q=220)" + * "W(Q= {100, 22})" would result. + * + * @param items list of legenditems. + * @return the merged label. + */ + public String mergeLegendNames(List items) { + if (items.size() == 0) { + return null; + } + if (items.size() == 1) { + return items.get(0).getLabel(); + } + // TODO consider using regionMathches for implementation. + int lastMatchedChar = 0; + boolean first = true; + String startPattern = ""; + String endPattern = ""; + String name = ""; + // First, figure out beginning and end patterns, then merge. + for (LegendItem item : items) { + if (first) { + startPattern = item.getLabel(); + endPattern = item.getLabel(); + first = false; + continue; + } + while (startPattern.length() > 0 && !item.getLabel().startsWith(startPattern)) { + startPattern = startPattern.substring(0, startPattern.length() -1); + } + while (endPattern.length() > 0 && !item.getLabel().endsWith(endPattern)) { + endPattern = endPattern.substring(1); + } + } + + // Then, merge. + name = startPattern + " {"; + first = true; + for (LegendItem item : items) { + if (!first) { + name += ", "; + } + else { + first = false; + } + name += item.getLabel().substring(startPattern.length(), + item.getLabel().length() - endPattern.length()); + } + name += "} "; + name += endPattern; + + return name; + } + + + /** + * Create new legend entries, dependant on settings. + * @param plot The plot for which to modify the legend. + */ + public void aggregateLegendEntries(XYPlot plot) { + LegendItemCollection old = plot.getLegendItems(); + // Find "similar" entries if aggregation is enabled. + + int maxListSize = 0; + int AGGR_THRESHOLD = 2; + + HashMap> entries = new LinkedHashMap>(); + for (Iterator i = old.iterator(); i.hasNext();) { + LegendItem item = (LegendItem) i.next(); + String hash = legendItemHash(item); + List itemList = entries.get(hash); + if (itemList == null) { + itemList = new ArrayList(); + } + itemList.add(item); + + if (itemList.size() > maxListSize) { + maxListSize = itemList.size(); + } + + entries.put(legendItemHash(item), itemList); + } + + if (maxListSize < AGGR_THRESHOLD) { + // No need to do anything. + return; + } + + // Run over collected entries, merge their names and create new + // entry if needed. + LegendItemCollection newLegend = new LegendItemCollection(); + for (Map.Entry> cursor: entries.entrySet()) { + List itemList = cursor.getValue(); + if (itemList.size() >= AGGR_THRESHOLD) { + // TODO now do merging + } + else { + // TODO create singular new entry + } + + LegendItem item = (LegendItem) itemList.get(0); + // Unfortunately we cannot clone and just setDescription, as this + // method was added in JFreeChart 1.0.14 (we are at .13). + LegendItem merged = new LegendItem( + mergeLegendNames(itemList), item.getDescription(), item.getToolTipText(), + item.getURLText(), item.isShapeVisible(), item.getShape(), + item.isShapeFilled(), item.getFillPaint(), item.isShapeOutlineVisible(), + item.getOutlinePaint(), item.getOutlineStroke(), item.isLineVisible(), + item.getLine(), item.getLineStroke(), item.getLinePaint()); + newLegend.add(merged); + } + + plot.setFixedLegendItems (newLegend); + } + + /** Two Ranges that span a rectangular area. */ public static class Area { protected Range xRange;