source: tmcsimulator/trunk/src/tmcsim/highwaymodel/Highways.java @ 655

Revision 655, 15.4 KB checked in by jdalbey, 4 years ago (diff)

Improve unit tests.

Line 
1package tmcsim.highwaymodel;
2
3import tmcsim.highwaymodel.trafficeventseditor.TrafficLaneEvent;
4import tmcsim.highwaymodel.Station.DIRECTION;
5import java.io.File;
6import java.io.FileInputStream;
7import java.io.FileNotFoundException;
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.HashMap;
11import java.util.List;
12import java.util.Map;
13import java.util.Scanner;
14import java.util.Set;
15import java.util.logging.Level;
16import java.util.logging.Logger;
17
18/**
19 * Highways represents California state highways supervised by CalTrans.
20 * The highways have sensors to detect traffic flow.  In the simulation
21 * the traffic flow is artificially created and Highways maintains the
22 * current levels of traffic flow throughout the network.
23 *
24 * Highways builds an internal representation of the highway network
25 * from VDS data (obtained from PeMS) The current state of the network
26 * is output to a json file for use by CPTMS.
27 *
28 * @author John A. Torres, jdalbey
29 */
30final public class Highways
31{
32    final public List<Highway> highways;
33
34    public Highways(String highwaysMapFileName)
35    {
36       // build highways data structure
37        this.highways = buildHighwayNetwork(highwaysMapFileName);
38    }
39   
40    private ArrayList<Highway> buildHighwayNetwork(String highwaysMapFileName)
41    {
42        // NOTE: Could this method be streamlined? Is it necessary to have the
43        // second data structure?
44        System.out.println("Building highways...");
45        // The list of highways to return
46        ArrayList<Highway> highways = new ArrayList<Highway>();
47       
48        // map of hwy number to its list of stations
49        Map<Integer, ArrayList<Station>> highwayMap = new HashMap<>();
50       
51        // Open the postmile file and scan each line
52        File file = new File(highwaysMapFileName);
53        try 
54        {
55            Scanner scanner = new Scanner(file);
56            while (scanner.hasNext())
57            {
58                Station station = loadStation(scanner); 
59                Integer hwyNum = station.routeNumber;
60                // if the map does not contain an entry for the highway, create
61                // a new entry (key/value pair) for the highway and instantiate
62                // the empty list of stations
63                if (!highwayMap.containsKey(hwyNum))
64                {
65                    ArrayList<Station> stnList = new ArrayList<>();
66                    stnList.add(station);
67                    highwayMap.put(hwyNum, stnList);
68                } 
69                // if the map does have an entry for the highway, add the current
70                // station to its list of stations
71                else
72                {
73                    highwayMap.get(hwyNum).add(station);
74                }
75            }
76            scanner.close();
77        }catch (FileNotFoundException e) {
78            e.printStackTrace();
79        }
80       
81        // get the set of highway numbers
82        Set<Integer> hwyKeys = highwayMap.keySet();
83        // get the highway number and associated stations and create a new hwy
84        // and add the hwy to this.highways
85        for (Integer hwyKey : hwyKeys)
86        {
87            ArrayList<Station> hwyStations = highwayMap.get(hwyKey);
88            Collections.sort(hwyStations);
89            System.out.println("Loaded highway " + hwyKey + " with " +
90                    hwyStations.size() + " stations.");
91            highways.add(new Highway(hwyKey,
92                    hwyStations));
93        }
94        System.out.println("");
95        return highways;
96    }
97
98    /** Search for a station with the given attributes
99     *
100     * @param routeNumber
101     * @param direction
102     * @param postmile
103     * @return the desired station, or null if not found.
104     */
105    public Station findStation(Integer routeNumber, Station.DIRECTION direction,
106            Double postmile)
107    {
108        // Get the highway by route number
109        Highway highway = getHighwayByRouteNumber(routeNumber);
110        if (highway == null)
111        {
112            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, 
113                    "Highway "+routeNumber+" not found in findStation()", "");
114            return null;
115        }
116        //Search the stations on this highway for a match
117        for (Station station : highway.stations)
118        {
119            if (station.matches(direction, postmile))
120            {
121                return station;
122            }
123        }
124        return null;
125    }
126    /**
127     * Applies specified color to the specified highway stretch. Route number
128     * and direction specify the highway. Postmile and range specify the stretch
129     * of specified highway.
130     * The purpose of this method is to modify the highway state to represent
131     * traffic conditions created by the Traffic Manager.  These conditions
132     * originate in the traffic events file which specifies traffic congestion arising
133     * during the simulation.  NOTE: Since these events describe congestion,
134     * the direction that the color should be applied is the REVERSE of the
135     * regular flow of traffic. 
136     * So a request to apply red to 55 S from 8.5 for 2 miles means
137     * the stretch 8.5 -> 10.5 because sobo traffic normally flows toward
138     * decreasing postmiles and we want to do the reverse.
139     * @param routeNumber highway route number
140     * @param direction highway direction (for normal traffic flow)
141     * @param postmile origin postmile value
142     * @param range range from origin postmile
143     * @param dotColor the color to be applied to specified highway stretch
144     */
145    public void applyColorToHighwayStretch(Integer routeNumber, Station.DIRECTION direction,
146            Double postmile, Double range, LoopDetector.DOTCOLOR dotColor)
147    {
148        System.out.println("Applying " + dotColor.name() + " dots to highway "
149                + routeNumber + " " + direction.name() + " at postmile "
150                + postmile + " with a range of " + range + " miles...");
151
152        // Get the highway by route number
153        Highway highway = getHighwayByRouteNumber(routeNumber);
154        // This check added to fix defect #118. Handles situation when the
155        // events file specifies a highway that doesn't exist in the network.
156        if (highway == null)
157        {
158            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, 
159                    "Highway "+routeNumber+" not found trying to applyColor", "");
160            return;
161        }
162        // start value for highway section, and end value for highway section
163        // by postmile
164        Double startPost;
165        Double endPost;
166        double epsilon = 0.001;
167
168        // postmiles increase from s to n and w to e
169        // S or W directions backup in a positive postmile direction
170        if (direction.equals(Station.DIRECTION.SOUTH) || direction.equals(Station.DIRECTION.WEST))
171        {
172            // add range value to startPost to get
173            // the end postmile value of the highway section
174            startPost = postmile;
175            endPost = postmile + range;
176            // iterate through the stations, if within the specified highway
177            // stretch, update the station by direction and apply dot color
178            for (Station station : highway.stations)
179            {
180                if (station.postmile >= startPost && station.postmile <= endPost)
181                {
182                    station.updateByDirection(direction, dotColor);
183                }
184            }
185        } 
186        // N or E directions backup in a negative postmile direction
187        else
188        {
189            // subtract range value from startPost
190            // to get the end postmile value of the highway section
191            startPost = postmile;
192            endPost = postmile - range;
193
194            // iterate through the stations, if within the specified highway
195            // section, update the station by direction and apply dot color
196            for (Station station : highway.stations)
197            {
198                if (station.postmile <= startPost && station.postmile >= endPost)
199                {
200                    station.updateByDirection(direction, dotColor);
201                }
202            }
203        }
204        System.out.println("");
205    }
206   
207   
208    /**
209     * Loads a single Station from the postmile coordinates file
210     * @param sc scanner at the current station line
211     * @return Station
212     */
213    private Station loadStation(Scanner sc)
214    {
215        String line = sc.nextLine();
216        Scanner scline = new Scanner(line);
217        scline.useDelimiter(",");
218       
219        String route  = scline.next(); // FWY DIR POSTMILE
220        scline.next();  // skip lat
221        scline.next();  // skip long
222        String description = scline.next(); // the description of the location
223
224        Scanner rteScan = new Scanner(route);
225        int fwy = rteScan.nextInt();
226        DIRECTION dir = DIRECTION.toDirection(rteScan.next());
227        double postmile = rteScan.nextDouble();
228
229        ArrayList<LoopDetector> loops = new ArrayList<>();
230        // For now we'll use just a dummy loop detector
231        LoopDetector detector = new LoopDetector(1,"ML","ML_1");
232        loops.add(detector);
233
234        return new Station(11, 123, 99, description, loops, fwy, dir, postmile);
235    }
236     
237    /**
238     * Returns a highway by given highway number.
239     *
240     * @param routeNum
241     * @return Highway with specified route number, or null if no highway with
242     *          the specified route num
243     */
244    public Highway getHighwayByRouteNumber(Integer routeNum)
245    {
246        Highway returnHwy = null;
247        // search through highways and check routeNums
248        for (Highway hwy : highways)
249        {
250            if (hwy.routeNumber.equals(routeNum))
251            {
252                returnHwy = hwy;
253                break;
254            }
255        }
256        return returnHwy;
257    }
258
259    /** Return a string representation of the Highways - for debugging */
260    public String toString()
261    {
262        StringBuilder result = new StringBuilder();
263        for (Highway hwy: highways)
264        {
265            // Consider each route direction
266            for (DIRECTION dir: hwy.availDirs)
267            {
268                String rowLabel = ""+String.format("%3s ",hwy.routeNumber)+dir.getLetter()+' ';
269                StringBuilder lineout = new StringBuilder();
270                // Examine every station on this highway and direction
271                for (Station station: hwy.stations)
272                {
273                    if (station.direction.equals(dir))
274                    {
275                    //lineout.append("" + dir.getLetter() + stat.postmile);
276                    lineout.append(station.getColor().symbol());
277                    //lineout.append("  ");
278                    }
279                    else 
280                    {
281                        lineout.append(".");
282                    }
283                }
284                // See if there were stations for this direction
285                String checkMe = lineout.toString().trim();
286                // if any stations were colored, output the line
287                if (checkMe.length() > 1)
288                {
289                    result.append(rowLabel);
290                    result.append(lineout + "\n");
291                }
292            }
293        }
294        result.append("\n");
295        return result.toString();
296    }
297    /** Return a json representation of the Highways, readable by Google Maps */
298    public String toJson()
299    {
300        // TODO: move loading this file to init method so it doesn't get
301        // called every time.
302        PostmileCoords pmList = new PostmileCoords();
303        FileInputStream fis = null;
304        try
305        {
306            fis = new FileInputStream("config/vds_data/postmile_coordinates.txt");
307        }
308        catch (FileNotFoundException ex)
309        {
310            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
311        }
312        Scanner s = new Scanner(fis).useDelimiter("\\A");
313        pmList.load(s);
314       
315        Collections.sort(highways);  // Sort the highways for easier inspection
316        String header = "{\n" +
317        "  \"type\": \"FeatureCollection\",\n" +
318        "  \"features\": [";
319        StringBuilder result = new StringBuilder();
320        result.append(header);
321        for (Highway hwy: highways)
322        {
323            // Examine every station on this highway
324            StringBuilder lineout = new StringBuilder();
325            Collections.sort(hwy.stations, new StationComparator());
326            for (Station stat: hwy.stations)
327            {
328                String pmID = "" + hwy.routeNumber + " " 
329                        + stat.direction.getLetter() + " " 
330                        + stat.postmile;
331                PostmileCoords.Postmile currentPM = pmList.find(pmID);
332                if (currentPM == null)
333                { 
334                Logger.getLogger(Highways.class.getName()).log(Level.INFO, 
335                        "Postmile Coords lookup couldn't find Station: "+pmID,
336                        " ");
337                }
338                if (currentPM != null)
339                {   
340                    //lineout.append("" + dir.getLetter() + stat.postmile);
341                    //lineout.append(stat.getColorByDirection(dir));
342                    String outString = currentPM.toJson();
343                    // replace the color code with the color name
344                    String colorName=stat.getColor().htmlColor();
345                    outString = outString.replace("desiredcolor",colorName);
346                    lineout.append(outString);
347                    lineout.append("  ");
348                }
349            }
350            //result.append(rowLabel);
351            result.append(lineout + "\n");
352
353        }
354        // remove last trailing comma
355        result.replace(result.lastIndexOf(","), result.lastIndexOf(",") + 1, " "  );
356
357        result.append("  ]\n" +  "}");
358        return result.toString();
359    }
360   
361    /**
362     * Generates the route number list, used for user input validation.
363     * @return list of route numbers.
364     */
365    public List<Integer> getAllRouteNums()
366    {
367        ArrayList<Integer> routeNums = new ArrayList<>();
368        // add the route number for each highway to the list
369        for(Highway hwy : highways)
370        {
371            routeNums.add(hwy.routeNumber);
372        }
373        return routeNums;
374    }
375   
376    /**
377     * XML tags used in writeToXML()
378     */
379    private static enum XML_TAGS
380    {
381
382        NETWORK("Network");
383
384        String tag;
385
386        private XML_TAGS(String n)
387        {
388            tag = n;
389        }
390    }
391   
392    /** Reset all the traffic levels to free flowing (green) */
393    public void reset()           
394    {
395        for(Highway hwy: highways)
396        {
397            for(Station stn : hwy.stations)
398            {
399                for(LoopDetector ld : stn.loops)
400                {
401                    ld.setAttributes(LoopDetector.DOTCOLOR.GREEN);
402                }
403            }
404        }
405    }
406   
407    public void applyTrafficLaneEvent(TrafficLaneEvent event)
408    {
409        Integer routeNum = event.routeNum;
410        Highway hwy = getHighwayByRouteNumber(routeNum);
411        for(Station stn: hwy.stations)
412        {
413            if(stn.equals(event.station))
414            {
415                for(LoopDetector ld : stn.loops)
416                {
417                    if(ld.equals(event.loopDetector))
418                    {
419                        ld.occ = event.color.occupancy();
420                        ld.vol = event.color.volume();
421                        break;
422                    }
423                }
424                break;
425            }
426        }
427    }
428}
Note: See TracBrowser for help on using the repository browser.