/* * #%L * gitools-core * %% * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ package org.gitools.heatmap.header; import com.google.common.base.Function; import com.google.common.collect.Lists; import org.apache.commons.math3.util.FastMath; import org.gitools.heatmap.HeatmapDimension; import org.gitools.heatmap.decorator.DetailsDecoration; import org.gitools.matrix.model.matrix.AnnotationMatrix; import org.gitools.utils.textpattern.TextPattern; import javax.xml.bind.annotation.*; import java.awt.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement public class HierarchicalClusterHeatmapHeader extends HeatmapHeader { public static final String PROPERTY_INTERACTION_LEVEL = "PROPERTY_INTERACTION_LEVEL"; @XmlElementWrapper(name="clusterLevels") @XmlElement(name="levels") private List<HeatmapColoredLabelsHeader> clusterLevels; @XmlTransient private int interactionLevel = -1; @XmlTransient private boolean reportLastInteraction; public static final String PROPERTY_VISIBLE_LEVELS = "visibleLevels"; @XmlTransient private String visibleLevels; @XmlTransient private String visibleLevelsFormat = "(\\d+(\\-\\d+)?(,(?!$))?)+"; public static final String PROPERTY_MAX_LEVELS = "maxLevels"; @XmlTransient private int maxLevels = -1; public static final String PROPERTY_COLOR_PALETTE = "colorPalette"; @XmlElement private String colorPalette = "Default"; @XmlElement private HierarchicalCluster hierarchicalCluster; public HierarchicalClusterHeatmapHeader() { super(); reportLastInteraction = false; } public HierarchicalClusterHeatmapHeader(HeatmapDimension hdim) { super(hdim); clusterLevels = new ArrayList<>(); reportLastInteraction = false; setMargin(1); } public static void createHierarchicalLevelsHeaders(HierarchicalClusterHeatmapHeader hierarchicalHeader, int maxLevels, String annotationPrefix) { int currentLevel = 0; HeatmapDimension clusteringDimension = hierarchicalHeader.getHeatmapDimension(); AnnotationMatrix annotationMatrix = clusteringDimension.getAnnotations(); Map<Integer, List<HierarchicalCluster>> clustersMapPerLevel = new HashMap<>(); List<HierarchicalCluster> children = hierarchicalHeader.getHierarchicalCluster().getChildren(); while (currentLevel < maxLevels) { currentLevel++; List<HierarchicalCluster> nextLevel = new ArrayList<>(); for (HierarchicalCluster cluster : children) { if (!cluster.getChildren().isEmpty()) { for (String identifier : cluster.getIdentifiers()) { annotationMatrix.setAnnotation(identifier, annotationPrefix + currentLevel, cluster.getName()); } } nextLevel.addAll(cluster.getChildren()); } clustersMapPerLevel.put(currentLevel, children); children = nextLevel; if (children.isEmpty()) { currentLevel--; break; } } // Hierarchical clustering headers int depth = FastMath.min(maxLevels, currentLevel); for (int l = depth; l >= 1; l--) { HeatmapColoredLabelsHeader levelHeader = new HeatmapColoredLabelsHeader(clusteringDimension); List<HierarchicalCluster> clusters = clustersMapPerLevel.get(l); List<ColoredLabel> coloredLabels = new ArrayList<>(clusters.size()); for (HierarchicalCluster cluster : clusters) { coloredLabels.add(new ColoredLabel(cluster.getName(), new Color(cluster.getColor()))); } levelHeader.setClusters(coloredLabels); levelHeader.setTitle(annotationPrefix + l); levelHeader.setSize(7); levelHeader.setAnnotationPattern("${" + annotationPrefix + l + "}"); hierarchicalHeader.addLevel(levelHeader); } } public void addLevel(HeatmapColoredLabelsHeader level) { level.setMargin(1); clusterLevels.add(level); } @Override public void init(HeatmapDimension heatmapDimension) { super.init(heatmapDimension); for (HeatmapColoredLabelsHeader levels : clusterLevels) { levels.init(heatmapDimension); } } @Override public void setMargin(int margin) { for (HeatmapColoredLabelsHeader levelHeader : getClusterLevels()) { levelHeader.setMargin(margin); } super.setMargin(margin); } public List<HeatmapColoredLabelsHeader> getClusterLevels() { return clusterLevels; } /* The thickness of the color band */ public int getThickness() { return clusterLevels.size() > 0 ? clusterLevels.get(0).getThickness() : 1; } /* The thickness of the color band */ public void setThickness(int thickness) { for (HeatmapColoredLabelsHeader level : clusterLevels) { level.setThickness(thickness); } } /* Separate different clusters with a grid */ public boolean isSeparationGrid() { return clusterLevels.size() > 0 ? clusterLevels.get(0).isSeparationGrid() : true; } /* Separate different clusters with a grid */ public void setSeparationGrid(boolean separationGrid) { for (HeatmapColoredLabelsHeader level : clusterLevels) { level.setSeparationGrid(separationGrid); } } public boolean isForceLabelColor() { return clusterLevels.size() > 0 ? clusterLevels.get(0).isForceLabelColor() : true; } public void setForceLabelColor(boolean forceLabelColor) { for (HeatmapColoredLabelsHeader level : clusterLevels) { level.setSeparationGrid(forceLabelColor); } } @Override public Font getFont() { return clusterLevels.size() > 0 ? clusterLevels.get(0).getFont() : null; } @Override public void setFont(Font font) { for (HeatmapColoredLabelsHeader level : clusterLevels) { level.setFont(font); } } @Override public int getSize() { size = 0; for (HeatmapColoredLabelsHeader level : clusterLevels) { if (level.isVisible()) { size += level.getSize(); } } return size; } @Override public void populateDetails(List<DetailsDecoration> details, String identifier, boolean selected) { DetailsDecoration desiredDecoration = null; for (HeatmapColoredLabelsHeader level : Lists.reverse(clusterLevels)) { DetailsDecoration decoration = new DetailsDecoration(this.getTitle(), getDescription(), null, null, null); decoration.setReference(this); if (identifier != null) { level.reset(); ColoredLabel cluster = level.getColoredLabel(identifier); Color clusterColor = cluster != null ? cluster.getColor() : getBackgroundColor(); decoration.setBgColor(clusterColor); if (!cluster.getDisplayedLabel().equals("")) { int levelIndex = clusterLevels.size() - clusterLevels.indexOf(level); decoration.setValue("L" + levelIndex + ": " + cluster.getDisplayedLabel()); desiredDecoration = decoration; } } if (isReportLastInteraction() && clusterLevels.indexOf(level) == interactionLevel) { break; } } if (desiredDecoration != null) { desiredDecoration.setSelected(selected); desiredDecoration.setVisible(isVisible()); details.add(desiredDecoration); } } @Override public Function<String, String> getIdentifierTransform() { return null; } public void setInteractionLevel(int interactionLevel) { int old = this.interactionLevel; this.interactionLevel = interactionLevel; firePropertyChange(PROPERTY_INTERACTION_LEVEL, old, interactionLevel); } public HeatmapColoredLabelsHeader getInteractionLevelHeader() { if (interactionLevel < 0) { return null; } return clusterLevels.get(interactionLevel); } public boolean isReportLastInteraction() { return reportLastInteraction; } public void setReportLastInteraction(boolean reportLastInteraction) { this.reportLastInteraction = reportLastInteraction; //firePropertyChange(); } public void setHierarchicalCluster(HierarchicalCluster hierarchicalCluster) { this.hierarchicalCluster = hierarchicalCluster; } public HierarchicalCluster getHierarchicalCluster() { return hierarchicalCluster; } public HierarchicalCluster getHierarchicalCluster(String clusterName) { return hierarchicalCluster.getHierarchicalSubCluster(clusterName); } @Override public int getZoomStepSize() { return clusterLevels.size(); } /** * The height/width */ public void setSize(int size) { int change = 0; if (this.size < size) { change = 1; } else if (this.size > size) { change = -1; } if (change != 0) { for (HeatmapColoredLabelsHeader level: clusterLevels) { if (level.getSize() + change < 3) { continue; } level.setSize(level.getSize() + change); } } int old = this.size; this.size = getSize(); firePropertyChange(PROPERTY_SIZE, old, this.size); } public String getVisibleLevels() { if (visibleLevels == null) { visibleLevels = generateVisibleLevelsString(); } return visibleLevels; } public void setVisibleLevels(String visibleLevels) { Pattern p = Pattern.compile(visibleLevelsFormat); Matcher m = p.matcher(visibleLevels); if (m.matches()) { //System.out.println("Valid pattern: " + visibleLevels); String old = this.visibleLevels; this.visibleLevels = visibleLevels; setLevelHeadersVisibility(visibleLevels); firePropertyChange(PROPERTY_VISIBLE_LEVELS, old, visibleLevels); } } public int getMaxLevels() { if (maxLevels == -1) { maxLevels = getClusterLevels().size(); } return maxLevels; } public void setMaxLevels(int maxLevels) { int old = this.maxLevels; if (maxLevels < 10) { maxLevels = 10; } if (maxLevels != this.maxLevels) { String annoPrefix = getClusterLevels().get(1).getTitle().split(" L")[0]; removeMetadata(); HierarchicalClusterHeatmapHeader.createHierarchicalLevelsHeaders(this, maxLevels, annoPrefix); this.maxLevels = maxLevels; firePropertyChange(PROPERTY_MAX_LEVELS, old, maxLevels); } } public String getColorPalette() { if (colorPalette == null) { colorPalette = "Default"; } return colorPalette; } public void setColorPalette(String colorPalette) { String old = this.colorPalette; this.colorPalette = colorPalette; if (!old.equals(colorPalette)) { // repaint String annoPrefix = getClusterLevels().get(1).getTitle().split("1")[0]; removeMetadata(); String oldname = this.hierarchicalCluster.getName(); hierarchicalCluster.setName(""); HierarchicalClusterNamer.nameClusters(this.hierarchicalCluster, colorPalette); hierarchicalCluster.setName(oldname); HierarchicalClusterHeatmapHeader.createHierarchicalLevelsHeaders(this, maxLevels, annoPrefix); firePropertyChange(PROPERTY_COLOR_PALETTE, old, colorPalette); } } private void setLevelHeadersVisibility(String visibleLevels) { List<Integer> visibleList = new ArrayList<>(); for (String section : visibleLevels.split(",")) { if (!section.contains("-")) { visibleList.add(Integer.parseInt(section)); } else { int start = Integer.parseInt(section.split("-")[0]); int end = Integer.parseInt(section.split("-")[1]); for (int i = start; i <= end; i++) { visibleList.add(i); } } } int level = 0; for (HeatmapColoredLabelsHeader levelHeader : Lists.reverse(getClusterLevels())) { level++; boolean isVisible = visibleList.contains(level); levelHeader.setVisible(isVisible); } } private String generateVisibleLevelsString() { int level = 1; String visibleLevels = ""; int lastReportedLevel = -1; int lastVisible = -1; for (HeatmapColoredLabelsHeader labelsHeader : getClusterLevels()) { if (labelsHeader.isVisible() && lastReportedLevel == -1) { visibleLevels = Integer.toString(level); lastReportedLevel = level; lastVisible = level; } else if (labelsHeader.isVisible()) { lastVisible = level; } else { if (lastVisible == lastReportedLevel) { visibleLevels += ","; } else { visibleLevels += "-" + Integer.toString(lastVisible) + ","; } } level++; } // after last loop if (lastVisible != lastReportedLevel) { visibleLevels += "-" + Integer.toString(lastVisible); } return visibleLevels; } public void removeMetadata() { AnnotationMatrix annotation = getHeatmapDimension().getAnnotations(); for (HeatmapColoredLabelsHeader level : getClusterLevels()) { //annotation.removeAnnotations(level.getAnnotationPattern()); for (TextPattern.Token token : new TextPattern(level.getAnnotationPattern()).getTokens()) { if (token instanceof TextPattern.VariableToken) { annotation.removeAnnotations(((TextPattern.VariableToken) token).getVariableName()); } } } clusterLevels.clear(); annotation.getLabels(); } }