1 /* 2 * This file is part of ELKI: 3 * Environment for Developing KDD-Applications Supported by Index-Structures 4 * 5 * Copyright (C) 2018 6 * ELKI Development Team 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Affero General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Affero General Public License for more details. 17 * 18 * You should have received a copy of the GNU Affero General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 package de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj; 22 23 import java.awt.image.RenderedImage; 24 25 import org.apache.batik.util.SVGConstants; 26 import org.w3c.dom.Element; 27 28 import de.lmu.ifi.dbs.elki.database.Database; 29 import de.lmu.ifi.dbs.elki.database.DatabaseUtil; 30 import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; 31 import de.lmu.ifi.dbs.elki.database.relation.Relation; 32 import de.lmu.ifi.dbs.elki.evaluation.similaritymatrix.ComputeSimilarityMatrixImage.SimilarityMatrix; 33 import de.lmu.ifi.dbs.elki.result.ResultUtil; 34 import de.lmu.ifi.dbs.elki.visualization.VisualizationTask; 35 import de.lmu.ifi.dbs.elki.visualization.VisualizationTree; 36 import de.lmu.ifi.dbs.elki.visualization.VisualizerContext; 37 import de.lmu.ifi.dbs.elki.visualization.gui.VisualizationPlot; 38 import de.lmu.ifi.dbs.elki.visualization.projections.Projection; 39 import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary; 40 import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil; 41 import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization; 42 import de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory; 43 import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization; 44 45 /** 46 * Visualize a similarity matrix with object labels 47 * 48 * @author Erich Schubert 49 * @since 0.4.0 50 * 51 * @stereotype factory 52 * @navassoc - create - Instance 53 */ 54 public class SimilarityMatrixVisualizer implements VisFactory { 55 /** 56 * Name for this visualizer. 57 */ 58 private static final String NAME = "Similarity Matrix Visualizer"; 59 60 /** 61 * Constructor. 62 */ SimilarityMatrixVisualizer()63 public SimilarityMatrixVisualizer() { 64 super(); 65 } 66 67 @Override processNewResult(VisualizerContext context, Object start)68 public void processNewResult(VisualizerContext context, Object start) { 69 VisualizationTree.findNewResults(context, start).filter(SimilarityMatrix.class).forEach(pr -> { 70 context.addVis(pr, new VisualizationTask(this, NAME, pr, null) // 71 .level(VisualizationTask.LEVEL_STATIC)); 72 }); 73 } 74 75 @Override makeVisualization(VisualizerContext context, VisualizationTask task, VisualizationPlot plot, double width, double height, Projection proj)76 public Visualization makeVisualization(VisualizerContext context, VisualizationTask task, VisualizationPlot plot, double width, double height, Projection proj) { 77 return new Instance(context, task, plot, width, height); 78 } 79 80 @Override allowThumbnails(VisualizationTask task)81 public boolean allowThumbnails(VisualizationTask task) { 82 // Don't use thumbnails 83 return false; 84 } 85 86 /** 87 * Instance 88 * 89 * @author Erich Schubert 90 * 91 * @navhas - visualizes 1 SimilarityMatrix 92 */ 93 public class Instance extends AbstractVisualization { 94 /** 95 * The actual pixmap result. 96 */ 97 private SimilarityMatrix result; 98 99 /** 100 * Constructor. 101 * 102 * @param context Visualizer context 103 * @param task Visualization task 104 * @param plot Plot to draw to 105 * @param width Embedding width 106 * @param height Embedding height 107 */ Instance(VisualizerContext context, VisualizationTask task, VisualizationPlot plot, double width, double height)108 public Instance(VisualizerContext context, VisualizationTask task, VisualizationPlot plot, double width, double height) { 109 super(context, task, plot, width, height); 110 this.result = task.getResult(); 111 addListeners(); 112 } 113 114 @Override fullRedraw()115 public void fullRedraw() { 116 final StyleLibrary style = context.getStyleLibrary(); 117 final double sizex = StyleLibrary.SCALE; 118 final double sizey = StyleLibrary.SCALE * getHeight() / getWidth(); 119 final double margin = style.getSize(StyleLibrary.MARGIN); 120 layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG); 121 final String transform = SVGUtil.makeMarginTransform(getWidth(), getHeight(), sizex, sizey, margin); 122 SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform); 123 124 RenderedImage img = result.getImage(); 125 // is ratio, target ratio 126 double iratio = img.getHeight() / img.getWidth(); 127 double tratio = getHeight() / getWidth(); 128 // We want to place a (iratio, 1.0) object on a (tratio, 1.0) screen. 129 // Both dimensions must fit: 130 double zoom = (iratio >= tratio) ? Math.min(tratio / iratio, 1.0) : Math.max(iratio / tratio, 1.0); 131 132 Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG); 133 SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE); 134 SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, margin * 0.75); 135 SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, margin * 0.75); 136 SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, StyleLibrary.SCALE * zoom * iratio); 137 SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, StyleLibrary.SCALE * zoom); 138 itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, result.getAsFile().toURI().toString()); 139 layer.appendChild(itag); 140 141 // Add object labels 142 final int size = result.getIDs().size(); 143 final double hlsize = StyleLibrary.SCALE * zoom * iratio / size; 144 final double vlsize = StyleLibrary.SCALE * zoom / size; 145 int i = 0; 146 Database database = ResultUtil.findDatabase(context.getHierarchy()); 147 final Relation<String> lrep = DatabaseUtil.guessObjectLabelRepresentation(database); 148 for(DBIDIter id = result.getIDs().iter(); id.valid(); id.advance()) { 149 String label = lrep.get(id); 150 if(label != null) { 151 // Label on horizontal axis 152 final double hlx = margin * 0.75 + hlsize * (i + .8); 153 final double hly = margin * 0.7; 154 Element lbl = svgp.svgText(hlx, hly, label); 155 SVGUtil.setAtt(lbl, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "rotate(-90," + hlx + "," + hly + ")"); 156 SVGUtil.setAtt(lbl, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + hlsize * 0.8); 157 layer.appendChild(lbl); 158 // Label on vertical axis 159 Element lbl2 = svgp.svgText(margin * 0.7, margin * 0.75 + vlsize * (i + .8), label); 160 SVGUtil.setAtt(lbl2, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, SVGConstants.SVG_END_VALUE); 161 SVGUtil.setAtt(lbl2, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + vlsize * 0.8); 162 layer.appendChild(lbl2); 163 } 164 i++; 165 } 166 } 167 } 168 } 169