diff options
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java')
-rw-r--r-- | src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java new file mode 100644 index 00000000..8ce6ad67 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java @@ -0,0 +1,185 @@ +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 org.apache.batik.util.SVGConstants; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.events.Event; +import org.w3c.dom.events.EventListener; +import org.w3c.dom.events.EventTarget; + +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; +import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil; + +/** + * General base class for a tooltip visualizer. + * + * @author Erich Schubert + */ +// TODO: can we improve performance by not adding as many hovers? +public abstract class AbstractTooltipVisualization extends AbstractScatterplotVisualization implements DataStoreListener { + /** + * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc. + */ + public static final String TOOLTIP_HIDDEN = "tooltip_hidden"; + + /** + * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc. + */ + public static final String TOOLTIP_VISIBLE = "tooltip_visible"; + + /** + * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc. + */ + public static final String TOOLTIP_STICKY = "tooltip_sticky"; + + /** + * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc. + */ + 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); + } + + @Override + public void destroy() { + super.destroy(); + context.removeDataStoreListener(this); + } + + @Override + public void redraw() { + setupCSS(svgp); + + double dotsize = context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT); + + 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.svgRect(v[0] - dotsize, v[1] - dotsize, 2 * dotsize, 2 * dotsize); + SVGUtil.addCSSClass(area, TOOLTIP_AREA); + + EventTarget targ = (EventTarget) area; + targ.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, hoverer, false); + targ.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, hoverer, false); + targ.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, hoverer, false); + + // NOTE: do not change the sequence in which these are inserted! + layer.appendChild(area); + layer.appendChild(tooltip); + } + } + + abstract protected Element makeTooltip(DBID id, double x, double y, double dotsize); + + /** + * Handle the hover events. + * + * @param evt Event. + */ + protected void handleHoverEvent(Event evt) { + if(evt.getTarget() instanceof Element) { + Element e = (Element) evt.getTarget(); + Node next = e.getNextSibling(); + if(next != null && next instanceof Element) { + toggleTooltip((Element) next, evt.getType()); + } + else { + LoggingUtil.warning("Tooltip sibling not found."); + } + } + else { + LoggingUtil.warning("Got event for non-Element?!?"); + } + } + + /** + * Toggle the Tooltip of an element. + * + * @param elem Element + * @param type Event type + */ + protected void toggleTooltip(Element elem, String type) { + String csscls = elem.getAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE); + if(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE.equals(type)) { + if(TOOLTIP_HIDDEN.equals(csscls)) { + SVGUtil.setAtt(elem, SVGConstants.SVG_CLASS_ATTRIBUTE, TOOLTIP_VISIBLE); + } + } + else if(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE.equals(type)) { + if(TOOLTIP_VISIBLE.equals(csscls)) { + SVGUtil.setAtt(elem, SVGConstants.SVG_CLASS_ATTRIBUTE, TOOLTIP_HIDDEN); + } + } + else if(SVGConstants.SVG_CLICK_EVENT_TYPE.equals(type)) { + if(TOOLTIP_STICKY.equals(csscls)) { + SVGUtil.setAtt(elem, SVGConstants.SVG_CLASS_ATTRIBUTE, TOOLTIP_HIDDEN); + } + if(TOOLTIP_HIDDEN.equals(csscls) || TOOLTIP_VISIBLE.equals(csscls)) { + SVGUtil.setAtt(elem, SVGConstants.SVG_CLASS_ATTRIBUTE, TOOLTIP_STICKY); + } + } + } + + /** + * Registers the Tooltip-CSS-Class at a SVGPlot. + * + * @param svgp the SVGPlot to register the Tooltip-CSS-Class. + */ + abstract protected void setupCSS(SVGPlot svgp); + + @Override + public void resultChanged(Result current) { + if(sample == current) { + synchronizedRedraw(); + } + } +}
\ No newline at end of file |