changeset 2584:5d5457a1bd5f

Stubby legend aggregation. flys-artifacts/trunk@4136 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Felix Wolfsteller <felix.wolfsteller@intevation.de>
date Fri, 09 Mar 2012 12:57:23 +0000
parents 046d5c82ea0c
children a71fc8f2030c
files flys-artifacts/ChangeLog flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java
diffstat 2 files changed, 157 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- 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	<felix.wolfsteller@intevation.de>
+
+	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 <ingo@intevation.de>
 
 	+ doc/conf/themes.xml: Fixed broken XML syntax.
--- 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<LegendItem> 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<String, List<LegendItem>> entries = new LinkedHashMap<String, List<LegendItem>>();
+        for (Iterator i = old.iterator(); i.hasNext();) {
+            LegendItem item = (LegendItem) i.next();
+            String hash = legendItemHash(item);
+            List<LegendItem> itemList = entries.get(hash);
+            if (itemList == null) {
+                itemList = new ArrayList<LegendItem>();
+            }
+            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<String, List<LegendItem>> cursor: entries.entrySet()) {
+            List<LegendItem> 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;

http://dive4elements.wald.intevation.org