diff options
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java')
-rw-r--r-- | src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java | 187 |
1 files changed, 187 insertions, 0 deletions
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 new file mode 100644 index 00000000..3d328b58 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java @@ -0,0 +1,187 @@ +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.List; + +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.logging.LoggingUtil; +import de.lmu.ifi.dbs.elki.math.DoubleMinMax; +import de.lmu.ifi.dbs.elki.result.HierarchicalResult; +import de.lmu.ifi.dbs.elki.result.HistogramResult; +import de.lmu.ifi.dbs.elki.result.Result; +import de.lmu.ifi.dbs.elki.result.ResultUtil; +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.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; +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.StaticVisualization; +import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization; +import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizationTask; +import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerContext; + +/** + * Visualizer to draw histograms. + * + * @author Erich Schubert + * + * @apiviz.stereotype factory + * @apiviz.uses StaticVisualization oneway - - «create» + * @apiviz.has HistogramResult oneway - - visualizes + */ +public class HistogramVisFactory extends AbstractVisFactory { + /** + * Histogram visualizer name + */ + private static final String NAME = "Histogram"; + + /** + * CSS class name for the series. + */ + private static final String SERIESID = "series"; + + /** + * Constructor, adhering to + * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable} + */ + public HistogramVisFactory() { + super(); + } + + @Override + public Visualization makeVisualization(VisualizationTask task) { + VisualizerContext context = task.getContext(); + SVGPlot svgp = task.getPlot(); + HistogramResult<? extends NumberVector<?, ?>> curve = task.getResult(); + + double scale = StyleLibrary.SCALE; + final double sizex = scale; + final double sizey = scale * task.getHeight() / task.getWidth(); + final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN); + Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG); + final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin); + SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform); + + // find maximum, determine step size + Integer dim = null; + DoubleMinMax xminmax = new DoubleMinMax(); + DoubleMinMax yminmax = new DoubleMinMax(); + for(NumberVector<?, ?> vec : curve) { + xminmax.put(vec.doubleValue(1)); + if(dim == null) { + dim = vec.getDimensionality(); + } + else { + // TODO: test and throw always + assert (dim == vec.getDimensionality()); + } + for(int i = 1; i < dim; i++) { + yminmax.put(vec.doubleValue(i + 1)); + } + } + // Minimum should always start at 0 for histograms + yminmax.put(0.0); + // remove one dimension which are the x values. + dim = dim - 1; + + int size = curve.size(); + double range = xminmax.getMax() - xminmax.getMin(); + double binwidth = range / (size - 1); + + LinearScale xscale = new LinearScale(xminmax.getMin() - binwidth / 2, xminmax.getMax() + binwidth / 2); + LinearScale yscale = new LinearScale(yminmax.getMin(), yminmax.getMax()); + + SVGPath[] path = new SVGPath[dim]; + for(int i = 0; i < dim; i++) { + path[i] = new SVGPath(sizex * xscale.getScaled(xminmax.getMin() - binwidth / 2), sizey); + } + + // draw curves. + for(NumberVector<?, ?> vec : curve) { + for(int d = 0; d < dim; d++) { + path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(1) - binwidth / 2)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 2)))); + path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(1) + binwidth / 2)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 2)))); + } + } + + // close all histograms + for(int i = 0; i < dim; i++) { + path[i].lineTo(sizex * xscale.getScaled(xminmax.getMax() + binwidth / 2), sizey); + } + + // 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()); + } + catch(CSSNamingConflict e) { + LoggingUtil.exception(e); + } + // Setup line styles and insert lines. + ColorLibrary cl = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT); + for(int d = 0; d < dim; d++) { + CSSClass csscls = new CSSClass(this, SERIESID + "_" + d); + csscls.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE); + csscls.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, cl.getColor(d)); + csscls.setStatement(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT)); + svgp.addCSSClassOrLogError(csscls); + + Element line = path[d].makeElement(svgp); + line.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, csscls.getName()); + layer.appendChild(line); + } + + return new StaticVisualization(task, layer); + } + + @Override + public void processNewResult(HierarchicalResult baseResult, Result newResult) { + List<HistogramResult<? extends NumberVector<?, ?>>> histograms = ResultUtil.filterResults(newResult, HistogramResult.class); + for(HistogramResult<? extends NumberVector<?, ?>> histogram : histograms) { + final VisualizationTask task = new VisualizationTask(NAME, histogram, null, this, null); + task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC); + baseResult.getHierarchy().add(histogram, task); + } + } + + @Override + public boolean allowThumbnails(@SuppressWarnings("unused") VisualizationTask task) { + // TODO: depending on the histogram complexity? + return false; + } + + @Override + public Class<? extends Projection> getProjectionType() { + return null; + } +}
\ No newline at end of file |