summaryrefslogtreecommitdiff
path: root/src/de/lmu/ifi/dbs/elki/visualization/visualizers
diff options
context:
space:
mode:
authorAndrej Shadura <andrewsh@debian.org>2019-03-09 22:30:32 +0000
committerAndrej Shadura <andrewsh@debian.org>2019-03-09 22:30:32 +0000
commitc36aa2a8fd31ca5e225ff30278e910070cd2c8c1 (patch)
treebdfe1a5ccb57999d4d664a2a44121a78c88b19d4 /src/de/lmu/ifi/dbs/elki/visualization/visualizers
parent89aa1958dbaf9052da0c24706308a2ef8cefa96e (diff)
Import Upstream version 0.5.0~beta2
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/visualization/visualizers')
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java49
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisFactory.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/Visualization.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java120
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/AbstractHistogramVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis1d/P1DVisualization.java)17
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis1d/P1DHistogramVisualizer.java)143
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/package-info.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis1d/package-info.java)6
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java28
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java13
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/package-info.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java708
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/SegmentsStylingPolicy.java287
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/package-info.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/ContextChangeListener.java)27
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java163
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java218
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java196
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java259
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java215
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/package-info.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/ResizedEvent.java)33
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java267
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/package-info.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/ContextChangedEvent.java)35
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/package-info.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java184
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java167
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java316
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java331
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/package-info.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/P2DVisualization.java)37
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/AbstractTooltipVisualization.java)45
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/AxisVisualization.java)40
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java175
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/PolygonVisualization.java)27
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ReferencePointsVisualization.java)28
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ToolBox2DVisualization.java)41
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TooltipScoreVisualization.java)57
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TooltipStringVisualization.java)38
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java276
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterMeanVisualization.java)142
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterOrderVisualization.java)31
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/EMClusterVisualization.java)54
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java298
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/package-info.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java236
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/package-info.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/package-info.java)6
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TreeMBRVisualization.java)39
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TreeSphereVisualization.java)43
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/package-info.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/BubbleVisualization.java)113
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/package-info.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/package-info.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/package-info.java)7
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/MoveObjectsToolVisualization.java)35
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionConvexHullVisualization.java)39
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionCubeVisualization.java)48
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionDotVisualization.java)43
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionToolCubeVisualization.java)57
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionToolDotVisualization.java)35
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/package-info.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailThread.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java83
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/Thumbnailer.java96
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/package-info.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterConvexHullVisualization.java207
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusteringVisualization.java154
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/DotVisualization.java150
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java156
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/CurveVisFactory.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisFactory.java122
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java198
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/package-info.java2
81 files changed, 5566 insertions, 1594 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java
index 4b017fac..40ae5cdf 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -35,6 +35,11 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*/
public abstract class AbstractVisFactory implements VisFactory {
/**
+ * Mask for redrawing thumbnails
+ */
+ protected int thumbmask = 0;
+
+ /**
* Constructor.
*/
protected AbstractVisFactory() {
@@ -46,7 +51,7 @@ public abstract class AbstractVisFactory implements VisFactory {
// Is this a thumbnail request?
Boolean isthumb = task.get(VisualizationTask.THUMBNAIL, Boolean.class);
if (isthumb != null && isthumb.booleanValue() && allowThumbnails(task)) {
- return new ThumbnailVisualization(this, task, 0);
+ return new ThumbnailVisualization(this, task, thumbmask);
}
return makeVisualization(task);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java
index dab1c6dd..bbff0117 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -25,21 +25,20 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers;
import org.w3c.dom.Element;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultListener;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangeListener;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ResizedEvent;
/**
* Abstract base class for visualizations.
*
* @author Erich Schubert
*/
-public abstract class AbstractVisualization implements Visualization, ContextChangeListener, ResultListener {
+public abstract class AbstractVisualization implements Visualization, ResultListener {
/**
* The visualization task we do.
*/
@@ -76,12 +75,18 @@ public abstract class AbstractVisualization implements Visualization, ContextCha
this.context = task.getContext();
this.svgp = task.getPlot();
this.layer = null;
+ // Note: we do not auto-add listeners, as we don't know what kind of
+ // listeners a visualizer needs, and the visualizer might need to do some initialization first
}
@Override
public void destroy() {
- context.removeContextChangeListener(this);
+ // Always unregister listeners, as this is easy to forget otherwise
+ // TODO: remove destroy() overrides that are redundant?
context.removeResultListener(this);
+ if (this instanceof DataStoreListener) {
+ context.removeDataStoreListener((DataStoreListener) this);
+ }
}
@Override
@@ -110,26 +115,6 @@ public abstract class AbstractVisualization implements Visualization, ContextCha
return task.getHeight();
}
- @Override
- public void contextChanged(ContextChangedEvent e) {
- if(testRedraw(e)) {
- synchronizedRedraw();
- }
- }
-
- /**
- * Override this method to add additional redraw triggers!
- *
- * @param e Event
- * @return Test result
- */
- protected boolean testRedraw(ContextChangedEvent e) {
- if(e instanceof ResizedEvent) {
- return true;
- }
- return false;
- }
-
/**
* Trigger a redraw, but avoid excessive redraws.
*/
@@ -186,4 +171,16 @@ public abstract class AbstractVisualization implements Visualization, ContextCha
public void resultRemoved(Result child, Result parent) {
// Ignore by default.
}
+
+ /**
+ * Default implementation for
+ * {@link de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener#contentChanged}
+ *
+ * Not enabled or used by default, but saves redundant code.
+ *
+ * @param e Data store event
+ */
+ public void contentChanged(DataStoreEvent e) {
+ synchronizedRedraw();
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java
index cfc653ef..3a4be45b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisFactory.java
index b89d1ae0..eb675951 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisFactory.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/Visualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/Visualization.java
index d2dfb687..d2d1e5fe 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/Visualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/Visualization.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java
index 14984a5d..a3b0e3f4 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -29,6 +29,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
@@ -53,9 +54,10 @@ public final class VisualizerUtil {
*/
public static VisualizerContext getContext(HierarchicalResult baseResult) {
IterableIterator<VisualizerContext> iter = ResultUtil.filteredResults(baseResult, VisualizerContext.class);
- if (iter.hasNext()) {
+ if(iter.hasNext()) {
return iter.next();
- } else {
+ }
+ else {
return null;
}
}
@@ -79,6 +81,44 @@ public final class VisualizerUtil {
}
/**
+ * Utility function to change Visualizer visibility.
+ *
+ * @param task Visualization task
+ * @param visibility Visibility value
+ */
+ public static void setVisible(VisualizationTask task, boolean visibility) {
+ VisualizerContext context = task.getContext();
+ if(context != null) {
+ setVisible(context, task, visibility);
+ }
+ else {
+ LoggingUtil.warning("setVisible called without context in task.", new Throwable());
+ }
+ }
+
+ /**
+ * Utility function to change Visualizer visibility.
+ *
+ * @param context Visualization context
+ * @param task Visualization task
+ * @param visibility Visibility value
+ */
+ public static void setVisible(VisualizerContext context, VisualizationTask task, boolean visibility) {
+ // Hide other tools
+ if(visibility && VisualizerUtil.isTool(task)) {
+ final Iterable<VisualizationTask> visualizers = ResultUtil.filteredResults(context.getResult(), VisualizationTask.class);
+ for(VisualizationTask other : visualizers) {
+ if(other != task && VisualizerUtil.isTool(other) && VisualizerUtil.isVisible(other)) {
+ other.put(VisualizationTask.META_VISIBLE, false);
+ context.getHierarchy().resultChanged(other);
+ }
+ }
+ }
+ task.put(VisualizationTask.META_VISIBLE, visibility);
+ context.getHierarchy().resultChanged(task);
+ }
+
+ /**
* Utility function to test for a visualizer being a "tool".
*
* @param vis Visualizer to test
@@ -91,34 +131,68 @@ public final class VisualizerUtil {
}
/**
+ * Utility function to test for a visualizer having options.
+ *
+ * @param vis Visualizer to test
+ * @return true when it has options
+ */
+ public static boolean hasOptions(VisualizationTask vis) {
+ // Currently enabled?
+ Boolean hasoptions = vis.getGenerics(VisualizationTask.META_HAS_OPTIONS, Boolean.class);
+ return (hasoptions != null) && hasoptions;
+ }
+
+ /**
* Filter for number vector field representations
*
* @param result Result to filter
* @return Iterator over suitable representations
*/
// TODO: move to DatabaseUtil?
- public static Iterator<Relation<? extends NumberVector<?, ?>>> iterateVectorFieldRepresentations(final Result result) {
- final Iterator<Relation<?>> parent = ResultUtil.filteredResults(result, Relation.class);
- return new AbstractFilteredIterator<Relation<?>, Relation<? extends NumberVector<?, ?>>>() {
- @Override
- protected Iterator<Relation<?>> getParentIterator() {
- return parent;
- }
+ public static IterableIterator<Relation<? extends NumberVector<?, ?>>> iterateVectorFieldRepresentations(final Result result) {
+ Iterator<Relation<?>> parent = ResultUtil.filteredResults(result, Relation.class);
+ return new VectorspaceIterator(parent);
+ }
+
+ /**
+ * Iterate over vectorspace
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ private static class VectorspaceIterator extends AbstractFilteredIterator<Relation<?>, Relation<? extends NumberVector<?, ?>>> implements IterableIterator<Relation<? extends NumberVector<?, ?>>> {
+ /** Parent iterator */
+ private Iterator<Relation<?>> parent;
- @SuppressWarnings("unchecked")
- @Override
- protected Relation<? extends NumberVector<?, ?>> testFilter(Relation<?> nextobj) {
- final SimpleTypeInformation<?> type = nextobj.getDataTypeInformation();
- if(!NumberVector.class.isAssignableFrom(type.getRestrictionClass())) {
- return null;
- }
- if(!(type instanceof VectorFieldTypeInformation)) {
- return null;
- }
- return (Relation<? extends NumberVector<?, ?>>) nextobj;
+ public VectorspaceIterator(Iterator<Relation<?>> parent) {
+ super();
+ this.parent = parent;
+ }
+
+ @Override
+ protected Iterator<Relation<?>> getParentIterator() {
+ return parent;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Relation<? extends NumberVector<?, ?>> testFilter(Relation<?> nextobj) {
+ final SimpleTypeInformation<?> type = nextobj.getDataTypeInformation();
+ if(!NumberVector.class.isAssignableFrom(type.getRestrictionClass())) {
+ return null;
}
- };
- }
+ if(!(type instanceof VectorFieldTypeInformation)) {
+ return null;
+ }
+ return (Relation<? extends NumberVector<?, ?>>) nextobj;
+ }
+
+ @Override
+ public Iterator<Relation<? extends NumberVector<?, ?>>> iterator() {
+ return this;
+ }
+ };
/**
* Test whether a thumbnail is enabled for this visualizer.
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis1d/P1DVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/AbstractHistogramVisualization.java
index 721d3432..95df9533 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis1d/P1DVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/AbstractHistogramVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis1d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.histogram;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -23,6 +23,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis1d;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection1D;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
@@ -35,7 +36,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
* @apiviz.landmark
* @apiviz.has Projection1D
*/
-public abstract class P1DVisualization extends AbstractVisualization {
+public abstract class AbstractHistogramVisualization extends AbstractVisualization {
/**
* The current projection
*/
@@ -46,8 +47,16 @@ public abstract class P1DVisualization extends AbstractVisualization {
*
* @param task Visualization task
*/
- public P1DVisualization(VisualizationTask task) {
+ public AbstractHistogramVisualization(VisualizationTask task) {
super(task);
this.proj = task.getProj();
}
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(current == proj) {
+ synchronizedRedraw();
+ }
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis1d/P1DHistogramVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java
index 7aee8108..d518d0cf 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis1d/P1DHistogramVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis1d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.histogram;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -23,50 +23,49 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis1d;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.Cluster;
-import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
-import de.lmu.ifi.dbs.elki.math.AggregatingHistogram;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
+import de.lmu.ifi.dbs.elki.math.histograms.AggregatingHistogram;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
+import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection;
import de.lmu.ifi.dbs.elki.visualization.projector.HistogramProjector;
-import de.lmu.ifi.dbs.elki.visualization.scales.LinearScale;
+import de.lmu.ifi.dbs.elki.visualization.style.ClassStylingPolicy;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleResult;
+import de.lmu.ifi.dbs.elki.visualization.style.StylingPolicy;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGSimpleLinearAxis;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
/**
* Generates a SVG-Element containing a histogram representing the distribution
@@ -80,16 +79,11 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*/
// FIXME: make non-static, react to database changes!
// FIXME: cache histogram instead of recomputing it.
-public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DVisualization {
+public class ColoredHistogramVisualizer<NV extends NumberVector<NV, ?>> extends AbstractHistogramVisualization {
/**
* Name for this visualizer.
*/
- private static final String NAME = "Histogram";
-
- /**
- * Name for this visualizer.
- */
- private static final String CNAME = "Cluster Histograms";
+ private static final String CNAME = "Histograms";
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
@@ -112,9 +106,9 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
private Relation<NV> relation;
/**
- * The clustering we visualize
+ * The style policy
*/
- private Clustering<Model> clustering;
+ private StyleResult style;
/**
* Constructor.
@@ -123,12 +117,19 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
* @param curves Curves flag
* @param bins Number of bins
*/
- public P1DHistogramVisualizer(VisualizationTask task, boolean curves, int bins) {
+ public ColoredHistogramVisualizer(VisualizationTask task, boolean curves, int bins) {
super(task);
this.curves = curves;
this.bins = bins;
this.relation = task.getRelation();
- this.clustering = task.getResult();
+ this.style = task.getResult();
+ context.addResultListener(this);
+ }
+
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
@@ -141,14 +142,23 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), xsize, ysize, margin);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- final List<Cluster<Model>> allClusters = clustering != null ? clustering.getAllClusters() : null;
- final int numc = allClusters != null ? allClusters.size() : 0;
-
- setupCSS(svgp, numc);
+ // Styling policy
+ final StylingPolicy spol = style.getStylingPolicy();
+ final ClassStylingPolicy cspol;
+ if(spol instanceof ClassStylingPolicy) {
+ cspol = (ClassStylingPolicy) spol;
+ }
+ else {
+ cspol = null;
+ }
+ // TODO also use min style?
+ setupCSS(svgp, (cspol != null) ? cspol.getMaxStyle() : 0);
- // Creating histograms
+ // Create histograms
+ final int off = (cspol != null) ? cspol.getMinStyle() : 0;
+ final int numc = (cspol != null) ? (cspol.getMaxStyle() - cspol.getMinStyle()) : 0;
DoubleMinMax minmax = new DoubleMinMax();
- final double frac = 1. / relation.size();
+ final double frac = 1. / relation.size(); // TODO: sampling?
final int cols = numc + 1;
AggregatingHistogram<double[], double[]> histogram = new AggregatingHistogram<double[], double[]>(bins, -.5, .5, new AggregatingHistogram.Adapter<double[], double[]>() {
@Override
@@ -165,12 +175,13 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
}
});
- if(allClusters != null) {
- int clusterID = 0;
- for(Cluster<Model> cluster : allClusters) {
+ if(cspol != null) {
+ for(int snum = 0; snum < numc; snum++) {
double[] inc = new double[cols];
- inc[clusterID + 1] = frac;
- for(DBID id : cluster.getIDs()) {
+ inc[0] = frac;
+ inc[snum + 1] = frac;
+ for(Iterator<DBID> iter = cspol.iterateClass(snum + off); iter.hasNext();) {
+ DBID id = iter.next();
try {
double pos = proj.fastProjectDataToRenderSpace(relation.get(id)) / Projection.SCALE;
histogram.aggregate(pos, inc);
@@ -179,18 +190,19 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
// Ignore. The object was probably deleted from the database
}
}
- clusterID += 1;
}
}
- // Actual data distribution.
- double[] inc = new double[cols];
- inc[0] = frac;
- for(DBID id : relation.iterDBIDs()) {
- double pos = proj.fastProjectDataToRenderSpace(relation.get(id)) / Projection.SCALE;
- histogram.aggregate(pos, inc);
+ else {
+ // Actual data distribution.
+ double[] inc = new double[cols];
+ inc[0] = frac;
+ for(DBID id : relation.iterDBIDs()) {
+ double pos = proj.fastProjectDataToRenderSpace(relation.get(id)) / Projection.SCALE;
+ histogram.aggregate(pos, inc);
+ }
}
// for scaling, get the maximum occurring value in the bins:
- for(Pair<Double, double[]> bin : histogram) {
+ for(DoubleObjPair<double[]> bin : histogram) {
for(double val : bin.second) {
minmax.put(val);
}
@@ -199,9 +211,9 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
LinearScale yscale = new LinearScale(0, minmax.getMax());
LinearScale xscale = new LinearScale(histogram.getCoverMinimum(), histogram.getCoverMaximum());
- // Axis. TODO: Use AxisVisualizer for this?
+ // Axis. TODO: Add an AxisVisualizer for this?
try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, ysize, 0, 0, true, false, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, ysize, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
// draw axes that are non-trivial
final int dimensionality = DatabaseUtil.dimensionality(relation);
@@ -214,7 +226,7 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
if(ax != orig) {
final double left = (orig / Projection.SCALE + 0.5) * xsize;
final double right = (ax / Projection.SCALE + 0.5) * xsize;
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), left, ysize, right, ysize, true, true, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), left, ysize, right, ysize, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
}
}
}
@@ -225,16 +237,16 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
double binwidth = histogram.getBinsize();
// Visualizing
if(!curves) {
- for(Pair<Double, double[]> bin : histogram) {
- double lpos = xscale.getScaled(bin.getFirst() - binwidth / 2);
- double rpos = xscale.getScaled(bin.getFirst() + binwidth / 2);
+ for(DoubleObjPair<double[]> bin : histogram) {
+ double lpos = xscale.getScaled(bin.first - binwidth / 2);
+ double rpos = xscale.getScaled(bin.first + binwidth / 2);
double stack = 0.0;
final int start = numc > 0 ? 1 : 0;
for(int key = start; key < cols; key++) {
double val = yscale.getScaled(bin.getSecond()[key]);
Element row = SVGUtil.svgRect(svgp.getDocument(), xsize * lpos, ysize * (1 - (val + stack)), xsize * (rpos - lpos), ysize * val);
stack = stack + val;
- SVGUtil.addCSSClass(row, BIN + (key - 1));
+ SVGUtil.addCSSClass(row, BIN + (off + key - 1));
layer.appendChild(row);
}
}
@@ -251,9 +263,9 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
}
// draw histogram lines
- for(Pair<Double, double[]> bin : histogram) {
- left = xscale.getScaled(bin.getFirst() - binwidth / 2);
- right = xscale.getScaled(bin.getFirst() + binwidth / 2);
+ for(DoubleObjPair<double[]> bin : histogram) {
+ left = xscale.getScaled(bin.first - binwidth / 2);
+ right = xscale.getScaled(bin.first + binwidth / 2);
for(int i = 0; i < cols; i++) {
double val = yscale.getScaled(bin.getSecond()[i]);
if(lasty[i] != val) {
@@ -271,7 +283,7 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
}
paths[i].lineTo(xsize * right, ysize * 1);
Element elem = paths[i].makeElement(svgp);
- SVGUtil.addCSSClass(elem, BIN + (i - 1));
+ SVGUtil.addCSSClass(elem, BIN + (off + i - 1));
layer.appendChild(elem);
}
}
@@ -368,20 +380,21 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
super();
this.curves = curves;
this.bins = bins;
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
}
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new P1DHistogramVisualizer<NV>(task, curves, bins);
+ return new ColoredHistogramVisualizer<NV>(task, curves, bins);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Cluster histograms
- ArrayList<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- Iterator<HistogramProjector<?>> ps = ResultUtil.filteredResults(baseResult, HistogramProjector.class);
- for(HistogramProjector<?> p : IterableUtil.fromIterator(ps)) {
+ // Find a style result to visualize:
+ IterableIterator<StyleResult> styleres = ResultUtil.filteredResults(result, StyleResult.class);
+ for(StyleResult c : styleres) {
+ IterableIterator<HistogramProjector<?>> ps = ResultUtil.filteredResults(baseResult, HistogramProjector.class);
+ for(HistogramProjector<?> p : ps) {
// register self
final VisualizationTask task = new VisualizationTask(CNAME, c, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
@@ -389,20 +402,6 @@ public class P1DHistogramVisualizer<NV extends NumberVector<NV, ?>> extends P1DV
baseResult.getHierarchy().add(p, task);
}
}
- // General data distribution
- {
- Iterator<HistogramProjector<?>> ps = ResultUtil.filteredResults(result, HistogramProjector.class);
- for(HistogramProjector<?> p : IterableUtil.fromIterator(ps)) {
- // register self
- final VisualizationTask task = new VisualizationTask(NAME, null, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- if(clusterings.size() > 0) {
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- }
- // baseResult.getHierarchy().add(p.getRelation(), task);
- baseResult.getHierarchy().add(p, task);
- }
- }
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis1d/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/package-info.java
index 36dae86c..4489904f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis1d/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/package-info.java
@@ -1,12 +1,12 @@
/**
- * <p>Visualizers based on 1D projections.</p>
+ * <p>Visualizers based on 1D projected histograms.</p>
*
*/
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-Copyright (C) 2011
+Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,4 +24,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis1d; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.visualizers.histogram; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java
index bcf25a84..a04a4f7f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.optics;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java
index 542dcea7..f69bdc59 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.optics;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -38,7 +38,7 @@ import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.OPTICSProjector;
@@ -190,12 +190,12 @@ public class OPTICSClusterVisualization<D extends Distance<D>> extends AbstractO
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- Iterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : IterableUtil.fromIterator(ops)) {
+ IterableIterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
final Clustering<OPTICSModel> ocl = findOPTICSClustering(baseResult);
if(ocl != null) {
final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
task.put(CLUSTERING, ocl);
baseResult.getHierarchy().add(p, task);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java
index 08ca7504..58cd9af3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java
@@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.optics;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Iterator;
-
import org.apache.batik.util.SVG12Constants;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
@@ -40,7 +38,7 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderResult;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
@@ -229,7 +227,7 @@ public class OPTICSPlotCutVisualization<D extends Distance<D>> extends AbstractO
Clustering<Model> cl = OPTICSCut.makeOPTICSCut(order, optics.getOPTICSPlot(context).getDistanceAdapter(), epsilon);
order.addChildResult(cl);
}
- context.resultChanged(this.task);
+ context.getHierarchy().resultChanged(this.task);
// synchronizedRedraw();
return true;
}
@@ -277,8 +275,8 @@ public class OPTICSPlotCutVisualization<D extends Distance<D>> extends AbstractO
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- Iterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : IterableUtil.fromIterator(ops)) {
+ IterableIterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
baseResult.getHierarchy().add(p, task);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java
index 69bc781e..f8f30317 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.optics;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Iterator;
import java.util.List;
import org.apache.batik.dom.events.DOMMouseEvent;
@@ -45,7 +44,7 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderEntry;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
@@ -350,8 +349,8 @@ public class OPTICSPlotSelectionVisualization<D extends Distance<D>> extends Abs
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- Iterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : IterableUtil.fromIterator(ops)) {
+ IterableIterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
baseResult.getHierarchy().add(p, task);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java
index 7f4e3248..da287252 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.optics;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -23,10 +23,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.optics;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.io.File;
-import java.io.IOException;
-import java.util.Iterator;
-
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
@@ -36,7 +32,7 @@ import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
import de.lmu.ifi.dbs.elki.visualization.opticsplot.OPTICSPlot;
@@ -74,13 +70,7 @@ public class OPTICSPlotVisualizer<D extends Distance<D>> extends AbstractOPTICSV
// addCSSClasses();
OPTICSPlot<D> opticsplot = optics.getOPTICSPlot(context);
- File imgfile = null;
- try {
- imgfile = opticsplot.getAsTempFile();
- }
- catch(IOException e) {
- LoggingUtil.exception("Could not generate OPTICS plot.", e);
- }
+ String ploturi = opticsplot.getSVGPlotURI();
Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
@@ -88,13 +78,13 @@ public class OPTICSPlotVisualizer<D extends Distance<D>> extends AbstractOPTICSV
SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, 0);
SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, plotwidth);
SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, plotheight);
- itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, imgfile.toURI().toString());
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, ploturi);
layer.appendChild(itag);
try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), 0, plotheight, 0, 0, true, false, context.getStyleLibrary());
- SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), plotwidth, plotheight, plotwidth, 0, true, true, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), 0, plotheight, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), plotwidth, plotheight, plotwidth, 0, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
}
catch(CSSNamingConflict e) {
LoggingUtil.exception("CSS naming conflict for axes on OPTICS plot", e);
@@ -120,11 +110,11 @@ public class OPTICSPlotVisualizer<D extends Distance<D>> extends AbstractOPTICSV
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- Iterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : IterableUtil.fromIterator(ops)) {
+ IterableIterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
// Add plots, attach visualizer
final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
baseResult.getHierarchy().add(p, task);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java
index b082836f..95b3d53b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.optics;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,7 +24,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.optics;
*/
import java.awt.Color;
-import java.util.Iterator;
import java.util.List;
import org.apache.batik.util.SVGConstants;
@@ -34,19 +33,19 @@ import de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICSXi;
import de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICSXi.SteepAreaResult;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderEntry;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderResult;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.opticsplot.OPTICSDistanceAdapter;
import de.lmu.ifi.dbs.elki.visualization.opticsplot.OPTICSPlot;
import de.lmu.ifi.dbs.elki.visualization.projector.OPTICSProjector;
-import de.lmu.ifi.dbs.elki.visualization.scales.LinearScale;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
@@ -196,12 +195,12 @@ public class OPTICSSteepAreaVisualization<D extends Distance<D>> extends Abstrac
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- Iterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : IterableUtil.fromIterator(ops)) {
+ IterableIterator<OPTICSProjector<?>> ops = ResultUtil.filteredResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
final SteepAreaResult steep = findSteepAreaResult(p.getResult());
if(steep != null) {
final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
baseResult.getHierarchy().add(p, task);
baseResult.getHierarchy().add(steep, task);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/package-info.java
index 05b02142..642f817c 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/package-info.java
@@ -5,7 +5,7 @@
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-Copyright (C) 2011
+Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java
index 985988c6..208d8cb7 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java
@@ -8,7 +8,7 @@
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-Copyright (C) 2011
+Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java
new file mode 100644
index 00000000..360197ab
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java
@@ -0,0 +1,708 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.pairsegments;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+import org.w3c.dom.events.MouseEvent;
+
+import de.lmu.ifi.dbs.elki.evaluation.clustering.pairsegments.Segment;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.pairsegments.Segments;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultListener;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGCheckbox;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+
+/**
+ * Visualizer to draw circle segments of clusterings and enable interactive
+ * selection of segments. For "empty" segments, all related segments are
+ * selected instead, to visualize the differences.
+ *
+ * <p>
+ * Reference:<br />
+ * Evaluation of Clusterings – Metrics and Visual Support<br />
+ * Elke Achtert, Sascha Goldhofer, Hans-Peter Kriegel, Erich Schubert, Arthur
+ * Zimek<br />
+ * In: Proc. 28th International Conference on Data Engineering (ICDE) 2012
+ * </p>
+ *
+ * @author Sascha Goldhofer
+ * @author Erich Schubert
+ */
+@Reference(title = "Evaluation of Clusterings – Metrics and Visual Support", authors = "Elke Achtert, Sascha Goldhofer, Hans-Peter Kriegel, Erich Schubert, Arthur Zimek", booktitle = "Proc. 28th International Conference on Data Engineering (ICDE) 2012", url = "http://elki.dbs.ifi.lmu.de/wiki/PairSegments")
+public class CircleSegmentsVisualizer extends AbstractVisualization implements ResultListener {
+ /**
+ * Class logger
+ */
+ private static final Logging logger = Logging.getLogger(CircleSegmentsVisualizer.class);
+
+ /**
+ * CircleSegments visualizer name
+ */
+ private static final String NAME = "CircleSegments";
+
+ /** Minimum width (radian) of Segment */
+ private final static double SEGMENT_MIN_ANGLE = 0.01;
+
+ /** Gap (radian) between segments */
+ private final static double SEGMENT_MIN_SEP_ANGLE = 0.005;
+
+ /** Offset from center to first ring */
+ private final static double RADIUS_INNER = 0.04 * StyleLibrary.SCALE;
+
+ /** Margin between two rings */
+ private final static double RADIUS_DISTANCE = 0.01 * StyleLibrary.SCALE;
+
+ /** Radius of whole CircleSegments except selection border */
+ private final static double RADIUS_OUTER = 0.47 * StyleLibrary.SCALE;
+
+ /** Radius of highlight selection (outer ring) */
+ private final static double RADIUS_SELECTION = 0.02 * StyleLibrary.SCALE;
+
+ /**
+ * CSS class name for the clusterings.
+ */
+ private static final String CLR_CLUSTER_CLASS_PREFIX = "clusterSegment";
+
+ /**
+ * CSS border class of a cluster
+ */
+ public static final String CLR_BORDER_CLASS = "clusterBorder";
+
+ /**
+ * CSS hover class for clusters of hovered segment
+ */
+ public static final String CLR_UNPAIRED_CLASS = "clusterUnpaired";
+
+ /**
+ * CSS hover class of a segment cluster
+ */
+ public static final String CLR_HOVER_CLASS = "clusterHover";
+
+ /**
+ * CSS class of selected Segment
+ */
+ public static final String SEG_UNPAIRED_SELECTED_CLASS = "unpairedSegmentSelected";
+
+ /**
+ * Style prefix
+ */
+ public static final String STYLE = "segments";
+
+ /**
+ * Style for border lines
+ */
+ public static final String STYLE_BORDER = STYLE + ".border";
+
+ /**
+ * Style for hover effect
+ */
+ public static final String STYLE_HOVER = STYLE + ".hover";
+
+ /**
+ * First color for producing segment-cluster colors
+ */
+ public static final String STYLE_GRADIENT_FIRST = STYLE + ".cluster.first";
+
+ /**
+ * Second color for producing segment-cluster colors
+ */
+ public static final String STYLE_GRADIENT_SECOND = STYLE + ".cluster.second";
+
+ /**
+ * Segmentation of Clusterings
+ */
+ protected final Segments segments;
+
+ /**
+ * The two main layers
+ */
+ private Element visLayer, ctrlLayer;
+
+ /**
+ * Map to connect segments to their visual elements
+ */
+ public Map<Segment, List<Element>> segmentToElements = new HashMap<Segment, List<Element>>();
+
+ /**
+ * Show unclustered Pairs in CircleSegments
+ */
+ boolean showUnclusteredPairs = false;
+
+ /**
+ * Styling policy
+ */
+ protected final SegmentsStylingPolicy policy;
+
+ /**
+ * Flag to disallow an incremental redraw
+ */
+ private boolean noIncrementalRedraw = true;
+
+ /**
+ * Constructor
+ */
+ public CircleSegmentsVisualizer(VisualizationTask task) {
+ super(task);
+ segments = task.getResult();
+ policy = new SegmentsStylingPolicy(segments, context.getStyleLibrary());
+ // Listen for result changes (Selection changed)
+ context.addResultListener(this);
+ }
+
+ public void toggleUnclusteredPairs(boolean show) {
+ noIncrementalRedraw = true;
+ showUnclusteredPairs = show;
+ synchronizedRedraw();
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ // Redraw on style result changes.
+ if(current == context.getStyleResult()) {
+ // When switching to a different policy, unhighlight segments.
+ if(context.getStyleResult().getStylingPolicy() != policy) {
+ policy.deselectAllSegments();
+ }
+ synchronizedRedraw();
+ }
+ }
+
+ @Override
+ protected void incrementalRedraw() {
+ if(noIncrementalRedraw) {
+ super.incrementalRedraw();
+ }
+ else {
+ redrawSelection();
+ }
+ }
+
+ @Override
+ public void redraw() {
+ logger.debug("Full redraw");
+ noIncrementalRedraw = false; // Done that.
+
+ // initialize css (needs clusterSize!)
+ addCSSClasses(segments.getHighestClusterCount());
+
+ layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ visLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ // Setup scaling for canvas: 0 to StyleLibrary.SCALE (usually 100 to avoid a
+ // Java drawing bug!)
+ String transform = SVGUtil.makeMarginTransform(task.width, task.height, StyleLibrary.SCALE, StyleLibrary.SCALE, 0) + " translate(" + (StyleLibrary.SCALE * .5) + " " + (StyleLibrary.SCALE * .5) + ")";
+ visLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+ ctrlLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+
+ // and create svg elements
+ drawSegments();
+
+ //
+ // Build Interface
+ //
+ SVGCheckbox checkbox = new SVGCheckbox(showUnclusteredPairs, "Show unclustered pairs");
+ checkbox.addCheckBoxListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ toggleUnclusteredPairs(((SVGCheckbox) e.getSource()).isChecked());
+ }
+ });
+
+ // Add ring:clustering info
+ Element clrInfo = drawClusteringInfo();
+ Element c = checkbox.renderCheckBox(svgp, 1, 5 + Double.parseDouble(clrInfo.getAttribute(SVGConstants.SVG_HEIGHT_ATTRIBUTE)), 11);
+ ctrlLayer.appendChild(clrInfo);
+ ctrlLayer.appendChild(c);
+
+ ctrlLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "scale(" + (0.25 / StyleLibrary.SCALE) + ")");
+
+ layer.appendChild(visLayer);
+ layer.appendChild(ctrlLayer);
+ }
+
+ /**
+ * Define and add required CSS classes
+ */
+ protected void addCSSClasses(int maxClusterSize) {
+ StyleLibrary style = context.getStyleLibrary();
+
+ // Cluster separation lines
+ CSSClass cssReferenceBorder = new CSSClass(this.getClass(), CLR_BORDER_CLASS);
+ cssReferenceBorder.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_BORDER));
+ svgp.addCSSClassOrLogError(cssReferenceBorder);
+
+ // Hover effect for clusters
+ CSSClass cluster_hover = new CSSClass(this.getClass(), CLR_HOVER_CLASS);
+ // Note: !important is needed to override the regular color assignment
+ cluster_hover.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
+ cluster_hover.setStatement(SVGConstants.SVG_CURSOR_TAG, SVGConstants.SVG_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(cluster_hover);
+
+ // Unpaired cluster segment
+ CSSClass cluster_unpaired = new CSSClass(this.getClass(), CLR_UNPAIRED_CLASS);
+ cluster_unpaired.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getBackgroundColor(STYLE));
+ cluster_unpaired.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cluster_unpaired);
+
+ // Selected unpaired cluster segment
+ CSSClass cluster_unpaired_s = new CSSClass(this.getClass(), SEG_UNPAIRED_SELECTED_CLASS);
+ cluster_unpaired_s.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
+ svgp.addCSSClassOrLogError(cluster_unpaired_s);
+
+ // create Color shades for clusters
+ String firstcol = style.getColor(STYLE_GRADIENT_FIRST);
+ String secondcol = style.getColor(STYLE_GRADIENT_SECOND);
+ String[] clusterColorShades = makeGradient(maxClusterSize, new String[] { firstcol, secondcol });
+
+ for(int i = 0; i < maxClusterSize; i++) {
+ CSSClass clusterClasses = new CSSClass(CircleSegmentsVisualizer.class, CLR_CLUSTER_CLASS_PREFIX + "_" + i);
+ clusterClasses.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, clusterColorShades[i]);
+ clusterClasses.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
+ svgp.addCSSClassOrLogError(clusterClasses);
+ }
+ }
+
+ /**
+ * Create the segments
+ */
+ private void drawSegments() {
+ final int clusterings = segments.getClusterings();
+
+ // Reinitialize
+ this.segmentToElements.clear();
+
+ double angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size())) / segments.getPairCount(showUnclusteredPairs);
+ final int pair_min_count = (int) Math.ceil(SEGMENT_MIN_ANGLE / angle_pair);
+
+ // number of segments needed to be resized
+ int cluster_min_count = 0;
+ for(Segment segment : segments) {
+ if(segment.getPairCount() <= pair_min_count) {
+ cluster_min_count++;
+ }
+ }
+
+ // update width of a pair
+ angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size() + cluster_min_count * SEGMENT_MIN_ANGLE)) / (segments.getPairCount(showUnclusteredPairs) - cluster_min_count);
+ double radius_delta = (RADIUS_OUTER - RADIUS_INNER - clusterings * RADIUS_DISTANCE) / clusterings;
+ double border_width = SEGMENT_MIN_SEP_ANGLE;
+
+ int refClustering = 0;
+ int refSegment = Segment.UNCLUSTERED;
+ double offsetAngle = 0.0;
+
+ for(final Segment segment : segments) {
+ long currentPairCount = segment.getPairCount();
+
+ // resize small segments if below minimum
+ double alpha = SEGMENT_MIN_ANGLE;
+ if(currentPairCount > pair_min_count) {
+ alpha = angle_pair * currentPairCount;
+ }
+
+ // ITERATE OVER ALL SEGMENT-CLUSTERS
+
+ ArrayList<Element> elems = new ArrayList<Element>(clusterings);
+ segmentToElements.put(segment, elems);
+ // draw segment for every clustering
+
+ for(int i = 0; i < clusterings; i++) {
+ double currentRadius = i * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
+
+ // Add border if the next segment is a different cluster in the
+ // reference clustering
+ if((refSegment != segment.get(refClustering)) && refClustering == i) {
+ Element border = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle - SEGMENT_MIN_SEP_ANGLE, border_width, currentRadius, RADIUS_OUTER - RADIUS_DISTANCE);
+ border.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_BORDER_CLASS);
+ visLayer.appendChild(border);
+
+ if(segment.get(refClustering) == Segment.UNCLUSTERED) {
+ refClustering = Math.min(refClustering + 1, clusterings - 1);
+ }
+ refSegment = segment.get(refClustering);
+ }
+
+ int cluster = segment.get(i);
+
+ // create ring segment
+ Element segelement = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + radius_delta);
+ elems.add(segelement);
+
+ // MouseEvents on segment cluster
+ EventListener listener = new SegmentListenerProxy(segment, i);
+ EventTarget targ = (EventTarget) segelement;
+ targ.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, listener, false);
+ targ.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, listener, false);
+ targ.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, listener, false);
+
+ // Coloring based on clusterID
+ if(cluster >= 0) {
+ segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_CLUSTER_CLASS_PREFIX + "_" + cluster);
+ }
+ // if its an unpaired cluster set color to white
+ else {
+ segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
+ }
+
+ visLayer.appendChild(segelement);
+ }
+
+ //
+ // Add a extended strip for each segment to emphasis selection
+ // (easier to track thin segments and their color coding and
+ // differentiates them from cluster border lines)
+ //
+
+ double currentRadius = clusterings * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
+ Element extension = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + RADIUS_SELECTION);
+ extension.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
+ elems.add(extension);
+
+ if(segment.isUnpaired()) {
+ if(policy.isSelected(segment)) {
+ SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
+ else {
+ // Remove highlight
+ SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
+ }
+ else {
+ int idx = policy.indexOfSegment(segment);
+ if(idx >= 0) {
+ String color = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT).getColor(idx);
+ extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
+ }
+ else {
+ // Remove styling
+ extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ }
+ }
+
+ visLayer.appendChild(extension);
+
+ // calculate angle for next segment
+ offsetAngle += alpha + SEGMENT_MIN_SEP_ANGLE;
+ }
+ }
+
+ private void redrawSelection() {
+ logger.debug("Updating selection only.");
+ for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
+ Segment segment = entry.getKey();
+ // The selection marker is the extra element in the list
+ Element extension = entry.getValue().get(segments.getClusterings());
+ if(segment.isUnpaired()) {
+ if(policy.isSelected(segment)) {
+ SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
+ else {
+ // Remove highlight
+ SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
+ }
+ else {
+ int idx = policy.indexOfSegment(segment);
+ if(idx >= 0) {
+ String color = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT).getColor(idx);
+ extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
+ }
+ else {
+ // Remove styling
+ extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a gradient over a set of colors
+ *
+ * @param shades number of colors in the gradient
+ * @param colors colors for the gradient
+ * @return array of colors for CSS
+ */
+ protected static String[] makeGradient(int shades, String[] colors) {
+ if(shades <= colors.length) {
+ return colors;
+ }
+
+ // Convert SVG colors into AWT colors for math
+ Color[] cols = new Color[colors.length];
+ for(int i = 0; i < colors.length; i++) {
+ cols[i] = SVGUtil.stringToColor(colors[i]);
+ if(cols[i] == null) {
+ throw new AbortException("Error parsing color: " + colors[i]);
+ }
+ }
+
+ // Step size
+ double increment = (cols.length - 1.) / shades;
+
+ String[] colorShades = new String[shades];
+
+ for(int s = 0; s < shades; s++) {
+ final int ppos = Math.min((int) Math.floor(increment * s), cols.length);
+ final int npos = Math.min((int) Math.ceil(increment * s), cols.length);
+ if(ppos == npos) {
+ colorShades[s] = colors[ppos];
+ }
+ else {
+ Color prev = cols[ppos];
+ Color next = cols[npos];
+ final double mix = (increment * s - ppos) / (npos - ppos);
+ final int r = (int) ((1 - mix) * prev.getRed() + mix * next.getRed());
+ final int g = (int) ((1 - mix) * prev.getGreen() + mix * next.getGreen());
+ final int b = (int) ((1 - mix) * prev.getBlue() + mix * next.getBlue());
+ colorShades[s] = SVGUtil.colorToString(((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF));
+ }
+ }
+
+ return colorShades;
+ }
+
+ protected Element drawClusteringInfo() {
+ Element thumbnail = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+
+ // build thumbnail
+ int startRadius = 4;
+ int singleHeight = 12;
+ int margin = 4;
+ int radius = segments.getClusterings() * (singleHeight + margin) + startRadius;
+
+ SVGUtil.setAtt(thumbnail, SVGConstants.SVG_HEIGHT_ATTRIBUTE, radius);
+
+ for(int i = 0; i < segments.getClusterings(); i++) {
+ double innerRadius = i * singleHeight + margin * i + startRadius;
+ Element clr = SVGUtil.svgCircleSegment(svgp, radius - startRadius, radius - startRadius, Math.PI * 1.5, Math.PI * 0.5, innerRadius, innerRadius + singleHeight);
+ // FIXME: Use StyleLibrary
+ clr.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#d4e4f1");
+ clr.setAttribute(SVGConstants.SVG_STROKE_ATTRIBUTE, "#a0a0a0");
+ clr.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "1.0");
+
+ String labelText = segments.getClusteringDescription(i);
+ Element label = svgp.svgText(radius + startRadius, radius - innerRadius - startRadius, labelText);
+ thumbnail.appendChild(label);
+
+ thumbnail.appendChild(clr);
+ }
+
+ return thumbnail;
+ }
+
+ protected void segmentHover(Segment segment, int ringid, boolean active) {
+ if(active) {
+ // abort if this are the unclustered pairs
+ if(segment.isNone()) {
+ return;
+ }
+ if(logger.isDebugging()) {
+ logger.debug("Hover on segment: " + segment + " unpaired: " + segment.isUnpaired());
+ }
+
+ if(!segment.isUnpaired()) {
+ //
+ // STANDARD CLUSTER SEGMENT
+ // highlight all ring segments in this clustering and this cluster
+ //
+ // highlight all corresponding ring Segments
+ for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
+ Segment other = entry.getKey();
+ // Same cluster in same clustering?
+ if(other.get(ringid) != segment.get(ringid)) {
+ continue;
+ }
+ Element ringSegment = entry.getValue().get(ringid);
+ SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
+ }
+ }
+ else {
+ //
+ // UNPAIRED SEGMENT
+ // highlight all ring segments in this clustering responsible for
+ // unpaired
+ // segment
+ //
+ // get the paired segments corresponding to the unpaired segment
+ List<Segment> paired = segments.getPairedSegments(segment);
+
+ for(Segment other : paired) {
+ Element ringSegment = segmentToElements.get(other).get(ringid);
+ SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
+ }
+ }
+ }
+ else {
+ for(List<Element> elems : segmentToElements.values()) {
+ for(Element current : elems) {
+ SVGUtil.removeCSSClass(current, CLR_HOVER_CLASS);
+ }
+ }
+ }
+ }
+
+ protected void segmentClick(Segment segment, Event evt, boolean dblClick) {
+ MouseEvent mouse = (MouseEvent) evt;
+
+ // CTRL (add) pressed?
+ boolean ctrl = false;
+ if(mouse.getCtrlKey()) {
+ ctrl = true;
+ }
+
+ // Unselect others on double click
+ if(dblClick) {
+ policy.deselectAllSegments();
+ }
+ policy.select(segment, ctrl);
+ // update stylePolicy
+ context.getStyleResult().setStylingPolicy(policy);
+ // fire changed event to trigger redraw
+ context.getHierarchy().resultChanged(context.getStyleResult());
+ }
+
+ /**
+ * Proxy element to connect signals.
+ *
+ * @author Erich Schubert
+ */
+ private class SegmentListenerProxy implements EventListener {
+ /**
+ * Mouse double click time window in milliseconds
+ *
+ * TODO: does Batik have double click events?
+ */
+ public static final int EVT_DBLCLICK_DELAY = 350;
+
+ /**
+ * Segment we are attached to
+ */
+ private Segment id;
+
+ /**
+ * Segment ring we are
+ */
+ private int ringid;
+
+ /**
+ * For detecting double clicks.
+ */
+ private long lastClick = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param id Segment id
+ * @param ringid Ring id
+ */
+ public SegmentListenerProxy(Segment id, int ringid) {
+ super();
+ this.id = id;
+ this.ringid = ringid;
+ }
+
+ @Override
+ public void handleEvent(Event evt) {
+ if(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE.equals(evt.getType())) {
+ segmentHover(id, ringid, true);
+ }
+ if(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE.equals(evt.getType())) {
+ segmentHover(id, ringid, false);
+ }
+ if(SVGConstants.SVG_CLICK_EVENT_TYPE.equals(evt.getType())) {
+ // Check Double Click
+ boolean dblClick = false;
+ long time = java.util.Calendar.getInstance().getTimeInMillis();
+ if(time - lastClick <= EVT_DBLCLICK_DELAY) {
+ dblClick = true;
+ }
+ lastClick = time;
+
+ segmentClick(id, evt, dblClick);
+ }
+ }
+ }
+
+ /**
+ * Factory for visualizers for a circle segment
+ *
+ * @author Sascha Goldhofer
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses CircleSegmentsVisualizer oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * Constructor
+ */
+ public Factory() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new CircleSegmentsVisualizer(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // If no comparison result found abort
+ List<Segments> segments = ResultUtil.filterResults(result, Segments.class);
+ for(Segments segmentResult : segments) {
+ // create task for visualization
+ final VisualizationTask task = new VisualizationTask(NAME, segmentResult, null, this);
+ task.width = 2.0;
+ task.height = 2.0;
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
+ baseResult.getHierarchy().add(segmentResult, task);
+ }
+ }
+ };
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/SegmentsStylingPolicy.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/SegmentsStylingPolicy.java
new file mode 100644
index 00000000..99ecf081
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/SegmentsStylingPolicy.java
@@ -0,0 +1,287 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.pairsegments;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.pairsegments.Segment;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.pairsegments.Segments;
+import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
+import de.lmu.ifi.dbs.elki.visualization.style.ClassStylingPolicy;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+
+/**
+ * Styling policy to communicate the segment selection to other visualizers.
+ *
+ * @author Sascha Goldhofer
+ * @author Erich Schubert
+ */
+public class SegmentsStylingPolicy implements ClassStylingPolicy {
+ /**
+ * The segments we use for visualization
+ */
+ protected final Segments segments;
+
+ /**
+ * Selected segments
+ */
+ protected ArrayList<Segment> selectedSegments = new ArrayList<Segment>();
+
+ /**
+ * Segments indirectly selected
+ */
+ protected TreeMap<Segment, Segment> indirectSelections = new TreeMap<Segment, Segment>();
+
+ /**
+ * Not selected IDs that will be drawn in default colors.
+ */
+ protected ModifiableDBIDs unselectedObjects = DBIDUtil.newHashSet();
+
+ /**
+ * Color library (only used in compatibility mode)
+ */
+ // TODO: move to abstract super class?
+ ColorLibrary colorset;
+
+ /**
+ * Constructor.
+ *
+ * @param segments Segments
+ */
+ public SegmentsStylingPolicy(Segments segments, StyleLibrary style) {
+ super();
+ this.segments = segments;
+ this.colorset = style.getColorSet(StyleLibrary.PLOT);
+
+ // get all selectable segments
+ for(Segment segment : segments) {
+ // store segmentID
+ if(!segment.isUnpaired()) {
+ // and store their get all objects
+ if(segment.getDBIDs() != null) {
+ unselectedObjects.addDBIDs(segment.getDBIDs());
+ }
+ }
+ }
+ }
+
+ /**
+ * Test whether a segment is selected.
+ *
+ * @param segment Segment to test
+ * @return true when selected
+ */
+ public boolean isSelected(Segment segment) {
+ return selectedSegments.contains(segment) || indirectSelections.containsValue(segment);
+ }
+
+ @Override
+ public int getStyleForDBID(DBID id) {
+ Iterator<Segment> s = selectedSegments.iterator();
+ for(int i = 0; s.hasNext(); i++) {
+ Segment seg = s.next();
+ DBIDs ids = seg.getDBIDs();
+ if(ids != null && ids.contains(id)) {
+ return i;
+ }
+ }
+ return -2;
+ }
+
+ @Override
+ public int getColorForDBID(DBID id) {
+ int style = getStyleForDBID(id);
+ // FIXME: slow
+ return SVGUtil.stringToColor(colorset.getColor(style)).getRGB();
+ }
+
+ @Override
+ // -2=grau, -1=schwarz, 0+=farben
+ public int getMinStyle() {
+ return -2;
+ }
+
+ @Override
+ public int getMaxStyle() {
+ return selectedSegments.size();
+ }
+
+ @Override
+ public Iterator<DBID> iterateClass(int cnum) {
+ // unselected
+ if(cnum == -2) {
+ return unselectedObjects.iterator();
+ }
+ else if(cnum == -1) {
+ return DBIDUtil.EMPTYDBIDS.iterator();
+ }
+ // colors
+ DBIDs ids = selectedSegments.get(cnum).getDBIDs();
+ return (ids != null) ? ids.iterator() : DBIDUtil.EMPTYDBIDS.iterator();
+ }
+
+ /**
+ * Adds or removes the given segment to the selection. Depending on the
+ * clustering and cluster selected and the addToSelection option given, the
+ * current selection will be modified. This method is called by clicking on a
+ * segment and ring and the CTRL-button status.
+ *
+ * Adding selections does only work on the same clustering and cluster, else a
+ * new selection will be added.
+ *
+ * @param segment the selected element representing a segment ring (specific
+ * clustering)
+ * @param addToSelection flag for adding segment to current selection
+ */
+ public void select(Segment segment, boolean addToSelection) {
+ // abort if segment represents pairs inNone. Would select all segments...
+ if(segment.isNone()) {
+ return;
+ }
+ if(!addToSelection) {
+ deselectAllSegments();
+ }
+
+ // get selected segments
+ if(segment.isUnpaired()) {
+ // check if all segments are selected
+ if(addToSelection) {
+ boolean allSegmentsSelected = true;
+ for(Segment other : segments.getPairedSegments(segment)) {
+ if(!isSelected(other)) {
+ allSegmentsSelected = false;
+ break;
+ }
+ }
+
+ // if all are selected, deselect all
+ if(allSegmentsSelected) {
+ deselectSegment(segment);
+ return;
+ }
+ }
+ if(isSelected(segment)) {
+ deselectSegment(segment);
+ }
+ else {
+ selectSegment(segment);
+ }
+ }
+ else {
+ // an object segment was selected
+ if(isSelected(segment)) {
+ deselectSegment(segment);
+ }
+ else {
+ selectSegment(segment);
+ }
+ }
+ }
+
+ /**
+ * Deselect all currently selected segments
+ */
+ public void deselectAllSegments() {
+ while(selectedSegments.size() > 0) {
+ deselectSegment(selectedSegments.get(selectedSegments.size() - 1));
+ }
+ }
+
+ /**
+ * Deselect a segment
+ *
+ * @param segment Segment to deselect
+ */
+ protected void deselectSegment(Segment segment) {
+ if(segment.isUnpaired()) {
+ ArrayList<Segment> remove = new ArrayList<Segment>();
+ // remove all object segments associated with unpaired segment from
+ // selection list
+ for(Entry<Segment, Segment> entry : indirectSelections.entrySet()) {
+ if(entry.getValue() == segment) {
+ remove.add(entry.getKey());
+ }
+ }
+
+ for(Segment other : remove) {
+ indirectSelections.remove(other);
+ deselectSegment(other);
+ }
+ }
+ else {
+ // check if deselected object Segment has a unpaired segment highlighted
+ Segment unpaired = indirectSelections.get(segment);
+ if(unpaired != null) {
+ // remove highlight
+ deselectSegment(unpaired);
+ }
+ if(selectedSegments.remove(segment)) {
+ if(segment.getDBIDs() != null) {
+ unselectedObjects.addDBIDs(segment.getDBIDs());
+ }
+ }
+ }
+ }
+
+ /**
+ * Select a segment
+ *
+ * @param segment Segment to select
+ */
+ protected void selectSegment(Segment segment) {
+ if(segment.isUnpaired()) {
+ // remember selected unpaired segment
+ for(Segment other : segments.getPairedSegments(segment)) {
+ indirectSelections.put(other, segment);
+ selectSegment(other);
+ }
+ }
+ else {
+ if(!selectedSegments.contains(segment)) {
+ selectedSegments.add(segment);
+ if(segment.getDBIDs() != null) {
+ unselectedObjects.removeDBIDs(segment.getDBIDs());
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the index of a selected segment.
+ *
+ * @param segment Segment to find
+ * @return Index, or -1
+ */
+ public int indexOfSegment(Segment segment) {
+ return selectedSegments.indexOf(segment);
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/ContextChangeListener.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/package-info.java
index 2d7d3e91..c0c71dd3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/ContextChangeListener.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/package-info.java
@@ -1,10 +1,11 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.events;
-
+/**
+ * <p>Visualizers for inspecting cluster differences using pair counting segments.</p>
+ */
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -22,22 +23,4 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.events;
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-import java.util.EventListener;
-
-/**
- * Listener for context changes.
- *
- * @author Erich Schubert
- *
- * @apiviz.uses de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent oneway - - listens
- */
-public interface ContextChangeListener extends EventListener {
- /**
- * Method called on context changes (e.g. projection changes).
- * Usually, this should trigger a redraw!
- *
- * @param e Change event
- */
- public void contextChanged(ContextChangedEvent e);
-} \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.visualizers.pairsegments; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java
new file mode 100644
index 00000000..babcb864
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java
@@ -0,0 +1,163 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.projections.ProjectionParallel;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
+
+/**
+ * Abstract base class for parallel visualizations.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @param <NV> Vector type in relation
+ */
+public abstract class AbstractParallelVisualization<NV extends NumberVector<?, ?>> extends AbstractVisualization {
+ /**
+ * The current projection
+ */
+ final protected ProjectionParallel proj;
+
+ /**
+ * The representation we visualize
+ */
+ final protected Relation<NV> relation;
+
+ /**
+ * margin
+ */
+ final double[] margins;
+
+ /**
+ * Space between two axes
+ */
+ protected double axsep;
+
+ /**
+ * viewbox size
+ */
+ final double[] size;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public AbstractParallelVisualization(VisualizationTask task) {
+ super(task);
+ this.proj = task.getProj();
+ this.relation = task.getRelation();
+
+ double ratio = task.width / task.height;
+
+ margins = new double[] { 0.05 * StyleLibrary.SCALE, 0.1 * StyleLibrary.SCALE, 0.05 * StyleLibrary.SCALE, 0.4 * StyleLibrary.SCALE };
+ size = new double[] { ratio * StyleLibrary.SCALE, StyleLibrary.SCALE };
+ recalcAxisPositions();
+
+ this.layer = setupCanvas(svgp, proj, task.getWidth(), task.getHeight());
+ }
+
+ /**
+ * Utility function to setup a canvas element for the visualization.
+ *
+ * @param svgp Plot element
+ * @param proj Projection to use
+ * @param width Width
+ * @param height Height
+ * @return wrapper element with appropriate view box.
+ */
+ public Element setupCanvas(SVGPlot svgp, ProjectionParallel proj, double width, double height) {
+ Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ final String transform = SVGUtil.makeMarginTransform(width, height, size[0], size[1], margins[0], margins[1], margins[2], margins[3]);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+ return layer;
+ }
+
+ /**
+ * Get width of main canvas.
+ *
+ * @return Width
+ */
+ protected double getSizeX() {
+ return size[0];
+ }
+
+ protected double getSizeY() {
+ return size[1];
+ }
+
+ protected double getMarginLeft() {
+ return margins[0];
+ }
+
+ protected double getMarginTop() {
+ return margins[1];
+ }
+
+ /**
+ * Distance between axes.
+ *
+ * @return Axis separation
+ */
+ protected double getAxisSep() {
+ return axsep;
+ }
+
+ /**
+ * Recalculate axis positions, in particular after projection changes.
+ */
+ private void recalcAxisPositions() {
+ axsep = size[0] / (proj.getVisibleDimensions() - 1.);
+ }
+
+ /**
+ * Get the position of visible axis d
+ *
+ * @param d Visible axis number
+ * @return Position
+ */
+ protected double getVisibleAxisX(double d) {
+ return d * axsep;
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(current == proj) {
+ recalcAxisPositions();
+ synchronizedRedraw();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java
new file mode 100644
index 00000000..3a2c7c1d
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java
@@ -0,0 +1,218 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Iterator;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.SamplingResult;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.ClassStylingPolicy;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.style.StylingPolicy;
+import de.lmu.ifi.dbs.elki.visualization.style.lines.LineStyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
+
+/**
+ * Generates data lines.
+ *
+ * @author Robert Rödler
+ */
+public class LineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ */
+ public static final String DATALINE = "Dataline";
+
+ /**
+ * Sample we visualize.
+ */
+ private SamplingResult sample;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public LineVisualization(VisualizationTask task) {
+ super(task);
+ this.sample = ResultUtil.getSamplingResult(relation);
+ context.addResultListener(this);
+ context.addDataStoreListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(current == sample || current == context.getStyleResult()) {
+ synchronizedRedraw();
+ }
+ }
+
+ @Override
+ protected void redraw() {
+ StylingPolicy sp = context.getStyleResult().getStylingPolicy();
+ addCSSClasses(svgp, sp);
+
+ Iterator<DBID> ids = sample.getSample().iterator();
+ if(ids == null || !ids.hasNext()) {
+ ids = relation.iterDBIDs();
+ }
+ if(sp instanceof ClassStylingPolicy) {
+ ClassStylingPolicy csp = (ClassStylingPolicy) sp;
+ for(int c = csp.getMinStyle(); c < csp.getMaxStyle(); c++) {
+ String key = DATALINE + "_" + c;
+ for(Iterator<DBID> iter = csp.iterateClass(c); iter.hasNext();) {
+ DBID id = iter.next();
+ if(!sample.getSample().contains(id)) {
+ continue; // TODO: can we test more efficiently than this?
+ }
+ SVGPath path = new SVGPath();
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(id));
+ for(int i = 0; i < yPos.length; i++) {
+ path.drawTo(getVisibleAxisX(i), yPos[i]);
+ }
+ Element line = path.makeElement(svgp);
+ SVGUtil.addCSSClass(line, key);
+ layer.appendChild(line);
+ }
+ }
+ }
+ else {
+ while(ids.hasNext()) {
+ DBID id = ids.next();
+ SVGPath path = new SVGPath();
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(id));
+ for(int i = 0; i < yPos.length; i++) {
+ path.drawTo(getVisibleAxisX(i), yPos[i]);
+ }
+ Element line = path.makeElement(svgp);
+ SVGUtil.addCSSClass(line, DATALINE);
+ // assign color
+ line.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_STROKE_PROPERTY + ":" + SVGUtil.colorToString(sp.getColorForDBID(id)));
+ layer.appendChild(line);
+ }
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp, StylingPolicy sp) {
+ final StyleLibrary style = context.getStyleLibrary();
+ final LineStyleLibrary lines = style.lines();
+ final double width = .5 * style.getLineWidth(StyleLibrary.PLOT);
+ if(sp instanceof ClassStylingPolicy) {
+ ClassStylingPolicy csp = (ClassStylingPolicy) sp;
+ for(int i = csp.getMinStyle(); i < csp.getMaxStyle(); i++) {
+ String key = DATALINE + "_" + i;
+ if(!svgp.getCSSClassManager().contains(key)) {
+ CSSClass cls = new CSSClass(this, key);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ lines.formatCSSClass(cls, i, width);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+ }
+ else {
+ // Class for the distance function
+ if(!svgp.getCSSClassManager().contains(DATALINE)) {
+ CSSClass cls = new CSSClass(this, DATALINE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ lines.formatCSSClass(cls, -1, width);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+ svgp.updateStyleElement();
+ }
+
+ /**
+ * Factory for axis visualizations
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses LineVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Data lines";
+
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public Factory() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new LineVisualization(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ IterableIterator<ParallelPlotProjector<?>> ps = ResultUtil.filteredResults(result, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java
new file mode 100644
index 00000000..a47ea734
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java
@@ -0,0 +1,196 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Iterator;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGSimpleLinearAxis;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+
+/**
+ * Generates a SVG-Element containing axes, including labeling.
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.uses SVGSimpleLinearAxis
+ */
+// TODO: split into interactive / non-interactive parts?
+public class ParallelAxisVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> {
+ /**
+ * Axis label class
+ */
+ public final static String AXIS_LABEL = "paxis-label";
+
+ /**
+ * Clickable area for the axis.
+ */
+ public final static String INVERTEDAXIS = "paxis-button";
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public ParallelAxisVisualization(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ context.addResultListener(this);
+ }
+
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+ final int dim = proj.getVisibleDimensions();
+ try {
+ for(int i = 0; i < dim; i++) {
+ final int truedim = proj.getDimForVisibleAxis(i);
+ final double axisX = getVisibleAxisX(i);
+ if(!proj.isAxisInverted(i)) {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, getSizeY(), axisX, 0, SVGSimpleLinearAxis.LabelStyle.ENDLABEL, context.getStyleLibrary());
+ }
+ else {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, 0, axisX, getSizeY(), SVGSimpleLinearAxis.LabelStyle.ENDLABEL, context.getStyleLibrary());
+ }
+ // Get axis label
+ final String label = DatabaseUtil.getColumnLabel(relation, truedim + 1);
+ // Add axis label
+ Element text = svgp.svgText(axisX, -.7 * getMarginTop(), label);
+ SVGUtil.setCSSClass(text, AXIS_LABEL);
+ layer.appendChild(text);
+ // TODO: Split into background + clickable layer.
+ Element button = svgp.svgRect(axisX - getAxisSep() * .475, -getMarginTop(), .95 * getAxisSep(), .5 * getMarginTop());
+ SVGUtil.setCSSClass(button, INVERTEDAXIS);
+ addEventListener(button, truedim);
+ layer.appendChild(button);
+ }
+ }
+ catch(CSSNamingConflict e) {
+ throw new RuntimeException("Conflict in CSS naming for axes.", e);
+ }
+ }
+
+ /**
+ * Add the main CSS classes.
+ *
+ * @param svgp Plot to draw to
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(AXIS_LABEL)) {
+ CSSClass cls = new CSSClass(this, AXIS_LABEL);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
+ cls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
+ cls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.AXIS_LABEL));
+ cls.setStatement(SVGConstants.CSS_TEXT_ANCHOR_PROPERTY, SVGConstants.SVG_MIDDLE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(INVERTEDAXIS)) {
+ CSSClass cls = new CSSClass(this, INVERTEDAXIS);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.1);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+
+ /**
+ * Add an event listener to the Element
+ *
+ * @param tag Element to add the listener
+ * @param i Tool number for the Element
+ */
+ private void addEventListener(final Element tag, final int i) {
+ EventTarget targ = (EventTarget) tag;
+ targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ proj.toggleDimInverted(i);
+ context.getHierarchy().resultChanged(proj);
+ }
+ }, false);
+ }
+
+ /**
+ * Factory for axis visualizations
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses ParallelAxisVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Parallel Axes";
+
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public Factory() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new ParallelAxisVisualization(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Iterator<ParallelPlotProjector<?>> ps = ResultUtil.filteredResults(result, ParallelPlotProjector.class);
+ while(ps.hasNext()) {
+ ParallelPlotProjector<?> p = ps.next();
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java
new file mode 100644
index 00000000..821ca2a5
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java
@@ -0,0 +1,259 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.cluster;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Iterator;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.Cluster;
+import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.model.Model;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVisualization;
+
+/**
+ * Generates a SVG-Element that visualizes the area covered by a cluster.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ */
+public class ClusterOutlineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ */
+ public static final String CLUSTERAREA = "Clusteroutline";
+
+ /**
+ * The result we visualize
+ */
+ private Clustering<Model> clustering;
+
+ /**
+ * Flag for using rounded shapes
+ */
+ boolean rounded = true;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public ClusterOutlineVisualization(VisualizationTask task, boolean rounded) {
+ super(task);
+ this.clustering = task.getResult();
+ this.rounded = rounded;
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+ int dim = proj.getVisibleDimensions();
+
+ DoubleMinMax[] mms = DoubleMinMax.newArray(dim);
+ DoubleMinMax[] midmm = DoubleMinMax.newArray(dim - 1);
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<?> clus = ci.next();
+ for(int i = 0; i < dim; i++) {
+ mms[i].reset();
+ if(i < dim - 1) {
+ midmm[i].reset();
+ }
+ }
+
+ // Process points
+ // TODO: do this just once, cache the result somewhere appropriately?
+ for(DBID objId : clus.getIDs()) {
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(objId));
+ for(int i = 0; i < dim; i++) {
+ mms[i].put(yPos[i]);
+ if(i > 0) {
+ midmm[i - 1].put((yPos[i] + yPos[i - 1]) / 2.);
+ }
+ }
+ }
+
+ SVGPath path = new SVGPath();
+ if(!rounded) {
+ // Straight lines
+ for(int i = 0; i < dim; i++) {
+ path.drawTo(getVisibleAxisX(i), mms[i].getMax());
+ if(i < dim - 1) {
+ path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMax());
+ }
+ }
+ for(int i = dim - 1; i >= 0; i--) {
+ if(i < dim - 1) {
+ path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMin());
+ }
+ path.drawTo(getVisibleAxisX(i), mms[i].getMin());
+ }
+ path.close();
+ }
+ else {
+ // Maxima
+ path.drawTo(getVisibleAxisX(0), mms[0].getMax());
+ for(int i = 1; i < dim; i++) {
+ path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMax(), getVisibleAxisX(i), mms[i].getMax());
+ }
+ // Minima
+ path.drawTo(getVisibleAxisX(dim - 1), mms[dim - 1].getMin());
+ for(int i = dim - 1; i > 0; i--) {
+ path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMin(), getVisibleAxisX(i - 1), mms[i - 1].getMin());
+ }
+ path.close();
+ }
+
+ Element intervals = path.makeElement(svgp);
+ SVGUtil.addCSSClass(intervals, CLUSTERAREA + cnum);
+ layer.appendChild(intervals);
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ if(!svgp.getCSSClassManager().contains(CLUSTERAREA)) {
+ ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+ int clusterID = 0;
+
+ for(@SuppressWarnings("unused")
+ Cluster<?> cluster : clustering.getAllClusters()) {
+ CSSClass cls = new CSSClass(this, CLUSTERAREA + clusterID);
+ // cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY,
+ // context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) / 2.0);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.5);
+ final String color;
+ if(clustering.getAllClusters().size() == 1) {
+ color = SVGConstants.CSS_BLACK_VALUE;
+ }
+ else {
+ color = colors.getColor(clusterID);
+ }
+ // cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+
+ svgp.addCSSClassOrLogError(cls);
+ clusterID++;
+ }
+ }
+ }
+
+ /**
+ * Factory for axis visualizations
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses ClusterOutlineVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Cluster Outline";
+
+ /**
+ * Currently unused option to enable/disable rounding
+ */
+ public static final OptionID ROUNDED_ID = OptionID.getOrCreateOptionID("parallel.clusteroutline.rounded", "Draw lines rounded");
+
+ /**
+ * Currently, always enabled.
+ */
+ private boolean rounded = true;
+
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public Factory() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new ClusterOutlineVisualization(task, rounded);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Iterator<Clustering<?>> clusterings = ResultUtil.filteredResults(result, Clustering.class);
+ while(clusterings.hasNext()) {
+ Clustering<?> c = clusterings.next();
+ if(c.getAllClusters().size() > 0) {
+ IterableIterator<ParallelPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java
new file mode 100644
index 00000000..df731ac2
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java
@@ -0,0 +1,215 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.cluster;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Iterator;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.Cluster;
+import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.model.MeanModel;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVisualization;
+
+/**
+ * Generates a SVG-Element that visualizes cluster means.
+ *
+ * @author Robert Rödler
+ */
+public class ClusterParallelMeanVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ */
+ public static final String CLUSTERMEAN = "Clustermean";
+
+ /**
+ * The result we visualize
+ */
+ private Clustering<MeanModel<? extends NumberVector<?, ?>>> clustering;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public ClusterParallelMeanVisualization(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+
+ Iterator<Cluster<MeanModel<? extends NumberVector<?, ?>>>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<MeanModel<? extends NumberVector<?, ?>>> clus = ci.next();
+ NumberVector<?, ?> mean = clus.getModel().getMean();
+ if(mean == null) {
+ continue;
+ }
+
+ double[] pmean = proj.fastProjectDataToRenderSpace(mean);
+
+ SVGPath path = new SVGPath();
+ for(int i = 0; i < pmean.length; i++) {
+ path.drawTo(getVisibleAxisX(i), pmean[i]);
+ }
+
+ Element meanline = path.makeElement(svgp);
+ SVGUtil.addCSSClass(meanline, CLUSTERMEAN + cnum);
+ layer.appendChild(meanline);
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ if(!svgp.getCSSClassManager().contains(CLUSTERMEAN)) {
+ ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+ int clusterID = 0;
+
+ for(@SuppressWarnings("unused")
+ Cluster<?> cluster : clustering.getAllClusters()) {
+ CSSClass cls = new CSSClass(this, CLUSTERMEAN + clusterID);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) * 2);
+
+ final String color;
+ if(clustering.getAllClusters().size() == 1) {
+ color = SVGConstants.CSS_BLACK_VALUE;
+ }
+ else {
+ color = colors.getColor(clusterID);
+ }
+
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+
+ svgp.addCSSClassOrLogError(cls);
+ clusterID++;
+ }
+ }
+ }
+
+ /**
+ * Factory for axis visualizations
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses ClusterParallelMeanVisualization oneway - - «create»
+ *
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Cluster Means";
+
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public Factory() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new ClusterParallelMeanVisualization(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Iterator<Clustering<?>> clusterings = ResultUtil.filteredResults(result, Clustering.class);
+ while(clusterings.hasNext()) {
+ Clustering<?> c = clusterings.next();
+ if(c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ Clustering<MeanModel<? extends NumberVector<?, ?>>> mcls = findMeanModel(c);
+ if(mcls != null) {
+ Iterator<ParallelPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ParallelPlotProjector.class);
+ while(ps.hasNext()) {
+ ParallelPlotProjector<?> p = ps.next();
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Test if the given clustering has a mean model.
+ *
+ * @param c Clustering to inspect
+ * @return the clustering cast to return a mean model, null otherwise.
+ */
+ @SuppressWarnings("unchecked")
+ private static Clustering<MeanModel<? extends NumberVector<?, ?>>> findMeanModel(Clustering<?> c) {
+ if(c.getAllClusters().get(0).getModel() instanceof MeanModel<?>) {
+ return (Clustering<MeanModel<? extends NumberVector<?, ?>>>) c;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/ResizedEvent.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/package-info.java
index 6eb6cad3..7977da0c 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/ResizedEvent.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/package-info.java
@@ -1,10 +1,11 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.events;
-
+/**
+ * <p>Visualizers for clustering results based on parallel coordinates.</p>
+ */
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -22,28 +23,4 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.events;
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
-
-/**
- * Event triggered when the contexts view was resized.
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype event
- */
-public class ResizedEvent extends ContextChangedEvent {
- /**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Constructor.
- *
- * @param source Visualization context
- */
- public ResizedEvent(VisualizerContext source) {
- super(source);
- }
-} \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.cluster; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java
new file mode 100644
index 00000000..c36dff0c
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java
@@ -0,0 +1,267 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.index;
+
+/*This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.spatial.SpatialUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialEntry;
+import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.AbstractRStarTree;
+import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.AbstractRStarTreeNode;
+import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.rstar.RStarTreeNode;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projections.ProjectionParallel;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index.TreeMBRVisualization;
+
+/**
+ * Visualize the of an R-Tree based index.
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.has AbstractRStarTree oneway - - visualizes
+ *
+ * @param <N> Tree node type
+ * @param <E> Tree entry type
+ */
+// TODO: listen for tree changes instead of data changes?
+public class RTreeParallelVisualization<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ */
+ public static final String INDEX = "parallelrtree";
+
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ public static final String NAME = "R-Tree Index MBRs";
+
+ /**
+ * Fill parameter.
+ */
+ protected boolean fill = true;
+
+ /**
+ * The tree we visualize
+ */
+ protected AbstractRStarTree<N, E> tree;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ * @param fill Fill flag
+ */
+ @SuppressWarnings("unchecked")
+ public RTreeParallelVisualization(VisualizationTask task, boolean fill) {
+ super(task);
+ this.tree = AbstractRStarTree.class.cast(task.getResult());
+ this.fill = fill;
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ @Override
+ protected void redraw() {
+ if(tree != null) {
+ addCSSClasses(svgp);
+ E root = tree.getRootEntry();
+ visualizeRTreeEntry(svgp, layer, proj, tree, root, 0, 0);
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+
+ for(int i = 0; i < tree.getHeight(); i++) {
+
+ if(!svgp.getCSSClassManager().contains(INDEX + i)) {
+ CSSClass cls = new CSSClass(this, INDEX + i);
+
+ // Relative depth of this level. 1.0 = toplevel
+ final double relDepth = 1. - (((double) i) / tree.getHeight());
+ if(fill) {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.2);
+ }
+ else {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ }
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+ svgp.updateStyleElement();
+ }
+
+ /**
+ * Recursively draw the MBR rectangles.
+ *
+ * @param svgp SVG Plot
+ * @param layer Layer
+ * @param proj Projection
+ * @param rtree Rtree to visualize
+ * @param entry Current entry
+ * @param depth Current depth
+ */
+ private void visualizeRTreeEntry(SVGPlot svgp, Element layer, ProjectionParallel proj, AbstractRStarTree<? extends N, E> rtree, E entry, int depth, int step) {
+ final int dim = proj.getVisibleDimensions();
+ double[] min = proj.fastProjectDataToRenderSpace(SpatialUtil.getMin(entry));
+ double[] max = proj.fastProjectDataToRenderSpace(SpatialUtil.getMax(entry));
+ assert (min.length == dim && max.length == dim);
+ SVGPath path = new SVGPath();
+ for(int i = 0; i < dim; i++) {
+ path.drawTo(getVisibleAxisX(i), Math.max(min[i], max[i]));
+ }
+ for(int i = dim - 1; i >= 0; i--) {
+ path.drawTo(getVisibleAxisX(i), Math.min(min[i], max[i]));
+ }
+ path.close();
+
+ Element intervals = path.makeElement(svgp);
+
+ SVGUtil.addCSSClass(intervals, INDEX + depth);
+ layer.appendChild(intervals);
+
+ if(!entry.isLeafEntry()) {
+ N node = rtree.getNode(entry);
+ for(int i = 0; i < node.getNumEntries(); i++) {
+ E child = node.getEntry(i);
+ if(!child.isLeafEntry()) {
+ visualizeRTreeEntry(svgp, layer, proj, rtree, child, depth + 1, ++step);
+ }
+ }
+ }
+ }
+
+ /**
+ * Factory
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses RTreeParallelVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * Fill parameter.
+ */
+ protected boolean fill = true;
+
+ /**
+ * Constructor.
+ *
+ * @param fill
+ */
+ public Factory(boolean fill) {
+ super();
+ this.fill = fill;
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new RTreeParallelVisualization<RStarTreeNode, SpatialEntry>(task, fill);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ ArrayList<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
+ for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
+ if(tree instanceof Result) {
+ IterableIterator<ParallelPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND + 2);
+ baseResult.getHierarchy().add((Result) tree, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ protected boolean fill = true;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag fillF = new Flag(TreeMBRVisualization.Factory.FILL_ID);
+ fillF.setDefaultValue(true);
+ if(config.grab(fillF)) {
+ fill = fillF.getValue();
+ }
+ }
+
+ @Override
+ protected Factory makeInstance() {
+ return new Factory(fill);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/ContextChangedEvent.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/package-info.java
index 6d3aa24a..1cefd6f0 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/ContextChangedEvent.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/package-info.java
@@ -1,10 +1,11 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.events;
-
+/**
+ * <p>Visualizers for index structure based on parallel coordinates.</p>
+ */
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -22,30 +23,4 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.events;
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-import java.util.EventObject;
-
-import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
-
-/**
- * Event produced when the visualizer context has changed.
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype event
- */
-public abstract class ContextChangedEvent extends EventObject {
- /**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Visualization context changed.
- *
- * @param source context that has changed
- */
- public ContextChangedEvent(VisualizerContext source) {
- super(source);
- }
-} \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.index; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/package-info.java
index f01505c7..0ab7d565 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/package-info.java
@@ -6,7 +6,7 @@
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-Copyright (C) 2011
+Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java
new file mode 100644
index 00000000..cb8ee645
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java
@@ -0,0 +1,184 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2011
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.result.DBIDSelection;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.RangeSelection;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.SelectionResult;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
+
+/**
+ * Visualizer for generating an SVG-Element representing the selected range for
+ * each dimension
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has RangeSelection oneway - - visualizes
+ */
+public class SelectionAxisRangeVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Selection Axis Range";
+
+ /**
+ * CSS Class for the range marker
+ */
+ public static final String MARKER = "selectionAxisRange";
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public SelectionAxisRangeVisualization(VisualizationTask task) {
+ super(task);
+ addCSSClasses(svgp);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleLibrary();
+ // Class for the cube
+ if(!svgp.getCSSClassManager().contains(MARKER)) {
+ CSSClass cls = new CSSClass(this, MARKER);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+
+ @Override
+ protected void redraw() {
+ DBIDSelection selContext = context.getSelection();
+ if(selContext == null || !(selContext instanceof RangeSelection)) {
+ return;
+ }
+ DoubleDoublePair[] ranges = ((RangeSelection) selContext).getRanges();
+ if(ranges == null) {
+ return;
+ }
+
+ // Project:
+ double[] min = new double[ranges.length];
+ double[] max = new double[ranges.length];
+ for(int d = 0; d < ranges.length; d++) {
+ if(ranges[d] != null) {
+ min[d] = ranges[d].first;
+ max[d] = ranges[d].second;
+ }
+ }
+ min = proj.fastProjectDataToRenderSpace(min);
+ max = proj.fastProjectDataToRenderSpace(max);
+
+ int dim = proj.getVisibleDimensions();
+ for(int d = 0; d < dim; d++) {
+ if(ranges[proj.getDimForVisibleAxis(d)] != null) {
+ double amin = Math.min(min[d], max[d]);
+ double amax = Math.max(min[d], max[d]);
+ Element rect = svgp.svgRect(getVisibleAxisX(d) - (0.01 * StyleLibrary.SCALE), amin, 0.02 * StyleLibrary.SCALE, amax - amin);
+ SVGUtil.addCSSClass(rect, MARKER);
+ layer.appendChild(rect);
+ }
+ }
+ }
+
+ /**
+ * Factory for visualizers to generate an SVG-Element containing a cube as
+ * marker representing the selected range for each dimension
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses SelectionAxisRangeVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * Constructor.
+ */
+ public Factory() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_SELECTION;
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new SelectionAxisRangeVisualization(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ final ArrayList<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ IterableIterator<ParallelPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java
new file mode 100644
index 00000000..f6de4133
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java
@@ -0,0 +1,167 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2011
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.result.DBIDSelection;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.SelectionResult;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
+
+/**
+ * Visualizer for generating SVG-Elements representing the selected objects
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.has SelectionResult oneway - - visualizes
+ */
+public class SelectionLineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Selection Line";
+
+ /**
+ * CSS Class for the range marker
+ */
+ public static final String MARKER = "SelectionLine";
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public SelectionLineVisualization(VisualizationTask task) {
+ super(task);
+ addCSSClasses(svgp);
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ @Override
+ protected void redraw() {
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = selContext.getSelectedIds();
+
+ for(DBID objId : selection) {
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(objId));
+
+ SVGPath path = new SVGPath();
+ for(int i = 0; i < proj.getVisibleDimensions(); i++) {
+ path.drawTo(getVisibleAxisX(i), yPos[i]);
+ }
+ Element marker = path.makeElement(svgp);
+ SVGUtil.addCSSClass(marker, MARKER);
+ layer.appendChild(marker);
+ }
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleLibrary();
+ // Class for the cube
+ if(!svgp.getCSSClassManager().contains(MARKER)) {
+ CSSClass cls = new CSSClass(this, MARKER);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * 2.);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+
+ /**
+ * Factory for visualizers to generate an SVG-Element containing a cube as
+ * marker representing the selected range for each dimension
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * Constructor.
+ */
+ public Factory() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new SelectionLineVisualization(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ final ArrayList<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ IterableIterator<ParallelPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA -1);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java
new file mode 100644
index 00000000..ead8e5ce
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java
@@ -0,0 +1,316 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2011
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.svg.SVGPoint;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.logging.Logging;
+import de.lmu.ifi.dbs.elki.result.DBIDSelection;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.RangeSelection;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.SelectionResult;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projections.Projection;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVisualization;
+
+/**
+ * Tool-Visualization for the tool to select axis ranges
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has RangeSelection oneway - - updates
+ */
+public class SelectionToolAxisRangeVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DragableArea.DragListener {
+ /**
+ * The logger for this class.
+ */
+ protected static final Logging logger = Logging.getLogger(SelectionToolAxisRangeVisualization.class);
+
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Axis Range Selection";
+
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ */
+ private static final String CSS_RANGEMARKER = "selectionAxisRangeMarker";
+
+ /**
+ * Element for selection rectangle
+ */
+ private Element rtag;
+
+ /**
+ * Element for the rectangle to add listeners
+ */
+ private Element etag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public SelectionToolAxisRangeVisualization(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ }
+
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+
+ // rtag: tag for the selected rect
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
+
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.1 * getMarginTop(), getSizeX() + getMarginLeft() * .2, getSizeY() + getMarginTop() * .2, this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
+ }
+
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
+ }
+ }
+
+ /**
+ * Set the selected ranges and the mask for the actual dimensions in the
+ * context
+ *
+ * @param x1 min x-value
+ * @param x2 max x-value
+ * @param y1 min y-value
+ * @param y2 max y-value
+ */
+ private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
+ final int dim = proj.getVisibleDimensions();
+ int minaxis = dim + 1;
+ int maxaxis = -1;
+ {
+ int i = 0;
+ while(i < dim) {
+ double axx = getVisibleAxisX(i);
+ if(x1 < axx || x2 < axx) {
+ minaxis = i;
+ break;
+ }
+ i++;
+ }
+ while(i <= dim) {
+ double axx = getVisibleAxisX(i);
+ if(x2 < axx && x1 < axx) {
+ maxaxis = i;
+ break;
+ }
+ i++;
+ }
+ }
+ double z1 = Math.max(Math.min(y1, y2), 0);
+ double z2 = Math.min(Math.max(y1, y2), getSizeY());
+ for(int i = minaxis; i < maxaxis; i++) {
+ double v1 = proj.fastProjectRenderToDataSpace(z1, i);
+ double v2 = proj.fastProjectRenderToDataSpace(z2, i);
+ if (logger.isDebugging()) {
+ logger.debug("Axis "+i+" dimension "+proj.getDimForVisibleAxis(i)+" "+v1+" to "+v2);
+ }
+ ranges[proj.getDimForVisibleAxis(i)] = new DoubleDoublePair(Math.min(v1, v2), Math.max(v1, v2));
+ }
+ }
+
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
+ }
+
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
+ }
+
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(proj, startPoint, dragPoint);
+ }
+ return true;
+ }
+
+ /**
+ * Update the selection in the context.
+ *
+ * @param proj The projection
+ * @param p1 First Point of the selected rectangle
+ * @param p2 Second Point of the selected rectangle
+ */
+ private void updateSelection(Projection proj, SVGPoint p1, SVGPoint p2) {
+ DBIDSelection selContext = context.getSelection();
+ ModifiableDBIDs selection;
+ if(selContext != null) {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+ }
+ else {
+ selection = DBIDUtil.newHashSet();
+ }
+ DoubleDoublePair[] ranges;
+
+ if(p1 == null || p2 == null) {
+ logger.warning("no rect selected: p1: " + p1 + " p2: " + p2);
+ }
+ else {
+ double x1 = Math.min(p1.getX(), p2.getX());
+ double x2 = Math.max(p1.getX(), p2.getX());
+ double y1 = Math.max(p1.getY(), p2.getY());
+ double y2 = Math.min(p1.getY(), p2.getY());
+
+ int dim = proj.getInputDimensionality();
+ if(selContext instanceof RangeSelection) {
+ ranges = ((RangeSelection) selContext).getRanges();
+ }
+ else {
+ ranges = new DoubleDoublePair[dim];
+ }
+ updateSelectionRectKoordinates(x1, x2, y1, y2, ranges);
+
+ selection.clear();
+ boolean idIn = true;
+ for(DBID id : relation.iterDBIDs()) {
+ NumberVector<?, ?> dbTupel = relation.get(id);
+ idIn = true;
+ for(int i = 0; i < dim; i++) {
+ if(ranges != null && ranges[i] != null) {
+ if(dbTupel.doubleValue(i + 1) < ranges[i].first || dbTupel.doubleValue(i + 1) > ranges[i].second) {
+ idIn = false;
+ break;
+ }
+ }
+ }
+ if(idIn == true) {
+ selection.add(id);
+ }
+ }
+ context.setSelection(new RangeSelection(selection, ranges));
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
+ }
+ }
+
+ /**
+ * Factory for tool visualizations for selecting ranges and the inclosed
+ * objects
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses SelectionToolAxisRangeVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public Factory() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new SelectionToolAxisRangeVisualization(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ final ArrayList<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ IterableIterator<ParallelPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
+ task.put(VisualizationTask.META_TOOL, true);
+ task.put(VisualizationTask.META_NOTHUMB, true);
+ task.put(VisualizationTask.META_NOEXPORT, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java
new file mode 100644
index 00000000..966e754a
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java
@@ -0,0 +1,331 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2011
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.awt.geom.Line2D;
+import java.util.ArrayList;
+
+import org.apache.batik.dom.events.DOMMouseEvent;
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.svg.SVGPoint;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.result.DBIDSelection;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.SelectionResult;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVisualization;
+
+/**
+ * Tool-Visualization for the tool to select objects
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has DBIDSelection oneway - - updates
+ */
+public class SelectionToolLineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DragableArea.DragListener {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Object Selection";
+
+ /**
+ * CSS class of the selection rectangle while selecting.
+ */
+ private static final String CSS_RANGEMARKER = "selectionRangeMarker";
+
+ /**
+ * Input modes
+ *
+ * @apiviz.exclude
+ */
+ private enum Mode {
+ REPLACE, ADD, INVERT
+ }
+
+ /**
+ * Element for selection rectangle
+ */
+ Element rtag;
+
+ /**
+ * Element for the rectangle to add listeners
+ */
+ Element etag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public SelectionToolLineVisualization(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ }
+
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
+
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.5 * getMarginTop(), getSizeX() + .2 * getMarginLeft(), getMarginTop() * 1.5 + getSizeY(), this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
+ }
+
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
+ }
+ }
+
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
+ }
+
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
+ }
+
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ Mode mode = getInputMode(evt);
+ deleteChildren(rtag);
+ if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(mode, startPoint, dragPoint);
+ }
+ return true;
+ }
+
+ /**
+ * Get the current input mode, on each mouse event.
+ *
+ * @param evt Mouse event.
+ * @return current input mode
+ */
+ private Mode getInputMode(Event evt) {
+ if(evt instanceof DOMMouseEvent) {
+ DOMMouseEvent domme = (DOMMouseEvent) evt;
+ // TODO: visual indication of mode possible?
+ if(domme.getShiftKey()) {
+ return Mode.ADD;
+ }
+ else if(domme.getCtrlKey()) {
+ return Mode.INVERT;
+ }
+ else {
+ return Mode.REPLACE;
+ }
+ }
+ // Default mode is replace.
+ return Mode.REPLACE;
+ }
+
+ /**
+ * Updates the selection in the context.<br>
+ *
+ * @param mode Input mode
+ * @param p1 first point of the selected rectangle
+ * @param p2 second point of the selected rectangle
+ */
+ private void updateSelection(Mode mode, SVGPoint p1, SVGPoint p2) {
+ DBIDSelection selContext = context.getSelection();
+ // Note: we rely on SET semantics below!
+ final HashSetModifiableDBIDs selection;
+ if(selContext == null || mode == Mode.REPLACE) {
+ selection = DBIDUtil.newHashSet();
+ }
+ else {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+ }
+ int[] axisrange = getAxisRange(Math.min(p1.getX(), p2.getX()), Math.max(p1.getX(), p2.getX()));
+ DBIDs objIds = ResultUtil.getSamplingResult(relation).getSample();
+ for(DBID objId : objIds){
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(objId));
+ if(checkSelected(axisrange, yPos, Math.max(p1.getX(), p2.getX()), Math.min(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()), Math.min(p1.getY(), p2.getY()))) {
+ if(mode == Mode.INVERT) {
+ if(!selection.contains(objId)) {
+ selection.add(objId);
+ }
+ else {
+ selection.remove(objId);
+ }
+ }
+ else {
+ // In REPLACE and ADD, add objects.
+ // The difference was done before by not re-using the selection.
+ // Since we are using a set, we can just add in any case.
+ selection.add(objId);
+ }
+ }
+ }
+ context.setSelection(new DBIDSelection(selection));
+ }
+
+ private int[] getAxisRange(double x1, double x2) {
+ final int dim = proj.getVisibleDimensions();
+ int minaxis = 0;
+ int maxaxis = 0;
+ boolean minx = true;
+ boolean maxx = false;
+ int count = -1;
+ for(int i = 0; i < dim; i++) {
+ if(minx && getVisibleAxisX(i) > x1) {
+ minaxis = count;
+ minx = false;
+ maxx = true;
+ }
+ if(maxx && (getVisibleAxisX(i) > x2 || i == dim - 1)) {
+ maxaxis = count + 1;
+ if(i == dim - 1 && getVisibleAxisX(i) <= x2) {
+ maxaxis++;
+ }
+ break;
+ }
+ count = i;
+ }
+ return new int[] { minaxis, maxaxis };
+ }
+
+ private boolean checkSelected(int[] ar, double[] yPos, double x1, double x2, double y1, double y2) {
+ final int dim = proj.getVisibleDimensions();
+ if (ar[0] < 0) {
+ ar[0] = 0;
+ }
+ if(ar[1] >= dim) {
+ ar[1] = dim - 1;
+ }
+ for(int i = ar[0] + 1; i <= ar[1] - 1; i++) {
+ if(yPos[i] <= y1 && yPos[i] >= y2) {
+ return true;
+ }
+ }
+ Line2D.Double idline1 = new Line2D.Double(getVisibleAxisX(ar[0]), yPos[ar[0]], getVisibleAxisX(ar[0] + 1), yPos[ar[0] + 1]);
+ Line2D.Double idline2 = new Line2D.Double(getVisibleAxisX(ar[1] - 1), yPos[ar[1] - 1], getVisibleAxisX(ar[1]), yPos[ar[1]]);
+ Line2D.Double rectline1 = new Line2D.Double(x2, y1, x1, y1);
+ Line2D.Double rectline2 = new Line2D.Double(x2, y1, x2, y2);
+ Line2D.Double rectline3 = new Line2D.Double(x2, y2, x1, y2);
+ if(idline1.intersectsLine(rectline1) || idline1.intersectsLine(rectline2) || idline1.intersectsLine(rectline3)) {
+ return true;
+ }
+ Line2D.Double rectline4 = new Line2D.Double(x1, y1, x1, y2);
+ if(idline2.intersectsLine(rectline1) || idline2.intersectsLine(rectline4) || idline2.intersectsLine(rectline3)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
+ }
+ }
+
+ /**
+ * Factory for tool visualizations for selecting objects
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses SelectionToolLineVisualization - - «create»
+ *
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public Factory() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new SelectionToolLineVisualization(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ final ArrayList<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ IterableIterator<ParallelPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
+ task.put(VisualizationTask.META_TOOL, true);
+ task.put(VisualizationTask.META_NOTHUMB, true);
+ task.put(VisualizationTask.META_NOEXPORT, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/package-info.java
new file mode 100644
index 00000000..e4589b05
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <p>Visualizers for object selection based on parallel projections.</p>
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.selection; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/P2DVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java
index b6b4b418..cce9ea5d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/P2DVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -28,7 +28,11 @@ import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.SamplingResult;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.projections.CanvasSize;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
@@ -43,7 +47,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
* @apiviz.landmark
* @apiviz.has Projection2D
*/
-public abstract class P2DVisualization<NV extends NumberVector<?, ?>> extends AbstractVisualization {
+public abstract class AbstractScatterplotVisualization extends AbstractVisualization {
/**
* The current projection
*/
@@ -52,17 +56,23 @@ public abstract class P2DVisualization<NV extends NumberVector<?, ?>> extends Ab
/**
* The representation we visualize
*/
- final protected Relation<NV> rel;
+ final protected Relation<? extends NumberVector<?, ?>> rel;
+
+ /**
+ * The DBID sample
+ */
+ final protected SamplingResult sample;
/**
* Constructor.
*
* @param task Visualization task
*/
- public P2DVisualization(VisualizationTask task) {
+ public AbstractScatterplotVisualization(VisualizationTask task) {
super(task);
this.proj = task.getProj();
this.rel = task.getRelation();
+ this.sample = ResultUtil.getSamplingResult(rel);
final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
this.layer = setupCanvas(svgp, proj, margin, task.getWidth(), task.getHeight());
}
@@ -78,8 +88,21 @@ public abstract class P2DVisualization<NV extends NumberVector<?, ?>> extends Ab
* @return wrapper element with appropriate view box.
*/
public static Element setupCanvas(SVGPlot svgp, Projection2D proj, double margin, double width, double height) {
- Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, proj.estimateTransformString(margin, width, height));
+ final CanvasSize canvas = proj.estimateViewport();
+ final double sizex = canvas.getDiffX();
+ final double sizey = canvas.getDiffY();
+ String transform = SVGUtil.makeMarginTransform(width, height, sizex, sizey, margin) + " translate(" + SVGUtil.fmt(sizex / 2) + " " + SVGUtil.fmt(sizey / 2) + ")";
+
+ final Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
return layer;
}
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(current == proj) {
+ synchronizedRedraw();
+ }
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/AbstractTooltipVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java
index 4e0171ae..8ce6ad67 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/AbstractTooltipVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -30,11 +30,10 @@ import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
+import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
@@ -44,11 +43,9 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
* General base class for a tooltip visualizer.
*
* @author Erich Schubert
- *
- * @param <NV> Number Vector
*/
// TODO: can we improve performance by not adding as many hovers?
-public abstract class AbstractTooltipVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements DataStoreListener {
+public abstract class AbstractTooltipVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -69,6 +66,21 @@ public abstract class AbstractTooltipVisualization<NV extends NumberVector<NV, ?
*/
public static final String TOOLTIP_AREA = "tooltip_area";
+ /**
+ * Our event listener.
+ */
+ EventListener hoverer = new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ handleHoverEvent(evt);
+ }
+ };
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
public AbstractTooltipVisualization(VisualizationTask task) {
super(task);
context.addDataStoreListener(this);
@@ -84,22 +96,15 @@ public abstract class AbstractTooltipVisualization<NV extends NumberVector<NV, ?
public void redraw() {
setupCSS(svgp);
- double dotsize = 2 * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT);
+ double dotsize = context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT);
- EventListener hoverer = new EventListener() {
- @Override
- public void handleEvent(Event evt) {
- handleHoverEvent(evt);
- }
- };
-
- for(DBID id : rel.iterDBIDs()) {
+ for(DBID id : sample.getSample()) {
double[] v = proj.fastProjectDataToRenderSpace(rel.get(id));
Element tooltip = makeTooltip(id, v[0], v[1], dotsize);
SVGUtil.addCSSClass(tooltip, TOOLTIP_HIDDEN);
// sensitive area.
- Element area = svgp.svgCircle(v[0], v[1], dotsize);
+ Element area = svgp.svgRect(v[0] - dotsize, v[1] - dotsize, 2 * dotsize, 2 * dotsize);
SVGUtil.addCSSClass(area, TOOLTIP_AREA);
EventTarget targ = (EventTarget) area;
@@ -172,7 +177,9 @@ public abstract class AbstractTooltipVisualization<NV extends NumberVector<NV, ?
abstract protected void setupCSS(SVGPlot svgp);
@Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
+ public void resultChanged(Result current) {
+ if(sample == current) {
+ synchronizedRedraw();
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/AxisVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java
index 4dc7e820..a8f7fdca 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/AxisVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -23,18 +23,14 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Iterator;
-
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
@@ -52,10 +48,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Remigius Wojdanowski
*
* @apiviz.uses SVGSimpleLinearAxis
- *
- * @param <NV> Type of the DatabaseObject being visualized.
*/
-public class AxisVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> {
+public class AxisVisualization extends AbstractScatterplotVisualization {
/**
* Constructor.
*
@@ -71,28 +65,28 @@ public class AxisVisualization<NV extends NumberVector<NV, ?>> extends P2DVisual
int dim = DatabaseUtil.dimensionality(rel);
// origin
- double[] orig = proj.fastProjectScaledToRender(new Vector(dim));
+ double[] orig = proj.fastProjectScaledToRenderSpace(new double[dim]);
// diagonal point opposite to origin
double[] diag = new double[dim];
for(int d2 = 0; d2 < dim; d2++) {
diag[d2] = 1;
}
- diag = proj.fastProjectScaledToRender(new Vector(diag));
+ diag = proj.fastProjectScaledToRenderSpace(diag);
// compute angle to diagonal line, used for axis labeling.
double diaga = Math.atan2(diag[1] - orig[1], diag[0] - orig[0]);
- double alfontsize = 1.2 * context.getStyleLibrary().getTextSize(StyleLibrary.AXIS_LABEL);
- CSSClass alcls = new CSSClass(svgp, "unmanaged");
+ double alfontsize = 1.1 * context.getStyleLibrary().getTextSize(StyleLibrary.AXIS_LABEL);
+ CSSClass alcls = new CSSClass(AxisVisualization.class, "unmanaged");
alcls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, SVGUtil.fmt(alfontsize));
alcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.AXIS_LABEL));
alcls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, context.getStyleLibrary().getFontFamily(StyleLibrary.AXIS_LABEL));
// draw axes
for(int d = 0; d < dim; d++) {
- Vector v = new Vector(dim);
- v.set(d, 1);
+ double[] v = new double[dim];
+ v[d] = 1;
// projected endpoint of axis
- double[] ax = proj.fastProjectScaledToRender(v);
+ double[] ax = proj.fastProjectScaledToRenderSpace(v);
boolean righthand = false;
double axa = Math.atan2(ax[1] - orig[1], ax[0] - orig[0]);
if(axa > diaga || (diaga > 0 && axa > diaga + Math.PI)) {
@@ -102,7 +96,7 @@ public class AxisVisualization<NV extends NumberVector<NV, ?>> extends P2DVisual
// " "+(axa*180/Math.PI)+" "+(diaga*180/Math.PI));
if(ax[0] != orig[0] || ax[1] != orig[1]) {
try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), orig[0], orig[1], ax[0], ax[1], true, righthand, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), orig[0], orig[1], ax[0], ax[1], righthand ? SVGSimpleLinearAxis.LabelStyle.RIGHTHAND : SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
// TODO: move axis labeling into drawAxis function.
double offx = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
double offy = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
@@ -125,10 +119,8 @@ public class AxisVisualization<NV extends NumberVector<NV, ?>> extends P2DVisual
*
* @apiviz.stereotype factory
* @apiviz.uses AxisVisualization oneway - - «create»
- *
- * @param <NV>
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
@@ -144,13 +136,13 @@ public class AxisVisualization<NV extends NumberVector<NV, ?>> extends P2DVisual
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new AxisVisualization<NV>(task);
+ return new AxisVisualization(task);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(result, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(result, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND);
baseResult.getHierarchy().add(p, task);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java
new file mode 100644
index 00000000..7672ee93
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java
@@ -0,0 +1,175 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Iterator;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.ClassStylingPolicy;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleResult;
+import de.lmu.ifi.dbs.elki.visualization.style.StylingPolicy;
+import de.lmu.ifi.dbs.elki.visualization.style.marker.MarkerLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
+
+/**
+ * Visualize e.g. a clustering using different markers for different clusters.
+ * This visualizer is not constraint to clusters. It can in fact visualize any
+ * kind of result we have a style source for.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.uses StyleResult
+ */
+public class MarkerVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ */
+ public static final String DOTMARKER = "dot";
+
+ /**
+ * The result we visualize
+ */
+ private StyleResult style;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public MarkerVisualization(VisualizationTask task) {
+ super(task);
+ this.style = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ }
+
+ @Override
+ public void redraw() {
+ final MarkerLibrary ml = context.getStyleLibrary().markers();
+ final double marker_size = context.getStyleLibrary().getSize(StyleLibrary.MARKERPLOT);
+ final StylingPolicy spol = style.getStylingPolicy();
+
+ if(spol instanceof ClassStylingPolicy) {
+ ClassStylingPolicy cspol = (ClassStylingPolicy) spol;
+ for(int cnum = cspol.getMinStyle(); cnum < cspol.getMaxStyle(); cnum++) {
+ for(Iterator<DBID> iter = cspol.iterateClass(cnum); iter.hasNext();) {
+ DBID cur = iter.next();
+ try {
+ final NumberVector<?, ?> vec = rel.get(cur);
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ ml.useMarker(svgp, layer, v[0], v[1], cnum, marker_size);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore.
+ }
+ }
+ }
+ }
+ else {
+ final String FILL = SVGConstants.CSS_FILL_PROPERTY + ":";
+ // Color-based styling. Fall back to dots
+ for(DBID id : sample.getSample()) {
+ try {
+ double[] v = proj.fastProjectDataToRenderSpace(rel.get(id));
+ Element dot = svgp.svgCircle(v[0], v[1], marker_size);
+ SVGUtil.addCSSClass(dot, DOTMARKER);
+ int col = spol.getColorForDBID(id);
+ SVGUtil.setAtt(dot, SVGConstants.SVG_STYLE_ATTRIBUTE, FILL + SVGUtil.colorToString(col));
+ layer.appendChild(dot);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore.
+ }
+ }
+ }
+ }
+
+ /**
+ * Visualization factory
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses MarkerVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Markers";
+
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public Factory() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new MarkerVisualization(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find a style result to visualize:
+ IterableIterator<StyleResult> styleres = ResultUtil.filteredResults(result, StyleResult.class);
+ for(StyleResult c : styleres) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/PolygonVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java
index b0f18666..4dc78ecf 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/PolygonVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,17 +24,13 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.DoubleVector;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.spatial.Polygon;
import de.lmu.ifi.dbs.elki.data.spatial.PolygonsObject;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
@@ -44,7 +40,7 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
@@ -62,7 +58,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @apiviz.has PolygonsObject - - visualizes
*/
-public class PolygonVisualization<V extends NumberVector<?, ?>> extends P2DVisualization<V> implements DataStoreListener {
+public class PolygonVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
/**
* A short name characterizing this Visualizer.
*/
@@ -122,13 +118,13 @@ public class PolygonVisualization<V extends NumberVector<?, ?>> extends P2DVisua
SVGPath path = new SVGPath();
for(Polygon ppoly : poly.getPolygons()) {
Vector first = ppoly.get(0);
- double[] f = proj.fastProjectDataToRenderSpace(first);
+ double[] f = proj.fastProjectDataToRenderSpace(first.getArrayRef());
path.moveTo(f[0], f[1]);
for(Vector v : ppoly) {
if(v == first) {
continue;
}
- double[] p = proj.fastProjectDataToRenderSpace(v);
+ double[] p = proj.fastProjectDataToRenderSpace(v.getArrayRef());
path.drawTo(p[0], p[1]);
}
// close path.
@@ -144,11 +140,6 @@ public class PolygonVisualization<V extends NumberVector<?, ?>> extends P2DVisua
}
}
- @Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
- }
-
/**
* The visualization factory
*
@@ -167,7 +158,7 @@ public class PolygonVisualization<V extends NumberVector<?, ?>> extends P2DVisua
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new PolygonVisualization<DoubleVector>(task);
+ return new PolygonVisualization(task);
}
@Override
@@ -176,8 +167,8 @@ public class PolygonVisualization<V extends NumberVector<?, ?>> extends P2DVisua
for(Relation<?> rel : results) {
if(TypeUtil.POLYGON_TYPE.isAssignableFromType(rel.getDataTypeInformation())) {
// Assume that a 2d projector is using the same coordinates as the polygons.
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
if(DatabaseUtil.dimensionality(p.getRelation()) == 2) {
final VisualizationTask task = new VisualizationTask(NAME, rel, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 10);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ReferencePointsVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java
index df1aac20..644fc96f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ReferencePointsVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -34,7 +34,7 @@ import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.ReferencePointsResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
@@ -52,7 +52,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @apiviz.has ReferencePointsResult oneway - - visualizes
*/
// TODO: add a result listener for the reference points.
-public class ReferencePointsVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> {
+public class ReferencePointsVisualization extends AbstractScatterplotVisualization {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -66,7 +66,7 @@ public class ReferencePointsVisualization<NV extends NumberVector<NV, ?>> extend
/**
* Serves reference points.
*/
- protected ReferencePointsResult<NV> result;
+ protected ReferencePointsResult<? extends NumberVector<?, ?>> result;
/**
* Constructor.
@@ -82,11 +82,11 @@ public class ReferencePointsVisualization<NV extends NumberVector<NV, ?>> extend
@Override
public void redraw() {
setupCSS(svgp);
- Iterator<NV> iter = result.iterator();
+ Iterator<? extends NumberVector<?, ?>> iter = result.iterator();
final double dotsize = context.getStyleLibrary().getSize(StyleLibrary.REFERENCE_POINTS);
while(iter.hasNext()) {
- NV v = iter.next();
+ NumberVector<?, ?> v = iter.next();
double[] projected = proj.fastProjectDataToRenderSpace(v);
Element dot = svgp.svgCircle(projected[0], projected[1], dotsize);
SVGUtil.addCSSClass(dot, REFPOINT);
@@ -112,10 +112,8 @@ public class ReferencePointsVisualization<NV extends NumberVector<NV, ?>> extend
*
* @apiviz.stereotype factory
* @apiviz.uses ReferencePointsVisualization oneway - - «create»
- *
- * @param <NV> Type of the DatabaseObject being visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Constructor, adhering to
* {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
@@ -126,10 +124,10 @@ public class ReferencePointsVisualization<NV extends NumberVector<NV, ?>> extend
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ReferencePointsResult<NV>> rps = ResultUtil.filterResults(result, ReferencePointsResult.class);
- for(ReferencePointsResult<NV> rp : rps) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ Collection<ReferencePointsResult<?>> rps = ResultUtil.filterResults(result, ReferencePointsResult.class);
+ for(ReferencePointsResult<?> rp : rps) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, rp, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
baseResult.getHierarchy().add(rp, task);
@@ -140,7 +138,7 @@ public class ReferencePointsVisualization<NV extends NumberVector<NV, ?>> extend
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new ReferencePointsVisualization<NV>(task);
+ return new ReferencePointsVisualization(task);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ToolBox2DVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java
index c2339fe2..7ec5f1ac 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ToolBox2DVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot;
/*
This file is part of ELKI:
@@ -24,7 +24,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import org.apache.batik.util.SVGConstants;
@@ -33,17 +32,15 @@ import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.logging.Logging;
-import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
-import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projections.CanvasSize;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
@@ -51,7 +48,6 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
/**
* Renders a tool box on the left of the 2D visualization
@@ -59,10 +55,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
* @author Heidi Kolb
*
* @apiviz.has VisualizationTask oneway - - visualizes
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
-public class ToolBox2DVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> {
+public class ToolBox2DVisualization extends AbstractScatterplotVisualization {
/**
* A short name characterizing this Visualizer.
*/
@@ -101,17 +95,11 @@ public class ToolBox2DVisualization<NV extends NumberVector<NV, ?>> extends P2DV
public ToolBox2DVisualization(VisualizationTask task) {
super(task);
// TODO: which result do we best attach to?
- context.addContextChangeListener(this);
context.addResultListener(this);
incrementalRedraw();
}
@Override
- public void contextChanged(ContextChangedEvent e) {
- synchronizedRedraw();
- }
-
- @Override
protected void redraw() {
addCSSClasses(svgp);
container = svgp.svgElement(SVGConstants.SVG_G_TAG);
@@ -146,12 +134,12 @@ public class ToolBox2DVisualization<NV extends NumberVector<NV, ?>> extends P2DV
}
// calculate the position of the first tool
- Pair<DoubleMinMax, DoubleMinMax> pt = proj.estimateViewport();
- double x = pt.getFirst().getMin() - 0.17 * scale;
+ CanvasSize viewport = proj.estimateViewport();
+ double x = viewport.getMinX() - 0.17 * scale;
double width = 0.07 * scale;
double height = 0.06 * scale;
- double miny = pt.getSecond().getMin();
- double maxy = pt.getSecond().getMax();
+ double miny = viewport.getMinY();
+ double maxy = viewport.getMaxY();
double y = (miny + maxy) / 2 - (vis.size() * height * 1.4) / 2;
if(y < miny) {
logger.warning("Too many Tools");
@@ -248,7 +236,7 @@ public class ToolBox2DVisualization<NV extends NumberVector<NV, ?>> extends P2DV
if(VisualizerUtil.isVisible(tool)) {
context.setSelection(null);
}
- context.setVisualizationVisibility(tool, true);
+ VisualizerUtil.setVisible(context, tool, true);
}
@Override
@@ -288,10 +276,8 @@ public class ToolBox2DVisualization<NV extends NumberVector<NV, ?>> extends P2DV
*
* @apiviz.stereotype factory
* @apiviz.uses ToolBox2DVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Constructor
*/
@@ -301,17 +287,18 @@ public class ToolBox2DVisualization<NV extends NumberVector<NV, ?>> extends P2DV
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new ToolBox2DVisualization<NV>(task);
+ return new ToolBox2DVisualization(task);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(result, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(result, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
task.put(VisualizationTask.META_NOTHUMB, true);
task.put(VisualizationTask.META_NOEXPORT, true);
+ task.put(VisualizationTask.META_NOEMBED, true);
baseResult.getHierarchy().add(p, task);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TooltipScoreVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java
index 309cc9c7..838390d0 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TooltipScoreVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,21 +24,20 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.text.NumberFormat;
-import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
@@ -59,13 +58,18 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Remigius Wojdanowski
*/
-public class TooltipScoreVisualization<NV extends NumberVector<NV, ?>> extends AbstractTooltipVisualization<NV> {
+public class TooltipScoreVisualization extends AbstractTooltipVisualization {
/**
* A short name characterizing this Visualizer.
*/
public static final String NAME = "Outlier Score Tooltips";
/**
+ * A short name characterizing this Visualizer.
+ */
+ public static final String NAME_GEN = "Score Tooltips";
+
+ /**
* Number format.
*/
NumberFormat nf;
@@ -142,10 +146,8 @@ public class TooltipScoreVisualization<NV extends NumberVector<NV, ?>> extends A
*
* @apiviz.stereotype factory
* @apiviz.uses TooltipScoreVisualization oneway - - «create»
- *
- * @param <NV> Data type visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Parameter for the gamma-correction.
*
@@ -178,7 +180,7 @@ public class TooltipScoreVisualization<NV extends NumberVector<NV, ?>> extends A
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new TooltipScoreVisualization<NV>(task, nf);
+ return new TooltipScoreVisualization(task, nf);
}
@Override
@@ -186,14 +188,39 @@ public class TooltipScoreVisualization<NV extends NumberVector<NV, ?>> extends A
// TODO: we can also visualize other scores!
List<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
for(OutlierResult o : ors) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, o.getScores(), p.getRelation(), this);
task.put(VisualizationTask.META_TOOL, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
baseResult.getHierarchy().add(o.getScores(), task);
baseResult.getHierarchy().add(p, task);
}
}
+ List<Relation<?>> rrs = ResultUtil.filterResults(result, Relation.class);
+ for(Relation<?> r : rrs) {
+ if(!TypeUtil.DOUBLE.isAssignableFromType(r.getDataTypeInformation())) {
+ continue;
+ }
+ // Skip if we already considered it above
+ boolean add = true;
+ for(Result p : baseResult.getHierarchy().getChildren(r)) {
+ if(p instanceof VisualizationTask && ((VisualizationTask) p).getFactory() instanceof Factory) {
+ add = false;
+ break;
+ }
+ }
+ if(add) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME_GEN, r, p.getRelation(), this);
+ task.put(VisualizationTask.META_TOOL, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
+ baseResult.getHierarchy().add(r, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
}
/**
@@ -203,7 +230,7 @@ public class TooltipScoreVisualization<NV extends NumberVector<NV, ?>> extends A
*
* @apiviz.exclude
*/
- public static class Parameterizer<NV extends NumberVector<NV, ?>> extends AbstractParameterizer {
+ public static class Parameterizer extends AbstractParameterizer {
protected int digits = 4;
@Override
@@ -217,8 +244,8 @@ public class TooltipScoreVisualization<NV extends NumberVector<NV, ?>> extends A
}
@Override
- protected Factory<NV> makeInstance() {
- return new Factory<NV>(digits);
+ protected Factory makeInstance() {
+ return new Factory(digits);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TooltipStringVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java
index 044dc66f..9baea926 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TooltipStringVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,7 +24,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
@@ -32,13 +31,12 @@ import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.ClassLabel;
import de.lmu.ifi.dbs.elki.data.ExternalID;
import de.lmu.ifi.dbs.elki.data.LabelList;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
@@ -56,10 +54,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.has Relation oneway - - visualizes
- *
- * @param <NV> Data type visualized.
*/
-public class TooltipStringVisualization<NV extends NumberVector<NV, ?>> extends AbstractTooltipVisualization<NV> {
+public class TooltipStringVisualization extends AbstractTooltipVisualization {
/**
* A short name characterizing this Visualizer.
*/
@@ -161,10 +157,8 @@ public class TooltipStringVisualization<NV extends NumberVector<NV, ?>> extends
*
* @apiviz.stereotype factory
* @apiviz.uses TooltipStringVisualization oneway - - «create»
- *
- * @param <NV>
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Constructor, adhering to
* {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
@@ -175,7 +169,7 @@ public class TooltipStringVisualization<NV extends NumberVector<NV, ?>> extends
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new TooltipStringVisualization<NV>(task);
+ return new TooltipStringVisualization(task);
}
@Override
@@ -183,37 +177,41 @@ public class TooltipStringVisualization<NV extends NumberVector<NV, ?>> extends
ArrayList<Relation<?>> reps = ResultUtil.filterResults(result, Relation.class);
for(Relation<?> rep : reps) {
if(DBID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME_ID, rep, p.getRelation(), this);
task.put(VisualizationTask.META_TOOL, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
baseResult.getHierarchy().add(rep, task);
baseResult.getHierarchy().add(p, task);
}
}
if(ClassLabel.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME_CLASS, rep, p.getRelation(), this);
task.put(VisualizationTask.META_TOOL, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
baseResult.getHierarchy().add(rep, task);
baseResult.getHierarchy().add(p, task);
}
}
if(LabelList.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME_LABEL, rep, p.getRelation(), this);
task.put(VisualizationTask.META_TOOL, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
baseResult.getHierarchy().add(rep, task);
baseResult.getHierarchy().add(p, task);
}
}
if(ExternalID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME_EID, rep, p.getRelation(), this);
task.put(VisualizationTask.META_TOOL, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
baseResult.getHierarchy().add(rep, task);
baseResult.getHierarchy().add(p, task);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java
new file mode 100644
index 00000000..430ab194
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java
@@ -0,0 +1,276 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2011
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.Cluster;
+import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.data.model.Model;
+import de.lmu.ifi.dbs.elki.data.spatial.Polygon;
+import de.lmu.ifi.dbs.elki.data.spatial.SpatialUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.math.geometry.AlphaShape;
+import de.lmu.ifi.dbs.elki.math.geometry.GrahamScanConvexHull2D;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projections.CanvasSize;
+import de.lmu.ifi.dbs.elki.visualization.projections.Projection;
+import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
+
+/**
+ * Visualizer for generating an SVG-Element containing the convex hull / alpha
+ * shape of each cluster.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.has Clustering oneway - - visualizes
+ * @apiviz.uses ConvexHull2D
+ * @apiviz.uses AlphaShape
+ */
+public class ClusterHullVisualization extends AbstractScatterplotVisualization {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Cluster Hull Visualization";
+
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ */
+ public static final String CLUSTERHULL = "cluster-hull";
+
+ /**
+ * The result we work on
+ */
+ Clustering<Model> clustering;
+
+ /**
+ * Alpha value
+ */
+ double alpha = Double.POSITIVE_INFINITY;
+
+ /**
+ * Constructor
+ *
+ * @param task VisualizationTask
+ * @param alpha Alpha value
+ */
+ public ClusterHullVisualization(VisualizationTask task, double alpha) {
+ super(task);
+ this.clustering = task.getResult();
+ this.alpha = alpha;
+ incrementalRedraw();
+ }
+
+ @Override
+ protected void redraw() {
+ // Viewport size, for "relative size" computations
+ final CanvasSize viewp = proj.estimateViewport();
+ double projarea = viewp.getDiffX() * viewp.getDiffY();
+
+ double opacity = 0.25;
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+
+ for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<?> clus = ci.next();
+ final DBIDs ids = clus.getIDs();
+
+ if(alpha >= Double.POSITIVE_INFINITY) {
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
+
+ for(DBID clpnum : ids) {
+ double[] projP = proj.fastProjectDataToRenderSpace(rel.get(clpnum));
+ hull.add(new Vector(projP));
+ }
+ Polygon chres = hull.getHull();
+
+ // Plot the convex hull:
+ if(chres != null && chres.size() > 1) {
+ SVGPath path = new SVGPath(chres);
+ // Approximate area (using bounding box)
+ double hullarea = SpatialUtil.volume(chres);
+ final double relativeArea = (projarea - hullarea) / projarea;
+ final double relativeSize = (double) ids.size() / rel.size();
+ opacity = Math.sqrt(relativeSize * relativeArea);
+
+ Element hulls = path.makeElement(svgp);
+ addCSSClasses(svgp, cnum, opacity);
+ SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
+ layer.appendChild(hulls);
+ }
+ }
+ else {
+ ArrayList<Vector> ps = new ArrayList<Vector>(ids.size());
+ for(DBID clpnum : ids) {
+ double[] projP = proj.fastProjectDataToRenderSpace(rel.get(clpnum));
+ ps.add(new Vector(projP));
+ }
+ List<Polygon> polys = (new AlphaShape(ps, alpha * Projection.SCALE)).compute();
+ for(Polygon p : polys) {
+ SVGPath path = new SVGPath(p);
+ Element hulls = path.makeElement(svgp);
+ addCSSClasses(svgp, cnum, 0.5);
+ SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
+ layer.appendChild(hulls);
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp, int clusterID, double opac) {
+ ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+
+ CSSClass cls = new CSSClass(this, CLUSTERHULL + clusterID);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+
+ final String color;
+ if(clustering.getAllClusters().size() == 1) {
+ color = "black";
+ }
+ else {
+ color = colors.getColor(clusterID);
+ }
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, opac);
+
+ svgp.addCSSClassOrLogError(cls);
+ }
+
+ /**
+ * Factory for visualizers to generate an SVG-Element containing the convex
+ * hull or alpha shape of a cluster.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses ClusterHullVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * Alpha value
+ */
+ double alpha = Double.POSITIVE_INFINITY;
+
+ /**
+ * Constructor.
+ *
+ * @param alpha Alpha value
+ */
+ public Factory(double alpha) {
+ super();
+ this.alpha = alpha;
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new ClusterHullVisualization(task, alpha);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Alpha-Value for alpha-shapes
+ *
+ * <p>
+ * Key: {@code -hull.alpha}
+ * </p>
+ */
+ public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("hull.alpha", "Alpha value for hull drawing (in projected space!).");
+
+ /**
+ * Alpha value
+ */
+ double alpha = Double.POSITIVE_INFINITY;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, Double.POSITIVE_INFINITY);
+ if(config.grab(alphaP)) {
+ alpha = alphaP.getValue();
+ }
+ }
+
+ @Override
+ protected Factory makeInstance() {
+ return new Factory(alpha);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterMeanVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java
index d1f1712d..c9443a9f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterMeanVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster;
/*
This file is part of ELKI:
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Collection;
import java.util.Iterator;
import org.apache.batik.util.SVGConstants;
@@ -33,19 +32,26 @@ import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.model.MeanModel;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.style.marker.MarkerLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
* Visualize the mean of a KMeans-Clustering
@@ -53,10 +59,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Heidi Kolb
*
* @apiviz.has MeanModel oneway - - visualizes
- *
- * @param <NV> Type of the DatabaseObject being visualized.
*/
-public class ClusterMeanVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> {
+public class ClusterMeanVisualization extends AbstractScatterplotVisualization {
/**
* A short name characterizing this Visualizer.
*/
@@ -73,14 +77,30 @@ public class ClusterMeanVisualization<NV extends NumberVector<NV, ?>> extends P2
private final static String CSS_MEAN = "mean-marker";
/**
+ * CSS class name for center of the means
+ */
+ private final static String CSS_MEAN_STAR = "mean-star";
+
+ /**
* Clustering to visualize.
*/
- Clustering<MeanModel<NV>> clustering;
+ Clustering<MeanModel<? extends NumberVector<?, ?>>> clustering;
- public ClusterMeanVisualization(VisualizationTask task) {
+ /**
+ * Draw stars
+ */
+ boolean stars;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ * @param stars Draw stars
+ */
+ public ClusterMeanVisualization(VisualizationTask task, boolean stars) {
super(task);
this.clustering = task.getResult();
- context.addContextChangeListener(this);
+ this.stars = stars;
incrementalRedraw();
}
@@ -91,9 +111,9 @@ public class ClusterMeanVisualization<NV extends NumberVector<NV, ?>> extends P2
MarkerLibrary ml = context.getStyleLibrary().markers();
double marker_size = context.getStyleLibrary().getSize(StyleLibrary.MARKERPLOT);
- Iterator<Cluster<MeanModel<NV>>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<MeanModel<NV>> clus = ci.next();
+ Iterator<Cluster<MeanModel<? extends NumberVector<?, ?>>>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; ci.hasNext(); cnum++) {
+ Cluster<MeanModel<? extends NumberVector<?, ?>>> clus = ci.next();
double[] mean = proj.fastProjectDataToRenderSpace(clus.getModel().getMean());
// add a greater Marker for the mean
@@ -108,6 +128,18 @@ public class ClusterMeanVisualization<NV extends NumberVector<NV, ?>> extends P2
layer.appendChild(meanMarkerCenter);
layer.appendChild(meanMarkerCenter2);
+
+ if(stars) {
+ SVGPath star = new SVGPath();
+ for(DBID id : clus.getIDs()) {
+ double[] obj = proj.fastProjectDataToRenderSpace(rel.get(id));
+ star.moveTo(mean);
+ star.drawTo(obj);
+ }
+ Element stare = star.makeElement(svgp);
+ SVGUtil.setCSSClass(stare, CSS_MEAN_STAR + "_" + cnum);
+ layer.appendChild(stare);
+ }
}
}
@@ -118,16 +150,31 @@ public class ClusterMeanVisualization<NV extends NumberVector<NV, ?>> extends P2
*/
private void addCSSClasses(SVGPlot svgp) {
if(!svgp.getCSSClassManager().contains(CSS_MEAN_CENTER)) {
- CSSClass center = new CSSClass(svgp, CSS_MEAN_CENTER);
+ CSSClass center = new CSSClass(this, CSS_MEAN_CENTER);
center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.DEFAULT));
center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.AXIS_TICK) / 2);
svgp.addCSSClassOrLogError(center);
}
if(!svgp.getCSSClassManager().contains(CSS_MEAN)) {
- CSSClass center = new CSSClass(svgp, CSS_MEAN);
+ CSSClass center = new CSSClass(this, CSS_MEAN);
center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
svgp.addCSSClassOrLogError(center);
}
+ if(stars) {
+ ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+
+ Iterator<Cluster<MeanModel<? extends NumberVector<?, ?>>>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; ci.hasNext(); cnum++) {
+ ci.next();
+ if(!svgp.getCSSClassManager().contains(CSS_MEAN_STAR + "_" + cnum)) {
+ CSSClass center = new CSSClass(this, CSS_MEAN_STAR + "_" + cnum);
+ center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(cnum));
+ center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
+ svgp.addCSSClassOrLogError(center);
+ }
+ }
+ }
}
/**
@@ -138,33 +185,50 @@ public class ClusterMeanVisualization<NV extends NumberVector<NV, ?>> extends P2
*
* @apiviz.stereotype factory
* @apiviz.uses ClusterMeanVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
- * Constructor
+ * Option ID for visualization of cluster means.
+ *
+ * <pre>
+ * -cluster.stars
+ * </pre>
+ */
+ public static final OptionID STARS_ID = OptionID.getOrCreateOptionID("cluster.stars", "Visualize mean-based clusters using stars.");
+
+ /**
+ * Draw stars
+ */
+ private boolean stars;
+
+ /**
+ * Constructor.
+ *
+ * @param stars Draw stars
*/
- public Factory() {
+ public Factory(boolean stars) {
super();
+ this.stars = stars;
}
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterMeanVisualization<NV>(task);
+ return new ClusterMeanVisualization(task, stars);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
// Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
+ Iterator<Clustering<?>> clusterings = ResultUtil.filteredResults(result, Clustering.class);
+ while(clusterings.hasNext()) {
+ Clustering<?> c = clusterings.next();
if(c.getAllClusters().size() > 0) {
// Does the cluster have a model with cluster means?
- Clustering<MeanModel<NV>> mcls = findMeanModel(c);
+ Clustering<MeanModel<? extends NumberVector<?, ?>>> mcls = findMeanModel(c);
if(mcls != null) {
Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ while(ps.hasNext()) {
+ ScatterPlotProjector<?> p = ps.next();
final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
baseResult.getHierarchy().add(c, task);
@@ -178,16 +242,40 @@ public class ClusterMeanVisualization<NV extends NumberVector<NV, ?>> extends P2
/**
* Test if the given clustering has a mean model.
*
- * @param <NV> Vector type
* @param c Clustering to inspect
* @return the clustering cast to return a mean model, null otherwise.
*/
@SuppressWarnings("unchecked")
- private static <NV extends NumberVector<NV, ?>> Clustering<MeanModel<NV>> findMeanModel(Clustering<?> c) {
+ private static Clustering<MeanModel<? extends NumberVector<?, ?>>> findMeanModel(Clustering<?> c) {
if(c.getAllClusters().get(0).getModel() instanceof MeanModel<?>) {
- return (Clustering<MeanModel<NV>>) c;
+ return (Clustering<MeanModel<? extends NumberVector<?, ?>>>) c;
}
return null;
}
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ protected boolean stars = false;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag starsF = new Flag(STARS_ID);
+ if(config.grab(starsF)) {
+ stars = starsF.getValue();
+ }
+ }
+
+ @Override
+ protected Factory makeInstance() {
+ return new Factory(stars);
+ }
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterOrderVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java
index f758ff99..1ac55851 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterOrderVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,12 +24,9 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.Collection;
-import java.util.Iterator;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
@@ -38,7 +35,7 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderEntry;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderResult;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
@@ -46,6 +43,7 @@ import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
* Cluster order visualizer.
@@ -55,7 +53,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @apiviz.has ClusterOrderResult oneway - - visualizes
*/
// TODO: listen for CLUSTER ORDER changes.
-public class ClusterOrderVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements DataStoreListener {
+public class ClusterOrderVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
/**
* A short name characterizing this Visualizer.
*/
@@ -99,6 +97,10 @@ public class ClusterOrderVisualization<NV extends NumberVector<NV, ?>> extends P
}
double[] thisVec = proj.fastProjectDataToRenderSpace(rel.get(thisId));
double[] prevVec = proj.fastProjectDataToRenderSpace(rel.get(prevId));
+
+ // FIXME: DO NOT COMMIT
+ thisVec[0] = thisVec[0] * 0.95 + prevVec[0] * 0.05;
+ thisVec[1] = thisVec[1] * 0.95 + prevVec[1] * 0.05;
Element arrow = svgp.svgLine(prevVec[0], prevVec[1], thisVec[0], thisVec[1]);
SVGUtil.setCSSClass(arrow, cls.getName());
@@ -107,11 +109,6 @@ public class ClusterOrderVisualization<NV extends NumberVector<NV, ?>> extends P
}
}
- @Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
- }
-
/**
* Visualize an OPTICS cluster order by drawing connection lines.
*
@@ -119,10 +116,8 @@ public class ClusterOrderVisualization<NV extends NumberVector<NV, ?>> extends P
*
* @apiviz.stereotype factory
* @apiviz.uses ClusterOrderVisualization oneway - - «create»
- *
- * @param <NV> object type
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Constructor, adhering to
* {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
@@ -133,15 +128,15 @@ public class ClusterOrderVisualization<NV extends NumberVector<NV, ?>> extends P
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterOrderVisualization<NV>(task);
+ return new ClusterOrderVisualization(task);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
Collection<ClusterOrderResult<DoubleDistance>> cos = ResultUtil.filterResults(result, ClusterOrderResult.class);
for(ClusterOrderResult<DoubleDistance> co : cos) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, co, p.getRelation(), this);
task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/EMClusterVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java
index f62ff1a6..6b2f43a3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/EMClusterVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -39,8 +39,8 @@ import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.data.spatial.Polygon;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
-import de.lmu.ifi.dbs.elki.math.ConvexHull2D;
import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.math.geometry.GrahamScanConvexHull2D;
import de.lmu.ifi.dbs.elki.math.linearalgebra.EigenPair;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.SortedEigenPairs;
@@ -50,7 +50,7 @@ import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.EmptyParameterization;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
@@ -62,6 +62,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
* Visualizer for generating SVG-Elements containing ellipses for first, second
@@ -76,7 +77,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
*/
// TODO: nicer stacking of n-fold hulls
// TODO: can we find a proper sphere for 3+ dimensions?
-public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> {
+public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends AbstractScatterplotVisualization {
/**
* A short name characterizing this Visualizer.
*/
@@ -115,7 +116,6 @@ public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends P2DV
public EMClusterVisualization(VisualizationTask task) {
super(task);
this.clustering = task.getResult();
- context.addContextChangeListener(this);
incrementalRedraw();
}
@@ -144,7 +144,7 @@ public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends P2DV
for(int i = 0; i < eps.size(); i++) {
EigenPair ep = eps.getEigenPair(i);
Vector sev = ep.getEigenvector().times(Math.sqrt(ep.getEigenvalue()));
- pc[i] = new Vector(proj.fastProjectRelativeDataToRenderSpace(sev));
+ pc[i] = new Vector(proj.fastProjectRelativeDataToRenderSpace(sev.getArrayRef()));
}
if(drawStyle != 0 || eps.size() == 2) {
drawSphere2D(cnum, cent, pc);
@@ -193,27 +193,29 @@ public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends P2DV
}
protected void drawHullLines(int cnum, Vector cent, Polygon chres) {
- for(int i = 1; i <= times; i++) {
- SVGPath path = new SVGPath();
- for(int p = 0; p < chres.size(); p++) {
- Vector cur = cent.plusTimes(chres.get(p), i);
- path.drawTo(cur);
- }
- path.close();
- Element ellipse = path.makeElement(svgp);
- SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
- if(opacStyle == 1) {
- CSSClass cls = new CSSClass(null, "temp");
- double s = (i >= 1 && i <= sigma.length) ? sigma[i - 1] : 0.0;
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, s);
- SVGUtil.setAtt(ellipse, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
+ if(chres.size() > 1) {
+ for(int i = 1; i <= times; i++) {
+ SVGPath path = new SVGPath();
+ for(int p = 0; p < chres.size(); p++) {
+ Vector cur = cent.plusTimes(chres.get(p), i);
+ path.drawTo(cur);
+ }
+ path.close();
+ Element ellipse = path.makeElement(svgp);
+ SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
+ if(opacStyle == 1) {
+ CSSClass cls = new CSSClass(null, "temp");
+ double s = (i >= 1 && i <= sigma.length) ? sigma[i - 1] : 0.0;
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, s);
+ SVGUtil.setAtt(ellipse, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
+ }
+ layer.appendChild(ellipse);
}
- layer.appendChild(ellipse);
}
}
protected Polygon makeHull(Vector[] pc) {
- ConvexHull2D hull = new ConvexHull2D();
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
Vector diag = new Vector(0, 0);
for(int j = 0; j < pc.length; j++) {
@@ -239,7 +241,7 @@ public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends P2DV
}
protected Polygon makeHullComplex(Vector[] pc) {
- ConvexHull2D hull = new ConvexHull2D();
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
Vector diag = new Vector(0, 0);
for(int j = 0; j < pc.length; j++) {
@@ -440,8 +442,8 @@ public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends P2DV
// Does the cluster have a model with cluster means?
Clustering<MeanModel<NV>> mcls = findMeanModel(c);
if(mcls != null) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 3);
baseResult.getHierarchy().add(c, task);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java
new file mode 100644
index 00000000..37f125d4
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java
@@ -0,0 +1,298 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster;
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.Cluster;
+import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.model.EMModel;
+import de.lmu.ifi.dbs.elki.data.model.MeanModel;
+import de.lmu.ifi.dbs.elki.data.model.Model;
+import de.lmu.ifi.dbs.elki.math.geometry.SweepHullDelaunay2D;
+import de.lmu.ifi.dbs.elki.math.geometry.SweepHullDelaunay2D.Triangle;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.EnumParameter;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.svg.VoronoiDraw;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
+
+/**
+ * Visualizer drawing Voronoi cells for k-means clusterings.
+ *
+ * See also: {@link de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansLloyd KMeans
+ * clustering}
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.has MeanModel oneway - - visualizes
+ */
+public class VoronoiVisualization extends AbstractScatterplotVisualization {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "k-means Voronoi cells";
+
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ */
+ private static final String KMEANSBORDER = "kmeans-border";
+
+ /**
+ * Visualization mode.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static enum Mode {
+ VORONOI, DELAUNAY, V_AND_D
+ }
+
+ /**
+ * The result we work on
+ */
+ Clustering<MeanModel<? extends NumberVector<?, ?>>> clustering;
+
+ /**
+ * The Voronoi diagram
+ */
+ Element voronoi;
+
+ /**
+ * Active drawing mode.
+ */
+ private Mode mode;
+
+ /**
+ * Constructor
+ *
+ * @param task VisualizationTask
+ * @param mode Drawing mode
+ */
+ public VoronoiVisualization(VisualizationTask task, Mode mode) {
+ super(task);
+ this.clustering = task.getResult();
+ this.mode = mode;
+ incrementalRedraw();
+ }
+
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+ final List<Cluster<MeanModel<? extends NumberVector<?, ?>>>> clusters = clustering.getAllClusters();
+
+ if(clusters.size() < 2) {
+ return;
+ }
+
+ // Collect cluster means
+ if(clusters.size() == 2) {
+ ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
+ {
+ for(Cluster<MeanModel<? extends NumberVector<?, ?>>> clus : clusters) {
+ means.add(clus.getModel().getMean().getColumnVector().getArrayRef());
+ }
+ }
+ if(mode == Mode.VORONOI || mode == Mode.V_AND_D) {
+ Element path = VoronoiDraw.drawFakeVoronoi(proj, means).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ if(mode == Mode.DELAUNAY || mode == Mode.V_AND_D) {
+ Element path = new SVGPath(proj.fastProjectDataToRenderSpace(means.get(0))).drawTo(proj.fastProjectDataToRenderSpace(means.get(1))).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ }
+ else {
+ ArrayList<Vector> vmeans = new ArrayList<Vector>(clusters.size());
+ ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
+ {
+ for(Cluster<MeanModel<? extends NumberVector<?, ?>>> clus : clusters) {
+ Vector v = clus.getModel().getMean().getColumnVector();
+ vmeans.add(v);
+ means.add(v.getArrayRef());
+ }
+ }
+ // Compute Delaunay Triangulation
+ ArrayList<Triangle> delaunay = new SweepHullDelaunay2D(vmeans).getDelaunay();
+ if(mode == Mode.VORONOI || mode == Mode.V_AND_D) {
+ Element path = VoronoiDraw.drawVoronoi(proj, delaunay, means).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ if(mode == Mode.DELAUNAY || mode == Mode.V_AND_D) {
+ Element path = VoronoiDraw.drawDelaunay(proj, delaunay, means).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the distance markers
+ if(!svgp.getCSSClassManager().contains(KMEANSBORDER)) {
+ CSSClass cls = new CSSClass(this, KMEANSBORDER);
+ cls = new CSSClass(this, KMEANSBORDER);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) * .5);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+
+ /**
+ * Factory for visualizers to generate an SVG-Element containing the lines
+ * between kMeans clusters
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses VoronoiVisualisation oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * Mode for drawing: Voronoi, Delaunay, both
+ *
+ * <p>
+ * Key: {@code -voronoi.mode}
+ * </p>
+ */
+ public static final OptionID MODE_ID = OptionID.getOrCreateOptionID("voronoi.mode", "Mode for drawing the voronoi cells (and/or delaunay triangulation)");
+
+ /**
+ * Drawing mode
+ */
+ private Mode mode;
+
+ /**
+ * Constructor
+ *
+ * @param mode Drawing mode
+ */
+ public Factory(Mode mode) {
+ super();
+ this.mode = mode;
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new VoronoiVisualization(task, mode);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ if(c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ Clustering<MeanModel<? extends NumberVector<?, ?>>> mcls = findMeanModel(c);
+ if(mcls != null) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ if(DatabaseUtil.dimensionality(p.getRelation()) == 2) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 3);
+ baseResult.getHierarchy().add(p, task);
+ baseResult.getHierarchy().add(c, task);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Test if the given clustering has a mean model.
+ *
+ * @param c Clustering to inspect
+ * @return the clustering cast to return a mean model, null otherwise.
+ */
+ @SuppressWarnings("unchecked")
+ private static Clustering<MeanModel<? extends NumberVector<?, ?>>> findMeanModel(Clustering<?> c) {
+ final Model firstModel = c.getAllClusters().get(0).getModel();
+ if(firstModel instanceof MeanModel<?> && !(firstModel instanceof EMModel<?>)) {
+ return (Clustering<MeanModel<? extends NumberVector<?, ?>>>) c;
+ }
+ return null;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ protected Mode mode;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ EnumParameter<Mode> modeP = new EnumParameter<Mode>(MODE_ID, Mode.class, Mode.VORONOI);
+ if(config.grab(modeP)) {
+ mode = modeP.getValue();
+ }
+ }
+
+ @Override
+ protected Factory makeInstance() {
+ return new Factory(mode);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/package-info.java
new file mode 100644
index 00000000..0f232498
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <p>Visualizers for clustering results based on 2D projections.</p>
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.cluster; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java
new file mode 100644
index 00000000..28e4da32
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java
@@ -0,0 +1,236 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.density;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import java.awt.image.BufferedImage;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.math.MathUtil;
+import de.lmu.ifi.dbs.elki.math.MeanVariance;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.KMLOutputHandler;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.batikutil.ThumbnailRegistryEntry;
+import de.lmu.ifi.dbs.elki.visualization.projections.CanvasSize;
+import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
+
+/**
+ * A simple density estimation visualization, based on a simple kernel-density
+ * <em>in the projection, not the actual data!</em>
+ *
+ * @author Erich Schubert
+ */
+// TODO: make parameterizable, in particular color map, kernel bandwidth and
+// kernel function
+public class DensityEstimationOverlay extends AbstractScatterplotVisualization {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Density estimation overlay";
+
+ /**
+ * Density map resolution
+ */
+ private int resolution = 500;
+
+ /**
+ * The actual image
+ */
+ private BufferedImage img = null;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public DensityEstimationOverlay(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ }
+
+ @Override
+ protected void redraw() {
+ if(img == null) {
+ renderImage();
+ }
+
+ CanvasSize canvas = proj.estimateViewport();
+ String imguri = ThumbnailRegistryEntry.INTERNAL_PREFIX + ThumbnailRegistryEntry.registerImage(img);
+ Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, canvas.minx);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, canvas.miny);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, canvas.maxx - canvas.minx);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, canvas.maxy - canvas.miny);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_OPACITY_PROPERTY + ": .5");
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, imguri);
+
+ layer.appendChild(itag);
+ }
+
+ @Reference(authors = "D. W. Scott", title = "Multivariate density estimation", booktitle = "Multivariate Density Estimation: Theory, Practice, and Visualization", url = "http://dx.doi.org/10.1002/9780470316849.fmatter")
+ private double[] initializeBandwidth(double[][] data) {
+ MeanVariance mv0 = new MeanVariance();
+ MeanVariance mv1 = new MeanVariance();
+ // For Kernel bandwidth.
+ for(double[] projected : data) {
+ mv0.put(projected[0]);
+ mv1.put(projected[1]);
+ }
+ // Set bandwidths according to Scott's rule:
+ // Note: in projected space, d=2.
+ double[] bandwidth = new double[2];
+ bandwidth[0] = MathUtil.SQRT5 * mv0.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
+ bandwidth[1] = MathUtil.SQRT5 * mv1.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
+ return bandwidth;
+ }
+
+ private void renderImage() {
+ // TODO: SAMPLE? Do region queries?
+ // Project the data just once, keep a copy.
+ double[][] data = new double[rel.size()][];
+ {
+ int i = 0;
+ for(DBID id : rel.iterDBIDs()) {
+ data[i] = proj.fastProjectDataToRenderSpace(rel.get(id));
+ i++;
+ }
+ }
+ double[] bandwidth = initializeBandwidth(data);
+ // Compare by first component
+ Comparator<double[]> comp0 = new Comparator<double[]>() {
+ @Override
+ public int compare(double[] o1, double[] o2) {
+ return Double.compare(o1[0], o2[0]);
+ }
+ };
+ // Compare by second component
+ Comparator<double[]> comp1 = new Comparator<double[]>() {
+ @Override
+ public int compare(double[] o1, double[] o2) {
+ return Double.compare(o1[1], o2[1]);
+ }
+ };
+ // TODO: choose comparator order based on smaller bandwidth?
+ Arrays.sort(data, comp0);
+
+ CanvasSize canvas = proj.estimateViewport();
+ double min0 = canvas.minx, max0 = canvas.maxx, ste0 = (max0 - min0) / resolution;
+ double min1 = canvas.miny, max1 = canvas.maxy, ste1 = (max1 - min1) / resolution;
+
+ double kernf = 9. / (16 * bandwidth[0] * bandwidth[1]);
+ double maxdens = 0.0;
+ double[][] dens = new double[resolution][resolution];
+ {
+ // TODO: incrementally update the loff/roff values?
+ for(int x = 0; x < resolution; x++) {
+ double xlow = min0 + ste0 * x, xhig = xlow + ste0;
+ int loff = unflip(Arrays.binarySearch(data, new double[] { xlow - bandwidth[0] }, comp0));
+ int roff = unflip(Arrays.binarySearch(data, new double[] { xhig + bandwidth[0] }, comp0));
+ // Resort by second component
+ Arrays.sort(data, loff, roff, comp1);
+ for(int y = 0; y < resolution; y++) {
+ double ylow = min1 + ste1 * y, yhig = ylow + ste1;
+ int boff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, ylow - bandwidth[1] }, comp1));
+ int toff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, yhig + bandwidth[1] }, comp1));
+ for(int pos = boff; pos < toff; pos++) {
+ double[] val = data[pos];
+ double d0 = (val[0] < xlow) ? (xlow - val[0]) : (val[0] > xhig) ? (val[0] - xhig) : 0;
+ double d1 = (val[1] < ylow) ? (ylow - val[1]) : (val[1] > yhig) ? (val[1] - yhig) : 0;
+ d0 = d0 / bandwidth[0];
+ d1 = d1 / bandwidth[1];
+ dens[x][y] += kernf * (1 - d0 * d0) * (1 - d1 * d1);
+ }
+ maxdens = Math.max(maxdens, dens[x][y]);
+ }
+ // Restore original sorting, as the intervals overlap
+ Arrays.sort(data, loff, roff, comp0);
+ }
+ }
+ img = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_ARGB);
+ {
+ for(int x = 0; x < resolution; x++) {
+ for(int y = 0; y < resolution; y++) {
+ int rgb = KMLOutputHandler.getColorForValue(dens[x][y] / maxdens).getRGB();
+ img.setRGB(x, y, rgb);
+ }
+ }
+ }
+ }
+
+ private int unflip(int binarySearch) {
+ if(binarySearch < 0) {
+ return (-binarySearch) - 1;
+ }
+ else {
+ return binarySearch;
+ }
+ }
+
+ /**
+ * The visualization factory
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses DensityEstimation2DVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ /**
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ */
+ public Factory() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new DensityEstimationOverlay(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(result, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/package-info.java
index 6c10d496..b9771aed 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/events/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/package-info.java
@@ -1,11 +1,11 @@
/**
- * <p>Events occuring in visualization contexts</p>
+ * <p>Visualizers for data set density in a scatterplot projection.</p>
*/
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-Copyright (C) 2011
+Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -23,4 +23,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.visualization.visualizers.events; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.density; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TreeMBRVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java
index cc3dfbe8..0b4bee2f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TreeMBRVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,25 +24,21 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialUtil;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.index.tree.spatial.SpatialEntry;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.AbstractRStarTree;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.AbstractRStarTreeNode;
import de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.rstar.RStarTreeNode;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -58,6 +54,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
* Visualize the bounding rectangles of an R-Tree based index.
@@ -67,12 +64,11 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @apiviz.has AbstractRStarTree oneway - - visualizes
* @apiviz.uses SVGHyperCube
*
- * @param <NV> Type of the DatabaseObject being visualized.
* @param <N> Tree node type
* @param <E> Tree entry type
*/
// TODO: listen for tree changes instead of data changes?
-public class TreeMBRVisualization<NV extends NumberVector<NV, ?>, N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends P2DVisualization<NV> implements DataStoreListener {
+public class TreeMBRVisualization<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractScatterplotVisualization implements DataStoreListener {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -152,11 +148,11 @@ public class TreeMBRVisualization<NV extends NumberVector<NV, ?>, N extends Abst
SpatialComparable mbr = entry;
if(fill) {
- Element r = SVGHyperCube.drawFilled(svgp, INDEX + depth, proj, new Vector(SpatialUtil.getMin(mbr)), new Vector(SpatialUtil.getMax(mbr)));
+ Element r = SVGHyperCube.drawFilled(svgp, INDEX + depth, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
layer.appendChild(r);
}
else {
- Element r = SVGHyperCube.drawFrame(svgp, proj, new Vector(SpatialUtil.getMin(mbr)), new Vector(SpatialUtil.getMax(mbr)));
+ Element r = SVGHyperCube.drawFrame(svgp, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
SVGUtil.setCSSClass(r, INDEX + depth);
layer.appendChild(r);
}
@@ -178,11 +174,6 @@ public class TreeMBRVisualization<NV extends NumberVector<NV, ?>, N extends Abst
context.removeDataStoreListener(this);
}
- @Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
- }
-
/**
* Factory
*
@@ -190,10 +181,8 @@ public class TreeMBRVisualization<NV extends NumberVector<NV, ?>, N extends Abst
*
* @apiviz.stereotype factory
* @apiviz.uses TreeMBRVisualization oneway - - «create»
- *
- * @param <NV> vector type
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Flag for half-transparent filling of bubbles.
*
@@ -220,7 +209,7 @@ public class TreeMBRVisualization<NV extends NumberVector<NV, ?>, N extends Abst
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new TreeMBRVisualization<NV, RStarTreeNode, SpatialEntry>(task, fill);
+ return new TreeMBRVisualization<RStarTreeNode, SpatialEntry>(task, fill);
}
@Override
@@ -228,8 +217,8 @@ public class TreeMBRVisualization<NV extends NumberVector<NV, ?>, N extends Abst
ArrayList<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
if(tree instanceof Result) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND + 1);
baseResult.getHierarchy().add((Result) tree, task);
@@ -246,7 +235,7 @@ public class TreeMBRVisualization<NV extends NumberVector<NV, ?>, N extends Abst
*
* @apiviz.exclude
*/
- public static class Parameterizer<NV extends NumberVector<NV, ?>> extends AbstractParameterizer {
+ public static class Parameterizer extends AbstractParameterizer {
protected boolean fill = false;
@Override
@@ -259,8 +248,8 @@ public class TreeMBRVisualization<NV extends NumberVector<NV, ?>, N extends Abst
}
@Override
- protected Factory<NV> makeInstance() {
- return new Factory<NV>(fill);
+ protected Factory makeInstance() {
+ return new Factory(fill);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TreeSphereVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java
index 73a02ddb..85429eb1 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/TreeSphereVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,13 +24,11 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
@@ -46,7 +44,7 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree.MTreeNode;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
@@ -61,6 +59,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
* Visualize the bounding sphere of a metric index.
@@ -70,12 +69,11 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @apiviz.has AbstractMTree oneway - - visualizes
* @apiviz.uses SVGHyperSphere
*
- * @param <NV> Type of the DatabaseObject being visualized.
* @param <N> Tree node type
* @param <E> Tree entry type
*/
// TODO: listen for tree changes!
-public class TreeSphereVisualization<NV extends NumberVector<NV, ?>, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<NV, D, N, E>, E extends MTreeEntry<D>> extends P2DVisualization<NV> implements DataStoreListener {
+public class TreeSphereVisualization<D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<?, D, N, E>, E extends MTreeEntry<D>> extends AbstractScatterplotVisualization implements DataStoreListener {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -105,7 +103,7 @@ public class TreeSphereVisualization<NV extends NumberVector<NV, ?>, D extends N
/**
* The tree we visualize
*/
- protected AbstractMTree<NV, D, N, E> tree;
+ protected AbstractMTree<?, D, N, E> tree;
/**
* Fill parameter.
@@ -208,10 +206,10 @@ public class TreeSphereVisualization<NV extends NumberVector<NV, ?>, D extends N
* @param entry Current entry
* @param depth Current depth
*/
- private void visualizeMTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractMTree<NV, D, N, E> mtree, E entry, int depth) {
+ private void visualizeMTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractMTree<?, D, N, E> mtree, E entry, int depth) {
DBID roid = entry.getRoutingObjectID();
if(roid != null) {
- NV ro = rel.get(roid);
+ NumberVector<?, ?> ro = rel.get(roid);
D rad = entry.getCoveringRadius();
final Element r;
@@ -247,11 +245,6 @@ public class TreeSphereVisualization<NV extends NumberVector<NV, ?>, D extends N
context.removeDataStoreListener(this);
}
- @Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
- }
-
/**
* Factory
*
@@ -259,10 +252,8 @@ public class TreeSphereVisualization<NV extends NumberVector<NV, ?>, D extends N
*
* @apiviz.stereotype factory
* @apiviz.uses TreeSphereVisualization oneway - - «create»
- *
- * @param <NV>
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Fill parameter.
*/
@@ -280,10 +271,10 @@ public class TreeSphereVisualization<NV extends NumberVector<NV, ?>, D extends N
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
- ArrayList<AbstractMTree<NV, DoubleDistance, ?, ?>> trees = ResultUtil.filterResults(result, AbstractMTree.class);
- for(AbstractMTree<NV, DoubleDistance, ?, ?> tree : trees) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ ArrayList<AbstractMTree<?, DoubleDistance, ?, ?>> trees = ResultUtil.filterResults(result, AbstractMTree.class);
+ for(AbstractMTree<?, DoubleDistance, ?, ?> tree : trees) {
if(canVisualize(tree) && tree instanceof Result) {
final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND + 1);
@@ -296,7 +287,7 @@ public class TreeSphereVisualization<NV extends NumberVector<NV, ?>, D extends N
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new TreeSphereVisualization<NV, DoubleDistance, MTreeNode<NV, DoubleDistance>, MTreeEntry<DoubleDistance>>(task, fill);
+ return new TreeSphereVisualization<DoubleDistance, MTreeNode<Object, DoubleDistance>, MTreeEntry<DoubleDistance>>(task, fill);
}
/**
@@ -306,7 +297,7 @@ public class TreeSphereVisualization<NV extends NumberVector<NV, ?>, D extends N
*
* @apiviz.exclude
*/
- public static class Parameterizer<NV extends NumberVector<NV, ?>> extends AbstractParameterizer {
+ public static class Parameterizer extends AbstractParameterizer {
protected boolean fill = false;
@Override
@@ -319,8 +310,8 @@ public class TreeSphereVisualization<NV extends NumberVector<NV, ?>, D extends N
}
@Override
- protected Factory<NV> makeInstance() {
- return new Factory<NV>(fill);
+ protected Factory makeInstance() {
+ return new Factory(fill);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/package-info.java
new file mode 100644
index 00000000..a4b3c0c9
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <p>Visualizers for index structures based on 2D projections.</p>
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/BubbleVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java
index 3d183a88..7c40c1cb 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/BubbleVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.outlier;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -23,17 +23,12 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.util.Iterator;
import java.util.List;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.Cluster;
-import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.data.model.Model;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
@@ -41,7 +36,7 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -53,11 +48,14 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.ClassStylingPolicy;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.style.StylingPolicy;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
* Generates a SVG-Element containing bubbles. A Bubble is a circle visualizing
@@ -68,11 +66,9 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.has OutlierResult oneway - - visualizes
- *
- * @param <NV> Type of the DatabaseObject being visualized.
*/
-@Reference(authors = "E. Achtert, H.-P. Kriegel, L. Reichert, E. Schubert, R. Wojdanowski, A. Zimek", title = "Visual Evaluation of Outlier Detection Models", booktitle = "Proceedings of the 15th International Conference on Database Systems for Advanced Applications (DASFAA), Tsukuba, Japan, 2010", url = "http://dx.doi.org/10.1007%2F978-3-642-12098-5_34")
-public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements DataStoreListener {
+@Reference(authors = "E. Achtert, H.-P. Kriegel, L. Reichert, E. Schubert, R. Wojdanowski, A. Zimek", title = "Visual Evaluation of Outlier Detection Models", booktitle = "Proceedings of the 15th International Conference on Database Systems for Advanced Applications (DASFAA), Tsukuba, Japan, 2010", url = "http://dx.doi.org/10.1007/978-3-642-12098-5_34")
+public class BubbleVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -120,22 +116,46 @@ public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisu
@Override
public void redraw() {
- Clustering<Model> clustering = context.getOrCreateDefaultClustering();
- setupCSS(svgp, clustering);
+ StylingPolicy stylepolicy = context.getStyleResult().getStylingPolicy();
// bubble size
- double bubble_size = context.getStyleLibrary().getSize(StyleLibrary.BUBBLEPLOT);
- // draw data
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<?> clus = ci.next();
- for(DBID objId : clus.getIDs()) {
+ final double bubble_size = context.getStyleLibrary().getSize(StyleLibrary.BUBBLEPLOT);
+ if(stylepolicy instanceof ClassStylingPolicy) {
+ ClassStylingPolicy colors = (ClassStylingPolicy) stylepolicy;
+ setupCSS(svgp, colors);
+ // draw data
+ for(DBID objId : sample.getSample()) {
+ final Double radius = getScaledForId(objId);
+ if(radius > 0.01 && !Double.isInfinite(radius)) {
+ final NumberVector<?, ?> vec = rel.get(objId);
+ if(vec != null) {
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
+ SVGUtil.addCSSClass(circle, BUBBLE + colors.getStyleForDBID(objId));
+ layer.appendChild(circle);
+ }
+ }
+ }
+ }
+ else {
+ // draw data
+ for(DBID objId : sample.getSample()) {
final Double radius = getScaledForId(objId);
- if(radius > 0.01) {
- final NV vec = rel.get(objId);
+ if(radius > 0.01 && !Double.isInfinite(radius)) {
+ final NumberVector<?, ?> vec = rel.get(objId);
if(vec != null) {
double[] v = proj.fastProjectDataToRenderSpace(vec);
Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
- SVGUtil.addCSSClass(circle, BUBBLE + cnum);
+ int color = stylepolicy.getColorForDBID(objId);
+ final StringBuffer style = new StringBuffer();
+ if(fill) {
+ style.append(SVGConstants.CSS_FILL_PROPERTY + ":").append(SVGUtil.colorToString(color));
+ style.append(SVGConstants.CSS_FILL_OPACITY_PROPERTY + ":0.5");
+ }
+ else {
+ style.append(SVGConstants.CSS_STROKE_VALUE + ":").append(SVGUtil.colorToString(color));
+ style.append(SVGConstants.CSS_FILL_PROPERTY + ":" + SVGConstants.CSS_NONE_VALUE);
+ }
+ SVGUtil.setAtt(circle, SVGConstants.SVG_STYLE_ATTRIBUTE, style.toString());
layer.appendChild(circle);
}
}
@@ -144,35 +164,27 @@ public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisu
}
@Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
+ public void resultChanged(Result current) {
+ if(sample == current) {
+ synchronizedRedraw();
+ }
}
/**
* Registers the Bubble-CSS-Class at a SVGPlot.
*
* @param svgp the SVGPlot to register the Tooltip-CSS-Class.
- * @param clustering Clustering to use
+ * @param policy Clustering to use
*/
- private void setupCSS(SVGPlot svgp, Clustering<? extends Model> clustering) {
+ private void setupCSS(SVGPlot svgp, ClassStylingPolicy policy) {
ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
// creating IDs manually because cluster often return a null-ID.
- int clusterID = 0;
-
- for(@SuppressWarnings("unused")
- Cluster<?> cluster : clustering.getAllClusters()) {
+ for(int clusterID = policy.getMinStyle(); clusterID < policy.getMaxStyle(); clusterID++) {
CSSClass bubble = new CSSClass(svgp, BUBBLE + clusterID);
bubble.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- String color;
-
- if(clustering.getAllClusters().size() == 1) {
- color = "black";
- }
- else {
- color = colors.getColor(clusterID);
- }
+ String color = colors.getColor(clusterID);
if(fill) {
bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
@@ -185,7 +197,6 @@ public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisu
}
svgp.addCSSClassOrLogError(bubble);
- clusterID += 1;
}
}
@@ -196,9 +207,9 @@ public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisu
* @return a Double representing a outlierness-score, after it has modified by
* the given scales.
*/
- protected Double getScaledForId(DBID id) {
- Double d = result.getScores().get(id).doubleValue();
- if(d == null) {
+ protected double getScaledForId(DBID id) {
+ double d = result.getScores().get(id).doubleValue();
+ if(Double.isNaN(d) || Double.isInfinite(d)) {
return 0.0;
}
if(scaling == null) {
@@ -216,10 +227,8 @@ public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisu
*
* @apiviz.stereotype factory
* @apiviz.uses BubbleVisualization oneway - - «create»
- *
- * @param <NV> Type of the DatabaseObject being visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Flag for half-transparent filling of bubbles.
*
@@ -266,14 +275,14 @@ public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisu
final OutlierResult outlierResult = task.getResult();
((OutlierScalingFunction) this.scaling).prepare(outlierResult);
}
- return new BubbleVisualization<NV>(task, scaling);
+ return new BubbleVisualization(task, scaling);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
List<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
for(OutlierResult o : ors) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
boolean vis = true;
// Quick and dirty hack: hide if parent result is also an outlier result
// Since that probably is already visible and we're redundant.
@@ -283,7 +292,7 @@ public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisu
break;
}
}
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, o, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
if(!vis) {
@@ -302,7 +311,7 @@ public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisu
*
* @apiviz.exclude
*/
- public static class Parameterizer<NV extends NumberVector<NV, ?>> extends AbstractParameterizer {
+ public static class Parameterizer extends AbstractParameterizer {
/**
* Fill parameter.
*/
@@ -328,8 +337,8 @@ public class BubbleVisualization<NV extends NumberVector<NV, ?>> extends P2DVisu
}
@Override
- protected Factory<NV> makeInstance() {
- return new Factory<NV>(fill, scaling);
+ protected Factory makeInstance() {
+ return new Factory(fill, scaling);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/package-info.java
new file mode 100644
index 00000000..7332fcb0
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <p>Visualizers for outlier scores based on 2D projections.</p>
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.outlier; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/package-info.java
index 6a0fa750..805379d7 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/package-info.java
@@ -1,9 +1,8 @@
/**
- * <p>Visualizers based on 2D projections.</p>
+ * <p>Visualizers based on scatterplots.</p>
*
* @apiviz.exclude de.lmu.ifi.dbs.elki.visualization.batikutil.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.visualization.svg.*
- * @apiviz.exclude de.lmu.ifi.dbs.elki.visualization.visualizers.events.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.result.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.index.*
* @apiviz.exclude de.lmu.ifi.dbs.elki.data.*
@@ -13,7 +12,7 @@
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-Copyright (C) 2011
+Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -31,4 +30,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/MoveObjectsToolVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java
index 6d798247..fb9de7d5 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/MoveObjectsToolVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection;
/*
This file is part of ELKI:
@@ -30,7 +30,6 @@ import org.w3c.dom.Element;
import org.w3c.dom.events.Event;
import org.w3c.dom.svg.SVGPoint;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.UpdatableDatabase;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
@@ -38,7 +37,7 @@ import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea;
import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea.DragListener;
@@ -49,7 +48,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
* Tool to move the currently selected objects.
@@ -58,10 +57,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
* @author Erich Schubert
*
* @apiviz.has NumberVector oneway - - edits
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
-public class MoveObjectsToolVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements DragListener {
+public class MoveObjectsToolVisualization extends AbstractScatterplotVisualization implements DragListener {
/**
* A short name characterizing this Visualizer.
*/
@@ -84,19 +81,14 @@ public class MoveObjectsToolVisualization<NV extends NumberVector<NV, ?>> extend
public MoveObjectsToolVisualization(VisualizationTask task) {
super(task);
- context.addContextChangeListener(this);
incrementalRedraw();
}
@Override
- public void destroy() {
- super.destroy();
- context.removeContextChangeListener(this);
- }
-
- @Override
- public void contextChanged(ContextChangedEvent e) {
- synchronizedRedraw();
+ public void resultChanged(Result current) {
+ if(sample == current) {
+ synchronizedRedraw();
+ }
}
@Override
@@ -200,10 +192,8 @@ public class MoveObjectsToolVisualization<NV extends NumberVector<NV, ?>> extend
*
* @apiviz.stereotype factory
* @apiviz.uses MoveObjectsToolVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Constructor
*/
@@ -213,7 +203,7 @@ public class MoveObjectsToolVisualization<NV extends NumberVector<NV, ?>> extend
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new MoveObjectsToolVisualization<NV>(task);
+ return new MoveObjectsToolVisualization(task);
}
@Override
@@ -222,13 +212,14 @@ public class MoveObjectsToolVisualization<NV extends NumberVector<NV, ?>> extend
if(!dbs.hasNext()) {
return;
}
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
task.put(VisualizationTask.META_TOOL, true);
task.put(VisualizationTask.META_NOTHUMB, true);
task.put(VisualizationTask.META_NOEXPORT, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
// baseResult.getHierarchy().add(p.getRelation(), task);
baseResult.getHierarchy().add(p, task);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionConvexHullVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java
index fde2c00d..5702800d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionConvexHullVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection;
/*
This file is part of ELKI:
@@ -24,18 +24,15 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.spatial.Polygon;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.math.ConvexHull2D;
+import de.lmu.ifi.dbs.elki.math.geometry.GrahamScanConvexHull2D;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.DBIDSelection;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
@@ -43,7 +40,7 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
@@ -53,7 +50,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangeListener;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
/**
@@ -65,10 +62,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* @apiviz.has SelectionResult oneway - - visualizes
* @apiviz.has DBIDSelection oneway - - visualizes
* @apiviz.uses ConvexHull2D
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
-public class SelectionConvexHullVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements ContextChangeListener, DataStoreListener {
+public class SelectionConvexHullVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
/**
* A short name characterizing this Visualizer.
*/
@@ -86,7 +81,6 @@ public class SelectionConvexHullVisualization<NV extends NumberVector<NV, ?>> ex
*/
public SelectionConvexHullVisualization(VisualizationTask task) {
super(task);
- context.addContextChangeListener(this);
context.addResultListener(this);
context.addDataStoreListener(this);
incrementalRedraw();
@@ -98,7 +92,7 @@ public class SelectionConvexHullVisualization<NV extends NumberVector<NV, ?>> ex
DBIDSelection selContext = context.getSelection();
if(selContext != null) {
DBIDs selection = selContext.getSelectedIds();
- ConvexHull2D hull = new ConvexHull2D();
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
for(DBID i : selection) {
try {
hull.add(new Vector(proj.fastProjectDataToRenderSpace(rel.get(i))));
@@ -139,11 +133,6 @@ public class SelectionConvexHullVisualization<NV extends NumberVector<NV, ?>> ex
}
}
- @Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
- }
-
/**
* Factory for visualizers to generate an SVG-Element containing the convex
* hull of the selected points
@@ -152,33 +141,27 @@ public class SelectionConvexHullVisualization<NV extends NumberVector<NV, ?>> ex
*
* @apiviz.stereotype factory
* @apiviz.uses SelectionConvexHullVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Constructor
*/
public Factory() {
super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionConvexHullVisualization<NV>(task);
- }
-
- @Override
- public Visualization makeVisualizationOrThumbnail(VisualizationTask task) {
- return new ThumbnailVisualization(this, task, ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION);
+ return new SelectionConvexHullVisualization(task);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
final ArrayList<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
for(SelectionResult selres : selectionResults) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 2);
baseResult.getHierarchy().add(selres, task);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionCubeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java
index 087ec6af..9fd24b43 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionCubeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,13 +24,10 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.DBIDSelection;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.RangeSelection;
@@ -38,7 +35,7 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -54,7 +51,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangeListener;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
/**
@@ -66,10 +63,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* @apiviz.has SelectionResult oneway - - visualizes
* @apiviz.has RangeSelection oneway - - visualizes
* @apiviz.uses SVGHyperCube
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
-public class SelectionCubeVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements ContextChangeListener {
+public class SelectionCubeVisualization extends AbstractScatterplotVisualization {
/**
* A short name characterizing this Visualizer.
*/
@@ -99,11 +94,16 @@ public class SelectionCubeVisualization<NV extends NumberVector<NV, ?>> extends
super(task);
this.nofill = nofill;
addCSSClasses(svgp);
- context.addContextChangeListener(this);
context.addResultListener(this);
incrementalRedraw();
}
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
/**
* Adds the required CSS-Classes
*
@@ -165,12 +165,12 @@ public class SelectionCubeVisualization<NV extends NumberVector<NV, ?>> extends
}
}
if(nofill) {
- Element r = SVGHyperCube.drawFrame(svgp, proj, new Vector(min), new Vector(max));
+ Element r = SVGHyperCube.drawFrame(svgp, proj, min, max);
SVGUtil.setCSSClass(r, CSS_CUBEFRAME);
layer.appendChild(r);
}
else {
- Element r = SVGHyperCube.drawFilled(svgp, CSS_CUBE, proj, new Vector(min), new Vector(max));
+ Element r = SVGHyperCube.drawFilled(svgp, CSS_CUBE, proj, min, max);
layer.appendChild(r);
}
@@ -193,10 +193,8 @@ public class SelectionCubeVisualization<NV extends NumberVector<NV, ?>> extends
*
* @apiviz.stereotype factory
* @apiviz.uses SelectionCubeVisualization oneway - - «create»
- *
- * @param <NV> vector type
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Flag for half-transparent filling of selection cubes.
*
@@ -219,19 +217,20 @@ public class SelectionCubeVisualization<NV extends NumberVector<NV, ?>> extends
public Factory(boolean nofill) {
super();
this.nofill = nofill;
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionCubeVisualization<NV>(task, nofill);
+ return new SelectionCubeVisualization(task, nofill);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
final ArrayList<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
for(SelectionResult selres : selectionResults) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 2);
baseResult.getHierarchy().add(selres, task);
@@ -240,11 +239,6 @@ public class SelectionCubeVisualization<NV extends NumberVector<NV, ?>> extends
}
}
- @Override
- public Visualization makeVisualizationOrThumbnail(VisualizationTask task) {
- return new ThumbnailVisualization(this, task, ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION);
- }
-
/**
* Parameterization class.
*
@@ -252,7 +246,7 @@ public class SelectionCubeVisualization<NV extends NumberVector<NV, ?>> extends
*
* @apiviz.exclude
*/
- public static class Parameterizer<NV extends NumberVector<NV, ?>> extends AbstractParameterizer {
+ public static class Parameterizer extends AbstractParameterizer {
protected boolean nofill;
@Override
@@ -265,8 +259,8 @@ public class SelectionCubeVisualization<NV extends NumberVector<NV, ?>> extends
}
@Override
- protected Factory<NV> makeInstance() {
- return new Factory<NV>(nofill);
+ protected Factory makeInstance() {
+ return new Factory(nofill);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionDotVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java
index 06167532..79d1afe5 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionDotVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java
@@ -1,10 +1,10 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,13 +24,10 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
@@ -40,7 +37,7 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
@@ -49,7 +46,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangeListener;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
/**
@@ -60,10 +57,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @apiviz.has SelectionResult oneway - - visualizes
* @apiviz.has DBIDSelection oneway - - visualizes
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
-public class SelectionDotVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements ContextChangeListener, DataStoreListener {
+public class SelectionDotVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
/**
* A short name characterizing this Visualizer.
*/
@@ -81,13 +76,18 @@ public class SelectionDotVisualization<NV extends NumberVector<NV, ?>> extends P
*/
public SelectionDotVisualization(VisualizationTask task) {
super(task);
- context.addContextChangeListener(this);
context.addResultListener(this);
context.addDataStoreListener(this);
incrementalRedraw();
}
@Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ @Override
protected void redraw() {
addCSSClasses(svgp);
final double size = context.getStyleLibrary().getSize(StyleLibrary.SELECTION);
@@ -124,11 +124,6 @@ public class SelectionDotVisualization<NV extends NumberVector<NV, ?>> extends P
}
}
- @Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
- }
-
/**
* Factory for visualizers to generate an SVG-Element containing dots as
* markers representing the selected Database's objects.
@@ -137,33 +132,27 @@ public class SelectionDotVisualization<NV extends NumberVector<NV, ?>> extends P
*
* @apiviz.stereotype factory
* @apiviz.uses SelectionDotVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Constructor
*/
public Factory() {
super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionDotVisualization<NV>(task);
- }
-
- @Override
- public Visualization makeVisualizationOrThumbnail(VisualizationTask task) {
- return new ThumbnailVisualization(this, task, ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION);
+ return new SelectionDotVisualization(task);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
final ArrayList<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
for(SelectionResult selres : selectionResults) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
baseResult.getHierarchy().add(selres, task);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionToolCubeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java
index ed0b456d..8e86f920 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionToolCubeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection;
/*
This file is part of ELKI:
@@ -25,7 +25,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
import java.util.ArrayList;
import java.util.BitSet;
-import java.util.Iterator;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
@@ -37,7 +36,6 @@ import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
import de.lmu.ifi.dbs.elki.logging.Logging;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.DBIDSelection;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.RangeSelection;
@@ -45,7 +43,7 @@ import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea;
@@ -57,7 +55,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
* Tool-Visualization for the tool to select ranges
@@ -66,10 +64,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
*
* @apiviz.has SelectionResult oneway - - updates
* @apiviz.has RangeSelection oneway - - updates
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
-public class SelectionToolCubeVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements DragableArea.DragListener {
+public class SelectionToolCubeVisualization extends AbstractScatterplotVisualization implements DragableArea.DragListener {
/**
* The logger for this class.
*/
@@ -108,22 +104,10 @@ public class SelectionToolCubeVisualization<NV extends NumberVector<NV, ?>> exte
public SelectionToolCubeVisualization(VisualizationTask task) {
super(task);
this.dim = DatabaseUtil.dimensionality(rel);
- context.addContextChangeListener(this);
incrementalRedraw();
}
@Override
- public void destroy() {
- super.destroy();
- context.removeContextChangeListener(this);
- }
-
- @Override
- public void contextChanged(ContextChangedEvent e) {
- synchronizedRedraw();
- }
-
- @Override
protected void redraw() {
addCSSClasses(svgp);
@@ -160,20 +144,18 @@ public class SelectionToolCubeVisualization<NV extends NumberVector<NV, ?>> exte
*/
private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
BitSet actDim = proj.getVisibleDimensions2D();
- Vector v1 = new Vector(dim);
- Vector v2 = new Vector(dim);
- v1.set(0, x1);
- v1.set(1, y1);
- v2.set(0, x2);
- v2.set(1, y2);
+ double[] v1 = new double[dim];
+ double[] v2 = new double[dim];
+ v1[0] = x1;
+ v1[1] = y1;
+ v2[0] = x2;
+ v2[1] = y2;
- NV factory = DatabaseUtil.assumeVectorField(rel).getFactory();
-
- NV nv1 = proj.projectRenderToDataSpace(v1, factory);
- NV nv2 = proj.projectRenderToDataSpace(v2, factory);
+ double[] nv1 = proj.fastProjectRenderToDataSpace(v1);
+ double[] nv2 = proj.fastProjectRenderToDataSpace(v2);
for(int d = actDim.nextSetBit(0); d >= 0; d = actDim.nextSetBit(d + 1)) {
- ranges[d] = new DoubleDoublePair(Math.min(nv1.doubleValue(d + 1), nv2.doubleValue(d + 1)), Math.max(nv1.doubleValue(d + 1), nv2.doubleValue(d + 1)));
+ ranges[d] = new DoubleDoublePair(Math.min(nv1[d], nv2[d]), Math.max(nv1[d], nv2[d]));
}
}
@@ -240,7 +222,7 @@ public class SelectionToolCubeVisualization<NV extends NumberVector<NV, ?>> exte
selection.clear();
boolean idIn = true;
for(DBID id : rel.iterDBIDs()) {
- NV dbTupel = rel.get(id);
+ NumberVector<?, ?> dbTupel = rel.get(id);
idIn = true;
for(int i = 0; i < dim; i++) {
if(ranges != null && ranges[i] != null) {
@@ -282,10 +264,8 @@ public class SelectionToolCubeVisualization<NV extends NumberVector<NV, ?>> exte
*
* @apiviz.stereotype factory
* @apiviz.uses SelectionToolCubeVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Constructor, adhering to
* {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
@@ -296,20 +276,21 @@ public class SelectionToolCubeVisualization<NV extends NumberVector<NV, ?>> exte
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolCubeVisualization<NV>(task);
+ return new SelectionToolCubeVisualization(task);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
final ArrayList<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
for(SelectionResult selres : selectionResults) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
task.put(VisualizationTask.META_TOOL, true);
task.put(VisualizationTask.META_NOTHUMB, true);
task.put(VisualizationTask.META_NOEXPORT, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
baseResult.getHierarchy().add(selres, task);
baseResult.getHierarchy().add(p, task);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionToolDotVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java
index 7593dac5..877833f7 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/SelectionToolDotVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java
@@ -1,4 +1,4 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection;
/*
This file is part of ELKI:
@@ -24,7 +24,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
*/
import java.util.ArrayList;
-import java.util.Iterator;
import org.apache.batik.dom.events.DOMMouseEvent;
import org.apache.batik.util.SVGConstants;
@@ -32,7 +31,6 @@ import org.w3c.dom.Element;
import org.w3c.dom.events.Event;
import org.w3c.dom.svg.SVGPoint;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
@@ -41,7 +39,7 @@ import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
+import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
@@ -52,7 +50,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
* Tool-Visualization for the tool to select objects
@@ -61,10 +59,8 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
*
* @apiviz.has SelectionResult oneway - - updates
* @apiviz.has DBIDSelection oneway - - updates
- *
- * @param <NV> vector type
*/
-public class SelectionToolDotVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements DragableArea.DragListener {
+public class SelectionToolDotVisualization extends AbstractScatterplotVisualization implements DragableArea.DragListener {
/**
* A short name characterizing this Visualizer.
*/
@@ -101,22 +97,10 @@ public class SelectionToolDotVisualization<NV extends NumberVector<NV, ?>> exten
*/
public SelectionToolDotVisualization(VisualizationTask task) {
super(task);
- context.addContextChangeListener(this);
incrementalRedraw();
}
@Override
- public void destroy() {
- super.destroy();
- context.removeContextChangeListener(this);
- }
-
- @Override
- public void contextChanged(ContextChangedEvent e) {
- synchronizedRedraw();
- }
-
- @Override
protected void redraw() {
addCSSClasses(svgp);
@@ -255,10 +239,8 @@ public class SelectionToolDotVisualization<NV extends NumberVector<NV, ?>> exten
*
* @apiviz.stereotype factory
* @apiviz.uses SelectionToolDotVisualization - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Factory extends AbstractVisFactory {
/**
* Constructor, adhering to
* {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
@@ -269,20 +251,21 @@ public class SelectionToolDotVisualization<NV extends NumberVector<NV, ?>> exten
@Override
public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolDotVisualization<NV>(task);
+ return new SelectionToolDotVisualization(task);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result result) {
final ArrayList<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
for(SelectionResult selres : selectionResults) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
+ IterableIterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
task.put(VisualizationTask.META_TOOL, true);
task.put(VisualizationTask.META_NOTHUMB, true);
task.put(VisualizationTask.META_NOEXPORT, true);
+ task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
baseResult.getHierarchy().add(selres, task);
baseResult.getHierarchy().add(p, task);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/package-info.java
new file mode 100644
index 00000000..3710de5c
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * <p>Visualizers for object selection based on 2D projections.</p>
+ */
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection; \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailThread.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailThread.java
index 5d79a986..2c9425de 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailThread.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailThread.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -26,7 +26,6 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
-
/**
* Thread to render thumbnails in the background.
*
@@ -47,11 +46,6 @@ public class ThumbnailThread extends Thread {
private boolean shutdown = false;
/**
- * Thumbnailer to use.
- */
- private Thumbnailer t = new Thumbnailer();
-
- /**
* The static thumbnail thread.
*/
private static ThumbnailThread THREAD = null;
@@ -113,7 +107,7 @@ public class ThumbnailThread extends Thread {
* @param ti Visualization task
*/
private void generateThumbnail(Task ti) {
- ti.callback.doThumbnail(t);
+ ti.callback.doThumbnail();
}
@Override
@@ -163,9 +157,7 @@ public class ThumbnailThread extends Thread {
public interface Listener {
/**
* Callback when to (re-)compute the thumbnail.
- *
- * @param t Thumbnailer to use
*/
- public void doThumbnail(Thumbnailer t);
+ public void doThumbnail();
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java
index 25f55d41..9ea016a5 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -23,32 +23,30 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.io.File;
+import java.awt.image.BufferedImage;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.batikutil.ThumbnailRegistryEntry;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleResult;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
import de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ContextChangedEvent;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.events.ResizedEvent;
/**
* Thumbnail visualization.
*
* @author Erich Schubert
*
- * @apiviz.uses Thumbnailer
* @apiviz.uses ThumbnailThread
*/
public class ThumbnailVisualization extends AbstractVisualization implements ThumbnailThread.Listener, DataStoreListener {
@@ -63,14 +61,24 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
public static final int ON_SELECTION = 2;
/**
+ * Constant to listen for style result changes
+ */
+ public static final int ON_STYLE = 4;
+
+ /**
+ * Constant to <em>not</em> listen for projection changes
+ */
+ public static final int NO_PROJECTION = 8;
+
+ /**
* Visualizer factory
*/
protected final VisFactory visFactory;
/**
- * The thumbnail file.
+ * The thumbnail id.
*/
- protected File thumb = null;
+ protected int thumbid = -1;
/**
* Pending redraw
@@ -83,11 +91,16 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
protected int tresolution;
/**
- * The event mask. See {@link #ON_DATA}, {@link #ON_SELECTION}
+ * The event mask. See {@link #ON_DATA}, {@link #ON_SELECTION}, {@link #ON_STYLE}, {@link #NO_PROJECTION}
*/
private int mask;
/**
+ * Our thumbnail (keep a reference to prevent garbage collection!)
+ */
+ private BufferedImage thumb;
+
+ /**
* Constructor.
*
* @param visFactory Visualizer Factory to use
@@ -100,14 +113,13 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
Integer tres = task.getGenerics(VisualizationTask.THUMBNAIL_RESOLUTION, Integer.class);
this.tresolution = tres;
this.layer = task.getPlot().svgElement(SVGConstants.SVG_G_TAG);
+ this.thumbid = -1;
this.thumb = null;
this.mask = mask;
// Listen for database events only when needed.
if((mask & ON_DATA) == ON_DATA) {
context.addDataStoreListener(this);
}
- // Always listen for context changes, in particular resize.
- context.addContextChangeListener(this);
// Listen for result changes, including the one we monitor
context.addResultListener(this);
}
@@ -117,45 +129,19 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
if(pendingThumbnail != null) {
ThumbnailThread.UNQUEUE(pendingThumbnail);
}
+ // TODO: remove image from registry?
context.removeResultListener(this);
- context.removeContextChangeListener(this);
context.removeDataStoreListener(this);
}
@Override
public Element getLayer() {
- if(thumb == null) {
+ if(thumbid < 0) {
synchronizedRedraw();
}
return layer;
}
- @Override
- public void contextChanged(ContextChangedEvent e) {
- if(testRedraw(e)) {
- refreshThumbnail();
- }
- }
-
- /**
- * Override this method to add additional redraw triggers!
- *
- * @param e Event
- * @return Test result
- */
- @Override
- protected boolean testRedraw(ContextChangedEvent e) {
- if(e instanceof ResizedEvent) {
- return true;
- }
- return false;
- }
-
- @Override
- public void contentChanged(DataStoreEvent e) {
- refreshThumbnail();
- }
-
/**
* Redraw the visualization (maybe incremental).
*
@@ -182,7 +168,7 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
*/
@Override
protected void redraw() {
- if(thumb == null) {
+ if(thumbid < 0) {
// LoggingUtil.warning("Generating new thumbnail " + this);
layer.appendChild(SVGUtil.svgWaitIcon(task.getPlot().getDocument(), 0, 0, task.getWidth(), task.getHeight()));
if(pendingThumbnail == null) {
@@ -196,13 +182,13 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
SVGUtil.setAtt(i, SVGConstants.SVG_Y_ATTRIBUTE, 0);
SVGUtil.setAtt(i, SVGConstants.SVG_WIDTH_ATTRIBUTE, task.getWidth());
SVGUtil.setAtt(i, SVGConstants.SVG_HEIGHT_ATTRIBUTE, task.getHeight());
- i.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, thumb.toURI().toString());
+ i.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, ThumbnailRegistryEntry.INTERNAL_PROTOCOL + ":" + thumbid);
layer.appendChild(i);
}
}
@Override
- public synchronized void doThumbnail(Thumbnailer t) {
+ public synchronized void doThumbnail() {
pendingThumbnail = null;
try {
SVGPlot plot = new SVGPlot();
@@ -217,7 +203,8 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
plot.updateStyleElement();
final int tw = (int) (task.getWidth() * tresolution);
final int th = (int) (task.getHeight() * tresolution);
- thumb = t.thumbnail(plot, tw, th);
+ thumb = plot.makeAWTImage(tw, th);
+ thumbid = ThumbnailRegistryEntry.registerImage(thumb);
// The visualization will not be used anymore.
vis.destroy();
synchronizedRedraw();
@@ -236,7 +223,9 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
protected void refreshThumbnail() {
// Discard an existing thumbnail
+ thumbid = -1;
thumb = null;
+ // TODO: also purge from ThumbnailRegistryEntry?
synchronizedRedraw();
}
@@ -246,6 +235,14 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
refreshThumbnail();
return;
}
+ if((mask & ON_STYLE) == ON_STYLE && current instanceof StyleResult) {
+ refreshThumbnail();
+ return;
+ }
+ if (task.getProj() != null && (mask & NO_PROJECTION) != NO_PROJECTION && current == task.getProj()) {
+ refreshThumbnail();
+ return;
+ }
super.resultChanged(current);
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/Thumbnailer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/Thumbnailer.java
deleted file mode 100644
index 1909821a..00000000
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/Thumbnailer.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2011
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.io.File;
-import java.io.IOException;
-
-import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
-import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
-
-/**
- * Class that will render a {@link SVGPlot} into a {@link File} as thumbnail.
- *
- * Note: this does not happen in the background - call it from your own Thread if you need that!
- *
- * @author Erich Schubert
- *
- * @apiviz.uses SVGPlot oneway - - renders
- * @apiviz.has File oneway - - «create»
- */
-public class Thumbnailer {
- /**
- * Default prefix
- */
- private static final String DEFAULT_PREFIX = "elki-";
-
- /**
- * Prefix storage.
- */
- private String prefix;
-
- /**
- * Constructor
- * @param prefix Filename prefix to avoid collisions (e.g "elki-")
- */
- public Thumbnailer(String prefix) {
- this.prefix = prefix;
- }
-
- /**
- * Constructor
- */
- public Thumbnailer() {
- this(DEFAULT_PREFIX);
- }
-
- /**
- * Generate a thumbnail for a given plot.
- *
- * @param plot Plot to use
- * @param thumbwidth Width of the thumbnail
- * @param thumbheight height of the thumbnail
- * @return File object of the thumbnail, which has deleteOnExit set.
- */
- public synchronized File thumbnail(SVGPlot plot, int thumbwidth, int thumbheight) {
- File temp = null;
- try {
- temp = File.createTempFile(prefix, ".png");
- temp.deleteOnExit();
- plot.saveAsPNG(temp, thumbwidth, thumbheight);
- }
- catch(org.apache.batik.bridge.BridgeException e) {
- plot.dumpDebugFile();
- LoggingUtil.exception("Exception rendering thumbnail: ", e);
- }
- catch(org.apache.batik.transcoder.TranscoderException e) {
- plot.dumpDebugFile();
- LoggingUtil.exception("Exception rendering thumbnail: ", e);
- }
- catch(IOException e) {
- LoggingUtil.exception(e);
- }
- return temp;
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/package-info.java
index 3006f52b..64676bb3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/package-info.java
@@ -6,7 +6,7 @@
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-Copyright (C) 2011
+Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterConvexHullVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterConvexHullVisualization.java
deleted file mode 100644
index 0b97986b..00000000
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusterConvexHullVisualization.java
+++ /dev/null
@@ -1,207 +0,0 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2011
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.Collection;
-import java.util.Iterator;
-
-import org.apache.batik.util.SVGConstants;
-import org.w3c.dom.Element;
-
-import de.lmu.ifi.dbs.elki.data.Cluster;
-import de.lmu.ifi.dbs.elki.data.Clustering;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.data.model.Model;
-import de.lmu.ifi.dbs.elki.data.spatial.Polygon;
-import de.lmu.ifi.dbs.elki.data.spatial.SpatialUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
-import de.lmu.ifi.dbs.elki.math.ConvexHull2D;
-import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
-import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
-import de.lmu.ifi.dbs.elki.result.Result;
-import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
-import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
-import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
-import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
-import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
-import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
-import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
-import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
-import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
-import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-
-/**
- * Visualizer for generating an SVG-Element containing the convex hull of each
- * cluster.
- *
- * @author Robert Rödler
- *
- * @apiviz.has Clustering oneway - - visualizes
- * @apiviz.uses ConvexHull2D
- *
- * @param <NV> Type of the NumberVector being visualized.
- */
-public class ClusterConvexHullVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> {
- /**
- * A short name characterizing this Visualizer.
- */
- private static final String NAME = "Cluster Convex Hull Visualization";
-
- /**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
- */
- public static final String CONVEXHULL = "convexHull";
-
- /**
- * The result we work on
- */
- Clustering<Model> clustering;
-
- /**
- * The hulls
- */
- Element hulls;
-
- /**
- * Constructor
- *
- * @param task VisualizationTask
- */
- public ClusterConvexHullVisualization(VisualizationTask task) {
- super(task);
- this.clustering = task.getResult();
- context.addContextChangeListener(this);
- incrementalRedraw();
- }
-
- @Override
- protected void redraw() {
- // Viewport size, for "relative size" computations
- final Pair<DoubleMinMax, DoubleMinMax> viewp = proj.estimateViewport();
- double projarea = (viewp.getFirst().getDiff()) * (viewp.getSecond().getDiff());
-
- double opacity = 0.25;
-
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
-
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<?> clus = ci.next();
-
- final DBIDs ids = clus.getIDs();
- ConvexHull2D hull = new ConvexHull2D();
-
- for(DBID clpnum : ids) {
- double[] projP = proj.fastProjectDataToRenderSpace(rel.get(clpnum).getColumnVector());
- hull.add(new Vector(projP));
- }
- Polygon chres = hull.getHull();
-
- // Plot the convex hull:
- if(chres != null) {
- SVGPath path = new SVGPath(chres);
- // Approximate area (using bounding box)
- double hullarea = SpatialUtil.volume(chres);
- final double relativeArea = (projarea - hullarea) / projarea;
- final double relativeSize = (double) ids.size() / rel.size();
- opacity = Math.sqrt(relativeSize * relativeArea);
-
- hulls = path.makeElement(svgp);
- addCSSClasses(svgp, cnum, opacity);
- SVGUtil.addCSSClass(hulls, CONVEXHULL + cnum);
- layer.appendChild(hulls);
- }
- }
- }
-
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp, int clusterID, double opac) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- CSSClass cls = new CSSClass(this, CONVEXHULL + clusterID);
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
-
- final String color;
- if(clustering.getAllClusters().size() == 1) {
- color = "black";
- }
- else {
- color = colors.getColor(clusterID);
- }
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, opac);
-
- svgp.addCSSClassOrLogError(cls);
- }
-
- /**
- * Factory for visualizers to generate an SVG-Element containing the convex
- * hull of a cluster.
- *
- * @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterConvexHullVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
- */
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
- /**
- * Constructor
- */
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterConvexHullVisualization<NV>(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusteringVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusteringVisualization.java
deleted file mode 100644
index 4bef6e11..00000000
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/ClusteringVisualization.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2011
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.Collection;
-import java.util.Iterator;
-
-import de.lmu.ifi.dbs.elki.data.Cluster;
-import de.lmu.ifi.dbs.elki.data.Clustering;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.data.model.Model;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
-import de.lmu.ifi.dbs.elki.result.Result;
-import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
-import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
-import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
-import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
-import de.lmu.ifi.dbs.elki.visualization.style.marker.MarkerLibrary;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-
-/**
- * Visualize a clustering using different markers for different clusters.
- *
- * @author Erich Schubert
- *
- * @apiviz.has Clustering oneway - - visualizes
- *
- * @param <NV> Type of the DatabaseObject being visualized.
- */
-// TODO: ensure we have the right database for this
-public class ClusteringVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements DataStoreListener {
- /**
- * The result we visualize
- */
- private Clustering<Model> clustering;
-
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public ClusteringVisualization(VisualizationTask task) {
- super(task);
- this.clustering = task.getResult();
- context.addDataStoreListener(this);
- incrementalRedraw();
- }
-
- @Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
- }
-
- @Override
- public void redraw() {
- MarkerLibrary ml = context.getStyleLibrary().markers();
- double marker_size = context.getStyleLibrary().getSize(StyleLibrary.MARKERPLOT);
- // draw data
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<?> clus = ci.next();
- for(DBID objId : clus.getIDs()) {
- try {
- final NV vec = rel.get(objId);
- double[] v = proj.fastProjectDataToRenderSpace(vec);
- ml.useMarker(svgp, layer, v[0], v[1], cnum, marker_size);
- }
- catch(ObjectNotFoundException e) {
- // ignore.
- }
- }
- }
- }
-
- @Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
- }
-
- /**
- * Visualization factory
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses ClusteringVisualization oneway - - «create»
- *
- * @param <NV> Type of the DatabaseObject being visualized.
- */
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
- /**
- * A short name characterizing this Visualizer.
- */
- private static final String NAME = "Cluster Markers";
-
- /**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
- */
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusteringVisualization<NV>(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/DotVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/DotVisualization.java
deleted file mode 100644
index 3cfd5a48..00000000
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/vis2d/DotVisualization.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.vis2d;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2011
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import org.w3c.dom.Element;
-
-import de.lmu.ifi.dbs.elki.data.Clustering;
-import de.lmu.ifi.dbs.elki.data.NumberVector;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
-import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
-import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
-import de.lmu.ifi.dbs.elki.result.Result;
-import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
-import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil;
-import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
-import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
-import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
-import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-
-/**
- * Generates a SVG-Element containing "dots" as markers representing the
- * Database's objects.
- *
- * @author Remigius Wojdanowski
- *
- * @apiviz.has NumberVector - - visualizes
- *
- * @param <NV> Type of the NumberVector being visualized.
- */
-public class DotVisualization<NV extends NumberVector<NV, ?>> extends P2DVisualization<NV> implements DataStoreListener {
- /**
- * A short name characterizing this Visualizer.
- */
- private static final String NAME = "Data Dots";
-
- /**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
- */
- public static final String MARKER = "marker";
-
- /**
- * Constructor.
- *
- * @param task Task to visualize
- */
- public DotVisualization(VisualizationTask task) {
- super(task);
- context.addDataStoreListener(this);
- incrementalRedraw();
- }
-
- @Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
- }
-
- @Override
- public void redraw() {
- // draw data
- double dot_size = context.getStyleLibrary().getSize(StyleLibrary.DOTPLOT);
- for(DBID id : rel.iterDBIDs()) {
- try {
- double[] v = proj.fastProjectDataToRenderSpace(rel.get(id));
- Element dot = svgp.svgCircle(v[0], v[1], dot_size);
- SVGUtil.addCSSClass(dot, MARKER);
- layer.appendChild(dot);
- }
- catch(ObjectNotFoundException e) {
- // ignore.
- }
-
- }
- }
-
- @Override
- public void contentChanged(DataStoreEvent e) {
- synchronizedRedraw();
- }
-
- /**
- * The visualization factory
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses DotVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
- */
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
- /**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
- */
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new DotVisualization<NV>(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- ArrayList<Clustering<?>> cs = ResultUtil.filterResults(result, Clustering.class);
- boolean hasClustering = (cs.size() > 0);
-
- Iterator<ScatterPlotProjector<?>> ps = ResultUtil.filteredResults(result, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : IterableUtil.fromIterator(ps)) {
- final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
- if(hasClustering) {
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- }
- // baseResult.getHierarchy().add(p.getRelation(), task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java
index 405ba673..05d9038c 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -24,12 +24,20 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
*/
import java.util.ArrayList;
+import java.util.List;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.evaluation.paircounting.EvaluatePairCountingFMeasure;
-import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.BCubed;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.ClusterContingencyTable;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.EditDistance;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.Entropy;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.EvaluateClustering;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.PairCounting;
+import de.lmu.ifi.dbs.elki.evaluation.clustering.SetMatchingPurity;
+import de.lmu.ifi.dbs.elki.math.MeanVariance;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
@@ -37,6 +45,7 @@ import de.lmu.ifi.dbs.elki.utilities.FormatUtil;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGScoreBar;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
@@ -46,10 +55,11 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* Pseudo-Visualizer, that lists the cluster evaluation results found.
*
* @author Erich Schubert
+ * @author Sascha Goldhofer
*
* @apiviz.stereotype factory
* @apiviz.uses StaticVisualization oneway - - «create»
- * @apiviz.has de.lmu.ifi.dbs.elki.evaluation.paircounting.EvaluatePairCountingFMeasure.ScoreResult oneway - - visualizes
+ * @apiviz.has EvaluateClustering.ScoreResult oneway - - visualizes
*/
public class ClusterEvaluationVisFactory extends AbstractVisFactory {
/**
@@ -58,68 +68,124 @@ public class ClusterEvaluationVisFactory extends AbstractVisFactory {
private static final String NAME = "Cluster Evaluation";
/**
+ * Constant: width of score bars
+ */
+ private static final double BARLENGTH = 5;
+
+ /**
+ * Constant: height of score bars
+ */
+ private static final double BARHEIGHT = 0.7;
+
+ /**
* Constructor.
*/
public ClusterEvaluationVisFactory() {
super();
}
-
+
@Override
public void processNewResult(HierarchicalResult baseResult, Result newResult) {
- final ArrayList<EvaluatePairCountingFMeasure.ScoreResult> srs = ResultUtil.filterResults(newResult, EvaluatePairCountingFMeasure.ScoreResult.class);
- for(EvaluatePairCountingFMeasure.ScoreResult sr : srs) {
+ final ArrayList<EvaluateClustering.ScoreResult> srs = ResultUtil.filterResults(newResult, EvaluateClustering.ScoreResult.class);
+ for(EvaluateClustering.ScoreResult sr : srs) {
final VisualizationTask task = new VisualizationTask(NAME, sr, null, this);
- task.width = 1.0;
- task.height = 0.5;
+ task.width = .5;
+ task.height = 2.0;
task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
baseResult.getHierarchy().add(sr, task);
}
}
+ private double addBarChart(SVGPlot svgp, Element parent, double ypos, String label, double maxValue, double value) {
+ SVGScoreBar barchart = new SVGScoreBar();
+ barchart.setFill(value, maxValue);
+ barchart.showValues(FormatUtil.NF4);
+ barchart.addLabel(label);
+ parent.appendChild(barchart.build(svgp, 0.0, ypos, BARLENGTH, BARHEIGHT));
+ ypos += 1;
+ return ypos;
+ }
+
+ private double addHeader(SVGPlot svgp, Element parent, double ypos, String text) {
+ ypos += .5;
+ Element object = svgp.svgText(0, ypos + BARHEIGHT * 0.5, text);
+ object.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6; font-weight: bold");
+ parent.appendChild(object);
+ ypos += 1;
+ return ypos;
+ }
+
@Override
public Visualization makeVisualization(VisualizationTask task) {
- SVGPlot svgp = task.getPlot();
- Element layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
- EvaluatePairCountingFMeasure.ScoreResult sr = task.getResult();
+ // TODO: make a utility class to wrap SVGPlot + parent layer + ypos.
- // TODO: use CSSClass and StyleLibrary
- int i = 0;
- {
- Element object = svgp.svgText(0, i + 0.7, "Same-cluster object pairs");
- object.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6; font-weight: bold");
- layer.appendChild(object);
- i++;
- }
- {
- Element object = svgp.svgText(0, i + 0.7, "F1-Measure, Precision and Recall:");
- object.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6; font-weight: bold");
- layer.appendChild(object);
- i++;
- }
- for(Vector vec : sr) {
- StringBuffer buf = new StringBuffer();
- double fmeasure = vec.get(0);
- double inboth = vec.get(1);
- double infirst = vec.get(2);
- double insecond = vec.get(3);
- buf.append(FormatUtil.format(fmeasure, FormatUtil.NF6));
- buf.append(" / ");
- buf.append(FormatUtil.format(inboth / (inboth + infirst), FormatUtil.NF6));
- buf.append(" / ");
- buf.append(FormatUtil.format(inboth / (inboth + insecond), FormatUtil.NF6));
- Element object = svgp.svgText(0, i + 0.7, buf.toString());
- object.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6");
- layer.appendChild(object);
- i++;
+ double ypos = -.5; // Skip space before first header
+ SVGPlot svgp = task.getPlot();
+ Element parent = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ EvaluateClustering.ScoreResult sr = task.getResult();
+ ClusterContingencyTable cont = sr.getContingencyTable();
+
+ List<Result> parents = task.getContext().getHierarchy().getParents(sr);
+
+ for(Result r : parents) {
+ if(r instanceof Clustering) {
+ ypos = addHeader(svgp, parent, ypos, r.getLongName());
+ }
}
+ // TODO: use CSSClass and StyleLibrary
+
+ ypos = addHeader(svgp, parent, ypos, "Pair counting measures");
+
+ PairCounting paircount = cont.getPaircount();
+ ypos = addBarChart(svgp, parent, ypos, "Jaccard", 1, paircount.jaccard());
+ ypos = addBarChart(svgp, parent, ypos, "F1-Measure", 1, paircount.f1Measure());
+ ypos = addBarChart(svgp, parent, ypos, "Precision", 1, paircount.precision());
+ ypos = addBarChart(svgp, parent, ypos, "Recall", 1, paircount.recall());
+ ypos = addBarChart(svgp, parent, ypos, "Rand", 1, paircount.randIndex());
+ ypos = addBarChart(svgp, parent, ypos, "ARI", 1, paircount.adjustedRandIndex());
+ ypos = addBarChart(svgp, parent, ypos, "FowlkesMallows", 1, paircount.fowlkesMallows());
+
+ ypos = addHeader(svgp, parent, ypos, "Entropy based measures");
+
+ Entropy entropy = cont.getEntropy();
+ ypos = addBarChart(svgp, parent, ypos, "NMI Joint", 1, entropy.entropyNMIJoint());
+ ypos = addBarChart(svgp, parent, ypos, "NMI Sqrt", 1, entropy.entropyNMISqrt());
+
+ ypos = addHeader(svgp, parent, ypos, "BCubed-based measures");
+
+ BCubed bcubed = cont.getBCubed();
+ ypos = addBarChart(svgp, parent, ypos, "F1-Measure", 1, bcubed.f1Measure());
+ ypos = addBarChart(svgp, parent, ypos, "Recall", 1, bcubed.recall());
+ ypos = addBarChart(svgp, parent, ypos, "Precision", 1, bcubed.precision());
+
+ ypos = addHeader(svgp, parent, ypos, "Set-Matching-based measures");
+
+ SetMatchingPurity setm = cont.getSetMatching();
+ ypos = addBarChart(svgp, parent, ypos, "F1-Measure", 1, setm.f1Measure());
+ ypos = addBarChart(svgp, parent, ypos, "Purity", 1, setm.purity());
+ ypos = addBarChart(svgp, parent, ypos, "Inverse Purity", 1, setm.inversePurity());
+
+ ypos = addHeader(svgp, parent, ypos, "Editing-distance measures");
+
+ EditDistance edit = cont.getEdit();
+ ypos = addBarChart(svgp, parent, ypos, "F1-Measure", 1, edit.f1Measure());
+ ypos = addBarChart(svgp, parent, ypos, "Precision", 1, edit.editDistanceFirst());
+ ypos = addBarChart(svgp, parent, ypos, "Recall", 1, edit.editDistanceSecond());
+
+ ypos = addHeader(svgp, parent, ypos, "Gini measures");
+
+ final MeanVariance gini = cont.averageSymmetricGini();
+ ypos = addBarChart(svgp, parent, ypos, "Mean +-" + FormatUtil.format(gini.getSampleStddev(), FormatUtil.NF4), 1, gini.getMean());
- int cols = Math.max(10, (int) (i * task.getHeight() / task.getWidth()));
- int rows = i;
+ // scale vis
+ double cols = 10; // Math.max(10, (int) (i * task.getHeight() /
+ // task.getWidth()));
+ double rows = ypos;
final double margin = task.getContext().getStyleLibrary().getSize(StyleLibrary.MARGIN);
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols, rows, margin / StyleLibrary.SCALE);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+ SVGUtil.setAtt(parent, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- return new StaticVisualization(task, layer);
+ return new StaticVisualization(task, parent);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/CurveVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/CurveVisFactory.java
index 4d0e3dc0..e12a392a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/CurveVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/CurveVisFactory.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -33,6 +33,7 @@ import de.lmu.ifi.dbs.elki.evaluation.roc.ComputeROCCurve;
import de.lmu.ifi.dbs.elki.evaluation.roc.ComputeROCCurve.ROCResult;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.IterableResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -45,7 +46,6 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
-import de.lmu.ifi.dbs.elki.visualization.scales.LinearScale;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
@@ -118,8 +118,8 @@ public class CurveVisFactory extends AbstractVisFactory {
// add axes
try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, scalex, 0, sizey, sizex, sizey, true, true, context.getStyleLibrary());
- SVGSimpleLinearAxis.drawAxis(svgp, layer, scaley, 0, sizey, 0, 0, true, false, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, scaley, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, scalex, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
}
catch(CSSNamingConflict e) {
LoggingUtil.exception(e);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java
index 81a77c13..117c3c2c 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -31,6 +31,7 @@ import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.HistogramResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -40,7 +41,6 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
-import de.lmu.ifi.dbs.elki.visualization.scales.LinearScale;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
@@ -141,8 +141,8 @@ public class HistogramVisFactory extends AbstractVisFactory {
// add axes
try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, xscale, 0, sizey, sizex, sizey, true, true, context.getStyleLibrary());
- SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, sizey, 0, 0, true, false, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, xscale, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
}
catch(CSSNamingConflict e) {
LoggingUtil.exception(e);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisFactory.java
deleted file mode 100644
index e3a5dfd4..00000000
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisFactory.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
-
-/*
- This file is part of ELKI:
- Environment for Developing KDD-Applications Supported by Index-Structures
-
- Copyright (C) 2011
- Ludwig-Maximilians-Universität München
- Lehr- und Forschungseinheit für Datenbanksysteme
- ELKI Development Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.batik.util.SVGConstants;
-import org.w3c.dom.Element;
-
-import de.lmu.ifi.dbs.elki.data.Cluster;
-import de.lmu.ifi.dbs.elki.data.Clustering;
-import de.lmu.ifi.dbs.elki.data.model.Model;
-import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
-import de.lmu.ifi.dbs.elki.result.Result;
-import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
-import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
-import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
-import de.lmu.ifi.dbs.elki.visualization.style.marker.MarkerLibrary;
-import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
-import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-
-/**
- * Pseudo-Visualizer, that gives the key for a clustering.
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
- * @apiviz.has Clustering oneway - - visualizes
- */
-public class KeyVisFactory extends AbstractVisFactory {
- /**
- * Name for this visualizer.
- */
- private static final String NAME = "Cluster Key";
-
- /**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
- */
- public KeyVisFactory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- Clustering<Model> clustering = task.getResult();
- SVGPlot svgp = task.getPlot();
- VisualizerContext context = task.getContext();
- final List<Cluster<Model>> allcs = clustering.getAllClusters();
- int numc = allcs.size();
-
- // FIXME: Use CSS and style library.
-
- Element layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
-
- MarkerLibrary ml = context.getStyleLibrary().markers();
-
- int i = 0;
- for(Cluster<Model> c : allcs) {
- ml.useMarker(svgp, layer, 0.3, i + 0.5, i, 0.3);
- Element label = svgp.svgText(0.7, i + 0.7, c.getNameAutomatic());
- label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6");
- layer.appendChild(label);
- i++;
- }
-
- int cols = Math.max(6, (int) (numc * task.getHeight() / task.getWidth()));
- int rows = numc;
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols, rows, margin / StyleLibrary.SCALE);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
-
- return new StaticVisualization(task, layer);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result newResult) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(newResult, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- final VisualizationTask task = new VisualizationTask(NAME, c, null, this);
- task.width = 1.0;
- task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
- baseResult.getHierarchy().add(c, task);
- }
- }
- }
-
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- return false;
- }
-} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java
new file mode 100644
index 00000000..15c50a39
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java
@@ -0,0 +1,198 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+
+import de.lmu.ifi.dbs.elki.data.Cluster;
+import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.data.model.Model;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.style.ClusterStylingPolicy;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.style.StylingPolicy;
+import de.lmu.ifi.dbs.elki.visualization.style.marker.MarkerLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGButton;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+
+/**
+ * Visualizer, displaying the key for a clustering.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Clustering oneway - - visualizes
+ */
+public class KeyVisualization extends AbstractVisualization {
+ /**
+ * Name for this visualizer.
+ */
+ private static final String NAME = "Cluster Key";
+
+ /**
+ * Clustering to display
+ */
+ private Clustering<Model> clustering;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public KeyVisualization(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ context.addResultListener(this);
+ }
+
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(current == context.getStyleResult()) {
+ incrementalRedraw();
+ }
+ }
+
+ @Override
+ protected void redraw() {
+ SVGPlot svgp = task.getPlot();
+ final List<Cluster<Model>> allcs = clustering.getAllClusters();
+
+ MarkerLibrary ml = context.getStyleLibrary().markers();
+ layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+
+ // Add a label for the clustering.
+ {
+ Element label = svgp.svgText(0.1, 0.7, clustering.getLongName());
+ label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.4");
+ layer.appendChild(label);
+ }
+
+ // TODO: multi-column layout!
+ int i = 0;
+ for(Cluster<Model> c : allcs) {
+ ml.useMarker(svgp, layer, 0.3, i + 1.5, i, 0.3);
+ Element label = svgp.svgText(0.7, i + 1.7, c.getNameAutomatic());
+ label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6");
+ layer.appendChild(label);
+ i++;
+ }
+
+ // Add a button to set style policy
+ {
+ StylingPolicy sp = context.getStyleResult().getStylingPolicy();
+ if(sp instanceof ClusterStylingPolicy && ((ClusterStylingPolicy) sp).getClustering() == clustering) {
+ // Don't show the button when active. May confuse people more than the disappearing button
+
+ // SVGButton button = new SVGButton(.1, i + 1.1, 3.8, .7, .2);
+ // button.setTitle("Active style", "darkgray");
+ // layer.appendChild(button.render(svgp));
+ }
+ else {
+ SVGButton button = new SVGButton(.1, i + 1.1, 3.8, .7, .2);
+ button.setTitle("Set style", "black");
+ Element elem = button.render(svgp);
+ // Attach listener
+ EventTarget etr = (EventTarget) elem;
+ etr.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ setStylePolicy();
+ }
+ }, false);
+ layer.appendChild(elem);
+ }
+ }
+
+ int rows = i + 2;
+ int cols = Math.max(6, (int) (rows * task.getHeight() / task.getWidth()));
+ final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols, rows, margin / StyleLibrary.SCALE);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+ }
+
+ /**
+ * Trigger a style change.
+ */
+ protected void setStylePolicy() {
+ context.getStyleResult().setStylingPolicy(new ClusterStylingPolicy(clustering, context.getStyleLibrary()));
+ context.getHierarchy().resultChanged(context.getStyleResult());
+ }
+
+ /**
+ * Visualization factory
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses KeyVisualization oneway - - «create»
+ */
+ public static class Factory extends AbstractVisFactory {
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result newResult) {
+ // Find clusterings we can visualize:
+ Iterator<Clustering<?>> clusterings = ResultUtil.filteredResults(newResult, Clustering.class);
+ while(clusterings.hasNext()) {
+ Clustering<?> c = clusterings.next();
+ if(c.getAllClusters().size() > 0) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, null, this);
+ task.width = 1.0;
+ task.height = 1.0;
+ task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.put(VisualizationTask.META_NODETAIL, true);
+ baseResult.getHierarchy().add(c, task);
+ }
+ }
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new KeyVisualization(task);
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java
index 6145b7a8..b6eb4549 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java
index 825d64b6..9269f404 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java
index 5d33fede..5bbc698c 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java
index 17424519..c53a722a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2011
+ Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/package-info.java
index 252bffc5..30042923 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/package-info.java
@@ -6,7 +6,7 @@
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-Copyright (C) 2011
+Copyright (C) 2012
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team