001 package ps6; 002 003 import java.io.*; 004 import java.util.*; 005 006 import ps2.GeoPoint; 007 008 import ps4.*; 009 import ps6.tigerdb.*; 010 import ps6.tigerdb.DatabaseReader.GeoChain; 011 012 013 /** 014 * Functions as an iterator over the set of StreetSegments represented 015 * by the contents of the .zip files in a directory. 016 **/ 017 public class StreetSegIterator 018 extends ImmIterator<StreetSegment> 019 { 020 /** When true, zero-length street segments will be filtered out. 021 * Default is true. */ 022 private boolean filter_zero_length = true; 023 024 /** When true, filtering will be performed as determined by the 025 * killfile. Default is true. */ 026 private boolean filter_killfile = true; 027 028 /** When true, progress messages are sent to System.err as 029 * segments are read. Default is false. */ 030 private boolean mention_progress = false; 031 032 /** When true, warning messages are sent to System.err if segments 033 * are filtered. Default is false. */ 034 private boolean mention_filter = false; 035 036 /** When true, warning messages are sent to System.err if numbers 037 * are not disjoint. Default is false. */ 038 private boolean mention_non_disjoint = false; 039 040 /** 041 * @requires files != null && 042 * elements of files are of type java.io.File && 043 * elements of files are .zip files 044 * 045 * @effects creates a new iterator over which produces segments 046 * read in from the given files 047 **/ 048 public StreetSegIterator(Iterator<File> files, StreetSegmentFilter filter){ 049 this.files = files; 050 this.filter = filter; 051 } 052 053 private boolean initialized = false; 054 055 /** filter which lets caller provide a way to filter results */ 056 private StreetSegmentFilter filter; 057 058 /** .zip files to be read */ 059 private Iterator<File> files; 060 061 /** chains from the current file */ 062 private Iterator<GeoChain> chains; 063 064 /** next segment to be returned or null if there are no more */ 065 private StreetSegment next; 066 067 /** number of segments returned so far */ 068 private long total = 0; 069 070 public boolean hasNext(){ 071 if (!initialized) { 072 initialized = true; 073 next = nextSegment(); 074 } 075 076 return (next != null); 077 } 078 079 public StreetSegment next(){ 080 // standard iterator behavior 081 if (!hasNext()) { 082 throw new NoSuchElementException(); 083 } 084 085 // grab the segment to be returned, then advance to the next one 086 StreetSegment result = next; 087 next = nextSegment(); 088 089 // instrument reading process, because it's a bit slow 090 total++; 091 if (mention_progress && ((total % 10000) == 0)) { 092 System.err.println("Returning "+total+"th StreetSegment"); 093 System.err.flush(); 094 } 095 096 return result; 097 } 098 099 // @return the next segment from the files (post-filtering), or null if none exist 100 private StreetSegment nextSegment(){ 101 // grab the next chain from the file 102 GeoChain chain = nextChain(); 103 if (chain == null) { 104 return null; 105 } 106 107 // make a segment from it 108 StreetSegment candidate = makeSegment(chain); 109 110 // if segment could not be made, try again 111 if (candidate == null) { 112 return nextSegment(); 113 } 114 115 // if segment doesn't pass the filter, try again 116 if (filter_killfile && !filter.apply(candidate)) { 117 if (mention_filter) { 118 System.err.println("Filtered out: " + candidate); 119 } 120 return nextSegment(); 121 } 122 123 // otherwise, it was a good segment 124 return candidate; 125 } 126 127 128 /** 129 * Retrieve the next GeoChain contained in the file(s) 130 * 131 * @return the next GeoChain contained in the file(s), 132 * or null if there are no more files left 133 */ 134 private GeoChain nextChain(){ 135 // return a chain if we have one ... 136 if (chains != null && chains.hasNext()) { 137 return (GeoChain) chains.next(); 138 } 139 140 // else, advance to the next file... 141 if (!files.hasNext()) { 142 return null; 143 } 144 File fileToRead = (File) files.next(); 145 if (mention_progress) { 146 System.err.println("Reading from " + fileToRead); 147 System.err.flush(); 148 } 149 150 // ... and open it ... 151 try { 152 DatabaseReader dr = new DatabaseReader(); 153 chains = dr.geoChains(fileToRead); 154 } catch (IOException ioe) { 155 throw new RuntimeException("IOException: " + ioe.getMessage()); 156 } 157 158 // ... and try again 159 return nextChain(); 160 } 161 162 /** 163 * Create a Street Segment from a Geo Chain 164 * @param chain 165 * @return a segment created from the chain, 166 * or null if the segment is not desirable 167 */ 168 private StreetSegment makeSegment(GeoChain chain){ 169 GeoPoint p1 = chain.getRT1().getStart(); 170 GeoPoint p2 = chain.getRT1().getEnd(); 171 String name = chain.getRT1().getFeature().fullName(); 172 173 if (filter_zero_length && p1.equals(p2)) { 174 if (mention_filter) { 175 System.err.println("Filtered out zero-length segment named " + name); 176 System.err.flush(); 177 } 178 return null; 179 } 180 181 String lftAddr = chain.getLeftAddresses(); 182 String rgtAddr = chain.getRightAddresses(); 183 if (!chain.sidesDisjoint()) { 184 if (mention_non_disjoint) { 185 System.err.println("Numbers on " + name + " were not disjoint, so were changed to empty sets"); 186 System.err.flush(); 187 } 188 rgtAddr = lftAddr = ""; 189 } 190 191 StreetNumberSet leftSns = makeSNS(lftAddr); 192 StreetNumberSet rightSns = makeSNS(rgtAddr); 193 194 String leftZip = chain.getRT1().getLeftZip(); 195 String rightZip = chain.getRT1().getRightZip(); 196 197 StreetClassification streetClass = getStreetClass(chain); 198 boolean incAddr = areAddressesIncreasing(chain); 199 200 return new StreetSegment(p1, p2, name.intern(), leftSns, rightSns, 201 leftZip, rightZip, streetClass, incAddr); 202 } 203 204 205 private static final StreetNumberSet EMPTY_SNS = new StreetNumberSet(""); 206 private static StreetNumberSet makeSNS(String s) 207 { 208 if (s.length() == 0) return EMPTY_SNS; 209 return new StreetNumberSet(s); 210 } 211 212 private static StreetClassification getStreetClass(GeoChain gc) { 213 String s = gc.getRT1().getCfc().toLowerCase(); 214 215 if (s.charAt(0) == 'a' || s.charAt(0) == 'A') { 216 switch (s.charAt(1)) { 217 case '1': 218 case '2': 219 return StreetClassification.PRIM_HWY; 220 case '3': 221 return StreetClassification.SEC_HWY; 222 case '4': 223 return StreetClassification.LOCAL_ROAD; 224 default: 225 return StreetClassification.UNKNOWN; 226 } 227 } else { 228 return StreetClassification.UNKNOWN; 229 } 230 } 231 232 private static boolean areAddressesIncreasing(GeoChain gc) { 233 return gc.getRT1().getLeftRange().couldBeLowToHigh(); 234 } 235 }