diff options
Diffstat (limited to 'addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style')
16 files changed, 1837 insertions, 0 deletions
diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/ClassStylingPolicy.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/ClassStylingPolicy.java new file mode 100644 index 00000000..7bc50182 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/ClassStylingPolicy.java @@ -0,0 +1,74 @@ +package de.lmu.ifi.dbs.elki.visualization.style; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; +import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; + +/** + * Styling policy that is based on <em>classes</em>, for example clusters or + * labels. This allows for certain optimizations such as marker reuse, and thus + * is preferred when possible. + * + * @author Erich Schubert + */ +public interface ClassStylingPolicy extends StylingPolicy { + /** + * Get the style number for a particular object + * + * @param id Object ID + * @return Style number + */ + int getStyleForDBID(DBIDRef id); + + /** + * Get the minimum style in use. + * + * @return Style number + */ + int getMinStyle(); + + /** + * Get the maximum style in use. + * + * @return Style number + */ + int getMaxStyle(); + + /** + * Iterate over all objects from a given class. + * + * @param cnum Class number + * @return Iterator over object IDs + */ + DBIDIter iterateClass(int cnum); + + /** + * Get the number of elements in the styling class. + * + * @param cnum Class number + * @return Size of class. + */ + int classSize(int cnum); +}
\ No newline at end of file diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java new file mode 100644 index 00000000..643f4328 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java @@ -0,0 +1,167 @@ +package de.lmu.ifi.dbs.elki.visualization.style; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import gnu.trove.list.array.TIntArrayList; +import gnu.trove.map.TObjectIntMap; +import gnu.trove.map.hash.TObjectIntHashMap; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import de.lmu.ifi.dbs.elki.data.Cluster; +import de.lmu.ifi.dbs.elki.data.Clustering; +import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; +import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; +import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil; +import de.lmu.ifi.dbs.elki.database.ids.DBIDs; +import de.lmu.ifi.dbs.elki.logging.LoggingUtil; +import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary; +import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil; + +/** + * Styling policy based on cluster membership. + * + * @author Erich Schubert + * + */ +// TODO: fast enough? Some other kind of mapping we can use? +public class ClusterStylingPolicy implements ClassStylingPolicy { + /** + * Object IDs + */ + ArrayList<DBIDs> ids; + + /** + * Map from cluster objects to color offsets. + */ + TObjectIntMap<Cluster<?>> cmap; + + /** + * Colors + */ + TIntArrayList colors; + + /** + * Clustering in use. + */ + Clustering<?> clustering; + + /** + * Constructor. + * + * @param clustering Clustering to use. + */ + public ClusterStylingPolicy(Clustering<?> clustering, StyleLibrary style) { + super(); + this.clustering = clustering; + ColorLibrary colorset = style.getColorSet(StyleLibrary.PLOT); + List<? extends Cluster<?>> clusters = clustering.getAllClusters(); + ids = new ArrayList<>(clusters.size()); + colors = new TIntArrayList(clusters.size()); + cmap = new TObjectIntHashMap<>(clusters.size(), .5f, -1); + + Iterator<? extends Cluster<?>> ci = clusters.iterator(); + for (int i = 0; ci.hasNext(); i++) { + Cluster<?> c = ci.next(); + ids.add(DBIDUtil.ensureSet(c.getIDs())); + cmap.put(c, i); + Color col = SVGUtil.stringToColor(colorset.getColor(i)); + if (col != null) { + colors.add(col.getRGB()); + } else { + LoggingUtil.warning("Unrecognized color name: " + colorset.getColor(i)); + } + if (!ci.hasNext()) { + break; + } + } + } + + @Override + public int getStyleForDBID(DBIDRef id) { + for (int i = 0; i < ids.size(); i++) { + if (ids.get(i).contains(id)) { + return i; + } + } + return -1; + } + + @Override + public int getColorForDBID(DBIDRef id) { + for (int i = 0; i < ids.size(); i++) { + if (ids.get(i).contains(id)) { + return colors.get(i); + } + } + return 0; + } + + @Override + public int getMinStyle() { + return 0; + } + + @Override + public int getMaxStyle() { + return ids.size(); + } + + @Override + public DBIDIter iterateClass(int cnum) { + return ids.get(cnum).iter(); + } + + @Override + public int classSize(int cnum) { + return ids.get(cnum).size(); + } + + /** + * Get the clustering used by this styling policy + * + * @return Clustering in use + */ + public Clustering<?> getClustering() { + return clustering; + } + + /** + * Get the style number for a cluster. + * + * @param c Cluster + * @return Style number + */ + public int getStyleForCluster(Cluster<?> c) { + return cmap.get(c); + } + + @Override + public String getMenuName() { + return clustering.getLongName(); + } +} diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java new file mode 100644 index 00000000..c8440067 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java @@ -0,0 +1,344 @@ +package de.lmu.ifi.dbs.elki.visualization.style; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import de.lmu.ifi.dbs.elki.logging.Logging; +import de.lmu.ifi.dbs.elki.utilities.FileUtil; +import de.lmu.ifi.dbs.elki.utilities.FormatUtil; +import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException; +import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary; +import de.lmu.ifi.dbs.elki.visualization.colors.ListBasedColorLibrary; +import de.lmu.ifi.dbs.elki.visualization.style.lines.LineStyleLibrary; +import de.lmu.ifi.dbs.elki.visualization.style.lines.SolidLineStyleLibrary; +import de.lmu.ifi.dbs.elki.visualization.style.marker.MarkerLibrary; +import de.lmu.ifi.dbs.elki.visualization.style.marker.PrettyMarkers; + +/** + * Style library loading the parameters from a properties file. + * + * @author Erich Schubert + */ +// TODO: also use Caching for String values? +public class PropertiesBasedStyleLibrary implements StyleLibrary { + /** + * Logger + */ + private static final Logging LOG = Logging.getLogger(PropertiesBasedStyleLibrary.class); + + /** + * Name of the default color scheme. + */ + public static final String DEFAULT_SCHEME_NAME = "Default"; + + /** + * File name of the default color scheme. + */ + public static final String DEFAULT_SCHEME_FILENAME = "default"; + + /** + * File extension + */ + public static final String DEFAULT_PROPERTIES_EXTENSION = ".properties"; + + /** + * Default properties path + */ + private static final String DEFAULT_PROPERTIES_PATH = PropertiesBasedStyleLibrary.class.getPackage().getName().replace('.', File.separatorChar) + File.separatorChar; + + /** + * Separator for lists. + */ + public static final String LIST_SEPARATOR = ","; + + /** + * Property string for the line style library + */ + public static final String PROP_LINES_LIBRARY = "lines-library"; + + /** + * Property string for the marker style library + */ + public static final String PROP_MARKER_LIBRARY = "marker-library"; + + /** + * Properties file to use. + */ + private Properties properties; + + /** + * Style scheme name + */ + private String name; + + /** + * Cache + */ + private Map<String, Object> cache = new HashMap<>(); + + /** + * Line style library to use + */ + private LineStyleLibrary linelib = null; + + /** + * Marker library to use + */ + private MarkerLibrary markerlib = null; + + /** + * Constructor without a properties file name. + */ + public PropertiesBasedStyleLibrary() { + this(DEFAULT_SCHEME_FILENAME, DEFAULT_SCHEME_NAME); + } + + /** + * Constructor with a given file name. + * + * @param filename Name of properties file. + * @param name NAme for this style + */ + public PropertiesBasedStyleLibrary(String filename, String name) { + this.properties = new Properties(); + this.name = name; + InputStream stream = null; + try { + stream = FileUtil.openSystemFile(filename); + } catch (FileNotFoundException e) { + try { + stream = FileUtil.openSystemFile(filename + DEFAULT_PROPERTIES_EXTENSION); + } catch (FileNotFoundException e2) { + try { + stream = FileUtil.openSystemFile(DEFAULT_PROPERTIES_PATH + filename + DEFAULT_PROPERTIES_EXTENSION); + } catch (FileNotFoundException e3) { + throw new AbortException("Could not find style scheme file '" + filename + "' for scheme '" + name + "'!"); + } + } + } + try { + properties.load(stream); + } catch (Exception e) { + throw new AbortException("Error loading properties file " + filename + ".\n", e); + } + } + + /** + * Get the style scheme name. + * + * @return the name + */ + protected String getName() { + return name; + } + + /** + * Get a value from the cache (to avoid repeated parsing) + * + * @param <T> Type + * @param prefix Tree name + * @param postfix Property name + * @param cls Class restriction + * @return Resulting value + */ + private <T> T getCached(String prefix, String postfix, Class<T> cls) { + return cls.cast(cache.get(prefix + '.' + postfix)); + } + + /** + * Set a cache value + * + * @param <T> Type + * @param prefix Tree name + * @param postfix Property name + * @param data Data + */ + private <T> void setCached(String prefix, String postfix, T data) { + cache.put(prefix + '.' + postfix, data); + } + + /** + * Retrieve the property value for a particular path + type pair. + * + * @param prefix Path + * @param postfix Type + * @return Value + */ + protected String getPropertyValue(String prefix, String postfix) { + String ret = properties.getProperty(prefix + "." + postfix); + if (ret != null) { + // logger.debugFine("Found property: "+prefix + "." + + // postfix+" for "+prefix); + return ret; + } + int pos = prefix.length(); + while (pos > 0) { + pos = prefix.lastIndexOf('.', pos - 1); + if (pos <= 0) { + break; + } + ret = properties.getProperty(prefix.substring(0, pos) + '.' + postfix); + if (ret != null) { + // logger.debugFine("Found property: "+prefix.substring(0, pos) + "." + + // postfix+" for "+prefix); + return ret; + } + } + ret = properties.getProperty(postfix); + if (ret != null) { + // logger.debugFine("Found property: "+postfix+" for "+prefix); + return ret; + } + return null; + } + + @Override + public String getColor(String key) { + return getPropertyValue(key, COLOR); + } + + @Override + public String getBackgroundColor(String key) { + return getPropertyValue(key, BACKGROUND_COLOR); + } + + @Override + public String getTextColor(String key) { + return getPropertyValue(key, TEXT_COLOR); + } + + @Override + public ColorLibrary getColorSet(String key) { + ColorLibrary cl = getCached(key, COLORSET, ColorLibrary.class); + if (cl == null) { + String[] colors = getPropertyValue(key, COLORSET).split(LIST_SEPARATOR); + cl = new ListBasedColorLibrary(colors, "Default"); + setCached(key, COLORSET, cl); + } + return cl; + } + + @Override + public double getLineWidth(String key) { + Double lw = getCached(key, LINE_WIDTH, Double.class); + if (lw == null) { + try { + lw = Double.valueOf(FormatUtil.parseDouble(getPropertyValue(key, LINE_WIDTH)) * SCALE); + } catch (NullPointerException e) { + throw new AbortException("Missing/invalid value in style library: " + key + '.' + LINE_WIDTH); + } + } + return lw.doubleValue(); + } + + @Override + public double getTextSize(String key) { + Double lw = getCached(key, TEXT_SIZE, Double.class); + if (lw == null) { + try { + lw = Double.valueOf(FormatUtil.parseDouble(getPropertyValue(key, TEXT_SIZE)) * SCALE); + } catch (NullPointerException e) { + throw new AbortException("Missing/invalid value in style library: " + key + '.' + TEXT_SIZE); + } + } + return lw.doubleValue(); + } + + @Override + public String getFontFamily(String key) { + return getPropertyValue(key, FONT_FAMILY); + } + + @Override + public double getSize(String key) { + Double lw = getCached(key, GENERIC_SIZE, Double.class); + if (lw == null) { + try { + lw = Double.valueOf(FormatUtil.parseDouble(getPropertyValue(key, GENERIC_SIZE)) * SCALE); + } catch (NullPointerException e) { + throw new AbortException("Missing/invalid value in style library: " + key + '.' + GENERIC_SIZE); + } + } + return lw.doubleValue(); + } + + @Override + public double getOpacity(String key) { + Double lw = getCached(key, OPACITY, Double.class); + if (lw == null) { + try { + lw = Double.valueOf(FormatUtil.parseDouble(getPropertyValue(key, OPACITY))); + } catch (NullPointerException e) { + throw new AbortException("Missing/invalid value in style library: " + key + '.' + OPACITY); + } + } + return lw.doubleValue(); + } + + @Override + public LineStyleLibrary lines() { + if (linelib == null) { + String libname = properties.getProperty(PROP_LINES_LIBRARY, SolidLineStyleLibrary.class.getName()); + try { + Class<?> cls; + try { + cls = Class.forName(libname); + } catch (ClassNotFoundException e) { + cls = Class.forName(LineStyleLibrary.class.getPackage().getName() + '.' + libname); + } + linelib = (LineStyleLibrary) cls.getConstructor(StyleLibrary.class).newInstance(this); + } catch (Exception e) { + LOG.exception(e); + linelib = new SolidLineStyleLibrary(this); + } + } + return linelib; + } + + @Override + public MarkerLibrary markers() { + if (markerlib == null) { + String libname = properties.getProperty(PROP_MARKER_LIBRARY, PrettyMarkers.class.getName()); + try { + Class<?> cls; + try { + cls = Class.forName(libname); + } catch (ClassNotFoundException e) { + cls = Class.forName(MarkerLibrary.class.getPackage().getName() + '.' + libname); + } + markerlib = (MarkerLibrary) cls.getConstructor(StyleLibrary.class).newInstance(this); + } catch (Exception e) { + LOG.exception(e); + markerlib = new PrettyMarkers(this); + } + } + return markerlib; + } +} diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/SingleObjectsStylingPolicy.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/SingleObjectsStylingPolicy.java new file mode 100644 index 00000000..0e26bf80 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/SingleObjectsStylingPolicy.java @@ -0,0 +1,34 @@ +package de.lmu.ifi.dbs.elki.visualization.style; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +/** + * Styling policy based on assigning objects individual colors. + * + * @author Erich Schubert + */ +public interface SingleObjectsStylingPolicy extends StylingPolicy { + // TODO: finish and use, e.g. for outliers and density visualization + // TODO: do we need anything here beyond "not a class styling policy"? +} diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/StyleLibrary.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/StyleLibrary.java new file mode 100755 index 00000000..5ca93be6 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/StyleLibrary.java @@ -0,0 +1,277 @@ +package de.lmu.ifi.dbs.elki.visualization.style; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary; +import de.lmu.ifi.dbs.elki.visualization.style.lines.LineStyleLibrary; +import de.lmu.ifi.dbs.elki.visualization.style.marker.MarkerLibrary; + +/** + * Style library interface. A style library allows the user to customize the + * visual rendering, for example for print media or screen presentation without + * having to change program code. + * + * @author Erich Schubert + * + * @apiviz.composedOf ColorLibrary + * @apiviz.composedOf LineStyleLibrary + * @apiviz.composedOf MarkerLibrary + */ +public interface StyleLibrary { + /** + * Default + */ + final static String DEFAULT = ""; + + /** + * Page + */ + final static String PAGE = "page"; + + /** + * Plot + */ + final static String PLOT = "plot"; + + /** + * Axis + */ + final static String AXIS = "axis"; + + /** + * Axis tick + */ + final static String AXIS_TICK = "axis.tick"; + + /** + * Axis minor tick + */ + final static String AXIS_TICK_MINOR = "axis.tick.minor"; + + /** + * Axis label + */ + final static String AXIS_LABEL = "axis.label"; + + /** + * Key + */ + final static String KEY = "key"; + + /** + * Clusterorder + */ + final static String CLUSTERORDER = "plot.clusterorder"; + + /** + * Margin + */ + final static String MARGIN = "margin"; + + /** + * Bubble size + */ + final static String BUBBLEPLOT = "plot.bubble"; + + /** + * Marker size + */ + final static String MARKERPLOT = "plot.marker"; + + /** + * Dot size + */ + final static String DOTPLOT = "plot.dot"; + + /** + * Grayed out objects + */ + final static String PLOTGREY = "plot.grey"; + + /** + * Reference points color and size + */ + final static String REFERENCE_POINTS = "plot.referencepoints"; + + /** + * Polygons style + */ + final static String POLYGONS = "plot.polygons"; + + /** + * Selection color and opacity + */ + final static String SELECTION = "plot.selection"; + + /** + * Selection color and opacity during selecting process + */ + final static String SELECTION_ACTIVE = "plot.selection.active"; + + /** + * Scaling constant. Keep in sync with + * {@link de.lmu.ifi.dbs.elki.visualization.projections.Projection#SCALE} + */ + public static final double SCALE = 100.0; + + /* ** Property types ** */ + /** + * Color + */ + final static String COLOR = "color"; + + /** + * Background color + */ + final static String BACKGROUND_COLOR = "background-color"; + + /** + * Text color + */ + final static String TEXT_COLOR = "text-color"; + + /** + * Color set + */ + final static String COLORSET = "colorset"; + + /** + * Line width + */ + final static String LINE_WIDTH = "line-width"; + + /** + * Text size + */ + final static String TEXT_SIZE = "text-size"; + + /** + * Font family + */ + final static String FONT_FAMILY = "font-family"; + + /** + * Generic size + */ + final static String GENERIC_SIZE = "size"; + + /** + * Opacity (transparency) + */ + final static String OPACITY = "opacity"; + + /** + * XY curve styling. + */ + static final String XYCURVE = "xycurve"; + + /** + * Retrieve a color for an item + * + * @param name Reference name + * @return color in CSS/SVG valid format: hexadecimal (#aabbcc) or names such + * as "red" + */ + public String getColor(String name); + + /** + * Retrieve background color for an item + * + * @param name Reference name + * @return color in CSS/SVG valid format: hexadecimal (#aabbcc) or names such + * as "red" + */ + public String getBackgroundColor(String name); + + /** + * Retrieve text color for an item + * + * @param name Reference name + * @return color in CSS/SVG valid format: hexadecimal (#aabbcc) or names such + * as "red" + */ + public String getTextColor(String name); + + /** + * Retrieve colorset for an item + * + * @param name Reference name + * @return color library + */ + public ColorLibrary getColorSet(String name); + + /** + * Get line width + * + * @param key Key + * @return line width as double + */ + public double getLineWidth(String key); + + /** + * Get generic size + * + * @param key Key + * @return size as double + */ + public double getSize(String key); + + /** + * Get text size + * + * @param key Key + * @return line width as double + */ + public double getTextSize(String key); + + /** + * Get font family + * + * @param key Key + * @return font family CSS string + */ + public String getFontFamily(String key); + + /** + * Get opacity + * + * @param key Key + * @return size as double + */ + public double getOpacity(String key); + + /** + * Get the line style library to use. + * + * @return line style library + */ + public LineStyleLibrary lines(); + + /** + * Get the marker library to use. + * + * @return marker library + */ + public MarkerLibrary markers(); +}
\ No newline at end of file diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/StylingPolicy.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/StylingPolicy.java new file mode 100644 index 00000000..406b6aff --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/StylingPolicy.java @@ -0,0 +1,48 @@ +package de.lmu.ifi.dbs.elki.visualization.style; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; +import de.lmu.ifi.dbs.elki.visualization.VisualizationItem; + +/** + * Styling policy. + * + * Implementations <em>must</em> implement either {@link ClassStylingPolicy} or + * {@link SingleObjectsStylingPolicy} interfaces, as most visualizers will only + * support these known interfaces. + * + * @author Erich Schubert + */ +public interface StylingPolicy extends VisualizationItem { + /** + * Get the color for an individual object. + * + * Note: if possible, use a class styling policy which can optimize better. + * + * @param id Object ID + * @return Color value + */ + public int getColorForDBID(DBIDRef id); +} diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java new file mode 100644 index 00000000..49b8cc91 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java @@ -0,0 +1,159 @@ +package de.lmu.ifi.dbs.elki.visualization.style.lines; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import org.apache.batik.util.CSSConstants; + +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.style.StyleLibrary; +import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil; + +/** + * Line library using various dashed and dotted line styles. + * + * This library is particularly useful for black and white output. + * + * {@link LineStyleLibrary#FLAG_STRONG} will result in thicker lines. + * + * {@link LineStyleLibrary#FLAG_WEAK} will result in thinner and + * semi-transparent lines. + * + * {@link LineStyleLibrary#FLAG_INTERPOLATED} will result in shorter dashing + * patterns. + * + * @author Erich Schubert + * + * @apiviz.composedOf ColorLibrary + */ +public class DashedLineStyleLibrary implements LineStyleLibrary { + /** + * The style library we use for colors + */ + private ColorLibrary colors; + + /** Dash patterns to regularly use */ + private double[][] dashpatterns = { + // solid, no dashing + {}, + // half-half + { .5, .5 }, + // quarters + { .25, .25, .25, .25 }, + // alternating long-quart + { .75, .25 }, + // dash-dot + { .7, .1, .1, .1 }, }; + + /** Replacement for the solid pattern in 'interpolated' mode */ + private double[] solidreplacement = { .1, .1 }; + + private int dashnum = dashpatterns.length; + + /** + * Color of "uncolored" dots + */ + private String dotcolor; + + /** + * Color of "greyed out" dots + */ + private String greycolor; + + /** + * Constructor + * + * @param style Style library + */ + public DashedLineStyleLibrary(StyleLibrary style) { + super(); + this.colors = style.getColorSet(StyleLibrary.PLOT); + this.dotcolor = style.getColor(StyleLibrary.MARKERPLOT); + this.greycolor = style.getColor(StyleLibrary.PLOTGREY); + } + + @Override + public void formatCSSClass(CSSClass cls, int style, double width, Object... flags) { + if(style == -2) { + cls.setStatement(CSSConstants.CSS_STROKE_PROPERTY, greycolor); + } + else if(style == -1) { + cls.setStatement(CSSConstants.CSS_STROKE_PROPERTY, dotcolor); + } + else { + cls.setStatement(CSSConstants.CSS_STROKE_PROPERTY, colors.getColor(style)); + } + boolean interpolated = false; + // process flavoring flags + for(Object flag : flags) { + if(LineStyleLibrary.FLAG_STRONG.equals(flag)) { + width = width * 1.5; + } + else if(LineStyleLibrary.FLAG_WEAK.equals(flag)) { + cls.setStatement(CSSConstants.CSS_STROKE_OPACITY_PROPERTY, ".50"); + width = width * 0.75; + } + else if(LineStyleLibrary.FLAG_INTERPOLATED.equals(flag)) { + interpolated = true; + } + } + cls.setStatement(CSSConstants.CSS_STROKE_WIDTH_PROPERTY, SVGUtil.fmt(width)); + // handle dashing + int styleflav = (style > 0) ? (style % dashnum) : (-style % dashnum); + if(!interpolated) { + double[] pat = dashpatterns[styleflav]; + assert (pat.length % 2 == 0); + if(pat.length > 0) { + StringBuilder pattern = new StringBuilder(); + for(int i = 0; i < pat.length; i++) { + if(i > 0) { + pattern.append(','); + } + pattern.append(SVGUtil.fmt(pat[i] * width * 30)); + // pattern.append("%"); + } + cls.setStatement(CSSConstants.CSS_STROKE_DASHARRAY_PROPERTY, pattern.toString()); + } + } + else { + double[] pat = dashpatterns[styleflav]; + if(styleflav == 0) { + pat = solidreplacement; + } + assert (pat.length % 2 == 0); + // TODO: add dotting. + if(pat.length > 0) { + StringBuilder pattern = new StringBuilder(); + for(int i = 0; i < pat.length; i++) { + if(i > 0) { + pattern.append(','); + } + pattern.append(SVGUtil.fmt(pat[i] * width)); + // pattern.append("%"); + } + cls.setStatement(CSSConstants.CSS_STROKE_DASHARRAY_PROPERTY, pattern.toString()); + } + } + } +}
\ No newline at end of file diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java new file mode 100644 index 00000000..48212f16 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java @@ -0,0 +1,76 @@ +package de.lmu.ifi.dbs.elki.visualization.style.lines; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import de.lmu.ifi.dbs.elki.visualization.css.CSSClass; + +/** + * Interface to obtain CSS classes for plot lines. + * + * {@code meta} is a set of Objects, usually constants that may or may not be + * used by the {@link LineStyleLibrary} to generate variants of the style. + * + * Predefined meta flags that are usually supported are: + * <dl> + * <dt>{@link #FLAG_STRONG}</dt> + * <dd>Request a "stronger" version of the same style</dd> + * <dt>{@link #FLAG_WEAK}</dt> + * <dd>Request a "weaker" version of the same style</dd> + * <dt>{@link #FLAG_INTERPOLATED}</dt> + * <dd>Request an "interpolated" version of the same style (e.g. lighter or + * dashed)</dd> + * </dl> + * + * @author Erich Schubert + * + * @apiviz.uses CSSClass oneway + */ +public interface LineStyleLibrary { + /** + * Meta flag to request a 'stronger' version of the style + */ + public static final String FLAG_STRONG = "strong"; + + /** + * Meta flag to request a 'weaker' version of the style + */ + public static final String FLAG_WEAK = "weak"; + + /** + * Meta flag to request an 'interpolated' version of the style + */ + public static final String FLAG_INTERPOLATED = "interpolated"; + + /** + * Add the formatting statements to the given CSS class. + * + * Note: this can overwrite some existing properties of the CSS class. + * + * @param cls CSS class to modify + * @param style style number + * @param width line width + * @param meta meta objects to request line variants + */ + public void formatCSSClass(CSSClass cls, int style, double width, Object... meta); +} diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java new file mode 100644 index 00000000..61765f1a --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java @@ -0,0 +1,106 @@ +package de.lmu.ifi.dbs.elki.visualization.style.lines; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import org.apache.batik.util.CSSConstants; + +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.style.StyleLibrary; +import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil; + +/** + * Line style library featuring solid lines for default styles only (combine + * with a color library to obtain enough classes!) + * + * {@link LineStyleLibrary#FLAG_STRONG} will result in thicker lines. + * + * {@link LineStyleLibrary#FLAG_WEAK} will result in thinner and + * semi-transparent lines. + * + * {@link LineStyleLibrary#FLAG_INTERPOLATED} will result in dashed lines. + * + * @author Erich Schubert + * + * @apiviz.composedOf ColorLibrary + */ +public class SolidLineStyleLibrary implements LineStyleLibrary { + /** + * Reference to the color library. + */ + private ColorLibrary colors; + + /** + * Color of "uncolored" dots + */ + private String dotcolor; + + /** + * Color of "greyed out" dots + */ + private String greycolor; + + /** + * Constructor. + * + * @param style Style library to use. + */ + public SolidLineStyleLibrary(StyleLibrary style) { + super(); + this.colors = style.getColorSet(StyleLibrary.PLOT); + this.dotcolor = style.getColor(StyleLibrary.MARKERPLOT); + this.greycolor = style.getColor(StyleLibrary.PLOTGREY); + } + + @Override + public void formatCSSClass(CSSClass cls, int style, double width, Object... flags) { + if(style == -2) { + cls.setStatement(CSSConstants.CSS_STROKE_PROPERTY, greycolor); + } + else if(style == -1) { + cls.setStatement(CSSConstants.CSS_STROKE_PROPERTY, dotcolor); + } + else { + cls.setStatement(CSSConstants.CSS_STROKE_PROPERTY, colors.getColor(style)); + } + boolean interpolated = false; + // process flavoring flags + for(Object flag : flags) { + if(LineStyleLibrary.FLAG_STRONG.equals(flag)) { + width = width * 1.5; + } + else if(LineStyleLibrary.FLAG_WEAK.equals(flag)) { + cls.setStatement(CSSConstants.CSS_STROKE_OPACITY_PROPERTY, ".50"); + width = width * 0.75; + } + else if(LineStyleLibrary.FLAG_INTERPOLATED.equals(flag)) { + interpolated = true; + } + } + cls.setStatement(CSSConstants.CSS_STROKE_WIDTH_PROPERTY, SVGUtil.fmt(width)); + if(interpolated) { + cls.setStatement(CSSConstants.CSS_STROKE_DASHARRAY_PROPERTY, "" + SVGUtil.fmt(width / StyleLibrary.SCALE * 2.) + "," + SVGUtil.fmt(width / StyleLibrary.SCALE * 2.)); + } + } +} diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/package-info.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/package-info.java new file mode 100755 index 00000000..69790fdf --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/lines/package-info.java @@ -0,0 +1,27 @@ +/** + * <p>Generate line styles for plotting in CSS</p> + * + */ +/* + 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 <http://www.gnu.org/licenses/>. + */ +package de.lmu.ifi.dbs.elki.visualization.style.lines;
\ No newline at end of file diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java new file mode 100644 index 00000000..c4078091 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java @@ -0,0 +1,90 @@ +package de.lmu.ifi.dbs.elki.visualization.style.marker; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import org.apache.batik.util.SVGConstants; +import org.w3c.dom.Element; + +import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary; +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; + +/** + * Simple marker library that just draws colored circles at the given + * coordinates. + * + * @author Erich Schubert + * + * @apiviz.composedOf ColorLibrary + */ +public class CircleMarkers implements MarkerLibrary { + /** + * Color library + */ + private ColorLibrary colors; + + /** + * Color of "uncolored" dots + */ + private String dotcolor = "black"; + + /** + * Color of "greyed out" dots + */ + private String greycolor = "gray"; + + /** + * Constructor + * + * @param style Style library to use + */ + public CircleMarkers(StyleLibrary style) { + super(); + this.colors = style.getColorSet(StyleLibrary.MARKERPLOT); + this.dotcolor = style.getColor(StyleLibrary.MARKERPLOT); + this.greycolor = style.getColor(StyleLibrary.PLOTGREY); + } + + /** + * Use a given marker on the document. + */ + @Override + public Element useMarker(SVGPlot plot, Element parent, double x, double y, int stylenr, double size) { + Element marker = plot.svgCircle(x, y, size * .5); + final String col; + if(stylenr == -1) { + col = dotcolor; + } + else if(stylenr == -2) { + col = greycolor; + } + else { + col = colors.getColor(stylenr); + } + SVGUtil.setStyle(marker, SVGConstants.CSS_FILL_PROPERTY + ":" + col); + parent.appendChild(marker); + return marker; + } +}
\ No newline at end of file diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/MarkerLibrary.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/MarkerLibrary.java new file mode 100755 index 00000000..522097aa --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/MarkerLibrary.java @@ -0,0 +1,56 @@ +package de.lmu.ifi.dbs.elki.visualization.style.marker; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import org.w3c.dom.Element; + +import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot; + +/** + * A marker library is a class that can generate and draw various styles of + * markers. Different uses might require different marker libraries (e.g. full + * screen, thumbnail, print) + * + * @author Erich Schubert + * + * @apiviz.uses Element oneway - - «create» + */ +public interface MarkerLibrary { + /** + * Insert a marker at the given coordinates. Markers will be defined in the + * defs part of the document, and then SVG-"use"d at the given coordinates. + * This supposedly is more efficient and significantly reduces file size. + * Symbols will be named "s0", "s1" etc.; these names must not be used by + * other elements in the SVG document! + * + * @param plot Plot to draw on + * @param parent parent node + * @param x coordinate + * @param y coordinate + * @param style style (enumerated) + * @param size size + * @return Element node generated. + */ + public Element useMarker(SVGPlot plot, Element parent, double x, double y, int style, double size); +}
\ No newline at end of file diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java new file mode 100755 index 00000000..a3390a6d --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java @@ -0,0 +1,90 @@ +package de.lmu.ifi.dbs.elki.visualization.style.marker; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import org.apache.batik.util.SVGConstants; +import org.w3c.dom.Element; + +import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary; +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; + +/** + * Simple marker library that just draws colored rectangles at the given + * coordinates. + * + * @author Erich Schubert + * + * @apiviz.composedOf ColorLibrary + */ +public class MinimalMarkers implements MarkerLibrary { + /** + * Color library + */ + private ColorLibrary colors; + + /** + * Color of "uncolored" dots + */ + private String dotcolor = "black"; + + /** + * Color of "greyed out" dots + */ + private String greycolor = "gray"; + + /** + * Constructor + * + * @param style Style library to use + */ + public MinimalMarkers(StyleLibrary style) { + super(); + this.colors = style.getColorSet(StyleLibrary.MARKERPLOT); + this.dotcolor = style.getColor(StyleLibrary.MARKERPLOT); + this.greycolor = style.getColor(StyleLibrary.PLOTGREY); + } + + /** + * Use a given marker on the document. + */ + @Override + public Element useMarker(SVGPlot plot, Element parent, double x, double y, int stylenr, double size) { + Element marker = plot.svgRect(x - size * .5, y - size * .5, size, size); + final String col; + if(stylenr == -1) { + col = dotcolor; + } + else if(stylenr == -2) { + col = greycolor; + } + else { + col = colors.getColor(stylenr); + } + SVGUtil.setStyle(marker, SVGConstants.CSS_FILL_PROPERTY + ":" + col); + parent.appendChild(marker); + return marker; + } +}
\ No newline at end of file diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java new file mode 100755 index 00000000..be99c6b1 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java @@ -0,0 +1,235 @@ +package de.lmu.ifi.dbs.elki.visualization.style.marker; + +/* + 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 <http://www.gnu.org/licenses/>. + */ + +import org.apache.batik.util.SVGConstants; +import org.w3c.dom.Element; + +import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary; +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; + +/** + * Marker library achieving a larger number of styles by combining different + * shapes with different colors. Uses object ID management by SVGPlot. + * + * @author Erich Schubert + * + * @apiviz.composedOf ColorLibrary + */ +public class PrettyMarkers implements MarkerLibrary { + /** + * Color library + */ + private ColorLibrary colors; + + /** + * Default prefix to use. + */ + private static final String DEFAULT_PREFIX = "s"; + + /** + * Prefix for the IDs generated. + */ + private String prefix; + + /** + * Color of "uncolored" dots + */ + private String dotcolor; + + /** + * Color of "greyed out" dots + */ + private String greycolor; + + /** + * Constructor + * + * @param prefix prefix to use. + * @param style style library to use + */ + public PrettyMarkers(String prefix, StyleLibrary style) { + this.prefix = prefix; + this.colors = style.getColorSet(StyleLibrary.MARKERPLOT); + this.dotcolor = style.getColor(StyleLibrary.MARKERPLOT); + this.greycolor = style.getColor(StyleLibrary.PLOTGREY); + } + + /** + * Constructor without prefix argument, will use {@link #DEFAULT_PREFIX} as + * prefix. + * + * @param style Style library to use + */ + public PrettyMarkers(StyleLibrary style) { + this(DEFAULT_PREFIX, style); + } + + /** + * Draw an marker used in scatter plots. If you intend to use the markers + * multiple times, you should consider using the {@link #useMarker} method + * instead, which exploits the SVG features of symbol definition and use + * + * @param plot containing plot + * @param parent parent node + * @param x position + * @param y position + * @param style marker style (enumerated) + * @param size size + */ + public void plotMarker(SVGPlot plot, Element parent, double x, double y, int style, double size) { + assert (parent != null); + if (style == -1) { + plotUncolored(plot, parent, x, y, size); + return; + } + if (style == -2) { + plotGray(plot, parent, x, y, size); + return; + } + // TODO: add more styles. + String colorstr = colors.getColor(style); + String strokestyle = SVGConstants.CSS_STROKE_PROPERTY + ":" + colorstr + ";" + SVGConstants.CSS_STROKE_WIDTH_PROPERTY + ":" + SVGUtil.fmt(size / 6); + + switch(style % 8){ + case 0: { + // + cross + Element line1 = plot.svgLine(x, y - size / 2.2, x, y + size / 2.2); + SVGUtil.setStyle(line1, strokestyle); + parent.appendChild(line1); + Element line2 = plot.svgLine(x - size / 2.2, y, x + size / 2.2, y); + SVGUtil.setStyle(line2, strokestyle); + parent.appendChild(line2); + break; + } + case 1: { + // X cross + Element line1 = plot.svgLine(x - size / 2.828427, y - size / 2.828427, x + size / 2.828427, y + size / 2.828427); + SVGUtil.setStyle(line1, strokestyle); + parent.appendChild(line1); + Element line2 = plot.svgLine(x - size / 2.828427, y + size / 2.828427, x + size / 2.828427, y - size / 2.828427); + SVGUtil.setStyle(line2, strokestyle); + parent.appendChild(line2); + break; + } + case 2: { + // O hollow circle + Element circ = plot.svgCircle(x, y, size / 2.2); + SVGUtil.setStyle(circ, "fill: none;" + strokestyle); + parent.appendChild(circ); + break; + } + case 3: { + // [] hollow rectangle + Element rect = plot.svgRect(x - size / 2.4, y - size / 2.4, size / 1.2, size / 1.2); + SVGUtil.setStyle(rect, "fill: none;" + strokestyle); + parent.appendChild(rect); + break; + } + case 4: { + // <> hollow diamond + Element rect = plot.svgRect(x - size / 2.7, y - size / 2.7, size / 1.35, size / 1.35); + SVGUtil.setStyle(rect, "fill: none;" + strokestyle); + SVGUtil.setAtt(rect, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "rotate(45," + SVGUtil.fmt(x) + "," + SVGUtil.fmt(y) + ")"); + parent.appendChild(rect); + break; + } + case 5: { + // O filled circle + Element circ = plot.svgCircle(x, y, size * .5); + SVGUtil.setStyle(circ, SVGConstants.CSS_FILL_PROPERTY + ":" + colorstr); + parent.appendChild(circ); + break; + } + case 6: { + // [] filled rectangle + Element rect = plot.svgRect(x - size / 2.2, y - size / 2.2, size / 1.1, size / 1.1); + SVGUtil.setStyle(rect, "fill:" + colorstr); + parent.appendChild(rect); + break; + } + case 7: { + // <> filled diamond + Element rect = plot.svgRect(x - size / 2.5, y - size / 2.5, size / 1.25, size / 1.25); + SVGUtil.setStyle(rect, "fill:" + colorstr); + SVGUtil.setAtt(rect, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "rotate(45," + SVGUtil.fmt(x) + "," + SVGUtil.fmt(y) + ")"); + parent.appendChild(rect); + break; + } + } + } + + /** + * Plot a replacement marker when an object is to be plotted as "disabled", usually gray. + * + * @param plot Plot to draw to + * @param parent Parent element + * @param x X position + * @param y Y position + * @param size Size + */ + protected void plotGray(SVGPlot plot, Element parent, double x, double y, double size) { + Element marker = plot.svgCircle(x, y, size * .5); + SVGUtil.setStyle(marker, SVGConstants.CSS_FILL_PROPERTY + ":" + greycolor); + parent.appendChild(marker); + } + + /** + * Plot a replacement marker when no color is set; usually black + * + * @param plot Plot to draw to + * @param parent Parent element + * @param x X position + * @param y Y position + * @param size Size + */ + protected void plotUncolored(SVGPlot plot, Element parent, double x, double y, double size) { + Element marker = plot.svgCircle(x, y, size * .5); + SVGUtil.setStyle(marker, SVGConstants.CSS_FILL_PROPERTY + ":" + dotcolor); + parent.appendChild(marker); + } + + @Override + public Element useMarker(SVGPlot plot, Element parent, double x, double y, int style, double size) { + String id = prefix + style + "_" + size; + Element existing = plot.getIdElement(id); + if(existing == null) { + Element symbol = plot.svgElement(SVGConstants.SVG_SYMBOL_TAG); + SVGUtil.setAtt(symbol, SVGConstants.SVG_ID_ATTRIBUTE, id); + plotMarker(plot, symbol, 2 * size, 2 * size, style, 2 * size); + plot.getDefs().appendChild(symbol); + plot.putIdElement(id, symbol); + } + Element use = plot.svgElement(SVGConstants.SVG_USE_TAG); + use.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, "#" + id); + SVGUtil.setAtt(use, SVGConstants.SVG_X_ATTRIBUTE, x - 2 * size); + SVGUtil.setAtt(use, SVGConstants.SVG_Y_ATTRIBUTE, y - 2 * size); + if(parent != null) { + parent.appendChild(use); + } + return use; + } +}
\ No newline at end of file diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/package-info.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/package-info.java new file mode 100755 index 00000000..293d5a67 --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/marker/package-info.java @@ -0,0 +1,27 @@ +/** + * <p>Draw plot markers</p> + * + */ +/* +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 <http://www.gnu.org/licenses/>. +*/ +package de.lmu.ifi.dbs.elki.visualization.style.marker;
\ No newline at end of file diff --git a/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/package-info.java b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/package-info.java new file mode 100755 index 00000000..90c548cd --- /dev/null +++ b/addons/batikvis/src/main/java/de/lmu/ifi/dbs/elki/visualization/style/package-info.java @@ -0,0 +1,27 @@ +/** + * <p>Style management for ELKI visualizations.</p> + * + */ +/* +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 <http://www.gnu.org/licenses/>. +*/ +package de.lmu.ifi.dbs.elki.visualization.style;
\ No newline at end of file |