使用Dijkstra Search的地图导航

使用交互式用户界面实现A*/Dijkstra搜索

寻找确切的最短路径

这个项目探讨了一种实现精确最短路径查找方法的方式,称为迪杰斯特拉算法。 这是一种非常适合指导更复杂搜索算法(如A*搜索)的算法。

示例输入:

//
GILBERT-LONG	43.130480	-77.631559
GILBERT-COMMON	43.130584	-77.631216
...
我1	GILBERT-LONG	I2
我2	I1	I2
...
//Beginning monroe.txt
...
//Beginning nys.txt
...

示例输出:

map outline

给定代表特定地理区域中道路和交叉路口的数据集,程序应能够绘制数据地图,并使用迪杰斯特拉算法为任意两个交叉路口提供最短路径指示方向

开发过程

存储:
  • HashMap<String, 节点> 用于存储交叉点
  • Hashmap<String, 边> && URLinkedList<边> 用于存储边

边缘与节点

public class Edge {
    
    //Attributes
    String name;
    Node from;
    Node to;
    

    //Constructors
    public Edge(){}
    public Edge(String name, Node from, Node to){
        this.name = name;
        this.from = from;
        this.to = to;
    }
    
    //Setters and getters
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public Node getFrom(){
        return from;
    }
    public Node getTo(){
        return to;
    }
    public void setFrom(Node from){
        this.from = from;
    }
    public void setTo(Node to){
        this.to = to;
    }



public class Node {

    //Attributes
    String name;
    Double latitude;
    Double longitude;
    Double minDistance;
    Node prececessor;
    //LinkedList of string names of this node's adjacent node
    URLinkedList<Node> adjacent = new URLinkedList<>();

//Constructors
    public Node(){}
    public Node(String name,Double la, Double lo){
        this.name = name;
        this.latitude = la;
        this.longitude = lo;
    }
    public Node(String name, Double la, Double lo, Double minDistance, URLinkedList<Node> list){
        this.name = name;
        this.latitude = la;
        this.longitude = lo;
        this.minDistance = minDistance;
        this.adjacent = list;
    }

    //Setters and getters
    public void setName(String name){this.name = name;}
    public void setLatitude(Double latitude){this.latitude = latitude;}
    public void setLongitude(Double longitude){this.longitude = longitude;}
    public String getName(){return name;}
    public Double getLatitude(){return latitude;}
    public Double getLongitude(){return longitude;}
    public URLinkedList<Node> getAdjacent(){return adjacent;}
    public void setMinDistance(Double distance){minDistance = distance;}
    public Double getMinDistance(){return minDistance;}
    public URLinkedList<Node> getList(){return adjacent;}
    public Node getPredecessor(){return prececessor;}
    public void setPredecessor(Node node){this.prececessor = node;}

    //Methods
    public void addAdjacent(Node vertex){
        adjacent.add(vertex);
    }    
}

}

迪科斯特拉和哈弗辛

/**
     * Find shortest path using Dijkstra algorithm
     * @param vertice1 Node of start point
     * @param vertice2 Node of end point
     * @throws IllegalArgumentException
     */
    public static void Dijkstra(String vertice1, String vertice2){

        //Get two vertices: start and end
        Node start = intersections.get(vertice1);//Lookup vertice using its string name
        Node end = intersections.get(vertice2);
        if(start == null || end == null){
            throw new IllegalArgumentException("Vertices entered not valid, check again");
        }
        //Create a priority queue to store all the unvisited node.
        MyPriorityQueue<MyKeyValueEntry<Node, Node>, Double> queue = new MyPriorityQueue<>();

        //Set distance from start to itself to be 0.0...
        start.setMinDistance(0.0);
        //Get starting node's adjacency list

        
        while(!(start.getAdjacent().isEmpty())){//Add adjacent list from started vertex.
            Node next = start.getAdjacent().removeFirst();
            Double distance = harversine(start.getLatitude(), start.getLongitude(), next.getLatitude(), next.getLongitude());
            next.setMinDistance(distance);
            next.setPredecessor(start);
            //Add node-pair into priority queue
            queue.add(new MyKeyValueEntry<>(start, next), distance);
        }

        while(!queue.isEmpty()){

            //Get second node in the pair
            MyKeyValueEntry<MyKeyValueEntry<Node, Node>, Double> pop = queue.poll();
            //get cur node and its predecessor
            Node previous = pop.getKey().getKey();
            Node cur = pop.getKey().getValue();

            while(!(cur.getAdjacent().isEmpty())){ //get cur's adjacent list
                
                //Get first node in cur's adjacent list
                Node next = cur.getAdjacent().removeFirst();
                
                if(next != previous){//Proceed if adjacent node isn't previously visited node

                    //Set distance to be previous distance + distance between two nodes
                    Double distance = cur.getMinDistance() + harversine(cur.getLatitude(), cur.getLongitude(), next.getLatitude(), next.getLongitude());
                    
                    //if calculated distance smaller than previously calculated distance, replace it
                    if(distance < next.getMinDistance()){
                        next.setMinDistance(distance);
                        //Set adjacent's predecessor to cur node 
                        next.setPredecessor(cur);
                        //Push new shorter distance into the PriorityQueue
                        queue.add(new MyKeyValueEntry<>(cur,next), distance); 
                    }
                }
                
            }
        }
        //If end's distance smaller than double max and predecessor not null, then path found
        if(end.getMinDistance() == Double.MAX_VALUE ){
            System.out.println("Shortest path not found, intersections not connected");
            return;
        }


        //Create shortest path in String type
        StringBuilder rstr = new StringBuilder();
        Double len = end.getMinDistance() / 1.6;
        while(end.getPredecessor() != null){//loop backup until no precedessor found (start point)
            rstr.append(end.getName());
            rstr.append(">");
            end = end.getPredecessor();
        }
        rstr.append(start.getName());
        String str = rstr.toString();
        String[] tokens = str.split(">"); //split reverse ordered string into parts

        //Build shortest path in names of intersections
        String intersection = "";
        for(int i = tokens.length-1; i > 0; i--){
            intersection += tokens[i];
            intersection += " > ";
        }
        intersection += intersections.get(vertice2).getName();

        shortestPath = intersection;
        //Build shortest path in names of edges
        String[] tokens2 = intersection.split(" > ");
        String paths = "";
        for(int i = 0; i < tokens2.length - 1; i++){
            for(int j = 0; j < road.size(); j++){
                if((tokens2[i].equals(road.get(j).getFrom().getName())&&tokens2[i + 1].equals(road.get(j).getTo().getName()))||
                (tokens2[i].equals(road.get(j).getTo().getName())&&tokens2[i + 1].equals(road.get(j).getFrom().getName()))){
                    paths += road.get(j).getName();
                    if(i+2<tokens2.length){//if the last one, no need to add arrow pointer
                        paths += " > ";
                    }
                    break;
                }
            }
        }
        System.out.println("Shortest path detected: " + paths);
        System.out.println("Shortest path detected: " + intersection + "\nTotal distance: " + len + "miles");
    }
创建地图图像,计算后端
  • 使用哈尔文斯距离来计算距离
  • 让迪杰斯特拉返回最短路径字符串
  • 转换 路径为多个节点并用于绘制高亮线条

障碍


URLinkedList链表
使用内部节点类使用URLinkedList来存储相邻列表,以实现双向链表
//Author: Tianyi Zhou


import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;




public class URLinkedList<T> implements URList<T>{

    /**
     * create attributions
     * 
     */

    int size = 0;

    URNode<T> first;
    URNode<T> last;

    /**
     * Constructors for URLinkedList
     * 
     */
    public URLinkedList(){}
    /**
	 * 
	 * @param c collection added to the linkedlist at the beginning of initialization
	 * @see URLinkedList.addAll()
	 */
	@SuppressWarnings("unchecked")
    public URLinkedList(Collection<?> c){
		Collection<? extends T> d = (Collection<? extends T>)c;
		addAll(d);
    }



	//METHODS

	URNode<T> node(int index){
		URNode<T> x = first;
		for(int i = 0;i < index;i++)
			x = x.next();
		return x;
	}
	/**
	 * add item e to the first node in the linked list
	 * @param e
	 */
	public void addFirst(T e){
		URNode<T> original = first;
		URNode<T> newNode = new URNode<T>(e, null, original);
		first = newNode;
		if(original == null) 
			last = newNode;//if there's only e in the linkedlist, then the first is also the last
		else 
			original.setPrev(newNode);
		size++;
	}
	public void addLast(T e){
		URNode<T> original = last;
		URNode<T> newNode = new URNode<T>(e, original, null);
		last = newNode;
		if(original == null) 
			first = newNode; // if only e exist, then the last is also the first
		else
			original.setNext(newNode);
		size++;
	}
	/**
	 * peekFirst/peekLast reuturn the First/Last element in the list, pollFirst/pollLast remove the First/Last element in the list 
	 * after element returned
	 * @see URLinkedList.removeFirst()]
	 * @see URLinkedList.removeLast()
	 */
	public T peekFirst(){
		if(first == null) return null;
		return first.element();
	}
	public T peekLast(){
		if(last == null) return null;
		return last.element();
	}
	public T pollFirst(){
		if(first == null) return null;
		T element = first.element();
		removeFirst();
		return element;
	}
	public T pollLast(){
		if(last == null) return null;
		T element = last.element();
		removeLast();
		return element;
	}

    /**
	 * add element to the end of the list
	 * @param e element added
	 * @see URLinkedList.addLast()
	 */
	public boolean add(T e){
		addLast(e);
		return true;
    }
	/**
	 * add element to the specific location
	 * @param index index for insertion
	 * @param element element for insertion
	 * @throws indexOutOfBoundsException if index > 0 && index <= list's size
	 */
	public void add(int index, T element){
		if(!(index >= 0 && index <= size)){
			throw new IndexOutOfBoundsException("index has to be larger than 0 and smaller than list size");
		}
		if(index == size)
			addLast(element);
		else
			linkBefore(element, node(index));
	}
	public void linkBefore(T element, URNode<T> succNode){
		URNode<T> prev = succNode.prev();
		URNode<T> newNode = new URNode<>(element, prev, succNode);
		if(prev == null)
			first = newNode;
		else
			prev.setNext(newNode);
		size++;
	}

	/**
	 * Appends all of the elements in the specified collection to the end of this list,
	 * in the order that they are returned by the specified collection's iterator
	 * @param c collection of elements waiting to add
	 * @throws IndexOutOfBoundsException if requested index illegal
	 * @throws NullPointerException if collection is null
	 * */ 
	public boolean addAll(Collection<? extends T> c){
		return addAll(size, c);
	}

	@SuppressWarnings("unchecked")
	public boolean addAll(int index, Collection<? extends T> c){
		if(!(size >= 0 && size <= size)) throw new IndexOutOfBoundsException();
		if(c == null) throw new NullPointerException("added collection can't be null");

		Object[] other = c.toArray();
		int len = other.length;
		if(len == 0) return false;

		URNode<T> prev;
		URNode<T> succ;
		if(index == size){
			succ = null;
			prev = last;
		}else{
			succ = node(index);
			prev = succ.prev();
		}
		
		for(Object i: other){
			T e = (T) i;
			URNode<T> newNode = new URNode<>(e, prev, null);
			if(prev == null) first = newNode;
			else prev.setNext(newNode);
			prev = newNode;
		}
		if(succ == null) last = prev;
		else{
			prev.setNext(succ);
			succ.setPrev(prev);
		}
		size++;
		return true;
	}
	
	// Removes all of the elements from this list 
	public void clear(){
		for(URNode<T> x = first; x != null;){
			x.setPrev(null);
			x.setElement(null);
			x.setNext(null);
			URNode<T> next = x.next();
			x = next;
		}
		first = null;
		last = null;
		size = 0;
	}

	// Returns true if this list contains the specified element.
	public boolean contains(Object o){
		return indexOf(o) >= 0;
	}
	/**
	 * Check if all elements in specific colleciton contained within the list
	 * @see URLinkedList.contains(
	 * @see URLinkedlist.toArray()
	 */
	public boolean containsAll(Collection<?> c){
		Object[] d = c.toArray();
		for(Object i: d){
			if(!(contains(i))) return false;
		}
		if(c.size() > size) return false;
		else return true;
	}

	/**
	 * Compares if Object equals to the list
	 * @param o Object
	 * @see URLinkedList.toArray()
	 * @throws NullPointerExcpetion
	 * @throws ClassCastException
	 */
	public boolean equals(Object o){
		if(o == null) throw new NullPointerException("comparing object cannot be null");
		if(!(o instanceof URLinkedList<?>)) throw new ClassCastException("not the same instance");
		
		Collection<?> c = (Collection<?>)o;
		Object[] d = c.toArray();
		if(size != d.length) return false;
		int index = 0;
		for(URNode<T> x = first; x != null; x = x.next()){
			if(!x.element().equals(d[index])) return false;
		}
		return true;
	}

	/**
	 * Returns the element at the specified position in this list.
	 * @param index index returned
	 * @throws IndexOutOfBound if index illegal
	 * @see URNode.element()
	 */
	public T get(int index){
		if(!(index >= 0 && index <= size)) throw new IndexOutOfBoundsException("cannot get outOfBound index");
		return node(index).element();
	}

	/**
	 * return -1 if not found, otherwise return index where first element found
	 * @param o object searching for
	 * @see URNode.element()
	 */
	public int indexOf(Object o){
		int index = 0;
		if(o == null){
			for(URNode<T> x = first;x != null;){
				if(x.element()==null) return index;
				index++;
				x = x.next();
			}
		}else{
			for(URNode<T> x = first;x != null;){
				if(o.equals(x.element())) return index;
				index++;
				x = x.next();
			}
		}
		return -1;
	}

	// Returns true if this list contains no elements.
	public boolean isEmpty(){
		return size == 0;
	}

	
	/**
	 * All type of remove methods based on different parameters (Multiple overrides)
	 * @param index element index
	 */
	public T remove(int index){
		if(!(index >= 0 && index <= size)) throw new IndexOutOfBoundsException("Index out of bound");
		
		URNode<T> x = node(index);
		T element = x.element();
		URNode<T> prev = x.prev();//ROY,IS,MY,SUPERHERO
		URNode<T> next = x.next();
		
		prev.setNext(next);
		next.setPrev(prev);
		x.setNext(null);
		x.setPrev(null);
		x.setElement(null);
		size--;
		return element;

	}
	public T removeFirst(){
		URNode<T> f = first;
		if(f == null) throw new NoSuchElementException("this list is empty");
		else{
			T element = f.element();
			URNode<T> next = f.next();
			f.setElement(null);
			first = next;
			if(next == null) last = null;//if next is null, then after first removed, list be empty
			else next.setPrev(null);//if next not null, then next(now first)'s previous points to null(head)
			size--;
			return element;
		}
	}
	public T removeLast(){
		URNode<T> l = last;
		if(l==null) throw new NoSuchElementException("this list is empty");
		else{
			T element = l.element();
			URNode<T> prev = l.prev();
			l.setElement(null);//clean up element value
			last = prev;
			if(prev == null) first = null;
			else prev.setNext(null);//clean up node value
			size--;
			return element;
		}
	}
	/**
	 * Remove specific Object within the list
	 * @param o object
	 * @see URLinkedList.remove()
	 */
	public boolean remove(Object o){

		if(o == null) throw new IllegalArgumentException("Don't need to remove null");

		if(indexOf(o) >= 0){
			int index = indexOf(o);
			if(index == 0) removeFirst();
			else if(index == size-1) removeLast();
			else remove(index);
			return true;
		}
		else return false;
	}

	/**
	 * Removes all elements within the specific collection and return true
	 * if failed to remove any or partial elements in the list return false
	 * @param c specific collection
	 * @see URLinkedList.toArray()
	 * @see URLinkedList.contains()
	 * @see URLinkedList.remove()
	 * @throws NullPointerException
	 */
	public boolean removeAll(Collection<?> c){
		if(c == null) throw new NullPointerException("given collection is null");
		URNode<T> x = first;
		Object[] d = c.toArray();
		int len = d.length;
		int count = 0;
		for(;x != null;x = x.next()){
			T element = x.element();
			Object e = (Object) element;
			if(contains(e)){
				remove(e);
				count++;
			} 
		}
		if(count == len) return true;
		else return false;
	}

	/**
	 * Set index element to element, return the replaced (original) element
	 * @param index index of element
	 * @param element element want to replace
	 * @throws IndexOutOfBound 
	 */
	public T set(int index, T element){
		if(!(index >= 0 && index <=size)) throw new IndexOutOfBoundsException();

		URNode<T> x = node(index);
		T e = x.element();
		x.setElement(element);
		return e;
	}

	// Returns the number of elements in this list.
	public int size(){
		return size;
	}


	/**
	 * return a object array type containing all elements within the list
	 */
	public Object[] toArray(){
		Object[] arr = new Object[size];
		int index = 0;
		for(URNode<T> x = first; x != null; x = x.next()){
			arr[index] = x.element();
			index++;
		}
		return arr;
	}

	// Returns an iterator over the elements in this list in proper sequence.
	public Iterator<T> iterator(){
		
		return new Iterator<T>(){
			URNode<T> current = first;

			@Override
			public boolean hasNext(){
				return current != null;
			}

			@Override
			public T next(){
				if(hasNext()){
					T element = current.element();
					current = current.next();
					return element;
				}
				return null;
			}
			
		};
	}

	/**
	 * Return a subList between given indexes.
	 * @param fromIndex where the subList began
	 * @param toIndex where the subList ended
	 * @throws IndexOutOfBoundException
	 * @throws IllegalArgumentExcpetion
	 * @see URLinkedList.add()
	 */
	public URList<T> subList(int fromIndex, int toIndex){
		if(fromIndex < 0) throw new IndexOutOfBoundsException("index less than 0");
        if(toIndex > size) throw new IndexOutOfBoundsException("index provided larger than ArrayList's size");
        if(fromIndex > toIndex) throw new IllegalArgumentException("fromIndex cannot be larger than toIndex");
		URLinkedList<T> newList = new URLinkedList<>();
		for(int i = fromIndex; i < toIndex; i++){
			URNode<T> x = node(i);
			newList.add(x.element());
		}
		return newList;
	}


	public class URNode<E> { // Doubly linked list node

		private E e; // Value for this node
		private URNode<E> n; // Reference to next node in list
		private URNode<E> p; // Reference to previous node
	
		// Constructors
		URNode(E it, URNode<E> inp, URNode<E> inn) { e = it; p = inp; n = inn; }
		URNode(URNode<E> inp, URNode<E> inn) { p = inp; n = inn; }
	
		// Get and set methods for the data members
		public E element() { return e; } // Return the value
		public E setElement(E it) { return e = it; } // Set element value
		public URNode<E> next() { return n; } // Return next link
		public URNode<E> setNext(URNode<E> nextval) { return n = nextval; } // Set next link
		public URNode<E> prev() { return p; } // Return prev link
		public URNode<E> setPrev(URNode<E> prevval) { return p = prevval; } // Set prev link
	
	}
}

节点和回溯

在每个节点类中存储最短路径中的上一个节点 —> 循环直到遇到起始节点

//在字符串类型中创建最短路径
        StringBuilder rstr = new StringBuilder();
        Double len = end.getMinDistance() / 1.6;
        while(end.getPredecessor() != null){//循环备份直到找不到前辈(起点)
            rstr.append(end.getName());
            rstr.append(">");
            end = end.getPredecessor();
        }
        rstr.append(start.getName());
        String str = rstr.toString();
        String[] tokens = str.split(">"); //将反向排序的字符串拆分成部分

        //以交叉点名称构建最短路径
        String intersection = "";
        for(int i = tokens.length-1; i > 0; i--){
            intersection += tokens[i];
            intersection += " > ";
        }
        intersection += intersections.get(vertice2).getName();

        shortestPath = intersection;
        //以边缘名称构建最短路径
        String[] tokens2 = intersection.split(" > ");
        String paths = "";
        for(int i = 0; i < tokens2.length - 1; i++){
            for(int j = 0; j < road.size(); j++){
                if((tokens2[i].equals(road.get(j).getFrom().getName())&&tokens2[i + 1].equals(road.get(j).getTo().getName()))||
                (tokens2[i].equals(road.get(j).getTo().getName())&&tokens2[i + 1].equals(road.get(j).getFrom().getName()))){
                    paths += road.get(j).getName();
                    if(i+2<tokens2.length){//如果是最后一个,就不需要添加箭头指针
                        paths += " > ";
                    }
                    break;
                }
            }
        }

扩展和UI设计

  • 获取相对比率:[最大纬度] - [最小纬度];[最大经度] - [最小经度]。
  • 将结果反转以适应正常的xy坐标
  • 另一种方法:实现UI:拖放和缩放功能。
public class Graph{

    //Attributes
    public HashMap<String, Edge> edges;
    public HashMap<String, Node> intersections;
    Double minLatitude; Double minLongitude; Double maxLatitude; Double maxLongitude;
    String shortestPath;
    
    //Constructors
    public Graph(String shortestPath, Double minLatitude, Double minLongitude, Double maxLatitude, Double maxLongitude, HashMap<String, Node> intersections, HashMap<String, Edge> edge){
        
        this.shortestPath = shortestPath;
        this.edges = edge;
        this.intersections = intersections;
        this.minLatitude = minLatitude; this.maxLatitude = maxLatitude;
        this.minLongitude = minLongitude; this.maxLongitude = maxLongitude;
        
    }

    //Methods
    public void drawGUI(){
        JFrame frame = new JFrame();
        frame.setTitle("地图");
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        URPanel panel = new URPanel();
        frame.add(panel);
        frame.setVisible(true);
    }


    public class URPanel extends JPanel implements MouseWheelListener, MouseListener, MouseMotionListener{
        
        private double zoomFactor = 1;
        private double prevZoomFactor = 1;
        private boolean zoomer;
        private boolean dragger;
        private boolean released;
        private double ox = 0;
        private double oy = 0;
        private int xDiff;
        private int yDiff;
        private Point startPoint;

        //Constructor
        public URPanel(){
            initComponent();
        }

        private void initComponent(){
            addMouseWheelListener(this);
            addMouseListener(this);
            addMouseMotionListener(this);
        }

        @Override 
        public void paintComponent(Graphics g){

            //Make super finished all background constructing
            super.paintComponent(g);

            //Painting own graph && if --show only
            Graphics2D graph = (Graphics2D) g;
            
            //Zoom
            if(zoomer){
                AffineTransform at = new AffineTransform();
                
                double realX = MouseInfo.getPointerInfo().getLocation().getX() - getLocationOnScreen().getX();
                double realY = MouseInfo.getPointerInfo().getLocation().getY() - getLocationOnScreen().getY();
                double ratio = zoomFactor / prevZoomFactor;

                ox = (ratio) * (ox) + (1 - ratio) * realX;
                oy = (ratio) * (oy) + (1 - ratio) * realY;

                at.translate(ox, oy);
                at.scale(zoomFactor, zoomFactor);
                prevZoomFactor = zoomFactor;
                graph.transform(at);
                zoomer = false;
            }
            //Drag
            if(dragger){
                AffineTransform at = new AffineTransform();
                at.translate(ox + xDiff, oy + yDiff);
                at.scale(zoomFactor, zoomFactor);
                graph.transform(at);

                if(released){
                    ox += xDiff;
                    oy += yDiff;
                    dragger = false;
                }
            }

            graph.setColor(Color.black);
            graph.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);


            int width = this.getWidth();
            int height = this.getHeight();
            double ratio = Math.min(width / Math.abs(maxLatitude - minLatitude), height / Math.abs(maxLongitude - minLongitude));
            edges.forEach((String, Edge)->{
                Edge edge = edges.get(String);
                Node node1 = edge.getFrom();
                Node node2 = edge.getTo();
                double bounderX = maxLongitude - minLongitude;

                int startX = (int) ((node1.getLongitude() - minLongitude) * ratio);
    			int startY = (int) (this.getHeight() - ((node1.getLatitude() - minLatitude) * ratio));
    			
    			
    			int endX = (int) ((node2.getLongitude() - minLongitude) * ratio);
    			int endY = (int) (this.getHeight() - ((node2.getLatitude() - minLatitude) * ratio));
    
    			graph.drawLine(startX, startY, endX, endY);

                //Zoom in and zoom out
                

            });
            //If --show --direction/--direction --show is true
            if(shortestPath.length()!=0){
                String[] tokens = shortestPath.split(" > ");

                for(int i = 0; i < tokens.length - 1; i++){
                    Node node1= intersections.get(tokens[i]);
                    Node node2 = intersections.get(tokens[i+1]);

                    int startX = (int) ((node1.getLongitude() - minLongitude) * ratio);
    			    int startY = (int) (this.getHeight() - ((node1.getLatitude() - minLatitude) * ratio));
    			
    			
    			    int endX = (int) ((node2.getLongitude() - minLongitude) * ratio);
    			    int endY = (int) (this.getHeight() - ((node2.getLatitude() - minLatitude) * ratio));
    
                    graph.setColor(Color.red);
    			    graph.drawLine(startX, startY, endX, endY);  

                }
            }
            
        }
        @Override
        public void mouseWheelMoved(MouseWheelEvent e){

            zoomer = true;
            //Zoom in
            if (e.getWheelRotation() < 0) {
                zoomFactor *= 1.1;
                repaint();
            }
            //Zoom out
            if (e.getWheelRotation() > 0) {
                zoomFactor /= 1.1;
                repaint();
            }
        }
        
        @Override
        public void mouseDragged(MouseEvent e){
            Point curPoint = e.getLocationOnScreen();
            xDiff = curPoint.x - startPoint.x;
            yDiff = curPoint.y - startPoint.y;

            dragger = true;
            repaint();
        }
        @Override
        public void mousePressed(MouseEvent e){
            released = false;
            startPoint = MouseInfo.getPointerInfo().getLocation();
        }
        @Override
        public void mouseReleased(MouseEvent e){
            released = true;
            repaint();
        }
        @Override
        public void mouseEntered(MouseEvent e){}
        @Override
        public void mouseExited(MouseEvent e){}
        @Override
        public void mouseClicked(MouseEvent e){}
        @Override
        public void mouseMoved(MouseEvent e){}
    }

}

看这里,{{Voilà}}

只需创造一个简单的导航