changeset 3023:e19ff9086035

Avoid collisions between line labels. flys-artifacts/trunk@4590 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Felix Wolfsteller <felix.wolfsteller@intevation.de>
date Tue, 05 Jun 2012 15:03:11 +0000
parents 705d2058b682
children 005f0130e6d6
files flys-artifacts/ChangeLog flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeLineLabelEntity.java flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeXYTextAnnotation.java flys-artifacts/src/main/java/de/intevation/flys/jfree/EnhancedLineAndShapeRenderer.java flys-artifacts/src/main/java/de/intevation/flys/jfree/JFreeUtil.java
diffstat 5 files changed, 84 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Tue Jun 05 14:56:57 2012 +0000
+++ b/flys-artifacts/ChangeLog	Tue Jun 05 15:03:11 2012 +0000
@@ -1,3 +1,17 @@
+2012-06-05	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* src/main/java/de/intevation/flys/jfree/JFreeUtil.java:
+	  Improved collision detection.
+
+	* src/main/java/de/intevation/flys/jfree/EnhancedLineAndShapeRenderer.java:
+	  Avoid collisions of line labels, documentation.
+
+	* src/main/java/de/intevation/flys/jfree/CollisionFreeXYTextAnnotation.java:
+	  Use JFreeUtil.
+
+	* src/main/java/de/intevation/flys/jfree/CollisionFreeLineLabelEntity.java:
+	  New, ChartEntity class for Line labels.
+
 2012-06-05	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
 
 	* src/main/java/de/intevation/flys/artifacts/model/fixings/FixResults.java:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeLineLabelEntity.java	Tue Jun 05 15:03:11 2012 +0000
@@ -0,0 +1,21 @@
+package de.intevation.flys.jfree;
+
+import java.awt.Shape;
+
+import org.jfree.chart.entity.XYAnnotationEntity;
+
+/**
+ * Chart Entity for Line Labels that should not collide.
+ */
+public class CollisionFreeLineLabelEntity
+extends XYAnnotationEntity {
+    public CollisionFreeLineLabelEntity(
+        Shape hotspot,
+        int rendererIndex,
+        String toolTip,
+        String url
+    ) {
+        super(hotspot, rendererIndex, toolTip, url);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeXYTextAnnotation.java	Tue Jun 05 14:56:57 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/CollisionFreeXYTextAnnotation.java	Tue Jun 05 15:03:11 2012 +0000
@@ -20,6 +20,8 @@
 
 import org.jfree.ui.RectangleEdge;
 
+import de.intevation.flys.jfree.JFreeUtil;
+
 
 /**
  * Custom Annotations class that is drawn only if no collisions with other
@@ -87,17 +89,9 @@
         // Deviation from superclass: prevent collision.
         Rectangle2D hotspotBox = hotspot.getBounds2D();
 
-        for (Iterator i = info.getOwner().getEntityCollection().iterator();
-                i.hasNext(); ) {
-            Object next = i.next();
-            // Collision with other stuff than XYAnnotations are okay.
-            if (next instanceof CollisionFreeXYTextAnnotationEntity) {
-                XYAnnotationEntity drawnShape = (XYAnnotationEntity) next;
-                if (drawnShape.getArea().intersects(hotspotBox)) {
-                    // Found collision, early stop.
-                    return;
-                }
-            }
+        if (JFreeUtil.collides(hotspot, info.getOwner().getEntityCollection(),
+            XYAnnotationEntity.class)) {
+            return;
         }
 
         if (this.getBackgroundPaint() != null) {
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/EnhancedLineAndShapeRenderer.java	Tue Jun 05 14:56:57 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/EnhancedLineAndShapeRenderer.java	Tue Jun 05 15:03:11 2012 +0000
@@ -21,11 +21,13 @@
 import org.jfree.data.xy.XYSeries;
 import org.jfree.data.xy.XYSeriesCollection;
 import org.jfree.ui.RectangleEdge;
+import org.jfree.ui.TextAnchor;
 import org.jfree.util.BooleanList;
 import org.jfree.util.ShapeUtilities;
+import org.jfree.text.TextUtilities;
 
 import de.intevation.flys.jfree.HasLabel;
-
+import de.intevation.flys.jfree.JFreeUtil;
 
 /**
  * Renderer with additional the additional functionality of renderering minima
@@ -87,6 +89,10 @@
     }
 
 
+    /**
+     * Whether or not a specific item in a series (maybe the maxima) should
+     * be rendered with shape.
+     */
     public boolean getItemShapeVisible(XYDataset dataset, int series, int item){
         if (super.getItemShapeVisible(series, item)) {
             return true;
@@ -104,11 +110,17 @@
     }
 
 
+    /**
+     * Rectangle used to draw maximums shape.
+     */
     public Shape getMaximumShape(int series, int column) {
         return new Rectangle2D.Double(-5d, -5d, 10d, 10d);
     }
 
 
+    /**
+     * Rectangle used to draw minimums shape.
+     */
     public Shape getMinimumShape(int series, int column) {
         return new Rectangle2D.Double(-5d, -5d, 10d, 10d);
     }
@@ -272,13 +284,13 @@
         }
 
         // Draw label of line.
-        // TODO add colision detection
         if (isShowLineLabel(series) && isMinimumX (dataset, series, item)) {
             XYSeries xYSeries = ((XYSeriesCollection) dataset).getSeries(series);
             String waterlevelLabel = (xYSeries instanceof HasLabel)
                 ? ((HasLabel)xYSeries).getLabel()
                 : xYSeries.getKey().toString();
             // TODO Force water of some German rivers to flow direction mountains.
+
             Font oldFont = g2.getFont();
 
             Color oldColor = g2.getColor();
@@ -286,6 +298,21 @@
             g2.setColor(this.getLineLabelTextColor(series));
             g2.setBackground(Color.black);
 
+            // Move to right until no collisions exist anymore
+            Shape hotspot = TextUtilities.calculateRotatedStringBounds(
+                waterlevelLabel, g2, (float)xx, (float)yy-3f, TextAnchor.CENTER_LEFT,
+                0f, TextAnchor.CENTER_LEFT);
+            while (JFreeUtil.collides(hotspot, entities, CollisionFreeLineLabelEntity.class)) {
+                xx += 5f;
+                hotspot = TextUtilities.calculateRotatedStringBounds(
+                    waterlevelLabel, g2, (float)xx, (float)yy-3f, TextAnchor.CENTER_LEFT,
+                    0f, TextAnchor.CENTER_LEFT);
+            }
+
+            // Register to avoid collissions.
+            entities.add(new CollisionFreeLineLabelEntity(hotspot,
+                1, "", ""));
+
             // Fill background.
             if (isShowLineLabelBG(series)) {
                 drawTextBox(g2, waterlevelLabel, (float)xx, (float)yy-3f,
@@ -311,6 +338,9 @@
     }
 
 
+    /**
+     * Sets whether or not the minimum should be rendered with shape.
+     */
     public void setIsMinimumShapeVisisble(int series, boolean isVisible) {
         this.isMinimumShapeVisible.setBoolean(series, isVisible);
     }
--- a/flys-artifacts/src/main/java/de/intevation/flys/jfree/JFreeUtil.java	Tue Jun 05 14:56:57 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/JFreeUtil.java	Tue Jun 05 15:03:11 2012 +0000
@@ -19,10 +19,13 @@
      * True if \param hotspot collides with a Entity in \param entities.
      * @param hotspot Shape to compare against other shapes (bounds only).
      * @param entities entities against which to compare shape.
-     * @param true if a collision (non-zero intersection) exists between
+     * @param exclusiveEntityClass If not null, consider only entities of
+     *        given class.
+     * @return true if a collision (non-zero intersection) exists between
      *        shapes.
      */
-    public static boolean collides(Shape hotspot, EntityCollection entities) {
+    public static boolean collides(Shape hotspot, EntityCollection entities,
+        Class exclusiveEntityClass) {
         if (entities == null) return false;
 
         Rectangle2D hotspotBox = hotspot.getBounds2D();
@@ -30,9 +33,13 @@
         for (Iterator i = entities.iterator(); i.hasNext(); ) {
             Object next = i.next();
             ChartEntity entity = (ChartEntity) next;
-            if (entity.getArea().intersects(hotspotBox)) {
-                // Found collision, early stop.
-                return true;
+            if (exclusiveEntityClass == null
+                || exclusiveEntityClass.isInstance(entity))
+                {
+                if (entity.getArea().intersects(hotspotBox)) {
+                    // Found collision, early stop.
+                    return true;
+                }
             }
         }
 

http://dive4elements.wald.intevation.org