package geography; import graph.*; import java.io.*; /** A street map. Supports address lookup and route planning. * The map can be constructed from a .map file, or by adding * StreetSegments one at a time. */ public class Streetmap { /** A directed graph of the streets. Each vertex holds a Point, * and is keyed by that Point so that we can look it up. * Each edge holds a string that is the street's name. */ protected KeyedDigraph g; /** An object that can look up addresses on the map. */ protected AddressFinder af; /** For finding the weights of edges in g. */ protected Weighter weighter; // ---------- CONSTRUCTORS AND METHODS FOR BUILDING THE MAP ---------- /** Constructs an empty streetmap (empty graph). */ public Streetmap() { g = new HashKeyedDigraph(new AdjListDigraph()); af = new AddressFinder(); weighter = ... FILL THIS IN -- AN APPROPRIATE SHORT DEFINITION OF WEIGHTER. // Hint: edgeLength() finds the length of an edge in meters. // Hint: Follow the examples in Dijkstra.main(). // Hint: Remember that to implement the Weighter interface, your weight() // method must take any Object but is allowed to try to cast it. } /** Constructs a streetmap from a .map file. */ public Streetmap(String filename) throws IOException, DataFormatException { this(); // start with empty map BufferedReader br = new BufferedReader(new FileReader(filename)); String s; int i = 0; while ((s = br.readLine()) != null) { addStreetSegment(new StreetSegment(s)); // print progress dots as we load if (++i % 100 == 0) System.out.print("."); } } /** Adds a new street segment to the map. */ protected void addStreetSegment(StreetSegment ss) { // Add two opposing directed edges to the graph, labeled by the street's name. FILL THIS IN -- 1 OR 2 LINES, USING METHODS OF g // Tell the address finder to remember the street name and house numbers from // this segment. FILL THIS IN -- 1 LINE, USING METHODS OF af } // ---------- METHODS FOR ROUTE PLANNING ---------- /** Return an address finder for this street map. (Surely a street map * should support address lookup by the user, even outside the context of * route planning.) */ public AddressFinder addressFinder() { return af; } /** Returns a shortest route between two points. * @throws NoSuchVertexException if the points are not * both vertices of our map graph. */ public Route shortestRoute(Point pStart, Point pEnd) throws NoSuchVertexException { FILL THIS IN -- 2 OR 3 LINES HERE, USING YOUR DIJKSTRA CLASS } /** Returns a shortest route between two intersections. * This is just a convenience method. * * @throws NoSuchVertexException if either intersection is * null, or is not on our map graph. */ public Route shortestRoute(Intersection isctStart, Intersection isctEnd) throws NoSuchVertexException { if (isctStart==null || isctEnd==null) throw new NoSuchVertexException("Null intersection"); return shortestRoute(isctStart.point, isctEnd.point); } /** Given two addresses, returns a shortest route between * them. Well, anyway, returns a shortest route between * intersections near them. * * @throws NoSuchVertexException if either address cannot * be found on this street map, even approximately * (e.g., if the map is empty). */ public Route shortestRoute(String addrStart, String addrEnd) throws DataFormatException, NoSuchVertexException { return shortestRoute(af.findIntersectionNear(addrStart), af.findIntersectionNear(addrEnd)); } // ---------- STATIC METHODS FOR GETTING INFORMATION ABOUT EDGES ---------- /** Given an edge of g, returns the length in meters of * the street segment it represents. * * At present, we just consider the distance between the segment's * endpoints. Note that this will underestimate the length of curvy * streets. (Data about street shape are actually available, but * we're ignoring those data for this assignment.) */ public static double edgeLength(DirectedEdge e) { Point pStart = (Point) e.origin().element(); Point pEnd = (Point) e.destination().element(); return pStart.distanceTo(pEnd); } /** Given an edge of g, returns the direction of the * street segment it represents, as a number in degrees * from -180 to 180. See {@link Point#directionTo}. */ public static double edgeDirection(DirectedEdge e) { Point pStart = (Point) e.origin().element(); Point pEnd = (Point) e.destination().element(); double d = pStart.directionTo(pEnd); return pStart.directionTo(pEnd); } // ---------- POTENTIALLY USEFUL TEST METHODS ---------- /** Get an address from the user on the input stream underlying br, * and return the corresponding intersection if we can find it. * If we can't find it, prompt the user to try again. * Returns null if the user's input is empty. */ public Intersection readIntersection(BufferedReader br, String prompt) throws IOException { Intersection isct = null; while (isct==null) { System.out.print(prompt); String address = br.readLine(); if (address.trim().equals("")) // empty input -- just quit return null; System.out.print(" "); try { FILL THIS IN -- 1 LINE, USING METHODS OF af TO SET isct if (isct==null) System.out.println("Sorry, couldn't find any address near there."); } catch (DataFormatException e) { System.out.println("Sorry, couldn't parse that address."); } } System.out.println("Found nearby intersection at "+isct); return isct; } /** A simple interaction with the user. Prompts the user * for two addresses, and prints the shortest route between * them. * * There is a single command-line argument -- the name of the * .map file to build the Streetmap from. * * This method could be improved by detecting error conditions * (the exceptions listed below, as well as too many arguments) and * exiting gracefully with an error message. */ public static void main(String args[]) throws IOException, DataFormatException, ArrayIndexOutOfBoundsException { // Set up by reading the map in. System.out.print("Reading map from "+args[0]+" (may take a moment) "); Streetmap stmap = new Streetmap(args[0]); // might throw various exceptions System.out.println("\nBuilt graph with "+stmap.g.numEdges()+" edges and " +stmap.g.numVertices()+" vertices."); System.out.println("Address finder knows "+stmap.af.numKnownStreets()+" street names."); System.out.println("Welcome to the route planner!\n"); // Repeatedly ask the user for 2 intersections, and print the route between them. BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); while (true) { // until we explicitly break because user quits Intersection isctStart = stmap.readIntersection(br, "Enter start address: "); if (isctStart==null) break; Intersection isctEnd = stmap.readIntersection(br, "Enter destination address: "); if (isctEnd==null) break; try { FILL THIS IN -- 1 OR 2 LINES THAT FIND AND PRINT THE SHORTEST ROUTE BETWEEN THESE INTERSECTIONS. // Note: The method that you will call might throw a NoSuchVertexException, // so we'll catch that below. } catch (NoSuchVertexException e) { System.out.println("Address finder is inconsistent with map graph -- I was able" +" to find the\n intersections geographically but they were not" +" graph vertices. Sorry."); } System.out.println("\nOkay, try another ..."); } System.out.println("\nHave a good trip, and don't fall asleep at the wheel!"); } }