Search code examples
javajgrapht

How to refer to custom Vertexes to add an Edge, using JGraphT


I have a graph SimpleWeightedGraph<Vertex, DefaultWeightedEdge> g where Vertex is a custom class. I have all the vertexes and the edges in a postgresql spatial db.
I need to load only a subset of them, to find a path from two vertexes, so I use some queries.

The Vertex class has a String as identifier and other parameters that I load from the db. I need them later.

I first load all the needed vertexes with some queries. In a second time I add the edges (with other queries) but I need to refer to the vertexes that are already in the graph.

Now the question: how can I make this?

Here are some extracts of my code.

The Vertex class:
(I want the Vertex are equals if they have the same id, and they are ordered with the same natural order of the Strings, by their id. I want it's also possible do vertex.equals("something"))

public class Vertex implements Comparable<Vertex>{
    private String id;      //identifier
    private double x;       //x in SRID 900913
    private double y;       //y in SRID 900913
    private String geom;    //geome in EWKT
    private int a;
    private int p;

    public Vertex(String id, double x, double y){
        [...constructor body...]
    }
    public Vertex(String id, Vertex v){
        [...constructor body...]
    }
    public Vertex(String id, double x, double y, int a, int p){
        [...constructor body...]
    }
    public Vertice(String id){
        this.id = id;
    }

    @Override
    public boolean equals(Object obj){
            boolean result;

            if (obj == this) {
                return true;
            }
            if (obj == null) {
                return false;
            }

            if (obj.getClass() != String.class)
            {
                if (obj.getClass() != this.getClass()) {
                    return false;
                }
                Vertex v = (Vertex) obj;
                result = this.id.equals(v.getId()); 
            }
            else
            {
                String s = (String) obj;
                result = this.id.equals(s);
            }

            return result;
    }

    @Override
    public int hashCode(){
        final int prime = 31;

        int result = 1;

        result = prime * result + ((id == null) ? 0 : id.hashCode());

        return result;
    }

    @Override
    public String toString(){
        return this.id;
    }

    public int compareTo(Vertex v){
        return this.id.compareTo(v.getId());
    }

    [...other methods...]
}


Extract of another part of the code, where I create the vertexes of the graph:

query = "select id_v, x, y from [table_name] where [conditions]";

rs = st.executeQuery(query);

while (rs.next())
{
    v = new Vertex("w"+rs.getInt("id_v"), rs.getDouble("x"), rs.getDouble("y"), start.getA(), 0);
    //start is a Vertex
    g.addVertex(v);
}

[...other parts of code like this one, but with different query...]


Now I need to create the edge. Here is the code:

query = "select v1, v2, weight from [table_name] where [conditions]";

rs = st.executeQuery(query);

DefaultWeightedEdge e;
String v1;
String v2;

while (rs.next())
{
    v1 = "w"+rs.getInt(1);    //source_vertex_of_edge.equals(v1) is true
    v2 = "w"+rs.getInt(2);    //target_vertex_of_edge.equals(v2) is true
    weight = rs.getDouble(3);

    //the next line doesen't work because addEdge wants (Vertex, Vertex) as parameter
    e = g.addEdge(v1, v2);

    g.setEdgeWeight(e, weight);
}


I also tried:

query = "select v1, v2, weight from [table_name] where [conditions]";

rs = st.executeQuery(query);

DefaultWeightedEdge e;
Vertex v1;
Vertex v2;

while (rs.next())
{
    v1 = new Vertex("w"+rs.getInt(1));    //source_vertex_of_edge.equals(v1) is true
    v2 = new Vertex("w"+rs.getInt(2));    //target_vertex_of_edge.equals(v2) is true
    weight = rs.getDouble(3);

    e = g.addEdge(v1, v2);

    g.setEdgeWeight(e, weight);
}


But this doesen't work: when I add the edge, the source and target vertexes (already being in the graph) lose all parameters except id.

How can I refer to them? Thanks.


Solution

  • The graph does not explicitly know anything about the id that you are storing in the vertex. Particularly, it does not know that this is the "key" by which a Vertex object should be identified later. There is no way of "extracting" an existing vertex from the graph when only the id of the vertex is known (except for iterating and checking each vertex - which is not feasible even for relatively small graphs)

    A very simple and pragmatic solution would here be to store all vertices in a map. This way, you can just look up the matching vertex for a given ID string.

    Sketched here, roughly based on your code:

    class TheClassThatLoadsTheGraph
    {
        private final Map<String, Vertex> idToVertex =
            new LinkedHashMap<String, Vertex>();
    
        void readVertices()
        {
            ...
            rs = st.executeQuery(query);
            while (rs.next())
            {
                String id = "w"+rs.getInt("id_v");
                Vertex v = new Vertex(
                    id, rs.getDouble("x"), rs.getDouble("y"), start.getA(), 0);
                g.addVertex(v);
    
                // Store the vertex in the map:
                idToVertex.put(id, v);
            }
        }
    
        void readEdges()
        {
            ...
            rs = st.executeQuery(query);
            while (rs.next())
            {
                String id1 = "w"+rs.getInt(1);
                String id1 = "w"+rs.getInt(2); 
                double weight = rs.getDouble(3);
    
                // Use the ids to look up the matching vertices
                Vertex v1 = idToVertex.get(id1);
                Vertex v2 = idToVertex.get(id2);
                DefaultWeightedEdge e = g.addEdge(v1, v2);
    
                g.setEdgeWeight(e, weight);
            }
        } 
    }
    

    NOTE:

    You mentioned that you wanted a special behavior for the equals method:

    I want it's also possible do vertex.equals("something"))

    This is not possible without severely breaking the contract of the equals method. If you could do this, you also would have to make sure that

    "something".equals(vertex);
    

    but this is obviously not the case. Equality is a very strong concept, and the details of properly implementing the equals method can be tricky. Whatever you intended to achieve there: Try to find a different approach for that! (Maybe the idToVertex map from the above code snippet may be helpful here as well...)