package org.baderlab.csplugins.enrichmentmap.style; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_LABEL_TRANSPARENCY; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_LINE_TYPE; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_STROKE_UNSELECTED_PAINT; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_TRANSPARENCY; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_UNSELECTED_PAINT; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_WIDTH; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NETWORK_BACKGROUND_PAINT; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_BORDER_PAINT; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_BORDER_TRANSPARENCY; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_BORDER_WIDTH; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_FILL_COLOR; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_LABEL; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_LABEL_TRANSPARENCY; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_SHAPE; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_SIZE; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_TOOLTIP; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_TRANSPARENCY; import static org.cytoscape.view.presentation.property.NodeShapeVisualProperty.DIAMOND; import static org.cytoscape.view.presentation.property.NodeShapeVisualProperty.ELLIPSE; import static org.cytoscape.view.presentation.property.NodeShapeVisualProperty.RECTANGLE; import java.awt.Color; import java.awt.Paint; import java.util.List; import java.util.stream.Collectors; import org.baderlab.csplugins.enrichmentmap.CytoscapeServiceModule.Continuous; import org.baderlab.csplugins.enrichmentmap.CytoscapeServiceModule.Discrete; import org.baderlab.csplugins.enrichmentmap.CytoscapeServiceModule.Passthrough; import org.baderlab.csplugins.enrichmentmap.model.AbstractDataSet; import org.baderlab.csplugins.enrichmentmap.model.EMDataSet; import org.baderlab.csplugins.enrichmentmap.model.EMSignatureDataSet; import org.baderlab.csplugins.enrichmentmap.model.EnrichmentMap; import org.cytoscape.event.CyEventHelper; import org.cytoscape.model.CyEdge; import org.cytoscape.model.CyNetwork; import org.cytoscape.model.CyNode; import org.cytoscape.view.model.CyNetworkView; import org.cytoscape.view.model.View; import org.cytoscape.view.model.VisualLexicon; import org.cytoscape.view.model.VisualProperty; import org.cytoscape.view.presentation.RenderingEngineManager; import org.cytoscape.view.presentation.customgraphics.CyCustomGraphics2; import org.cytoscape.view.presentation.property.BasicVisualLexicon; import org.cytoscape.view.presentation.property.LineTypeVisualProperty; import org.cytoscape.view.presentation.property.values.LineType; import org.cytoscape.view.presentation.property.values.NodeShape; import org.cytoscape.view.vizmap.VisualMappingFunction; import org.cytoscape.view.vizmap.VisualMappingFunctionFactory; import org.cytoscape.view.vizmap.VisualStyle; import org.cytoscape.view.vizmap.events.VisualStyleChangeRecord; import org.cytoscape.view.vizmap.events.VisualStyleChangedEvent; import org.cytoscape.view.vizmap.mappings.BoundaryRangeValues; import org.cytoscape.view.vizmap.mappings.ContinuousMapping; import org.cytoscape.view.vizmap.mappings.ContinuousMappingPoint; import org.cytoscape.view.vizmap.mappings.DiscreteMapping; import org.cytoscape.view.vizmap.mappings.PassthroughMapping; import org.jcolorbrewer.ColorBrewer; import com.google.inject.Inject; public class EMStyleBuilder { public final static String DEFAULT_NAME_SUFFIX = "Visual_Style"; // TEMPORARY probably won't be called 'MasterMap' in the final version public final static String COMBINED = "Combined"; public final static Integer DEF_NODE_TRANSPARENCY = 220; public final static Integer FILTERED_OUT_NODE_TRANSPARENCY = 40; private final static double MIN_NODE_SIZE = 20.0; private final static double MAX_NODE_SIZE = 60.0; public static final double DEF_NODE_BORDER_WIDTH = 1.0; public final static Integer DEF_EDGE_TRANSPARENCY = 200; public final static Integer FILTERED_OUT_EDGE_TRANSPARENCY = 10; private static final NodeShape SIGNATURE_NODE_SHAPE = DIAMOND; public static class Columns { // Common attributes that apply to the entire network public static final ColumnDescriptor<String> NODE_NAME = new ColumnDescriptor<>("Name", String.class); public static final ColumnDescriptor<String> NODE_GS_DESCR = new ColumnDescriptor<>("GS_DESCR", String.class); public static final ColumnDescriptor<String> NODE_GS_TYPE = new ColumnDescriptor<>("GS_Type", String.class); public static final String NODE_GS_TYPE_ENRICHMENT = "ENR"; public static final String NODE_GS_TYPE_SIGNATURE = "SIG"; public static final ColumnDescriptor<String> NODE_FORMATTED_NAME = new ColumnDescriptor<>("Formatted_name", String.class); public static final ColumnListDescriptor<String> NODE_GENES = new ColumnListDescriptor<>("Genes", String.class); public static final ColumnDescriptor<Integer> NODE_GS_SIZE = new ColumnDescriptor<>("gs_size", Integer.class); // Per-DataSet attributes // GSEA attributes public static final ColumnDescriptor<Double> NODE_PVALUE = new ColumnDescriptor<>("pvalue", Double.class); public static final ColumnDescriptor<Double> NODE_FDR_QVALUE = new ColumnDescriptor<>("fdr_qvalue", Double.class); public static final ColumnDescriptor<Double> NODE_FWER_QVALUE = new ColumnDescriptor<>("fwer_qvalue", Double.class); public static final ColumnDescriptor<Double> NODE_ES = new ColumnDescriptor<>("ES", Double.class); public static final ColumnDescriptor<Double> NODE_NES = new ColumnDescriptor<>("NES", Double.class); public static final ColumnDescriptor<Double> NODE_COLOURING = new ColumnDescriptor<>("Colouring", Double.class); // Post-analysis Node attributes public static final ColumnListDescriptor<String> NODE_ENR_GENES = new ColumnListDescriptor<>("Enrichment_Genes", String.class); // Per-DataSet attributes // Edge attributes public static final ColumnDescriptor<Double> EDGE_SIMILARITY_COEFF = new ColumnDescriptor<>("similarity_coefficient", Double.class); public static final ColumnDescriptor<Integer> EDGE_OVERLAP_SIZE = new ColumnDescriptor<>("Overlap_size", Integer.class); public static final ColumnListDescriptor<String> EDGE_OVERLAP_GENES = new ColumnListDescriptor<>("Overlap_genes", String.class); // Multi-edge case public static final ColumnDescriptor<String> EDGE_DATASET = new ColumnDescriptor<>("Data Set", String.class); public static final String EDGE_DATASET_VALUE_COMPOUND = "compound"; public static final String EDGE_DATASET_VALUE_SIG = "signature"; // post-analysis edges public static final String EDGE_INTERACTION_VALUE_SIG = "sig"; // post-analysis edges // Post-analysis Edge Attributes public static final ColumnDescriptor<Double> EDGE_HYPERGEOM_PVALUE = new ColumnDescriptor<>("Overlap_Hypergeom_pVal", Double.class); public static final ColumnDescriptor<Double> EDGE_HYPERGEOM_CUTOFF = new ColumnDescriptor<>("Overlap_Hypergeom_cutoff", Double.class); public static final ColumnDescriptor<Integer> EDGE_HYPERGEOM_U = new ColumnDescriptor<>("HyperGeom_N_Universe", Integer.class); public static final ColumnDescriptor<Integer> EDGE_HYPERGEOM_N = new ColumnDescriptor<>("HyperGeom_n_Sig_Universe", Integer.class); public static final ColumnDescriptor<Integer> EDGE_HYPERGEOM_K = new ColumnDescriptor<>("k_Intersection", Integer.class); public static final ColumnDescriptor<Integer> EDGE_HYPERGEOM_M = new ColumnDescriptor<>("m_Enr_Genes", Integer.class); public static final ColumnDescriptor<Double> EDGE_MANN_WHIT_TWOSIDED_PVALUE = new ColumnDescriptor<>("Overlap_Mann_Whit_pVal", Double.class); public static final ColumnDescriptor<Double> EDGE_MANN_WHIT_GREATER_PVALUE = new ColumnDescriptor<>("Overlap_Mann_Whit_greater_pVal", Double.class); public static final ColumnDescriptor<Double> EDGE_MANN_WHIT_LESS_PVALUE = new ColumnDescriptor<>("Overlap_Mann_Whit_less_pVal", Double.class); public static final ColumnDescriptor<Double> EDGE_MANN_WHIT_CUTOFF = new ColumnDescriptor<>("Overlap_Mann_Whit_cutoff", Double.class); public static final ColumnDescriptor<String> EDGE_CUTOFF_TYPE = new ColumnDescriptor<>("Overlap_cutoff", String.class); /** Column in edge table that holds the formula */ public static final ColumnDescriptor<Double> EDGE_WIDTH_FORMULA_COLUMN = new ColumnDescriptor<>("Edge_width_formula", Double.class); /** Column in network table that holds the edge parameters */ public static final ColumnDescriptor<String> NETWORK_EDGE_WIDTH_PARAMETERS_COLUMN = new ColumnDescriptor<>("EM_Edge_width_parameters", String.class); public static final ColumnDescriptor<String> NET_REPORT1_DIR = new ColumnDescriptor<>("GSEA_Report_Dataset1_folder", String.class); public static final ColumnDescriptor<String> NET_REPORT2_DIR = new ColumnDescriptor<>("GSEA_Report_Dataset2_folder", String.class); } public static class Colors { // See http://colorbrewer2.org/#type=diverging&scheme=RdBu&n=6 public static final Color DEF_NODE_BORDER_COLOR = new Color(148, 148, 148); public static final Color DEF_NODE_COLOR = new Color(240, 240, 240); public static final Color SIG_NODE_BORDER_COLOR = new Color(239, 138, 98); public static final Color SIG_NODE_COLOR = new Color(253, 219, 199); // See http://colorbrewer2.org/#type=qualitative&scheme=Set2&n=3 public static final Color SIG_EDGE_COLOR = new Color(252, 141, 98); public static final Color COMPOUND_EDGE_COLOR = new Color(102, 194, 165); /* See http://colorbrewer2.org/#type=diverging&scheme=RdBu&n=9 */ public static final Color MAX_PHENOTYPE_1 = new Color(178, 24, 43); public static final Color LIGHTER_PHENOTYPE_1 = new Color(214, 96, 77); public static final Color LIGHTEST_PHENOTYPE_1 = new Color(244, 165, 130); public static final Color OVER_COLOR = new Color(247, 247, 247); public static final Color MAX_PHENOTYPE_2 = new Color(33, 102, 172); public static final Color LIGHTER_PHENOTYPE_2 = new Color(67, 147, 195); public static final Color LIGHTEST_PHENOTYPE_2 = new Color(146, 197, 222); public static final Color LIGHT_GREY = new Color(190, 190, 190); private static final Color BG_COLOR = Color.WHITE; } @Inject private @Continuous VisualMappingFunctionFactory cmFactory; @Inject private @Discrete VisualMappingFunctionFactory dmFactory; @Inject private @Passthrough VisualMappingFunctionFactory pmFactory; @Inject private RenderingEngineManager renderingEngineManager; @Inject private CyEventHelper eventHelper; public static String getStyleName(EnrichmentMap map) { String prefix = map.getParams().getAttributePrefix(); return prefix + DEFAULT_NAME_SUFFIX; } public static NodeShape getGeneSetNodeShape(VisualStyle style) { return style.getDefaultValue(BasicVisualLexicon.NODE_SHAPE); } public static NodeShape getSignatureNodeShape(VisualStyle style) { return SIGNATURE_NODE_SHAPE; } public static NodeShape getDefaultNodeShape(ChartType chartType) { return chartType == null || chartType == ChartType.RADIAL_HEAT_MAP ? ELLIPSE : RECTANGLE; } /** * Updates the whole EM style. */ public void updateProperties(VisualStyle vs, EMStyleOptions options, CyCustomGraphics2<?> chart) { eventHelper.silenceEventSource(vs); try { // Network Properties vs.setDefaultValue(NETWORK_BACKGROUND_PAINT, Colors.BG_COLOR); setEdgeDefaults(vs, options); setEdgePaint(vs, options); setEdgeWidth(vs, options); setEdgeLineType(vs, options); String chartName = chart != null ? chart.getDisplayName() : null; ChartType chartType = ChartType.toChartType(chartName); setNodeDefaults(vs, options, chartType); setNodeShapes(vs, options, chartType); setNodeSize(vs, options, chartType); setNodeBorderColors(vs, options); setNodeColors(vs, options); setNodeLabels(vs, options); setNodeTooltip(vs, options); setNodeChart(vs, chart); if (options.isPublicationReady()) { vs.removeVisualMappingFunction(BasicVisualLexicon.NODE_LABEL); vs.setDefaultValue(BasicVisualLexicon.NODE_LABEL, ""); vs.setDefaultValue(BasicVisualLexicon.NETWORK_BACKGROUND_PAINT, Color.WHITE); } } finally { eventHelper.unsilenceEventSource(vs); eventHelper.addEventPayload(vs, new VisualStyleChangeRecord(), VisualStyleChangedEvent.class); } } public void updateNodeChart(VisualStyle vs, EMStyleOptions options, CyCustomGraphics2<?> chart) { eventHelper.silenceEventSource(vs); try { String chartName = chart != null ? chart.getDisplayName() : null; ChartType chartType = ChartType.toChartType(chartName); setNodeChartDefaults(vs, chartType); setNodeShapes(vs, options, chartType); setNodeSize(vs, options, chartType); setNodeChart(vs, chart); } finally { eventHelper.unsilenceEventSource(vs); eventHelper.addEventPayload(vs, new VisualStyleChangeRecord(), VisualStyleChangedEvent.class); } } @SuppressWarnings({ "rawtypes", "unchecked" }) private void setNodeChart(VisualStyle vs, CyCustomGraphics2<?> chart) { VisualLexicon lexicon = renderingEngineManager.getDefaultVisualLexicon(); VisualProperty customPaint1 = lexicon.lookup(CyNode.class, "NODE_CUSTOMGRAPHICS_1"); if (customPaint1 != null) vs.setDefaultValue(customPaint1, chart); } private void setEdgeDefaults(VisualStyle vs, EMStyleOptions options) { vs.setDefaultValue(EDGE_TRANSPARENCY, DEF_EDGE_TRANSPARENCY); vs.setDefaultValue(EDGE_LABEL_TRANSPARENCY, DEF_EDGE_TRANSPARENCY); } private void setEdgePaint(VisualStyle vs, EMStyleOptions options) { DiscreteMapping<String, Paint> edgePaint = createEdgeColorMapping(options, EDGE_UNSELECTED_PAINT); vs.addVisualMappingFunction(edgePaint); DiscreteMapping<String, Paint> edgeStrokePaint = createEdgeColorMapping(options, EDGE_STROKE_UNSELECTED_PAINT); vs.addVisualMappingFunction(edgeStrokePaint); } private DiscreteMapping<String, Paint> createEdgeColorMapping(EMStyleOptions options, VisualProperty<Paint> vp) { String prefix = options.getAttributePrefix(); String col = Columns.EDGE_DATASET.with(prefix, null); DiscreteMapping<String, Paint> dm = (DiscreteMapping<String, Paint>) dmFactory .createVisualMappingFunction(col, String.class, vp); // Silence events fired by this mapping, or it will fire too many VisualMappingFunctionChangedEvents, // which can be captured by VisualStyle later and cause unnecessary view updates, // even though this mapping has not been set to a style yet // (unfortunately that's just how Cytoscape's event payloads work). eventHelper.silenceEventSource(dm); try { dm.putMapValue(Columns.EDGE_DATASET_VALUE_COMPOUND, Colors.COMPOUND_EDGE_COLOR); dm.putMapValue(Columns.EDGE_DATASET_VALUE_SIG, Colors.SIG_EDGE_COLOR); List<EMDataSet> dataSets = options.getEnrichmentMap().getDataSetList(); final ColorBrewer colorBrewer; // Try colorblind and/or print friendly colours first if (dataSets.size() <= 4) // Try a colorblind safe color scheme first colorBrewer = ColorBrewer.Paired; // http://colorbrewer2.org/#type=qualitative&scheme=Paired&n=4 else if (dataSets.size() <= 5) // Same--more than 5, it adds a RED that can be confused with the edge selection color colorBrewer = ColorBrewer.Paired; // http://colorbrewer2.org/#type=qualitative&scheme=Paired&n=5 else colorBrewer = ColorBrewer.Set3; // http://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 Color[] colors = colorBrewer.getColorPalette(dataSets.size()); // Do not use the filtered data sets here, because we don't want edge colours changing when filtering for (int i = 0; i < dataSets.size(); i++) { EMDataSet ds = dataSets.get(i); Color color = colors[i]; dm.putMapValue(ds.getName(), color); ds.setColor(color); } } finally { eventHelper.unsilenceEventSource(dm); } return dm; } private void setEdgeWidth(VisualStyle vs, EMStyleOptions options) { String prefix = options.getAttributePrefix(); EnrichmentMap map = options.getEnrichmentMap(); if (options.isPostAnalysis()) { // Replace the edge width mapping that was created by EnrichmentMapVisualStyle String widthAttribute = Columns.EDGE_WIDTH_FORMULA_COLUMN.with(prefix, null); PassthroughMapping<Double, Double> edgewidth = (PassthroughMapping<Double, Double>) pmFactory .createVisualMappingFunction(widthAttribute, Double.class, BasicVisualLexicon.EDGE_WIDTH); vs.addVisualMappingFunction(edgewidth); } else { // Continous Mapping - set edge line thickness based on the number of genes in the overlap ContinuousMapping<Double, Double> cm = (ContinuousMapping<Double, Double>) cmFactory .createVisualMappingFunction(Columns.EDGE_SIMILARITY_COEFF.with(prefix, null), Double.class, EDGE_WIDTH); Double underWidth = 0.5; Double minWidth = 1.0; Double maxWidth = 5.0; Double overWidth = 6.0; // Create boundary conditions BoundaryRangeValues<Double> bv4 = new BoundaryRangeValues<>(underWidth, minWidth, minWidth); BoundaryRangeValues<Double> bv5 = new BoundaryRangeValues<>(maxWidth, maxWidth, overWidth); // Silence events fired by this mapping to prevent unnecessary style and view updates eventHelper.silenceEventSource(cm); try { cm.addPoint(map.getParams().getSimilarityCutoff(), bv4); cm.addPoint(1.0, bv5); } finally { eventHelper.unsilenceEventSource(cm); } vs.addVisualMappingFunction(cm); } } private void setEdgeLineType(VisualStyle vs, EMStyleOptions options) { String col = CyEdge.INTERACTION; DiscreteMapping<String, LineType> dm = (DiscreteMapping<String, LineType>) dmFactory .createVisualMappingFunction(col, String.class, EDGE_LINE_TYPE); // Silence events fired by this mapping to prevent unnecessary style and view updates eventHelper.silenceEventSource(dm); try { dm.putMapValue(Columns.EDGE_DATASET_VALUE_COMPOUND, LineTypeVisualProperty.SOLID); dm.putMapValue(Columns.EDGE_INTERACTION_VALUE_SIG, LineTypeVisualProperty.DOT); } finally { eventHelper.unsilenceEventSource(dm); } vs.addVisualMappingFunction(dm); } private void setNodeDefaults(VisualStyle vs, EMStyleOptions options, ChartType chartType) { // Set the default node appearance vs.setDefaultValue(NODE_FILL_COLOR, Colors.DEF_NODE_COLOR); vs.setDefaultValue(NODE_BORDER_PAINT, Colors.DEF_NODE_BORDER_COLOR); vs.setDefaultValue(NODE_BORDER_WIDTH, DEF_NODE_BORDER_WIDTH); vs.setDefaultValue(NODE_TRANSPARENCY, DEF_NODE_TRANSPARENCY); vs.setDefaultValue(NODE_BORDER_TRANSPARENCY, DEF_NODE_TRANSPARENCY); vs.setDefaultValue(NODE_LABEL_TRANSPARENCY, DEF_NODE_TRANSPARENCY); setNodeChartDefaults(vs, chartType); } /** * Sets default node visual properties that can be affected by the chart type. */ private void setNodeChartDefaults(VisualStyle vs, ChartType chartType) { vs.setDefaultValue(NODE_SHAPE, getDefaultNodeShape(chartType)); vs.setDefaultValue(NODE_SIZE, chartType == ChartType.RADIAL_HEAT_MAP ? MIN_NODE_SIZE : (MAX_NODE_SIZE + MIN_NODE_SIZE) / 2); } @SuppressWarnings({ "unchecked", "rawtypes" }) private void setNodeShapes(VisualStyle vs, EMStyleOptions options, ChartType chartType) { String prefix = options.getAttributePrefix(); String columnName = Columns.NODE_GS_TYPE.with(prefix, null); NodeShape enrShape = getDefaultNodeShape(chartType); VisualMappingFunction<?, NodeShape> oldMapping = vs.getVisualMappingFunction(NODE_SHAPE); // This is done for performance optimization only! boolean update = oldMapping instanceof DiscreteMapping == false; if (!update) { // Also test the mapped column name update = !columnName.equals(oldMapping.getMappingColumnName()); if (!update) { // Finally test the mapping values Object enrVal = ((DiscreteMapping) oldMapping).getMapValue(Columns.NODE_GS_TYPE_ENRICHMENT); Object sigVal = ((DiscreteMapping) oldMapping).getMapValue(Columns.NODE_GS_TYPE_SIGNATURE); update = !enrShape.equals(enrVal) || !SIGNATURE_NODE_SHAPE.equals(sigVal); } } if (update) { // Add mapping function for node shape DiscreteMapping<String, NodeShape> dm = (DiscreteMapping<String, NodeShape>) dmFactory .createVisualMappingFunction(columnName, String.class, NODE_SHAPE); // Silence events fired by this mapping to prevent unnecessary style and view updates eventHelper.silenceEventSource(dm); try { dm.putMapValue(Columns.NODE_GS_TYPE_ENRICHMENT, enrShape); dm.putMapValue(Columns.NODE_GS_TYPE_SIGNATURE, SIGNATURE_NODE_SHAPE); } finally { eventHelper.unsilenceEventSource(dm); } vs.addVisualMappingFunction(dm); } } private void setNodeBorderColors(VisualStyle vs, EMStyleOptions options) { String prefix = options.getAttributePrefix(); // Add mapping function for node border color DiscreteMapping<String, Paint> dm = (DiscreteMapping<String, Paint>) dmFactory .createVisualMappingFunction(Columns.NODE_GS_TYPE.with(prefix, null), String.class, NODE_BORDER_PAINT); // Silence events fired by this mapping to prevent unnecessary style and view updates eventHelper.silenceEventSource(dm); try { dm.putMapValue(Columns.NODE_GS_TYPE_ENRICHMENT, Colors.DEF_NODE_BORDER_COLOR); dm.putMapValue(Columns.NODE_GS_TYPE_SIGNATURE, Colors.SIG_NODE_BORDER_COLOR); } finally { eventHelper.unsilenceEventSource(dm); } vs.addVisualMappingFunction(dm); } private void setNodeColors(VisualStyle vs, EMStyleOptions options) { String prefix = options.getAttributePrefix(); List<AbstractDataSet> dataSets = options.getDataSets() .stream() .filter(ds -> ds instanceof EMDataSet) // Ignore Signature Data Sets in charts .collect(Collectors.toList()); if (dataSets.size() == 1) { // Only 1 Data Set? Use node colour instead of charts... EMDataSet ds = (EMDataSet) dataSets.iterator().next(); // Create boundary conditions BoundaryRangeValues<Paint> bv3a = new BoundaryRangeValues<>( Colors.MAX_PHENOTYPE_2, Colors.MAX_PHENOTYPE_2, Colors.MAX_PHENOTYPE_2); BoundaryRangeValues<Paint> bv3b = new BoundaryRangeValues<>( Colors.LIGHTER_PHENOTYPE_2, Colors.LIGHTER_PHENOTYPE_2, Colors.MAX_PHENOTYPE_2); BoundaryRangeValues<Paint> bv3c = new BoundaryRangeValues<>( Colors.LIGHTEST_PHENOTYPE_2, Colors.LIGHTEST_PHENOTYPE_2, Colors.LIGHTER_PHENOTYPE_2); BoundaryRangeValues<Paint> bv3d = new BoundaryRangeValues<>( Colors.LIGHTEST_PHENOTYPE_2, Colors.OVER_COLOR, Colors.OVER_COLOR); BoundaryRangeValues<Paint> bv3e = new BoundaryRangeValues<>( Colors.OVER_COLOR, Colors.OVER_COLOR, Colors.OVER_COLOR); BoundaryRangeValues<Paint> bv3f = new BoundaryRangeValues<>( Colors.OVER_COLOR, Colors.OVER_COLOR, Colors.LIGHTEST_PHENOTYPE_1); BoundaryRangeValues<Paint> bv3g = new BoundaryRangeValues<>( Colors.LIGHTEST_PHENOTYPE_1, Colors.LIGHTEST_PHENOTYPE_1, Colors.LIGHTER_PHENOTYPE_1); BoundaryRangeValues<Paint> bv3h = new BoundaryRangeValues<>( Colors.LIGHTER_PHENOTYPE_1, Colors.LIGHTER_PHENOTYPE_1, Colors.MAX_PHENOTYPE_1); BoundaryRangeValues<Paint> bv3i = new BoundaryRangeValues<>( Colors.MAX_PHENOTYPE_1, Colors.MAX_PHENOTYPE_1, Colors.MAX_PHENOTYPE_1); // Continuous Mapping - set node colour based on the sign of the ES score of the dataset ContinuousMapping<Double, Paint> cm = (ContinuousMapping<Double, Paint>) cmFactory .createVisualMappingFunction(Columns.NODE_COLOURING.with(prefix, ds.getName()), Double.class, BasicVisualLexicon.NODE_FILL_COLOR); // Silence events fired by this mapping to prevent unnecessary style and view updates eventHelper.silenceEventSource(cm); try { // Set the attribute point values associated with the boundary values cm.addPoint(-1.0, bv3a); cm.addPoint(-0.995, bv3b); cm.addPoint(-0.95, bv3c); cm.addPoint(-0.9, bv3d); cm.addPoint(0.0, bv3e); cm.addPoint(0.9, bv3f); cm.addPoint(0.95, bv3g); cm.addPoint(0.995, bv3h); cm.addPoint(1.0, bv3i); } finally { eventHelper.unsilenceEventSource(cm); } vs.addVisualMappingFunction(cm); // Then we need to use bypass to colour the hub nodes (signature genesets) List<EMSignatureDataSet> signatureDataSets = options.getEnrichmentMap().getSignatureSetList(); CyNetworkView netView = options.getNetworkView(); CyNetwork net = netView.getModel(); for (EMSignatureDataSet sds : signatureDataSets) { for (Long suid : sds.getNodeSuids()) { CyNode node = net.getNode(suid); if (node != null) { View<CyNode> nv = netView.getNodeView(node); if (nv != null) nv.setLockedValue(NODE_FILL_COLOR, Colors.SIG_NODE_COLOR); } } } } else { // 2 or more Data Sets? Use simple node colours and charts... // Add mapping function for node fill color DiscreteMapping<String, Paint> dm = (DiscreteMapping<String, Paint>) dmFactory.createVisualMappingFunction( Columns.NODE_GS_TYPE.with(prefix, null), String.class, NODE_FILL_COLOR); // Silence events fired by this mapping to prevent unnecessary style and view updates eventHelper.silenceEventSource(dm); try { dm.putMapValue(Columns.NODE_GS_TYPE_ENRICHMENT, Colors.DEF_NODE_COLOR); dm.putMapValue(Columns.NODE_GS_TYPE_SIGNATURE, Colors.SIG_NODE_COLOR); } finally { eventHelper.unsilenceEventSource(dm); } vs.addVisualMappingFunction(dm); } } private void setNodeLabels(VisualStyle vs, EMStyleOptions options) { String prefix = options.getAttributePrefix(); PassthroughMapping<String, String> nodeLabel = (PassthroughMapping<String, String>) pmFactory .createVisualMappingFunction(Columns.NODE_GS_DESCR.with(prefix, null), String.class, NODE_LABEL); vs.addVisualMappingFunction(nodeLabel); } private void setNodeTooltip(VisualStyle vs, EMStyleOptions options) { String prefix = options.getAttributePrefix(); PassthroughMapping<String, String> nodeLabel = (PassthroughMapping<String, String>) pmFactory .createVisualMappingFunction(Columns.NODE_GS_DESCR.with(prefix ,null), String.class, NODE_TOOLTIP); vs.addVisualMappingFunction(nodeLabel); } @SuppressWarnings("rawtypes") private void setNodeSize(VisualStyle vs, EMStyleOptions options, ChartType chartType) { if (chartType == null || chartType == ChartType.RADIAL_HEAT_MAP) { String prefix = options.getAttributePrefix(); String columnName = Columns.NODE_GS_SIZE.with(prefix, null); int val0 = 10; int val1 = 474; VisualMappingFunction<?, Double> oldMapping = vs.getVisualMappingFunction(NODE_SIZE); // This is done for performance optimization only! boolean update = oldMapping instanceof ContinuousMapping == false; if (!update) { try { // Also test the mapped column name and number of points update = !columnName.equals(oldMapping.getMappingColumnName()) || ((ContinuousMapping) oldMapping).getPointCount() != 2; if (!update) { // And the mapping values ContinuousMappingPoint pt0 = ((ContinuousMapping) oldMapping).getPoint(0); ContinuousMappingPoint pt1 = ((ContinuousMapping) oldMapping).getPoint(1); update = val0 != (Integer) pt0.getValue(); update = update || val1 != (Integer) pt1.getValue(); if (!update) // Finally test the boundary ranges update = MIN_NODE_SIZE != (Double) pt0.getRange().equalValue || MAX_NODE_SIZE != (Double) pt1.getRange().equalValue; } } catch (NullPointerException | ClassCastException e) { update = true; } } if (update) { ContinuousMapping<Integer, Double> cm = (ContinuousMapping<Integer, Double>) cmFactory .createVisualMappingFunction(columnName, Integer.class, NODE_SIZE); BoundaryRangeValues<Double> bv0 = new BoundaryRangeValues<>(MIN_NODE_SIZE, MIN_NODE_SIZE, MIN_NODE_SIZE); BoundaryRangeValues<Double> bv1 = new BoundaryRangeValues<>(MAX_NODE_SIZE, MAX_NODE_SIZE, MAX_NODE_SIZE); // Silence events fired by this mapping to prevent unnecessary style and view updates eventHelper.silenceEventSource(cm); try { cm.addPoint(val0, bv0); cm.addPoint(val1, bv1); } finally { eventHelper.unsilenceEventSource(cm); } vs.addVisualMappingFunction(cm); } } else { vs.removeVisualMappingFunction(NODE_SIZE); } } }