I started to use the Jung library for my visualization of a graph. Main problem currently is that I don't know how to set labels/metadata for my vertices. Depending on a specific attribute of a vertex, I would like to color the vertices differently. The object of class Node contains an additional Integer-value, I would like to add as an additional attribute (by getGroup()) for the vertices. The following code only visualizes the getId()-String of each node.
Any recommendation?
This is my following code in the main class:
Graph<String,Double> g = new SparseGraph<String,Double>();
List<Link> linkList = new ArrayList<Link>();
List<Node> nodeList = new ArrayList<Node>();
linkList = f.getLinks();
nodeList = f.getNodes();
for(Node nodeElement:nodeList){
g.addVertex(nodeElement.getId());
}
for(Link linkElement:linkList){
g.addEdge(linkElement.getValue(), linkElement.getSource(), linkElement.getTarget());
}
VisualizationImageServer vs =
new VisualizationImageServer(
new SpringLayout(g), new Dimension(500, 500));
vs.getRenderContext().setVertexLabelTransformer(new ToStringLabeller() {
public String transform(Object v) {
return Integer.toString(((Node)v).getGroup());
}
});
JFrame frame = new JFrame("");
frame.getContentPane().add(vs);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
You didn't specify which version of JUNG you're using, but you've got the right general idea: call setVertexLabelTransformer() with a function that converts nodes to the string that you want to render.
That said, there are a few issues with your code as written and I'm not sure I understand how you're getting the reported behavior with the code as quoted above.
(0) I don't understand why you're adding the node ID as the vertex, rather than just the Node. Are the endpoints of your Links node IDs or Nodes?
In any event, if you don't add the Node, then it's not clear from your code as written how you expect the vertex Group to be available.
(1) The vertices of the graph are node IDs, not Nodes, but you're casting the vertex object passed to transform() to a Node. I would expect this to throw a ClassCastException.
(2) Even supposing that your vertices were actually Node objects,
I'm surprised that you're reporting that the vertex ID (rather than the Group) is what's showing up on the label, because the only thing that you're asking for in the vertex label is the Group; I wouldn't expect the ID to be showing up at all.
That said, if the vertices are Node objects, and you want multiple elements to show up, it's pretty easy; just provide a function that does what you want on the Node object.
* If that function is toString(), then you can use ToStringLabeller (and that should actually be the default, so you shouldn't even need to specify it); more on that below.
* If that function is (say) getLabel(), then this should work (in Java 8):
// either of these should work
setVertexLabelTransformer(Node::getLabel())
setVertexLabelTransformer(v -> v.getLabel())
or you can do the anonymous inner class thing if you're not using Java 8.
If you don't want your vertices to be Node objects, then you'll need to provide a way of mapping vertex objects (node IDs) to nodes, such as a Map<String, Node>, and then supply that map to the function that converts vertices to labels:
setVertexLabelTransformer(v -> labelToNode.get(v).getLabel())
Note: ToStringLabeller() shouldn't generally have its transform() method overridden; it just specifies that you want to use the toString() of the object itself as the label.
// These two are equivalent
setVertexLabelTransformer(v -> v.toString())
setVertexLabelTransformer(new ToStringLabeller())
(Needless to say, ToStringLabeller() predates Java 8. :) )
Related
My program builds a tree based on input data. Before running the program, there is no way of knowing how many nodes should be created and where the position of the nodes (under which parent) should be.
Using JTree, we can add nodes easily if we, beforehand, are aware of the structure.
E.g.
//create the root node
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
/create the child nodes
DefaultMutableTreeNode vegetableNode = new DefaultMutableTreeNode("Vegetables");
vegetableNode.add(new DefaultMutableTreeNode("Capsicum"));
vegetableNode.add(new DefaultMutableTreeNode("Carrot"));
vegetableNode.add(new DefaultMutableTreeNode("Tomato"));
vegetableNode.add(new DefaultMutableTreeNode("Potato"));
root.add(vegetableNode);
But my tree, once the root node is specified, is incrementally built. So I'd like my JTree to add a node under a specific parent at any time.
Please note that the tree will also be built inside a recursive method. Which means the main JTree object should be created outside this method.
The tree may have more than one layer, which means the path from the node to a leaf may require e.g. 10 jumps.
What is the best way of adding a node to a JTree at run-time which only knows about its parent?
A caveat here is when two different parents have the same name e.g. if node apple should go under a node called fruit but fruit is found in two different paths
root -> aaa -> bbb -> ccc -> fruit
root -> aaa -> fff -> ggg -> hhh -> fruit
You should look into providing your own tree model then.
Quoting the Oracle tutorial for JTree:
One of the ways you can lazily load children of a Tree is by utilizing the TreeWillExpandListener interface. For example, you can declare and load root, grandparent and parent of a Tree along with the application as shown in the following code:
Given the comments by the OP: the DefaultMutableTreeNode does not support "names". That would mean: you need to implement that yourself, in a super simply approach, probably like:
class MyTreeNode extends DefaultMutableTreeNode {
private final String name;
MyTreeNode(String name, ... whatever ) {
...
SOME_NODE_REGISTRY.put(name, this);
}
Then you need to provide that registry, probably as some sort of singleton map instance. In other words: you have to write code that A) enables you to store nodes by name to then B) identify/find nodes by name.
I'm learning about search algorithms BFS and DFS. I plan to implement both but before I do that, I need to implement my graph structure. Here's my idea:
A graph of connecting cities: Each city is represented by a Node.
Our graph will simply be an ArrayList of Nodes added as they're created, and each Node will have a list of it's neighbors, and a parent which will let us know where we came from (for path retrieval). I haven't coded anything up yet, I wanted to get some feedback on my ideas before spending the time writing up something that won't work. Here's some pseudocode-ish, code. One potential problem I can see is how we're going to deal with Nodes that we can get to from multiple places (multiple parents). If anyone has any suggestions on dealing with that, feel free to share.
public class Node{
String name;
Node parent;
ArrayList<Node> neighbors;
public addNeighbor(Node n);
public setParent(Node n);
public getNeighbors()
...
}
public static void main(String[] args){
ArrayList<Node> graph = new ArrayList<Node>();
//build node
Node node = new Node(String name);
//add neighbors
node.addNeighbor(neighbor1);
node.addNeighbor(neighbor2);
//set parent
node.setParent(parent1);
//add to graph
graph.add(node);
path = dfs(graph, startNode, goalNode);
System.out.print(path);
}
Edit: I know I could look online and find implementations of this pretty easily, but I'd prefer to come up with my own solutions.
Your implementation look good. It's the classic implentation of a graph structure (a node with a list of neighbors). Some points:
You can use backtracking to deal with multiples paths that reach the same node. If the dfs method have a recursive implementation, you need to avoid the recursive call if the Node has already a parent. But if the new path is better that the old one, then you discard the old parent, and set the new one.
Your implementation is a directional graph. In other words, you can build a graph that has a path from A to B, but has no path from B to A. I don't know if this is ok for you.
I recommend you encapsulate the building of the graph inside a wrapper, that build both paths automatically, whith a unique call to a method. That way, always you build bidirectional paths.
You can use a Set to store the neighbors. That way, there is no duplicates. Of course, you need to implements the "equals" method in the Node class.
I want to create a class node, class Edge and class Graph. In class node, it must take some number as input to create nodes. Also, I want this class to have methods where I can change different attributes of the nodes. Also, it must contain a method to send and receive information from its adjacent nodes over Edge. These information receiving and sending methods should have provision to control information flow.
I am not sure which data structure to use for nodes to fulfill these requirements.
Create a class Node that has all the attributes that you want. Ex. If you want to create a Node for a binary Tree your Node class can be something like.
class Node
{
Node left;
Node right;
int info;
Node(int value)
{
this.info = value;
}
//Add more attributes or functionalities
}
This is one representation of how you can create a Node class. Depending upon your requirements the representation might change but the underlying concept remains the same.
Create your own class node.
A node is just an object. Give it some attributes (for your example you probably want weight and value).
public class Node{
private double weight;
private String value;
private ArrayList<Node> edges;
//setters and getters
}
Create a class Node which has a list of references to other Nodes (in a directed graph, this is especially usefull). An edge is not actually a "thing" to create - it's more like a connection.
The two most common ways to implement graphs are to use either an adjacency matrix or an adjacency list.
The matrix approach involves using a 2d array to determine if node i and node j are connected by asking if node[i][j] is true. Wasteful on space, but has constant time access.
Adjacency lists keep track of every adjacent node to the current node, which save on space but cost linear time in order to determine if a node is connected to it.
These articles explain it better:
Matrix
List
I'm implementing some algorithms to teach myself about graphs and how to work with them. What would you recommend is the best way to do that in Java?
I just wanted to ask u if u can give just a short help with a short very simply class definition for directed graph and weighted directed graph?
I looked over the web but i do not want an implementation of it, just a short definition of the classes....what u think is the best data structure to use ? Adjacent Lists?
For an undirected Graph i defined it as follow:
public interface Graph {
Collection vertices(); // returns a collection of all the
// Vertex objects in the graph
Collection edges(); // returns a collection of all the
// Edge objects in the graph
Collection incidentEdges(Vertex v); // returns a collection of
// Edges incident to v
boolean isAdjacent(Vertex v, Vertex w); // return true if v and
} // w are adjacent
public class Vertex {
public boolean visited; // initially false for all vertices
}
public class Edge {
Vertex v1, v2; // undirected edge
Vertex opposite(Vertex v); // given a vertex return the one
} // at the other end of this edge
Here is a simple implementation (this should be good enough for many basic use cases):
public class Graph {
public List<Node> nodes = new ArrayList<Node>();
}
public class Node{
public List<Node> neighbors = new ArrayList<Node>();
//here you can add stuff like "public boolean visited;", if your algorithm requires it
}
(This is just an example - there are tons of ways to implement a graph structure).
The data structure to use depends on the purpose of your code... Lists usually do quite well, but if the graph is highly connected (most of the nodes are connected to most of the nodes), then a list is cumbersome, and a matrix of some kind is usually to be preferred.
For example, to use a matrix, keep a vector of nodes, then use a 2-dimensional vector of integers to reference the nodes.
Also, for making a lightweight class, you don't have to declare a class for nodes: you can provide functions for adding nodes and edges, and you can identify such elements with integers.
Having Node or Edge classes may be suitable if you plan to subclass them (for example, could be useful when doing a GUI which display a graph), but if you need the graph for some algorithms, identifying nodes/edges with integers is faster and simpler.
I have a custom DefaultMutableTreeNode class that is designed to support robust connections between many types of data attributes (for me those attributes could be strings, user-defined tags, or timestamps).
As I aggregate data, I'd like to give the user a live preview of the stored data we've seen so far. For efficiency reasons, I'd like to only keep one copy of a particular attribute, that may have many connections to other attributes.
Example: The user-defined tag "LOL" occurs at five different times (represented by TimeStamps). So my JTree (the class that is displaying this information) will have five parent nodes (one for each time that tag occured). Those parent nodes should ALL SHARE ONE INSTANCE of the DefaultMutableTreeNode defined for the "LOL" tag.
Unfortunately, using the add(MutableTreeNode newChild) REMOVES newChild from WHATEVER the current parent node is. That's really too bad, since I want ALL of the parent nodes to have THE SAME child node.
Here is a picture of DOING IT WRONG (Curtis is the author and he should appear FOR ALL THE SHOWS):
How can I accomplish this easily in Java?
Update
I've been looking at the code for DefaultMutableTreeNode.add()... and I'm surprised it works the way it does (comments are mine):
public void add(MutableTreeNode child)
{
if (! allowsChildren)
throw new IllegalStateException();
if (child == null)
throw new IllegalArgumentException();
if (isNodeAncestor(child))
throw new IllegalArgumentException("Cannot add ancestor node.");
// one of these two lines contains the magic that prevents a single "pointer" from being
// a child within MANY DefaultMutableTreeNode Vector<MutableTreeNode> children arrays...
children.add(child); // just adds a pointer to the child to the Vector array?
child.setParent(this); // this just sets the parent for this particular instance
}
If you want easily, you should probably give up on sharing the actual TreeNodes themselves. The whole model is built on the assumption that each node has only one parent. I'd focus instead on designing your custom TreeNode so that multiple nodes can all read their data from the same place, thereby keeping them synced.
I'm not sure it qualifies as easy, but you might look at Creating a Data Model by implementing TreeModel, which "does not require that nodes be represented by DefaultMutableTreeNode objects, or even that nodes implement the TreeNode interface." In addition to the tutorial example, there's a file system example cited here.
Unfortunately, I believe the answer is no. In order to do what you're talking about, you would need to have DefaultMutableTreeNode's internal userObject be a pointer to some String, so that all the corresponding DefaultMutableTreeNode's could point to and share the same String object.
However, you can't call DefaultMutableTreeNode.setUserObject() with any such String pointer, because Java does not have such a concept on the level that C or C++ does. Check out this outstanding blog article on the confusing misconceptions about pass-by-value and pass-by-reference in Java.
Update: Responding to your comment here in the answer space, so I can include a code example. Yes, it's true that Java works with pointers internally... and sometimes you have to clone an object reference to avoid unwanted changes to the original. However, to make a long story short (read the blog article above), this isn't one of those occasions.
public static void main(String[] args) {
// This HashMap is a simplification of your hypothetical collection of values,
// shared by all DefaultMutableTreeNode's
HashMap<String, String> masterObjectCollection = new HashMap<String, String>();
masterObjectCollection.put("testString", "The original string");
// Here's a simplification of some other method elsewhere making changes to
// an object in the master collection
modifyString(masterObjectCollection.get("testString"));
// You're still going to see the original String printed. When you called
// that method, a reference to you object was passed by value... the ultimate
// result being that the original object in you master collection does
// not get changed based on what happens in that other method.
System.out.println(masterObjectCollection.get("testString"));
}
private static void modifyString(String theString) {
theString += "... with its value modified";
}
You might want to check out the JIDE Swing extensions, of which some components are commercial while others are open source and free as in beer. You might find some kind of component that comes closer to accomplishing exactly what you want.