package com.schneeloch.bostonbusmap_library.parser; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.schneeloch.bostonbusmap_library.data.BusLocation; import com.schneeloch.bostonbusmap_library.data.CommuterRailPrediction; import com.schneeloch.bostonbusmap_library.data.CommuterTrainLocation; import com.schneeloch.bostonbusmap_library.data.Direction; import com.schneeloch.bostonbusmap_library.data.Directions; import com.schneeloch.bostonbusmap_library.data.PredictionStopLocationPair; import com.schneeloch.bostonbusmap_library.data.RouteConfig; import com.schneeloch.bostonbusmap_library.data.RouteTitles; import com.schneeloch.bostonbusmap_library.data.StopLocation; import com.schneeloch.bostonbusmap_library.data.TransitSourceCache; import com.schneeloch.bostonbusmap_library.data.VehicleLocations; import com.schneeloch.bostonbusmap_library.database.Schema; import com.schneeloch.bostonbusmap_library.util.LogUtil; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.List; import java.util.Map; import java.util.Set; public class CommuterRailPredictionsFeedParser { private final RouteConfig routeConfig; private final Directions directions; private final VehicleLocations busMapping; private final RouteTitles routeKeysToTitles; /** * Keep this value consistent while reading data */ private final long currentTimeMillis; public CommuterRailPredictionsFeedParser(RouteConfig routeConfig, Directions directions, VehicleLocations busMapping, RouteTitles routeKeysToTitles) { this.routeConfig = routeConfig; this.directions = directions; this.busMapping = busMapping; this.routeKeysToTitles = routeKeysToTitles; this.currentTimeMillis = System.currentTimeMillis(); } private void clearPredictions() throws IOException { for (StopLocation stopLocation : routeConfig.getStops()) { stopLocation.clearPredictions(routeConfig); } } public void runParse(Reader data) throws IOException { BufferedReader bufferedReader = new BufferedReader(data, 2048); JsonElement root = new JsonParser().parse(bufferedReader); List<PredictionStopLocationPair> pairs = parseTree(root.getAsJsonObject()); clearPredictions(); for (PredictionStopLocationPair pair : pairs) { pair.stopLocation.addPrediction(pair.prediction); } } private List<PredictionStopLocationPair> parseTree(JsonObject root) { List<PredictionStopLocationPair> pairs = Lists.newArrayList(); String routeName = routeConfig.getRouteName(); String routeTitle = routeKeysToTitles.getTitle(routeName); long nowMillis = System.currentTimeMillis(); long nowSeconds = nowMillis / 1000; Map<VehicleLocations.Key, BusLocation> newLocations = Maps.newHashMap(); JsonArray messages = root.get("Messages").getAsJsonArray(); for (JsonElement element : messages) { JsonObject message = element.getAsJsonObject(); String dirTag = message.get("Destination").getAsString(); // add vehicle if exists String vehicle = message.get("Vehicle").getAsString(); // not related to GTFS trip String trip = message.get("Trip").getAsString(); String timestampString = message.get("TimeStamp").getAsString(); long timestamp = Long.parseLong(timestampString); long timestampMillis = timestamp * 1000; String latenessString = message.get("Lateness").getAsString(); int lateness; if (latenessString.length() != 0) { lateness = Integer.parseInt(latenessString); } else { lateness = 0; } if (vehicle.length() != 0) { String latitudeString = message.get("Latitude").getAsString(); String longitudeString = message.get("Longitude").getAsString(); String headingString = message.get("Heading").getAsString(); Optional<Integer> heading; if (headingString == null || headingString.length() == 0) { heading = Optional.absent(); } else { heading = Optional.of(Integer.parseInt(headingString)); } if (longitudeString.length() != 0 && latitudeString.length() != 0) { float lat = Float.parseFloat(latitudeString); float lon = Float.parseFloat(longitudeString); CommuterTrainLocation location = new CommuterTrainLocation(lat, lon, trip, timestampMillis, heading, routeName, dirTag); newLocations.put(new VehicleLocations.Key(Schema.Routes.SourceId.CommuterRail, routeName, trip), location); } } // handle predictions String flag = message.get("Flag").getAsString(); String stopId = message.get("Stop").getAsString(); String scheduledString = message.get("Scheduled").getAsString(); long scheduled = Long.parseLong(scheduledString); if (stopId.length() != 0 && scheduledString.length() != 0) { StopLocation stop = routeConfig.getStop(stopId); if (stop != null) { int seconds = (int)(scheduled - nowSeconds); int minutes = seconds / 60; long arrivalTimeMillis = currentTimeMillis + minutes * 60 * 1000; CommuterRailPrediction prediction = new CommuterRailPrediction(arrivalTimeMillis, trip, dirTag, routeName, routeTitle, false, lateness > 5*60, lateness, "", stopId, CommuterRailPrediction.Flag.toFlagEnum(flag) ); PredictionStopLocationPair pair = new PredictionStopLocationPair(prediction, stop); pairs.add(pair); } else { LogUtil.w("Commuter rail stop missing: " + stopId); } } } busMapping.update(Schema.Routes.SourceId.CommuterRail, ImmutableSet.of(routeName), false, newLocations); return pairs; } }