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) 2015
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 .
*/
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.algorithm.outlier.COP;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.math.linearalgebra.VMath;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
import de.lmu.ifi.dbs.elki.utilities.datastructures.hierarchy.Hierarchy;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTree;
import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.gui.VisualizationPlot;
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.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 error vectors as produced by COP.
*
* @author Erich Schubert
* @since 0.5.5
*
* @apiviz.stereotype factory
* @apiviz.uses Instance oneway - - «create»
* @apiviz.has OutlierResult oneway - - visualizes
*/
@Title("COP: Correlation Outlier Probability")
@Reference(authors = "Hans-Peter Kriegel, Peer Kröger, Erich Schubert, Arthur Zimek", //
title = "Outlier Detection in Arbitrarily Oriented Subspaces", //
booktitle = "Proc. IEEE International Conference on Data Mining (ICDM 2012)", //
url = "http://dx.doi.org/10.1109/ICDM.2012.21")
public class COPVectorVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
public static final String NAME = "Error Vectors";
/**
* Constructor.
*/
public COPVectorVisualization() {
super();
}
@Override
public Visualization makeVisualization(VisualizationTask task, VisualizationPlot plot, double width, double height, Projection proj) {
return new Instance(task, plot, width, height, proj);
}
@Override
public void processNewResult(VisualizerContext context, Object start) {
VisualizationTree.findNewSiblings(context, start, OutlierResult.class, ScatterPlotProjector.class, new VisualizationTree.Handler2>() {
@Override
public void process(VisualizerContext context, OutlierResult o, ScatterPlotProjector> p) {
final Relation> rel2 = p.getRelation();
if(!TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel2.getDataTypeInformation())) {
return;
}
Hierarchy.Iter> it1 = VisualizationTree.filterResults(context, o, Relation.class);
for(; it1.valid(); it1.advance()) {
Relation> rel = it1.get();
if(!rel.getShortName().equals(COP.COP_ERRORVEC)) {
continue;
}
final VisualizationTask task = new VisualizationTask(NAME, context, rel, rel2, COPVectorVisualization.this);
task.level = VisualizationTask.LEVEL_DATA;
task.addUpdateFlags(VisualizationTask.ON_DATA | VisualizationTask.ON_SAMPLE);
context.addVis(o, task);
context.addVis(p, task);
}
}
});
}
/**
* Visualize error vectors as produced by COP.
*
* @author Erich Schubert
*/
public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes
* etc.
*/
public static final String VEC = "copvec";
/**
* The outlier result to visualize
*/
protected Relation result;
/**
* Constructor.
*
* @param task Visualization task
* @param plot Plot to draw to
* @param width Embedding width
* @param height Embedding height
* @param proj Projection
*/
public Instance(VisualizationTask task, VisualizationPlot plot, double width, double height, Projection proj) {
super(task, plot, width, height, proj);
this.result = task.getResult();
addListeners();
}
@Override
public void fullRedraw() {
setupCanvas();
setupCSS(svgp);
for(DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
Vector evec = result.get(objId);
if(evec == null) {
continue;
}
double[] ev = proj.fastProjectRelativeDataToRenderSpace(evec);
// TODO: avoid hard-coded plot threshold
if(VMath.euclideanLength(ev) < 0.01) {
continue;
}
final NumberVector vec = rel.get(objId);
if(vec == null) {
continue;
}
double[] v = proj.fastProjectDataToRenderSpace(vec);
if(v[0] != v[0] || v[1] != v[1]) {
continue; // NaN!
}
Element arrow = svgp.svgLine(v[0], v[1], v[0] + ev[0], v[1] + ev[1]);
SVGUtil.addCSSClass(arrow, VEC);
layer.appendChild(arrow);
}
}
/**
* Registers the COP error vector-CSS-Class at a SVGPlot.
*
* @param svgp the SVGPlot to register the Tooltip-CSS-Class.
*/
private void setupCSS(SVGPlot svgp) {
final StyleLibrary style = context.getStyleLibrary();
CSSClass bubble = new CSSClass(svgp, VEC);
bubble.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) / 2);
// ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
String color = "red"; // TODO: use style library
bubble.setStatement(SVGConstants.CSS_STROKE_VALUE, color);
bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
svgp.addCSSClassOrLogError(bubble);
}
}
}