package geography; import java.util.Iterator; import java.io.*; /** A street segment corresponding to a line in a .map file. This * class exists specifically to read the data in .map files. Such * files were constructed just for this homework, by taking a simple * subset of the TIGER/Line data publicly available from the US Census * Bureau. * * This class is not public. It is only visible within the geography package. */ class StreetSegment { // ---------- INSTANCE VARIABLES ---------- /** Name of the street, e.g., "N Charles St". May be "" if unknown. */ String name; /** Base name of the "superstreet" that this is part of, e.g., "Charles St". * Will be null for most streets. */ String nameSuper; /** Physical start and end points of this segment */ Point pStart, pEnd; /** House numbers nearest the pStart end of this segment, * on the left and right sides. Similarly for the pEnd end * of this segment. May be null if there are no buildings on * that side of the street. */ Integer housenumLeftStart, housenumRightStart; Integer housenumLeftEnd, housenumRightEnd; // ---------- CONSTRUCTOR ---------- /** Constructs a StreetSegment from a line in a .map file. */ public StreetSegment(String line) throws DataFormatException { java.util.StringTokenizer st = new java.util.StringTokenizer(line,"\t\n"); try { pStart = new Point(longlat(st.nextToken().trim()), longlat(st.nextToken().trim())); pEnd = new Point(longlat(st.nextToken().trim()), longlat(st.nextToken().trim())); housenumLeftStart = houseNum(st.nextToken().trim()); housenumRightStart = houseNum(st.nextToken().trim()); housenumLeftEnd = houseNum(st.nextToken().trim()); housenumRightEnd = houseNum(st.nextToken().trim()); String arm = st.nextToken().trim(); // arm of the street, e.g., "N" name = st.nextToken().trim(); String type = st.nextToken().trim(); // St, Rd, Ave, etc. if (name.equals("") && !(arm.equals("") && type.equals(""))) throw new DataFormatException("Unnamed street has a name modifier:\n"+line); if (!type.equals("")) // we have a type name += " "+type; if (!arm.equals("")) { // we are an arm of a larger street nameSuper = name; name = arm+" "+name; } } catch (java.util.NoSuchElementException e) { throw new DataFormatException("Line too short (expected 11 tab-separated fields):\n"+line); } catch (NumberFormatException e) { throw new DataFormatException("One of the numbers wasn't an integer" +" (nor even \"\" in the case of a house number): " +e+ "\n"+line); } if (st.hasMoreTokens()) throw new DataFormatException("Line too long (expected 11 tab-separated fields):\n"+line); } /** Copy constructor. */ public StreetSegment(StreetSegment ss) { name = ss.name; nameSuper = ss.nameSuper; pStart = ss.pStart; pEnd = ss.pEnd; housenumLeftStart = ss.housenumLeftStart; housenumRightStart = ss.housenumRightStart; housenumLeftEnd = ss.housenumLeftEnd; housenumRightEnd = ss.housenumRightEnd; } // ---------- METHODS ---------- /* Makes a new version of the street segment, but the * name is replaced by the superstreet's name. * Returns null if there is no superstreet. */ public StreetSegment makeSuperStreetSegment() { if (nameSuper==null) return null; StreetSegment ss = new StreetSegment(this); // a copy ss.name = nameSuper; ss.nameSuper = null; return ss; } // ---------- PRIVATE METHODS USED IN PARSING STRINGS ---------- /** Converts a longitude or latitude string like +39210703 * into a double like 39.210703. */ private double longlat(String s) throws NumberFormatException { if (s.charAt(0)=='+') // skip leading + sign s = s.substring(1); return Integer.parseInt(s) / 1000000.0; } /** Converts a house number string like "323" or "" into * an Integer like 323 or null, respectively. * * Occasionally a number may be a hyphenated range, like "5916-5900", * in which case we arbitrarily take the start of the range, i.e., 5916. */ private Integer houseNum(String s) throws NumberFormatException { s = s.trim(); if (s.equals("")) return null; int iHyphen = s.indexOf('-'); if (iHyphen != -1) // if there's a hyphen s = s.substring(0,iHyphen); // keep only the part before the hyphen return new Integer(Integer.parseInt(s)); } }