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 . */ import java.util.ArrayList; import java.util.Collection; 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.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.stereotype factory * @apiviz.uses Instance oneway - - «create» */ public class RTreeParallelVisualization extends AbstractVisFactory { /** * 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"; /** * Settings */ protected Parameterizer settings; /** * Constructor. * * @param settings Settings */ public RTreeParallelVisualization(Parameterizer settings) { super(); this.settings = settings; } @Override public Visualization makeVisualization(VisualizationTask task) { return new Instance(task); } @Override public void processNewResult(HierarchicalResult baseResult, Result result) { ArrayList> trees = ResultUtil.filterResults(result, AbstractRStarTree.class); for(AbstractRStarTree tree : trees) { if(tree instanceof Result) { Collection> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class); for(ParallelPlotProjector p : ps) { final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this); task.level = VisualizationTask.LEVEL_BACKGROUND + 2; baseResult.getHierarchy().add((Result) tree, task); baseResult.getHierarchy().add(p, task); } } } } /** * Instance for a particular data set and tree * * @author Robert Rödler * * @apiviz.has AbstractRStarTree oneway - - visualizes * * @param Tree node type * @param Tree entry type */ // TODO: listen for tree changes instead of data changes? public class Instance, E extends SpatialEntry> extends AbstractParallelVisualization> implements DataStoreListener { /** * The tree we visualize */ protected AbstractRStarTree tree; /** * Constructor. * * @param task Visualization task */ @SuppressWarnings("unchecked") public Instance(VisualizationTask task) { super(task); this.tree = AbstractRStarTree.class.cast(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() { 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) { final StyleLibrary style = context.getStyleResult().getStyleLibrary(); final ColorLibrary colors = style.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(settings.fill) { cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i)); cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.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 * style.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 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); } } } } } /** * 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.Parameterizer.FILL_ID); fillF.setDefaultValue(Boolean.TRUE); if(config.grab(fillF)) { fill = fillF.isTrue(); } } @Override protected RTreeParallelVisualization makeInstance() { return new RTreeParallelVisualization(this); } } }