Search code examples
javagraph-algorithmpriority-queuecomparable

Java: Uniform Cost Search with Node class


The below code is supposed to detect an image, create a 2d array containing the pixel values from that image, and determine the path of lowest cost (I used Uniform Cost Search) from a Point A inside the image to a Point B inside the image. When I run my code, I get the following error:

Exception in thread "main" java.lang.ClassCastException: UniformCostSearch$Node
cannot be cast to java.lang.Comparable
        at java.util.PriorityQueue.siftUpComparable(Unknown Source)
        at java.util.PriorityQueue.siftUp(Unknown Source)
        at java.util.PriorityQueue.offer(Unknown Source)
        at java.util.PriorityQueue.add(Unknown Source)
        at UniformCostSearch.uCostSearch(UniformCostSearch.java:159)
        at UniformCostSearch.main(UniformCostSearch.java:216)

Why does this occur, and how can I fix this? I've viewed other solutions, but I believe I have a different issue.

public class UniformCostSearch
{   
    //------------------------------------
    // Node Class
    //------------------------------------
    public static class Node implements Comparator<Node>
    {
        public double cost;
        Node parent;
        public int x; 
        public int y;

        public Node(){}

        public Node(double cost, Node parent, int x, int y)
        {
            this.cost = cost;
            this.parent = parent;
            this.x = x;
            this.y = y;
        }

        @Override
        public int compare(Node node1, Node node2)
        {
            if(node1.cost < node2.cost)
                return -1;
            if(node1.cost > node2.cost)
                return 1;
            return 0;
        }

        @Override
        public boolean equals(Object obj)
        {
            if(obj instanceof Node)
            {
                Node node = (Node) obj;
                if (this.parent == node.parent)
                    return true;
            }
            return false;
        }
    }

    public static void uCostSearch(Node startState, Node endNode, 
        BufferedImage img){

        int adjacencyMatrix[][] = null;
        PriorityQueue<Node> pq = new PriorityQueue<Node>();
        startState.cost = 0.0;
        startState.parent = null;
        int inc = 0;

        pq.add(startState);

        Set<Point> visited = new HashSet<Point>();
        Point startPoint = new Point(startState.x, startState.y);
        visited.add(startPoint);

        byte[] terrain =
            ((DataBufferByte)img.getRaster().getDataBuffer()).getData();
        int width = img.getWidth();
        int height = img.getHeight();
        final boolean hasAlphaChannel = img.getAlphaRaster() != null;
        int channel_count = 4;
        int green_channel = 2; // 0=alpha, 1=blue, 2=green, 3=red

        int[][] result = new int[height][width];

// Source: http://stackoverflow.com/questions/6524196/java-get-pixel-array-from-image
         if (hasAlphaChannel) {
         final int pixelLength = 4;
         for (int pixel = 0, row = 0, col = 0; pixel < terrain.length; pixel += pixelLength) {
            int argb = 0;
            argb += (((int) terrain[pixel] & 0xff) << 24); // alpha
            argb += ((int) terrain[pixel + 1] & 0xff); // blue
            argb += (((int) terrain[pixel + 2] & 0xff) << 8); // green
            argb += (((int) terrain[pixel + 3] & 0xff) << 16); // red
            result[row][col] = argb;
            col++;
            if (col == width) {
               col = 0;
               row++;
            } 
         } 
      } else { 
         final int pixelLength = 3;
         for (int pixel = 0, row = 0, col = 0; pixel < terrain.length; pixel += pixelLength) {
            int argb = 0;
            argb += -16777216; // 255 alpha
            argb += ((int) terrain[pixel] & 0xff); // blue
            argb += (((int) terrain[pixel + 1] & 0xff) << 8); // green
            argb += (((int) terrain[pixel + 2] & 0xff) << 16); // red
            result[row][col] = argb;
            col++;
            if (col == width) {
               col = 0;
               row++;
            } 
         }
      }

        while(!pq.isEmpty()){
            Node s = pq.remove();
            if(s == endNode)
                return;
            else{
                if(inc++ % 5000 < 1000){
                    // set to Green
                    img.setRGB(s.y, s.x, 0xFF00FF00);       
                }
            ArrayList<Point> directions = new ArrayList<Point>();
            directions.add(new Point(s.x, s.y+1));
            directions.add(new Point(s.x, s.y-1));
            directions.add(new Point(s.x+1, s.y));
            directions.add(new Point(s.x-1, s.y));
            for(Point step: directions){
                int x = step.x;
                int y = step.y;

                if(0 <= y && y < height && 0 <= x && x < width 
                    && !(visited.contains(step))){
                        visited.add(step);
                        Node temp = new Node(s.cost + channel_count * 
                        (y * width + x) + green_channel, s, x, y);
                        pq.add(temp);   
                    }
            }
            }
            inc++;
            System.out.print(inc);
        }       
    }

    public static BufferedImage GetImage(String filename)
    {
        BufferedImage bImage = null;
        try{
            bImage = ImageIO.read(new File(filename));
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return bImage;
    }

    /*
    public createPath(Node node, BufferedImage image){
        while(node.parent != null){
            //set pixel to red;
            node = node.parent;
        }
    }
    */

    // main method
    public static void main(String[] args)
    {
        int startX = 0, startY = 0, endX = 0, endY = 0;
        BufferedImage bufferedImage = null;
        // Get filename and start and end points from command line
        try{
            String filename = args[0];
            startX = Integer.parseInt(args[1]);
            startY = Integer.parseInt(args[2]);
            endX = Integer.parseInt(args[3]);
            endY = Integer.parseInt(args[4]);
            bufferedImage = GetImage(filename);
            }
            catch(Exception e)
            {
                e.printStackTrace();
                System.out.print("Please enter filename start.x start.y" + 
                    " end.x end.y in that order");
            }

            Node startingPoint = new Node(0.0, null, startX, startY);
            Node endingPoint = new Node();
            endingPoint.x = endX;
            endingPoint.y = endY;

            uCostSearch(startingPoint, endingPoint, bufferedImage);

            //createPath(endingPoint, bufferedImage)
            //saveImage
            //PrintCost
            //Output Image

    }

}

Solution

  • To satisfy the sorting capability of the PriorityQueue class (used by UniformCostSearch), your Node class must implement the Comparable interface, not the Comparator interface.

    To use the node parent in your comparison, you might try this:

        @Override
        public int compareTo(Node that)
        {
            if (this.parent != null && that.parent != null) {
                return this.parent.compareTo(that.parent);
            }
            return this.cost - that.cost;
        }
    

    That's just an example though. I make no claim it will fill your requirements, but it will prevent NullPointerException.