Java tutorial
/* * This file is part of JFlowMap. * * Copyright 2009 Ilya Boyandin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jflowmap.views.flowmap; import java.awt.Color; import java.awt.Paint; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import jflowmap.FlowMapAttrSpec; import jflowmap.FlowMapGraph; import jflowmap.data.FlowMapGraphBuilder; import jflowmap.geom.GeomUtils; import jflowmap.geom.Point; import jflowmap.util.ColorUtils; import jflowmap.util.Pair; import prefuse.data.Node; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; /** * @author Ilya Boyandin */ public class VisualNodeCluster implements Iterable<VisualNode> { private static final String JOINED_FLOW_MAP_CLUSTER_ATTR = "visualNodeCluster"; private static final String LABEL_SEPARATOR = "; "; // private static final String LABEL_SEPARATOR = "\n "; private ClusterTag tag; private final List<VisualNode> nodes; private VisualNodeCluster(int id, Paint color, Iterator<VisualNode> it) { this.nodes = new ArrayList<VisualNode>(); this.tag = ClusterTag.createFor(id, color); Iterators.addAll(nodes, it); updateClusterTags(); } public void setVisible(boolean visible) { if (visible == tag.isVisible()) { return; // no change } tag = tag.withVisible(visible); updateClusterTags(); } private void updateClusterTags() { for (VisualNode node : nodes) { node.setClusterTag(tag); } } public ClusterTag getTag() { return tag; } private transient String cachedNodeListAsString; public String getNodeListAsString() { if (cachedNodeListAsString == null) { cachedNodeListAsString = makeNodeClusterLabel(this); } return cachedNodeListAsString; } public List<VisualEdge> getIncomingEdges() { List<VisualEdge> edges = new ArrayList<VisualEdge>(); for (VisualNode node : nodes) { edges.addAll(node.getIncomingEdges()); } return edges; } public List<VisualEdge> getOutgoingEdges() { List<VisualEdge> edges = new ArrayList<VisualEdge>(); for (VisualNode node : nodes) { edges.addAll(node.getIncomingEdges()); } return edges; } public Iterator<VisualNode> iterator() { return nodes.iterator(); } public int size() { return nodes.size(); } public static VisualNodeCluster createFor(int id, Paint color, Iterator<VisualNode> it) { return new VisualNodeCluster(id, color, it); } public static List<VisualNodeCluster> createClusters(List<List<VisualNode>> nodeClusterLists, double clusterDistanceThreshold) { // List<List<VisualNode>> clusters = Lists.newArrayList(Iterators.filter( // ClusterSetBuilder.getClusters(rootCluster, clusterDistanceThreshold).iterator(), // new Predicate<List<VisualNode>>() { // public boolean apply(List<VisualNode> nodes) { // return (nodes.size() > 1); // } // } // )); final int numClusters = nodeClusterLists.size(); Color[] colors = ColorUtils.createCategoryColors(numClusters); List<VisualNodeCluster> nodeClusters = Lists.newArrayListWithExpectedSize(numClusters); int cnt = 0; for (Collection<VisualNode> nodes : nodeClusterLists) { nodeClusters.add(VisualNodeCluster.createFor(cnt + 1, colors[cnt], nodes.iterator())); cnt++; } return nodeClusters; } public static List<List<VisualNode>> combineClusters(List<List<VisualNode>> clusters1, List<List<VisualNode>> clusters2) { Map<VisualNode, Integer> map1 = createNodeToClusterIndexMap(clusters1); Map<VisualNode, Integer> map2 = createNodeToClusterIndexMap(clusters2); Multimap<Pair<Integer, Integer>, VisualNode> newClusters = LinkedHashMultimap.create(); for (List<VisualNode> cluster : clusters1) { for (VisualNode node : cluster) { newClusters.put(Pair.of(map1.get(node), map2.get(node)), node); } } List<List<VisualNode>> newClustersList = Lists.newArrayList(); for (Pair<Integer, Integer> key : newClusters.asMap().keySet()) { newClustersList.add(ImmutableList.copyOf(newClusters.get(key))); } return newClustersList; } private static Map<VisualNode, Integer> createNodeToClusterIndexMap(List<List<VisualNode>> nodeClusterLists) { Map<VisualNode, Integer> map = Maps.newHashMap(); for (int clusterIndex = 0, size = nodeClusterLists.size(); clusterIndex < size; clusterIndex++) { for (VisualNode node : nodeClusterLists.get(clusterIndex)) { map.put(node, clusterIndex); } } return map; } public static FlowMapGraph createClusteredFlowMap(FlowMapAttrSpec flowMapAttrSpec, List<VisualNodeCluster> clusters) { FlowMapGraphBuilder builder = new FlowMapGraphBuilder(null, flowMapAttrSpec).withCumulatedEdges() .addNodeAttr(JOINED_FLOW_MAP_CLUSTER_ATTR, VisualNodeCluster.class); // Create (visualNode->cluster node) mapping Map<VisualNode, Node> visualToNode = Maps.newHashMap(); for (VisualNodeCluster cluster : clusters) { Point centroid = GeomUtils .centroid(Iterators.transform(cluster.iterator(), VisualNode.TRANSFORM_NODE_TO_POSITION)); Node node = builder.addNode(centroid, makeNodeClusterLabel(cluster)); node.set(JOINED_FLOW_MAP_CLUSTER_ATTR, cluster); for (VisualNode visualNode : cluster) { visualToNode.put(visualNode, node); } } // Edges between clusters for (VisualNodeCluster cluster : clusters) { for (VisualNode node : cluster) { for (VisualEdge visualEdge : node.getEdges()) { builder.addEdge(visualToNode.get(visualEdge.getSourceNode()), visualToNode.get(visualEdge.getTargetNode()), visualEdge.getEdgeWeight()); } } } return builder.build(); } private static String makeNodeClusterLabel(VisualNodeCluster cluster) { List<VisualNode> nodes = Lists.newArrayList(cluster); Collections.sort(nodes, VisualNode.LABEL_COMPARATOR); StringBuilder label = new StringBuilder(); label.append("["); for (VisualNode visualNode : nodes) { label.append(visualNode.getLabel()).append(LABEL_SEPARATOR); } if (label.length() > 0) { label.setLength(label.length() - LABEL_SEPARATOR.length()); // remove the last separator } label.append("]"); return label.toString(); } public static VisualNodeCluster getJoinedFlowMapNodeCluster(Node node) { if (!node.canGet(JOINED_FLOW_MAP_CLUSTER_ATTR, VisualNodeCluster.class)) { // throw new IllegalArgumentException("Node " + node + " doesn't have a cluster attr"); return null; } return (VisualNodeCluster) node.get(JOINED_FLOW_MAP_CLUSTER_ATTR); } }