source: tmcsimulator/trunk/src/atmsdriver/model/Highways.java @ 243

Revision 243, 23.3 KB checked in by jdalbey, 7 years ago (diff)

Highways.java: Added toJson() method.

Line 
1package atmsdriver.model;
2
3import atmsdriver.trafficeventseditor.TrafficLaneEvent;
4import atmsdriver.model.LoopDetector.DOTCOLOR;
5import atmsdriver.model.Station.DIRECTION;
6import java.io.File;
7import java.io.FileInputStream;
8import java.io.FileNotFoundException;
9import java.io.IOException;
10import java.io.PrintWriter;
11import java.io.StringWriter;
12import java.io.Writer;
13import java.net.Socket;
14import java.util.ArrayList;
15import java.util.Collections;
16import java.util.HashMap;
17import java.util.List;
18import java.util.Map;
19import java.util.Scanner;
20import java.util.Set;
21import java.util.logging.Level;
22import java.util.logging.Logger;
23import javax.xml.parsers.DocumentBuilder;
24import javax.xml.parsers.DocumentBuilderFactory;
25import javax.xml.transform.OutputKeys;
26import javax.xml.transform.Transformer;
27import javax.xml.transform.TransformerFactory;
28import javax.xml.transform.dom.DOMSource;
29import javax.xml.transform.stream.StreamResult;
30import org.w3c.dom.Document;
31import org.w3c.dom.Element;
32import tmcsim.common.SimulationException;
33
34/**
35 * The Highways class aggregates all Highway instances within a geographic
36 * region, and all of the FEPLines within an electronic detector network, in the
37 * same geographic region. An instance of Highways.java comprises the underlying
38 * model for the ATMSDriver application.
39 *
40 * Highways uses method writeToFEP() to communicate with the FEP Simulator. It
41 * creates a socket client which sends the FEP Simulator a highways status
42 * message over the socket. This message is sent in the format required by the
43 * FEP Simulator.
44 *
45 * Currently, there is no driving logic within the highways class. It is only
46 * done through an instance of the ConsoleDriver.java class. Eventually, it will
47 * receive incident data (from exchange.xml or better yet, directly from the
48 * TMCSimulator itself) and drive the highways using resident logic.
49 *
50 * @author John A. Torres
51 */
52final public class Highways
53{
54
55    final private String FEPHostName;
56    final private int FEPPortNum;
57   
58    final private List<FEPLine> lines;
59    final public List<Highway> highways;
60
61    public Highways(String highwaysMapFileName, String FEPHostName, int FEPPortNum)
62    {
63        // load FEP Lines
64        lines = loadLines(highwaysMapFileName);
65        // configure and load highways
66        this.highways = configureHighways();
67
68        // write to FEP host and port number
69        this.FEPHostName = FEPHostName;
70        this.FEPPortNum = FEPPortNum;
71    }
72
73    private ArrayList<Highway> configureHighways()
74    {
75        System.out.println("Loading highways...");
76        // The list of highways to return
77        ArrayList<Highway> highways = new ArrayList<Highway>();
78       
79        // map of hwy number to its list of stations
80        Map<Integer, ArrayList<Station>> highwayMap = new HashMap<>();
81       
82        // iterate through FEPLines and get data to add to the above map
83        for (FEPLine line : lines)
84        {
85            // grab all stations from the current FEPLine
86            ArrayList<Station> lineStations = (ArrayList<Station>) line.stations;
87            // iterate through each station in the list of stations
88            for (Station station : lineStations)
89            {
90                Integer hwyNum = station.routeNumber;
91               
92                // if the map does not contain an entry for the highway, create
93                // a new entry (key/value pair) for the highway and instantiate
94                // the empty list of stations
95                if (!highwayMap.containsKey(hwyNum))
96                {
97                    ArrayList<Station> stnList = new ArrayList<>();
98                    stnList.add(station);
99                    highwayMap.put(hwyNum, stnList);
100                } 
101                // if the map does have an entry for the highway, add the current
102                // station to its list of stations
103                else
104                {
105                    highwayMap.get(hwyNum).add(station);
106                }
107            }
108        }
109       
110        // get the set of highway numbers
111        Set<Integer> hwyKeys = highwayMap.keySet();
112        // get the highway number and associated stations and create a new hwy
113        // and add the hwy to this.highways
114        for (Integer hwyKey : hwyKeys)
115        {
116            ArrayList<Station> hwyStations = highwayMap.get(hwyKey);
117            Collections.sort(hwyStations);
118            System.out.println("Loaded highway " + hwyKey + "...");
119            highways.add(new Highway(hwyKey,
120                    hwyStations));
121        }
122        System.out.println("");
123        return highways;
124    }
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. Dot color is the color to be applied to the
130     * stretch.
131     *
132     * @param routeNumber highway route number
133     * @param direction highway direction
134     * @param postmile origin postmile value
135     * @param range range from origin postmile
136     * @param dotColor the color to be applied to specified highway stretch
137     */
138    public void applyColorToHighwayStretch(Integer routeNumber, Station.DIRECTION direction,
139            Double postmile, Double range, LoopDetector.DOTCOLOR dotColor)
140    {
141        System.out.println("Applying " + dotColor.name() + " dots to highway "
142                + routeNumber + " " + direction.name() + " at postmile "
143                + postmile + " with a range of " + range + " miles...");
144
145        // Get the highway by route number
146        Highway highway = getHighwayByRouteNumber(routeNumber);
147
148        // start value for highway section, and end value for highway section
149        // by postmile
150        Double startPost;
151        Double endPost;
152
153        // postmiles increase from s to n and w to e
154        // if the direction is south or west
155        if (direction.equals(Station.DIRECTION.SOUTH) || direction.equals(Station.DIRECTION.WEST))
156        {
157            // add range value to startPost to get
158            // the end postmile value of the highway section
159            startPost = postmile;
160            endPost = postmile + range;
161
162            // iterate through the stations, if within the specified highway
163            // stretch, update the station by direction and apply dot color
164            for (Station station : highway.stations)
165            {
166                if (station.postmile >= startPost && station.postmile <= endPost)
167                {
168                    station.updateByDirection(direction, dotColor);
169                }
170            }
171        } // if the direction is north or east
172        else
173        {
174            // subtract range value from startPost
175            // to get the end postmile value of the highway section
176            startPost = postmile;
177            endPost = postmile - range;
178
179            // iterate through the stations, if within the specified highway
180            // section, update the station by direction and apply dot color
181            for (Station station : highway.stations)
182            {
183                if (station.postmile <= startPost && station.postmile >= endPost)
184                {
185                    station.updateByDirection(direction, dotColor);
186                }
187            }
188        }
189        System.out.println("");
190    }
191   
192    /**
193     * Loads all FEPLines from the specified highways map file.
194     *
195     * @param highwaysMapFileName
196     * @return List of FEPLines
197     */
198    private ArrayList<FEPLine> loadLines(String highwaysMapFileName)
199    {
200        ArrayList<FEPLine> lines = new ArrayList<>();
201        try
202        {
203            Scanner sc = new Scanner(new File(highwaysMapFileName));
204            // first line of file contains number of FEP Lines
205            String firstLine = sc.nextLine();
206            Scanner linesc = new Scanner(firstLine);
207            int numLines = linesc.nextInt();
208            linesc.close();
209            // FOR each FEP Line
210            for (int i = 0; i < numLines; i++)
211            {
212                lines.add(loadLine(sc));
213            }
214            sc.close();
215
216        } catch (FileNotFoundException ex)
217        {
218            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
219        }
220        return lines;
221    }
222   
223    /**
224     * Loads a single FEP Line from the highways map file.
225     *
226     * @param sc scanner at the current FEPLine line
227     * @return FEPLine
228     */
229    private FEPLine loadLine(Scanner sc)
230    {
231        String line = sc.nextLine();
232        Scanner scline = new Scanner(line);
233        // Get the attributes of this FEP Line
234        int lineNum = scline.nextInt();
235        int count = scline.nextInt();
236        int numStations = scline.nextInt();
237       
238        // initialze stations array
239        ArrayList<Station> stations = new ArrayList<>();
240        // Read all the stations for thie FEP Line
241        for (int i = 0; i < numStations; i++)
242        {
243            stations.add(loadStation(sc, lineNum));
244        }
245
246        return new FEPLine(lineNum, stations, count);
247    }
248   
249    /**
250     * Loads a single Station from the highways map file
251     * @param sc scanner at the current station line
252     * @param lineNum the FEPLine number for the station
253     * @return Station
254     */
255    private Station loadStation(Scanner sc, int lineNum)
256    {
257        String line = sc.nextLine();
258        Scanner scline = new Scanner(line);
259       
260        int ldsID = scline.nextInt();
261        int drop = scline.nextInt();
262        int fwy = scline.nextInt();
263        DIRECTION dir = DIRECTION.toDirection(scline.next());
264        double postmile = scline.nextDouble();
265        int numLoops = scline.nextInt();
266        String location = getStationLoc(line);
267        ArrayList<LoopDetector> loops = new ArrayList<>();
268        for (int i = 0; i < numLoops; i++)
269        {
270            loops.add(loadLoop(sc));
271        }
272
273        return new Station(lineNum, ldsID, drop, location, loops, fwy, dir, postmile);
274    }
275   
276    /**
277     * Loads a single loop from the highways map file
278     *
279     * @param sc scanner at the current loop line
280     * @return LoopDetector
281     */
282    private LoopDetector loadLoop(Scanner sc)
283    {
284        String line = sc.nextLine();
285        Scanner scline = new Scanner(line);
286
287        int loopID = scline.nextInt();
288        String loopLocID = scline.next();
289        String loopLoc = scline.next();
290        scline.close();
291        return new LoopDetector(loopID, loopLocID, loopLoc);
292    }
293
294    /**
295     * Scans the LoopDetector line and grabs the String location from the line.
296     *
297     * @param line the line containing the location
298     * @return A String loop location.
299     */
300    private String getLoopLoc(String line)
301    {
302        Scanner sc = new Scanner(line);
303        sc.nextInt();
304
305     // GRABS FROM CURRENT TO END OF LINE
306        sc.useDelimiter("\\z");
307        String loc = sc.next().trim();
308        sc.close();
309        return loc;
310    }
311
312    /**
313     * Scans the Station line and grabs the String location from the line.
314     *
315     * @param line the line containing the location
316     * @return A String station location.
317     */
318    private String getStationLoc(String line)
319    {
320        Scanner scline = new Scanner(line);
321        scline.nextInt();
322        scline.nextInt();
323        scline.nextInt();
324        scline.next();
325        scline.nextDouble();
326        scline.nextInt();
327
328        // GRABS FROM CURRENT TO END OF LINE
329        scline.useDelimiter("\\z");
330        String loc = scline.next().trim();
331        scline.close();
332        return loc;
333    }
334   
335    /**
336     * Creates a socket client that writes the Highways data to the FEP Simulator.
337     *
338     * @throws SimulationException
339     */
340    public void writeToFEP() throws SimulationException
341    {
342        try
343        {
344            // Create the socket to the FEP Simulator
345            Socket sock = new Socket(FEPHostName, FEPPortNum);
346            PrintWriter out = new PrintWriter(sock.getOutputStream(), true);
347           
348            // Print the number of bytes the highways data message contains
349            System.out.println("BYTES: " + this.toCondensedFormat(false).toCharArray().length + 1);
350           
351            // Write the highways data over the socket
352            out.println(this.toCondensedFormat(false));
353           
354            // close the socket
355            sock.close();
356        } catch (java.net.ConnectException ex)
357        {
358            //Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
359            System.out.println("writeToFEP() can't connect, no data sent to FEP.");
360            throw new SimulationException(SimulationException.BINDING);
361        } catch (IOException ex)
362        {
363            //Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
364            System.out.println("Highway Model failed writing to FEPSim.");
365            throw new SimulationException(SimulationException.BINDING);
366        }
367    }
368   
369    /** Returns a string of highways data. If MetaDataOnly is true, you get a full
370     *  dump of the highways meta data, which does not include dynamic loop values,
371     *  and does include the string location names. If MetaDataOnly is false,
372     *  dynamic loop values are included, and unnecessary information like string
373     *  location values are included.
374     *
375     *  The FEPSimulator takes in the toCondensedFormat() output, with a MetaDataOnly
376     *  value of false, over the socket.
377     *
378     *  The MetaDataOnly flag should be used to get a full dump of the highways
379     *  information. This was used to get the highways_fullmap.txt output.
380     *
381     * @param MetaDataOnly Whether you want meta data, or a full dump for FEPSim
382     * @return String, highways data in condensed format
383     *
384     * Example toCondensedFormat(MetaDataOnly = false) output:
385     *
386     * 43                       // "number of lines"
387     * 32 0 13                  // "line id" "count num" "number of stations"
388     * 1210831 1 5 S 0.9 8      // "station id" "drop num" "route num"...
389     *                          //      ..."direction" "postmile" "number of loops"
390     * 1210832  0.0 0  ML_1     // "loop id" "occ" "vol"
391     * 1210833  0.0 0  ML_2     // ..
392     * 1210834  0.0 0  ML_3     // ..
393     * 1210835  0.0 0  ML_4     // ..
394     * 1210836  0.0 0  PASSAGE  // ..
395     * 1210837  0.0 0  DEMAND   // ..
396     * 1210838  0.0 0  QUEUE    // ..
397     * 1210839  0.0 0  RAMP_OFF // ..
398     * ...
399     *
400     * Example toCondensedFormat(MetaDataOnly = true) output:
401     *
402     * 43                           // "number of lines"
403     * 32 0 13                      // "line id" "count num" "number of stations"
404     * 1210831 1 5 S 0.9 8 CALAFIA  // "station id" "drop num" "route num"...
405     *                              //      ..."direction" "postmile"...
406     *                              //      ..."number of loops" "string location"
407     * 1210832 ML_1                 // "loop id" "loop location"
408     * 1210833 ML_2                 // "            "
409     * 1210834 ML_3                 // "            "
410     * 1210835 ML_4                 // "            "
411     * 1210836 PASSAGE              // "            "
412     * 1210837 DEMAND               // "            "
413     * 1210838 QUEUE                // "            "
414     * 1210839 RAMP_OFF             // "            "
415     * ...
416     */
417    public String toCondensedFormat(boolean MetaDataOnly)
418    {
419        // first line: number of FEPLines
420        StringBuilder build = new StringBuilder();
421        build.append(lines.size());
422        build.append("\n");
423        // append each fep line to the string
424        for(FEPLine line : lines)
425        {
426            build.append(line.toCondensedFormat(MetaDataOnly));
427        }
428        // return the full condensed format string
429        return build.toString();
430    }
431   
432    /**
433     * Returns the Highways model data in XML format.
434     *
435     * @return highways data in XML format
436     */
437    public String toXML()
438    {
439        String xml = null;
440        try
441        {
442            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
443            DocumentBuilder builder = factory.newDocumentBuilder();
444            Document theDoc = builder.newDocument();
445
446            Element networkElement = theDoc.createElement(XML_TAGS.NETWORK.tag);
447            theDoc.appendChild(networkElement);
448
449            for (FEPLine line : lines)
450            {
451                line.toXML(networkElement);
452            }
453
454            Transformer tf = TransformerFactory.newInstance().newTransformer();
455
456            tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
457            tf.setOutputProperty(OutputKeys.INDENT, "yes");
458            tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
459
460            Writer out = new StringWriter();
461            tf.transform(new DOMSource(theDoc), new StreamResult(out));
462            xml = out.toString();
463            out.close();
464        } catch (Exception ex)
465        {
466            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
467        }
468        return xml;
469
470    }
471
472    /**
473     * Returns a highway by given highway number.
474     *
475     * @param routeNum
476     * @return Highway with specified route number, or null if no highway with
477     *          the specified route num
478     */
479    public Highway getHighwayByRouteNumber(Integer routeNum)
480    {
481        Highway returnHwy = null;
482        // search through highways and check routeNums
483        for (Highway hwy : highways)
484        {
485            if (hwy.routeNumber.equals(routeNum))
486            {
487                returnHwy = hwy;
488                break;
489            }
490        }
491        return returnHwy;
492    }
493
494    /** Return a string representation of the Highways */
495    public String toString()
496    {
497        StringBuilder result = new StringBuilder();
498        for (Highway hwy: highways)
499        {
500            // Consider each route direction
501            for (DIRECTION dir: DIRECTION.values())
502            {
503                String rowLabel = ""+String.format("%3s ",hwy.routeNumber)+dir.getLetter()+' ';
504                StringBuilder lineout = new StringBuilder();
505                // Examine every station on this highway and direction
506                for (Station stat: hwy.stations)
507                {
508                    //lineout.append("" + dir.getLetter() + stat.postmile);
509                    lineout.append(stat.getColorByDirection(dir));
510                    //lineout.append("  ");
511                }
512                // See if there were stations for this direction
513                String checkMe = lineout.toString().trim();
514                // if any stations were colored, output the line
515                if (checkMe.length() > 1)
516                {
517                    result.append(rowLabel);
518                    result.append(lineout + "\n");
519                }
520            }
521        }
522        result.append("\n");
523        return result.toString();
524    }
525    /** Return a json representation of the Highways, readable by Google Maps */
526    public String toJson()
527    {
528        PostmileCoords pmList = new PostmileCoords();
529        FileInputStream fis = null;
530        try
531        {
532            fis = new FileInputStream("config/vds_data/postmile_coordinates.txt");
533        }
534        catch (FileNotFoundException ex)
535        {
536            Logger.getLogger(Highways.class.getName()).log(Level.SEVERE, null, ex);
537        }
538        Scanner s = new Scanner(fis).useDelimiter("\\A");
539        pmList.load(s);
540        String header = "{\n" +
541        "  \"type\": \"FeatureCollection\",\n" +
542        "  \"features\": [";
543        StringBuilder result = new StringBuilder();
544        result.append(header);
545        for (Highway hwy: highways)
546        {
547            // Consider each route direction
548            //for (DIRECTION dir: DIRECTION.values())
549            // TODO: Needs fixing so proper direction is displayed
550            Station.DIRECTION dir = Station.DIRECTION.SOUTH;
551            if (hwy.routeNumber == 55)
552                dir = Station.DIRECTION.SOUTH;
553            if (hwy.routeNumber == 405)
554                dir = Station.DIRECTION.NORTH;
555            {
556                String rowLabel = ""+String.format("%3s ",hwy.routeNumber)+dir.getLetter()+' ';
557                StringBuilder lineout = new StringBuilder();
558                // Examine every station on this highway and direction
559                for (Station stat: hwy.stations)
560                {
561                    String pmID = "" + hwy.routeNumber + " " + stat.postmile;
562                    PostmileCoords.Postmile currentPM = pmList.find(pmID);
563                    if (currentPM != null)
564                    {   
565                    //lineout.append("" + dir.getLetter() + stat.postmile);
566                    //lineout.append(stat.getColorByDirection(dir));
567                    String outString = currentPM.toJson();
568                    String colorName="";
569                    switch (stat.getColorByDirection(dir))
570                    {
571                        case '@': colorName = "red";break;
572                        case '+': colorName = "yellow"; break;
573                        case '-': colorName = "green";break;
574                        case ' ': colorName = "green";break;
575                    }
576                    outString = outString.replace("green",colorName);
577                    lineout.append(outString);
578                    lineout.append("  ");
579                    }
580                }
581                                // See if there were stations for this direction
582                String checkMe = lineout.toString().trim();
583                // if any stations were colored, output the line
584                if (checkMe.length() > 1)
585                {
586                    //result.append(rowLabel);
587                    result.append(lineout + "\n");
588                }
589
590            }
591        }
592        // remove last trailing comma
593        result.replace(result.lastIndexOf(","), result.lastIndexOf(",") + 1, " "  );
594
595        result.append("  ]\n" +  "}");
596        return result.toString();
597    }
598   
599    /**
600     * Generates the route number list, used for user input validation.
601     * @return list of route numbers.
602     */
603    public List<Integer> getAllRouteNums()
604    {
605        ArrayList<Integer> routeNums = new ArrayList<>();
606        // add the route number for each highway to the list
607        for(Highway hwy : highways)
608        {
609            routeNums.add(hwy.routeNumber);
610        }
611        return routeNums;
612    }
613   
614    /**
615     * XML tags used in writeToXML()
616     */
617    private static enum XML_TAGS
618    {
619
620        NETWORK("Network");
621
622        String tag;
623
624        private XML_TAGS(String n)
625        {
626            tag = n;
627        }
628    }
629   
630    public void reset()
631    {
632        for(FEPLine line : lines)
633        {
634            for(Station stn : line.stations)
635            {
636                for(LoopDetector ld : stn.loops)
637                {
638                    ld.occ = 0;
639                    ld.vol = 0;
640                }
641            }
642        }
643    }
644   
645    public void applyTrafficLaneEvent(TrafficLaneEvent event)
646    {
647        Integer routeNum = event.routeNum;
648        Highway hwy = getHighwayByRouteNumber(routeNum);
649        for(Station stn: hwy.stations)
650        {
651            if(stn.equals(event.station))
652            {
653                for(LoopDetector ld : stn.loops)
654                {
655                    if(ld.equals(event.loopDetector))
656                    {
657                        ld.occ = event.color.occupancy();
658                        ld.vol = event.color.volume();
659                        break;
660                    }
661                }
662                break;
663            }
664        }
665    }
666}
Note: See TracBrowser for help on using the repository browser.