be.ugent.psb.coexpnetviz.layout.CENVLayoutTask.java Source code

Java tutorial

Introduction

Here is the source code for be.ugent.psb.coexpnetviz.layout.CENVLayoutTask.java

Source

package be.ugent.psb.coexpnetviz.layout;

/*
 * #%L
 * CoExpNetViz
 * %%
 * Copyright (C) 2015 PSB/UGent
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 * #L%
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.cytoscape.model.CyEdge;
import org.cytoscape.model.CyNetwork;
import org.cytoscape.model.CyNode;
import org.cytoscape.model.CyRow;
import org.cytoscape.model.CyTable;
import org.cytoscape.view.layout.AbstractLayoutTask;
import org.cytoscape.view.model.CyNetworkView;
import org.cytoscape.view.model.View;
import org.cytoscape.work.TaskMonitor;
import org.cytoscape.work.undo.UndoSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;

import be.ugent.psb.util.Sets;

/**
 * Lay out nodes of a CENV network.
 * 
 * First the network is separated into a grid according to its connected components.
 * 
 * Within each connected component, each partition is laid out as a grid. The baits
 * in the connected component are laid out in the middle as a circle, with other
 * partitions placed around it in a circle. Singleton partitions are grouped together
 * in a grid as if it were a single partition (but the colours will give away they're
 * not really of the same partition).
 * 
 * Within each partition, nodes are sorted by their name. Partitions in the circle of
 * partitions are sorted by their size.
 */
public class CENVLayoutTask extends AbstractLayoutTask {

    private static final String TYPE_ATTRIBUTE = "type"; // 'bait node' or 'family node'
    private static final String PARTITION_ATTRIBUTE = "partition_id";

    private CENVLayoutContext context;

    public CENVLayoutTask(String displayName, CyNetworkView networkView, Set<View<CyNode>> nodesToLayOut,
            CENVLayoutContext context, UndoSupport undo) {
        super(displayName, networkView, nodesToLayOut, PARTITION_ATTRIBUTE, undo);
        this.context = context;
    }

    @Override
    final protected void doLayout(final TaskMonitor taskMonitor) {
        // Check the required node attributes are present (i.e. is it a CENV network?)
        CyTable dataTable = getNetwork().getDefaultNodeTable();
        ensureExists(dataTable, TYPE_ATTRIBUTE);
        ensureExists(dataTable, PARTITION_ATTRIBUTE);

        // Do layout
        Container container = layOutConnectedComponents(getConnectedComponents());
        container.updateView(0, 0);
    }

    private CyNetwork getNetwork() {
        return networkView.getModel();
    }

    private void ensureExists(CyTable dataTable, String nodeAttribute) {
        if (dataTable.getColumn(nodeAttribute) == null) {
            throw new RuntimeException(String.format("Could not find node attribute: %s", nodeAttribute));
        }
    }

    private Set<Set<CyNode>> getConnectedComponents() {
        Set<Set<CyNode>> connectedComponents = new HashSet<>();
        Set<CyNode> unvisited = new HashSet<CyNode>(getNetwork().getNodeList());

        while (!unvisited.isEmpty()) {
            Set<CyNode> connectedComponent = new HashSet<>();
            connectedComponents.add(connectedComponent);

            Set<CyNode> toVisit = new HashSet<>(); // nodes of the connected component that we know we haven't visited yet
            toVisit.add(Iterables.getFirst(unvisited, null));

            while (!toVisit.isEmpty()) {
                CyNode node = Sets.pop(toVisit);
                unvisited.remove(node);
                connectedComponent.add(node);
                for (CyNode neighbour : getNetwork().getNeighborList(node, CyEdge.Type.ANY)) {
                    if (unvisited.contains(neighbour)) {
                        toVisit.add(neighbour);
                    }
                }
            }
            System.out.println("connected component: " + connectedComponent.size());
        }

        System.out.println("got the connected components");
        return connectedComponents;
    }

    private Container layOutConnectedComponents(Set<Set<CyNode>> connectedComponents) {
        Container container = new Container();

        // Create children
        for (Set<CyNode> connectedComponent : connectedComponents) {
            container.getChildren().add(layOutConnectedComponent(connectedComponent));
        }

        // Lay out children
        container.layOutInGrid(context.connectedComponentSpacing);
        return container;
    }

    private Container layOutConnectedComponent(Set<CyNode> connectedComponent) {
        // Split into family partitions and baits partition
        Map<Object, Set<CyNode>> familyPartitions = new HashMap<>(); // partition id -> partition
        Set<CyNode> baitPartition = new HashSet<>();
        for (CyNode node : connectedComponent) {
            CyRow row = getNetwork().getRow(node);
            String type = row.get(TYPE_ATTRIBUTE, String.class);
            if (type.equals("bait node")) {
                baitPartition.add(node);
            } else if (type.equals("family node")) {
                Object partitionId = row.getRaw(PARTITION_ATTRIBUTE);
                if (!familyPartitions.containsKey(partitionId)) {
                    familyPartitions.put(partitionId, new HashSet<CyNode>());
                }
                familyPartitions.get(partitionId).add(node);
            } else {
                throw new RuntimeException("Invalid node type: " + type);
            }
        }

        // Create children
        Set<CyNode> singletons = new HashSet<>();
        Container familyPartitionsContainer = new Container();
        for (Set<CyNode> partition : familyPartitions.values()) { // family partitions and merge singleton partitions into a pseudo partition
            if (partition.size() > 1) {
                familyPartitionsContainer.getChildren().add(layOutFamilyPartition(partition));
            } else {
                singletons.add(Iterables.getOnlyElement(partition));
            }
        }
        familyPartitionsContainer.getChildren().add(layOutFamilyPartition(singletons));

        Container baitsContainer = layOutBaitPartition(baitPartition);

        Container container = new Container();
        container.getChildren().add(familyPartitionsContainer);
        container.getChildren().add(baitsContainer);

        // Lay out children
        familyPartitionsContainer.sortByArea();
        familyPartitionsContainer.layOutInCircle(context.familyNodeSpacing, context.baitToFamilyPartitionSpacing
                + Math.max(baitsContainer.getWidth(), baitsContainer.getHeight()));
        baitsContainer.setCenter(familyPartitionsContainer.getWidth() / 2.0,
                familyPartitionsContainer.getHeight() / 2.0);

        return container;
    }

    private Container layOutFamilyPartition(Set<CyNode> partition) {
        Container container = layOutPartition(partition);
        container.layOutInGrid(context.familyNodeSpacing);
        return container;
    }

    private Container layOutBaitPartition(Set<CyNode> partition) {
        Container container = layOutPartition(partition);
        container.layOutInCircle(context.baitNodeSpacing, 0);
        return container;
    }

    /**
     * Get container of partition with nodes sorted by name 
     */
    private Container layOutPartition(Set<CyNode> partition) {
        // Sort nodes by name
        List<CyNode> sortedPartition = new ArrayList<>(partition);
        Collections.sort(sortedPartition, new Comparator<CyNode>() {
            @Override
            public int compare(CyNode n1, CyNode n2) {
                String name1 = getNetwork().getRow(n1).get(CyNetwork.NAME, String.class);
                String name2 = getNetwork().getRow(n2).get(CyNetwork.NAME, String.class);
                return name1.compareToIgnoreCase(name2);
            }
        });

        // Create `Container` with `Node`s
        Container container = new Container();
        for (CyNode node : sortedPartition) {
            container.getChildren().add(new Node(networkView.getNodeView(node)));
        }

        return container;
    }

}